Lab 0 - Machine Learning - une introduction

Une introduction aux réseaux de neurones Une explication simple de leur fonctionnement et comment en implémenter un à partir de zéro en Python.

0.1 Un Neurone

Tout d'abord, nous devons parler des neurones, l'unité de base d'un réseau de neurones. Un neurone prend des entrées, fait des calculs avec elles et produit une sortie. Voici à quoi ressemble un neurone à 2 entrées :

AI.Lab0.1.neuron.png

(x1​∗w1​)+(x2​∗w2​)+b

3 choses se passent ici. Tout d'abord, chaque entrée est multipliée par un poids :

x1 : x1​∗w1 et x2 : x2​∗w2

Ensuite, toutes les entrées pondérées sont additionnées avec un biais b :

(x1​∗w1​)+(x2​∗w2​)+b

Enfin, la somme passe par une fonction d'activation :

y=f(x1​∗w1​+x2​∗w2​+b)

La fonction d'activation est utilisée pour transformer une entrée illimitée en une sortie qui a une forme agréable et prévisible. Une fonction d'activation couramment utilisée est la fonction sigmoïde :

AI.Lab0.2.sigmoid.png

La fonction sigmoïde ne sort que des nombres dans la plage (0,1). Vous pouvez considérer cela comme une compression (−∞,+∞) en (0,1) - grands nombres négatifs devient ~0, et les grands nombres positifs deviennent ~1.

0.1.1 Un exemple simple

Supposons que nous ayons un neurone à 2 entrées qui utilise la fonction d'activation sigmoïde et possède les paramètres suivants :

w=[0,1] et b=4

w=[0,1] est juste une façon d'écrire w1​=0, w2​=1 sous forme vectorielle.

Maintenant, donnons au neurone une entrée de x=[2,3]. Nous utiliserons le produit scalaire pour écrire les choses de manière plus concise :

(w⋅x)+b=((w1∗x1)+(w2∗x2))+b=0∗2+1∗3+4=7

y=f(w⋅x+b)=f(7)=0.999

Le neurondérivéee sort 0,9990 étant donné les entrées x=[2,3].

C'est ça! Ce processus de transmission d'entrées pour obtenir une sortie est connu sous le nom de feedforward.

0.1.2 Codage d'un neurone

Il est temps d'implanter un neurone ! Nous utiliserons NumPy, une bibliothèque informatique populaire et puissante pour Python, pour nous aider à faire des mathématiques :

Reconnaîssez ces chiffres ? C'est l'exemple que nous venons de faire à la main! Nous obtenons la même réponse de 0,999.

0.2 Combinaison de neurones dans un réseau de neurones (NN)

Un réseau de neurones n'est rien de plus qu'un groupe de neurones connectés entre eux. Voici à quoi pourrait ressembler un simple réseau de neurones :

AI.Lab0.3.NN.png

Ce réseau a 2 entrées, une couche cachée avec 2 neurones (h1 et h2), et une couche de sortie avec 1 neurone (o1). Notez que les entrées de o1 sont les sorties de h1 et h2- c'est ce qui en fait un réseau.

Une couche cachée est une couche entre la couche d'entrée (première) et la couche de sortie (dernière). Il peut y avoir plusieurs calques cachés !

0.2.1 Un exemple : Feedforward

Utilisons le réseau illustré ci-dessus et supposons que tous les neurones ont les mêmes poids w=[0,1], le même biais b=0 et le même fonction d'activation sigmoïde. Soit h1,h2,o1 les sorties des neurones qu'ils représentent.

Que se passe-t-il si on passe l'entrée x=[2,3] ?

h1=h2=f(w⋅x+b)=f((0∗2)+(1∗3)+0)=f(3)=0.9526

o1=f(w⋅[h1,h2]+b)=f((0∗h1)+(1∗h2)+0)=f(0.9526)=0.7216

La sortie du réseau neuronal pour l'entrée x=[2,3] est de 0,72160.

Assez simple, non?

Un réseau de neurones peut avoir n'importe quel nombre de couches avec n'importe quel nombre de neurones dans ces couches. L'idée de base reste la même : transmettre la ou les entrées à travers les neurones du réseau pour obtenir la ou les sorties à la fin. Par souci de simplicité, nous continuerons à utiliser le réseau illustré ci-dessus pour le reste de ce laboratoire.

0.2.2 Codage d'un réseau de neurones : feedforward

Implémentons le feedforward pour notre réseau de neurones. Voici à nouveau l'image du réseau pour référence. Notons que les 3 neurones sont initialisés avec les mêmes poids: w=[0,1] et le même biais: b=0.

AI.Lab0.3.NN.png

Nous avons à nouveau 0,7216 ! On dirait que ça marche.

0.3 Formation d'un réseau de neurones, partie 1

Disons que nous avons les mesures suivantes :

AI.Lab0.4.table.png

AI.Lab0.5.gender.png

0.3.1 Perte

Avant de former notre réseau, nous avons d'abord besoin d'un moyen de quantifier à quel point il se comporte « bien » afin qu'il puisse essayer de faire « mieux ». C'est ça la perte.

Nous utiliserons la perte erreur quadratique moyenne (MSE) :

AI.Lab0.6.MSE.png

Décomposons ceci :

n est le nombre d'échantillons, qui est de 4 (Alice, Bob, Charlie, Diana).

y représente la variable prédite, qui est le sexe.

ytruey est la vraie valeur de la variable (la "bonne réponse"). Par exemple, ytruey pour Alice serait 1 (Femme).

ypredy est la valeur prédite de la variable. C'est quelle que soit la sortie de notre réseau.

(ytrue−ypred)^2 est connu comme l'erreur quadratique. Notre fonction de perte prend simplement la moyenne de toutes les erreurs au carré (d'où le nom erreur quadratique moyenne).

Plus nos prédictions sont bonnes, plus notre perte sera faible !

Former un réseau = essayer de minimiser sa perte.

0.3.2 Un exemple de calcul de perte

Disons que notre réseau affiche toujours 0 - en d'autres termes, il est sûr que tous les humains sont des hommes 🤔. Quelle serait notre perte ?

AI.Lab0.7.loss.example.png

0.3.3 Code : Perte MSE

Voici un code pour calculer la perte pour nous:

C'est parfait. Continuons !

0.4 Formation d'un réseau de neurones, partie 2

Nous avons maintenant un objectif clair : minimiser la perte du réseau de neurones.

Nous savons que nous pouvons modifier les pondérations et les biais du réseau pour influencer ses prévisions, mais comment le faire de manière à réduire les pertes ?

Pour simplifier, supposons que nous n'ayons qu'Alice dans notre ensemble de données :

AI.Lab0.8.gender.alice.png

Dans ce cas, la perte d'erreur quadratique moyenne n'est que l'erreur quadratique d'Alice = 1 :

AI.Lab0.9.alice.mse.png

Une autre façon de penser à la perte est en fonction des poids et des biais. Étiquetons chaque poids et biais de notre réseau :

AI.Lab0.10.bias.layers.png

Ensuite, nous pouvons écrire la perte sous la forme d'une fonction multivariable :

AI.Lab0.11.loss.multi.png

Imaginez que nous voulions modifier w1. Comment la perte L changerait-elle si on changeait w1 ?

C'est une question à laquelle la dérivée partielle ∂L/∂w1 peut répondre. Comment le calcule-t-on ?

Pour commencer, réécrivons la dérivée partielle en termes de ∂ypred/∂w1 à la place :

AI.Lab0.12.part.deriv.png

Nous pouvons calculer ∂L∂ypred car nous avons calculé L=(1−ypred)^2 ci-dessus :

AI.Lab0.12a.calc.deriv.png

Voyons maintenant quoi faire avec ∂ypred∂w1. Tout comme avant, soit h1,h2,o1 les sorties des neurones qu'ils représentent. Puis:

AI.Lab0.13.ypred.sigmoid.png

Puisque w1 n'affecte que h1 (pas h2), nous pouvons écrire:

AI.Lab0.14.ypred.h1.png

Nous faisons la même chose pour ∂h1∂w1 :

AI.Lab0.15.h1.delatw1.png

x1 ici est le poids et x2 est la taille.

C'est la deuxième fois que nous voyons f′(x) (le dérivé de la fonction sigmoïde) maintenant ! Dérivons-le :

AI.Lab0.16.sigmoid.fun.png

Nous utiliserons cette belle forme pour f′(x) plus tard.

Succés. Nous avons réussi à décomposer ∂L∂w1 en plusieurs parties que nous pouvons calculer :

AI.Lab0.17.dellaL.deltaw1.png

Ce système de calcul des dérivées partielles en travaillant à rebours est connu sous le nom de rétropropagation, ou «backprop».

C'était beaucoup de symboles - ce n'est pas grave si vous êtes encore un peu confus. Faisons un exemple pour voir cela en action !

0.4.1 Exemple : Calcul de la dérivée partielle

Nous allons continuer à prétendre que seule Alice est dans notre ensemble de données :

AI.Lab0.18.table.alice.png

Initialisons tous les poids à 1 et tous les biais à 0. Si nous effectuons un feedforward pass à travers le réseau, nous obtenons :

AI.Lab0.19.weights.init.png

Le réseau génère ypred=0,524, ce qui ne favorise pas fortement Male (0) ou Female (1). Calculons ∂L∂w1 :