[PYTHON] Deep learning / Deep learning made from scratch Chapitre 6 Mémo

1.Tout d'abord

Je lis un chef-d'œuvre, ** "Deep Learning from Zero" **. Cette fois, c'est un mémo du chapitre 6. Pour exécuter le code, téléchargez le code complet depuis Github et utilisez le notebook jupyter dans ch06.

2. Code pour essayer la méthode d'optimisation

Afin d'essayer réellement la méthode d'optimisation, nous utiliserons ch06 / optimizer_compare_mnist.py avec quelques modifications / ajouts. Le réseau est une couche 100x4 qui classe MNIST. Dans le code ci-dessous, ʻoptimizer key setting`, commentez uniquement l'optimiseur que vous n'utilisez pas et exécutez-le.

import os
import sys
sys.path.append(os.pardir)  #Paramètres d'importation des fichiers dans le répertoire parent
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.util import smooth_curve  # smooth_curve (Une fonction qui lisse la transition de la valeur de perte)importer
from common.multi_layer_net import MultiLayerNet  #Importation MultiLayerNet
from common.optimizer import *  #import de l'optimiseur

#Lire les données MNIST
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

#Réglage initial
train_size = x_train.shape[0]
batch_size = 128
max_iterations = 2001

#réglage des touches de l'optimiseur
optimizers = {}
optimizers['SGD'] = SGD()
optimizers['Momentum'] = Momentum()
optimizers['Nesterov'] = Nesterov()
optimizers['AdaGrad'] = AdaGrad()
optimizers['RMSprop'] = RMSprop() 
optimizers['Adam'] = Adam()

#réseau et train_Définir la perte pour chaque clé d'optimisation
networks = {}
train_loss = {}
for key in optimizers.keys():
    networks[key] = MultiLayerNet(
        input_size=784, hidden_size_list=[100, 100, 100, 100],
        output_size=10)
    train_loss[key] = []    


#Boucle d'apprentissage
for i in range(max_iterations):
    
    #Extraire les données du mini-lot
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    
    #Mettez à jour le dégradé et enregistrez la perte pour chaque clé d'optimisation
    for key in optimizers.keys():
        grads = networks[key].gradient(x_batch, t_batch)
        optimizers[key].update(networks[key].params, grads)
    
        loss = networks[key].loss(x_batch, t_batch)
        train_loss[key].append(loss)
    
    #Affichage des pertes(Tous les 500 iter)
    if i % 500 == 0:
        print( "===========" + "iteration:" + str(i) + "===========")
        for key in optimizers.keys():
            loss = networks[key].loss(x_batch, t_batch)
            print(key + ":" + str(loss))


#Dessiner un graphique
fig = plt.figure(figsize=(8,6))  #Spécification de la taille du graphique
markers = {"SGD": "o", "Momentum": "x", "Nesterov": "^", "AdaGrad": "s", "RMSprop":"*", "Adam": "D", } 
x = np.arange(max_iterations)
for key in optimizers.keys():
    plt.plot(x, smooth_curve(train_loss[key]), marker=markers[key], markevery=100, label=key)
plt.xlabel("iterations")
plt.ylabel("loss")
plt.ylim(0, 1)
plt.legend()
plt.show()

3.SGD Le modèle de base de la méthode d'optimisation est «SGD», qui a été utilisé jusqu'au chapitre 5. スクリーンショット 2020-05-06 10.20.23.png

En regardant l'implémentation de SGD dans common / optimizer.py,

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] 

4.Momentum L'optimisation de SGD prend du temps, en particulier dans les premiers stades, car les mises à jour du gradient sont toujours constantes. C'est là qu'intervient «Momentum».

スクリーンショット 2020-05-06 10.22.35.png

«Momentum» est une méthode pour augmenter progressivement le degré de mise à jour du gradient tandis que la direction du gradient ne change pas. C'est juste une image de la balle roulant en fonction de la pente du sol, et $ \ alpha = 0,9 $ peut être considéré comme le frottement et la résistance de l'air du sol.

Pour exprimer l'image un peu plus concrètement, par exemple, en supposant que les résultats des quatre calculs de gradient sont les mêmes pour $ \ frac {\ partial L} {\ partial W} $, v est スクリーンショット 2020-05-04 14.24.01.png Vous pouvez voir que le degré de mise à jour du dégradé augmente progressivement jusqu'à -1,0, -1,9, -2,71, -3,439. En regardant l'implémentation de Momentum dans common / optimizer.py,

class Momentum:

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None
        
    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():                                
                self.v[key] = np.zeros_like(val)
                
        for key in params.keys():
            self.v[key] = self.momentum*self.v[key] - self.lr*grads[key] 
            params[key] += self.v[key]

5.Nesterov L'élan est susceptible de dépasser lorsque la direction du gradient est inversée après avoir augmenté le degré de mise à jour du gradient. C'est là que le Nesterov (également connu sous le nom de Momentum de Nestrov), qui est une modification partielle de l'élan, est introduit.

Nesterov change la position où le gradient est calculé à la position après la mise à jour du gradient, une longueur d'avance, au lieu de la position actuelle. Bien sûr, je ne connais pas la position exacte après la mise à jour du dégradé, mais je la remplacerai en trouvant le v approximatif en utilisant le dégradé actuel. On peut s'attendre à ce que cela supprime le dépassement. スクリーンショット 2020-05-04 19.34.07.png En regardant l'implémentation dans common / optimizer.py,

class Nesterov:

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None
        
    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.v[key] *= self.momentum
            self.v[key] -= self.lr * grads[key]
            params[key] += self.momentum * self.momentum * self.v[key]
            params[key] -= (1 + self.momentum) * self.lr * grads[key]

Comparons maintenant SGD, Momentum, Nesterov.

スクリーンショット 2020-05-05 19.14.14.png

Par rapport à «SGD», «Momentum» et «Nesterov» sont extrêmement améliorés à la fois en termes de vitesse de réduction de perte initiale et de taux de perte final. «Nesterov» est un pas mieux que «Momentum», et il semble que la variation de la perte soit légèrement inférieure.

6.AdaGrad ʻAda Grad` introduit deux idées importantes.

Le premier est l'idée de «taux d'apprentissage adaptatif» (adaptatif), qui stipule qu'un grand nombre de paramètres doivent être optimisés en fonction des paramètres, plutôt que d'être optimisés en une seule fois.

La seconde est l'idée de "décomposer le coefficient d'apprentissage" pour augmenter le taux d'apprentissage au début de l'apprentissage et diminuer le taux d'apprentissage au fur et à mesure que l'apprentissage progresse pour promouvoir l'apprentissage efficacement.

スクリーンショット 2020-05-06 10.25.44.png

Le taux d'apprentissage est corrigé en accumulant la somme des carrés du gradient sur h et en la multipliant par $ \ frac {1} {\ sqrt {h} + \ epsilon} $ lors de la mise à jour du gradient. En d'autres termes, le taux d'apprentissage des paramètres fortement mis à jour est progressivement réduit. Au fait, $ \ epsilon $ est un très petit nombre (pour éviter la division par zéro). En regardant l'implémentation de common / optimizer.py,

class AdaGrad:

    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None
        
    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

7.RMSprop RMSprop est une version améliorée de ʻAdaGrad` qui stocke la ** moyenne mobile exponentielle ** du carré du gradient en h (oubliez progressivement les résultats des calculs passés et intégrez de nouveaux résultats de calcul) et mettez à jour le gradient. Le taux d'apprentissage est corrigé en multipliant $ \ frac {1} {\ sqrt {h} + \ epsilon} $ lors de cette opération.

スクリーンショット 2020-05-06 10.28.25.png

En regardant l'implémentation de common / optimizer.py,

class RMSprop:

    def __init__(self, lr=0.01, decay_rate = 0.99):
        self.lr = lr
        self.decay_rate = decay_rate
        self.h = None
        
    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.h[key] *= self.decay_rate
            self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

8.Adam ʻAdam est le résultat de l'idée de tirer le meilleur parti de Momentum et de ʻAdaGrad.

スクリーンショット 2020-05-06 10.31.16.png

m est comme la moyenne mobile exponentielle de Momentum, et v est ʻAdaGrad` lui-même. Après cela, "m" et "v" sont utilisés dans une large mesure tandis que l'itère est petit, et le degré d'utilisation est affaibli lorsque l'itère augmente. La mise en œuvre se fait en modifiant légèrement la formule comme indiqué ci-dessous.

スクリーンショット 2020-05-06 10.02.53.png

En regardant l'implémentation de common / optimizer.py,

class Adam:

    def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.iter = 0
        self.m = None
        self.v = None
        
    def update(self, params, grads):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)
        
        self.iter += 1
        lr_t  = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)         
        
        for key in params.keys():
            self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])            
            params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)

Comparons maintenant ʻAdaGrad, RMSprop, ʻAdam.

![Capture d'écran 2020-05-05 19.06.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/4ba770be-3cfe-15a1-16bb- 030899994296.png)

Il n'y a pas de méthode d'optimisation qui donne les meilleurs résultats pour n'importe quelle tâche. Cette fois, «Ada Grad» a donné les meilleurs résultats.

La correction du taux d'apprentissage par la moyenne mobile exponentielle du carré du gradient de «RMSprop» était excessive pour cette tâche, et l'amplitude de la perte est devenue très grande, et le taux de perte final est également devenu élevé.

«Adam» est censé toujours montrer des performances stables avec la méthode d'optimisation qui doit être utilisée en premier pour le moment. Cette tâche n'est pas la meilleure, mais elle montre une transition honorifique de réduction des pertes des étudiants.

9. Initialisation du poids

Si la valeur initiale du poids est zéro, toutes les valeurs de poids seront mises à jour uniformément et l'expressivité sera perdue. Le type d'initialisation qui convient dépend du type de fonction d'activation.

Si la fonction sigmoïde, la fonction tanh, etc. sont symétriques et que la zone centrale peut être considérée comme une fonction linéaire, la distribution gaussienne avec $ \ sqrt {\ frac {1} {n}} $ comme écart type appelé «initialisation de Xavier» est dite optimale. Il est

Lors de l'utilisation de ReUL, on dit que la distribution gaussienne avec $ \ sqrt {\ frac {2} {n}} $ comme écart-type appelé «initialisation He» est optimale.

10.Dropout «Dropout» est une méthode pour supprimer le surapprentissage même dans un réseau hautement expressif en déconnectant des neurones sélectionnés au hasard pour chaque iter pendant l'apprentissage. En regardant le code d'implémentation,

class Dropout:

    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None

    #Propagation vers l'avant
    def forward(self, x, train_flg=True):
        #Au moment de l'apprentissage, créez un masque qui détermine s'il faut ou non se connecter et multipliez le signal de propagation vers l'avant par un masque.
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio
            return x * self.mask

        #N'appliquez pas de masque lors de l'inférence et couvrez tout le signal(1 - dropout_ratio)Multiplier
        else:
            return x * (1.0 - self.dropout_ratio)
    
    #Multipliez le signal de rétropropagation par masque
    def backward(self, dout):
        return dout * self.mask

A chaque session d'apprentissage, un nombre aléatoire uniforme et un seuil (dropout_ratio) sont utilisés pour créer un masque (True pour connectable, False pour non connectable) qui détermine s'il faut ou non se connecter. Masquez ensuite le signal de propagation vers l'avant (x * self.mask). De même, masquez le signal lors de la rétro-propagation.

Dans une image concrète, ça ressemble à ça,

スクリーンショット 2020-05-06 18.49.05.png

Au moment de l'inférence, le signal entier est multiplié par (1 - dropout_ratio) sans masquage, et seule l'amplitude du signal entier est ajustée.

11.Batch Normalization La normalisation par lots '' est une méthode annoncée en 2015, qui améliore la vitesse de convergence d'apprentissage, réduit le besoin de suppression '' et pondère initialement en normalisant chaque mini-lot de sorte qu'il ait une moyenne de 0 et une distribution de 1. Vous pouvez obtenir des effets tels que la réduction du besoin de conversion (robuste pour l'initialisation du poids). Voici l'algorithme. スクリーンショット 2020-05-06 14.05.00.png

12.Weight decay La décroissance de poids est une méthode de suppression du surapprentissage en imposant une pénalité pour avoir un poids important dans le processus d'apprentissage.

Lorsque le poids est W, en ajoutant $ \ frac {1} {2} \ lambda W ^ 2 $ à la fonction de perte, il est possible de supprimer l'augmentation du poids W, et cette méthode est ** L2 régularisée ** Est appelé. Si vous augmentez $ \ lambda $ ici, vous pouvez augmenter la pénalité. ![Capture d'écran 2020-05-06 14.31.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/ddbf678e-4b99-e2f8-a182- 76a1329147c5.png)

Au fait, dans la fonction de perte\lambda |W|Ce qui a été ajouté àRégularisation L1Est appelé.

Recommended Posts

[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] Deep Learning fait de zéro [Chapitre 5]
[Mémo d'apprentissage] Le Deep Learning fait de zéro [Chapitre 6]
Deep learning / Deep learning made from scratch Chapitre 7 Mémo
[Mémo d'apprentissage] Deep Learning fait de zéro [~ Chapitre 4]
Deep Learning from scratch Chapter 2 Perceptron (lecture du mémo)
Deep learning / Deep learning from scratch 2 Chapitre 4 Mémo
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 5 Mémo
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 7 Mémo
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
Deep learning / Deep learning from scratch 2 Chapitre 3 Mémo
Mémo d'apprentissage profond créé à partir de zéro
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 6 Mémo
Apprentissage profond à partir de zéro
Mémo d'auto-apprentissage "Deep Learning from scratch" (partie 12) Deep learning
"Deep Learning from scratch" Mémo d'auto-apprentissage (n ° 9) Classe MultiLayerNet
Deep Learning from scratch ① Chapitre 6 "Techniques liées à l'apprentissage"
[Mémo d'apprentissage] Apprentissage profond à partir de zéro ~ Mise en œuvre de l'abandon ~
Mémo d'auto-apprentissage «Deep Learning from scratch» (10) Classe MultiLayerNet
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
Application de Deep Learning 2 à partir de zéro Filtre anti-spam
Apprentissage profond à partir de zéro (calcul des coûts)
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
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 7
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
Mémo d'auto-apprentissage «Deep Learning from scratch» (n ° 18) One! Miaou! Grad-CAM!
Un amateur a trébuché dans le Deep Learning à partir de zéro.
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 2
Mémo d'auto-apprentissage "Deep Learning from scratch" (n ° 15) Tutoriel pour débutants TensorFlow
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
Écrivez vos impressions sur l'édition du framework Deep Learning 3 créée à partir de zéro
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
Django memo n ° 1 à partir de zéro
"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
Chapitre 2 Implémentation de Perceptron Ne découpez que les bons points de Deeplearning à partir de zéro
GitHub du bon livre "Deep Learning from scratch"
Résumé Python vs Ruby "Deep Learning from scratch"
Python vs Ruby «Deep Learning from scratch» Chapitre 2 Circuit logique par Perceptron
Python vs Ruby "Deep Learning from scratch" Chapitre 4 Implémentation de la fonction de perte
Chapitre 1 Introduction à Python Découpez uniquement les bons points de Deeplearning à partir de zéro
[Deep Learning from scratch] J'ai implémenté la couche Affine
Python vs Ruby "Deep Learning from scratch" Chapitre 3 Implémentation d'un réseau neuronal à 3 couches