[PYTHON] Deep Learning from scratch 4.4.2 Gradient pour les réseaux de neurones La question sur la fonction numerical_gradient a été résolue.

Contenu de cet article

Le contenu est ["Deep Learning from scratch"](https://www.amazon.co.jp/%E3%82%BC%E3%83%AD%E3%81%8B%E3%82%89%E4% BD% 9C% E3% 82% 8B Apprentissage en profondeur-% E2% 80% 95 Python% E3% 81% A7% E5% AD% A6% E3% 81% B6% E3% 83% 87% E3% 82% A3% E3 % 83% BC% E3% 83% 97% E3% 83% A9% E3% 83% BC% E3% 83% 8B% E3% 83% B3% E3% 82% B0% E3% 81% AE% E7% 90 % 86% E8% AB% 96% E3% 81% A8% E5% AE% 9F% E8% A3% 85-% E6% 96% 8E% E8% 97% A4-% E5% BA% B7% E6% AF % 85 / dp / 4873117585) 4.4.2 Gradient pour le réseau de neurones (par p.110). La question étant résolue, j'écrirai un article.

Question

Ce que je me demandais, c'était le code au bas de la page 111. (Suivant)

>>> def f(W):
... return net.loss(x, t)
...
>>> dW = numerical_gradient(f, net.W)
>>> print(dW)
[[ 0.21924763 0.14356247 -0.36281009]
[ 0.32887144 0.2153437 -0.54421514]]

J'ai défini la fonction f et je l'ai transmise comme argument à la fonction numerical_gradient définie peu avant ce livre. Lorsque j'ai changé le deuxième argument de cette fonction numerical_gradient en une valeur appropriée, la valeur de dW a changé. (Suivant)

#Net comme deuxième argument.Spécifiez W.(net.Pour W, p dans ce livre.Voir le commentaire du 110.)
>>> dW = numerical_gradient(f, net.W) 
>>> print(dW)
[[ 0.06281915  0.46086202 -0.52368118]
 [ 0.09422873  0.69129304 -0.78552177]]

#Stockez le tableau numpy dans a et spécifiez-le comme deuxième argument.
>>> a = np.array([[0.2, 0.1, -0.3],
         [0.12, -0.17, 0.088]])
>>> dW = numerical_gradient(f, a)
>>> print(dW)
[[0. 0. 0.]
 [0. 0. 0.]]

Je n'ai pas compris pourquoi la valeur de «dW» a changé. Cet article est la réponse à cette question.

Pourquoi demandé

Je vais expliquer pourquoi je me demandais pourquoi la valeur de dW avait changé.

Point 1

Tout d'abord, la valeur de retour de cette fonction f n'a rien à voir avec la valeur de l'argument W. C'est parce que «W» n'apparaît pas après «return» dans la fonction «f». Donc, quelle que soit la valeur que vous modifiez l'argument de la fonction f`` W, la valeur de retour ne changera pas du tout. (Voir ci-dessous)

#Spécifiez 3 comme argument de la fonction f.
>>> f(3) 
2.0620146712373737


#fonction net to f.Spécifiez W.
>>> f(net.W) 
2.0620146712373737


#Définissez un tableau numpy et attribuez-le à un. Comparez quand a est passé à la fonction f et quand 3 est passé.
>>> a = np.array([[0.2, 0.1, -0.3], 
         [0.12, -0.17, 0.088]]) 
>>> f(a) == f(3) 
True

Point 2

Comme autre point, je présenterai la fonction numerical_gradient. (Suivant)

def numerical_gradient(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

Cette fonction renvoie le grad défini dans la fonction. Si vous suivez du bas du code comment ce grad est dérivé, Vous pouvez trouver le code grad [idx] = (fxh1 --fxh2) / (2 * h). Alors, que sont «fxh1» et «fxh2»? Vous pouvez trouver le code fxh1 = f (x) fxh2 = f (x) `.

Résumé des points 1 et 2

A partir du point 2, la valeur de retour de la fonction `` numerical_gradient 'grad peut être attribuée à la valeur de f (x). A partir du point 1, la fonction f renvoie une valeur constante quelle que soit la valeur de l'argument. À partir des points 1 et 2, quelle que soit la valeur attribuée au deuxième argument x de la fonction numerical_gradient J'ai trouvé étrange que la valeur de retour de la fonction numerical_gradient change.

Solution

Tout d'abord, examinons de plus près la fonction numerical_gradient. Et j'expliquerai la fonction f plus en détail.

En savoir plus sur la fonction numerical_gradient

Un petit ajustement du code numerical_gradient. Plus précisément, sous fxh1 = f (x) fxh2 = f (x) respectivement Entrez print (fxh1) print (fxh2). (Suivant)

def numerical_gradient(f, x):
    h = 1e-4 
    grad = np.zeros_like(x)
    
    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index
        print('idx:', idx)
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x) # f(x+h)
        print('fxh1:', fxh1) # print(fxh1)Entrer
        
        x[idx] = tmp_val - h 
        fxh2 = f(x) # f(x-h)
        print('fxh2:', fxh2) # print(fxh2)Entrer
        
        grad[idx] = (fxh1 - fxh2) / (2*h)
        
        x[idx] = tmp_val 
        it.iternext()   
        
    return grad

Maintenant, déplaçons le code en changeant le deuxième argument.

Remplacez le deuxième argument par net.W

>>> dW = numerical_gradient(f, net.W)
fxh1: 2.062020953321506
fxh2: 2.0620083894906935
fxh1: 2.062060757760379
fxh2: 2.061968585355599
fxh1: 2.061962303319411
fxh2: 2.062067039554999
fxh1: 2.062024094490122
fxh2: 2.062005248743893
fxh1: 2.062083801262337
fxh2: 2.0619455426551796
fxh1: 2.061936119510309
fxh2: 2.06209322386368

Remplacez le deuxième argument par votre propre tableau numpy ʻa`

>>> a = np.array([[0.2, 0.1, -0.3],
         [0.12, -0.17, 0.088]])
>>> dW = numerical_gradient(f, a)
fxh1: 2.0620146712373737
fxh2: 2.0620146712373737
fxh1: 2.0620146712373737
fxh2: 2.0620146712373737
fxh1: 2.0620146712373737
fxh2: 2.0620146712373737
fxh1: 2.0620146712373737
fxh2: 2.0620146712373737
fxh1: 2.0620146712373737
fxh2: 2.0620146712373737
fxh1: 2.0620146712373737
fxh2: 2.0620146712373737

Pour ceux qui ont assigné «net.W» au deuxième argument, les valeurs sont légèrement différentes entre «fxh1» et «fxh2». D'un autre côté, lorsque vous remplacez votre propre tableau numpy ʻa, fxh1 et fxh2` ont la même valeur. Pourquoi? A partir de maintenant, j'expliquerai en considérant le cas où «net.W» est entré dans le deuxième argument.

Regardons de plus près la fonction numerical_gradient. Il y a le code suivant au milieu. Ce code change le numéro d'index de ʻidx, récupère le x` de ce numéro d'index, et Un petit «h» est ajouté au «x» extrait et affecté à la fonction «f».

it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index #Changer le numéro d'index de idx
        tmp_val = x[idx]     #Retirez le x de ce numéro d'index
        x[idx] = tmp_val + h #Ajouter un petit h au x extrait
        fxh1 = f(x) # f(x+h) #Affecté à la fonction f.

La valeur de retour de la fonction «f» a-t-elle changé en raison de l'ajout d'un petit «h» à «x»? Cependant, le fait que la valeur de retour de la fonction f ne change pas en raison de changements dans les arguments est montré au point 1 de" Pourquoi vous êtes-vous demandé? "

En fait, il y a une partie qui a changé en ajoutant un petit «h» à «x». Le x ici est le net.W assigné au deuxième argument de la fonction numerical_gradient. Après avoir ajouté un petit «h» à «net.W», il est passé à l'argument de la fonction «f». La partie suivante de la fonction numerical_gradient montrée précédemment.

x[idx] = tmp_val + h #Ajouter un petit h au x extrait
fxh1 = f(x)          #Affecté à la fonction f.

L'important ici est l'ordre dans lequel la fonction f est appelée après que le net.W a changé. Comment le changement de «net.W» affecte-t-il la fonction «f»?

Expliquez la fonction f plus en détail

Voyons comment changer net.W affecte la fonction f. La fonction «f» est illustrée ci-dessous.

def f(W):
    return net.loss(x, t)

La fonction loss qui apparaît dans la fonction f est définie dans la classe simpleNet définie à la p.110 de ce manuel. La classe simpleNet est illustrée ci-dessous.

import sys, os
sys.path.append(os.pardir)  
import numpy as np
from common.functions import softmax, cross_entropy_error
from common.gradient import numerical_gradient


class simpleNet:
    def __init__(self):
        self.W = np.random.randn(2,3)

    def predict(self, x):
        return np.dot(x, self.W)

    def loss(self, x, t):
        z = self.predict(x)
        y = softmax(z)
        loss = cross_entropy_error(y, t)

        return loss

Vous verrez la fonction loss en bas de simpleNet. À l'intérieur de la fonction «perte» se trouve la fonction «prédire». La fonction «prédire» est définie juste au-dessus de la fonction «perte». Si vous regardez de plus près la fonction predire, vous verrez le paramètre de poids W.

À la fin de "En savoir plus sur la fonction numerical_gradient", quel effet le changement de net.W a-t-il sur la fonction f? La réponse est ici. En changeant «net.W», le paramètre de poids «W» de la fonction «prédire» appelée par la fonction «perte» dans la fonction «f» a changé. Ensuite, bien sûr, la valeur de retour de la fonction loss changera.

Résumé

L'explication est enfin terminée.

Revenons à la fonction numerical_gradient. La fonction numerical_gradient est à nouveau représentée ci-dessous.

def numerical_gradient(f, x):
    h = 1e-4 
    grad = np.zeros_like(x)

    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index
        print('idx:', idx)
        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)
        print('fxh2:', fxh2)

        grad[idx] = (fxh1 - fxh2) / (2*h)

        x[idx] = tmp_val 
        it.iternext()   

    return grad

Comme mentionné ci-dessus, la valeur de retour de la fonction «loss» dans la fonction «f» change en raison du changement de «net.W». Dans ce code, l'ajout d'un petit «h» à «x» («net.W») a changé la fonction «f», et la valeur de «fxh1» a changé. La même chose s'applique à la suivante «fxh2». Puis passée au code suivant, la fonction numerical_gradient imprime la valeur de retour. Cela a résolu la première question que j'ai posée.

Le point important est ● Supprimez que le deuxième argument de la fonction numerical_gradient, x, soit net.W. ● La valeur de retour de la fonction f a changé à mesure que le net.W a changé.

Continuons à lire "Deep Learning from scratch"!

Recommended Posts

Deep Learning from scratch 4.4.2 Gradient pour les réseaux de neurones La question sur la fonction numerical_gradient a été résolue.
[Deep Learning from scratch] Principales méthodes de mise à jour des paramètres pour les réseaux neuronaux
[Deep Learning from scratch] À propos des couches requises pour implémenter le traitement de rétropropagation dans un réseau neuronal
[Deep Learning from scratch] À propos de l'optimisation des hyper paramètres
Préparez l'environnement pour le livre O'Reilly "Deep Learning from scratch" avec apt-get (Debian 8)
[Deep Learning from scratch] Accélération du réseau de neurones J'ai expliqué le traitement de la propagation arrière
[Deep Learning from scratch] Valeur initiale du poids du réseau neuronal utilisant la fonction sigmoïde
[Deep Learning from scratch] J'ai implémenté la couche Affine
Apprentissage profond à partir de zéro
Deep Learning from scratch 4.3.3 Dessinez un vecteur de gradient de votre propre fonction en vous basant sur l'exemple de code de différenciation partielle.
[Deep Learning from scratch] Poids initial du réseau neuronal lors de l'utilisation de la fonction Relu
Chapitre 3 Réseau de neurones Ne découpez que les bons points de Deeplearning à partir de zéro
[Deep Learning from scratch] J'ai essayé d'expliquer la confirmation du gradient d'une manière facile à comprendre.
Créez un environnement pour "Deep Learning from scratch" avec Docker
Apprentissage profond à partir de zéro 1 à 3 chapitres
Version Lua Apprentissage profond à partir de zéro Partie 6 [Traitement d'inférence de réseau neuronal]
Apprentissage profond à partir de zéro (calcul des coûts)
Mémo d'apprentissage profond créé à partir de zéro
Réaliser la construction d'environnement pour "Deep Learning from scratch" avec docker et Vagrant
[Deep Learning from scratch] Implémentation de couche de la fonction softmax à l'erreur d'entropie croisée
Python vs Ruby "Deep Learning from scratch" Chapitre 4 Implémentation de la fonction de perte
[Mémo d'apprentissage] Le Deep Learning fait de zéro [Chapitre 7]
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 / Deep learning made from scratch Chapitre 6 Mémo
[Mémo d'apprentissage] Le Deep Learning fait de zéro [Chapitre 6]
"Deep Learning from scratch" avec Haskell (inachevé)
Deep learning / Deep learning made from scratch Chapitre 7 Mémo
[Windows 10] Construction de l'environnement "Deep Learning from scratch"
Enregistrement d'apprentissage de la lecture "Deep Learning from scratch"
À propos du traitement d'expansion des données pour l'apprentissage en profondeur
Mémo d'auto-apprentissage "Deep Learning from scratch" (partie 12) Deep learning
[Mémo d'apprentissage] Deep Learning fait de zéro [~ Chapitre 4]
Python vs Ruby "Deep Learning from scratch" Chapitre 3 Implémentation d'un réseau neuronal à 3 couches
Deep Learning from scratch La théorie et la mise en œuvre de l'apprentissage profond appris avec Python Chapitre 3
[Pour les débutants] Après tout, qu'est-ce qui est écrit dans Deep Learning fait à partir de zéro?