Главная / Статьи / KV-кэш простыми словами: почему длинный контекст — это счёт за память, а не ум

KV-кэш простыми словами: почему длинный контекст — это счёт за память, а не ум

Понятное объяснение KV-кэша: что он хранит, как посчитать его размер, почему длинный контекст съедает память и четыре способа снизить расходы на локальных моделях.

1921 слов

Когда считаешь память для локальных моделей, постоянно натыкаешься на термин «KV-кэш». Посчитать его легко, а объяснить на удивление трудно. Этот текст — чистая теория, без выбора железа и без форматов файлов. Дочитав, вы будете понимать, что лежит в кэше, почему модель без него не может работать и почему окно контекста побольше — это на самом деле запрос памяти, а не интеллекта.

Зачем нужен кэш

Языковая модель пишет по одному токену за раз и, чтобы выбрать следующий, оглядывается на все предыдущие. Этот взгляд назад называется вниманием (attention), и это дорогой шаг.

Теперь представьте, что кэша нет вовсе. Чтобы выдать токен 5, модель обрабатывает токены с 1 по 4. Для токена 6 — с 1 по 5. Для токена 7 — с 1 по 6. Одна и та же работа повторяется над одними и теми же токенами, и потери растут с каждым новым словом.

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

Запросы, ключи и значения

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

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

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

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

Что выбрасывается, а что остаётся

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

С ключами и значениями другая история. К ключу и значению токена 5 модель будет обращаться, когда напишет токен 6, токен 600 и токен 60 000. И они никогда не меняются, ведь ничто из происходящего потом не меняет того, что предложил токен 5. Посчитать один раз и хранить всегда — именно правильный компромисс.

Почему кэш так разрастается

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

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

На этой модели один токен разговора стоит 320 КиБ. Тысяча токенов — 320 МиБ. Кэш крошечный в расчёте на токен и огромный в расчёте на разговор.

Всё это одной формулой

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

cache bytes = 2  ×  layers  ×  tokens  ×  kv_heads  ×  head_dim  ×  bytes_each
              │        │          │           │            │            │
              │        │          │           │            │            └ 2 at FP16, 1 at 8-bit
              │        │          │           │            └ numbers per head
              │        │          │           └ how many key/value sets per layer
              │        │          └ your context length
              │        └ every layer keeps its own
              └ one key and one value

Выбирать вы можете только один член. Слои, головы и размерности заданы при обучении. Байты на значение следуют из точности кэша. Токены — это длина контекста, и это та ручка, которую вы контролируете.

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

Сравним две реальные модели

На реальных цифрах это становится менее абстрактным:

  • Qwen3.6-27B держит растущий кэш лишь в 16 из своих 64 слоёв, по 4 набора ключей и значений в каждом. Стоимость: 64 МиБ на каждую 1000 токенов.
  • Llama-3.1-70B держит его во всех 80 слоях, по 8 наборов ключей и значений в каждом. Стоимость: 320 МиБ на каждую 1000 токенов.

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

Ещё одна тонкость: у Llama-3.1-70B 64 читателя, а наборов карточек хранится лишь 8. Это несоответствие намеренное. Группы по восемь читателей делят один набор вместо того, чтобы каждый копил собственную копию. Техника называется grouped-query attention (внимание с группировкой запросов), и её использует почти каждая современная модель. Это как если бы шестьдесят четыре человека покупали каждый свою газету, а вместо этого по офису ходят восемь экземпляров. Читают по-прежнему все, но счёт из газетного киоска уменьшается в восемь раз.

Экономия восьмикратная при небольшой потере качества. Без неё длинный контекст на модели 70B требовал бы больше памяти, чем есть у любой отдельной машины.

Переводим токены в страницы

Число токенов трудно представить, поэтому вот те же окна в привычных единицах чтения, считая примерно три четверти слова на токен:

8K tokens  ≈   6,000 words  ≈   25 pages   a long report
32K tokens  ≈  25,000 words  ≈  100 pages   a short book
128K tokens  ≈  98,000 words  ≈  390 pages   a full novel
256K tokens  ≈ 197,000 words  ≈  790 pages   two novels

Запрашивая окно на 128K, вы просите модель держать в голове роман и не отпускать его. Перенесём это на реальное железо: Llama-3.1-70B с 8-битными весами на машине, где доступно около 114 ГиБ.

context      weights      cache      total      left over
   8K        65.2 GiB     2.5 GiB    67.7 GiB    46.0 GiB
  32K        65.2 GiB    10.0 GiB    75.2 GiB    38.5 GiB
 128K        65.2 GiB    40.0 GiB   105.2 GiB     8.5 GiB

На 25 страницах кэш почти незаметен. На длине романа он вырастает до 40 ГиБ. Для сравнения: вся Qwen3.6-27B со всеми весами занимает 25 ГиБ, так что один чат длиной в роман на модели 70B требует больше памяти, чем хорошей модели 27B нужно целиком.

Веса не изменились. Изменилось только то, сколько разговора модель попросили помнить.

Длинное окно контекста — это не настройка. Это постоянный резерв памяти, который оплачивается заново за каждый открытый разговор.

Эту часть часто упускают. Веса похожи на товарный запас магазина: закуплены один раз, и все покупатели смотрят одно и то же. Кэш похож на примерочную. Каждому покупателю нужна своя, и она занята, пока он не уйдёт. Двум читателям, каждый из которых читает свой роман, нужно два кэша по 40 ГиБ. Вот почему модель, которой просторно на личной машине, начинает задыхаться, когда ею одновременно пользуются несколько человек.

Четыре способа уменьшить счёт

Вот четыре рычага в том порядке, в котором за них стоит браться.

  1. Ограничьте контекст под задачу. Это самое дешёвое решение. Обычный чат использует несколько тысяч токенов, а 32K покрывает большинство задач по программированию. Запрашивая максимальное окно для каждой задачи, вы каждый раз платите максимальную цену.
  2. Квантизируйте кэш, но осторожно. Хранение каждого числа в 8 битах вместо 16 уменьшает его вдвое. Подвох в том, что ключи и значения переносят это неодинаково. Опубликованные замеры показывают, что 8-битные ключи сохраняют около 98% совпадения с прогоном в полной точности, а 4-битные падают примерно до 12%. Значения выдерживают более грубое обращение, поэтому сначала сжимайте значения, а ключи держите в 8 битах или точнее.
  3. Выбирайте архитектуры, рассчитанные на это. Grouped-query attention сейчас повсюду, а гибридные архитектуры идут дальше: большинство слоёв получает небольшую сводку фиксированного размера вместо растущей стопки карточек. Так модель может заявлять 256K и всё равно помещаться.
  4. Пересмотрите веса. Понижение точности весов освобождает память для кэша. В строке для 128K выше 4-битные веса превращают 8,5 ГиБ свободного места в 37,8 ГиБ.

Выбор модели с учётом кэша

Посчитайте цифры для трёх моделей рядом, все с 8-битными весами на одной машине, и картина может сместиться в неожиданную сторону.

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

Qwen3.6-35B-A3B здесь — хорошая модель на каждый день. При 20 МиБ на 1000 токенов контекст почти бесплатен, и даже полному окну на 256K нужно всего 5 ГиБ кэша. Экономить контекст больше не приходится, а ради этого и стоит иметь мощную машину. Когда короткой задаче нужно больше глубины рассуждений, следующий шаг — плотная 27B, втрое дороже на токен, что всё равно немного.

Llama-3.1-70B сложнее оправдать на длинном контексте на машине с 114 ГиБ. Модель не слабее. На 128K она занимает 105 ГиБ, а на 256K хочет 145 ГиБ, которых просто нет. Чтобы удержать её кэш в разумных 20 ГиБ, контекст придётся ограничить примерно 66K. Если модель всё равно заставляет экономить контекст, память лучше потратить на ту, что не заставляет.

Отсюда полезная привычка: смотрите стоимость кэша на 1000 токенов раньше, чем число параметров. Параметры говорят, сколько стоит загрузить модель. Цифра кэша говорит, сколько стоит ею пользоваться. Это ответы на разные вопросы, а название модели рекламирует только один из них.

Если коротко

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

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

В следующий раз, задавая длину контекста, думайте о ней в гигабайтах, а не в токенах. У рассмотренных здесь моделей окно на 128K обходится от 8 до 40 ГиБ в зависимости от выбора, так что решать стоит осознанно.