Qu’est-ce qu’un neurone artificiel ?

Le terme de « neurone artificiel » peut faire peur, cependant c’est quelque chose de très simple.

Il est inspiré par les neurones du cerveau humain, qui à partir de signaux extérieurs produisent une sortie.

Voici un exemple de neurone artificiel, appelé « perceptron » :
Perceptron Wikipedia
Source : Wikimedia

Concrètement un perceptron reçoit des valeurs d’entrées, il les multiplie par un poids et en fait la somme.

Cette somme est comparée à un seuil. Si elle est inférieure, la valeur de sortie sera 0, 1 sinon.

On peut donc écrire une fonction pour réaliser cela en Python :

def perceptron(val1, val2):
  poids1 = 0.82
  poids2 = 0.26
  poids3 = -0.13
  seuil = 0
  somme = poids1 * val1 + poids2 * val2 + poids3
  if somme > seuil:
	return 1
  else:
	return 0

print("perceptron(0,0) => ", perceptron(0,0))
print("perceptron(1,0) => ", perceptron(1,0))
print("perceptron(0,1) => ", perceptron(0,1))
print("perceptron(1,1) => ", perceptron(1,1))

Voici le résultat :

 ('perceptron(0,0) => ', 0)
 ('perceptron(1,0) => ', 1)
 ('perceptron(0,1) => ', 1)
 ('perceptron(1,1) => ', 1)
 Vous aurez remarquez que le perceptron se comporte comme une porte logique OR.

Mais comment trouver les valeurs des poids ?

Laissons donc la machine les découvrir toute seule !

Pour cela nous allons initialiser les poids de façon aléatoire.

Puis nous ferons une boucle de 100 itérations.

A chaque itération nous calculerons la somme des produits entre les poids et les valeurs, comme précédemment. Sauf que nous utiliserons par commodité la fonction « dot » de la librairie NumPy qui permet de faire toute seule le produit scalaire (dot product).

Cette somme sera déduite du résultat attendu, pour calculer la valeur de l’erreur.

On multiplie ensuite cette erreur par les valeurs d’entrée et le taux d’apprentissage pour déterminer les nouveaux poids.

On revient ensuite au début.

Après 100 itérations on affiche le résultat de chaque valeur d’entrée possible.

Voici le code :

from random import choice
from numpy import array, dot, random

# Le jeux de données avec le résultat attentu pour un OR
donnees_entrainement_OR = [
    (array([0,0,1]), 0),
    (array([0,1,1]), 1),
    (array([1,0,1]), 1),
    (array([1,1,1]), 1),
]

# Le jeux de données avec le résultat attentu pour un AND
donnees_entrainement_AND = [
    (array([0,0,1]), 0),
    (array([0,1,1]), 0),
    (array([1,0,1]), 0),
    (array([1,1,1]), 1),
]

# Choix du jeu de données
donnees_entrainement = donnees_entrainement_AND

# Initialisation des 3 valeurs de poids
poids = random.rand(3)
# Initialisation du taux d'apprentissage
taux_apprentissage = 0.2
# On fixe le nombre d'itération
nombre_iteration = 100
# Initialisation d'un tableau pour stocker l'historique des erreurs
historique_des_erreurs = []

# Ecriture de la fonction d'activation
def fonction_d_activation(val):
    if val < 0:
        return 0
    else:
        return 1

# Note : on peut l'écrire de façon plus concise :
# fonction_d_activation = lambda val: 0 if val < 0 else 1 for i in xrange(nombre_iteration): valeurs_entrees, resultat_attendu = choice(donnees_entrainement) # On réalise le produit scalaire (dot product) produit_scalaire = dot(poids, valeurs_entrees) # On le soustrait à la valeur attendue erreur = resultat_attendu - fonction_d_activation(produit_scalaire) #print erreur historique_des_erreurs.append(erreur) # Cette erreur est multipliée par le taux d'aprentissage et les valeurs d'entrées, # pour estimer les poids pour la prochaine itération. poids += taux_apprentissage * erreur * valeurs_entrees #print w #print poids # Utilisation sur les différentes valeurs for valeurs_entrees, _ in donnees_entrainement: produit_scalaire = dot(valeurs_entrees, poids) print("{}: {} -> {}".format(valeurs_entrees[:2], produit_scalaire, fonction_d_activation(produit_scalaire)))

Le résultat correspond bien à une opération AND :

 [0 0]: -0.344550175325 -> 0
 [0 1]: -0.266750285168 -> 0
 [1 0]: -0.015112422108 -> 0
 [1 1]: 0.0626874680487 -> 1
 Vous pouvez maintenant changer la lignedonnees_entrainement = donnees_entrainement_ANDendonnees_entrainement = donnees_entrainement_ORpour voir qu'on peut bien lui faire apprendre différentes opération.

Le nombre d’itérations et le taux d’apprentissage sont appelé les « hyper-paramètres » car ils ne modifient pas le neurone mais la façon dont on va le faire apprendre.

Pour voir leurs effets nous pouvons afficher la courbe de l’historique des erreurs :

from pylab import plot, ylim, show
print historique_des_erreurs
ylim([-1,1])
plot(historique_des_erreurs)
show()

On voit que 50 itérations auraient suffis pour apprendre.

Vous pouvez passer nombre_iteration à 200 et faire varier le taux_apprentissage de 1 à 0.01 pour voir la différence.

Vous devriez constater que plus la valeur du taux_apprentissage est petite, plus notre neurone met de temps à apprendre.

Bravo, vous venez de coder votre premier système d’apprentissage !

Je vous rassure, pour la suite nous allons prendre du recule et utiliser des librairies spécialisées.

Mais je pense qu’il est important de comprendre le fonctionnement interne de base pour démystifier le domaine de l’apprentissage profond (Deep Learning)

La notion que nous venons de voir date des années 60, par la suite nous allons voir comment on passe d’un neurone à un réseau de neurone, ce qui va permettre à la machine d’apprendre des choses plus complexe qu’une porte logique.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *