فاز 4 · درس 18

بینایی با واژگان باز: درک عمیق و پیاده‌سازی CLIP

نوع
ساختنی و کاربردی
زمان
حدود 45 دقیقه
زبان‌ها
Python
دو برج قرینه نوری موازی که پرتوهای نوری خود را به سمت یک نقطه مرکزی مشترک در فضا متمرکز می‌کنند
فهرست مطالب
  1. اهداف یادگیری
  2. صورت مسئله
  3. مفاهیم کلیدی
  4. دو برج
  5. تابع هدف
  6. مدل SigLIP: تابع زیانی بهتر
  7. دسته‌بندی صفر-شات
  8. کاربرد مدل‌های سبک CLIP در سال 2026
  9. پیاده‌سازی عملی
  10. گام 1: یک مدل دوبرجی بسیار کوچک
  11. گام 2: تابع زیان تقابلی
  12. گام 3: دسته‌بند صفر-شات
  13. گام 4: بررسی اولیه‌ی صحت عملکرد (Sanity check)
  14. استفاده‌ی کاربردی
  15. تحویل پروژه
  16. تمرین‌ها
  17. اصطلاحات کلیدی
  18. مطالعه‌ی بیشتر

مدل‌های سنتی بینایی ماشین با محدودیت دسته‌بندی بسته (closed-vocabulary) روبه‌رو هستند؛ اگر مدلی را روی هزار کلاس آموزش داده باشید، اضافه کردن کلاس هزارویکم به جمع‌آوری داده، برچسب‌گذاری مجدد و آموزش دوباره‌ی لایه‌ی خروجی نیاز دارد. مدل CLIP با پیوند دادن زبان طبیعی و تصویر در یک فضای برداری مشترک، این چالش را برای همیشه حل کرد تا بتوانید صرفاً با نوشتن یک جمله، هر مفهوم بصری جدیدی را در زمان اجرا دسته‌بندی کنید.

امروزه تسک‌های مدرن هوش مصنوعی از تشخیص شیء و بخش‌بندی تصویر گرفته تا بازیابی چندوجهی و مدل‌های بینایی-زبان (VLM)، همگی بر پایه‌ی امبدینگ‌های مشترک سبک CLIP بنا شده‌اند. در این درس یاد می‌گیرید که معماری دوبرجی، تابع زیان تقابلی و ترفندهای مهندسی پرامپت چگونه دسته‌بندی صفر-شات (zero-shot) را ممکن می‌کنند و چرا ساختار مدرن‌تر SigLIP عملکرد بهتری در اندازه‌های کوچک دسته داده‌ها ارائه می‌دهد.

برای اجرای کدهای پایه و پیاده‌سازی دسته‌بندی در این درس، نیازی به پردازنده‌های گرافیکی سنگین نخواهید داشت و محاسبات به‌سادگی روی CPU یا تراشه‌های شخصی قابل انجام است. همچنین بارگیری مدل‌های پیش‌آموزش‌دیده از طریق کتابخانه‌های معتبری مثل OpenCLIP و Hugging Face صورت می‌گیرد که در صورت مواجهه با کندی در بارگیری این وزن‌ها از ایران، استفاده از میرورهای داخلی بسته‌های پایتون راهکاری کاربردی خواهد بود.

یک انکودر تصویر و یک انکودر متن را هم‌زمان طوری آموزش دهید که جفت‌های متناظر (تصویر، کپشن) در فضایی مشترک دقیقاً روی یک نقطه قرار بگیرند. تمام ترفند کار همین است.

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

  • تشریح معماری دوبرجی (two-tower architecture) در CLIP و تابع هدف آموزش تقابلی (contrastive training) آن
  • استفاده از CLIP (یا SigLIP) پیش‌آموزش‌دیده برای دسته‌بندی صفر-شات (zero-shot classification) بدون نیاز به هیچ‌گونه آموزش اختصاصی برای تسک
  • پیاده‌سازی دسته‌بندی صفر-شات از صفر: انکود پرامپت‌های کلاس، محاسبه‌ی شباهت کسینوسی (cosine similarity) و گرفتن argmax
  • تشخیص تفاوت‌های CLIP، SigLIP، OpenCLIP و مدل‌های LLaVA/LLaMA-vision — و کاربرد هرکدام در سال 2026

صورت مسئله

دسته‌بندهای سنتی دامنه‌ی واژگان بسته‌ای دارند (closed-vocabulary): مدلی که روی 1000 کلاس ImageNet آموزش دیده، فقط می‌تواند همین 1000 برچسب را پیش‌بینی کند. هر دسته‌ی جدید به داده‌های برچسب‌خورده و آموزش دوباره‌ی لایه‌ی خروجی (head) نیاز دارد.

مدل CLIP (ارائه‌شده توسط Radford و همکاران، OpenAI در سال 2021) نشان داد که با آموزش روی 400 میلیون جفت (تصویر، کپشن) جمع‌آوری‌شده از وب، مدلی به دست می‌آید که در زمان استنتاج، می‌تواند هر مجموعه‌ای از دسته‌ها را صرفاً با توصیف متنی به زبان طبیعی دسته‌بندی کند. کافی است با نوشتن یک جمله، کلاس جدیدی به آن معرفی کنید.

همین قابلیت — یعنی انتقال صفر-شات (zero-shot transfer) — دلیل اصلی این است که امروزه تقریباً تمام سیستم‌های بینایی ماشین با یک چک‌پوینت از خانواده‌ی CLIP کارشان را شروع می‌کنند. تشخیص شیء (Grounding DINO، OWL-ViT)، بخش‌بندی تصویر (CLIPSeg، SAM)، بازیابی (retrieval)، مدیریت محتوا، مدل‌های چندوجهی زبان و تصویر (VLM) و مدل‌های تولید تصویر از متن، همگی بر پایه‌ی امبدینگ‌های مشترک سبک CLIP بنا شده‌اند.

مفاهیم کلیدی

دو برج

تصویرانکودر تصویر(ViT-L/14)امبدینگ تصویر(1024,)کپشنانکودر متن(ترنسفورمر)امبدینگ متن(1024,)شباهت کسینوسی

هر دو انکودر به یک لایه‌ی تصویر کردن خطی (linear projection) با بُعد امبدینگ یکسان ختم می‌شوند (512 برای CLIP-B/32 و 1024 برای CLIP-L/14). سپس بردارها نرمال‌سازی L2 می‌شوند و شباهت کسینوسی آن‌ها محاسبه می‌شود.

تابع هدف

با داشتن یک بچ شامل NN جفت (تصویر، کپشن)، یک ماتریس شباهت N×NN \times N می‌سازیم. هر دو انکودر را طوری آموزش می‌دهیم که قطر اصلی (جفت‌های متناظر) بیشترین شباهت و درایه‌های غیرقطری (جفت‌های نامرتبط) کمترین شباهت را داشته باشند.

sim_matrix = image_embeddings @ text_embeddings.T / tau

loss_i2t = cross_entropy(sim_matrix,       targets=arange(N))
loss_t2i = cross_entropy(sim_matrix.T,     targets=arange(N))
loss = (loss_i2t + loss_t2i) / 2

این فرمول متقارن است؛ چون بازیابی تصویر-به-متن و متن-به-تصویر باید هر دو به‌درستی کار کنند. پارامتر tau (دما یا temperature) معمولاً یک اسکالر قابل‌یادگیری با مقدار اولیه‌ی 0.07 است.

مدل SigLIP: تابع زیانی بهتر

مدل SigLIP (توسط Zhai و همکاران، 2023) تابع سافت‌مکس (softmax) را با یک تابع سیگموئید مجزا به ازای هر جفت جایگزین کرد:

loss=meanpairslog⁡(1+exp⁡(−yij⋅simij))yij={+1if matching−1otherwise\begin{aligned} \text{loss} &= \mathop{\mathrm{mean}}\limits_{\text{pairs}} \log\left(1 + \exp(-y_{ij} \cdot \text{sim}_{ij})\right) \\ y_{ij} &= \begin{cases} +1 & \text{if matching} \\ -1 & \text{otherwise} \end{cases} \end{aligned}

محاسبه‌ی تابع زیان (loss) به‌ازای هر جفت، نیاز به نرمال‌سازی در سطح کل بچ را — که CLIP به آن وابسته بود — از بین می‌برد. SigLIP با اندازه‌ی بچ کوچک‌تر بهتر آموزش می‌بیند و با داده‌ی یکسان، عملکردی هم‌تراز یا حتی بهتر از CLIP ثبت می‌کند.

دسته‌بندی صفر-شات

وقتی یک CLIP آموزش‌دیده در اختیار دارید:

  1. برای هر کلاس، یک پرامپت می‌سازید: "a photo of a {class}".
  2. همه‌ی پرامپت‌های کلاس‌ها را با انکودر متن انکود می‌کنید -> خروجی T با ابعاد (C, d).
  3. تصویر تست را انکود می‌کنید -> خروجی I با ابعاد (1, d).
  4. محاسبه‌ی شباهت: I @ T.T با ابعاد (1, C).
  5. گرفتن Argmax -> کلاس پیش‌بینی‌شده.

مهندسی پرامپت اهمیت زیادی دارد. OpenAI تعداد 80 قالب پرامپت برای ImageNet منتشر کرده است ("a photo of a {}", "a blurry photo of a {}", "a sketch of a {}", ...). اگر از امبدینگ‌های همه‌ی این قالب‌ها برای هر کلاس میانگین بگیرید، دقت top-1 بین 1 تا 3 درصد بهتر می‌شود.

کاربرد مدل‌های سبک CLIP در سال 2026

  • دسته‌بندی صفر-شات — استفاده‌ی مستقیم.
  • بازیابی تصویر — یک بار انکود کردن تمام تصاویر و محاسبه‌ی امبدینگ پرسمان در زمان استنتاج.
  • تشخیص شیء مبتنی بر متن — مدل‌هایی مثل Grounding DINO و OWL-ViT که یک برج متنی CLIP را به دتکتور متصل می‌کنند.
  • بخش‌بندی مبتنی بر متن — مدل CLIPSeg؛ مدل SAM نیز پرامپت‌های متنی ورودی را از طریق CLIP دریافت می‌کند.
  • مدل‌های بینایی-زبان (VLM) — مدل‌های LLaVA، Qwen-VL و InternVL که انکودر تصویر خانواده‌ی CLIP را به یک LLM پیوند می‌زنند.
  • تولید تصویر از متن — مدل‌های Stable Diffusion و DALL-E 3 که خروجی خود را بر اساس امبدینگ‌های متنی CLIP شرطی‌سازی می‌کنند.

به محض اینکه یک فضای امبدینگ مشترک داشته باشید، هر تسک بینایی + زبان به یک محاسبه‌ی فاصله‌ی ساده تبدیل می‌شود.

CLIP · ماتریس کنتراستیوجفت‌های منطبق روی قطر اصلی قرار می‌گیرند
CLIP · CONTRASTIVE MATRIXCLIP embeds images and captions into one shared space. For a batch of N pairs it builds an NxN similarity matrix and trains so the diagonal (true pairs) scores high and every off-diagonal scores low. Watch the diagonal brighten as matching pairs are pulled together and mismatches pushed apart.امبدینگ‌های متن ←تصویرتصویرتصویرتصویرتصویرجفت منطبق (تصویر، متن) — جذب به همجفت‌های نامنطبق — push به دوردستسافت‌مکس روی هر سطر و ستونافزایش مقادیر قطر اصلی
مدل CLIP تصاویر و زیرنویس‌ها را درون یک فضای مشترک امبد می‌کند. برای یک دسته شامل N جفت، یک ماتریس شباهت N×N می‌سازد و به نحوی آموزش می‌بیند که امتیاز عناصر قطر اصلی (جفت‌های واقعی) بالا و عناصر غیرقطری پایین برود. با نزدیک شدن جفت‌های منطبق و push شدن نمونه‌های نامرتبط به دوردست، روشن‌تر شدن قطر اصلی را مشاهده کنید.

پیاده‌سازی عملی

گام 1: یک مدل دوبرجی بسیار کوچک

مدل واقعی CLIP از ترکیب ViT و ترنسفورمر متنی ساخته شده است. در این درس، برج‌ها را MLPهای کوچکی روی ویژگی‌های از پیش استخراج‌شده در نظر می‌گیریم تا روند آموزش حتی روی CPU هم ملموس و قابل‌مشاهده باشد.

import torch
import torch.nn as nn
import torch.nn.functional as F


class TwoTower(nn.Module):
    def __init__(self, img_in=128, txt_in=64, emb=64):
        super().__init__()
        self.image_proj = nn.Sequential(nn.Linear(img_in, 128), nn.ReLU(), nn.Linear(128, emb))
        self.text_proj = nn.Sequential(nn.Linear(txt_in, 128), nn.ReLU(), nn.Linear(128, emb))
        self.logit_scale = nn.Parameter(torch.ones([]) * 2.6592)  # ln(1/0.07)

    def forward(self, img_feats, txt_feats):
        i = F.normalize(self.image_proj(img_feats), dim=-1)
        t = F.normalize(self.text_proj(txt_feats), dim=-1)
        return i, t, self.logit_scale.exp()

دو تصویر، خروجی با ابعاد مشترک و دمای قابل‌یادگیری؛ ساختاری دقیقاً مطابق با API واقعی CLIP.

گام 2: تابع زیان تقابلی

def clip_loss(image_emb, text_emb, logit_scale):
    N = image_emb.size(0)
    sim = logit_scale * image_emb @ text_emb.T
    targets = torch.arange(N, device=sim.device)
    l_i = F.cross_entropy(sim, targets)
    l_t = F.cross_entropy(sim.T, targets)
    return (l_i + l_t) / 2

فرمول متقارن است. مقدار بالاتر logit_scale یعنی سافت‌مکس تیزتر و در نتیجه پیش‌بینی‌های بااطمینان‌تر، اما خطر ناپایداری آموزش هم بیشتر می‌شود.

گام 3: دسته‌بند صفر-شات

@torch.no_grad()
def zero_shot_classify(model, image_feats, class_text_feats, class_names):
    """
    image_feats:      (N, img_in)
    class_text_feats: (C, txt_in)   one averaged embedding per class
    """
    i = F.normalize(model.image_proj(image_feats), dim=-1)
    t = F.normalize(model.text_proj(class_text_feats), dim=-1)
    sim = i @ t.T
    pred = sim.argmax(dim=-1)
    return [class_names[p] for p in pred.tolist()]

هر مرحله در یک خط. این دقیقاً همان فرایند صفر-شاتی است که در محیط‌های پروداکشن با چک‌پوینت‌های CLIP اجرا می‌شود.

گام 4: بررسی اولیه‌ی صحت عملکرد (Sanity check)

torch.manual_seed(0)
model = TwoTower()

img = torch.randn(8, 128)
txt = torch.randn(8, 64)
i, t, scale = model(img, txt)
loss = clip_loss(i, t, scale)
print(f"batch size: {i.size(0)}   loss: {loss.item():.3f}")

مقدار تابع زیان (loss) برای مدلی با وزن‌های تصادفی باید نزدیک به log(N) = log(8) = 2.08 باشد؛ یعنی همان مقدار هدف در آنتروپی متقاطع (cross-entropy) متقارن، زمانی که هنوز هیچ ساختاری در داده‌ها یاد گرفته نشده است.

استفاده‌ی کاربردی

در سال 2026، کتابخانه‌ی OpenCLIP انتخاب اول جامعه‌ی متن‌باز است:

import open_clip
import torch
from PIL import Image

model, _, preprocess = open_clip.create_model_and_transforms("ViT-B-32", pretrained="laion2b_s34b_b79k")
tokenizer = open_clip.get_tokenizer("ViT-B-32")

image = preprocess(Image.open("dog.jpg")).unsqueeze(0)
text = tokenizer(["a photo of a dog", "a photo of a cat", "a photo of a car"])

with torch.no_grad():
    image_features = model.encode_image(image)
    text_features = model.encode_text(text)
    image_features = image_features / image_features.norm(dim=-1, keepdim=True)
    text_features = text_features / text_features.norm(dim=-1, keepdim=True)
    probs = (100.0 * image_features @ text_features.T).softmax(dim=-1)

print(probs)

مدل SigLIP جدیدتر است، در مقیاس‌های کوچک‌تر بهتر آموزش می‌بیند و برای پروژه‌های تازه گزینه‌ی بهتری به شمار می‌رود: google/siglip-base-patch16-224. پلتفرم Hugging Face از هر دو پشتیبانی می‌کند.

تحویل پروژه

در این درس فایل‌های زیر را تولید می‌کنید:

  • outputs/prompt-zero-shot-class-picker.md — پرامپتی که بر اساس فهرست کلاس‌ها و حوزه‌ی کاری، قالب‌های کلاسی مناسب را برای صفر-شات در CLIP طراحی می‌کند.
  • outputs/skill-image-text-retriever.md — مهارتی که یک نمایه‌ی امبدینگ تصویر با هر چک‌پوینت دلخواه CLIP می‌سازد و از جست‌وجو با متن و تصویر پشتیبانی می‌کند.

تمرین‌ها

  1. (ساده) با استفاده از مدل پیش‌آموزش‌دیده‌ی OpenCLIP ViT-B/32 و مجموعه‌ی پرامپت‌های 80 قالبی، دسته‌بندی صفر-شات را روی CIFAR-10 اجرا کنید. دقت top-1 را گزارش دهید؛ این عدد باید حدود 85 تا 90 درصد باشد.
  2. (متوسط) روی همان مجموعه‌ی CIFAR-10، عملکرد پرامپت تک‌قالبی ("a photo of a {}") را با میانگین امبدینگ‌های 80 قالب مقایسه کنید. این اختلاف را به‌صورت دقیق اندازه بگیرید و توضیح دهید چرا قالب‌های چندگانه باعث بهبود نتایج می‌شوند.
  3. (سخت) یک نمایه‌ی بازیابی تصویر صفر-شات بسازید: 1000 تصویر را با CLIP امبد کنید، یک ایندکس FAISS بسازید و با توصیف‌های زبان طبیعی آن را جست‌وجو کنید. نرخ بازخوانی (recall@5) را برای 20 پرسمان دست‌نویس مجزا گزارش دهید.

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

اصطلاح در گفتگوها معنای دقیق فنی
Two-tower «انکودر دوگانه» انکودرهای مجزا برای تصویر و متن که به یک لایه‌ی تصویر کردن (projection head) با ابعاد مشترک ختم می‌شوند
Zero-shot «بدون آموزش اختصاصی تسک» دسته‌بندی روی کلاس‌هایی که در زمان استنتاج فقط با متن توصیف شده‌اند؛ بدون نیاز به تغییر یا مشاهده‌ی برچسب‌ها
Temperature / logit_scale «پارامتر tau» اسکالر قابل‌یادگیری که ماتریس شباهت را قبل از سافت‌مکس مقیاس‌بندی می‌کند
Prompt template «A photo of a {}» قالبی به زبان طبیعی دور نام کلاس‌ها؛ میانگین‌گیری از چندین قالب دقت صفر-شات را بالاتر می‌برد
CLIP «مدل تصویر+متن» مدل عرضه‌شده توسط OpenAI در سال 2021؛ زبان مشترک این حوزه در سال 2026
SigLIP «مدل Sigmoid CLIP» جایگزین کردن سافت‌مکس با تابع سیگموئید به‌ازای هر جفت؛ آموزش بهتر در بچ‌های کوچک‌تر
OpenCLIP «بازتولید متن‌باز» مدل‌های بازتولیدشده‌ی CLIP توسط جامعه‌ی متن‌باز روی مجموعه‌داده‌ی LAION؛ گزینه‌ی پیش‌فرض در پایپ‌لاین‌های پروداکشن
VLM «مدل بینایی-زبان» انکودری از خانواده‌ی CLIP متصل به یک LLM که برای پاسخ دادن به پرسش‌ها درباره‌ی تصویر آموزش دیده است

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

خودآزمایی

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

  1. پیش از درستابع زیان (loss) کنتراستیو CLIP متقارن است (تصویر-به-متن + متن-به-تصویر). چرا هر دو جهت لحاظ می‌شوند؟
  2. پیش از درسدسته‌بندی Zero-shot با CLIP چگونه عمل می‌کند؟
  3. پس از درسمدل SigLIP سافت‌مکس CLIP را با یک تابع زیان (loss) سیگموئید به ازای هر جفت جایگزین می‌کند. این کار چه مزیتی دارد؟
  4. پس از درسیک متخصص دقت top-1 به میزان 88٪ را در تسک zero-shot روی CIFAR-10 با مدل CLIP ViT-B/32 گزارش می‌کند و با همان مدل و استفاده از 80 قالب پرامپت به ازای هر کلاس به 90٪ می‌رسد. چرا میانگین‌گیری قالب‌ها کمک می‌کند؟
  5. پس از درسچرا مدل‌های چندوجهی مدرن (مانند LLaVA، Qwen-VL، InternVL) به جای یک ResNet سوپروایزد آموزش‌دیده روی ImageNet، از یک انکودر بینایی از خانواده CLIP استفاده می‌کنند؟

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

دسته‌بندی صفر-شات (zero-shot) در مدل CLIP چگونه کار می‌کند؟

ابتدا نام کلاس‌ها درون قالب‌های متنی (مانند a photo of a {}) قرار گرفته و با انکودر متن به بردار تبدیل می‌شوند. سپس تصویر ورودی نیز با انکودر تصویر امبد شده و شباهت کسینوسی آن با همه‌ی بردارهای متنی محاسبه می‌شود؛ کلاسی که بیشترین شباهت را داشته باشد انتخاب خواهد شد.

تفاوت اصلی مدل SigLIP با CLIP در چیست؟

مدل SigLIP به جای تابع سافت‌مکس با نرمال‌سازی در سطح کل بچ، از تابع سیگموئید مجزا به‌ازای هر جفت تصویر و متن استفاده می‌کند. این تغییر باعث پایداری و بهبود کیفیت آموزش با بچ‌سایزهای کوچک‌تر می‌شود.

چرا استفاده از میانگین پرامپت‌های چندگانه دقت CLIP را افزایش می‌دهد؟

توصیف یک مفهوم با چندین زاویه دید زبانی (مثل عکس تار، طرح نقاشی یا نمای نزدیک) خطای ناشی از نحوه بیان متن را کاهش داده و بردار بازنمایی جامع‌تری در فضای ویژگی می‌سازد که دقت top-1 را 1 تا 3 درصد ارتقا می‌دهد.

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

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