Asosiy qismga o'tish

Transfer learning — eng foydali ko'nikma

6-bob 19 kod bloki 1850 so'z

Bu bob amalda eng ko'p ishlatiladigan usulni o'rgatadi.

Haqiqiy ishda siz modelni noldan o'qitmaysiz. Deyarli hech qachon. Tayyor modelni olib, o'z vazifangizga moslashtirasiz.

Nima uchun noldan o'qitmaymiz

ResNet-50 ni ImageNet da noldan o'qitish uchun kerak:

  • 1.2 million belgilangan rasm
  • Bir necha kun GPU vaqti
  • Ko'p tajriba (learning rate, augmentatsiya, regularizatsiya)

Sizda esa odatda 200–2000 rasm bor va bir soat vaqt.

Noldan o'qitsangiz — model overfitting qiladi va ishlamaydi.

G'oya: birinchi qatlamlar UNIVERSAL

4-bobning oxirini eslang: birinchi qatlamlar chet, burchak va tekstura topadi. Bu har qanday rasm uchun foydali — mushuk bo'ladimi, hujjat bo'ladimi.

Faqat oxirgi qatlam vazifaga xos: "bu 1000 ta ImageNet sinfidan qaysi biri?"

Shuning uchun:

python3 qator
Tayyor ResNet
  [conv qatlamlar] <- SAQLAYMIZ (chet, tekstura — universal)
  [oxirgi qatlam]  <- ALMASHTIRAMIZ (bizning sinflar)

Tayyor model yuklash

python7 qator
import torch
import torch.nn as nn
from torchvision import models

model = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
print("oxirgi qatlam:", model.fc)
print("parametr:", sum(p.numel() for p in model.parameters()))

Natija:

python2 qator
oxirgi qatlam: Linear(in_features=512, out_features=1000, bias=True)
parametr: 11,689,512

1000 chiqish — ImageNet ning 1000 ta sinfi. Bizga bu kerak emas, shuning uchun almashtiramiz.

weights=...DEFAULT — ImageNet da o'qitilgan vaznlar yuklanadi. weights=None bersangiz — tasodifiy vaznlar, ya'ni noldan.

Birinchi marta ishga tushirganda model yuklab olinadi (~45 MB).

Oxirgi qatlamni almashtirish

python4 qator
SINFLAR = 2                      # masalan: hujjat / hujjat emas

model.fc = nn.Linear(model.fc.in_features, SINFLAR)
print("yangi qatlam:", model.fc)

model.fc.in_features — eski qatlamning kirish o'lchamini o'qiymiz (ResNet-18 da 512). Qo'lda yozmang — model o'zgarsa xato bo'ladi.

Yangi qatlam tasodifiy vaznlar bilan boshlanadi — uni o'qitish kerak.

Ikki strategiya

A. Feature extraction — faqat oxirgi qatlamni o'qitish

python11 qator
model = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)

for p in model.parameters():
    p.requires_grad = False          # HAMMASINI muzlatamiz

model.fc = nn.Linear(model.fc.in_features, SINFLAR)
# yangi qatlam avtomatik requires_grad=True bo'ladi

oqitiladigan = sum(p.numel() for p in model.parameters() if p.requires_grad)
jami = sum(p.numel() for p in model.parameters())
print(f"o'qitiladigan: {oqitiladigan:,} / {jami:,}")

Natija:

matn1 qator
o'qitiladigan: 1,026 / 11,177,538

Atigi 1026 ta parametr o'qitiladi (512x2 vazn + 2 bias). Qolgan 11 milliondan ortig'i muzlatilgan. Shuning uchun bu usul CPU da ham daqiqalarda ishlaydi.

Qachon: ma'lumot kam (< 1000 rasm), yoki vazifa ImageNet ga o'xshash.

Afzalligi: juda tez, CPU da ham bo'ladi, overfitting kam.

B. Fine-tuning — hammasini kichik learning rate bilan

python5 qator
model_ft = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
model_ft.fc = nn.Linear(model_ft.fc.in_features, SINFLAR)
# hech narsa muzlatilmaydi

optimizator = torch.optim.Adam(model_ft.parameters(), lr=1e-4)

lr=1e-4 — 10 barobar KICHIK. Noldan o'qitishda 1e-3 ishlatgan edik.

Nega kichik: vaznlar allaqachon yaxshi. Katta qadam ularni buzadi — bu "catastrophic forgetting" deyiladi.

Qachon: ma'lumot ko'p (> 5000 rasm) va vazifa ImageNet dan farq qiladi (masalan tibbiy rasm, hujjat).

Oraliq variant: oxirgi bloklarni ochish

python12 qator
model_q = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)

for p in model_q.parameters():
    p.requires_grad = False

for p in model_q.layer4.parameters():    # faqat oxirgi blok
    p.requires_grad = True

model_q.fc = nn.Linear(model_q.fc.in_features, SINFLAR)

n = sum(p.numel() for p in model_q.parameters() if p.requires_grad)
print(f"o'qitiladigan: {n:,}")

Amalda eng ko'p ishlatiladigan variant: past qatlamlar (universal belgilar) muzlatilgan, yuqori qatlamlar (vazifaga xos) o'qitiladi.

BATCHNORM MUAMMOSI — jimgina buziladigan xato

Bu — transfer learning dagi eng ko'p uchraydigan yashirin xato.

requires_grad = False vaznlarni muzlatadi. Lekin BatchNorm qatlamida vazndan tashqari yana ikkita narsa bor:

python10 qator
import torch
import torch.nn as nn

bn = nn.BatchNorm2d(4)
print("o'qitiladigan (parameters):")
for nom, _ in bn.named_parameters():
    print("   ", nom)
print("BUFERLAR (o'qitilmaydi, lekin O'ZGARADI):")
for nom, _ in bn.named_buffers():
    print("   ", nom)

running_mean va running_var — bu parametr EMAS, bufer. requires_grad ularga ta'sir qilmaydi.

Muammoni ko'rsatamiz

python13 qator
bn = nn.BatchNorm2d(4)
for p in bn.parameters():
    p.requires_grad = False          # "muzlatdik"

boshlangich = bn.running_mean.clone()

bn.train()                            # o'qitish rejimi
for _ in range(5):
    bn(torch.randn(8, 4, 16, 16) * 5 + 10)   # boshqa taqsimot

print("o'zgardimi:", not torch.allclose(boshlangich, bn.running_mean))
print("boshlang'ich:", boshlangich.tolist())
print("keyin       :", [round(x, 3) for x in bn.running_mean.tolist()])

Natija:

matn3 qator
o'zgardimi: True
boshlang'ich: [0.0, 0.0, 0.0, 0.0]
keyin       : [4.11, 4.098, 4.102, 4.099]

0 dan 4.1 ga siljidi — atigi 5 ta partiyadan keyin. Vaznlar muzlatilgan bo'lsa ham.

Vaznlar muzlatilgan, lekin statistika o'zgardi. Ya'ni "muzlatilgan" backbone aslida o'zgaryapti.

Nega bu zarar

Tayyor model ImageNet statistikasi bilan kalibrlangan. Sizning 1000 ta rasmingiz boshqa taqsimotga ega. BatchNorm bufer sekin-asta shu kichik taqsimotga siljiydi va ImageNet belgilarini buzadi.

Natija: aniqlik kutilganidan past, lekin xato xabari yo'q. Sabab topish juda qiyin.

Yechim: BatchNorm ni eval rejimida qoldirish

python14 qator
def bn_muzlat(model):
    """BatchNorm qatlamlarini eval rejimiga o'tkazadi."""
    for m in model.modules():
        if isinstance(m, (nn.BatchNorm1d, nn.BatchNorm2d, nn.BatchNorm3d)):
            m.eval()


bn = nn.BatchNorm2d(4)
bn.train()
bn_muzlat(bn)
boshlangich = bn.running_mean.clone()
for _ in range(5):
    bn(torch.randn(8, 4, 16, 16) * 5 + 10)
print("eval bilan o'zgardimi:", not torch.allclose(boshlangich, bn.running_mean))

Natija: eval bilan o'zgardimi: False — endi haqiqatan muzlatilgan.

MUHIM: model.train() dan KEYIN chaqiring. model.train() hamma narsani train rejimiga qaytaradi, shu jumladan BatchNorm ni.

To'g'ri tartib har epochda:

python3 qator
# parcha
model.train()        # avval hammasi train
bn_muzlat(model)     # keyin BatchNorm ni qaytarib eval ga

Teskari qilsangiz — tuzatish bekor bo'ladi.

Qachon muzlatish kerak

Vaziyat BatchNorm
Feature extraction (hamma muzlatilgan) eval — albatta
Kam ma'lumot (< 1000 rasm) eval — tavsiya
Ko'p ma'lumot (> 10 000) train qoldirish mumkin
Batch juda kichik (< 8) eval — statistika ishonchsiz

Oxirgi qator alohida muhim: kichik batch da BatchNorm statistikasi shovqinli bo'ladi. CPU da o'qitayotgan bo'lsangiz — batch kichik, demak BatchNorm ni eval da qoldiring.

MUHIM: kirishni to'g'ri tayyorlash

Tayyor model aniq qanday tayyorlangan rasmni kutadi. Boshqacha bersangiz — aniqlik keskin tushadi, lekin xato chiqmaydi.

python11 qator
from torchvision import transforms

IMAGENET_ORT = [0.485, 0.456, 0.406]
IMAGENET_STD = [0.229, 0.224, 0.225]

test_ozgartirish = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(IMAGENET_ORT, IMAGENET_STD),
])

Bu uchta son ImageNet ning haqiqiy statistikasi. Ular bilan o'qitilgan model shu bilan ishlashi kerak.

5-bobda MNIST uchun (0.1307,), (0.3081,) ishlatgan edik — boshqa dataset, boshqa sonlar.

224x224 — ResNet ImageNet da shu o'lchamda o'qitilgan.

Augmentatsiya — faqat o'qitish uchun

python7 qator
oqitish_ozgartirish = transforms.Compose([
    transforms.RandomResizedCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.ColorJitter(brightness=0.2, contrast=0.2),
    transforms.ToTensor(),
    transforms.Normalize(IMAGENET_ORT, IMAGENET_STD),
])

Har epochda rasm biroz boshqacha ko'rinadi — model yodlab ola olmaydi. Kam ma'lumotda bu juda yordam beradi.

Testda augmentatsiya QILMANG. Baholash barqaror bo'lishi kerak.

Diqqat — hujjat uchun RandomHorizontalFlip ISHLATMANG. Ko'zguga aylantirilgan matn — bu boshqa narsa. Mushuk uchun mayli, matn uchun zarar.

To'liq misol

python23 qator
def transfer_model(sinflar, strategiya="feature"):
    """Tayyor ResNet-18 ni yangi vazifaga moslashtiradi."""
    m = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)

    if strategiya == "feature":
        for p in m.parameters():
            p.requires_grad = False
    elif strategiya == "qisman":
        for p in m.parameters():
            p.requires_grad = False
        for p in m.layer4.parameters():
            p.requires_grad = True
    elif strategiya != "full":
        raise ValueError(f"noma'lum strategiya: {strategiya}")

    m.fc = nn.Linear(m.fc.in_features, sinflar)
    return m


for s in ("feature", "qisman", "full"):
    m = transfer_model(2, s)
    n = sum(p.numel() for p in m.parameters() if p.requires_grad)
    print(f"{s:<8} o'qitiladigan parametr: {n:,}")

Natija:

matn3 qator
feature  o'qitiladigan parametr: 1,026
qisman   o'qitiladigan parametr: 8,394,754
full     o'qitiladigan parametr: 11,177,538

Uch daraja: 1 mingdan 8 millionga, keyin 11 millionga. Har qadamda ko'proq moslashuvchanlik, lekin ko'proq ma'lumot va ehtiyotkorlik kerak.

Diqqat: qisman da 8.4 million — bu jami ning 75% i. ResNet parametrlarining katta qismi oxirgi blokda joylashgan.

Learning rate — strategiyaga qarab

python6 qator
# parcha
tavsiya = {
    "feature": 1e-3,     # faqat yangi qatlam — katta qadam mumkin
    "qisman":  1e-4,     # tayyor vaznlar bor — ehtiyot
    "full":    1e-5,     # hammasi tayyor — juda ehtiyot
}

Qoida: qancha ko'p tayyor vazn o'qitilsa, learning rate shuncha kichik.

Eng ko'p uchraydigan xato — fine-tuning da 1e-3 ishlatish. Birinchi epochda aniqlik tushib ketadi va tiklanmaydi. Model ImageNet da o'rgangan hamma narsani unutgan bo'ladi.

Qaysi modelni tanlash

Model Hajm Qachon
resnet18 45 MB boshlash uchun, CPU da ham tez
resnet50 98 MB aniqroq, GPU bo'lsa
efficientnet_b0 21 MB kichik va aniq, mobil uchun
mobilenet_v3_small 10 MB telefon, edge qurilma

resnet18 dan boshlang. Ishlasa — yetarli. Ishlamasa, kattaroqqa o'ting.

Katta model doim yaxshi emas: kam ma'lumotda u tezroq overfitting qiladi.

Amaliy tartib

python8 qator
1. resnet18 + feature extraction bilan boshlang
2. O'lchang (aniqlik, chalkashlik matritsasi)
3. Yetarli bo'lsa — TO'XTANG
4. Yetmasa: "qisman" ga o'ting, lr=1e-4
5. Hali yetmasa: ko'proq MA'LUMOT yig'ing

Ma'lumot qo'shish deyarli doim modelni kattalashtirish dan
foydaliroq.

Keyingi bobda shu usulni hujjat vazifasiga qo'llaymiz.


Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar