برچسبگذاری دستی مجموعهدادههای تصویری در حوزههای تخصصی مثل پزشکی یا خطوط تولید صنعتی، هزینهبر و در بسیاری از پروژهها ناممکن است. مدلهای نظارتشده سنتی بدون دهها هزار نمونهی دستهبندیشده کیفیت مطلوبی ارائه نمیدهند و تغییر توزیع دادهها نیز بهسرعت آنها را از کار میاندازد.
یادگیری خودنظارتی (Self-Supervised Learning) این بنبست را با حذف نیاز به برچسب در مرحلهی پیشآموزش حل میکند. مدل با یادگیری ویژگیهای غنی از میلیونها تصویر بدون برچسب، به پایهای قدرتمند تبدیل میشود که تنها با چند نمونهی برچسبدار و یک لایهی خطی ساده، دقتی فراتر از آموزشهای نظارتشدهی کلاسیک به دست میآورد.
در این درس ساختار سه رویکرد اصلی یادگیری خودنظارتی در بینایی ماشین یعنی روشهای مقابلهای (SimCLR)، معماری معلم-شاگرد (DINO) و خودرمزنگارهای پوشاندهشده (MAE) را بررسی میکنیم و پیادهسازی گامبهگام آنها را در پایتورچ پیش میبریم.
برچسبها گلوگاه بینایی ماشین در یادگیری نظارتشده (Supervised Learning) هستند. پیشآموزش خودنظارتی (self-supervised pretraining) این گلوگاه را حذف میکند: ویژگیهای بصری را از 100M تصویر بدون برچسب یاد بگیرید و سپس مدل را روی 10k تصویر برچسبدار فاینتیون کنید.
اهداف یادگیری
- بررسی سه خانوادهی اصلی روشهای خودنظارتی: یادگیری مقابلهای (contrastive مانند SimCLR)، معلم-شاگرد (teacher-student مانند DINO) و بازسازی پوشاندهشده (masked reconstruction مانند MAE) و مشخص کردن تابع هدف هر کدام
- پیادهسازی تابع زیان (loss) موسوم به InfoNCE از صفر و تحلیل اینکه چرا در batch size برابر با 512 کار میکند، اما در batch size برابر با 32 شکست میخورد
- توضیح علت تصادفی نبودن نسبت ماسک 75% در MAE و تفاوت ماهوی آن با نسبت 15% در BERT برای متن
- استفاده از چکپوینتهای ImageNet در DINOv2 یا MAE برای پروب خطی (linear probing) و بازیابی دادهی صفرشات (zero-shot retrieval)
صورت مسئله
مجموعهدادهی نظارتشدهی ImageNet حدود 1.3M تصویر برچسبدار دارد که برچسبگذاری آنها نزدیک به $10M هزینه برداشته است. دیتاستهای حوزهی پزشکی و صنعتی حتی از این هم کوچکتر و پرهزینهترند. دغدغهی همیشگی هر تیم کامپیوتر ویژن این است: آیا میتوانیم ابتدا روی دادههای ارزان و بدون برچسب (فریمهای YouTube، خزشهای وب، تصاویر وبکم یا دادههای ماهوارهای) مدل را پیشآموزش دهیم و سپس آن را روی یک مجموعهی کوچک از دادههای برچسبدار فاینتیون کنیم؟
پاسخ یادگیری خودنظارتی است. یک ViT مدرن خودنظارتی که روی LAION یا JFT آموزش دیده باشد، پس از فاینتیونینگ به دقتی برابر با ImageNet نظارتشده یا حتی بهتر از آن میرسد. همچنین در مقایسه با پیشآموزش نظارتشده، به تسکهای پاییندستی (مانند تشخیص شیء، بخشبندی و تخمین عمق) بهتر منتقل میشود. در حال حاضر مدلهای DINOv2 (محصول متا، 2023) و MAE (محصول متا، 2022) گزینههای پیشفرض محیط پروداکشن برای استخراج ویژگیهای بصری انتقالپذیر هستند.
تغییر بنیادین در این رویکرد به وظیفهی بهانهای (pretext task) برمیگردد؛ کاری که مدل برای انجامش آموزش میبیند لزوماً نباید همان تسک نهایی پاییندستی باشد. مهم این است که این وظیفه مدل را وادار به یادگیری ویژگیهای مفید کند. پیشبینی رنگ تصاویر سیاهوسفید، چرخاندن تصویر و درخواست از مدل برای تشخیص زاویهی چرخش، یا پوشاندن پچها و بازسازی آنها همگی جواب دادهاند. سه رویکردی که در مقیاس بالا پاسخگو بودهاند عبارتاند از: یادگیری مقابلهای، تقطیر معلم-شاگرد و بازسازی پوشاندهشده.
مبانی نظری
سه خانوادهی اصلی
یادگیری مقابلهای (SimCLR)
یک تصویر را بردارید، دو نوع دادهافزایی (augmentation) تصادفی روی آن اعمال کنید و دو نما بسازید. هر دو نما را به یک انکودر یکسان و سپس یک هد تصویر کردن (projection head) بدهید. حالا تابعی را کمینه کنید که میگوید: «امبدینگ این دو نما باید به هم نزدیک باشد» و «امبدینگ این نما باید از امبدینگ تمام تصاویر دیگر موجود در بچ دور باشد».
Loss for positive pair (z_i, z_j) among 2N views per batch:
L_ij = -log( exp(sim(z_i, z_j) / tau) / sum_k in batch \ {i} exp(sim(z_i, z_k) / tau) )
sim = cosine similarity
tau = temperature (0.1 standard)
این همان تابع زیان InfoNCE است. این روش به ازای هر جفت مثبت به نمونههای منفی متعددی نیاز دارد؛ به همین دلیل اندازهی بچ (batch size) اهمیت پیدا میکند؛ SimCLR به مقادیری بین 512 تا 8192 نیاز دارد. معماری MoCo با معرفی یک صف تکانه (momentum queue) از بچهای گذشته، توانست تعداد نمونههای منفی را از اندازهی بچ مستقل کند.
روش معلم-شاگرد (DINO)
دو شبکه با معماری یکسان داریم: شاگرد (student) و معلم (teacher). معلم، میانگین متحرک نمایی (EMA) وزنهای شاگرد است. هر دو شبکه نماهای تغییریافتهی تصویر را میبینند. شاگرد طوری آموزش داده میشود که خروجیاش با خروجی معلم مطابقت داشته باشد؛ بدون هیچ نمونهی منفی صریحی.
loss = CE( student_output(view_1), teacher_output(view_2) )
+ CE( student_output(view_2), teacher_output(view_1) )
teacher_weights = m * teacher_weights + (1 - m) * student_weights (m ≈ 0.996)
چرا مدل در تلهی «پیشبینی یک مقدار ثابت» سقوط نمیکند (collapse رخ نمیدهد)؟ چون خروجی مدل معلم هم مرکززدایی (centering - تفریق میانگین در هر بُعد) و هم تیزسازی (sharpening - تقسیم بر یک دمای کوچک) میشود. مرکززدایی مانع از تسلط یک بُعد خاص میشود؛ تیزسازی هم جلوی سقوط خروجی به توزیع یکنواخت (uniform) را میگیرد.
معماری DINOv2 نسخهی بزرگمقیاس DINO است که روی 142M تصویر گلچینشده آموزش دیده است. ویژگیهای حاصل، در حال حاضر پیشرفتهترین نتایج (SOTA) را برای بازیابی بصری صفرشات و پیشبینی متراکم پیکسلی ثبت کردهاند.
بازسازی پوشاندهشده (MAE)
در این روش 75% از پچهای ورودی یک ViT ماسک میشوند. فقط 25% پچهای مرئی را از انکودر عبور میدهیم. یک دیکودر کوچک، خروجی انکودر را به همراه توکنهای ماسک در موقعیتهای پوشاندهشده دریافت میکند و آموزش میبیند تا پیکسلهای پچهای ماسکشده را بازسازی کند.
Encoder: visible 25% of patches -> features
Decoder: features + mask tokens at masked positions -> reconstructed pixels
Loss: MSE between reconstructed and original pixels on masked patches only
انتخابهای کلیدی در طراحی که باعث موفقیت MAE میشوند:
- نسبت ماسک 75% — بسیار بالا. انکودر را مجبور میکند ویژگیهای معنایی را یاد بگیرد؛ بازسازی 25% پچ پوشاندهشده تقریباً پیشپاافتاده میشد (پیکسلهای مجاور آنقدر همبستگی بالایی دارند که یک CNN هم میتوانست از پس آن برآید).
- انکودر/دیکودر نامتقارن — انکودر بزرگ ViT فقط پچهای مرئی را میبیند؛ یک دیکودر کوچک (8 لایه با بعد 512) مسئولیت بازسازی را بر عهده دارد. سرعت پیشآموزش در این حالت 3 برابر سریعتر از BEiT اولیه است.
- تارگت بازسازی در فضای پیکسل — بسیار سادهتر از تارگت توکنایزشدهی BEiT است و روی ViT بهتر جواب میدهد.
پس از پایان پیشآموزش، دیکودر را کنار بگذارید. انکودر همان استخراجکنندهی ویژگی شماست.
چرا 75% و نه 15%؟
مدل BERT حدود 15% از توکنها را ماسک میکند، اما MAE حدود 75% را. تفاوت در چگالی اطلاعات است.
- زبان طبیعی در هر توکن آنتروپی بالایی دارد. پیشبینی 15% از توکنها همچنان دشوار است، چون برای هر موقعیت ماسکشده گزینههای محتمل زیادی وجود دارد.
- پچهای تصویری آنتروپی پایینی دارند؛ ناحیهی بدون ماسک مجاور اغلب پیکسلهای پچ ماسکشده را تقریباً با دقت کامل لو میدهد. برای اینکه پیشبینی نیازمند درک معنایی باشد، باید تهاجمی ماسک کنید.
نسبت 75% آنقدر بالا هست که برونیابی سادهی فضایی نتواند تسک را حل کند؛ انکودر مجبور است کل محتوای تصویر را بازنمایی کند.
ارزیابی با پروب خطی
روش استاندارد ارزیابی پس از پیشآموزش خودنظارتی، پروب خطی (linear probe) است: انکودر را فریز کنید و یک کلاسیفایر خطی تکی روی آن با برچسبهای ImageNet آموزش دهید. خروجی بر اساس دقت top-1 گزارش میشود.
- مدل SimCLR ResNet-50: حدود 71% (سال 2020)
- مدل DINO ViT-S/16: حدود 77% (سال 2021)
- مدل MAE ViT-L/16: حدود 76% (سال 2022)
- مدل DINOv2 ViT-g/14: حدود 86% (سال 2023)
پروب خطی معیاری خالص برای کیفیت ویژگیهاست؛ در مقابل، فاینتیونینگ معمولاً 2 تا 5 درصد دقت را بالاتر میبرد، اما اثر آموزش مجدد هد را هم با آن درمیآمیزد.
پیادهسازی
گام 1: پایپلاین تولید دو نما با دادهافزایی
import torch
import torchvision.transforms as T
two_view_train = lambda: T.Compose([
T.RandomResizedCrop(96, scale=(0.2, 1.0)),
T.RandomHorizontalFlip(),
T.ColorJitter(0.4, 0.4, 0.4, 0.1),
T.RandomGrayscale(p=0.2),
T.ToTensor(),
])
class TwoViewDataset(torch.utils.data.Dataset):
def __init__(self, base):
self.base = base
self.aug = two_view_train()
def __len__(self):
return len(self.base)
def __getitem__(self, i):
img, _ = self.base[i]
v1 = self.aug(img)
v2 = self.aug(img)
return v1, v2
هر __getitem__ دو نمای تغییریافته از یک تصویر را برمیگرداند؛ نیازی به برچسب نیست.
گام 2: تابع زیان InfoNCE
import torch.nn.functional as F
def info_nce(z1, z2, tau=0.1):
"""
z1, z2: (N, D) L2-normalised embeddings of paired views
"""
N, D = z1.shape
z = torch.cat([z1, z2], dim=0) # (2N, D)
sim = z @ z.T / tau # (2N, 2N)
mask = torch.eye(2 * N, dtype=torch.bool, device=z.device)
sim = sim.masked_fill(mask, float("-inf"))
targets = torch.cat([torch.arange(N, 2 * N), torch.arange(0, N)]).to(z.device)
return F.cross_entropy(sim, targets)
پیش از فراخوانی، امبدینگها را با نرم L2 نرمالسازی کنید. مقدار پیشفرض در SimCLR برابر با tau=0.1 است؛ دمای پایینتر تابع زیان را تندتر (sharper) میکند و به منفیهای بیشتری نیاز دارد.
گام 3: بررسی اولیه و اعتبارسنجی InfoNCE
z1 = F.normalize(torch.randn(16, 32), dim=-1)
z2 = z1.clone()
loss_same = info_nce(z1, z2, tau=0.1).item()
z2_random = F.normalize(torch.randn(16, 32), dim=-1)
loss_random = info_nce(z1, z2_random, tau=0.1).item()
print(f"InfoNCE with identical pairs: {loss_same:.3f}")
print(f"InfoNCE with random pairs: {loss_random:.3f}")
جفتهای یکسان باید زیان پایینی بدهند (در بچ بزرگ و دمای پایین، نزدیک به 0). جفتهای تصادفی در بچی با 16 جفت باید مقداری حدود داشته باشند.
گام 4: ماسک کردن به سبک MAE
def random_mask_indices(num_patches, mask_ratio=0.75, seed=0):
g = torch.Generator().manual_seed(seed)
n_keep = int(num_patches * (1 - mask_ratio))
perm = torch.randperm(num_patches, generator=g)
visible = perm[:n_keep]
masked = perm[n_keep:]
return visible.sort().values, masked.sort().values
num_patches = 196
visible, masked = random_mask_indices(num_patches, mask_ratio=0.75)
print(f"visible: {len(visible)} / {num_patches}")
print(f"masked: {len(masked)} / {num_patches}")
ساده، سریع و به ازای یک seed مشخص کاملاً معین (deterministic). پیادهسازیهای واقعی MAE این کار را بهصورت دستهای انجام میدهند و ماسکهای مجزا را برای هر نمونه نگه میدارند.
کاربرد عملی
مدل DINOv2 استاندارد محیط پروداکشن در سال 2026 است:
import torch
from transformers import AutoImageProcessor, AutoModel
processor = AutoImageProcessor.from_pretrained("facebook/dinov2-base")
model = AutoModel.from_pretrained("facebook/dinov2-base")
model.eval()
# امبدینگهای به ازای هر تصویر برای بازیابی zero-shot
with torch.no_grad():
inputs = processor(images=[pil_image], return_tensors="pt")
outputs = model(**inputs)
embedding = outputs.last_hidden_state[:, 0] # CLS token
امبدینگ 768 بعدی حاصل، ستون فقرات پایپلاینهای مدرن بازیابی تصویر، تطبیق چگال (dense correspondence) و انتقال صفرشات است. برای فاینتیونینگ روی تسکهای پاییندستی، معمولاً به چیزی بیش از یک هد خطی نیاز ندارید.
برای امبدینگهای تصویر-متن، SigLIP یا OpenCLIP گزینههای معادل هستند؛ برای فاینتیونینگ به سبک MAE نیز مخزن timm تمام چکپوینتهای MAE را در اختیارتان میگذارد.
تحویل نهایی
این درس خروجیهای زیر را تولید میکند:
- فایل
outputs/prompt-ssl-pretraining-picker.md— پرامپتی که بر اساس اندازهی دیتاست، منابع محاسباتی و تسک پاییندستی، بهترین گزینه را از بین SimCLR ،MAE یا DINOv2 انتخاب میکند. - فایل
outputs/skill-linear-probe-runner.md— مهارتی که کد ارزیابی پروب خطی را برای هر انکودر فریزشده به همراه مجموعهدادهی برچسبدار مینویسد.
تمرینها
- (ساده) بررسی کنید که با کاهش دما، تابع زیان InfoNCE برای امبدینگهای کاملاً همراستا افت میکند و برای امبدینگهای تصادفی بالا میرود. نمودار تابع زیان برحسب
tau in [0.05, 0.1, 0.2, 0.5]را رسم کنید. - (متوسط) بافر مرکززدایی (centre buffer) را به سبک DINO پیادهسازی کنید. نشان دهید که بدون این مرحله، خروجیهای مدل شاگرد ظرف چند اپوک به یک بردار ثابت فرو میپاشد.
- (سخت) مدل MAE را روی CIFAR-100 با استفاده از TinyUNet درس 10 بهعنوان ستون فقرات آموزش دهید. دقت پروب خطی را در اپوکهای 10، 50 و 200 گزارش کنید. نشان دهید که پروب خطی مدلِ پیشآموزشدیده با MAE، از یک پروب خطی نظارتشده که از صفر روی همان زیرمجموعهی 1000 تصویری آموزش دیده بهتر عمل میکند.
اصطلاحات کلیدی
| اصطلاح | در گفتار رایج | معنای دقیق فنی |
|---|---|---|
| خودنظارتی (Self-supervised) | «بدون برچسب» | طراحی یک وظیفهی بهانهای برای استخراج بازنماییهای مفید از دادههای بدون برچسب |
| وظیفهی بهانهای (Pretext task) | «تسک ساختگی» | تابع هدفی که در حین پیشآموزش خودنظارتی حل میشود (مثل بازسازی پچها، تطبیق نماها) و پس از آن کنار گذاشته میشود |
| پروب خطی (Linear probe) | «انکودر فریز + هد خطی» | ارزیابی استاندارد خودنظارتی: آموزش صرفاً یک کلاسیفایر خطی روی ویژگیهای فریزشده |
| اینفوانسیای (InfoNCE) | «زیان مقابلهای» | تابع سافتمکس روی شباهتهای کسینوسی؛ جفت مثبت کلاس هدف است و همهی دادههای دیگر منفی محسوب میشوند |
| معلم ایامای (EMA teacher) | «معلم با میانگین متحرک» | معلمی که وزنهایش میانگین متحرک نمایی وزنهای شاگرد است؛ مورد استفاده در BYOL ،MoCo و DINO |
| نسبت ماسک (Mask ratio) | «درصد پچهای مخفی» | کسری از پچها که در طول MAE پوشانده میشوند؛ 75% برای تصویر، 15% برای متن |
| فروپاشی بازنمایی (Representation collapse) | «خروجی ثابت» | شکست یادگیری خودنظارتی که در آن انکودر برای همهی ورودیها یک بردار ثابت تولید میکند؛ با مرکززدایی، تیزسازی یا نمونههای منفی جلوی آن گرفته میشود |
| دینو نسخه 2 (DINOv2) | «ستون فقرات خودنظارتی در پروداکشن» | مدل ViT خودنظارتی متا (2023)؛ قویترین ویژگیهای همهمنظورهی تصویر در سال 2026 |
برای مطالعهی بیشتر
- SimCLR (Chen et al., 2020) — مرجع یادگیری مقابلهای
- DINO (Caron et al., 2021) — معلم-شاگرد با تکانه، مرکززدایی و تیزسازی
- MAE (He et al., 2022) — پیشآموزش خودرمزنگار پوشاندهشده برای ViT
- DINOv2 (Oquab et al., 2023) — مقیاسدهی ViT خودنظارتی برای استخراج ویژگیهای پروداکشن
