Гайд по LLM-инжинирингу: 45 концепций для production-систем

Автоматический перевод

Эта статья была автоматически переведена с оригинальной английской версии.

Это справочник для инженеров, которые уже знают базовые концепции ML и систем и хотят связать production-симптом с нужной частью стека. В нём 45 концепций из областей железа, инференса, обучения, деплоя, приложений и эксплуатации. Каждый раздел описывает механизм, то, что он меняет в работающей системе, и условие, при котором справедлив приведённый результат. Если вы идёте от симптома, например от медленного первого токена или OOM под нагрузкой, найдите нужные разделы в таблице симптомов ниже.

Для конкретного вопроса откройте короткие ответы по LLM-инжинирингу. У каждой темы отдельный ответ со ссылкой на соответствующий раздел этого руководства.

ЧастьТемыРазделы
I — Основы железаМодель roofline, память GPU, глоссарий по железу1–3
II — Основы инференсаЛатентность, throughput, KV cache, аттеншн, квантизация4–9
III — Оптимизации инференсаCUDA-ядра, FlashAttention, батчинг, PagedAttention, speculative decoding10–17
IV — Архитектура моделиУстройство трансформера, decoder-only, MoE, токенизация, контекстные окна18–22
V — Обучение и алайнментПретрейнинг, LoRA, смешанная точность, ZeRO, scaling laws, RLHF/DPO/GRPO, дистилляция23–32
VI — Масштабирование и деплойПараллелизм, фреймворки для сервинга, выбор GPU, роутинг33–36
VII — ПриложенияЭмбеддинги, RAG, агенты, промпт-инжиниринг37–40
VIII — Эксплуатация в productionRate limiting, режимы отказа, мониторинг, стоимость, планирование ёмкости41–45

Начните с симптома

В каждой строке указано, какие разделы читать первыми и что измерить, прежде чем менять стек.

Симптом или решениеС чего начатьЧто измерить перед изменением стека
Медленный первый токенПрефилл и декод, TTFTВремя в очереди, длину промпта, время префилла и P99 TTFT
Токены стримятся медленноRoofline, TPOTTPOT при разной конкурентности, пропускную способность памяти и форму батча
Длинные контексты вызывают вытеснение или OOMKV cache, PagedAttentionИспользование KV, длины запросов, потери аллокатора и число вытеснений
Модель не помещается в бюджетКвантизация, Выбор GPUКачество, throughput ядер, резерв памяти и goodput при целевом SLO
Выбор рантайма для сервингаФреймворки для сервинга, ПараллелизмThroughput и P99-латентность при целевом SLO на ваших промптах
Запросы падают по таймауту под нагрузкойРежимы отказа, Rate limitingГлубину очереди, вытеснения, долю повторов и длины ответов
Обучение не помещается в памятьLoRA и QLoRA, ZeRO, FSDPПамять под состояние модели, коммуникацию, throughput и качество на отложенной выборке
Растёт стоимостьРоутинг, Оптимизация стоимостиДолю подходящего трафика, ошибки качества, hit rate кэша и данные счетов

Глубже по решениям

Если вы решаете…С чего начатьЧто читать дальше
Как обслуживать много дообученных адаптеровОсновы инференса и деплойРуководство по сервингу с LoRAX
Нужна ли квантизацияКвантизацияРуководство по квантизации моделей
Как разделить модель между GPUПараллелизмМасштабирование больших языковых моделей
Нужен ли файн-тюнингОбучение и алайнментРуководство по файн-тюнингу LLM
Как встроить retrieval в приложениеЭмбеддинги и RAGМетрики оценки RAG
Как устроены агентные системыАгенты и промпт-инжинирингЦиклы ризонинга ИИ-агентов
Как ранжировать результаты поискаЭмбеддинги и реранкингСтек ранжирования поиска

Часть I — Основы железа

Арифметическая интенсивность, иерархия памяти GPU и термины из этой части объясняют многие решения, которые встретятся дальше в гайде.

1. Memory-bound и compute-bound, модель roofline

Отправная точка для производительности LLM — арифметическая интенсивность: сколько полезных вычислений GPU выполняет на каждый байт данных, загруженный из памяти. Это отношение определяет, чем ограничена операция: вычислениями (compute-bound, ждёт процессор) или памятью (memory-bound, ждёт загрузки данных).

У каждого GPU есть «критическая интенсивность»: порог, при котором пиковая вычислительная производительность равна пропускной способности памяти. Для NVIDIA H100 SXM с плотными Tensor Cores в BF16 или FP16 (989 TFLOPS; спецификация на 1 979 TFLOPS предполагает структурную разреженность):

989 TFLOPS3.35 TB/s≈295 FLOPs/byte\frac{989 \text{ TFLOPS}}{3.35 \text{ TB/s}} \approx 295 \text{ FLOPs/byte}

График roofline ниже показывает достижимую производительность в зависимости от арифметической интенсивности. Наклонная линия — область memory-bound, где предел задаёт пропускная способность памяти; горизонтальная линия — максимальный вычислительный throughput; излом между ними лежит на пороге 295 FLOPs/byte.

График roofline для H100 SXM: линия растёт вдоль наклона пропускной способности до точки излома около 295 FLOPs на байт и дальше идёт горизонтально на уровне 989 TFLOPS. Декод с батчем 1 находится около 1 FLOP на байт и около 3,35 TFLOPS на наклонном участке; пунктирная стрелка показывает, как батчинг поднимает его вверх по наклону. Длинный или батчевый префилл попадает на горизонтальный участок.График roofline для H100 SXM: линия растёт вдоль наклона пропускной способности до точки излома около 295 FLOPs на байт и дальше идёт горизонтально на уровне 989 TFLOPS. Декод с батчем 1 находится около 1 FLOP на байт и около 3,35 TFLOPS на наклонном участке; пунктирная стрелка показывает, как батчинг поднимает его вверх по наклону. Длинный или батчевый префилл попадает на горизонтальный участок.

Декод с батчем 1 и длинный либо достаточно батчированный префилл обычно оказываются по разные стороны этого порога:

  • Декод ограничен памятью. Когда токены генерируются по одному, для умножения на единственный новый токен приходится загружать из памяти матрицу весов в несколько гигабайт. В упрощённом анализе для плотной модели в 16 битах и батча 1 у операции около 1 FLOP/byte, примерно в 295 раз ниже порога roofline для H100. Этот разрыв объясняет, почему в таком режиме декод не может приблизиться к пиковой вычислительной производительности.
  • Длинный или достаточно батчированный префилл часто ограничен вычислениями. Обработка многих токенов промпта переиспользует веса в больших матричных умножениях, и арифметическая интенсивность может подняться выше порога roofline. Короткие промпты и малые батчи могут упираться в трафик памяти или в накладные расходы на запуск ядер.

Поэтому, чтобы ускорить декод, работайте с пропускной способностью памяти: уменьшайте веса с помощью квантизации, снижайте расход памяти на KV с помощью GQA и PagedAttention, а интенсивность повышайте батчингом. Для длинных или хорошо батчированных префиллов могут помочь более быстрые матричные вычисления и вычисления в меньшей точности; нагрузки с короткими префиллами профилируйте отдельно.

2. Иерархия памяти GPU

У GPU четыре уровня памяти с разной ёмкостью и скоростью доступа. Регистры небольшие и быстрые; HBM хранит намного больше данных, но доступ к ней занимает больше времени. Перемещение данных между уровнями — значительное ограничение производительности. На рисунке показана H100 SXM: регистры и shared memory внутри каждого SM, L2 на кристалле и 80 GB HBM3 в стеках рядом с ним.

Схема H100: кристалл GPU содержит 132 SM, в каждом из которых регистры отдельных потоков и до 228 KB shared memory, и кэш L2 на 50 MB, общий для всех SM. Стеки DRAM HBM3 рядом с кристаллом вмещают 80 GB, в том числе веса и KV cache, и соединены с ним суммарной пропускной способностью около 3,35 TB/s.Схема H100: кристалл GPU содержит 132 SM, в каждом из которых регистры отдельных потоков и до 228 KB shared memory, и кэш L2 на 50 MB, общий для всех SM. Стеки DRAM HBM3 рядом с кристаллом вмещают 80 GB, в том числе веса и KV cache, и соединены с ним суммарной пропускной способностью около 3,35 TB/s.

От самого быстрого уровня к самому медленному на H100 SXM:

  1. Регистры — самая быстрая память, привязанная напрямую к вычислительным потокам. В WGMMA на Hopper матрица A может поступать из регистров или из shared memory, а матрица B — из shared memory.
  2. SRAM (shared memory) — быстрая рабочая память на кристалле, локальная для каждого SM.
  3. Кэш L2 — общий слой на 50 MB. Он может отдавать данные, которые переиспользуются между SM, без повторного обращения к HBM.
  4. HBM3 — основная память на 80 GB, в которой лежат веса модели и KV cache; её пропускная способность составляет ~3,35 TB/s.

Разрыв между памятью на кристалле и вне его велик. По оценке из статьи про FlashAttention, на A100 пропускная способность SRAM на кристалле составляет около 19 TB/s против 1,5–2,0 TB/s у HBM (Dao et al., 2022), то есть примерно в 10 раз. FlashAttention и слияние ядер сокращают трафик HBM, оставляя промежуточные результаты на кристалле или объединяя шаги вычисления. PagedAttention решает другую задачу: он отображает логические блоки KV на несмежные физические блоки памяти GPU, уменьшая фрагментацию и позволяя разделять блоки.

3. Глоссарий по железу GPU

Эти термины встречаются во всём остальном гайде.

HBM (High Bandwidth Memory) — стек кристаллов DRAM, соединённых сквозными кремниевыми переходами (TSV) и расположенных рядом с кристаллом GPU. Поколения: HBM2e (A100, 2 TB/s), HBM3 (H100, 3,35 TB/s) и HBM3e (H200/B200, 4,8–8 TB/s). Пропускная способность HBM напрямую ограничивает TPOT в режимах декода, ограниченных памятью.

GDDR (Graphics DDR) — традиционная графическая память в потребительских GPU и GPU для рабочих станций, например RTX 4090 и L40S. У этих карт пропускная способность памяти ниже, чем у H100 SXM. Согласно документу NVIDIA об архитектуре Ada, GDDR6X на RTX 4090 обеспечивает 1008 GB/s против 3,35 TB/s у HBM3 на H100 SXM.

SM (Streaming Multiprocessor) — базовый вычислительный блок GPU NVIDIA. Каждый SM содержит ядра CUDA, Tensor Cores, shared memory и планировщик варпов. В H100 SXM 132 SM, а в H100 PCIe — 114. В A100 — 108.

Tensor Cores — специализированные блоки умножения матриц с накоплением внутри каждого SM. Они ускоряют матричные умножения смешанной точности, которые доминируют в вычислениях трансформера. Tensor Cores у H100 SXM дают 494,5 TFLOPS TF32 в плотном режиме или 989 TFLOPS в разреженном; при сравнении этой цифры всегда указывайте точность и режим разреженности.

Ядра CUDA (CUDA Cores) — универсальные блоки для чисел с плавающей точкой и целых чисел. Они выполняют поэлементные операции, функции активации и другую нематричную работу, пока Tensor Cores выполняют поддерживаемые матричные операции.

Варп (warp) — группа из 32 потоков на SM. Потоки могут расходиться по ветвям, а GPU начиная с Volta поддерживают независимое планирование потоков. Специализация варпов назначает разные варпы на перемещение данных и вычисления, чтобы эти задачи перекрывались.

NVLink — высокоскоростное соединение GPU с GPU внутри узла от NVIDIA. NVLink 4.0 на H100 даёт 900 GB/s двунаправленной пропускной способности; NVLink 5.0 на B200 достигает 1,8 TB/s. Эта пропускная способность важна для тензорного параллелизма, потому что GPU обмениваются частичными результатами на каждом слое трансформера.

InfiniBand — высокоскоростная сетевая фабрика для связи между узлами. Адаптеры InfiniBand класса ConnectX образуют межузловую фабрику для трафика конвейерного параллелизма и распределённого обучения; пропускная способность зависит от адаптера и конфигурации портов.

RDMA (Remote Direct Memory Access) позволяет устройству обращаться к памяти другой машины, не пропуская путь данных через CPU ни одной из сторон. GPUDirect RDMA поддерживает прямые передачи между GPU на разных узлах, в том числе передачу KV cache при дезагрегированном сервинге.

NVMe (Non-Volatile Memory Express) — интерфейс SSD, который используют для выгрузки KV cache и для выгрузки параметров в ZeRO-Infinity, когда памяти GPU и CPU не хватает. Последовательная пропускная способность зависит от диска и нагрузки и остаётся намного ниже пропускной способности HBM.

TFLOPS и PFLOPS — триллионы и квадриллионы операций с плавающей точкой в секунду. Один TFLOPS равен 101210^{12} FLOPS. H100 достигает 989 TFLOPS TF32 на тензорных ядрах в разреженном режиме, а FlashAttention-3 сообщает около 1,2 PFLOPS в FP8; эти значения относятся к разным форматам, и сравнивать их как одну метрику нельзя.


Часть II — Основы инференса

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

4. Латентность: TTFT, TPOT и перцентили

Time to First Token (TTFT) — сквозная задержка от отправки запроса до первого выходного токена. Она включает очередь, токенизацию, планирование, проход префилла, который порождает первый токен, и доставку по сети. Распространённые движки сервинга сэмплируют первый выходной токен из логитов прохода префилла, поэтому отдельного шага декода перед ним нет. Более длинные промпты обычно увеличивают долю префилла, но доминировать может любая из этих стадий. Целевые значения зависят от продукта; MLPerf Inference v5.0 задаёт предел P99 TTFT ≤\leq 450 ms для интерактивного сценария с Llama 2 70B.

Time Per Output Token (TPOT) — средний интервал между последовательными токенами после первого. Он соответствует фазе декода. Декод с батчем 1 или с низкой интенсивностью обычно ограничен пропускной способностью памяти, а достаточно большие батчи могут стать compute-bound:

TPOT=E2E Latency−TTFTOutput Tokens−1\text{TPOT} = \frac{\text{E2E Latency} - \text{TTFT}}{\text{Output Tokens} - 1}

Это определение требует как минимум двух выходных токенов; для ответа из одного токена TPOT не определён. В этой формуле латентность E2E заканчивается на последнем выходном токене. Таймер до завершения HTTP может также учитывать более поздние метаданные или закрытие соединения; записывайте это время отдельно.

Оценка скорости чтения на английском даёт приблизительный ориентир для стриминга. Средняя скорость чтения про себя у взрослого читателя на английском — около 238 слов в минуту для нон-фикшна (Brysbaert, 2019), то есть около 4 слов в секунду. Если токен в среднем соответствует трём четвертям слова, получается около 5 токенов в секунду, или 200 ms на токен. Это не порог удобства использования: результат зависит от языка, токенизатора, задачи и буферизации в клиенте. В интерактивном сценарии MLPerf предел P99 TPOT намного жёстче: ≤\leq 40 ms. Свою цель задавайте по результатам тестирования продукта.

P50 и P99 важно различать, потому что медиана скрывает хвост. Система с хорошим P50 и плохим P99 может страдать от батчинга, вытеснений, очередей или перекоса нагрузки; чтобы их различить, нужны трейсы.

5. Throughput: токены в секунду и компромисс с латентностью

Throughput измеряют в выходных токенах в секунду по всем параллельным запросам. Запросы в секунду сами по себе — более слабая метрика, потому что ответ из 10 токенов и ответ из 1 000 токенов стоят совершенно по-разному. Опубликованные результаты бенчмарков зависят от модели, точности, железа, длин промпта и ответа, конкурентности и SLO. vLLM, SGLang и TensorRT-LLM сравнивайте в одном харнессе, а не складывайте их заголовочные результаты.

Увеличение небольшого батча декода может повысить throughput за счёт совместного чтения весов. Более крупные батчи или более высокая входящая нагрузка со временем увеличивают латентность или достигают ограничений памяти, вычислений либо коммуникации. Измеряйте эту зависимость, не предполагая линейного масштабирования. Goodput — число запросов в секунду, которые укладываются в ваши целевые SLO, — связывает сырой throughput с тем, что реально видят пользователи.

6. KV cache: память, которая растёт с каждым токеном

При авторегрессионной генерации с полным аттеншном каждый новый токен учитывает текущий и предыдущие токены. KV cache хранит проекции Key и Value, чтобы при генерации не пересчитывать проекции предыдущих токенов на каждом шаге. Декод с полным аттеншном всё равно читает растущую историю; суммарная работа аттеншна на генерируемой последовательности остаётся квадратичной по её длине.

Для обычного кэша MHA или GQA с полным аттеншном память растёт линейно с длиной последовательности, размером батча и числом слоёв. Следующая формула без изменений не применима к архитектурам MLA, sliding-window, рекуррентным или гибридным архитектурам:

KVcache=2×L×hkv×dh×s×B×bytesKVcache = 2 \times L \times h_{kv} \times d_h \times s \times B \times \text{bytes}

где:

  • LL = число слоёв
  • hkvh_{kv} = число KV-голов
  • dhd_h = размерность головы
  • ss = длина последовательности
  • BB = размер батча
  • bytes\text{bytes} = байт на кэшируемый элемент, определяется точностью KV cache

Конкретные примеры для FP16 и батча 1: Llama 3.1 8B при 8 192 токенах занимает ~1,07 GB (1 GiB) KV cache; при 128K токенов — 16 GiB. Llama 3.1 70B при 128K токенов требует 40 GiB (около 43 GB) на одну последовательность, то есть примерно половину 80 GB у H100. При высокой конкурентности или длинном контексте KV cache может превысить память под веса модели; результат зависит от длины последовательности, размера активного батча, точности KV и числа KV-голов. В исходной оценке vLLM 2023 года сообщалось о 60–80% потерь из-за фрагментации и предварительного выделения в сравниваемых системах.

Основные оптимизации: GQA (меньше KV-голов), квантизация KV cache (FP8/INT8), PagedAttention (блочное выделение) и выгрузка повторно используемых данных KV на CPU или NVMe. Их польза зависит от нагрузки и реализации.

Чтобы ответить на вопрос «сколько последовательностей поместится в мой бюджет KV?» для другой модели, возьмите из её config.json три числа: num_hidden_layers, num_key_value_heads и head_dim, если это поле есть. В примерах Llama размерность головы равна hidden_size / num_attention_heads. Память на устройстве также зависит от шардинга кэша и возможной репликации KV-голов. Функция ниже применяет формулу; assert-ы воспроизводят числа выше и пример ёмкости из раздела 45.

def kv_cache_bytes(layers, kv_heads, head_dim, seq_len, batch=1, bytes_per_elem=2):
    """Bytes of KV cache: a key and a value per layer, KV head, and token."""
    return 2 * layers * kv_heads * head_dim * seq_len * batch * bytes_per_elem

GiB = 2**30

# Llama 3.1 8B: 32 layers, 8 KV heads, head dimension 128, FP16 cache.
llama_8b = kv_cache_bytes(32, 8, 128, seq_len=8_192)
print(f"8B at 8K tokens: {llama_8b / 1e9:.2f} GB")  # 1.07 GB
assert llama_8b == 1 * GiB

# Llama 3.1 70B: 80 layers, 8 KV heads, head dimension 128.
assert kv_cache_bytes(80, 8, 128, seq_len=131_072) == 40 * GiB

# How many 4K-token sequences fit in a 40 GiB KV budget?
per_4k = kv_cache_bytes(80, 8, 128, seq_len=4_096)
print(f"70B at 4K tokens: {per_4k / GiB:.2f} GiB per sequence")  # 1.25 GiB
print("4K sequences in 40 GiB:", 40 * GiB // per_4k)  # 32
assert 40 * GiB // per_4k == 32

# An FP8 KV cache halves the size per sequence and doubles the count.
assert 40 * GiB // kv_cache_bytes(80, 8, 128, 4_096, bytes_per_elem=1) == 64

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

7. Префилл и декод: две фазы, два боттлнека

Фаза префилла обрабатывает входной промпт параллельно и заполняет KV cache. Длинные или достаточно батчированные префиллы часто становятся compute-bound, потому что используют большие матричные умножения; короткие префиллы могут оставаться ограниченными трафиком памяти или накладными расходами на запуск ядер. Префилл вносит вклад в время до первого токена (TTFT) наряду с очередью, токенизацией, планированием и доставкой по сети; его проход вперёд также порождает первый выходной токен. Фаза декода генерирует по одному токену. На каждом шаге из HBM читаются веса модели и KV cache, поэтому декод с батчем 1 ограничен пропускной способностью памяти и главным образом определяет время на выходной токен (TPOT).

На рисунке показан один запрос: префилл повторно использует веса для разных токенов промпта и заканчивается первым токеном в пределах TTFT; каждый последующий шаг декодирования снова читает веса и KV cache для одного токена и влияет на TPOT.

Временная шкала одного запроса: после очереди префилл обрабатывает восемь токенов промпта за один проход, повторно использует для них веса и выдаёт токен t1, завершающий TTFT. Каждый шаг декодирования обрабатывает один токен, снова читает веса и KV cache из HBM и выдаёт следующий токен; интервал между двумя токенами — TPOT.Временная шкала одного запроса: после очереди префилл обрабатывает восемь токенов промпта за один проход, повторно использует для них веса и выдаёт токен t1, завершающий TTFT. Каждый шаг декодирования обрабатывает один токен, снова читает веса и KV cache из HBM и выдаёт следующий токен; интервал между двумя токенами — TPOT.

Chunked prefill разбивает длинный промпт на чанки и планирует их вместе с работающими декодами. Фактический размер чанка может меняться в зависимости от доступного бюджета токенов на итерацию. В Sarathi-Serve (раздел 4.2) и в vLLM планировщик сначала кладёт в батч работающие декоды, а остаток бюджета токенов на итерацию заполняет токенами префилла. Длинный промпт больше не мешает остальным запросам получать токены. Платит за это новый запрос: его префилл растягивается на несколько итераций, и каждый чанк заново читает KV cache предыдущих чанков. Sarathi-Serve измерил около 25% дополнительного времени префилла для Yi-34B с тензорным параллелизмом степени два и чанками по 512 токенов (раздел 5.4.1), а документация vLLM сообщает, что меньший бюджет токенов даёт лучшую межтокенную латентность и худший TTFT. Sarathi-Serve сообщает о лучших компромиссах между throughput и латентностью на проверенных нагрузках, но выигрыш зависит от модели, железа, распределения длин запросов, размера чанка и базы для сравнения.

Дезагрегированный сервинг размещает префилл и декод на отдельных пулах GPU, так что каждый пул может целиться в свой боттлнек. Этот паттерн описывают Splitwise и DistServe. Пул префилла отправляет KV cache каждого запроса в пул декода. DistServe использует NVLink внутри узла и InfiniBand между узлами; Splitwise использует InfiniBand, который поддерживает RDMA. Стоимость передачи становится частью архитектуры.

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

Две временные шкалы на одном GPU с общей временной осью; приходит длинный промпт, пока другие запросы находятся в декоде. Без чанкинга префилл всего промпта выполняется как один длинный шаг, работающие декоды ждут его целиком, а первый токен t1 нового запроса приходит в конце префилла. При chunked prefill чанки P1–P5 выполняются каждый в одной итерации вместе с шагом декода, так что декоды не останавливаются, но каждая итерация длится дольше, а t1 приходит позже, чем без чанкинга.Две временные шкалы на одном GPU с общей временной осью; приходит длинный промпт, пока другие запросы находятся в декоде. Без чанкинга префилл всего промпта выполняется как один длинный шаг, работающие декоды ждут его целиком, а первый токен t1 нового запроса приходит в конце префилла. При chunked prefill чанки P1–P5 выполняются каждый в одной итерации вместе с шагом декода, так что декоды не останавливаются, но каждая итерация длится дольше, а t1 приходит позже, чем без чанкинга.

8. GQA и MQA: сжатие KV cache

Стандартный Multi-Head Attention (MHA) выдаёт каждой query-голове собственные головы K и V. Multi-Query Attention (MQA) использует одну KV-голову на все query-головы — это радикальное сокращение. Grouped-Query Attention (GQA) — практичная золотая середина: группы query-голов делят одну KV-голову. На рисунке нарисованы восемь query-голов и KV-головы, которые остаются у каждого варианта; KV cache уменьшается пропорционально числу KV-голов.

Три панели по восемь query-голов в каждой. MHA хранит восемь KV-голов, по одной на query-голову. MQA хранит одну KV-голову, общую для всех восьми, то есть 12,5 процента кэша MHA. GQA хранит две KV-головы, каждая из которых общая для четырёх query-голов, то есть 25 процентов кэша MHA.Три панели по восемь query-голов в каждой. MHA хранит восемь KV-голов, по одной на query-голову. MQA хранит одну KV-голову, общую для всех восьми, то есть 12,5 процента кэша MHA. GQA хранит две KV-головы, каждая из которых общая для четырёх query-голов, то есть 25 процентов кэша MHA.

У Llama 3 70B 64 query-головы, но всего 8 KV-голов, то есть KV cache в 8 раз меньше, чем у той же архитектуры с одной KV-головой на каждую query-голову. У Llama 3.1 405B 128 query-голов и 8 KV-голов, то есть сокращение в 16 раз по тому же расчёту (Meta, 2024). Ainslie et al. сообщают, что качество GQA близко к MHA на проверенных моделях, а скорость приближается к MQA. Меньший KV cache позволяет использовать большие батчи, но реальный выигрыш в латентности и throughput всё равно зависит от ядра и нагрузки.

9. Квантизация: меняем биты на скорость и память

Квантизация снижает точность весов модели и (или) активаций. Основные компромиссы:

ФорматБитПамять под веса (модель 7B)Замечание о качестве
FP16/BF1616~14 GBБаза для сравнения
FP88~7 GBНативная аппаратная поддержка на Hopper; проверяйте модель
INT88~7 GBЗависит от калибровки и ядра
INT44~3,5 GBБолее сильное сжатие; проверяйте внимательно

AWQ (Activation-Aware Weight Quantization) определяет значимые каналы весов по величинам активаций и применяет поканальное масштабирование, чтобы их защитить. Потребность в калибровочных данных зависит от модели и конфигурации; в одном сравнении на OPT-6.7B INT3-g128 AWQ использовал 16 калибровочных последовательностей, а GPTQ — 192. В воспроизводимой настройке указывайте и число последовательностей, и их длину. GPTQ использует приближённую информацию второго порядка для послойной квантизации. bitsandbytes умеет квантизовать модель прямо при загрузке, без отдельного прохода предобработки; его формат NF4 лежит в основе файн-тюнинга QLoRA. FP8 на железе класса Hopper вдвое уменьшает память под веса по сравнению с FP16/BF16, но качество и скорость по-прежнему зависят от модели, калибровки и ядра.

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

МетодКалибровочные данныеТипичное применение
AWQ или GPTQ (INT4 weight-only)Да, небольшая выборкаСервинг большой модели на GPU с ограниченной памятью через поддерживаемое ядро INT4
bitsandbytes NF4НетФайн-тюнинг QLoRA и быстрые эксперименты
FP8Небольшая выборка или динамическое масштабированиеСервинг на Hopper или более новых GPU с проверкой качества на целевой задаче
INT8 (W8A8)Да, для масштабов активацийGPU без FP8, например A100 или A10G, если сервинговое ядро это поддерживает

Сервинговое ядро может значить не меньше, чем алгоритм квантизации. Раздел 10 приводит ограниченный по условиям результат Marlin и объясняет, почему выигрыш зависит от конфигурации сервинга.


Часть III — Оптимизации инференса

Оптимизации из этой части решают разные ограничения. FlashAttention сокращает трафик HBM в аттеншне; PagedAttention улучшает выделение памяти под KV; continuous batching не даёт завершённым последовательностям занимать место в батче.

10. CUDA-ядра и слияние ядер

CUDA-ядро (kernel) — функция, написанная для GPU, которая выполняется параллельно на тысячах потоков. Когда CPU вызывает ядро, GPU распределяет работу по своим SM: каждый SM запускает несколько варпов по 32 потока, и каждый поток обрабатывает свой фрагмент данных. Реализация сервинга на CUDA выполняет работу GPU через ядра. Число запусков зависит от графа модели, компилятора, рантайма и слияния. Реализация ядра может определять, уложится ли система в SLO по латентности.

Основные категории ядер в сервинге LLM:

  • GEMM-ядра для матричного умножения, которые доминируют в вычислениях и префилла, и декода.
  • Ядра аттеншна, такие как FlashAttention, которые разбивают вычисления на тайлы, чтобы оставаться в SRAM, а не выгружаться в HBM.
  • Слитые ядра, которые объединяют несколько операций (например, сложение + layer norm или проекцию QKV) в один запуск и пропускают промежуточные обращения к HBM.
  • Ядра сэмплирования, которые превращают логиты в ID токенов через top-k, top-p или температурное сэмплирование.

Качество ядра может определять, даст ли сжатие весов ускорение. В статье про Marlin сообщается о сквозном ускорении до 2,8x относительно базы FP16 для проверенных конфигураций vLLM с INT4 weight-only. Этот результат относится к моделям, GPU, размерам батча и конфигурации сервинга из статьи, поэтому его нельзя считать универсальным выигрышем от INT4.

Triton снижает порог входа в написание собственных ядер: он открывает программирование GPU через Python вместо чистого CUDA C++, и оптимизация на уровне ядер становится доступной ML-инженерам, а не только специалистам по GPU. Большинство оптимизаций дальше в этой части (FlashAttention, слитые ядра, PagedAttention) — это либо более удачные ядра, либо более умные способы оркестрации их запусков.

Слияние ядер объединяет последовательные операции в одно ядро GPU и пропускает промежуточные записи в HBM. Типичные слияния: проекция QKV, аттеншн вместе с softmax, остаточное сложение вместе с RMSNorm. FlashNorm вместо этого объединяет масштабирование RMSNorm со следующей линейной проекцией и откладывает нормализацию. DeepFusionKernel объединяет полный MLP SwiGLU, включая матричные умножения. Точный выигрыш в числе запусков и в утилизации зависит от графа модели, компилятора, GPU и фреймворка для сервинга, поэтому профилируйте развёрнутый стек, а не полагайтесь на универсальный процент.

11. FlashAttention: аттеншн по тайлам внутри SRAM

Стандартный аттеншн материализует полную матрицу внимания N×NN \times N в HBM, что стоит O(N2)O(N^2) памяти и порождает много трафика памяти. Идея FlashAttention — не материализовать эту матрицу вообще. Он разбивает матрицы Q, K, V на блоки, помещающиеся в SRAM, считает частичный аттеншн внутри каждого тайла и объединяет результаты с помощью online softmax (постепенно отслеживая текущий максимум и сумму по блокам). Объём промежуточного хранения снижается с O(N2)O(N^2) до O(N)O(N), с меньшим числом чтений и записей HBM для форм тензоров, проверенных в статье. Арифметика плотного аттеншна остаётся квадратичной по длине последовательности.

Каждая версия целится в боттлнек своего поколения GPU:

  • FlashAttention v1 (A100, 2022) доказал, что идея тайлинга с online softmax работает. В статье сообщалось об ускорении в 2–4 раза по сравнению со стандартным аттеншном, но всего о 25–40% утилизации GPU. Статья FA2 связывает это с работой вне матричных умножений, коммуникацией через shared memory и ограниченным параллелизмом.
  • FlashAttention v2 (A100, 2023) переработал параллелизм: работа делится по измерению последовательности, а также по батчу и головам. Он достиг 50–73% утилизации на A100, примерно в 2 раза быстрее v1.
  • FlashAttention v3 (H100 Hopper, 2024) добавил специализацию варпов (отдельные варпы для перемещения данных и для математики) и конвейеризацию GEMM и softmax, чтобы перекрывать загрузки из памяти с вычислениями. В статье сообщается о до 740 TFLOPS в FP16 (75% утилизации) и почти 1,2 PFLOPS в FP8 на H100.
  • FlashAttention v4 (B200 Blackwell, 2026) решает новый боттлнек: на Blackwell производительность тензорных ядер растёт так быстро, что ограничителем становятся немматричные операции (экспоненты softmax, перемасштабирование). FA4 программно эмулирует экспоненту полиномиальными приближениями на блоках FMA, использует условное перемасштабирование для снижения накладных расходов и хранит промежуточные значения в выделенной тензорной памяти Blackwell (TMEM), а не в регистрах. В статье сообщается о примерно 1,6 PFLOPS на B200 в BF16, в 1,3 раза быстрее cuDNN 9.13 и в 2,7 раза быстрее Triton в её тестах.

12. FlashDecoding: распараллеливаем боттлнек декода

Стандартный FlashAttention держит GPU занятым, разделяя работу по размеру батча и длине query. Во время декода модель генерирует ровно 1 токен за раз (длина query = 1). Если размер батча, умноженный на число attention heads, намного меньше числа SM у GPU (108 у A100), многие SM могут простаивать, пока активные SM обрабатывают историю токенов.

FlashDecoding решает это, добавляя новое измерение параллелизма: саму длину KV-последовательности. Он нарезает KV cache на более мелкие чанки и раздаёт их всем простаивающим процессорам GPU для параллельной обработки, а затем объединяет их частичные результаты через редукцию log-sum-exp.

В бенчмарке Stanford с батчем 1 на CodeLlama-34B на четырёх GPU A100 при длинах последовательностей от 512 до 64K токенов FlashDecoding достиг до 8x сквозного ускорения по сравнению с проверенными базами и улучшал масштабирование аттеншна на проверенных длинных контекстах. Результат ограничен этим железом и этим бенчмарком и не является общей гарантией для декода.

13. Continuous batching и static batching

Static batching ждёт завершения всех последовательностей в батче, прежде чем начать следующий, поэтому короткие последовательности впустую тратят такты GPU, простаивая после end-of-sequence. Continuous batching (представлен в статье про Orca, OSDI 2022) работает с гранулярностью итерации: на каждом шаге декода завершённые последовательности убираются, а новые вставляются. На рисунке показаны четыре слота батча на десяти шагах декода при каждой политике.

Две сетки из четырёх слотов батча на десяти шагах декода. При static batching слоты запросов B, C и D простаивают после завершения, пока не закончится запрос A: 6, 4 и 7 шагов простоя, 17 из 40 слот-шагов. При continuous batching новые запросы E, F и G занимают освободившиеся слоты на шагах 4, 5 и 7.Две сетки из четырёх слотов батча на десяти шагах декода. При static batching слоты запросов B, C и D простаивают после завершения, пока не закончится запрос A: 6, 4 и 7 шагов простоя, 17 из 40 слот-шагов. При continuous batching новые запросы E, F и G занимают освободившиеся слоты на шагах 4, 5 и 7.

В бенчмарке Anyscale 2023 года на OPT-13B на A100-40GB, с 1000 запросов, промптами по 512 токенов и выходами в среднем по 128 токенов, оптимизированный static batching достиг 4x от наивной базы, continuous batching — 8x, а vLLM с continuous batching и PagedAttention — 23x (Anyscale, 2023). Continuous batching также увеличивает нагрузку на выделение памяти под KV, поэтому его обычно сочетают со страничным управлением памятью.

14. PagedAttention: виртуальная память для KV cache

PagedAttention в vLLM применяет идею виртуальной памяти ОС к управлению KV cache. KV cache разбивается на блоки фиксированного размера (16 токенов в этой иллюстрации; размер блока зависит от рантайма), блоки выделяются по мере генерации токенов, а логические (последовательные) позиции отображаются на физические (разбросанные) области памяти через таблицы блоков. Несколько запросов с общим префиксом (системные промпты, beam search) могут ссылаться на одни и те же физические блоки. Повторное использование префиксов также требует совместимых ключей кэша и политики поиска в кэше. На рисунке показана таблица блоков для двух запросов с общим системным промптом.

У запроса A логические блоки A0–A2, у запроса B — B0 и B1. Записи таблицы блоков отображают их на разбросанные физические блоки в памяти GPU; A0 и B0 оба указывают на блок 7, где хранится общий системный промпт. В каждом блоке есть место для 16 токенов: последние блоки A и B заполнены частично, а блоки 0, 2, 4 и 6 свободны.У запроса A логические блоки A0–A2, у запроса B — B0 и B1. Записи таблицы блоков отображают их на разбросанные физические блоки в памяти GPU; A0 и B0 оба указывают на блок 7, где хранится общий системный промпт. В каждом блоке есть место для 16 токенов: последние блоки A и B заполнены частично, а блоки 0, 2, 4 и 6 свободны.

В оценке vLLM за июнь 2023 года сообщалось о 60–80% потерь памяти KV в проверенных прежних системах и о менее 4% со страничным аллокатором. Throughput был в 2–4 раза выше TGI и до 24x выше Hugging Face Transformers на отдельных нагрузках. Это исторические сквозные сравнения систем сервинга, а не универсальное ускорение или предел потерь PagedAttention. Частично заполненный последний блок может терять большую долю памяти на коротких последовательностях.

15. Speculative decoding: несколько токенов за один проход вперёд

В speculative decoding небольшая draft-модель генерирует KK токенов-кандидатов, а затем большая целевая модель оценивает все KK позиций за один проход вперёд. Декодер принимает токены черновика слева направо с вероятностями, выведенными из распределений целевой модели и черновика. После первого отклонения он сэмплирует поправку из остаточного распределения целевой модели и отбрасывает оставшиеся токены черновика. Этот модифицированный шаг rejection sampling сохраняет распределение выходов целевой модели с точностью до численных особенностей железа; одного точного совпадения токенов для этого недостаточно. На рисунке разобран один раунд с четырьмя токенами черновика.

Один раунд при K = 4: draft-модель предлагает d1–d4, целевая модель оценивает все четыре за один проход вперёд, d1 и d2 принимаются, d3 отклоняется и заменяется поправкой c3, сэмплированной из целевой модели, а d4 отбрасывается, так что раунд выдаёт три токена.Один раунд при K = 4: draft-модель предлагает d1–d4, целевая модель оценивает все четыре за один проход вперёд, d1 и d2 принимаются, d3 отклоняется и заменяется поправкой c3, сэмплированной из целевой модели, а d4 отбрасывается, так что раунд выдаёт три токена.

Выигрыш наиболее правдоподобен при малых батчах сервинга и коротких черновиках, когда оценка черновика определяется трафиком весов, KV cache или коммуникации, а не вычислениями для дополнительных токенов. В оригинальной статье про speculative sampling сообщалось об ускорении декодинга в 2–2,5 раза для проверенной конфигурации Chinchilla 70B; EAGLE-3 v3 сообщает об ускорении до 6,5x в своих тестах. Варианты включают Medusa (дополнительные головы предсказания, без отдельной модели), prompt lookup decoding (сопоставление n-грамм с входом без отдельного прохода draft-модели) и EAGLE. Исходный EAGLE экстраполирует признаки; EAGLE-3 объединяет несколько уровней признаков и напрямую предсказывает токены.

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

16. Prefix caching и повторное использование KV cache

Вместо того чтобы выбрасывать KV cache по завершении запроса, prefix caching сохраняет его для новых запросов с тем же префиксом токенов. Это сокращает лишний префилл для системных промптов, few-shot примеров, контекста RAG и истории многоходового диалога.

Automatic Prefix Caching в vLLM хеширует блоки KV и ищет их в глобальной хеш-таблице. RadixAttention в SGLang поддерживает radix-дерево кэшированных тензоров KV с гранулярностью до токена. Оба подхода требуют префиксов с одинаковыми токенами и совместимых ключей кэша. Идентичность модели или адаптера, мультимодальные входы и настройки изоляции также могут влиять на повторное использование. Указывайте hit rate рядом с латентностью или throughput.

17. Стриминг на практике

Стриминг отправляет токены клиенту по мере генерации, а не ждёт полного ответа. Многие фреймворки для сервинга отдают его через Server-Sent Events: клиент открывает долгоживущее HTTP-соединение, а сервер присылает каждый токен или пачку токенов событием data:. TTFT определяет, когда пользователь впервые увидит вывод; TPOT помогает определить, насколько плавным он будет. Целевое значение задавайте по результатам тестов продукта и выбранной модели взаимодействия.

На стороне клиента стриминг заставляет принимать решения о буферизации. Отрисовка токен за токеном может вызывать визуальное дрожание, особенно с markdown и блоками кода, которым для корректного форматирования нужен контекст из нескольких токенов. Распространённые паттерны: буферизация по словам (накапливать токены до границы пробела), буферизация по строкам (ждать перевода строки перед отрисовкой) и адаптивная буферизация (прозу показывать сразу, блоки кода буферизовать). В OpenAI Chat Completions API параметр stream_options: {"include_usage": true} добавляет финальный чанк с usage перед сообщением data: [DONE]. При прерывании стрима OpenAI финальный чанк с usage может отсутствовать. Серверы, совместимые с OpenAI, могут отличаться, поэтому проверяйте выбранную реализацию.

Chunked prefill — один из способов не дать длинным префиллам задерживать выдачу токенов параллельным пользователям. Планирование с приоритетом декода может защитить запросы, которые уже выполняются, а дезагрегированный сервинг изолирует префилл и декод на отдельных пулах GPU.


Часть IV — Архитектура модели

Архитектура задаёт объём памяти, поведение аттеншна и динамику обучения, вокруг которых строятся разделы про сервинг и обучение.

18. Основы архитектуры трансформера

Современный трансформер decoder-only (GPT, Llama) — это стопка одинаковых слоёв, в каждом из которых два подблока: аттеншн и feed-forward. Каждый подблок обёрнут residual-связью и нормализацией. На рисунке показан один слой в стиле Llama, включая два пути, которые обходят подблок, и то, как его веса проекций делятся между аттеншном и feed-forward сетью; компоненты описаны под ним.

Слева: один слой декодера Llama 3 8B, повторяющийся 32 раза: RMSNorm, grouped-query attention с 32 query-головами и 8 KV-головами и сложение, которое получает и вход слоя; затем RMSNorm, feed-forward сеть SwiGLU и второе сложение, которое получает и результат аттеншна. Справа: веса проекций на слой, около 42M у аттеншна и около 176M, то есть 81 процент, у feed-forward сети.Слева: один слой декодера Llama 3 8B, повторяющийся 32 раза: RMSNorm, grouped-query attention с 32 query-головами и 8 KV-головами и сложение, которое получает и вход слоя; затем RMSNorm, feed-forward сеть SwiGLU и второе сложение, которое получает и результат аттеншна. Справа: веса проекций на слой, около 42M у аттеншна и около 176M, то есть 81 процент, у feed-forward сети.

Ключевые компоненты:

Multi-Head Attention позволяет каждому токену взвешивать информацию от токенов, видимых по маске аттеншна. Вход проецируется в три матрицы: Queries (что я ищу?), Keys (что я содержу?) и Values (какую информацию я несу?). Оценки аттеншна затем вычисляются так:

Attention(Q,K,V)=softmax(QKTdk)V\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V

Скалярное произведение QKTQK^T измеряет сходство между каждой парой токенов. Деление на dk\sqrt{d_k} не даёт скалярным произведениям вырасти слишком сильно (иначе softmax попадает в области с затухающими градиентами). Softmax превращает оценки в вероятности, а умножение на VV даёт взвешенную комбинацию векторов значений. Параллельный запуск на нескольких головах позволяет модели одновременно обращать внимание на разные отношения (головы могут обучаться разным отношениям; их роли не фиксированы).

Feed-Forward Network (FFN) преобразует представление каждого токена независимо после того, как аттеншн смешал информацию между токенами. Современные LLM часто используют SwiGLU вместо исходной FFN с двумя матрицами и ReLU:

SwiGLU(x)=(Swish(xWgate)⊙xWup)Wdown\text{SwiGLU}(x) = \left(\text{Swish}(xW_{gate}) \odot xW_{up}\right)W_{down}

У SwiGLU три матрицы весов против двух у FFN с ReLU, и он использует гладкую функцию Swish. Его применяют Llama, Mistral и Qwen; Gemma использует приближённый GeGLU. Привычное правило «двух третей» применимо к обычному блоку с полным MHA и dff≈4dd_{ff} \approx 4d: две матрицы FFN дают около 8d28d^2 параметров против около 4d24d^2 у аттеншна. Для архитектур SwiGLU/GQA это не общая оценка. В Llama 3 8B d=4,096d=4{,}096, dff=14,336d_{ff}=14{,}336, а при восьми key/value-головах FFN даёт 3d×dff≈1763d \times d_{ff} \approx 176M параметров на слой против около 42M параметров проекций аттеншна: около 81% этих весов проекций, не считая эмбеддингов и нормализации.

Residual-связи прибавляют выход каждого подблока к его входу: Output=Input+Sublayer(Input)\text{Output} = \text{Input} + \text{Sublayer}(\text{Input}). Обходной путь улучшает распространение сигнала и градиентов через глубокие стопки.

RMSNorm распространён в современных семействах LLM. LayerNorm центрирует, вычитая среднее, и масштабирует по стандартному отклонению. RMSNorm пропускает вычитание среднего и только масштабирует; в её статье сообщается о сокращении времени выполнения на 7–64% на проверенных моделях и фреймворках без снижения измеренного качества на задачах. Pre-norm, то есть нормализация перед аттеншном или FFN, тоже распространена, потому что улучшает стабильность градиентов.

Оценка числа параметров для обычного декодера с полным MHA, FFN шириной 4d4d с двумя матрицами и общими входными и выходными эмбеддингами:

Total≈V×d+12×L×d2\text{Total} \approx V \times d + 12 \times L \times d^2

где VV — размер словаря, dd — скрытая размерность, а LL — число слоёв. Член V×dV \times d — это матрица входных эмбеддингов; член 12×d212 \times d^2 приближает веса аттеншна и FFN в каждом слое. Для Llama 3 8B (V=128,256V=128{,}256, d=4,096d=4{,}096, L=32L=32) оценка составляет около 0.53B+6.44B=6.97B0.53B + 6.44B = 6.97B параметров. Опубликованный итог в 8,03B выше, потому что приближение не учитывает архитектурные детали, такие как точная ширина FFN и отдельная выходная проекция.

19. Модели decoder-only для универсальной генерации

Исходный Transformer (2017) состоял из энкодера и декодера. С тех пор область разделилась на три архитектурных семейства, и одно из них стало стандартом для генеративного AI.

СемействоАттеншнЦель обученияДля чего используется сегодняПримеры
Encoder-onlyДвунаправленныйПредсказание замаскированных токеновЭмбеддинги, реранкеры, классификаторыBERT, RoBERTa
Encoder-decoderДвунаправленный энкодер; каузальный декодер с cross-attentionSequence to sequence (span corruption, T5)Перевод, распознавание речиT5, BART, Whisper
Decoder-onlyКаузальныйПредсказание следующего токенаГенерация текста общего назначения, чат, кодGPT, Llama

Модели encoder-only используют двунаправленный аттеншн: каждый токен обращает внимание на все остальные в обоих направлениях. Это даёт представления для задач понимания. Стандартные модели encoder-only обычно не используют как авторегрессионные декодеры; они часто служат базовой архитектурой для эмбеддинг-моделей, реранкеров и лёгких классификаторов (например, роутеров на основе BERT в RouteLLM).

Модели encoder-decoder разделяют понимание и генерацию. Энкодер обрабатывает весь вход с двунаправленным аттеншном, а затем декодер генерирует выход авторегрессионно, обращаясь к представлениям энкодера через cross-attention. Это подходит для задач, где вход и выход — разные последовательности, например для перевода и распознавания речи.

Модели decoder-only, например GPT, Llama и Mistral, используют каузальный аттеншн: каждый токен учитывает себя и предыдущие токены. Четыре полезных свойства для генерации:

  • Повторное использование KV cache. При каузальном аттеншне ранние токены никогда не обращаются к более поздним, поэтому их ключи и значения остаются корректными по мере генерации новых токенов. Один кэш на последовательность растёт добавлением (раздел 6). Encoder-decoder дополнительно хранит состояние cross-attention по выходу энкодера.
  • Обучение на сыром тексте. Для предсказания следующего токена не нужны парные вход и выход, поэтому любой текстовый корпус подходит как обучающие данные.
  • Один повторяющийся блок. Модель — это стопка одинаковых слоёв декодера, без отдельной стопки энкодера или пути cross-attention, которые пришлось бы строить, шардировать и оптимизировать.
  • In-context learning. Инструкции, few-shot примеры и запрос — всё это токены одного префикса, поэтому одна модель может освоить новую задачу из промпта.

20. Mixture of experts

MoE заменяет отдельные плотные FFN несколькими экспертными FFN и лёгким гейтинговым роутером. Роутер вычисляет оценку для каждого эксперта (обычно softmax по обучаемым линейным проекциям) и выбирает для каждого токена top-kk экспертов. Вычисляют только активированные эксперты, поэтому у модели может быть огромная суммарная ёмкость при низкой стоимости на токен. Это разреженные условные вычисления. Активные параметры приблизительно характеризуют арифметику на токен, но хранить нужно веса всех экспертов. Коммуникация, роутинг, заполнение батча и память KV также влияют на стоимость выполнения. Рисунок прослеживает один токен через слой MoE с шестью маршрутизируемыми экспертами и общим экспертом.

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

МодельВсего параметровАктивных параметровЭксперты (маршрутизируемые + общие)Top-kk
Mixtral 8x7B47B~13B8 + 02
DeepSeek-V3671B37B256 + 18

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

При обучении MoE регулярно встречаются три проблемы: дисбаланс нагрузки, коллапс экспертов и накладные расходы на коммуникацию для экспертного параллелизма. Традиционные модели MoE добавляют вспомогательный лосс, штрафующий несбалансированный роутинг, но он может конфликтовать с основной целью. DeepSeek-V3 в основном использует батчевую стратегию без вспомогательного лосса: смещения вне обратного распространения снижают оценку перегруженных экспертов и повышают оценку недогруженных. Дополнительно применяется крайне малый комплементарный лосс баланса на уровне последовательности, чтобы не допустить сильного дисбаланса внутри одной последовательности. В статье сообщается о более сбалансированном роутинге без главного компромисса вспомогательного лосса в их постановке.

21. Токенизация: BPE, SentencePiece и tiktoken

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

Byte Pair Encoding (BPE) — самый распространённый алгоритм. Он итеративно объединяет самые частые соседние пары в обучающем корпусе. Упрощённый пример:

  1. Начинаем со словаря из символов: [l, o, w, e, r, _]
  2. Самая частая пара — (l, o) → объединяем в lo → словарь: [l, o, w, e, r, _, lo]
  3. Следующая по частоте пара — (lo, w) → объединяем в low → в словарь добавляется low
  4. Продолжаем, пока словарь не достигнет целевого размера (например, 128K токенов)

Частые слова вроде «the» становятся одиночными токенами, а редкие, например «defenestration», распадаются на подслова вроде ["def", "en", "est", "ration"] в этом вымышленном примере. Фактическое разбиение зависит от токенизатора и его версии. Это компромисс между размером словаря и длиной последовательности.

Три реализации токенизаторов покрывают большую часть production-использования:

  • SentencePiece обучается прямо на сыром тексте Unicode без языково-специфичного пре-токенизатора. Он сохраняет пробелы с помощью метасимвола ▁ и при желании может откатываться к байтовым токенам UTF-8. Он поддерживает модели BPE и unigram и используется в Llama 1/2, T5 и ранних токенизаторах Mistral. Более новые токенизаторы Mistral Tekken используют BPE в стиле tiktoken.
  • tiktoken — токенизатор OpenAI на Rust с байтовым BPE. В опубликованном бенчмарке на GPT-2 он работал в 3–6 раз быстрее проверенной конфигурации GPT2TokenizerFast. Llama 3 перешла с SentencePiece на алгоритм tiktoken.
  • Hugging Face Tokenizers — широко используемая библиотека на Rust, поддерживающая BPE, WordPiece и Unigram.

Размер словаря сильно различается между семействами моделей:

МодельТокенизаторРазмер словаря
GPT-2Byte-level BPE50,257
Llama 2SentencePiece BPE32,000
Llama 3BPE в стиле tiktoken128,256
GPT-4otiktoken o200k_base~200K

При сопоставимых условиях обучения токенизатора больший словарь может означать меньше токенов на тот же текст, но и большую матрицу эмбеддингов (член V×dV \times d из раздела 18).

Fertility измеряет, сколько токенов токенизатор выдаёт на слово или другую выбранную единицу текста. Она зависит от конкретного токенизатора, языка, письменности, нормализации, предметной области и выборки. Измеряйте её на репрезентативном трафике, а не экстраполируйте с одного токенизатора или языка.

22. Контекстные окна и позиционные кодировки

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

МодельОпубликованный лимит токеновГод
Llama 12,0482023
Llama 3.1128K2024
GPT-4.11,047,5762025
Gemini 2.5 Pro1,048,576 на входе2025

GPT-4.1 также ограничивает выход 32 768 токенами, Gemini 2.5 Pro — 65 536. Строка Gemini указывает лимит входа, а GPT-4.1 — контекстное окно, поэтому эти поля не идентичны.

Аттеншн сравнивает каждую пару видимых токенов, но само сравнение не указывает расстояние между ними. Без позиционной информации полный self-attention не может различить порядок слов одного набора токенов; состояния токенов следуют перестановке входа. Каузальная маска декодера даёт некоторый сигнал порядка, поскольку каждый токен видит только свой префикс, но не задаёт явных относительных позиций. Позиционные кодировки явно добавляют позицию и относительное расстояние.

Три распространённых подхода:

  • RoPE (Rotary Position Embeddings) поворачивает векторы query и key на углы, зависящие от позиции, так что их скалярное произведение зависит от относительной позиции. Оценку аттеншна по-прежнему определяет содержимое токенов; RoPE добавляет информацию о позиции без обучаемых эмбеддингов абсолютных позиций. Его используют открытые семейства моделей, включая Llama, Mistral и Qwen.

  • ALiBi (Attention with Linear Biases) не меняет эмбеддинги и добавляет штраф прямо к оценкам аттеншна: чем дальше друг от друга два токена, тем больше отрицательное смещение. Обучаемых позиционных параметров у него нет. В исходной статье модель на 1,3B, обученная на последовательностях по 1 024 токена, на 2 048 токенах показала результат, сопоставимый с моделью с синусоидальными позициями, обученной на 2 048. Поведение за пределами проверенных в статье моделей и длин зависит от модели.

  • YaRN (Yet another RoPE extensioN) расширяет модель с RoPE за пределы её обучающего контекста. Он группирует частотные измерения в три категории и масштабирует каждую по-своему. В сравнении с одинаковой схемой обучения в статье с интерполяцией позиций YaRN использовал 400 вместо 1000 шагов обучения (приложение B.2). Более широкое утверждение аннотации о 10x меньшем объёме данных использует другое сравнение.


Часть V — Обучение и алайнмент

Эта часть различает цель, которая создаёт способности, техники, позволяющие обучению поместиться в доступное железо, и методы, которые затем формируют поведение модели.

23. Претрейнинг, файн-тюнинг и алайнмент

Претрейнинг авторегрессионной LLM использует самосупервизорное предсказание следующего токена на большом корпусе. Энкодерные модели могут использовать другие цели, например предсказание замаскированных токенов. Его вычислительные затраты охватывают много порядков величины; например, Llama 3 405B потребовала 3.8×10253.8 \times 10^{25} FLOPs. Supervised fine-tuning (SFT) адаптирует предобученную модель к размеченным данным конкретной задачи. RLHF / RLAIF формирует поведение по данным о предпочтениях: традиционный пайплайн RLHF собирает сравнения, обучает reward-модель, а затем оптимизирует политику. RLAIF заменяет часть человеческих оценок обратной связью, сгенерированной AI. Раздел 31 разбирает дистилляцию — отдельный путь, на котором выровненная модель-учитель генерирует обучающие данные для меньшей модели-ученика.

Вычислительные затраты зависят от размера модели, длины последовательности, объёма данных, оптимизатора и метода. PPO также несёт больше состояния модели, чем SFT, потому что типичная конфигурация включает модели политики, референса, reward и критика. Полную схему решения о файн-тюнинге я разобрал в статье Руководство по файн-тюнингу LLM.

24. LoRA и QLoRA: параметрически эффективный файн-тюнинг

LoRA замораживает предобученные веса и добавляет обучаемые матрицы низкого ранга AA (r×kr \times k) и BB (d×rd \times r), так что обновлённый вес равен W0+BAW_0 + BA. В статье про LoRA число обучаемых параметров GPT-3 175B в их постановке сократили до примерно 18 миллионов. Ранг — это параметр настройки, а не правило, привязанное к сложности задачи; выбирайте его перебором по качеству и памяти. Адаптеры LoRA после обучения можно слить с базовыми весами, чтобы не иметь отдельного пути адаптера на инференсе.

QLoRA загружает базовую модель в 4-битной квантизации NF4 и обучает адаптеры LoRA в BF16. NormalFloat4 размещает больше уровней квантизации вблизи нуля, где плотность весов наибольшая. В статье модель 65B дообучили на одном GPU на 48 GB и получили результаты, близкие к 16-битным базам. Компромиссы по рантайму и памяти относятся к проверенному стеку.

25. Обучение со смешанной точностью

Каждый формат с плавающей точкой распределяет свои биты между тремя полями: знак (всегда 1 бит), экспонента (задаёт динамический диапазон) и мантисса (задаёт точность). Больше бит экспоненты — шире диапазон представимых величин; больше бит мантиссы — тоньше различия между близкими значениями. У целочисленных форматов экспоненты нет совсем, они представляют только равномерно расположенные целые числа в фиксированном диапазоне.

ФорматБитРаскладка (S / E / M)ДиапазонТочностьТипичное применение
FP32321 / 8 / 23±3.4×1038\pm 3.4 \times 10^{38}~7 десятичных цифрМастер-веса, состояния оптимизатора (момент и дисперсия Adam)
BF16161 / 8 / 7±3.4×1038\pm 3.4 \times 10^{38}~2 десятичные цифрыПредпочтительный формат для обучения; тот же диапазон, что у FP32, и обычно без loss scaling
FP16161 / 5 / 10±65,504\pm 65{,}504~3 десятичные цифрыОбучение с loss scaling (старые GPU); инференс на железе до Hopper
FP8 E4M381 / 4 / 3±448\pm 448~1 десятичная цифраПроход вперёд на Hopper (H100) — выше точность для весов и активаций
FP8 E5M281 / 5 / 2±57,344\pm 57{,}344~0,6 десятичной цифрыПроход назад на Hopper — шире диапазон для градиентов
INT88целое со знаком−128-128 … 127127Точные целые числаПосттренировочная квантизация весов для инференса (W8A8); квантизация KV cache
INT44целое со знаком−8-8 … 77Точные целые числаАгрессивная квантизация только весов (AWQ, GPTQ) для инференса на железе с ограниченной памятью

BF16 имеет тот же диапазон, что FP32, потому что диапазон задаётся полем экспоненты, а BF16 сохраняет все 8 бит экспоненты из FP32. Взамен он отдаёт биты мантиссы (7 против 23): меняет точность на двукратное сокращение памяти и при этом избегает многих проблем с диапазоном, которые затрагивают обучение в FP16. У FP16 всего 5 бит экспоненты, поэтому его конечный диапазон ограничен примерно 65K. Многие градиенты, наоборот, слишком малы для FP16 и обнуляются при потере значимости. Loss scaling умножает лосс перед обратным распространением, чтобы эти градиенты оставались представимыми, а перед шагом оптимизатора возвращает масштаб; динамическое масштабирование уменьшает множитель при переполнении. Более широкий диапазон экспоненты BF16 обычно избавляет от этого требования.

Целочисленные форматы нетипичны для основной арифметики обучения, потому что обратному распространению нужен широкий динамический диапазон. Их широко используют для инференса, где замороженные веса можно отобразить на калиброванные масштабы. Квантизация весов в INT4 сокращает модель 7B примерно с 14 GB до 3,5 GB до накладных расходов рантайма; качество необходимо измерить для выбранной модели и метода.

Обучение в FP8 на H100 через Transformer Engine использует E4M3 там, где важна точность, и E5M2 там, где важнее более широкий диапазон. В статье FP8-LM сообщается, что её фреймворк смешанной точности обучал GPT-175B на 75% быстрее, чем база BF16 на Megatron-LM, и на 37% быстрее, чем NVIDIA Transformer Engine, в проверенной конфигурации на H100. DeepSeek-V3 использовала смешанную точность FP8 и сообщила о примерно $5.6 млн в пересчёте на аренду вычислений для финального обучающего прогона без учёта R&D и инфраструктуры.

26. Gradient checkpointing

Каждый слой прохода вперёд порождает промежуточный выход, называемый активацией:

Input→[Layer 1]→activation1→[Layer 2]→activation2→[Layer 3]→output\text{Input} \rightarrow [\text{Layer 1}] \rightarrow \text{activation}_1 \rightarrow [\text{Layer 2}] \rightarrow \text{activation}_2 \rightarrow [\text{Layer 3}] \rightarrow \text{output}

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

Gradient checkpointing меняет вычисления на память: большая часть активаций отбрасывается и пересчитывается на лету во время backprop. Стандартная стратегия (Chen et al., 2016) делит сеть из nn слоёв на n\sqrt{n} равномерно расположенных сегментов и сохраняет только граничную активацию каждого сегмента. Эти сохранённые границы и есть «чекпоинты». Все промежуточные активации внутри сегмента сразу отбрасываются.

Когда обратный проход доходит до слоя внутри сегмента, его активации пересчитываются от ближайшего чекпоинта. При равномерной сегментации память под сохранённые активации падает с O(n)O(n) до O(n)O(\sqrt{n}). Каждый сегмент пересчитывается один раз, что стоит примерно одного дополнительного прохода вперёд на шаг обучения (Chen et al., 2016). Реальная экономия памяти и накладные расходы на пересчёт зависят от модели, границ чекпоинтов, длины последовательности, фреймворка и реализации, поэтому измеряйте оба показателя на целевом прогоне обучения. FlashAttention применяет тот же принцип внутри аттеншна, не материализуя полную матрицу внимания. Для обычных запусков Hugging Face Trainer включите его через gradient_checkpointing=True. При FSDP используйте интеграцию activation checkpointing фреймворка, если она поддерживается; общая опция может вызывать избыточные all-gather в обратном проходе.

27. Стадии DeepSpeed ZeRO

При стандартном параллелизме данных каждый GPU хранит полную копию весов модели, градиентов и состояний оптимизатора. Для Adam каждый параметр занимает 2 байта на вес FP16 + 4 байта на мастер-вес FP32 + 4 байта на момент + 4 байта на дисперсию + 2 байта на градиент, то есть 16 байт на параметр. Модели на 7,5B параметров нужно ~120 GB на каждый GPU, и каждый GPU хранит одно и то же. На 64 GPU это 64 одинаковые копии по 120 GB.

DeepSpeed ZeRO (Zero Redundancy Optimizer) убирает это дублирование: вместо репликации компоненты шардируются между GPU:

  • Стадия 1 — разделение состояний оптимизатора. Каждый GPU хранит только 1/N состояний оптимизатора (мастер-веса FP32 плюс первый и второй моменты Adam, 12 байт/параметр). Когда GPU нужно обновить вес, он обновляет только свой фрагмент и рассылает результат. При допущениях ниже память падает со ~120 GB до ~41,3 GB на GPU.
  • Стадия 2 — дополнительно разделяются градиенты. Градиенты (2 байта/параметр) больше не сводятся через all-reduce на каждый GPU. Каждый GPU получает через reduce-scatter только тот фрагмент градиента, который ему нужен. При тех же допущениях память падает до ~28,1 GB на GPU.
  • Стадия 3 — дополнительно разделяются веса модели. Каждый GPU хранит только 1/N весов FP16. Перед проходом вперёд или назад каждого слоя GPU вызывает all-gather, чтобы временно собрать полные веса слоя со всех остальных GPU, считает и отбрасывает собранные веса. При тех же допущениях память падает до ~15,0 GB на GPU.

Рисунок в масштабе показывает состояние модели на каждом GPU на каждой стадии вместе со стоимостью коммуникации на этой стадии.

Столбцы в масштабе для 7,5B параметров на 8 GPU. Без ZeRO каждый GPU хранит 90 GB состояний оптимизатора, 15 GB градиентов и 15 GB весов, всего 120 GB. Стадия 1 шардирует состояния оптимизатора, получается 41,3 GB, стадия 2 дополнительно градиенты, 28,1 GB, а стадия 3 дополнительно веса, 15,0 GB, ценой all-gather перед каждым слоем.Столбцы в масштабе для 7,5B параметров на 8 GPU. Без ZeRO каждый GPU хранит 90 GB состояний оптимизатора, 15 GB градиентов и 15 GB весов, всего 120 GB. Стадия 1 шардирует состояния оптимизатора, получается 41,3 GB, стадия 2 дополнительно градиенты, 28,1 GB, а стадия 3 дополнительно веса, 15,0 GB, ценой all-gather перед каждым слоем.

Это приблизительные значения состояния модели для модели на 7,5B параметров на 8 GPU (размер мира N=8N=8) с весами и градиентами FP16 и мастер-весами, моментом и дисперсией Adam в FP32. Они не включают активации, временные буферы all-gather, фрагментацию аллокатора и накладные расходы фреймворка и рантайма. Граница расчёта: 7.5B×7.5B \times байт на параметр, причём каждое состояние делится на NN только на тех стадиях, которые его шардируют.

Чтобы повторить расчёт для другого размера модели или числа GPU:

def zero_model_state_gb(params_b, n_gpus, stage):
    """Model-state GB per GPU for Adam with FP16 weights and gradients.

    Optimizer states (FP32 master weights, momentum, variance) take 12 bytes
    per parameter, gradients 2, and weights 2. Stage 1 shards the optimizer
    states, stage 2 also the gradients, and stage 3 also the weights.
    """
    optimizer, gradients, weights = 12, 2, 2
    if stage >= 1:
        optimizer /= n_gpus
    if stage >= 2:
        gradients /= n_gpus
    if stage >= 3:
        weights /= n_gpus
    return params_b * (optimizer + gradients + weights)

for stage in range(4):
    print(f"stage {stage}: {zero_model_state_gb(7.5, 8, stage):g} GB per GPU")

assert [zero_model_state_gb(7.5, 8, s) for s in range(4)] == [120.0, 41.25, 28.125, 15.0]
# 70B parameters: Stage 3 on 8 GPUs still needs 140 GB of model state per GPU;
# 16 GPUs bring it to 70 GB, before activations.
assert zero_model_state_gb(70, 8, 3) == 140.0
assert zero_model_state_gb(70, 16, 3) == 70.0

Цена — коммуникация. Стадия 1 добавляет минимальные накладные расходы, а стадия 2 заменяет all-reduce на reduce-scatter при сопоставимой стоимости. Стадии 3 нужны вызовы all-gather перед каждым слоем и на проходе вперёд, и на проходе назад, то есть примерно в 1,5 раза больший объём коммуникации по сравнению со стандартным параллелизмом данных.

ZeRO-Infinity расширяет стадию 3: разделённые состояния выгружаются в оперативную память CPU и даже на NVMe SSD, что может сделать возможным обучение моделей с триллионами параметров на ограниченных кластерах GPU. Выгрузка на накопители добавляет стоимость PCIe и передачи данных; измеряемый эффект зависит от диска, топологии PCIe, разделения, предвыборки, перекрытия передач и нагрузки. Используйте её, чтобы удовлетворить требования к ёмкости, а не исходя из фиксированного замедления, и профилируйте целевую конфигурацию.

28. FSDP: нативное шардирование в PyTorch

Fully Sharded Data Parallel (FSDP) — встроенный в PyTorch ответ на DeepSpeed ZeRO-3. Он шардирует параметры, градиенты и состояния оптимизатора между GPU на той же основной идее. При включённых полном шардинге и решардинге операции для каждой обёрнутой единицы такие:

  1. All-gather полных параметров со всех GPU (временно собрать слой целиком).
  2. Вычислить проход вперёд или назад для этого слоя.
  3. Освободить собранные параметры сразу. Каждый GPU хранит только свой шард.
  4. Reduce-scatter градиентов, чтобы каждый GPU получил только отведённый ему фрагмент градиента.

Поскольку FSDP нативен для PyTorch, он напрямую интегрируется с инструментами отладки PyTorch, профилировщиками и torch.compile. Производительность относительно DeepSpeed ZeRO-3 зависит от политики обёртывания, топологии коммуникаций, настроек выгрузки и размера модели, поэтому сравнивайте их на одном кластере.

КритерийFSDP (PyTorch)DeepSpeed ZeRO
Стиль управленияПолное шардирование через API PyTorchВыбираемые стадии ZeRO
ВыгрузкаВыгрузка на CPUCPU + NVMe с ZeRO-Infinity
Интеграция с фреймворкомНативный PyTorch, пути torch.compileОтдельная библиотека и система конфигурации
Критерий выбораПрофилируйте целевую нагрузку на PyTorchПрофилируйте нужные функции и выгрузку

FSDP2 (2024–2025) — переписанная версия, которая лучше интегрируется с torch.compile для более эффективного слияния ядер, добавляет поддержку обучения в FP8 через TorchAO и упрощает API. И FSDP, и DeepSpeed доступны через Hugging Face Accelerate, который поддерживает оба варианта через конфигурацию для совместимых, подготовленных скриптов обучения. Требуемые настройки wrapping, checkpointing и offload всё равно различаются.

29. Scaling laws и стоимость инференса

Chinchilla scaling (DeepMind, 2022) нашёл вычислительно оптимальное распределение около 20 обучающих токенов на параметр при своих допущениях. Эта цель не включает стоимость последующего сервинга. Если меньшая модель, обученная на большем объёме данных, достигает нужного качества, она может обойтись дешевле за жизненный цикл с большим объёмом инференса.

Одна из стратегий снижения стоимости жизненного цикла — обучить меньшую модель на гораздо большем объёме данных:

МодельПараметрыТокены обученияТокенов/параметрТокенов/параметр ÷ 20 (расчёт)
Chinchilla70B1.4T20:11×
Llama 165B1.4T22:11×
Llama 270B2.0T29:11.4×
Llama 3 8B8B15T1,875:194×
Qwen3-0.6B0.6B36T60,000:13,000×

Это указанное в таблице отношение токенов к параметрам, делённое на приблизительную точку в 20 токенов на параметр из статьи про Chinchilla. Это описательное отношение, а не измеренный множитель качества или стоимости.

Для модели, которая обслуживает большой объём запросов, дополнительные вычисления на обучение меньшей модели могут снизить стоимость жизненного цикла. Llama 3 8B иллюстрирует эту стратегию, но выигрывает ли она, зависит от требуемого качества и прогнозируемого объёма инференса. «Chinchilla-optimal» относится к эффективности вычислений на обучение, а это другая цель, чем стоимость жизненного цикла.

30. RLHF, DPO, GRPO и ландшафт алайнмента

Алайнмент направляет предобученную модель к желаемым инструкциям, предпочтениям и политикам безопасности. Сам по себе он не гарантирует правдивость или безопасное поведение. Методы ниже по-разному балансируют сложность реализации, требования к данным, исследование и стабильность обучения.

Классический пайплайн RLHF: SFT → сбор пар человеческих предпочтений → обучение reward-модели на этих парах → файн-тюнинг политики с помощью PPO (Proximal Policy Optimization). В обычной конфигурации PPO четыре роли моделей: политика, референс, критик/value-модель и reward-модель. Потребление памяти зависит от совместного использования, шардинга, точности, выгрузки и размещения; четыре роли не означают четыре полные копии в памяти GPU. PPO также чувствителен к гиперпараметрам. Он также склонен к reward hacking, когда модель эксплуатирует особенности reward-модели, например многословные уверенно звучащие ответы, вместо реального улучшения качества.

DPO (Direct Preference Optimization) обходится без обучаемой reward-модели и онлайнового цикла RL, напрямую оптимизируя лосс на парах предпочтений. Это упрощает пайплайн обучения. Стандартный DPO офлайновый: он обучается на фиксированном датасете и не исследует новые ответы в цикле обновления. Важно ли это ограничение, зависит от задачи и покрытия данных.

GRPO (Group Relative Policy Optimization, DeepSeek) убирает обучаемого критика из PPO: для каждого промпта генерируется несколько ответов, а в качестве базы используются групповые относительные награды. Это снижает нагрузку на состояние модели по сравнению с типичной конфигурацией PPO. В отличие от DPO, GRPO on-policy: модель генерирует свежие ответы прямо во время обучения. DeepSeek-R1 сочетает GRPO с RLVR (reinforcement learning from verifiable rewards), используя проверки вроде ответов на математические задачи, компиляции кода и юнит-тестов. Такие награды проще аудировать, чем выученную оценку предпочтений, но неполные тесты и прокси-цели всё равно можно эксплуатировать.

МетодРоли моделей в обычной конфигурацииСигнал наградыOnline/offlineКлючевое ограничение
PPOПолитика, референс, критик, rewardОбученная reward-модельOnlineReward hacking, сложная настройка
DPOПолитика и референсные оценки, которые можно вычислить заранееНеявный (пары предпочтений)OfflineНет исследования, фиксированные данные
GRPOПолитика; опциональный референс для KL и опциональная обученная reward-модельЯвный (правило/верификатор или обученный)OnlineЗависит от качества награды и информативного разброса внутри группы

Референсная политика в GRPO зависит от реализации. Например, TRL GRPOTrainer не загружает референсную модель, когда коэффициент KL beta равен нулю. При сравнении памяти для обучения учитывайте размещение моделей и точность.

31. Дистилляция: сжатие знаний между моделями

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

DeepSeek-R1 отобрала смесь примерно из 800 000 примеров — около 600 000 образцов, связанных с ризонингом, и 200 000 образцов без ризонинга — и использовала её для дистилляции моделей Qwen2.5 и Llama 3 размером от 1,5B до 70B параметров. В оценке из статьи:

  • DeepSeek-R1-Distill-Qwen-32B набирает 72,6% на AIME 2024 и 94,3% на MATH-500, выше приведённых в статье результатов OpenAI o1-mini.
  • DeepSeek-R1-Distill-Qwen-7B набирает 55,5% на AIME 2024, выше результата QwQ-32B-Preview из статьи, при меньшей модели.

В экспериментах DeepSeek-R1 с малыми моделями дистилляция превзошла прямой GRPO на проверенных базовых моделях. Этот результат говорит в пользу дистилляции для данной постановки; он не устанавливает универсального рейтинга между дистилляцией и RL.

32. Генерация синтетических данных

Обучающие данные, сгенерированные LLM, используются в нескольких повторяющихся паттернах:

  • Self-Instruct стартует с небольшого начального набора написанных человеком инструкций: LLM генерирует новые инструкции, входы и выходы, которые фильтруются и возвращаются в пул. В проекте Alpaca использовали 52 000 примеров, сгенерированных из 175 начальных. Stanford сообщил о стоимости генерации данных менее $500 и файн-тюнинга менее $100, то есть о стоимости первоначального воспроизведения менее $600; сравнение с GPT-3.5 было ограниченной оценкой проекта, а не доказательством широкой эквивалентности.
  • Evol-Instruct (WizardLM) берёт существующие инструкции и итеративно развивает их по осям сложности (добавляет ограничения, углубляет ризонинг, делает задачи конкретнее), чтобы получать всё более сложные обучающие примеры.
  • Phi-4 (14B) от Microsoft использовала синтетические данные для значительной части претрейнинга, включая генерацию, критику, самопересмотр и обращение инструкций. Её технический отчёт сравнивает полученные результаты по STEM и коду с более крупными моделями на выбранных бенчмарках.

Один из рисков — коллапс модели при рекурсивной замене реальных данных сгенерированными. В изученных условиях редкие части исходного распределения постепенно исчезают (Shumailov et al., опубликовано в Nature в 2024 году). Это не неизбежный результат любого использования синтетических данных. Отдельное исследование накопления обнаружило иное поведение, когда реальные и сгенерированные данные сохранялись между поколениями. Отдельное исследование классификатора Ahrefs взяло по одной недавно обнаруженной английской странице на домен из 900 000 страниц в апреле 2025 года и классифицировало 74,2% как содержащие хотя бы часть текста, сгенерированного AI. Это исследование вендора не является переписью всего веба. Смягчение начинается со смешивания синтетических и реальных данных, фильтрации и отслеживания происхождения, чтобы рекурсивно сгенерированный материал можно было измерить.


Часть VI — Масштабирование и деплой

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

33. Четыре формы параллелизма

Эти четыре формы различаются тем, какую часть модели хранит каждый GPU и что передаётся по связи между GPU. Рисунок показывает каждую из них на двух GPU.

Четыре карточки, в каждой GPU 0 и GPU 1 и модель из четырёх слоёв. Параллелизм данных: оба хранят полную модель, и по связи на запрос ничего не передаётся. Тензорный параллелизм: каждый хранит половину каждой матрицы весов, и после каждого слоя они делают all-reduce. Конвейерный параллелизм: GPU 0 хранит слои 1 и 2, GPU 1 — слои 3 и 4, активации передаются на границе. Экспертный параллелизм: каждый хранит половину экспертов, токены перемещаются по схеме all-to-all. Каждая карточка также подсказывает, когда с неё стоит начинать.Четыре карточки, в каждой GPU 0 и GPU 1 и модель из четырёх слоёв. Параллелизм данных: оба хранят полную модель, и по связи на запрос ничего не передаётся. Тензорный параллелизм: каждый хранит половину каждой матрицы весов, и после каждого слоя они делают all-reduce. Конвейерный параллелизм: GPU 0 хранит слои 1 и 2, GPU 1 — слои 3 и 4, активации передаются на границе. Экспертный параллелизм: каждый хранит половину экспертов, токены перемещаются по схеме all-to-all. Каждая карточка также подсказывает, когда с неё стоит начинать.

Тензорный параллелизм (TP) разделяет отдельные матрицы весов между GPU и обычно обменивается данными после каждого слоя. Быстрые внутриузловые связи вроде NVLink делают его наиболее практичным внутри одного узла. Больше шардов уменьшают память и вычисления на устройство, но увеличивают коммуникацию, поэтому степень параллелизма выбирайте по бенчмарку латентности.

Конвейерный параллелизм (PP) последовательно разделяет слои между GPU, передавая активации между стадиями. Его паттерн коммуникации может работать между узлами, но пузыри конвейера и неравномерное время стадий снижают утилизацию. Крупные деплои часто сочетают TP внутри узла и PP между узлами.

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

Экспертный параллелизм (EP) распределяет экспертов MoE между GPU, используя коммуникацию all-to-all для роутинга токенов. Его производительность зависит от баланса токенов, размещения экспертов и топологии соединений; трафик all-to-all может стать главным боттлнеком.

Стартовая эвристика по параллелизму:

  • Модель помещается на один GPU: начните с независимых реплик и измерьте масштабирование DP.
  • Модель помещается в один узел: проверьте TP внутри узла, затем реплицируйте группу, если этого требует трафик.
  • Модель занимает несколько узлов: проверьте сочетание TP и PP относительно соединения и целевой латентности.
  • Mixture of experts: добавляйте EP, только если этого требует размещение экспертов.

34. Сравнение фреймворков для сервинга

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

РантаймКогда использоватьЧто проверить в первую очередь
vLLMУниверсальный сервинг на GPU с OpenAI-совместимым APIЦелевую модель и GPU в актуальной матрице совместимости
SGLangТрафик делит длинные префиксы (системные промпты, многоходовой чат, агенты) или требует structured outputHit rate prefix cache на вашем собственном трафике
TensorRT-LLMПарк состоит только из NVIDIA, и минимальная латентность важнее переносимостиПоддержку вашей модели и точности (FP8, FP4)
llama.cppCPU, Apple silicon или один потребительский GPU, квантизованные модели GGUFТокены в секунду по его бенчмарк-инструменту на целевой машине
OllamaЛокальная разработка и демоПереходите на рантайм для сервинга до высокой конкурентности или работы с SLO

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

  1. Одна локальная машина или один потребительский GPU: начните с llama.cpp, а для разработки с Ollama.
  2. Только NVIDIA и латентность приоритетна: включите TensorRT-LLM в бенчмарк.
  3. Длинные общие префиксы или structured output: SGLang.
  4. Иначе: vLLM.

Проверьте первый выбор бенчмарком на ваших промптах, длинах вывода, железе и SLO.

vLLM предоставляет страничное выделение KV, continuous batching, OpenAI-совместимый API и несколько режимов параллелизма. Поддержка моделей и железа в нём часто меняется.

SGLang сочетает RadixAttention для повторного использования префиксов, собственный планировщик и структурированную генерацию. Опубликованный выигрыш в throughput зависит от нагрузки и конфигурации.

TensorRT-LLM нацелен на низкую латентность отдельных запросов за счёт захвата и повторного выполнения графов CUDA, которые снижают накладные расходы на запуск, и оптимизации ядер, включая слияние, с нативной поддержкой FP8/FP4. Более ранние версии, например v1.2.0, поддерживали предварительную сборку движка TensorRT. В текущем руководстве по миграции NVIDIA указывает PyTorch как единственный бэкенд: API LLM и trtllm-serve напрямую загружают чекпоинт Hugging Face, без сборки движка. Опубликованные показатели относятся к конкретному оборудованию и моделям; TensorRT-LLM работает только на GPU NVIDIA.

TGI был архивирован 21 марта 2026 года, и его репозиторий доступен только для чтения. В его README для новых деплоев рекомендуются vLLM, SGLang, llama.cpp или MLX.

Ollama делает упор на простой локальный рабочий процесс с моделями.

llama.cpp — переносимый рантайм на C/C++ с путями для ARM, x86, Metal, CUDA, ROCm и Vulkan. GGUF поддерживает несколько уровней квантизации. Производительность сильно зависит от модели, квантизации, контекста и бэкенда.

35. Выбор GPU для инференса

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

GPUПамятьПропускная способностьНативная низкая точностьNVLink на GPU
B200 SXM180 GB HBM3eДо 8 TB/sFP4, FP81,8 TB/s
H200 SXM141 GB HBM3e4,8 TB/sFP8900 GB/s
H100 SXM80 GB HBM33,35 TB/sFP8900 GB/s
A100 80 GB SXM80 GB HBM2e2,039 TB/sINT8 (без FP8)600 GB/s
L40S48 GB GDDR6864 GB/sFP8Нет
A10G (AWS G5)24 GB GDDR6600 GB/sINT8 (без FP8)Нет
RTX 409024 GB GDDR6X~1 TB/sFP8Нет

Строка A10G использует спецификацию NVIDIA A10 как приближение пропускной способности; указанная спецификация AWS G5 не устанавливает точную пропускную способность A10G. Значение для RTX 4090 (1008 GB/s) взято из документа об архитектуре Ada.

Ёмкость 141 GB у H200 может упростить некоторые деплои больших моделей. У карт GDDR в нижних строках пропускная способность составляет треть от H100 или меньше, и NVLink отсутствует, поэтому они лучше подходят для квантизованных моделей, помещающихся на один GPU, чем для моделей, которым нужен тензорный параллелизм.

AWQ и GPTQ обслуживают 4-битные модели, деквантизуя поддерживаемые матричные операции в вычислительный формат вроде FP16 или BF16. Совместимость и скорость всё равно зависят от архитектуры модели, формата квантизации, бэкенда сервинга, ядра и GPU, поэтому проверяйте матрицу поддержки бэкенда и бенчмаркайте конкретный артефакт. Все перечисленные GPU поддерживают матричные операции INT8.

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

36. Каскадирование и роутинг моделей

Роутинг моделей выбирает, какая LLM обработает каждый запрос, исходя из предсказанной сложности или нужных возможностей. RouteLLM (LMSYS/UC Berkeley, ICLR 2025) сообщает в таблице 6 об отношении стоимости 3,66x при 95% базового балла GPT-4-Turbo на MT-Bench. Это соответствует примерно 73% снижению расчётной стоимости при ценах, слабой модели и схеме оценки того эксперимента. Окупится ли роутинг, зависит от текущих цен, структуры трафика, ошибок роутера и минимально допустимого качества.

Роутеры варьируются от лёгких классификаторов до джаджей на основе LLM. Каскадирование — последовательный вариант: запрос начинает с более дешёвой модели и переходит к более сильной, если функция оценки отклоняет ответ. FrugalGPT сообщает о снижении стоимости до 98% или росте точности до 4% в оцениваемом пуле моделей. Production-каскаду нужны откалиброванные критерии эскалации и мониторинг запросов, которые дешёвая модель приняла ошибочно. Рисунок показывает, где каждый подход делает свой выбор и где каждый из них даёт сбой.

Две панели. Роутинг: роутер отправляет сложные запросы сильной модели, а простые — дешёвой, так что работает одна модель; ошибка роутера отправляет сложный запрос дешёвой модели. Каскадирование: сначала всегда отвечает дешёвая модель; если оценка достигает порога, её ответ возвращается, иначе запрос эскалируется сильной модели. Эскалированные запросы оплачивают два вызова, а ложное принятие пропускает неверный ответ дешёвой модели.Две панели. Роутинг: роутер отправляет сложные запросы сильной модели, а простые — дешёвой, так что работает одна модель; ошибка роутера отправляет сложный запрос дешёвой модели. Каскадирование: сначала всегда отвечает дешёвая модель; если оценка достигает порога, её ответ возвращается, иначе запрос эскалируется сильной модели. Эскалированные запросы оплачивают два вызова, а ложное принятие пропускает неверный ответ дешёвой модели.


Часть VII — Приложения

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

37. Эмбеддинг-модели и генеративные модели

Эмбеддинг-модели кодируют текст в векторы фиксированной размерности, которые передают семантический смысл. Сравниваемые ниже одновекторные модели возвращают один плотный вектор на вход, обычно размерностью от нескольких сотен до нескольких тысяч. Другие архитектуры retrieval могут хранить несколько векторов на уровне токенов; следующее сравнение хранения относится к одновекторным эмбеддингам. Их основой служат двунаправленные трансформеры encoder-only и модели на основе декодера, адаптированные для обучения представлений. Слой пулинга часто сворачивает представления отдельных токенов в один вектор через усреднение, специальный классификационный токен или метод последнего токена, зависящий от модели. Затем контрастивный файн-тюнинг сближает семантически похожие тексты и отдаляет непохожие.

Современные эмбеддинг-модели различаются свойствами, которые определяют стоимость хранения и выбор индекса: выходной размерностью, принимаемыми типами входа, способом развёртывания и точностью выхода.

МодельМакс. размерностьВходВесаНативная точность выхода
Qwen3-Embedding-8B4,096 (32–4,096 через MRL)ТекстОткрытые (Apache 2.0)Float
Gemini Embedding 23,072 (128–3,072)Текст, изображение, видео, аудио, PDFAPIНе указана
pplx-embed-v1-4B2,560 (MRL)ТекстОткрытые (MIT)INT8 и бинарная
OpenAI text-embedding-3-large3,072 (сокращается через dimensions)ТекстAPIFloat

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

Matryoshka Representation Learning (MRL, Kusupati et al., NeurIPS 2022) делает размерность эмбеддинга гибкой. Название дано по русским матрёшкам. MRL обучает выбранные префиксы эмбеддингов сохранять полезную информацию. В экспериментах эти префиксы сравниваются с отдельно обученными представлениями меньшей размерности; совпадение качества — эмпирический результат, а не гарантия для любой задачи. Во время обучения MRL агрегирует лоссы по выбранному набору из O(log⁡d)O(\log d) префиксных размерностей, обычно по последовательным делениям пополам; в примере из статьи для 2048-мерного случая это {8,16,…,1024,2048}\{8, 16, \ldots, 1024, 2048\}. Агрегированный лосс заставляет первые измерения нести грубую семантическую информацию, а последующие добавляют более тонкие детали.

После обучения эмбеддинг MRL можно обрезать до поддерживаемой префиксной размерности. OpenAI сообщает, что text-embedding-3-large на 256 измерениях превосходит text-embedding-ada-002 на 1 536 измерениях в приведённом сравнении на MTEB. Это даёт сокращение объёма сырых векторов в 6 раз; задержка поиска и стоимость базы данных также зависят от индекса, метаданных, фильтрации и железа.

Эмбеддинг-модель — один из важных компонентов пайплайна RAG наряду с парсингом, чанкингом, поиском, реранкингом и генерацией. Если релевантные данные не извлечены, более сильный генератор не сможет надёжно это исправить.

38. Архитектура RAG в production

Retrieval-Augmented Generation предоставляет LLM документы, найденные в момент запроса. Он может дать актуальные или приватные данные, которых нет в весах модели, но retrieval не гарантирует, что ответ использует эти данные правильно. Production-система RAG — это многостадийный пайплайн, каждую стадию которого нужно оценивать отдельно. Рисунок показывает офлайновый путь индексации, путь отдельного запроса и места, где возникают три перечисленных ниже режима отказа.

Архитектура RAG. Офлайн: разобрать и разбить документы на чанки, получить эмбеддинг каждого чанка и сохранить векторы в индексе. На запрос: при необходимости переписать вопрос через HyDE, получить его эмбеддинг той же моделью, выполнить гибридный поиск (dense и BM25) по индексу, переранжировать кандидатов и дать LLM ответить по верхним чанкам. Пронумерованные метки показывают, что сбой retrieval происходит на поиске, а отравление контекста и lost-in-the-middle — на входе LLM.Архитектура RAG. Офлайн: разобрать и разбить документы на чанки, получить эмбеддинг каждого чанка и сохранить векторы в индексе. На запрос: при необходимости переписать вопрос через HyDE, получить его эмбеддинг той же моделью, выполнить гибридный поиск (dense и BM25) по индексу, переранжировать кандидатов и дать LLM ответить по верхним чанкам. Пронумерованные метки показывают, что сбой retrieval происходит на поиске, а отравление контекста и lost-in-the-middle — на входе LLM.

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

Чанкинг влияет и на recall при retrieval, и на контекст, доступный генератору. Подходящие размеры зависят от структуры документов, гранулярности запросов, ограничений эмбеддера и ограничений реранкера. Распространённые подходы: фиксированный размер с перекрытием, рекурсивное разбиение по границам документа и семантический чанкинг по сходству эмбеддингов. Сравнивайте их по меткам релевантности на уровне страниц или разделов, а не принимайте один диапазон токенов как универсальный.

Каждый чанк затем получает эмбеддинг от модели вроде тех, что описаны в разделе 37, и сохраняется в векторном индексе. pgvector хранит векторы в уже работающей у вас базе Postgres. Специализированное хранилище, такое как Qdrant, Weaviate или Pinecone, добавляет встроенный гибридный поиск и масштабирует индекс отдельно от базы данных приложения.

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

  • Гибридный поиск сочетает плотный векторный retrieval с разреженным, например BM25, часто объединяя результаты через Reciprocal Rank Fusion (RRF). Плотный поиск справляется с семантическими перефразированиями; разреженный находит точные идентификаторы, коды ошибок и акронимы. Бенчмарки вендоров сообщают о приросте относительно чисто векторных баз, но его размер зависит от корпуса и меток релевантности.
  • Реранкинг пропускает найденных кандидатов через модель, которая оценивает запрос и документ вместе. Это может улучшить тонкую релевантность ценой ещё одного вызова модели. Число кандидатов, число сохраняемых и латентность нужно настраивать вместе. Полный многостадийный пайплайн я разобрал в статье Создание современного стека ранжирования поиска.
  • Преобразование запроса переписывает запрос пользователя перед retrieval, чтобы улучшить recall. HyDE (Hypothetical Document Embeddings) заставляет LLM сгенерировать гипотетический документ, эмбеддинг которого используется для retrieval реальных документов. Сгенерированный текст — представление для поиска, а не проверенное свидетельство для ответа. Multi-query expansion генерирует несколько формулировок одного вопроса. Step-back prompting сначала задаёт более общий вопрос для retrieval или ризонинга на основе более общих принципов.

Типичные режимы отказа:

  • Сбой retrieval — нужный документ существует, но не извлекается. Проверьте на этом промахе чанкинг, преобразование запроса, гибридный поиск и фильтрацию по метаданным.
  • Отравление контекста — нерелевантные извлечённые чанки вводят LLM в заблуждение. Проверьте реранкинг, фильтры контекста и меньшие наборы сохраняемых чанков.
  • Lost-in-the-middle — в проверенных постановках вопросов-ответов по нескольким документам и извлечения пар «ключ — значение» Liu et al. обнаружили, что качество ответов в целом было наивысшим, когда релевантная информация находилась в начале или конце входа, и ниже, когда она находилась в середине.

GraphRAG (Microsoft, 2024) извлекает сущности и связи, обнаруживает сообщества графа и строит их резюме. Его метод глобального поиска отвечает на вопросы уровня корпуса по этим резюме; это не просто векторный retrieval с добавленным графом. Цена — дополнительная работа по извлечению, индексации, хранению и оценке.

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

39. Архитектуры агентов и tool calling

ИИ-агенты на основе LLM используют модели, чтобы выбирать и упорядочивать tool calls вокруг меняющегося состояния. Три полезных паттерна оркестрации:

  • ReAct — чередует выбор действия с наблюдениями. Он может адаптироваться после каждого результата инструмента, но растущая история увеличивает стоимость в токенах и латентность.
  • ReWOO — планирует tool calls с плейсхолдерами и явными зависимостями результатов, затем выполняет план и синтезирует ответ. Параллельно могут выполняться только независимые вызовы; вызов, ссылающийся на предыдущий результат, должен его дождаться. Статья сообщает об экономии токенов по сравнению с ReAct, но фиксированному плану нужен явный путь восстановления на случай сбоя инструмента.
  • Planner-executor — отделяет планирование от выполнения и может добавлять политику перепланирования после сбоя. Он позволяет использовать разные модели для разных ролей, но добавляет состояние плана и второй вызов модели, который может сбоить.
ПаттернПовторная работа моделиАдаптивностьПолезная отправная точка
ReActЧитает наблюдения перед следующим действиемОбновляется после наблюденийНеопределённый или исследовательский tool use
ReWOOПовторно использует план с явными зависимостямиФиксированный план, если не расширитьРабота, которую можно спланировать до результатов инструментов
Planner-executorПланирует, выполняет и может перепланироватьМожет пересматривать явный планДлинные задачи, которым нужен контроль

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

Три ряда вызовов модели и tool calls. ReAct чередует вызовы модели и tool calls; модель читает каждый результат перед следующим решением, а каждый вызов содержит более длинную историю. ReWOO делает один вызов для планирования, запускает три независимых tool calls параллельно в этом примере и делает один вызов для написания ответа. Зависимые вызовы должны выполняться по порядку; в этом примере нет пути перепланирования. Планировщик-экзекьютор записывает план, выполняет шаги и возвращается к планировщику для перепланирования, если шаг 2 завершается ошибкой.Три ряда вызовов модели и tool calls. ReAct чередует вызовы модели и tool calls; модель читает каждый результат перед следующим решением, а каждый вызов содержит более длинную историю. ReWOO делает один вызов для планирования, запускает три независимых tool calls параллельно в этом примере и делает один вызов для написания ответа. Зависимые вызовы должны выполняться по порядку; в этом примере нет пути перепланирования. Планировщик-экзекьютор записывает план, выполняет шаги и возвращается к планировщику для перепланирования, если шаг 2 завершается ошибкой.

Function calling — распространённый механизм вызова инструментов. API предоставляют определения инструментов и возвращают структурированные аргументы, снижая потребность в разборе свободного текста. Аргументы, валидные по схеме, всё равно могут выбрать не тот инструмент или содержать недопустимые значения. Параллельный function calling может сократить число обращений туда-обратно, когда операции независимы.

Structured output и constrained decoding обеспечивают соблюдение схемы, ограничивая токены, доступные на каждом шаге генерации. Движки вроде xgrammar, который используется в vLLM и SGLang, могут убрать многие сбои синтаксиса и разбора с малыми накладными расходами в поддерживаемых конфигурациях. Они не гарантируют, что извлечённые значения или решения верны. Schema-Guided Reasoning (SGR) использует порядок полей и структуру схемы, чтобы сделать промежуточное состояние проверяемым до итогового решения. Его три паттерна: Cascade (последовательные шаги), Routing (union-типы как семантические переключатели) и Cycle (ограниченные списки).

Больший набор инструментов и более длинная последовательность действий могут снизить качество выбора и увеличить латентность или стоимость в токенах. Измеряйте эти кривые на реальных описаниях инструментов и реальном распределении сбоев. Фреймворки вроде LangGraph могут сделать состояние и пути восстановления явными, но не избавляют от необходимости оценки.

40. Промпт-инжиниринг для production

Промптинг в production — это задача оценки: измените одну часть промпта или контекста, а затем измерьте качество задачи и режимы отказа. Приведённые ниже техники — типичные отправные точки, а не универсальный порядок.

Few-shot примеры часто эффективны для управления форматом вывода. Начните с 3–5 примеров, которые покрывают пустые входы, неоднозначные запросы и многосоставные ответы, затем измерьте результат на отложенной выборке. Примеры должны охватывать реальное распределение входов, а не только удачный сценарий. Больше примеров занимает больше контекста и не гарантирует дальнейшего улучшения.

Промптинг chain-of-thought (CoT) просит модель показать промежуточные рассуждения перед ответом. Kojima et al. (2022) сообщили о приросте от суффикса «Let’s think step by step» на проверенных ими задачах на ризонинг, но эффект зависит от модели, а новые API с ризонингом могут не раскрывать скрытые трассы рассуждений. В production предпочитайте проверяемую декомпозицию задачи или краткое обоснование, если оно полезно для эвалуатора. Self-consistency (Wang et al., 2023) сэмплирует несколько путей рассуждения и агрегирует ответы, обменивая дополнительную стоимость инференса на устойчивость на подходящих задачах.

Structured output с явными JSON Schema (раздел 39) убирает многие сбои разбора. Движки constrained decoding вроде xgrammar могут обеспечивать поддерживаемую грамматику во время генерации; фактическую точность и семантическую корректность всё равно нужно оценивать, а неподдерживаемые возможности схем всё ещё могут требовать обработки.

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

Температура меняет распределение сэмплирования. Низкие значения — разумная отправная точка для классификации или извлечения; более высокие могут повысить разнообразие для генерации идей. Точное поведение различается между API моделей и взаимодействует с top_p, top_k и значениями по умолчанию у провайдера, поэтому перебирайте поддерживаемые настройки на своей задаче, а не копируйте один диапазон.

Разделение системных и пользовательских сообщений отделяет постоянную политику от содержимого конкретного запроса. Chat-шаблоны и instruction tuning дают этим ролям разный приоритет, но не превращают системное сообщение в границу принудительного исполнения. Стабильное поведение помещайте в системное сообщение, недоверенные данные держите в пользовательском контенте или контенте инструментов, а жёсткие ограничения вроде удаления PII обеспечивайте и вне модели.

Контекст-инжиниринг расширяет работу с промптом до сборки извлечённых документов, результатов инструментов, истории диалога и примеров. Liu et al. обнаружили эффект lost-in-the-middle у проверенных ими моделей с длинным контекстом, поэтому позицию нужно включать в оценку, а не считать несущественной. Более широкий рабочий процесс я разобрал в статье Контекст-инжиниринг для ИИ-агентов.


Часть VIII — Эксплуатация в production

Эксплуатация в production превращает прежние концепции в лимиты допуска, load tests, алерты и решения о ёмкости при реальном трафике.

41. Rate limiting для запросов с переменной стоимостью

Традиционный rate limiting по запросам в секунду предполагает примерно одинаковую стоимость каждого запроса. LLM нарушают это допущение. Классификация с промптом на 10 токенов и анализ документа на 100K токенов попадают на один и тот же endpoint API, но различаются по длине входа в 10 000 раз. Общая стоимость также зависит от длины выхода, модели, кэширования и цен. Rate limiting по RPS либо пропускает дорогие запросы без контроля, либо без нужды душит дешёвые.

Production-системам нужен rate limiting на основе токенов по нескольким измерениям. OpenAI документирует лимиты запросов и токенов по уровням использования. Anthropic разделяет лимиты входных и выходных токенов. Точные квоты и алгоритмы могут меняться, поэтому считайте документацию провайдера источником истины; архитектурная мысль в том, что запросы и токены нужно бюджетировать независимо.

Один вариант реализации на уровне приложения — многомерная иерархия лимитов (пользователь → приложение → организация → глобально), с уровнями приоритета для премиального доступа. Резервируйте токены при допуске запроса: оцените общее количество (вход + max_tokens), вычтите оценку из доступного лимита и после завершения верните неиспользованную часть по фактическому расходу. Это не позволяет серии запросов с длинной генерацией исчерпать ресурсы до начала выдачи ответа. Запрос с 2000 входных токенов и max_tokens = 1000 резервирует 3000. Если ответ использует 200 выходных токенов, фактический расход составляет 2200, а 800 возвращаются. Это локальная политика допуска; алгоритмы квот провайдеров могут использовать другие оценки и отдельные бюджеты входа и выхода. Если доступно менее 3000, верните 429 или поставьте запрос в очередь.

Для self-hosted деплоев эквивалент — выделенная пропускная способность: резервирование выделенной ёмкости GPU под целевые скорости токенов. Для деплоев vLLM это означает настройку admission control вокруг активных слотов декода и давления на KV cache, а не только вокруг числа запросов. Как объясняет раздел 5, и throughput, и допуск требуют лимитов, учитывающих токены.

42. Режимы отказа, под которые нужно проектировать

Сервинг LLM добавляет режимы отказа, связанные с переменной длиной последовательности, памятью KV и долгой работой декода. Проектируйте защиту и нагрузочно тестируйте её до того, как production-трафик начнёт от неё зависеть.

Нехватка памяти (OOM) — частый сбой. Модели 70B в FP16 нужно около 140 GB только под веса, а KV cache одной последовательности Llama 3.1 70B с контекстом 128K может добавить около 40 GiB при допущениях из раздела 6. Разрыв между «помещается в память» и «OOM под нагрузкой» меньше, чем кажется, потому что батч запросов с длинным контекстом может занять больше памяти KV, чем ожидалось. Профилактика сочетает измеренный резерв памяти с квантизацией и страничным выделением KV. В vLLM --gpu-memory-utilization задаёт долю памяти GPU, которую движок может использовать под веса, активации и KV cache; остаток — это резерв. Для нагрузок с высоким давлением на KV LMCache может выгружать данные KV в память CPU или на диск; используйте его опубликованные результаты как отправную точку и бенчмаркайте локальную иерархию памяти.

Вытеснение (preemption) происходит, когда давление на KV cache заставляет планировщик выгнать работу или пересчитать её. Точная стратегия зависит от версии и конфигурации сервинга. Со стороны пользователя симптом — рост сквозной латентности без очевидной ошибки приложения. Следите за числом вытеснений и сопоставляйте его с использованием KV, глубиной очереди и длинами запросов.

Хвостовая латентность может подскакивать, когда большие префиллы задерживают работу декода. Chunked prefill (раздел 7) и планирование с учётом длины направлены на это взаимное влияние. В статьях про планировщик Learning-to-Rank (Fu et al., 2024) и CascadeInfer сообщается об улучшениях относительно их баз на оцениваемых нагрузках, но точный результат зависит от распределения длин запросов и конфигурации планировщика.

Каскадные отказы могут начаться, когда медленные запросы растят очередь, клиенты выше по цепочке падают по таймауту, а повторы добавляют нагрузку. Защита включает admission control, лимиты конкурентности на тенанта, ограничения на длину вывода, бюджеты повторов и circuit breaker на шлюзе. Дезагрегированные пулы префилла и декода могут помочь, когда load tests показывают устойчивое взаимное влияние фаз. Рисунок показывает цикл и защиту на каждом ребре.

Цикл из четырёх шагов: долгие запросы занимают слоты декода, очередь растёт, клиенты отваливаются по таймауту, пока сервер продолжает генерацию, и клиенты повторяют запрос, так что та же работа приходит снова. Полоса на каждом ребре называет защиту, которая его разрывает: ограничения вывода, admission control и лимиты на тенанта, бюджеты повторов и circuit breaker на шлюзе.Цикл из четырёх шагов: долгие запросы занимают слоты декода, очередь растёт, клиенты отваливаются по таймауту, пока сервер продолжает генерацию, и клиенты повторяют запрос, так что та же работа приходит снова. Полоса на каждом ребре называет защиту, которая его разрывает: ограничения вывода, admission control и лимиты на тенанта, бюджеты повторов и circuit breaker на шлюзе.

43. Мониторинг LLM-систем

Мониторинг LLM отличается от мониторинга API тремя вещами: у каждого запроса своя стоимость, у каждого запроса две фазы с разными боттлнеками, а его объём памяти зависит и от длины входа, и от длины выхода. Латентность запросов и доля ошибок сами по себе не показывают ничего из этого.

Goodput — число запросов в секунду, которые укладываются во все заданные пороги SLO, такие как TTFT, TPOT и общая латентность. Это полезная комбинированная мера, потому что сырой throughput может выглядеть здоровым, пока латентные SLO нарушаются: система, обрабатывающая 100 запросов в секунду, но не укладывающаяся в пороги у 40% из них, имеет goodput 60 запросов в секунду. Оптимизация по goodput сохраняет видимость распределения производительности, а не сводит отчёт к среднему.

vLLM отдаёт endpoint Prometheus по адресу /metrics с числом выполняемых и ожидающих запросов, использованием KV cache, распределениями длин генерации и статистикой prefix cache. Имена метрик могут меняться между релизами, поэтому привязывайте дашборды к развёрнутой версии. Типичный стек использует Prometheus для метрик, Grafana для визуализации и совместимые с OpenTelemetry трейсы по компонентам приложения и сервинга.

Полезные паттерны алертов приведены ниже. Пороги для них выводите из результатов load tests, а не копируйте эти примеры без изменений:

  • Всплески числа вытеснений — рантайм выгружает или пересчитывает работу, что добавляет латентность без ошибки приложения.
  • Использование KV cache приближается к проверенной области вытеснений — добавьте ёмкость или сбросьте нагрузку, пока вытеснения не пошли каскадом.
  • Глубина очереди устойчиво выше проверенной огибающей батча — admission control должен начать отклонять запросы или понижать их приоритет.
  • TTFT растёт, а TPOT остаётся ровным — это расхождение прежде всего указывает на очередь, допуск, сеть или давление префилла, а не на throughput декода. Чтобы их различить, используйте трейсы и метрики очереди.

В vLLM первые три алерта соответствуют vllm:num_preemptions_total, vllm:kv_cache_usage_perc и vllm:num_requests_waiting (метрики vLLM).

44. Оптимизация стоимости: стратегия с накоплением эффекта

Цены провайдеров и соотношения цен на вход и выход меняются. Перед решением о закупке берите актуальные тарифы; уровень модели и длина вывода могут определять счёт ещё до оптимизаций инфраструктуры.

OpenAI, Anthropic и Google оценивают выходные токены своих флагманских моделей в несколько раз дороже входных. Отдельно разделение на префилл и декод объясняет разную стоимость выполнения входа и выхода: префилл обрабатывает токены промпта вместе, а обычный декод генерирует токены последовательно. Сам по себе этот механизм не устанавливает, как провайдер задаёт коммерческие цены. Сокращение ненужного вывода (жёсткий max_tokens, structured output, без повторения входа) обычно экономит больше, чем сокращение того же числа входных токенов.

Несколько подходов можно накладывать друг на друга, но только после измерения того, какие из них подходят нагрузке:

  1. Квантизация с FP16 до INT4 сокращает хранение исходных значений весов на 75%, до учёта масштабов и других метаданных. Снизит ли это счёт, зависит от скорости ядра, размера батча и загрузки железа (раздел 9).
  2. Роутинг моделей отправляет подходящий трафик на более дешёвые модели. Измеряйте долю ложных принятий роутера и сквозное качество, прежде чем увеличивать долю трафика, которую обрабатывает более дешёвая модель (раздел 36).
  3. Кэширование промптов сокращает работу с повторяющимся префиксом. Скидки провайдеров и учёт в rate limit меняются со временем, поэтому сочетайте измеренный hit rate с текущими условиями (раздел 16).
  4. Batch API могут давать скидку на работу не в реальном времени: эвалы, генерацию синтетических данных и массовую классификацию. Проверяйте актуальные цены и окна выполнения.
  5. Self-hosting может выиграть при устойчивой загрузке, но универсальной точки безубыточности по объёму токенов не существует. Помимо аренды GPU учитывайте затраты на инжиниринг, оркестрацию, observability, запас ёмкости и дежурства.

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

45. Планирование ёмкости и автомасштабирование

Планирование ёмкости для сервинга LLM должно учитывать переменную стоимость запросов, долгую работу декода и память, зависящую от последовательности. В зависимости от нагрузки ограничивающим ресурсом может быть память KV, пропускная способность памяти, вычисления или межсоединение.

Теоретический предел по памяти для числа одновременных запросов задаёт бюджет KV cache:

max concurrent sequences=GPU memory−model weights−overheadper-sequence KV cache size\text{max concurrent sequences} = \frac{\text{GPU memory} - \text{model weights} - \text{overhead}}{\text{per-sequence KV cache size}}

Для арифметики ёмкости предположим, что рантайм выделяет бюджет KV в 40 GiB для Llama 3.1 70B с KV cache в FP16. Каждая последовательность на 4K занимает около 1,25 GiB, а каждая на 128K — около 40 GiB. Поэтому в этот бюджет помещается не более примерно 32 последовательностей по 4K или одна на 128K ещё до накладных расходов аллокатора, рантайма, разброса нагрузки и SLO. Функция kv_cache_bytes из раздела 6 повторяет этот расчёт для другой модели или бюджета. Поэтому выбор GPU и оптимизация KV cache напрямую определяют план ёмкости.

Формула ёмкости для определения размера парка:

required GPUs=peak tokens/s×safety-factor multiplierper-GPU tokens/s at target SLO\text{required GPUs} = \frac{\text{peak tokens/s} \times \text{safety-factor multiplier}}{\text{per-GPU tokens/s at target SLO}}

Перед делением приведите обе скорости к одной единице времени. Используйте throughput на GPU, измеренный при целевом SLO, а не пиковый. Множитель 1,3 обеспечивает на 30% больше throughput, чем ожидаемый спрос; при таком спросе около 23% обеспеченной ёмкости остаётся неиспользованной. Округляйте вверх до полных реплик сервинга; реплике TP/PP нужна вся её группа GPU. Показатели на GPU полезны только для фиксированной, измеренной топологии реплик. Выбирайте множитель по измеренной неравномерности нагрузки, сбоям и времени восстановления. Пиковый throughput токенов и throughput в рамках SLO могут сильно расходиться по мере роста конкурентности. Бенчмаркайте на реальном распределении длин промптов и ответов при требуемых порогах TTFT и TPOT, а не берите теоретический максимум.

Одной загрузки GPU недостаточно как сигнала автомасштабирования, потому что она может оставаться высокой и при здоровой обработке, и при перегрузке. Сочетайте её с глубиной очереди, использованием KV cache и деградацией goodput. Пороги настраивайте по результатам load tests; значения вроде 80% использования KV — отправные точки, а не универсальные пределы. Эти метрики введены в разделе 43.

Scale-to-zero может подойти для окружений разработки и staging с долгими простоями. Платформы serverless-инференса и автомасштабировщики на основе Kubernetes, такие как KEDA, могут убирать простаивающую ёмкость, но экономия и время холодного старта зависят от размера модели, кэширования образов и весов и инфраструктуры. Прежде чем применять ту же политику к production-трафику, чувствительному к латентности, измерьте время запуска.


Как связаны концепции инференса

Части I–III связывают ёмкость памяти с производительностью сервинга. Размер KV на последовательность (раздел 6) задаёт исходный предел числа последовательностей в памяти. Более крупные батчи декода могут улучшить повторное использование весов и арифметическую интенсивность (раздел 1), но трафик аттеншна, планирование и коммуникация также влияют на TPOT и throughput. GQA и хранение KV с меньшей точностью сокращают объём кэша; PagedAttention сокращает потери при выделении. При указанной конфигурации Llama 3.1 70B, KV cache в FP16 и бюджете 40 GiB исходный предел — 32 последовательности по 4K токенов или одна на 128K, до накладных расходов рантайма и ограничений латентности. Контроль допуска может поставить работу в очередь или отклонить её до достижения этого предела. При давлении на KV рантайм может вытеснять запросы; неконтролируемое выделение памяти может привести к OOM (раздел 42).

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


Дополнительное чтение

Связанные подробные материалы из этого блога по темам:


Источники

Сгруппированы по тематике.

Инференс и аттеншн

Speculative decoding

Квантизация

Обучение и файн-тюнинг

Алайнмент

Масштабирование и архитектура

Эмбеддинги

Архитектуры агентов

Промптинг

Роутинг

Бенчмарки

Архитектуры сервинга

Фреймворки для сервинга

  • vLLM - движок сервинга на основе PagedAttention
  • SGLang - RadixAttention и структурированная генерация
  • TensorRT-LLM - оптимизированный инференс от NVIDIA
  • llama.cpp - переносимый инференс на C/C++
  • DeepSpeed - библиотека распределённого обучения от Microsoft
  • Ollama - локальный запуск LLM

Эксплуатация