Привет! Вчера вышла отличная статья от Себастьяна Рашки, которая детально разбирает основные способы оценки LLM-моделей. Глобально их можно разделить на 4 категории: оценка по бенчмаркам, использование верификаторов, лидерборды и LLM-as-a-judge.

Для каждого метода есть описание и код реализации с нуля, которые отлично показывают, что под капотом у каждого из методов оценки. И такой материал заслуживает того, чтобы быть на русском языке, поэтому я сделал качественный перевод, включая ключевые картиночки. Объёмные блоки кода скрыты за спойлерами, основные схемы переведены — если вы слышали про оценку (ее еще называют evals), то всё очень доходчиво разложено.

Важное уточнение: статья позиционирует себя как «создание с нуля» (from scratch), и для этой цели она отлично подходит. Однако, будучи глубоко погружённым в эту тему, я посчитал многие моменты достаточно базовыми. Поэтому финальные выводы с radar-диаграммой и таблицей плюсов-минусов я вынес в самое начало — это отличный способ быстро освежить знания и систематизировать понимание для тех, кто уже глубоко в теме. И продублирую идею о том, что в реальной жизни под конкретную задачу стоит создавать свой бенчмарк и замеряться уже на нем.

В остальном — из песни слов не выкинешь, всё переведено как в оригинале, и это действительно отличный материал. Дальше будет именно он.


Как мы на самом деле оцениваем LLM?

Это простой вопрос, но он всегда приводит к оживленным дискуссиям.

Когда я консультирую проекты, один из главных вопросов, который мне задают чаще всего — как выбирать между различными моделями и как интерпретировать результаты оценки, которые есть в открытом доступе. (И, конечно же, как измерять прогресс при файнтюнинге или разработке собственных моделей)

Поскольку этот вопрос возникает так часто, я подумал, что было бы полезно поделиться кратким обзором основных методов оценки, которые люди используют для сравнения LLM. Разумеется, оценка LLM — это очень обширная тема, которую невозможно исчерпывающе охватить в одном материале, но я считаю, что наличие ясной ментальной карты этих основных подходов значительно облегчает интерпретацию бенчмарков, лидербордов и научных статей.

Давайте обсудим четыре основных метода оценки LLM вместе с их реализацией кода с нуля, чтобы лучше понять их преимущества и недостатки.

Понимание основных методов оценки для LLM

На практике существует четыре распространённых способа оценки обученных LLM: бенчмарки с множественным выбором, верификаторы, лидерборды и LLM-судьи, как показано на Рисунке 1 ниже. Научные статьи, новости и технические отчёты (model cards) часто включают результаты именно из этих категорий.

Рисунок 1: Обзор 4 различных подходов к оценке, рассматриваемых в этой статье
Рисунок 1: Обзор 4 различных подходов к оценке, рассматриваемых в этой статье

Кроме того, четыре категории, представленные здесь, делятся на две группы: оценка на основе бенчмарков и оценка на основе суждений, как показано на рисунке выше.

(Существуют также другие метрики, такие как loss обучения, perplexity и reward, но они обычно используются внутренне в процессе разработки модели.)

В следующих подразделах приводятся краткие обзоры и примеры каждого из четырёх методов.

Плюсы и минусы каждого метода

При этом вы, вероятно, задаётесь вопросом: «Как же лучше оценивать LLM?».

К сожалению, не существует единственного лучшего метода, поскольку каждый имеет различные компромиссы. Вкратце:

Бенчмарки:

  • (+) Относительно быстрый и дешёвый для запуска в масштабе

  • (+) Стандартизированный и воспроизводимый в разных статьях

  • (-) Измеряет лишь базовое воспроизведение знаний (прим. пер. если не специализированный)

  • (-) Не отражает то, как LLM используются в реальном мире

Верификаторы:

  • (+) Стандартизированная, объективная оценка для областей с ground truth

  • (+) Позволяют давать ответы в свободной форме (с некоторыми ограничениями на форматирование финального ответа)

  • (+) Могут также оценивать промежуточные шаги при использовании процессных верификаторов или моделей процессных вознаграждений

  • (-) Требуют верифицируемых областей (например, математика или код), построение хороших верификаторов может быть сложным

  • (-) Верификаторы только результата оценивают лишь финальный ответ, а не качество рассуждений

Лидерборды в стиле LLM Arena (попарные предпочтения людей):

  • (+) Напрямую отвечают на вопрос «Какую модель люди предпочитают?» на реальных промптах

  • (+) Позволяют давать ответы в свободной форме и неявно учитывают стиль, полезность и безопасность

  • (-) Дорого и времязатратно для людей

  • (-) Не измеряет корректность, по большей части только предпочтение

  • (-) Не всегда стабильны

LLM-судья (LLM-as-a-judge):

  • (+) Масштабируемо для многих задач

  • (+) Позволяет давать ответы в свободной форме

  • (-) Зависит от возможностей судьи (ансамбли могут сделать это более робастно)

  • (-) Зависит от выбора рубрики

Хотя обычно я не большой поклонник лепестковых диаграмм (radar plots), одна из них может быть здесь полезна для визуализации этих различных областей оценки, как показано ниже.

Рисунок 15: Лепестковая диаграмма, концептуально показывающая, что в идеале мы хотим обращать внимание на различные области при оценке LLM, чтобы определить её сильные и слабые стороны.
Рисунок 15: Лепестковая диаграмма, концептуально показывающая, что в идеале мы хотим обращать внимание на различные области при оценке LLM, чтобы определить её сильные и слабые стороны

Например, высокий рейтинг по бенчмаркам предполагает, что модель обладает солидными общими знаниями. Объедините это с высокой оценкой верификатора, и модель, вероятно, также правильно отвечает на технические вопросы. Однако если модель показывает слабые результаты в оценках LLM-в-качестве-судьи и лидербордах, она может испытывать трудности с эффективным написанием или формулированием ответов и могла бы получить пользу от RLHF.

Таким образом, лучшая оценка объединяет несколько областей. Но в идеале она также использует данные, которые напрямую соответствуют вашим целям или бизнес-задачам.

Например, предположим, вы внедряете LLM для помощи в юридических или правовых задачах. Имеет смысл запустить модель на стандартных бенчмарках, таких как MMLU, в качестве быстрой проверки здравомыслия, но в конечном итоге вам захочется адаптировать оценки к вашей целевой области, такой как право. Вы можете найти публичные бенчмарки онлайн, которые служат хорошими отправными точками, но в конце концов вам захочется протестировать на ваших собственных проприетарных данных. Только тогда вы можете быть достаточно уверены, что модель ещё не видела тестовые данные во время обучения.

Итак, переходим к самим методам.

Метод 1: Оценка точности выбора ответа

Мы начинаем с метода, основанного на бенчмарках: ответы на вопросы с множественным выбором.

Исторически одним из наиболее широко используемых методов оценки являются бенчмарки с множественным выбором, такие как MMLU (сокращение от Massive Multitask Language Understanding). Чтобы проиллюстрировать этот подход, на рисунке 2 показана репрезентативная задача из датасета MMLU.

Рисунок 2: Оценка LLM на MMLU путём сравнения её предсказания множественного выбора с правильным ответом из датасета
Рисунок 2: Оценка LLM на MMLU путём сравнения её предсказания множественного выбора с правильным ответом из датасета

Рисунок 2 показывает лишь один пример из датасета MMLU. Полный датасет MMLU состоит из 57 предметов (от математики старших классов до биологии) с общим количеством около 16 тысяч вопросов с множественным выбором, и производительность измеряется в терминах точности (доли правильных ответов на вопросы), например 87,5%, если 14 000 из 16 000 вопросов даны правильно.

Бенчмарки со множественным выбором, такие как MMLU, тестируют способность LLM к воспроизведению знаний прямолинейным, измеримым способом, аналогично стандартизированным тестам, многим школьным экзаменам или теоретическим экзаменам по вождению.

Обратите внимание, что рисунок 2 показывает упрощённую версию оценки с множественным выбором, где предсказанная моделью буква ответа сравнивается напрямую с правильной. Существуют два других популярных метода, которые включают оценку по логарифмической вероятности (log-probability scoring). Я реализовал их на GitHub.

Следующие подразделы иллюстрируют, как можно реализовать в коде оценку MMLU, показанную на рисунке 2.

1.2 Загрузка модели

Во-первых, прежде чем мы сможем оценить её на MMLU, нам необходимо загрузить предобученную модель. Здесь мы будем использовать реализацию Qwen3 0.6B с нуля на чистом PyTorch, которая требует всего около 1,5 ГБ оперативной памяти.

Обратите внимание, что детали реализации модели Qwen3 здесь не важны; мы просто рассматриваем её как LLM, которую хотим оценить. Вместо того чтобы копировать и вставлять множество строк исходного кода Qwen3, мы импортируем его из моей Python-библиотеки reasoning_from_scratch, которую можно установить через

pip install reasoning_from_scratch

или

uv add reasoning_from_scratch

Блок кода 1: Загрузка предобученной модели

Код загрузки предобученной модели
from pathlib import Path
import torch
from reasoning_from_scratch.ch02 import get_device
from reasoning_from_scratch.qwen3 import (
    download_qwen3_small, Qwen3Tokenizer,
    Qwen3Model, QWEN_CONFIG_06_B
)

device = get_device()

# Set matmul precision to "high" to 
# enable Tensor Cores on compatible GPUs
torch.set_float32_matmul_precision("high")

# Uncomment the following line 
# if you encounter device compatibility issues
# device = "cpu"

# Use the base model by default
WHICH_MODEL = "base"

if WHICH_MODEL == "base":
    download_qwen3_small(
        kind="base", tokenizer_only=False, out_dir="qwen3"
    )
    tokenizer_path = Path("qwen3") / "tokenizer-base.json"
    model_path = Path("qwen3") / "qwen3-0.6B-base.pth"
    tokenizer = Qwen3Tokenizer(tokenizer_file_path=tokenizer_path)

elif WHICH_MODEL == "reasoning":
    download_qwen3_small(
        kind="reasoning", tokenizer_only=False, out_dir="qwen3"
    )
    tokenizer_path = Path("qwen3") / "tokenizer-reasoning.json"
    model_path = Path("qwen3") / "qwen3-0.6B-reasoning.pth"
    tokenizer = Qwen3Tokenizer(
        tokenizer_file_path=tokenizer_path,
        apply_chat_template=True,
        add_generation_prompt=True,
        add_thinking=True,
    )

else:
    raise ValueError(f"Invalid choice: WHICH_MODEL={WHICH_MODEL}")

model = Qwen3Model(QWEN_CONFIG_06_B)
model.load_state_dict(torch.load(model_path))
model.to(device)

# Optionally enable model compilation for potential performance gains
USE_COMPILE = False
if USE_COMPILE:
    torch._dynamo.config.allow_unspec_int_on_nn_module = True
    model = torch.compile(model)

1.3 Проверка варианта сгенерированного ответа

В этом разделе мы реализуем простейший и, возможно, наиболее интуитивный метод оценки MMLU, который основан на проверке того, соответствует ли сгенерированная буква ответа с множественным выбором правильному ответу. Это аналогично тому, что было проиллюстрировано ранее на Рисунке 2, который для удобства показан ниже снова.

Рисунок 3: Оценка LLM на MMLU путём сравнения её предсказания множественного выбора с правильным ответом из датасета
Рисунок 3: Оценка LLM на MMLU путём сравнения её предсказания множественного выбора с правильным ответом из датасета

Для этого мы будем работать с примером из датасета MMLU:

example = {
    "question": (
        "Сколькими способами можно разместить 4"
        " различимых шара в 2 неразличимых коробки?"
    ),
    “choices”: ["7", "11", "16", "8"],
    “answer”: "D",
}

Далее мы определяем функцию для форматирования промптов для LLM.

def format_prompt(example):
    return (
        f"{example['question']}\n"
        f"A. {example['choices'][0]}\n"
        f"B. {example['choices'][1]}\n"
        f"C. {example['choices'][2]}\n"
        f"D. {example['choices'][3]}\n"
        "Ответ: "
    )
# Завершающий пробел в "Ответ: " стимулирует генерацию однобуквенного следующего токена

Давайте выполним функцию на примере MMLU, чтобы получить представление о том, как выглядит отформатированный входной промпт для LLM:

prompt = format_prompt(example)
print(prompt)

Вывод:

Сколькими способами можно разместить 4 различимых шара в 2 неразличимых коробки?

Сколькими способами можно разместить 4 различимых шара в 2 неразличимых коробки?
A. 7
B. 11
C. 16
D. 8
Ответ:

Промпт модели, как показано выше, предоставляет модели список различных вариантов ответа и заканчивается текстом «Ответ:», который побуждает модель сгенерировать правильный ответ.

Хотя это не является строго обязательным, иногда также может быть полезно предоставить дополнительные вопросы вместе с правильными ответами в качестве входных данных, чтобы модель могла наблюдать, как ожидается решение задачи. (Например, случаи, когда предоставляется 5 примеров, также известны как 5-shot MMLU). Однако для современных поколений LLM, где даже базовые модели достаточно умные, это не требуется.

Загрузка различных примеров MMLU

Вы можете загрузить примеры из датасета MMLU напрямую через библиотеку datasets (которую можно установить через pip install datasets или uv add datasets):

from datasets import load_dataset

configs = get_dataset_config_names("cais/mmlu")
dataset = load_dataset("cais/mmlu", "high_school_mathematics")
# Inspect the first example from the test set:
example = dataset["test"][0]
print(example)

Выше мы использовали подмножество "high_school_mathematics"; чтобы получить список других подмножеств, используйте следующий код:

from datasets import get_dataset_config_names

subsets = get_dataset_config_names(”cais/mmlu”)
print(subsets)

Далее мы токенизируем промпт и оборачиваем его в объект PyTorch-тензора в качестве входных данных для LLM:

prompt_ids = tokenizer.encode(prompt)
prompt_fmt = torch.tensor(prompt_ids, device=device)
# Add batch dimension:
prompt_fmt = prompt_fmt.unsqueeze(0)

Затем, разобравшись со всей этой подготовкой, мы определяем основную функцию оценки ниже, которая генерирует несколько токенов (здесь 8 токенов по умолчанию) и извлекает первое вхождение буквы A/B/C/D, которую выводит модель.

Блок кода 3: Извлечение сгенерированной буквы

Извлечение сгенерированной буквы
from reasoning_from_scratch.ch02_ex import (
    generate_text_basic_stream_cache
)

def predict_choice(
    model, tokenizer, prompt_fmt, max_new_tokens=8
):
    pred = None
    for t in generate_text_basic_stream_cache(
        model=model,
        token_ids=prompt_fmt,
        max_new_tokens=max_new_tokens,
        eos_token_id=tokenizer.eos_token_id,
    ):
        answer = tokenizer.decode(t.squeeze(0).tolist())
        for letter in answer:
            letter = letter.upper()
            # stop as soon as a letter appears
            if letter in "ABCD":
                pred = letter
                break
        if pred:
            break
    return pred

Затем мы можем проверить сгенерированную букву, используя функцию из блока кода выше следующим образом:

pred1 = predict_choice(model, tokenizer, prompt_fmt)
print(
    f"Generated letter: {pred1}\n"
    f"Correct? {pred1 == example['answer']}"
)

Результат:

Сгенери: C
Correct? False

Как мы видим, сгенерированный ответ в данном случае неверен (False).

Это был всего лишь один из 270 примеров из подмножества high_school_mathematics в MMLU. Скриншот (Рисунок 4) ниже показывает производительность базовой модели и варианта с рассуждениями (reasoning variant) при выполнении на полном подмножестве. Код для этого доступен на GitHub.

Рисунок 4: Производительность базовой модели и модели с рассуждениями на подмножестве MMLU high_school_mathematics
Рисунок 4: Производительность базовой модели и модели с рассуждениями на подмножестве MMLU high_school_mathematics

Предполагая, что вопросы имеют равную вероятность ответа, случайный угадыватель (с равномерной вероятностью выбирающий A, B, C или D) ожидаемо достигнет 25% точности. Таким образом, как базовая модель, так и модель с рассуждениями работают не очень хорошо.

Форматы ответов с множественным выбором

Обратите внимание, что в этом разделе реализована упрощённая версия оценки с множественным выбором для целей иллюстрации, где предсказанная моделью буква ответа сравнивается напрямую с правильной. На практике существуют более широко используемые вариации, такие как оценка по логарифмической вероятности (log-probability scoring), где мы измеряем, насколько вероятным модель считает каждый вариант ответа, а не просто проверяем финальный выбор буквы. (Мы обсуждаем оценку на основе вероятностей в главе 4.) Для моделей с рассуждениями оценка также может включать в себя определение вероятности генерации правильного ответа, когда он предоставляется в качестве входных данных.

Рисунок 5: Другие методы оценки MMLU описаны и представлены на GitHub
Рисунок 5: Другие методы оценки MMLU описаны и представлены на GitHub

Однако независимо от того, какой вариант оценки MMLU мы используем, оценка всё равно сводится к проверке того, выбирает ли модель один из предопределённых вариантов ответа.

Ограничение бенчмарков с множественным выбором, таких как MMLU, заключается в том, что они измеряют только способность LLM выбирать из предопределённых вариантов и, таким образом, не очень полезны для оценки способностей к рассуждению, кроме проверки того, забыла ли модель знания и в каком объёме по сравнению с базовой моделью. Они не отражают способность к свободной форме написания текста или практическую полезность в реальном мире.

Тем не менее, бенчмарки со множественным выбором остаются простыми и полезными диагностическими инструментами: например, высокая оценка MMLU не обязательно означает, что модель сильна в практическом использовании, но низкая оценка может выявить потенциальные пробелы в знаниях.

Метод 2: Использование верификаторов для проверки ответов

В связи с ответами на вопросы со множественным выбором, которые обсуждались в предыдущем разделе, подходы на основе верификации количественно оценивают возможности LLM через метрику точности. Однако здесь методы верификации позволяют LLM давать ответ в свободной форме. Затем мы извлекаем соответствующую часть ответа и используем так называемый верификатор для сравнения этой части ответа с правильным ответом, предоставленным в датасете, как показано на Рисунке 6 ниже.

Рисунок 6: Оценка LLM методом верификации при ответе на вопросы в свободной форме. Модель генерирует ответ в свободной форме (который может включать несколько шагов) и финальный ответ в рамке, который извлекается и сравнивается с правильным ответом из датасета
Рисунок 6: Оценка LLM методом верификации при ответе на вопросы в свободной форме. Модель генерирует ответ в свободной форме (который может включать несколько шагов) и финальный ответ в рамке, который извлекается и сравнивается с правильным ответом из датасета

Для сравнения извлеченного ответа с эталонным, как показано на рисунке выше, можно применять внешние инструменты — интерпретаторы кода, калькуляторы и подобное программное обеспечение.

Недостаток заключается в том, что этот метод может применяться только к областям, которые можно легко (и в идеале детерминированно) верифицировать, таким как математика и код. Кроме того, этот подход может вносить дополнительную сложность и зависимости, и он может переносить часть нагрузки по оценке с самой модели на внешний инструмент.

Однако, поскольку он позволяет нам программно генерировать неограниченное количество вариаций математических задач и получает преимущества от пошагового рассуждения, он стал краеугольным камнем оценки и разработки моделей с рассуждениями.

Здесь я пропускаю реализацию кода, так как написал ее в своей книге, тем не менее, вы можете найти пошаговый код на GitHub.

Рисунок 7: Фрагмент подхода к оценке на основе верификации
Рисунок 7: Фрагмент подхода к оценке на основе верификации

Метод 3: Сравнение моделей с использованием предпочтений и лидербордов

До сих пор мы рассмотрели два метода, которые предлагают легко измеримые метрики, такие как точность модели. Однако ни один из вышеупомянутых методов не оценивает LLM более целостным образом, включая оценку стиля ответов. В этом разделе, как проиллюстрировано на Рисунке 8 ниже, мы обсуждаем метод, основанный на суждениях, а именно лидерборды LLM.

Рисунок 8: Ментальная модель тем, рассматриваемых в этой книге, с акцентом на методы оценки, основанные на суждениях и бенчмарках, рассмотренные в этом приложении. Уже рассмотрев подходы, основанные на бенчмарках (множественный выбор, верификаторы) в предыдущем разделе, мы теперь представляем подходы, основанные на суждениях, для измерения производительности LLM, причём данный подраздел фокусируется на лидербордах
Рисунок 8: Ментальная модель тем, рассматриваемых в этой книге, с акцентом на методы оценки, основанные на суждениях и бенчмарках, рассмотренные в этом приложении. Уже рассмотрев подходы, основанные на бенчмарках (множественный выбор, верификаторы) в предыдущем разделе, мы теперь представляем подходы, основанные на суждениях, для измерения производительности LLM, причём данный подраздел фокусируется на лидербордах

Метод лидерборда, описанный здесь, представляет собой подход, основанный на суждениях, где модели ранжируются не по значениям точности или другим фиксированным оценкам бенчмарков, а по предпочтениям пользователей (или других LLM) относительно их выходных данных.

Популярным лидербордом является LM Arena (ранее Chatbot Arena) [прим.пер. в РФ есть своя LLM Arena], где пользователи сравнивают ответы от двух выбранных пользователем или анонимных моделей и голосуют за ту, которую они предпочитают, как показано на Рисунке 9.

Рисунок 9: Пример интерфейса лидерборда, основанного на суждениях (RU: LLM Arena). Двум LLM даётся одинаковый промпт, их ответы показываются рядом друг с другом, и пользователи голосуют за предпочитаемый ответ.
Рисунок 9: Пример интерфейса лидерборда, основанного на суждениях (RU: LLM Arena). Двум LLM даётся одинаковый промпт, их ответы показываются рядом друг с другом, и пользователи голосуют за предпочитаемый ответ.

Эти голоса предпочтений затем агрегируются по всем пользователям в лидерборд, который ранжирует различные модели по предпочтениям пользователей. Текущий снимок лидерборда LLM Arena (по состоянию на 6 октября 2025 года) показан ниже на Рисунке 10.

Рисунок 10: Скриншот лидерборда LLM Arena, который показывает текущие лидирующие LLM на основе предпочтений пользователей в текстовых задачах
Рисунок 10: Скриншот лидерборда LLM Arena, который показывает текущие лидирующие LLM на основе предпочтений пользователей в текстовых задачах

В оставшейся части этого раздела мы реализуем простой пример лидерборда.

Для создания конкретного примера рассмотрим пользователей, которые запрашивают различные LLM в настройке, аналогичной Рисунку 9. Список ниже представляет собой попарные голоса, где первая модель является победителем:

votes = [
    ("GPT-5", "Claude-3"),
    ("GPT-5", "Llama-4"),
    ("Claude-3", "Llama-3"),
    ("Llama-4", "Llama-3"),
    ("Claude-3", "Llama-3"),
    ("GPT-5", "Llama-3"),
]

В списке выше каждый кортеж в списке votes представляет собой попарное предпочтение между двумя моделями, записанное как (победитель, проигравший). Таким образом, ("GPT-5", "Claude-3") означает, что пользователь предпочёл ответ GPT-5 ответу модели Claude-3.

В оставшейся части этого раздела мы преобразуем список votes в лидерборд. Для этого мы будем использовать популярную систему рейтинга Эло (Elo rating system), которая изначально была разработана для ранжирования шахматистов.

Прежде чем мы рассмотрим конкретную реализацию кода, вкратце, это работает следующим образом. Каждая модель начинает с базовой оценки. Затем, после каждого сравнения и голосования предпочтений, рейтинг модели обновляется. В частности, если пользователь предпочитает текущую модель высокорейтинговой модели, текущая модель получит относительно большое обновление рейтинга и будет занимать более высокое место в лидерборде. И наоборот, если текущая модель проигрывает низкорейтинговой модели, это увеличивает рейтинг лишь немного. (А если текущая модель проигрывает, она обновляется аналогичным образом, но с вычитанием рейтинговых очков вместо добавления.)

Код для преобразования этих попарных рейтингов в лидерборд показан в блоке кода ниже.

Блок кода 4: Построение лидерборда
def elo_ratings(vote_pairs, k_factor=32,
                initial_rating=1000):
    # Initialize all models with the same base rating
    ratings = {
        model: initial_rating
        for pair in vote_pairs
        for model in pair
    }

    # Update ratings after each match
    for winner, loser in vote_pairs:

        # Expected score for the current winner
        expected_winner = 1.0 / (
            1.0 + 10 ** (
                (ratings[loser] - ratings[winner])
                / 400.0
            )
        )

        # k_factor determines sensitivity of updates
        ratings[winner] = (
            ratings[winner]
            + k_factor * (1 - expected_winner)
        )
        ratings[loser] = (
            ratings[loser]
            + k_factor * (0 - (1 - expected_winner))
        )

    return ratings

Функция elo_ratings, определённая выше, принимает голоса в качестве входных данных и преобразует их в лидерборд следующим образом:

ratings = elo_ratings(votes, k_factor=32, initial_rating=1000)
for model in sorted(ratings, key=ratings.get, reverse=True):
    print(f"{model:8s} : {ratings[model]:.1f}")

Это приводит к следующему рейтингу лидерборда, где чем выше оценка, тем лучше:

GPT-5 : 1043.7
Claude-3 : 1015.2
Llama-4 : 1000.7
Llama-3 : 940.4

Итак, как это работает? Для каждой пары мы вычисляем ожидаемую оценку победителя, используя следующую формулу:

expected_winner = 1 / (1 + 10 ** ((rating_loser - rating_winner) / 400))

Это значение expected_winner является предсказанной моделью вероятностью победы в условиях без ничьей на основе текущих рейтингов. Оно определяет, насколько большим будет обновление рейтинга.

Сначала каждая модель начинает с initial_rating = 1000. Если два рейтинга (победителя и проигравшего) равны, мы имеем expected_winner = 0.5, что указывает на равный матч. В этом случае обновления следующие:

rating_winner + k_factor * (1 - 0.5) = rating_winner + 16
rating_loser + k_factor * (0 - (1 - 0.5)) = rating_loser - 16

Теперь, если явный фаворит (модель с высоким рейтингом) побеждает, мы имеем expected_winner ≈ 1. Фаворит получает лишь небольшой прирост, а проигравший теряет лишь немного:

rating_winner + 32 * (1 - 0.99) = rating_winner + 0.32
rating_loser + 32 * (0 - (1 - 0.99)) = rating_loser - 0.32

Однако если аутсайдер (модель с низким рейтингом) побеждает, мы имеем expected_winner ≈ 0, и победитель получает почти полные k_factor очков, в то время как проигравший теряет примерно такую же величину:

rating_winner + 32 * (1 - 0.01) = rating_winner + 31.68
rating_loser + 32 * (0 - (1 - 0.01)) = rating_loser - 31.68
Порядок имеет значение

Подход Эло обновляет рейтинги после каждого матча (сравнения моделей), поэтому более поздние результаты строятся на рейтингах, которые уже были обновлены. Это означает, что один и тот же набор результатов, представленный в другом порядке, может завершиться немного отличающимися итоговыми оценками. Этот эффект обычно незначителен, но он может проявиться, особенно когда неожиданный результат происходит в начале, а не в конце.

Чтобы уменьшить этот эффект порядка, мы можем перемешать пары голосов и запустить функцию elo_ratings несколько раз и усреднить рейтинги.

Подходы с использованием лидербордов, такие как описанный выше, предоставляют более динамичное представление о качестве модели, чем статические оценки бенчмарков. Однако результаты могут быть подвержены влиянию демографии пользователей, выбора промптов и предвзятости при голосовании. Бенчмарки и лидерборды также могут быть подвержены манипуляциям, и пользователи могут выбирать ответы на основе стиля, а не корректности. Наконец, по сравнению с автоматизированными системами выполнения бенчмарков (benchmark harnesses), лидерборды не предоставляют мгновенную обратную связь по новым разработанным вариантам, что затрудняет их использование в процессе активной разработки модели.

Другие методы ранжирования

LM Arena изначально использовала метод Эло, описанный в этом разделе, но недавно перешла на статистический подход, основанный на модели Брэдли-Терри (Bradley-Terry model). Основное преимущество модели Брэдли-Терри заключается в том, что, будучи статистически обоснованной, она позволяет построить доверительные интервалы для выражения неопределённости в рейтингах. Кроме того, в отличие от рейтингов Эло, модель Брэдли-Терри оценивает все рейтинги совместно, используя статистическую подгонку по всему датасету, что делает её невосприимчивой к эффектам порядка.

Чтобы сохранить представленные оценки в привычном диапазоне, модель Брэдли-Терри подгоняется для получения значений, сопоставимых с Эло. Несмотря на то, что лидерборд официально больше не использует рейтинги Эло, термин "Эло" остаётся широко используемым исследователями и практиками LLM при сравнении моделей. Пример кода, демонстрирующий рейтинг Эло, доступен на GitHub.

Рисунок 11: Сравнение рейтингов Эло и Брэдли-Терри; исходный код доступен на GitHub
Рисунок 11: Сравнение рейтингов Эло и Брэдли-Терри; исходный код доступен на GitHub

Метод 4: Оценка ответов с помощью других LLM

В ранние дни LLM оценивались с использованием статистических методов и методов, основанных на эвристиках, включая метрику под названием BLEU, которая является грубой мерой того, насколько хорошо сгенерированный текст соответствует эталонному тексту. Проблема таких метрик заключается в том, что они требуют точного совпадения слов и не учитывают синонимы, изменения слов и так далее.

Одним из решений этой проблемы, если мы хотим оценить письменный текст ответа в целом, является использование относительных рейтингов и подходов, основанных на лидербордах, как обсуждалось в предыдущем разделе. Однако недостатком лидербордов является субъективная природа сравнений, основанных на предпочтениях, поскольку это включает в себя обратную связь от людей (а также проблемы, связанные со сбором этой обратной связи).

Связанный метод заключается в использовании другой LLM с предопределённой оценочной рубрикой (то есть руководством по оценке) для сравнения ответа LLM с эталонным ответом и оценки качества ответа на основе предопределённой рубрики, как показано на Рисунке 12.

Рисунок 12: Пример оценки LLM-судьей. Модель, подлежащая оценке, генерирует ответ, который затем оценивается отдельной LLM-судьей в соответствии с рубрикой и предоставленным эталонным ответом
Рисунок 12: Пример оценки LLM-судьей. Модель, подлежащая оценке, генерирует ответ, который затем оценивается отдельной LLM-судьей в соответствии с рубрикой и предоставленным эталонным ответом

На практике подход, основанный на судье, показанный на Рисунке 12, работает хорошо, когда LLM-судья является сильным. Обычно используются флагманские модели LLM через API (например, API GPT-5), хотя также существуют специализированные модели-судьи. (Например, одним из многих примеров является Phudge; но в конечном итоге большинство этих специализированных моделей — это просто модели меньшего размера, дообученные для имитации поведения оценки проприетарных моделей GPT.)

Одна из причин, по которой судьи работают так хорошо, заключается также в том, что оценить ответ часто легче, чем сгенерировать его.

Чтобы реализовать оценку модели на основе судьи, как показано на Рисунке 12, программно на Python, мы могли бы либо загрузить одну из более крупных моделей Qwen3 в PyTorch и запросить её с оценочной рубрикой и ответом модели, который мы хотим оценить.

В качестве альтернативы мы можем использовать другие LLM через API, например ChatGPT или Ollama API.

Поскольку мы уже знаем, как загружать модели Qwen3 в PyTorch, чтобы сделать это более интересным, в оставшейся части раздела мы реализуем оценку на основе судьи, показанную на Рисунке 12, используя Ollama API на Python.

В частности, мы будем использовать модель с открытыми весами gpt-oss от OpenAI с 20 миллиардами параметров, поскольку она предлагает хороший баланс между возможностями и эффективностью.

3.1 Реализация подхода LLM-в-качестве-судьи в Ollama

Ollama — это эффективное приложение с открытым исходным кодом для запуска LLM на ноутбуке. Оно служит оболочкой вокруг библиотеки llama.cpp с открытым исходным кодом, которая реализует LLM на чистом C/C++ для максимизации эффективности. Однако обратите внимание, что Ollama — это только инструмент для генерации текста с использованием LLM (инференс) и не поддерживает обучение или файнтюнинг LLM.

Чтобы выполнить следующий код, пожалуйста, установите Ollama и следуйте предоставленным инструкциям для вашей операционной системы:

  • Для пользователей macOS и Windows: Откройте загруженное приложение Ollama. Если появится запрос на установку использования из командной строки, выберите «да».

  • Для пользователей Linux: Используйте команду установки, доступную на веб-сайте Ollama.

Прежде чем реализовать код оценки модели, давайте сначала загрузим модель gpt-oss и убедимся, что Ollama функционирует корректно, используя её из терминала командной строки.

Выполните следующую команду в командной строке, чтобы опробовать модель gpt-oss с 20 миллиардами параметров:

ollama run gpt-oss:20b

При первом выполнении этой команды модель gpt-oss с 20 миллиардами параметров, которая занимает 14 ГБ дискового пространства, будет автоматически загружена. Вывод выглядит следующим образом:

$ ollama run gpt-oss:20b
pulling manifest 
pulling b112e727c6f1: 100% ▕██████████████████████▏  13 GB                         
pulling fa6710a93d78: 100% ▕██████████████████████▏ 7.2 KB                         
pulling f60356777647: 100% ▕██████████████████████▏  11 KB                         
pulling d8ba2f9a17b3: 100% ▕██████████████████████▏   18 B                         
pulling 55c108d8e936: 100% ▕██████████████████████▏  489 B                         
verifying sha256 digest 
writing manifest 
removing unused layers 
success
Альтернативные модели Ollama

Обратите внимание, что gpt-oss:20b в команде ollama run gpt-oss:20b относится к модели gpt-oss с 20 миллиардами параметров. Использование Ollama с моделью gpt-oss:20b требует примерно 13 ГБ оперативной памяти. Если на вашей машине недостаточно оперативной памяти, вы можете попробовать использовать модель меньшего размера, например модель qwen3:4b с 4 миллиардами параметров через ollama run qwen3:4b, которая требует всего около 4 ГБ оперативной памяти.

Для более мощных компьютеров вы также можете использовать более крупную модель gpt-oss со 120 миллиардами параметров, заменив gpt-oss:20b на gpt-oss:120b. Однако имейте в виду, что эта модель требует значительно больше вычислительных ресурсов.

После завершения загрузки модели нам предоставляется интерфейс командной строки, который позволяет взаимодействовать с моделью. Например, попробуйте спросить модель «Сколько будет 1+2?»:

>>> Сколько будет 1+2?
Думаю...
Пользователь спрашивает: "Сколько будет 1+2?" Это просто: ответ 3. Предоставить объяснение? Возможно, спрашивает про простую арифметику. Предоставить ответ: 3.
...конец размышления.
1 + 2 = **3**

Вы можете завершить сессию ollama run gpt-oss:20b, используя команду /bye.

В оставшейся части этого раздела мы будем использовать ollama API. Этот подход требует, чтобы Ollama работала в фоновом режиме. Для этого существует три варианта:

  1. Запустите команду ollama serve в терминале (рекомендуется). Это запускает бэкенд Ollama как сервер, обычно на http://localhost:11434. Обратите внимание, что модель не загружается до тех пор, пока она не будет вызвана через API (позже в этом разделе).

  2. Запустите команду ollama run gpt-oss:20b аналогично предыдущему, но оставьте её открытой и не выходите из сессии через /bye. Как обсуждалось ранее, это открывает минимальную удобную оболочку вокруг локального сервера Ollama. За кулисами используется тот же серверный API, что и ollama serve.

  3. Десктопное приложение Ollama. Открытие десктопного приложения автоматически запускает тот же бэкенд и предоставляет графический интерфейс поверх него, как показано на Рисунке 12 ранее.

Рисунок 13 (единственный непереведенный): Два различных варианта для поддержания работы сервера Ollama (/приложения), чтобы мы могли использовать его через Ollama API в Python.
Рисунок 13 (единственный непереведенный): Два различных варианта для поддержания работы сервера Ollama (/приложения), чтобы мы могли использовать его через Ollama API в Python.
IP-адрес сервера Ollama

Ollama запускается локально на нашей машине, запуская процесс, подобный локальному серверу. При запуске ollama serve в терминале, как описано выше, вы можете столкнуться с сообщением об ошибке Error: listen tcp 127.0.0.1:11434: bind: address already in use (Ошибка: прослушивание tcp 127.0.0.1:11434: привязка: адрес уже используется).

Если это происходит, попробуйте использовать команду OLLAMA_HOST=127.0.0.1:11435 ollama serve (и если этот адрес также используется, попробуйте увеличивать числа на единицу, пока не найдёте незанятый адрес).

Следующий код проверяет, что сессия Ollama работает корректно, прежде чем мы используем Ollama для оценки ответов тестового набора, сгенерированных в предыдущем разделе:

Блок кода 5: Проверка работы Ollama
import psutil

def check_if_running(process_name):
    running = False
    for proc in psutil.process_iter(["name"]):
        if process_name in proc.info["name"]:
            running = True
            break
    return running

ollama_running = check_if_running("ollama")

if not ollama_running:
    raise RuntimeError(
        "Ollama not running. "
        "Launch ollama before proceeding."
    )
print("Ollama running:", check_if_running("ollama"))

Убедитесь, что вывод из выполнения предыдущего кода отображает Ollama running: True. Если отображается False, пожалуйста, проверьте, что команда ollama serve или приложение Ollama активно запущено (см. Рисунок 13).

В оставшейся части этой статьи мы будем взаимодействовать с локальной моделью gpt-oss, работающей на нашей машине, через Ollama REST API, используя Python. Следующая функция query_model демонстрирует, как использовать API:

Блок кода 6: Запрос к локальной модели Ollama
import json
import urllib.request


def query_model(
    prompt,
    model="gpt-oss:20b",
    # If you used 
    # OLLAMA_HOST=127.0.0.1:11435 ollama serve
    # update the address below
    url="http://localhost:11434/api/chat"
):
    # Create the data payload as a dictionary:
    data = {
        "model": model,
        "messages": [
            {"role": "user", "content": prompt}
        ],
        # Settings required for deterministic responses:
        "options": {
            "seed": 123,
            "temperature": 0,
            "num_ctx": 2048
        }
    }

    # Convert the dictionary to JSON and encode it to bytes
    payload = json.dumps(data).encode("utf-8")

    # Create a POST request and add headers
    request = urllib.request.Request(  
        url,
        data=payload,
        method="POST"
    )
    request.add_header("Content-Type", "application/json")

    response_data = ""

    # Send the request and capture the streaming response
    with urllib.request.urlopen(request) as response:
        while True:
            line = response.readline().decode("utf-8")
            if not line:
                break
            # Parse each line into JSON
            response_json = json.loads(line)
            response_data += response_json["message"]["content"]

    return response_data

Вот пример того, как использовать функцию query_model, которую мы только что реализовали:

ollama_model = "gpt-oss:20b"
result = query_model("What is 1+2?", ollama_model)
print(result)

Полученный ответ — «3". (Он отличается от того, что мы получили бы, если бы запустили Ollama run или приложение Ollama, из-за различных настроек по умолчанию.)

Используя функцию query_model, мы можем оценить ответы, сгенерированные нашей моделью, с помощью промпта, который включает оценочную рубрику, просящую модель gpt-oss оценить ответы нашей целевой модели по шкале от 1 до 5 на основе правильного ответа в качестве эталона.

Промпт, который мы используем для этого, показан ниже:

Блок кода 7: Настройка шаблона промпта, включая оценочную рубрику
def rubric_prompt(instruction, reference_answer, model_answer):
    rubric = (
        "Вы — справедливый помощник-судья. Вам будут "
        "предоставлены инструкция, эталонный ответ и "
        "ответ-кандидат для оценки в соответствии "
        "со следующей рубрикой:\n\n"
        "1: Ответ не отвечает на инструкцию, "
        "предоставляя нерелевантный, неправильный "
        "или чрезмерно многословный контент.\n"
        "2: Ответ частично отвечает на инструкцию, "
        "но содержит серьёзные ошибки, упущения "
        "или нерелевантные детали.\n"
        "3: Ответ в некоторой степени отвечает на "
        "инструкцию, но является неполным, частично "
        "правильным или местами неясным.\n"
        "4: Ответ в основном соответствует инструкции, "
        "с лишь незначительными ошибками, упущениями "
        "или недостатком ясности.\n"
        "5: Ответ полностью соответствует инструкции, "
        "предоставляя чёткий, точный и релевантный "
        "ответ в лаконичной и эффективной манере.\n\n"
        "Теперь вот инструкция, эталонный ответ "
        "и ответ для оценки.\n"
    )

    prompt = (
        f"{rubric}\n"
        f"Инструкция:\n{instruction}\n\n"
        f"Эталонный ответ:\n{reference_answer}\n\n"
        f"Ответ:\n{model_answer}\n\n"
        f"Оценка: "
    )
    return prompt

Параметрmodel_answer в rubric_prompt предназначен для представления ответа, созданного нашей собственной моделью на практике. Для целей иллюстрации мы здесь жёстко задаём правдоподобный ответ модели, а не генерируем его динамически. (Однако не стесняйтесь использовать модель Qwen3, которую мы загрузили в начале этой статьи, чтобы сгенерировать реальный model_answer).

Отрендеренный промпт
rendered_prompt = rubric_prompt(
    instruction=(
        "Если все птицы могут летать, и пингвин — это птица, "
        "может ли пингвин летать?"
    ),
    reference_answer=(
        "Да, согласно предпосылке, что все птицы могут летать, "
        "пингвин может летать."
    ),
    model_answer=(
        "Да – при этих предпосылках пингвин смог бы летать."
    )
)
print(rendered_prompt)
Вывод после запуска:
Вы — справедливый помощник-судья. Вам будут предоставлены инструкция,
эталонный ответ и ответ-кандидат для оценки в соответствии со следующей
рубрикой:

1: Ответ не отвечает на инструкцию, предоставляя нерелевантный,
неправильный или чрезмерно многословный контент.
2: Ответ частично отвечает на инструкцию, но содержит серьёзные ошибки,
упущения или нерелевантные детали.
3: Ответ в некоторой степени отвечает на инструкцию, но является
неполным, частично правильным или местами неясным.
4: Ответ в основном соответствует инструкции, с лишь незначительными
ошибками, упущениями или недостатком ясности.
5: Ответ полностью соответствует инструкции, предоставляя чёткий, точный
и релевантный ответ в лаконичной и эффективной манере.

Теперь вот инструкция, эталонный ответ и ответ для оценки.

Инструкция:
Если все птицы могут летать, и пингвин — это птица, может ли пингвин
летать?

Эталонный ответ:
Да, согласно предпосылке, что все птицы могут летать, пингвин может
летать.

Ответ:
Да – при этих предпосылках пингвин смог бы летать.

Оценка:

Завершение промпта на «Оценка: » стимулирует модель к генерации ответа. Давайте посмотрим, как модель gpt-oss:20b оценивает ответ:

result = query_model(rendered_prompt, ollama_model)
print(result)

Дает нам:

**Оценка: 5**

Ответ-кандидат напрямую отвечает на вопрос, корректно применяет данные предпосылки и лаконично утверждает, что пингвин смог бы летать. Он точен, релевантен и ясен.

Как мы видим, ответ получает наивысшую оценку, что разумно, поскольку он действительно правильный. Хотя это был простой пример с пошаговым прохождением процесса вручную, мы могли бы развить эту идею дальше и реализовать цикл for, который итеративно запрашивает модель (например, модель Qwen3, которую мы загрузили ранее) с вопросами из оценочного датасета и оценивает её через gpt-oss, а затем вычисляет среднюю оценку. Вы можете найти реализацию такого скрипта, где мы оцениваем модель Qwen3 на датасете MATH-500, на GitHub.

Рисунок 14: Сравнение базового варианта Qwen3 0.6 и варианта с рассуждениями на первых 10 примерах из MATH-500, оценённых gpt-oss:20b в качестве судьи
Рисунок 14: Сравнение базового варианта Qwen3 0.6 и варианта с рассуждениями на первых 10 примерах из MATH-500, оценённых gpt-oss:20b в качестве судьи
Оценка промежуточных шагов рассуждений через модели пошаговых вознаграждений

В связи с символьными верификаторами и LLM-судьями существует класс обученных моделей, называемых моделями процессных вознаграждений (Process Reward Models, PRM). Как и судьи, PRM могут оценивать цепочки рассуждений не только по финальному ответу, но в отличие от общих судей, они фокусируются конкретно на промежуточных шагах рассуждений. И в отличие от верификаторов, которые проверяют корректность символьно и обычно только на уровне результата, PRM предоставляют пошаговые сигналы вознаграждения во время обучения с подкреплением. Мы можем категоризировать PRM как "пошаговых судей", которые преимущественно разработаны для обучения, а не для чистой оценки. (На практике PRM трудно надёжно обучать в масштабе. Например, DeepSeek R1 не использовала PRM, а вместо этого комбинировала верификаторы для обучения рассуждениям.)


Оценки на основе судей предлагают преимущества перед лидербордами, основанными на предпочтениях, включая масштабируемость и консистентность, поскольку они не зависят от больших групп голосующих людей. (Технически возможно передать оценку предпочтений, лежащую в основе лидербордов, также и LLM-судьям). Однако LLM-судьи также разделяют схожие недостатки с голосующими людьми: результаты могут быть смещены из-за предпочтений модели, дизайна промпта и стиля ответа. Кроме того, существует сильная зависимость от выбора модели-судьи и рубрики, и им не хватает воспроизводимости фиксированных бенчмарков.

Выводы

В этой статье мы рассмотрели четыре различных подхода к оценке: множественный выбор, верификаторы, лидерборды и LLM-судьи.

Я понимаю, что это была длинная статья, но надеюсь, вы нашли её полезной для получения обзора того, как оцениваются LLM. Подход с нуля, подобный этому, может быть многословным, но это отличный способ понять, как эти методы работают под капотом, что, в свою очередь, помогает нам выявлять слабые стороны и области для улучшения.

В любом случае, оценка моделей — это очень большая и важная тема. Надеюсь, эта статья была полезна в объяснении того, как работают основные подходы, и что вы почерпнули несколько полезных инсайтов для следующего раза, когда будете смотреть на оценки моделей или проводить их самостоятельно.


Спасибо!

Мой скромный тг-канальчик и другие статьи: