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 ~
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).
Erstens ist die Theorie. 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?
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 ~.
Lassen Sie es uns implementieren. Der zu implementierende Code lautet [baselayer.py](https://qiita.com/kuroitu/items/884c62c48c2daa3def08#layer Modulcodevorbereitung).
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
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).
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).
Stellen wir uns zunächst ein Ebenenobjekt vor, das wie ein Stapel von zwei Neuronenobjekten aussieht. 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. 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.
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
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
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. ** ** **
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.
Übrigens gibt es einige Elemente, die das Ebenenobjekt haben sollte. Lassen Sie uns dies implementieren. Außerdem werde ich einige Mitglieder haben.
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)
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.
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. ** ** **
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.
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. 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 $. ** ** **
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.
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.
Recommended Posts