နားလည်ထားရမယ့် အချက်
Transfer learning ရဲ့ core idea က pretrained resnet18 ဟာ ImageNet ပေါ်မှာ image တွေအများကြီးကနေ generic visual feature (edge, texture, shape) တွေကို သင်ယူထားပြီးသား ဖြစ်တယ်ဆိုတာပါ။ Backbone ရဲ့ parameter အားလုံးကို requires_grad = False သတ်မှတ်ပြီး freeze လုပ်လိုက်တာက gradient calculation ကို ရပ်တန့်စေပြီး၊ compute ကို သက်သာစေရုံသာမက tiny fine-tuning dataset တစ်ခုနဲ့ backprop လုပ်လိုက်ရင် ဖြစ်တတ်တဲ့ catastrophic forgetting (learned general feature တွေ ပျက်စီးသွားခြင်း) ကိုလည်း တားဆီးပေးတယ်။ Final fully-connected layer (model.fc) ကို Linear layer အသစ်တစ်ခုနဲ့ လဲလိုက်တာက ImageNet class 1000 အတွက် specialize လုပ်ထားတဲ့ output head ကို ကျွန်တော်တို့ရဲ့ class အရေအတွက် (ဒီနေရာမှာ 4 ခု) အတွက် head အသစ်နဲ့ အစားထိုးလိုက်တာပါ။
Optimizer ကို model.parameters() အားလုံးအစား model.fc.parameters() တစ်ခုတည်းအပေါ်သာ တည်ဆောက်လိုက်တာက fine-tune လုပ်နေတဲ့ parameter အရေအတွက်ကို သန်းနဲ့ချီရှိတဲ့ backbone ကနေ ရာနဲ့ချီသာရှိတဲ့ fc layer တစ်ခုတည်းအထိ အသေအချာ ကျုံ့ပေးတယ် — sample အနည်းငယ်၊ step အနည်းငယ်နဲ့တောင် reasonable performance ရနိုင်တဲ့ transfer learning ရဲ့ အဓိက promise ပါ။ Training ပြီးနောက် named_parameters() နဲ့ .grad is None ကို စစ်ကြည့်တာက freeze က တကယ် အလုပ်လုပ်ခဲ့လား၊ ဒါမှမဟုတ် requires_grad သတ်မှတ်ချက်ကို ယုံနေတာသက်သက်လား ဆိုတာ အတည်ပြုနိုင်တဲ့ ကောင်းတဲ့ debugging habit တစ်ခုပါ။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Tutorial Platform မှာ instructor တွေ upload လုပ်တဲ့ lesson screenshot တွေ ရှုပ်ရှက်/blur ဖြစ်နေသလားဆိုတာကို flag လုပ်ပေးတဲ့ lesson-screenshot quality classifier တစ်ခု ရှိပါတယ်။ Label ရထားတဲ့ screenshot နမူနာအရေအတွက်က နည်းနေလို့ (instructor manual review ကနေ ရလာတာဖြစ်လို့) frozen resnet18 backbone + fine-tuned fc head ဆိုတဲ့ transfer learning approach ကသာ practical ဖြစ်ပါတယ် — sample အနည်းငယ်နဲ့တောင် usable classifier တစ်ခု ရနိုင်တယ်။
အတူတူ စမ်းရေးကြည့်မယ်
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision.models import resnet18, ResNet18_Weights
torch.manual_seed(0)
num_classes = 4
model = resnet18(weights=ResNet18_Weights.DEFAULT)
# Freeze all pretrained layers first
for param in model.parameters():
param.requires_grad = False
# Then replace the final layer -- its params require_grad=True by default
in_features = model.fc.in_features
model.fc = nn.Linear(in_features, num_classes)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.fc.parameters(), lr=1e-3)
# Synthetic images shaped like the inputs resnet18 expects
num_samples = 16
images = torch.randn(num_samples, 3, 224, 224)
labels = torch.randint(0, num_classes, (num_samples,))
model.train()
losses = []
for step in range(5):
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
losses.append(loss.item())
print(f"Step {step+1}/5 - loss: {loss.item():.4f}")
# Confirm only the new fc layer received gradients
frozen_untouched = all(
p.grad is None for name, p in model.named_parameters() if not name.startswith("fc")
)
fc_has_grad = all(p.grad is not None for p in model.fc.parameters())
print(f"Backbone parameters untouched (grad is None): {frozen_untouched}")
print(f"New fc layer parameters have gradients: {fc_has_grad}")
(ပထမဆုံး run မှာ resnet18 pretrained weights ကို download လုပ်ဖို့ progress log တစ်ခု ပေါ်နိုင်ပါတယ်၊ ဒါက internet connection လိုအပ်တယ်။) ပြီးရင် "Step 1/5 - loss: X.XXXX" ကနေ "Step 5/5 - loss: X.XXXX" အထိ line 5 ကြောင်း ထွက်ပါမယ်။ Class 4 ခုအတွက် random guess loss ဖြစ်တဲ့ ln(4)≈1.386 အနီးက စတင်ပြီး၊ fc layer တစ်ခုတည်းက frozen feature 512 dim ပေါ်မှာ sample 16 ခုသာ fit လုပ်ရတာဖြစ်လို့ (heavily overparameterized) step 5 ခုအတွင်းမှာပင် loss က သိသိသာသာ ကျသွားနိုင်ပါတယ်။ နောက်ဆုံး line 2 ကြောင်းက "Backbone parameters untouched (grad is None): True" နဲ့ "New fc layer parameters have gradients: True" ဖြစ်ပါလိမ့်မယ်။၅ မိနစ် စမ်းကြည့်
Backbone freeze ကို ပြင်ပြီး layer3/layer4 (နောက်ဆုံးဆီရှိ conv block နှစ်ခု) ကိုသာ unfreeze လုပ်ကြည့်ပါ (requires_grad = True ပြန်သတ်မှတ်)၊ optimizer ထဲမှာ layer3/layer4/fc parameter အားလုံးကို ထည့်ပါ။ Trainable parameter အရေအတွက်ကို count လုပ်ပြီး fc-only fine-tuning နဲ့ ဘယ်လောက်ကွာခြားလဲ နှိုင်းယှဉ်ကြည့်ပါ။
သတိလေးတစ်ချက်
ပထမဆုံးအကြိမ် run တဲ့အခါ pretrained weights ကို download လုပ်ဖို့ internet connection လိုအပ်ပါတယ် — offline environment ဒါမှမဟုတ် firewall ကန့်သတ်ထားတဲ့ CI pipeline ထဲမှာဆို resnet18(weights=...) ခေါ်ရင် error တက်နိုင်ပါတယ်။
model.fc ကို freeze လုပ်တဲ့ loop ရဲ့ အရင်ကနေ replace လုပ်လိုက်ရင် (order ပြောင်းလိုက်ရင်) freeze loop က layer အသစ်ကိုပါ requires_grad = False လုပ်ပစ်လိုက်နိုင်ပြီး၊ optimizer.step() က ဘာမှ update စရာ မရှိတော့တာကြောင့် loss က error တစ်ခုမှ မတက်ဘဲ step 5 ခုလုံးမှာ တည်ငြိမ်နေမှာပါ။
PyTorch Tutorials — Transfer Learning for Computer Vision — Computer Vision