Поиск по сайту:

Спекулятивная декодировка на vLLM: конфигурация и рамки принятия решений


Вы добавляете --speculative-model в вашу конфигурацию vLLM, запускаете бенчмарк и видите примерно в 2 раза больше пропускной способности токенов. Вы выпускаете это. Через три недели ваша 99-й перцентиль задержки (P99 - самые медленные 1% запросов) увеличилась, часть запросов вызывает ошибки OOM, а ваш дежурный инженер смотрит на метрики, которые не имеют смысла - модель технически «быстрее», но пользователи жалуются, что API кажется медленнее.

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

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

Примечание о цифрах в этой статье: показатели производительности, на которые мы ссылаемся, взяты из опубликованных собственных бенчмарков команды vLLM (Llama-3-70B, 4× H100 SXM5, октябрь 2024). Основной аргумент этой статьи заключается в том, что вы должны измерять производительность в условиях вашей собственной эксплуатации. Данные vLLM — это лучший из доступных опубликованных эталонов; рассматривайте их как базу для калибровки, а не как прогноз для вашей системы. Раздел о измерениях в конце статьи показывает, что именно нужно запустить, чтобы получить ваши собственные показатели.

Кратко

  • Спекулятивная декодировка запускает небольшую черновую модель для предложения токенов, а затем проверяет все они за один проход в целевой модели — более быстрая генерация токенов без изменения качества вывода.
  • Это помогает при низких скоростях запросов на структурированных рабочих нагрузках при низкой температуре: до 2,8× ускорение на суммаризации, 1,5× на чате (команда vLLM, 4× H100).
  • Производительность снижается при высоких нагрузках запросов — те же тесты vLLM показывают замедление в 1,4-1,8 раза при насыщении GPU.
  • Выберите черновую модель с масштабным соотношением 1:8-1:12 из той же семейства моделей. Стоимость VRAM черновой модели вычитается непосредственно из вашего бюджета кэша KV.
  • Мониторьте spec_decode_draft_acceptance_rate в производстве. Ниже ~0,5 вы добавляете задержку, а не убираете её — выключите его.

Выбор подходящей модели предварительного черновика для спекулятивной декодировки

Первое решение — какую модель черновика использовать — также является самым важным, и к нему редко относятся именно так. Интуиция проста: используйте меньшую модель из той же семьи, выберите разумное значение --num-speculative-tokens и дайте математике сделать свою работу. Работает это на самом деле или нет, зависит почти полностью от одного фактора: насколько велика модель черновика по сравнению с целевой моделью.

Почему соотношение является основным рычагом

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

Standard generation — 5 tokens = 5 sequential target model passes:

  [70B] → t₁ → [70B] → t₂ → [70B] → t₃ → [70B] → t₄ → [70B] → t₅
 (pass 1)      (pass 2)      (pass 3)      (pass 4)      (pass 5)


Speculative decoding — 5 proposed tokens = 1 draft pass + 1 verify pass:

  [8B Draft]  → t₁  t₂  t₃  t₄  t₅     (one fast pass, all proposed)
                              |
                              ▼
  [70B Target] → ✓t₁ ✓t₂ ✓t₃ ✗t₄  —    (one parallel verify pass)
                 └─────────────┘
                 3 tokens accepted, 2 rejected
                 target model ran once instead of five times

Это верно только тогда, когда принимается достаточно черновых токенов. Коэффициент принятия является ключевой переменной. Важен коэффициент размера, потому что большая черновая модель лучше предсказывает, что бы сказала целевая модель. Модель с 1 миллиардом параметров просто не имеет достаточно мощности, чтобы надежно имитировать выводы модели с 70 миллиардами параметров, поэтому больше её кандидатов отклоняется. Более крупная черновая модель предсказывает точнее, что означает больше принятых токенов за шаг.

Черновая модель 1B против модели-цели 70B (~соотношение 1:70) генерирует токены быстро и дешево, но слишком часто ошибается в выборе токена, поэтому модель-цель отклоняет большую часть того, что предложил черновик — и вы платите за работу двух моделей, не получая достаточного числа принятых токенов, чтобы это имело смысл. Черновая модель 13B против той же модели-цели 70B (~соотношение 1:5) предсказывает хорошо, но требует VRAM и вычислений, соответствующих 13B параметрам, что смещает точку, в которой было бы выгоднее просто запустить модель-цель.

Механика алгоритма указывает на постоянную оптимальную точку: соотношение размеров 1:8 до 1:12 при использовании моделей из одного семейства и с одинаковым распределением обучения. Таблица ниже показывает распространённые сочетания и расчёт по этому соотношению — рассматривайте их как исходную основу для выбора того, что тестировать, а не как эталоны для цитирования. Единственная норма приёма, которая имеет значение для вашего внедрения, — это та, которую вы измеряете на вашем собственном оборудовании с фактическим распределением запросов.

Черновая модельЦелевая модельСоотношениеПочему это важно
Лама-3.1-8BЛлама-3.1-70B~1:9Идеальное сочетание - та же семья, хорошее соответствие по емкости
Qwen2.5-7BQwen2.5-72B~1:10Сочетание одной семьи, похожее соотношение
Ллама-3.2-1BЛлама-3.1-70B~1:70Слишком маленький - проект слишком часто расходится с целью
Ллама-3.1-8BЛлама-3.1-405B~1:50Тот же черновик, но гораздо слабее по сравнению с большей целью

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

Пара 1B/70B выглядит дешево, но редко оправдывает себя — модель для черновика отклоняет слишком много токенов, так что в итоге вы запускаете две модели по цене одной.

Одна практическая заметка: собственные опубликованные бенчмарки команды vLLM использовали черновую модель на 0,5B (turboderp/Qwama-0.5B-Instruct) против Llama-3-70B — соотношение 1:140, что значительно выходит за пределы оптимального диапазона 1:8-1:12. При этом они всё равно наблюдали ускорение в 1,5 раза при низкой частоте запросов. При высокой частоте запросов они наблюдали замедление в 1,4 раза. Это подчеркивает основной момент: частота запросов важнее, чем соотношение. Даже субоптимальная черновая модель может выглядеть как выигрыш на отдельном бенчмарке. Режим отказа проявляется при нагрузке в условиях производства.

Температура разрушает ваши контрольные показатели

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

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

Проблема в том, что большинство бенчмарков выполняются при температуре=0, что является наилучшей точкой для спекулятивного декодирования. В производстве температура, которую вы фактически используете, зависит от задачи — для генерации кода и структурированного вывода обычно используют низкие температуры, тогда как для общения и творческого письма обычно используют более высокие. Если ваша реальная рабочая нагрузка выполняется при температуре=0,7 и выше, ваши показатели бенчмарка будут значительно более оптимистичными, чем то, что вы увидите в производстве.

Самый простой способ увидеть это напрямую — запустить тот же запрос при разных температурах и наблюдать за изменением spec_decode_draft_acceptance_rate в реальном времени:

import requests

VLLM_URL = "http://localhost:8000/v1/chat/completions"
METRICS_URL = "http://localhost:8000/metrics"
PROMPT = "Write a short story about a robot learning to paint."

def get_acceptance_rate():
    text = requests.get(METRICS_URL).text
    for line in text.split("\n"):
        if "spec_decode_draft_acceptance_rate" in line and not line.startswith("#"):
            return float(line.split()[-1])
    return None

for temperature in [0.0, 0.4, 0.8, 1.0]:
    # Send 20 requests at this temperature
    for _ in range(20):
        requests.post(VLLM_URL, json={
            "model": "meta-llama/Llama-3.1-70B-Instruct",
            "messages": [{"role": "user", "content": PROMPT}],
            "temperature": temperature,
            "max_tokens": 200,
        })
    rate = get_acceptance_rate()
    print(f"temperature={temperature}  acceptance_rate={rate:.2f}")

Ожидаемая форма выхода (ваши числа могут отличаться в зависимости от пары моделей и запроса):

temperature=0.0  acceptance_rate=0.81
temperature=0.4  acceptance_rate=0.71
temperature=0.8  acceptance_rate=0.52
temperature=1.0  acceptance_rate=0.38

Найдите строку, соответствующую вашей производственной температуре. Если коэффициент принятия меньше 0,5, прекратите — спекулятивная декодировка приносит отрицательный результат для этой рабочей нагрузки, и её следует отключить. Если он находится между 0,5 и 0,65, вы близки к точке безубыточности; измерьте задержку P99 под реальной нагрузкой, прежде чем принимать решение. Выше 0,65, вероятно, вы видите реальную пользу — подтвердите это с помощью сравнения с базовым вариантом и внедрите.

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

НагрузкаНабор данныхQPSИзмеренный результатМетод
СуммированиеCNN/DailyMailНизкий (QPS=1)Ускорение в 2,8×Спекулятивная декодировка N-грамм
Чат/общийShareGPTНизкий (QPS=1)Ускорение 1,5×Черновая модель (0,5B → 70B)
Чат/общийShareGPTВысокийЗамедление в 1,4×Черновая модель (0,5B → 70B)
СуммированиеCNN/DailyMailВысокийЗамедление в 1,8×Спекулятивная декодировка N-грамм

Источник: команда vLLM, «Как спекулятивное декодирование увеличивает производительность vLLM до 2,8×», октябрь 2024 г. Тестировалось на Llama-3-70B с 4× H100.

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

Интуиция за точкой пересечения: модель draft на 8B стоит примерно 1/9 от вычислительных ресурсов модели-цели на 70B. Если принимается меньше примерно половины ваших черновых токенов, вычислительные ресурсы, потраченные на модель draft, плюс потраченные впустую шаги проверки перевешивают полученные вами токены. При высокой QPS вы уже находитесь на пределе вычислительных возможностей — нет свободной мощности, чтобы поглотить эти накладные расходы.

Практическое значение предельно ясно: не считайте, что спекулятивная декодировка помогает, только потому что ваш бенчмарк показал хорошие результаты. Если ваши пользователи занимаются творческим письмом, общением в открытом чате или чем-либо при температурах выше 0.7, измеряйте показатели принятия именно при этих температурах. Бенчмарк, запущенный при temperature=0, ничего не говорит о том, что происходит при temperature=0.8 в производстве.

Реальность бюджета памяти

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

Фактические показатели экологического следа

Используя Llama-3.1 в качестве конкретного примера. Размеры весов здесь определяются количеством параметров и точностью (BF16=2 байта/параметр, INT8=1 байт/параметр, INT4=0,5 байта/параметр) — фактический доступный VRAM на каждом GPU будет зависеть от вашей конфигурации оборудования и накладных расходов на уровне хоста.

Целевая модель Llama-3.1-70B:

  • BF16: ~140 ГБ → слишком велико для одного H100 на 80 ГБ; требует 2× H100 (всего 160 ГБ), остаётся всего ~20 ГБ свободного места до того, как вы добавите черновую модель или кэш KV
  • INT8: ~70 ГБ → помещается на одном H100 с ~10 ГБ в запасе, или на 2× H100 с ~90 ГБ свободного места для кеша KV и черновой модели
  • INT4 (AWQ/GPTQ): ~35 ГБ → помещается с запасом на одной H100 с ~45 ГБ свободной памяти, самый экономичный вариант по памяти, но с некоторой потерей качества вывода

Черновая модель Llama-3.1-8B:

  • BF16: ~16 ГБ
  • INT8: ~8 ГБ
  • INT4: ~4ГБ

На конфигурации с 2× H100 SXM5 (всего 160 ГБ), обычная настройка для производственного обслуживания модели 70B:

  • Target (70B INT8, ~70GB) + Draft (8B BF16, ~16GB)=~86GB весов. Это оставляет примерно 74GB для KV-кэша, таблиц страниц и памяти активации на обоих GPU.
  • Target (70B BF16, ~140GB) + Draft (8B BF16, ~16GB)=~156GB весов. Это оставляет только ~4GB — практически непригодно для какого-либо значимого размера батча или длины контекста.

Практический вывод для H100: если вы хотите использовать спекулятивное декодирование с целью 70B, вам необходимо квантизировать её до INT8. Запуск обеих моделей в полном BF16 просто не оставляет достаточно места для кэша KV.

На GPU Droplets DO с H200 (2× H200 SXM5, всего 282 ГБ) это ограничение исчезает. Запуск 70B BF16 + 8B BF16 использует ~156 ГБ под веса и оставляет ~126 ГБ для KV-кэша — по сравнению с 74 ГБ на конфигурации H100 INT8, это примерно в 1,7 раза больше свободного места. Более важно, что теперь вам совсем не нужно квантизировать целевую модель, чтобы уложиться в лимит памяти. Если вы обслуживаете запросы с длинным контекстом (32K+ токенов), эта разница имеет большое значение.

Как черновая модель сжимает ваш KV-кэш

Чтобы понять это, нужно знать, что такое KV-кэш. Каждый раз, когда модель обрабатывает токен, она генерирует промежуточные значения, называемые ключами и значениями (K и V). Они сохраняются в памяти GPU, чтобы модели не приходилось вычислять их на каждом шаге — это и есть KV-кэш. Чем больше токенов в запросе (длиннее разговоры, длиннее документы), тем больше KV-кэша требуется. Чем больше одновременных запросов вы обслуживаете, тем больше KV-кэша нужно в сумме.

vLLM выделяет кеш KV при запуске, используя простую формулу: общий объём VRAM × использование памяти GPU (по умолчанию: 0.9) - веса модели=бюджет кеша KV. Вся память, которая не используется для весов модели, полностью идёт на кеш KV. Это означает, что добавление черновой модели на 16 ГБ требует точно такого же объёма кеша KV — это прямой обмен один к одному. Теперь у вас на 16 ГБ меньше ёмкости для хранения контекста для текущих запросов.

На практике это означает одно или оба из следующего:

  • Более короткая максимальная длина контекста - запросы с длинными входными данными или длинной историей разговоров будут раньше достигать пределов памяти
  • Меньший максимальный размер партии - вы можете обслуживать меньше одновременных запросов, прежде чем исчерпается пространство кеша KV

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

# vLLM config for speculative decoding on 2× H100
python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-3.1-70B-Instruct \
  --speculative-model meta-llama/Llama-3.1-8B-Instruct \
  --num-speculative-tokens 5 \
  --speculative-draft-tensor-parallel-size 1 \
  --tensor-parallel-size 2 \
  --gpu-memory-utilization 0.92 \
  --dtype bfloat16

Флаг --speculative-draft-tensor-parallel-size 1 стоит отметить отдельно: черновая модель обычно работает на одном GPU, в то время как целевая модель распределена на оба. Это держит черновую модель быстрой (маленькая модель, проход по одной GPU), но означает, что вы фактически выделяете 16 ГБ на одном из ваших H100 для модели, которая генерирует возможные токены, от которых вы можете отказаться в 25% случаев при высоких температурах.

Квантование и спекулятивная декодировка

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

Почему квантизация изменяет показатели принятия

Алгоритм спекулятивной декодировки работает путём сравнения предложенных моделью чернового варианта токенов с тем, что сгенерировала бы целевая модель. Когда целевая модель квантизирована, её распределение выходных данных смещается немного. Квантизация INT8 вводит небольшие числовые ошибки округления по всем матрицам весов; INT4 (GPTQ, AWQ) вводит более значительные ошибки, которые накапливаются по слоям. Эти ошибки систематические, а не случайные — квантизированная модель последовательно выдаёт немного иные распределения вероятностей, чем версия с полной точностью.

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

Величина штрафа за уровень принятия зависит от архитектуры вашей модели, метода квантизации и распределения подсказок — универсального значения нет, и мы не измеряли это на оборудовании DO. То, что последовательно наблюдается в представленных конфигурациях, — это направление и относительный порядок: INT8 накладывает меньший штраф, чем INT4, и штраф увеличивается с ростом агрессивности квантизации. Точный порог, который имеет значение для вашего развертывания, — это тот, при котором уровень принятия падает ниже вашей точки безубыточности — поэтому правильный подход заключается в том, чтобы измерить spec_decode_draft_acceptance_rate до и после смены уровня квантизации на вашей фактической рабочей нагрузке, а не полагаться на какие-либо общие оценки. Если уровень принятия падает более чем на несколько пунктов при том же распределении подсказок, это сигнал вернуться с INT4 на INT8 на целевой системе.

Асимметрия между квантизацией черновика и целевого

Две модели могут быть запрограммированы независимо, и последствия для производительности не являются симметричными:

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

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

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

Квантование целиЧерновая квантизацияВидеопамять (2× H100, 70B+8B)Заметки
BF16 (~140 ГБ)BF16 (~16 ГБ)~156 ГБ всегоНе подходит; осталось ~4 ГБ для кеша KV
INT8 (~70 ГБ)BF16 (~16 ГБ)~86 ГБРекомендуемый базовый уровень; ~74 ГБ для кеша KV
INT8 (~70 ГБ)INT8 (~8 ГБ)~78 ГБЭкономит 8 ГБ; минимальное влияние на уровень принятия по сравнению с черновиком BF16
INT4 (~35 ГБ)BF16 (~16 ГБ)~51 ГБВмещается на один H100; штраф за уровень приема реален
INT4 (~35 ГБ)INT4 (~4 ГБ)~39 ГБМаксимальная эффективность использования памяти; ожидайте заметно более низкие показатели принятия

Только веса. KV-кэш, активации и таблицы страниц потребляют дополнительную видеопамять. Фактические веса могут незначительно отличаться в зависимости от реализации квантования.

Флаги

Квантование модели черновика обрабатывается отдельно от целевой модели через --speculative-model-quantization. Основной флаг --quantization применяется только к целевой модели:

python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-3.1-70B-Instruct \
  --quantization bitsandbytes \
  --load-format bitsandbytes \
  --speculative-model meta-llama/Llama-3.1-8B-Instruct \
  --speculative-model-quantization bitsandbytes \
  --num-speculative-tokens 5 \
  --speculative-draft-tensor-parallel-size 1 \
  --tensor-parallel-size 2 \
  --gpu-memory-utilization 0.92

Есть второй флаг, о котором стоит знать: --spec-decoding-acceptance-method. По умолчанию используется rejection_sampler, который обеспечивает строгую проверку токенов на основе соотношения вероятностей между черновым и целевым распределениями. Альтернатива, typical_acceptance_sampler, настраивается - она слегка снижает качество выхода в обмен на более высокий уровень принятия. Если вы запускаете квантованную целевую модель и видите уровни принятия, которые в противном случае опустили бы вас ниже порога безубыточности, переход на typical_acceptance_sampler может вернуть часть этой потери без изменения конфигурации вашей модели:

--spec-decoding-acceptance-method typical_acceptance_sampler \
--typical-acceptance-sampler-posterior-threshold 0.09 \
--typical-acceptance-sampler-posterior-alpha 0.3

Значения по умолчанию (threshold=0.09, alpha=0.3) являются разумной отправной точкой. Понижение порога принимает больше черновых токенов; повышение — обеспечивает более строгое качество. Протестируйте на фактическом распределении запросов, прежде чем вносить изменения.

Еще один полезный флаг для квантизированных развертываний при переменной нагрузке: --speculative-disable-by-batch-size. Установите это на порог размера батча, и сервер автоматически отключит спекулятивную декодировку для новых запросов, когда очередь превысит этот размер. Это дает вам преимущества при низком QPS без необходимости вручную переключать конфигурацию при всплесках трафика.

Что смотреть после переключения на квантизированную конфигурацию

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

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

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

Преимущество производительности непрерывной пакетной обработки заключается в том, что запросы используют совместные вычисления на GPU в одном прямом проходе, при этом новые запросы динамически встраиваются в доступную емкость. PagedAttention управляет кешем KV, чтобы обеспечить это без использования заполнителей или пустой памяти. Спекулятивная декодировка вводит структурные предположения, которые создают трение с обеими этими механизмами.

Что на самом деле предполагает планировщик

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

Спекулятивная декодировка нарушает это предположение. Каждая итерация состоит из:

  1. Черновая модель генерирует k кандидатных токенов за запрос
  2. Целевая модель проверяет все k+1 позиций (k черновых токенов + 1 исправление) за один проход вперёд
  3. Принятие некоторого префикса этих токенов на основе критерия принятия

Количество токенов, фактически добавленных к последовательности каждого запроса после шага 3, является переменным — от 1 до k+1. Это создаёт неправильные формы батчей, которые усложняют работу планировщика.

Что это значит для смешанных рабочих нагрузок

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

Представьте себе пакет, в котором половина запросов выполняет структурированный извлечение JSON при температуре=0,1 (уровень принятия ~80%), а другая половина выполняет творческую генерацию с открытым окончанием при температуре=1,0 (уровень принятия ~35%). Запросы с высоким уровнем принятия эффективно завершают свои спекулятивные шаги. Запросы с низким уровнем принятия используют черновую модель, оплачивая стоимость пропускной способности памяти за второй проход вперёд, и отвергают большую часть того, что она производит — фактически добавляя накладные расходы на токен вместо их сокращения.

Планировщик не может разделить их аккуратно. Они используют один и тот же этап проверки, что означает, что запросы, которые не выигрывают от спекулятивного исполнения, все равно несут его стоимость. На практике однородность нагрузки важнее, чем думают большинство команд. Спекулятивная декодировка хорошо подходит для специализированных развертываний — конечная точка автозаполнения кода, структурализованная конвейерная обработка данных, система RAG с ограниченными форматами вывода. Она плохо подходит для универсальных чат-API, где температура и тип задач варьируются между запросами в одном и том же пакете.

Сигналом, на который следует обратить внимание, является разрыв между задержками P50 и P99, который увеличивается после включения спекулятивного декодирования. При смешанной нагрузке P50 часто улучшается (запросы с высокой вероятностью принятия тянут медиану вниз), в то время как P99 ухудшается (запросы с низкой вероятностью принятия добавляют задержку в хвосте, которая усиливается под нагрузкой). Если ваш P50 выглядит как улучшение, а P99 — как регрессия, вероятной причиной является взаимодействие планировщика. Для подтверждения: выполните тот же тест нагрузки на однородной низкотемпературной нагрузке и сравните поведение P99. Если оно значительно сужается, у вас проблема со смешанной нагрузкой, а не со спекулятивным декодированием.

Как измерить это на вашей собственной развертке

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

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

Что на самом деле измерять

Уровень принятия, по запросу. Метрика spec_decode_draft_acceptance_rate доступна на эндпоинте /metrics. Отслеживайте её как гистограмму, а не как среднее значение - вам важно видеть распределение. Если P10 вашего распределения уровня принятия ниже 0.5, у вас есть существенная часть трафика, где спекулятивное декодирование является отрицательным по результату.

TTFT против TPOT отдельно. Спекулятивная декодировка влияет на время на выходной токен (TPOT), а не на время до первого токена (TTFT). TTFT на самом деле может немного увеличиться — модель черновика добавляет шаг предварительного заполнения перед возвращением первого токена. Если ваше целевое соглашение об уровне обслуживания (SLO) в основном зависит от TTFT (например, интерактивный чат, где пользователи больше заботятся о быстродействии, чем о пропускной способности), спекулятивная декодировка может не повлиять на интересующий вас показатель.

Задержка P50 против P99 под нагрузкой. Здесь проявляются взаимодействия планировщика. Бенчмарк с 10 одновременными запросами при load=0.3 будет выглядеть совсем иначе, чем 100 одновременных запросов при load=0.8. Проводите ваши нагрузочные тесты на уровнях конкуренции, которые вы реально наблюдаете в продакшене.

# Pull speculative decoding metrics from the metrics endpoint
curl -s http://localhost:8000/metrics | grep spec_decode

Здоровое развертывание выглядит так — коэффициент принятия стабильно выше 0,7, принятое количество токенов близко к числу черновых токенов:

# HELP vllm:spec_decode_draft_acceptance_rate Speculative decoding draft acceptance rate
vllm:spec_decode_draft_acceptance_rate{...} 0.76
vllm:spec_decode_num_draft_tokens_total{...} 48200
vllm:spec_decode_num_accepted_tokens_total{...} 36600   # ~76% accepted

Нездоровое развертывание — уровень принятия ниже 0,5, большинство черновых токенов отклонено, затраты не окупаются:

vllm:spec_decode_draft_acceptance_rate{...} 0.38
vllm:spec_decode_num_draft_tokens_total{...} 51000
vllm:spec_decode_num_accepted_tokens_total{...} 19400   # ~38% accepted

При 38% принятии с соотношением черновика/целевого текста 1:9 вы добавляете задержку, а не уменьшаете её. Так выглядит в производстве модель черновика с высокой температурой или несовпадением семейства. Если ваши метрики выглядят как во втором блоке, отключите спекулятивное декодирование, пока не устраните основную причину.

Настройка мониторинга, необходимая перед тем, как доверять флагу:

  • Гистограмма коэффициента принятия (P10, P50, P90) сегментированная по диапазону температур
  • TTFT и TPOT на P50, P95, P99 - по сравнению с базовым уровнем без спекулятивного декодирования
  • Использование памяти GPU и коэффициент попадания в KV-кэш (чтобы поймать сжатие)
  • Пропускная способность (токены/секунда) при вашей фактической производственной параллельности, а не при синтетических тестах с одиночным запросом

Почему сводные показатели обманывают

Рассмотрим ситуацию, когда 60% запросов — это структурированные запросы с низкой температурой и уровнем принятия 82%, а 40% — это креативные запросы с высокой температурой и уровнем принятия 38%. Взвешенный средний уровень принятия составляет ~65%, что выглядит хорошо. Но 40% запросов, которые ухудшают производительность, делают это так, что добавляют задержку на хвосте для всей партии. P50 выглядит как выигрыш; P99 — как регрессия. Агрегированные бенчмарки покажут выигрыш и скроют регрессию.

Рамки принятия решений

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

Используйте это, когда:

  • Ваша рабочая нагрузка однородна по температуре и склоняется к структурированному результату или коду - целевая температура ≤ 0,5
  • У вас есть запас видеопамяти после целевых весов модели (черновая модель не должна потреблять более ~15% от общей доступной видеопамяти)
  • Ваша рабочая нагрузка последовательна по размеру пакета - вы не смешиваете типы запросов с существенно разными коэффициентами принятия в одном пакете
  • Вы проверили коэффициенты принятия при производственных температурах, а не только на жадных эталонных показателях
  • Ваш основной SLO — TPOT/пропускная способность, а не TTFT

Оставьте выключенным, когда:

  • Температура сильно варьируется в вашем миксе запросов, или ваша средняя температура выше 0,7
  • У вас ограничена видеопамять и вы обрабатываете запросы с длинным контекстом — ограничение кеша KV обойдется вам дороже, чем прирост пропускной способности токенов
  • Ваша рабочая нагрузка ограничена TTFT, а не пропускной способностью
  • Вы не настроили мониторинг уровня принятия — вы не можете сказать, помогает ли это

Черновой список для выбора модели:

требованиеПочему это важно
Та же семейство моделейОбщая распределение обучения=более высокие показатели принятия
Соотношение размеров 1:8-1:12Достаточно большой, чтобы предсказывать точно, достаточно маленький, чтобы быть дешёвым
Тот же токенизаторНесоответствующие токенизаторы требуют дорогостоящего перекодирования между моделями
Квантовать черновик перед тем, как отдыхать с семьейЧерновик INT8 подходит; ослабление семейного ограничения вредит принятию больше, чем INT8

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

Часто задаваемые вопросы

Изменяет ли спекулятивная декодировка результаты модели?

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

Улучшает ли это время до первого токена (TTFT) или время на выходной токен (TPOT)?

Это улучшает TPOT, а не TTFT. Черновая модель добавляет небольшой этап предварительной подготовки перед тем, как возвращается первый токен, поэтому TTFT на самом деле может немного увеличиться. Если ваше SLO в основном ограничено TTFT — интерактивный чат, где пользователи замечают задержку первого ответа больше, чем скорость генерации — спекулятивная декодировка может не повлиять на метрику, которая для вас важна. Она наиболее ценна, когда узким местом является пропускная способность или скорость генерации, а не начальная отзывчивость.

В чем разница между черновой моделью и n-граммовым спекулятивным декодированием?

Спекуляция с моделью черновика использует отдельную меньшую модель для предложения токенов — она работает с любым типом запроса, но требует VRAM и совместимой семейства моделей. N-граммовая спекуляция повторно использует фразы, уже встречающиеся в самом входном запросе, что делает её практически бесплатной по памяти, но полезной только тогда, когда вывод тесно повторяет вход (рефераты, RAG, вопросы и ответы по документам). Для общего чата или генерации кода используйте модель черновика. Для конвейеров суммаризации, где ответ в основном пересказывает исходный текст, n-граммовая спекуляция часто является лучшим выбором и не требует никакой дополнительной модели.

Ресурсы

  • Спекулятивная декодировка - документация vLLM
  • EAGLE: Спекулятивная выборка требует переосмысления неопределенности признаков
  • Спекулятивная декодировка и далее: подробный обзор
  • Как выбрать подходящий GPU для инференса vLLM | Linux-Console.net
  • Оптимизация вывода LLM 101 | Linux-Console.net
  • Разделение LLM на несколько GPU | Linux-Console.net
  • Трилемма вывода LLM | Linux-Console.net
  • FlashAttention 4 | Linux-Console.net
  • Развертывание NVIDIA Dynamo | Linux-Console.net

Спасибо за обучение с сообществом Linux-Console.net.