Für diejenigen, die mehr über die im2col-Funktion erfahren möchten, die bei der Bilderkennung mit CNN angezeigt wird Wir werden von der ersten Implementierung bis zur verbesserten Version, der Batch-Channel-kompatiblen Version und der Schritt-Padding-kompatiblen Version unter Verwendung von Gifs und Bildern ausführlich erläutern.
col2im
](Was ist # col2im)col2im
](Verhalten und Erstimplementierung von # col2im)col2im
](Verbesserung von # col2im)col2im
](# Abgeschlossene Version col2im)Die Funktion "col2im" ist eine wichtige Funktion, die in Bereichen wie der Bilderkennung, die mit der Funktion "im2im" gekoppelt ist, unverzichtbar ist.
Seine Rolle ist das Gegenteil der im2col
-Funktion, die von der im2col
-Funktion während der ** Vorwärtsausbreitung ** in eine ** Tensor $ \ rightarrow $ -Matrix ** konvertiert wurde, während sie in ** Backpropagation ** konvertiert wurde. Konvertieren Sie mit der Funktion col2im
in ** Matrix $ \ rightarrow $ tensor **.
Auf diese Weise wird es in eine lernfähige Form wie einen Filter umgewandelt.
col2im
Beginnen wir mit der ersten Implementierung. Mit anderen Worten
stride = 1 \\
pad = 0
Nehme an, dass Die Operation ist die Umkehrung der Funktion "im2col", also wie folgt. Bitte beachten Sie zu diesem Zeitpunkt, dass ** die überlappenden Teile hinzugefügt werden **. Der Grund kann unter Berücksichtigung des Filtervorgangs verstanden werden. Wenn Sie sich auf ein Element konzentrieren, wird in der folgenden Abbildung die nächste Ebene der von der Filterung betroffenen Elemente angezeigt. Mit anderen Worten, es verzweigt sich in jedes Element. Dies bedeutet, dass ** die Gradienten, die in der Rückausbreitung flossen, addiert werden müssen **. Daher ist es beim Transformieren mit der Funktion "col2im" erforderlich, "die überlappenden Teile hinzuzufügen".
Lassen Sie uns nun einfach ein Programm nach dieser Logik erstellen.
col2im.py
def col2im(cols, I_shape, O_shape):
def get_f_shape(i, o):
return i - o + 1
I_h, I_w = I_shape
O_h, O_w = O_shape
F_h = get_f_shape(I_h, O_h)
F_w = get_f_shape(I_w, O_w)
images = np.zeros((I_h, I_w))
for h in range(O_h):
h_lim = h + F_h
for w in range(O_w):
w_lim = w + F_w
images[h:h_lim, w:w_lim] += cols[:, h*O_h+w].reshape(F_h, F_w)
return images
x = np.ones((4, 4))
f = np.arange(-2*2, 0).reshape(2, 2)
im2col_x, O_shape = im2col(x, f, pad=0, get_out_size=True)
im2col_f, Of_shape = im2col(f, f, get_out_size=True)
print(im2col_x)
print(im2col_f)
print(col2im(im2col_x, x.shape, O_shape))
print(col2im(im2col_f, f.shape, Of_shape))
Ich fühle mich so. Zuerst bereiten wir eine Box vor, die nach der Verformung die Form hat, und transformieren sie dann und werfen sie in jede Reihe. Hier ist die Form des Filters der relationale Ausdruck zwischen der Eingabe und Ausgabe von im2col und dem Filter.
O_h = I_h - F_h + 1 \\
O_w = I_w - F_w + 1
Es wird berechnet mit.
col2im
Schließlich erfordert die anfängliche Implementierung einen $ O_h O_w $ -Zugriff wie "im2col", was den Nachteil einer langsamen Verarbeitungsgeschwindigkeit und Unpraktikabilität hat. Entwickeln Sie also dasselbe wie für "im2col". Die Methode ist nur in umgekehrter Reihenfolge.
col2im.py
def col2im(cols, I_shape, O_shape):
def get_f_shape(i, o):
return i - o + 1
I_h, I_w = I_shape
O_h, O_w = O_shape
F_h = get_f_shape(I_h, O_h)
F_w = get_f_shape(I_w, O_w)
cols = cols.reshape(F_h, F_w, O_h, O_w)
images = np.zeros((I_h, I_w))
for h in range(F_h):
h_lim = h + O_h
for w in range(F_w):
w_lim = w + O_w
images[h:h_lim, w:w_lim] += cols[h, w, :, :]
return images
x = np.ones((4, 4))
f = np.arange(-2*2, 0).reshape(2, 2)
im2col_x, O_shape = im2col(x, f, pad=0, get_out_size=True)
im2col_f, Of_shape = im2col(f, f, get_out_size=True)
print(im2col_x)
print(im2col_f)
print(col2im(im2col_x, x.shape, O_shape))
print(col2im(im2col_f, f.shape, Of_shape))
Zuerst wird die Matrix in col2im
eingegeben
Von
Es verwandelt sich in eine solche Form. Beim Zuweisen von Speicher für die Ausgabematrix in der verbesserten Version im2col
Es hat die gleiche Form.
später
Ich werde so darauf zugreifen. Es ist technisch ~
col2im
Betrachten Sie zum Schluss Schritt und Polsterung.
col2im.py
import numpy as np
def col2im(cols, I_shape, O_shape, stride=1, pad=0):
def get_f_shape(i, o, s, p):
return int(i + 2*p - (o - 1)*s)
if len(I_shape) == 2:
B = C = 1
I_h, I_w = I_shape
elif len(img_shape) == 3:
C = 1
B, I_h, I_w = I_shape
else:
B, C, I_h, I_w = I_shape
O_h, O_w = O_shape
if isinstance(stride, tuple):
stride_ud, stride_lr = stride
else:
stride_ud = stride
stride_lr = stride
if isinstance(pad, tuple):
pad_ud, pad_lr = pad
elif isinstance(pad, int):
pad_ud = pad
pad_lr = pad
F_h = get_f_shape(I_h, O_h, stride_ud, pad_ud)
F_w = get_f_shape(I_w, O_w, stride_lr, pad_lr)
pad_ud = int(np.ceil(pad_ud))
pad_lr = int(np.ceil(pad_lr))
cols = cols.reshape(C, F_h, F_w, B, O_h, O_w).transpose(3, 0, 1, 2, 4, 5)
images = np.zeros((B, C, I_h+2*pad_ud+stride-1, I_w+2*pad_lr+stride-1))
for h in range(F_h):
h_lim = h + stride*O_h
for w in range(F_w):
w_lim = w + stride*O_w
images[:, :, h:h_lim:stride, w:w_lim:stride] += cols[:, :, h, w, :, :]
return images[:, :, pad_ud : I_h+pad_ud, pad_lr : I_w+pad_lr]
x = np.ones((4, 4))
f = np.arange(-2*2, 0).reshape(2, 2)
im2col_x, O_shape, x_pad = im2col(x, f, pad="same")
im2col_f, Of_shape, f_pad = im2col(f, f)
print(im2col_x)
print(im2col_f)
#print((im2col_f.T@im2col_x).reshape(*O_shape))
print(col2im(im2col_x, x.shape, O_shape, pad=x_pad))
print(col2im(im2col_f, f.shape, Of_shape, pad=f_pad))
Formberechnung unter Berücksichtigung von Schritt und Polsterung
O_h = \left\lceil \cfrac{I_h - F_h + 2\textrm{pad}_{ud}}{\textrm{stride}_{ud}} \right\rceil + 1 \\
O_w = \left\lceil \cfrac{I_w - F_w + 2\textrm{pad}_{lr}}{\textrm{stride}_{lr}} \right\rceil + 1 \\
Berechnen Sie also von hier aus die Form des Filters.
F_h = I_h + 2\textrm{pad}_{ud} - (O_h - 1) \textrm{stride}_{ud} \\
F_w = I_w + 2\textrm{pad}_{lr} - (O_w - 1) \textrm{stride}_{lr}
Ich habe über verschiedene Dinge nachgedacht, aber um es richtig wiederherzustellen, ist der genaue Wert von $ \ textrm {pad} \ _ {ud}, \ textrm {pad} \ _ {lr} $ (der Wert vor dem Aufrunden durch die Deckenfunktion) Es scheint notwendig, also habe ich die Implementierung der Funktion "im2col" entsprechend geändert.
Während ich experimentierte, bemerkte ich, dass das Hinzufügen von $ 4 \ mal 4 $ Matrix-Eingabematrix nach oben, unten, links und rechts $ \ textrm {pad} = 1 $ $ 6 \ mal 6 $ ergibt, was $ 2 \ mal 2 $ Matrix ist. Wenn Sie den Filter mit $ \ textrm {stride} = 1 $ anwenden, sollte die Ausgabematrix $ 5 \ times 5 $ sein, aber das ist nicht der Fall.
Ich habe mich gefragt, warum, aber übrigens, wenn Sie unter dieser Bedingung $ \ textrm {pad} = \ textrm {same} $ in die Funktion im2col
eingeben, lautet das Auffüllen des Berechnungsergebnisses $ \ textrm {pad} = 0,5 $. Es wird sein. Und natürlich ist die Auffüllbreite eine ganze Zahl, daher wird sie auf $ \ textrm {pad} = 1 $ aufgerundet, sodass sie zu einer $ 6 \ mal 6 $ -Matrix wird.
Daher sollte es als $ 5 \ times 5 $ -Matrix behandelt werden, und Sie können sehen, dass die Funktion im2col
tatsächlich ein Produkt zurückgibt, das nur die obere linke $ 5 \ times 5 $ -Matrix verwendet.
Der Beweis ist, dass der überlappende Teil der col2im
-Funktion
Ebenso wird der obere linke Teil viermal hinzugefügt.
Die Erklärung wird erheblich vereinfacht, da es sich nur um die umgekehrte Reihenfolge der Funktion "im2col" handelt. Detailliertere Erklärungen können hinzugefügt werden, wenn Zeit verfügbar ist.