در سیستمهای نرمافزاری مدرن، جستجوی بصری یا پیدا کردن کالاهای مشابه در فروشگاههای اینترنتی یکی از نیازمندیهای حیاتی است. در حالت عادی، تبدیل تصویر به بردار با مدلهای آماده همیشه دقت لازم را برای تشخیص تمایزهای ظریف (مثل دو مدل کفش بسیار شبیه به هم) ندارد. متریک لرنینگ (Metric Learning) دانشی است که به شما یاد میدهد چگونه فضای برداری را طوری تربیت کنید که فاصلهی بردارها دقیقاً بیانگر شباهت واقعی مد نظرتان باشد.
در این درس میآموزید که چگونه توابع زیان مختلف مانند Triplet Loss را به کار بگیرید، نمونههای منفی چالشبرانگیز را استخراج کنید و با استفاده از کتابخانهی FAISS روی صدها هزار یا میلیونها تصویر جستجوی بسیار سریعی انجام دهید. برای توسعهدهندگان ایرانی، پیادهسازی و آزمایش پایهای این الگوریتمها روی سیستم شخصی و پردازندههای معمولی (یا تراشههای Apple Silicon) امکانپذیر است و برای گامهای اولیه وابستگی ضروری به سرویسهای ابری خارجی ندارید.
اگر در مرحلهی تمرینهای پیشرفته نیاز به بارگذاری مدلها از Hugging Face پیدا کردید، به دلیل ناپایداریهای احتمالی شبکه در ارتباط با این مخزن، استفاده از ابزارهای مدیریت دانلود یا میرورها به پایداری فرایند توسعه کمک میکند.
سیستم بازیابی (retrieval system) گزینهها را بر اساس فاصله در فضای امبدینگ رتبهبندی میکند. متریک لرنینگ (metric learning) دانش شکلدهی به این فضاست؛ بهطوریکه فاصلهها دقیقاً همان معنایی را بدهند که مد نظر شماست.
اهداف یادگیری
- توضیح توابع زیان سهگانه (triplet loss)، کنتراستی (contrastive) و مبتنی بر پروکسی (proxy-based) در متریک لرنینگ، و انتخاب گزینهی مناسب برای یک مجموعهدادهی مشخص
- پیادهسازی صحیح نرمالسازی L2 و شباهت کسینوسی، و بررسی تفاوت میان بازیابی «همان قلم کالا» (same item) و بازیابی «همان دسته» (same class)
- ساخت یک شاخص (index) در FAISS، ارسال کوئریهای متنی و تصویری به آن، و گزارش بازخوانی در K (معیار recall@K) روی یک مجموعهی ارزیابی مستقل
- استفاده از DINOv2، CLIP و SigLIP بهعنوان مدلهای پایهی آمادهی امبدینگ و شناخت نقاط قوت هر کدام
صورت مسئله
بازیابی در سیستمهای بینایی ماشینِ عملیاتی همهجا به چشم میخورد: تشخیص موارد تکراری، جستجوی معکوس تصویر، جستجوی بصری («کالاهای مشابه را پیدا کن»)، بازشناسی چهره (face re-identification)، بازشناسی افراد برای سیستمهای نظارتی، و تطبیق در سطح نمونه (instance-level matching) در فروشگاههای اینترنتی. پرسش محصولی همیشه یکسان است: «با داشتن این تصویر کوئری، کاتالوگ من را رتبهبندی کن.»
دو تصمیم در طراحی، کل سیستم را شکل میدهند: امبدینگ (اینکه چه مدلی بردارها را تولید میکند) و شاخص (نحوهی پیدا کردن نزدیکترین همسایهها در مقیاس بالا). در سال 2026 هر دوی اینها به ابزارهای استاندارد و آماده تبدیل شدهاند (DINOv2 برای امبدینگ و FAISS برای شاخص). همین موضوع سطح انتظارات را بالاتر میبرد: بخش دشوار کار، تعریف این است که در کاربرد خاص شما چه چیزی شبیه به حساب میآید، و سپس شکلدهی به فضای امبدینگ بهگونهای که فاصلهها با این تعریف همخوانی داشته باشند.
این شکلدهی همان متریک لرنینگ است؛ حوزهای جمعوجور اما فوقالعاده اثرگذار.
مفاهیم پایه
نگاهی گذرا به بازیابی
چهار خانوادهی اصلی توابع زیان
| تابع زیان (Loss) | ورودیهای لازم | مزایا | معایب |
|---|---|---|---|
| Contrastive | (لنگر/anchor، مثبت/positive) + منفیها | ساده است و با هر نوع برچسب جفتی کار میکند | بدون تعداد زیادی نمونهی منفی، همگرایی آن کند است |
| Triplet | (لنگر، مثبت، منفی) | شهودی است؛ کنترل مستقیم روی حاشیه (margin) دارد | استخراج سهگانههای دشوار (hard-triplet mining) هزینهبر است |
| NT-Xent / InfoNCE | جفتها + منفیهای استخراجشده از درون دسته (batch) | برای دستههای بزرگ مقیاسپذیر است | به دستههای بزرگ یا صف تکانه (momentum queue) نیاز دارد |
| مبتنی بر پروکسی (ProxyNCA) | فقط برچسبهای کلاس | سریع و پایدار است؛ نیازی به استخراج نمونه ندارد | روی مجموعهدادههای کوچک ممکن است روی پروکسیها بیشبرازش کند |
برای اکثر سناریوهای عملیاتی، کار را با یک مدل پایهی ازپیشآموزشدیده شروع کنید و فقط در صورتی سراغ فاینتیونینگ با متریک لرنینگ بروید که امبدینگهای آماده روی مجموعهی آزمون شما عملکرد ضعیفی داشته باشند.
فرمولبندی تابع زیان سهگانه
نمونهی لنگر a را به نمونهی مثبت p نزدیک کنید و از نمونهی منفی n push کنید (دور نگه دارید)، همراه با یک حاشیه (margin) که ایجاد فاصله را تضمین کند. ساختار سهتصویری به هر نوع ترتیب شباهتی تعمیم مییابد.
استخراج نمونهها اهمیت حیاتی دارد: سهگانههای ساده (n از قبل فاصلهی زیادی با a دارد) هیچ زیانی تولید نمیکنند؛ فقط سهگانههای دشوار به شبکه آموزش میدهند. استخراج نمونههای نیمهدشوار یا semi-hard mining (جایی که n از p دورتر است اما داخل محدودهی حاشیه قرار دارد) فرمول سال 2016 مدل FaceNet است و هنوز هم بهترین عملکرد را دارد.
شباهت کسینوسی در برابر فاصلهی اقلیدسی (L2)
دو معیار، دو رویکرد:
- کسینوسی: زاویهی بین بردارها. به امبدینگهای نرمالشده با L2 نیاز دارد.
- L2: فاصلهی اقلیدسی. روی امبدینگهای خام یا نرمالشده کار میکند، اما معمولاً همراه با امبدینگ نرمالشده با L2 بهتوان دو استفاده میشود.
در اکثر شبکههای امروزی، این دو معادل یکدیگرند: وقتی ||a|| = ||b|| = 1 باشد، رابطه بهصورت ||a - b||^2 = 2 - 2 cos(a, b) خواهد بود. رویکردی را انتخاب کنید که با روش آموزش امبدینگ شما همخوانی دارد؛ ترکیب نسنجیدهی آنها بدون اینکه متوجه شوید مفهوم «نزدیکترین» را تغییر میدهد.
معیار Recall@K
معیار استاندارد ارزیابی در سیستمهای بازیابی:
recall@K = fraction of queries where at least one correct match is in the top K results
همیشه recall@1، @5 و @10 را کنار هم گزارش کنید. اگر recall@10 بالاتر از 0.95 اما recall@1 زیر 0.5 باشد، یعنی فضای امبدینگ ساختار درستی دارد اما رتبهبندی نویز دارد؛ در این حالت فاینتیونینگ طولانیتر یا اضافهکردن مرحلهی بازرتبهبندی (re-ranking) را امتحان کنید.
برای تشخیص موارد تکراری، دقت در K (یا precision@K) اهمیت بیشتری دارد؛ چون هر مثبت کاذب، خطایی است که مستقیماً به چشم کاربر میآید. برای جستجوی بصری، recall@K سیگنال اصلی محصول است.
FAISS در یک بند
کتابخانهی Facebook AI Similarity Search، ابزار استاندارد و مرجع برای جستجوی نزدیکترین همسایه است. سه گزینهی اصلی برای ساخت شاخص دارد:
IndexFlatIP/IndexFlatL2— جستجوی جامع (brute force)، دقیق و بدون نیاز به آموزش. تا حدود 1M بردار کاربرد دارد.IndexIVFFlat— فضا را به K خانه افراز میکند و فقط چند خانهی نزدیکتر را میگردد. تقریبی و سریع است و به دادههای آموزشی نیاز دارد.IndexHNSW— مبتنی بر گراف، سریعترین گزینه برای تعداد کوئریهای بالا، با حجم شاخص نسبتاً بزرگ.
برای 100k بردار، احتمالاً به IndexFlatIP با شباهت کسینوسی نیاز دارید. برای 10M بردار IndexIVFFlat مناسب است، و برای بیش از 100M بردار ترکیب آن با کوانتیزاسیون ضربی (IndexIVFPQ) پیشنهاد میشود.
بازیابی در سطح نمونه در برابر بازیابی در سطح دسته
دو مسئلهی کاملاً متفاوت با یک نام مشترک:
- در سطح دسته (Category-level) — «گربهها را در کاتالوگ من پیدا کن». شباهت شرطی بر اساس کلاس است؛ امبدینگهای آمادهی CLIP یا DINOv2 در اینجا بسیار خوب جواب میدهند.
- در سطح نمونه (Instance-level) — «دقیقاً همین کالای خاص را در کاتالوگ من پیدا کن». به تمایز دقیق بین اشیای از نظر بصری مشابه از همان کلاس نیاز دارد؛ امبدینگهای آماده در این سناریو ضعیف عمل میکنند و فاینتیونینگ با متریک لرنینگ در اینجا ضروری میشود.
قبل از انتخاب مدل، همیشه مشخص کنید که دقیقاً کدامیک از این دو مسئله را حل میکنید.
پیادهسازی از صفر
مرحلهی 1: تابع زیان سهگانه
import torch
import torch.nn.functional as F
def triplet_loss(anchor, positive, negative, margin=0.2):
d_ap = F.pairwise_distance(anchor, positive, p=2)
d_an = F.pairwise_distance(anchor, negative, p=2)
return F.relu(d_ap - d_an + margin).mean()
در یک خط کد؛ روی امبدینگهای خام یا نرمالشده با L2 کار میکند.
مرحلهی 2: استخراج نمونههای نیمهدشوار
با داشتن دستهای از امبدینگها و برچسبها، سختترین منفیِ نیمهدشوار را برای هر لنگر پیدا میکنیم.
def semi_hard_negatives(emb, labels, margin=0.2):
dist = torch.cdist(emb, emb)
same_class = labels[:, None] == labels[None, :]
diff_class = ~same_class
N = emb.size(0)
positives = dist.clone()
positives[~same_class] = float("-inf")
positives.fill_diagonal_(float("-inf"))
pos_idx = positives.argmax(dim=1)
semi_hard = dist.clone()
semi_hard[same_class] = float("inf")
d_ap = dist[torch.arange(N), pos_idx].unsqueeze(1)
semi_hard[dist <= d_ap] = float("inf")
neg_idx = semi_hard.argmin(dim=1)
fallback_mask = semi_hard[torch.arange(N), neg_idx] == float("inf")
if fallback_mask.any():
hardest = dist.clone()
hardest[same_class] = float("inf")
neg_idx = torch.where(fallback_mask, hardest.argmin(dim=1), neg_idx)
return pos_idx, neg_idx
به هر لنگر، سختترین نمونهی مثبتِ همکلاس و یک نمونهی منفی نیمهدشوار اختصاص داده میشود؛ منفیای که از نمونهی مثبت دورتر است اما همچنان در محدودهی حاشیه قرار دارد.
مرحلهی 3: محاسبهی Recall@K
def recall_at_k(query_emb, gallery_emb, query_labels, gallery_labels, k=1):
sim = query_emb @ gallery_emb.T
_, top_k = sim.topk(k, dim=-1)
matches = (gallery_labels[top_k] == query_labels[:, None]).any(dim=-1)
return matches.float().mean().item()
یافتن k گزینهی برتر بر اساس ضرب داخلی روی امبدینگهای نرمالشده با L2، معادل پیدا کردن k گزینهی برتر بر اساس شباهت کسینوسی است. میانگین نسبتی از کوئریها را گزارش کنید که حداقل یک همسایهی درست دارند.
مرحلهی 4: سرهمکردن قطعات
import torch
import torch.nn as nn
from torch.optim import Adam
class Encoder(nn.Module):
def __init__(self, in_dim=128, emb_dim=64):
super().__init__()
self.net = nn.Sequential(
nn.Linear(in_dim, 128), nn.ReLU(),
nn.Linear(128, emb_dim),
)
def forward(self, x):
return F.normalize(self.net(x), dim=-1)
torch.manual_seed(0)
num_classes = 6
protos = F.normalize(torch.randn(num_classes, 128), dim=-1)
def sample_batch(bs=32):
labels = torch.randint(0, num_classes, (bs,))
x = protos[labels] + 0.15 * torch.randn(bs, 128)
return x, labels
enc = Encoder()
opt = Adam(enc.parameters(), lr=3e-3)
for step in range(200):
x, y = sample_batch(32)
emb = enc(x)
pos_idx, neg_idx = semi_hard_negatives(emb, y)
loss = triplet_loss(emb, emb[pos_idx], emb[neg_idx])
opt.zero_grad(); loss.backward(); opt.step()
پس از چند صد گام، خوشههای امبدینگ شکل میگیرند: برای هر کلاس، یک خوشه.
استفاده از ابزارهای واقعی
ترکیبهای نرمافزاری عملیاتی در سال 2026:
- DINOv2 + FAISS — بازیابی بصری چندمنظوره؛ بهصورت آماده کار میکند.
- CLIP + FAISS — زمانی که کوئریها متن هستند.
- DINOv2 فاینتیونشده + FAISS — بازیابی در سطح نمونه، بازشناسی چهره، مد و پوشاک، و فروشگاههای اینترنتی.
- Milvus / Weaviate / Qdrant — پایگاهدادههای برداری مدیریتشده که بهعنوان پوستهای روی FAISS یا HNSW عمل میکنند.
بهترین روش برای بازیابی در سطح نمونه (SOTA): مدل پایهی DINOv2 را بردارید، یک سرِ امبدینگ (embedding head) به آن اضافه کنید، با تابع زیان سهگانه یا InfoNCE روی جفتهای برچسبدارِ سطحنمونه فاینتیون کنید و خروجی را در FAISS شاخصگذاری کنید.
خروجی این درس
از این درس خروجیهای زیر به دست میآید:
outputs/prompt-retrieval-loss-picker.md— پرامپتی که با توجه به مسئلهی بازیابی مشخص، بین توابع زیان سهگانه، InfoNCE یا ProxyNCA بهترین گزینه را پیشنهاد میدهد.outputs/skill-recall-at-k-runner.md— مهارتی که یک بستر ارزیابی تروتمیز برای recall@K مینویسد؛ همراه با تفکیک مجموعههای آموزش/اعتبارسنجی/گالری و قرارداد دادهای مشخص.
تمرینها
- (ساده) مثال کوچک بالا را اجرا کنید. امبدینگها را با تحلیل مؤلفههای اصلی (PCA) قبل و بعد از آموزش رسم کنید تا شکلگیری شش خوشه را به چشم ببینید.
- (متوسط) تابعی برای زیان ProxyNCA پیادهسازی کنید: بهازای هر کلاس یک «پروکسی» قابل یادگیری در نظر بگیرید و آنتروپی متقاطع (Cross-Entropy) استاندارد را روی شباهت کسینوسی اعمال کنید. سرعت همگرایی آن را با تابع زیان سهگانه روی همان دادههای آزمایشی مقایسه کنید.
- (سخت) تعداد 1000 تصویر اعتبارسنجی ImageNet را بردارید، با DINOv2 از طریق HuggingFace امبدینگ آنها را بسازید، یک شاخص مسطح (flat) در FAISS ایجاد کنید و recall@{1, 5, 10} را یک بار با استفاده از همان تصاویر بهعنوان کوئری (که باید 1.0 شود) و یک بار روی یک بخش ارزیابی مجزا با استفاده از برچسبهای واقعی ImageNet گزارش دهید.
اصطلاحات کلیدی
| اصطلاح | در گفتگوهای عامیانه | معنای دقیق فنی |
|---|---|---|
| متریک لرنینگ (Metric learning) | «شکلدادن به فضا» | آموزش یک انکودر بهگونهای که فاصلهها در فضای خروجی آن بازتابدهندهی شباهت مد نظر باشند |
| تابع زیان سهگانه (Triplet loss) | «بکش و هل بده» | ؛ تابع زیان کلاسیک در متریک لرنینگ |
| استخراج نیمهدشوار (Semi-hard mining) | «منفیهای بهدردبخور» | نمونههای منفیای که از مثبت دورترند اما هنوز داخل حاشیهاند؛ از نظر تجربی بیشترین بار اطلاعاتی را دارند |
| تابع زیان مبتنی بر پروکسی (Proxy-based loss) | «نمونههای اولیهی کلاس» | یک پروکسیِ آموختهشده بهازای هر کلاس؛ محاسبهی آنتروپی متقاطع روی شباهت با پروکسیها، بدون نیاز به استخراج جفتها |
| بازخوانی در K (معیار Recall@K) | «نرخ اصابت در K تای اول» | کسری از کوئریها که حداقل یک نتیجهی درست در میان K نتیجهی اول خود دارند |
| بازیابی در سطح نمونه (Instance retrieval) | «دقیقاً همین شیء را پیدا کن» | تطبیق در سطح جزئیات دقیق؛ ویژگیهای مدلهای آماده معمولاً در این کار ضعیف عمل میکنند |
| ابزار FAISS | «کتابخانهی جستجوی همسایهها» | کتابخانهی فیسبوک برای جستجوی نزدیکترین همسایه با پشتیبانی از شاخصهای دقیق و تقریبی |
| الگوریتم HNSW | «شاخص گرافی» | Hierarchical navigable small world؛ روشی سریع برای یافتن تقریبی نزدیکترین همسایهها با سربار حافظهی کم |
برای مطالعهی بیشتر
- FaceNet: A Unified Embedding for Face Recognition (Schroff et al., 2015) — مقالهی مرجع تابع زیان سهگانه و استخراج نمونههای نیمهدشوار
- In Defense of the Triplet Loss for Person Re-Identification (Hermans et al., 2017) — راهنمای عملی برای فاینتیونینگ با تابع زیان سهگانه
- مستندات FAISS — بررسی همهی انواع شاخصها و سبکسنگین کردن مزایا و معایب هرکدام
- SMoT: Metric Learning Taxonomy (Kim et al., 2021) — مروری جامع بر توابع زیان مدرن و ارتباطات میان آنها
