Формат GGUF: структура, использование и виды квантования

Стандартные методы сохранения моделей часто не справляются с требованиями квантизированных моделей, такими как хранение низкоразрядных весов вместе с соответствующими масштабными коэффициентами и нулевыми точками. Формат GGUF (Georgi Gerganov Universal Format) появился как практическое решение, специально разработанное для этих задач, особенно в экосистеме llama.cpp и для эффективного вывода на CPU, хотя он также совместим с ускорением на GPU.

GGUF развился из более раннего формата GGML и ставит целью создание более расширяемого формата файлов для распространения и запуска квантизированных крупных языковых моделей. Его основная задача — упаковать всё необходимое для работы модели (детали архитектуры, информацию о токенизаторе, параметры квантизации и сами квантизированные веса) в один переносимый файл.

Структура файла GGUF

Файл GGUF — это бинарный формат, состоящий из трёх основных частей: заголовка, метаданных и данных тензоров.

  • Заголовок: Содержит "магическое число" (GGUF) для идентификации типа файла и номер версии формата. Это позволяет инструментам быстро проверять, является ли файл корректным GGUF, и обрабатывать возможные различия версий.

  • Метаданные: Гибкое хранилище пар "ключ-значение", содержащее важную информацию о модели. Это значительное улучшение по сравнению со старыми форматами, так как позволяет хранить произвольные детали, необходимые для корректной загрузки и запуска модели без двусмысленности. Распространённые ключи метаданных включают:

    • general.architecture: Указывает архитектуру модели (например, llama, mistral).
    • general.name: Человекочитаемое имя модели.
    • [architecture].context_length: Максимальная длина последовательности, которую поддерживает модель.
    • [architecture].embedding_length: Размерность эмбеддингов модели.
    • [architecture].block_count: Количество трансформерных блоков/слоёв.
    • tokenizer.ggml.model: Модель токенизатора (например, llama, gpt2).
    • tokenizer.ggml.tokens: Список токенов в словаре.
    • tokenizer.ggml.scores: Необязательные оценки, связанные с токенами (например, для SentencePiece).
    • tokenizer.ggml.merges: Правила слияния для BPE-токенизаторов.
    • tokenizer.chat_template: Шаблон Jinja2 для форматирования чат-запросов (необязательно, но очень полезно).
    • Информация о тензорах: Имена, размерности, типы данных и смещения для каждого тензора в файле. Сюда входит тип квантизации, используемый для каждого квантизированного тензора (например, Q4_0, Q8_K_S).

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

  • Данные тензоров: Непрерывный блок бинарных данных, содержащий фактические параметры модели (веса, смещения, параметры нормализации). Для квантизированных моделей этот раздел хранит низкоточные целочисленные значения и связанные с ними масштабные коэффициенты или метаданные, необходимые для деквантизации. Данные упакованы в соответствии с типом квантизации, указанным в метаданных (например, Q4_0, Q5_K, Q8_0, F16, F32 и т.д.). Данные часто выравниваются по определённым границам байтов для облегчения эффективного отображения в памяти и загрузки.

Типы квантизации в GGUF

В спецификации GGUF определены различные типы квантизации. Это означает, что файл может содержать тензоры, квантизированные разными методами. Некоторые распространённые примеры:

  • F32: Стандартный 32-битный формат с плавающей запятой (неквантизированный).
  • F16: 16-битный формат с плавающей запятой.
  • Q8_0: 8-битная квантизация с размером блока 32 и одним масштабным коэффициентом на блок.
  • Q4_0: 4-битная квантизация (тип 0), размер блока 32, один масштабный коэффициент.
  • Q4_K: 4-битная квантизация с использованием стратегии K-Quant, часто обеспечивающая лучшее качество, чем Q4_0. Использует 256 блоков, масштабный коэффициент и минимум в формате FP16.
  • Q5_K: 5-битная квантизация с использованием K-Quant.
  • Q6_K: 6-битная квантизация с использованием K-Quant.
  • IQ2_XS: 2-битная квантизация (экспериментальные/новые типы).

Конкретный тип, используемый для каждого тензора, указан в метаданных, что позволяет механизму вывода применять корректную логику деквантизации. Типы "K-Quant" (_K) обычно подразумевают реализации, направленные на повышение точности по сравнению с более простыми схемами квантизации, часто с более сложными структурами блоков или масштабными коэффициентами.

Преимущества GGUF

  • Портативность: Упаковывает модель, конфигурацию и данные токенизатора в один файл, упрощая распространение и использование.
  • Расширяемость: Система метаданных "ключ-значение" позволяет добавлять новую информацию без нарушения обратной совместимости. Также можно добавлять новые типы квантизации.
  • Быстрая загрузка: Разработан для эффективной загрузки, часто с использованием отображения в память (mmap), что позволяет лениво загружать данные тензоров, сокращая время запуска и использование ОЗУ, особенно для крупных моделей.
  • Широкая поддержка: Первоначально разработан для llama.cpp, но получил распространение в различных инструментах и библиотеках в сообществе открытого ПО, ориентированного на локальное развертывание моделей.

Использование с llama.cpp и другими инструментами

Файлы GGUF являются родным форматом для популярного механизма вывода llama.cpp. Запуск модели GGUF с помощью llama.cpp обычно прост:

./main -m путь/к/вашей/модели.gguf -p "Какова столица Малайзии?" -n 50

Здесь -m указывает путь к файлу модели GGUF. Программа llama.cpp считывает метаданные, определяет архитектуру и схемы квантизации, загружает тензоры и выполняет вывод.

Создание файлов GGUF обычно включает использование скриптов конвертации. Например, в репозитории llama.cpp есть скрипты на Python (convert.py, convert-hf-to-gguf.py), которые могут преобразовывать модели в форматах, таких как Hugging Face Transformers (файлы PyTorch .bin или safetensors), в GGUF, применяя желаемую квантизацию в процессе. Обычно на этапе конвертации указывается целевой тип квантизации (например, q4_K_M, q8_0).

Пример конвертации модели Hugging Face в GGUF:

python llama.cpp/convert.py models/my-hf-model --outfile models/my-model.q4_K_M.gguf --outtype q4_K_M

Понимание формата GGUF важно при работе с локально запускаемыми LLM, так как он предоставляет стандартизированный и эффективный способ обработки квантизированных моделей, особенно за пределами типичных фреймворков глубокого обучения, ориентированных на Python/GPU. Его конструктивные решения направлены на удобство использования и производительность для вывода на более широком спектре оборудования.

Метод K-Quant

  • K-Quant — это метод квантования, который используется для сокращения размера моделей и улучшения их производительности на CPU. Он позволяет сохранить больше качества оригинальной модели по сравнению с более старыми методами квантования.
  • K-Quant использует блоковое квантование, что означает, что веса модели разбиваются на блоки, и каждый блок квантуется отдельно. Это позволяет более точно управлять процессом квантования и уменьшать ошибки.
  • В K-Quant используется важность весов. Это означает, что более важные веса (те, которые больше влияют на качество модели) квантуются с большей точностью, чем менее важные. Это достигается с помощью матрицы важности (imatrix), которая помогает определить, какие веса более важны.

Преимущества K-Quant:

  • Скорость и память: K-Quant может быть быстрее и использовать меньше памяти по сравнению с другими методами квантования, сохраняя при этом качество модели.
  • Гибкость: K-Quant поддерживает различные уровни квантования, такие как Q2_K, Q3_K, Q4_K, Q5_K, Q6_K и Q8_K, что позволяет выбирать баланс между размером модели и её точностью.
  • Совместимость с GGUF: K-Quant особенно хорошо работает с форматом GGUF, который предназначен для эффективного хранения и загрузки квантованных моделей.

Основные особенности K-Quant:

  1. Блоковое квантование: K-Quant использует блоковое квантование, что означает, что веса модели разбиваются на блоки, и каждый блок квантуется отдельно. Это позволяет более точно управлять процессом квантования и уменьшать ошибки.

  2. Использование матрицы важности: Одной из ключевых особенностей K-Quant является использование матрицы важности (imatrix). Эта матрица помогает определить, какие веса более важны для качества модели, и квантует их с большей точностью. Это позволяет сохранить качество модели даже при сильном сокращении её размера.

  3. Различные уровни квантования: K-Quant поддерживает различные уровни квантования, такие как Q2_K, Q3_K, Q4_K, Q5_K, Q6_K и Q8_K. Каждый уровень предлагает разный баланс между размером модели, скоростью вычислений и точностью. Например, Q4_K и Q5_K часто рекомендуются для баланса между производительностью и качеством.

Для запуска модели с использованием K-Quant в формате GGUF можно использовать инструменты, такие как llama.cpp. Например, команда для запуска модели может выглядеть следующим образом:

./main -m путь/к/вашей/модели.gguf -p "Ваш запрос" -n 50

Где -m указывает путь к файлу модели GGUF, а -p — это ваш запрос.

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

Устаревшие методы квантования (Legacy quants: Q4_0, Q4_1, Q8_0, ...)

  • Простота и скорость: Это очень простые, базовые и быстрые методы квантования.
  • Блочная структура: Каждый слой разделяется на блоки по 256 весов, и каждый блок преобразуется в 256 квантованных значений и одну (_0) или две (_1) дополнительные константы (именно эти дополнительные константы, делают среднее количество бит на вес в Q4_1 равным 4.0625).
  • Распаковка: Квантованные веса легко распаковываются с использованием побитового сдвига, операции AND и умножения (а также сложения в вариантах _1).
  • Совместимость: Работают везде.

K-quants (Q3_K_S, Q5_K_M, ...)

  • Внедрение: Введены в pull request #1684 для llama.cpp.
  • Умное распределение бит: Биты распределяются более интеллектуально, чем в простых методах квантования.
  • Обозначения: Q3_K или Q4_K относятся к преобладающему типу квантования, используемому в файле (и к тому факту, что используется этот смешанный "K" формат), в то время как суффиксы _XS, _S или _M являются псевдонимами, указывающими на конкретную смесь типов квантования, используемых в файле (некоторые слои более важны, поэтому выделение им большего количества бит на вес может быть полезным).
  • Распаковка: отдельные веса хранятся похожим образом на простые методы квантования, поэтому их можно распаковать так же легко, но с некоторыми дополнительными сдвигами/AND для распаковки констант на блок.
  • Преимущества: В результате K-quants работают так же быстро как базовые методы квантования, и, учитывая, что они также имеют меньшую ошибку квантования, они являются очевидным лучшим выбором в большинстве случаев.

I-quants (IQ2_XXS, IQ3_S, ...)

  • Новый метод квантования, представленный в pull request #4773.
  • Блочное квантование: В основе всё ещё лежит блочное квантование, но с некоторыми новыми функциями.
  • Таблица поиска: Одним из отличий является использование таблицы поиска для хранения специальных значений, необходимых в процессе декодирования.
  • Требовательность к ресурсам: Дополнительный доступ к памяти для таблицы поиска, что делает этап де-квантования значительно более требовательным, чем у простых методов и K-quants, до такой степени, что вы можете столкнуться с ограничениями по производительности CPU, а не по пропускной способности памяти.
  • Преимущества при достаточной вычислительной мощности: если у вас достаточно вычислительной мощности, уменьшенный размер модели может улучшить общую производительность по сравнению с K-quants, уменьшая узкое место в виде пропускной способности памяти.

Расшифровки аббревиатур типов квантования

Актуальную информацию смотрите тут https://huggingface.co/docs/hub/gguf

Тип Источник Описание
F64 Wikipedia 64-битное число с плавающей запятой двойной точности по стандарту IEEE 754.
I64 GH 64-битное целое число фиксированной разрядности.
F32 Wikipedia 32-битное число с плавающей запятой одинарной точности по стандарту IEEE 754.
I32 GH 32-битное целое число фиксированной разрядности.
F16 Wikipedia 16-битное число с плавающей запятой половинной точности по стандарту IEEE 754.
BF16 Wikipedia 16-битная укороченная версия 32-битного числа с плавающей запятой одинарной точности по стандарту IEEE 754 (Brain Floating Point).
I16 GH 16-битное целое число фиксированной разрядности.
Q8_0 GH 8-битное квантование с округлением до ближайшего (q). Каждый блок содержит 32 веса. Формула веса: w = q * block_scale. Устаревший метод квантования (сегодня используется не широко).
Q8_1 GH 8-битное квантование с округлением до ближайшего (q). Каждый блок содержит 32 веса. Формула веса: w = q * block_scale + block_minimum. Устаревший метод квантования (сегодня используется не широко).
Q8_K GH 8-битное квантование (q). Каждый блок содержит 256 весов. Используется только для квантования промежуточных результатов. Для этого типа квантования реализованы все скалярные произведения для 2-6 бит. Формула веса: w = q * block_scale.
I8 GH 8-битное целое число фиксированной разрядности.
Q6_K GH 6-битное квантование (q). Суперблоки с 16 блоками, каждый блок содержит 16 весов. Формула веса: w = q * block_scale (8-бит), что даёт 6.5625 бит на вес.
Q5_0 GH 5-битное квантование с округлением до ближайшего (q). Каждый блок содержит 32 веса. Формула веса: w = q * block_scale. Устаревший метод квантования (сегодня используется не широко).
Q5_1 GH 5-битное квантование с округлением до ближайшего (q). Каждый блок содержит 32 веса. Формула веса: w = q * block_scale + block_minimum. Устаревший метод квантования (сегодня используется не широко).
Q5_K GH 5-битное квантование (q). Суперблоки с 8 блоками, каждый блок содержит 32 веса. Формула веса: w = q * block_scale (6-бит) + block_min (6-бит), что даёт 5.5 бит на вес.
Q4_0 GH 4-битное квантование с округлением до ближайшего (q). Каждый блок содержит 32 веса. Формула веса: w = q * block_scale. Устаревший метод квантования (сегодня используется не широко).
Q4_1 GH 4-битное квантование с округлением до ближайшего (q). Каждый блок содержит 32 веса. Формула веса: w = q * block_scale + block_minimum. Устаревший метод квантования (сегодня используется не широко).
Q4_K GH 4-битное квантование (q). Суперблоки с 8 блоками, каждый блок содержит 32 веса. Формула веса: w = q * block_scale (6-бит) + block_min (6-бит), что даёт 4.5 бит на вес.
Q3_K GH 3-битное квантование (q). Суперблоки с 16 блоками, каждый блок содержит 16 весов. Формула веса: w = q * block_scale (6-бит), что даёт 3.4375 бит на вес.
Q2_K GH 2-битное квантование (q). Суперблоки с 16 блоками, каждый блок содержит 16 весов. Формула веса: w = q * block_scale (4-бит) + block_min (4-бит), что даёт 2.625 бит на вес.
IQ4_NL GH 4-битное квантование (q). Суперблоки с 256 весами. Вес w получается с использованием super_block_scale и матрицы важности.
IQ4_XS HF 4-битное квантование (q). Суперблоки с 256 весами. Вес w получается с использованием super_block_scale и матрицы важности, что даёт 4.25 бит на вес.
IQ3_S HF 3-битное квантование (q). Суперблоки с 256 весами. Вес w получается с использованием super_block_scale и матрицы важности, что даёт 3.44 бит на вес.
IQ3_XXS HF 3-битное квантование (q). Суперблоки с 256 весами. Вес w получается с использованием super_block_scale и матрицы важности, что даёт 3.06 бит на вес.
IQ2_XXS HF 2-битное квантование (q). Суперблоки с 256 весами. Вес w получается с использованием super_block_scale и матрицы важности, что даёт 2.06 бит на вес.
IQ2_S HF 2-битное квантование (q). Суперблоки с 256 весами. Вес w получается с использованием super_block_scale и матрицы важности, что даёт 2.5 бит на вес.
IQ2_XS HF 2-битное квантование (q). Суперблоки с 256 весами. Вес w получается с использованием super_block_scale и матрицы важности, что даёт 2.31 бит на вес.
IQ1_S HF 1-битное квантование (q). Суперблоки с 256 весами. Вес w получается с использованием super_block_scale и матрицы важности, что даёт 1.56 бит на вес.
IQ1_M GH 1-битное квантование (q). Суперблоки с 256 весами. Вес w получается с использованием super_block_scale и матрицы важности, что даёт 1.75 бит на вес.