Pull to refresh

Comments 27

PinnedPinned comments

Для продакшена я бы рекомендовал Q6_K или Q8_0: в них баланс точности и скорости обычно оказывается оптимальным.

Всё так и есть. Я бы ещё добавил, что Q8_0 может быть предпочтительнее там, где нужна генерация кода или какой-нибудь серьёзный reasoning, Q6_K подошёл бы лучше для обычных чатботов или ассистентов, которые дают короткие ответы.

Почему не делают Q8_K_x? И почему llama.cpp не рекомендует Q8_0? Гемма-3 с этим квантом самая адекватная, по моему.

О! Спасибо, отстал от ии-жизни, попробую!
И раз уж у нас диалог завязался 🙂, спрошу по похожей теме: мне, для экономии места, надо в опенсёрч int8 вектора сохранять. Есть ли какой прокси, например для Олламы, который из F16 в int8 на лету конвертирует? Мне тут Перплексити по быстрому натокензировал и уверяет, что всё класс. Проблема: я ему не очень верю.
Могу конечно спросить ещё Гемини или Сонет, но как мне понять, что там будет все нормально с масштабированием и нулевой точкой? Планирую использовать snowflake 2.

Понять так же как и везде, сделать выборку запросов, взять top-k для f16 и сравнить их с top-k int8.
А Lucene вам не подходит? Он же сам конвертирует веса в int8, правда только из fp32, но конвертнуть в разы проще.

Интересно, я про такое конвертипование не в курсе. И Перплексити тоже сомневается.
Могли бы вы ссылкой на доку поделиться, если есть под рукой? Если нет - я сам погуглю

Как стыдно... Я не понимаю, как я эту фичу пропустил! Спасибо )

M — средняя точность, L — низкая точность, выше скорость, но ниже качество

L и S перепутаны. M - medium, L - large, S - small. Смысл значений S, M, L в том какие тензоры как будут квантованы.

Упрощенно говоря, каждый слой модели состоит из 2 видов тензоров: это тензоры внимания attn текущего слоя и тензоры полносвязной сети ffn, основное тело модели.
Указание Qx - где x число от 1 до 8, это квантование именно ffn тензоров.
Указание S, M, L - означает, что важные тензоры внимания будут квантованы на 1, 2 или 3 шага выше, чем число X. Именно тензоры внимания определяют, на сколько квантованная модель будет качественной.

Дальше идут IQ кванты.
IQ - это квантование с использованием imatrix, матрицы важности. imatrix создается из обычного текстового файла, в который собирается типичное использование модели, примеры программирования, языка, фактов и т.д. Во время квантования те блоки, что откликаются на текст из этого файла, квантуются выше, те, которые ни на что не откликаются, получают более низкий квант. За счет этого уменьшается вес квантованной модели оставляя почти тоже качество. "Почти тоже" тут главное, так как важно, чтобы imatrix попала в ваш сценарий использования. Обычно матрица делается из английских слов, соответственно, русский язык может не особо хорошо перенести такое квантование, но обычно i-кванты нормально работают.

Дальше идёт динамическое квантование, сейчас таких два: UD и R4.

UD-...-XL - квантование Unsloth Dynamic 2.0. XL означает, что важные тензоры оставлены в очень высоком качестве. Поэтому даже UD 2-битное (UD-Q2_K_XL ) сравнимо с оригиналом по качеству, теряя всего несколько процентов на примере DeepSeek R1, будучи в 3.3 раза меньше по размеру (212гб против 700гб).

https://arxiv.org/html/2505.02390v1
https://arxiv.org/html/2505.02390v1

R4 - это sota квантование от ikawrakow, уже известного ранее созданием хороших квантов, вроде IQ4_XS. Сейчас он создал форк ik_llama сосредоточившись на оптимизации CPU и гибридного GPU/CPU. R4 доступен только в ik_llama, но там есть возможность конвертации либо на лету, во время загрузки, либо скриптом. Также в ik_llama реализовано MLA, оригинальная технология DeepSeek, позволяющая в 11гб памяти засунуть 160к контекста. И другие оптимизации для MoE моделей DeepSeek, Qwen3, Llama4.

R4 позволяет создать самый маленький квант R1 в мире, занимающий всего 130 гб и подходящий для домашних ПК 128гб + 1 GPU:

Также ik_llama недавно добавил квантование iqN_rt, это Trellis квантование аналогичное QTIP, используемого в exl3. Trellis квантование находит оптимальные параметры для каждого блока позволяет ещё лучше сохранить свойства оригинальной модели при минимальном bpw, на графике это IQ2_KT. KLD лучше отображает снижение качества, чем PPL.

RMS Δp показывает среднее отклонение показателей от Q8_0 кванта.
RMS Δp показывает среднее отклонение показателей от Q8_0 кванта.

Спасибо за развёрнутый комментарий. Исправил про группы S, M, L.

Но мне нигде не удалось найти инфомрацию, что Qx - отображает квантование тензоров ffn, а S, M, L - тензоров внимания, по крайней мере применительно к Group-wise квантованию. Есть ссылки, где почитать?

IQ, R4 и UD — это отдельный интереснейший пласт. Наверное, это уже материал для отдельной статьи, спасибо за наводки!

Не нашёл в обеих статьях упоминания о принадлежности S, M, L к attn и Qx к ffn тензорам - или наоборот. Пока оставлю как есть

Раздел How (Details), там идет описание самой первой реализации статического Qx_K квантования для архитектуры GPT-2, ещё времена когда gguf назывались ggml. В части про quantization mixes идет описание для attn и ffn.
attn - это attention, ffn - это feed forward network. Сейчас S/M/L делаются на 1 шаг выше, чем указано там.

По ссылке указывают конкретные тензоры которые получают повышенный квант, и "все остальные" базовый, а также формулу расчёта супер-блоков. Среди "неважных" есть и attn, а среди важных есть один из ffn, а именно feed_forward.w2, в современных архитектурах он называется ffn_down, это тензор отвечает за сжатие данных после обработки в высокоразмерном пространстве d_ff (это ffn_gate и ffn_up), которое и квантуется как x.

Структура статичного Q4_K_M кванта если нажать на квант на huggingface
Структура статичного Q4_K_M кванта если нажать на квант на huggingface

Я упростил до "важные тензоры внимания" и в целом описал упрощенное понимание, что означает сочетание XS/S/M/L/XL и как они зависят от Qx_K, что важные тензоры оставляют тем ближе к оригиналу, чем "больше" буквы, так как это полезно при выборе квантов.

Ещё стоит учесть, что первые реализации Qx_K квантования были зашиты в код и были статичны, а сейчас каждый может сделать свой рецепт и поднять или понизить приоритет для любого типа тензоров, при этом продолжая называть их так же. Например, некоторые квантуют ffn_up и ffn_gate на 1 шаг ниже, чем x, так как уверены, что это работает хорошо. Или, например, bartowski называет кванты как Q4_K_M, использует правильную схему, но при этом даже для статичных квантов использует imatrix, для всех кроме Q8_0. В обоих случаях это не настоящие статичные Q4_K_M.

Общая идея плюс-минус конечно остается той же, но именно поэтому разные источники квантов, но одинаково названные, от разных создателей будут вести себя по разному. У одного из таких создателей была ситуация, что его gguf Qwen2.5-coder сбоила, а официальные кванты gguf работали хорошо, хотя и те и те назывались одинаково Q4_K_M.

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

Благодарю. Это крайне редкое уточнение, которое мне до этого не удалось найти ни в одной популярной статье про суффиксы квантования и квантование в целом. Постараюсь встроить эти уточнения в статью не перегружая её по смыслу (всё-таки статья рассчитана для понимания основ, более глубокие детали имеет смысл выделить в отдельную).

Когда комментарий интересней статьи)

Ого, Хабр снова торт. Спасибо :)

Инструмент раздвижной молоток на кдпв это что-то

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

В дискусиях репозитория llama.cpp на GitHub-e можно ещё встретить вот такую табличку:

Allowed quantization types:
   2  or  Q4_0   :  3.50G, +0.2499 ppl @ 7B - small, very high quality loss - legacy, prefer using Q3_K_M
   3  or  Q4_1   :  3.90G, +0.1846 ppl @ 7B - small, substantial quality loss - legacy, prefer using Q3_K_L
   8  or  Q5_0   :  4.30G, +0.0796 ppl @ 7B - medium, balanced quality - legacy, prefer using Q4_K_M
   9  or  Q5_1   :  4.70G, +0.0415 ppl @ 7B - medium, low quality loss - legacy, prefer using Q5_K_M
  10  or  Q2_K   :  2.67G, +0.8698 ppl @ 7B - smallest, extreme quality loss - not recommended
  12  or  Q3_K   : alias for Q3_K_M
  11  or  Q3_K_S :  2.75G, +0.5505 ppl @ 7B - very small, very high quality loss
  12  or  Q3_K_M :  3.06G, +0.2437 ppl @ 7B - very small, very high quality loss
  13  or  Q3_K_L :  3.35G, +0.1803 ppl @ 7B - small, substantial quality loss
  15  or  Q4_K   : alias for Q4_K_M
  14  or  Q4_K_S :  3.56G, +0.1149 ppl @ 7B - small, significant quality loss
  15  or  Q4_K_M :  3.80G, +0.0535 ppl @ 7B - medium, balanced quality - *recommended*
  17  or  Q5_K   : alias for Q5_K_M
  16  or  Q5_K_S :  4.33G, +0.0353 ppl @ 7B - large, low quality loss - *recommended*
  17  or  Q5_K_M :  4.45G, +0.0142 ppl @ 7B - large, very low quality loss - *recommended*
  18  or  Q6_K   :  5.15G, +0.0044 ppl @ 7B - very large, extremely low quality loss
   7  or  Q8_0   :  6.70G, +0.0004 ppl @ 7B - very large, extremely low quality loss - not recommended
   1  or  F16    : 13.00G              @ 7B - extremely large, virtually no quality loss - not recommended
   0  or  F32    : 26.00G              @ 7B - absolutely huge, lossless - not recommended

Ссылка: https://github.com/ggml-org/llama.cpp/discussions/2094#discussioncomment-6351796

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

Ого, огромное спасибо за статью и комментарии )

У меня только один момент в голове пока не сходится. Если например из Q4_K_<> мы потом во время инференса восстанвливаем с помощью групповых коэффициентов более точные значения, то почему модели с низким квантом занимают место в памяти все-таки пропорционально квантованному размеру, а не разбухают до базового FP32/16/8?

И хотелось бы все таки добавить, что perplexity - это отличный коэффициент для рассчёта, но к сожалению нет никаких гарантий, что в вашей конкретной задаче большее или меньшее его значение будет означать лучшие или худшие ответы. И еще сложнее оценить насколько они лучше или хуже (особенно, когда речь про совсем уж нетривиальные режимы квантования типа IQ и т.д)

почему модели с низким квантом занимают место в памяти все-таки пропорционально квантованному размеру, а не разбухают до базового FP32/16/8?

Веса распаковываются на лету, потери на распаковку компенсируются общим выигрышем по размеру. И не все кванты одинаковы по вычислительным ресурсам, например, i-кванты будут тяжелее, чем статичные K-кванты, и другие факторы:

cpu only, имитация 6 ядерного ПК
cpu only, имитация 6 ядерного ПК

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

В работе Accuracy is Not All You Need (https://arxiv.org/abs/2407.09141) показали, что KLD лучше отображает корреляцию между ошибками квантования и метрикой KLD, чем PPL, так как PPL скрывает ошибки квантования из-за усреднения.

PPL (Perplexity) - это степень неуверенности модели в предсказании токена, чем ниже, тем увереннее модель. PPL усредняет логарифмические вероятности по всем токенам, поэтому ошибки, например, завышение вероятности одних токенов и занижение других, могут компенсировать друг друга - в результате PPL близок к оригиналу, хотя результат искажен. Ещё PPL слабо реагирует на ошибки в редких токенах, важных для генерации разнообразных ответов.

KLD (KL Divergence) измеряет расхождение между распределениями исходной и квантованной моделей для каждого токена, потом суммирует расхождения для всех токенов. Тут ошибки никак не компенсируются друг другом, отклонения в вероятностях редких и частых токенов одинаково повлияют на итог. Это куда лучше позволяет оценить потери при квантовании, и если оптимизировать квантование под минимизацию KLD, то в среднем это улучшает кванты.

Спасибо, про KLD очень интересное дополнение. Я это добавлю в статью.

Веса распаковываются на лету, потери на распаковку компенсируются общим выигрышем по размеру. 

На лету - то есть на каждом проходе слоя для каждого токена выполняется это преобразование? У меня мелькала мысль о таком подходе, но навскидку казалось что потери должны быть заметно страшнее.

Про табличку еще вопрос - это ваши внутренние замеры, или такое где-то спрятано в глубинах обсуждений репы llama.cpp? Я просто как раз собирался погонять на разных квантах модели, чтобы собрать примерно такие же данные, но с радостью воспользуюсь и готовым результатом, если он в открытом доступе ))

но навскидку казалось что потери должны быть заметно страшнее.

На CPU ускорение за счет SIMD, а конкретно avx2/avx512, если ядер мало (например, 4), то потери могут быть заметны. Именно оптимизацией этих операций, особенно pp, занимается форк ik_llama. В случае GPU эффект тоже можно заметить, особенно для pp которое может достигать тысяч t/s.

Вот запуск gpu only, хотя физический размер модели одинаковый, разница всё равно есть:

Про табличку еще вопрос - это ваши внутренние замеры, или такое где-то спрятано в глубинах обсуждений репы llama.cpp? Я просто как раз собирался погонять на разных квантах модели, чтобы собрать примерно такие же данные

Да, собственные замеры через llama-bench. Можете указать любое количество моделей через -m, llama-bench сама прогонит все модели по очереди. Или если запускаете в разные моменты, то результаты из консоли можете отправить в те же llm и попросите собрать в одну markdown табличку.

Sign up to leave a comment.

Articles