Dieses Mal werde ich versuchen, ein sich tatsächlich bewegendes neuronales Netzwerk mit Scratch zu implementieren, um die Vorwärtsausbreitung und die Fehlerrückausbreitung zu verstehen. Der Datensatz verwendet MNIST.
Bei einem neuronalen Netzwerk mit 28 x 28 = 784 Eingangsschichten, 10 Zwischenschichten und 2 Ausgangsschichten ist die Aktivierungsfunktion / Fehlerfunktion sigmoid und die Optimierungsmethode ist die Gradientenabstiegsmethode.
Für den Datensatz werden "1" und "7" aus MNIST extrahiert und eine binäre Klassifizierung durchgeführt.
Erstens, wenn Sie $ a ^ 0_0 $ berechnen, gibt es 784 Eingaben von $ x ^ 0_0 $ bis $ x ^ 0_ {783} $, jede mit den Gewichten $ w ^ 0_ {00} $ bis $ w ^ 0_ {783,0} $ ist also aufgehängt
Als Matrix ausgedrückt können alle Berechnungen von $ a ^ 0_0 $ bis $ a ^ 0_9 $ leicht dargestellt werden.
Da $ x ^ 1_0 $ bis $ x ^ 1_9 $ das Ergebnis der Übergabe von $ a ^ 0_0 $ an $ a ^ 0_9 $ durch die Aktivierungsfunktion Sigmoid ist,
Wenn Sie als nächstes $ a ^ 1_0 $ berechnen, gibt es 10 Eingaben von $ x ^ 1_0 $ bis $ x ^ 1_9 $ mit jeweils Gewichten von $ w ^ 1_ {00} $ bis $ w ^ 1_ {90. } $ Ist aufgehängt, also
Wenn $ a ^ 1_0 $ und $ a ^ 1_1 $ durch eine Matrix dargestellt werden,
Schließlich sind $ y ^ 0 $ und $ y ^ 1 $
Auf diese Weise kann die Vorwärtsausbreitung leicht durch das innere Produkt der Matrix oder Addition durchgeführt werden.
Aktualisieren Sie zunächst die Gewichte und Verzerrungen von der mittleren Ebene zur Ausgabeebene.
Der Aktualisierungsausdruck für das Gewicht w kann durch $ w = w- \ eta * \ frac {\ partielles E} {\ partielles w} $ dargestellt werden. Wobei $ \ eta $ die Lernrate ist und $ \ frac {\ partielles E} {\ partielles w} $ der Fehler E ist, der durch das Gewicht w differenziert wird.
Geben wir ein konkretes Beispiel für $ \ frac {\ partielles E} {\ partielles w} $ und drücken es mit einer allgemeinen Formel aus, um es zu implementieren. Zunächst die Gewichte von der mittleren Ebene zur Ausgabeebene.
Suchen Sie $ \ frac {\ partielles E ^ 0} {\ partielles w ^ 1_ {00}} $, um das Gewicht $ w ^ 1_ {00} $ zu aktualisieren. Aus dem Kettenverhältnis der Differenzierung Ausgedrückt als allgemeine Formel, k = 0 bis 9, j = 0 bis 1, Dadurch kann das Gewicht $ w ^ 1_ {kj} $ aktualisiert werden. Was die Verzerrung betrifft, ist $ b ^ 1 $ 1, also ersetzt $ x ^ 1_k $ in der obigen Formel nur 1. Dadurch kann der Bias $ b ^ 1_j $ aktualisiert werden.
Als nächstes werden die Gewichte und Verzerrungen von der Eingabeebene zur mittleren Ebene aktualisiert.
Um das Gewicht $ w ^ 0_ {00} $, $ \ frac {\ partielles E ^ 0} {\ partielles w ^ 0_ {00}} $ und $ \ frac {\ partielles E ^ 1} {\ zu aktualisieren Sie müssen teilweise w ^ 0_ {00}} $ finden. Aus dem Kettenverhältnis der Differenzierung Ausgedrückt als allgemeine Formel, k = 0 bis 783, j = 0 bis 9, Dadurch kann das Gewicht $ w ^ 0_ {kj} $ aktualisiert werden. Was die Verzerrung betrifft, ist $ b ^ 0 $ 1, also ersetzt $ x ^ 0_k $ in der obigen Formel nur 1. Dadurch kann der Bias $ b ^ 0_j $ aktualisiert werden.
Implementieren Sie basierend auf der zuvor erhaltenen allgemeinen Formel die Teile Vorwärtsausbreitung und Fehlerrückausbreitung.
#Sigmaid-Funktion
def sigmoid(a):
return 1 / (1 + np.exp(-a))
#Differenzierung der Sigmoidfunktion
def sigmoid_d(a):
return (1 - sigmoid(a)) * sigmoid(a)
#Fehler bei der Weitergabe
def back(l, j):
if l == max_layer - 1:
return (y[j] - t[j]) * sigmoid_d(A[l][j])
else:
output = 0
m = A[l+1].shape[0]
for i in range(m):
output += back(l+1, i) * W[l+1][i,j] * sigmoid_d(A[l][j])
return output
Die spezifische Bewegung von def back (l, j):
ist
Wenn "l = 1",
(y [j] -t [j]) * sigmoid_d (A [1] [j])
wird zurückgegeben.
Wenn "l = 0",
(y[0]-t[0])*sigmoid_d(A[1][0])*W[1][0,j]*sigmoid_d(A[0][j]) +(y[1]-t[1])*sigmoid_d(A[1][1])*W[1][1,j]*sigmoid_d(A[0][j])
Ist zurück gekommen.
#Gewicht W Einstellung
np.random.seed(seed=7)
w0 = np.random.normal(0.0, 1.0, (10, 784))
w1 = np.random.normal(0.0, 1.0, (2, 10))
W = [w0, w1]
#Bias b Einstellung
b0 = np.ones((10, 1))
b1 = np.ones((2, 1))
B = [b0, b1]
#Andere Einstellungen
max_layer = 2 #Festlegen der Anzahl der Ebenen
n = 0.5 #Einstellung der Lernrate
Stellen Sie das Gewicht W, die Vorspannung b und andere Einstellungen ein.
Jeder Term der Gewichtsmatrix w0 und w1 ist eine Zufallszahl, die einer Normalverteilung von 0 bis 1 folgt, damit das Lernen reibungslos beginnen kann. Übrigens, wenn Sie den Startwert = Anzahl von np.random.seed (seed = 7)
ändern, ändert sich die Startbedingung des Lernens (ob es reibungslos beginnt oder etwas träge ist). Jeder Term der Vorspannungsmatrizen b0 und b1 ist 1.
#Lernschleife
count = 0
acc = []
for x, t in zip(xs, ts):
#Vorwärtsausbreitung
x0 = x.flatten().reshape(784, 1)
a0 = W[0].dot(x0) + B[0]
x1 = sigmoid(a0)
a1 = W[1].dot(x1) + B[1]
y = sigmoid(a1)
#X für Parameteraktualisierung,Liste a
X = [x0, x1]
A = [a0, a1]
#Parameteraktualisierung
for l in range(len(X)):
for j in range(W[l].shape[0]):
for k in range(W[l].shape[1]):
W[l][j, k] = W[l][j, k] - n * back(l, j) * X[l][k]
B[l][j] = B[l][j] - n * back(l, j)
Es ist eine Lernschleife. Die Vorwärtsausbreitung kann leicht durch das innere Produkt der Matrix, Addition usw. durchgeführt werden. In der Parameteraktualisierung
Wenn "l = 0" im Bereich von "j = 0 bis 9" ist, ist k = 0 bis 783 "
W[0][j,k] = W[0][j,k] - n * back(0,j) * X[0][k]
B[0][j] = B[0][j] - n * back(0,j)
Wird Aktualisiert.
Wenn "l = 1" im Bereich von "j = 0 bis 1" ist, ist k = 0 bis 9 "
W[1][j,k] = W[1][j,k] - n * back(0,j) * X[0][k]
B[1][j] = B[1][j] - n * back(0,j)
Wird Aktualisiert.
Lesen Sie den MNIST-Datensatz mit Keras und extrahieren Sie nur "1" und "7".
import numpy as np
from keras.datasets import mnist
from keras.utils import np_utils
import matplotlib.pyplot as plt
#Numerische Anzeige
def show_mnist(x):
fig = plt.figure(figsize=(7, 7))
for i in range(100):
ax = fig.add_subplot(10, 10, i+1, xticks=[], yticks=[])
ax.imshow(x[i].reshape((28, 28)), cmap='gray')
plt.show()
#Datensatz lesen
(x_train, y_train), (x_test, y_test) = mnist.load_data()
show_mnist(x_train)
# 1,7 extrahiert
x_data, y_data = [], []
for i in range(len(x_train)):
if y_train[i] == 1 or y_train[i] == 7:
x_data.append(x_train[i])
if y_train[i] == 1:
y_data.append(0)
if y_train[i] == 7:
y_data.append(1)
show_mnist(x_data)
#Konvertieren Sie vom Listenformat in das Numpy-Format
x_data = np.array(x_data)
y_data = np.array(y_data)
# x_Normalisierung von Daten, y_One-Hot-Darstellung von Daten
x_data = x_data.astype('float32')/255
y_data = np_utils.to_categorical(y_data)
#Lernen Sie, erhalten Sie Testdaten
xs = x_data[0:200]
ts = y_data[0:200]
xt = x_data[2000:3000]
tt = y_data[2000:3000]
Die Daten von 0 bis 9 und die Daten, aus denen 1 und 7 extrahiert werden, werden von Anfang an angezeigt.
Bereiten Sie 200 Trainingsdaten xs und ts sowie 1.000 Testdaten xt und tt vor.
Es ist die gesamte Implementierung mit der Hinzufügung einer Genauigkeitsbestätigung durch Testdaten und einer Anzeige des Genauigkeitsübergangsdiagramms für jedes Lernen.
import numpy as np
from keras.datasets import mnist
from keras.utils import np_utils
#Datensatz lesen
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 1,Extrahieren Sie nur 7 Zahlen
x_data, y_data = [], []
for i in range(len(x_train)):
if y_train[i] == 1 or y_train[i] == 7:
x_data.append(x_train[i])
if y_train[i] == 1:
y_data.append(0)
if y_train[i] == 7:
y_data.append(1)
#Konvertieren Sie vom Listenformat in das Numpy-Format
x_data = np.array(x_data)
y_data = np.array(y_data)
# x_Normalisierung von Daten, y_Eine heiße Daten
x_data = x_data.astype('float32')/255
y_data = np_utils.to_categorical(y_data)
#Erfassung von Trainingsdaten und Testdaten
xs = x_data[0:200]
ts = y_data[0:200]
xt = x_data[2000:3000]
tt = y_data[2000:3000]
#Sigmaid-Funktion
def sigmoid(a):
return 1 / (1 + np.exp(-a))
#Differenzierung der Sigmoidfunktion
def sigmoid_d(a):
return (1 - sigmoid(a)) * sigmoid(a)
#Fehler bei der Weitergabe
def back(l, j):
if l == max_layer - 1:
return (y[j] - t[j]) * sigmoid_d(A[l][j])
else:
output = 0
m = A[l+1].shape[0]
for i in range(m):
output += back(l + 1, i) * W[l + 1][i, j] * sigmoid_d(A[l][j])
return output
#Gewicht W Einstellung
np.random.seed(seed=7)
w0 = np.random.normal(0.0, 1.0, (10, 784))
w1 = np.random.normal(0.0, 1.0, (2, 10))
W = [w0, w1]
#Bias b Einstellung
b0 = np.ones((10, 1))
b1 = np.ones((2, 1))
B = [b0, b1]
#Andere Einstellungen
max_layer = 2 #Festlegen der Anzahl der Ebenen
n = 0.5 #Einstellung der Lernrate
#Lernschleife
count = 0
acc = []
for x, t in zip(xs, ts):
#Vorwärtsausbreitung
x0 = x.flatten().reshape(784, 1)
a0 = W[0].dot(x0) + B[0]
x1 = sigmoid(a0)
a1 = W[1].dot(x1) + B[1]
y = sigmoid(a1)
#X für Parameteraktualisierung,Liste a
X = [x0, x1]
A = [a0, a1]
#Parameteraktualisierung
for l in range(len(X)):
for j in range(W[l].shape[0]):
for k in range(W[l].shape[1]):
W[l][j, k] = W[l][j, k] - n * back(l, j) * X[l][k]
B[l][j] = B[l][j] - n * back(l, j)
#Genauigkeitsprüfung durch Testdaten
correct, error = 0, 0
for i in range(1000):
#Rückschluss auf gelernte Parameter
x0 = xt[i].flatten().reshape(784, 1)
a0 = W[0].dot(x0) + B[0]
x1 = sigmoid(a0)
a1 = W[1].dot(x1) + B[1]
y = sigmoid(a1)
if np.argmax(y) == np.argmax(tt[i]):
correct += 1
else:
error += 1
calc = correct/(correct+error)
acc.append(calc)
count +=1
print("\r[%s] acc: %s"%(count, calc))
#Anzeige des Genauigkeitsübergangsdiagramms
import matplotlib.pyplot as plt
plt.plot(acc, label='acc')
plt.legend()
plt.show()
In 200 Schritten betrug die Klassifizierungsgenauigkeit 97,8%. Es wäre großartig, wenn das neuronale Netzwerk, das ich durch Scratchen implementiert habe, ordnungsgemäß funktioniert.