GGUF: квантизация с калибровкой (imatrix). gguf.. gguf. imatrix.. gguf. imatrix. llama.cpp.. gguf. imatrix. llama.cpp. modelfile.. gguf. imatrix. llama.cpp. modelfile. ollama.. gguf. imatrix. llama.cpp. modelfile. ollama. калибровка.. gguf. imatrix. llama.cpp. modelfile. ollama. калибровка. квантизация.

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

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

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

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


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

The Мотивация™

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

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

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

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

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

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

Run, GGUF, run

Для начала понадобится скачать и конвертировать модель ai-sage/GigaChat-20B-A3B-instruct в 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

Подробнее про компиляцию 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 и зная, как обычно тренеры моделей формируют из семплов датасета обучающие примеры (спойлер: при помощи метода apply_chat_template объекта токенизатора), создадим небольшой скриптик, обзовём его wikipedia_generator.py, положим его в папку с 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

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

Добавляем в 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 в документации 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 ходим клиентом ollama, на http://localhost:11434/v1 клиентом openai, в поле model узказываем GigaChat-20B-A3B-instruct:Q4_K_M.

Заключение

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

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

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

Автор: efreelancer

Источник

Rambler's Top100