Werkplaats / De werkplaats
In jouw eigen tempo
← Neurale netwerken
MODULE 05 · LES 15

Kies welke waarden samen een schaal krijgen

Implementeer LayerNorm en een kleine BatchNorm, inclusief bewaarde statistieken voor voorspellen.

90 min3 checkpoints
DIT GA JE ONTDEKKEN
  • Je onderscheidt normaliseren over features van normaliseren over voorbeelden.
  • Je bewaart trainingsstatistieken voor BatchNorm en verandert die niet tijdens evaluatie.

Een laag ontvangt activaties die sterk van schaal verschillen. Met welke andere waarden vergelijk je één activatie om haar te normaliseren? De keuze voor een rij of kolom bepaalt welke voorbeelden elkaar beïnvloeden.

We normaliseren hier tussenactivaties van een netwerk, met één voorbeeld per rij en één feature per kolom. Vooraf ruwe data schalen blijft een aparte stap. Je bouwt LayerNorm en een kleine BatchNorm, inclusief het verschil tussen trainen en evalueren.

1. Kies eerst wie bij dezelfde groep hoort

Verander in de visualisatie alleen de derde rij. Bij LayerNorm blijft de eerste uitvoerrij gelijk, omdat iedere rij zijn eigen groep vormt. Bij BatchNorm tijdens training delen voorbeelden per feature een groep, zodat de eerste rij wel kan veranderen.

DE AS BEPAALT WIE ELKAAR BEÏNVLOEDTNormaliseer per rij of per kolom

Verander alleen voorbeeld 3. Kijk of de uitvoer voor voorbeeld 1 mee verandert. γ=1, β=0 en ε=10⁻⁵.

Invoer X
Rijf1f2f3
1135
2246
3357
Genormaliseerd
Rijf1f2f3
1-1.220.001.22
2-1.220.001.22
3-1.220.001.22
Eerste uitvoerrij-1.225 · 0.000 · 1.225

Gemiddelden per rij: [3.00, 4.00, 5.00]. Elke rij gebruikt alleen zijn eigen features.

Neem X=[[1,3],[5,7]]. De rijgemiddelden zijn 2 en 6, met beide variantie 1. De kolomgemiddelden zijn 3 en 5, met beide variantie 4: dezelfde matrix levert dus verschillende normalisatiegroepen op.

import numpy as np
x = np.array([[1.,3.], [5.,7.]])
print(x.mean(axis=1).tolist())  # [2.0, 6.0]
print(x.var(axis=1).tolist())   # [1.0, 1.0]
print(x.mean(axis=0).tolist())  # [3.0, 5.0]
print(x.var(axis=0).tolist())   # [4.0, 4.0]

Gamma geeft elke feature een schaalfactor en beta een verschuiving. Dit zijn parameters die een netwerk kan leren; in de opdrachten geven we hun waarden zelf mee. Epsilon is een kleine positieve stabilisator, zodat ook een constante groep veilig verwerkt wordt.

z=x−μv+εuitvoer=γ⊙z+β

Mu en v beschrijven de gekozen groep; ⊙ betekent elementgewijs vermenigvuldigen. Hier staat epsilon binnen de wortel. De tussenstap z heeft groepsgemiddelde nul en ongeveer variantie één als epsilon klein is ten opzichte van de groepsvariantie; na gamma en beta hoeft dat niet meer zo te zijn.

Maak layernorm(x,gamma,beta,epsilon=1e-5) als nieuwe NumPy-matrix. Neem gemiddelde en populatievariantie per rij, dus axis=1; keepdims=True bewaart een kolom die langs de features wordt uitgezonden. Laat alle invoer intact.

def layernorm(x,gamma,beta,epsilon=1e-5):
    x = np.asarray(x,dtype=float)
    mean = x.mean(axis=1,keepdims=True)
    var = x.var(axis=1,keepdims=True)
    z = (x-mean)/np.sqrt(var+epsilon)
    return np.asarray(gamma)*z+beta

out = layernorm(x,[2,3],[.5,-.5])
print(np.round(out,4).tolist())
# [[-1.5, 2.5], [-1.5, 2.5]]

Zonder de stabilisator zijn de genormaliseerde rijen hier [−1,1]. De affine stap geeft dan [−1.5,2.5]; de code benadert dit door epsilon. Bij een constante rij wordt z juist nul en blijft alleen beta over.

01
CHECKPOINTVergelijk features binnen één rij

2. Train met batchstatistieken en bewaar een lopende schatting

BatchNorm gebruikt dezelfde formule, nu met gemiddelde en variantie per kolom, axis=0. Voor onze matrix zijn de genormaliseerde rijen ongeveer [−1,−1] en [1,1]. Met dezelfde gamma en beta worden de uitkomsten ongeveer [−1.5,−3.5] en [2.5,2.5].

Bewaar daarnaast lopende schattingen voor later. Decay is hier het gewicht van de oude state; een waarde 0.5 mengt oud en batch gelijk. Gebruik de huidige batch voor de trainingsuitvoer en de gemengde statistieken alleen als nieuwe state.

Snieuw=dSoud+(1−d)Sbatch

S staat voor mean of var en d voor decay. Vanuit mean=[0,0] en var=[1,1] geeft onze batch bij decay=0.5 de nieuwe mean=[1.5,2.5] en var=[2.5,2.5]. Maak batchnorm_train als (out,new_state), met een nieuwe matrix en dictionary; wijzig geen bestaande arrays.

def batchnorm_train(x,gamma,beta,state,
                    decay=.9,epsilon=1e-5):
    x = np.asarray(x,dtype=float)
    mean, var = x.mean(axis=0),x.var(axis=0)
    z = (x-mean)/np.sqrt(var+epsilon)
    out = np.asarray(gamma)*z+beta
    old_m = np.asarray(state['mean'])
    old_v = np.asarray(state['var'])
    updated = dict(
        mean=decay*old_m+(1-decay)*mean,
        var=decay*old_v+(1-decay)*var)
    return out, updated

state = dict(mean=np.zeros(2),
             var=np.ones(2))
out, new = batchnorm_train(
    x,[2,3],[.5,-.5],state,decay=.5)
print(np.round(out,4).tolist())
# [[-1.5, -3.5], [2.5, 2.5]]
print(new['mean'].tolist())  # [1.5, 2.5]
print(new['var'].tolist())   # [2.5, 2.5]

Deze educatieve variant gebruikt populatievariantie bij trainen én bewaren. Bibliotheken kunnen voor het bewaren een andere variantieschatter kiezen. De twee afspraken bewust vastleggen voorkomt dat verschillende implementaties onterecht als dezelfde berekening worden vergeleken.

02
CHECKPOINTGebruik de batch tijdens trainen

3. Evalueer met opgeslagen statistieken

Tijdens evaluatie hoort BatchNorm de bewaarde mean en var te gebruiken. Eén testvoorbeeld krijgt daardoor dezelfde uitvoer wanneer je andere testvoorbeelden toevoegt. Statistieken van de testbatch opnieuw gebruiken zou de voorspelling afhankelijk maken van die andere voorbeelden.

Voor een kleine controle geven we een opgeslagen state met mean=[2,3] en var=[4,9]. Invoer [4,6] wordt vóór de affine stap ongeveer [1,1]. Met gamma=[2,0.5] en beta=[1,−1] wordt de uitvoer ongeveer [3,−0.5].

def batchnorm_eval(x,gamma,beta,state,
                   epsilon=1e-5):
    x = np.asarray(x,dtype=float)
    mean = np.asarray(state['mean'])
    var = np.asarray(state['var'])
    z = (x-mean)/np.sqrt(var+epsilon)
    return np.asarray(gamma)*z+beta

saved = dict(mean=np.array([2.,3.]),
             var=np.array([4.,9.]))
one = batchnorm_eval([[4,6]],
                      [2,.5],[1,-1],saved)
print(np.round(one,4).tolist())
# [[3.0, -0.5]]

Maak batchnorm_eval met een nieuwe NumPy-matrix en ongewijzigde state. LayerNorm heeft deze lopende batchstate niet nodig: zij normaliseert in beide modi per eigen rij. De keuze voor train- of evaluatiemodus hoort dus bij de betekenis van de laag.

together = batchnorm_eval(
    [[4,6],[10000,-20000]],
    [2,.5],[1,-1],saved)
print(np.array_equal(one,together[:1]))
# True
print(saved['mean'].tolist())  # [2.0, 3.0]
03
CHECKPOINTVoorspel met bewaarde statistieken

Wat neem je mee?

  • De gekozen as bepaalt welke waarden samen een normalisatiegroep vormen.
  • Gamma en beta geven een leerbare affine stap; epsilon stabiliseert de wortelnoemer.
  • BatchNorm bewaart trainingsstatistieken en gebruikt die bij evaluatie; LayerNorm blijft per rij rekenen.

Hierna beperk je overfitting met een gewichtsstraf en dropout.

Verder lezen & bronnen

Inhoud gecontroleerd op 2026-10-28.
DE VOLGENDE STAPBeperk afhankelijkheid van één toevallige fit
JOUW EXPERIMENT

Probeer het zelf

Python
main.py

Python start zodra jij code uitvoert.

UITVOER

Je uitvoer verschijnt hier.
Alles rekent in je eigen browser.

Jouw checkpoints0 / 3
01
Vergelijk features binnen één rij

Schrijf layernorm(x,gamma,beta,epsilon=1e-5) met normalisatie over axis=1.

Een hint

Gebruik keepdims=True voor rijgemiddelde en variantie, zodat broadcasting langs features werkt.

02
Gebruik de batch tijdens trainen

Schrijf batchnorm_train(x,gamma,beta,state,decay=.9,epsilon=1e-5) als (out,new_state), met state-sleutels mean,var.

Een hint

Bereken mean/var over axis=0. Gebruik batchstatistieken voor out en de gemengde waarden alleen voor new_state.

03
Voorspel met bewaarde statistieken

Schrijf batchnorm_eval(x,gamma,beta,state,epsilon=1e-5), zonder state te wijzigen.

Een hint

De formule is dezelfde, maar mean en var komen uitsluitend uit state.

Vastgelopen? Bekijk de voorbeeldoplossing
import numpy as np

def layernorm(x,gamma,beta,epsilon=1e-5):
    x=np.asarray(x,dtype=float)
    mean=x.mean(axis=1,keepdims=True)
    variance=x.var(axis=1,keepdims=True)
    return np.asarray(gamma)*(x-mean)/np.sqrt(variance+epsilon)+np.asarray(beta)

def batchnorm_train(x,gamma,beta,state,decay=.9,epsilon=1e-5):
    x=np.asarray(x,dtype=float)
    mean=x.mean(axis=0); variance=x.var(axis=0)
    out=np.asarray(gamma)*(x-mean)/np.sqrt(variance+epsilon)+np.asarray(beta)
    updated={"mean":decay*np.asarray(state["mean"])+(1-decay)*mean,"var":decay*np.asarray(state["var"])+(1-decay)*variance}
    return out,updated

def batchnorm_eval(x,gamma,beta,state,epsilon=1e-5):
    x=np.asarray(x,dtype=float)
    return np.asarray(gamma)*(x-np.asarray(state["mean"]))/np.sqrt(np.asarray(state["var"])+epsilon)+np.asarray(beta)

Voer de code uit en verklaar voor jezelf waarom elke stap werkt.