Werkplaats / De werkplaats
In jouw eigen tempo
← Neurale netwerken
MODULE 04 · LES 11

Geef het signaal een bruikbaar begin

Kies een schaal op basis van fan-in en fan-out en meet wat er met activaties door meerdere lagen gebeurt.

65 min3 checkpoints
DIT GA JE ONTDEKKEN
  • Je initialiseert lagen reproduceerbaar met Xavier- of He-schaal.
  • Je onderscheidt variantie van het tweede moment en meet signaalgroei door een netwerk.

Een netwerk kan al vóór de eerste update zijn signaal kwijtraken of opblazen. Geef je alle verborgen neuronen dezelfde beginwaarden, dan kunnen ze bovendien dezelfde feature blijven leren. We kiezen daarom verschillende willekeurige gewichten, met een schaal die bij de laag past.

Begin bij de visualisatie: vergelijk klein, He en groot, en verhoog het aantal lagen. Dezelfde vaste normale trekkingen worden anders geschaald. Kijk naar het tweede moment, een straks uitgelegde maat voor activatiegrootte, én de variantie; de berekening gaat over echt doorgerekende activaties.

EEN ECHTE VOORWAARTSE PASSBlijft er na tien lagen nog signaal over?

64 voorbeelden, 16 features per laag, ReLU en nulbias. Dezelfde vaste normale trekkingen worden anders geschaald.

10^-1010^-510^010^510^10012345678Tweede moment E[a²] · logaritmische as
Laatste tweede moment0.768Laatste variantie0.674

Laag 0 is de invoer. De as is begrensd van 10⁻¹⁰ tot 10¹⁰. De gemeten waarden hieronder blijven onbegrensd; He is ook hier geen perfecte behoudsgarantie.

1. Meer ingangen vragen een andere schaal

Een neuron telt bijdragen van alle ingangen op. Als je meer bijdragen toevoegt zonder hun schaal te verkleinen, kan de spreiding van de som groeien. Fan-in telt de ingangen per neuron; fan-out telt de uitvoerneuronen van de laag.

We gebruiken twee startregels voor normaal verdeelde gewichten met gemiddelde nul. Xavier-normal houdt rekening met beide breedtes en past bij ongeveer lineaire of niet sterk verzadigde symmetrische activaties. He-normal kiest voor ReLU een grotere variantie: onder symmetrieaannames valt bij ReLU ongeveer de helft van het tweede moment weg.

σXavier2=2nin+nuitσHe2=2ninσ=σ2

Hier zijn n-in en n-uit de twee laagbreedtes, σ² de gewichtsvariantie en σ de standaardafwijking. Bij vier ingangen en twaalf uitgangen is de Xaviervariantie 2/16 = 0.125 en de Hevariantie 2/4 = 0.5. De standaardafwijkingen zijn ongeveer 0.353553 en 0.707107.

import numpy as np
fan_in, fan_out = 4, 12
varianten = [2/(fan_in+fan_out), 2/fan_in]
for variantie in varianten:
    print(variantie,
          round(np.sqrt(variantie), 6))
# 0.125 0.353553
# 0.5 0.707107

Maak startvariantie(fan_in,fan_out,method) voor xavier en he. We gebruiken gehele laagbreedtes; weiger niet-positieve fans aan beide kanten en onbekende methodes met ValueError. De functie geeft variantie terug, terwijl de normale generator straks standaardafwijking verwacht.

01
CHECKPOINTLaat de laagbreedte meetellen

2. Herhaalbare gewichten, verschillende neuronen

Maak initialiseer(fan_in,fan_out,method="he",seed=7) als tuple (w,b). W krijgt vorm (fan_in,fan_out) en de nulbias vorm (fan_out,). Nulbias is hier een vaste startkeuze; de verschillende gewichten doorbreken de symmetrie tussen neuronen.

Gebruik voor deze oefening np.random.default_rng(seed) als lokale generator. Zo kunnen we dezelfde beginlaag volgen zonder de globale toevalsreeks van andere experimenten te veranderen. Trek één matrix uit de normale verdeling met gemiddelde nul en de gekozen standaardafwijking.

rng = np.random.default_rng(7)
std = np.sqrt(2/(4+12))
w = rng.normal(0, std, size=(4,12))
b = np.zeros(12)
print(w.shape, b.shape)  # (4, 12) (12,)
nogmaals = np.random.default_rng(7)
zelfde = nogmaals.normal(0,std,(4,12))
print(np.array_equal(w, zelfde))  # True

Dezelfde seed herhaalt in dezelfde omgeving de laag; een andere seed geeft andere gewichten. Trek niet voor iedere kolom opnieuw met dezelfde seed, want dan maak je juist identieke neuronen. Een kleine steekproef hoeft bovendien niet exact de theoretische variantie te hebben.

02
CHECKPOINTMaak één reproduceerbare laag

3. Meet variantie én tweede moment

De visualisatie gebruikt zestien ingangen, dus He heeft daar standaardafwijking √(2/16), ongeveer 0.354. Toch blijft het signaal niet perfect gelijk. De schaalregels steunen op aannames; meet wat jouw concrete netwerk doet.

Het tweede moment is het gemiddelde kwadraat. In de formule benoemt a een activatiewaarde en betekent E[...] het gemiddelde over alle arraywaarden. Variantie trekt daar het kwadraat van het gemiddelde af; ReLU maakt een positief gemiddelde mogelijk, waardoor die twee maten uit elkaar gaan.

E[a2]=Var(a)+E[a]2

Voor [−1,1] zijn gemiddelde 0, variantie 1 en tweede moment 1. Na ReLU krijg je [0,1]: gemiddelde 0.5, variantie 0.25, tweede moment 0.5. Gebruik populatievariantie, dus ddof=0, omdat we deze concrete array beschrijven.

a = np.array([-1., 1.])
for waarden in [a, np.maximum(0,a)]:
    print(waarden.mean(), waarden.var(),
          (waarden*waarden).mean())
# 0.0 1.0 1.0
# 0.5 0.25 0.5

Maak activatiestatistiek(x,weights) als lijst dictionaries met mean, var en second. Neem eerst de invoer op; voer daarna telkens het matrixproduct en ReLU uit en voeg de statistieken van die laag toe. Laat invoer en gewichtsmatrices intact, ook bij verschillende laagbreedtes.

a = np.array([[-1.,1.], [1.,-1.]])
weights = [np.eye(2), np.ones((2,2))]
moments = [float((a*a).mean())]
for w in weights:
    a = np.maximum(0, a@w)
    moments.append(float((a*a).mean()))
print(moments)  # [1.0, 0.5, 1.0]
03
CHECKPOINTMeet wat er werkelijk door de lagen komt

Wat neem je mee?

  • Kies schaal op basis van laagbreedte en activatie; gebruik de wortel als standaardafwijking.
  • Een lokale seed maakt een beginlaag herhaalbaar zonder andere experimenten te verstoren.
  • Meet tweede moment en variantie apart; initialisatie geeft een bruikbaar begin, geen trainingsgarantie.

Hierna kies je hoeveel voorbeelden samen één trainingsupdate bepalen.

DE VOLGENDE STAPLeer met een handvol voorbeelden tegelijk
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
Laat de laagbreedte meetellen

Schrijf startvariantie(fan_in,fan_out,method) voor method="xavier" of "he"; weiger onbekende methodes en niet-positieve fans.

Een hint

De functie retourneert variantie; np.random.normal verwacht later de wortel daarvan als standaardafwijking.

02
Maak één reproduceerbare laag

Schrijf initialiseer(fan_in,fan_out,method="he",seed=7) als (w,b).

Een hint

Maak np.random.default_rng(seed), gebruik sqrt(startvariantie(...)) als scale en np.zeros(fan_out) als bias.

03
Meet wat er werkelijk door de lagen komt

Schrijf activatiestatistiek(x,weights) als lijst dictionaries met mean,var,second voor de invoer en elke ReLU-laag.

Een hint

Gebruik populatievariantie (ddof=0), a.mean(), a.var() en (a*a).mean().

Vastgelopen? Bekijk de voorbeeldoplossing
import numpy as np

def startvariantie(fan_in,fan_out,method):
    if fan_in<=0 or fan_out<=0 or method not in ("xavier","he"):
        raise ValueError("Ongeldige initialisatie")
    return 2/(fan_in+fan_out) if method=="xavier" else 2/fan_in

def initialiseer(fan_in,fan_out,method="he",seed=7):
    rng=np.random.default_rng(seed)
    w=rng.normal(0,np.sqrt(startvariantie(fan_in,fan_out,method)),size=(fan_in,fan_out))
    return w,np.zeros(fan_out)

def activatiestatistiek(x,weights):
    a=np.asarray(x,dtype=float)
    def stats(v):
        return {"mean":float(v.mean()),"var":float(v.var()),"second":float((v*v).mean())}
    result=[stats(a)]
    for w in weights:
        a=np.maximum(0,a@np.asarray(w,dtype=float))
        result.append(stats(a))
    return result

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