[PYTHON] CNN-Implementierung mit nur Numpy

Einführung

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.

Faltschicht

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.

conv_layer.png

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-Schicht

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.

pool.png

Lernen

Ich werde das Lernen von CNN erklären. Das Kapitel zur Fehlerrückübertragung in "Implementieren eines neuronalen Netzwerks mit Python" ist hilfreich.

Gewichtsaktualisierung

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}}

Fehler bei der Weitergabe

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.

Implementierung in Python

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.

Implementierung der Faltungsschicht

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. (12 - \lfloor 5 / 2 \rfloor \times 2) \times (12 - \lfloor 5 / 2 \rfloor \times 2) = 64

im2patch.png

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

Implementierung der Pooling-Schicht

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)

Experimentieren Sie mit dem MNIST-Datensatz

Ich habe handschriftliche Zahlen mit dem MNIST-Datensatz gelernt. Die Struktur der Schicht ist dieselbe wie Implementierung eines Faltungsnetzwerks durch Chainer.

Lernen

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

Ergebnis

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} $.

loss.png

abschließend

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

CNN-Implementierung mit nur Numpy
Gleitender Durchschnitt mit Numpy
Erste Schritte mit Numpy
Lernen Sie mit Chemo Informatics NumPy
Verkettung von Matrizen mit Numpy
Summencode mit Numpy
Führen Sie eine Regressionsanalyse mit NumPy durch
Erweitern Sie NumPy mit Rust
Ich habe GP mit Numpy geschrieben
Künstliche Datengenerierung mit Numpy
[Python] Berechnungsmethode mit numpy
Versuchen Sie die Matrixoperation mit NumPy
Animation der Diffusionsgleichung mit NumPy
Schuldenrückzahlungssimulation mit Numpy
Versuchen Sie, CNN mit ChainerRL auszuführen
SMO mit Python + NumPy implementiert
Kleben Sie die Saiten mit Numpy zusammen
Erstellen Sie einfach CNNs mit Keras
Verwenden Sie Maxout + CNN mit Pylearn2
Behandle numpy Arrays mit f2py
Verwenden Sie OpenBLAS mit numpy, scipy
Überlebe Weihnachten mit CNN auf Charakterebene
Implementierung eines neuronalen Netzwerks (nur NumPy)
Implementierung der logistischen Regression mit NumPy
QR-Code mit CNN entschlüsseln
Zeichne einen schönen Kreis mit Numpy
Spielen Sie mit der Pythonista-UI-Implementierung [Action-Implementierung]
Implementieren Sie Keras LSTM Feed Forward mit Numpy
Überprüfen Sie einfach die serielle Kommunikation mit tk
Score-CAM-Implementierung mit Keras. Vergleich mit Grad-CAM
Implementierung von Light CNN (Python Keras)
Implementierung der Dyxtra-Methode durch Python
Extrahieren Sie mehrere Elemente mit dem Numpy-Array
Deshalb beende ich Pandas [Drei Möglichkeiten, um groupby.mean () mit nur NumPy]