Перейти к содержимому
ПереводыStanford

CS336, лекции 10 и 12: инференс и оценка моделей

Перевод исполняемых лекций Перси Ляна (весна 2025): почему генерация упирается в память, арифметическая интенсивность prefill и decode, KV-кэш и способы его уменьшить (GQA, MLA, CLA, локальное внимание), квантование, прунинг, спекулятивное сэмплирование, непрерывный батчинг и PagedAttention; затем — как оценивать LLM: перплексия, MMLU, GPQA, Chatbot Arena, агентные бенчмарки, безопасность, реалистичность и валидность.

  • Лекция 10 (инференс) — расширенная версия модуля 7.1: prefill и decode, KV-кэш (урок 4.6.4), GQA и MLA (модуль 5.2), квантование (7.2.1), vLLM и непрерывный батчинг (7.1.2). Главная ценность лекции — расчёт арифметической интенсивности, который объясняет, почему всё это нужно.
  • Лекция 12 (оценка) — к модулю 7.3: что меряют популярные бенчмарки, почему цифрам из пресс-релизов нельзя верить слепо и как думать о собственной оценке.
  • Перплексию и её связь с кросс-энтропией вы видели в модуле 4.1; здесь — её место среди других метрик.

Инференс: при фиксированной модели генерировать ответы на промпты.

Инференс встречается во многих местах:

  • реальное использование (чат-боты, автодополнение кода, пакетная обработка данных);
  • оценка моделей (например, на следовании инструкциям);
  • вычисления во время ответа (test-time compute: «размышление» требует больше инференса);
  • обучение с подкреплением (сгенерировать примеры, затем оценить их).

Почему важна эффективность: обучение — разовая трата, а инференс повторяется очень много раз. [Твит Сэма Альтмана: OpenAI генерирует около 100 млрд слов в день. Твит Амана Сэнгера: Cursor пишет около миллиарда строк принятого кода в день.]

Метрики:

  • Время до первого токена (time-to-first-token, TTFT) — сколько пользователь ждёт, пока начнётся генерация (важно для интерактивных приложений).
  • Задержка (latency, секунд на токен) — как быстро токены появляются у пользователя (важно для интерактивных приложений).
  • Пропускная способность (throughput, токенов в секунду) — полезна для пакетной обработки.

Ключевые соображения эффективности:

  • Обучение (с учителем): видны все токены, можно распараллелить по последовательности (матмулы в трансформере).
  • Инференс: генерировать приходится последовательно, распараллелить нельзя, поэтому труднее полностью загрузить вычислители.

Кто занимается инференсом (это важно для всех, у кого есть продукт или платформа):

  • провайдеры закрытых моделей (OpenAI, Anthropic, Google и др.);
  • провайдеры моделей с открытыми весами (Together, Fireworks, DeepInfra и др.).

Пакеты с открытым кодом:

[Схема трансформера из книги «How To Scale Your Model», глава о трансформерах: https://jax-ml.github.io/scaling-book/transformers/.]

Упрощения (по общепринятым соглашениям): F = 4*D, D = N*H, N = K*G, S = T. Здесь BB — батч, SS — длина контекста, TT — число генерируемых токенов, DD — размер модели, FF — размер скрытого слоя MLP, NN — число голов запросов, KK — число голов ключей/значений, G=N/KG = N/K — размер группы, HH — размер головы, LL — число слоёв, VV — размер словаря.

FLOPs прямого прохода: 6 * (B*T) * (num_params + O(T)).

Напоминание: арифметическая интенсивность

Заголовок раздела «Напоминание: арифметическая интенсивность»

Постановка: умножаем матрицу X (B×DB \times D) на W (D×FD \times F). Интуиция: BB — размер батча, DD — скрытая размерность, FF — размерность расширения в MLP.

Посчитаем FLOPs и байты чтения/записи для умножения X⋅WX \cdot W (числа в bf16 — по 2 байта).

  1. Читаем X (B×DB \times D) из HBM: 2BD2BD байт.
  2. Читаем W (D×FD \times F) из HBM: 2DF2DF байт.
  3. Вычисляем Y=XWY = X W: 2BDF2BDF FLOPs.
  4. Пишем Y (B×FB \times F) в HBM: 2BF2BF байт.

Итого: FLOPs =2BDF= 2BDF, байты =2BD+2DF+2BF= 2BD + 2DF + 2BF. Арифметическая интенсивность — сколько вычислений мы делаем на каждый переданный байт (хотим, чтобы она была высокой):

интенсивность=BDFBD+BF+DF.\text{интенсивность} = \frac{BDF}{BD + BF + DF}.

Если BB много меньше DD и FF, это упрощается до

интенсивность=B.\text{интенсивность} = B.

Интенсивность ускорителя H100: 989⋅1012989 \cdot 10^{12} FLOP/с делим на 3,35⋅10123{,}35 \cdot 10^{12} байт/с — получаем 295.

  • Если интенсивность вычисления больше интенсивности ускорителя — ограничено вычислениями (хорошо).
  • Если меньше — ограничено памятью (плохо).
  • Вывод: ограничено вычислениями тогда и только тогда, когда B>295B > 295.

Крайний случай (B=1B = 1, умножение матрицы на вектор):

  • арифметическая интенсивность 1;
  • ограничено памятью (читаем матрицу D×FD \times F, а делаем всего 2DF2DF FLOPs);
  • по сути, именно это и происходит при генерации…

[Глава книги «How To Scale Your Model» об инференсе: https://jax-ml.github.io/scaling-book/inference/.]

[Рисунок: наивный инференс — для каждого нового токена вся история заново проходит через трансформер.] Наивный инференс: чтобы сгенерировать каждый токен, подаём в трансформер всю историю. Сложность: генерация TT токенов требует O(T3)O(T^3) FLOPs (один прямой проход — O(T2)O(T^2)).

Наблюдение: значительную часть работы можно переиспользовать между префиксами. Решение: хранить KV-кэшKV-кэшEN KV cache в HBM. [Рисунок: инференс с кэшем — новый токен вычисляется по сохранённым ключам и значениям.] KV-кэш: для каждой последовательности (BB), токена (SS), слоя (LL), головы (KK) храним вектор размерности HH.

Две стадии инференса:

  1. Prefill (предзаполнениеprefill (предзаполнение)EN prefillПервая фаза генерации: весь промпт проходит через модель одним параллельным проходом, заполняется KV-кэш и получается первый токен. Упирается в вычисления.): по промпту вычисляем векторы (распараллеливается, как в обучении).
  2. Generation (генерация, decode): генерируем новые токены ответа (последовательно).

Посчитаем FLOPs и обмен с памятью для слоёв MLP и внимания. SS — число токенов, на которые мы опираемся, TT — число генерируемых токенов. Потом специализируем: prefill (T=ST = S) и генерация (T=1T = 1).

Слои MLP (смотрим только на умножения матриц):

  1. Читаем X (B×T×DB \times T \times D) из HBM: 2BTD2BTD байт.
  2. Читаем WupW_{up} (D×FD \times F), WgateW_{gate} (D×FD \times F), WdownW_{down} (F×DF \times D): 3⋅2DF3 \cdot 2DF байт.
  3. Вычисляем U=XWupU = X W_{up}: 2BTDF2BTDF FLOPs.
  4. Пишем U (B×T×FB \times T \times F): 2BTF2BTF байт.
  5. Вычисляем G=XWgateG = X W_{gate}: 2BTDF2BTDF FLOPs.
  6. Пишем G (B×T×FB \times T \times F): 2BTF2BTF байт.
  7. Вычисляем Y=GeLU(G)⋅U⋅WdownY = \mathrm{GeLU}(G) \cdot U \cdot W_{down}: 2BTDF2BTDF FLOPs.
  8. Пишем Y (B×T×DB \times T \times D): 2BTD2BTD байт.

Итого: FLOPs =6BTDF= 6BTDF, байты =4BTD+4BTF+6DF= 4BTD + 4BTF + 6DF, интенсивность

3BTDF2BTD+2BTF+3DF  →  BT(если BT≪D,F).\frac{3BTDF}{2BTD + 2BTF + 3DF} \;\to\; BT \quad (\text{если } BT \ll D, F).

Для двух стадий:

  1. Prefill: легко сделать ограниченным вычислениями (хорошо), взяв BTBT достаточно большим.
  2. Генерация: генерируем по одному токену (T=1T = 1); BB — число одновременных запросов, его трудно сделать достаточно большим!

Слои внимания (смотрим на умножения матриц, считая, что используется FlashAttention):

  1. Читаем Q (B×T×DB \times T \times D), K (B×S×DB \times S \times D), V (B×S×DB \times S \times D): 2BTD+2BSD+2BSD2BTD + 2BSD + 2BSD байт.
  2. Вычисляем A=QK⊤A = Q K^\top: 2BSTD2BSTD FLOPs.
  3. Вычисляем Y=softmax(A)VY = \mathrm{softmax}(A) V: 2BSTD2BSTD FLOPs.
  4. Пишем Y (B×T×DB \times T \times D): 2BTD2BTD байт.

Итого: FLOPs =4BSTD= 4BSTD, байты =4BSD+4BTD= 4BSD + 4BTD, интенсивность

STS+T.\frac{ST}{S + T}.
  • Prefill (T=ST = S): интенсивность S/2S/2 — хорошо!
  • Генерация (T=1T = 1): интенсивность SS+1\frac{S}{S + 1}, меньше 1 — плохо!

В отличие от MLP, нет зависимости от BB, так что батчинг не помогает! Почему?

  • В слоях MLP все последовательности используют одни и те же веса MLP (Wup,Wgate,WdownW_{up}, W_{gate}, W_{down} не зависят от BB).
  • В слоях внимания у каждой последовательности свои векторы — свой KV-кэш (Q, K, V зависят от BB).

Итоги:

  • prefill ограничен вычислениями, генерация — памятью;
  • интенсивность MLP равна BB (нужны одновременные запросы), интенсивность внимания — 1 (улучшить невозможно).

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

Память, пропускная способность и задержка зависят от формы трансформера:

# Число параметров трансформера:
num_params = 2*V*D + D*F*3*L + (2*D*N*H + 2*D*K*H)*L
# Для хранения параметров достаточно bf16 (обучению нужен fp32)
parameter_size = num_params * 2 # 2 байта в bf16
# Градиенты и состояния оптимизатора не нужны — мы не обучаем.
# Но нужно хранить KV-кэш (это часть активаций) для каждой последовательности длины S:
kv_cache_size = S * (K*H) * L * 2 * 2 # 2 — ключ + значение, 2 — байта в bf16
# Общая память:
memory = B * kv_cache_size + parameter_size
# Задержка определяется обменом с памятью (на каждом шаге читаем все параметры и KV-кэш)
latency = memory / memory_bandwidth
# Пропускная способность обратна задержке, но мы генерируем B токенов параллельно
throughput = B / latency

Подставим Llama 2 13B на H100 (S=1024S = 1024, D=5120D = 5120, F=13824F = 13824, N=K=40N = K = 40, H=128H = 128, L=40L = 40, V=32000V = 32000, пропускная способность памяти 3,35⋅10123{,}35 \cdot 10^{12} байт/с):

  • num_params = 13 015 449 600;
  • memory = 838 860 800 · B + 26 030 899 200 байт;
  • latency = 0,000250 · B + 0,00777 с;
  • throughput = 127 792 · B / (32 · B + 993) токенов/с.
БатчПамятьЗадержка на шагПропускная способность
126,9 ГБ8,0 мс125 токенов/с
6479,7 ГБ23,8 мс2689 токенов/с
256240,8 ГБ71,9 мс3562 токенов/с

При батче 256 модель не помещается в память, да и выигрыш в пропускной способности убывает…

Компромисс между задержкой и пропускной способностью:

  1. Меньшие батчи дают лучшую задержку, но худшую пропускную способность.
  2. Большие батчи дают лучшую пропускную способность, но худшую задержку.

Простой параллелизм: запустите MM копий модели — задержка та же, пропускная способность растёт в MM раз! Сложный параллелизм: шардировать модель и KV-кэш (см. главу об инференсе в книге «How To Scale Your Model»).

Замечание: время до первого токена (TTFT) — по сути функция prefill. Для быстрого TTFT используйте меньшие батчи при prefill, а для пропускной способности — большие батчи при генерации.

Вспомним: память — узкое место инференса. Так что попробуем уменьшить KV-кэш… но так, чтобы не потерять слишком много точности.

Grouped-query attention (GQA) (статья). [Схема: MHA, GQA, MQA.] Идея: NN голов запросов, но только KK голов ключей и значений, каждая взаимодействует с N/KN/K головами запросов.

  • Multi-headed attention (MHA): K=NK = N.
  • Multi-query attention (MQA): K=1K = 1.
  • Group-query attention (GQA): KK где-то посередине.

[График из статьи GQA: время на образец против качества — GQA почти так же быстр, как MQA, и почти так же точен, как MHA.] KV-кэш уменьшается в N/KN/K раз.

Конфигурация (Llama 2 13B, H100)ПамятьЗадержкаПропускная способность
исходная, K=40K = 40, B=64B = 6479,7 ГБ23,8 мс2689 токенов/с
GQA, K=8K = 8 (1:5), B=64B = 6433,4 ГБ10,0 мс6417 токенов/с
GQA, K=8K = 8, B=256B = 25665,6 ГБ19,6 мс13 068 токенов/с

Это также позволяет взять больший батч: задержка хуже, но пропускная способность лучше (и теперь всё помещается в память!). Проверяем, что точность не падает: [таблица из статьи GQA — качество GQA близко к MHA].

Multi-head latent attention (MLA) (статья DeepSeek-V2). [Схема MLA.] Ключевая идея: проецируем каждый вектор ключа и значения из N⋅HN \cdot H измерений в CC измерений. DeepSeek-V2: уменьшает N⋅H=16384N \cdot H = 16384 до C=512C = 512. Загвоздка: MLA несовместим с RoPE, поэтому добавляют ещё 64 измерения для RoPE — всего 512 + 64 = 576. Улучшения задержки и пропускной способности следуют из уменьшения KV-кэша так же, как выше.

Теперь точность. Во-первых, MHA лучше GQA (хотя дороже) [таблица 8 статьи]. Во-вторых, MLA немного лучше MHA (и гораздо дешевле) [таблица 9 статьи]. Подробно — урок 5.2.2.

Cross-layer attention (CLA) (статья). [Схема.] Идея: делить ключи и значения между слоями (так же, как GQA делит их между головами). Эмпирически улучшает границу Парето между точностью и размером KV-кэша (то есть задержкой и пропускной способностью). [График результатов.]

Локальное внимание (Longformer, Sparse Transformer, Mistral 7B). [Схемы шаблонов внимания Longformer.] Идея: смотреть только на локальный контекст — он важнее всего для моделирования. Эффективный контекст растёт линейно с числом слоёв. KV-кэш не зависит от длины последовательности!

Проблема: точность всё же может пострадать. Решение: чередовать локальное внимание с глобальным (гибридные слои). Пример: character.ai использует 1 глобальный слой на каждые 6 слоёв (плюс CLA) (пост).

Итоги:

  • цель — уменьшить KV-кэш (ведь инференс ограничен памятью), не навредив точности;
  • KV-кэш меньшей размерности (GQA, MLA, общий KV-кэш);
  • локальное внимание на части слоёв.

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

Модели пространства состояний (state-space models; презентация из CS229S):

  • идея из обработки сигналов — моделировать последовательности с длинным контекстом за субквадратичное время;
  • S4: на основе классических моделей пространства состояний, хороша на синтетических задачах с длинным контекстом (статья);
  • слабость: плохо решает задачи ассоциативного припоминания, важные для языка (где трансформеры сильны);
  • Mamba: параметры SSM зависят от входа, догоняет трансформеры на масштабе 1B (статья);
  • Jamba: чередует слои трансформера и Mamba (1:7) в MoE на 52B (статья);
  • BASED: линейное внимание + локальное внимание (статья);
  • MiniMax-01: линейное внимание + полное внимание (MoE на 456B параметров) (статья).

Выводы:

  • линейное + локальное внимание (всё ещё нужно немного полного) дают серьёзные модели уровня state of the art;
  • KV-кэш размера O(T)O(T) заменяется состоянием размера O(1)O(1) ⇒ гораздо эффективнее для инференса.

Подробнее о линейном внимании и гибридах — урок 5.5.3.

Диффузионные модели:

  • популярны для генерации изображений, но для текста их труднее заставить работать (Diffusion-LM);
  • идея: генерировать все токены параллельно (не авторегрессионно) и уточнять за несколько шагов;
  • начинаем со случайного шума (по всей последовательности) и итеративно его уточняем;
  • результаты Inception Labs (новости, демо): гораздо быстрее на бенчмарках по коду. [График скорости генерации.]

В целом более радикальные изменения архитектуры обещают значительный выигрыш в инференсе!

Ключевая идея: уменьшить точность чисел. Меньше памяти — выше скорость (задержка/пропускная способность), ведь инференс ограничен памятью. Конечно, приходится беспокоиться о точности… [Схема форматов чисел из блога Baseten о FP8.]

  • fp32 (4 байта): нужен для параметров и состояний оптимизатора при обучении;

  • bf16 (2 байта): стандарт для инференса;

  • fp8 (1 байт), диапазон [−240, 240] у e4m3 на H100: можно и обучать, если не боитесь (статья);

  • int8 (1 байт), [−128, 127]: менее точен, но дешевле fp8; только для инференса (статья);

  • int4 (0,5 байта), [−8, 7]: ещё дешевле и ещё менее точен.

  • Обучение с учётом квантования (quantization-aware training, QAT): обучаем с квантованием, но это не масштабируется.

  • Квантование после обучения (post-training quantization, PTQ): прогоняем на образцах данных, чтобы найти масштаб и нулевую точку для каждого слоя или тензора. Обзор подходов.

LLM.int8() (статья, пост Hugging Face). Стандартное квантование — масштабируем по максимуму модулей значений. [Рисунок.] Проблема: выбросы (появляются в больших сетях) всё портят. Решение: извлечь выбросы и обработать их в fp16. [Анимация смешанного умножения int8/fp16.] Работает хорошо (но на 15–23% медленнее fp16). [График качества BLOOM.]

Квантование с учётом активаций (AWQ, статья). Идея: выбирать, какие веса (0,1–1%) оставить в высокой точности, по активациям. Переход fp16 → int3 даёт в 4 раза меньше памяти и ускорение в 3,2 раза. [Схема AWQ.]

Ключевая идея: просто вырезать части дорогой модели, чтобы сделать её дешевле… а затем починить.

Статья NVIDIA (Minitron). [Схема цикла «прунинг → дистилляция».] Алгоритм:

  1. Найти важные {слои, головы, скрытые измерения} на маленьком калибровочном датасете (1024 примера).
  2. Удалить неважные, получить модель меньше.
  3. Дистиллировать исходную модель в обрезанную.

[График результатов: обрезанные и дистиллированные модели догоняют обученные с нуля при гораздо меньших затратах.]

Итог: уменьшаем сложность инференса, не вредя точности.

Рецепт «с нуля»:

  1. Определить более быструю архитектуру.
  2. Обучить быструю модель.

Рецепт «дистилляции»:

  1. Определить более быструю архитектуру.
  2. Инициализировать веса из исходной модели (у которой другая архитектура).
  3. Починить быструю модель (дистилляцией).

Срезаем углы, но перепроверяем (без потерь)

Заголовок раздела «Срезаем углы, но перепроверяем (без потерь)»

Вспомним две стадии инференса:

  • prefill: по последовательности кодируем токены параллельно (ограничено вычислениями) [заметим: это заодно даёт вероятности];
  • генерация: по одному токену (ограничено памятью).

Иначе говоря, проверять быстрее, чем генерировать.

Спекулятивное сэмплирование (Leviathan et al., Chen et al.):

  • более дешёвая черновая модель pp угадывает несколько токенов (например, 4);
  • целевая модель qq оценивает их (обрабатывая токены параллельно) и принимает, если всё выглядит хорошо.

Видео Google о спекулятивном декодировании, пост.

[Алгоритм из статьи.] Это модифицированное сэмплирование с отклонением с предложением pp и целью qq. Модификация: всегда генерируем хотя бы одного кандидата (обычное сэмплирование с отклонением крутилось бы в цикле). Ключевое свойство: гарантированно получаем точный сэмпл из целевой модели!

Доказательство на примере: пусть в словаре два элемента {A, B}.

  • Вероятности целевой модели: [q(A),q(B)][q(A), q(B)].
  • Вероятности черновой модели: [p(A),p(B)][p(A), p(B)].
  • Пусть p(A)>q(A)p(A) > q(A) [черновая модель пересэмплирует A].
  • Тогда p(B)<q(B)p(B) < q(B) [черновая модель недосэмплирует B].
  • Остаточные вероятности max⁡(q−p,0)\max(q - p, 0): [0,1][0, 1].

Вероятности того, что спекулятивно будет выбран токен:

  • P[выбрать A]=p(A)⋅q(A)p(A)+p(B)⋅1⋅0=q(A)P[\text{выбрать A}] = p(A) \cdot \frac{q(A)}{p(A)} + p(B) \cdot 1 \cdot 0 = q(A);
  • P[выбрать B]=p(B)⋅1+p(A)⋅(1−q(A)p(A))⋅1=q(B)P[\text{выбрать B}] = p(B) \cdot 1 + p(A) \cdot \left(1 - \frac{q(A)}{p(A)}\right) \cdot 1 = q(B).

[Графики результатов из статей: ускорение в 2–3 раза при той же выходной выборке; статистика доли принятых токенов.]

На практике:

  • целевая модель на 70B параметров, черновая — на 8B;
  • целевая модель на 8B, черновая — на 1B;
  • черновую модель стараются сделать максимально близкой к целевой (дистилляцией).

Расширения для улучшения черновой модели:

  • Medusa: черновая модель генерирует несколько токенов параллельно (статья);
  • EAGLE: черновая модель берёт высокоуровневые признаки целевой модели (статья).

[Схемы Medusa и EAGLE.]

Итоги:

  • точное сэмплирование из целевой модели (спасибо математике)!
  • использует асимметрию между проверкой и генерацией;
  • много простора для изобретений в черновой модели (это требует обучения).

Батчинг последовательностей в живом трафике сложен, потому что:

  1. Запросы приходят в разное время (ждать набора батча плохо для ранних запросов).
  2. У последовательностей общие префиксы (системные промпты, генерация нескольких ответов).
  3. Последовательности разной длины (паддинг неэффективен).

Orca: A Distributed Serving System for Transformer-Based Generative Models, доклад.

Проблема:

  • при обучении — плотный блок токенов (размер батча × длина последовательности);
  • при инференсе запросы приходят и завершаются в разное время — получается «рваный» массив.

[Схема статического батчинга: короткие запросы закончились, но их место простаивает до конца самого длинного.]

Решение: планирование на уровне итераций:

  • декодируем шаг за шагом;
  • новые запросы добавляем в батч по мере прихода (не надо ждать окончания генерации).

Проблема: батчинг работает, только когда все последовательности одной размерности (так ведь?). Но у каждого запроса может быть своя длина.

Решение: выборочный батчинг:

  • при обучении, когда все последовательности одной длины, работаем с тензором B×S×HB \times S \times H;
  • но длины могут быть разными: [3,H][3, H], [9,H][9, H], [5,H][5, H] и т. д.;
  • вычисление внимания: каждую последовательность обрабатываем отдельно;
  • всё остальное: склеиваем все последовательности в [3+9+5,H][3 + 9 + 5, H].

Статья, в которой появились vLLM и PagedAttentionPagedAttention (страничный KV-кэш)EN PagedAttentionKV-кэш хранится блоками фиксированного размера (например, по 16 токенов), а таблица блоков связывает логические позиции с физическими — как страницы виртуальной памяти в ОС. Основа vLLM. (статья).

Прежнее положение дел:

  • приходит запрос;
  • выделяем кусок KV-кэша под промпт и ответ (до максимальной длины).

[Рисунок фрагментации памяти.] Проблема: фрагментация (как на вашем жёстком диске):

  • это расточительно, ведь мы можем сгенерировать гораздо меньше токенов (внутренняя фрагментация)!
  • между кусками может оставаться неиспользуемое место (внешняя фрагментация)!

Решение: PagedAttention (вспомните операционные системы):

  • делим KV-кэш последовательности на несмежные блоки. [Рисунок.]
  • Два запроса делят KV-кэши. [Рисунок логических и физических блоков.]

В общем случае KV-кэш делят между последовательностями по-разному:

  • общий системный промпт;
  • генерация нескольких ответов на один промпт (например, для синтеза программ).

Решение: делить префиксы, копировать при записи (copy-on-write) на уровне блоков. [Рисунок.]

Другие оптимизации vLLM:

  • ядро, сливающее чтение блоков и внимание (меньше накладных расходов на запуск ядер);
  • новейшие ядра (FlashAttention, FlashDecoding);
  • CUDA graphs, чтобы избежать накладных расходов на запуск ядер.

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

  • Инференс важен (реальное использование, оценка, обучение с подкреплением).
  • Он устроен иначе, чем обучение (ограничен памятью, динамичен).
  • Приёмы: новые архитектуры, квантование, прунинг/дистилляция, спекулятивное декодирование.
  • Идеи из системного программирования (спекулятивное выполнение, страничная память).
  • У новых архитектур огромный потенциал улучшений.

Оценка: насколько «хороша» данная фиксированная модель?

[Таблицы бенчмарков из анонсов DeepSeek-R1, Llama 4 и OLMo 2 32B.] Современные языковые модели оценивают на похожих, но не совсем одинаковых бенчмарках (MMLU, MATH и т. д.). Что это за бенчмарки? Что значат эти числа?

Лидерборд HELM Capabilities. Внимательно смотрите на стоимость! Artificial Analysis — качество против цены.

Может, модель хороша, если люди её выбирают (и платят)… Рейтинг использования на OpenRouter, Chatbot Arena.

[Твит Демиса Хассабиса о Gemini 2.5 — восторженные отзывы пользователей.] Кризис… [Твит Андрея Карпатого: он не знает, каким метрикам сейчас верить, — «кризис оценки».]

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

Зачем вообще оценивать? Единственно верной оценки нет: всё зависит от вопроса, на который вы хотите ответить.

  1. Пользователь или компания принимает решение о покупке (модель A или B) для своей задачи (например, чат-бот поддержки).
  2. Исследователи хотят измерить «сырые» способности модели (например, интеллект).
  3. Мы хотим понять пользу и вред модели (для бизнеса и регулирования).
  4. Разработчики моделей хотят обратную связь, чтобы улучшить модель.

В каждом случае абстрактную цель нужно перевести в конкретную оценку.

Схема:

  1. Каковы входы?
  2. Как вызывать языковую модель?
  3. Как оценивать выходы?
  4. Как интерпретировать результаты?

Входы:

  1. Какие сценарии использования покрыты?
  2. Представлены ли трудные входы из «хвоста»?
  3. Адаптированы ли входы под модель (например, многоходовый диалог)?

Вызов модели:

  1. Как вы строите промпт?
  2. Использует ли модель цепочку рассуждений, инструменты, RAG и т. д.?
  3. Оцениваем языковую модель или агентную систему (разработчику модели нужно первое, пользователю — второе)?

Оценка выходов:

  1. Безошибочны ли эталонные ответы?
  2. Какие метрики (например, pass@k)?
  3. Как учитывать стоимость (инференс + обучение)?
  4. Как учитывать несимметричные ошибки (например, галлюцинации в медицине)?
  5. Что делать с открытой генерацией (без эталона)?

Интерпретация метрик:

  1. Как понимать число (например, 91%) — готово ли к внедрению?
  2. Как оценивать обобщение при пересечении обучающих и тестовых данных?
  3. Мы оцениваем итоговую модель или метод?

Итог: при оценке нужно продумать множество вопросов.

Вспомним: языковая модель — это распределение вероятностей p(x)p(x) над последовательностями токенов. Перплексия (1/p(D))1/∣D∣\left(1/p(D)\right)^{1/|D|} измеряет, присваивает ли pp высокую вероятность датасету DD. При претрейне мы минимизируем перплексию на обучающей выборке. Очевидно, надо измерять перплексию на тестовой.

Стандартные датасеты: Penn Treebank (WSJ), WikiText-103 (Википедия), One Billion Word Benchmark (из машинного перевода WMT11 — EuroParl, ООН, новости). Статьи обучали на датасете (обучающая часть) и оценивали на нём же (тестовая часть). Чистые CNN + LSTM на One Billion Word Benchmark: перплексия 51,3 → 30,0 (статья).

GPT-2 обучали на WebText (40 ГБ текста — сайты по ссылкам с Reddit) и оценивали zero-shot на стандартных датасетах. Это оценка вне распределения (но идея в том, что обучение покрывает очень многое). [Таблица из статьи GPT-2.] Лучше работает на маленьких датасетах (перенос помогает), но не на больших (1BW).

Со времён GPT-2 и GPT-3 статьи по языковым моделям сместились к точности на прикладных задачах. Но перплексия всё ещё полезна:

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

Предупреждение (если вы ведёте лидерборд): оценщику приходится доверять языковой модели. Для точности на задаче можно просто взять выход «чёрного ящика» и посчитать метрику. Для перплексии нужно, чтобы модель выдала вероятности, и приходится верить, что они суммируются в 1 (в старые времена с UNK было ещё хуже).

Позиция «перплексия — всё»:

  • истинное распределение — tt, модель — pp;
  • лучшая возможная перплексия — H(t)H(t), достигается тогда и только тогда, когда p=tp = t;
  • если у нас есть tt, мы решаем все задачи;
  • значит, снижая перплексию, мы в конце концов придём к AGI;
  • оговорка: возможно, это не самый эффективный путь (снижаем перплексию и на тех частях распределения, которые не важны).

То, что по духу близко к перплексии:

  • задачи на заполнение пропуска вроде LAMBADA (статья) [пример: угадать последнее слово абзаца, которое понятно только из широкого контекста];
  • HellaSwag (статья) [пример: выбрать правдоподобное продолжение описания ситуации из четырёх вариантов].

Massive Multitask Language Understanding (MMLU) (статья):

  • 57 предметов (например, математика, история США, право, мораль), множественный выбор;
  • «собраны студентами и аспирантами из свободно доступных источников в интернете»;
  • на деле проверяет знания, а не понимание языка;
  • оценивали GPT-3 с few-shot промптами.

[Примеры вопросов.] HELM MMLU — просмотр предсказаний.

MMLU-Pro (статья):

  • убрали шумные и тривиальные вопросы MMLU;
  • расширили 4 варианта ответа до 10;
  • оценивают с цепочкой рассуждений (даёт модели больше шансов);
  • точность моделей падает на 16–33% (бенчмарк не так насыщен).

HELM MMLU-Pro.

Graduate-Level Google-Proof Q&A (GPQA) (статья):

  • вопросы написали 61 подрядчик с PhD с Upwork;
  • эксперты с PhD достигают 65% точности;
  • неэксперты — 34% за 30 минут с доступом к Google;
  • GPT-4 — 39%.

HELM GPQA.

Humanity’s Last Exam (статья):

  • 2500 вопросов: мультимодальные, по многим предметам, с выбором ответа и с коротким ответом;
  • авторам вопросов — призовой фонд 500 тыс. долл. и соавторство;
  • вопросы отфильтрованы фронтирными LLM, несколько этапов проверки.

[Схема отбора, результаты, свежий лидерборд.]

До сих пор мы оценивали довольно структурированные задачи. Следование инструкциям (популяризированное ChatGPT): просто выполни инструкцию. Трудность: как оценить открытый ответ?

Chatbot Arena (статья). Как это работает:

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

Instruction-Following Eval (IFEval) (статья). [Категории ограничений.]

  • К инструкциям добавляют простые синтетические ограничения (например, «ответ не длиннее 100 слов»).
  • Ограничения проверяются автоматически, но смысл ответа — нет.
  • Довольно простые инструкции, ограничения несколько искусственные.

AlpacaEval (сайт):

  • 805 инструкций из разных источников;
  • метрика: доля побед над GPT-4 preview по мнению GPT-4 preview (возможна предвзятость).

WildBench (статья):

  • 1024 примера из 1 млн диалогов людей с чат-ботами;
  • судья — GPT-4 Turbo с чек-листом (как цепочка рассуждений для судейства) + GPT-4 как судья;
  • хорошо коррелирует (0,95) с Chatbot Arena (похоже, это фактическая проверка здравости бенчмарков).

Подробнее о модели-судье — урок 7.3.2 (LLM-as-judgeLLM-судья (LLM-as-a-judge)EN LLM-as-a-judgeСильная модель оценивает ответы другой модели по рубрике или сравнивает пару ответов. Дёшево и быстро, но со смещениями.).

Рассмотрим задачи, требующие инструментов (например, запуска кода) и итераций в течение какого-то времени. Агент = языковая модель + агентная обвязка (логика, решающая, как использовать LM).

SWE-Bench (статья):

  • 2294 задачи из 12 репозиториев на Python;
  • дано: кодовая база и описание issue; нужно прислать PR;
  • метрика: юнит-тесты.

CyBench (статья):

  • 40 задач Capture the Flag (CTF);
  • как мера трудности — время первого решения людьми.

MLE-Bench (статья):

  • 75 соревнований Kaggle (нужно обучать модели, обрабатывать данные и т. д.).

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

ARC-AGI, предложен в 2019 году Франсуа Шолле.

  • ARC-AGI-1: [примеры задач — по нескольким парам цветных сеток «вход → выход» нужно угадать правило и применить его к новой сетке; лидерборд моделей серии o от OpenAI].
  • ARC-AGI-2: сложнее.

[Фото краш-теста автомобиля.] Что значит безопасность для ИИ? HELM Safety — подборка бенчмарков.

HarmBench (статья): основан на 510 вредных поведениях, нарушающих законы или нормы. HarmBench в HELM.

AIR-Bench (статья): основан на регуляторных требованиях и политиках компаний; 314 категорий рисков, 5694 промпта. HELM AIR-Bench.

Джейлбрейки:

  • языковые модели обучены отказываться от вредных инструкций;
  • Greedy Coordinate Gradient (GCG) автоматически оптимизирует промпты, чтобы обойти защиту (статья);
  • атаки переносятся с моделей с открытыми весами (Llama) на закрытые (GPT-4).

Тестирование до выпуска:

  • Институт безопасности ИИ США и Институт безопасности ИИ Великобритании работают вместе;
  • компания даёт институтам доступ к модели до выпуска (сейчас добровольно);
  • институты проводят оценки и готовят отчёт для компании (отчёт).

Но что такое безопасность?

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

Два свойства модели, снижающие безопасность: способности + склонность.

  • Система может уметь что-то сделать, но отказываться.
  • Для моделей через API важна склонность.
  • Для моделей с открытыми весами важны способности (защиту легко «дообучить прочь»).

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

Языковые модели активно используют на практике. [Те же твиты о 100 млрд слов в день и миллиарде строк кода.] Однако большинство существующих бенчмарков (например, MMLU) далеки от реального использования. А живой трафик реальных людей содержит мусор — это тоже не всегда то, что нужно.

Два типа промптов:

  1. Экзамен: пользователь знает ответ и проверяет систему (как стандартизированные тесты).
  2. Вопрос: пользователь не знает ответа и хочет получить его с помощью системы.

«Вопрос» реалистичнее и приносит пользователю пользу.

Clio (Anthropic) (статья):

  • языковые модели анализируют реальные данные пользователей;
  • публикуются общие закономерности того, о чём спрашивают люди. [Таблица самых частых категорий запросов.]

MedHELM (лидерборд):

  • прежние медицинские бенчмарки строились на стандартизированных экзаменах;
  • 121 клиническая задача от 29 клиницистов, смесь закрытых и открытых датасетов.

К сожалению, реалистичность и приватность иногда противоречат друг другу.

Откуда мы знаем, что наши оценки валидны?

Пересечение обучения и теста (утечка тестовых данныхзагрязнение данных (утечка бенчмарка в обучение)EN data contaminationЗадачи из тестового бенчмарка попали в обучающий корпус LLM — оценка становится завышенной. Родственное явление — «шуабан», подгонка модели под лидерборд.):

  • машинное обучение, урок 1: не обучайтесь на тестовой выборке;
  • до фундаментальных моделей (ImageNet, SQuAD): чётко определённые разбиения на обучение и тест;
  • сейчас: обучаются на интернете и не рассказывают, на каких данных.

Путь 1: пытаться определить пересечение по самой модели — использовать перестановочность (exchangeability) примеров (статья). [Идея: если модель видела тестовый набор, она даёт заметно большую вероятность его «каноническому» порядку, чем случайным перестановкам.]

Путь 2: поощрять нормы отчётности (как люди сообщают доверительные интервалы) — провайдеры моделей должны сообщать о пересечении обучения и теста (статья).

Качество датасетов:

  • SWE-Bench исправили и получили SWE-Bench Verified (пост OpenAI);
  • делают «платиновые» версии бенчмарков (статья). [Примеры ошибочных эталонных ответов в популярных бенчмарках.]

Иначе говоря, каковы правила игры? До фундаментальных моделей мы оценивали методы (стандартизированные разбиения). Сегодня мы оцениваем модели/системы (можно всё).

Есть исключения…

  • nanoGPT speedrun: фиксированные данные, меряется время до заданных потерь на валидации. [Твит Карпатого.]
  • DataComp-LM: дан сырой датасет, нужно получить лучшую точность со стандартным пайплайном обучения (статья).

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

  • Единственно верной оценки нет; выбирайте оценку в зависимости от того, что хотите измерить.
  • Всегда смотрите на отдельные примеры и предсказания.
  • Нужно учитывать много аспектов: способности, безопасность, стоимость, реалистичность.
  • Чётко формулируйте правила игры (методы против моделей/систем).

  1. Посчитайте интенсивность. Повторите расчёт лекции для своей задачи: модель Qwen2.5-7B (возьмите DD, FF, LL, NN, KK, HH из её config.json), длина контекста 4096, батч 1 и 32, пропускная способность 716,8 ГБ/с. Сколько памяти займёт KV-кэш и какая выйдет теоретическая скорость генерации на одной RTX 4080? Сравните с замером из урока 7.1.1.
  2. Спекулятивное сэмплирование. Реализуйте шаг принятия/отклонения из слайда-доказательства для словаря из 5 токенов и проверьте сэмплированием (100 000 раз), что распределение совпадает с qq.
  3. Свой мини-бенчмарк. Возьмите 50 вопросов из MMLU или GSM8K, прогоните локальную модель через vLLM и посмотрите глазами на 10 ошибок — что это: ошибка модели, промпта, разбора ответа или эталона? (Первый вывод лекции 12.)
ENRU
inference, prefill, decode (generation)инференс, предзаполнение, генерация
time-to-first-token (TTFT), latency, throughputвремя до первого токена, задержка, пропускная способность
arithmetic intensity, accelerator intensityарифметическая интенсивность (FLOP на байт), интенсивность ускорителя
compute-limited / memory-limitedограничено вычислениями / памятью
KV cacheKV-кэш
GQA, MQA, MLA, cross-layer attentionвнимание с группами запросов, многозапросное, многоголовое латентное, межслойное
local (sliding window) attentionлокальное внимание (скользящее окно)
state-space model (SSM)модель пространства состояний
quantization, QAT, PTQквантование, обучение с учётом квантования, квантование после обучения
pruning, distillationпрунинг (обрезка), дистилляция
speculative sampling, draft model, target modelспекулятивное сэмплирование, черновая модель, целевая модель
continuous batching, iteration-level schedulingнепрерывный батчинг, планирование на уровне итераций
PagedAttention, fragmentation, copy-on-writeстраничное внимание, фрагментация, копирование при записи
perplexityперплексия
benchmark, leaderboardбенчмарк, таблица лидеров
win rate, ELO ratingдоля побед, рейтинг ELO
jailbreak, dual-useджейлбрейк, двойное назначение
train-test overlap (contamination)пересечение обучения и теста (утечка)