[PYTHON] Deep learning / Deep learning made from scratch Chapitre 4 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 4. Pour exécuter le code, téléchargez le code complet depuis Github et utilisez le notebook jupyter dans ch04.

2. Apprendre un réseau neuronal à deux couches

À la fin du chapitre 4, il y a du code (train_neuralnet.py) qui forme un réseau de neurones à deux couches en calculant le gradient de paramètres par différenciation numérique. Cette fois, je vais exécuter ce code et ensuite passer par les détails. Cependant, le code de Github n'est pas le même, mais certaines modifications et ajouts sont effectués comme suit.

** 1) Correspondance avec une vitesse d'exécution lente ** Même si l'utilisation de la "différenciation numérique" prend énormément de temps, si l'affichage de la précision est tous les 600 iters (toutes les 1 époque), le deuxième calcul de précision sera affiché après quelques heures. Ainsi, afin de voir le résultat dans un peu plus d'une heure, réglez "l'affichage de précision sur" tous les 1 itères ", le" nombre d'exécution de 10000 à 100 "et le" taux d'apprentissage de 0,1 à 1,0 ".

** 2) Réduisez le nombre de codes d'appel externes pour améliorer la visibilité ** La particularité de Zero work est que le code déjà expliqué fait l'objet d'un appel externe autant que possible, et le code à afficher en une seule fois est exprimé de la manière la plus concise possible. Cependant, il est très déroutant d'appeler «two_layer_net.py» depuis «train_neuralnet.py» et «gradient.py» dans le «dossier commun» depuis «two_layer_net.py». Donc, je vais ajouter cette zone à train_neuralnet.py pour améliorer les perspectives.

Cependant, seul function.py dans le dossier commun est utilisé comme code externe (sigmoid, sofytmax, cross_entropy_error, etc.).

Exécutons d'abord le code suivant.

import sys, os
sys.path.append(os.pardir)  #Paramètres d'importation des fichiers dans le répertoire parent
from common.functions import *  #fonction dans le dossier commun.Configuré pour utiliser toutes les fonctions de py
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist

class TwoLayerNet:
    #Initialisation des paramètres
    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)

    #Propagation vers l'avant
    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
        
    #Calcul des pertes
    def loss(self, x, t):
        y = self.predict(x)        
        return cross_entropy_error(y, t)
    
    #Calcul de la précision
    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
        
    #Calcul du gradient
    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
    
    #Différenciation numérique
    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 #Restaurer la valeur
            it.iternext()           
        return grad  
            
#Lire les données
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

#Instancier TwoLayerNet
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)  

#Réglage initial
iters_num = 100  #Le nombre d'exécutions est passé de 10000 à 100
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 1   #Le taux d'apprentissage est de 0.1 → 1.Changer à 0
train_loss_list, train_acc_list, test_acc_list = [], [], []
iter_per_epoch = 1  #L'affichage de la précision passe de 1 époque à 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]
    
    #Calcul du gradient
    grad = network.numerical_gradient(x_batch, t_batch)
    
    #Mise à jour des paramètres
    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)

    #Affichage de la précision
    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)
        
        #afficher(iter et former_Ajouter une perte)
        print('[iter='+str(i)+'] '+'train_loss='+str(loss)+', '+'train_acc='+str(train_acc)+', '+'test_acc='+str(test_acc))

#Dessiner un graphique
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()

スクリーンショット 2020-04-29 16.40.37.png Il a fallu 75 minutes pour exécuter 100 litres sur mon Macbook air. Que diriez-vous de mettre le graphique de précision qui a exécuté 16epoch (= 9600iter) dans le texte même dans le cas de la différenciation numérique? Je pense que, mis à part cela, la différenciation numérique prend énormément de temps.

3. Classe TwoLayerNet

    #Initialisation des paramètres
    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)

C'est la partie qui n'est exécutée qu'une seule fois lors de l'instanciation d'une classe, et ici chaque paramètre est initialisé. np.random.randn () est la génération d'une distribution normale avec une moyenne de 0 et une variance de 1, et np.zeros () est la génération d'une matrice nulle. La taille de chaque matrice est la suivante. スクリーンショット 2020-04-29 17.32.04.png

    #Propagation vers l'avant
    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

C'est la partie de propagation vers l'avant. Lisez la matrice des poids «W1», «W2» et biais «b1», «b2» stockée dans les paramètres du dictionnaire et propagez-la en avant par opération de matrice.

    #Calcul des pertes
    def loss(self, x, t):
        y = self.predict(x)        
        return cross_entropy_error(y, t)

C'est la partie qui calcule la perte. Trouvez l'entropie croisée de y et les données d'enseignant t obtenues en appelant la propagation avant plus tôt. L'entropie croisée utilise les fonctions de function.py dans le dossier commun. Pour le moment,

スクリーンショット 2020-04-30 08.44.59.png

y [np.arange (batch_size), t] signifie découper l'élément correspondant de y en fonction de l'étiquette de réponse correcte dans l'ordre de t.

    #Calcul de la précision
    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

C'est la partie du calcul de la précision. Y est déduit des données d'entrée x, les index de y et l'étiquette de réponse correcte t sont retirés respectivement, et le nombre lorsque les deux index sont identiques est divisé par le nombre de données de x pour calculer la précision.

    #Calcul du gradient
    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

C'est la partie du calcul du gradient. Utilisez la fonction numerical_gradient2 qui apparaît plus tard pour résumer les résultats du calcul du gradient dans un format de dictionnaire. Les arguments sont «l'expression de la fonction de perte» qui trouve l'entropie croisée basée sur x et le libellé de réponse correct t, et la «spécification du paramètre».

Au fait, l'original est grades ['W1'] = numerical_gradient (loss_W, self.params ['W1']), c'est ça? Est-ce une utilisation récursive? Au début, j'ai pensé, mais ce n'est pas le cas.

Dans l'original, TwoLayerNet.py a la description de common.gradient import numerical_gradient au début, et la fonction numerical_gradient dans gradient.py dans le dossier commun est importée, alors utilisez-la. Voici ce que vous faites. Il y a moins d'erreurs si vous changez le nom ici, donc j'ai changé le nom en numerical_gradient2.

    #Différenciation numérique
    def numerical_gradient2(f, x):   
        h = 1e-4 # 0.0001

        #Gradient de matrice zéro qui stocke le résultat du calcul du dégradé(Taille spécifiée par x)Préparer
        grad = np.zeros_like(x)

        #Indexation séquentielle de la matrice x(Spécifiez la ligne et la colonne)Faire
        it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
        
        #Continuez jusqu'à ce que toutes les lignes et colonnes de la matrice x soient spécifiées
        while not it.finished:
            
            idx = it.multi_index  #à idx(ligne,Colonne)Remplacer
            tmp_val = x[idx]  #tmp la valeur de x spécifiée par idx_Enregistrer dans val
            
            #Calculez la perte en ajoutant un petit nombre h et en propageant vers l'avant
            x[idx] = tmp_val + h
            fxh1 = f(x) # f(x+h)

            #Calculer la perte en soustrayant un petit nombre h et en se propageant vers l'avant
            x[idx] = tmp_val - h 
            fxh2 = f(x) # f(x-h)

            #Calculer la pente de l'indice correspondant
            grad[idx] = (fxh1 - fxh2) / (2*h)

            x[idx] = tmp_val #Restaurer la valeur enregistrée
            it.iternext()  #Exécutez l'index suivant
        return grad  

C'est la partie qui effectue la différenciation numérique. En termes simples, pour chacun des `paramètres, la petite valeur h est ajoutée pour calculer la propagation / perte directe, et la petite valeur h est soustraite pour calculer la propagation / perte directe. J'ai décidé. ''

Cette fois, le nombre de paramètres est de 784 * 50 = 39200 pour W1, 50 * 10 = 500 pour W2, 50 pour b1 et 10 pour b2, soit un total de 39760. Étant donné que le calcul de la propagation / perte aller est effectué deux fois pour chaque paramètre, 79 520 calculs de propagation / perte aller sont effectués pour chaque mise à jour des paramètres du réseau. L'opération est extrêmement lente.

Maintenant, j'ai un np.nditer que je ne connais pas dans le code. Normalement, la spécification d'index de la matrice utilise la boucle for pour la spécification de ligne + la boucle for et la double boucle pour la spécification de colonne, mais ce np.nditer ne peut le faire qu'une seule fois.

Comme je l'ai expliqué précédemment, ce code se trouve à l'origine dans gradient.py dans le dossier commun. Le nom de la fonction d'origine est déroutant avec numerical_gradient (identique au nom de la fonction dans le calcul du gradient), donc cette fois il est changé en numerical_gradient2.

4. Partie du corps

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

#Instancier TwoLayerNet
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)  

#Réglage initial
iters_num = 100  #Le nombre d'exécutions est passé de 10000 à 100
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 1   #Le taux d'apprentissage est de 0.1 → 1.Changer à 0
train_loss_list, train_acc_list, test_acc_list = [], [], []
iter_per_epoch = 1  #L'affichage de la précision passe de 1 époque à 1 iter

Après avoir chargé les données, instanciez la classe TwoLayerNet. Puisque input_size = 784, hidden_size = 50, output_size = 10, le modèle et le fonctionnement de la matrice sont les suivants. スクリーンショット 2020-04-30 12.56.08.png

for i in range(iters_num):

    #Obtenez des données de mini-lots
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]

    #Calcul du gradient
    grad = network.numerical_gradient(x_batch, t_batch)

    #Mise à jour des paramètres
    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)

    #Affichage de la précision
    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)

        #afficher(iter et former_Ajouter une perte)
        print('[iter='+str(i)+'] '+'train_loss='+str(loss)+', '+'train_acc='+str(train_acc)+', '+'test_acc='+str(test_acc))

Tout d'abord, préparez les données pour l'apprentissage par mini-lots. np.random.choice (train_size, batch_size) attribue le résultat de la sélection aléatoire de 100 des 60000 données de train (quel nombre a été sélectionné) à batch_mask, et l'utilise pour les données d'entraînement. Et obtenez la bonne étiquette de réponse.

Ensuite, calcul du gradient. Appelez la fonction numerical_gradient de la classe TwoLayerNet et, comme expliqué précédemment, trouvez le gradient par différenciation numérique pour chaque paramètre.

Il met ensuite à jour les paramètres avec le gradient calculé, calcule et enregistre la perte et affiche la précision.

#Dessiner un graphique
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()

Une fois la formation terminée, le graphique de précision s'affiche. Cela n'a besoin d'aucune explication.

Recommended Posts

Deep learning / Deep learning from scratch 2 Chapitre 4 Mémo
Deep learning / Deep learning made from scratch Chapitre 3 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
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 6 Mémo
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)
Apprentissage amélioré pour apprendre de zéro à profond
Mémo d'auto-apprentissage "Deep Learning from scratch" (partie 12) Deep learning
Mémo d'auto-apprentissage "Deep Learning from scratch" (glossaire illisible)
"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
Apprentissage profond à partir de zéro 1 à 3 chapitres
Mémo d'auto-apprentissage «Deep Learning from scratch» (n ° 19) Augmentation des données
Mémo d'apprentissage Python pour l'apprentissage automatique par Chainer du chapitre 2
Deep Learning 2 from scratch 1.3 Traitement du langage naturel 1.3 Résumé
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
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 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 ❷ 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.
Mémo d'apprentissage profond créé à partir de zéro
L'apprentissage en profondeur
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
Tutoriel d'apprentissage en profondeur de la construction d'environnement
"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
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
Apprentissage profond à partir de zéro (propagation vers l'avant)
Apprentissage profond / Apprentissage profond à partir de zéro 2-Essayez de déplacer GRU
Alignement d'image: du SIFT au deep learning
"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
Apprentissage profond à partir des bases mathématiques (pendant la fréquentation)
Note d'étude LPIC201
Mémo d'apprentissage Django
Commencer l'apprentissage en profondeur