Ich lese ein Meisterwerk, ** "Deep Learning from Zero" **. Diesmal ist ein Memo von Kapitel 4. Um den Code auszuführen, laden Sie den gesamten Code von Github herunter und verwenden Sie jupyter notebook in ch04.
Am Ende von Kapitel 4 gibt es einen Code (train_neuralnet.py
), der ein zweischichtiges neuronales Netzwerk trainiert, indem der Gradient von Parametern durch numerische Differenzierung berechnet wird. Dieses Mal werde ich diesen Code ausführen und dann die Details durchgehen. Der Code von Github ist jedoch nicht derselbe, aber einige Änderungen und Ergänzungen werden wie folgt vorgenommen.
** 1) Entsprechung zu langsamer Ausführungsgeschwindigkeit ** Obwohl die Verwendung der "numerischen Differenzierung" sehr viel Zeit in Anspruch nimmt, wird die zweite Genauigkeitsberechnung nach einigen Stunden angezeigt, wenn die Genauigkeitsanzeige alle 600 Iter (alle 1 Epoche) angezeigt wird. Um das Ergebnis in etwas mehr als einer Stunde zu sehen, stellen Sie die "Präzisionsanzeige" auf "alle 1 Iter", die "Ausführungszahl von 10000 bis 100" und die "Lernrate von 0,1 bis 1,0" ein.
** 2) Reduzieren Sie die Anzahl der externen Anrufcodes, um die Sichtbarkeit zu verbessern ** Das Merkmal von Zero Work ist, dass der bereits erläuterte Code so oft wie möglich von außen aufgerufen wird und der gleichzeitig anzuzeigende Code so präzise wie möglich ausgedrückt wird. Das Aufrufen von "two_layer_net.py" von "train_neuralnet.py" und "gradient.py" im "gemeinsamen Ordner" von "two_layer_net.py" ist jedoch sehr verwirrend. Also werde ich diesen Bereich zu train_neuralnet.py hinzufügen, um die Aussichten zu verbessern.
Es wird jedoch nur "function.py" im "gemeinsamen Ordner" als externer Code verwendet (Sigmoid, Sofytmax, Cross_entropy_error usw.).
Lassen Sie uns zuerst den folgenden Code ausführen.
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 numerical_gradient(self, x, t):
loss_W = lambda W: self.loss(x, t)
grads = {}
grads['W1'] = numerical_gradient2(loss_W, self.params['W1'])
grads['b1'] = numerical_gradient2(loss_W, self.params['b1'])
grads['W2'] = numerical_gradient2(loss_W, self.params['W2'])
grads['b2'] = numerical_gradient2(loss_W, self.params['b2'])
return grads
#Numerische Differenzierung
def numerical_gradient2(f, x):
h = 1e-4 # 0.0001
grad = np.zeros_like(x)
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
while not it.finished:
idx = it.multi_index
tmp_val = x[idx]
x[idx] = tmp_val + h
fxh1 = f(x) # f(x+h)
x[idx] = tmp_val - h
fxh2 = f(x) # f(x-h)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp_val #Stellen Sie den Wert wieder her
it.iternext()
return grad
#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 = 100 #Die Ausführungsanzahl wurde von 10000 auf 100 geändert
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 1 #Die Lernrate beträgt 0.1 → 1.Wechseln Sie zu 0
train_loss_list, train_acc_list, test_acc_list = [], [], []
iter_per_epoch = 1 #Die Genauigkeitsanzeige ändert sich von 1 Epoche auf 1 Iter
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.numerical_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)
#Anzeige(iter und trainieren_Verlust hinzufügen)
print('[iter='+str(i)+'] '+'train_loss='+str(loss)+', '+'train_acc='+str(train_acc)+', '+'test_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("iter")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()
Es dauerte 75 Minuten, um 100iter auf meinem "Macbook Air" laufen zu lassen. Wie wäre es, wenn Sie das Genauigkeitsdiagramm, das 16epoch (= 9600iter) ausgeführt hat, auch bei numerischer Differenzierung in den Text einfügen? Abgesehen davon nimmt die numerische Differenzierung enorm viel Zeit in Anspruch.
#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)
Dies ist der Teil, der beim Instanziieren einer Klasse nur einmal ausgeführt wird. Hier wird jeder Parameter initialisiert. "np.random.randn ()" ist die Erzeugung einer Normalverteilung mit einem Mittelwert von 0 und einer Varianz von 1, und "np.zeros ()" ist die Erzeugung einer Nullmatrix. Die Größe jeder Matrix ist wie folgt.
#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
Dies ist der Vorwärtsausbreitungsteil. Lesen Sie die in den Wörterbuchparametern gespeicherte Matrix der Gewichte 'W1', 'W2' und Bias 'b1', 'b2' und geben Sie sie durch Matrixoperation weiter.
#Verlustberechnung
def loss(self, x, t):
y = self.predict(x)
return cross_entropy_error(y, t)
Dies ist der Teil, der den Verlust berechnet. Finden Sie die Kreuzentropie von y und die Lehrerdaten t, die Sie erhalten, indem Sie die Vorwärtsausbreitung früher aufrufen. Cross Entropy verwendet die Funktionen in function.py
im common folder
. Vorerst,
y [np.arange (batch_size), t]
bedeutet, das entsprechende Element von y gemäß dem richtigen Antwortetikett in der Reihenfolge t zu schneiden.
#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
Dies ist der Teil der Genauigkeitsberechnung. Y wird aus den Eingabedaten x abgeleitet, die Indizes von y und das richtige Antwortetikett t werden herausgenommen, und die Zahl, wenn die beiden Indizes gleich sind, wird durch die Anzahl der Daten von x geteilt, um die Genauigkeit zu berechnen.
#Gradientenberechnung
def numerical_gradient(self, x, t):
loss_W = lambda W: self.loss(x, t)
grads = {}
grads['W1'] = numerical_gradient2(loss_W, self.params['W1'])
grads['b1'] = numerical_gradient2(loss_W, self.params['b1'])
grads['W2'] = numerical_gradient2(loss_W, self.params['W2'])
grads['b2'] = numerical_gradient2(loss_W, self.params['b2'])
return grads
Dies ist der Teil der Gradientenberechnung. Verwenden Sie die später angezeigte Funktion "numerischer Gradient2", um die Ergebnisse der Gradientenberechnung in einem Wörterbuchformat zusammenzufassen. Die Argumente sind der "Verlustfunktionsausdruck", der die Kreuzentropie basierend auf x und der korrekten Antwortbezeichnung t und die "Parameterspezifikation" findet.
Das Original ist übrigens "grads ['W1'] = numerischer_gradient (loss_W, self.params ['W1'])", oder? Ist es rekursive Verwendung? Zuerst dachte ich, aber das ist es nicht.
Im Original enthält "TwoLayerNet.py" am Anfang eine Beschreibung von "from common.gradient import numerischer_gradient", und die "numerische_gradient-Funktion" in "gradient.py" im "gemeinsamen Ordner" wird importiert. Verwenden Sie sie daher. Hier ist was du tust. Es gibt weniger Fehler, wenn Sie den Namen hier ändern, also habe ich den Namen in "numerischer_gradient2" geändert.
#Numerische Differenzierung
def numerical_gradient2(f, x):
h = 1e-4 # 0.0001
#Nullmatrixgrad, der das Berechnungsergebnis des Gradienten speichert(Größe angegeben durch x)Bereiten
grad = np.zeros_like(x)
#Sequentielle Indizierung der Matrix x(Geben Sie Zeile und Spalte an)Machen
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
#Fahren Sie fort, bis alle Zeilen und Spalten der Matrix x angegeben sind
while not it.finished:
idx = it.multi_index #zu idx(Linie,Säule)Ersatz
tmp_val = x[idx] #tmp der durch idx angegebene Wert von x_In val speichern
#Berechnen Sie den Verlust, indem Sie eine kleine Zahl h addieren und sich vorwärts ausbreiten
x[idx] = tmp_val + h
fxh1 = f(x) # f(x+h)
#Berechnen Sie den Verlust, indem Sie eine kleine Zahl h subtrahieren und sich vorwärts ausbreiten
x[idx] = tmp_val - h
fxh2 = f(x) # f(x-h)
#Berechnen Sie die Steigung des entsprechenden Index
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp_val #Stellen Sie den gespeicherten Wert wieder her
it.iternext() #Führen Sie den folgenden Index aus
return grad
Dies ist der Teil, der die numerische Differenzierung durchführt. Einfach ausgedrückt, addieren Sie für jeden der Parameter einen kleinen Wert h, um die Ausbreitung / den Verlust vorwärts zu berechnen, subtrahieren Sie einen kleinen Wert h, um die Ausbreitung / den Verlust vorwärts zu berechnen, und den Gradienten, abhängig davon, wie sich die beiden Verlustberechnungen geändert haben. Ich habe entschieden. ``
Diesmal beträgt die Anzahl der Parameter 784 * 50 = 39.200 für W1, 50 * 10 = 500 für W2, 50 für b1 und 10 für b2 für insgesamt 39.760. Da die Vorwärtsausbreitungs- / Verlustberechnung für jeden Parameter zweimal durchgeführt wird, werden für jede Parameteraktualisierung des Netzwerks 79.520 Vorwärtsausbreitungs- / Verlustberechnungen durchgeführt. Die Operation ist extrem langsam.
Jetzt habe ich einen np.nditer
, mit dem ich im Code nicht vertraut bin. Normalerweise verwendet die Indexspezifikation der Matrix for loop für die Zeilenspezifikation + for loop und double loop für die Spaltenspezifikation, aber dieser np.nditer
kann dies nur einmal tun.
Wie ich zuvor erklärt habe, befindet sich dieser Code ursprünglich in "gradient.py" im "gemeinsamen Ordner". Der ursprüngliche Funktionsname ist verwirrend mit "numerischer_gradient" (wie der Funktionsname in der Gradientenberechnung), daher wird er diesmal in "numerischer_gradient2" geändert.
#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 = 100 #Die Ausführungsanzahl wurde von 10000 auf 100 geändert
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 1 #Die Lernrate beträgt 0.1 → 1.Wechseln Sie zu 0
train_loss_list, train_acc_list, test_acc_list = [], [], []
iter_per_epoch = 1 #Die Genauigkeitsanzeige ändert sich von 1 Epoche auf 1 Iter
Instanziieren Sie nach dem Laden der Daten die Klasse "TwoLayerNet". Da input_size = 784, hidden_size = 50, output_size = 10 ist, sind die Modell- und Matrixoperation wie folgt.
for i in range(iters_num):
#Holen Sie sich Mini-Batch-Daten
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
#Gradientenberechnung
grad = network.numerical_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)
#Anzeige(iter und trainieren_Verlust hinzufügen)
print('[iter='+str(i)+'] '+'train_loss='+str(loss)+', '+'train_acc='+str(train_acc)+', '+'test_acc='+str(test_acc))
Bereiten Sie zunächst die Daten für das Mini-Batch-Lernen vor. np.random.choice (train_size, batch_size)
weist batch_mask
das Ergebnis der zufälligen Auswahl von 100 von 60.000 Zugdaten (welche Nummer ausgewählt wurde) zu und verwendet es für Trainingsdaten. Und erhalten Sie das richtige Antwortetikett.
Als nächstes Gradientenberechnung. Rufen Sie die Funktion "numerischer Gradient" der "TwoLayerNet-Klasse" auf und ermitteln Sie, wie bereits erläutert, den Gradienten durch numerische Differenzierung für jeden Parameter.
Anschließend werden die Parameter mit dem berechneten Gradienten aktualisiert, der Verlust berechnet und aufgezeichnet sowie die Genauigkeit angezeigt.
#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("iter")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()
Nach Abschluss des Trainings wird das Genauigkeitsdiagramm angezeigt. Dies bedarf keiner Erklärung.
Recommended Posts