Метрики оценки ответов LLM: BLEU, ROUGE, BERTScore и семантическая близость — что выбрать
Как оценивать ответы LLM: BLEU, ROUGE, BERTScore и семантическая близость. Чем отличаются, где ошибаются, что выбрать для чат-бота и RAG. Код на Python.
Какие метрики использовать для оценки ответов LLM? Краткий ответ
Для коротких фактов (даты, числа, идентификаторы) достаточно Exact Match и F1. BLEU и ROUGE считают совпадение слов с эталоном и подходят для перевода и суммаризации, но не для свободных ответов чат-бота. Семантическая близость на эмбеддингах и BERTScore понимают перефраз, но пропускают подмену чисел. Для смысла, фактичности и RAG используют LLM-судью и метрики faithfulness. Лучше всего работает сочетание нескольких метрик, откалиброванное по оценкам людей.
Почему assertEquals не работает для LLM
В обычном тесте вы сравниваете фактический результат с ожидаемым. С LLM это ломается сразу: один и тот же смысл выражается десятками формулировок. «Вернуть товар можно в течение 14 дней» и «У вас есть две недели на возврат» — одинаковый ответ, но строки различаются полностью. И наоборот: «в течение 30 дней» отличается от эталона одним числом, но это уже неверный факт.
Поэтому для оценки текстов придумали метрики: числа, которые пытаются приблизить человеческое суждение. Их много, и они измеряют разное. Одни считают совпадение слов, другие сравнивают смысл, третьи проверяют, что ответ опирается на источник. Выбрать не ту метрику — значит получить красивый график, не связанный с реальным качеством.
Эта статья — карта метрик: что каждая делает, где ломается и как применять её в тестировании. Разбор ведём от самых простых к самым «умным».
Три семейства метрик
| Семейство | Что сравнивает | Примеры | Главная слабость |
|---|---|---|---|
| Лексические (по совпадению слов) | Пересечение слов и n-грамм с эталоном | BLEU, ROUGE, METEOR, Exact Match, token F1 | Не понимают смысл: перефраз получает низкий балл, а неверный факт с теми же словами — высокий |
| Семантические (по смыслу) | Близость смыслов через эмбеддинги | Косинусная близость эмбеддингов, BERTScore | Не различают противоположный смысл при похожих словах; порог подбирается вручную |
| На основе LLM и контекста | Оценка по рубрике или сверка с источником | LLM-судья, faithfulness, answer relevance | Дороже, недетерминированы, сами требуют проверки |
Лексические метрики
Exact Match и token-level F1
Самое простое: ответ совпал с эталоном после нормализации (регистр, пунктуация, артикли) или нет. Exact Match (EM) — это 0 или 1. Token F1 мягче: считает долю общих слов между ответом и эталоном (гармоническое среднее precision и recall по токенам). Эти метрики пришли из датасетов вопросов и ответов вроде SQuAD.
Где применять: короткие фактологические ответы с однозначным правильным значением — даты, числа, имена, идентификаторы. Где не применять: развёрнутые ответы, рассуждения, генерацию текста. Любое перефразирование обнуляет EM.
BLEU
BLEU появилась для оценки машинного перевода [1]. Идея: посмотреть, какая доля n-грамм (последовательностей из 1–4 слов) из ответа модели встречается в эталоне. Это метрика precision: она отвечает на вопрос «сколько из того, что сказала модель, есть в эталоне». Чтобы модель не выигрывала, выдавая один-два безопасных слова, добавлен штраф за краткость (brevity penalty).
Упрощённо BLEU = BP × exp(среднее логарифмов точностей по n-граммам). Значение лежит от 0 до 1 (иногда его пишут в процентах от 0 до 100). Если у ответа нет ни одной общей 4-граммы, оценка без сглаживания обнуляется, поэтому на коротких текстах обязательно нужно сглаживание (smoothing).
- ▸Хорошо: перевод, строго ограниченные форматы, где формулировка должна быть близка к эталону.
- ▸Плохо: свободные ответы чат-ботов, где правильных формулировок много. Точный перифраз получает низкий балл.
- ▸Ловушка: BLEU не видит фактических ошибок. Замена «14 дней» на «30 дней» почти не меняет оценку.
- ▸Ловушка: значение зависит от токенизации, сглаживания и числа эталонов, поэтому сравнивать цифры из разных источников нельзя.
ROUGE
ROUGE зеркальна BLEU: она про recall, то есть «какая доля эталона нашлась в ответе модели». Её придумали для оценки суммаризации [2], где важно, чтобы в кратком пересказе не потерялись ключевые мысли. Основные варианты:
- ▸ROUGE-1 — совпадение отдельных слов (униграмм).
- ▸ROUGE-2 — совпадение пар слов (биграмм), чувствительнее к порядку.
- ▸ROUGE-L — длиннейшая общая подпоследовательность (LCS): учитывает порядок слов, но допускает пропуски между ними.
Для каждого варианта считаются precision, recall и их гармоническое среднее F-measure. Обычно смотрят на F-measure. ROUGE хорошо подходит для суммаризации и извлечения ключевой информации, но наследует ту же главную слабость: она не понимает смысл.
METEOR
METEOR пытается исправить часть недостатков BLEU: учитывает точные совпадения, стеммы и синонимы, а также порядок слов. Он лучше коррелирует с человеческой оценкой на переводах, но зависит от языковых ресурсов (словари синонимов, стеммеры), а для русского языка и специфичных доменов покрытие хуже. На практике в LLM-тестировании его берут реже, чем BLEU и ROUGE.
Чем BLEU отличается от ROUGE?
Коротко: BLEU измеряет precision (какая доля слов и n-грамм ответа есть в эталоне), а ROUGE — recall (какая доля эталона нашлась в ответе). BLEU создавали для машинного перевода, ROUGE для суммаризации. Обе метрики не понимают смысл и не замечают фактических ошибок.
| Признак | BLEU | ROUGE |
|---|---|---|
| Что измеряет | Precision: сколько сказанного есть в эталоне | Recall: сколько эталона нашлось в ответе |
| Для чего создана | Машинный перевод | Суммаризация текстов |
| Основные варианты | BLEU-1…4 со штрафом за краткость (brevity penalty) | ROUGE-1, ROUGE-2, ROUGE-L (LCS) |
| Слабое место | Перефраз получает низкий балл, подмена числа почти не влияет | Те же проблемы: смысл не понимает, галлюцинации не видит |
Пример: BLEU и ROUGE на Python (и ловушка с русским языком)
Библиотеки nltk (BLEU) и rouge-score (ROUGE) считают метрики за несколько строк. Но есть важная деталь: стандартный токенизатор rouge-score оставляет только латиницу и цифры, поэтому русский текст он «съест», и вы получите нули или странные значения. Для русского языка передавайте собственный токенизатор и не включайте английский стеммер.
1import re
2from nltk.translate.bleu_score import SmoothingFunction, sentence_bleu
3from rouge_score import rouge_scorer
4
5reference = "Вернуть товар можно в течение 14 дней с момента получения."
6candidate = "Товар можно вернуть в течение 14 дней после получения."
7
8
9def tokenize(text: str) -> list[str]:
10 # \w+ учитывает кириллицу, в отличие от токенизатора rouge-score по умолчанию
11 return re.findall(r"\w+", text.lower())
12
13
14# BLEU: эталонов может быть несколько, поэтому список списков токенов
15bleu = sentence_bleu(
16 [tokenize(reference)],
17 tokenize(candidate),
18 smoothing_function=SmoothingFunction().method1, # без сглаживания на коротких текстах будет 0
19)
20
21
22class RuTokenizer:
23 def tokenize(self, text: str) -> list[str]:
24 return tokenize(text)
25
26
27scorer = rouge_scorer.RougeScorer(
28 ["rouge1", "rouge2", "rougeL"],
29 use_stemmer=False, # стеммер в rouge-score только английский
30 tokenizer=RuTokenizer(),
31)
32rouge = scorer.score(reference, candidate) # порядок: (эталон, ответ модели)
33
34print(f"BLEU: {bleu:.3f}")
35for name, s in rouge.items():
36 print(f"{name}: P={s.precision:.3f} R={s.recall:.3f} F={s.fmeasure:.3f}")Теперь проверьте на этом же эталоне ответ с ошибкой: «в течение 30 дней с момента получения». Лексические метрики поставят ему оценку почти такую же, как правильному перифразу, а иногда и выше, потому что общих слов у него больше. Это не баг библиотеки, а свойство метрики.
Семантические метрики
Косинусная близость эмбеддингов
Идея: превратить каждый текст в вектор (эмбеддинг) так, чтобы близкие по смыслу тексты лежали рядом, и измерить угол между векторами. Косинусная близость принимает значения от -1 до 1, где чем больше, тем ближе по смыслу. Модели вроде sentence-transformers делают это быстро и локально, без обращения к внешним API.
1from sentence_transformers import SentenceTransformer, util
2
3# Мультиязычная модель: подходит и для русского языка
4model = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")
5
6
7def semantic_similarity(a: str, b: str) -> float:
8 emb = model.encode([a, b], convert_to_tensor=True)
9 return util.cos_sim(emb[0], emb[1]).item()
10
11
12ref = "Вернуть товар можно в течение 14 дней с момента получения."
13print(semantic_similarity(ref, "У вас есть две недели после получения, чтобы оформить возврат."))
14print(semantic_similarity(ref, "Вернуть товар можно в течение 30 дней с момента получения."))Сравните два числа. Эмбеддинги улавливают перефраз, и это их сильная сторона, но они же часто слабо реагируют на изменение числа, отрицание («можно» и «нельзя») или подмену сущности. Такие тексты остаются «похожими по теме», хотя фактически противоречат друг другу. Поэтому семантическая близость отвечает на вопрос «про то же ли говорится», а не «то же ли утверждается».
- ▸Порог («считаем ответ правильным, если близость выше 0.8») нужно подбирать на своих данных: для разных моделей эмбеддингов шкала разная.
- ▸Метрика симметрична и плохо отличает противоречие от согласия: добавляйте проверки на числа, даты и отрицания.
- ▸Качество зависит от модели эмбеддингов и её поддержки вашего языка и домена.
BERTScore
BERTScore [3] идёт на шаг дальше простого сравнения векторов целых предложений. Он получает контекстные эмбеддинги отдельных токенов, для каждого токена ответа ищет самый похожий токен эталона (и наоборот) и усредняет эти близости. Получаются precision, recall и F1 уже на уровне смысла, а не поверхностных слов. Для русского языка достаточно указать lang в вызове, а для более точных результатов можно выбрать мультиязычную модель явно.
1from bert_score import score
2
3cands = ["Товар можно вернуть в течение 14 дней после получения."]
4refs = ["Вернуть товар можно в течение 14 дней с момента получения."]
5
6# lang="ru" подберёт мультиязычную модель по умолчанию
7P, R, F1 = score(cands, refs, lang="ru")
8print(f"BERTScore F1: {F1.mean().item():.3f}")BERTScore заметно ближе к человеческой оценке, чем BLEU и ROUGE, на перефразах, но у него те же проблемы с фактами: разные числа и отрицания он различает слабо. Кроме того, его значения сложно интерпретировать в абсолюте: часто их пересчитывают относительно базовой линии (baseline rescaling), чтобы шкала стала осмысленной.
Метрики для RAG: оцениваем не только ответ
Если ваш бот отвечает на основе найденных документов (RAG), одной оценки итогового текста мало. Ошибка может быть на любом этапе, поэтому метрики делят по компонентам. Названия чаще всего берут из фреймворков вроде RAGAS [4], но идеи универсальны:
| Метрика | Что проверяет | Что показывает при падении |
|---|---|---|
| Faithfulness (groundedness) | Каждое утверждение в ответе подтверждается найденным контекстом | Модель выдумывает факты (галлюцинации) |
| Answer relevance | Ответ действительно отвечает на заданный вопрос | Ответ по теме, но мимо вопроса |
| Context precision | Релевантные фрагменты стоят в выдаче выше нерелевантных | Поиск приносит много мусора, полезное тонет |
| Context recall | Найдено ли всё, что нужно для правильного ответа | Поиск пропускает нужные документы |
Полезное следствие: если faithfulness высокая, а итоговый ответ неверный, проблема, скорее всего, в поиске (context recall), а не в генерации. Раздельные метрики превращают «стало хуже» в конкретный адрес поломки. Большинство этих метрик считают с помощью LLM-судьи, поэтому судью стоит откалибровать по ручной разметке. Как встроить судью в тестирование, собрать golden dataset и корзины, разбираем в статье про LLM-as-a-Judge.
Что не стоит путать: perplexity
Perplexity (перплексия) показывает, насколько модель «удивлена» текстом: чем ниже, тем увереннее модель предсказывает следующие слова. Это метрика качества языковой модели на корпусе, но не качества ответов приложения. Низкая перплексия не означает, что ответ верен, полезен или безопасен. Для тестирования продуктов на LLM её почти не используют.
Как выбрать метрику: по типу задачи
| Задача | Что использовать | Чего не хватает |
|---|---|---|
| Короткие факты, числа, идентификаторы | Exact Match, token F1, проверка по regex | Ничего: детерминированные проверки надёжнее всего |
| Структурированный вывод (JSON, SQL, код) | Валидация схемы, выполнение, сравнение результата | Лексические метрики тут вводят в заблуждение |
| Суммаризация | ROUGE + семантическая близость + LLM-судья на полноту и фактичность | ROUGE не ловит галлюцинации в пересказе |
| Перевод | BLEU/METEOR как базовая линия + BERTScore | Оценка идиом и тона требует людей или судьи |
| Ответы чат-бота, свободный текст | LLM-судья по рубрике + семантическая близость как дешёвый фильтр | BLEU и ROUGE здесь практически бесполезны |
| RAG | Faithfulness, answer relevance, context precision и recall | Нужен размеченный набор вопросов с эталонными источниками |
Правило: дешёвое перед дорогим
Метрики удобно выстраивать в конвейер от быстрых и надёжных проверок к медленным и дорогим. Каждая следующая ступень запускается только на том, что прошло предыдущую:
- 1. Детерминированные проверки
Формат, JSON-схема, длина, запрещённые слова, наличие обязательных чисел и ссылок. Мгновенно и без шума
- 2. Дешёвые метрики близости
Exact Match, token F1, семантическая близость. Отсеивают явный брак и дубли на больших объёмах
- 3. LLM-судья по рубрике
Фактичность, полнота, тон. Дороже и медленнее, но понимает смысл и контекст
- 4. Люди на выборке
Периодическая ручная проверка, на которой калибруются все остальные ступени
Самое важное: проверьте, что метрика измеряет то, что вы думаете
Любая метрика — это прокси, приближение к настоящему качеству. Перед тем как класть её в CI и принимать по ней решения, убедитесь, что она согласована с человеческой оценкой на ваших данных.
- ▸Соберите 50–200 примеров (вопрос, эталон, ответ) и попросите людей поставить оценки.
- ▸Посчитайте метрику на тех же примерах и найдите ранговую корреляцию (Spearman или Kendall) с человеческими оценками. Метрика, которая не коррелирует с людьми, бесполезна независимо от того, насколько она популярна.
- ▸Проверьте на «ловушках»: ответ с подменённым числом, с отрицанием, с выдуманным фактом должен получать низкую оценку. Если не получает, метрика слепа к этой ошибке.
- ▸Смотрите на доверительные интервалы (например, через bootstrap): различие в 0.02 между двумя версиями промпта на 30 примерах — это, скорее всего, шум.
1import pytest
2from scipy.stats import spearmanr
3
4# Каждый пример: {"reference", "answer", "human_score"} — оценка людей от 1 до 5
5def test_metric_correlates_with_humans(examples, metric):
6 machine = [metric(e["reference"], e["answer"]) for e in examples]
7 human = [e["human_score"] for e in examples]
8 rho, _ = spearmanr(machine, human)
9 assert rho >= 0.6, f"метрика слабо согласуется с людьми: rho={rho:.2f}"
10
11
12# Порог приёмки, подобранный по вашим данным: балл ниже — ответ считается браком
13ACCEPT = 0.85
14
15# (эталон, ответ с подменённым фактом, верный перефраз)
16TRAPS = [
17 ("Возврат возможен в течение 14 дней.",
18 "Возврат возможен в течение 30 дней.",
19 "Оформить возврат можно в течение двух недель."),
20 ("Доставка бесплатна от 3000 рублей.",
21 "Доставка не бывает бесплатной.",
22 "Если заказ от трёх тысяч рублей, доставим бесплатно."),
23]
24
25
26@pytest.mark.parametrize("reference,wrong,paraphrase", TRAPS)
27def test_metric_separates_wrong_fact_from_paraphrase(reference, wrong, paraphrase, metric):
28 assert metric(reference, paraphrase) >= ACCEPT, "метрика бракует верный перефраз"
29 assert metric(reference, wrong) < ACCEPT, "метрика пропускает подмену факта"Если этот тест падает, метрика не подходит для вашей задачи, и это нормальный результат проверки. BLEU и ROUGE, как правило, не пройдут первую проверку (перифраз получит низкий балл), а эмбеддинги нередко не пройдут вторую (подмена факта останется «похожей»). Именно поэтому ловушки нужно прогонять на своей метрике и своих данных, а не полагаться на репутацию метрики.
Частые ошибки
- Оценивать чат-бота по BLEU и ROUGE
Эти метрики созданы для перевода и суммаризации. На свободных ответах они почти не коррелируют с качеством
- Использовать эталон-строку как единственно верный ответ
Правильных формулировок много. Хотя бы несколько эталонов или оценка по смыслу
- Сравнивать цифры BLEU из разных источников
Токенизация, сглаживание и число эталонов меняют значение. Сравнимы только замеры одним и тем же кодом
- Молча считать ROUGE для русского языка с настройками по умолчанию
Токенизатор по умолчанию отбрасывает кириллицу, и метрика теряет смысл
- Доверять высокой семантической близости как признаку правильности
Она не различает подмену числа, отрицание и противоположный смысл в похожих формулировках
- Оптимизировать метрику вместо качества
Когда цифра становится целью, модель или промпт подгоняют под неё (закон Гудхарта). Периодически сверяйтесь с людьми
Итоги
Нет одной «правильной» метрики для LLM. Лексические метрики (BLEU, ROUGE) дёшевы и объяснимы, но слепы к смыслу и подходят для перевода и суммаризации. Семантические (эмбеддинги, BERTScore) понимают перифраз, но пропускают подмену фактов. Метрики для RAG и LLM-судья ближе всего к человеческому суждению, но сами требуют проверки. Рабочая стратегия для QA-инженера: детерминированные проверки там, где ответ можно проверить точно; дешёвые метрики близости для фильтрации; судья по рубрике для смысла; и регулярная сверка с людьми, на которой держится доверие ко всей системе.
Начните с малого: возьмите 50 своих реальных ответов, посчитайте на них две-три метрики и сверьте с ручной оценкой. Уже эта простая проверка покажет, какая метрика для вашей задачи полезна, а какая только рисует красивые графики.
FAQ
Подходят ли BLEU и ROUGE для оценки чат-ботов?
Как правило, нет. Эти метрики сравнивают слова с эталоном, а у чат-бота правильных формулировок много: точный перефраз получит низкий балл, а ответ с неверным числом при совпадающих словах — высокий. Они подходят для перевода и суммаризации, а для чат-ботов лучше использовать LLM-судью и метрики фактичности.
Что такое BERTScore и чем он лучше BLEU?
BERTScore сравнивает не слова, а контекстные эмбеддинги токенов: для каждого токена ответа ищется самый похожий токен эталона. Поэтому он понимает перефраз и обычно ближе к человеческой оценке, чем BLEU. Но, как и косинусная близость, он слабо различает подмену числа или отрицание.
Как считать ROUGE для русского языка?
Токенизатор rouge-score по умолчанию оставляет только латиницу и цифры и «съедает» кириллицу. Передайте собственный токенизатор (например, по регулярному выражению \w+) и отключите английский стеммер. Пример кода есть выше в статье.
Какие метрики нужны для оценки RAG?
Оценивают и ответ, и поиск: faithfulness (ответ подтверждается контекстом), answer relevance (ответ по вопросу), context precision и context recall (качество поиска документов). Они показывают, где сломалось: в генерации или в поиске.
Как проверить, что метрике можно доверять?
Разметьте людьми 50–200 примеров и посчитайте ранговую корреляцию (Spearman или Kendall) метрики с оценками людей. Затем проверьте «ловушки»: ответ с подменённым числом или отрицанием должен получать низкий балл. Метрика, не согласующаяся с людьми на ваших данных, бесполезна независимо от популярности.
Источники
- Bleu: a Method for Automatic Evaluation of Machine Translation — Papineni K., Roukos S., Ward T., Zhu W.-J., ACL 2002
- ROUGE: A Package for Automatic Evaluation of Summaries — Lin C.-Y., Text Summarization Branches Out, 2004
- BERTScore: Evaluating Text Generation with BERT — Zhang T., Kishore V., Wu F., Weinberger K. Q., Artzi Y., ICLR 2020 (arXiv:1904.09675)
- Ragas: Automated Evaluation of Retrieval Augmented Generation — Es S., James J., Espinosa-Anke L., Schockaert S., arXiv:2309.15217, 2023