====== Decăderea Ponderilor (Weight Decay) ======
Acum că am caracterizat problema supra-ajustării, putem introduce prima noastră tehnică de //regularizare//. Reamintiți-vă că putem atenua oricând supra-ajustarea prin colectarea mai multor date de antrenament. Totuși, acest lucru poate fi costisitor, consumator de timp sau în întregime în afara controlului nostru, făcându-l imposibil pe termen scurt. Deocamdată, putem presupune că avem deja atât de multe date de înaltă calitate pe cât permit resursele noastre și ne putem concentra pe instrumentele pe care le avem la dispoziție atunci când setul de date este luat ca un dat.
Reamintiți-vă că în exemplul nostru de regresie polinomială () am putut limita capacitatea modelului nostru prin ajustarea gradului polinomului potrivit. Într-adevăr, limitarea numărului de caracteristici este o tehnică populară pentru atenuarea supra-ajustării. Totuși, pur și simplu a arunca la o parte caracteristicile poate fi un instrument prea brut. Rămânând la exemplul de regresie polinomială, luați în considerare ce s-ar putea întâmpla cu o intrare multidimensională. Extensiile naturale ale polinoamelor la date multivariate se numesc //monoame//, care sunt pur și simplu produse de puteri ale variabilelor. Gradul unui monom este suma puterilor. De exemplu, $x_1^2 x_2$ și $x_3 x_5^2$ sunt amândouă monoame de grad 3.
Rețineți că numărul de termeni cu gradul $d$ explodează rapid pe măsură ce $d$ crește. Având $k$ variabile, numărul de monoame de grad $d$ este ${k - 1 + d} \choose {k - 1}$. Chiar și schimbări mici în grad, să zicem de la $2$ la $3$, cresc dramatic complexitatea modelului nostru. Astfel, avem adesea nevoie de un instrument mai fin pentru ajustarea complexității funcției.
%matplotlib inline
from d2l import torch as d2l
import torch
from torch import nn
===== Norme și Decăderea Ponderilor =====
(**În loc să manipuleze direct numărul de parametri, //decăderea ponderilor// (weight decay) funcționează prin restricționarea valorilor pe care le pot lua parametrii.**) Mai frecvent numită regularizare $\ell_2$ în afara cercurilor de deep learning atunci când este optimizată prin coborârea pe gradient stochastic de minilot, decăderea ponderilor ar putea fi cea mai utilizată tehnică pentru regularizarea modelelor parametrice de machine learning. Tehnica este motivată de intuiția de bază că dintre toate funcțiile $f$, funcția $f = 0$ (care atribuie valoarea $0$ tuturor intrărilor) este într-un anumit sens cea mai //simplă// și că putem măsura complexitatea unei funcții prin distanța parametrilor săi față de zero. Dar cum ar trebui să măsurăm cu exactitate distanța dintre o funcție și zero? Nu există un singur răspuns corect. De fapt, ramuri întregi de matematică, inclusiv părți din analiza funcțională și teoria spațiilor Banach, sunt dedicate abordării unor astfel de probleme.
O interpretare simplă ar putea fi măsurarea complexității unei funcții liniare $f(\mathbf{x}) = \mathbf{w}^\top \mathbf{x}$ prin intermediul unei norme a vectorului său de ponderi, de exemplu $\| \mathbf{w} \|^2$. Reamintiți-vă că am introdus norma $\ell_2$ și norma $\ell_1$, care sunt cazuri particulare ale normei mai generale $\ell_p$, în . Cea mai comună metodă pentru asigurarea unui vector de ponderi mic este adăugarea normei sale ca termen de penalizare la problema minimizării pierderii. Astfel, înlocuim obiectivul nostru original, //minimizarea pierderii de predicție pe etichetele de antrenament//, cu un obiectiv nou, //minimizarea sumei pierderii de predicție și a termenului de penalizare//. Acum, dacă vectorul nostru de ponderi crește prea mult, algoritmul nostru de învățare s-ar putea concentra pe minimizarea normei ponderilor $\| \mathbf{w} \|^2$ mai degrabă decât pe minimizarea erorii de antrenament. Aceasta este exact ceea ce ne dorim. Pentru a ilustra lucrurile în cod, readucem exemplul nostru anterior din pentru regresia liniară. Acolo, pierderea noastră a fost dată de
$$L(\mathbf{w}, b) = \frac{1}{n}\sum_{i=1}^n \frac{1}{2}\left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right)^2.$$
Reamintiți-vă că $\mathbf{x}^{(i)}$ reprezintă caracteristicile, $y^{(i)}$ este eticheta pentru orice exemplu de date $i$, iar $(\mathbf{w}, b)$ sunt parametrii de pondere și, respectiv, de bias (deplasament). Pentru a penaliza mărimea vectorului de ponderi, trebuie să adăugăm într-un fel $\| \mathbf{w} \|^2$ la funcția de pierdere, dar cum ar trebui să echilibreze modelul pierderea standard cu această nouă penalizare aditivă? În practică, caracterizăm acest compromis prin intermediul //constantei de regularizare// $\lambda$, un hiperparametru nenegativ pe care îl potrivim folosind datele de validare:
$$L(\mathbf{w}, b) + \frac{\lambda}{2} \|\mathbf{w}\|^2.$$
Pentru $\lambda = 0$, recuperăm funcția noastră de pierdere originală. Pentru $\lambda > 0$, restricționăm mărimea lui $\| \mathbf{w} \|$. Divizăm prin $2$ prin convenție: atunci când luăm derivata unei funcții pătratice, $2$ și $1/2$ se anulează reciproc, asigurându-ne că expresia pentru actualizare arată frumos și simplu. Cititorul atent s-ar putea întreba de ce lucrăm cu norma la pătrat și nu cu norma standard (adică distanța euclidiană). Facem acest lucru pentru comoditate computațională. Prin ridicarea la pătrat a normei $\ell_2$, eliminăm rădăcina pătrată, rămânând cu suma pătratelor fiecărei componente a vectorului de ponderi. Acest lucru face derivata penalizării ușor de calculat: suma derivatelor este egală cu derivata sumei.
Mai mult, m-ați putea întreba de ce lucrăm cu norma $\ell_2$ în primul rând și nu, să zicem, cu norma $\ell_1$. De fapt, alte alegeri sunt valide și populare în întreaga statistică. În timp ce modelele liniare regularizate cu $\ell_2$ constituie algoritmul clasic de //ridge regression//, regresia liniară regularizată cu $\ell_1$ este o metodă fundamentală similară în statistică, cunoscută popular sub numele de //lasso regression//. Un motiv pentru a lucra cu norma $\ell_2$ este că plasează o penalizare mare asupra componentelor mari ale vectorului de ponderi. Acest lucru orientează algoritmul nostru de învățare către modele care distribuie ponderea uniform pe un număr mai mare de caracteristici. În practică, acest lucru le-ar putea face mai robuste la erorile de măsurare într-o singură variabilă. În schimb, penalizările $\ell_1$ duc la modele care concentrează ponderile pe un set mic de caracteristici prin aducerea celorlalte ponderi la zero. Acest lucru ne oferă o metodă eficientă pentru //selecția caracteristicilor// (feature selection), care poate fi de dorit din alte motive. De exemplu, dacă modelul nostru se bazează doar pe câteva caracteristici, atunci s-ar putea să nu fie nevoie să colectăm, să stocăm sau să transmitem date pentru celelalte caracteristici (eliminate).
Folosind aceeași notație ca în :eqref:''%%eq_linreg_batch_update%%'', actualizările coborârii pe gradient stochastic de minilot pentru regresia regularizată cu $\ell_2$ sunt următoarele:
$$\begin{aligned}
\mathbf{w} & \leftarrow \left(1- \eta\lambda \right) \mathbf{w} - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \mathbf{x}^{(i)} \left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right).
\end{aligned}$$
Ca și înainte, actualizăm $\mathbf{w}$ pe baza valorii cu care estimarea noastră diferă de observație. Totuși, comprimăm, de asemenea, mărimea lui $\mathbf{w}$ spre zero. De aceea, metoda este uneori numită „decădere a ponderilor” (weight decay): având în vedere doar termenul de penalizare, algoritmul nostru de optimizare //scade// ponderea la fiecare pas al antrenamentului. Spre deosebire de selecția caracteristicilor, decăderea ponderilor ne oferă un mecanism pentru ajustarea continuă a complexității unei funcții. Valorile mai mici ale lui $\lambda$ corespund unui $\mathbf{w}$ mai puțin constrâns, în timp ce valorile mai mari ale lui $\lambda$ constrâng $\mathbf{w}$ mai considerabil. Dacă includem o penalizare corespunzătoare pentru bias $b^2$ poate varia între implementări și poate varia între straturile unei rețele neuronale. Adesea, nu regularizăm termenul de bias. În plus, deși regularizarea $\ell_2$ s-ar putea să nu fie echivalentă cu decăderea ponderilor pentru alți algoritmi de optimizare, ideea regularizării prin micșorarea dimensiunii ponderilor rămâne valabilă.
===== Regresia Liniară de Înaltă Dimensiune =====
Putem ilustra beneficiile decăderii ponderilor printr-un exemplu sintetic simplu.
Mai întâi, [**generăm niște date ca înainte**]:
(**$$y = 0.05 + \sum_{i = 1}^d 0.01 x_i + \epsilon \textrm{ unde }
\epsilon \sim \mathcal{N}(0, 0.01^2).$$**)
În acest set de date sintetic, eticheta noastră este dată de o funcție liniară subiacentă a intrărilor noastre, coruptă de zgomot Gaussian cu media zero și deviația standard 0.01. În scopuri ilustrative, putem face ca efectele supra-ajustării să fie pronunțate prin creșterea dimensionalității problemei noastre la $d = 200$ și lucrând cu un set mic de antrenament de doar 20 de exemple.
class Data(d2l.DataModule):
def __init__(self, num_train, num_val, num_inputs, batch_size):
self.save_hyperparameters()
n = num_train + num_val
self.X = d2l.randn(n, num_inputs)
noise = d2l.randn(n, 1) * 0.01
w, b = d2l.ones((num_inputs, 1)) * 0.01, 0.05
self.y = d2l.matmul(self.X, w) + b + noise
def get_dataloader(self, train):
i = slice(0, self.num_train) if train else slice(self.num_train, None)
return self.get_tensorloader([self.X, self.y], train, i)
===== Implementarea de la Zero =====
Acum, să încercăm să implementăm decăderea ponderilor de la zero. Deoarece coborârea pe gradient stochastic de minilot este optimizatorul nostru, trebuie doar să adăugăm penalizarea $\ell_2$ la pătrat la funcția de pierdere originală.
==== (Definirea Penalizării Normei \ell_2) ====
Poate cea mai convenabilă cale de a implementa această penalizare este ridicarea la pătrat a tuturor termenilor pe loc și sumarea lor.
def l2_penalty(w):
return d2l.reduce_sum(w**2) / 2
==== Definirea Modelului ====
În modelul final, regresia liniară și pierderea pătratică nu s-au schimbat față de , așa încât vom defini doar o subclasă a ''%%d2l.LinearRegressionScratch%%''. Singura schimbare aici este că pierderea noastră include acum termenul de penalizare.
class WeightDecayScratch(d2l.LinearRegressionScratch):
def __init__(self, num_inputs, lambd, lr, sigma=0.01):
super().__init__(num_inputs, lr, sigma)
self.save_hyperparameters()
def loss(self, y_hat, y):
return (super().loss(y_hat, y) +
self.lambd * l2_penalty(self.w))
Următorul cod potrivește modelul nostru pe setul de antrenament cu 20 de exemple și îl evaluează pe setul de validare cu 100 de exemple.
data = Data(num_train=20, num_val=100, num_inputs=200, batch_size=5)
trainer = d2l.Trainer(max_epochs=10)
def train_scratch(lambd):
model = WeightDecayScratch(num_inputs=200, lambd=lambd, lr=0.01)
model.board.yscale='log'
trainer.fit(model, data)
==== [Antrenarea fără Regularizare] ====
Acum rulăm acest cod cu ''%%lambd = 0%%'', dezactivând decăderea ponderilor. Observați că avem o supra-ajustare severă, scăzând eroarea de antrenament, dar nu și eroarea de validare — un caz clasic de supra-ajustare.
train_scratch(0)
==== [Utilizarea Decăderii Ponderilor] ====
Mai jos, rulăm cu o decădere a ponderilor substanțială. Observați că eroarea de antrenament crește, dar eroarea de validare scade. Acesta este exact efectul pe care îl așteptăm de la regularizare.
train_scratch(3)
===== [Implementarea Concisă] =====
Deoarece decăderea ponderilor este omniprezentă în optimizarea rețelelor neuronale, framework-ul de deep learning o face deosebit de convenabilă, integrând decăderea ponderilor chiar în algoritmul de optimizare pentru o utilizare ușoară în combinație cu orice funcție de pierdere. Mai mult, această integrare oferă un beneficiu computațional, permițând trucuri de implementare pentru a adăuga decăderea ponderilor la algoritm fără nicio cheltuială computațională suplimentară. Deoarece partea de decădere a ponderilor a actualizării depinde doar de valoarea actuală a fiecărui parametru, optimizatorul trebuie oricum să acceseze fiecare parametru o dată.
Mai jos, specificăm hiperparametrul de decădere a ponderilor direct prin ''%%weight_decay%%'' la instanțierea optimizatorului nostru. În mod implicit, PyTorch scade atât ponderile, cât și biasurile simultan, dar putem configura optimizatorul să gestioneze diferiți parametri conform unor politici diferite. Aici, setăm doar ''%%weight_decay%%'' pentru ponderi (parametrii ''%%net.weight%%''), prin urmare biasul (parametrul ''%%net.bias%%'') nu va scădea.
class WeightDecay(d2l.LinearRegression):
def __init__(self, wd, lr):
super().__init__(lr)
self.save_hyperparameters()
self.wd = wd
def configure_optimizers(self):
return torch.optim.SGD([
{'params': self.net.weight, 'weight_decay': self.wd},
{'params': self.net.bias}], lr=self.lr)
[**Graficul arată similar cu cel de când am implementat decăderea ponderilor de la zero**]. Totuși, această versiune rulează mai rapid și este mai ușor de implementat, beneficii care vor deveni mai pronunțate pe măsură ce abordați probleme mai mari și această muncă devine mai rutinară.
model = WeightDecay(wd=3, lr=0.01)
model.board.yscale='log'
trainer.fit(model, data)
Până acum, am abordat o noțiune a ceea ce constituie o funcție liniară simplă. Totuși, chiar și pentru funcții neliniare simple, situația poate fi mult mai complexă. Pentru a vedea acest lucru, conceptul de [[https://en.wikipedia.org/wiki/Reproducing_kernel_Hilbert_space|spațiu Hilbert cu nucleu reproducător (Reproducing Kernel Hilbert Space - RKHS)]] permite aplicarea instrumentelor introduse pentru funcții liniare într-un context neliniar. Din păcate, algoritmii bazați pe RKHS tind să nu scaleze bine la date mari, de înaltă dimensiune. În această carte vom adopta adesea euristica comună prin care decăderea ponderilor este aplicată tuturor straturilor unei rețele profunde.
===== Rezumat =====
Regularizarea este o metodă comună pentru gestionarea supra-ajustării. Tehnicile clasice de regularizare adaugă un termen de penalizare la funcția de pierdere (în timpul antrenamentului) pentru a reduce complexitatea modelului învățat. O alegere specifică pentru menținerea modelului simplu este utilizarea unei penalizări $\ell_2$. Aceasta conduce la decăderea ponderilor în pașii de actualizare ai algoritmului de coborâre pe gradient stochastic de minilot. În practică, funcționalitatea de decădere a ponderilor este furnizată în optimizatorii din framework-urile de deep learning. Diferite seturi de parametri pot avea comportamente de actualizare diferite în cadrul aceleiași bucle de antrenament.
===== Exerciții =====
- Experimentați cu valoarea lui $\lambda$ în problema de estimare din această secțiune. Trasați acuratețea de antrenament și de validare în funcție de $\lambda$. Ce observați?
- Utilizați un set de validare pentru a găsi valoarea optimă a lui $\lambda$. Este aceasta într-adevăr valoarea optimă? Contează acest lucru?
- Cum ar arăta ecuațiile de actualizare dacă în loc de $\|\mathbf{w}\|^2$ am folosi $\sum_i |w_i|$ ca penalizare preferată (regularizare $\ell_1$)?
- Știm că $\|\mathbf{w}\|^2 = \mathbf{w}^\top \mathbf{w}$. Puteți găsi o ecuație similară pentru matrice (vezi norma Frobenius în )?
- Revizuiți relația dintre eroarea de antrenament și eroarea de generalizare. Pe lângă decăderea ponderilor, creșterea antrenamentului și utilizarea unui model de complexitate adecvată, ce alte căi ne-ar putea ajuta să gestionăm supra-ajustarea?
- În statistica Bayesiană, folosim produsul dintre distribuția priori și verosimilitate pentru a ajunge la o distribuție posteriori via $P(w \mid x) \propto P(x \mid w) P(w)$. Cum puteți identifica $P(w)$ cu regularizarea?
[[https://discuss.d2l.ai/t/99|Discuții]]