Архив рубрики ~Лента новостей~

Как и зачем ускоряют LLM

Как и зачем ускоряют LLM
Как и зачем ускоряют LLM

Зачем вообще нужен KV cache?

Сегодня вы узнаете об одном из самых важных понятий в современном ИИ — о KV cache. С помощью него происходит оптимизация трансформера.

Мы знаем, что сегодняшние большие языковые модели опираются на архитектуру transformer, а самое важное понятие в трансформере — механизм attention(он же механизм внимания). Кратко напомним: attention помогает уловить смысл слова, учитывая все окружающие слова. Это делается через attention-вычисления.

Формула self-attention из оригинальной статьи. Для attention необходимо вычислить Query / Key / Value.

Модели генерации текста вроде Chat GPT тоже основаны на архитектуре трансформера, но используют только decoder-часть. Они генерируют текст авторегрессивно — проще говоря, по одному токену за раз.

Decoder и Encoder архитектура
Decoder и Encoder архитектура

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

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

Авторегрессивная генерация: каждый новый токен дописывается в контекст
Авторегрессивная генерация: каждый новый токен дописывается в контекст

Почему без кэша это неэффективно

Внутри модели самая важная часть это — вычисление attention. Если присмотреться внимательнее, видно: вычислительная сложность растёт по мере роста размера контекста — то есть по мере того, как текст становится всё длиннее и длиннее.

Если подумать, это действительно неэффективно. При генерации тысяч токенов такие вычисления становятся очень медленными. Кроме того, если посмотреть на attention-расчёты, которые мы делаем при генерации текста, заметно, что в вычислениях много повторений.

Рост матрицы attention при удлинении контекста
Рост матрицы attention при удлинении контекста

Допустим, для первого токена мы посчитали attention score. Но когда генерируем следующий токен, мы снова считаем attention scores, которые уже были посчитаны раньше. И чем больше токенов генерируем, тем больше раз мы пересчитываем эти scores снова и снова. Отсюда видно: есть повторение вычислений, которое на самом деле не нужно.

Синим выделены scores, которые уже считались на прошлых шагах и при наивном подходе пересчитываются.
Синим выделены scores, которые уже считались на прошлых шагах и при наивном подходе пересчитываются.

Это особенно важно, потому что attention имеет квадратичную сложность по размеру контекста. Именно здесь нам помогает KV cache.

Итак, если понятно, зачем нужен KV cache, пойдём дальше и разберём, как он решает эту неэффективность.

Как работает KV cache

Чтобы понять, как работает KV cache, используем его во время inference и посмотрим, как он себя ведёт. KV cache используется только на инференсе, а не на обучении, и помогает снизить латентность генерации токенов.

Пример: возьмём пустой контекст и начнём генерировать текст. Первый шаг — посчитать embedding для токена. Назовём его embedding для токена 1.

Затем в блоке attention считаем соответствующие query, key и value для этого токена. По query и key считаем attention score, а с помощью этого score и value получаем новый контекстный embedding. В финальном слое получаем предсказание следующего токена.

Теперь сохраним key и value в кэш-памяти и попробуем использовать их для следующего токена. Если у вас несколько attention heads и несколько decoder-блоков, вы делаете это для каждого из них и кладёте посчитанные keys и values в кэш. Но здесь для простоты предположим, что у нас одна attention head и один decoder-блок.

Инференс первого токена: Q/K/V, attention a11, новый токен «The»; в Cache — Key Token 1 и Value Token 1
Инференс первого токена. Посчитали Q/K/V для token 1, сгенерировали «The», положили K и V в cache.

Во время инференса мы дописываем токен в контекст и генерируем следующий. Снова начинаем с расчёта embedding для нового токена. Затем в блоке attention считаем query, key и value для последнего сгенерированного токена.

Для key и value у нас теперь есть значения, посчитанные для токена 2. А в кэше уже лежат значения для токена 1. Используя и кэшированные, и только что посчитанные значения, мы собираем key- и value-матрицы для всего контекста.

Дальше в attention считаем score через query-вектор токена 2 и key-матрицу. Так получается attention score для токена 2. Для финального embedding умножаем attention score на value-матрицу — получаем новый контекстный embedding для токена 2 и генерируем следующий токен. Key и value для токена 2 тоже кладём в KV cache.

Второй шаг: Embedding Token 2 → Q/K/V Token 2; из cache берутся K/V Token 1; новый токен «cat»
Второй шаг: для нового токена считаем Q/K/V, склеиваем с кэшем, attention только по последнему query (1×2), в cache уже два токена. Новый токен «cat»

Могут возникнуть вопросы: почему мы берём только последний токен и почему не кэшируем query, а считаем attention только для токена 2? Чтобы это понять, сравним инференс с KV cache и без.

Разница подходов с KV Chache и без него

В контексте сейчас два сгенерированных токена. Посмотрим дальнейшие расчёты.

Сначала считаем embeddings. Без KV cache считаем embeddings для обоих токенов. С KV cache используем embedding только последнего токена.

Слева No KV Cache — два embedding; справа KV Cache — только Embedding Token 2
Слева No KV Cache — два embedding? справа KV Cache — только Embedding Token 2

В блоке attention без KV cache, раз мы используем embeddings всех токенов, считаем обычные матрицы query, key и value — те, что вам знакомы. В случае KV cache считаем только query последнего токена, а для key и value берём данные из кэша плюс значения, посчитанные для последнего токена.

В attention без KV cache мы считаем attention для всех токенов. С KV cache — только для последнего.

Attention: слева полная матрица 2×2, справа только строка последнего токена 1×2
Attention: слева полная матрица 2×2, справа только строка последнего токена 1×2

В этой архитектуре у нас один attention-блок и один decoder-слой, поэтому пропустим остальные компоненты вроде layer normalization и feed-forward сети: единственное отличие двух случаев — размер считаемых матриц.

Перейдём к финальному линейному слою: он даёт выходные logits, а через softmax — вероятности для генерации следующего токена. Если вспомнить: когда KV cache не используем, мы не берём вероятности предыдущих токенов и учитываем только вероятности последнего. Следующий токен сэмплируем только по ним. В случае KV cache у нас и так один набор вероятностей — по нему и сэмплируем.

Логиты: без кэша есть Prob Token 1 и Prob Token 2, но для сэмпла нужен только Prob Token 2; с кэшем сразу один вектор
Логиты. Без кэша есть Prob Token 1 и Prob Token 2, но для сэмпла нужен только Prob Token 2. с кэшем сразу один вектор

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

Полное сравнение пайплайнов No KV Cache vs KV Cache
Логиты. Продолжение схемы

Почему кэшируют K и V, а не Q

Если это понятно, сделаем шаг назад и посмотрим, что мы сделали. Раз мы не используем logits предыдущих токенов, то и считать эти logits не нужно. Значит, в модели можно безопасно пропустить эти вычисления и считать только необходимое. В нашем случае нужен attention-расчёт только для последнего токена.

Если понимать, как считается attention, становится ясно, почему нам нужен только query последнего токена, но при этом всё равно нужны полные key- и value-матрицы всех токенов. В attention, чтобы получить контекстную информацию от всех токенов, мы умножаем query последнего токена на key-матрицу всех токенов. В результате embedding содержит всю контекстную информацию — это и есть суть attention.

Поэтому в KV cache мы берём только последний токен и используем закэшированные keys и values для предыдущих. Так генерируем новые токены. По мере генерации продолжаем сохранять и доставать пары key–value из KV cache.

Cache Memory: стеки Key Token 1…5 и Value Token 1…5
Cache Memory: стеки Key Token 1…5 и Value Token 1…5

Эти данные лежат в памяти GPU. Требование к памяти зависит от нескольких факторов: длины последовательности, размерности модели, числа слоёв и batch size. Чем больше модель, тем больше нужно GPU-памяти.

Плюсы, минусы и цена в памяти

Посмотрим на плюсы и минусы KV cache. На графике compute против числа сгенерированных токенов видно: инференс без KV cache требует значительно больше вычислений. Причина — attention масштабируется квадратично с длиной последовательности, когда keys и values пересчитываются на каждом шаге.

С KV cache модель избегает этого повторного вычисления. На каждом шаге декодирования attention считается только для последнего токена, поэтому per-token attention растёт линейно с длиной контекста — инференс становится гораздо эффективнее.

График Compute vs Tokens: без кэша — крутая кривая, с кэшем — почти плоская линия
График Compute vs Tokens. Без кэша — квадратичная зависимость, с кэшем — линейная зависимость

Минус KV cache — требования к памяти на хранение keys и values. Объём кэша зависит от числа decoder-блоков, batch size, числа attention heads, размерности на голову и длины контекста. Поскольку храним и keys, и values, памяти нужно вдвое больше. При точности 16 bit каждое значение занимает 2 байта.

Формула памяти KV cache: L × B × N × H × C × 2 × 2
Формула памяти KV cache:L — число decoder-блоков, B — batch, N — число голов attention, H — размер головы, C — контекст; два «×2» — за K и V и за FP16 (2 байта).

По расчёту маленькая модель GPT-2 требует примерно 36 MB памяти — это терпимо, но по сегодняшним меркам модель очень маленькая.

Если взять чуть большую модель — примерно втрое относительно предыдущей архитектуры — требование к памяти растёт примерно до 500 MB (0.5 GB).

Второй пример: 32 × 1 × 8 × 128 × 4096 × 2 × 2 ≈ 0.5 GB
Пример необходимой памяти

Для ещё более крупных моделей — около 60 слоёв и контекст порядка 100 000 токенов — требование к памяти может дойти почти до 400 GB, что крайне много только на кэш.

Large Model: 60 слоёв, dim 128×128, context 100k → ≈ 366 GB
Расчеты для большой модели

Итак: KV cache сильно ускоряет инференс, но требует много памяти — и это становится вызовом для современных моделей с миллиардами параметров. Тем не менее инференс всё равно становится намного быстрее. Если сравнить генерацию токенов с KV cache и без, без кэша инференс замедляется по мере роста контекста. Любой современный LLM продукт от Deepseek до Claude не обходится без кеширования.

Надеюсь, теперь понятно вам стало понятнее что такое KV cache, зачем мы его используем и какие сложности с ним связаны.

Спасибо за прочтение!

Источник: habr.com

Оцените материал:

Поделиться
Понравилась статья? Расскажите другим
ВКонтакте
Читайте также
Новости робототехники Tacta Systems ориентирована на высококвалифицированную производственную работу с TactaBot Архив рубрики ~Коротко из Telegram~ Дайджест новостей одной строкой 1️⃣Средняя стоимость аренды стойки в московских… Архив рубрики ~Коротко из Telegram~ Нейросети добрались до штрафов через Госуслуги В Московской области впервые… Архив рубрики ~Коротко из Telegram~ Anthropic попробует замкнуть цикл чипов Разработчик Claude заходит в нишу,… Архив рубрики ~Коротко из Telegram~ OpenAI и Anthropic почти синхронно прокачали голосовой режим OpenAI обновила голосовое… Архив рубрики ~Коротко из Telegram~ OpenAI убирает лимиты для бесплатных пользователей ChatGPT — текстовые чаты… Архив рубрики ~Коротко из Telegram~ ‼️ Видео IBM Technology разбирает, как меняется природа галлюцинаций у… Архив рубрики ~Коротко из Telegram~ 💯 Команда VS Code опубликовала честный разбор своей практики —… Архив рубрики ~Коротко из Telegram~ 👑 Компьютерные агенты плохо справляются с многошаговыми задачами вроде работы… Архив рубрики ~Коротко из Telegram~ В России с Google продолжают взыскивать накопившиеся штрафы и неустойки:… Архив рубрики ~Коротко из Telegram~ Энтузиаст из Южной Кореи создал приложение, которое строит маршрут по… Архив рубрики ~Коротко из Telegram~ GPT-6 откладывается (спойлер — слишком опасная) OpenAI впервые признали, что будущая… Архив рубрики ~Коротко из Telegram~ #слухи Apple может поднять цены на iPhone 17 серии уже… Архив рубрики ~Коротко из Telegram~ OpenAI поставила Astra на ручник из-за киберрисков — разработку топовой модели… Новости робототехники Tacta Systems ориентирована на высококвалифицированную производственную работу с TactaBot Архив рубрики ~Коротко из Telegram~ Дайджест новостей одной строкой 1️⃣Средняя стоимость аренды стойки в московских… Архив рубрики ~Коротко из Telegram~ Нейросети добрались до штрафов через Госуслуги В Московской области впервые… Архив рубрики ~Коротко из Telegram~ Anthropic попробует замкнуть цикл чипов Разработчик Claude заходит в нишу,… Архив рубрики ~Коротко из Telegram~ OpenAI и Anthropic почти синхронно прокачали голосовой режим OpenAI обновила голосовое… Архив рубрики ~Коротко из Telegram~ OpenAI убирает лимиты для бесплатных пользователей ChatGPT — текстовые чаты… Архив рубрики ~Коротко из Telegram~ ‼️ Видео IBM Technology разбирает, как меняется природа галлюцинаций у… Архив рубрики ~Коротко из Telegram~ 💯 Команда VS Code опубликовала честный разбор своей практики —… Архив рубрики ~Коротко из Telegram~ 👑 Компьютерные агенты плохо справляются с многошаговыми задачами вроде работы… Архив рубрики ~Коротко из Telegram~ В России с Google продолжают взыскивать накопившиеся штрафы и неустойки:… Архив рубрики ~Коротко из Telegram~ Энтузиаст из Южной Кореи создал приложение, которое строит маршрут по… Архив рубрики ~Коротко из Telegram~ GPT-6 откладывается (спойлер — слишком опасная) OpenAI впервые признали, что будущая… Архив рубрики ~Коротко из Telegram~ #слухи Apple может поднять цены на iPhone 17 серии уже… Архив рубрики ~Коротко из Telegram~ OpenAI поставила Astra на ручник из-за киберрисков — разработку топовой модели…

Оставить комментарий