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

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

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

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

ЧастьТемыРазделы
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 Serving Guide
Нужна ли квантизацияКвантизацияModel quantization guide
Как разделить модель между GPUПараллелизмScaling Large Language Models
Нужен ли файн-тюнингОбучение и алайнментLLM Fine-Tuning Guide
Как встроить retrieval в приложениеЭмбеддинги и RAGRAG Evaluation Metrics
Как устроены агентные системыАгенты и промпт-инжинирингAI Agent Reasoning Loops
Как ранжировать результаты поискаЭмбеддинги и реранкингSearch Ranking Stack

Часть 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, где предел задаёт пропускная способность памяти; горизонтальная линия — потолок вычислений; излом между ними лежит на пороге 295 FLOPs/byte.

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

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

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

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

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

У GPU четыре уровня памяти, выстроенные как пирамида: большая, но медленная основная память (HBM) внизу и крошечные, но очень быстрые регистры наверху. Перемещение данных между этими уровнями — одно из главных ограничений производительности. На рисунке показаны четыре уровня H100: от регистров потока наверху до 80 GB HBM3 в основании.

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

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

  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, например RTX 4090 и L40S. Её пропускная способность ниже, чем у HBM, зато стоимость за GB меньше. GDDR6X на RTX 4090 даёт около 1 TB/s против 3,35 TB/s у HBM3 на H100.

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

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

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

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

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 не определён.

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

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

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

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

Компромисс такой: при низкой конкурентности каждый запрос получает отличную латентность, но GPU недогружен. Рост размера батча почти линейно повышает throughput, пока вычисления не насытятся, после чего латентность резко растёт. Goodput — число запросов в секунду, которые укладываются в ваши целевые SLO, — связывает сырой throughput с тем, что реально видят пользователи.

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

При авторегрессионной генерации каждый новый токен обращает внимание на все предыдущие. KV cache хранит проекции Key и Value каждого токена на каждом слое, чтобы избежать пересчёта за O(n2)O(n^2). Без него для генерации токена nn пришлось бы заново прогонять модель по всем n−1n-1 предыдущим токенам.

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

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-голов. Наивные реализации теряют 60–80% выделенной памяти под KV из-за фрагментации, и именно для решения этой проблемы создан PagedAttention.

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

Чтобы ответить на вопрос «сколько последовательностей поместится в мой бюджет KV?» для другой модели, возьмите из её config.json три числа: num_hidden_layers, num_key_value_heads и размерность головы (hidden_size / num_attention_heads). Функция ниже применяет формулу; 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, а каждый следующий шаг декода прибавляет к TPOT.

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

Chunked prefill разбивает промпт на чанки фиксированного размера вместо того, чтобы обрабатывать его целиком. Планировщик может чередовать эти чанки с работой декода, чтобы один длинный промпт не занимал целую итерацию. Sarathi-Serve сообщает о лучших компромиссах между throughput и латентностью на проверенных нагрузках, но выигрыш зависит от модели, железа, распределения длин запросов, размера чанка и базы для сравнения. Дополнительное планирование и более мелкие ядра могут увеличить TTFT нового запроса.

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

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

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

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-голову, общую для всех восьми. GQA хранит две KV-головы, каждая из которых общая для четырёх query-голов.Три панели по восемь query-голов в каждой. MHA хранит восемь KV-голов, по одной на query-голову. MQA хранит одну KV-голову, общую для всех восьми. GQA хранит две KV-головы, каждая из которых общая для четырёх query-голов.

У 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 потока, и каждый поток обрабатывает свой фрагмент данных. Каждая операция в инференсе LLM, от матричного умножения до сэмплирования токена, в конечном счёте является запуском ядра. Один проход вперёд по модели 70B вызывает сотни или тысячи запусков ядер, и разрыв между наивным и оптимизированным ядром может решить, уложится ли система в 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) и активация SwiGLU (DeepFusionKernel). Точный выигрыш в числе запусков и в утилизации зависит от графа модели, компилятора, 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, потому что планирование ядер оставляло многие SM без работы.
  • FlashAttention v2 (A100, 2023) переработал параллелизм: работа делится по измерению последовательности, а не по батчу и головам. Он достиг 50–73% утилизации на A100, примерно в 2 раза быстрее v1.
  • FlashAttention v3 (H100 Hopper, 2024) добавил специализацию варпов (отдельные варпы для перемещения данных и для математики) и конвейеризацию GEMM и softmax, чтобы перекрывать загрузки из памяти с вычислениями. В статье сообщается о до 740 TFLOPS/s в FP16 (75% утилизации) и почти 1,2 PFLOPS/s в 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 занятым, разделяя работу по размеру батча и длине запроса. Во время декода модель генерирует ровно 1 токен за раз (длина запроса = 1). Если размер батча, умноженный на число голов аттеншна, меньше общего числа SM у GPU (108 у A100), большая часть GPU простаивает, пока несколько блоков последовательно перебирают историю токенов.

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

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

13. Continuous batching и static batching

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

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

В бенчмарке Anyscale на OPT-13B оптимизированный 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, где хранится общий системный промпт. Блоки 0, 2, 4 и 6 свободны.У запроса A логические блоки A0–A2, у запроса B — B0 и B1. Таблица блоков отображает их на разбросанные физические блоки в памяти GPU; A0 и B0 оба указывают на физический блок 7, где хранится общий системный промпт. Блоки 0, 2, 4 и 6 свободны.

Прежние системы теряли 60–80% памяти KV cache из-за фрагментации и предварительного выделения. PagedAttention снижает эти потери до <4%, что даёт рост throughput в 2–4 раза при той же латентности и до 24x по сравнению с HuggingFace Transformers (vLLM Blog, 2023).

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

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

Схема speculative decoding: draft-модель предлагает K токенов, целевая модель оценивает все их за один проход, а rejection sampling оставляет принятый префикс, на первом отклонении сэмплирует одну поправку и отбрасывает остальное.Схема speculative decoding: draft-модель предлагает K токенов, целевая модель оценивает все их за один проход, а rejection sampling оставляет принятый префикс, на первом отклонении сэмплирует одну поправку и отбрасывает остальное.

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

При больших батчах дополнительная работа по черновику и проверке может свести выигрыш на нет. 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, могут отличаться, поэтому проверяйте выбранную реализацию.

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


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

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

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

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

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

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

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, тоже распространена, потому что улучшает стабильность градиентов.

Оценка числа параметров для модели decoder-only:

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 используют двунаправленный аттеншн: каждый токен обращает внимание на все остальные в обоих направлениях. Это даёт богатые представления для задач понимания, но не позволяет генерировать текст авторегрессионно, поэтому такие модели теперь в основном лежат в основе эмбеддинг-моделей, реранкеров и лёгких классификаторов (например, роутеров на основе BERT в RouteLLM).

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

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

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

20. Mixture of experts

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

Роутер выбирает для одного токена два лучших из шести маршрутизируемых экспертов; работают эксперты 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.
  • 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_base200,019

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

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

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

Контекстное окно — максимальное число токенов, которое модель может обработать за один проход вперёд. Оно сильно выросло:

МодельКонтекстное окноГод
Llama 12,0482023
Llama 3.1128K2024
GPT-4.11,047,5762025
Gemini 2.5 Pro1,048,5762025

Аттеншн сравнивает каждую пару видимых токенов, но в самом сравнении ничего не говорит о том, как далеко они друг от друга. Без информации о позиции энкодер представил бы «the cat sat on the mat» и «the mat sat on the cat» одинаково. Каузальная маска декодера даёт некоторый сигнал порядка, потому что каждый токен видит только свой префикс, но расстояние она не кодирует. Позиционные кодировки явно добавляют позицию и относительное расстояние.

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

  • 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 за пределы её обучающего контекста. Он группирует частотные измерения в три категории и масштабирует каждую по-своему. В статье сообщается о в 10 раз меньшем числе токенов для файн-тюнинга и в 2,5 раза меньшем числе шагов обучения по сравнению с базой на интерполяции позиций.


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

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

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

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

Пайплайн обучения от претрейнинга на предсказании следующего токена через supervised fine-tuning к алайнменту по предпочтениям; дистилляция показана отдельным путём, на котором выровненная модель-учитель генерирует данные для меньшей модели-ученика.Пайплайн обучения от претрейнинга на предсказании следующего токена через supervised fine-tuning к алайнменту по предпочтениям; дистилляция показана отдельным путём, на котором выровненная модель-учитель генерирует данные для меньшей модели-ученика.

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

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 million в пересчёте на аренду вычислений для финального обучающего прогона без учёта 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 применяет тот же принцип внутри аттеншна, не материализуя полную матрицу внимания. В HuggingFace его включает gradient_checkpointing=True.

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 хранит целиком, а какое шардирует на каждой стадии.

Сетка из четырёх строк (без ZeRO и стадии 1–3) и трёх столбцов: состояния оптимизатора по 12 байт на параметр, градиенты по 2 и веса по 2. Каждая стадия шардирует ещё один столбец. Для 7,5B параметров на 8 GPU память под состояние модели на один GPU падает со 120 GB до 41,3, 28,1 и 15,0 GB.Сетка из четырёх строк (без ZeRO и стадии 1–3) и трёх столбцов: состояния оптимизатора по 12 байт на параметр, градиенты по 2 и веса по 2. Каждая стадия шардирует ещё один столбец. Для 7,5B параметров на 8 GPU память под состояние модели на один GPU падает со 120 GB до 41,3, 28,1 и 15,0 GB.

Это приблизительные значения состояния модели для модели на 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 доступны через HuggingFace Accelerate, что позволяет переключаться между ними одним изменением конфигурации.

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 держит в памяти одновременно 4 копии модели (политика, референс, критик/value-модель, reward-модель) и чувствителен к гиперпараметрам. Он также склонен к 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Ключевое ограничение
PPO4 (политика, референс, критик, reward)Обученная reward-модельOnlineReward hacking, сложная настройка
DPO2 (политика, референс)Неявный (пары предпочтений)OfflineНет исследования, фиксированные данные
GRPO2 с наградами по правилам; 3 с обученной наградой (политика, референс, reward)Явный (правило/верификатор или обученный)OnlineЗависит от качества награды и информативного разброса внутри группы

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ифайн−тюнингаменее500 и файн-тюнинга менее 100, то есть о стоимости первоначального воспроизведения менее $600; сравнение с GPT-3.5 было ограниченной оценкой проекта, а не доказательством широкой эквивалентности.
  • Evol-Instruct (WizardLM) берёт существующие инструкции и итеративно развивает их по осям сложности (добавляет ограничения, углубляет ризонинг, делает задачи конкретнее), чтобы получать всё более сложные обучающие примеры.
  • Phi-4 (14B) от Microsoft использовала синтетические данные для значительной части претрейнинга, включая генерацию, критику, самопересмотр и обращение инструкций. Её технический отчёт сравнивает полученные результаты по STEM и коду с более крупными моделями на выбранных бенчмарках.

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


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

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

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

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

Четыре панели, в каждой GPU 0 и GPU 1. Параллелизм данных: оба хранят полную модель и обслуживают разные запросы. Тензорный параллелизм: каждый хранит половину каждой матрицы весов, и на каждом слое они делают all-reduce. Конвейерный параллелизм: GPU 0 хранит слои 1–40, GPU 1 — слои 41–80, активации передаются между ними. Экспертный параллелизм: каждый хранит половину экспертов, токены перемещаются по схеме all-to-all.Четыре панели, в каждой GPU 0 и GPU 1. Параллелизм данных: оба хранят полную модель и обслуживают разные запросы. Тензорный параллелизм: каждый хранит половину каждой матрицы весов, и на каждом слое они делают all-reduce. Конвейерный параллелизм: GPU 0 хранит слои 1–40, GPU 1 — слои 41–80, активации передаются между ними. Экспертный параллелизм: каждый хранит половину экспертов, токены перемещаются по схеме 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, только если этого требует размещение экспертов.

Карта решений ниже рисует ту же эвристику в виде вопросов о том, помещается ли модель.

Схема решений, выбирающая параллелизм данных, тензорный, конвейерный или экспертный в зависимости от того, помещается ли модель, от границ узла и от архитектуры mixture-of-experts.Схема решений, выбирающая параллелизм данных, тензорный, конвейерный или экспертный в зависимости от того, помещается ли модель, от границ узла и от архитектуры mixture-of-experts.

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 graph и оптимизации ядер, с нативной поддержкой FP8/FP4. Текущие релизы загружают чекпоинт Hugging Face напрямую через бэкенд PyTorch в его API LLM и trtllm-serve; прежняя заблаговременная сборка движка — устаревший вариант. Опубликованные им цифры зависят от железа и модели, и работает он только на GPU NVIDIA.

TGI находится в режиме поддержки. В его 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, а значение для RTX 4090 (1 008 GB/s) — из whitepaper по архитектуре 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) сообщает о снижении стоимости на 85% в своей постановке на MT-Bench при сохранении 95% качества базы GPT-4. Окупится ли роутинг, зависит от текущих цен, структуры трафика, ошибок роутера и минимально допустимого качества.

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

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


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

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

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

Эмбеддинг-модели кодируют текст в векторы фиксированной размерности, которые передают семантический смысл. В отличие от генеративных моделей, выдающих последовательности токенов, они возвращают для входа один плотный вектор, обычно размерностью от нескольких сотен до нескольких тысяч. Их основой служат двунаправленные трансформеры 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 выстраивает эмбеддинг так, что его первые mm измерений так же информативны, как независимо обученная mm-мерная модель. Во время обучения 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 или multi-query expansion, выполнить гибридный поиск (dense и BM25) по индексу, переранжировать лучших кандидатов и дать LLM ответить по верхним чанкам.Архитектура RAG. Офлайн: разобрать и разбить документы на чанки, получить эмбеддинги чанков и сохранить векторы в индексе. На запрос: получить эмбеддинг вопроса пользователя той же моделью, при необходимости после переписывания через HyDE или multi-query expansion, выполнить гибридный поиск (dense и BM25) по индексу, переранжировать лучших кандидатов и дать LLM ответить по верхним чанкам.

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

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

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

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

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

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

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

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

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

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

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

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

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

Три потока. ReAct повторяет цикл «думать, действовать, наблюдать», и каждое наблюдение питает следующую мысль. ReWOO один раз планирует все вызовы инструментов с плейсхолдерами, запускает инструменты параллельно и пишет ответ, без обратной связи. Planner-executor пишет пошаговый план, выполняет следующий шаг и возвращается к планировщику для перепланирования, когда шаг завершается неудачей.Три потока. ReAct повторяет цикл «думать, действовать, наблюдать», и каждое наблюдение питает следующую мысль. ReWOO один раз планирует все вызовы инструментов с плейсхолдерами, запускает инструменты параллельно и пишет ответ, без обратной связи. Planner-executor пишет пошаговый план, выполняет следующий шаг и возвращается к планировщику для перепланирования, когда шаг завершается неудачей.

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-схемами (раздел 39) убирает многие сбои разбора. Движки constrained decoding вроде xgrammar могут обеспечивать поддерживаемую грамматику во время генерации; фактическую точность и семантическую корректность всё равно нужно оценивать, а неподдерживаемые возможности схем всё ещё могут требовать обработки.

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

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

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

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


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

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

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

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

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

Практический паттерн реализации — многомерная иерархия лимитов (пользователь → приложение → организация → глобальный), с приоритетными уровнями для премиального доступа. На уровне запроса резервируйте токены при допуске: оцените суммарное число токенов (вход + max_tokens), вычтите оценку из корзины, а затем верните неиспользованную часть, когда запрос завершится с фактическим расходом. Это не даёт всплеску запросов с длинной генерацией исчерпать ёмкость ещё до того, как они начнут выдавать вывод. Запрос с 2000 входных токенов и max_tokens = 1000 резервирует 3000. Если ответ использует 1200 выходных токенов, 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 на шлюзе. Дезагрегированные пулы префилла и декода могут помочь, когда нагрузочные тесты показывают устойчивое взаимное влияние фаз. Рисунок показывает цикл и защиту на каждом ребре.

Цикл из четырёх шагов: долгий запрос занимает слот GPU, очередь растёт, клиенты отваливаются по таймауту, пока сервер продолжает генерацию, и клиенты повторяют запрос, так что та же работа приходит снова. Пунктирные рёбра называют защиты: ограничить длину вывода, ограничить допуск, backoff с jitter, а также бюджет повторов и circuit breaker.Цикл из четырёх шагов: долгий запрос занимает слот GPU, очередь растёт, клиенты отваливаются по таймауту, пока сервер продолжает генерацию, и клиенты повторяют запрос, так что та же работа приходит снова. Пунктирные рёбра называют защиты: ограничить длину вывода, ограничить допуск, backoff с jitter, а также бюджет повторов и 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 трейсы по компонентам приложения и сервинга.

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

  • Всплески числа вытеснений — рантайм выгружает или пересчитывает работу, что добавляет латентность без ошибки приложения.
  • Использование 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 токенов и throughput в рамках SLO могут сильно расходиться по мере роста конкурентности. Бенчмаркайте на реальном распределении длин промптов и ответов при требуемых порогах TTFT и TPOT, а не берите теоретический максимум.

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

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


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

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

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


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

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


Источники

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

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

Speculative decoding

Квантизация

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

Алайнмент

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

Эмбеддинги

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

Промптинг

Роутинг

Бенчмарки

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

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

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

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