Comprendre le Réseau Neuronal

DATE: Février 2025
CATÉGORIE: Intelligence artificielle

Résumé : L'article ci-dessous parlera des concepts de l'intelligence artificielle : ce qu'est un réseau neuronal, comment il est construit, comment on crée et gère l'apprentissage.

01.0 L'intelligence artificielle ?

Si vous ne connaissez pas Alan Turing, je vous invite vivement à découvrir son histoire à travers le film Imitation Game . Considéré comme le père de l'informatique moderne, Turing a conçu durant la Seconde Guerre mondiale les bases logiques de nos machines actuelles. Quelques années plus tard, il posait cette question fondamentale : « Une machine est-elle capable de réfléchir ?»

Dès 1951, les premières réponses concrètes apparaissent : Christopher Strachey développe un programme permettant de faire jouer un ordinateur aux dames, tandis que Dietrich Prinz conçoit un algorithme capable de résoudre des problèmes d'échecs. Mais qu'en est-il aujourd'hui ? Nos ordinateurs nous répondent, génèrent des images, des vidéos et exécutent des tâches d'une complexité fascinante.

C’est ce que nous allons explorer ensemble. Je m'appelle Geoffrey FRANZ, et j'étudie l'intelligence artificielle au moment même où j'écris ces lignes. Mon objectif est de vulgariser ces concepts pour vous aider à comprendre comment, derrière le code, un programme parvient aujourd'hui à prendre des décisions et à répondre à des besoins de plus en plus exigeants.

02.0 Le réseau neuronal

Le réseau neuronal est la représentation schématique et mathématique d'un cerveau humain. Bien que le cerveau biologique soit d'une complexité infinie et que nous ne sachions probablement pas encore exactement comment il fonctionne dans sa totalité, les chercheurs en intelligence artificielle s'en inspirent pour bâtir leurs modèles. Le but est de reproduire, via un programme informatique, le mécanisme de raisonnement et de réflexion qu'un humain pourrait avoir après avoir acquis de la connaissance et du savoir. Contrairement à un programme classique qui suit des ordres fixes, le réseau neuronal "apprend" à partir d'exemples pour devenir capable de prendre des décisions de manière autonome face à de nouvelles situations. Ci-dessous, vous pourrez voir la représentation d'un réseau neuronal :

Représentation schématique d'un réseau neuronal
Schéma d'un réseau de neurones avec des entrées (vert), des couches cachées (bleu) et une sortie (jaune).

Vous verrez souvent, dans différents tutoriels ou supports, que le réseau neuronal est toujours présenté de cette manière. Parfois avec plus de cercles ou plus de colonnes, ou à l'inverse, avec moins de neurones. En réalité, chaque colonne représente une couche de neurones.

02.1 Les couches de neurones

Avant de continuer, j'ai mentionné les couches, mais concrètement, c'est quoi ? Imaginez que le réseau neuronal est une usine et que chaque neurone est un ouvrier. Chaque colonne (voir le schéma ci-dessus) représente un atelier :

La 1ère colonne est la couche d'entrée : elle prend la matière brute et fait une première transformation. La seconde couche, qui est une couche interne, prend la pièce transformée pour la retravailler ou l'assembler avec d'autres matières brutes. La 3ème couche sur le schéma est aussi une couche interne, mais au lieu de prendre la matière première de notre couche d'entrée, elle prend la sortie de la seconde couche. Autrement dit, elle travaille les pièces créées par cette dernière pour les transformer encore une fois. Après que la 3ème couche a terminé son travail, on arrive sur la dernière couche, la couche de sortie, qui délivre le produit fini.

03.0 La création du réseau neuronal

03.1 À noter

Avant de continuer, sachez que le code utilisé ici provient du tutoriel NNFS. Si vous souhaitez consulter le livre et le tutoriel complet en anglais, vous pouvez les retrouver sur nnfs.io.

Si le code est libre de droit, le contenu pédagogique original appartient à l'auteur. L'article que j'écris ici s'inspire uniquement de ce que j'ai appris et compris de ces concepts, il est donc normal d'y trouver des similitudes. Le livre sera bien évidemment plus complet : de mon côté, je ne traiterai pas des formules mathématiques complexes. Pour ceux qui recherchent une plus grande profondeur théorique, je vous invite vivement à vous procurer son ouvrage.

03.2 L'entrée, le biais et le poids

Comme dans une usine, il y a la matière première qui doit entrer : chez nous, c'est la donnée ou l'information. Comme toute information, elle possède une importance plus ou moins grande, que l'on appelle ici un poids.

Il y a aussi le biais. En programmation, si une variable ne contient aucune valeur (zéro ou nulle), la fonction peut tout simplement ne pas s'activer. Dans notre réseau neuronal, cela poserait un problème : nous avons besoin que le neurone puisse transmettre quelque chose malgré tout. Si un neurone ne produit rien en sortie, il faut que le suivant puisse quand même s'activer pour éviter tout blocage de notre chaîne de production. Le biais sert donc de "valeur de base" pour garantir ce flux.

Je m'explique : imaginez une usine qui fabrique des voitures. Un client veut l'option climatisation, mais un autre ne la souhaite pas. Il ne faut pas que la construction de la voiture de ce second client soit bloquée chez l'ouvrier chargé d'installer la climatisation. Il doit pouvoir passer la main au suivant (souvenez-vous de mon explication précédente : un neurone est un ouvrier). Le biais permet de s'assurer que le travail continue, même en l'absence de certaines données d'entrée.

Le poids c'est quoi ? C'est en réalité l'importance que l'intelligence artificielle va donner à l'information entrée. Pour continuer avec la production de voiture, vous imaginez bien que toutes les pièces qui sont données n'ont pas la même importance pour le fonctionnement de la voiture. Le moteur c'est l'une des pièces les plus importantes pour son fonctionnement et le logo par exemple, qui est mis à l'avant ou à l'arrière, n'a pas du tout d'importance pour le bon fonctionnement de la voiture.

LOGIQUE DU BIAIS : Le point de départ
BIAIS Le biais déplace le point de départ de la fonction.
LOGIQUE DU POIDS : L'angle d'influence
RÉSULTAT Le poids définit l'angle de la pente (inclinaison).

03.3 class layer_denses

Bien que ce soit des vulgarisations théoriques, voici la pratique. Il faudra donc les bases de la programmation en Python.

dense_layer.py
        import numpy as np
        class Layer_Dense:
            def __init__(self, n_inputs, n_neurons):
                # Initialisation des poids (W) et des biais (b)
                self.weights = 0.10 * np.random.randn(n_inputs, n_neurons)
                self.biases = np.zeros((1, n_neurons)
            def forward(self, inputs):
                # Stockage des entrées pour la suite
                self.inputs = inputs
                # Calcul de la sortie : Y = (X . W) + b
                self.output = np.dot(inputs,
                self.weights) + self.biases

Concrètement, on fait quoi là-dedans ?

Ici, nous avons créé une classe Layer_Dense. Pour faire court : à chaque fois que nous allons créer une couche (que ce soit notre couche d'entrée, nos couches internes ou notre couche de sortie), nous allons l'appeler.

Dans cette classe, on initie deux paramètres essentiels : n_inputs et n_neurons.

  • n_inputs : C'est le nombre d'entrées que l'on va recevoir. Pour reprendre le fonctionnement de l'usine, c'est le nombre de matières premières qui entrent pour être transformées.
  • n_neurons : C'est le nombre d'ouvriers qui vont travailler ensemble dans l'atelier.

Si vous voulez aller plus loin, il faudra vous pencher sur ce que sont les matrices et les vecteurs. Personnellement, comme je veux m'adresser à un public large, je ne vais pas rentrer dans le détail mathématique pur.

Dans ce code, le poids possède déjà une valeur de base (0,1) qui est multipliée par un chiffre pris au hasard. Cela permet de créer une distinction entre chaque ouvrier dès le départ. Le biais, quant à lui, génère un tableau de zéros. Selon la configuration, il contiendra au minimum un seul zéro et au maximum autant de zéros qu'il y a d'ouvriers (n_neurons).

03.4 la propagation et les fonctions d'activations

Maintenant que nous savons comment crée une couche denses (layer_dense), avec ses poid et ses biais, nous devont les faire communiquer. MAIS si on se contente de crée des couches denses et de faire passer les donées notre réseau neuronal ne seras pas si intelligent que cela.

On utilise ce qu'on appelle les fonctions d'activations. Il en existe plusieurs, comme la fonction Sigmoid, ReLU ou Softmax. Bien sûr, il y en a plein d'autres, mais nous allons nous concentrer sur ReLU et Softmax. Ce sont les fonctions les plus utilisées aujourd'hui et c'est sur celles-ci que nous allons porter notre attention.

La propagation

la propagation désigne le fais de propagé dans tout le réseau neuronnal les donnée afin quel soit traité par toute les couches afin que le réseaux puisse sortir une réponses

Animation de la propagation avant dans un réseau de neurones
Animation montrant la propagation des données de gauche à droite à travers les couches du réseau neuronal.

Fonction d'activation Relu

La fonction ReLU (Rectified Linear Unit) : en français, on pourrait dire que c'est une fonction qui permet de rectifier le signal linéaire. bon comme cela on comprend pas trop ce quelle permet de faire mais comme nous faison de la programmations on fait aussi des maths bien que je fais le choix de pas trop en parler cette fois on est un peu obligé le but de la fonction relu c'est que si en sortie de la couche denses il y'a une donnée négative il faut qu'elle soit null afin de permettre d'évité d'avoir des neuronnes mortes et de les activé car pour la propagations nous avions besoins que les neuronnes s'active si elle ne le fais pas on pourras pas passé à la suite donc pas de sortie

Fonction d'activation Softmax

La fonction d'activation Softmax transforme les scores bruts de notre réseau en une distribution de probabilités. Chaque valeur devient un nombre décimal compris entre 0 et 1, représentant le niveau de confiance du réseau pour chaque classe.

Exemple concret :

Imaginons que notre réseau soit entraîné à reconnaître des animaux (chien, chat, lapin, mouton). Nous avons donc 4 sorties possibles. Lorsqu'on présente au réseau une image d'un chien, il calcule des scores pour chaque catégorie. Après le passage dans la fonction Softmax, on pourrait obtenir ceci :

  • Chien : 0.82
  • Lapin : 0.16
  • Chat : 0.01
  • Mouton : 0.01

Ce que cela signifie pour notre système : Notre IA est confiante à 82 % qu'il s'agit d'un chien. Elle hésite un peu avec le lapin (16 %), et très peu pour le chat et le mouton (1 % chacun).

La règle d'or : La somme de toutes ces probabilités fait toujours exactement 1 (ou 100 %). C’est cette propriété qui rend la classification possible : quand la confiance pour une classe augmente, celle des autres classes doit mécaniquement diminuer. C'est ce mécanisme qui permet au réseau de faire un choix éclairé.

maintenant que nous avons vue les fonctions d'activation et que nous avons crée notre classe layer_dense je vous propose qu'on passe à l'écriture des fonctions d'activations et qu'on fasse tournée notre réseaux neuronal

Implémentation : Activation ReLU

La fonction ReLU (Rectified Linear Unit) est l'un des piliers de l'apprentissage profond. Voici comment elle se traduit concrètement en Python :

class Activation_ReLU:
    def forward(self, inputs):
        # On sauvegarde les entrées pour la suite
        self.inputs = inputs
        # Le seuillage : on garde le max entre 0 et l'entrée
        self.output = np.maximum(0, inputs)

Analyse du code :

  • self.inputs = inputs : On stocke les données d'entrée dans l'instance de la classe. Cette étape est indispensable pour pouvoir calculer les gradients plus tard, lors de la phase d'apprentissage.
  • np.maximum(0, inputs) : C'est le cœur de l'algorithme. Cette fonction NumPy parcourt le tableau d'entrées et applique un seuil simple : toute valeur inférieure à 0 devient 0, tandis que les valeurs positives restent inchangées.

Le Softmax : Transformer des scores en confiance

class Activation_Softmax:
    def forward(self, inputs):
        # On ajuste les scores pour éviter que le calcul ne devienne fou
        exp_values = np.exp(inputs - np.max(inputs, axis=1, keepdims=True))
        
        # On divise chaque score par le total pour obtenir 100%
        self.output = exp_values / np.sum(exp_values, axis=1, keepdims=True)

En résumé :

  • L'idée : Le réseau donne des scores bruts, mais on ne sait pas quoi en faire. Le Softmax transforme ces scores en pourcentages lisibles qui, additionnés, valent toujours 100%.
  • L'astuce : La soustraction du maximum est juste une sécurité pour éviter d'avoir des nombres tellement gigantesques que l'ordinateur ne peut plus les calculer.

Le Pipeline : De la donnée brute à la décision

# 1. Création d'un dataset fictif (X)
# 3 échantillons avec 2 caractéristiques chacun
X = np.array([[1.0, 2.0],
            [3.0, 4.0],
            [5.0, 6.0]])

# 2. Le passage dans le pipeline
# Couche Dense : Multiplication matricielle
dense1.forward(X)
# ReLU : Filtrage des valeurs négatives
activation1.forward(dense1.output)
# Dense finale : Transformation vers la taille de sortie
dense2.forward(activation1.output)
# Softmax : Probabilités finales
activation2.forward(dense2.output)

print(activation2.output)

Analyse du processus :

  • L'interdépendance : Chaque étape (la sortie `output` d'un bloc) devient l'entrée du bloc suivant. C'est ce qui crée la continuité du flux c'est de la propagation.
  • La transformation : `X` ne change jamais de forme interne, mais il est "redessiné" à chaque étape par les poids de tes neurones, jusqu'à ce qu'il devienne une probabilité compréhensible par ton système.