فاز 19 · درس 49

ساخت بستر ارزیابی مدل زبانی (LM Eval Harness) از صفر

نوع
ساختنی
زمان
حدود 90 دقیقه
زبان‌ها
Python
یک ترازو و چندین وزنه فلزی کوچک با درجه‌بندی دقیق در کنار خط‌کشی مدرج روی میز کار چوبی
فهرست مطالب
  1. اهداف یادگیری
  2. صورت‌مسئله
  3. مفهوم
  4. مشخصات وظیفه
  5. پنج وظیفه‌ی نمونه
  6. قرارداد معیار ارزیابی
  7. تطبیق‌دهنده‌ی مدل
  8. اجراکننده
  9. پیاده‌سازی
  10. مرحله‌ی 1: مقداردهی اولیه‌ی وظیفه‌های نمونه
  11. مرحله‌ی 2: بارگذاری وظیفه‌ها
  12. مرحله‌ی 3: پیاده‌سازی معیارها
  13. مرحله‌ی 4: نوشتن اجراکننده
  14. مرحله‌ی 5: تولید خروجی JSON
  15. شیوه‌ی استفاده
  16. تحویل پروژه
  17. تمرین‌ها
  18. اصطلاحات کلیدی
  19. برای مطالعه‌ی بیشتر

با انتشار مداوم مدل‌های زبانی جدید، ادعاهای تبلیغاتی درباره‌ی توانایی آن‌ها بیش از پیش گمراه‌کننده شده است. مقایسه‌ی مدل‌ها بدون آزمون‌های تکرارپذیر و سنجش آن‌ها صرفاً بر پایه‌ی برداشت‌های ذهنی، آسیب‌پذیری بزرگی برای پروژه‌های واقعی به همراه دارد؛ زیرا هر تغییری در نسخه‌ی مدل یا پرامپت‌ها می‌تواند بدون ثبت رسمی، موجب افت کیفیت در محیط عملیاتی شود.

راه‌حل این چالش، ساخت یک بستر ارزیابی اختصاصی (Evaluation Harness) است؛ سازوکاری کوچک و مستقل که وظیفه‌ها را در قالب فایل‌های استاندارد نگه می‌دارد، پاسخ‌ها را با توابع سنجش دقیق می‌آزماید و خروجی تابلوی امتیازات (Leaderboard) را در قالب JSON برای مقایسه‌ی مداوم ذخیره می‌کند. در این ساختار، منطق وابستگی به مدل از چارچوب آزمون جدا می‌شود تا بتوان هر ارائه‌دهنده‌ای را در چند ثانیه تعویض و مقایسه کرد.

اگر برای ارسال درخواست به مدل‌ها از APIهای خارجی استفاده می‌کنید، توجه داشته باشید که پرداخت مستقیم با کارت‌های بانکی ایرانی امکان‌پذیر نیست و برخی سرویس‌ها دسترسی از نشانی‌های اینترنتی ایران را محدود کرده‌اند. در چنین شرایطی می‌توانید از مدل‌های محلی سبک یا واسط‌های سازگار با API استفاده کنید تا بستر ارزیابی بدون تغییر ساختاری به کار خود ادامه دهد.

مدلی که روی وظیفه‌ای که نمی‌توانید تعریفش کنید خوب عمل می‌کند، صرفاً از سر تصادف خوب عمل کرده است. بستر آزمایشی (harness) یعنی تعریف وظیفه، معیار ارزیابی، اجراکننده و تابلوی امتیازات؛ همه در قالبی جمع‌وجور و تعویض‌پذیر.

اهداف یادگیری

  • تعریف وظیفه (Task) در قالب یک فایل JSONL با فیلدهای prompt، targets، metric و فیلد اختیاری extras به‌ازای هر نمونه.
  • پیاده‌سازی پنج معیار ارزیابی: تطابق دقیق (Exact Match)، معیار rouge-l F1، بررسی با اجرای کد، چندگزینه‌ای و وجود زیررشته.
  • ساخت یک اجراکننده که نمونه‌های هر وظیفه را دسته‌بندی (batch) کرده و به یک تطبیق‌دهنده (Adapter) مدل با قابلیت تعویض می‌فرستد.
  • تولید خروجی JSON تابلوی امتیازات، شامل امتیازهای تفکیکی هر وظیفه، تاخیر (Latency) و میانگین کلی با قابلیت بازتولید.

صورت‌مسئله

هر هفته مدل زبانی جدیدی از راه می‌رسد. ادعای بازاریابی همه‌شان این است که عملکرد درخشانی دارند. پرسش صادقانه این است: عملکرد درخشان در چه کاری؟ پاسخ صادقانه همان تابلوی امتیازاتی است که خودتان نوشته‌اید؛ چون تابلوی ارائه‌دهنده‌ی مدل بر اساس معیارهایی تنظیم شده که خودشان مدل را برایش بهینه‌سازی کرده‌اند.

بدون داشتن بستر آزمایشی در مخزن کدتان، دو مدل را بر اساس حس‌وحال مقایسه می‌کنید. با داشتن این بستر، آن‌ها را بر اساس امتیاز روی مجموعه‌وظیفه‌ای مشخص با معیاری ثابت و روی خروجی JSON قابل مقایسه (diff) می‌سنجید. بستر آزمایشی، قراردادی میان اجرای دیروز و اجرای امروز شماست؛ بدون آن، افت‌های عملکردی بدون متوجه شدن شما سر از محیط عملیاتی (پروداکشن) درمی‌آورند.

دام اصلی، بیش‌برازش (overfitting) بستر آزمایشی روی یک مدل خاص است. راه‌حل این است که همه‌ی تصمیم‌های مختص یک مدل را از بستر آزمایشی بیرون نگه دارید: بستر آن‌قدر جمع‌وجور باشد که در 15 دقیقه خوانده شود؛ وظیفه‌ها آن‌قدر سبک باشند که بشود آن‌ها را در خود مخزن کد گنجاند؛ معیارها از پایه پیاده‌سازی شوند تا همکارتان بتواند بررسی‌شان کند؛ و تطبیق‌دهنده تنها جایی باشد که کد وابسته به مدل در آن قرار می‌گیرد. تطبیق‌دهنده را عوض کنید، تابلوی امتیازات جابه‌جا می‌شود؛ وظیفه‌ها را عوض کنید، تابلوی امتیازات جابه‌جا می‌شود؛ هیچ چیز دیگری نباید تکان بخورد.

مفهوم

JSONLهای وظیفه: پرامپت، اهداف، متریک،موارد اضافیload_all_tasksrun_leaderboardتولید دسته‌ای ModelAdapterتوزیع METRIC_FNS بر اساس نامامتیاز هر نمونهجدول رده‌بندی: به تفکیک وظیفه + کلیleaderboard.json

مشخصات وظیفه

هر نمونه یک سطر در فایل JSONL است:

{"id": "arith-00", "prompt": "compute: 2 + 2", "targets": ["4"], "metric": "exact_match"}

برای معیارهایی که به داده‌های کمکی برای نمره‌دهی نیاز دارند، extras این اطلاعات جانبی را حمل می‌کند:

{
  "id": "code-00",
  "prompt": "python: write a function f that doubles its input",
  "targets": ["ok"],
  "metric": "code_exec",
  "extras": {"io_pairs": [[1, 2], [3, 6]]}
}

هر وظیفه یک فایل .jsonl در مسیر outputs/tasks/ است. نام فایل همان نام وظیفه است و همه‌ی نمونه‌های داخل فایل از یک معیار مشترک استفاده می‌کنند.

پنج وظیفه‌ی نمونه

وظیفه معیار هدف آزمون
arithmetic exact_match درستی در سطح توکن برای پاسخی قطعی
summary rouge_l امتیاز F1 طولانی‌ترین زیردنباله‌ی مشترک در برابر خلاصه‌ی مرجعِ تک‌سطری
code-exec code_exec آزمون با اجرای کد: تابع پیش‌بینی‌شده باید مجموعه‌ای از جفت‌های ورودی-خروجی را برآورده کند
multiple-choice multiple_choice حرف اول پاسخ پیش‌بینی‌شده باید با یکی از حروف مجاز تطابق داشته باشد
generation substring_contains متن آزاد تولیدشده باید حداقل یکی از زیررشته‌های هدف را دربرداشته باشد

قرارداد معیار ارزیابی

هر معیار تابعی با امضای (prediction, targets, extras) -> float in [0.0, 1.0] است. بستر آزمایشی میانگین امتیاز تک‌تک نمونه‌ها را می‌گیرد تا امتیاز وظیفه مشخص شود، سپس میانگین امتیاز وظیفه‌ها را محاسبه می‌کند تا امتیاز کلی به دست آید. توابع معیار بسیار کوتاه‌اند:

  • exact_match: تبدیل به حروف کوچک، فشرده‌سازی فاصله‌ها و بررسی برابری.
  • substring_contains: همان نرمال‌سازی و بررسی وجود زیررشته.
  • multiple_choice: بزرگ‌کردن حرف اول و بررسی تطابق.
  • rouge_l: تقسیم طول LCS بر طول پیش‌بینی و طول مرجع، و محاسبه‌ی F1 حاصل از دقت (Precision) و بازخوانی (Recall).
  • code_exec: اجرای پیش‌بینی در یک فضای نام (namespace) محدود، فراخوانی f(x) روی تک‌تک جفت‌های ورودی-خروجی، و شمارش موارد منطبق.

معیار code_exec پیش‌بینی را در فضای نامی با توابع توکار (builtins) محدودشده اجرا می‌کند. تست‌های این درس تضمین می‌کنند که دستور import os با شکست روبه‌رو شود؛ چون os در فضای نام وجود ندارد و کد پیش‌بینی‌شده به هیچ‌وجه نمی‌تواند به سیستم فایل دسترسی پیدا کند.

تطبیق‌دهنده‌ی مدل

class ModelAdapter(Protocol):
    def generate(self, prompts: Sequence[str]) -> List[str]: ...
    @property
    def name(self) -> str: ...

تطبیق‌دهنده مرز تفکیک (seam) است. در این درس، ما ToyAdapter را قرار داده‌ایم؛ تطبیق‌دهنده‌ای بر پایه‌ی الگویاب قطعی که پاسخ درست را برای همه‌ی پرامپت‌های پنج وظیفه‌ی نمونه برمی‌گرداند. یک تطبیق‌دهنده‌ی واقعی، مدل را فراخوانی کرده و خروجی‌اش را تحویل می‌دهد؛ برای بستر آزمایشی فرقی میان این دو نیست.

اجراکننده

تابع run_task پرامپت‌ها را در دسته‌هایی به اندازه‌ی batch_size پردازش کرده و نتیجه را به تابع معیار می‌فرستد. run_leaderboard تمام وظیفه‌ها را پیمایش می‌کند و از آن‌ها میانگین می‌گیرد. write_leaderboard خروجی را با قید رشته‌ی ساختار (schema string) در قالب JSON ذخیره می‌کند تا تغییرات احتمالیِ فرمت در آینده، داشبوردها را بی‌سرصدا از کار نیندازد.

N نمونهدسته‌های با اندازه Badapter.generateامتیاز هر نمونه 0..1امتیاز وظیفهامتیاز کلی = میانگین امتیاز وظایف
ماتریس بستر ارزیابییک نسخه را انتخاب کنید
EVAL HARNESS MATRIXAn eval harness runs every task against every model variant and records pass or fail in a grid. Reading down a column shows which tasks are hard; reading across a row gives one variant its aggregate score. The matrix is what turns a vague hunch into a number you can regression-test against.parse-jsonsort-listsql-joinregex-extractrecursionedge-casesbaselinePPFPPFtunedPPPPPPablationPFFPFF
یک بستر آزمایشی ارزیابی، هر وظیفه را روی تمام نسخه‌های مدل اجرا کرده و قبولی یا رد را در یک جدول ثبت می‌کند. بررسی ستونی نشان می‌دهد کدام وظایف دشوارند؛ بررسی سطری نمره تجمیعی یک نسخه را مشخص می‌سازد. این ماتریس همان چیزی است که حدس‌های مبهم را به عددی قابل آزمون بازگشتی تبدیل می‌کند.

پیاده‌سازی

فایل code/main.py همان دست‌سازه (Artifact) قابل اجراست.

مرحله‌ی 1: مقداردهی اولیه‌ی وظیفه‌های نمونه

تابع seed_fixture_tasks(target_dir) پنج فایل .jsonl را می‌نویسد. اولین اجرای main.py در صورت خالی بودن پوشه، این فایل‌ها را می‌سازد.

مرحله‌ی 2: بارگذاری وظیفه‌ها

تابع load_all_tasks(task_dir) تک‌تک فایل‌های .jsonl را می‌خواند و دیکشنری‌ای از نام وظیفه به لیستی از رکوردهای Example برمی‌گرداند. خطوط توضیحی که با # شروع می‌شوند و خطوط خالی نادیده گرفته می‌شوند تا توسعه‌دهندگان بتوانند فایل‌ها را یادداشت‌گذاری کنند.

مرحله‌ی 3: پیاده‌سازی معیارها

هر معیار تابعی کوچک همراه با آزمون واحد (unit test) است. مجموعه‌ی آزمون‌های این درس شامل 13 حالت آزمایشی برای پوشش نرمال‌سازی، هم‌پوشانی جزئی، اجرای کد و رد کدهای ناامن است.

مرحله‌ی 4: نوشتن اجراکننده

تابع run_task روی دسته‌ها پیمایش کرده و یک TaskResult شامل امتیاز، تعداد پاسخ‌های درست، تعداد کل و تاخیر تولید می‌کند. run_leaderboard همه‌ی وظیفه‌ها را بررسی می‌کند و یک Leaderboard با میانگین کلی خروجی می‌دهد.

مرحله‌ی 5: تولید خروجی JSON

تابع write_leaderboard جدول را سریالی‌سازی می‌کند. پرچم --include-per-example رکوردهای تفکیکی هر نمونه را ذخیره می‌کند تا وقتی امتیازها تغییر کردند، بتوانید پیش‌بینی‌ها را با اجرای قبلی مقایسه (diff) کنید.

اجرای برنامه:

python3 code/main.py

این اسکریپت در اولین اجرا فایل‌های نمونه را می‌سازد، آن‌ها را با تطبیق‌دهنده‌ی آزمایشی (که همه‌ی نمونه‌ها را درست حل می‌کند) می‌سنجد و نتیجه را در outputs/leaderboard.json ذخیره می‌کند. امتیاز کلی با تطبیق‌دهنده‌ی آزمایشی 1.0 است؛ آزمون تطبیق‌دهنده‌ی ساختگی (stub) در test_main.py نشان می‌دهد وقتی تطبیق‌دهنده نتواند پاسخ دهد، همین بستر امتیاز 0.0 تولید می‌کند.

شیوه‌ی استفاده

برای اتصال یک مدل واقعی، کافی است یک تطبیق‌دهنده بنویسید. ساختار آن به این شکل است:

class HttpAdapter:
    name = "vendor.v1"

    def __init__(self, endpoint, api_key):
        self.endpoint = endpoint
        self.api_key = api_key

    def generate(self, prompts):
        out = []
        for prompt in prompts:
            response = http_post(self.endpoint, prompt, self.api_key)
            out.append(response["text"])
        return out

در ابتدای تابع main()، کلاس ToyAdapter را با HttpAdapter تعویض کنید. بستر آزمایشی، وظیفه‌ها، معیارها و تابلوی امتیازات دست‌نخورده باقی می‌مانند.

سه الگویی که هنگام استفاده از این بستر در پروژه‌های واقعی باید پیاده کنید:

  • فایل‌های وظیفه را تثبیت (Pin) کنید. فایل leaderboard.json باید هش محتوای وظیفه‌ها را در خود داشته باشد یا فایل‌های JSONL را کنار خود نگه دارد؛ وگرنه با تغییر فایل وظیفه امتیازها جابه‌جا می‌شوند و نمی‌فهمید تغییر از طرف مدل بوده یا داده‌ها.
  • به‌جای اکتفا به امتیاز، خودِ پیش‌بینی‌ها را مقایسه کنید. پرچم --include-per-example به شما اجازه می‌دهد ببینید مدل دقیقاً در روزی که امتیازش افت کرد، چه خروجی‌ای داده است.
  • اندازه‌ی دسته‌ها را محدود کنید. تطبیق‌دهنده‌های واقعی محدودیت نرخ درخواست (Rate limit) دارند. یک اندازه‌ی دسته‌ی کوچک باعث می‌شود بستر با سرویس‌دهنده‌های مختلف سازگار بماند.

تحویل پروژه

فایل outputs/skill-lm-eval-harness.md حاوی دستورالعمل کامل است: مشخصات وظیفه در قالب JSONL، پنج معیار، تطبیق‌دهنده‌ی تعویض‌پذیر، اجراکننده‌ی دسته‌ای و JSON تابلوی امتیازات همراه با رشته‌ی ساختار. فایل‌های وظیفه در outputs/tasks/ همان فایل‌های نمونه هستند؛ آن‌ها را برای شروع کار به پروژه‌های واقعی‌تان منتقل کنید.

تمرین‌ها

  1. وظیفه‌ی ششمی اضافه کنید و برای آن یک معیار اختصاصی از پایه بنویسید (هم‌پوشانی شبیه به BLEU، امتیازدهی بر اساس مرجع شبیه به BLEURT، یا هر معیاری با قرارداد مشخص).
  2. معیار code_exec را طوری توسعه دهید که خروجی استاندارد (stdout) را دریافت کند و لیستی از stdoutهای مورد انتظار را به‌عنوان هدف بپذیرد.
  3. یک دستور مقایسه‌ی تابلوی امتیازات اضافه کنید: با دریافت دو فایل leaderboard.json، نمایش دهد که امتیاز کدام وظیفه‌ها و به چه میزان جابه‌جا شده است.
  4. تاخیر هر نمونه را محدود کنید. فراخوانی تطبیق‌دهنده را در یک مهلت زمانی (timeout) قرار دهید؛ ستون مجزایی برای timeouts در تابلوی امتیازات نمایش دهید.
  5. محتوای وظیفه‌ها را با هش sha256 در تابلوی امتیازات تثبیت کنید تا هر بررسی‌کننده‌ای در آینده بتواند مطمئن شود ارزیابی دقیقاً روی همان وظیفه‌ها انجام شده است.

اصطلاحات کلیدی

اصطلاح در گفت‌وگوها در عمل
مشخصات وظیفه (Task spec) «فرمت ارزیابی» فایل JSONL با فیلدهای prompt ،targets ،metric و فیلد اختیاری extras برای هر نمونه
معیار (Metric) «شیوه‌ی نمره‌دهی» تابعی از (prediction, targets, extras) به یک عدد اعشاری در بازه‌ی [0, 1]
تطبیق‌دهنده (Adapter) «کلاینت مدل» شیئی با متد generate(prompts) -> list[str]؛ تنها بخش کدی که به یک مدل خاص وابستگی دارد
تابلوی امتیازات (Leaderboard) «جدول نتایج» خروجی JSON شامل امتیاز هر وظیفه، تعداد کل، تاخیر و میانگین کلی
معیار اجرای کد (Code exec metric) «اجرا کن و ببین» اجرای پاسخ پیش‌بینی‌شده در یک فضای نام محدود و مقایسه‌ی خروجی با جفت‌های ورودی-خروجی

برای مطالعه‌ی بیشتر

  • مخزن اصلی lm-evaluation-harness به‌عنوان مرجع محیط عملیاتی (پروداکشن)؛ بسیار حجیم‌تر است اما ساختار یکسانی دارد.
  • کتابخانه‌ی lighteval از HuggingFace به‌عنوان پیاده‌سازی دیگری از همین قرارداد.
  • درس 46 از فاز 19 الگوهای انباشت گرادیان (Gradient Accumulation) را در پشته‌ی آموزشی که این بستر ارزیابی می‌کند، پوشش می‌دهد.
  • درس 47 از فاز 19 ساختار چک‌پوینت (Checkpoint) مورد ارزیابی را شرح می‌دهد؛ هش چک‌پوینت را در تابلوی امتیازات ثبت کنید.
  • درس 48 از فاز 19 پشته‌ی آموزش توزیع‌شده (Distributed Training) را بررسی می‌کند که مدلِ تحت‌آزمون را تولید کرده است.

خودآزمایی

به سؤال‌ها جواب بدهید تا ببینید چقدر از درس در ذهنتان مانده است.

  1. پیش از درسکدام چهار فیلد نمونه وظیفه را در قالب JSONL بستر آزمایشی تعریف می‌کنند؟
  2. پیش از درسچرا امضای تابع متریک به صورت (prediction, targets, extras) -> float است؟
  3. حین درسمتریک code_exec چگونه در برابر پیش‌بینی‌های مخرب مقاومت می‌کند؟
  4. حین درسانتزاع تطبیق‌دهنده مدل (Adapter) چه مزیتی برای شما فراهم می‌کند؟
  5. حین درسچرا فایل JSON جدول رده‌بندی حاوی رشته شمایی مانند leaderboard.v1 است؟
  6. پس از درسهنگام خواندن جدول رده‌بندی، overall_score چگونه محاسبه می‌شود و در مقایسه دو اجرا باید مراقب چه چیزی بود؟

سؤالات متداول

چرا نباید به بنچمارک‌ها و تابلوهای امتیازات شرکت‌های ارائه‌دهنده‌ی مدل اعتماد کرد؟

چون معیارهای ارائه‌دهنده معمولاً بر اساس داده‌ها و وظایفی تنظیم شده‌اند که مدل دقیقاً برای آن‌ها بهینه‌سازی شده است؛ در حالی که شما به ارزیابی مدل روی داده‌ها و نیازهای اختصاصی پروژه‌ی خودتان نیاز دارید.

تطبیق‌دهنده (Adapter) در بستر ارزیابی چه نقشی دارد؟

تنها بخش کدی است که به سازوکار یک مدل خاص وابسته است؛ ورودی متنی را گرفته، به مدل ارسال می‌کند و پاسخ را برمی‌گرداند تا بستر ارزیابی و توابع سنجش به هیچ مدلی وابسته نمانند.

معیار code_exec چگونه امنیت سیستم را هنگام ارزیابی کدهای تولیدشده حفظ می‌کند؟

کد را درون یک فضای نام (namespace) با توابع درونیِ محدودشده اجرا می‌کند؛ به‌طوری که دسترسی به ماژول‌هایی مانند os مسدود است و امکان ارتباط با سیستم فایل یا اجرای دستورات مخرب وجود ندارد.

ما رو توی گوگل بیشتر ببین

با افزودن نکست ادمین به «منابع ترجیحی» گوگل، درس‌ها و مطالب ما را در بخش اخبار برتر جستجو بیشتر می‌بینید.