Neuronales Netzwerk von Grund auf in Python | von Sayali Dalvi | Mai 2023

0
29


Ein neuronales Netzwerk besteht aus mehreren Schichten miteinander verbundener Neuronen. Durch das Stapeln mehrerer Schichten von Neuronen kann ein neuronales Netzwerk lernen, komplexe Funktionen zu modellieren, die Eingabedatenmerkmalen Ausgabevorhersagen zuordnen. Versuchen wir, die Ausgabe eines XOR-Gatters vorherzusagen.

XOR-Gate-Eingang

Betrachten Sie das folgende neuronale Netzwerk. Es enthält eine Eingabeebene, eine verborgene Ebene und eine Ausgabeebene. Die Eingabeschicht enthält 3 Neuronen, die verborgene Schicht enthält 4 und die letzte Ausgabeschicht enthält ein einzelnes Neuron.

2-schichtiges neuronales Netzwerk

Jedes Neuron ist eine mathematische Funktion, die Eingaben akzeptiert und eine Ausgabe erzeugt, die an andere Neuronen in der nächsten Schicht weitergeleitet wird. Die Ausgabe erfolgt auf der Grundlage bestimmter Parameter, die als Gewichtungen und Verzerrungen bezeichnet werden. Die Eingabe für jedes Neuron ist die gewichtete Summe der Datenpunkte und der Bias 𝑊𝑥+𝑏. Definieren wir eine Klasse mit diesen Grundkomponenten.

Neuron

Gewichtungen definieren die Wichtigkeit oder den Beitrag dieses bestimmten Merkmals zur Ausgabe. Sie können als Parameter unseres Datenmodells betrachtet werden. Wir haben zwei Sätze von Gewichten, einen zwischen der Eingabeebene und der verborgenen Ebene und einen weiteren zwischen der verborgenen Ebene und der Ausgabeebene.

class NeuralNetwork:
def __init__(self, x, y):
self.enter = x
self.weights1 = np.random.rand(self.enter.form[1],4)
self.weights2 = np.random.rand(4,1)
self.y = y
self.output = np.zeros(y.form)

Eine Aktivierungsfunktion ist eine mathematische Funktion, die auf die Ausgabe eines Neurons in einem neuronalen Netzwerk angewendet wird. Es gibt verschiedene Aktivierungsfunktionen wie Sigmoid, hyperbolischer Tangens und ReLu und die Wahl hängt von der Artwork des Issues ab. Wir werden das verwenden Sigmoid Funktion in unserem Beispiel. Es stellt sich heraus, dass es sich um eine gute Annäherung an die tatsächliche Aktivierungsfunktion in menschlichen Neuronen handelt.

Die Sigmoidfunktion
def sigmoid(x):
return 1.0/(1+ np.exp(-x))

Wenn die Eingabe 𝑥 ist, lautet die Ausgabe ŷ eines einfachen zweischichtigen neuronalen Netzwerks wie oben für jede unserer beiden Schichten:

2-schichtige Feed-Ahead-Gleichungen

Hier ist 𝑦1 die Ausgabe der ersten Schicht, die als Eingabe an die zweite Schicht übergeben wird. W1 und W2 sind die Gewichte und b1 und b2 sind die Baises. Kombination der beiden obigen Gleichungen:

Aber diese Variablen sind nicht nur Zahlen. Hier beschäftigen wir uns mit Vektoren und Matrizen. Um diese Gleichungen zu kodieren, müssen wir sie verstehen Maße.

  • Eingabeebene: (3,1) – XOR-Eingaben
  • Gewichte 1: (3,4) – Sie liegen zwischen der Eingabeschicht mit 3 Neuronen und der verborgenen Schicht mit 4 Neuronen
  • Gewichte 2: (4,1) – Sie verbinden sich von der verborgenen Schicht mit 4 Neuronen zur Ausgabeschicht mit einem einzelnen Neuron
  • Grund 1: (4,1) – Diese Vorspannung wird auf die verborgene Schicht mit 4 Neuronen angewendet
  • Bias 2: (1,1) – Wird mit einem Neuron auf die Ausgabeschicht angewendet
def feedforward(self):
self.layer1 = sigmoid(np.dot(self.enter, self.weights1))
self.output = sigmoid(np.dot(self.layer1, self.weights2))

Hübsch! Wir haben das Grundausbildungsnetzwerk aufgebaut. Intestine gemacht! Jetzt ist es an der Zeit, die falschen Erkenntnisse zu erkennen und zu korrigieren sowie Fehler zu beheben.

Die Verlustfunktion, auch Kostenfunktion genannt, ist eine mathematische Funktion, die die Differenz zwischen der vorhergesagten Ausgabe eines neuronalen Netzwerks und der tatsächlichen Ausgabe misst, die auch als Grundwahrheit bezeichnet wird. Es bewertet die Leistung des Modells und zeigt die Anpassungen an, die bei Gewichtungen und Verzerrungen vorgenommen werden müssen, um die Genauigkeit zu erhöhen. Ziel ist es, den optimalen Satz an Gewichten und Gewichten zu finden, um die Verlustfunktion zu minimieren. Die Wahl der Verlustfunktion hängt von der Kategorie des Issues ab. Hier verwenden wir eine einfache Quadratsumme Fehler als unsere Verlustfunktion: Der Quadratsummenfehler ist einfach die Summe der Differenz zwischen jeder vorhergesagten Beobachtung 𝑦̂ und der tatsächlichen Beobachtung 𝑦. Die Differenz wird quadriert, sodass wir die messen Absolutwert des Unterschieds:

Die Verlustfunktion

Hier kommt der wichtige Schritt! Nachdem wir nun unser Maß für die Güte von Vorhersagen haben, nämlich die Verlustfunktion, werden wir die Gewichte und Verzerrungen aktualisieren, um den Fehler zu minimieren. Dazu nehmen wir die Ableitung (Gradientenabstieg) der Verlustfunktion in Bezug auf Gewichte und Baises. Der Gradientenabstieg gibt die Steigung der Funktion an und sagt uns, wie weit wir von den Minima entfernt sind. Daher werden wir unsere Gewichtungen (und Verzerrungen) wie folgt aktualisieren:

Aktualisierung der Gewichtungen und Verzerrungen

wobei ∂ die partielle Ableitung ist und L=Verlust(𝑦,𝑦̂ )

Aktualisieren der Gewichte: Seit wir … Haben zwei Gewichtssätze, die wir brauchen zwei Ableitungen von Verlust(𝑦,𝑦̂):

Die Verlustfunktion hängt von y und 𝑦̂ ab und nicht von den Gewichten. Aber y und 𝑦̂ sind selbst Funktionen von Gewichten. Wir können die Gleichungen additionally wie folgt umschreiben:

Lassen Sie uns zuerst nach dem gemeinsamen Begriff suchen:

Wenn wir dies und die Gleichungen für 𝑧 und 𝑦̂ einsetzen, erhalten wir:

Um die Ableitungen zu bilden, wenden wir die Kettenregel an. Die Kettenregel lautet: 𝑑/𝑑𝑥[𝑓(𝑔(𝑥))] = 𝑓′(𝑔(𝑥)) ∗ 𝑔′(𝑥)

Additionally mit der Änderung der Variablen 𝑊2→𝑞:

Die erste Gleichung lautet additionally:

Ableitung der Verlustfunktion nach W2

In ähnlicher Weise ändert sich die Variable 𝑊1→𝑞:

Und die zweite Gleichung:

Ableitung der Verlustfunktion nach W1

Aktualisierung der Vorurteile: Ebenso haben wir zwei Tendenzen, sich anzupassen

Aktualisierung für Vorurteile

Wir können die Gleichungen wie folgt umschreiben:

Auflösen nach einzelnen Begriffen:

Wie zuvor berechnet, haben wir:

Die erste Gleichung lautet additionally:

Ableitung des Verlusts b2

Und die zweite Gleichung lautet:

Ableitung des Verlusts b1

Rauschen! Das warfare eine Menge komplizierter Mathematik! Herzlichen Glückwunsch! Wir sind mit dem schwierigen Teil fertig. Fügen wir nun die Backpropagation-Funktion zu unserem Python-Code hinzu.

#operate to calculate the deivative of sigmoid activation operate
def sigmoid_derivative(x):
return sigmoid(x) * (1.0 - sigmoid(x))

def backpropagation(self):
# software of the chain rule to search out by-product of the loss operate with respect to weights2 and weights1
sigmoid_derivative_1 = self.sigmoid_derivative(self.z1) #sigma'(W1 x + b1)
sigmoid_derivative_2 = self.sigmoid_derivative(self.z2) #sigma'(W2 z + b2)
d_weights2 = np.dot(self.layer1.T,
(2*(self.y - self.output) * sigmoid_derivative_2))
d_weights1 = np.dot(self.enter.T,
np.dot(2*(self.y - self.output) * sigmoid_derivative_2, self.weights2.T) *
sigmoid_derivative_1)

# replace the weights
self.weights1 += d_weights1
self.weights2 += d_weights2

d_bias2 = np.sum((2*(self.y - self.output) * sigmoid_derivative_2), axis=0)
d_bias1 = np.sum((np.dot(2*(self.y - self.output) * sigmoid_derivative_2, self.weights2.T)), axis=0)

# replace the biases
self.bias1 += d_bias1
self.bias2 += d_bias2

Fassen wir alles zu einer Klasse zusammen und trainieren wir unser neuronales Netzwerk

import numpy as np
import matplotlib.pyplot as plt
class NeuralNetwork:
def __init__(self, x, y):
self.enter = x
self.weights1 = np.random.rand(self.enter.form[1],4)
self.bias1 = np.random.rand(4)
self.weights2 = np.random.rand(4,1)
self.bias2 = np.random.rand(1)
self.y = y
self.output = np.zeros(self.y.form)

def feedforward(self):
self.z1 = np.dot(self.enter, self.weights1) + self.bias1
self.layer1 = self.sigmoid(self.z1)
self.z2 = np.dot(self.layer1, self.weights2) + self.bias2
self.output = self.sigmoid(self.z2)
return self.calculate_loss()

def reload(self, x):
self.enter = x

def predict(self):
return self.output

def calculate_loss(self):
return (self.y - self.output) ** 2

def sigmoid(self,x):
return 1.0/(1+ np.exp(-x))

#operate to calculate the deivative of sigmoid activation operate
def sigmoid_derivative(self,x):
return self.sigmoid(x) * (1.0 - self.sigmoid(x))

def backprop(self):
# software of the chain rule to search out by-product of the loss operate with respect to weights2 and weights1
sigmoid_derivative_1 = self.sigmoid_derivative(self.z1) #sigma'(W1 x + b1)
sigmoid_derivative_2 = self.sigmoid_derivative(self.z2) #sigma'(W2 z + b2)
d_weights2 = np.dot(self.layer1.T,
(2*(self.y - self.output) * sigmoid_derivative_2))
d_weights1 = np.dot(self.enter.T,
np.dot(2*(self.y - self.output) * sigmoid_derivative_2, self.weights2.T) *
sigmoid_derivative_1)

# replace the weights with the by-product (slope) of the loss operate
self.weights1 += d_weights1
self.weights2 += d_weights2

d_bias2 = np.sum((2*(self.y - self.output) * sigmoid_derivative_2), axis=0)
d_bias1 = np.sum((np.dot(2*(self.y - self.output) * sigmoid_derivative_2, self.weights2.T) *
sigmoid_derivative_1), axis=0)

# replace the weights with the by-product (slope) of the loss operate
self.bias1 += d_bias1
self.bias2 += d_bias2

Wir trainieren mit dem XOR-Datensatz. Unser X und Y werden additionally wie folgt aussehen:

X = np.array([[0,0,1],
[0,1,1],
[1,0,1],
[1,1,1]])
y = np.array([[0],[1],[1],[0]])

Lass uns trainieren !!!

nn = NeuralNetwork(X,y)
for i in vary(1500):
nn.feedforward()
nn.backprop()
print(nn.output)

Schauen wir uns die endgültige Vorhersage (Ausgabe) des neuronalen Netzwerks nach 1500 Iterationen an. Vorhersagen nach 1500 Trainingsiterationen:

Herzlichen Glückwunsch, Sie verfügen jetzt über ein voll funktionsfähiges zweischichtiges neuronales Netzwerk für eine binäre Klassifizierungsaufgabe.

Sie können den gesamten laufenden Code finden Hier.

Dank an Professor Dino dafür, dass Sie mir dieses Verständnis gegeben und mich unterstützt haben!



Source link

HINTERLASSEN SIE EINE ANTWORT

Please enter your comment!
Please enter your name here