Faza 03 · lecția 08

Inițializarea ponderilor și stabilitatea antrenării

Scopul lecției: Inițializați greșit și antrenarea nu începe. Inițializați potrivit și 50 de straturi se pot antrena aproape la fel de lin ca trei.

Versiunea curentă AlexBred.com: primele 100 de lecții ale programului în limba română.

Curs
AI Engineering from Scratch
Fază
Fundamentele învățării profunde
Lectură
16 min.
Verificat
Cuprinsul lecției
  1. Obiective de învățare
  2. Problema
  3. Conceptul
  4. Problema simetriei
  5. Propagarea varianței prin straturi
  6. Inițializarea Xavier/Glorot
  7. Inițializarea Kaiming/He
  8. Inițializarea transformatoarelor
  9. Amplitudinea activărilor prin 50 de straturi
  10. Alegerea inițializării
  11. Construiți
  12. Pasul 1: strategii de inițializare
  13. Pasul 2: funcțiile de activare
  14. Pasul 3: propagarea înainte prin 50 de straturi
  15. Pasul 4: experimentul
  16. Pasul 5: demonstrarea simetriei
  17. Pasul 6: raportul amplitudinii strat cu strat
  18. Folosiți
  19. Livrați
  20. Exerciții
  21. Termeni-cheie
  22. Lecturi suplimentare

Inițializați greșit și antrenarea nu începe. Inițializați potrivit și 50 de straturi se pot antrena aproape la fel de lin ca trei.

Tip: Construire Limbaje: Python Cerințe preliminare: Lecția 03.04 — Funcții de activare; lecția 03.07 — Regularizare Durată: ~90 de minute

Obiective de învățare

  • Să implementați inițializarea cu zero, aleatoare, Xavier/Glorot și Kaiming/He și să măsurați efectul asupra activărilor prin 50 de straturi.
  • Să deduceți de ce inițializarea Xavier folosește Var(w) = 2/(fan_in + fan_out), iar Kaiming folosește Var(w) = 2/fan_in.
  • Să demonstrați problema simetriei la inițializarea cu zero și să explicați de ce o scară aleatoare arbitrară nu este suficientă.
  • Să asociați strategia de inițializare cu activarea: Xavier pentru sigmoidă și tanh, Kaiming pentru ReLU și, ca punct de pornire, pentru activări asemănătoare ReLU.

Problema

Inițializați toate ponderile cu zero. Nimic nu învață în configurația complet simetrică. Fiecare neuron calculează aceeași funcție, primește același gradient și se actualizează identic. După 10.000 de epoci, stratul ascuns cu 512 neuroni poate rămâne format din 512 de copii ale aceluiași neuron. Ați plătit pentru lățime, dar ați obținut o singură caracteristică repetată.

Inițializați ponderile cu valori prea mari. Activările pot exploda prin rețea. Într-un exemplu, la stratul 10 valorile ajung la 1e15, iar la stratul 20 produc overflow până la infinit. Gradienții pot urma în sens invers o dinamică la fel de instabilă.

Inițializați-le aleator din distribuția normală standard. Metoda poate funcționa pentru trei straturi, dar la 50 semnalul poate dispărea sau exploda, în funcție de scară, arhitectură și activare. Intervalul scalelor stabile se poate îngusta odată cu profunzimea.

Inițializarea ponderilor este o decizie esențială în învățarea profundă. Arhitectura primește lucrări, optimizatorii primesc articole, iar inițializarea uneori doar o notă de subsol. Dacă scările inițiale sunt nepotrivite, activările și gradienții pot fi inutilizabili înainte ca optimizatorul să aibă șansa de a corecta modelul.

Notă tehnică a traducerii: Inițializarea tuturor ponderilor cu aceeași valoare păstrează simetria numai când și restul arhitecturii și actualizărilor sunt simetrice. Biasurile diferite, ponderile de ieșire diferite sau alte surse de aleatorietate pot rupe simetria; afirmația originală descrie cazul uzual în care toți parametrii relevanți pornesc identic.

Conceptul

Problema simetriei

Fiecare neuron dintr-un strat are aceeași structură: înmulțește intrările cu ponderile, adaugă un bias și aplică activarea. Dacă toate ponderile pornesc cu aceeași valoare — zero este cazul extrem —, fiecare neuron calculează aceeași ieșire. În retropropagare, neuronii simetrici primesc aceiași gradienți și se modifică la fel.

Rețeaua rămâne blocată în subspațiul simetric. Are sute de parametri, dar aceștia se deplasează sincron. Inițializarea aleatoare este metoda directă de rupere a simetriei: fiecare neuron pornește dintr-un punct diferit al spațiului ponderilor și poate învăța altă caracteristică.

Dar „aleator” nu este suficient. Scara distribuției determină dacă semnalul și gradienții rămân utilizabili.

Propagarea varianței prin straturi

Considerați un singur strat cu fan_in intrări:

z = w1*x1 + w2*x2 + ... + w_n*x_n

Dacă fiecare pondere wi este extrasă dintr-o distribuție cu varianța Var(w), iar fiecare intrare xi are varianța Var(x), atunci, sub ipotezele uzuale de medie zero și independență sau necorelare:

Var(z) = fan_in * Var(w) * Var(x)

Dacă Var(w) = 1 și fan_in = 512, varianța ieșirii este de 512 ori varianța intrării. După zece straturi, factorul idealizat devine 512^10 = 1.2e27, iar semnalul explodează.

Dacă Var(w) = 0.001, varianța se înmulțește cu 0.001 * 512 = 0.512 la fiecare strat. După zece straturi: 0.512^10 ≈ 0.00124. Semnalul s-a micșorat drastic.

Notă tehnică a traducerii: Sursa dă eronat 0.512^10 = 0.00013; valoarea corectă este aproximativ 0.00124. Formula varianței presupune ponderi și intrări cu medie zero, termeni necorelați și bias neglijat. Aceste ipoteze nu se păstrează exact după activări neliniare sau în timpul antrenării.

Obiectivul propagării înainte este alegerea lui Var(w) astfel încât scara lui Var(z) să rămână comparabilă cu Var(x). Pentru propagarea înapoi trebuie controlată și varianța gradienților.

Inițializarea Xavier/Glorot

Glorot și Bengio au propus în 2010 o soluție de compromis pentru fluxul înainte și înapoi, potrivită activărilor cu comportament aproximativ liniar în jurul originii:

Var(w) = 2 / (fan_in + fan_out)

În practică, ponderile pot fi extrase din:

w ~ Uniform(-limit, limit)  unde limit = sqrt(6 / (fan_in + fan_out))

sau:

w ~ Normal(0, sqrt(2 / (fan_in + fan_out)))

Această inițializare este folosită frecvent cu tanh și sigmoidă deoarece păstrează preactivările într-o zonă mai puțin saturată. Poate stabiliza scara prin multe straturi, dar nu garantează antrenarea oricărei rețele sigmoidale profunde.

Notă tehnică a traducerii: Când fan_in != fan_out, o singură varianță nu poate satisface simultan condițiile ideale înainte și înapoi; formula Glorot este un compromis. Sigmoida logistică are și ieșire cu medie pozitivă, astfel încât ipotezele simple de medie zero nu se păstrează.

Inițializarea Kaiming/He

ReLU aduce la zero valorile negative. Pentru intrări simetrice, aproximativ jumătate dintre activări devin zero, iar al doilea moment este redus. Inițializarea Xavier nu compensează complet acest efect.

He și colaboratorii au ajustat în 2015 formula:

Var(w) = 2 / fan_in

Ponderile sunt extrase din:

w ~ Normal(0, sqrt(2 / fan_in))

Factorul 2 compensează, în analiza idealizată, anularea jumătății negative prin ReLU. Fără această compensare, al doilea moment s-ar putea micșora aproximativ cu 0,5 la fiecare strat. După 50 de straturi, 0.5^50 = 8.8e-16. Inițializarea Kaiming urmărește să prevină acest colaps.

Notă tehnică a traducerii: Formula 2/fan_in este dedusă pentru rectificatoare și ipoteze statistice specifice. GELU și Swish nu anulează exact jumătate dintre valori, deci Kaiming pentru ReLU este doar un punct de pornire, nu o potrivire teoretică exactă pentru aceste activări.

Inițializarea transformatoarelor

GPT-2 a popularizat un tipar suplimentar. Conexiunile reziduale adaugă ieșirea fiecărui subnivel la intrare:

x = x + sublayer(x)

Dacă ramurile reziduale ar contribui independent cu aceeași varianță, fiecare adunare ar mări varianța. În implementările de tip GPT-2, ponderile proiecțiilor reziduale sunt inițializate cu o abatere standard redusă prin factorul 1/sqrt(2N), unde N este numărul straturilor. Factorul 2 reflectă cele două ramuri reziduale principale pe bloc.

Notă tehnică a traducerii: Afirmația că Llama 3 folosește aceeași schemă 1/sqrt(2N) nu este susținută de descrierea sau implementarea publică standard Llama, care folosește o abatere initializer_range pentru straturile liniare. Strategiile de scalare reziduală diferă între familiile de transformatoare și nu trebuie transferate prin analogie.

Диаграмма к уроку «Inițializarea ponderilor și stabilitatea antrenării»

Amplitudinea activărilor prin 50 de straturi

Диаграмма к уроку «Inițializarea ponderilor și stabilitatea antrenării»

Notă tehnică a traducerii: Valorile originale „~1,0 la stratul 50” nu rezultă din teorie și nici din metrica scriptului. Experimentul măsoară media valorilor absolute, nu varianța; pentru sigmoidă, ieșirile sunt centrate în jurul lui 0,5, nu 1. Xavier și Kaiming urmăresc în principal momente statistice sub ipoteze idealizate, nu o amplitudine medie universală.

Alegerea inițializării

Диаграмма к уроку «Inițializarea ponderilor și stabilitatea antrenării»

Notă tehnică a traducerii: Intervalul original 0,5–2,0 pentru amplitudini nu este un criteriu universal. Monitorizați media, varianța, fracția de unități saturate sau inactive și normele gradienților în raport cu activarea și normalizarea folosite.

weight-init-variance

Construiți

Pasul 1: strategii de inițializare

Patru moduri de a inițializa o matrice de ponderi. Fiecare întoarce o listă de liste — o matrice bidimensională — cu fan_in coloane și fan_out rânduri.

import math
import random


def zero_init(fan_in, fan_out):
    return [[0.0 for _ in range(fan_in)] for _ in range(fan_out)]


def random_init(fan_in, fan_out, scale=1.0):
    return [[random.gauss(0, scale) for _ in range(fan_in)] for _ in range(fan_out)]


def xavier_init(fan_in, fan_out):
    std = math.sqrt(2.0 / (fan_in + fan_out))
    return [[random.gauss(0, std) for _ in range(fan_in)] for _ in range(fan_out)]


def kaiming_init(fan_in, fan_out):
    std = math.sqrt(2.0 / fan_in)
    return [[random.gauss(0, std) for _ in range(fan_in)] for _ in range(fan_out)]

Pasul 2: funcțiile de activare

Avem nevoie de sigmoidă, tanh și ReLU pentru a testa fiecare strategie cu activarea căreia îi este destinată.

def sigmoid(x):
    x = max(-500, min(500, x))
    return 1.0 / (1.0 + math.exp(-x))


def tanh_act(x):
    return math.tanh(x)


def relu(x):
    return max(0.0, x)

Pasul 3: propagarea înainte prin 50 de straturi

Propagați date aleatoare printr-o rețea profundă și măsurați amplitudinea medie a activării la fiecare strat.

def forward_deep(init_fn, activation_fn, n_layers=50, width=64, n_samples=100):
    random.seed(42)
    layer_magnitudes = []

    inputs = [[random.gauss(0, 1) for _ in range(width)] for _ in range(n_samples)]

    for layer_idx in range(n_layers):
        weights = init_fn(width, width)
        biases = [0.0] * width

        new_inputs = []
        for sample in inputs:
            output = []
            for neuron_idx in range(width):
                z = sum(weights[neuron_idx][j] * sample[j] for j in range(width)) + biases[neuron_idx]
                output.append(activation_fn(z))
            new_inputs.append(output)
        inputs = new_inputs

        magnitudes = []
        for sample in inputs:
            magnitudes.append(sum(abs(v) for v in sample) / width)
        mean_mag = sum(magnitudes) / len(magnitudes)
        layer_magnitudes.append(mean_mag)

    return layer_magnitudes

Notă tehnică a traducerii: Funcția nu măsoară varianța și nici gradienții, deși lecția motivează inițializările prin ambele. Pentru un diagnostic complet, înregistrați media și varianța preactivărilor și activărilor și executați o propagare înapoi pentru normele gradienților.

Pasul 4: experimentul

Rulați toate combinațiile: zero, aleator N(0,1), aleator N(0,0.01), Xavier cu sigmoidă, Xavier cu tanh și Kaiming cu ReLU. Afișați amplitudinea la straturile-cheie.

def run_experiment():
    configs = [
        ("Zero init + Sigmoid", lambda fi, fo: zero_init(fi, fo), sigmoid),
        ("Random N(0,1) + ReLU", lambda fi, fo: random_init(fi, fo, 1.0), relu),
        ("Random N(0,0.01) + ReLU", lambda fi, fo: random_init(fi, fo, 0.01), relu),
        ("Xavier + Sigmoid", xavier_init, sigmoid),
        ("Xavier + Tanh", xavier_init, tanh_act),
        ("Kaiming + ReLU", kaiming_init, relu),
    ]

    print(f"{'Strategy':<30} {'L1':>10} {'L5':>10} {'L10':>10} {'L25':>10} {'L50':>10}")
    print("-" * 80)

    for name, init_fn, act_fn in configs:
        mags = forward_deep(init_fn, act_fn)
        row = f"{name:<30}"
        for idx in [0, 4, 9, 24, 49]:
            val = mags[idx]
            if val > 1e6:
                row += f" {'EXPLODED':>10}"
            elif val < 1e-6:
                row += f" {'VANISHED':>10}"
            else:
                row += f" {val:>10.4f}"
        print(row)

Pasul 5: demonstrarea simetriei

Arătați că inițializarea cu zero produce neuroni identici.

def symmetry_demo():
    random.seed(42)
    weights = zero_init(2, 4)
    biases = [0.0] * 4

    inputs = [0.5, -0.3]
    outputs = []
    for neuron_idx in range(4):
        z = sum(weights[neuron_idx][j] * inputs[j] for j in range(2)) + biases[neuron_idx]
        outputs.append(sigmoid(z))

    print("\nSymmetry Demo (4 neurons, zero init):")
    for i, out in enumerate(outputs):
        print(f"  Neuron {i}: output = {out:.6f}")
    all_same = all(abs(outputs[i] - outputs[0]) < 1e-10 for i in range(len(outputs)))
    print(f"  All identical: {all_same}")
    print(f"  Effective parameters: 1 (not {len(weights) * len(weights[0])})")

Notă tehnică a traducerii: Demonstrația arată numai că ieșirile inițiale coincid; nu execută retropropagarea și nu demonstrează direct că rămân identice după actualizare. Numărul „1 parametru efectiv” este o metaforă pentru un singur neuron distinct, nu un calcul formal al gradelor de libertate.

Pasul 6: raportul amplitudinii strat cu strat

Afișați o diagramă textuală a amplitudinilor activărilor prin 50 de straturi.

def magnitude_report(name, magnitudes):
    print(f"\n{name}:")
    for i, mag in enumerate(magnitudes):
        if i % 5 == 0 or i == len(magnitudes) - 1:
            if mag > 1e6:
                bar = "X" * 50 + " EXPLODED"
            elif mag < 1e-6:
                bar = "." + " VANISHED"
            else:
                bar_len = min(50, max(1, int(mag * 10)))
                bar = "#" * bar_len
            print(f"  Layer {i+1:3d}: {bar} ({mag:.6f})")

Folosiți

PyTorch oferă aceste strategii ca funcții integrate:

import torch
import torch.nn as nn

layer = nn.Linear(512, 256)

nn.init.xavier_uniform_(layer.weight)
nn.init.xavier_normal_(layer.weight)

nn.init.kaiming_uniform_(layer.weight, nonlinearity='relu')
nn.init.kaiming_normal_(layer.weight, nonlinearity='relu')

nn.init.zeros_(layer.bias)

La apelarea nn.Linear(512, 256), PyTorch inițializează implicit ponderile uniform în intervalul [-1/sqrt(fan_in), 1/sqrt(fan_in)]. Implementarea internă folosește kaiming_uniform_ cu parametrul a=sqrt(5), ceea ce produce acest interval; nu este inițializarea Kaiming standard pentru ReLU cu varianța 2/fan_in.

Notă tehnică a traducerii: Originalul spune simplu că nn.Linear folosește implicit Kaiming uniform și că aceasta este „alegerea corectă”. Deși implementarea apelează funcția Kaiming, parametrul special a=sqrt(5) o reduce la distribuția documentată mai sus. Stratul nu cunoaște activarea următoare, deci inițializarea implicită este generică, nu adaptată automat la ReLU.

Pentru transformatoare, modelele Hugging Face gestionează adesea inițializarea în metoda _init_weights. Implementarea GPT-2 scalează proiecțiile reziduale în funcție de numărul straturilor. Dacă construiți de la zero un transformator, urmați schema arhitecturii alese și verificați statisticile, nu presupuneți că aceeași regulă se aplică tuturor modelelor.

Livrați

Această lecție produce:

  • outputs/prompt-init-strategy.md — un prompt care diagnostichează problemele inițializării ponderilor și recomandă o strategie potrivită.

Exerciții

  1. Adăugați inițializarea LeCun — Var = 1/fan_in, folosită cu activarea SELU. Rulați experimentul cu 50 de straturi folosind LeCun plus tanh și comparați cu Xavier plus tanh.

  2. Implementați scalarea reziduală în stil GPT-2: scalați ramurile reziduale cu 1/sqrt(2*N) prin inițializarea proiecțiilor sau printr-o formulare echivalentă atent definită. Rulați 50 de straturi cu și fără scalare și măsurați creșterea fluxului rezidual.

  3. Creați o funcție de „verificare a sănătății inițializării” care primește dimensiunile straturilor și tipul activării, apoi recomandă inițializarea și avertizează asupra riscurilor configurației curente.

  4. Rulați experimentul cu fan_in = 16 și cu fan_in = 1024. Xavier și Kaiming se adaptează la fan_in, dar inițializarea aleatoare cu scară fixă nu. Arătați cum se schimbă intervalul dintre funcționare și instabilitate pentru straturi mai late.

  5. Implementați inițializarea ortogonală: generați o matrice aleatoare, calculați-i SVD și folosiți factorul ortogonal potrivit formei, U sau V^T. Comparați cu Kaiming pentru rețele ReLU cu 50 de straturi.

Termeni-cheie

Termen Ce spun oamenii Ce înseamnă de fapt
Inițializarea ponderilor „Stabiliți aleator ponderile inițiale” Strategia de alegere a parametrilor inițiali, care influențează scara activărilor și gradienților și stabilitatea antrenării
Ruperea simetriei „Faceți neuronii diferiți” Folosirea inițializării nesimetrice pentru ca neuronii să poată învăța caracteristici distincte
Fan-in „Numărul intrărilor unui neuron” Numărul conexiunilor de intrare, care determină acumularea varianței în suma ponderată
Fan-out „Numărul ieșirilor unui neuron” Numărul conexiunilor de ieșire, relevant pentru propagarea varianței gradientului
Inițializare Xavier/Glorot „Inițializarea pentru sigmoidă” Var(w) = 2/(fan_in + fan_out), un compromis pentru păstrarea scărilor înainte și înapoi
Inițializare Kaiming/He „Inițializarea pentru ReLU” Var(w) = 2/fan_in, dedusă pentru a compensa rectificarea activărilor simetrice
Propagarea varianței „Cum cresc sau se micșorează semnalele” Analiza schimbării varianței activărilor și gradienților în funcție de scara ponderilor și de ipotezele distribuționale
Scalare reziduală „Trucul de inițializare GPT-2” Reducerea scării proiecțiilor ramurilor reziduale în funcție de profunzime pentru a controla acumularea
Rețea moartă „Nu se antrenează nimic” Rețea în care inițializarea produce gradienți nuli, activări saturate sau alte dinamici care împiedică învățarea
Activări care explodează „Valorile tind la infinit” Creșterea rapidă a activărilor prin straturi când scara efectivă a transformărilor este prea mare

Lecturi suplimentare

  • Glorot și Bengio, „Understanding the difficulty of training deep feedforward neural networks” (2010) — lucrarea originală despre inițializarea Xavier și analiza varianței.
  • He și colaboratorii, „Delving Deep into Rectifiers” (2015) — a introdus inițializarea Kaiming pentru rețele cu rectificatoare.
  • Radford și colaboratorii, „Language Models are Unsupervised Multitask Learners” (2019) — raportul GPT-2 asociat scalării inițiale a proiecțiilor reziduale.
  • Mishkin și Matas, „All You Need is a Good Init” (2016) — inițializare secvențială pe straturi cu varianță unitară, o alternativă empirică la formulele analitice.

Sursă: Originalul în limba engleză

Navigare: ← Lecția 03.07 — Regularizare · Faza 3 — Fundamentele învățării profunde · Catalog complet