« Professeur, pourquoi on n'utilise pas Python comme tout le monde ? — Parce que vous êtes en cours de programmation fonctionnelle, pas en cours de "taper pip install et prier". — Mais… TensorFlow… — TensorFlow, c'est pour les gens qui ne savent pas faire une multiplication de matrices à la main. Et vous, vous allez la faire à la main. En OCaml. Avec des listes. Comme des vrais. »
Introduction
Rappel du cours 3 : Monade option (gestion d'erreur), Monade Writer (journalisation), Pattern général retour / >>=.
Ce cours : on implémente un mini-réseau de neurones en OCaml pur — neurone, couche, réseau, gradient, descente.
Tous les programmes présentés ici sont disponibles dans le répertoire du cours.
Neurone artificiel
f(x, w, b) = σ(∑ wi xi + b)
type neurone = { w : float list; b : float }
let sigmoid x =
1.0 /. (1.0 +. exp (-. x))
let forward n x =
let sum = List.fold_left (+.) 0.0
(List.map2 ( *. ) n.w x) in
sigmoid (sum +. n.b)
Calculez la sortie du neurone w = [0.5; -0.3], b = 0.1 pour x = [1.0; 2.0]. Vérifiez à la main.
Propagation avant (forward)
Une couche = une liste de neurones.
type layer = { neurones : neurone list }
let forward_layer l x =
List.map (fun n -> forward n x) l.neurones
Réseau = liste de couches.
type reseau = { couches : layer list }
let forward_network r x =
List.fold_left (fun x layer ->
forward_layer layer x) x r.couches
« Un réseau de neurones, c'est comme un buffet à volonté : vous mangez (forward), vous regrettez (backward), et vous ajustez votre stratégie pour le prochain plat (SGD). »
Fonction de coût (loss)
On mesure l'erreur entre prédiction et cible — Erreur quadratique moyenne (MSE) :
let mse pred target =
List.map2 (fun p t -> (p -. t) ** 2.0) pred target
|> List.fold_left (+.) 0.0
|> (fun s -> s /. float_of_int (List.length pred))
Gradient : dérivée de la sigmoïde
Pour corriger les poids, on suit le gradient.
let sigmoid_deriv sigma =
sigma *. (1.0 -. sigma)
Pourquoi c'est utile : σ'(x) s'exprime facilement à partir de σ(x).
Vérifiez : si σ(x) = 0.8, que vaut σ'(x) ? Si σ(x) = 0.5, que vaut σ'(x) ? Que remarquez-vous ?
Gradient pas à pas
Pour un neurone de sortie :
- Erreur : δo = (p - t) · σ'(x)
- Mise à jour poids : wi ← wi - α · δo · xi
- Mise à jour biais : b ← b - α · δo
let gradient_sortie n x p t =
let delta = (p -. t) *. sigmoid_deriv p in
let dw = List.map (fun xi ->
-. alpha *. delta *. xi) x in
let db = -. alpha *. delta in
(dw, db)
Rétropropagation (backpropagation)
Pour une couche cachée, δj se propage :
let gradient_cache n inputs deltas_next w_next =
let sum = List.fold_left2 (fun acc d w ->
acc +. d *. w) 0.0 deltas_next w_next in
let out = forward n inputs in
let delta = sum *. sigmoid_deriv out in
let dw = List.map (fun i ->
-. alpha *. delta *. i) inputs in
let db = -. alpha *. delta in
(dw, db, delta)
Descente de gradient stochastique (SGD)
Algorithme complet :
- Forward : calculer toutes les sorties
- Backward : calculer les δ de la sortie vers l'entrée
- Mettre à jour tous les poids et biais
- Recommencer (epoch)
let rec train r data epochs alpha =
if epochs <= 0 then r
else
let r' = List.fold_left (fun r (x, t) ->
let activations = forward_all r x in
let gradients =
backward_all r x activations t alpha in
update_weights r gradients
) r data in
train r' data (epochs - 1) alpha
« La descente de gradient, c'est comme descendre une montagne dans le brouillard : vous ne voyez pas le sommet, mais vous sentez la pente. SGD, c'est le faire en courant, les yeux fermés, en espérant ne pas tomber dans une rivière. »
Test : XOR avec un réseau 2 → 2 → 1
let data = [
([0.0;0.0], [0.0]); ([0.0;1.0], [1.0]);
([1.0;0.0], [1.0]); ([1.0;1.0], [0.0]);
]
let reseau_xor = init_reseau [2; 2; 1]
let trained = train reseau_xor data 10000 0.5
Après entraînement :
# forward_network trained [1.0;0.0] -> ~0.95 (proche de 1)
# forward_network trained [0.0;0.0] -> ~0.05 (proche de 0)
Conclusion
Félicitations ! Vous avez survécu aux 4 cours. Vous êtes capables d'écrire un réseau de neurones en OCaml pur, sans bibliothèque externe, sans boucle while, sans effet de bord.
« Si vous pouvez faire du machine learning en OCaml pur, vous pouvez tout faire en OCaml pur. Maintenant, allez coder. »