Есть три базовых шага: (i) разметить небольшой датасет, (ii) «откалибровать» LLM-оценщиков и (iii) запускать эксперимент и прогонять обвязку для оценки при каждом изменении конфигурации.
Всё начинается с того, что мы выбираем часть входов и выходов из прогонов (вызовов) к нашей LLM и размечаем, соответствует ли выход нашим критериям оценки (например, достоверность по источнику, релевантность и т. п.). Начните с простого: заведите таблицу со столбцами для входа, выхода, дополнительной метаинформации, которая помогает оценить результат, и отдельным столбцом для метки.
Сфокусируйтесь на бинарных метках: «прошёл/не прошёл» или «победа/поражение». Если критерии объективны — например, насколько краткое резюме верно отражает источник или содержит ли оно отказ, — используйте метки «прошёл/не прошёл». Для субъективных критериев, вроде того, какое из двух резюме более лаконично, используйте сравнения «победа/поражение/ничья». В последнем случае полезно позволить разметчикам отмечать «ничью». Если заставлять их выбирать победителя, когда два ответа почти одинаковые, это добавляет шум и мешает понять, что некоторые различия можно считать несущественными.
А как насчёт числовых меток или шкал Лайкерта? Хотя шкалы 1–5 дают больше градаций, мне оказалось сложно калибровать и людей-разметчиков, и LLM-оценщиков. Разница между «3» и «4» часто едва уловима. Даже при подробных правилах разметки разные разметчики будут ставить разные оценки. А если людям трудно стабильно размечать по одному и тому же регламенту, то и LLM-оценщикам будет не проще. Бинарные метки смягчают эту проблему, потому что задают чёткую границу решения.
Более того, хотя стейкхолдеры иногда просят «детальные» оценки, чтобы потом иметь гибкость подвинуть порог того, что считается «пройденным» (например, поднять его с 3 до 4 или поменять «незначительная ошибка» на «без ошибок»), по моему опыту, ровно ноль из них действительно это делает. В итоге они всё равно просят порекомендовать порог, чтобы можно было отчитаться долей «прошёл/не прошёл». Если мы всё равно придём к этому, проще начать с бинарных меток. Это даёт более быстрые и более согласованные метки от людей-разметчиков и облегчает «калибровку» LLM-оценщиков.
Стремитесь к 50–100 «провалам». Это зависит от общего числа меток и — что важнее — от того, какие метки нам реально важны. В оценках «прошёл/не прошёл» почти всегда самое важное — это «не прошёл», потому что именно такие дефекты подрывают доверие. Датасет на сотни примеров, в котором всего пять «провалов», не подходит для настройки и проверки наших оценщиков. Нужен сбалансированный датасет. Обычно я рекомендую иметь как минимум 50–100 «провалов» при 200+ примерах всего.
Как получить «провалы»? У меня хорошо работало использование более маленьких, менее способных моделей для генерации ответов. Даже стараясь изо всех сил, такие модели естественным образом дают «органические» ошибки. Они могут плохо справляться с длинным контекстом, иметь недостаточную способность к рассуждениям или проваливаться на граничных случаях — это как раз те виды ошибок, с которыми мы столкнёмся в продакшене.
Популярный подход — попросить сильную модель сгенерировать синтетические дефекты. Я считаю такие дефекты проблемными. Они часто оказываются «вне распределения»: либо слишком утрированными, либо слишком тонкими — и при этом не отражают того, что происходит в реальности в продакшене. Если мы «калибруем» оценщиков на таких примерах, они могут не научиться замечать грязные, органические проблемы, которые реально мешают пользователям. Понимаю, что иногда нужно от чего-то оттолкнуться, чтобы «раскачать» систему оценок, но если наш eval-датасет состоит только из таких примеров, стоит сделать приоритетом добавление органических примеров из продакшена.
Также можно применить активное обучение. Когда у нас появляется достаточно точный оценщик, мы можем прогнать его по неразмеченным данным, чтобы найти вероятные «провалы» и отдать их в приоритет на ручную разметку. Это помогает собрать сбалансированный датасет, не размечая вслепую тысячи примеров.
Имея размеченные примеры, следующий шаг — сделать шаблон промпта, который принимает вход и выход (и дополнительную метаинформацию) и возвращает ожидаемую метку. К этому стоит относиться как к обычной задаче машинного обучения и разделить данные на dev-выборку (валидационную) и test-выборку. Например, 75% примеров использовать для калибровки (то есть для итераций над шаблоном промпта), а оставшиеся 25% отложить как тестовый набор. Так мы измеряем, насколько хорошо оценщик обобщает на новые данные, а не подгоняемся под исходные 75% примеров.
Один оценщик — на одно измерение. Гораздо проще откалибровать оценщика под один критерий и добиться высокой точности. Один из антипаттернов — пытаться собрать одного «Божественного оценщика» (см. также God Object), который в одном промпте оценивает 5–10 измерений: достоверность по источнику, релевантность, лаконичность, тон и т. д. Я ни разу не видел, чтобы это хорошо работало. Более того, таких «универсальных» оценщиков — кошмар калибровать, потому что мы не можем легко изолировать, какое именно измерение «съехало».
Вместо этого делайте отдельные оценщики и объединяйте их простыми эвристиками (например: выход (результат) проходит проверку, только если прошли все измерения). Такой подход даёт более детальные метрики и позволяет точно видеть, какое измерение тянет производительность вниз. Ещё это позволяет по-разному относиться к метрикам: одни являются защитными (guardrail) — несоответствие им блокирует релиз, — а другие являются целевыми (north star), которые мы стремимся постоянно улучшать.
Если вы оцениваете в формате win/lose, учитывайте позиционный bias. Для этого запускайте оценку дважды, меняя порядок местами. Я обычно использую XML-теги, например <control> и <treatment>, помещая оцениваемые ответы внутрь. В первой оценке базовый вариант кладём в <control>, а сравниваемый — в <treatment>. Во второй оценке — наоборот: сравниваемый вариант в <control>, базовый — в <treatment>. Так мы добиваемся, что оценщик судит по содержанию, а не «клюёт» на порядок или на сами XML-теги.
Хорошо откалиброванный оценщик должен быть последовательным. Если в первой оценке побеждает базовый вариант, то и во второй он должен побеждать. Если решение «переворачивается», значит ответы, вероятно, слишком похожи, чтобы их уверенно различать, и такие случаи можно помечать как «ничья», а не вынуждать шумное решение.
Оценивайте этих оценщиков по precision, recall и коэффициенту Каппы Коэна. Поскольку мы используем бинарные метки, оценка получается простой. Для задач «прошёл/не прошёл» можно отдавать приоритет полноте (recall) по классу «не прошёл», потому что нам важно быть уверенными, что мы отлавливаем дефекты. При этом нужна и приемлемая точность, чтобы мы не помечали слишком много «провалов» ошибочно. Чтобы измерить согласованность оценок с человеческой разметкой, можно смотреть на коэффициент Каппы Коэна. Значение 0,4–0,6 означает существенное согласие, а всё, что выше 0,7, — отличный результат.
Ориентир — человеческий уровень, а не идеал. Иногда прилетают требования «90%+ точности». В ответ я обычно мягко напоминаю, что люди-разметчики редко до такого дотягивают. Часто вижу, что согласованность между разметчиками (Каппа Коэна) бывает всего 0,2–0,3. А ещё люди могут пропускать до 50% дефектов из-за усталости после просмотра сотен примеров. Поэтому если наш LLM-оценщик показывает более высокую полноту и более устойчивые решения, чем люди, я бы считал это успехом.
На мой взгляд, реальная польза не в том, чтобы быть «точнее людей», а в масштабируемости. Хорошо откалиброванный оценщик позволяет применять последовательное, (сверх)человеческое по уровню суждение к сотням примеров за минуты, круглосуточно, без узкого места в виде ручной проверки. Это даёт возможность запускать эксперименты в масштабе и, следовательно, быстрее итератироваться.
Наконец, можно объединить наши отдельные оценщики в единый eval-harness. Он должен принимать датасет пар «вход–выход», параллельно запускать нужных оценщиков (с учётом лимитов по запросам) и агрегировать результаты. Мне также кажется полезным иметь утилитную функцию, которая выводит метрики в виде датафрейма из одной строки. Тогда результаты легко копировать и вставлять в Excel (который менеджеры продукта предпочитают для трекинга). С небольшой условной разметкой ячеек можно быстро видеть улучшения или регрессии.
Интегрируйте eval-harness в конвейер экспериментов. Когда он умеет напрямую «съедать» выход экспериментов, запуск экспериментов и оценок в масштабе становится простым. Мы можем подкрутить конфигурацию — шаблоны промптов, параметры retrieval, выбор модели и её параметры, — сгенерировать выход и сразу же его оценить. Такой плотный цикл обратной связи позволяет быстро итератироваться. Например, если мы хотим оценить миграцию модели с Claude Haiku 3.5 на Haiku 4.5, мы делаем одно изменение в конфиге, запускаем пайплайн, идём пообедать и потом смотрим результаты.
Сколько примеров нужно оценивать? Это зависит от того, какая статистическая уверенность нам нужна. Допустим, продуктовое требование — держать долю дефектов ниже 5%. Если мы запускаем эксперимент на 200 примерах и наблюдаем 3% дефектов, наш 95%-й доверительный интервал будет примерно 3% ± 2,4%. То есть получаем диапазон 0,6–5,4% по доле дефектов. А поскольку верхняя граница превышает 5%, мы не можем уверенно заключить, что текущая конфигурация удовлетворяет требованию для релиза.
Можно сузить оценку, увеличив число примеров. Если удвоить объём до 400 примеров, интервал сожмётся до 3% ± 1,7%. Теперь верхняя граница 4,7% уже ниже порога 5%. Примечание: поскольку стандартная ошибка убывает пропорционально квадратному корню из размера выборки, чтобы уменьшить погрешность вдвое, нужно увеличить выборку в четыре раза. Поэтому отдача от добавления новых примеров постепенно снижается. (См. также подробный разбор от Anthropic.)
• • •
Хочу закончить историей о том, как правильно применять оценки. Недавно я наблюдал, как команда вложила около 4 недель в построение своего eval-harness (обвязки для оценки). Это включало определение критериев оценки, сбор человеческой разметки, калибровку оценщиков и создание экспериментальной обвязки. Стейкхолдеры поначалу переживали, что это отвлекает от разработки самого продукта.
Но эффект проявился почти сразу. В следующие две недели команда прогнала десятки экспериментов с разными моделями, конфигурациями извлечения и шаблонами промптов — и довела продукт до рабочего состояния. А в следующие несколько месяцев они запустили ещё несколько сотен экспериментов, чтобы отполировать продукт, добавить новые функции и улучшить работу на граничных случаях. Это было бы невозможно, если бы после каждого изменения конфигурации они упирались в узкое место ручной разметки.
Вот в чём польза продуктовых оценок: не только в том, чтобы измерять и улучшать качество продукта, но и в том, чтобы «стягивать» цикл обратной связи и помогать итератироваться быстрее.
Если строите LLM-фичи как продукт, рано или поздно упрётесь в те же вопросы: как калибровать оценщиков, собирать фейлы и встроить eval-harness в пайплайн. На курсе «LLM Driven Development» разбираем полный цикл — от архитектуры и RAG до LLMOps: деплой, мониторинг, обновления и тестирование моделей на практических кейсах.
Чтобы узнать больше о формате обучения и познакомиться с преподавателями, приходите на бесплатные демо-уроки:
21 января 20:00. «Мониторинг: как понять, что твой сервис болен». Записаться
28 января 20:00. «Как GPT понимает язык и формулирует ответы». Записаться
29 января 20:00. «Какие ИИ-инструменты реально нужны Data Engineer». Записаться
Источник


