Technologische Neuigkeiten, Bewertungen und Tipps!

Aufbau neuronaler Netze mit Python-Code und Mathematik im Detail – II | von Towards AI Editorial Team

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.

Abbildung 19: Das OP-Tor.
Abbildung 20: Eingabefunktionen.

Beachten Sie, dass wir hier zwei Eingabefunktionen und eine Ausgabefunktion haben. In diesem neuronalen Netzwerk werden wir eine verborgene Schicht mit drei Knoten verwenden.

Abbildung 21: Neuronales Netzwerk.
Abbildung 22: Eingaben im Diagramm. Beachten Sie, dass dieselben Farbpunkte dieselbe Ausgabe haben.

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:

Abbildung 23: NumPy 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.

Abbildung 24: Zuweisen von Eingabewerten zum Trainieren unseres neuronalen Netzes.

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.

Abbildung 25: Definieren unserer Zielausgabe und Umformen unserer Zielausgabe in einen Vektor

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.

Abbildung 26: Definieren der Gewichte für unser neuronales Netz zusammen mit unserer Lernrate (LR)

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.

Abbildung 27: Anwendung unserer Sigmoidfunktion.

F. Ableitung der Sigmoidfunktion:

In einem Gradientenabstiegsalgorithmus benötigen wir die Ableitung der Sigmoidfunktion.

Abbildung 28: Anwenden einer Ableitung auf unsere Sigmoidfunktion.

G. Die Hauptlogik zur Vorhersage der Ausgabe und Aktualisierung der Gewichtswerte:

Wir werden den folgenden Code Schritt für Schritt verstehen.

Abbildung 29: Phase 1 des Trainings unseres neuronalen Netzwerks.
Abbildung 30: Phase 2 des Trainings unseres neuronalen Netzwerks.

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:

Abbildung 31: Finden der Eingabe für die verborgene Schicht unseres neuronalen Netzwerks.

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.

Abbildung 32: Darstellung der Matrixwerte.

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:

Abbildung 33: Ausgabe unserer verborgenen Ebene.

D. Als nächstes multiplizieren wir die Ausgabe der verborgenen Schicht mit dem Gewicht der Ausgabeschicht:

Abbildung 34: Formel, die die Ausgabe unserer verborgenen Schicht mit dem Gewicht der Ausgabeschicht darstellt.

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,

Abbildung 35: Darstellung der verborgenen Ebene und unserer Ausgabeebene.

e. Anschließend berechnen wir die Ausgabe der Ausgabeebene durch Anwendung einer Sigmoidfunktion. Es kann auch wie folgt in Matrixform dargestellt werden.

Abbildung 36: Ausgabe unserer Ebene nach einer Sigmoidfunktion.

F. Nachdem wir nun unsere vorhergesagte Ausgabe haben, ermitteln wir das Mittelquadrat zwischen der Zielausgabe und der vorhergesagten Ausgabe.

Abbildung 37: Ermitteln des Mittelwerts zwischen unserem Zieloutput und unserem vorhergesagten Output.

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.

Abbildung 38: Aktualisierung der Gewichtswerte für unsere Ausgabeschicht.

ga Matrixdarstellung der ersten Ableitung. Matrixgröße (4*1).

derror_douto = Output_op -target_output

Abbildung 39: Darstellung der Matrix der ersten Ableitung.

GB Matrixdarstellung der zweiten Ableitung. Matrixgröße (4*1).

dout_dino = sigmoid_der(input_op)

Abbildung 40: Darstellung der Matrix der zweiten Ableitung.

gc Matrixdarstellung der dritten Ableitung. Matrixgröße (4*3).

dino_dwo = Output_hidden

Abbildung 41: Darstellung der dritten Ableitungsmatrix.

gd Matrixdarstellung der Transponierten von dino_dwo. Matrixgröße (3*4).

Abbildung 42: Matrixdarstellung unserer Variable dino_dwo, Einzelheiten finden Sie in der Implementierung.

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.

Abbildung 43: Endgültige Matrix des Ausgabegewichts.

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

Abbildung 44: Matrixdarstellung der ersten Ableitung.

hb Matrixdarstellung für die zweite Ableitung.

dino_douth =weight_output

Abbildung 45: Matrixdarstellung der zweiten Ableitung.

hc Matrixdarstellung für die dritte Ableitung.

derror_douth = np.dot(derror_dino, dino_douth.T)

Abbildung 46: Matrixdarstellung der dritten Ableitung.

hd Matrixdarstellung für die vierte Ableitung.

douth_dinh = sigmoid_der(input_hidden)

Abbildung 47: Matrixdarstellung der vierten Ableitung.

Er Matrixdarstellung für die fünfte Ableitung.

dinh_dwh = input_features

Abbildung 48: Matrixdarstellung der fünften Ableitung.

hf Matrixdarstellung für die sechste Ableitung.

derror_dwh = np.dot(dinh_dwh.T, douth_dinh * derror_douth)

Abbildung 49: Matrixdarstellung der sechsten Ableitung.

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.

  1. Das Originalgewicht: Wir haben es bereits.
  2. Die Lernrate (LR): Wir haben ihr den Wert 0,05 zugewiesen.
  3. Die Ableitung: Im vorherigen Schritt gefunden.

Gradientenabstiegsalgorithmus:

Abbildung 50: Formel für einen 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.

Abbildung 51: Anzeige der endgültigen Gewichtswerte der verborgenen Ebene.
Abbildung 52: Anzeige der endgültigen Gewichtswerte der Ausgabeebene.

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.

Abbildung 53: Drucken unserer Ergebnisse für Zielausgabe = 1.

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

Abbildung 54: Drucken unserer Ergebnisse für 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.

Abbildung 55: Anzeige des endgültigen Fehlerwerts nach 200.000 Iterationen.

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.

Abbildung 56: Diagramm, das zeigt, dass Daten linear trennbar sind und die Ausgabe mit 1 Wert oder 0 Werten klassifizieren können.

Starten Sie es auf Google Colab: