Während ich "Deep Learning from Grund" (gelesen von Yasuki Saito, veröffentlicht von O'Reilly Japan) lese, werde ich die Websites notieren, auf die ich mich bezog. Teil 9 ← → Teil 11
Nachdem wir die Implementierung in Schichten in Kapitel 5 erklärt haben, werden wir das Programm selbst in Kapitel 6 und höher nicht viel erklären. Das Beispielprogramm befindet sich in der Datei, die Sie zuerst heruntergeladen haben. Sie sollten es daher selbst ausführen und den Inhalt überprüfen. Für Anfänger ist es jedoch recht schwierig.
Nun, ich werde nach und nach gehen.
Kapitel 3 enthielt eine grundlegende Erklärung des neuronalen Netzes, und Kapitel 4 implementierte die zweischichtige neuronale Netzwerkklasse TwoLayerNet. Danach gab es verschiedene Erklärungen und es wurde die MultiLayerNet-Klasse. Es sieht sehr kompliziert aus, aber die Grundlagen sind die gleichen wie bei TwoLayerNet. Wenn Sie sich den Inhalt der Bibliotheksschicht.py ansehen, auf die diese Klasse verweist, entspricht dies der von der TwoLayerNet-Klasse verwendeten. Was kompliziert aussieht, ist Schichtweise implementiert, um die Vielseitigkeit des Programms zu erhöhen Die Aktivierungsfunktion, die Parameteraktualisierungsmethode, der anfängliche Gewichtswert usw. können jetzt ausgewählt werden. Es scheint von zu sein.
Wenn Sie das Programm verstehen möchten, müssen Sie es zeilenweise manuell verfolgen.
Verfolgen wir also das Programm auf P192.
weight_decay_lambda = 0.1
network = MultiLayerNet(input_size=784,
hidden_size_list=[100, 100, 100, 100, 100, 100],
output_size=10,
weight_decay_lambda=weight_decay_lambda)
input_size = 784 bedeutet, MNIST-Daten mit 784 Elementen zu verwenden. output_size = 10 bedeutet, dass 10 Ergebnisse erkannt wurden. damit hidden_size_list=[100, 100, 100, 100, 100, 100] Was passiert innerhalb des Netzwerkobjekts?
Bei der Initialisierung in der Definition von MultiLayerNet in multi_layer_net.py
def __init__(self, input_size, hidden_size_list, output_size,
activation='relu', weight_init_std='relu', weight_decay_lambda=0):
self.input_size = input_size
self.output_size = output_size
self.hidden_size_list = hidden_size_list
self.hidden_layer_num = len(hidden_size_list)
self.weight_decay_lambda = weight_decay_lambda
self.params = {}
#Gewichtsinitialisierung
self.__init_weight(weight_init_std)
Ich habe es bei der Objekterstellung weggelassen Aktivierung = 'relu' Verwenden Sie relu als Aktivierungsfunktion weight_init_std = 'relu' Der Anfangswert des Gewichts ist mit relu kompatibel. Verwenden Sie den Anfangswert von He. self.hidden_layer_num = len (hidden_size_list) Erstellt so viele Ebenen für versteckte Ebenen, wie Elemente in der Liste hidden_size_list enthalten sind. Es sollte sein.
Also für Schleife so viele wie die Anzahl der Elemente
#Schichterzeugung
activation_layer = {'sigmoid': Sigmoid, 'relu': Relu}
self.layers = OrderedDict()
for idx in range(1, self.hidden_layer_num+1):
self.layers['Affine' + str(idx)] = Affine(self.params['W' + str(idx)],
self.params['b' + str(idx)])
self.layers['Activation_function' + str(idx)] = activation_layer[activation]()
Am Ende davon als Ausgabeebene last_layer SoftmaxWithLoss Wird hinzugefügt werden.
idx = self.hidden_layer_num + 1
self.layers['Affine' + str(idx)] = Affine(self.params['W' + str(idx)],
self.params['b' + str(idx)])
self.last_layer = SoftmaxWithLoss()
Mit anderen Worten, es gibt 6 versteckte Schichten + 1 Ausgangsschicht, wodurch ein 7-Schicht-Netzwerk entsteht. Der Inhalt der Listenebenen lautet wie folgt:
OrderedDict([ ('Affine1', Affine(params[W1],params[b1])), ('Activation_function1', Relu), ('Affine2', Affine(params[W2],params[b2])), ('Activation_function2', Relu), ('Affine3', Affine(params[W3],params[b3])), ('Activation_function3', Relu), ('Affine4', Affine(params[W4],params[b4])), ('Activation_function4', Relu), ('Affine5', Affine(params[W5],params[b5])), ('Activation_function5', Relu), ('Affine6', Affine(params[W6],params[b6])), ('Activation_function6', Relu), ('Affine7', Affine(params[W7],params[b7])) ])
Indem Sie es Schicht für Schicht implementieren, können Sie sehen, dass die Anzahl der ausgeblendeten Ebenen durch die Anzahl der Elemente in hidden_size_list angegeben werden kann. Wenn Sie ungefähr 6 Ebenen haben, können Sie die Anzahl der Ebenen im Programm wie in der TwoLayerNet-Klasse erhöhen. Wenn diese jedoch 100 erreichen, ist dies eine Verschwendung.
MNIST-Daten werden diesem Netzwerkobjekt zum Training übergeben.
optimizer = SGD(lr=0.01)
for i in range(1000000000):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
grads = network.gradient(x_batch, t_batch)
optimizer.update(network.params, grads)
In einer Mini-Batch-Schleife grads = network.gradient(x_batch, t_batch) Also suche ich einen Farbverlauf Der Inhalt der Absolventen sieht so aus
{ 'W1': array([[-0.00240062, -0.01276378, 0.00096349, ..., 0.0054993 ], [-0.00232299, -0.0022137 , 0.0036697 , ..., -0.00693252], ..., [-0.00214929, 0.00358515, -0.00982791, ..., 0.00342722]]), 'b1': array([-4.51501921e-03, 5.25825778e-03, ..., -8.60827293e-03]), 'W2': array([[ 0.00394647, -0.01781943, 0.00114132, ..., 0.0029042 ], [-0.00551014, 0.00238989, 0.01442266, ..., 0.00171659], ..., [ 0.00279524, 0.01496588, 0.01859664, ..., -0.02194152]]), 'b2': array([ 2.08738753e-03, -8.28071395e-03, ..., 1.22945079e-02]), 'W3': array([[ ..., ]]), 'b3': array([ ..., ]), 'W4': array([[ ..., ]]), 'b4': array([ ..., ]), 'W5': array([[ ..., ]]), 'b5': array([ ..., ]), 'W6': array([[ ..., ]]), 'b6': array([ ..., ]), 'W7': array([ [ 6.72420338e-02,3.36979669e-04,1.26773417e-02,-2.30916938e-03, -4.84414774e-02, -2.58458587e-02,-5.26754173e-02,3.61136740e-02,-4.29689699e-03, -2.85799599e-02], [ ...], [-1.68008362e-02, 6.87882255e-03, -3.15578291e-03, -8.00362948e-04, 8.81555008e-03, -9.23032804e-03,-1.83337109e-02, 2.17933554e-02, -6.52331525e-03, 1.50930257e-02] ]), 'b7': array([ 0.11697053, -0.02521648, 0.03697393, -0.015763 , -0.0456317 , -0.03476072, -0.05961871, 0.0096403 , 0.03581566, -0.01840983]) }
Im Inhalt der letzten Noten ['W7'] wird die Wahrscheinlichkeit, welche der von der Softmax-Funktion ausgegebenen Zahlen 0 bis 9 in eine Liste von 10 Elementen umgewandelt wird, und die Anzahl der Zeilen der gelesenen Trainingsdaten angeordnet. Ich bin raus. Und
optimizer.update(network.params, grads)
Aktualisieren Sie, indem Sie den Inhalt von Absolventen vom Inhalt der Parameterparameter mit der Aktualisierungsmethode der Funktion SGD der Bibliothek optimizer.py im allgemeinen Ordner subtrahieren. Im obigen Beispiel aktualisieren wir mit der SGD-Methode. Zusätzlich zu SGD sind Momentum, AdaGrad, Adam und RMSprop in der Bibliothek definiert.
Die aktualisierten Parameter werden für die nächste Stapelverarbeitung verwendet, sodass das Lernen in den Stapelschleifen fortgesetzt wird.
Diese Gradientenmethode ermittelt dann den Gradienten des Gewichtungsparameters durch die Fehlerrückausbreitungsmethode. Berechnen Sie zuerst den Wert der Verlustfunktion in Vorwärtsrichtung und verfolgen Sie dann die Schicht, die beim Erstellen des Netzwerkobjekts in umgekehrter Richtung festgelegt wurde, um den Gradienten zu erhalten.
def gradient(self, x, t):
# forward
self.loss(x, t)
# backward
dout = 1
dout = self.last_layer.backward(dout)
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
#Aufbau
grads = {}
for idx in range(1, self.hidden_layer_num+2):
grads['W' + str(idx)] = self.layers['Affine' + str(idx)].dW + self.weight_decay_lambda * self.layers['Affine' + str(idx)].W
grads['b' + str(idx)] = self.layers['Affine' + str(idx)].db
return grads
Zunaechst, self.loss(x, t) Das habe ich nicht wirklich verstanden. Ich führe eine Funktion aus, aber es sieht nicht so aus, als würde ich das Ergebnis als nächstes verwenden. Also habe ich versucht, den Inhalt zu verfolgen. Was wir ausführen, ist der in multi_layer_net.py definierte Funktionsverlust.
network.loss(x_batch, t_batch)
62.09479496490768
def loss(self, x, t):
y = self.predict(x)
weight_decay = 0
for idx in range(1, self.hidden_layer_num + 2):
W = self.params['W' + str(idx)]
weight_decay += 0.5 * self.weight_decay_lambda * np.sum(W ** 2)
return self.last_layer.forward(y, t) + weight_decay
In der Verlustfunktion sagt Predict das Ergebnis y aus den Eingabedaten voraus. Dabei wird die Vorwärtsmethode der Ebene von Affine1 nach Affine7 ausgeführt.
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
Berechnen Sie weight_decay aus Gewichten (Parameter ['W1'] usw.), um Überlernen zu vermeiden, und fügen Sie diese hinzu Ausgabe.
weight_decay
59.84568388277881
network.last_layer.forward(y, t_batch)
2.2491110821288687
self.last_layer.forward (y, t) ist die Initialisierung der MultiLayerNet-Klasse.
self.last_layer = SoftmaxWithLoss()
Da es definiert ist als, wird tatsächlich die in layer.py definierte Forward-Methode von SoftmaxWithLoss () ausgeführt.
class SoftmaxWithLoss:
def __init__(self):
self.loss = None
self.y = None #Ausgabe von Softmax
self.t = None #Lehrerdaten
def forward(self, x, t):
self.t = t
self.y = softmax(x)
self.loss = cross_entropy_error(self.y, self.t)
return self.loss
def backward(self, dout=1):
batch_size = self.t.shape[0]
if self.t.size == self.y.size: #Lehrerdaten sind eins-hot-Für Vektor
dx = (self.y - self.t) / batch_size
else:
dx = self.y.copy()
dx[np.arange(batch_size), self.t] -= 1
dx = dx / batch_size
return dx
Bei dieser Vorwärtsmethode wird der Kreuzentropiefehler berechnet und zurückgegeben.
network.last_layer.loss
2.2491110821288687
from common.functions import *
cross_entropy_error(network.last_layer.y, network.last_layer.t)
2.2491110821288687
Indem ich das sagte, wusste ich, worauf ich mich bezog und was ich mit self.loss (x, t) machte.
damit,
Die SoftmaxWithLoss-Funktion verwendet dann die Rückwärtsmethode bei der Fehlerrückübertragung, um den Gradienten zu ermitteln. Es bezieht sich auf self.y und self.t, die Variablen sind, die festgelegt werden, wenn die Forward-Methode ausgeführt wird. Mit anderen Worten, ** der erste Selbstverlust (x, t) sucht nicht nach der Verlustfunktion, sondern bereitet die Verwendung der Rückwärtsmethode in der Fehler-Backpropagation-Methode ** vor.
Um zurück zu gehen, muss man vorwärts gehen. Wenn man es versteht, ist das eine Selbstverständlichkeit.
Berechnen Sie nach dem Ausführen von self.loss (x, t) und dem Einstellen des vorhergesagten Werts aus den Eingabedaten den Gradienten mit der Methode der Fehlerrückübertragung.
# backward
dout = 1
dout = self.last_layer.backward(dout)
self.last_layer.backward (dout) steht für SoftmaxWithLoss.backward (). dout gibt eine Liste der Unterschiede zwischen dem vorhergesagten Wert y und der Lehrerbezeichnung t zurück. [y1 - t1, y2 - t2, y3 - t3, ・ ・ ・, y100 - t100]
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
In layer.reverse () werden die gestapelten Ebenen umgekehrt und dout = layer.backward (dout) wird wiederholt, um den Gradienten zu ermitteln. Wenn Sie die Iteration erweitern, sieht sie folgendermaßen aus.
dout = layers[0].backward(dout) #Affine7
dout = layers[1].backward(dout) #Activation_function6 Relu
dout = layers[2].backward(dout) #Affine6
dout = layers[3].backward(dout) #Activation_function5 Relu
dout = layers[4].backward(dout) #Affine5
dout = layers[5].backward(dout) #Activation_function4 Relu
dout = layers[6].backward(dout) #Affine4
dout = layers[7].backward(dout) #Activation_function3 Relu
dout = layers[8].backward(dout) #Affine3
dout = layers[9].backward(dout) #Activation_function2 Relu
dout = layers[10].backward(dout) #Affine2
dout = layers[11].backward(dout) #Activation_function1 Relu
dout = layers[12].backward(dout) #Affine1
Das self.x self.W, auf das in jeder Affine-Ebene verwiesen wird, wurde bei der Ausführung der Forward-Methode festgelegt.
class Affine:
def __init__(self, W, b):
self.W =W
self.b = b
self.x = None
self.original_x_shape = None
#Differenzierung von Gewichts- / Bias-Parametern
self.dW = None
self.db = None
def forward(self, x):
#Tensol kompatibel
self.original_x_shape = x.shape
x = x.reshape(x.shape[0], -1)
self.x = x
out = np.dot(self.x, self.W) + self.b
return out
def backward(self, dout):
dx = np.dot(dout, self.W.T)
self.dW = np.dot(self.x.T, dout)
self.db = np.sum(dout, axis=0)
dx = dx.reshape(*self.original_x_shape) #Kehren Sie zur Form der Eingabedaten zurück (kompatibel mit Tensor).
return dx
Stellen Sie unter Verwendung von dw und db, die für jede Schicht erhalten wurden, das Gewicht und den Vorspannungsgradienten jeder Schicht ein und geben Sie ihn als Wert der Funktion zurück.
#Aufbau
grads = {}
for idx in range(1, self.hidden_layer_num+2):
grads['W' + str(idx)] = self.layers['Affine' + str(idx)].dW + self.weight_decay_lambda * self.layers['Affine' + str(idx)].W
grads['b' + str(idx)] = self.layers['Affine' + str(idx)].db
Mit dem zurückgegebenen Gradienten werden die Parameter aktualisiert und der Mini-Batch-Prozess endet einmal.
grads = network.gradient(x_batch, t_batch)
optimizer.update(network.params, grads)
class SGD:
def __init__(self, lr=0.01):
self.lr = lr
def update(self, params, grads):
for key in params.keys():
params[key] -= self.lr * grads[key]
lr ist die Lernrate In diesem Beispiel wird 0,01 festgelegt.
Die MultiLayerNetExtend-Klasse in multi_layer_net_extend.py unterstützt Dropout- und Batch-Normalisierung bei der Layer-Generierung, die Grundlagen sind jedoch dieselben wie bei MultiLayerNet.
Recommended Posts