Квантизация с нуля: как запустить 160 ГБ LLM на ноутбуке и не потерять в качестве

Квантизация с нуля: как запустить 160 ГБ LLM на ноутбуке и не потерять в качестве

Qwen-3-Coder-Next — модель с 80 миллиардами параметров и весом 159,4 ГБ. Примерно столько оперативной памяти потребовалось бы для её запуска, не считая длинного контекстного окна. И при этом эта модель не считается крупной. По слухам, у frontier-моделей более триллиона параметров, для которых понадобилось бы минимум 2 ТБ RAM. Последний раз я видел столько памяти в одной машине — никогда.

Но что если можно сделать LLM в 4 раза меньше и в 2 раза быстрее — достаточно, чтобы запускать мощные модели на ноутбуке, — и при этом потерять в точности всего 5–10%?

В этом и заключается магия квантизации.

В этой статье вы узнаете:

  • Почему параметры делают модель такой большой
  • Как работает точность чисел с плавающей точкой и чем жертвуют модели
  • Как сжимать числа с плавающей точкой с помощью квантизации
  • Как измерить потерю качества после квантизации

Если вы уже знаете, что такое параметры и как хранятся числа с плавающей точкой, можете сразу перейти к квантизации.

Что делает большие языковые модели такими большими?

Параметры, или «веса», составляют основную часть LLM в памяти или на диске. В своей статье о кешировании промптов я писал, что LLM — это «огромный граф из миллиардов тщательно выстроенных операций». Как выглядят эти графы? Начнём с простейшего примера: 1 вход, 1 параметр, 1 выход.

Он выглядит просто: входное значение 2.0 умножается на параметр 0.5, результат — 1.0. Но это фундаментальный строительный блок современного ИИ.

Настоящие LLM намного сложнее. У них миллиарды таких параметров. Один из способов, как они становятся такими большими, — это «слои». Вот пример с двумя узлами посередине.

Оба узла показывают 1.0, потому что 2.0 × 0.5 = 1.0. Каждое соединение между узлами имеет свой параметр — итого 4 параметра. Когда два значения приходят в один узел, они складываются. Например, выход 1.5 получается как (1.0 × 1.0) + (0.5 × 1.0).

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

Как компьютеры хранят числа?

Компьютеры работают с битами — 1 и 0. Целые числа (integer) легко представляются в битовом виде: каждый бит — степень двойки, сумма даёт итоговое число.

Целые числа дискретны: между 1 и 3 есть только 2. С десятичными дробями сложнее — между ними бесконечно много значений. Компьютеры не могут хранить бесконечность, поэтому идут на компромисс: обеспечивают точность до определённого количества значащих цифр, остальное — приближение.

Например, 32-битные числа с плавающей точкой (float32) охватывают диапазон ±3,40×10³⁸ с точностью до 7 значащих цифр. Для этого 32 бита делятся на 3 части: 1 знаковый бит, 8 бит экспоненты и 23 бита мантиссы. Больше бит мантиссы — выше точность; больше бит экспоненты — шире диапазон.

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

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

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

Можно ли использовать числа с меньшей точностью?

Нужны ли LLM 32-битные числа? Судя по распределению параметров, широкий диапазон не требуется. А нужны ли 7 значащих цифр?

Нет. LLM хорошо работают с менее точными форматами. Например, 16-битное число (float16) имеет 5 бит экспоненты и 10 бит мантиссы. Оно даёт 3 значащие цифры и диапазон ±65504. Занимает вдвое меньше памяти, чем float32.

Google Brain разработала формат bfloat16 с 8 битами экспоненты и 7 битами мантиссы. Он даёт широкий диапазон, но всего 2 значащие цифры. Оказалось, этого достаточно для LLM, а большой диапазон предотвращает переполнение при вычислениях.

Есть и более экстремальные форматы: float8 и float4. Они используют меньше бит, но теряют в точности. Ниже — примеры их приближений синусоиды.

Чем меньше бит, тем более ступенчатой и неточной становится кривая. Но как использовать эти форматы, не разрушив модель?

Что такое квантизация?

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

При конвертации, например, из float16 в float8, значения округляются до ближайшего представимого. Такой метод называется «round-to-nearest».

Однако для LLM прямое округление не подходит. Рассмотрим небольшую модель: при квантизации до float4 многие параметры становятся нулями. Поскольку все пути от входа к выходу проходят через умножение на 0, результат всегда будет нулём.

Проблема в том, что float4 неэффективно использует 4 бита: его диапазон от -3 до 3, а параметры модели лежат в диапазоне от -0,89 до 0,16. Кроме того, float4 может представлять бесконечность и NaN — бесполезно для квантизации.

Как эффективнее использовать 4 бита и 16 возможных значений?

Симметричная квантизация

Вместо фиксированного диапазона можно масштабировать данные под реальный диапазон параметров. Например, если значения лежат от -14 до 14, а нужно уместить в -7 до 7 — просто делим на 2.

Чтобы вернуть значение, умножаем на 2. Нечётные числа становятся непредставимыми и округляются. Например, 5 / 2 = 2.5 → округляется до 3 → при деквантизации даёт 6. Это и есть потеря точности.

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

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

Сравнение с оригиналом показывает среднюю ошибку +18,0%. Модель стала в 4 раза меньше (с 16 до 4 бит), но значения близки к исходным.

На практике такая квантизация даёт выход, отличающийся на ~30% — огромный прогресс по сравнению с float4 (где выход всегда 0), особенно с учётом 4-кратного сокращения объёма памяти.

Но можно сделать ещё лучше.

Асимметричная квантизация

Симметричная квантизация центрирует диапазон вокруг нуля. Но если данные несимметричны — например, от -0,89 до 0,16 — положительная часть диапазона используется неэффективно.

Асимметричная квантизация решает это: она сжимает данные вокруг их реального диапазона и вводит «нулевую точку» для компенсации при деквантизации.

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

Средняя ошибка снижается до +8,5% — почти вдвое меньше, чем при симметричной схеме. На модели это даёт выход, отличающийся всего на ~10%.

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

Как квантизация применяется на практике?

Квантизируют ли всю модель за один раз? Нет — из-за выбросов.

Ранее мы видели, что у всех моделей есть небольшое количество параметров-выбросов — значений, намного больше или меньше остальных. Они сильно искажают квантизацию. Например, добавление выброса 10 к параметрам с диапазоном от -0,89 до 0,16 увеличивает среднюю ошибку до +116,8%.

Если квантизировать всю модель целиком, она станет неработоспособной. Поэтому применяют квантизацию блоками — по 32–256 параметров. Это локализует влияние выбросов.

Для деквантизации нужно хранить масштаб (для симметричной схемы) или масштаб и нулевую точку (для асимметричной). Эти данные хранятся рядом с каждым блоком — это накладные расходы.

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

Важно: выбросы, хотя их и мало, критически важны для качества модели. Удаление даже одного «супервеса» (super weight) может привести к генерации бессмыслицы.

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

Насколько квантизация влияет на точность модели?

Оценить потерю качества можно разными способами. Ниже — несколько методов, применённых к модели Qwen3.5 9B.

Перплексия (Perplexity)

LLM строят распределение вероятностей следующего токена. Перплексия — метрика, сворачивающая это распределение в одно число. Чем ниже перплексия, тем увереннее модель в правильном ответе.

Для теста использовался инструмент llama-perplexity и датасет wikitext-2. Результаты:

  • 8-bit симметричная: 8,193 (+0,1%)
  • 4-bit асимметричная: 8,563 (+4,6%)
  • 4-bit симметричная: 8,71 (+6,4%)
  • 2-bit асимметричная: 66,1 (+707,5%)

При 8-битной квантизации — почти без потерь. При 4-битной — небольшая деградация. При 2-битной — коллапс. Модель стала менее уверенной, рассматривая более широкий набор токенов.

Но перплексия учитывает только вероятность правильного токена. Остальные распределения игнорируются. Поэтому она не даёт полной картины.

KL-дивергенция

KL-дивергенция измеряет, насколько два вероятностных распределения отличаются. Она равна 0, если распределения идентичны. Чем выше значение — тем хуже.

В отличие от перплексии, KL-дивергенция учитывает всё распределение вероятностей. Это даёт более полное представление о том, как квантизация изменила поведение модели.

Результаты (средняя KL-дивергенция):

  • 8-bit симметричная: низкая
  • 4-bit асимметричная: умеренная
  • 4-bit симметричная: выше
  • 2-bit асимметричная: очень высокая

KL-дивергенция подтверждает: 8- и 4-битная квантизация работают хорошо, 2-битная — нет.

Бенчмарк GPQA Diamond

Тест GPQA Diamond — 198 сложных вопросов по биологии, химии и физике. Случайное угадывание даёт 25%.

Результаты:

  • 8-bit симметричная: выше оригинала (возможно, из-за удачи)
  • 4-bit асимметричная: незначительно ниже
  • 4-bit симметричная: чуть хуже
  • 2-bit асимметричная: 97% ответов не найдено

Вывод: 8- и 4-битная квантизация сохраняют качество, 2-битная — нет.

Просто поговорить с моделью

Зададим вопрос: «Какова столица Англии, Великобритания?»

  • Оригинал (bfloat16): «The capital city of England and the United Kingdom is London.»
  • 8-bit симметричная: такой же ответ
  • 4-bit асимметричная: «The capital city of England is London. It is also the capital city of the entire United Kingdom.»
  • 4-bit симметричная: «The capital city of England is London.»
  • 2-bit асимметричная: <нет ответа>

Модель с 2-битной квантизацией почти всегда молчала. «Reasoning trace» шёл вразнос. Очевидно: 2-битная квантизация — слишком сильное сжатие для Qwen3.5 9B.

Это не строгий тест, но он помогает интерпретировать метрики.

Насколько квантизация влияет на скорость модели?

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

Тест с llama-bench на MacBook Pro M1 Max и H100 SXM показал:

  • 8-bit и 4-bit квантизации: значительный прирост скорости
  • 2-bit асимметричная: неожиданно медленнее 4-bit

Почему 2-битная медленнее — непонятно. Возможно, из-за особенностей реализации в llama.cpp.

Заключение

Главное: квантизованные модели — вполне хорошие.

Я думал, что качество будет падать линейно: 8-бит — вдвое хуже, 4-бит — вчетверо. Но это не так.

Переход с 16-битной до 8-битной квантизации почти не теряет в качестве. Переход до 4-битной заметен, но это не «в четыре раза хуже». Скорее, около 90% качества оригинала.

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

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

Эта статья посвящена квантизации после обучения (post-training quantization, PTQ). Есть также квантизация во время обучения (quantization aware training, QAT), когда модель обучается с учётом будущей квантизации.

Рассмотрены лишь базовые методы. Есть и более сложные: AWQ и GPTQ.

Квантизация — не единственный способ сжатия. Есть также прунинг (удаление параметров) и дистилляция (обучение малой модели по большой). Обзор методов — в статье Efficient Large Language Models: A Survey (май 2024).

Читать оригинал