Vai al contenuto
ELECTRONICS ENGINEERINGelectronics-engineering.com
Machine Learning e Reti Neurali

L’addestramento in pratica: dati, inizializzazione di He, normalizzazione, dropout e metriche

Addestramento, validazione e prova, l'inizializzazione di He dimostrata e misurata su 20 strati, batch e layer normalization, dropout, quantità di dati, precisione, richiamo e F1, ricerca degli iperparametri, con esperimenti onesti.

https://www.electronics-engineering.com/addestramento-in-pratica-inizializzazione-dropout-metriche

Nessun voto ancora: sii il primo a votare questa pagina.

Tra la teoria delle lezioni precedenti e una rete che funziona davvero ci sono molte scelte pratiche: come dividere i dati, come inizializzare i pesi, come normalizzare, come evitare il sovradattamento, come misurare il risultato. Questa lezione le presenta con le ragioni matematiche e con esperimenti misurati, compresi quelli che non confermano le aspettative: nell’addestramento delle reti molti “trucchi” funzionano su alcuni problemi e non su altri, e l’unico modo di saperlo è misurare.

Dividere i dati

I dati si dividono in tre parti: addestramento (per i pesi), validazione (per le scelte: dimensione della rete, passo, regolarizzazione, quando fermarsi) e prova (test), usata una sola volta alla fine per stimare le prestazioni su dati nuovi. Se si fanno scelte guardando la prova, la prova diventa una seconda validazione e la stima è ottimistica. Con pochi dati si usa la validazione incrociata: si divide l’insieme in k parti, si addestra k volte lasciandone fuori una diversa e si fa la media. Due avvertenze pratiche: la divisione va fatta prima di ogni normalizzazione (media e varianza si calcolano solo sull’addestramento), e per dati nel tempo (serie di sensori) la prova deve essere dopo l’addestramento, non mescolata.

L’inizializzazione dei pesi

Se i pesi iniziali sono troppo piccoli le attivazioni (e i gradienti) si spengono strato dopo strato; se sono troppo grandi esplodono. L’inizializzazione giusta mantiene costante la varianza.

Teorema (inizializzazione di He, 2015). In una rete con attivazioni ReLU, pesi indipendenti a media nulla e n ingressi per neurone, la varianza delle somme pesate resta costante da uno strato all’altro se \text{Var}(w) = \dfrac{2}{n}.

Dimostrazione. Per un neurone z = \sum_{j=1}^nw_jh_j con w_j indipendenti da h_j e a media nulla, \text{Var}(z) = n\,\text{Var}(w)\,\mathbb E[h^2]. Con h = \max(0, z_{prec}) e z_{prec} simmetrico rispetto allo zero, la ReLU lascia passare metà della distribuzione: \mathbb E[h^2] = \frac{1}{2}\text{Var}(z_{prec}). Quindi \text{Var}(z) = \frac{n}{2}\text{Var}(w)\,\text{Var}(z_{prec}), che resta uguale se \text{Var}(w) = 2/n. ∎

Per attivazioni simmetriche come la tanh il fattore 1/2 non c’è e si ottiene \text{Var}(w) = 1/n (Xavier o Glorot, 2010). Esperimento: 20 strati da 256 neuroni ReLU, deviazione standard delle attivazioni:

pesidopo 1 stratodopo 5dopo 10dopo 20
\mathcal N(0;\ 0{,}01^2)0,0941{,}6\cdot 10^{-5}3\cdot 10^{-10}10^{-19}
\mathcal N(0;\ 1)9,351{,}8\cdot 10^53\cdot 10^{10}10^{21}
Xavier, 1/n0,580,150,0280,00067
He, 2/n0,830,860,800,76

Con la ReLU solo l’inizializzazione di He mantiene il segnale; quella di Xavier lo dimezza (in varianza) a ogni strato, esattamente il fattore 1/2 della dimostrazione.

Deviazione standard delle attivazioni attraverso 20 strati ReLU
In scala logaritmica: con pesi troppo piccoli, grigio, il segnale si spegne; con pesi troppo grandi, rosso, esplode; con l’inizializzazione di Xavier, arancione, cala lentamente; con quella di He, blu, resta costante.

La normalizzazione

La batch normalization (2015) normalizza le somme pesate di ogni neurone sul mini-lotto (media 0, varianza 1) e poi le riscala con due parametri imparati. Rende l’addestramento molto meno sensibile all’inizializzazione e al passo, e permette passi più grandi. Nei transformer si usa la layer normalization, che normalizza sulle componenti di un singolo esempio invece che sul lotto, e funziona anche con lotti piccoli e sequenze di lunghezza variabile. Anche gli ingressi vanno normalizzati: variabili con scale molto diverse (una temperatura in kelvin e una tensione in millivolt) peggiorano il condizionamento (lezione 1).

Contro il sovradattamento

  • Più dati: è il rimedio più efficace di tutti, come mostra l’esperimento qui sotto.
  • Aumento dei dati: nuove copie degli esempi trasformate in modi che non cambiano la risposta (spostamenti, rotazioni, rumore, variazioni di luce).
  • Weight decay: la penalità \lambda\|\mathbf w\|^2 della lezione 1.
  • Dropout (Srivastava e altri, 2014): durante l’addestramento ogni neurone viene spento a caso con probabilità p, e quelli rimasti vengono moltiplicati per 1/(1 - p). Così il valore atteso di ogni uscita è lo stesso con e senza dropout: \mathbb E[\tilde h] = (1 - p)\cdot\frac{h}{1 - p} + p\cdot 0 = h, e in uso la rete si usa intera senza correzioni. L’idea è impedire che i neuroni si “appoggino” gli uni sugli altri.
  • Arresto anticipato (early stopping): si controlla l’errore di validazione durante l’addestramento e ci si ferma quando comincia a salire.

Gli esperimenti. Rete densa 64 → 1024 → 1024 → 10 sulle cifre 8×8, addestrata per 300 passi su sottoinsiemi di dimensione crescente, accuratezza sulle 450 cifre di prova (media su 3 semi):

esempi di addestramento501002004001347
accuratezza di prova76,4 %89,5 %91,9 %95,5 %97,0 %

Il dropout (p = 0,5), sulla stessa rete con 100 esempi, ha dato 89,5 % contro 89,1 % senza (media su 5 semi): nessuna differenza misurabile; con 50 esempi 77,8 % contro 77,9 %. Il weight decay con \lambda = 10^{-2} ha peggiorato leggermente (86,0 %). Su questo problema, piccolo e già facile, la regolarizzazione conta poco e la quantità di dati conta moltissimo. Su reti molto grandi e problemi più difficili (le immagini di ImageNet, i modelli linguistici) dropout e weight decay danno invece vantaggi misurabili. La morale è quella della lezione: le tecniche si provano sul proprio problema, con una validazione onesta.

Le metriche

L’accuratezza (frazione di risposte giuste) inganna quando le classi sono sbilanciate: se un guasto capita nell’1 % dei casi, un classificatore che dice sempre “nessun guasto” ha il 99 % di accuratezza ed è inutile. Per una classe “positiva”, con VP, FP, FN i veri positivi, i falsi positivi e i falsi negativi:

    \[ \text{precisione} = \frac{VP}{VP + FP},\qquad \text{richiamo} = \frac{VP}{VP + FN},\qquad F_1 = \frac{2\cdot\text{precisione}\cdot\text{richiamo}}{\text{precisione} + \text{richiamo}} \]

La precisione dice quante delle segnalazioni sono vere, il richiamo quanti dei casi veri vengono trovati. Spostando la soglia di decisione si scambia l’una con l’altro; quale sia più importante dipende dall’applicazione (in un sistema di frenata d’emergenza un falso negativo è molto più grave di un falso positivo, in un filtro antispam il contrario). La matrice di confusione, usata nell’ultima lezione, mostra tutti gli errori classe per classe.

La scelta degli iperparametri

Passo, dimensioni, regolarizzazione, dimensione del lotto sono iperparametri: non si imparano con il gradiente, si scelgono confrontando i risultati sulla validazione. La ricerca casuale (Bergstra e Bengio, 2012) è di solito migliore della ricerca a griglia: se solo pochi iperparametri contano davvero, provare valori casuali esplora molti più valori diversi di quelli importanti. Il passo si cerca su scala logaritmica (10^{-4}, 3\cdot 10^{-4}, 10^{-3}, …).

import numpy as np, torch, torch.nn as nn
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split

# 1) inizializzazione: deviazione standard delle attivazioni attraverso 20 strati ReLU
g = np.random.default_rng(0)
h0 = g.normal(0, 1, (1000, 256))
for nome, dev in (("N(0, 0.01^2)", lambda n: 0.01), ("N(0, 1)", lambda n: 1.0),
                  ("Xavier 1/n", lambda n: np.sqrt(1 / n)), ("He 2/n", lambda n: np.sqrt(2 / n))):
    h, valori = h0.copy(), []
    for strato in range(20):
        h = np.maximum(h @ g.normal(0, dev(256), (256, 256)), 0)
        valori.append(h.std())
    print(f"{nome:13s}: dopo 1, 5, 10, 20 strati {valori[0]:.3g}, {valori[4]:.3g}, {valori[9]:.3g}, {valori[19]:.3g}")

# 2) quantità di dati: la stessa rete addestrata su sottoinsiemi sempre più grandi
d = load_digits()
Xa, Xp, ya, yp = train_test_split(d.data / 16.0, d.target, test_size=0.25, random_state=0, stratify=d.target)
Xa, Xp = torch.tensor(Xa, dtype=torch.float32), torch.tensor(Xp, dtype=torch.float32)
ya, yp = torch.tensor(ya), torch.tensor(yp)

def prova(n, seme, dropout=0.0):
    torch.manual_seed(seme)
    strati = [nn.Linear(64, 1024), nn.ReLU()] + ([nn.Dropout(dropout)] if dropout else []) \
           + [nn.Linear(1024, 1024), nn.ReLU()] + ([nn.Dropout(dropout)] if dropout else []) + [nn.Linear(1024, 10)]
    rete = nn.Sequential(*strati)
    opt = torch.optim.Adam(rete.parameters(), lr=1e-3)
    idx = torch.randperm(len(Xa), generator=torch.Generator().manual_seed(seme))[:n]
    for _ in range(300):                                   # 300 passi sull'intero sottoinsieme
        rete.train()
        perdita = nn.functional.cross_entropy(rete(Xa[idx]), ya[idx])
        opt.zero_grad(); perdita.backward(); opt.step()
    rete.eval()                                            # in uso il dropout è spento
    with torch.no_grad():
        return float((rete(Xp).argmax(1) == yp).float().mean())

for n in (50, 100, 200, 400, 1347):
    print(f"{n:4d} esempi: accuratezza di prova {np.mean([prova(n, s) for s in range(3)]):.3f}")
Iscriviti
Notificami
guest
1 commento
Vecchi
Più recenti Le più votate
1
0
Esprimete la vostra opinione commentando.x