En lisant "Deep Learning from scratch" (écrit par Yasuki Saito, publié par O'Reilly Japan), je prendrai note des sites auxquels j'ai fait référence. Partie 9 ← → Partie 11
Après avoir expliqué l'implémentation en couches au chapitre 5, nous n'expliquerons pas beaucoup le programme lui-même au chapitre 6 et plus tard. L'exemple de programme se trouve dans le fichier que vous avez téléchargé en premier, vous devez donc l'exécuter vous-même et vérifier le contenu, mais c'est assez difficile pour les débutants.
Eh bien, j'irai petit à petit.
Le chapitre 3 a fourni une explication de base du réseau neuronal et le chapitre 4 a implémenté la classe de réseau neuronal à deux couches TwoLayerNet. Après cela, il y a eu diverses explications, et c'est devenu la classe MultiLayerNet. Cela semble bien compliqué, mais les bases sont les mêmes que TwoLayerNet. En regardant le contenu de la bibliothèque layer.py référencée par cette classe, c'est le même que celui utilisé par la classe TwoLayerNet. Ce qui semble compliqué est Implémenté couche par couche pour augmenter la polyvalence du programme La fonction d'activation, la méthode de mise à jour des paramètres, la valeur de poids initiale, etc. peuvent maintenant être sélectionnées. Cela semble provenir de.
Lorsque vous voulez comprendre le programme, il est sûr de tracer manuellement ligne par ligne.
Alors, traçons le programme sur 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 signifie utiliser des données MNIST avec 784 éléments. output_size = 10 signifie qu'il y a 10 résultats reconnus. alors hidden_size_list=[100, 100, 100, 100, 100, 100] Que se passe-t-il à l'intérieur de l'objet réseau
Avec initialisation dans la définition de MultiLayerNet dans 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 = {}
#Initialisation du poids
self.__init_weight(weight_init_std)
Je l'ai omis dans la création de l'objet activation = 'relu' Utiliser relu comme fonction d'activation weight_init_std = 'relu' La valeur initiale du poids est compatible avec relu. Utilisez la valeur initiale de He. self.hidden_layer_num = len (hidden_size_list) Créez autant de couches cachées qu'il y a d'éléments dans la liste hidden_size_list, C'est supposé être.
Donc, pour boucle autant que le nombre d'éléments
#Génération de couches
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]()
À la fin de ceci comme couche de sortie last_layer SoftmaxWithLoss Sera ajouté.
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()
En d'autres termes, il y a 6 couches cachées + 1 couche de sortie, ce qui fait un réseau à 7 couches. Le contenu des couches de liste est le suivant:
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])) ])
En l'implémentant couche par couche, vous pouvez voir que le nombre de couches cachées peut être spécifié par le nombre d'éléments dans hidden_size_list. Si vous avez environ 6 couches, vous pouvez augmenter le nombre de couches dans le programme comme la classe TwoLayerNet, mais quand cela atteint 100, c'est un gaspillage.
Les données MNIST sont données à cet objet de réseau pour l'entraînement.
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)
Dans une boucle mini-batch grads = network.gradient(x_batch, t_batch) Alors je cherche un dégradé Le contenu des diplômés ressemble à ceci
{ '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]) }
Dans le contenu des derniers grades ['W7'], la probabilité de laquelle des nombres 0 à 9 émis par la fonction softmax est transformée en une liste de 10 éléments, et le nombre de lignes des données d'apprentissage lues est organisé. Je suis dehors. Et
optimizer.update(network.params, grads)
Mettre à jour en soustrayant le contenu des grades du contenu des paramètres params avec la méthode de mise à jour de la fonction SGD de la bibliothèque optimizer.py dans le dossier commun. Dans l'exemple ci-dessus, nous mettons à jour avec la méthode SGD. En plus de SGD, Momentum, AdaGrad, Adam et RMSprop sont définis dans la bibliothèque.
Les paramètres mis à jour seront utilisés pour le prochain traitement par lots, donc l'apprentissage se poursuivra au fur et à mesure que le lot boucle.
Ensuite, ce que fait cette méthode de gradient est de trouver le gradient du paramètre de poids par la méthode de propagation des erreurs. Tout d'abord, calculez la valeur de la fonction de perte dans le sens direct, puis tracez l'ensemble de couches lorsque l'objet réseau a été créé dans le sens inverse pour obtenir le dégradé.
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)
#Réglage
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
En premier, self.loss(x, t) Je n'ai pas vraiment compris cela. J'exécute une fonction, mais il ne semble pas que j'utilise le résultat ensuite. J'ai donc essayé de retracer le contenu. Ce que nous exécutons est la perte de fonction définie dans multi_layer_net.py.
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
Dans la fonction de perte, prédire prédit le résultat y à partir des données d'entrée. En cela, la méthode de transfert de la couche d'Affine1 à Affine7 est exécutée.
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
Calculer weight_decay à partir des poids (params ['W1'] etc.) pour éviter le surapprentissage et ajouter ceci Production.
weight_decay
59.84568388277881
network.last_layer.forward(y, t_batch)
2.2491110821288687
self.last_layer.forward (y, t) est l'initialisation de la classe MultiLayerNet.
self.last_layer = SoftmaxWithLoss()
Puisqu'il est défini comme, ce qui est réellement exécuté est la méthode forward de SoftmaxWithLoss () définie dans layer.py.
class SoftmaxWithLoss:
def __init__(self):
self.loss = None
self.y = None #sortie de softmax
self.t = None #Données des enseignants
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: #Les données des enseignants en sont une-hot-Pour le vecteur
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
Ainsi, dans cette méthode directe, l'erreur d'entropie croisée est calculée et renvoyée.
network.last_layer.loss
2.2491110821288687
from common.functions import *
cross_entropy_error(network.last_layer.y, network.last_layer.t)
2.2491110821288687
En disant cela, je savais à quoi je parlais et ce que je faisais avec self.loss (x, t).
alors,
La fonction SoftmaxWithLoss utilisera alors la méthode arrière dans la rétropropagation d'erreur pour trouver le dégradé. Il fait référence à self.y et self.t, qui sont des variables définies lorsque la méthode forward est exécutée. En d'autres termes, ** le premier self.loss (x, t) ne recherche pas la fonction de perte, mais se prépare à utiliser la méthode descendante dans la méthode de rétropropagation d'erreur **.
Pour revenir en arrière, il faut avancer, eh bien, si vous le comprenez, c'est une évidence.
Après avoir exécuté self.loss (x, t) et défini la valeur prédite à partir des données d'entrée, calculez le gradient par la méthode de propagation des erreurs.
# backward
dout = 1
dout = self.last_layer.backward(dout)
self.last_layer.backward (dout) signifie SoftmaxWithLoss.backward (). dout renvoie une liste des différences entre la valeur prédite y et le libellé enseignant t. [y1 --t1, y2 --t2, y3 --t3, ・ ・ ・, y100 --t100]
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
Dans layer.reverse (), les couches empilées sont inversées et dout = layer.backward (dout) est répété pour trouver le dégradé. Si vous développez l'itération, cela ressemblera à ceci.
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
Le self.x self.W référencé dans chaque couche Affine est celui qui a été défini lors de l'exécution de la méthode forward.
class Affine:
def __init__(self, W, b):
self.W =W
self.b = b
self.x = None
self.original_x_shape = None
#Différenciation des paramètres poids / biais
self.dW = None
self.db = None
def forward(self, x):
#Compatible Tensol
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) #Revenir à la forme des données d'entrée (compatible avec le tenseur)
return dx
En utilisant les dw et db obtenus pour chaque couche, définissez le gradient de poids et de biais de chaque couche et renvoyez-le comme valeur de la fonction.
#Réglage
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
Avec le dégradé renvoyé, les paramètres sont mis à jour et le processus du mini-lot se termine une fois.
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 est le taux d'apprentissage Dans cet exemple, 0,01 est défini.
La classe MultiLayerNetExtend dans multi_layer_net_extend.py prend en charge l'abandon et la normalisation par lots dans la génération de couches, mais les bases sont les mêmes que celles de MultiLayerNet.
Recommended Posts