[PYTHON] Einführung in Deep Learning ~ Forward Propagation ~

Zielperson

Dies ist ein Artikel aus [Deep Learning Series](# Deep Learning Series). Der vorherige Artikel war hier. Hier werden wir zuerst die Theorie der Vorwärtsausbreitung im Skalar erklären und sie dann auf eine Matrix erweitern. Es wird zu dem im vorherigen Artikel eingeführten Code hinzugefügt oder geändert. Bitte holen Sie sich zuerst den Code aus dem vorherigen Artikel ~

Inhaltsverzeichnis

Vorwärtsausbreitung im Skalar

Dieser Abschnitt beschreibt die Theorie und Implementierung der Vorwärtsausbreitung in Skalaren (reelle Zahlen). Es ist jedoch fast wie bereits in [Grundlagen] erwähnt (https://qiita.com/kuroitu/items/221e8c477ffdd0774b6b).

Vorwärtsausbreitungstheorie im Skalar

Erstens ist die Theorie. sinple_neuron_model.png Beginnen wir mit diesem Neuronenmodell. Wenn dies formuliert ist, wird $ f (x) = \ sigma (wx + b) $ erhalten, wie in [hier] beschrieben (https://qiita.com/kuroitu/items/221e8c477ffdd0774b6b#activation function). ist. Durch Übergeben der Aktivierungsfunktion $ \ sigma (•) $ wird sie nichtlinear und hat die Bedeutung von Layering. Wie würde diese Operation in einem Rechengraphen aussehen? neuron_object.png

Ich fühle mich so. Bisher war die Eingabe nur $ x $ und andere Elemente wurden weggelassen, aber im Berechnungsdiagramm werden auch ** Gewicht $ w $ ** und ** Bias (Schwellenwert) $ b $ ** korrekt und aktiv beschrieben. Es kann umgeschrieben werden, um über eine Konvertierungsfunktion ausgegeben zu werden. Diese Variablen $ x, w, b, y $ sind die Variablen, die Neuronenobjekte haben sollten. In Bezug auf Aktivierungsfunktionen kann Python Funktionen und Klassen als Objekte in Variablen speichern. Daher scheint es gut, dass die Implementierung gekapselt werden kann, indem sie in einem Neuronenobjekt gespeichert wird. Das ist alles für die Theorie der Vorwärtsausbreitung in SCARA. Es ist einfach und schön ~.

Forward Propagation Implementierung im Skalar

Lassen Sie es uns implementieren. Der zu implementierende Code lautet [baselayer.py](https://qiita.com/kuroitu/items/884c62c48c2daa3def08#layer Modulcodevorbereitung).

baselayer.py

baselayer.py


    def forward(self, x):
        """
Implementierung der Vorwärtsausbreitung
        """
        #Erinnern Sie sich an Ihre Eingabe
        self.x = x.copy()

        #Vorwärtsausbreitung
        y = self.w * x + self.b
        self.y = self.act.forward(y)
        
        return self.y
Es enthält die Eingabe, da sie für die Backpropagation benötigt wird. Auch die Aktivierungsfunktion wurde noch nicht angesprochen, aber ich denke darüber nach, sie so zu implementieren, dass sie wie oben beschrieben verwendet werden kann. Die andere Methode, die ich habe, ist "rückwärts", die das Differential berechnet. Abhängig von der Sache kann es notwendig sein, eine "Update" -Methode zu haben.

Die Vorwärtsausbreitung selbst ist extrem einfach, nicht wahr? Nach der Formel. Das ist alles für die skalare Implementierung. Betrachten Sie als nächstes die Implementierung mit einem Vektor (anstelle einer Matrix).

Vorwärtsausbreitung in einer Matrix

Betrachten Sie als nächstes die Vorwärtsausbreitung in einer Matrix. Matrix ist streng, wenn Sie das Konzept des Matrixprodukts der linearen Algebra nicht kennen. Wenn Sie es also nicht kennen, werde ich es in [hier] kurz erläutern (# Über die Matrixoperation).

Vorwärtsausbreitungstheorie in einer Matrix

Stellen wir uns zunächst ein Ebenenobjekt vor, das wie ein Stapel von zwei Neuronenobjekten aussieht. layer_object.png Ich konnte mir keinen guten Ausdruck der Figur vorstellen, deshalb werde ich es für einen Moment erklären. Zuallererst sind die schwarzen Pfeile in Ordnung mit dem Verständnis, dass es sich um Neuronenobjekte handelt. Es sind die Pfeile anderer Farben. Der hellblaue Pfeil </ font> repräsentiert die Synapse, die das obere Neuron mit dem unteren Neuron verbindet. Multiplizieren Sie das hellblaue Gewicht $ w_ {1, 2} $ </ font>, während Sie durch den mittleren Multiplikationsknoten gehen und ihn mit dem unteren Additionsknoten verbinden. Gleiches gilt für roter Pfeil </ font>. Multiplizieren Sie das Rotgewicht $ w_ {2, 1} $ </ font> beim Durchlaufen des mittleren Multiplikationsknotens und verbinden Sie es mit dem oberen Additionsknoten. Die Eingabe des Additionsknotens ist zu drei geworden, kann jedoch durch Verwendung mehrerer Additionsknoten in zwei Eingaben zerlegt werden. Beachten Sie daher, dass sie weggelassen wird. tri_add_cal_graph.png Folgen wir dem mit einer mathematischen Formel. Im Folgenden ist $ \ sigma_i (•) $ der Einfachheit halber eine gleiche Funktion. Zunächst werde ich es aufschreiben.

y_1 = w_{1, 1}x_1 + w_{2, 1}x_2 + b_1 \\
y_2 = w_{1, 2}x_1 + w_{2, 2}x_2 + b_2

Ich denke, das selbst ist offensichtlich, wenn man sich die Figur ansieht. Lassen Sie uns dies in einer Matrixdarstellung ausdrücken.

\left(
  \begin{array}{c}
    y_1 \\
    y_2
  \end{array}
\right)
=
\left(
  \begin{array}{cc}
    w_{1, 1} & w_{2, 1} \\
    w_{1, 2} & w_{2, 2}
  \end{array}
\right)
\left(
  \begin{array}{c}
    x_1 \\
    x_2
  \end{array}
\right)
+
\left(
  \begin{array}{c}
    b_1 \\
    b_2
  \end{array}
\right)

Ich fühle mich so. Wenn Sie das Matrixprodukt verstehen, werden Sie feststellen, dass es ein äquivalenter Ausdruck ist. Achten Sie übrigens bei $ w_ {i, j} $ auf die Indizes. Normalerweise liest du Indizes wie ** $ i $ row $ j $ column **, oder? In der obigen Formel sieht es jedoch so aus, als ob ** $ j $ Zeile $ i $ Spalte **. So wie es ist, ist es schwierig, sowohl theoretisch als auch praktisch damit umzugehen. Lassen Sie es uns also verschieben.

\left(
  \begin{array}{c}
    y_1 \\
    y_2
  \end{array}
\right)
=
\left(
  \begin{array}{cc}
    w_{1, 1} & w_{1, 2} \\
    w_{2, 1} & w_{2, 2}
  \end{array}
\right)^{\top}
\left(
  \begin{array}{c}
    x_1 \\
    x_2
  \end{array}
\right)
+
\left(
  \begin{array}{c}
    b_1 \\
    b_2
  \end{array}
\right) \\
\Leftrightarrow
\boldsymbol{Y} = \boldsymbol{W}^{\top}\boldsymbol{X} + \boldsymbol{B}

Umzug wird auch in [hier] erwähnt (# Umzug). Damit ist der mathematische Ausdruck des Ebenenobjekts vorerst mit 2 Eingängen und 2 Ausgängen abgeschlossen. Es ist einfach. Verallgemeinern wir das. Das ändert sich nicht. Mathematisch

\boldsymbol{Y} = \boldsymbol{W}^{\top}\boldsymbol{X} + \boldsymbol{B}

Bleibt. Schauen wir uns das genauer an. Betrachtet man die $ M $ Eingabe $ N $ Ausgabeebene

\underbrace{\boldsymbol{Y}}_{N \times 1} = \underbrace{\boldsymbol{W}^{\top}}_{N \times M}\underbrace{\boldsymbol{X}}_{M \times 1} + \underbrace{\boldsymbol{B}}_{N \times 1}

Es sieht aus wie. $ \ Boldsymbol {W} ^ {\ top} $ zeigt die Form nach der Translokation. Vor der Übertragung ist es $ \ underbrace {\ boldsymbol {W}} _ {M \ times N} $. Das ist alles für die Theorie. Fahren wir nun mit der Implementierung fort.

Implementierung der Vorwärtsausbreitung in einer Matrix

Der Implementierungsspeicherort ist [baselayer.py](https://qiita.com/kuroitu/items/884c62c48c2daa3def08#layer Modulcodevorbereitung) wie bei der skalaren Implementierung. Schreiben Sie die Implementierung in Skalar um.

baselayer.py

baselayer.py


    def forward(self):
        """
Implementierung der Vorwärtsausbreitung
        """
        #Erinnern Sie sich an Ihre Eingabe
        self.x = x.copy()

        #Vorwärtsausbreitung
        y = self.w.T @ x + self.b
        self.y = self.act.forward(y)
        
        return self.y
Nur eine Zeile hat sich geändert.

baselayer.py


        y = self.w * x + self.b

Aber

baselayer.py


        y = self.w.T @ x + self.b

Es ist geworden. Für Numpy-Arrays kann die Translokation mit "ndarray.T" durchgeführt werden. Einige Leute kennen den Operator "@" möglicherweise nicht, aber es ist der gleiche Vorgang wie "np.dot". ** Es kann mit Numpy Version 1.10 oder höher verwendet werden. Seien Sie also vorsichtig, wenn Sie eine niedrigere Version verwenden. ** ** **

`@` operator description

test_at.py


x = np.array([1, 2])
w = np.array([[1, 0], [0, 1]])
b = np.array([1, 1])

y = w.T @ x + b
print(y)
print(y == np.dot(w.T, x) + b)

#----------
#Die Ausgabe ist
# [2 3]
# [ True  True]
#Es wird sein.

Tatsächlich wird hier implizit die Funktion zum Berechnen der Matrix und des Vektors des Operators "@" verwendet. Wenn Sie als Matrix richtig berechnen möchten, müssen Sie möglicherweise "np.matrix" anstelle von "np.array" und dann "umformen" verwenden.

test_at.py


x = np.matrix([1, 2]).reshape(2, -1)
w = np.matrix([[1, 0], [0, 1]])
b = np.matrix([1, 1]).reshape(2, -1)

y = w.T @ x + b
print(y)
print(y == np.dot(w.T, x) + b)

#----------
#Die Ausgabe ist
# [[2]
#  [3]]
# [[ True]
#  [ True]]
#Es wird sein.

Es ist nervig, nicht wahr? Sie können np.array verwenden.

Damit ist die Implementierung der Vorwärtsausbreitung abgeschlossen. Ich werde es (vielleicht) nicht mehr ändern.

Implementierung der Methode __init __

Übrigens gibt es einige Elemente, die das Ebenenobjekt haben sollte. Lassen Sie uns dies implementieren. Außerdem werde ich einige Mitglieder haben.

Implementierung von `__init__`

baselayer.py


    def __init__(self, *, prev=1, n=1, 
                 name="", wb_width=1,
                 act="ReLU",
                 **kwds):
        self.prev = prev  #Anzahl der Ausgänge der vorherigen Ebene=Anzahl der Eingaben in diese Ebene
        self.n = n        #Anzahl der Ausgänge in dieser Ebene=Anzahl der Eingaben in die nächste Ebene
        self.name = name  #Der Name dieser Ebene
        
        #Stellen Sie Gewicht und Vorspannung ein
        self.w = wb_width*np.random.randn(prev, n)
        self.b = wb_width*np.random.randn(n)
        
        #Aktivierungsfunktion(Klasse)Erhalten
        self.act = get_act(act)
Wie Sie jeweils schreiben. Für Gewichte und Verzerrungen legt die Methode `numpy.random.randn` Zufallszahlen gemäß der Standardnormalverteilung fest. Es wird auch mit "wb_width" multipliziert, damit die Größe angepasst werden kann. Die Aktivierungsfunktion wird durch Importieren der Methode "get_act" aus activations.py erhalten. Schließlich werde ich den Code von [hier](https://qiita.com/kuroitu/items/73cd401afd463a78115a) anpassen und ihn bringen.

Informationen zur Matrixoperation

Hier werden wir kurz Matrixoperationen vorstellen. Bitte beachten Sie, dass ich nichts mathematisch erklären werde (ich kann es nicht tun), nur weil es so berechnet wird.

Matrixsumme

Erstens ist die Matrixsumme.

\left(
  \begin{array}{cc}
    a & b \\
    c & d
 \end{array}
\right)
+
\left(
  \begin{array}{cc}
    A & B \\
    C & D
  \end{array}
\right)
=
\left(
  \begin{array}{cc}
    a + A & b + B \\
    c + C & d + D
  \end{array}
\right)

Nun, es ist ein natürliches Ergebnis. Für jedes Element hinzufügen. Ganz zu schweigen von der Addition ** Die Form der Matrix muss genau übereinstimmen. ** ** **

Matrixelementprodukt

Ich werde auch auf elementare Produkte eingehen, die in diesem Artikel überhaupt nicht erwähnt werden. Das Elementprodukt wird auch als Adamar-Produkt bezeichnet.

\left(
  \begin{array}{cc}
    a & b \\
    c & d
 \end{array}
\right)
\otimes
\left(
  \begin{array}{cc}
    A & B \\
    C & D
  \end{array}
\right)
=
\left(
  \begin{array}{cc}
    aA & bB \\
    cC & dD
  \end{array}
\right)

Das Symbol des Adamar-Produkts kann übrigens als "\ otimes" geschrieben werden. Es gibt viele andere symbolbezogene Elemente auf hier.

Matrix Produkt

Dies ist ein Matrixprodukt, kein Elementprodukt. Es ist ein großer Unterschied zum Elementprodukt, wie z. B. Formbeschränkungen und Nichtkommutierbarkeit.

\left(
  \begin{array}{cc}
    a & b \\
    c & d
 \end{array}
\right)
\left(
  \begin{array}{cc}
    A & B \\
    C & D
  \end{array}
\right)
=
\left(
  \begin{array}{cc}
    aA + bC & aB + bD \\
    cA + dC & cB + dD
  \end{array}
\right)

Als Berechnungsmethode fühlt es sich an, als würde man horizontal $ \ times $ rollen. matrix_product_image.gif Es ist so. Um auf diese Weise berechnen zu können, müssen die Anzahl der Elemente von ** horizontal ** </ font> und die Anzahl der Elemente von ** vertikal ** </ font> berechnet werden Muss passen. Mit anderen Worten, die ** Spalte ** </ font> der ersten Matrix und die ** Zeile ** </ font> der zweiten Matrix sind eins. Du musst es tun. Im Allgemeinen ist die resultierende Matrix des Produkts der Matrizen ** $ L \ mal M $ und $ M \ mal N $ $ L \ mal N $. ** ** **

Translokation

Bei der Translokation werden Zeilen und Spalten einer Matrix ausgetauscht.

\left(
  \begin{array}{cc}
    a & b & c \\
    d & e & f
 \end{array}
\right)^{\top}
=
\left(
  \begin{array}{cc}
    a & d \\
    b & e \\
    c & f
 \end{array}
\right)

Das Inversionssymbol wird als "\ top" geschrieben. Diese Operation benötigt nur diese Erklärung.

abschließend

Damit ist die Implementierung der Vorwärtsausbreitung abgeschlossen. Insbesondere kann es erforderlich sein, eine unterschiedliche Verarbeitung zwischen der Zwischenschicht und der Ausgabeschicht durchzuführen, sodass diese in middlelayer.py und outputlayer.py überschrieben werden muss. Es gibt kein. Die Vorwärtsausbreitung ist einfach und nett.

Referenz

Deep Learning-Serie

Recommended Posts