[PYTHON] Mémo d'auto-apprentissage «Deep Learning from scratch» (10) Classe MultiLayerNet

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.

Vérifiez le contenu de la classe MultiLayerNet au chapitre 6

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.

Générer un réseau d'objets de réseau neuronal

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.

Générer un calque

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.

Laisse moi apprendre

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.

Ce que fait la méthode du gradient

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.

J'ai essayé de retracer la perte de fonction de perte

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.

Trouvez le dégradé avec en arrière

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.

Classe MultiLayerNetExtend

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.

Partie 9 ← → Partie 11

Recommended Posts

"Deep Learning from scratch" Mémo d'auto-apprentissage (n ° 9) Classe MultiLayerNet
Mémo d'auto-apprentissage «Deep Learning from scratch» (10) Classe MultiLayerNet
Mémo d'auto-apprentissage "Deep Learning from scratch" (partie 12) Deep learning
Mémo d'auto-apprentissage "Deep Learning from scratch" (glossaire illisible)
Mémo d'auto-apprentissage «Deep Learning from scratch» (n ° 11) CNN
Mémo d'auto-apprentissage «Deep Learning from scratch» (n ° 19) Augmentation des données
Apprentissage profond à partir de zéro
[Mémo d'apprentissage] Le Deep Learning fait de zéro [Chapitre 7]
Deep learning / Deep learning made from scratch Chapitre 6 Mémo
[Mémo d'apprentissage] Le Deep Learning fait de zéro [Chapitre 6]
Mémo d'auto-apprentissage «Deep Learning from scratch» (n ° 18) One! Miaou! Grad-CAM!
Deep learning / Deep learning made from scratch Chapitre 7 Mémo
Mémo d'auto-apprentissage "Deep Learning from scratch" (n ° 15) Tutoriel pour débutants TensorFlow
[Mémo d'apprentissage] Deep Learning fait de zéro [~ Chapitre 4]
Mémo d'auto-apprentissage "Deep Learning from scratch" (n ° 13) Essayez d'utiliser Google Colaboratory
Mémo d'auto-apprentissage «Deep Learning from scratch» (n ° 10-2) Valeur initiale du poids
Deep Learning from scratch Chapter 2 Perceptron (lecture du mémo)
Apprentissage profond à partir de zéro 1 à 3 chapitres
Deep learning / Deep learning made from scratch Chapitre 3 Mémo
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 5 Mémo
Apprentissage profond à partir de zéro (calcul des coûts)
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 8 Mémo
Deep learning / Deep learning made from scratch Chapitre 5 Mémo
Deep learning / Deep learning made from scratch Chapitre 4 Mémo
Mémo d'apprentissage profond créé à partir de zéro
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 6 Mémo
"Deep Learning from scratch" Mémo d'auto-apprentissage (n ° 17) J'ai essayé de créer DeepConvNet avec Keras
Apprentissage profond à partir de zéro (propagation vers l'avant)
Apprentissage profond / Apprentissage profond à partir de zéro 2-Essayez de déplacer GRU
"Deep Learning from scratch" avec Haskell (inachevé)
[Windows 10] Construction de l'environnement "Deep Learning from scratch"
Enregistrement d'apprentissage de la lecture "Deep Learning from scratch"
[Deep Learning from scratch] À propos de l'optimisation des hyper paramètres
"Deep Learning from scratch" Mémo d'auto-apprentissage (n ° 14) Exécutez le programme du chapitre 4 sur Google Colaboratory
Mémo d'auto-apprentissage "Deep Learning from scratch" (partie 8) J'ai dessiné le graphique du chapitre 6 avec matplotlib
Classe Python (mémo d'apprentissage Python ⑦)
Deep Learning from scratch ① Chapitre 6 "Techniques liées à l'apprentissage"
Résumé Python vs Ruby "Deep Learning from scratch"
[Deep Learning from scratch] J'ai implémenté la couche Affine
Application de Deep Learning 2 à partir de zéro Filtre anti-spam
[Deep Learning from scratch] J'ai essayé d'expliquer le décrochage
Apprentissage profond / code de travail LSTM
[Deep Learning from scratch] Implémentation de la méthode Momentum et de la méthode AdaGrad
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 1
Un amateur a trébuché dans le Deep Learning ❷ fait à partir de zéro Note: Chapitre 5
Un amateur a trébuché dans le Deep Learning ❷ fait à partir de zéro Note: Chapitre 2
Créez un environnement pour "Deep Learning from scratch" avec Docker
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 3
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 5
Un amateur a trébuché dans le Deep Learning ❷ fait de zéro Note: Chapitre 1
Un amateur a trébuché dans le Deep Learning ❷ fait à partir de zéro Note: Chapitre 4
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 2
J'ai essayé d'implémenter Perceptron Part 1 [Deep Learning from scratch]
Un mémo lors de l'exécution de l'exemple de code de Deep Learning créé à partir de zéro avec Google Colaboratory
Tutoriel d'apprentissage en profondeur de la construction d'environnement
[Deep Learning from scratch] Principales méthodes de mise à jour des paramètres pour les réseaux neuronaux
Version Lua Apprentissage profond à partir de zéro Partie 6 [Traitement d'inférence de réseau neuronal]
Pourquoi ModuleNotFoundError: Aucun module nommé'dataset.mnist 'n'apparaît dans "Deep Learning from scratch".
Écrivez vos impressions sur l'édition du framework Deep Learning 3 créée à partir de zéro