Introduction au Deep Learning ~ Règles d'apprentissage ~

Personne cible

L'article précédent était ici. Dans cet article, nous examinerons la mise en œuvre des règles d'apprentissage. Cependant, le corps principal a déjà été installé sur ici, alors jetez un œil.

Le prochain article est ici

table des matières

Règles d'apprentissage

Tout d'abord, je penserai avec scalaire comme d'habitude. Les objets Neuron ont des variables $ w et b $.

y = \sigma(xw + b)

Ici, si l'entrée $ x $ est considérée comme une constante

y = f(w, b) = \sigma(xw + b)

Peut être écrit comme En d'autres termes, le but de la règle d'apprentissage est de changer les valeurs de $ w et b $ de manière appropriée pour les rapprocher de la valeur cible $ y ^ {\ star} $. </ font>

Apprendre la théorie du droit

Regardons cela théoriquement.

y = f(w, b) = \sigma(wx + b)

Dans

\begin{align}
  y &= y^{\star} = 0.5 \\
  x &= x_0 = 1
\end{align}

Ensuite, si la fonction d'activation est la fonction sigmoïde et que l'espace des paramètres de $ w et b $ est illustré, ce sera comme suit. Loss_space.png <détails>

code </ summary>

show_loss_space.py


%matplotlib nbagg
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D


x_0 = 1
y_star = 0.5
sigma = lambda x: 1/(1+np.exp(-x))

w = np.arange(-2, 2, 1e-2)
b = np.arange(-2, 2, 1e-2)
W, B = np.meshgrid(w, b)
y = 0.5*(sigma(x_0*W + B) - y_star)**2

elevation = np.arange(np.min(y), np.max(y), 1/2**8)

fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
ax.set_xlabel("w")
ax.set_ylabel("b")
ax.set_zlabel("loss")
ax.view_init(60)
ax.grid()
ax.plot_surface(W, B, y, cmap="autumn", alpha=0.8)
ax.contour(W, B, y, cmap="autumn", levels=elevation, alpha=0.8)
fig.suptitle("Loss space")
fig.show()
fig.savefig("Loss_space.png ")

La figure montre l'espace de perte lorsque l'erreur quadratique est utilisée pour la fonction de perte. La règle d'apprentissage est que la valeur initiale aléatoire $ w_0, b_0 $ s'approche progressivement de $ w ^ {\ star}, b ^ {\ star} $, ce qui donne la valeur optimale $ y ^ {\ star} $. C'est le but de. </ font> À ce stade, la règle d'apprentissage est la ** méthode de descente de gradient **, qui est une évolution de la ** méthode de descente la plus raide **. La méthode de descente de gradient est une méthode de descente d'une pente utilisant ** gradient (différenciation partielle) en un certain point de chaque paramètre. Ici contient des formules et des codes pour chaque méthode. De plus, ici montre la descente dans certains espaces d'exploration. <img src=https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F640911%2F6f55cfbc-4a9f-d4fe-c70e-49dca7cbf683.gif?ixlib=rb-1.2.2&auto=format&gif-q=60&q=75&s=02b37020417dbead312cc8c82f5eac7e>

Cet article traite du SGD le plus simple. La formule de SGD est la suivante.

\begin{align}
  g_t &= \nabla_{w_t}\mathcal{L}(w_t, b_t) \\
  \Delta w_t &= - \eta g_t \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Cette formule ne répertorie que $ w $, mais vous pouvez voir que si vous la remplacez par $ b $, la règle d'apprentissage du biais sera la même. Pensez à $ \ mathcal {L} (w_t, b_t) $ comme une fonction de perte et $ \ nabla_ {w_t} $ comme un différentiel partiel pour $ w_t $ (la formule ci-dessus est une représentation matricielle). </ font> Exprimer la formule ci-dessus en japonais

--Trouver le gradient par différenciation partielle --Calculer la quantité de mouvement

  • En mouvement

On dirait. C'est simple. Regardons de plus près.

La première "recherche du gradient par différenciation partielle" utilise la méthode de propagation de retour d'erreur introduite dans Back Propagation. C'est très bien. "Déplacer" est également littéral.

Concernant la partie "calcul de la quantité de mouvement"

  1. Pourquoi ajouter un signe moins
  2. Pourquoi multiplier $ \ eta \ ll 1 $ sans utiliser le dégradé tel quel

Je voudrais parler de deux points.

Tout d'abord, en ce qui concerne 1., je pense que c'est facile à comprendre si vous y réfléchissez concrètement. y=x^2.png Par exemple, la pente au point de $ (x, y) = (1, 1) $ est $ 2 $, mais la direction que vous voulez déplacer est la direction moins, n'est-ce pas? Bien sûr, l'inverse est également vrai. Par conséquent, la direction et le dégradé que vous souhaitez déplacer sont toujours opposés, ils sont donc négatifs. Quant à 2., comme vous pouvez le voir sur le graphique, si vous utilisez la pente $ 2 $ telle quelle et définissez $ \ Delta x = -2 $ etc., ce sera $ x = -1 $ et il passera la valeur optimale. .. </ font> y=x^2_move.png Par conséquent, nous multiplions le coefficient $ \ eta \ ll 1 $ appelé taux d'apprentissage pour limiter la quantité de mouvement afin qu'elle tombe progressivement vers la valeur optimale. Ce taux d'apprentissage est une valeur appelée ** hyperparamètre **, et il existe de nombreuses règles d'apprentissage que les humains doivent concevoir pour cette partie. Dans la plupart des cas, l'utilisation des valeurs par défaut indiquées dans les articles fonctionnera, mais en fonction du problème que vous souhaitez résoudre, vous devrez peut-être expérimenter.

Implémentation des règles d'apprentissage

Eh bien, mis à part les détails, implémentons-le pour le moment. La destination de mise en œuvre est comme d'habitude [baselayer.py](https://qiita.com/kuroitu/items/884c62c48c2daa3def08#%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC % E3% 83% A2% E3% 82% B8% E3% 83% A5% E3% 83% BC% E3% 83% AB% E3% 81% AE% E3% 82% B3% E3% 83% BC% E3 % 83% 89% E6% BA% 96% E5% 82% 99).

baselayer.py

baselayer.py


    def update(self, **kwds):
        """
Mise en œuvre de l'apprentissage des paramètres
        """
        dw, db = self.opt.update(self.grad_w, self.grad_b, **kwds)
        
        self.w += dw
        self.b += db

La partie de self.opt.update (self.grad_w, self.grad_b, ** kwds) est lancée à ici. Voici le code SGD à titre d'exemple.

optimizers.py

optimziers.py


import numpy as np


class Optimizer():
    """
Une super classe héritée de la méthode d'optimisation.
    """
    def __init__(self, *args, **kwds):
        pass


    def update(self, *args, **kwds):
        pass


class SGD(Optimizer):
    def __init__(self, eta=1e-2, *args, **kwds):
        super().__init__(*args, **kwds)

        self.eta = eta


    def update(self, grad_w, grad_b, *args, **kwds):
        dw = -self.eta*grad_w
        db = -self.eta*grad_b
        return dw, db

Le contenu du code est conforme à la formule présentée ci-dessus. Reçoit le gradient concernant $ w et b $ de l'extérieur, et selon la règle d'apprentissage, le multiplie par $ - \ eta $ pour déterminer la quantité de mouvement et le renvoyer. </ font> L'objet de calque reçoit cette quantité de mouvement et met à jour ses propres paramètres.

Eh bien, c'est tout pour cette fois. Vous vous demandez peut-être: "Quoi? Dans le cas d'une file d'attente?" En fait, le code est exactement le même pour les matrices. [optimizers.py](https://qiita.com/kuroitu/items/36a58b37690d570dc618#%E5%AE%9F%E8%A3%85%E3%82%B3%E3%83%BC%E3%83%89 Même si vous regardez% E4% BE% 8B), vous ne trouvez pas le calcul du produit matriciel. La raison est naturelle quand on y pense, mais même si on apprend avec un mini-lot, le gradient qui s'écoule doit être unique à chaque paramètre, et il est nécessaire de calculer en faisant intervenir les gradients d'autres paramètres. Parce qu'il n'y a pas. Donc, cette fois, si vous y pensez avec un scalaire et que vous l'implémentez, vous pouvez le calculer avec une matrice de la même manière.

Implémentation de la méthode __init __

Enfin, faisons en sorte que l'objet layer ait l'optimiseur ʻopt avec la méthode __init __`.

__init__.py


    def __init__(self, *, prev=1, n=1, 
                 name="", wb_width=1,
                 act="ReLU", err_func="square", opt="Adam",
                 act_dict={}, opt_dict={}, **kwds):
        self.prev = prev  #Nombre de sorties de la couche précédente=Nombre d'entrées dans cette couche
        self.n = n        #Nombre de sorties dans cette couche=Nombre d'entrées vers la couche suivante
        self.name = name  #Le nom de cette couche
        
        #Définir le poids et le biais
        self.w = wb_width*np.random.randn(prev, n)
        self.b = wb_width*np.random.randn(n)
        
        #Fonction d'activation(classe)Avoir
        self.act = get_act(act, **act_dict)
        
        #Fonction de perte(classe)Avoir
        self.errfunc = get_errfunc(err_func)
        
        #Optimiseur(classe)Avoir
        self.opt = get_opt(opt, **opt_dict)

en conclusion

La prochaine fois, je présenterai la localisation de la fonction d'activation et de l'optimiseur, ainsi que la fonction de perte.

Série d'apprentissage en profondeur

Recommended Posts