Ich lese ein Meisterwerk, ** "Deep Learning from Zero" **. Diesmal ist das Memo von Kapitel 5. Um den Code auszuführen, laden Sie den gesamten Code von Github herunter und verwenden Sie jupyter notebook in ch05.
Am Ende von Kapitel 5 befindet sich ein Code (twoLayerNet.py & train_neuralnet.py), der eine Ebene erstellt und eine Fehlerausbreitung durchführt. Zuvor befindet sich der Code, der eine Fehlerausbreitung ohne Generierung einer Ebene durchführt, bereits in Kapitel 4. Es gibt (auch twoLayerNet.py & train_neuralnet.py), also werde ich es von dort aus versuchen.
Wie in Kapitel 4, um die Sichtbarkeit zu verbessern, ist es, wenn wir es in einem Code zusammenfassen, fast dasselbe wie beim letzten Mal, nur die --- Gradientenberechnung --- ist anders. Vorerst werde ich es verschieben.
import sys, os
sys.path.append(os.pardir) #Einstellungen zum Importieren von Dateien in das übergeordnete Verzeichnis
from common.functions import * #Funktion im gemeinsamen Ordner.Stellen Sie ein, dass alle Funktionen in py verwendet werden sollen
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
class TwoLayerNet:
#Parameterinitialisierung
def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
self.params = {}
self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
self.params['b2'] = np.zeros(output_size)
#Vorwärtsausbreitung
def predict(self, x):
W1, W2 = self.params['W1'], self.params['W2']
b1, b2 = self.params['b1'], self.params['b2']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = softmax(a2)
return y
#Verlustberechnung
def loss(self, x, t):
y = self.predict(x)
return cross_entropy_error(y, t)
#Genauigkeitsberechnung
def accuracy(self, x, t):
y = self.predict(x)
y = np.argmax(y, axis=1)
t = np.argmax(t, axis=1)
accuracy = np.sum(y == t) / float(x.shape[0])
return accuracy
# -----------------Gradientenberechnung-------------------
def gradient(self, x, t):
W1, W2 = self.params['W1'], self.params['W2']
b1, b2 = self.params['b1'], self.params['b2']
grads = {}
batch_num = x.shape[0]
# forward
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = softmax(a2)
# backward
dy = (y - t) / batch_num
grads['W2'] = np.dot(z1.T, dy)
grads['b2'] = np.sum(dy, axis=0)
dz1 = np.dot(dy, W2.T)
da1 = sigmoid_grad(a1) * dz1
grads['W1'] = np.dot(x.T, da1)
grads['b1'] = np.sum(da1, axis=0)
return grads
# ------------------------------------------------
#Daten lesen
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
#TwoLayerNet instanziieren
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
#Grundeinstellung
iters_num = 10000 #Anzahl der Hinrichtungen
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
train_loss_list, train_acc_list, test_acc_list = [], [], []
iter_per_epoch = max(train_size / batch_size, 1)
for i in range(iters_num):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
#Gradientenberechnung
grad = network.gradient(x_batch, t_batch)
#Parameteraktualisierung
for key in ('W1', 'b1', 'W2', 'b2'):
network.params[key] -= learning_rate * grad[key]
loss = network.loss(x_batch, t_batch)
train_loss_list.append(loss)
#Genauigkeitsanzeige
if i % iter_per_epoch == 0:
train_acc = network.accuracy(x_train, t_train)
test_acc = network.accuracy(x_test, t_test)
train_acc_list.append(train_acc)
test_acc_list.append(test_acc)
print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))
#Zeichnen eines Diagramms
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()
Toll! Im Vergleich zur numerischen Differenzierung ist die Ausführungsgeschwindigkeit so unterschiedlich wie Himmel und Erde. Es ist super schnell! (Tatsächlich ist die numerische Differenzierung extrem langsam.)
Der Inhalt des Codes wurde bereits in Kapitel 4 Memo erläutert, mit Ausnahme von --- Gradientenberechnung ---. Wenn Sie ihn also sehen möchten, Kapitel 4 Memo. Bitte beziehen Sie sich auf Artikel / be171ff7626d370072d1). In diesem Kapitel wird nur der Gradientenberechnungsteil erläutert.
Wenn Sie einen Teil des Gradientenberechnungscodes mit einem Berechnungsdiagramm erklären, sieht es so aus. Die Summe von "grad [" b2 "] = np.sum (dy, axis = 0)" ist die Entsprechung zur Stapelverarbeitung. Trotzdem bin ich beeindruckt, dass die scheinbar komplizierte Fehler-Backpropagation durch Matrixoperationen ersetzt wird.
Leiten Sie zunächst $ \ frac {\ partielles L} {\ partielles W2} = z1 ^ {T} * dy $ ab. Abhängig von der Kettenrate
Als nächstes leiten wir $ \ frac {\ partielles y} {\ partielles Z1} = \ frac {\ partielles y} {\ partielles a2} W2 ^ T $ ab.
Der Übergang von der Welt der numerischen Differenzierung zur Welt der Rückausbreitung von Fehlern durch Matrixoperation ist wirklich innovativ, nicht wahr?
Wie bereits erwähnt, können Sie eine praktische Ausführungsgeschwindigkeit erzielen, indem Sie die Matrixoperationsformel sowohl für die Vorwärtsausbreitung als auch für die Fehlerrückübertragung direkt in den Code schreiben. Das Schreiben des Codes ist jedoch etwas mühsam, nicht wahr? Es gibt also eine Möglichkeit, eine Ebene zu generieren und den Code einfacher zu schreiben.
Um eine Ebene zu generieren, importieren Sie zuerst das OrderedDict mit "aus Sammlungen importieren Sie OrderedDict".
class TwoLayerNet:
def __init__(self, input_size, hidden_size, output_size,
# ................
#Schichterzeugung
self.layers = OrderedDict()
self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
self.layers['Relu1'] = Relu()
self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])
self.lastLayer = SoftmaxWithLoss()
Instanziieren Sie OrderedDict mit self.layers = OrderedDict ()
während der Ersteinrichtung der Klasse TwoLayerNet.
OrderedDict wird einschließlich der Bestellung gespeichert. Wenn Sie also "Affine1", "Relu1", "Affine2", den Layernamen und den Prozess in der Reihenfolge im Wörterbuch "self.layers" registrieren, wird die Bestellung ebenfalls gespeichert.
Nur die letzte Ebene hat eine andere Fehler-Backpropagation-Formel. Behandeln Sie sie daher separat als "self.lastLayer".
#Vorwärtsausbreitung
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
#Vorwärtsausbreitung / Verlustberechnung
def loss(self, x, t):
y = self.predict(x)
return self.lastLayer.forward(y, t)
Dann liest "Forward Propagation" Predict "einfach die Layernamen einzeln aus dem Wörterbuch" self.layers "und führt den" Forward "-Prozess wiederholt aus, unabhängig davon, wie viele Layer es gibt Alles was Sie brauchen ist Code.
Vorwärtsausbreitung / Verlustberechnung "Verlust" breitet sich auch vorwärts aus und führt nur eine "Vorwärts" -Verarbeitung der letzten Schicht mit "self.lastLayer.forward (y, t)" durch. Selbst wenn sich der Verarbeitungsinhalt der letzten Schicht ändert, ändert sich dies Es bleibt der Code.
#Gradientenberechnung
def gradient(self, x, t):
#Vorwärtsausbreitung / Verlustberechnung
self.loss(x, t)
#Fehler bei der Weitergabe
dout = 1
dout = self.lastLayer.backward(dout) #Ersetzen Sie das Rückwärtsergebnis der letzten Schicht durch Dout
layers = list(self.layers.values()) #Wörterbuch selbst.Lesen Sie den Ebenennamen aus den Ebenen
layers.reverse() #Kehren Sie die Reihenfolge der Ebenennamen um
for layer in layers: #Lesen Sie den Namen der invertierten Ebene
dout = layer.backward(dout) #Führen Sie den Layernamen rückwärts aus
# ..................
return grads
Die Gradientenberechnung ist auch dann dieselbe, wenn sich die Kombination der Ebenen ändert. Das ist praktisch, nicht wahr?
Lassen Sie uns nun den Code mit Layer-Generierung ausführen.
Nach wie vor kombinieren wir twoLayerNet.py
und train_neuralnet.py
für eine bessere Code-Sichtbarkeit. Außerdem wird der Zeichnungsteil des Diagramms hinzugefügt.
import sys, os
sys.path.append(os.pardir) #Einstellungen zum Importieren von Dateien in das übergeordnete Verzeichnis
from common.layers import * #Ebenen im gemeinsamen Ordner.Stellen Sie ein, dass alle Funktionen in py verwendet werden sollen
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from collections import OrderedDict #OrderedDict importieren
class TwoLayerNet:
#Initialisieren
def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):
#Parameterinitialisierung
self.params = {}
self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
self.params['b2'] = np.zeros(output_size)
#Schichterzeugung
self.layers = OrderedDict()
self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
self.layers['Relu1'] = Relu()
self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])
self.lastLayer = SoftmaxWithLoss()
#Vorwärtsausbreitung
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
#Vorwärtsausbreitung / Verlustberechnung
def loss(self, x, t):
y = self.predict(x)
return self.lastLayer.forward(y, t)
#Genauigkeitsberechnung
def accuracy(self, x, t):
y = self.predict(x)
y = np.argmax(y, axis=1)
if t.ndim != 1 : t = np.argmax(t, axis=1)
accuracy = np.sum(y == t) / float(x.shape[0])
return accuracy
#Gradientenberechnung
def gradient(self, x, t):
# forward
self.loss(x, t)
# backward
dout = 1
dout = self.lastLayer.backward(dout)
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
#Aufbau
grads = {}
grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db
return grads
#Daten lesen
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
#TwoLayerNet instanziieren
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
#Grundeinstellung
iters_num = 10000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
train_loss_list, train_acc_list, test_acc_list = [], [], []
iter_per_epoch = max(train_size / batch_size, 1)
for i in range(iters_num):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
#Gradientenberechnung
grad = network.gradient(x_batch, t_batch)
#Parameteraktualisierung
for key in ('W1', 'b1', 'W2', 'b2'):
network.params[key] -= learning_rate * grad[key]
loss = network.loss(x_batch, t_batch)
train_loss_list.append(loss)
#Genauigkeitsanzeige
if i % iter_per_epoch == 0:
train_acc = network.accuracy(x_train, t_train)
test_acc = network.accuracy(x_test, t_test)
train_acc_list.append(train_acc)
test_acc_list.append(test_acc)
print(train_acc, test_acc)
#Zeichnen eines Diagramms
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epoch")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()
Die Genauigkeit ist im Vergleich zum Fall ohne Schichterzeugung um etwa +3 Punkte verbessert. Der Grund dafür ist natürlich nicht die Schichterzeugung. Dies liegt daran, dass die Aktivierungsfunktion von "Sigmoid" auf "ReLU" geändert wurde.
Recommended Posts