در پروژههای ارزیابی مدلهای زبانی، نبود یک فرمت استاندارد و یکپارچه برای وظایف (tasks) بهسرعت باعث آشفتگی کدبیس میشود. وقتی هر آزمایش از یک ساختار فایل متفاوت استفاده کند و سنجهها بارها بازنویسی شوند، مقایسهی دقیق عملکرد مدلها ناممکن خواهد بود.
در این درس یاد میگیرید که چگونه پیش از اجرای مدلها و امتیازدهی، یک مشخصات فنی تثبیتشده (frozen spec) در قالب JSONL طراحی کنید. با تعریف اسکیما، قوانین پسپردازش و یک اعتبارسنج سختگیر، دادههای ناقص پیش از مصرف فیلتر میشوند و ارزیابی مدلها کاملاً قطعی و تکرارپذیر خواهد بود.
یک بستر آزمایشی ارزیابی (eval harness) تنها به اندازهی قراردادی که وظایفش رعایت میکنند ارزش دارد. ساختار JSONL و دامنهی واژگان سنجهها را پیش از نوشتن حتی یک تابع امتیازدهی نهایی و تثبیت کنید.
اهداف یادگیری
- تعریف اسکیمای رکوردهای وظیفه در قالب JSONL، بهگونهای که محاسبات ریاضی، چندگزینهای، اجرای کد، دستهبندی و خلاصهسازی متن آزاد را در یک ساختار واحد پوشش دهد.
- تثبیت دامنهی بستهای از نامهای سنجهها تا درسهای بعدی (71 تا 73) بتوانند تنها بر اساس یک فیلد، روال مربوطه را فراخوانی کنند.
- مشخص کردن نمونههای چندشات (few-shot) و قواعد پسپردازش بهعنوان بخشی از خود وظیفه (و نه runner)، تا یک پرامپت مشخص روی مدلهای مختلف هدف یکسانی تولید کند.
- پیادهسازی یک اعتبارسنج (validator) سختگیر که رکوردهای ناصحیح را پیش از رسیدن به runner رد کند.
- ارائهی یک مجموعهدادهی نمونه (fixture) شامل 10 وظیفه که تمام شاخههای مشخصات فنی را پوشش دهد تا اعتبارسنج دادههای واقعی برای بررسی در اختیار داشته باشد.
چرا مشخصات فنیِ تثبیتشده؟
کدبیسهای پژوهشی سریعتر از آنکه تست جمع کنند، اسکریپتهای ارزیابی جمع میکنند. شش ماه که میگذرد، هر نوتبوک ساختار JSON مخصوص به خودش را دارد، هر سنجه دو بار بازنویسی شده و خروجی هیچ اجرایی با اجرای دیگر مقایسهپذیر نیست. راهحل ساده و بدون هیجان است: یک اسکیما انتخاب کنید، برایش اعتبارسنج بنویسید و هر چیز دیگری را رد کنید. این درس دقیقاً همین کار را انجام میدهد.
این ساختار از ایدههای بسترهای آزمایشی مانند BIG-bench ،HELM و lm-eval الهام گرفته است، اما نامگذاری فیلدها کار خودمان است. هر فیلد یک متولی مشخص دارد: runner وظیفه را میخواند؛ سنجه، اهداف (targets) را بررسی میکند؛ مرحلهی پسپردازش، متن تولیدشده را استانداردسازی میکند؛ و هیچ فیلدی در میانهی پایپلاین تغییرپذیر نیست.
ساختار رکورد
هر وظیفه یک شیء JSON در یک خط مستقل است. بستر آزمایشی فایل tasks.jsonl را میخواند و هر خط را جداگانه اعتبارسنجی میکند. وجود یک خط نامعتبر تنها باعث کنار گذاشته شدن همان رکورد میشود، نه متوقف شدن کل فرایند اجرا.
{
"task_id": "arith_001",
"category": "arithmetic",
"prompt": "Compute the result. Question: 17 + 24\nAnswer:",
"targets": ["41"],
"metric_name": "exact_match",
"few_shot_examples": [
{"prompt": "Question: 2 + 2\nAnswer:", "completion": "4"}
],
"post_process": "strip_whitespace",
"metadata": {"difficulty": "easy"}
}
فیلدهای الزامی عبارتاند از: task_id ،category ،prompt ،targets ،metric_name و post_process. فیلدهای few_shot_examples و metadata اختیاری هستند. وجود فیلدهای ناشناخته در سطح ریشه (top-level) اعتبارسنجی را با خطا متوقف میکند.
قوانین فیلدها
فیلد task_id یک رشتهی متنی بدون فاصلهی خالی (whitespace) است. اعتبارسنج، یکتا بودن آن را در کل فایل الزامی میکند.
فیلد category یکی از مقادیر arithmetic ،mcq ،code_exec ،classification یا summary است. این دستهبندی مشخص میکند کدام جفتِ سنجه و پسپردازش مجاز است. وظیفهای با دستهی code_exec باید از metric_name = code_exec استفاده کند و وظیفهای با دستهی mcq باید از metric_name = exact_match در برابر یک هدف تکحرفی بهره ببرد.
فیلد prompt یک رشتهی غیرخالی است. اعتبارسنج وجود فاصلهی خالی در انتهای متن را نمیپذیرد و رکوردهایی را که از قبل بلوک چندشات در بدنهی پرامپت دارند رد میکند. رندر کردن چندشات وظیفهی runner است، نه نویسندهی پرامپت.
فیلد targets یک لیست غیرخالی از رشتهها است. برای exact_match، تطابق با هر یک از اعضا کافی است. برای f1 و rouge_l، هدفی که بالاترین امتیاز را بگیرد ملاک خواهد بود. برای mcq، این لیست دقیقاً شامل یک عضو است.
فیلد metric_name یکی از مقادیر exact_match ،f1 ،bleu_4 ،rouge_l ،accuracy یا code_exec است. این دامنهی واژگان بسته است؛ تعریف سنجهی جدید به یک درس جدید و افزودن یک ورودی تازه در اینجا نیاز دارد.
فیلد few_shot_examples لیستی از جفتهای {prompt, completion} است. اعتبارسنج این لیست را به حداکثر 8 ورودی محدود میکند تا طول پرامپت بیش از حد بزرگ نشود.
فیلد post_process یکی از مقادیر none ،strip_whitespace ،lower ،extract_letter ،extract_code_block یا extract_first_line است. هر قاعده یک رفتار قطعی (deterministic) و مشخص دارد. اعتبارسنج ترکیب کردن این قواعد را مجاز نمیداند.
رفتار اعتبارسنج
اعتبارسنج دو لیست برمیگرداند: رکوردهای معتبر، و رکوردهای دارای خطا به همراه خط مربوطه، قاعدهی نقضشده و فیلدی که دچار خطا شده است. در صورتی که لیست خطاها خالی نباشد، runner اجرا نمیشود؛ مگر اینکه فلگ صریح --allow-bad-tasks تنظیم شده باشد.
رندر کردن نمونههای چندشات
بخش runner نمونههای چندشات را با یک خط خالی جداکننده به ابتدای پرامپت الحاق میکند. همین مسیر کد برای همهی مدلها اجرا میشود؛ بنابراین تنها عامل اختلاف در خروجی، خود مدل خواهد بود. نویسندگان پرامپت، نمونهها را فقط یک بار مینویسند، نه به ازای هر سرویسدهنده (provider).
def render(task):
parts = []
for ex in task.get("few_shot_examples", []):
parts.append(ex["prompt"] + " " + ex["completion"])
parts.append(task["prompt"])
return "\n\n".join(parts)
قواعد پسپردازش
مرحلهی پسپردازش پس از تولید متن و قبل از محاسبهی سنجه اجرا میشود. این فرایند قطعی و بیوضعیت (stateless) است.
noneرشته را بدون تغییر برمیگرداند.strip_whitespaceفاصلههای خالی ابتدا و انتها را حذف میکند.lowerحروف رشته را کوچک میکند.extract_letterاولین نویسهای را که با[A-E]مطابقت داشته باشد برمیگرداند؛ کاربرد در آزمونهای چندگزینهای (MCQ).extract_code_blockمحتوای اولین بلوک کد محصور میان سهگانه بکتیک را برمیگرداند؛ کاربرد در اجرای کد.extract_first_lineاولین خط غیرخالی را برمیگرداند؛ کاربرد در دستهبندی خلاصه.
هر وظیفهای که به قاعدهای بیرون از این لیست نیاز داشته باشد، باید در یک درس جدید بررسی شود.
آنچه این درس انجام نمیدهد
این درس امتیازی محاسبه نمیکند، هیچ مدلی را فراخوانی نمیکند و کدی اجرا نمیکند. این موارد در درسهای 71، 72 و 75 خواهند آمد. این درس صرفاً قراردادی را تثبیت میکند که همهی آنها باید رعایت کنند.
مجموعهدادهی نمونهی 10 وظیفهای شامل دو مورد محاسبات ریاضی، دو مورد چندگزینهای، دو مورد اجرای کد، دو مورد دستهبندی و دو مورد خلاصهسازی است. اعتبارسنج هر 10 مورد را تأیید میکند. یک fixture جداگانه (tasks_bad.jsonl) تکتک قوانین را نقض میکند و اعتبارسنج دقیقاً به همان تعداد خطا برمیگرداند.
نحوهی مطالعهی کد
فایل main.py شامل تعاریف TaskSpec ،validate_task ،validate_file و نقطهی ورودی رابط خط فرمان (CLI) است. بارگذاری دادههای نمونه بر عهدهی load_fixtures است. توابع کمکی رندر و پسپردازش در کنار کدهای اعتبارسنجی قرار گرفتهاند تا runner در درس 75 تنها یک ماژول را import کند.
فایل main.py را از بالا به پایین بخوانید. سپس به سراغ code/tests/test_spec.py بروید. این تستها تکتک قوانین اعتبارسنجی و رفتارهای پسپردازش را تثبیت میکنند. دموی انتهای main.py دادههای نمونهی همراه درس را اعتبارسنجی کرده و خلاصهای از آن را چاپ میکند.
گامی فراتر
مجموعههای ارزیابی در دنیای واقعی همانطور دستهبندیهای جدید اضافه میکنند که اسکیمای دیتابیس ستونهای جدید میگیرد. اقدام سنجیده این است که از اضافه کردن هر دستهبندی تازه خودداری کنید، مگر اینکه همزمان سنجه، قاعدهی پسپردازش و حداقل یک وظیفهی تستی نمونه برای آن تعریف کرده باشید. با این مشخصات فنی درست مثل تغییرات اسکیمای دیتابیس (migration) برخورد کنید: هر تغییری باید بررسی شود، نسخهبندی داشته باشد و همراه با تست ارائه شود. اعتبارسنجِ این درس، دروازهبان این فرایند است.
