Faza 04 · lecția 05
Învățare prin transfer și ajustare fină
Scopul lecției: Altcineva a cheltuit un milion de ore-GPU învățând o rețea cum arată marginile, texturile și părțile obiectelor. Împrumutați acele caracteristici înainte de a o antrena pe a dumneavoastră.
Versiunea curentă AlexBred.com: primele 100 de lecții ale programului în limba română.
Cuprinsul lecției
- Obiective de învățare
- Problema
- Conceptul
- Extragerea de caracteristici vs. ajustarea fină
- De ce funcționează înghețarea
- Rate de învățare discriminatorii
- Problema BatchNorm
- Proiectarea capului
- Decăderea ratei de învățare pe straturi
- Ce trebuie evaluat
- Construiți-l
- Pasul 1: Încărcați un backbone preantrenat și inspectați-l
- Pasul 2: Extragerea de caracteristici — înghețați totul, înlocuiți capul
- Pasul 3: Ajustare fină discriminatorie
- Pasul 4: Tratarea BatchNorm
- Pasul 5: O buclă minimală de ajustare fină de la un capăt la altul
- Pasul 6: Dezghețarea progresivă
- Utilizați-l
- Livrați-l
- Exerciții
- Termeni-cheie
- Lecturi suplimentare
Altcineva a cheltuit un milion de ore-GPU învățând o rețea cum arată marginile, texturile și părțile obiectelor. Împrumutați acele caracteristici înainte de a o antrena pe a dumneavoastră.
Tip: Construcție Limbaje: Python Cerințe preliminare: Faza 4, Lecția 03 (CNN-uri), Faza 4, Lecția 04 (Clasificarea imaginilor) Timp: ~75 de minute
Obiective de învățare
- Deosebiți extragerea de caracteristici de ajustarea fină și alegeți-o pe cea potrivită în funcție de dimensiunea setului de date, distanța dintre domenii și bugetul de calcul
- Încărcați un backbone preantrenat, înlocuiți-i capul de clasificare și antrenați numai capul pentru un reper funcțional în mai puțin de 20 de linii
- Dezghețați progresiv straturile cu rate de învățare discriminatorii, astfel încât caracteristicile generice timpurii să primească actualizări mai mici decât cele specifice sarcinii, din final
- Diagnosticați cele trei eșecuri frecvente: devierea caracteristicilor cauzată de o rată de învățare prea mare pe blocurile dezghețate, colapsul statisticilor BN pe seturi de date mici și uitarea catastrofică
Problema
Antrenarea unui ResNet-50 pe ImageNet costă aproximativ 2.000 de ore-GPU. Foarte puține echipe au acest buget pentru fiecare sarcină pe care o livrează. Aproape fiecare echipă livrează, de fapt, un backbone preantrenat cu un cap nou, antrenat pe câteva sute sau câteva mii de imagini specifice sarcinii.
Aceasta nu este o scurtătură. Primul bloc convoluțional al oricărei CNN antrenate pe ImageNet învață filtre asemănătoare marginilor și filtrelor Gabor. Următoarele câteva blocuri învață texturi și motive simple. Blocurile de mijloc învață părți ale obiectelor. Blocurile finale învață combinații care încep să semene cu cele 1.000 de categorii ImageNet. Primele 90% din această ierarhie se transferă aproape neschimbate la imagistica medicală, inspecția industrială, datele satelitare și orice altă sarcină de viziune — deoarece natura are un vocabular limitat de margini și texturi. Ultimele 10% sunt ceea ce antrenați efectiv.
Notă tehnică a traducerii: Nu există o proporție universală de „90%” a caracteristicilor transferabile. Transferabilitatea depinde de strat, sarcină și diferența dintre domeniile sursă și țintă; caracteristicile timpurii tind să fie mai generale, dar adaptarea poate fi necesară și pentru ele. Consultați Yosinski și colab..
Realizarea corectă a transferului are trei erori care vă așteaptă: distrugerea caracteristicilor preantrenate printr-o rată de învățare prea mare, înfometarea modelului de informație prin înghețarea unei porțiuni prea mari și lăsarea statisticilor curente ale BatchNorm să derive către un set de date mic, pe care restul rețelei nu l-a învățat niciodată. Această lecție le parcurge intenționat pe fiecare.
Conceptul
Extragerea de caracteristici vs. ajustarea fină
Două regimuri, alese după câtă încredere aveți în caracteristicile preantrenate și după câte date aveți.
Reguli orientative:
| Dimensiunea setului de date | Distanța dintre domenii | Rețetă |
|---|---|---|
| < 1k imagini | apropiat de ImageNet | Înghețați backbone-ul, antrenați numai capul |
| 1k–10k | apropiat | Înghețați primele 2–3 etape, ajustați fin restul |
| 10k–100k | oricare | Ajustați fin de la un capăt la altul cu LR discriminatorii |
| 100k+ | îndepărtat | Ajustați fin totul; luați în calcul antrenarea de la zero dacă domeniul este suficient de îndepărtat |
Notă tehnică a traducerii: Pragurile din tabel sunt euristici de pornire, nu limite demonstrate. Efectul preantrenării și alegerea dintre înghețare, ajustare fină ori antrenare de la zero depind de sarcină, arhitectură, mărimea datelor și protocol; Raghu și colab. și He, Girshick & Dollár arată contexte în care beneficiul preantrenării ImageNet nu este universal.
„Apropiat de ImageNet” înseamnă, în linii mari, fotografii RGB naturale cu conținut asemănător obiectelor. Scanările CT medicale, imaginile satelitare de sus și microscopia sunt domenii îndepărtate — caracteristicile continuă să ajute, însă va trebui să permiteți adaptarea mai multor straturi.
De ce funcționează înghețarea
Caracteristicile ImageNet învățate de o CNN nu sunt specializate pentru cele 1.000 de categorii. Sunt specializate pentru statisticile imaginilor naturale: margini la orientări specifice, texturi, tipare de contrast, primitive de formă. Aceste statistici sunt stabile în aproape orice domeniu vizual pe care îl poate numi un om. De aceea, un model antrenat pe ImageNet și evaluat „zero-shot” pe CIFAR-10 doar cu un cap liniar nou (fără ajustarea fină a backbone-ului) ajunge la o acuratețe de peste 80%. Capul învață ce caracteristici deja învățate să pondereze pentru această sarcină.
Notă tehnică a traducerii: Antrenarea unui cap liniar nou peste un backbone înghețat nu este evaluare zero-shot; este evaluare prin sondă liniară (linear probe). Pragul de 80% depinde de backbone, ponderi, preprocesare și protocol. Kornblith și colab. discută relația dintre modelele ImageNet și transfer.
Rate de învățare discriminatorii
Când dezghețați straturi, cele timpurii trebuie antrenate mai lent decât cele târzii. Straturile timpurii codifică caracteristici generice pe care doriți să le păstrați; straturile târzii codifică structura specifică sarcinii, pe care trebuie să o modificați semnificativ.
Rețetă tipică:
etapa 0 (stem + primul grup): lr = base_lr / 100 (în mare parte fixă)
etapa 1: lr = base_lr / 10
etapa 2: lr = base_lr / 3
etapa 3 (ultimul grup din backbone): lr = base_lr
cap: lr = base_lr (sau puțin mai mare)
În PyTorch, aceasta este doar o listă de grupuri de parametri transmisă optimizatorului. Un model, cinci rate de învățare, zero cod suplimentar.
Problema BatchNorm
Straturile BN păstrează bufferele running_mean și running_var care au fost calculate pe ImageNet. Dacă sarcina are o distribuție diferită a pixelilor — iluminare diferită, senzor diferit, spațiu de culoare diferit — aceste buffere sunt greșite. Trei opțiuni, în ordinea preferinței:
- Ajustați fin cu BN în modul train. Lăsați BN să își actualizeze statisticile curente împreună cu toate celelalte componente. Este alegerea implicită când setul de date al sarcinii are dimensiune medie (>= 5k exemple).
- Înghețați BN în modul eval. Păstrați statisticile ImageNet și antrenați doar ponderile. Este corect când setul de date este suficient de mic încât media mobilă a BN ar fi zgomotoasă.
- Înlocuiți BN cu GroupNorm. Aceasta elimină în întregime problema mediei mobile. Este folosită în backbone-uri pentru detecție și segmentare, unde dimensiunea lotului pe GPU este foarte mică.
Alegerea greșită reduce pe tăcute acuratețea cu 5–15%.
Notă tehnică a traducerii: Pragul de 5k exemple și intervalul de 5–15% sunt euristici, nu reguli universale. Comportamentul depinde de dimensiunea efectivă a lotului, distribuție și arhitectură; BatchNorm folosește statistici de lot în antrenare și statistici curente la evaluare, iar GroupNorm nu depinde de lot. Consultați Batch Normalization și Group Normalization.
Proiectarea capului
Capul clasificatorului are 1–3 straturi liniare, plus un dropout opțional. Fiecare backbone torchvision este livrat cu un cap implicit pe care îl înlocuiți:
backbone.fc = nn.Linear(backbone.fc.in_features, num_classes) # ResNet
backbone.classifier[1] = nn.Linear(..., num_classes) # EfficientNet, MobileNet
backbone.heads.head = nn.Linear(..., num_classes) # torchvision ViT
Pentru seturi de date mici, un singur strat liniar este de obicei suficient. Adăugarea unui strat ascuns (Linear -> ReLU -> Dropout -> Linear) ajută când distribuția sarcinii este mai îndepărtată de distribuția de antrenare a backbone-ului.
Decăderea ratei de învățare pe straturi
O versiune mai netedă a LR discriminatorii, folosită în ajustarea fină modernă (BEiT, DINOv2, ajustări fine ViT-B). În loc să grupați straturile în etape, acordați fiecărui strat o LR puțin mai mică decât cea a stratului de deasupra:
lr_layer_k = base_lr * decay^(L - k)
Cu decay = 0.75 și L = 12 blocuri Transformer, primul bloc se antrenează la 0.75^11 ≈ 0.04x din LR a capului. Contează mai mult pentru ajustările fine Transformer decât pentru CNN-uri, pentru care LR grupate pe etape sunt de regulă suficiente.
Ce trebuie evaluat
Rulările de învățare prin transfer necesită două numere pe care nu le-ați urmări într-o rulare de la zero:
- Acuratețea numai cu preantrenare — acuratețea capului cu backbone-ul înghețat. Aceasta este limita inferioară.
- Acuratețea după ajustarea fină — același model după antrenarea de la un capăt la altul. Aceasta este limita superioară.
Dacă rezultatul după ajustarea fină este mai mic decât cel numai cu preantrenare, aveți o eroare a ratei de învățare sau a BN. Afișați-le întotdeauna pe ambele.
Notă tehnică a traducerii: Acuratețea după ajustarea fină nu este o limită superioară garantată. Un rezultat sub baseline-ul cu backbone înghețat este un semnal de diagnostic; pe lângă LR și BN, verificați preprocesarea, împărțirea datelor, protocolul de evaluare, regularizarea și scheduler-ul. He și colab. ilustrează cât de dependente de protocol sunt rezultatele clasificării imaginilor.
Construiți-l
Pasul 1: Încărcați un backbone preantrenat și inspectați-l
import torch
import torch.nn as nn
from torchvision.models import resnet18, ResNet18_Weights
backbone = resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)
print(backbone)
print()
print("classifier head:", backbone.fc)
print("feature dim:", backbone.fc.in_features)
ResNet18 are patru etape (layer1..layer4), plus un stem și un cap fc. Fiecare backbone torchvision de clasificare are o structură analogă.
Pasul 2: Extragerea de caracteristici — înghețați totul, înlocuiți capul
def make_feature_extractor(num_classes=10):
model = resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)
for p in model.parameters():
p.requires_grad = False
model.fc = nn.Linear(model.fc.in_features, num_classes)
return model
model = make_feature_extractor(num_classes=10)
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
frozen = sum(p.numel() for p in model.parameters() if not p.requires_grad)
print(f"trainable: {trainable:>10,}")
print(f"frozen: {frozen:>10,}")
Numai model.fc poate fi antrenat. Backbone-ul este un extractor de caracteristici înghețat.
Pasul 3: Ajustare fină discriminatorie
O utilitate care construiește grupuri de parametri cu rate de învățare specifice etapei.
def discriminative_param_groups(model, base_lr=1e-3, decay=0.3):
stages = [
["conv1", "bn1"],
["layer1"],
["layer2"],
["layer3"],
["layer4"],
["fc"],
]
groups = []
for i, names in enumerate(stages):
lr = base_lr * (decay ** (len(stages) - 1 - i))
params = [p for n, p in model.named_parameters()
if any(n.startswith(k) for k in names)]
if params:
groups.append({"params": params, "lr": lr, "name": "_".join(names)})
return groups
model = resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)
model.fc = nn.Linear(model.fc.in_features, 10)
for p in model.parameters():
p.requires_grad = True
groups = discriminative_param_groups(model)
for g in groups:
print(f"{g['name']:>10s} lr={g['lr']:.2e} params={sum(p.numel() for p in g['params']):>8,}")
decay=0.3 înseamnă că fiecare etapă se antrenează la 30% din rata următoarei etape. fc primește base_lr, layer4 primește 0.3 * base_lr, iar conv1 primește 0.3^5 * base_lr ≈ 0.00243 * base_lr. Sună extrem; empiric, funcționează.
Pasul 4: Tratarea BatchNorm
O funcție auxiliară pentru înghețarea statisticilor curente BN fără înghețarea ponderilor sale.
def freeze_bn_stats(model):
for m in model.modules():
if isinstance(m, (nn.BatchNorm1d, nn.BatchNorm2d, nn.BatchNorm3d)):
m.eval()
for p in m.parameters():
p.requires_grad = False
return model
Apelați-o după ce setați model.train() la începutul fiecărei epoci. model.train() comută totul în modul de antrenare; aceasta inversează setarea numai pentru straturile BN.
Notă tehnică a traducerii: Contrar propoziției introductive, funcția de mai sus îngheață și parametrii afini ai BatchNorm (
weightșibias), nu doar statisticile curente. Pentru a îngheța numai statisticile, păstrațim.eval()și omiteți bucla care seteazărequires_grad = False; vedeți documentația PyTorch BatchNorm2d.
Pasul 5: O buclă minimală de ajustare fină de la un capăt la altul
from torch.optim import SGD
from torch.utils.data import DataLoader
from torch.optim.lr_scheduler import CosineAnnealingLR
import torch.nn.functional as F
def fine_tune(model, train_loader, val_loader, device, epochs=5, base_lr=1e-3, freeze_bn=False):
model = model.to(device)
groups = discriminative_param_groups(model, base_lr=base_lr)
optimizer = SGD(groups, momentum=0.9, weight_decay=1e-4, nesterov=True)
scheduler = CosineAnnealingLR(optimizer, T_max=epochs)
for epoch in range(epochs):
model.train()
if freeze_bn:
freeze_bn_stats(model)
tr_loss, tr_correct, tr_total = 0.0, 0, 0
for x, y in train_loader:
x, y = x.to(device), y.to(device)
logits = model(x)
loss = F.cross_entropy(logits, y, label_smoothing=0.1)
optimizer.zero_grad()
loss.backward()
optimizer.step()
tr_loss += loss.item() * x.size(0)
tr_total += x.size(0)
tr_correct += (logits.argmax(-1) == y).sum().item()
scheduler.step()
model.eval()
va_total, va_correct = 0, 0
with torch.no_grad():
for x, y in val_loader:
x, y = x.to(device), y.to(device)
pred = model(x).argmax(-1)
va_total += x.size(0)
va_correct += (pred == y).sum().item()
print(f"epoch {epoch} train {tr_loss/tr_total:.3f}/{tr_correct/tr_total:.3f} "
f"val {va_correct/va_total:.3f}")
return model
Cinci epoci cu rețeta de mai sus pe CIFAR-10 duc ResNet18-IMAGENET1K_V1 de la aproximativ 70% acuratețe zero-shot cu sondă liniară la aproximativ 93% acuratețe după ajustarea fină. Numai capul ar atinge un platou în jurul a 86%, fără să atingă backbone-ul.
Notă tehnică a traducerii: Aceste procente sunt rezultate orientative ale unei rețete concrete, nu o garanție de reproductibilitate. Ele depind de împărțirea datelor, augmentări, preprocesare, numărul epocilor și versiunea ponderilor; consultați documentația torchvision pentru
resnet18pentru ponderi și transformările asociate.
Pasul 6: Dezghețarea progresivă
Un program care dezgheață o etapă pe epocă, de la final către început. Atenuează devierea caracteristicilor cu prețul unor epoci suplimentare.
def progressive_unfreeze_schedule(model):
stages = ["layer4", "layer3", "layer2", "layer1"]
yielded = set()
def start():
for p in model.parameters():
p.requires_grad = False
for p in model.fc.parameters():
p.requires_grad = True
def unfreeze(epoch):
if epoch < len(stages):
name = stages[epoch]
yielded.add(name)
for n, p in model.named_parameters():
if n.startswith(name):
p.requires_grad = True
return name
return None
return start, unfreeze
Apelați start() o dată înaintea primei epoci. Apelați unfreeze(epoch) la începutul fiecărei epoci. Reconstruiți optimizatorul ori de câte ori se schimbă setul parametrilor antrenabili; altfel, parametrii înghețați păstrează momente memorate care îl încurcă.
Notă tehnică a traducerii: Nu este întotdeauna necesară reconstruirea optimizatorului la dezghețare. Dacă parametrul se află deja într-un grup al optimizatorului, activarea lui prin
requires_grad = Trueeste suficientă; dacă a fost omis din grupuri, adăugați-l prinoptimizer.add_param_group(...). Reconstruirea resetează starea curentă, inclusiv momentele parametrilor deja activi; consultați PyTorchOptimizer.add_param_group.
Utilizați-l
Pentru majoritatea sarcinilor reale, torchvision.models și trei linii sunt suficiente. Mecanismele mai grele de mai sus contează când întâlniți probleme pe care valorile implicite ale bibliotecii nu le pot rezolva.
from torchvision.models import resnet50, ResNet50_Weights
model = resnet50(weights=ResNet50_Weights.IMAGENET1K_V2)
model.fc = nn.Linear(model.fc.in_features, num_classes)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4)
Alte două valori implicite de nivel de producție:
timmoferă aproximativ 800 de backbone-uri de viziune preantrenate, cu un API consecvent (timm.create_model("resnet50", pretrained=True, num_classes=10)). Pentru orice ajustare fină dincolo de colecția torchvision, este standardul.- Pentru Transformere,
transformers.AutoModelForImageClassification.from_pretrained(name, num_labels=N)vă oferă ViT / BEiT / DeiT cu aceeași semantică de încărcare ca modelele de text.
Notă tehnică a traducerii: „Aproximativ 800” este un număr dependent de versiune și se poate învechi. Consultați documentația
timmpentru catalogul curent și compatibilitatea modelelor.
Livrați-l
Această lecție produce:
outputs/prompt-fine-tune-planner.md— un prompt care alege între extragerea de caracteristici, ajustare fină progresivă și ajustare fină de la un capăt la altul în funcție de dimensiunea setului de date, distanța dintre domenii și bugetul de calcul.outputs/skill-freeze-inspector.md— o abilitate care, pentru un model PyTorch dat, raportează ce parametri pot fi antrenați, ce straturi BatchNorm sunt în modul eval și dacă optimizatorul primește efectiv parametrii antrenabili.
Exerciții
- (Ușor) Antrenați un
ResNet18ca sondă liniară (backbone înghețat) și cu ajustare fină completă pe același set CIFAR sintetic. Raportați ambele acurateți alăturat. Explicați ce diferență arată că caracteristicile se transferă bine și ce diferență arată că nu se transferă bine. - (Mediu) Introduceți intenționat o eroare: setați
base_lr = 1e-1pe etapa backbone-ului în locul capului. Arătați explozia pierderii de antrenare, apoi recuperați prin aplicarea funcției auxiliarediscriminative_param_groups. Notați LR la care fiecare etapă începe să divergă. - (Dificil) Alegeți un set de date de imagistică medicală (de exemplu, CheXpert-small, PatchCamelyon sau HAM10000) și comparați trei regimuri: (a) backbone ImageNet preantrenat înghețat + cap liniar; (b) ajustare fină de la un capăt la altul a modelului preantrenat pe ImageNet; (c) antrenare de la zero. Raportați acuratețea și costul de calcul pentru fiecare. La ce dimensiune a setului de date devine competitivă antrenarea de la zero?
Termeni-cheie
| Termen | Ce spun oamenii | Ce înseamnă de fapt |
|---|---|---|
| Extragerea de caracteristici | „Înghețați și antrenați capul” | Parametrii backbone-ului sunt înghețați; numai capul nou de clasificare primește gradient |
| Ajustare fină | „Reantrenați de la un capăt la altul” | Toți parametrii pot fi antrenați, de obicei cu o LR mult mai mică decât la antrenarea de la zero |
| LR discriminatorie | „LR mai mică pentru straturile timpurii” | Grupuri de parametri ale optimizatorului în care LR a etapelor timpurii este o fracțiune din LR a etapelor târzii |
| Decăderea LR pe straturi | „Gradient LR neted” | LR pe fiecare strat, înmulțită cu decay^(L - k); frecventă în ajustările fine Transformer |
| Uitare catastrofică | „Modelul a pierdut ImageNet” | O LR prea mare suprascrie caracteristicile preantrenate înainte ca semnalul noii sarcini să fie învățat |
| Deriva statisticilor BN | „Media curentă este greșită” | running_mean/running_var BatchNorm sunt calculate pe o distribuție diferită de sarcina curentă și reduc pe tăcute acuratețea |
| Sondă liniară | „Backbone înghețat + cap liniar” | Evaluarea caracteristicilor preantrenate — acuratețea celui mai bun clasificator liniar peste reprezentarea înghețată |
| Colaps catastrofic | „Totul prezice o singură clasă” | Apare la ajustarea fină cu o LR suficient de mare încât să distrugă caracteristicile înainte ca gradienții capului să se poată stabiliza |
Lecturi suplimentare
- How transferable are features in deep neural networks? (Yosinski et al., 2014) — lucrarea care a cuantificat transferabilitatea caracteristicilor între straturi
- Universal Language Model Fine-tuning (ULMFiT, Howard & Ruder, 2018) — rețeta originală pentru LR discriminatorii și dezghețare progresivă; ideile se transferă direct în viziune
- timm documentation — referința pentru backbone-uri moderne de viziune și valorile implicite exacte de ajustare fină cu care au fost antrenate
- A Simple Framework for Linear-Probe Evaluation (Kornblith et al., 2019) — de ce contează acuratețea unei sonde liniare și cum trebuie raportată corect
Notă tehnică a traducerii: Adresa
1805.08974corespunde lucrării Do Better ImageNet Models Transfer Better? de Kornblith și colaboratorii, nu titlului afișat în lista originală; ea rămâne relevantă pentru evaluarea transferului prin sondă liniară.
Sursă: Originalul în limba engleză
Navigare: ← Lecția 04.04 — Clasificarea imaginilor · Faza 4 — Viziune computerizată · Catalog complet