နားလည်ထားရမယ့် အချက်
CIFAR-10 ပုံစံ problem ဆိုတာ 32x32 အရွယ်အစား RGB image တွေကို class 10 မျိုးထဲက တစ်ခုအဖြစ် သတ်မှတ်ရတဲ့ classification task ပါ။ ဒီ project မှာ Conv2d → ReLU → MaxPool2d ကို နှစ်ကြိမ်ထပ်ခြင်း သုံးထားပြီး၊ spatial resolution ကို 32→16→8 အဖြစ် တဖြည်းဖြည်း လျှော့ချသွားရင်း channel depth ကို 3→16→32 အဖြစ် တိုးပေးထားတယ်၊ ဒါက vision CNN တွေမှာ ပုံမှန်တွေ့ရတဲ့ pattern ဖြစ်တယ်— fine spatial detail ကို sacrifice လုပ်ပြီး semantic feature ပိုများများ ထုတ်ယူတာပါ။ နောက်ဆုံးမှာ feature map ကို flatten လုပ်ပြီး Linear layer တစ်ခုက class 10 ခုအတွက် score (logits) ထုတ်ပေးတယ်၊ ဒါကို CrossEntropyLoss နဲ့ တွက်ပါတယ်။
TensorDataset + DataLoader ကို batch လုပ်ဖို့ သုံးထားတာက Deep Learning with PyTorch course က basics ကို ဒီနေရာမှာ practical vision pipeline တစ်ခုလုံးထဲ ပြန်ချိတ်ဆက်ပေးတာပါ— epoch တစ်ခုချင်းစီမှာ DataLoader က batch တွေကို shuffle လုပ်ပြီး ဖြန့်ဝေပေးတယ်၊ ပြီးရင် per-epoch average loss ကို print ထုတ်ပါတယ်။ Image တွေနဲ့ label တွေက အပြင်းအထန် random ဖြစ်နေလို့ model က real pattern ဘာမှ မသင်နိုင်ပေမယ့်၊ ဒီလို synthetic data နဲ့ pipeline တစ်ခုလုံးကို အရင်ဆုံး run ကြည့်တာက tensor shape တွေ ကိုက်ညီမှုရှိမရှိ၊ backward pass အလုပ်လုပ်မလုပ်၊ optimizer က parameter တွေကို အမှန်တကယ် update လုပ်ပေးမလားဆိုတာကို object real dataset ကို download မလုပ်ခင် စစ်ဆေးနိုင်တဲ့ အလွန်အသုံးဝင်တဲ့ workflow တစ်ခုပါ။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Tutorial Platform မှာ instructor တွေ upload လုပ်တဲ့ course thumbnail ပုံတွေကို catalog grid ထဲ ပြသခင်မှာ အလိုအလျောက် category tag ခတ်ပေးတဲ့ course-thumbnail category tagger တစ်ခု ရှိပါတယ်— ပုံတွေကို "code editor screenshot", "diagram/illustration", "presenter webcam shot", "text-heavy slide" စတဲ့ category ငယ်လေးတွေထဲ ခွဲခြားပေးပါတယ်။ ဒီ project ရဲ့ small-CNN + multi-epoch training loop ဟာ အဲဒီ classifier ရဲ့ core shape အတိအကျပါ — class အရေအတွက်ကိုသာ 10 ကနေ 4 ကို ပြောင်းလိုက်ရင် ရပါပြီ။
အတူတူ စမ်းရေးကြည့်မယ်
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader
torch.manual_seed(0)
# Synthetic CIFAR-10-shaped data: 10 classes, 32x32 RGB images
num_samples = 200
num_classes = 10
images = torch.randn(num_samples, 3, 32, 32)
labels = torch.randint(0, num_classes, (num_samples,))
dataset = TensorDataset(images, labels)
loader = DataLoader(dataset, batch_size=32, shuffle=True)
class SmallCNN(nn.Module):
def __init__(self, num_classes=10):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 16, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2), # 32x32 -> 16x16
nn.Conv2d(16, 32, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2), # 16x16 -> 8x8
)
self.classifier = nn.Linear(32 * 8 * 8, num_classes)
def forward(self, x):
x = self.features(x)
x = x.flatten(1)
return self.classifier(x)
model = SmallCNN(num_classes)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)
num_epochs = 5
epoch_losses = []
for epoch in range(num_epochs):
running_loss = 0.0
for batch_images, batch_labels in loader:
optimizer.zero_grad()
outputs = model(batch_images)
loss = criterion(outputs, batch_labels)
loss.backward()
optimizer.step()
running_loss += loss.item() * batch_images.size(0)
epoch_loss = running_loss / num_samples
epoch_losses.append(epoch_loss)
print(f"Epoch {epoch+1}/{num_epochs} - loss: {epoch_loss:.4f}")
print("Training complete.")
print(f"Loss went from {epoch_losses[0]:.4f} to {epoch_losses[-1]:.4f} over {num_epochs} epochs.")
Line 5 ခု ထွက်ပါမယ် — "Epoch 1/5 - loss: X.XXXX" ကနေ "Epoch 5/5 - loss: X.XXXX" အထိ၊ ပြီးရင် "Training complete." နဲ့ loss trend summary line တစ်ကြောင်း။ Label တွေက image တွေနဲ့ ဘာမှ ဆက်စပ်မှု မရှိတဲ့ random data ဖြစ်လို့ Epoch 1 ရဲ့ loss က 10-class cross entropy ရဲ့ random-guess တန်ဖိုးဖြစ်တဲ့ ln(10)≈2.30 အနီးအနားက စတင်ပါမယ်။ Sample 200 ခုသာ ရှိပြီး CNN မှာ capacity လုံလောက်စွာ ရှိတာကြောင့် epoch 5 ထိကို loss က တဖြည်းဖြည်း ကျသွားနိုင်ပါတယ် (monotonic မဖြစ်ချင်ဖြစ်နိုင်) — ဒါက small training set ကို memorize လုပ်နေတာသာ ဖြစ်ပြီး real generalization မဟုတ်ပါ။၅ မိနစ် စမ်းကြည့်
Training loop ကို ပြောင်းလဲပြီး validation split တစ်ခု ထည့်ပါ — synthetic sample 200 ခုကို train 160 / validation 40 ခွဲပြီး၊ epoch တိုင်းမှာ validation loss ကိုပါ ထပ်တွက်ပြီး print ထုတ်ကြည့်ပါ။ Train loss က ကျနေပေမယ့် validation loss က မကျဘူးဆိုရင် ဘာကြောင့်ဖြစ်နေမလဲဆိုတာ တွေးကြည့်ပါ။
သတိလေးတစ်ချက်
DataLoader ကနေ batch_images/batch_labels ကို optimizer.zero_grad() မခေါ်ဘဲ loss.backward() ခေါ်မိရင် gradient တွေက batch အသီးသီးကနေ စုပေါင်း (accumulate) သွားပြီး loss trend က ရှင်းရှင်းလင်းလင်း ကြည့်လို့ မရတော့ပါဘူး။
Loss ကျသွားတာကို "model က learn နေတယ်" လို့ တိုက်ရိုက်ယူဆလိုက်ရင် မှားနိုင်ပါတယ် — random label ပေါ်မှာ small dataset ကို memorize နေတာသာ ဖြစ်နိုင်ပြီး၊ real (unrelated) test data နဲ့ စစ်ကြည့်ရင် ဘာမှ မကောင်းပါဘူး။
PyTorch Tutorials — Training a Classifier (CIFAR10) — Computer Vision