Ich habe CNN in Python implementiert. Ich habe es nur mit numpy implementiert, ohne die Deep-Learning-Bibliothek zu verwenden. Ich habe ["Deep Learning"](http://www.amazon.co.jp/ Deep Learning-Maschinelles Lernen Professional Series-Okaya-Takayuki / dp / 4061529021) als Lehrbuch verwendet.
Struktur dieses Artikels
CNN
CNN ist ein Vorwärtsausbreitungsnetzwerk, das Faltungsoperationen verwendet und hauptsächlich zur Bilderkennung angewendet wird. Ein typisches neuronales Netzwerk ist eine vollständig verbundene Einheit benachbarter Schichten. CNNs haben eine spezielle Schicht, in der nur bestimmte Einheiten zwischen benachbarten Schichten kombiniert werden. Diese speziellen Schichten führen die Operationen ** Faltung ** und ** Pooling ** aus. Im Folgenden werden Faltung und Pooling beschrieben.
Faltung ist eine Operation, bei der das Produkt der entsprechenden Pixel des Filters auf dem Bild und die Summe davon genommen wird. Es hat die Funktion, ein Farbmuster zu erkennen, das dem Farbmuster des Filters ähnelt. Die Bildgröße wird durch $ W \ mal W $ dargestellt, der Index wird durch $ (i, j) $ dargestellt und der Pixelwert wird durch $ x_ {ij} $ dargestellt. Die Filtergröße wird durch $ H \ mal H $ dargestellt, der Index wird durch $ (p, q) $ dargestellt und der Filterpixelwert wird durch $ h_ {pq} $ dargestellt. Die Faltung wird durch die folgende Formel ausgedrückt.
u_{ij} = \sum^{H-1}_{p=0} \sum^{H-1}_{q=0} x_{i+p, j+q} \, h_{pq}
Wenn der Filter innerhalb des Bereichs bewegt wird, der in das Bild passt, ist die Bildgröße des Ergebnisses der Faltung wie folgt. $ \ Lfloor \ cdot \ rfloor $ ist jedoch ein Operator, der nach dem Dezimalpunkt abschneidet und ihn in eine Ganzzahl konvertiert.
(W - 2 \lfloor H / 2 \rfloor) \times (W - 2 \lfloor H / 2 \rfloor)
In der Faltungsschicht wird die Faltungsoperation wie in der folgenden Abbildung gezeigt ausgeführt. Die Größe des Eingabebildes sei $ W \ mal W \ mal K $ und die Größe des Faltungsfilters sei $ H \ mal H \ mal K \ mal M $. $ K $ steht für die Anzahl der Bildkanäle und $ M $ für die Anzahl der Filtertypen. Das Ergebnis der Faltung des Bildes des $ k $ -Kanals der $ l - 1 $ -Schicht mit dem $ m $ -ten Filter ist wie folgt. $ B_ {ijm} $ repräsentiert jedoch die Vorspannung und $ f $ repräsentiert die Aktivierungsfunktion.
\begin{align}
u_{ijm} &= \sum^{K-1}_{k=0} \sum^{H-1}_{p=0} \sum^{H-1}_{q=0} z^{(l-1)}_{i+p, j+q, k} \, h_{pqkm} + b_{ijm} \\
\\
z_{ijm} &= f(u_{ijm})
\end{align}
Die Reihenfolge von $ z_ {ijm} $ wird als das Bild des $ M $ -Kanals angesehen, und die Eingabe für die nächste Ebene ist $ z ^ {(l)} _ {ijm} $. Außerdem werden dieselben Gewichte wiederholt verwendet, da die Filter beim Verschieben angewendet werden. Dies wird als ** Gewichtsverteilung ** bezeichnet.
In der obigen Abbildung beträgt die Anzahl der Kanäle des Eingabebildes $ K = 3 $, der Filtertyp ist $ M = 2 $ und das Bild des $ 2 $ -Kanals wird ausgegeben.
Pooling ist eine Operation, bei der die lokalen Bereiche eines Bildes zu einem einzigen Wert zusammengefasst werden. Es verringert die Positionsempfindlichkeit der in der Faltungsschicht extrahierten Merkmale, so dass sich die Ausgabe der Poolschicht nicht mit einer gewissen Fehlausrichtung ändert. $ H \ mal H $ quadratische Region, zentriert auf Pixel $ (i, j) $ auf einem Bild der Größe $ W \ mal W \ mal K $, und die Menge der Pixel in der Region ist $ P_ {ij} $ Es wird vertreten durch. Der durch Pooling erhaltene Wert $ u_ {ijk} $ kann wie folgt ausgedrückt werden.
u_{ijk} = \biggl(\frac{1}{H^2} \sum_{(p, q) \in P_{ij}} z^{P}_{pqk} \biggr)^{\frac{1}{P}}
Wenn $ P = 1 $ ist, wird dies als ** durchschnittliches Pooling ** bezeichnet, da die Pixel in dem Bereich gemittelt werden. Wenn $ P = \ infty $ ist, wird dies als ** maximales Pooling ** bezeichnet, da der maximale Wert der Pixel in dem Bereich verwendet wird. Die folgende Abbildung zeigt das maximale Pooling. Das Eingabebild von $ 4 \ mal 4 $ wird mit der Bereichsgröße $ 2 \ mal 2 $ gepoolt und $ 2 $ geschritten.
Ich werde das Lernen von CNN erklären. Das Kapitel zur Fehlerrückübertragung in "Implementieren eines neuronalen Netzwerks mit Python" ist hilfreich.
Erwägen Sie, die Fehlerfunktion $ E $ zu minimieren, um den aus den Trainingsdaten berechneten Ausgabewert näher an das Lehreretikett heranzuführen. Die Fehlerfunktion $ E $ wird teilweise durch das Gewicht $ w $ unterschieden, und das Gewicht wird aktualisiert, so dass es sich $ 0 $ nähert.
w_{new} = w_{old} - \varepsilon \frac{\partial E}{\partial w_{old}}
Die Berechnungsmethode für die Fehlerrückausbreitung ist dieselbe wie die eines allgemeinen neuronalen Netzwerks. $ l + 1 $ Ebenenfehler $ \ delta ^ {(l + 1)} $ und Gewichte $ w ^ {(l + 1)} $ und $ l $ Das Produkt der Differenzwerte der Eingaben aus der Ebene, $ Sie finden den Fehler in der Ebene l $. Die folgenden zwei Punkte unterscheiden sich jedoch von CNN und vollständig verbundenen neuronalen Netzen.
Dieses Mal haben wir das in [Implementierung eines Faltungsnetzwerks durch Chainer] verwendete Netzwerk implementiert (http://aidiary.hatenablog.com/entry/20151007/1444223445). Der implementierte Code ist hier aufgeführt [https://github.com/shota-takayama/cnn]. Wir werden die Befestigungspunkte getrennt für die Faltschicht und die Poolschicht einführen.
In der Faltungsschicht wird zuerst der lokale Bereich des Bildes ausgeschnitten, und diejenigen, die in der Reihenfolge angeordnet sind, werden als Eingabe vorwärts propagiert.
Wenn Sie beispielsweise das Eingabebild von $ 20 \ mal 12 \ mal 12 $ mit dem Filter von $ 50 \ mal 20 \ mal 5 \ mal 5 $ falten möchten, wird das Eingabebild wie in der folgenden Abbildung gezeigt geformt.
Die Größe des geformten Eingangs beträgt $ 64 \ mal 20 \ mal 5 \ mal 5 $.
Die Zahl $ 64 $ wird wie folgt berechnet.
Berechnen Sie die Faltung des geformten Eingabebildes und des Filters. Wiederum betragen die Eingangs- und Filtergrößen $ 64 \ mal 20 \ mal 5 \ mal 5 $ bzw. $ 50 \ mal 20 \ mal 5 \ mal 5 $.
np.tensordot(X, weight, ((1, 2, 3), (1, 2, 3)))Durch$64 \times 50$Die Ausgabe von wird erhalten.
<img width="1000" alt="tensordot.png " src="https://qiita-image-store.s3.amazonaws.com/0/82527/1f8aaf87-bc78-a0f2-e798-888dec58990b.png ">
Der folgende Code ist der Schlüsselteil der Vorwärtsausbreitung in der Faltungsschicht.
Da die Anzahl der Eingabedimensionen um eins erhöht wurde, damit sie in einem Mini-Batch gelernt werden können, wird das Argument von "Tensordot" um eins verschoben und "=" = ((2, 3, 4), (1, 2,) 3)) Es ist `` `.
```py
def __forward(self, X):
s_batch, k, xh, xw = X.shape
m = self.weight.shape[0]
oh, ow = xh - self.kh / 2 * 2, xw - self.kw / 2 * 2
self.__patch = self.__im2patch(X, s_batch, k, oh, ow)
return np.tensordot(self.__patch, self.weight, ((2, 3, 4), (1, 2, 3))).swapaxes(1, 2).reshape(s_batch, m, oh, ow)
def __im2patch(self, X, s_batch, k, oh, ow):
patch = np.zeros((s_batch, oh * ow, k, self.kh, self.kw))
for j in range(oh):
for i in range(ow):
patch[:, j * ow + i, :, :, :] = X[:, :, j:j+self.kh, i:i+self.kw]
return patch
Bei der Rückausbreitung in der Faltungsschicht ist das Produkt von $ \ delta $ in der vorherigen Schicht und dem Koeffizienten des Filters der Fehler bei der Rückausbreitung. Ich habe es wie folgt implementiert. Der für jeden lokalen Bereich erhaltene Fehler wird in die Form des Eingabebildes umgeformt. Dies ist die Umkehrung des Prozesses des Ausschneidens des Eingabebildes in den lokalen Bereich während der Vorwärtsausbreitung.
def backward(self, delta, shape):
s_batch, k, h, w = delta.shape
delta_patch = np.tensordot(delta.reshape(s_batch, k, h * w), self.weight, (1, 0))
return self.__patch2im(delta_patch, h, w, shape)
def __patch2im(self, patch, h, w, shape):
im = np.zeros(shape)
for j in range(h):
for i in range(w):
im[:, :, j:j+self.kh, i:i+self.kw] += patch[:, j * w + i]
return im
Die Vorwärtsausbreitung in der Pooling-Schicht formt auch das Eingabebild und ordnet die lokalen Regionen der Reihe nach an. Der Unterschied zur Faltungsschicht besteht darin, dass sie den Index des Pixels speichert, das den Maximalwert ergab. Dies liegt daran, dass der Fehler unter Verwendung der Information, von welchem Pixel der Wert weitergegeben wurde, rückwärts weitergegeben wird. Der folgende Code ist der Schlüsselteil der Vorwärtsausbreitung in der Pooling-Schicht.
def forward(self, X):
s_batch, k, h, w = X.shape
oh, ow = (h - self.kh) / self.s + 1, (w - self.kw) / self.s + 1
val, self.__ind = self.__max(X, s_batch, k, oh, ow)
return val
def __max(self, X, s_batch, k, oh, ow):
patch = self.__im2patch(X, s_batch, k, oh, ow)
return map(lambda _f: _f(patch, axis = 3).reshape(s_batch, k, oh, ow), [np.max, np.argmax])
def __im2patch(self, X, s_batch, k, oh, ow):
patch = np.zeros((s_batch, oh * ow, k, self.kh, self.kw))
for j in range(oh):
for i in range(ow):
_j, _i = j * self.s, i * self.s
patch[:, j * ow + i, :, :, :] = X[:, :, _j:_j+self.kh, _i:_i+self.kw]
return patch.swapaxes(1, 2).reshape(s_batch, k, oh * ow, -1)
Wie oben erwähnt, breitet die Rückausbreitung in der Poolschicht den Fehler so aus, wie er ist, und zwar auf das Pixel, das den Maximalwert ergibt. Ich habe es wie folgt implementiert.
def backward(self, X, delta, act):
s_batch, k, h, w = X.shape
oh, ow = delta.shape[2:]
rh, rw = h / oh, w / ow
ind = np.arange(s_batch * k * oh * ow) * rh * rw + self.__ind.flatten()
return self.__backward(delta, ind, s_batch, k, h, w, oh, ow) * act.derivate(X)
def __backward(self, delta, ind, s_batch, k, h, w, oh, ow):
_delta = np.zeros(s_batch * k * h * w)
_delta[ind] = delta.flatten()
return _delta.reshape(s_batch, k, oh, ow, self.kh, self.kw).swapaxes(3, 4).reshape(s_batch, k, h, w)
Ich habe handschriftliche Zahlen mit dem MNIST-Datensatz gelernt. Die Struktur der Schicht ist dieselbe wie Implementierung eines Faltungsnetzwerks durch Chainer.
Verschiedene Parameter sind wie folgt. Eingabedaten: $ 28 \ mal 28 $ Graustufenbild $ 10000 $ Blätter Lernrate: $ \ varepsilon = 0,005 $ Regularisierungstermkoeffizient: $ \ lambda = 0,0001 $ Abklingkoeffizient der Lernrate: $ \ gamma = 0,9 $ Losgröße: $ 5 $ Epoche: $ 50 $ Testdaten: $ 100 $ Graustufenbild mit der gleichen Größe wie die Eingabedaten
Unten sehen Sie eine Grafik, die den Verlust in jeder Epoche darstellt. Schließlich fiel der Verlust auf 0,104299490259 $. Darüber hinaus betrug die Identifikationsgenauigkeit von $ 100 $ Testbildern $ \ boldsymbol {0.96} $.
Ich konnte CNN implementieren. Ich habe diesmal weder Polsterung noch Chargennormalisierung implementiert, aber ich bin müde, also werde ich es beenden. Ich fand, dass die Leute, die die Bibliothek gemacht haben, unglaublich waren.
Recommended Posts