هنگام مطالعهی مقالات و بنچمارکهای مدلهای زبانی، دیدن تفاوت فاحش در اعدادی مثل BLEU یا ROUGE-L میان دو کتابخانهی مختلف بسیار رایج است؛ تفاوتهایی که اغلب به جزئیاتی چون روش توکنسازی، هموارسازی یا تبدیل حروف بزرگ و کوچک برمیگردد و تحلیل نتایج را سردرگمکننده میکند.
در این درس پنج معیار بنیادی ارزیابی متن شامل تطابق دقیق (Exact Match)، معیار F1، دقت، BLEU-4 و ROUGE-L را از صفر و بر پایهی اصول ریاضی پیادهسازی میکنیم تا بدون وابستگی کورکورانه به پکیجهای آماده، سازوکار هر متریک و دلیل تغییر اعداد را دقیقاً بشناسید.
این پیادهسازیها کاملاً بر پایهی کتابخانهی استاندارد پایتون و ابزار پایهای مثل numpy انجام میشوند و نیازی به پردازشهای سنگین ابری یا دسترسی به APIهای خارجی ندارند، بنابراین مستقیماً روی سیستم شخصی یا محیطهای محلی قابل اجرا هستند.
BLEU، ROUGE-L، F1، تطابق دقیق (exact-match) و دقت (accuracy)؛ پنج معیاری که هنوز هم بار اصلی اعداد ارزیابی منتشرشده برای مدلهای زبانی بزرگ (LLM) را به دوش میکشند. هر کدام را از صفر و بر پایهی اصول اولیه پیادهسازی کنید تا دقیقاً بدانید هر عدد چه معنایی دارد.
اهداف یادگیری
- پیادهسازی تطابق دقیق، F1 و دقت در سطح توکن با قواعد توکنسازی صریح.
- پیادهسازی BLEU-4 از پایه: دقت n-gram اصلاحشده، میانگین هندسی روی n از 1 تا 4، و جریمهی کوتاهی طول (brevity penalty).
- پیادهسازی ROUGE-L با استفاده از طولانیترین زیردنبالهی مشترک (LCS)، به همراه ترکیب F-beta برای دقت و بازخوانی.
- توزیع فراخوانی بر اساس فیلد
metric_nameاز درس 70، بهگونهای که اجراکننده (runner) به نوع معیار وابسته نباشد. - تثبیت رفتار سیستم با بردارهای مرجع برگرفته از مثالهای حلشده، نه با تکیه بر کتابخانههای جانبی.
چرا پیادهسازی مجدد؟
گاهی مقالهای میخوانید که BLEU را 28.3 گزارش کرده و در مقالهی دیگری همان معیار 0.283 آمده است. یا میبینید امتیاز ROUGE-L در دو کتابخانهی مختلف 10 نمره با هم فرق دارد، فقط به این دلیل که یکی متن را به حروف کوچک تبدیل میکند و دیگری نه. سریعترین راه برای خلاص شدن از این سردرگمیها این است که خودتان معیارها را بنویسید؛ آنوقت میتوانید دقیقاً به خطی اشاره کنید که توکنساز در آن تعیین شده و خطی که هموارسازی (smoothing) در آن اعمال میشود. بعد از آن، مقایسهی اعداد میان مقالات مختلف به بررسی تنظیمات معیار تبدیل میشود، نه بحث و جدل دربارهی کتابخانهها.
کتابخانهی استاندارد پایتون به همراه numpy کاملاً کافی است. BLEU فقط شمارش است و یک محدودسازی مقدار (clamp). ROUGE-L برنامهریزی پویا (dynamic programming) است. F1 هم اشتراک مجموعهای روی توکنها است. سختترین بخش کار، انتخاب یک توکنساز و پایبندی به آن است.
توکنسازی
توکنساز ما این است: re.findall(r"\w+", text.lower()). تبدیل به حروف کوچک، جدا کردن توالی کاراکترهای کلمهای (حروف، ارقام و زیرخط)، و دور ریختن هر چیز دیگر. تکتک معیارهای این درس دقیقاً از همین توکنساز استفاده میکنند. اجراکننده حق انتخاب ندارد. اگر توکنساز را عوض کنید، عملاً بنچمارک متفاوتی را اجرا کردهاید.
TOKEN_RE = re.compile(r"\w+", re.UNICODE)
def tokenize(text):
return TOKEN_RE.findall(text.lower())
این یک سادهسازی عمدی است. در محیطهای عملیاتی (پروداکشن) مواردی مثل نویسههای CJK (چینی، ژاپنی و کرهای)، فرمهای مخفف (contractions) و شناسههای کد اهمیت پیدا میکنند. نکتهی اصلی این درس این است که توکنساز یک قرارداد است، نه پیچی که مدام تنظیمش کنیم.
تطابق دقیق
def exact_match(pred, targets):
return float(any(pred.strip() == t.strip() for t in targets))
این تابع برای هر وظیفه مقدار 1.0 یا 0.0 را برمیگرداند. میانگین این مقادیر روی کل مجموعهداده، امتیاز نهایی است. این معیار، ابزار اصلی برای ارزیابی وظایف محاسباتی، سوالات چندگزینهای و دستهبندیهای کوتاه است.
معیار F1 در سطح توکن
چندمجموعهی (multiset) توکنها را برای متن پیشبینیشده و هدف تشکیل دهید. دقت برابر است با اندازهی اشتراک چندمجموعهها تقسیم بر اندازهی چندمجموعهی پیشبینی. بازخوانی نیز همان اشتراک تقسیم بر اندازهی چندمجموعهی هدف است. F1 میانگین همساز (harmonic mean) این دو است. این پیادهسازی حالتهای خاص پیشبینی خالی و هدف خالی را هم مدیریت میکند.
برای وظایفی با چند هدف، بهترین F1 را در میان فهرست اهداف در نظر میگیریم. این کار با رفتار استاندارد سبک SQuAD که در مقالات بسیار رایج است مطابقت دارد.
BLEU-4
معیار BLEU شاخص کلاسیک ترجمهی ماشینی است و هنوز در کارهای خلاصهسازی هم دیده میشود. فرمولی که ما استفاده میکنیم BLEU-4 در سطح جمله است؛ یعنی برای هر جفت پیشبینی و مرجع محاسبه میشود، جریمهی کوتاهی طول استاندارد را دارد، و برای تعداد n-gramهای اصلاحشده از هموارسازی جمع با یک (additive-one) استفاده میکند تا نبودِ یک 4-gram کل امتیاز را صفر نکند.
برای هر جفت کاندید و مرجع، دقت n-gram اصلاحشده را برای n برابر با 1، 2، 3 و 4 میشماریم. دقت اصلاحشده، تعداد تکرار هر n-gram در کاندید را به بیشترین تعداد تکرار همان n-gram در هر یک از مراجع محدود میکند؛ بنابراین متن کاندید نمیتواند با تکرار مداوم یک عبارت، امتیازش را به شکل کاذب بالا ببرد. میانگین هندسی این چهار دقت، در نهایت با جریمهی کوتاهی طول تعدیل میشود.
قاعدهی هموارسازی مورد استفاده همان روشی است که لین و اوچ (Lin and Och) «روش 1» نامیدند: پیش از گرفتن لگاریتم، به صورت و مخرج دقت هر n-gram یک واحد اضافه کنید. این کار مانع از بروز log 0 در شرایطی میشود که مرجع هیچ 4-gram مشترکی ندارد، و در متنهای کاندید طولانی هم مقدار نهایی بسیار نزدیک به حالت بدون هموارسازی باقی میماند.
ROUGE-L
معیار ROUGE-L طولانیترین زیردنبالهی مشترک (LCS) میان توالی توکنهای کاندید و مرجع را مقایسه میکند. LCS ترتیب کلمات را بدون اجبار به پیوستگی آنها در نظر میگیرد؛ به همین دلیل معیار پیشفرض خلاصهسازی است. ما طول LCS را با یک جدول استاندارد برنامهریزی پویا محاسبه میکنیم، سپس بازخوانی را بهصورت lcs / طول مرجع و دقت را بهصورت lcs / طول کاندید به دست میآوریم و با F-beta ترکیب میکنیم که در آن بتا برابر با یک است تا فرم متقارن F1 حاصل شود.
def lcs_length(a, b):
n, m = len(a), len(b)
dp = numpy.zeros((n + 1, m + 1), dtype=int)
for i in range(n):
for j in range(m):
if a[i] == b[j]:
dp[i+1, j+1] = dp[i, j] + 1
else:
dp[i+1, j+1] = max(dp[i+1, j], dp[i, j+1])
return int(dp[n, m])
استفاده از جدول numpy خوانایی کد را بالا میبرد؛ هرچند با لیستهای خالص پایتون هم میشد آن را پیاده کرد. وظایفی که ROUGE-L را به کار میگیرند، به ازای هر وظیفه هزینهی زمانی را میپردازند. برای طول معمول خلاصهها، این زمان زیر 1 میلیثانیه میماند.
دقت
برای وظایف دستهبندی، معیار دقت به تطابق دقیق تقلیل پیدا میکند: اگر پیشبینی با هر یک از اهداف نرمالسازیشده در فهرست برابر باشد، امتیاز 1 میگیرد. ما آن را بهعنوان یک تابع مجزا ارائه میدهیم تا توزیعکننده (dispatcher) بتواند بر اساس metric_name تصمیم بگیرد، بدون اینکه نیازی به مقایسههای رشتهای درون اجراکننده باشد.
قرارداد توزیع فراخوانی
تنها نقطهی ورود سیستم score(metric_name, prediction, targets) است. این تابع یک عدد اعشاری در بازهی [0, 1] برمیگرداند. اجراکننده بر اساس نام معیار شاخهبندی (branch) نمیکند؛ فراخوانی را واگذار میکند و نتیجه را مینویسد. این همان رابطی است که در درس 75 به مشخصات وظیفه (task spec) از درس 70 متصل خواهد شد.
def score(metric_name, pred, targets):
if metric_name == "exact_match":
return exact_match(pred, targets)
if metric_name == "f1":
return max(f1_score(pred, t) for t in targets)
if metric_name == "bleu_4":
return max(bleu4(pred, t) for t in targets)
if metric_name == "rouge_l":
return max(rouge_l(pred, t) for t in targets)
if metric_name == "accuracy":
return accuracy(pred, targets)
raise ValueError(f"unknown metric_name: {metric_name}")
معیار code_exec در درس 72 بررسی میشود و همانجا به توزیعکننده اضافه خواهد شد.
مواردی که این درس به آنها نمیپردازد
این درس مدلی را فراخوانی نمیکند. خروجیها را فراتر از قواعد پسپردازش درس 70 نرمالسازی نمیکند. بازههای اطمینان (confidence intervals) را حساب نمیکند. معیارهای BLEURT یا BERTScore را هم پیادهسازی نمیکند (چون آنها به مدل نیاز دارند و در درس دیگری بررسی میشوند). تمرکز این درس روی کف ماجرا است: پنج معیار، یک توکنساز، یک جدول توزیع.
راهنمای خواندن کد
فایل main.py هر معیار را در قالب یک تابع مستقل به همراه توزیعکننده تعریف میکند. بردارهای مرجع در بلوک _reference_examples در انتهای فایل قرار دارند. دموی برنامه، توزیعکننده را روی 8 مثال اجرا کرده و امتیاز هر معیار را چاپ میکند. تستهای موجود در code/tests/test_metrics.py بردارهای مرجع را تثبیت میکنند و تمام حالتهای مرزی (پیشبینی خالی، مرجع خالی، نبود توکن مشترک، تطابق دقیق و محدودسازی عبارتهای تکراری) را تحت فشار میسنجند.
فایل main.py را از بالا به پایین بخوانید. توابع به ترتیب پیچیدگی چیده شدهاند. exact_match و accuracy هر کدام یک خط هستند. تابع F1 شش خط است. بخشهای سنگین کار BLEU و ROUGE-L هستند که توضیحات کاملی دربارهی قاعدهی هموارسازی و رابطهی بازگشتی LCS در کامنتهای آنها آمده است.
گامهای بعدی
معیارهای کلاسیک لازم هستند، اما کافی نیستند. آنها به همپوشانی ظاهری پاداش میدهند و از درک معنا بازمیمانند. راهحل این است که وقتی پایهی معیارهای کلاسیک تثبیت شد، معیارهای مبتنی بر مدل (مثل BLEURT، BERTScore و GEval) را روی آنها سوار کنید. این موضوع به درسهای بعدی مربوط میشود. فعلاً همین پنج معیار را به کار بیندازید، آنها را با تستها تثبیت کنید؛ آنوقت یک پشتهی ارزیابی در اختیار دارید که قابل بررسی، سریع و تکرارپذیر است.
