طراحی یک بنچمارک کوچک بازیابی فارسی برای سنجش مقاومت در برابر املاهای چندگانه: معرفی persian-rag-bench

13 آبان 1405 - خواندن 9 دقیقه - 37 بازدید

مقدمه

در یادداشت قبلی ام درباره ی «ی عربی، نیم فاصله و PDF برعکس» توضیح دادم که چرا جست وجوی فارسی در سامانه های پرسش وپاسخ مبتنی بر بازیابی (RAG) بی صدا خراب می شود. این یادداشت درباره ی روی دیگر همان سکه است: چطور بفهمیم یک خط لوله ی بازیابی فارسی در برابر این مشکلات مقاوم است، و وقتی تغییرش می دهیم بدتر نشده است؟

پاسخ من یک بنچمارک کوچک و تکرارپذیر به نام persian-rag-bench است که آن را به صورت عمومی در Figshare (با DOI) و Hugging Face منتشر کرده ام. در ادامه طراحی آن، داده ها، معیارها، نحوه ی استفاده و محدودیت هایش را شرح می دهم.

چرا یک بنچمارک کوچک؟

بنچمارک های بزرگ فارسی وجود دارند؛ مثلا زیرمجموعه ی فارسی MIRACL یا PQuAD. این ها برای مقایسه ی کلی مدل ها ارزشمندند، اما برای یک کار روزمره ی مهندسی مناسب نیستند: آزمون پسرفت (regression check). وقتی یک قاعده ی یکسان سازی را عوض می کنید یا اندازه ی تکه ها را تغییر می دهید، می خواهید در چند ثانیه و بدون GPU بفهمید چیزی خراب شده یا نه.

بنچمارک های بزرگ همچنین یک ضعف خاص دارند: معمولا پرسش هایی که عمدا با املای متفاوت از متن نوشته شده باشند را به طور جداگانه علامت نمی زنند. پس نمی توان مستقیما پرسید «سامانه ی من با املاهای چندگانه چطور کنار می آید؟».

بنابراین سه هدف طراحی داشتم:

  1. سرعت و تکرارپذیری: اجرای کامل روی یک لپ تاپ معمولی، حتی با یک embedder آفلاین.
  2. برچسب صریح برای املای چندگانه: پرسش های املایی قابل گزارش جداگانه باشند.
  3. سادگی قالب: دو فایل JSONL که با هر ابزاری خوانده شوند.

و یک «غیرهدف» صریح: این بنچمارک برای رتبه بندی کلی مدل ها ساخته نشده و برای این کار بیش از حد کوچک است.

ساختار داده

بنچمارک دو بخش دارد: پیکره (corpus) با ۳۴ ردیف و فیلدهای id، title و text، و پرسش ها (qa) با ۴۲ ردیف و فیلدهای id، question، relevant_ids و tags. جدول زیر همین را خلاصه می کند:

  • corpus: تعداد ردیف: 34؛ فیلدها: id، title، text
  • qa: تعداد ردیف: 42؛ فیلدها: id، question، relevant_ids، tags

پیکره (corpus)

پیکره ۳۴ متن کوتاه دانشنامه ای است درباره ی شاعران فارسی زبان، شهرها، بناهای تاریخی، جشن ها، خوراک ها و موضوعات روزمره مثل اتاق، هیئت مدیره و بورس. طول متن ها بین ۱۸ تا ۳۵ کلمه است (میانگین حدود ۲۷ کلمه، با شمارش ساده ی فاصله ها). یک نمونه:

{"id": "hafez", "title": "حافظ", "text": "حافظ شیرازی شاعر قرن هشتم هجری است. ..."}

کوتاه بودن متن ها عمدی است: هر متن تقریبا یک واقعیت اصلی دارد و ابهام درباره ی اینکه کدام متن جواب پرسش است کم می شود.

پرسش ها (qa)

۴۲ پرسش طبیعی یا عبارت کوتاه جست وجو، هر کدام با شناسه ی متن های مرتبط. بر اساس شمارش خودم روی فایل:

  • ۴۱ پرسش دقیقا یک متن مرتبط دارند و یک پرسش دو متن مرتبط.
  • ۲۶ متن از ۳۴ متن پیکره دست کم یک بار هدف یک پرسش اند؛ ۸ متن باقی مانده فقط نقش متن رقیب (distractor) را دارند.
  • ۱۲ پرسش برچسب spelling دارند.

پرسش ها هم به شکل پرسش کامل اند («آرامگاه حافظ در کدام شهر است؟») و هم به شکل عبارت کوتاه جست وجو («قیمت بلیط»)، چون کاربران واقعی هر دو را می نویسند.

طراحی پرسش های املایی

قلب این بنچمارک ۱۲ پرسش املایی است. هر کدام یک کلمه را با املایی متفاوت اما رایج نسبت به متن نوشته اند. هیچ کدام از این املاها «غلط» نیستند؛ هر دو شکل در متن های فارسی واقعی دیده می شوند. الگوهای پوشش داده شده:

  • ت/ط و تفاوت حروف هم آوا: طهران به جای تهران، اطاق به جای اتاق، بلیط و باطری.
  • همزه و یای میانی: هیات به جای هیئت، رییس، مساله به جای مسئله.
  • همزه روی کرسی یا ی: پائیز، آئین.

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

نکته ی مهم این است که این مشکل با یکسان سازی حروف (مثل تبدیل ی عربی به فارسی) حل نمی شود، چون اینجا خود حروف فرق دارند. بنابراین بنچمارک دقیقا چیزی را می سنجد که یکسان سازی ساده از آن غافل است.

معیارها

بنچمارک با معیارهای استاندارد بازیابی گزارش می شود:

  • Hit@k: سهم پرسش هایی که دست کم یک متن مرتبط در k نتیجه ی اول آمده است. Hit@1 سخت گیرانه ترین شکل آن است.
  • MRR@10: میانگین معکوس رتبه ی اولین متن مرتبط در ده نتیجه ی اول.
  • nDCG@10: کیفیت ترتیب ده نتیجه ی اول با در نظر گرفتن جایگاه.
  • Hit@1 (spelling): همان Hit@1 فقط روی ۱۲ پرسش املایی.

چون تقریبا هر پرسش فقط یک متن مرتبط دارد، MRR و nDCG رفتار نزدیکی دارند. معیار تفکیک کننده ی اصلی، شکاف میان Hit@1 کل و Hit@1 املایی است: اگر سامانه روی کل پرسش ها خوب است اما روی پرسش های املایی افت شدید دارد، مشکل دقیقا در مقاومت املایی است.

یک نکته ی آماری

با ۴۲ پرسش، جابه جا شدن فقط یک پرسش Hit@k را حدود ۰٫۰۲۴ تغییر می دهد (۱ تقسیم بر ۴۲). روی زیرمجموعه ی ۱۲ پرسشی املایی، هر پرسش حدود ۰٫۰۸۳ اثر دارد. پس اختلاف های کوچک را نباید بیش از حد تفسیر کرد. این بنچمارک برای دیدن پسرفت های واضح ساخته شده، نه اختلاف های ظریف.

نتیجه های مرجع

نتیجه های زیر از persian-rag نسخه ی 0.3.1 با embedder آفلاین مبتنی بر hashing (بعد ۵۱۲) و اندازه ی تکه ی ۴۰۰ به دست آمده اند، همان طور که در مستندات بنچمارک پروژه منتشر شده است. به طور خلاصه: BM25 ساده به Hit@1 برابر 0.881 و Hit@1 املایی 0.583 می رسد، و هر دو پیکربندی «BM25 با بازنویسی پرسش» و «ترکیبی» به Hit@1 برابر 0.952 و Hit@1 املایی 0.833 می رسند. جزئیات در جدول زیر است:

  • sparse (BM25): Hit@1: 0.881؛ Hit@5: 0.881؛ MRR@10: 0.881؛ nDCG@10: 0.879؛ Hit@1 (املایی): 0.583
  • sparse + rewrite: Hit@1: 0.952؛ Hit@5: 0.976؛ MRR@10: 0.964؛ nDCG@10: 0.967؛ Hit@1 (املایی): 0.833
  • hybrid: Hit@1: 0.952؛ Hit@5: 0.976؛ MRR@10: 0.964؛ nDCG@10: 0.967؛ Hit@1 (املایی): 0.833

خوانش این جدول آموزنده است. BM25 ساده روی کل پرسش ها 0.881 می گیرد که بد به نظر نمی رسد، اما روی پرسش های املایی به 0.583 می رسد؛ یعنی تقریبا در ۵ پرسش از ۱۲ پرسش املایی جواب درست در رتبه ی اول نیست. افزودن بازنویسی پرسش (ساختن شکل های املایی دیگر و ادغام نتیجه ها) Hit@1 املایی را به 0.833 می رساند. بدون برچسب spelling، این تفاوت در میانگین کل گم می شد.

نحوه ی استفاده

بارگذاری از Hugging Face

from datasets import load_dataset

corpus = load_dataset("sadrmanesh/persian-rag-bench", "corpus", split="test")
qa = load_dataset("sadrmanesh/persian-rag-bench", "qa", split="test")

اجرای بنچمارک پروژه

git clone https://github.com/sadrmanesh/persian-rag && cd persian-rag
pip install -e .
python benchmarks/run_benchmark.py

این اسکریپت همه ی حالت های بازیابی را با و بدون بازنویسی پرسش اجرا می کند.

استفاده با سامانه ی خودتان

اگر خط لوله ی دیگری دارید، کافی است:

  1. متن های corpus را با همان یکسان سازی ای که در سامانه ی اصلی تان دارید ایندکس کنید.
  2. هر پرسش qa را با همان یکسان سازی جست وجو کنید.
  3. شناسه های بازیابی شده را با relevant_ids مقایسه و معیارها را محاسبه کنید.
  4. نتیجه را یک بار برای همه ی پرسش ها و یک بار فقط برای پرسش هایی که tags آن ها شامل spelling است گزارش کنید.

بهترین کاربرد آن قرار دادنش در CI است: هر تغییر در یکسان سازی، تکه بندی یا مدل embedding باید Hit@1 املایی را حفظ کند.

محدودیت ها

  • کوچک و دست نویس: با یک یا چند متن مرتبط برای هر پرسش، امتیازها سریع به سقف می رسند و برای مقایسه ی مدل های قوی تفکیک کننده نیستند.
  • دامنه ی محدود: متن ها کوتاه اند و بیشتر دانش عمومی درباره ی ایران و فرهنگ فارسی اند؛ متن های حقوقی، فنی یا فروشگاهی را نمایندگی نمی کنند.
  • سازنده و ارزیاب یکی است: بنچمارک را خودم ساخته ام و همان پروژه ای که آن را ساخته، روی آن ارزیابی شده است. این خطر بیش برازش به بنچمارک را دارد.
  • تنها ۱۲ پرسش املایی: پوشش الگوهای املایی کامل نیست.
  • متن پیش یکسان سازی نشده: داده عمدا یکسان سازی نشده است. هر دو طرف را به یک روش یکسان سازی کنید.

جمع بندی

یک بنچمارک لازم نیست بزرگ باشد تا مفید باشد؛ باید پرسش درستی بپرسد. persian-rag-bench یک پرسش مشخص می پرسد: آیا خط لوله ی بازیابی فارسی شما وقتی کاربر «طهران» یا «اطاق» می نویسد هم متن درست را پیدا می کند؟ با برچسب جداگانه برای پرسش های املایی، این پرسش در هر اجرای CI در چند ثانیه پاسخ داده می شود. پیشنهاد و مشارکت برای گسترش پرسش ها، به خصوص در حوزه های تخصصی تر، را با کمال میل می پذیرم.

منابع و لینک ها

نوشته های مرتبط