Introduction au Deep Learning ~ Propagation vers l'avant ~

Personne cible

Ceci est un article de [Deep Learning Series](#Deep Learning Series). L'article précédent était ici. Ici, nous allons d'abord expliquer la théorie de la propagation directe en scalaire, puis l'étendre à une matrice. Il sera ajouté ou modifié au code introduit dans l'article précédent, veuillez donc d'abord récupérer le code de l'article précédent ~

table des matières

Propagation vers l'avant en scalaire

Cette section décrit la théorie et l'implémentation de la propagation directe en scalaires (nombres réels). Cependant, c'est presque comme déjà mentionné dans Basics.

Théorie de la propagation directe en scalaire

La première est la théorie. sinple_neuron_model.png Commençons par ce modèle neuronal. Lorsque cela est formulé, $ f (x) = \ sigma (wx + b) $ est obtenu comme décrit dans [ici](https://qiita.com/kuroitu/items/221e8c477ffdd0774b6b#activation function). est. En passant la fonction d'activation $ \ sigma (•) $, elle est rendue non linéaire et a le sens de couches superposées. Alors, à quoi ressemblerait cette opération dans un graphe de calcul? neuron_object.png

Je me sens comme cela. Jusqu'à présent, l'entrée n'était que de $ x $ et d'autres éléments étaient omis, mais dans le graphique de calcul, ** poids $ w $ ** et ** biais (seuil) $ b $ ** sont également décrits correctement et actifs. Il peut être réécrit en sortie via une fonction de conversion. Ces variables $ x, w, b, y $ sont les variables que devraient avoir les objets neurones. De plus, en ce qui concerne les fonctions d'activation, Python peut stocker des fonctions et des classes en tant qu'objets dans des variables, il semble donc bon que l'implémentation puisse être encapsulée en l'incluant dans un objet neurone. C'est tout pour la théorie de la propagation directe dans les scalaires. C'est simple et agréable ~.

Implémentation de propagation directe en scalaire

Mettons-le en œuvre. Le code à implémenter est [baselayer.py](https://qiita.com/kuroitu/items/884c62c48c2daa3def08#layer préparation du code du module).

baselayer.py

baselayer.py


    def forward(self, x):
        """
Mise en œuvre de la propagation directe
        """
        #Souvenez-vous de votre entrée
        self.x = x.copy()

        #Propagation vers l'avant
        y = self.w * x + self.b
        self.y = self.act.forward(y)
        
        return self.y
Il contient l'entrée car il est nécessaire pour la rétropropagation. De plus, la fonction d'activation n'a pas encore été abordée, mais je pense à la mettre en œuvre pour qu'elle puisse être utilisée comme décrit ci-dessus. L'autre méthode que j'ai est «en arrière», qui calcule le différentiel. Selon la chose, il peut être nécessaire d'avoir une méthode ʻupdate`.

La propagation vers l'avant elle-même est extrêmement facile, n'est-ce pas? Selon la formule. C'est tout pour l'implémentation scalaire. Ensuite, considérons l'implémentation avec un vecteur (plutôt qu'une matrice).

Propagation directe dans une matrice

Ensuite, considérez la propagation vers l'avant dans une matrice. La matrice est stricte si vous ne connaissez pas le concept de produit matriciel de l'algèbre linéaire, donc si vous ne le connaissez pas, je l'expliquerai brièvement dans [ici](# About Matrix operation).

Théorie de la propagation directe dans une matrice

Tout d'abord, pensons à un objet de couche qui ressemble à une pile de deux objets neurones. layer_object.png Je ne pouvais pas penser à une bonne expression de la figure, alors je vais l'expliquer un instant. Tout d'abord, les flèches noires acceptent la compréhension que ce sont des objets neuronaux. Ce sont les flèches d'autres couleurs. La flèche bleu clair </ font> représente la synapse qui relie le neurone supérieur au neurone inférieur. Multipliez le poids bleu clair $ w_ {1, 2} $ </ font> lorsqu'il passe par le nœud de multiplication du milieu et joignez-le au nœud d'addition inférieur. La même chose est vraie pour flèche rouge </ font>. Multipliez le poids rouge $ w_ {2, 1} $ </ font> lorsqu'il passe par le nœud de multiplication du milieu et joignez-le au nœud d'addition supérieur. L'entrée du nœud d'addition est devenue trois, mais elle peut être décomposée en deux entrées en utilisant plusieurs nœuds d'addition, veuillez donc considérer qu'elle est omise. tri_add_cal_graph.png Suivons cela avec une formule mathématique. Dans ce qui suit, pour simplifier, $ \ sigma_i (•) $ est une fonction égale. Tout d'abord, je vais l'écrire.

y_1 = w_{1, 1}x_1 + w_{2, 1}x_2 + b_1 \\
y_2 = w_{1, 2}x_1 + w_{2, 2}x_2 + b_2

Je pense que cela en soi est évident si vous regardez la figure. Exprimons cela dans une représentation matricielle.

\left(
  \begin{array}{c}
    y_1 \\
    y_2
  \end{array}
\right)
=
\left(
  \begin{array}{cc}
    w_{1, 1} & w_{2, 1} \\
    w_{1, 2} & w_{2, 2}
  \end{array}
\right)
\left(
  \begin{array}{c}
    x_1 \\
    x_2
  \end{array}
\right)
+
\left(
  \begin{array}{c}
    b_1 \\
    b_2
  \end{array}
\right)

Je me sens comme cela. Si vous comprenez le produit matriciel, vous constaterez que c'est une expression équivalente. Au fait, concernant $ w_ {i, j} $, faites attention aux indices. Vous lisez habituellement des indices comme ** $ i $ row $ j $ column **, n'est-ce pas? Cependant, dans la formule ci-dessus, cela ressemble à ** $ j $ ligne $ i $ colonne **. Dans l'état actuel des choses, il est difficile de le gérer à la fois théoriquement et pratiquement, alors délocalisons-le.

\left(
  \begin{array}{c}
    y_1 \\
    y_2
  \end{array}
\right)
=
\left(
  \begin{array}{cc}
    w_{1, 1} & w_{1, 2} \\
    w_{2, 1} & w_{2, 2}
  \end{array}
\right)^{\top}
\left(
  \begin{array}{c}
    x_1 \\
    x_2
  \end{array}
\right)
+
\left(
  \begin{array}{c}
    b_1 \\
    b_2
  \end{array}
\right) \\
\Leftrightarrow
\boldsymbol{Y} = \boldsymbol{W}^{\top}\boldsymbol{X} + \boldsymbol{B}

La réinstallation est également mentionnée dans [ici](# relocation). Pour le moment, cela complète l'expression mathématique de l'objet calque avec 2 entrées et 2 sorties. C'est facile. Généralisons cela. Cela ne change pas. Mathématiquement

\boldsymbol{Y} = \boldsymbol{W}^{\top}\boldsymbol{X} + \boldsymbol{B}

Il reste. Regardons cela de plus près. Considérant la couche de sortie $ M $ input $ N $

\underbrace{\boldsymbol{Y}}_{N \times 1} = \underbrace{\boldsymbol{W}^{\top}}_{N \times M}\underbrace{\boldsymbol{X}}_{M \times 1} + \underbrace{\boldsymbol{B}}_{N \times 1}

On dirait. $ \ Boldsymbol {W} ^ {\ top} $ montre la forme après translocation. Avant le transfert, c'est $ \ underbrace {\ boldsymbol {W}} _ {M \ times N} $. C'est tout pour la théorie. Passons maintenant à la mise en œuvre.

Implémentation de propagation directe dans une matrice

L'emplacement d'implémentation est [baselayer.py](https://qiita.com/kuroitu/items/884c62c48c2daa3def08#layer préparation du code du module) comme dans l'implémentation scalaire. Réécrivez l'implémentation en scalaire.

baselayer.py

baselayer.py


    def forward(self):
        """
Mise en œuvre de la propagation directe
        """
        #Souvenez-vous de votre entrée
        self.x = x.copy()

        #Propagation vers l'avant
        y = self.w.T @ x + self.b
        self.y = self.act.forward(y)
        
        return self.y
Une seule ligne a changé.

baselayer.py


        y = self.w * x + self.b

Mais

baselayer.py


        y = self.w.T @ x + self.b

Il est devenu. Pour les tableaux numpy, la translocation peut être effectuée avec ndarray.T. Certaines personnes peuvent ne pas être familières avec l'opérateur @, mais c'est le même que np.dot. ** Il peut être utilisé avec Numpy version 1.10 ou supérieure, donc soyez prudent si vous utilisez une version inférieure. ** ** <détails>

@ description de l'opérateur </ summary>

test_at.py


x = np.array([1, 2])
w = np.array([[1, 0], [0, 1]])
b = np.array([1, 1])

y = w.T @ x + b
print(y)
print(y == np.dot(w.T, x) + b)

#----------
#La sortie est
# [2 3]
# [ True  True]
#Ce sera.

En fait, la fonction de calcul de la matrice et du vecteur de l'opérateur «@» est implicitement utilisée ici. Si vous voulez calculer correctement comme une matrice, vous devrez peut-être utiliser np.matrix au lieu de np.array et ensuite reshape.

test_at.py


x = np.matrix([1, 2]).reshape(2, -1)
w = np.matrix([[1, 0], [0, 1]])
b = np.matrix([1, 1]).reshape(2, -1)

y = w.T @ x + b
print(y)
print(y == np.dot(w.T, x) + b)

#----------
#La sortie est
# [[2]
#  [3]]
# [[ True]
#  [ True]]
#Ce sera.

C'est ennuyeux, non? Vous pouvez utiliser np.array.

Ceci termine la mise en œuvre de la propagation vers l'avant. Je ne le changerai plus (peut-être).

Implémentation de la méthode __init __

À propos, il y a des membres que l'objet de calque devrait avoir. Implémentons ceci. De plus, j'aurai quelques membres.

Implémentation de `__init__`

baselayer.py


    def __init__(self, *, prev=1, n=1, 
                 name="", wb_width=1,
                 act="ReLU",
                 **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)
Au fur et à mesure que vous écrivez chacun. Pour les poids et les biais, la méthode `numpy.random.randn` définit des nombres aléatoires selon la distribution normale standard. Il est également multiplié par `wb_width` afin que la taille puisse être ajustée. La fonction d'activation est obtenue en important la méthode `get_act` depuis activations.py. Finalement, je vais ajuster le code de [ici](https://qiita.com/kuroitu/items/73cd401afd463a78115a) et l'apporter.

À propos du fonctionnement de la matrice

Ici, nous présenterons brièvement les opérations matricielles. Veuillez noter que je n'expliquerai rien mathématiquement (je ne peux pas le faire) simplement parce que c'est calculé comme ça.

Somme matricielle

Le premier est la somme de la matrice.

\left(
  \begin{array}{cc}
    a & b \\
    c & d
 \end{array}
\right)
+
\left(
  \begin{array}{cc}
    A & B \\
    C & D
  \end{array}
\right)
=
\left(
  \begin{array}{cc}
    a + A & b + B \\
    c + C & d + D
  \end{array}
\right)

Eh bien, c'est un résultat naturel. Ajoutez pour chaque élément. Sans parler de l'ajout ** La forme de la matrice doit correspondre exactement. ** **

Produit élément de matrice

J'aborderai également les produits élémentaires qui ne sont pas du tout mentionnés dans cet article. Le produit élément est également appelé produit Adamar.

\left(
  \begin{array}{cc}
    a & b \\
    c & d
 \end{array}
\right)
\otimes
\left(
  \begin{array}{cc}
    A & B \\
    C & D
  \end{array}
\right)
=
\left(
  \begin{array}{cc}
    aA & bB \\
    cC & dD
  \end{array}
\right)

À propos, le symbole du produit Adamar peut être écrit comme «\ otimes». Il existe de nombreux autres éléments liés aux symboles sur ici.

Produit matriciel

Ceci est un produit matriciel, pas un produit élémentaire. C'est une grande différence avec le produit de l'élément, comme les restrictions de forme et la non-commutabilité.

\left(
  \begin{array}{cc}
    a & b \\
    c & d
 \end{array}
\right)
\left(
  \begin{array}{cc}
    A & B \\
    C & D
  \end{array}
\right)
=
\left(
  \begin{array}{cc}
    aA + bC & aB + bD \\
    cA + dC & cB + dD
  \end{array}
\right)

En tant que méthode de calcul, on a l'impression de rouler horizontalement $ \ times $. matrix_product_image.gif C'est comme ça. Afin de pouvoir calculer de cette manière, le nombre d'éléments de ** horizontal ** </ font> et le nombre d'éléments de ** vertical ** </ font> Doit correspondre. En d'autres termes, la ** colonne ** </ font> de la première matrice et la ** row ** </ font> de la deuxième matrice ne font qu'un. Vous devez le faire. En général, la matrice résultante du produit des matrices ** $ L \ fois M $ et $ M \ fois N $ est $ L \ fois N $. ** **

Translocation

La translocation est l'opération d'échange de lignes et de colonnes d'une matrice.

\left(
  \begin{array}{cc}
    a & b & c \\
    d & e & f
 \end{array}
\right)^{\top}
=
\left(
  \begin{array}{cc}
    a & d \\
    b & e \\
    c & f
 \end{array}
\right)

Le symbole d'inversion s'écrit «\ top». Cette opération n'a besoin que de cette explication.

en conclusion

Ceci termine la mise en œuvre de la propagation vers l'avant. En particulier, il peut être nécessaire d'effectuer un traitement différent entre la couche intermédiaire et la couche de sortie, il est donc nécessaire de le remplacer dans [middlelayer.py et outputlayer.py](https://qiita.com/kuroitu/items/884c62c48c2daa3def08#layer préparation du code du module). Il n'y a pas. La propagation vers l'avant est facile et agréable.

référence

Série d'apprentissage en profondeur

Recommended Posts