[PYTHON] Réseau de neurones pour comprendre et mettre en œuvre en mathématiques au secondaire

introduction

Cet article explique le mécanisme du réseau de neurones à partir de la compréhension et de la mise en œuvre en utilisant le concept de mathématiques de niveau secondaire. Nous confirmerons en ne mettant en œuvre que la manière dont les calculs avant et arrière sont effectués sans expliquer la théorie détaillée (il n'y a pas d'explication sur l'apprentissage comme la méthode du gradient et l'optimisation). Utilisez Python + Numpy pour l'implémentation. Fondamentalement, numpy écrit le code comme s'il avait été importé. Veuillez noter que cet article est axé sur la compréhension intuitive et peut être trompeur en termes de théorie ou de théorème. De plus, si vous le généralisez, les indices peuvent être confus et vous ne le comprendrez peut-être pas, nous le comprendrons donc essentiellement avec des exemples concrets.

À propos des mathématiques utilisées pour l'explication

Afin de comprendre et de mettre en œuvre le réseau neuronal, le produit interne des vecteurs, le produit matriciel et certaines formules différentielles sont nécessaires, je vais donc l'expliquer brièvement. Si vous avez une solide compréhension des mathématiques au secondaire, vous pouvez les ignorer.

Produit intérieur des vecteurs

Considérez les deux vecteurs suivants.


\vec{x} = ({x_1, x_2, x_3}),\:\vec{w} = ({w_1, w_2, w_3})

Le produit interne de ce vecteur $ \ vec {x} $ et $ \ vec {w} $ est défini ci-dessous.


\vec{w}・\vec{x} = (w_1x_1+w_2x_2+w_3x_3)

L'histoire est simple, il suffit de multiplier et d'ajouter chaque élément correspondant. En utilisant numpy, le produit interne peut être écrit comme suit.

x = np.random.randn(3)
w = np.random.randn(3)
np.dot(w,x)

Produit matriciel

Il semble que les mathématiques du secondaire ne traitent pas actuellement de matrices, mais elles sont plus faciles à écrire et à mettre en œuvre à l'aide de matrices, alors faites de votre mieux pour les comprendre. Cependant, tout ce que vous devez savoir, c'est que la matrice est un tableau de vecteurs, et comment la multiplication et l'addition sont effectuées, alors soyez patient et rappelez-vous. Considérez les deux matrices suivantes.


X=\left(\begin{matrix}
x_{11} & x_{12} & x_{13} \\
x_{21} & x_{22} & x_{32}
\end{matrix}\right),\:
W=\left(\begin{matrix}
w_{11} & w_{12} \\
w_{21} & w_{22} \\
w_{31} & w_{32}
\end{matrix}\right)

La multiplication de ces deux matrices $ X $ et $ W $ est définie ci-dessous.


WX = \left(\begin{matrix}
w_{11}x_{11}+w_{21}x_{12}+w_{31}x_{13} & w_{12}x_{11}+w_{22}x_{12}+w_{32}x_{13} \\
w_{11}x_{21}+w_{21}x_{22}+w_{31}x_{23} & w_{12}x_{21}+w_{22}x_{22}+w_{32}x_{23}
\end{matrix}\right)

C'est un peu déroutant, mais la première ligne de $ X $ {$ x_ {11}, x_ {12}, x_ {13} $} et la première colonne de $ W $ {$ w_ {11}, w_ {21}, Le produit interne des vecteurs de w_ {31} $} est dans la 1ère ligne et la 1ère colonne, et la 2ème ligne de $ X $ {$ x_ {21}, x_ {22}, x_ {23} $} et la 1ère colonne de $ W $ Le produit interne des vecteurs des yeux est la valeur de la nème ligne et de la première colonne, et le produit interne des vecteurs de la nème ligne de la matrice précédente et de la mème colonne de la matrice arrière est la valeur de la nème ligne et de la mème colonne. Notez que vous ne pouvez multiplier que si le nombre de lignes dans la colonne avant et le nombre de colonnes dans la colonne arrière sont égaux. Le produit matriciel est exprimé par numpy comme suit.

X = np.random.randn(2,3)
W = np.random.randn(3,2)
np.dot(X,W)

De plus, si le nombre de lignes et le nombre de colonnes ne correspondent pas, une erreur se produit.

X = np.random.randn(3,2)
W = np.random.randn(3,2)
np.dot(X,W) #Erreur
X = np.random.randn(1,4)
W = np.random.randn(4,2)
np.dot(X, W) #Calculable

Formule de différenciation

Voici quelques-unes des formules différentielles utilisées cette fois.

f(x)=x+4\:\:\:---->\:\:f'(x)=1\\
f(x)=1/x\:\:\:---->\:\:f'(x)=-1/x^2\\
f(x)=4x\:\:\:---->\:\:f'(x)=4\\
f(x)=\exp(x)\:\:---->\:\:f'(x)=\exp(x)

Qu'est-ce qu'un réseau neuronal?

Outre les complications, un réseau de neurones exprime une sortie spécifique en multipliant une certaine entrée par une valeur numérique appelée poids et en ajoutant une valeur numérique appelée biais, comme indiqué dans la figure ci-dessous. Capture d'écran 2017-03-11 20.22.50.png La formule ci-dessus est $ o = wx + b $. Une fonction comme une fonction linéaire, qui a la même forme que la fonction linéaire apprise en mathématiques du premier cycle du secondaire, est appelée une fonction linéaire. Dans un réseau neuronal réel, les entrées et les sorties vont de centaines à des milliers, et la formule est $ o = w_ {1} x_ {1} + w_ {2} x_ {2} + ... + w_ {n} Cela ressemble à x_ {n} + b $. De plus, le réseau de neurones exprime une fonction compliquée en utilisant une fonction non linéaire (une fonction courbe telle qu'une fonction quadratique) par rapport à cette sortie. Nous apprendrons ce poids et ce biais afin que le réseau neuronal produise une sortie appropriée en fonction du problème. Vous pouvez voir ce que vous pouvez faire spécifiquement en regardant les [convnetjs] de Stanford (http://cs.stanford.edu/people/karpathy/convnetjs/). À titre d'exemple, voir démo: classification jouet 2d avec réseau neuronal à 2 couches dans convnetjs. La figure ci-dessous montre les résultats de la formation d'un réseau neuronal formé pour résoudre le problème de la séparation des données vertes bidimensionnelles et des données rouges. Capture d'écran 2017-03-11 20.34.52.png <img width =" 421 "alt =" Capture d'écran 11/03/2017 23.59.36.png "src =" https://qiita-image-store.s3.amazonaws.com /0/123951/f672a1d4-c560-2f3b-3ec6-3e6bc592e925.png "> スクリーンショット 2017-03-11 23.59.51.png

Dans le réseau neuronal, les valeurs de poids et de biais sont déterminées par apprentissage, et la fonction de la ligne de démarcation qui sépare la zone rouge et la zone verte est exprimée en répétant le couplage linéaire et les fonctions non linéaires. Les fonctions qui peuvent être exprimées varient en fonction du nombre de neurones et de la manière de sélectionner la fonction d'activation (expliquée plus loin). Dans l'ordre, le nombre de neurones intermédiaires est 6 et la fonction d'activation est tanh, le nombre de neurones intermédiaires est 2 et la fonction d'activation est tanh, et le nombre de neurones intermédiaires est 6 et la fonction d'activation est relu. En tant qu'image, à mesure que le nombre de neurones augmente, le nombre de lignes droites augmente, et si vous utilisez tanh pour la fonction d'activation, les articulations entre les lignes droites seront rondes et si vous utilisez relu, elles seront nettes. La raison de cela ne sera pas mentionnée dans cet article, veuillez donc étudier dans les manuels.

Calcul de la propagation vers l'avant

Tout d'abord, comprenons Forward. Je pense que le calcul de propagation avant est beaucoup plus simple et plus facile à comprendre que le calcul de propagation arrière. Le calcul de propagation avant fait référence à $ o = wx + b $ effectué dans la partie précédente. Tout d'abord, considérons un réseau neuronal à 1 entrée et 1 sortie. <Img width = "679" alt = "Screenshot 2017-03-11 20.22.50.png " src = "https://qiita-image-store.s3.amazonaws.com/0" dans la figure précédente pour une visualisation facile / 123951 / 2e99484b-2c69-76a7-a5b6-439680a543c6.png "> À スクリーンショット 2017-03-11 20.43.53.png Il est exprimé comme. Il n'y aura aucun parti pris à l'avenir pour faciliter la visualisation et la compréhension. Je pense qu'il n'est pas nécessaire d'écrire 1 entrée et 1 sortie, mais c'est une implémentation simple comme suit.

one2one.py


x = np.random.randn(1)
w = np.random.randn(1)
w*x

Étendez cela à plusieurs entrées et 1 sortie. スクリーンショット 2017-03-11 20.46.40.png Ce calcul peut être exprimé comme $ o = w_1x_1 + w_2x_2 + w_3x_3 $. Puisque cette formule est le produit interne des vecteurs lui-même, elle peut être mise en œuvre comme suit.

many2one.py


x = np.random.randn(1,3)
w = np.random.randn(3,1)
np.dot(x,w)

Ensuite, considérez 1 entrée et plusieurs sorties. スクリーンショット 2017-03-11 20.50.58.png Cela peut être exprimé comme $ o_1 = w_1x, o_2 = w_2x, o_3 = w_3x $. Cela définit o et w comme $ \ vec {o} = {o_1, o_2, o_3}, \ vec {w} = {w_1, w_2, w_3} $ et $ \ vec {o} = x \ vec Il peut être réécrit comme {w} $. Cela peut être mis en œuvre comme suit:

one2many.py


x = np.random.randn(1,1)
w = np.random.randn(1,3)
np.dot(x,w)

Enfin, considérez plusieurs entrées et plusieurs sorties. スクリーンショット 2017-03-11 20.58.46.png Cela peut être exprimé comme $ o_1 = w_ {11} x_1 + w_ {21} x_2 + w_ {31} x_3 $, $ o_2 = w_ {12} x_1 + w_ {22} x_2 + w_ {32} x_3 $, $ o_3 = w_ {13} x_1 + w_ {23} x_2 + w_ {33} x_3 $. Converti en vecteur, $ \ vec {x} = {x_1, x_2, x_3} $, $ \ vec {w_1} = {w_ {11}, w_ {21}, w_ {31}} $, $ \ vec {w_2 } = {w_ {12}, w_ {22}, w_ {32}} $, $ \ vec {w_3} = {w_ {13}, w_ {23}, w_ {33}} $, qui est calculé comme $ o_1 = \ vec {w_1} \ vec {x} $, $ o_2 = \ vec {w_2} \ vec {x} $, $ o_3 = \ vec {w_3} \ vec {x} $. Cela peut être exprimé en utilisant un produit matriciel, et chaque valeur est exprimée comme une matrice comme suit.

X=\begin{matrix} x_1 & x_2 & x_3 \end{matrix},
W=\begin{matrix} w_{11} & w_{12} & w_{13} \\
w_{21} & w_{22} & w_{23} \\
w_{31} & w_{32} & w_{33} \end{matrix},
O=\begin{matrix} o_1 & o_2 & o_3\end{matrix}

Ensuite, il peut être exprimé comme $ O = XW $ comme un calcul. J'ai pu l'exprimer assez clairement. Si vous n'êtes pas familier avec la procession, veuillez bouger vos mains et la vérifier. La mise en œuvre de ceci est la suivante.

many2many.py


X=np.random.randn(1,3)
W=np.random.randn(3,3)
np.dot(X,W)

C'est la fin de la propagation directe, mais en réalité cela ajoute un biais. Enfin, si vous connectez ceci à plusieurs couches comme indiqué ci-dessous, vous aurez un réseau neuronal multicouche. スクリーンショット 2017-03-11 21.14.04.png Fondamentalement, la sortie O obtenue dans la couche précédente est considérée comme la sortie X du réseau neuronal de la couche suivante, et le même calcul est effectué.

multi_layer.py


X=np.random.randn(1,3)
layer1_W=np.random.randn(3,2)
layer2_W=np.random.randn(2,1)
layer1_O=np.dot(X,layer1_W)
layer2_O=np.dot(layer1_O, layer2_W)

Mises à jour du poids et du biais

En plus de la propagation en avant, nous allons comprendre comment apprendre les valeurs de poids et de biais. Tout d'abord, laissez le réseau de neurones être une fonction telle que $ f (X) = w_1x_1 + w_2x_2 + ... + w_nx_n $ (cependant, $ X = {x_1, x_2, ..., x_n} $). D'autre part, le poids est mis à jour à l'aide de la formule suivante.

w_i^{new}=w_i-lr\frac{\partial{f(x)}}{\partial{w_i}}

$ lr $ est appelé le taux d'apprentissage, qui contrôle combien la valeur est modifiée. Si le taux d'apprentissage est trop élevé ou trop petit, l'apprentissage ne fonctionnera pas. Dans le contexte des réseaux neuronaux récents, les valeurs vont souvent de 0,1 à 0,0001, mais fondamentalement cela dépend de l'expérience et du ressenti car nous ne savons pas quelle valeur est appropriée. $ \ frac {\ partial {f (x)}} {\ partial {w_i}} $ est $ f (X) $ différencié par rapport à $ w_i $, et la définition ci-dessus de $ f (x) $ est $ \ frac {\ partial {f (x)}} {\ partial {w_i}} = x_i $. Pour le biais, remplacez simplement $ w $ par $ b $ dans le même calcul. Je n'expliquerai pas pourquoi les poids sont mis à jour pour que la valeur de sortie devienne la valeur cible. Bien que ce soit la partie la plus importante, si vous n'avez une compréhension intuitive que de la partie importante au niveau des mathématiques du lycée, des problèmes peuvent survenir, donc si vous en avez l'occasion, je l'expliquerai dans un autre article. Revenant au sujet principal, la différenciation ci-dessus peut être calculée très facilement dans le cas d'un réseau neuronal monocouche, mais pas dans le réseau neuronal multicouche couramment utilisé. Comme je l'ai écrit au début, le réseau de neurones effectue la transformation en utilisant la fonction non linéaire après le couplage linéaire décrit dans la section propagation directe. Utiliser une fonction non linéaire revient à calculer comme suit.

o = w_1x_1+w_2x_2+w_3x_3,\:g(x)=\frac{1}{1+\exp(-x)},\:o'=g(o)=\frac{1}{1+\exp(-o)}=\frac{1}{1+\exp(-(w_1x_1+w_2x_2+w_3x_3))}

Au fait, $ g (x) $ ci-dessus est une fonction non linéaire appelée fonction sigmoïde, qui est souvent utilisée dans les réseaux neuronaux (récemment, elle n'est pas souvent utilisée sauf dans la couche de sortie). Une fonction non linéaire qui affecte la sortie du réseau neuronal de cette manière est appelée une fonction d'activation. Il est un peu difficile de différencier cette sortie finale $ o '$ pour un $ w_1 $ particulier, mais vous pouvez toujours faire de votre mieux pour le calculer. Cependant, considérez le cas où cela est multicouche, comme illustré dans la figure ci-dessous. スクリーンショット 2017-03-11 21.46.45.png Si vous écrivez ceci dans une formule mathématique, ce sera comme suit.


o_{1} = w_{11}x_1+w_{21}x_2+w_{31}x_3,\:o_{2} = w_{12}x_1+w_{22}x_2+w_{32}x_3,\\

o'_{1}=g(o_1),\:o'_{2}=g(o_2),\\
o=w'_1o'_1+w'_2o'_2,\:o'=g(o)

Si vous écrivez le $ o '$ final depuis le début, ce sera comme suit.

o'=\frac{1}{1+\exp(-(w'_1\frac{1}{1+\exp(-(w_{11}x_1+w_{21}x_2+w_{31}x_3)}+w'_2\frac{1}{1+\exp(-(w_{12}x_1+w_{22}x_2+w_{32}x_3)})}

Par exemple, il est très difficile de différencier cela pour $ w_ {11} $. Si cela est fait sur des dizaines de couches comme un réseau neuronal profond, ce sera un calcul formidable. Afin de calculer cela efficacement, le calcul de rétro-propagation présenté ci-dessous est utilisé.

Calcul de rétropropagation

Le concept de règle de chaîne est important pour effectuer des calculs de rétropropagation. La loi des chaînes dit que la formule suivante est valable.


\frac{\partial{f(x)}}{\partial{w}}=\frac{\partial{f(x)}}{\partial{o}}\frac{\partial{o}}{\partial{w}}

Autrement dit, lorsque vous essayez de différencier $ f (X) $ pour une variable appelée $ w $, elle peut être exprimée en multipliant $ f (x) $ par $ o $ et $ o $ par $ w $. C'est. Si vous réduisez $ \ partial {o} $ en tant qu'image, ce sera le même que l'original. Par exemple, appliquez-le aux éléments suivants (il peut être résolu sans utiliser la règle de chaîne).

o=w+z,\:f(x)=(w+z)y=oy,\\
\frac{\partial{o}}{\partial{w}}=1,\:\frac{\partial{f(x)}}{\partial{o}}=y,\:\frac{\partial{f(x)}}{\partial{w}}=\frac{\partial{f(x)}}{\partial{o}}\frac{\partial{o}}{\partial{w}}=y

Le calcul ci-dessus peut être effectué sans utiliser la règle de la chaîne, mais c'est une méthode de calcul très efficace lorsqu'elle est appliquée à une méthode multicouche utilisant la fonction sigmoïde. Ensuite, nous effectuerons un calcul de rétropropagation sur le réseau neuronal réel. Tout d'abord, considérons un simple réseau neuronal à 1 entrée et 1 sortie. スクリーンショット 2017-03-11 22.12.27.png

Ce réseau neuronal peut être exprimé comme $ f (x) = wx + b $, et disons $ A = wx $ comme une sortie intermédiaire. $ \ Frac {\ partial {f (x)}} {\ partial {w}} $ nécessaire pour mettre à jour les poids comme cible et $ \ frac {\ partial {f (x)} nécessaire pour mettre à jour le biais Pensez à calculer} {\ partial {b}} $. Tout d'abord, calculez $ \ frac {\ partial {f (x)}} {\ partial {b}} $. Puisqu'il peut être exprimé comme $ f (x) = A + b $, la différenciation de $ f (x) $ pour $ b $ est $ \ frac {\ partial {f (x)}} {\ partial {b}} = Ce sera 1 $. Puis calculez $ \ frac {\ partial {f (x)}} {\ partial {w}} $. En utilisant la règle de chaîne, $ \ frac {\ partial {f (x)}} {\ partial {w}} = \ frac {\ partial {f (x)}} {\ partial {A}} \ frac {\ partial Il peut être exprimé comme {A}} {\ partial {w}} $, $ \ frac {\ partial {f (x)}} {\ partial {A}} = 1 $, $ \ frac {\ partial {A} } {\ partial {w}} = x = 3 $, donc $ \ frac {\ partial {f (x)}} {\ partial {w}} = 3 $. L'état de la propagation arrière est indiqué en bleu ci-dessous. スクリーンショット 2017-03-11 22.17.10.png Ce calcul de rétropropagation est très puissant. Par exemple, la différenciation de la fonction sigmoïde mentionnée ci-dessus peut être calculée comme suit (essayez de la calculer).

f(x)=\frac{1}{1+\exp(-x)},\\
f'(x)=f(x)(1-f(x))=\frac{1}{1+\exp(-x)}(1-\frac{1}{1+\exp(-x)})

Alors $ \ frac {\ partial {sans calculer le différentiel de la fonction sigmoïde elle-même Trouvons la valeur de f (x)}} {\ partial {x}} $ par calcul de rétropropagation. La fonction sigmoïde est exprimée comme suit. スクリーンショット 2017-03-12 10.40.37.png Ici, $ D $ représente la fonction sigmoïde ($ D = f (x) $). Nous calculerons la propagation arrière.

\frac{\partial{D}}{\partial{C}}=(\frac{1}{C})'=\frac{-1}{C^2}=\frac{-1}{(1.37)^2}=-0.53\\
\frac{\partial{D}}{\partial{B}}=\frac{\partial{D}}{\partial{C}}\frac{\partial{C}}{\partial{B}}=(-0.53)(1+B)'=(-0.53)(1)=-0.53\\
\frac{\partial{D}}{\partial{A}}=\frac{\partial{D}}{\partial{B}}\frac{\partial{B}}{\partial{A}}=(-0.53)(\exp(A))'=(-0.53)(\exp(A))=(-0.53)(0.37)=0.2\\
\frac{\partial{D}}{\partial{x}}=\frac{\partial{D}}{\partial{A}}\frac{\partial{A}}{\partial{x}}=(-0.2)(-x)'=(-0.2)(-1)=0.2

Si vous remplacez réellement $ x = 1 $ par $ f '(x) = f (x) (1-f (x)) $, qui est la différenciation de la fonction sigmoïde, vous devriez obtenir le même résultat (valide). Veuillez noter qu'il est calculé avec 2 chiffres). Revenons au réseau neuronal. Ensuite, considérons la rétropropagation suivante d'un réseau neuronal multicouche. スクリーンショット 2017-03-11 22.40.38.png Ce sera difficile à voir, mais l'expression sera corrigée comme suit. スクリーンショット 2017-03-11 22.46.50.png

Le calcul de rétro-propagation de ce réseau neuronal est effectué. Si vous omettez le processus intermédiaire et affichez uniquement le résultat, ce sera comme suit. Veuillez calculer. スクリーンショット 2017-03-11 22.46.01.png La valeur soulignée est $ \ frac {\ partial {f (x)}} {\ partial {w_i}} $ qui est en fait utilisée pour mettre à jour les paramètres. L'apprentissage peut être effectué en multipliant la valeur ainsi calculée par le taux d'apprentissage et en la soustrayant de la valeur d'origine. Enfin, implémentons une série de flux de propagation avant, de propagation arrière et de mise à jour des paramètres. Cette fois, nous implémentons celui sans biais, mais essayez de l'implémenter vous-même pour vérifier si vous le comprenez. Définissez la classe nn suivante.

nn.py


class nn():
  def __init__(self, n_i, n_o, lr):
    self.weight = np.random.randn(n_o, n_i)
    self.input = None
    self.grad = np.zeros((n_i, n_o))
    self.lr = lr

  def forward(self, x):
    self.inputs = x.reshape(-1, 1)  #Maintenir la valeur d'entrée
    return np.dot(self.weight, self.inputs)

  def backward(self, dx):
    self.grad = np.dot(self.inputs.reshape(-1,1), dz.reshape(1,-1)).reshape(self.weight.shape) #Calcul de différenciation pour w
    return np.dot(dz.reshape(1, -1), self.weight) #Calcul de différenciation pour x

  def update(self):
    self.weight -= self.grad*self.lr

En ce qui concerne l'avant, il s'agit de la même implémentation que celle mise en œuvre par propagation directe. La valeur d'entrée est conservée car elle est nécessaire pour calculer la valeur différentielle de w pendant le calcul de rétropropagation. Comme vous pouvez le voir en calculant manuellement l'exemple précédent, la valeur différentielle de w est la valeur de l'entrée x multipliée par la valeur différentielle propagée d'avant. En arrière, le produit matriciel est utilisé pour calculer la différenciation. Si vous notez chaque calcul comme vous l'avez fait dans la propagation vers l'avant, vous pouvez voir que les différentiels pour tout w peuvent être calculés à la fois par le produit matriciel. De même, la valeur différentielle liée à x peut être calculée par le produit matriciel de la valeur différentielle et du poids qui ont été propagés d'avant. Si vous vérifiez si vous pouvez réellement calculer avec le produit interne, vous pouvez voir si vous le comprenez, alors veuillez le vérifier. Si la valeur différentielle (grad) de w peut être calculée, le poids peut être mis à jour en multipliant le taux d'apprentissage et en soustrayant de la valeur d'origine.

test.py


from nn import nn
fc = nn(10, 2, 0.1)
x = np.random.randn(10, 1)
fc.forward(x)
grad = np.random.randn(1,2) #Valeur de différenciation venant du front
fc.backward(grad)
fc.update()

Vous pouvez mettre à jour le poids comme ci-dessus. Dans ce qui précède, la valeur différentielle provenant du front est générée avec un nombre aléatoire, mais en réalité, elle est calculée à l'aide d'une fonction d'erreur qui calcule l'erreur à partir de la valeur cible. Lors de la construction d'un modèle de réseau neuronal, une fonction d'activation (fonction non linéaire) est insérée entre les couches. Cette fois, je vais implémenter la fonction sigmoïde. Comme je l'ai déjà écrit, les formules avant et arrière ont des formules, et l'arrière peut être implémentée comme suit car elle est calculée à l'aide du résultat du calcul de la propagation avant.

sigmoid.py


class sigmoid():
  def __init__(self):
    self.output = None

  def forward(self, x):
    self.output = 1/(1+np.exp(-x))
    return self.output

  def backward(self, dx):
    return self.output * (1 - self.output)

Il peut être implémenté simplement en écrivant l'expression précédemment écrite telle quelle. Comme la fonction sigmoïde n'a pas de paramètres à apprendre, il n'est pas nécessaire de maintenir la fonction de mise à jour ou la valeur différentielle. Si vous créez réellement un modèle de réseau neuronal multicouche à l'aide de cette fonction sigmoïde, ce sera comme suit.

multi_layer_perceptron.py


from nn import nn
from sigmoid import sigmoid
fc1 = nn(10, 5, 0.1)
sig1 = sigmoid()
fc2 = nn(5, 2, 0.1)
sig2 = sigmoid()
x = np.random.randn(10, 1)
sig2.forward(fc2.forward(sig1.forward(fc1.forward(x))))
grad = np.random.randn(1,2)
fc1.backward(sig1.backward(fc2.backward(sig2.backward(grad))))
fc1.update()
fc2.update()

Fonction d'erreur

Enfin, j'expliquerai la fonction d'erreur. Tout ce que vous avez à faire pour la propagation avant, la propagation arrière et la formation du réseau est de savoir comment mettre à jour les poids afin que la sortie du réseau neuronal s'approche de la valeur cible. Jusqu'à présent, l'entrée à la sortie du réseau de neurones était représentée par une seule fonction appelée $ f (x) $, qui était différenciée par rapport à $ w $. Cependant, même si le poids est mis à jour avec cette valeur, il ne sera mis à jour qu'à une valeur aléatoire et ne sera pas utile. Par conséquent, à la fin du réseau de neurones, une fonction appelée fonction d'erreur qui exprime la différence par rapport à la valeur cible est mordue, et l'entrée de la fonction d'erreur est considérée comme une fonction appelée $ f (x) $. En mettant à jour $ f (x) $ en incluant cette fonction d'erreur avec une valeur différenciée, le poids peut être mis à jour afin que la valeur de sortie s'approche de la valeur cible. Dans l'apprentissage automatique, le modèle est entraîné à l'aide d'une fonction d'erreur qui correspond au problème. Cette fois, nous utiliserons l'erreur quadratique moyenne pour faciliter la mise en œuvre et la compréhension. L'erreur quadratique moyenne est l'une des fonctions d'erreur utilisées pour la régression et est définie comme suit.

f(x)=\frac{1}{n}\sum_{i=1}^n(y_i-x_i)^2\\
\frac{\partial{f(x)}}{\partial{x_i}} = \frac{2}{n}(y_i-x_i)

Où $ x_i $ est la i-ème sortie du réseau et $ y_i $ est la valeur cible correspondant à la i-ème sortie. Le réseau neuronal vise à minimiser la valeur de la fonction d'erreur définie. Donc, si vous utilisez l'erreur quadratique moyenne, les poids seront mis à jour de sorte que la différence entre les valeurs de $ x_i $ et $ y_i $ soit égale à zéro, c'est-à-dire exactement la même valeur. Voici un exemple de mise en œuvre.

MSE.py


class MSE():
  def forward(self, x, y):
    return np.square(y.reshape(-1)-x.reshape(-1)).mean()

  def backward(self, x, y):
    return 2*(y.reshape(-1) - x.reshape(-1)).mean()

Après cela, il est possible de résoudre le problème de régression tant que les données sont prêtes. Un exemple de mise en œuvre est présenté ci-dessous.

train.py


from nn import nn
from sigmoid import sigmoid
from MSE import MSE
fc1 = nn(10, 5, 0.1)
sig1 = sigmoid()
fc2 = nn(5, 2, 0.1)
sig2 = sigmoid()
mse = MSE()
x = np.random.randn(10) #Génération de données d'entraînement
t = np.random.randn(2) #Génération des données des enseignants
for i in range(100):
  out = sig2.forward(fc2.forward(sig1.forward(fc1.forward(x))))
  loss = mse.forward(out, t)
  print(loss)
  grad = mse.backward(out, t)
  fc1.backward(sig1.backward(fc2.backward(sig2.backward(grad))))
  fc1.update()
  fc2.update()

Lorsque vous exécutez le code ci-dessus, vous verrez que la valeur de perte de sortie diminue. Cette fois, les données sont générées avec des nombres aléatoires, mais si vous lisez les données réelles, elles apprendront qu'elles conviennent à ces données. Il existe différentes fonctions d'erreur, veuillez donc les vérifier et les implémenter. Ce faisant, vous serez en mesure de comprendre les caractéristiques de chaque fonction d'erreur et vous serez en mesure de concevoir votre propre fonction d'erreur qui convient au problème que vous souhaitez résoudre.

Résumé

J'ai essayé d'expliquer la compréhension et la mise en œuvre de l'apprentissage des réseaux de neurones au niveau des mathématiques du lycée. Essayez-le vous-même pour voir si vous comprenez l'implémentation lorsqu'il y a un biais du réseau neuronal et l'endroit pour préparer et apprendre les données réelles (je suis désolé qu'il ait été difficile de mettre en œuvre la partie d'apprentissage. ). Je peux expliquer pourquoi il peut être optimisé par mise à jour avec une valeur différentielle, disparition de gradient, explosion de gradient, initialisation de paramètres, importance des hyperparamètres, fonctions d'activation autres que la fonction sigmoïde, CNN, RNN, etc. Il y en a beaucoup, mais cette fois je voudrais terminer dans un tel endroit. Soyez prudent car il s'agit d'un article écrit avec élan, donc je pense qu'il y a des erreurs typographiques, des erreurs de mise en œuvre, etc. Après cela, il est recommandé d'étudier réellement une théorie appropriée avec des livres.

Recommended Posts

Réseau de neurones pour comprendre et mettre en œuvre en mathématiques au secondaire
Implémentez un réseau de neurones feedforward dans Chainer pour classer les documents
Implémenter et comprendre l'arborescence de recherche d'union dans Go
Implémenter un réseau neuronal convolutif
Implémenter le réseau neuronal à partir de zéro
Intelligence artificielle, machine learning, deep learning pour mettre en œuvre et comprendre
J'ai essayé de mettre en œuvre le modèle de base du réseau neuronal récurrent
Implémenter un réseau neuronal à 3 couches
Implémentation de réseau neuronal en python
J'ai créé mon propre réseau de neurones à propagation directe à 3 couches et j'ai essayé de comprendre le calcul en profondeur.
[Deep Learning from scratch] À propos des couches requises pour implémenter le traitement de rétropropagation dans un réseau neuronal
Essayer d'implémenter et de comprendre les arborescences de segments étape par étape (python)
Paramètres réseau et confirmation dans CentOS7
Théorie et implémentation simples des réseaux neuronaux
[Mathématiques au lycée + python] Problème de logistique
Comprendre les rouages et les extensions dans discord.py
J'ai essayé de comprendre attentivement la fonction d'apprentissage dans le réseau de neurones sans utiliser la bibliothèque d'apprentissage automatique (deuxième moitié)
Résumé des problèmes d'AtCoder C qui peuvent être résolus en mathématiques au secondaire
[Python] Les lycéens d'Ikiri expliqueront la blockchain aux débutants en 10 minutes! !! !!
J'ai essayé d'implémenter PLSA en Python
J'ai essayé d'implémenter la permutation en Python
J'ai essayé d'implémenter PLSA dans Python 2
J'ai essayé d'implémenter ADALINE en Python
[Python] pandas à bien comprendre en 10 minutes
J'ai essayé d'implémenter PPO en Python
Comprendre et mettre en œuvre la régression des crêtes (régularisation L2)
Comment utiliser is et == en Python
2. Écart moyen et standard avec le réseau neuronal!
[Python] Un lycéen a implémenté Perceptron et a essayé de classer les iris.