- BrainTools - https://www.braintools.ru -

GGUF: квантизация с калибровкой (imatrix)

Привет, хабровчане!

Признаюсь, я не большой любитель vLLM [1], Triton Inference Server [2] и всяких там NeMo [3], вместо них я предпочитаю ollama [4] вообще и llama.cpp [5] в частности, поскольку придерживаюсь мнения, что 1-2% потери в точности и отсутствие некоторых плюшек – не так важно, по сравнению с удобством деплоя, спекулятивным декодингом, многократным приростом скорости, динамическим оффлодом в память [6] системы и возможностью запускать модели на любом “ведре”, навроде древних зионов, андройдофонов, малинок или, скажем, макбуков.

Поэтому вполне ожидаемым для меня является, когда авторы моделей заморачиваются с конвертацией оных в GGUF [7] – особом формате сжатия весов моделей, пригодном для запуска через упомянутые выше ollama и llama.cpp.

Однако реальность обычно немного отличается от ожиданий, и конвертацию в GGUF с последующей квантизацией приходится делать самостоятельно, а чтобы качество работы модели не падало, желательно генерировать imatrix [8] через калибровочный датасет, о чём я и хочу рассказать в данной публикации.


GGUF: квантизация с калибровкой (imatrix) - 1

The Мотивация™

Поскольку большинство вендоров (и наши соотечественники в том числе) частенько пренебрегают данным форматом, приходится брать рога за быка и конвертировать, а затем квантовать модельки в GGUF самостоятельно. Но у конвертации в данный формат с дальнейшей квантизацией “в лоб” есть один фатальный недостаток – такие квантованные модели обычно проседают в точности, так как сжатие выполняется наивно, без учёта архитектуры и знаний, заложенных при обучении [9] модели.

Более правильным методом является квантизация модели при помощи калибровочного датасета, содержащего в себе только необходимые для нас данные; данный приём можно условно описать как “LoRA наоборот”, так как мы не обучаем модель на специализированном корпусе, а наблюдаем за её поведением [10] на близких к продуктовым задачам кейсах, затем отсекаем всё ненужное. Нечто подобное применяется в методе GPTQ [11], но поскольку формат GPTQ так и не обрёл популярности, о его поддержке в llama.cpp (и, как следствие, ollama) не может быть и речи.

Да и зачем он нужен, когда в llama.cpp сравнительно недавно добавили утилиту imatrix [8]?

Вот небольшая справка от ChatGPT обогащённая контекcтом и моим советами:

Инструмент llama-imatrix в llama.cpp предназначен для вычисления и сохранения матрицы важности (importance matrix), отражающей, насколько сильно различные тензоры модели (веса слоёв) влияют на результат при обработке реального текстового корпуса, эта статистика используется при квантизации, чтобы сохранить качество модели – более “важные” параметры квантуются с меньшими потерями. llama-imatrix анализирует активации модели на заданных текстах, накапливает показатели важности для каждого тензора, сохраняет их в файл, после чего квантизатор llama-quantize может учитывать эти данные для адаптивного понижения точности весов.

Поэтому решил разобраться сам и рассказать вам, как конвертировать, а затем квантовать в GGUF на примере модельки ai-sage/GigaChat-20B-A3B-instruct [12], используя для калибровки датасет wikimedia/wikipedia [13], а точнее – первые 500 элементов сабсета 20231101.ru.

Run, GGUF, run

Для начала понадобится скачать и конвертировать модель ai-sage/GigaChat-20B-A3B-instruct [12] в GGUF без квантизации, в формате F16. Для этого скачаем репозиторий llama.cpp, затем создадим в нём .venv, поставим пакеты и выполним сборку llama.cpp:

git clone https://github.com/ggml-org/llama.cpp.git
cd llama.cpp
python3.12 -m venv .venv
source .venv/bin/activate

Скомпилируем бинарники:

cmake -S . -B build -DGGML_CUDA=ON -DCMAKE_BUILD_TYPE=Release

Подробнее [14] про компиляцию llama.cpp под разные платформы, в том числе и CUDA в README проекта.

Поставим ещё пару пакетов до кучи:

pip install "datasets>=4.1" "transformers>=4.56"

Песочница готова, так что теперь можем скачать веса модели и выполнить конвертацию:

hf download ai-sage/GigaChat-20B-A3B-instruct --local-dir ./models/ai-sage/GigaChat-20B-A3B-instruct
python convert_hf_to_gguf.py models/ai-sage/GigaChat-20B-A3B-instruct

Результат будет сохранён тут:

./models/ai-sage/GigaChat-20B-A3B-instruct/GigaChat-20B-A3B-instruct-F16.gguf

Запомним и вернёмся к этому чуть позже.

Калибруй это

Зная формат данных датасета wikimedia/wikipedia [15] и зная, как обычно тренеры моделей формируют из семплов датасета обучающие примеры (спойлер: при помощи метода apply_chat_template [16] объекта токенизатора), создадим небольшой скриптик, обзовём его wikipedia_generator.py [17], положим его в папку с llama.cpp и запустим вот так:

python wikipedia_generator.py 
  --model ai-sage/GigaChat-20B-A3B-instruct 
  --subset 20231101.ru 
  --split train 
  --limit 500 
  --output ./calib.txt

Рядом появится файлик ./calib.txt, его и будем использовать далее.

Теперь вызовем скомпилированный ранее бинарник llama-imatrix, передадим на вход путь до калибровачного датасета, созданного шагом ранее, и путь до квантованной в GGUF модели:

./build/bin/llama-imatrix 
  -m ./models/ai-sage/GigaChat-20B-A3B-instruct/GigaChat-20B-A3B-instruct-F16.gguf 
  -f ./calib.txt 
  -o ./models/ai-sage/GigaChat-20B-A3B-instruct/GigaChat-20B-A3B-instruct-F16.imatrix.gguf
  --n-gpu-layers 10

И идём читать arXiv пару часов, так как процедура обычно не быстрая.

По прошествии некоторого времени результат работы скрипта будет сохранён тут:

./models/ai-sage/GigaChat-20B-A3B-instruct/GigaChat-20B-A3B-instruct-F16.imatrix.gguf

Размером примерно 41 мегабайт, для разных моделей размер может варьироваться от 1-2 до 30-40 мегабайт, как правило у классических dense моделей размер этого файла меньше чем у MoE моделей, вероятно в силу особенностей архитекруты.

Теперь мы можем при помощи скомпилированной утилиты llama-quantize выполнить квантизацию модели до уровня q4_k_m, но не абы как, а с учётом поправок полученных на датасете wikimedia/wikipedia при помощи llama-imatrix.

Пишем команду:

./build/bin/llama-quantize 
  --imatrix ./models/ai-sage/GigaChat-20B-A3B-instruct/GigaChat-20B-A3B-instruct-F16.imatrix.gguf 
  ./models/ai-sage/GigaChat-20B-A3B-instruct/GigaChat-20B-A3B-instruct-F16.gguf 
  ./models/ai-sage/GigaChat-20B-A3B-instruct/GigaChat-20B-A3B-instruct-Q4_K_M.gguf 
  Q4_K_M

Квантованная в q4_k_m модель будет сохранена тут:

./models/ai-sage/GigaChat-20B-A3B-instruct/GigaChat-20B-A3B-instruct-Q4_K_M.gguf

Аналогичным образом будет выглядеть процедура квантизации до других уровней, только циферки меняй, полный список всех доступных уровней квантизации тут [18].

Добавляем в ollama

Отлично, у нас есть моделька, конвертированная в GGUF и квантованная до q4_k_m, что дальше? Дальше можно запустить её, например, через llama-server или llama-cli и попробовать повыполнять на ней запросы, а можно пойти ещё дальше и импортировать её в локальную ollama.

Для этого создадим файл Modelfile следующего содержимого:

FROM ./models/ai-sage/GigaChat-20B-A3B-instruct/GigaChat-20B-A3B-instruct-Q4_K_M.gguf

TEMPLATE """
{{- if .System }}
<s>{{ .System }}<|message_sep|>
{{- end }}
{{- range .Messages }}
    {{- if eq .Role "user" }}
{{ .Role }}<|role_sep|>{{ .Content }}<|message_sep|>
available functions<|role_sep|>[]<|message_sep|>
    {{- else if eq .Role "assistant" }}
{{ .Role }}<|role_sep|>{{ .Content }}<|message_sep|>
    {{- end }}
{{- end }}
{{- if not .Response }}assistant<|role_sep|>
{{- end }}
"""

Поле TEMPLATE обычно отличается у разных моделей.

Подробнее про создание Modelfile [19] в документации ollama.

Далее импортируем этот Modelfile в нашу локальную ollama:

ollama create -f Modelfile GigaChat-20B-A3B-instruct:Q4_K_M

Вместо GigaChat-20B-A3B-instruct:Q4_K_M можно указать любое другое название, это проссто тег, которым ollama пометит эту модель.

Попробуем запустить модель:

ollama run GigaChat-20B-A3B-instruct:Q4_K_M

И можем пообщаться.

Про интеграцию через API рассказывать уже не буду, это и так понятно, на http://localhost:11434 [20] ходим клиентом ollama [21], на http://localhost:11434/v1 [22] клиентом openai [23], в поле model узказываем GigaChat-20B-A3B-instruct:Q4_K_M.

Заключение

Если коротко то история вида: конвертация в GGUF > калибровка imatrix > квантизация > запуск в llama.cpp/ollama помогает нам отлично масштабироваться “вниз”, не требуя дата-центров и дорогих GPU. Используя imatrix мы целенаправленно “подгоняем” квантизацию под свои кейсы (через калибровочный корпус), срезая лишнее, сохраняя только нужное, получаем быстрый деплой и переносимость, но при этом сохраняем качество.

Надеюсь моя небольшая инструкция пригодится вам в ваших начинаниях!

Если вам интересно, как это развивать дальше, то заглядывайте ко мне в Телегу @evilfreelancer [24], и само собой задавайте вопросы под постом и/или в личку, буду рад пообщаться.

Автор: efreelancer

Источник [25]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/20332

URLs in this post:

[1] vLLM: https://github.com/vllm-project/vllm

[2] Triton Inference Server: https://github.com/triton-inference-server/server

[3] NeMo: https://github.com/NVIDIA-NeMo/NeMo

[4] ollama: https://github.com/ollama/ollama

[5] llama.cpp: https://github.com/ggml-org/llama.cpp

[6] память: http://www.braintools.ru/article/4140

[7] GGUF: https://huggingface.co/docs/hub/en/gguf

[8] imatrix: https://github.com/ggml-org/llama.cpp/tree/master/tools/imatrix

[9] обучении: http://www.braintools.ru/article/5125

[10] поведением: http://www.braintools.ru/article/9372

[11] GPTQ: https://github.com/ModelCloud/GPTQModel

[12] ai-sage/GigaChat-20B-A3B-instruct: https://huggingface.co/ai-sage/GigaChat-20B-A3B-instruct

[13] wikimedia/wikipedia: https://huggingface.co/datasets/wikimedia/wikipedia

[14] Подробнее: https://github.com/ggml-org/llama.cpp?tab=readme-ov-file#supported-backends

[15] wikimedia/wikipedia: https://huggingface.co/datasets/wikimedia/wikipedia/viewer/20231101.ru

[16] apply_chat_template: https://huggingface.co/docs/transformers/en/chat_templating

[17] wikipedia_generator.py: https://gist.github.com/EvilFreelancer/dd1333ee965dc8a01a9218c7ffd95af2

[18] тут: https://github.com/ggml-org/llama.cpp/blob/master/gguf-py/gguf/constants.py#L2905

[19] Modelfile: https://ollama.readthedocs.io/en/modelfile/

[20] http://localhost:11434: http://localhost:11434

[21] ollama: https://pypi.org/project/ollama/

[22] http://localhost:11434/v1: http://localhost:11434/v1

[23] openai: https://pypi.org/project/openai/

[24] @evilfreelancer: https://t.me/evilfreelancer

[25] Источник: https://habr.com/ru/articles/953682/?utm_campaign=953682&utm_source=habrahabr&utm_medium=rss

www.BrainTools.ru

Rambler's Top100