فاز 4 · درس 5

یادگیری انتقالی و فاین‌تیونینگ در بینایی ماشین

نوع
ساختنی
زمان
حدود 75 دقیقه
زبان‌ها
Python
یک مجسمه‌ساز که در حال تراشیدن و شکل‌دادن دقیق به قسمت بالایی یک ستون سنگی از پیش‌ساخته است
فهرست مطالب
  1. اهداف یادگیری
  2. صورت مسئله
  3. مفاهیم پایه
  4. استخراج ویژگی در برابر فاین‌تیونینگ
  5. چرا انجماد لایه‌ها اصلاً جواب می‌دهد؟
  6. نرخ‌های یادگیری تفکیکی
  7. معضل نرمال‌سازی دسته‌ای (BatchNorm)
  8. طراحی سر مدل (Head)
  9. کاهش تدریجی لایه‌ای نرخ یادگیری (Layer-wise LR decay)
  10. چه مواردی را باید ارزیابی کرد؟
  11. پیاده‌سازی گام‌به‌گام
  12. مرحله‌ی 1: بارگذاری یک ستون فقرات از‌پیش‌آموزش‌دیده و بررسی آن
  13. مرحله‌ی 2: استخراج ویژگی — انجماد کامل و تعویض سر مدل
  14. مرحله‌ی 3: فاین‌تیونینگ تفکیکی
  15. مرحله‌ی 4: مدیریت نرمال‌سازی دسته‌ای (BatchNorm)
  16. مرحله‌ی 5: یک حلقه‌ی کمینه‌ی فاین‌تیونینگ سراسری
  17. مرحله‌ی 6: یخ‌زدایی تدریجی
  18. استفاده از ابزارهای واقعی
  19. خروجی این درس
  20. تمرین‌ها
  21. اصطلاحات کلیدی
  22. برای مطالعه‌ی بیشتر

آموزش یک شبکه‌ی عصبی عمیق بینایی مانند ResNet از نقطه‌ی صفر، به هزاران ساعت پردازش گرافیکی و حجم عظیمی از داده‌های برچسب‌خورده نیاز دارد. برای بیشتر برنامه‌نویسان، دسترسی به چنین سخت‌افزارهای سنگینی مقدور یا منطقی نیست. با به‌کارگیری یادگیری انتقالی (Transfer Learning)، می‌توانیم الگوها و ویژگی‌های بصری پایه‌ای را که شرکت‌های بزرگ با هزینه‌های بالا استخراج کرده‌اند، مستقیماً به پروژه‌ی خود منتقل کنیم.

در ایران، با توجه به دشواری و هزینه‌ی بسیار بالای اجاره یا تهیه‌ی GPUهای ابری خارجی، یادگیری انتقالی اهمیت دوچندانی پیدا می‌کند. این رویکرد به شما اجازه می‌دهد حتی با داده‌های محدود و روی سیستم شخصی، پردازنده‌های معمولی (CPU) یا تراشه‌های در دسترس، مدل‌هایی با دقت بالا برای کاربردهای بومی مانند پردازش تصاویر پزشکی، صنعتی یا نظارتی بسازید.

در این درس یاد می‌گیرید که چه زمانی لایه‌های شبکه را منجمد کنید، چگونه سر کلاسیفایر جدید بسازید و با استفاده از نرخ یادگیری تفکیکی (Discriminative Learning Rate) و مدیریت صحیح نرمال‌سازی دسته‌ای (BatchNorm)، جلوی خطاهای خاموشی مثل رانش ویژگی‌ها و فراموشی فاجعه‌بار را بگیرید.

دیگران یک میلیون ساعت پردازش گرافیکی (GPU-hour) صرف کرده‌اند تا به یک شبکه یاد بدهند لبه‌ها، بافت‌ها و بخش‌های اشیا چه شکلی هستند. بهتر است پیش از آموزش مدل خودتان، از همان ویژگی‌ها قرض بگیرید.

اهداف یادگیری

  • تشخیص تفاوت استخراج ویژگی (feature extraction) از فاین‌تیونینگ (fine-tuning) و انتخاب روش مناسب بر اساس اندازه‌ی دیتاست، فاصله‌ی دامنه (domain distance) و بودجه‌ی محاسباتی
  • بارگذاری یک ستون فقرات (backbone) از‌پیش‌آموزش‌دیده، جایگزینی سرِ کلاسیفایر (classifier head) آن، و آموزش صرفاً همان سر تا رسیدن به یک خط مبنای (baseline) کارآمد در کمتر از 20 خط کد
  • یخ‌زدایی تدریجی (progressive unfreezing) لایه‌ها با نرخ‌های یادگیری تفکیکی (discriminative learning rates)، به‌طوری‌که ویژگی‌های عمومی لایه‌های اولیه نسبت به ویژگی‌های وظیفه‌محور لایه‌های پایانی، به‌روزرسانی‌های کوچک‌تری دریافت کنند
  • عیب‌یابی سه مشکل رایج: رانش ویژگی (feature drift) ناشی از نرخ یادگیری بیش‌ازحد بالا در بلوک‌های یخ‌زدایی‌شده، فروپاشی آماره‌های نرمال‌سازی دسته‌ای (BatchNorm) روی دیتاست‌های بسیار کوچک، و فراموشی فاجعه‌بار (catastrophic forgetting)

صورت مسئله

آموزش یک ResNet-50 روی ImageNet حدود 2000 ساعت پردازش GPU هزینه دارد. کمتر تیمی برای هر تسکی که دیپلوی می‌کند چنین بودجه‌ای در اختیار دارد. در عمل، تقریباً همه‌ی تیم‌ها یک ستون فقرات از‌پیش‌آموزش‌دیده برمی‌دارند، سرِ جدیدی رویش می‌گذارند و آن را فقط روی چند صد یا چند هزار تصویرِ مخصوص همان مسئله آموزش می‌دهند.

این کار صرفاً یک میان‌بُر نیست. اولین بلوک کانولوشنی هر شبکه‌ی عصبی پیچشی (CNN) آموزش‌دیده روی ImageNet، لبه‌ها و فیلترهای شبه‌گابور (Gabor-like) را یاد می‌گیرد. چند بلوک بعدی بافت‌ها و الگوهای ساده را یاد می‌گیرند. بلوک‌های میانی بخش‌های مختلف اشیا را می‌آموزند و بلوک‌های انتهایی هم ترکیب‌هایی را یاد می‌گیرند که شبیه به 1000 دسته‌ی ImageNet به نظر می‌رسند. 90% نخستِ این سلسله‌مراتب تقریباً دست‌نخورده به حوزه‌های تصویربرداری پزشکی، بازرسی صنعتی، داده‌های ماهواره‌ای و هر وظیفه‌ی بینایی ماشینِ دیگری منتقل می‌شود — چون طبیعت واژگان محدودی از لبه‌ها و بافت‌ها دارد. آن 10% آخر چیزی است که شما واقعاً باید آموزش دهید.

یادگیری انتقالی (transfer learning) درست، سه تله‌ی پنهان پیش پای شما می‌گذارد: نابود کردن ویژگی‌های از‌پیش‌آموزش‌دیده با نرخ یادگیری بیش‌ازحد بالا، محروم کردن مدل از اطلاعات به دلیل منجمد کردن بیش‌ازحد لایه‌ها، و اجازه دادن به آماره‌های متحرکِ BatchNorm برای سوق پیدا کردن به سمت دیتاست بسیار کوچکی که سایر بخش‌های شبکه هرگز چیزی از آن یاد نگرفته‌اند. در این درس عمداً به سراغ تک‌تک این چالش‌ها می‌رویم.

مفاهیم پایه

استخراج ویژگی در برابر فاین‌تیونینگ

دو رویکرد مختلف وجود دارد که بر اساس میزان اعتماد شما به ویژگی‌های از‌پیش‌آموزش‌دیده و حجم داده‌هایتان انتخاب می‌شوند.

فاین‌تیونینگ — سرتاسریاستخراج ویژگی — ستون‌فقرات منجمدستون‌فقرات از پیش‌آموزش‌دیده(بدون گرادیان)سر جدید(آموزش‌دیده)ستون‌فقرات از پیش‌آموزش‌دیده(LR بسیار کم)سر جدید(LR معمولی)

قواعد سرانگشتی:

اندازه‌ی دیتاست فاصله‌ی دامنه دستورالعمل
کمتر از 1k تصویر نزدیک به ImageNet ستون فقرات منجمد (freeze)، فقط سرِ مدل آموزش ببیند
1k تا 10k نزدیک 2 تا 3 مرحله‌ی اول منجمد شود، بقیه فاین‌تیون شوند
10k تا 100k هر حالتی فاین‌تیونینگ سراسری (end-to-end) با نرخ یادگیری تفکیکی
بیش از 100k دور همه‌چیز فاین‌تیون شود؛ اگر دامنه خیلی دور است، آموزش از صفر را بررسی کنید

«نزدیک به ImageNet» یعنی عکس‌های طبیعی RGB با محتوایی از جنس اشیا. سی‌تی‌اسکن‌های پزشکی، تصاویر هوایی ماهواره‌ای و تصاویر میکروسکوپی دامنه‌های دور محسوب می‌شوند — ویژگی‌های قبلی باز هم مفیدند، اما باید بگذارید لایه‌های بیشتری خود را تطبیق دهند.

چرا انجماد لایه‌ها اصلاً جواب می‌دهد؟

ویژگی‌هایی که یک CNN از ImageNet یاد می‌گیرد به آن 1000 دسته محدود نمی‌شوند؛ بلکه به آماره‌های بنیادینِ تصاویر طبیعی اختصاص یافته‌اند: لبه‌ها در زوایای خاص، بافت‌ها، الگوهای کنتراست و ساختارهای اولیه‌ی شکل‌ها. این آماره‌ها در تقریباً هر دامنه‌ی بصری شناخته‌شده‌ای پایدارند. به همین دلیل مدلی که روی ImageNet آموزش دیده، روی CIFAR-10 به‌صورت صفر-شات (zero-shot) و صرفاً با یک سرِ خطی جدید (بدون فاین‌تیونینگ ستون فقرات) به دقت بالای 80% می‌رسد. سرِ مدل فقط دارد یاد می‌گیرد به کدام‌یک از ویژگی‌های از‌پیش‌آموخته وزن بدهد.

نرخ‌های یادگیری تفکیکی

وقتی لایه‌ها را یخ‌زدایی می‌کنید، لایه‌های اولیه باید کُندتر از لایه‌های انتهایی آموزش ببینند. لایه‌های اولیه ویژگی‌های عمومی را کدگذاری کرده‌اند که می‌خواهید حفظ شوند؛ لایه‌های انتهایی ساختارهای مختص وظیفه را دارند که باید تغییرات زیادی بکنند.

Typical recipe:

  stage 0 (stem + first group): lr = base_lr / 100    (mostly fixed)
  stage 1:                       lr = base_lr / 10
  stage 2:                       lr = base_lr / 3
  stage 3 (last backbone group): lr = base_lr
  head:                          lr = base_lr  (or slightly higher)

در PyTorch این کار فقط با ارسال لیستی از گروه‌های پارامتری (parameter groups) به بهینه‌ساز (optimizer) انجام می‌شود. یک مدل، پنج نرخ یادگیری، بدون حتی یک خط کد اضافه.

معضل نرمال‌سازی دسته‌ای (BatchNorm)

لایه‌های BN دارای بافرهای running_mean و running_var هستند که روی ImageNet محاسبه شده‌اند. اگر داده‌های مسئله‌ی شما توزیع پیکسلی متفاوتی داشته باشند — نورپردازی متفاوت، سنسور دیگر، یا فضای رنگی دیگر — این بافرها خطا دارند. سه راهکار به ترتیب اولویت وجود دارد:

  1. فاین‌تیونینگ با BN در حالت train. بگذارید BN آماره‌های متحرک خود را همراه با بقیه‌ی شبکه به‌روز کند. انتخاب پیش‌فرض برای زمانی که دیتاستِ مسئله متوسط است (حداقل 5k نمونه).
  2. انجماد BN در حالت eval. آماره‌های ImageNet را نگه دارید و فقط وزن‌ها را آموزش دهید. این حالت زمانی درست است که دیتاست‌تان آن‌قدر کوچک باشد که میانگین متحرک BN نویز زیادی بگیرد.
  3. جایگزینی BN با GroupNorm. مشکل میانگین متحرک را به‌کل حذف می‌کند. این کار بیشتر در ستون‌های فقرات تشخیص و بخش‌بندی (segmentation) به کار می‌رود که اندازه‌ی دسته (batch size) به ازای هر GPU بسیار کوچک است.

اشتباه در این بخش می‌تواند بی‌سر‌و‌صدا دقت مدل را بین 5 تا 15% کاهش دهد.

طراحی سر مدل (Head)

سرِ کلاسیفایر معمولاً از 1 تا 3 لایه‌ی خطی به همراه یک دراپ‌اوت (dropout) اختیاری تشکیل می‌شود. هر ستون فقرات در torchvision یک سر پیش‌فرض دارد که شما آن را جایگزین می‌کنید:

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

برای دیتاست‌های کوچک، یک لایه‌ی خطی تکی معمولاً کافی است. افزودن یک لایه‌ی مخفی (Linear -> ReLU -> Dropout -> Linear) زمانی کمک می‌کند که توزیع داده‌های مسئله فاصله‌ی بیشتری از توزیع داده‌های آموزشی ستون فقرات داشته باشد.

کاهش تدریجی لایه‌ای نرخ یادگیری (Layer-wise LR decay)

نسخه‌ی هموارترِ نرخ یادگیری تفکیکی است که در فاین‌تیونینگ‌های مدرن (مانند BEiT، DINOv2 و ترنسفورمرهای بینایی ViT-B) استفاده می‌شود. به‌جای گروه‌بندی لایه‌ها در چند مرحله، به هر لایه نسبت به لایه‌ی بالاتر از خود، نرخ یادگیری کمی کوچک‌تری اختصاص می‌دهید:

lr_layerk=base_lr⋅decayL−k\text{lr\_layer}_k = \text{base\_lr} \cdot \text{decay}^{L - k}

با نرخ کاهش (decay) برابر 0.75 و 12 بلوک ترنسفورمر (L = 12)، اولین بلوک با نرخ یادگیری معادل 0.75^11 ≈ 0.04x نسبت به سر مدل آموزش می‌بیند. این موضوع در فاین‌تیونینگ ترنسفورمرها بسیار حیاتی‌تر از CNNها است؛ در CNNها معمولاً همان گروه‌بندی مرحله‌ای کفایت می‌کند.

چه مواردی را باید ارزیابی کرد؟

در اجرای یادگیری انتقالی باید دو عدد را ثبت کنید که در آموزش از صفر به آن‌ها نیازی نداشتید:

  • دقت فقط از‌پیش‌آموزش‌دیده (Pretrained-only accuracy) — دقت سرِ مدل در حالتی که ستون فقرات منجمد است. این کفِ عملکرد شماست.
  • دقت فاین‌تیون‌شده (Fine-tuned accuracy) — عملکرد همان مدل پس از آموزش سراسری. این سقفِ عملکرد شماست.

اگر دقت فاین‌تیون‌شده کمتر از دقت فقط از‌پیش‌آموزش‌دیده باشد، مطمئن باشید در نرخ یادگیری یا تنظیمات BN باگ دارید. همیشه هر دو مقدار را چاپ کنید.

یادگیری انتقالی (Transfer Learning)خط فریز (انجماد) را بکشید
TRANSFER LEARNINGA pretrained backbone already knows generic features — edges, textures, shapes. Transfer learning freezes those lower layers and trains only the top few plus a new head on the target task. Fewer trainable parameters means fewer gradients to store, far less data to fit, and faster convergence; freeze more when your dataset is tiny, fewer when it is large and different.ورودی ← 18 لایه فریز (خاکستری) · 6 لایه آموزش‌پذیر (آبی) ← headگرادیان‌ها فقط در لایه‌های آبی جریان می‌یابند
یک ستون فقرات (backbone) از پیش آموزش‌دیده از قبل ویژگی‌های عمومی مانند لبه‌ها، بافت‌ها و اشکال را می‌شناسد. یادگیری انتقالی این لایه‌های پایینی را فریز می‌کند و فقط چند لایه بالایی به‌همراه یک هد (head) جدید را روی وظیفه هدف آموزش می‌دهد. پارامترهای آموزش‌پذیر کمتر به معنای گرادیان‌های کمتر برای ذخیره‌سازی، داده‌های بسیار کمتر برای برازش و همگرایی سریع‌تر است؛ زمانی که مجموعه‌داده شما بسیار کوچک است لایه‌های بیشتری را فریز کنید و وقتی بزرگ و متفاوت است، لایه‌های کمتری را.

پیاده‌سازی گام‌به‌گام

مرحله‌ی 1: بارگذاری یک ستون فقرات از‌پیش‌آموزش‌دیده و بررسی آن

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 چهار مرحله (layer1..layer4) به همراه یک بخش ورودی (stem) و یک سرِ fc دارد. همه‌ی مدل‌های پایه‌ی طبقه‌بندی در torchvision ساختار مشابهی دارند.

مرحله‌ی 2: استخراج ویژگی — انجماد کامل و تعویض سر مدل

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,}")

اکنون فقط model.fc قابل‌آموزش است و ستون فقرات نقش یک استخراج‌کننده‌ی ویژگیِ منجمد را بازی می‌کند.

مرحله‌ی 3: فاین‌تیونینگ تفکیکی

تابعی کمکی برای ساخت گروه‌های پارامتری با نرخ‌های یادگیری اختصاصی به ازای هر مرحله.

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 به این معنی است که هر مرحله با 30% نرخ یادگیری مرحله‌ی بعد از خود آموزش می‌بیند. لایه‌ی fc مقدار base_lr را می‌گیرد، layer4 مقدار 0.3 * base_lr، و conv1 مقدار 0.3^5 * base_lr ≈ 0.00243 * base_lr را دریافت می‌کند. شاید شدید به نظر برسد، اما در عمل جواب می‌دهد.

مرحله‌ی 4: مدیریت نرمال‌سازی دسته‌ای (BatchNorm)

یک تابع کمکی برای منجمد نگه‌داشتن آماره‌های متحرک BN بدون انجمادِ وزن‌های آن.

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

این تابع را در شروع هر دوره‌ی آموزشی (epoch)، درست بعد از فراخوانی model.train() صدا بزنید. دستور model.train() همه‌چیز را به حالت آموزش می‌برد؛ این تابع فقط لایه‌های BN را دوباره به حالت ارزیابی برمی‌گرداند.

مرحله‌ی 5: یک حلقه‌ی کمینه‌ی فاین‌تیونینگ سراسری

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

پنج دوره‌ی آموزشی با دستورالعمل بالا روی CIFAR-10، دقت مدل ResNet18-IMAGENET1K_V1 را از حدود 70% در حالت ارزیابی خطی صفر-شات به حدود 93% در حالت فاین‌تیون‌شده می‌رساند. اگر ستون فقرات را دست‌نخورده رها می‌کردید، سرِ مدل به‌تنهایی روی حدود 86% متوقف می‌شد.

مرحله‌ی 6: یخ‌زدایی تدریجی

یک زمان‌بندی که در هر دوره‌ی آموزشی، یک مرحله از مدل را از انتها به سمت ابتدا یخ‌زدایی می‌کند. این روش رانش ویژگی‌ها را با هزینه‌ی چند دوره‌ی آموزشیِ بیشتر مهار می‌کند.

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

متد start() را یک بار پیش از دوره‌ی اول صدا بزنید. در ابتدای هر دوره‌ی آموزشی، unfreeze(epoch) را فراخوانی کنید. هر زمان که مجموعه‌ی پارامترهای آموزش‌پذیر تغییر کرد، بهینه‌ساز را از نو بسازید؛ در غیر این صورت پارامترهای منجمدشده هنوز مقادیر گشتاور (moment) کش‌شده‌ای دارند که بهینه‌ساز را گیج می‌کند.

استفاده از ابزارهای واقعی

برای بیشتر کارهای واقعی، ترکیب torchvision.models به همراه سه خط کد کافی است. منطق پیچیده‌تری که در بالا بررسی کردیم زمانی اهمیت پیدا می‌کند که به مشکلاتی بخورید که تنظیمات پیش‌فرض کتابخانه‌ها قادر به حل آن‌ها نیستند.

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)

دو ابزار استاندارد و آماده‌ی استفاده در محیط‌های عملیاتی:

  • کتابخانه‌ی timm حدود 800 ستون فقرات بینایی از‌پیش‌آموزش‌دیده را با یک API کاملاً یکپارچه ارائه می‌دهد (timm.create_model("resnet50", pretrained=True, num_classes=10)). برای هر فاین‌تیونینگی فراتر از مدل‌های torchvision، این کتابخانه گزینه‌ی استاندارد است.
  • برای ترنسفورمرها، دستور transformers.AutoModelForImageClassification.from_pretrained(name, num_labels=N) مدل‌های ViT ،BEiT یا DeiT را دقیقاً با همان ساختار بارگذاری مدل‌های متنی در اختیارتان می‌گذارد.

خروجی این درس

از این درس به دو خروجی می‌رسید:

  • outputs/prompt-fine-tune-planner.md — پرامپتی که بر اساس اندازه‌ی دیتاست، فاصله‌ی دامنه و بودجه‌ی محاسباتی، بهترین روش را از میان استخراج ویژگی، یخ‌زدایی تدریجی یا فاین‌تیونینگ سراسری پیشنهاد می‌دهد.
  • outputs/skill-freeze-inspector.md — مهارتی که با دریافت یک مدل PyTorch گزارش می‌دهد کدام پارامترها آموزش‌پذیرند، کدام لایه‌های BatchNorm در حالت eval هستند، و آیا بهینه‌ساز واقعاً فقط پارامترهای آموزش‌پذیر را دریافت می‌کند یا خیر.

تمرین‌ها

  1. (ساده) مدل ResNet18 را یک بار به عنوان ارزیاب خطی (linear probe با ستون فقرات منجمد) و یک بار به‌صورت فاین‌تیونینگ کامل روی داده‌های شبیه‌سازی‌شده‌ی CIFAR آموزش دهید. دقت هر دو حالت را کنار هم گزارش کنید. توضیح دهید کدام فاصله‌ی دقتی نشان‌دهنده‌ی انتقال خوب ویژگی‌هاست و کدام نشان می‌دهد ویژگی‌ها به‌خوبی منتقل نشده‌اند.
  2. (متوسط) عمداً یک باگ ایجاد کنید: مقدار base_lr = 1e-1 را به‌جای سر مدل، روی مراحل ستون فقرات تنظیم کنید. نشان دهید که چگونه تابع زیان (loss) آموزش منفجر می‌شود، سپس با اعمال تابع کمکی discriminative_param_groups مدل را بازیابی کنید. نرخ یادگیری‌ای را که در آن هر مرحله شروع به واگرایی می‌کند ثبت کنید.
  3. (سخت) یک دیتاست تصاویر پزشکی (مانند CheXpert-small ،PatchCamelyon یا HAM10000) بردارید و سه روش را مقایسه کنید: الف) ستون فقرات منجمدِ از‌پیش‌آموزش‌دیده روی ImageNet به همراه سرِ خطی؛ ب) فاین‌تیونینگ سراسری از‌پیش‌آموزش‌دیده روی ImageNet؛ ج) آموزش از صفر. دقت و هزینه‌ی محاسباتی هر کدام را گزارش کنید. در چه اندازه‌ای از دیتاست، آموزش از صفر توان رقابت با بقیه را پیدا می‌کند؟

اصطلاحات کلیدی

اصطلاح در گفتار عامیانه معنای دقیق فنی
Feature extraction «منجمد کردن و آموزشِ سر» پارامترهای ستون فقرات منجمد هستند و فقط سرِ جدید کلاسیفایر گرادیان دریافت می‌کند
Fine-tuning «آموزش مجدد از اول تا آخر» همه‌ی پارامترها آموزش‌پذیرند، معمولاً با نرخ یادگیری بسیار کمتری نسبت به آموزش از صفر
Discriminative LR «نرخ یادگیری کمتر برای لایه‌های اول» گروه‌بندی پارامترهای بهینه‌ساز به‌طوری‌که نرخ یادگیری مراحل اولیه کسری از نرخ مراحل انتهایی باشد
Layer-wise LR decay «گرادیان هموارِ نرخ یادگیری» ضرب نرخ یادگیری هر لایه در decay^(L - k)؛ رایج در فاین‌تیونینگ ترنسفورمرها
Catastrophic forgetting «مدل اطلاعات ImageNet را از دست داد» نرخ یادگیری بیش‌ازحد بالا، ویژگی‌های از‌پیش‌آموزش‌دیده را قبل از یادگیری وظیفه‌ی جدید بازنویسی و نابود می‌کند
BN statistics drift «میانگین متحرک اشتباه است» مقادیر running_mean/var در لایه‌های BatchNorm روی توزیعی متفاوت از مسئله‌ی فعلی محاسبه شده‌اند و بی‌سر‌و‌صدا به دقت ضربه می‌زنند
Linear probe «ستون فقرات منجمد + سرِ خطی» ارزیابی کیفیت ویژگی‌های از‌پیش‌آموزش‌دیده — یعنی دقت بهترین کلاسیفایر خطی که روی این بازنمایی منجمد سوار می‌شود
Catastrophic collapse «مدل برای همه‌چیز یک کلاس را پیش‌بینی می‌کند» وقتی رخ می‌دهد که فاین‌تیونینگ با چنان نرخ یادگیری بالایی انجام شود که ویژگی‌ها پیش از پایدار شدن گرادیان‌های سر مدل نابود شوند

برای مطالعه‌ی بیشتر

خودآزمایی

به سؤال‌ها جواب بدهید تا ببینید چقدر از درس در ذهنتان مانده است.

  1. پیش از درسشما 500 تصویر برچسب‌دار برای یک وظیفه جدید نزدیک به توزیع ImageNet دارید. کدام رژیم آموزشی منطقی‌تر است؟
  2. پیش از درسچرا لایه‌های کانولوشنی اولیه یک شبکه از پیش آموزش‌دیده روی ImageNet به خوبی به تصاویر پزشکی منتقل می‌شوند، در حالی که ImageNet حاوی هیچ تصویر اشعه ایکسی نیست؟
  3. پس از درسهنگام فاین‌تیونینگ سراسری (end-to-end) با نرخ‌های یادگیری تفکیکی (discriminative)، چرا لایه‌های اولیه باید نرخ یادگیری کمتری نسبت به لایه‌های انتهایی دریافت کنند؟
  4. پس از درسیک ResNet را روی مجموعه‌داده پزشکی با 10 کلاس شامل 800 تصویر سیاه و سفید (کپی‌شده در 3 کانال) فاین‌تیون می‌کنید. دقت 10٪ است (معادل شانس تصادفی برای 10 کلاس). محتمل‌ترین علت چیست؟
  5. پس از درسدو اجرا را مقایسه می‌کنید: (الف) کاوشگر خطی روی ستون فقرات فریز شده ImageNet با دقت 82٪؛ (ب) فاین‌تیونینگ سراسری با دقت 78٪. چه نتیجه‌ای باید بگیرید؟

سؤالات متداول

تفاوت استخراج ویژگی و فاین‌تیونینگ چیست؟

در استخراج ویژگی، وزن‌های ستون فقرات (backbone) کاملاً منجمد می‌شوند و فقط سرِ جدید کلاسیفایر آموزش می‌بیند؛ اما در فاین‌تیونینگ، بخشی از لایه‌ها یا تمام شبکه با نرخ یادگیری کوچک دوباره آموزش داده می‌شوند.

چرا لایه‌های ابتدایی شبکه باید نرخ یادگیری کمتری داشته باشند؟

لایه‌های اولیه ویژگی‌های عمومی مانند لبه‌ها و بافت‌ها را یاد گرفته‌اند که در تمام تصاویر یکسانند. کاهش نرخ یادگیری در این لایه‌ها مانع از نابودی این ویژگی‌های مفید در طول آموزش می‌شود.

چرا آماره‌های BatchNorm در دیتاست‌های کوچک دچار مشکل می‌شوند؟

چون مقادیر میانگین و واریانس متحرک BatchNorm بر اساس حجم عظیمی از داده‌های قبلی تنظیم شده‌اند و به‌روزرسانی آن‌ها روی دیتاست‌های بسیار کوچک باعث نویز و افت 5 تا 15 درصدی دقت می‌شود.

ما رو توی گوگل بیشتر ببین

با افزودن نکست ادمین به «منابع ترجیحی» گوگل، درس‌ها و مطالب ما را در بخش اخبار برتر جستجو بیشتر می‌بینید.