در سامانههای بازیابی اطلاعات و RAG، جستجوی برداری مبتنی بر بایانکودرها بسیار پرسرعت است؛ اما این سرعت بالا به بهای از دست رفتن دقت معنایی تمام میشود. وقتی پرسش و سند بهصورت مجزا امبد میشوند، مدل ارتباط متقابل جزئیات متن را در نظر نمیگیرد و اسناد غیرمرتبط اما همموضوع در رتبههای ابتدایی قرار میگیرند.
کراسانکودر با پردازش همزمان جفت پرسش و سند، امکان توجه (Attention) کامل میان تکتک کلمات آنها را فراهم میکند. اجرای این مدل روی تمام اسناد پایگاه داده از نظر تاخیر پردازشی ناممکن است؛ به همین دلیل معماری دومرحلهای «بازیابی و بازرتبهبندی» استاندارد طلایی سامانههای مدرن محسوب میشود.
کدها و محاسبات این درس در قالب یک ترنسفورمر آموزشی کوچک پیاده شدهاند و بدون نیاز به دانلود مدلهای سنگین از Hugging Face یا اجارهی GPUهای ابری گرانقیمت، روی CPU سیستم شخصی بهراحتی قابل اجرا و آزمایش هستند.
بایانکودر (Bi-encoder) پرسش و سند را مستقل از هم امبد میکند. کراسانکودر (Cross-encoder) آنها را به یکدیگر میچسباند و هر دو را همزمان میخواند. کراسانکودر هوشمندانهترین خواننده و در عین حال کُندترین آنهاست. اگر از آن به عنوان مرحلهی دوم روی k نتیجهی برتر بایانکودر استفاده کنید، ارزش هزینهاش را کاملاً جبران میکند.
اهداف یادگیری
- تشخیص تفاوت بازیاب بایانکودر از بازرتبهبند کراسانکودر بر اساس ابعاد ورودی، تعداد پارامترها و هزینهی پردازش به ازای هر پرسش.
- پیادهسازی یک کراسانکودر کوچک از صفر در قالب یک بلوک ترنسفورمر (Transformer) که توالی ادغامشدهی (پرسش، سند) را دریافت میکند و یک عدد اسکالر به عنوان میزان ارتباط تحویل میدهد.
- راهاندازی یک پایپلاین (Pipeline) دومرحلهایِ بازیابی و سپس بازرتبهبندی: بازیابی N سند برتر با یک بازیاب ارزان، بازرتبهبندی این N سند به K سند برتر با کراسانکودر، و بازگرداندن K مورد.
- سنجش مصالحهی میان تاخیر (Latency) و کیفیت روی یک پیکرهی متنیِ نمونه (Fixture corpus) و انتخاب N مناسب متناسب با سقف تاخیر (Latency budget) مشخص.
صورت مسئله
بایانکودر پرسش و سند را به یک فضای برداری مشترک نگاشت میکند و رتبهبندی را با شباهت کسینوسی انجام میدهد. این دو امبدینگ هیچگاه همدیگر را نمیبینند. مدل ناچار است تمام اطلاعات مفید سند را بدون آگاهی از پرسش، درون یک بردار فشرده کند. این رویکرد سریع است — یک امبدینگ (Embedding) به ازای هر سند در زمان ایندکس و یک امبدینگ به ازای هر پرسش در زمان استعلام — و تنها راه رتبهبندی در مقیاس کل پیکرهی متنی به شمار میرود.
بهای این کار، افت دقت (Precision) است. دو سندی که موضوع کلی یکسانی دارند ممکن است امبدینگهای تقریباً یکسانی داشته باشند، حتی اگر یکی از آنها پاسخ پرسش باشد و دیگری نباشد. بایانکودر توان تفکیک آنها را ندارد.
کراسانکودر با خواندن همزمان پرسش و سند این مشکل را برطرف میکند. مدل توالی واحدِ [query] [SEP] [document] را میگیرد، اتنشن (Attention) کامل را روی این پیوند اجرا میکند و یک عدد اسکالر به عنوان میزان ارتباط تحویل میدهد. هر توکن (Token) سند میتواند به هر توکن پرسش اتنشن داشته باشد؛ بنابراین مدل امتیاز را با دیدن کل بافتار تعیین میکند.
بهای این کار، افت نرخ پردازش (Throughput) است. در حالی که بایانکودر یک بار امبد میکند و تا همیشه پاسخ میدهد، کراسانکودر باید برای هر جفت (پرسش، سند) یک بار اجرا شود. برای یک پیکرهی متنیِ 10 میلیون سندی، این یعنی 10 میلیون گذر رو به جلو (Forward pass) به ازای هر پرسش؛ رقمی که به هیچ وجه در سقف تاخیر یک درخواست جا نمیشود.
راهکار، دومرحلهای کردن کار است: با بایانکودر N نتیجهی برتر را بازیابی کنید، سپس با کراسانکودر این N مورد را بازرتبهبندی کرده و به K گزینهی برتر برسانید. N مقدار کوچکی است (50 تا 200) و افزایش کیفیتِ کراسانکودر درست در همان جایی که اهمیت دارد متمرکز میشود. با این روش، تاخیر کل درون سقف تاخیر درخواست باقی میماند و کیفیت نهایی همان کیفیت کراسانکودر خواهد بود که سقف آن را بازخوانیِ (Recall) بایانکودر در N تعیین میکند.
مفهوم
ابعاد ورودی کراسانکودر
بستهبندی استاندارد به صورت [CLS] query_tokens [SEP] document_tokens [SEP] است. خروجی جایگاه CLS به یک سرِ (Head) خطی تکی داده میشود که اسکالر ارتباط را تولید میکند. برخی پیادهسازیها به جای CLS از میانگینگیری (Mean-pooling) استفاده میکنند که تفاوت ناچیزی دارد. نکتهی اصلی این است که مدل برای هر جفت فقط یک عدد تولید میکند.
یک کراسانکودر 22 میلیون پارامتری (ردهی وزنی شناختهشدهی ms-marco-MiniLM-L-6-v2) نقطهی مرسوم در محیط عملیاتی (پروداکشن) است. مدلهای کوچکتر، کیفیت را سریعتر از آنکه تاخیر را کم کنند از دست میدهند. مدلهای بزرگتر (مانند bge-reranker-v2-m3 با 568 میلیون پارامتر) برای بازرتبهبندی آفلاین یا بازرتبهبندی صفحهی اول که در آن K کوچک است کنار گذاشته میشوند.
چرا در این درس یک مدل بندانگشتی آموزش میدهیم؟
یک کراسانکودر واقعی، یک ترنسفورمر انکودریِ فاینتیونشده است. در محیط عملیاتی فقط یک چکپوینت (Checkpoint) را لود و اجرا میکنید. هدف این درس نشان دادن ساختار مدل و شکل منحنی تاخیر-کیفیت است، نه آموزش یک رتبهبند در لبهی دانش. بنابراین، یک nn.Module کوچک با یک بلوک ترنسفورمر، اتنشن چندسر (Multi-Head Attention - بهطور پیشفرض 4 سر) و یک سر رگرسیون میسازیم. مدل با یک سید (Seed) بهشکل قطعی مقداردهی اولیه میشود تا دمو بدون نیاز به وزنهای ذخیرهشده روی دیسک، تکرارپذیر باشد.
این مدل اسباببازی، ساختار درست را از پیکرهی دادهی نمونه یاد میگیرد: جفتهای مرتبطِ پرسش و سند امتیاز پیشبینیشدهی بالاتری نسبت به جفتهای نامرتبط میگیرند. پایپلاین سرتاسری (End-to-end) خروجی بایانکودر را بازرتبهبندی میکند و نتایج برترِ حاصل، با برچسبهای مرجع (Gold labels) همبستگی دارند.
تاخیر در برابر کیفیت
پایپلاین دومرحلهای فقط یک متغیر تنظیمی دارد: N. اگر روی یک مجموعهی پرسش ارزیابی، N را از 5 تا 100 جاروب کنید، به این منحنی میرسید:
| N | Recall@1 مرحلهی 2 | گذر رو به جلوی کراسانکودر به ازای هر پرسش | تاخیر |
|---|---|---|---|
| 5 | 0.62 | 5 | کم |
| 20 | 0.81 | 20 | متوسط |
| 50 | 0.86 | 50 | زیاد |
| 100 | 0.86 | 100 | بسیار زیاد |
اعداد بالا صرفاً برای نشان دادن شکل رفتار منحنی هستند، نه حاصل اندازهگیری روی این دادهی نمونه. با این حال، خود این شکل کاملاً واقعی است. همیشه حوالی 20 تا 50 کاندیدا به یک «نقطهی زانو» (Knee) میرسید که بعد از آن، ارتقای کیفیت بازرتبهبندی اشباع میشود. فراتر از نقطهی زانو، عملاً هزینهی پردازشی بیهوده میدهید.
مقدار N را بر اساس منحنی ارزیابی در کنار سقف تاخیر انتخاب کنید. کراسانکودر نمیتواند بازخوانی را از بازخوانیِ بایانکودر در N بالاتر ببرد؛ پس N پایین، علاوه بر تاخیر، سقف کیفیت را هم میبندد.
پیادهسازی
فایل code/main.py این بخشها را پیادهسازی میکند:
CrossEncoder— یکtorch.nn.Moduleجمعوجور: امبدینگ توکن، یک بلوک ترنسفورمر با اتنشن چندسر و شبکه پیشخور (Feedforward)، و یک سر با میانگینگیری که یک عدد اسکالر خروجی میدهد.tokenize_pair(query, document)— دو رشته را در قالب یک توالی واحد از شناسهها همراه با شناسههای نوع (Type IDs) که مرز را مشخص میکنند میپیچد؛ کاملاً قطعی و با استفاده از کتابخانهی استاندارد پایتون.train_tiny(pairs)— یک دور آموزش با نظارت روی فهرستی از سهگانههای (پرسش، سند، ارتباط) برچسبگذاریشده با دست، تا مدل امتیازهای معقولی روی دادهی نمونه بدهد.rerank(query, candidates, top_k)— رابط کاربری محیط عملیاتی.pipeline(query, retriever, top_n, top_k)— جریان کاری دومرحلهای.- یک تابع دموی
main()که پیکرهی متنی را طبق الگوی درس 65 بارگذاری میکند، N مورد برتر را بازیابی کرده، آنها را به K مورد برتر بازرتبهبندی میکند، هر دو فهرست را کنار هم نمایش میدهد و تاخیر هر مرحله را گزارش میکند.
اجرایش کنید:
python3 code/main.py
خروجی، N مورد برتر بایانکودر، K مورد برتر کراسانکودر و خلاصهی زمانبندی را نشان میدهد. کراسانکودر در هر فراخوانی زمان بیشتری میبرد اما روی کل پیکره اجرا نمیشود. در نتیجه، مجموع زمان دو مرحله درون سقف تاخیر درخواست باقی میماند و پاسخی را انتخاب میکند که بایانکودر در رتبهی دوم یا سوم قرار داده بود.
حالتهای شکستی که دمو پنهان میکند
کراسانکودر متقارن نیست. خروجی rerank(q, d) و rerank(d, q) دو امتیاز متفاوت است. همیشه اول پرسش را به مدل بدهید. اگر تصادفی جایشان عوض شود، بازخوانی فرو میریزد.
مقدار N برای رو شدن باگ خیلی پایین است. اگر N = K بگذارید، کراسانکودر میتواند ترتیب همان K کاندیدا را جابهجا کند اما نمیتواند کاندیدای جدیدی را وارد جمع K مورد برتر کند. بازخوانی در K تکان نمیخورد و در نتیجه به نظر میرسد این مرحله هیچ بهبودی نداشته است. همیشه N را دستکم 3 برابر K بگیرید.
نشت دادههای آموزشی به ارزیابی. اگر جفتهای آموزشی دستساز شامل پرسشهای ارزیابی باشند، بازرتبهبندی به شکل اغراقآمیزی بینقص به نظر میرسد. حتی روی دادهی نمونه هم آموزش و ارزیابی را کاملاً جدا کنید.
وزنهای محیط عملیاتی سنگین هستند. یک کراسانکودر 22 میلیون پارامتری با فرمت float32 حدود 88MB حافظه میگیرد. قبل از اینکه قول تاخیر صدک 95 زیر 100 میلیثانیه را بدهید، حافظهی سرور مدل را بسنجید.
دستهبندی (Batching) اهمیت دارد. یک کراسانکودر واقعی، N کاندیدا را در قالب یک بچ اجرا میکند. این درس این کار را در _batch_encode انجام میدهد؛ یعنی تنسورهای (Tensor) دستهایِ شناسهها و شناسههای نوع را با torch.tensor(...) میسازد و یک گذر رو به جلو اجرا میکند. اگر دستهبندی را حذف کنید، تاخیر در N ضرب میشود.
استفاده در عمل
الگوهای محیط عملیاتی:
- بایانکودر، کراسانکودر و مقدار N را به هم قفل کنید؛ تغییر هر یک از آنها ارزیابی قبلی را باطل میکند.
- خروجی بازرتبهبند را بر اساس هشِ (پرسش، شناسهی سند) کش کنید. یک پرسش مشخص در برابر یک پیکرهی دادهی ثابت به همان ترتیب بازرتبهبندی میشود؛ اصابت به کش (Cache hit) بدون هزینه تاخیر را کم میکند.
- امتیاز کراسانکودر را برای رتبهی 1 لاگ کنید. اگر امتیاز گزینهی اول پرسشی پایینتر از یک آستانهی مشخص برای آن پیکره باشد، با نتیجهای خارج از دامنه روبرو هستید؛ این موضوع را به مدل زبانی گزارش دهید و بگویید «پاسخ با اطمینان پیدا نشد».
ادامهی مسیر
در درس 68 این پایپلاین دومرحلهای را بهصورت سرتاسری ارزیابی میکنیم. در درس 69 این بازرتبهبند را بعد از بازیاب هیبریدی درس 65 و قبل از تولیدکنندهی پاسخ قرار میدهیم. بازرتبهبند، مرحلهی دوم سیستم سرتاسری خواهد بود.
تمرینها
- مقدار N را از 5 تا 50 جاروب و Recall@1 خروجی بازرتبهبندیشده را رسم کنید. نقطهی زانو را روی دادههای این نمونه پیدا کنید.
- کراسانکودر را به جای یک دور، در 10 دور (Epoch) آموزش دهید. فاصلهی امتیازی میان جفتهای مثبت و منفی را در هر دور بسنجید.
- میانگینگیری را با یک سر مبتنی بر توکن CLS جایگزین کنید. سرعت همگرایی را روی دادههای این نمونه بسنجید.
- سر دومی به کراسانکودر اضافه کنید که برچسب دودویی «آیا پاسخ در این سند هست یا نه» را پیشبینی کند. در زمان استنتاج از هر دو سر استفاده کنید: یکی برای رتبهبندی و دیگری برای اعمال آستانه (Threshold).
- بایانکودر ماک و قطعی را با بایانکودر درس 65 عوض کنید و دو مرحله را به هم زنجیر کنید. تغییرات top-K را نسبت به حالتی که فقط بایانکودر به کار رفته بسنجید.
اصطلاحات کلیدی
| اصطلاح | در بازار چه میگویند | معنای فنی و دقیق |
|---|---|---|
| بایانکودر (Bi-encoder) | «بازیاب برداری» | پرسش و سند را مستقل از هم کدگذاری میکند؛ شباهت کسینوسی آنها را رتبهبندی میکند |
| کراسانکودر (Cross-encoder) | «بازرتبهبند» | (پرسش، سند) را مشترکاً کدگذاری میکند؛ یک عدد اسکالر برای میزان ارتباط تحویل میدهد |
| پایپلاین دومرحلهای (Two-stage pipeline) | «بازیابی و بازرتبهبندی» | بازیاب ارزان N مورد را برمیگرداند، بازرتبهبند گران K مورد را نگه میدارد |
| N (بودجهی کاندیداها) | «استخر بازرتبهبندی» | تعداد کاندیداهایی که کراسانکودر برای هر پرسش امتیازدهی میکند |
| سر میانگینگیری (Mean-pooling head) | «میانگین آخرین لایهی پنهان» | میانگین گرفتن از خروجیهای آخرین لایهی انکودر برای ساخت یک بردار واحد |
برای مطالعهی بیشتر
- Nogueira, Cho, "Passage Re-ranking with BERT", 2019 — مقالهی بنیادین رتبهبندی با کراسانکودر
- Reimers, Gurevych, "Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks", 2019 — دربارهی مقایسهی بایانکودرها و کراسانکودرها
- مستندات کراسانکودرهای SentenceTransformers
- کارت مدل BGE Reranker v2
- فاز 19، درس 65 — بازیاب هیبریدی که ورودی این مرحلهی بازرتبهبندی را تامین میکند
- فاز 19، درس 68 — ارزیابی جامعی که میزان ارتقای حاصل از این بازرتبهبندی را میسنجد
