Skip to content
The loss curve

Chapitre 6 · 14 min

Empiler les couches

Assemble des neurones en couches, écris une passe avant de MLP et étends ton module de réseau de neurones local au-delà d’une seule ligne.

Au chapitre 5, tu as entraîné un unique et vu une droite glisser entre deux nuages de points. Cette droite était la seule chose qu’il pouvait faire. Sur des données comme XOR, un damier ou un cercle dans un cercle, il échoue.

La correction est simple : empiler des . Une couche est plusieurs en parallèle sur la même entrée. Chacun découpe sa propre ligne. Combine leurs sorties, et tu obtiens des courbes, des coins, des régions.

On va inspecter un () à deux couches, l’entraîner sur XOR, puis sauvegarder une passe avant locale réutilisable.

1. La couche linéaire

Une couche linéaire est y = Wx + b, où W est une matrice et b un vecteur de biais. Si x a longueur n et que la couche a out , W a la forme [out, n].

Chaque ligne de W est le vecteur de d’un .

Code · JavaScript

C’est le cheval de trait des réseaux denses. Lis-le une fois, habitue-toi aux shapes, puis réutilise-le partout.

2. La passe avant du MLP

On chaîne deux couches linéaires avec une non-linéarité entre les deux. Sans non-linéarité, deux couches linéaires s’effondrent en une seule couche linéaire. Ici, la non-linéarité est : relu(x) = max(0, x).

z1=W1x+b1h=ReLU(z1)z2=W2h+b2y^=σ(z2)\begin{aligned} z_1 &= W_1 x + b_1 \\ h &= \text{ReLU}(z_1) \\ z_2 &= W_2 \cdot h + b_2 \\ \hat{y} &= \sigma(z_2) \end{aligned}

Écris cette fonction. Le chapitre compare ta sortie à une référence sur quatre points de test.

Code · JavaScript

Si les barres correspondent, la passe avant est correcte. Non entraîné, le réseau prédit surtout du bruit. C’est normal : l’ donne du sens aux couches.

3. Entraîner sur XOR

XOR est le cas classique où un seul échoue : deux quadrants opposés valent 0, les deux autres valent 1. Aucune droite ne les sépare.

Entraîne un avec hidden size 8 sur un XOR bruité.

Code · JavaScript

Le panneau gauche montre les régions de décision : chaque pixel est coloré selon la probabilité prédite. Après , tu devrais voir un motif de damier. La à droite doit descendre, parfois avec des plateaux.

Si l’ échoue, augmente hiddenSize, iterations ou ajuste lr. La plupart des échecs de réseaux neuronaux se résument à capacité, temps ou taille de pas.

4. Étendre llm/nn.py

Ajoute ces helpers sous le code du :

# [1]
Vector = list[float]
Matrix = list[Vector]
 
 
# [2]
def relu(x: Vector) -> Vector:
    return [max(0.0, value) for value in x]
 
 
# [3]
def linear(x: Vector, weight: Matrix, bias: Vector) -> Vector:
    return [
        sum(xi * wi for xi, wi in zip(x, row)) + b
        for row, b in zip(weight, bias)
    ]
 
 
def mlp_forward(
    x: Vector,
    w1: Matrix,
    b1: Vector,
    w2: Matrix,
    b2: Vector,
) -> Vector:
    # [4]
    hidden = relu(linear(x, w1, b1))
    # [5]
    return linear(hidden, w2, b2)

Lis d’abord les shapes :

  • [1] nomme les formes : vecteur et matrice.
  • [2] relu garde les signaux positifs et coupe les négatifs à zéro.
  • [3] linear applique plusieurs à un même vecteur.
  • [4] crée des features cachées apprises.
  • [5] mélange ces features vers la sortie.

Le code reste en listes pour rendre chaque shape visible. Au chapitre 12, linear deviendra torch.nn.Linear, mais le contrat ne changera pas.

Ce que ça corrige (et ce que ça coûte)

Corrige : n’importe quelle frontière imaginable. Le théorème d’approximation universelle garantit qu’un avec assez de cachés peut approcher n’importe quelle fonction continue sur un domaine compact, avec une précision arbitraire. XOR, spirales, cercles concentriques, tout y passe.

Coûte :

  • Beaucoup plus de . Hidden size 8 avec 2 entrées, c’est 8×2 + 8 + 8 + 1 = 33 . Les vrais réseaux en ont des millions ou des milliards.
  • Minima locaux. La sur une non convexe peut rester coincée. Les modernes s’en sortent presque toujours parce que leurs paysages de sont indulgents en haute dimension, mais la théorie reste largement empirique.
  • . Hidden size, , initialisation, choix d’optimiseur — tout ça décide si l’ marche. Le chapitre 7 ouvre cette conversation.

Recap

  • Une couche linéaire est y = Wx + b. Une ligne de W par de sortie. - Un est au moins deux couches linéaires avec des non-linéarités (souvent ) entre elles. Sans non-linéarité, le réseau s’effondre en une seule fonction linéaire. - L’ est le même algorithme que pour un sur la — mais le traverse toutes les couches (règle de chaîne). - Les résolvent XOR. C’est l’exemple canonique, mais le principe se généralise : toute frontière descriptible, un avec assez de capacité peut l’apprendre. - Ton llm/nn.py contient maintenant linear, relu et mlp_forward, les mêmes briques utilisées dans les couches feed-forward des . - Le prochain chapitre regarde comment la trouve réellement ces — et pourquoi la naïve ne suffit en général pas.

Pour aller plus loin

Prochaine étape : la en direct — on a appelé mlpStep comme une boîte noire. Il est temps de regarder dedans, de voir pourquoi la vanilla cale souvent, et de rencontrer et .