Глава 3.4 — Эффективность на этапе инференса
Содержание
- От «больше бесплатно» к реальному обслуживанию модели
- Почему генерация — иной вычислительный режим, чем обучение
- KV-кэш: устранение избыточных повторных вычислений
- Квантование: GPTQ и AWQ
- Спекулятивное декодирование: обмен вычислений черновика на задержку
- PagedAttention и vLLM: идеи страничной организации памяти применительно к KV-кэшу
- Как эти техники складываются в реальной системе обслуживания
- Взгляд с точки зрения собеседования
- Вопросы для самопроверки
- Источники
1. От «больше бесплатно» к реальному обслуживанию модели
Глава 3.3 показала, как смесь экспертов позволяет общему числу параметров модели расти колоссально, при этом вычисления на токен остаются примерно фиксированными, и завершилась указанием на то, что реальное обслуживание любой крупной модели — MoE или плотной — реальным пользователям порождает собственные отдельные инженерные проблемы, никак не связанные с FLOPs на этапе обучения. Эта глава именно об этом разрыве. Всё в части II и в предыдущих главах части III было неявно построено вокруг обучения: имея батч полных последовательностей, вычислить прямой и обратный проход максимально эффективно. Инференс — другая задача, надевшая одежду той же архитектуры, и разница достаточно существенна, чтобы у большинства техник этой главы просто не было аналога на стороне обучения.
Причина, по которой инференс заслуживает собственной главы, а не сноски к эффективности обучения, в том, что авторегрессивная генерация по своей конструкции последовательна: декодер-модель (decoder-only) выдаёт один токен, затем должна подать этот токен обратно на вход, чтобы выдать следующий, и так далее, по одному токену, столько раз, сколько требует ответ. Обучение обрабатывает целую последовательность токенов параллельно, потому что каждый целевой токен уже известен заранее. Инференс не может так делать — токен $t+1$ буквально не существует до тех пор, пока не сэмплирован токен $t$, — и эта последовательная зависимость является коренной причиной практически каждой техники этой главы, от KV-кэша до спекулятивного декодирования и проблемы управления памятью, которую решает PagedAttention.
2. Почему генерация — иной вычислительный режим, чем обучение
Чтобы точно понять, почему эта последовательная структура важна, полезно сравнить арифметическую интенсивность двух режимов. Арифметическая интенсивность — это отношение выполненных вычислений к объёму данных, перемещённых из памяти — $I = \text{FLOPs} / \text{байты перемещённых данных}$; нагрузка является вычислительно ограниченной (compute-bound), если она выполняет много арифметики на байт перемещённых данных, и ограниченной по пропускной способности памяти (memory-bandwidth-bound), если перемещает много данных относительно выполняемой над ними арифметики. Обучение обрабатывает длинные последовательности и, на практике, большие батчи параллельно — один и тот же набор весов повторно используется для огромного числа токенов в пределах одного прямого прохода, а значит, одни и те же весовые матрицы, будучи однажды загруженными из памяти, умножаются на множество разных векторов токенов, прежде чем понадобится подгружать новые данные. Это благоприятное соотношение вычислений к перемещению данных из памяти, и обучение в целом вычислительно ограничено, то есть связывающим ограничением обычно оказывается сырая пропускная способность GPU по FLOPs.
Стоит точно понимать, что «генерация» на самом деле состоит из двух различных фаз с противоположными арифметическими профилями, — на это различие по названию опирается и остаток этой главы, и следующая: prefill (заполнение) — единственный прямой проход, обрабатывающий целиком весь входной промпт сразу, чтобы получить первый выходной токен, и decode (декодирование) — каждый последующий шаг, производящий по одному токену за раз. Prefill вычислительно выглядит как обучение: он обрабатывает много токенов (весь промпт) за один параллельный проход, переиспользуя загруженные веса для всех них сразу, поэтому обычно ограничен вычислениями точно так же, как и обучение. Decode переворачивает эту картину. На каждом шаге декодирования модель выдаёт ровно один новый токен, а значит, тот же самый огромный набор весовых матриц приходится загружать из памяти и использовать для обработки лишь одного нового вектора токена (или небольшого батча таких векторов, если обслуживается несколько запросов одновременно), прежде чем процесс повторится для следующего токена. Объём вычислений на байт перемещённых весовых данных сравнительно ничтожен, и, если не обслуживается много одновременных запросов, генерация, как правило, ограничена пропускной способностью памяти, а не вычислениями — GPU проводит значительную часть времени в ожидании прибытия весов из памяти, а не в насыщении своих арифметических блоков. Это тот же самый фундаментальный аппаратный факт, который мотивировал FlashAttention в главе 3.1, но здесь он проявляется в иной форме, относясь ко всем весам модели на каждом шаге декодирования, а не конкретно к матрице оценок внимания, и именно поэтому практически каждая техника этой главы так или иначе связана с уменьшением трафика памяти или уменьшением числа последовательных шагов, а не с уменьшением сырого числа FLOPs.
3. KV-кэш: устранение избыточных повторных вычислений
Самая базовая неэффективность наивной авторегрессивной генерации легко упускается из виду именно потому, что она настолько структурна: без какого-либо кэширования генерация токена $t+1$ путём подачи модели последовательности $x_1, \ldots, x_t$ требует повторного вычисления проекций ключей и значений для каждого из этих $t$ токенов на каждом слое, хотя $x_1$ через $x_{t-1}$ уже были обработаны, идентично, при генерации токена $t$ на предыдущем шаге. Ничего в ключах и значениях более ранних токенов не меняется по мере продолжения генерации — проекции ключа и значения токена на данном слое зависят только от собственного представления этого токена, поступающего в этот слой, а не от того, какие более поздние токены были с тех пор сгенерированы, — так что их повторное вычисление на каждом последующем шаге является чистой тратой, повторяемой с нуля на каждом отдельном шаге для растущего префикса.
KV-кэш напрямую устраняет эту трату: на каждом слое кэшируются векторы ключа и значения для каждого токена сразу после их вычисления, а на каждом последующем шаге декодирования запрос, ключ и значение вычисляются только для единственного самого нового токена, после чего этот один запрос сопоставляется со всем набором кэшированных ключей и значений (новые ключ и значение добавляются в кэш для будущих шагов). Это превращает вычисление внимания на каждом шаге декодирования из «переобработать всю последовательность целиком» в «обработать ровно один новый токен относительно уже вычисленной истории» — большой и немедленный выигрыш, превращающий генерацию, которая иначе была бы квадратичной по суммарному объёму работы (повторное вычисление постоянно растущего префикса на каждом шаге), в линейную по суммарному объёму работы. KV-кэш повсеместно используется в продакшен-генерации и настолько близок к бесплатному обеду, насколько может предложить эта глава, но он не совсем бесплатен: сам кэш занимает память, и этот объём памяти растёт линейно как с длиной последовательности, так и с размером батча — для $L$ слоёв, $h$ голов размерности $d_h$, длины последовательности $n$, размера батча $b$ и $p$ байт на элемент: $\text{байты кэша} = 2 \cdot L \cdot h \cdot d_h \cdot n \cdot b \cdot p$, где ведущая двойка — за ключи и значения (больше одновременных запросов означает больше отдельных кэшей, которые нужно держать одновременно), — и именно поэтому техники эффективности внимания из глав 3.1 и 3.2 вдвойне важны на этапе инференса — разреженный паттерн внимания или ограниченное скользящее окно не просто экономят вычисления в течение одного прямого прохода, они также напрямую уменьшают размер KV-кэша, который нужно держать в памяти на протяжении всей генерации, что при обслуживании длинного контекста может стать связывающим ограничением на то, сколько запросов система может обслуживать одновременно.
4. Квантование: GPTQ и AWQ
Если генерация обычно ограничена пропускной способностью памяти, один из самых прямых способов её ускорить — просто перемещать меньше данных, а самый прямой способ перемещать меньше данных — представлять веса модели меньшим числом битов. Квантование делает именно это: вместо хранения весов как 16-битных чисел с плавающей точкой они хранятся как 8- или 4-битные целые числа (с небольшим количеством дополнительной информации о масштабировании на группу, чтобы отобразить пониженной точности целые числа обратно в пригодный к использованию диапазон), что уменьшает объём памяти, занимаемый весами, и, поскольку инференс ограничен пропускной способностью памяти, соответственно ускоряет то, насколько быстро их можно передавать из памяти во время генерации. Очевидный риск — что отбрасывание точности ухудшает выходы модели, и интересное инженерное содержание обоих методов ниже заключается именно в том, как они минимизируют это ухудшение.
GPTQ подходит к этому как к задаче послойной оптимизации с тщательной коррекцией ошибки, а не как к наивному округлению каждого веса до ближайшего представимого значения пониженной точности. Он квантует веса столбец за столбцом внутри каждого слоя, и после квантования каждого столбца корректирует оставшиеся, ещё не квантованные столбцы, чтобы скомпенсировать только что внесённую ошибку — используя приближённую, вычислительно доступную форму информации второго порядка (на основе гессиана) о поверхности потерь слоя, чтобы решить, как распределить эту компенсацию, вместо того чтобы считать каждый оставшийся вес одинаково важным для коррекции. Именно этот учитывающий информацию второго порядка последовательный процесс коррекции позволяет GPTQ снижать точность весов до очень низких значений (обычно 4 бита), сохраняя выходы получившейся модели близкими к выходам исходной модели полной точности, — тогда как наивное независимое округление каждого веса, напротив, склонно накапливать гораздо большие ошибки, поскольку никогда не учитывает, как ошибка, внесённая в один вес, взаимодействует с остальными.
AWQ подходит к той же лежащей в основе проблеме с другого угла, мотивированного эмпирическим наблюдением: не все весовые каналы одинаково важны для выходов модели, и небольшая доля каналов — конкретно те, что соответствуют активациям с непропорционально большими величинами, — непропорционально важны для сохранения точности. Вместо равномерного квантования каждого веса AWQ выявляет эти значимые каналы (используя статистику активаций, а не статистику весов, отсюда название «activation-aware» — учитывающий активации) и защищает их, фактически перемасштабируя веса и активации перед квантованием так, чтобы небольшой набор важных каналов сохранял более эффективную точность, тогда как остальные веса квантуются более агрессивно. Поскольку это перемасштабирование можно заранее свернуть в параметры модели, AWQ избегает сложности смешанной точности во время исполнения — буквального хранения одних весов при более высокой битности, чем других, — при этом всё же концентрируя ошибку квантования вдали от каналов, которые важнее всего. Оба метода — примеры одного и того же более широкого принципа: ошибка квантования неоднородна по своему влиянию, и современный уровень пост-обучающего квантования достигается методами, моделирующими, где концентрируется это влияние — через информацию второго порядка о потерях в случае GPTQ, через статистику величины активаций в случае AWQ, — а не распределяющими ошибку вслепую.
5. Спекулятивное декодирование: обмен вычислений черновика на задержку
KV-кэш и квантование оба атакуют стоимость одного шага декодирования. Спекулятивное декодирование атакует совершенно другое узкое место: тот факт, что генерация строго последовательна, по одному токену за раз, независимо от того, насколько дешёвым становится каждый отдельный шаг. Даже при идеально оптимизированной стоимости одного шага получение ста токенов всё равно требует ста последовательных проходов туда-обратно через модель, и каждый такой проход несёт фиксированные накладные расходы на задержку (стоимость запуска ядер (kernel launch), ограниченная пропускной способностью памяти загрузка весов и так далее), которые не сокращаются просто потому, что сам шаг эффективен. Спекулятивное декодирование, предложенное одновременно Leviathan, Kalman и Matias из Google и Chen, Borgeaud, Irving, Lespiau, Sifre и Jumper из DeepMind (под названием «спекулятивное сэмплирование»), уменьшает число последовательных шагов крупной модели, а не стоимость какого-либо отдельного шага.
Механизм работает, сочетая крупную целевую модель с гораздо меньшей, более быстрой моделью-черновиком (draft model), которая её аппроксимирует. На каждом раунде модель-черновик предлагает несколько токенов вперёд — скажем, пять токенов, — сгенерированных быстро и дёшево, поскольку модель-черновик мала. Затем крупная целевая модель верифицирует все пять предложенных токенов за один прямой проход, вычисляемый параллельно по всем пяти позициям сразу (это возможно потому, что при фиксированной последовательности токенов для оценки вычисление вероятностей целевой модели для каждой позиции не требует последовательной генерации — именно этот параллелизм уже используют прямые проходы на этапе обучения, и спекулятивное декодирование заимствует его для верификации на этапе инференса). Шаг верификации использует тщательное правило принятия, основанное на сравнении вероятностей модели-черновика и целевой модели для каждого предложенного токена, принимая префикс предложений черновика до тех пор, пока они остаются согласованными с тем, что сгенерировала бы целевая модель (принимая безоговорочно, когда целевая модель сильно согласна, и вероятностно отклоняя и пересэмплируя, когда нет), так что итоговые выданные токены гарантированно распределены в точности так, как если бы крупная целевая модель сгенерировала их по одному сама, — спекулятивное декодирование меняет задержку, а не распределение выходов.
Выгода в том, что всякий раз, когда догадки модели-черновика хороши, за один прямой проход целевой модели принимается несколько токенов, схлопывая то, что было бы несколькими последовательными дорогими шагами, в один. Цена — дополнительные вычисления, потраченные на запуск модели-черновика и верификацию предложений, которые в итоге отклоняются, когда черновик и цель расходятся, плюс практическое бремя поддержания и синхронизации второй, меньшей модели, специально построенной для аппроксимации первой. Это наглядный пример более широкой идеи, которую стоит запомнить саму по себе: обмен дополнительных, более дешёвых вычислений (прямые проходы небольшой модели-черновика) на сокращение числа дорогих последовательных шагов (прямые проходы крупной модели) — форма компромисса, которая повторяется всякий раз, когда именно последовательная задержка, а не сырая пропускная способность, является тем, что реально оптимизируется.
Сама модель-черновик не обязана быть отдельной, независимо обученной сетью — именно на это самое «практическое бремя» из предыдущего абзаца и нацелены более современные подходы. Методы предсказания нескольких токенов (multi-token prediction, MTP) навешивают дополнительные головы предсказания прямо на целевую модель — или на лёгкий модуль, обучаемый совместно с ней, — которые предсказывают несколько токенов вперёд из того же самого прямого прохода целевой модели, вместо запуска целой второй авторегрессионной модели. Medusa навешивает несколько дополнительных голов поверх финального скрытого состояния замороженной базовой модели; EAGLE улучшает простые головы на уровне логитов, экстраполируя в пространстве признаков — используя собственную динамику скрытых состояний целевой модели, чтобы предсказывать будущие скрытые состояния, что оказывается точнее, чем предсказывать будущие токены напрямую; а DeepSeek-V3 обучает выделенный MTP-модуль совместно с базовой моделью начиная с предобучения — специально для того, чтобы он попутно служил точным спекулятивным черновиком на этапе инференса, вообще не будучи отдельной моделью, которую нужно синхронизировать. Диффузионные черновики развивают ту же идею ещё дальше: небольшая диффузионная языковая модель, которая на каждом шаге зашумления-очистки обрабатывает сразу целый блок позиций, а не генерирует строго слева направо, может предложить целое многотокенное продолжение за один шаг черновика, а не по токену за раз, отдавая целевой модели более широкую партию кандидатов на верификацию за раунд — без того, чтобы сам процесс черновика платил какую-либо последовательную цену.
6. PagedAttention и vLLM: идеи страничной организации памяти применительно к KV-кэшу
KV-кэш решает проблему избыточных повторных вычислений, но вносит собственную проблему управления ресурсами, которая становится острой в тот момент, когда система обслуживания пытается справиться со множеством одновременных запросов, а не с одним запросом за раз. KV-кэш каждого запроса нужно где-то хранить в памяти GPU на протяжении всей длительности генерации этого запроса, и наивная реализация обычно выделяет большой непрерывный блок памяти для кэша каждого запроса, рассчитанный на максимальную длину последовательности, которая ему может когда-либо понадобиться, — что впустую тратит огромное количество памяти на запросы, которые в итоге оказываются короче этого максимума, и, поскольку память выделяется большими непрерывными кусками, приводит к фрагментации: даже когда в совокупности технически достаточно свободной памяти для обслуживания нового запроса, эта память может быть разбросана по множеству несмежных промежутков, каждый из которых слишком мал сам по себе, чтобы удовлетворить новое большое непрерывное выделение.
PagedAttention авторства Kwon, Li, Zhuang, Sheng, Zheng, Yu, Gonzalez, Zhang и Stoica, реализованный в системе обслуживания vLLM, заимствует напрямую из операционных систем идею с многолетней историей, чтобы решить эту проблему: страничную организацию виртуальной памяти. Вместо хранения KV-кэша каждого запроса в одном непрерывном блоке PagedAttention делит кэш на блоки фиксированного размера (страницы) и позволяет отобразить логическую последовательность записей кэша запроса на набор физических блоков, которым не обязательно быть непрерывными в памяти, — точно так же, как виртуальная память операционной системы отображает логическое адресное пространство процесса на разбросанные физические страницы памяти. Это почти по построению устраняет проблему фрагментации, поскольку любой свободный блок в любом месте памяти может удовлетворить следующий фрагмент растущего кэша любого запроса, вместо необходимости искать один большой непрерывный участок. Это также открывает ещё одну ценную оптимизацию: когда несколько запросов разделяют общий префикс (например, один и тот же системный промпт, или общую часть нескольких параллельных попыток сэмплирования из одного и того же промпта), их логические кэши могут отображаться на одни и те же физические блоки для этой общей части, копируя данные только тогда, когда один из запросов расходится с остальными (схема копирования при записи, copy-on-write, напрямую аналогичная тому, как операционные системы обрабатывают разделяемые страницы памяти между процессами).
Практическая выгода — значительный рост достижимой пропускной способности за счёт непрерывного батчинга (continuous batching): система обслуживания может уместить гораздо больше одновременных запросов в тот же объём памяти GPU, чем позволила бы наивная схема непрерывного выделения, поскольку память больше не тратится впустую на избыточно зарезервированные, недоиспользуемые непрерывные блоки и больше не фрагментируется на непригодные к использованию промежутки. Это чрезвычайно важно в продакшене, потому что стоимость обслуживания, как правило, определяется тем, сколько запросов фиксированный объём памяти и вычислений GPU может обслуживать одновременно, и вклад PagedAttention относится именно к этому измерению, независимо от всего рассмотренного ранее в этой главе о стоимости генерации любого отдельного токена.
7. Как эти техники складываются в реальной системе обслуживания
Как и в случае с техниками длинного контекста из главы 3.2, стоит явно отметить, что реальная продакшен-система обслуживания не выбирает одну из этих четырёх идей — она объединяет все их, поскольку каждая устраняет по-настоящему отдельное узкое место. KV-кэш устраняет избыточные вычисления между шагами декодирования и по сути предполагается по умолчанию всем остальным в этой главе. Квантование уменьшает объём памяти и требования к пропускной способности самих весов (а в некоторых реализациях — и KV-кэша тоже), напрямую атакуя ограниченность генерации пропускной способностью памяти, описанную в разделе 2. Спекулятивное декодирование уменьшает число последовательных шагов крупной модели, необходимых для получения данного объёма выхода, атакуя именно задержку, а не пропускную способность или память. PagedAttention атакует проблему управления ресурсами, которая становится видимой только тогда, когда обслуживается много одновременных запросов, а не один, максимизируя, сколько таких запросов может одновременно вместить фиксированный пул памяти GPU. Ни одна из четырёх техник не заменяет другую, и система, реализующая лишь одну или две из них, оставляет на столе вполне определённый, идентифицируемый класс эффективности.
Ещё две техники обслуживания нацелены напрямую на разделение prefill/decode, названное в разделе 2, и их стоит знать, поскольку они атакуют проблему планирования, которую четыре техники выше не затрагивают. Prefill длинного промпта достаточно тяжёл по вычислениям, чтобы, будучи прогнанным до конца без прерываний, заблокировать шаги decode для всех остальных запросов, выполняющихся на том же ускорителе, раздувая их задержку, хотя эти запросы вообще не имеют отношения к только что прибывшему новому запросу. Chunked prefill (пофрагментное заполнение) решает это, разбивая prefill длинного промпта на более мелкие фрагменты и перемежая их с шагами decode других запросов, вместо того чтобы прогонять весь prefill одним непрерывным блоком, — ценой небольшого дополнительного учёта выигрывая гораздо более равномерную задержку среди одновременных запросов. Разделение prefill и decode (prefill/decode disaggregation) идёт ещё дальше и разносит две фазы на совершенно разные ускорители или пулы ускорителей — один пул настроен и выделен под ограниченный вычислениями профиль prefill, другой — под ограниченный пропускной способностью памяти профиль decode, — поскольку у этих двух фаз разные аппаратные компромиссы, и, выполняясь на общем оборудовании, они, как правило, взаимно ухудшают задержку друг друга ровно так, как это и латает chunked prefill. Обе техники касаются планирования и распределения ресурсов между запросами, а не эффективности какого-то одного запроса самого по себе, — а это ровно тот тип проблемы, который проявляется, только когда системе обслуживания приходится жонглировать множеством одновременных запросов, а не оптимизировать один запрос в изоляции.
Отступив назад и окинув взглядом всю эту главу и часть III до сих пор, можно увидеть паттерн, связывающий главы 3.1–3.4: «трансформер» как чистая архитектурная идея, полностью описанная в части II, далеко не достаточен сам по себе, чтобы объяснить, как сегодняшние крупнейшие модели на самом деле обучаются и обслуживаются, — каждая глава этой части была посвящена разрыву между чистым математическим описанием архитектуры и запутанной, зависящей от оборудования и систем реальностью её эксплуатации в масштабе, будь то квадратичная стоимость внимания, ограничения обобщения позиционных кодировок, желание получить больше ёмкости без больших вычислений или конкретные последовательные требования и требования к управлению памятью авторегрессивной генерации. Впрочем, каждое исправление в этой главе применялось к модели, архитектура которой уже зафиксирована: кэш обученной модели можно квантовать, разбивать на страницы и ограничивать окном, но нельзя изменить, сколько векторов ключей и значений она производит на токен. Это число определяется вместе с архитектурой и оказывается самым значимым числом для стоимости обслуживания. Глава 3.5 — про три последовательные попытки поля его уменьшить.
8. Взгляд с точки зрения собеседования
«Почему инференс LLM обычно описывается как ограниченный пропускной способностью памяти, а не вычислениями, и чем это отличается от обучения?» Сильный ответ напрямую объясняет арифметическую интенсивность: обучение повторно использует загруженные веса для множества токенов параллельно в пределах одного прямого/обратного прохода, давая благоприятное соотношение вычислений к перемещению памяти и делая обучение в целом вычислительно ограниченным. Авторегрессивная генерация обрабатывает один новый токен (или небольшой батч) за шаг, поэтому те же самые крупные весовые матрицы приходится перезагружать из памяти, чтобы выполнить сравнительно мало арифметики на шаге, что делает генерацию для одного запроса обычно ограниченной пропускной способностью памяти — именно поэтому техники, уменьшающие трафик памяти (KV-кэш, квантование) или число последовательных шагов (спекулятивное декодирование), центральны для эффективности инференса так, как они не являются центральными для эффективности обучения.
«Проведите меня через то, что именно пошло бы не так вычислительно, если бы система обслуживания не использовала KV-кэш.» Сильный ответ выявляет конкретную трату: без кэширования генерация каждого нового токена потребовала бы повторного вычисления проекций ключа и значения на каждом слое для каждого сгенерированного к этому моменту токена — хотя эти проекции для уже сгенерированных токенов никогда не меняются, — превращая генерацию во всё более расточительный процесс, который переделывает постоянно растущий объём предыдущей работы на каждом отдельном шаге. KV-кэш исправляет это, вычисляя ключи и значения для каждого токена ровно один раз и повторно используя их, сводя вычисление внимания на каждом шаге к обработке только самого нового токена относительно кэшированной истории.
«Как GPTQ решает, насколько агрессивно квантовать разные веса, и зачем это вообще нужно вместо равномерного округления?» Сильный ответ объясняет, что наивное независимое округление каждого веса до значения пониженной точности накапливает ошибку неконтролируемо, поскольку игнорирует, как ошибка в одном весе взаимодействует с остальным выходом слоя. Вместо этого GPTQ квантует веса столбец за столбцом и после квантования каждого столбца корректирует оставшиеся неквантованные столбцы, чтобы скомпенсировать только что внесённую ошибку, используя приближённую информацию второго порядка (гессиан) о поверхности потерь, чтобы решить, как распределить эту компенсацию, что удерживает выход квантованного слоя намного ближе к оригиналу, чем дало бы наивное округление.
«Меняет ли спекулятивное декодирование то, что генерирует модель, или только то, насколько быстро она это генерирует?» Сильный ответ ясно утверждает, что меняется только скорость, а не распределение выходов: предложенные моделью-черновиком токены верифицируются относительно целевой модели способом (правило принятия/отклонения, сравнивающее вероятности черновика и цели), специально спроектированным так, чтобы итоговые выданные токены были распределены в точности так, как если бы целевая модель сама сгенерировала их последовательно. Ускорение достигается за счёт выполнения этой верификации для нескольких предложенных токенов за один параллельный прямой проход целевой модели, схлопывая несколько последовательных дорогих шагов в один всякий раз, когда предложения черновика принимаются.
«Какую конкретную идею из операционных систем заимствует PagedAttention, и какую проблему она решает, которую не решает сам по себе KV-кэш?» Сильный ответ напрямую называет страничную организацию виртуальной памяти: вместо хранения KV-кэша каждого запроса в одном непрерывном блоке памяти, рассчитанном на наихудший случай (что впустую тратит память на избыточное резервирование и фрагментирует свободную память на непригодные к использованию промежутки), PagedAttention делит кэши на блоки фиксированного размера, которые могут отображаться на разбросанную, несмежную физическую память, — точно как отображение виртуальных страниц в физические в ОС. Это решает проблему управления ресурсами и фрагментации, специфичную для обслуживания множества одновременных запросов, отдельную от проблемы избыточных вычислений, для решения которой сам KV-кэш и был изобретён, и именно поэтому vLLM достигает существенно более высокой пропускной способности за счёт более плотного непрерывного батчинга.
9. Вопросы для самопроверки
- Объясните, почему обучение трансформера обычно вычислительно ограничено, тогда как авторегрессивная генерация для одного запроса обычно ограничена пропускной способностью памяти, в терминах арифметической интенсивности.
- Точно опишите избыточные вычисления, происходящие при наивной (без кэша) авторегрессивной генерации, и объясните, почему кэшированные ключ и значение токена никогда не нужно пересчитывать после того, как они сгенерированы.
- Почему стоимость памяти KV-кэша делает техники эффективности внимания из глав 3.1 и 3.2 вдвойне важными на этапе инференса, помимо их исходной выгоды в течение одного прямого прохода?
- Сравните стратегии GPTQ и AWQ по минимизации потери точности, вызванной квантованием — на какую статистику опирается каждый из них, решая, где сконцентрировать точность?
- В спекулятивном декодировании, почему верификация нескольких предложенных черновиком токенов за один прямой проход целевой модели возможна, тогда как генерация тех же самых токенов авторегрессивно из целевой модели была бы невозможна?
- Объясните, почему правило принятия спекулятивного декодирования гарантирует то же распределение выходов, что и стандартное декодирование только целевой моделью, несмотря на участие другой, меньшей модели в процессе.
- Какую конкретную неэффективность в управлении памятью KV-кэша решает PagedAttention, и какую концепцию уровня ОС отражает его решение?
10. Источники
- Kwon, W., Li, Z., Zhuang, S., Sheng, Y., Zheng, L., Yu, C. H., Gonzalez, J., Zhang, H., & Stoica, I. (2023). Efficient Memory Management for Large Language Model Serving with PagedAttention. SOSP 2023. arXiv:2309.06180. https://arxiv.org/abs/2309.06180
- Frantar, E., Ashkboos, S., Hoefler, T., & Alistarh, D. (2022). GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers. ICLR 2023. arXiv:2210.17323. https://arxiv.org/abs/2210.17323
- Lin, J., Tang, J., Tang, H., Yang, S., Chen, W.-M., Wang, W.-C., Xiao, G., Dang, X., Gan, C., & Han, S. (2023). AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration. MLSys 2024. arXiv:2306.00978. https://arxiv.org/abs/2306.00978
- Leviathan, Y., Kalman, M., & Matias, Y. (2022, Google). Fast Inference from Transformers via Speculative Decoding. ICML 2023. arXiv:2211.17192. https://arxiv.org/abs/2211.17192
- Chen, C., Borgeaud, S., Irving, G., Lespiau, J.-B., Sifre, L., & Jumper, J. (2023, DeepMind). Accelerating Large Language Model Decoding with Speculative Sampling. arXiv:2302.01318. https://arxiv.org/abs/2302.01318
- Cai, T., Li, Y., Geng, Z., Peng, H., Lee, J. D., Chen, D., & Dao, T. (2024). Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads. arXiv:2401.10774. https://arxiv.org/abs/2401.10774
- Li, Y., Wei, F., Zhang, C., & Zhang, H. (2024). EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty. ICML 2024. arXiv:2401.15077. https://arxiv.org/abs/2401.15077
- DeepSeek-AI (2024). DeepSeek-V3 Technical Report (вводит модуль multi-token prediction). arXiv:2412.19437. https://arxiv.org/abs/2412.19437
- Agrawal, A., Panwar, A., Mohan, J., Kwatra, N., Gulavani, B. S., & Ramjee, R. (2023). SARATHI: Efficient LLM Inference by Piggybacking Decodes with Chunked Prefills. arXiv:2308.16369. https://arxiv.org/abs/2308.16369
- Patel, P., Choukse, E., Zhang, C., Shah, A., Goiri, Í., Maleki, S., & Bianchini, R. (2023, Microsoft). Splitwise: Efficient Generative LLM Inference Using Phase Splitting (разделение prefill/decode). arXiv:2311.18677. https://arxiv.org/abs/2311.18677