Hinweis: Der folgende Artikel hilft Ihnen dabei: Aufbau neuronaler Netze mit Python-Code und Mathematik im Detail – II | von Towards AI Editorial Team
📚 Mehrschichtiges neuronales Netzwerk: Ein neuronales Netzwerk mit einer verborgenen Schicht 📚 Weitere Definitionen finden Sie in unserem Artikel zur Terminologie im maschinellen Lernen.
Im Folgenden implementieren wir das „OR“-Gatter ohne den Bias-Wert. Zusammenfassend lässt sich sagen, dass uns das Hinzufügen verborgener Schichten in einem neuronalen Netzwerk dabei hilft, eine höhere Genauigkeit unserer Modelle zu erreichen.
Beachten Sie, dass wir hier zwei Eingabefunktionen und eine Ausgabefunktion haben. In diesem neuronalen Netzwerk werden wir eine verborgene Schicht mit drei Knoten verwenden.
Im Folgenden werden wir unser neuronales Netz mit versteckten Schichten Schritt für Schritt in Python implementieren. Lassen Sie uns Folgendes programmieren:
A. Erforderliche Bibliotheken importieren:
B. Definieren Sie Eingabefunktionen:
Als nächstes nehmen wir Eingabewerte, für die wir unser neuronales Netzwerk trainieren möchten. Wir können sehen, dass wir zwei Eingabefunktionen übernommen haben. Bei konkreten Datensätzen ist der Wert der Eingabemerkmale meist hoch.
C. Definieren Sie Zielausgabewerte:
Für die Eingabefunktionen möchten wir eine spezifische Ausgabe für bestimmte Eingabefunktionen haben. Es wird als Zielausgabe bezeichnet. Wir werden das Modell trainieren, das uns die Zielausgabe für unsere Eingabefunktionen liefert.
D. Zufällige Gewichte zuweisen:
Als Nächstes weisen wir den Eingabemerkmalen zufällige Gewichte zu. Note dass unser Modell diese Gewichtswerte so modifizieren wird, dass sie optimal sind. An dieser Stelle nehmen wir diese Werte zufällig. Hier haben wir zwei Ebenen, daher müssen wir ihnen separat Gewichte zuweisen.
Die andere Variable ist die Lernrate. Wir werden die Lernrate (LR) in einem Gradientenabstiegsalgorithmus verwenden, um die Gewichtswerte zu aktualisieren. Generell halten wir LR so gering wie möglich, um eine minimale Fehlerquote zu erreichen.
e. Sigmoidfunktion:
Sobald wir unsere Gewichtswerte und Eingabemerkmale haben, senden wir sie an die Hauptfunktion, die die Ausgabe vorhersagt. Beachten Sie, dass unsere Eingabemerkmale und Gewichtswerte alles sein können, aber hier möchten wir Daten klassifizieren, daher benötigen wir die Ausgabe zwischen 0 und 1. Für eine solche Ausgabe verwenden wir eine Sigmoidfunktion.
F. Ableitung der Sigmoidfunktion:
In einem Gradientenabstiegsalgorithmus benötigen wir die Ableitung der Sigmoidfunktion.
G. Die Hauptlogik zur Vorhersage der Ausgabe und Aktualisierung der Gewichtswerte:
Wir werden den folgenden Code Schritt für Schritt verstehen.
Wie funktioniert es?
A. Zunächst führen wir den obigen Code 2.00.000 Mal aus. Bedenken Sie, dass wir wahrscheinlich eine höhere Fehlerrate haben, wenn wir diesen Code nur ein paar Mal ausführen. Daher aktualisieren wir die Gewichtswerte 10.000 Mal, um den optimalen Wert zu erreichen.
B. Als nächstes finden wir die Eingabe für die verborgene Ebene. Definiert durch die folgende Formel:
Wir können es auch als Matrizen darstellen, um es besser zu verstehen.
Die erste Matrix besteht hier aus Eingabemerkmalen mit der Größe (4*2) und die zweite Matrix aus Gewichtswerten für eine verborgene Ebene mit der Größe (2*3). Die resultierende Matrix hat also die Größe (4*3).
Die Intuition hinter der endgültigen Matrixgröße:
Die Zeilengröße der endgültigen Matrix entspricht der Zeilengröße der ersten Matrix, und die Spaltengröße der endgültigen Matrix entspricht der Spaltengröße der zweiten Matrix bei der Multiplikation (Skalarprodukt).
In der folgenden Darstellung stellt jedes dieser Kästchen einen Wert dar.
C. Anschließend haben wir eine Eingabe für die verborgene Ebene und diese wird die Ausgabe durch Anwenden einer Sigmoidfunktion berechnen. Unten ist die Ausgabe der verborgenen Ebene:
D. Als nächstes multiplizieren wir die Ausgabe der verborgenen Schicht mit dem Gewicht der Ausgabeschicht:
Die erste Matrix zeigt die Ausgabe der verborgenen Ebene, die eine Größe von (4*3) hat. Die zweite Matrix stellt die Gewichtswerte der Ausgabeschicht dar,
e. Anschließend berechnen wir die Ausgabe der Ausgabeebene durch Anwendung einer Sigmoidfunktion. Es kann auch wie folgt in Matrixform dargestellt werden.
F. Nachdem wir nun unsere vorhergesagte Ausgabe haben, ermitteln wir das Mittelquadrat zwischen der Zielausgabe und der vorhergesagten Ausgabe.
G. Als nächstes beginnen wir mit der ersten Trainingsphase. In diesem Schritt aktualisieren wir die Gewichtswerte für die Ausgabeebene. Wir müssen herausfinden, wie stark sich die Ausgabegewichte auf den Fehlerwert auswirken. Um die Gewichte zu aktualisieren, verwenden wir einen Gradientenabstiegsalgorithmus. Beachten Sie, dass wir die Ableitungen, die wir während der Trainingsphase verwenden werden, bereits gefunden haben.
ga Matrixdarstellung der ersten Ableitung. Matrixgröße (4*1).
derror_douto = Output_op -target_output
GB Matrixdarstellung der zweiten Ableitung. Matrixgröße (4*1).
dout_dino = sigmoid_der(input_op)
gc Matrixdarstellung der dritten Ableitung. Matrixgröße (4*3).
dino_dwo = Output_hidden
gd Matrixdarstellung der Transponierten von dino_dwo. Matrixgröße (3*4).
ge Jetzt werden wir die endgültige Matrix des Ausgabegewichts finden. Eine detaillierte Erklärung dieses Schritts finden Sie hier unser vorheriges Tutorial. Die Matrixgröße beträgt (3*1), was mit der Ausgabegewichtsmatrix identisch ist.
Daher ist es uns gelungen, die Ableitungswerte zu finden. Als nächstes aktualisieren wir die Gewichtswerte mithilfe eines Gradientenabstiegsalgorithmus entsprechend.
Nichtsdestotrotz müssen wir auch die Ableitung für Phase-2 finden. Lassen Sie uns das zuerst herausfinden und dann am Ende die Gewichte für beide Ebenen aktualisieren.
H. Phase 2. Aktualisierung der Gewichte in der verborgenen Ebene.
Da wir bereits besprochen haben, wie wir die Ableitungswerte abgeleitet haben, sehen wir uns zum besseren Verständnis nur die Matrixdarstellung für jeden von ihnen an. Unser Ziel hier ist es, die Gewichtsmatrix für die verborgene Schicht zu finden, die eine Größe (2*3) hat.
Ha Matrixdarstellung für die erste Ableitung.
derror_dino = derror_douto * douto_dino
hb Matrixdarstellung für die zweite Ableitung.
dino_douth =weight_output
hc Matrixdarstellung für die dritte Ableitung.
derror_douth = np.dot(derror_dino, dino_douth.T)
hd Matrixdarstellung für die vierte Ableitung.
douth_dinh = sigmoid_der(input_hidden)
Er Matrixdarstellung für die fünfte Ableitung.
dinh_dwh = input_features
hf Matrixdarstellung für die sechste Ableitung.
derror_dwh = np.dot(dinh_dwh.T, douth_dinh * derror_douth)
Beachten Sie, dass unser Ziel darin bestand, eine versteckte Gewichtsmatrix mit der Größe (2*3) zu finden. Darüber hinaus ist es uns gelungen, es zu finden.
hg Aktualisierung der Gewichtswerte:
Wir werden den Gradientenabstiegsalgorithmus verwenden, um die Werte zu aktualisieren. Es werden drei Parameter benötigt.
- Das Originalgewicht: Wir haben es bereits.
- Die Lernrate (LR): Wir haben ihr den Wert 0,05 zugewiesen.
- Die Ableitung: Im vorherigen Schritt gefunden.
Gradientenabstiegsalgorithmus:
Da wir alle unsere Parameterwerte haben, ist dies ein unkomplizierter Vorgang. Zuerst aktualisieren wir die Gewichtungswerte für die Ausgabeebene und dann aktualisieren wir die Gewichtungswerte für die verborgene Ebene.
ich. Endgewichtswerte:
Unten zeigen wir die aktualisierten Gewichtswerte für beide Schichten – unsere Vorhersage basiert auf diesen Werten.
J. Vorhersagen treffen:
ja Vorhersage für (1,1).
Zielausgabe = 1
Erläuterung:
Zunächst nehmen wir die Eingabewerte, für die wir die Ausgabe vorhersagen möchten. Die Variable „result1“ speichert den Wert des Skalarprodukts aus Eingabevariablen und Gewicht der verborgenen Schicht. Die Ausgabe erhalten wir durch Anwendung einer Sigmoidfunktion, das Ergebnis wird in der Variablen result2 gespeichert. Dies ist das Eingabemerkmal für die Ausgabeschicht. Wir berechnen die Eingabe für den Ausgabe-Layer, indem wir Eingabe-Features mit dem Gewicht des Ausgabe-Layers multiplizieren. Um den endgültigen Ausgabewert zu ermitteln, nehmen wir dessen Sigmoidwert.
Beachten Sie, dass die vorhergesagte Ausgabe sehr nahe bei 1 liegt. Wir haben es also geschafft, genaue Vorhersagen zu treffen.
jb Vorhersage für (0,0).
Zielausgabe = 0
Note dass die vorhergesagte Ausgabe sehr nahe bei 0 liegt, was die Erfolgsquote unseres Modells anzeigt.
k. Endgültiger Fehlerwert:
Nach 200.000 Iterationen haben wir unseren endgültigen Fehlerwert – je niedriger der Fehler, desto höher die Genauigkeit des Modells.
Wie oben gezeigt, können wir sehen, dass der Fehlerwert 0,0000000189 beträgt. Dieser Wert ist der endgültige Fehlerwert in der Vorhersage nach 200.000 Iterationen.
Alles zusammen:
# Erforderliche Bibliotheken importieren :import numpy as np# Eingabefunktionen definieren :input_features = np.array([[0,0],[0,1],[1,0],[1,1]])print (input_features.shape)print (input_features)# Zielausgabe definieren :target_output = np.array([[0,1,1,1]])# Umformen unserer Zielausgabe in einen Vektor :target_output = target_output.reshape(4,1)print(target_output.shape)print (target_output)# Gewichte definieren:# 6 für versteckte Ebene# 3 für Ausgabeebene# 9 totalweight_hidden = np. Array([[0.1,0.2,0.3],[0.4,0.5,0.6]])weight_output = np.array([[0.7],[0.8],[0.9]])# Lernrate :lr = 0,05# Sigmoidfunktion :def sigmoid(x):return 1/(1+np.exp(-x))# Ableitung der Sigmoidfunktion :def sigmoid_der(x):return sigmoid(x) *(1-sigmoid(x))für Epoche im Bereich(200000):# Eingabe für verborgene Ebene:input_hidden = np.dot(input_features,weight_hidden)
# Ausgabe von versteckter Ebene :output_hidden = sigmoid(input_hidden)
# Eingabe für Ausgabeebene:input_op = np.dot(output_hidden,weight_output)
# Ausgabe von der Ausgabeebene :output_op = sigmoid(input_op)#==================================== =====================# Phase1
# Berechnung des mittleren quadratischen Fehlers :error_out = ((1 / 2) * (np.power((output_op — target_output), 2)))print(error_out.sum())
# Ableitungen für Phase 1 :derror_douto = Output_op — target_outputdouto_dino = sigmoid_der(input_op) dino_dwo = Output_hiddenderror_dwo = np.dot(dino_dwo.T, derror_douto * douto_dino)#================= ========================================# Phase 2 # derror_w1 = derror_douth * douth_dinh * dinh_dw1# derror_douth = derror_dino * dino_outh
# Ableitungen für Phase 2 :derror_dino = derror_douto * douto_dinodino_douth =weight_outputderror_douth = np.dot(derror_dino , dino_douth.T)douth_dinh = sigmoid_der(input_hidden) dinh_dwh = input_featuresderror_wh = np.dot(dinh_dwh.T, douth_dinh * derror_douth)# Weightsweight_hidden aktualisieren – = lr * derror_whweight_output -= lr * derror_dwo
# Endgültige Gewichtswerte der verborgenen Ebene :print (weight_hidden)# Endgültige Gewichtswerte der Ausgabeebene :print (weight_output)# Vorhersagen :#Eingaben entgegennehmen :single_point = np.array([1,1])#1. Schritt :result1 = np.dot(single_point,weight_hidden) #2. Schritt :result2 = sigmoid(result1)#3. Schritt :result3 = np.dot(result2,weight_output)#4. Schritt :result4 = sigmoid(result3)print (Ergebnis4)#============================================ ===#Eingaben entgegennehmen :single_point = np.array([0,0])#1. Schritt :result1 = np.dot(single_point,weight_hidden) #2. Schritt :result2 = sigmoid(result1)#3. Schritt :result3 = np.dot(result2,weight_output)#4. Schritt :result4 = sigmoid(result3)print (Ergebnis4)#============================================ =======#Eingaben entgegennehmen :single_point = np.array([1,0])#1. Schritt :result1 = np.dot(single_point,weight_hidden) #2. Schritt :result2 = sigmoid(result1)#3. Schritt :result3 = np.dot(result2,weight_output)#4. Schritt :result4 = sigmoid(result3)print (Ergebnis4)
Beachten Sie unten, dass die Daten, die wir in diesem Beispiel verwendet haben, linear trennbar waren, was bedeutet, dass wir anhand einer einzelnen Zeile Ausgaben mit 1-Werten und Ausgaben mit 0-Werten klassifizieren können.
Starten Sie es auf Google Colab: