Dieser Artikel erklärt den Mechanismus des neuronalen Netzwerks beim Verstehen und Implementieren unter Verwendung des Konzepts der Mathematik auf High-School-Ebene. Ich werde bestätigen, indem ich nur implementiere, wie die Vorwärts- und Rückwärtsberechnungen durchgeführt werden, ohne die detaillierte Theorie zu erläutern (es gibt keine Erklärung zum Lernen wie Gradientenmethode und Optimierung). Verwenden Sie Python + Numpy für die Implementierung. Grundsätzlich schreibt numpy Code so, als ob er importiert worden wäre. Bitte beachten Sie, dass dieser Artikel auf intuitives Verständnis ausgerichtet ist und theoretisch oder theoretisch irreführend sein kann. Wenn Sie es verallgemeinern, können die Indizes verwirrt sein und Sie verstehen es möglicherweise nicht. Im Grunde werden wir es anhand konkreter Beispiele verstehen.
Um das neuronale Netzwerk zu verstehen und zu implementieren, sind das innere Produkt von Vektoren, das Matrixprodukt und einige Differentialformeln erforderlich, daher werde ich kurz erläutern. Wenn Sie ein solides Verständnis der Mathematik der High School haben, können Sie es überspringen.
Betrachten Sie die folgenden zwei Vektoren.
\vec{x} = ({x_1, x_2, x_3}),\:\vec{w} = ({w_1, w_2, w_3})
Das innere Produkt dieses Vektors $ \ vec {x} $ und $ \ vec {w} $ ist unten definiert.
\vec{w}・\vec{x} = (w_1x_1+w_2x_2+w_3x_3)
Die Geschichte ist einfach, multiplizieren Sie einfach und fügen Sie jedes entsprechende Element hinzu. Mit numpy kann das innere Produkt wie folgt geschrieben werden.
x = np.random.randn(3)
w = np.random.randn(3)
np.dot(w,x)
Es scheint, dass sich die Mathematik der High School derzeit nicht mit Matrizen befasst, aber es ist einfacher, mit Matrizen zu schreiben und zu implementieren. Bitte tun Sie Ihr Bestes, um sie zu verstehen. Alles, was Sie wissen müssen, ist, dass die Matrix ein Array von Vektoren ist und wie die Multiplikation und Addition erfolgt. Seien Sie also geduldig und denken Sie daran. Betrachten Sie die folgenden zwei Matrizen.
X=\left(\begin{matrix}
x_{11} & x_{12} & x_{13} \\
x_{21} & x_{22} & x_{32}
\end{matrix}\right),\:
W=\left(\begin{matrix}
w_{11} & w_{12} \\
w_{21} & w_{22} \\
w_{31} & w_{32}
\end{matrix}\right)
Die Multiplikation dieser beiden Matrizen $ X $ und $ W $ ist unten definiert.
WX = \left(\begin{matrix}
w_{11}x_{11}+w_{21}x_{12}+w_{31}x_{13} & w_{12}x_{11}+w_{22}x_{12}+w_{32}x_{13} \\
w_{11}x_{21}+w_{21}x_{22}+w_{31}x_{23} & w_{12}x_{21}+w_{22}x_{22}+w_{32}x_{23}
\end{matrix}\right)
Es ist ein wenig verwirrend, aber die erste Zeile von $ X $ {$ x_ {11}, x_ {12}, x_ {13} $} und die erste Spalte von $ W $ {$ w_ {11}, w_ {21}, Das innere Produkt der Vektoren von w_ {31} $} befindet sich in der 1. Zeile und 1. Spalte und in der 2. Zeile von $ X $ {$ x_ {21}, x_ {22}, x_ {23} $} und der 1. Spalte von $ W $ Das innere Produkt des Vektors des Auges ist der Wert der n-ten Zeile und der ersten Spalte, und das innere Produkt des Vektors der n-ten Zeile der vorherigen Matrix und der m-ten Spalte der hinteren Matrix ist der Wert der n-ten Zeile und der m-ten Spalte. Beachten Sie, dass Sie nur multiplizieren können, wenn die Anzahl der Zeilen in der vorderen Spalte und die Anzahl der Spalten in der hinteren Spalte gleich sind. Das Matrixprodukt wird wie folgt durch Numpy ausgedrückt.
X = np.random.randn(2,3)
W = np.random.randn(3,2)
np.dot(X,W)
Wenn die Anzahl der Zeilen und die Anzahl der Spalten nicht übereinstimmen, tritt ein Fehler auf.
X = np.random.randn(3,2)
W = np.random.randn(3,2)
np.dot(X,W) #Error
X = np.random.randn(1,4)
W = np.random.randn(4,2)
np.dot(X, W) #Berechenbar
Nachfolgend sind einige der diesmal verwendeten Differentialformeln aufgeführt.
f(x)=x+4\:\:\:---->\:\:f'(x)=1\\
f(x)=1/x\:\:\:---->\:\:f'(x)=-1/x^2\\
f(x)=4x\:\:\:---->\:\:f'(x)=4\\
f(x)=\exp(x)\:\:---->\:\:f'(x)=\exp(x)
Abgesehen von den Komplikationen drückt ein neuronales Netzwerk eine bestimmte Ausgabe aus, indem es eine bestimmte Eingabe mit einem numerischen Wert multipliziert, der als Gewicht bezeichnet wird, und einen numerischen Wert addiert, der als Bias bezeichnet wird, wie in der folgenden Abbildung gezeigt. Die obige Formel lautet $ o = wx + b $. Eine Funktion wie eine lineare Funktion, die dieselbe Form hat wie die in der Mathematik der Mittelstufe erlernte lineare Funktion, wird als lineare Funktion bezeichnet. In einem tatsächlichen neuronalen Netzwerk reichen die Ein- und Ausgänge von Hunderten bis Tausenden, und die Formel lautet $ o = w_ {1} x_ {1} + w_ {2} x_ {2} + ... + w_ {n} Es sieht aus wie x_ {n} + b $. Darüber hinaus drückt das neuronale Netzwerk eine komplizierte Funktion aus, indem eine nichtlineare Funktion (eine gekrümmte Funktion wie eine quadratische Funktion) in Bezug auf diese Ausgabe verwendet wird. Wir werden dieses Gewicht und diese Vorspannung lernen, so dass das neuronale Netzwerk entsprechend dem Problem die entsprechende Ausgabe ausgibt. Sie können sehen, was Sie speziell tun können, indem Sie sich Stanfords [convnetjs] ansehen (http://cs.stanford.edu/people/karpathy/convnetjs/). Als Beispiel siehe Demo: Toy 2d-Klassifizierung mit 2-Schicht-Neuronalen Netzwerk in Convnetjs. Die folgende Abbildung zeigt die Trainingsergebnisse eines neuronalen Netzwerks, das darauf trainiert ist, das Problem der Trennung der zweidimensionalen grünen und roten Daten zu lösen. <img width =" 421 "alt =" Screenshot 2017-03-11 23.59.36.png "src =" https://qiita-image-store.s3.amazonaws.com /0/123951/f672a1d4-c560-2f3b-3ec6-3e6bc592e925.png ">
Im neuronalen Netz werden die Gewichts- und Vorspannungswerte durch Lernen bestimmt, und die Funktion der Grenzlinie, die den roten und den grünen Bereich trennt, wird durch Wiederholen der linearen Kopplung und nichtlinearer Funktionen ausgedrückt. Die Funktionen, die ausgedrückt werden können, hängen von der Anzahl der Neuronen und der Auswahl der Aktivierungsfunktion ab (wird später erläutert). In der Reihenfolge ist die Anzahl der intermediären Neuronen 6 und die Aktivierungsfunktion ist tanh, die Anzahl der intermediären Neuronen ist 2 und die Aktivierungsfunktion ist tanh und die Anzahl der intermediären Neuronen ist 6 und die Aktivierungsfunktion ist relu. Als Bild nimmt mit zunehmender Anzahl von Neuronen die Anzahl der geraden Linien zu, und wenn Sie tanh für die Aktivierungsfunktion verwenden, sind die Gelenke zwischen den geraden Linien rund, und wenn Sie relu verwenden, sind sie scharf. Der Grund dafür wird in diesem Artikel nicht erwähnt. Bitte studieren Sie in Lehrbüchern.
Lassen Sie uns zunächst Forward verstehen. Ich denke, die Vorwärtsausbreitungsberechnung ist viel einfacher und verständlicher als die Rückwärtsausbreitungsberechnung. Die Berechnung der Vorwärtsausbreitung bezieht sich auf $ o = wx + b $, die im vorherigen Teil durchgeführt wurden. Betrachten Sie zunächst ein neuronales Netzwerk mit 1 Eingang und 1 Ausgang. Um es einfacher zu machen, Zu Es wird ausgedrückt als. In Zukunft wird es keine Vorurteile mehr geben, die das Betrachten und Verstehen erleichtern. Ich denke, es ist nicht notwendig, 1 Eingabe und 1 Ausgabe zu schreiben, aber es ist eine einfache Implementierung wie folgt.
one2one.py
x = np.random.randn(1)
w = np.random.randn(1)
w*x
Erweitern Sie dies auf mehrere Eingänge und 1 Ausgang. Diese Berechnung kann ausgedrückt werden als $ o = w_1x_1 + w_2x_2 + w_3x_3 $. Da diese Formel das innere Produkt der Vektoren selbst ist, kann sie wie folgt implementiert werden.
many2one.py
x = np.random.randn(1,3)
w = np.random.randn(3,1)
np.dot(x,w)
Betrachten Sie als nächstes 1 Eingabe und mehrere Ausgabe. Dies kann ausgedrückt werden als $ o_1 = w_1x, o_2 = w_2x, o_3 = w_3x $. Dies definiert o und w als $ \ vec {o} = {o_1, o_2, o_3}, \ vec {w} = {w_1, w_2, w_3} $ und $ \ vec {o} = x \ vec Es kann als {w} $ umgeschrieben werden. Dies kann wie folgt implementiert werden:
one2many.py
x = np.random.randn(1,1)
w = np.random.randn(1,3)
np.dot(x,w)
Betrachten Sie schließlich mehrere Eingänge und mehrere Ausgänge. Dies kann ausgedrückt werden als $ o_1 = w_ {11} x_1 + w_ {21} x_2 + w_ {31} x_3 $, $ o_2 = w_ {12} x_1 + w_ {22} x_2 + w_ {32} x_3 $, $ o_3 = w_ {13} x_1 + w_ {23} x_2 + w_ {33} x_3 $. In einen Vektor konvertiert, $ \ vec {x} = {x_1, x_2, x_3} $, $ \ vec {w_1} = {w_ {11}, w_ {21}, w_ {31}} $, $ \ vec {w_2 } = {w_ {12}, w_ {22}, w_ {32}} $, $ \ vec {w_3} = {w_ {13}, w_ {23}, w_ {33}} $, berechnet als $ o_1 = \ vec {w_1} \ vec {x} $, $ o_2 = \ vec {w_2} \ vec {x} $, $ o_3 = \ vec {w_3} \ vec {x} $. Dies kann unter Verwendung eines Matrixprodukts ausgedrückt werden, und jeder Wert wird wie folgt als Matrix ausgedrückt.
X=\begin{matrix} x_1 & x_2 & x_3 \end{matrix},
W=\begin{matrix} w_{11} & w_{12} & w_{13} \\
w_{21} & w_{22} & w_{23} \\
w_{31} & w_{32} & w_{33} \end{matrix},
O=\begin{matrix} o_1 & o_2 & o_3\end{matrix}
Dann kann es als Berechnung als $ O = XW $ ausgedrückt werden. Ich konnte es ganz ordentlich ausdrücken. Wenn Sie mit der Prozession nicht vertraut sind, bewegen Sie bitte Ihre Hände und überprüfen Sie sie. Die Implementierung erfolgt wie folgt.
many2many.py
X=np.random.randn(1,3)
W=np.random.randn(3,3)
np.dot(X,W)
Dies ist das Ende der Vorwärtsausbreitung, aber in Wirklichkeit fügt es Voreingenommenheit hinzu. Wenn Sie dies wie unten gezeigt mit mehreren Schichten verbinden, haben Sie schließlich ein mehrschichtiges neuronales Netzwerk. Grundsätzlich wird die in der vorherigen Schicht erhaltene Ausgabe O als die Ausgabe X des neuronalen Netzwerks in der nächsten Schicht angesehen, und die gleiche Berechnung wird durchgeführt.
multi_layer.py
X=np.random.randn(1,3)
layer1_W=np.random.randn(3,2)
layer2_W=np.random.randn(2,1)
layer1_O=np.dot(X,layer1_W)
layer2_O=np.dot(layer1_O, layer2_W)
Neben der Vorwärtsausbreitung werden wir verstehen, wie man Gewichts- und Bias-Werte lernt. Zunächst sei das neuronale Netz eine Funktion wie $ f (X) = w_1x_1 + w_2x_2 + ... + w_nx_n $ (jedoch $ X = {x_1, x_2, ..., x_n} $). Andererseits wird das Gewicht unter Verwendung der folgenden Formel aktualisiert.
w_i^{new}=w_i-lr\frac{\partial{f(x)}}{\partial{w_i}}
$ lr $ wird als Lernrate bezeichnet, die steuert, wie stark der Wert geändert wird. Wenn die Lernrate zu groß oder zu klein ist, funktioniert das Lernen nicht. Im Zusammenhang mit neueren neuronalen Netzen liegen die Werte häufig zwischen 0,1 und 0,0001, aber im Grunde hängt dies von Erfahrung und Gefühl ab, da wir nicht wissen, welcher Wert angemessen ist. $ \ frac {\ partielle {f (x)}} {\ partielle {w_i}} $ ist $ f (X) $ differenziert in Bezug auf $ w_i $, und die obige $ f (x) $ Definition ist $ \ frac {\ teilweise {f (x)}} {\ teilweise {w_i}} = x_i $. Ersetzen Sie aus Gründen der Verzerrung in derselben Berechnung einfach $ w $ durch $ b $. Ich werde nicht erklären, warum die Gewichte aktualisiert werden, so dass der Ausgabewert zum Zielwert wird. Obwohl dies der wichtigste Teil ist, können Probleme auftreten, wenn Sie nur den wichtigen Teil auf der Ebene der High-School-Mathematik intuitiv verstehen. Wenn Sie also die Möglichkeit haben, werde ich dies in einem anderen Artikel erläutern. Zurück zum Hauptthema: Die obige Differenzierung kann im Fall eines einschichtigen neuronalen Netzwerks sehr leicht berechnet werden, jedoch nicht in dem üblicherweise verwendeten mehrschichtigen neuronalen Netz. Wie ich zu Beginn geschrieben habe, führt das neuronale Netzwerk die Transformation unter Verwendung der nichtlinearen Funktion nach der im Abschnitt über die Vorwärtsausbreitung beschriebenen linearen Kopplung durch. Eine nichtlineare Funktion zu verwenden bedeutet, wie folgt zu berechnen.
o = w_1x_1+w_2x_2+w_3x_3,\:g(x)=\frac{1}{1+\exp(-x)},\:o'=g(o)=\frac{1}{1+\exp(-o)}=\frac{1}{1+\exp(-(w_1x_1+w_2x_2+w_3x_3))}
Übrigens ist das obige $ g (x) $ eine nichtlineare Funktion, die als Sigmoidfunktion bezeichnet wird und häufig in neuronalen Netzen verwendet wird (in letzter Zeit wird sie nur in der Ausgabeschicht verwendet). Eine nichtlineare Funktion, die auf diese Weise die Ausgabe des neuronalen Netzes beeinflusst, wird als Aktivierungsfunktion bezeichnet. Es ist ein wenig schwierig, diese endgültige Ausgabe $ o '$ für ein bestimmtes $ w_1 $ zu unterscheiden, aber Sie können trotzdem Ihr Bestes tun, um sie zu berechnen. Betrachten Sie jedoch den Fall, in dem dies mehrschichtig ist, wie in der folgenden Abbildung gezeigt. Wenn Sie dies in eine mathematische Formel schreiben, sieht es wie folgt aus.
o_{1} = w_{11}x_1+w_{21}x_2+w_{31}x_3,\:o_{2} = w_{12}x_1+w_{22}x_2+w_{32}x_3,\\
o'_{1}=g(o_1),\:o'_{2}=g(o_2),\\
o=w'_1o'_1+w'_2o'_2,\:o'=g(o)
Wenn Sie das letzte $ o '$ von Anfang an schreiben, sieht es wie folgt aus.
o'=\frac{1}{1+\exp(-(w'_1\frac{1}{1+\exp(-(w_{11}x_1+w_{21}x_2+w_{31}x_3)}+w'_2\frac{1}{1+\exp(-(w_{12}x_1+w_{22}x_2+w_{32}x_3)})}
Zum Beispiel ist es sehr schwierig, dies für $ w_ {11} $ zu unterscheiden. Wenn dies über Dutzende von Schichten wie ein tiefes neuronales Netz erfolgt, ist dies eine enorme Berechnung. Um dies effektiv zu berechnen, wird die unten eingeführte Berechnung der Rückausbreitung verwendet.
Das Konzept der Kettenregel ist wichtig für die Durchführung von Backpropagation-Berechnungen. Das Kettengesetz besagt, dass die folgende Formel gilt.
\frac{\partial{f(x)}}{\partial{w}}=\frac{\partial{f(x)}}{\partial{o}}\frac{\partial{o}}{\partial{w}}
Das heißt, wenn versucht wird, $ f (X) $ für eine Variable namens $ w $ zu unterscheiden, kann dies durch Multiplizieren von $ f (x) $ mit $ o $ und $ o $ mit $ w $ ausgedrückt werden. Das ist. Wenn Sie $ \ teilweise {o} $ als Bild verkleinern, entspricht dies dem Original. Wenden Sie es beispielsweise auf Folgendes an (es kann ohne Verwendung der Kettenregel gelöst werden).
o=w+z,\:f(x)=(w+z)y=oy,\\
\frac{\partial{o}}{\partial{w}}=1,\:\frac{\partial{f(x)}}{\partial{o}}=y,\:\frac{\partial{f(x)}}{\partial{w}}=\frac{\partial{f(x)}}{\partial{o}}\frac{\partial{o}}{\partial{w}}=y
Die obige Berechnung kann ohne Verwendung der Kettenregel durchgeführt werden, ist jedoch eine sehr effektive Berechnungsmethode, wenn sie mit der Sigmoid-Funktion auf eine mehrschichtige angewendet wird. Als nächstes werden wir eine Backpropagation-Berechnung für das tatsächliche neuronale Netz durchführen. Betrachten Sie zunächst ein einfaches neuronales Netz mit 1 Eingang und 1 Ausgang.
Dieses neuronale Netz kann ausgedrückt werden als $ f (x) = wx + b $, und sagen wir $ A = wx $ als Zwischenausgabe. $ \ Frac {\ partiell {f (x)}} {\ partiell {w}} $ wird benötigt, um Gewichte als Ziel zu aktualisieren, und $ \ frac {\ partiell {f (x)} wird benötigt, um die Verzerrung zu aktualisieren Betrachten Sie die Berechnung von} {\ teilweise {b}} $. Berechnen Sie zunächst $ \ frac {\ partielle {f (x)}} {\ partielle {b}} $. Da es ausgedrückt werden kann als $ f (x) = A + b $, ist die Differenzierung von $ f (x) $ für $ b $ $ \ frac {\ partiell {f (x)}} {\ partiell {b}} = Es wird 1 $ sein. Berechnen Sie dann $ \ frac {\ partielle {f (x)}} {\ partielle {w}} $. Unter Verwendung der Kettenregel ist $ \ frac {\ partiell {f (x)}} {\ partiell {w}} = \ frac {\ partiell {f (x)}} {\ partiell {A}} \ frac {\ partiell Es kann ausgedrückt werden als {A}} {\ teilweise {w}} $, und es kann ausgedrückt werden als $ \ frac {\ teilweise {f (x)}} {\ teilweise {A}} = 1 $, $ \ frac {\ teilweise {A} } {\ teilweise {w}} = x = 3 $, also $ \ frac {\ teilweise {f (x)}} {\ teilweise {w}} = 3 $. Der Zustand der Rückausbreitung ist unten blau dargestellt. Diese Backpropagation-Berechnung ist sehr leistungsfähig. Zum Beispiel kann die Differenzierung der oben erwähnten Sigmoidfunktion wie folgt berechnet werden (versuchen Sie es zu berechnen).
f(x)=\frac{1}{1+\exp(-x)},\\
f'(x)=f(x)(1-f(x))=\frac{1}{1+\exp(-x)}(1-\frac{1}{1+\exp(-x)})
Dann $ \ frac {\ partiell {ohne das Differential der Sigmoidfunktion selbst zu berechnen Lassen Sie uns den Wert von f (x)}} {\ partielle {x}} $ durch Backpropagation-Berechnung ermitteln. Die Sigmoidfunktion wird wie folgt ausgedrückt. Hier repräsentiert $ D $ die Sigmoidfunktion ($ D = f (x) $). Wir werden die Rückausbreitung berechnen.
\frac{\partial{D}}{\partial{C}}=(\frac{1}{C})'=\frac{-1}{C^2}=\frac{-1}{(1.37)^2}=-0.53\\
\frac{\partial{D}}{\partial{B}}=\frac{\partial{D}}{\partial{C}}\frac{\partial{C}}{\partial{B}}=(-0.53)(1+B)'=(-0.53)(1)=-0.53\\
\frac{\partial{D}}{\partial{A}}=\frac{\partial{D}}{\partial{B}}\frac{\partial{B}}{\partial{A}}=(-0.53)(\exp(A))'=(-0.53)(\exp(A))=(-0.53)(0.37)=0.2\\
\frac{\partial{D}}{\partial{x}}=\frac{\partial{D}}{\partial{A}}\frac{\partial{A}}{\partial{x}}=(-0.2)(-x)'=(-0.2)(-1)=0.2
Wenn Sie $ f '(x) = f (x) (1-f (x)) $ durch $ x = 1 $ ersetzen, was die Differenzierung der Sigmoidfunktion darstellt, sollten Sie das gleiche Ergebnis erhalten (gültig). Bitte beachten Sie, dass es mit 2 Ziffern berechnet wird). Kehren wir zum neuronalen Netzwerk zurück. Betrachten Sie als nächstes die folgende Rückausbreitung eines mehrschichtigen neuronalen Netzes. Es wird schwer zu sehen sein, aber der Ausdruck wird wie folgt korrigiert.
Die Rückausbreitungsberechnung dieses neuronalen Netzes wird durchgeführt. Wenn Sie den Zwischenprozess weglassen und nur das Ergebnis anzeigen, ist dies wie folgt. Bitte rechnen Sie tatsächlich. Der unterstrichene Wert ist $ \ frac {\ partielle {f (x)}} {\ partielle {w_i}} $, der tatsächlich zum Aktualisieren der Parameter verwendet wird. Das Lernen kann durchgeführt werden, indem der auf diese Weise berechnete Wert mit der Lernrate multipliziert und vom ursprünglichen Wert subtrahiert wird. Lassen Sie uns abschließend eine Reihe von Vorwärts-, Rückwärts- und Parameteraktualisierungsabläufen implementieren. Dieses Mal implementieren wir das voreingenommene, aber bitte versuchen Sie, das voreingenommene selbst zu implementieren, um zu überprüfen, ob Sie es verstehen. Definieren Sie die folgende nn-Klasse.
nn.py
class nn():
def __init__(self, n_i, n_o, lr):
self.weight = np.random.randn(n_o, n_i)
self.input = None
self.grad = np.zeros((n_i, n_o))
self.lr = lr
def forward(self, x):
self.inputs = x.reshape(-1, 1) #Eingabewert halten
return np.dot(self.weight, self.inputs)
def backward(self, dx):
self.grad = np.dot(self.inputs.reshape(-1,1), dz.reshape(1,-1)).reshape(self.weight.shape) #Differenzierungsberechnung für w
return np.dot(dz.reshape(1, -1), self.weight) #Differenzierungsberechnung für x
def update(self):
self.weight -= self.grad*self.lr
In Bezug auf Forward ist es dieselbe Implementierung wie durch Forward Propagation implementiert. Der Eingabewert bleibt erhalten, da er zur Berechnung des Differenzwerts von w während der Backpropagation-Berechnung benötigt wird. Wie Sie sehen können, indem Sie das vorherige Beispiel tatsächlich manuell berechnen, ist der Differenzwert für w der Wert der Eingabe x multipliziert mit dem Differenzwert, der von zuvor weitergegeben wurde. Rückwärts wird das Matrixprodukt verwendet, um die Differenzierung zu berechnen. Wenn Sie jede Berechnung wie bei der Vorwärtsausbreitung aufschreiben, können Sie sehen, dass die Differentiale für alle w vom Matrixprodukt auf einmal berechnet werden können. In ähnlicher Weise kann der auf x bezogene Differenzwert durch das Matrixprodukt des Differenzwerts und des Gewichts berechnet werden, die zuvor propagiert wurden. Wenn Sie prüfen, ob Sie tatsächlich mit dem inneren Produkt rechnen können, können Sie feststellen, ob Sie es verstehen. Überprüfen Sie es daher bitte. Wenn der Differenzwert (Grad) von w berechnet werden kann, kann das Gewicht aktualisiert werden, indem die Lernrate multipliziert und vom ursprünglichen Wert subtrahiert wird.
test.py
from nn import nn
fc = nn(10, 2, 0.1)
x = np.random.randn(10, 1)
fc.forward(x)
grad = np.random.randn(1,2) #Differenzierungswert von vorne
fc.backward(grad)
fc.update()
Sie können das Gewicht wie oben aktualisieren. Oben wird der Differenzwert, der von vorne kommt, mit einer Zufallszahl erzeugt, aber in Wirklichkeit wird er unter Verwendung einer Fehlerfunktion berechnet, die den Fehler aus dem Zielwert berechnet. Beim tatsächlichen Erstellen eines Modells eines neuronalen Netzwerks wird eine Aktivierungsfunktion (nichtlineare Funktion) zwischen den Schichten eingefügt. Dieses Mal werde ich die Sigmoid-Funktion implementieren. Wie ich zuvor geschrieben habe, haben sowohl Vorwärts als auch Rückwärts Formeln, und Rückwärts kann wie folgt implementiert werden, da es unter Verwendung des Berechnungsergebnisses der Vorwärtsausbreitungsberechnung berechnet wird.
sigmoid.py
class sigmoid():
def __init__(self):
self.output = None
def forward(self, x):
self.output = 1/(1+np.exp(-x))
return self.output
def backward(self, dx):
return self.output * (1 - self.output)
Es kann einfach implementiert werden, indem der zuvor geschriebene Ausdruck so geschrieben wird, wie er ist. Da die Sigmoid-Funktion keine zu lernenden Parameter hat, ist es nicht erforderlich, die Aktualisierungsfunktion oder den Differenzwert zu halten. Wenn Sie mit dieser Sigmoid-Funktion tatsächlich ein Modell eines mehrschichtigen neuronalen Netzes erstellen, ist dies wie folgt.
multi_layer_perceptron.py
from nn import nn
from sigmoid import sigmoid
fc1 = nn(10, 5, 0.1)
sig1 = sigmoid()
fc2 = nn(5, 2, 0.1)
sig2 = sigmoid()
x = np.random.randn(10, 1)
sig2.forward(fc2.forward(sig1.forward(fc1.forward(x))))
grad = np.random.randn(1,2)
fc1.backward(sig1.backward(fc2.backward(sig2.backward(grad))))
fc1.update()
fc2.update()
Abschließend werde ich die Fehlerfunktion erläutern. Alles, was Sie für die Vorwärtsausbreitung, Rückwärtsausbreitung und das Netzwerktraining tun müssen, ist, die Gewichte so zu aktualisieren, dass sich die Ausgabe des neuronalen Netzes dem Zielwert nähert. Bisher wurde die Eingabe zur Ausgabe des neuronalen Netzwerks durch eine einzelne Funktion namens $ f (x) $ dargestellt, die in Bezug auf $ w $ differenziert wurde. Selbst wenn das Gewicht mit diesem Wert aktualisiert wird, wird es nur auf einen zufälligen Wert aktualisiert und ist nicht nützlich. Daher wird am Ende des neuronalen Netzwerks eine Funktion, die als Fehlerfunktion bezeichnet wird und die Differenz zum Zielwert ausdrückt, gebissen, und die Eingabe in die Fehlerfunktion wird als eine Funktion betrachtet, die als $ f (x) $ bezeichnet wird. Durch Aktualisieren von $ f (x) $ einschließlich dieser Fehlerfunktion mit einem differenzierten Wert kann das Gewicht aktualisiert werden, so dass sich der Ausgabewert dem Zielwert nähert. Beim maschinellen Lernen wird das Modell mit einer Fehlerfunktion trainiert, die dem Problem entspricht. Dieses Mal verwenden wir den mittleren quadratischen Fehler, um die Implementierung und das Verständnis zu vereinfachen. Der mittlere quadratische Fehler ist eine der für die Regression verwendeten Fehlerfunktionen und wird wie folgt definiert.
f(x)=\frac{1}{n}\sum_{i=1}^n(y_i-x_i)^2\\
\frac{\partial{f(x)}}{\partial{x_i}} = \frac{2}{n}(y_i-x_i)
Dabei ist $ x_i $ der i-te Ausgang des Netzwerks und $ y_i $ der Zielwert, der dem i-ten Ausgang entspricht. Das neuronale Netzwerk zielt darauf ab, den Wert der eingestellten Fehlerfunktion zu minimieren. Wenn Sie also den mittleren quadratischen Fehler verwenden, werden die Gewichte aktualisiert, sodass die Differenz zwischen den Werten von $ x_i $ und $ y_i $ Null ist, dh genau der gleiche Wert. Das Folgende ist ein Implementierungsbeispiel.
MSE.py
class MSE():
def forward(self, x, y):
return np.square(y.reshape(-1)-x.reshape(-1)).mean()
def backward(self, x, y):
return 2*(y.reshape(-1) - x.reshape(-1)).mean()
Danach ist es möglich, das Regressionsproblem zu lösen, solange die Daten bereit sind. Ein Implementierungsbeispiel ist unten gezeigt.
train.py
from nn import nn
from sigmoid import sigmoid
from MSE import MSE
fc1 = nn(10, 5, 0.1)
sig1 = sigmoid()
fc2 = nn(5, 2, 0.1)
sig2 = sigmoid()
mse = MSE()
x = np.random.randn(10) #Generierung von Trainingsdaten
t = np.random.randn(2) #Generierung von Lehrerdaten
for i in range(100):
out = sig2.forward(fc2.forward(sig1.forward(fc1.forward(x))))
loss = mse.forward(out, t)
print(loss)
grad = mse.backward(out, t)
fc1.backward(sig1.backward(fc2.backward(sig2.backward(grad))))
fc1.update()
fc2.update()
Wenn Sie den obigen Code ausführen, werden Sie feststellen, dass der Ausgangsverlustwert kleiner wird. Dieses Mal werden die Daten mit Zufallszahlen generiert. Wenn Sie jedoch die tatsächlichen Daten lesen, werden diese für diese Daten geeignet. Es gibt verschiedene Fehlerfunktionen. Bitte überprüfen Sie sie und implementieren Sie sie. Auf diese Weise können Sie die Eigenschaften jeder Fehlerfunktion verstehen und Ihre eigene Fehlerfunktion entwerfen, die zu dem Problem passt, das Sie lösen möchten.
Ich habe versucht, das Verständnis und die Implementierung des Lernens neuronaler Netze auf der Ebene der Mathematik der High School zu erklären. Bitte versuchen Sie es selbst, um sicherzustellen, dass Sie die Implementierung verstehen, wenn das neuronale Netz und der Ort, an dem die tatsächlichen Daten vorbereitet und gelernt werden sollen, verzerrt sind (es tut mir leid, dass die Implementierung des Lernteils schwierig war. ). Ich kann erklären, warum es durch Aktualisieren mit einem Differenzwert, Verschwinden des Gradienten, Gradientenexplosion, Parameterinitialisierung, Wichtigkeit von Hyperparametern, anderen Aktivierungsfunktionen als Sigmoidfunktion, CNN, RNN usw. optimiert werden kann. Es gibt viele, aber dieses Mal möchte ich an einem solchen Ort enden. Bitte seien Sie vorsichtig, da es sich um einen Artikel handelt, der mit Schwung geschrieben wurde. Ich denke, es gibt Tippfehler, Implementierungsfehler und so weiter. Danach wird empfohlen, dass Sie tatsächlich eine richtige Theorie mit Büchern studieren.
Recommended Posts