- 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.
Verander alleen voorbeeld 3. Kijk of de uitvoer voor voorbeeld 1 mee verandert. γ=1, β=0 en ε=10⁻⁵.
| Rij | f1 | f2 | f3 |
|---|---|---|---|
| 1 | 1 | 3 | 5 |
| 2 | 2 | 4 | 6 |
| 3 | 3 | 5 | 7 |
| Rij | f1 | f2 | f3 |
|---|---|---|---|
| 1 | -1.22 | 0.00 | 1.22 |
| 2 | -1.22 | 0.00 | 1.22 |
| 3 | -1.22 | 0.00 | 1.22 |
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.
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.
012. 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.
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.
023. 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]
03Wat 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.