چیتشیت
ساخت یک VLM از صفر
Build a VLM from Scratch
امبدینگ پچ، انکودر ViT، تصویرساز MLP، دیکودر کراساتنشن، پیشآموزش InfoNCE بههمراه LM، معیارهای R@K، EM و BLEU-4.
فهرست مطالب
استک VLM
معماریاجرای هر درس
دستورها| دستور | عملکرد |
|---|---|
`python3 code/main.py` (58) | توکنهایی با ابعاد (1, 197, 768). تطابق Unfold با Conv2d |
`python3 code/main.py` (59) | انکود به (1, 197, 768). حدود 86 میلیون پارامتر |
`python3 code/main.py` (60) | 200 گام. کاهش تابع زیان از حدود 1.07 به حدود 0.80 |
`python3 code/main.py` (61) | لاجیتهای (2, 10, text_vocab)، ماسک (10, 10)، بررسی کش |
`python3 code/main.py` (62) | 50 گام. کاهش زیان تطبیقی از 2.77 به 2.4. کاهش LM از 6.24 به 4.7 |
`python3 code/main.py` (63) | معیارها قبل و بعد از 50 گام آموزش |
python3 -m unittest code/test_main.py | اجرای تستهای یک درس |
ساخت و انکود
کدpython
cfg = ViTConfig() # 224، 16، 768، عمق 12، 12 هد
encoder = VisionEncoder(cfg).eval() # فرانتاند بههمراه استک ViT
img = synthesize_image(seed=0) # (1, 3, 224, 224) in [0, 1]
tokens, cls = encoder(img) # (1, 197, 768) and (1, 768)
pos = sinusoidal_2d(grid_h, grid_w, dim) # dim بخشپذیر بر 4
ref = unfold_then_linear(img, weight, bias, cfg.patch_size)
nn.Conv2d(cfg.in_channels, cfg.hidden,
kernel_size=cfg.patch_size, stride=cfg.patch_size)
x = x + self.attn(self.ln1(x)) # باقیماندهی پیشنرمالسازی لایهای (pre-LN residual)
x = x + self.ffn(self.ln2(x)) # GELU MLP، با عرض 4x
همترازسازی، ادغام و پیشآموزش
کدpython
projector = MLPProjector(cfg.vision_hidden, cfg.projection_hidden, cfg.text_hidden)
freeze(encoder) # requires_grad_(False)
loss = cosine_alignment_loss(image_emb, text_emb) # mean of 1 - cos
mask = causal_mask(10) # (10, 10) lower triangle
logits = decoder(text_ids, memory, use_cache=True) # کش شدن K و V تصویر
contrast, sim = info_nce_loss(image_emb, text_emb, self.log_tau)
lm = lm_loss(logits, targets, padding_id=PAD_ID) # مقدار PAD_ID برابر 0 است
total = contrast + cfg.lm_weight * lm
اتنشن در یک VLM
مقایسه| عامل | خوداتنشن ViT | خوداتنشن دیکودر | کراساتنشن |
|---|---|---|---|
| Query | توکنهای تصویر | توکنهای متن | توکنهای متن |
| Keys و Values | توکنهای تصویر | توکنهای متن | توکنهای حافظه تصویر |
| ماسک | بدون ماسک | علّی (Nt, Nt) | بدون ماسک |
| ابعاد خروجی | (B, 197, 768) | (B, Nt, hidden) | (B, Nt, hidden) |
| دلیل | فقط-انکودر و دوطرفه | عدم نگاهبهجلو در خودبازگشت | قابل مشاهده بودن کل تصویر |
| کش | استفاده نمیشود | استفاده نمیشود | محاسبه K و V تصویر یکبار به ازای هر تصویر |
یک گام پیشآموزش
گامبهگام- نمونهگیرانتخاب 16 جفت بدون جایگذاری از پیکره متنی 200 جفتی.
- انکودربرگرداندن توکنهای حافظه و امبدینگ تصویر تصویرشده.
- بخش متنمیانگینگیری از شناسههای امبدشده کپشن به
text_emb. - InfoNCEنرمالسازی L2 هر دو. آنتروپی متقاطع روی سطرها و ستونها، سپس میانگینگیری.
- دیکودرخواندن
text_ids[:, :-1]و پیشبینیtext_ids[:, 1:]. - تابع زیان LMآنتروپی متقاطع که شناسه پدینگ 0 را نادیده میگیرد.
- Adamاعمال گام بهینهسازی روی
contrast + lm_weight * lm. چاپ خروجی هر 5 گام یکبار.
ابعاد و اعداد
مفهومها- پچها
- تقسیم 224 بر 16 یک شبکه 14x14 شامل 196 پچ با 768 مقدار میدهد.
- هزینه اتنشن
- 196 توکن، 38,416 امتیاز به ازای هر هد در هر لایه تولید میکند.
- تصویر
- 590,592 پارامتر برای ViT-Base/16. توکن CLS مقدار 768 پارامتر اضافه میکند.
- ابعاد هر هد
- تقسیم 768 بر 12 هد، 64 میدهد.
- به ازای هر بلوک
- حدود 7.1 میلیون. 12 بلوک حدود 85 میلیون. انکودر حدود 86 میلیون.
- تصویرساز
- حدود 1.3 میلیون برای یک هد
768 -> 1024 -> 512. - تابع زیان کسینوسی
- 0 در صورت همراستایی، 2 در جهت مخالف.
- معیار R@K تصادفی، N=50
- مقدار R@1 برابر 0.02، مقدار R@5 برابر 0.10، مقدار R@10 برابر 0.20.
ارزیابی مدل
کار و کد| وظیفه | کد |
|---|---|
| ساخت مجموعه ارزیابی | build_eval_suite(seed, n_samples, vocab_size, max_len) |
| بازخوانی تصویر و متن | i2t, t2i = recall_at_k(sim, k) |
| دقت VQA | vqa_exact_match(predictions, references) |
| امتیاز کپشننویسی | bleu4(generated, references, smoothing=True) |
| هر سه معیار | evaluate(model, suite) |
| آموزش بین ارزیابیها | contrast, lm, _ = model(imgs, ids) |
پیشفرضهای پیکربندی
پیشفرضها| تنظیم | پیشفرض | نکته |
|---|---|---|
`image_size`, `patch_size` | 224 | پچ 16، hidden برابر 768 |
`depth`, `heads` | 12 | mlp_ratio برابر 4.0، اپسیلون LayerNorm برابر 1e-6 |
Align `steps` | 200 | 32 جفت، lr برابر 3e-4 |
projection_hidden | 1024 | text_hidden برابر 512 |
Decoder `hidden` | 256 | 8 هد، عمق 4، 197 توکن دیداری |
Pretrain `batch_size` | 16 | 50 گام، lr برابر 5e-4 |
lm_weight | 1.0 | وزن تابع زیان LM |
init_log_tau | math.log(1.0 / 0.07) | محدود شدن مقیاس بین 1e-3 و 100.0 |
خطاها و راهحلها
عیبیابیpatch_size ... must divide image_size- علت
- شبکه کامل نیست
- راهحل
- اندازه پچی انتخاب کنید که تصویر بر آن بخشپذیر باشد
spatial mismatchازPatchEmbed- علت
- تفاوت اندازه ورودی با پیکربندی
- راهحل
- تغییر اندازه تصویر به
image_size
sinusoidal_2d dim must be divisible by 4- علت
- اندازه مخفی مضرب 4 نیست
- راهحل
- استفاده از اندازه مخفیِ بخشپذیر بر 4
hidden ... not divisible by heads- علت
- ابعاد هد عدد صحیح نیست
- راهحل
- انتخاب تعداد هدهایی که اندازه مخفی بر آن بخشپذیر باشد
could not load lesson 58 main.py- علت
- پوشههای درس جابهجا شدهاند
- راهحل
- پوشههای همسطح درس را در جای خود نگهدارید
causal mask shape ... does not match- علت
- ماسک
(Nt, Nt)نیست - راهحل
- ساخت آن با
causal_mask(nt)
batch_size ... cannot exceed corpus size- علت
- نمونهگیری بدون جایگذاری
- راهحل
- کاهش اندازه دسته یا افزودن جفتها
k ... not in [1, N]- علت
- مقدار K بزرگتر از استخر دادههاست
- راهحل
- نگهداشتن k مساوی یا کمتر از N
پرسشها
پرسش و پاسخ- چرا پچها و نه پیکسلها؟
- 150,528 توکن پیکسلی، هزینه اتنشن را غیرقابل تحمل میکند.
- چرا فریز کردن انکودر حین همترازسازی؟
- فقط تصویرساز 1.3 میلیونی نیاز به یادگیری دارد.
- چرا ادغام دیرهنگام؟
- متن دستنخورده میماند. K و V تصویر تنها یکبار به ازای هر تصویر محاسبه میشوند.
- چرا یادگیری دما؟
- مقدار خیلی کوچک نویز ایجاد میکند؛ مقدار خیلی بزرگ سافتمکس را مسطح میکند.