Две недели назад мы выпустили GGUF-форматы jina-embeddings-v4 — универсальной модели 向量模型 для мультимодального многоязычного поиска — с различными квантованными версиями. Наша мотивация была проста: поскольку jina-embeddings-v4 является моделью с 3.75B параметрами, ванильная transformer-версия не очень хорошо масштабируется на наших API-инстансах GCP G2 (L4 GPU), поэтому мы хотели ускорить вывод, используя эти меньшие по размеру и более быстрые GGUF-версии. В ходе наших экспериментов мы обнаружили несколько интересных моментов при преобразовании и запуске GGUF 向量模型ей. Поскольку основное внимание сообщества llama.cpp сосредоточено на 大模型ях, мы подумали, что было бы полезно поделиться этим с точки зрения поставщика 向量模型ей.
Особенно актуально то, что сегодняшние 向量模型и практически идентичны 大模型ям — например, jina-embeddings-v4 основана на Qwen2.5-VL-3B-instruct, а jina-reranker-m0 — на Qwen2-VL-2B. Единственное реальное отличие — это вывод: 大模型и являются генеративными, а 向量模型и и 重排器ы — дискриминативными. Это создает как возможности, так и проблемы: с одной стороны, мы можем использовать эффективную реализацию llama.cpp (например, ubatch_size) для обслуживания 向量模型ей/重排器ов; с другой стороны, реализации 向量模型ей в llama.cpp в основном разрабатывались для более старых архитектур, использующих только кодировщик (например, модели на основе RoBERTa), и еще не полностью соответствуют современным 向量模型ям/重排器ам, использующим только декодировщик. В этой статье мы расскажем о том, что мы узнали при адаптации современных 向量模型ей для работы с форматом GGUF и инструментами llama.cpp, например, llama-embedding и llama-serving.
tagБазовые и квантованные GGUF
jina-embeddings-v4 основана на Qwen2.5-VL-3B-instruct с тремя LoRA-адаптерами: retrieval (оптимизирован для задач поиска), text-matching (оптимизирован для задач определения сходства предложений) и code (оптимизирован для задач поиска кода). Она также интенсивно обучается для визуального поиска документов и многовекторного вывода в стиле позднего взаимодействия. Таким образом, идея здесь заключается в том, чтобы использовать существующую графовую реализацию Qwen2.5-VL-3B в llama.cpp и использовать llama-embedding для вывода.
Однако первое, что мы заметили, — это ошибочное поведение в реализации mmproj или vision transformer в llama.cpp, которая выдает разные 向量模型и по отношению к torch-реализации Qwen2.5-VL-3B при одинаковом изображении на входе. Пока мы исправляем эту проблему в нашей ветке, мы решили пока исключить vision tower из GGUF-версий. Более подробную информацию об этом обсуждении можно найти здесь.
Многовекторный вывод 向量模型ей также не поддерживается из коробки, но это не такая большая проблема, как vision transformers. Многовекторный вывод происходит из обученного MLP в последнем transformer-блоке, поэтому в худшем случае мы всегда можем экспортировать этот MLP отдельно в numpy и применить его после получения 向量模型ей на уровне 词元ов из llama.cpp — что мы и сделали для jina-reranker-m0-GGUF. Конечно, это не очень эффективно, но работает без необходимости изменять и перекомпилировать llama.cpp.
Мы удалили vision transformer и многовекторный проектор и получили три базовые GGUF-модели в F16.
Итак, чтобы полностью соответствовать существующей реализации графа Qwen2.5-VL-3B в llama.cpp, мы удалили vision transformer и многовекторный проектор в последнем transformer-блоке и объединили все LoRA-адаптеры обратно в базовую языковую модель. Это дало нам три task-specific v4 модели по 3.09B параметров каждая — по сравнению с 3.75B параметрами оригинальной v4:
| HuggingFace Repo | Task |
|---|---|
jinaai/jina-embeddings-v4-text-retrieval-GGUF |
Text retrieval |
jinaai/jina-embeddings-v4-text-code-GGUF |
Code retrieval |
jinaai/jina-embeddings-v4-text-matching-GGUF |
Sentence similarity |
Затем мы использовали calibration_data_v5_rc.txt (который можно найти здесь и рекомендован Unsloth) для калибровки всех трех базовых GGUF-моделей и получили три файла imatrix, затем использовали llama-quantize с imatrix для квантования моделей из float16 следующим образом:
# build imatrix
llama-imatrix -m jina-embeddings-v4-text-retrieval-F16.gguf -f calibration_data_v5_rc.txt -ngl 99 --no-ppl -o imatrix-retrieval-512.dat
# quantize
./quantize.sh jina-embeddings-v4-text-retrieval-F16.gguf retrieval-i3 imatrix-retrieval-512.dat jinaai/jina-embeddings-v4-text-retrieval-GGUFСкрипт quantize.sh показан ниже:
#!/bin/bash
F16_MODEL_FILE="$1"
OUTPUT_DIR="$2"
IMATRIX="$3"
HF_REPO="$4"
FILENAME="$(basename "$F16_MODEL_FILE")"
BASE_NAME="${FILENAME%-F16.gguf}"
BASE_NAME="${BASE_NAME%.gguf}"
mkdir -p "$OUTPUT_DIR"
# Array of quantization types
QUANT_TYPES=("IQ1_S" "IQ1_M" "IQ2_XXS" "IQ2_M" "Q2_K" "IQ4_NL" "IQ4_XS" "IQ3_XXS" "IQ3_S" "IQ3_M" "IQ3_XS" "Q3_K_M" "Q4_K_M" "Q5_K_S" "Q5_K_M" "Q6_K" "Q8_0")
for quant_type in "${QUANT_TYPES[@]}"; do
llama-quantize --imatrix "${IMATRIX}" "$F16_MODEL_FILE" "${OUTPUT_DIR}/${BASE_NAME}-${quant_type}.gguf" $quant_type 8
doneВ конечном итоге, мы загрузили все квантизации на HuggingFace.
| Квантизация | BPW | Размер файла (ГБ) |
|---|---|---|
| IQ1_S | 2.04 | 0.73 |
| IQ1_M | 2.19 | 0.79 |
| IQ2_XXS | 2.44 | 0.88 |
| IQ2_M | 2.94 | 1.06 |
| Q2_K | 3.29 | 1.18 |
| IQ3_XXS | 3.31 | 1.19 |
| IQ3_XS | 3.59 | 1.29 |
| IQ3_S | 3.76 | 1.35 |
| IQ3_M | 3.84 | 1.38 |
| Q3_K_M | 4.11 | 1.48 |
| IQ4_NL | 4.72 | 1.69 |
| IQ4_XS | 4.49 | 1.61 |
| Q4_K_M | 4.99 | 1.79 |
| Q5_K_S | 5.61 | 2.02 |
| Q5_K_M | 5.75 | 2.07 |
| Q6_K | 6.56 | 2.36 |
| Q8_0 | 8.50 | 3.05 |
| F16 | 16.00 | 5.75 |
| v3 (Transformers) | 16.00 | 1.10 |
| v4 (Transformers) | 16.00 | 7.40 |

tagИспользование и предостережения
Теперь мы можем использовать llama-server и llama-embedding для обслуживания GGUF для создания 向量模型. В отличие от библиотек transformer, где у нас есть гибкость в написании пользовательского кода предварительной обработки входных данных, мы должны обрабатывать эту часть вручную (если мы не хотим перекомпилировать llama-server и llama-embedding). В частности, чтобы получить результаты, которые полностью согласуются с использованием AutoModel.from_pretrained("jinaai/jina-embeddings-v4")..., вам нужно быть очень осторожным с префиксами и вручную добавлять их к входным данным вашей GGUF-модели. Вот справочная таблица:
| Задача | prompt_name в реализации Transformer |
Фактический ввод в модель |
|---|---|---|
retrieval |
query (по умолчанию) |
Query: {original_text} |
retrieval |
passage |
Passage: {original_text} |
text-matching |
query (по умолчанию) |
Query: {original_text} |
text-matching |
passage |
Query: {original_text} ⚠️ |
code |
query (по умолчанию) |
Query: {original_text} |
code |
passage |
Passage: {original_text} |
Некоторых пользователей может удивить ⚠️, что prompt_name='passage' переопределяется на "Query: " при использовании text-matching в исходном AutoModel.from_pretrained("jinaai/jina-embeddings-v4").... Но на самом деле это имеет смысл, поскольку text-matching — это задача определения сходства предложений без левой/правой ролей, входные данные симметричны.
tagЧерез llama-server
После установки llama.cpp вы можете запустить llama-server, чтобы разместить модель создания 向量模型 в качестве HTTP-сервера, совместимого с OpenAI API. Например, чтобы использовать text-matching с F16, вы можете сделать следующее:
llama-server -hf jinaai/jina-embeddings-v4-text-matching-GGUF:F16 --embedding --pooling mean -ub 8192
--pooling mean требуется, поскольку v4 является 向量模型 со средним пулингом.
Затем отправьте запрос через:
curl -X POST "http://127.0.0.1:8080/v1/embeddings" \
-H "Content-Type: application/json" \
-d '{
"input": [
"Query: A beautiful sunset over the beach",
"Query: Un beau coucher de soleil sur la plage",
"Query: 海滩上美丽的日落",
"Query: 浜辺に沈む美しい夕日"
]
}'
При использовании моделей retrieval и code добавьте Query: или Passage: перед вводом текста, как здесь:
curl -X POST "http://127.0.0.1:8080/v1/embeddings" \
-H "Content-Type: application/json" \
-d '{
"input": [
"Query: A beautiful sunset over the beach",
"Query: Un beau coucher de soleil sur la plage",
"Passage: 海滩上美丽的日落",
"Passage: 浜辺に沈む美しい夕日"
]
}'
tagЧерез llama-embedding
Для быстрой проверки работоспособности вы также можете использовать предварительно скомпилированный llama-embedding для однократного создания 向量模型. Мы не рекомендуем использовать его для массового создания 向量模型, поскольку у него есть некоторые проблемы с производительностью, которые мы обсудим в следующем разделе:
llama-embedding -hf jinaai/jina-embeddings-v4-text-matching-GGUF:F16 --pooling mean -p "Query: jina is awesome" --embd-output-format json 2>/dev/null
Прочтите следующий раздел для более производительного массового создания 向量模型 с нашей сборкой llama-embedding с некоторыми исправлениями и улучшениями.
tagКраткое изложение предостережений
Прежде чем перейти к более производительной реализации, давайте подытожим предостережения относительно моделей GGUF:
- Вы должны вручную добавлять
Query:илиPassage:перед текстовыми входными данными. - Они не могут обрабатывать ввод изображений прямо сейчас, потому что мы удалили vision transformers из модели GGUF. Нам пришлось удалить их из-за ошибок в реализации vision transformer/
mmprojв llama.cpp дляQwen2.5-vl-3b, которые мы сейчас исправляем вместе с upstream. - Они не могут выводить многовекторные 向量模型, поскольку это не является частью графовой реализации
Qwen2.5-vl-3bвllama.cpp. Самый простой обходной путь без перекомпиляцииllama.cpp— экспортировать и запустить MLP отдельно после получения 词元-level 向量模型, установив--pooling noneвllama-embedding. - v4 обучена с помощью Matryoshka representation learning, и преобразование в GGUF сохраняет эту функцию. Если вы получаете 向量模型 формы
NxD, вы можете просто использоватьembeddings[:, :truncate_dim], чтобы получить меньшие усеченные 向量模型. Однако не каждое измерение обучено. Для v4 мы обучилиtruncate_dimдля этих конкретных значений:[128, 256, 512, 1024, 2048]. Это означает, что качествоembeddings[:, :131]не будет какой-то интерполяцией между качествомembeddings[:, :128]иembeddings[:, :256], но будет значительно хуже, чем 向量模型 128-dim или 256-dim, потому что 131-dim не обучено. - Late chunking по-прежнему может работать как часть постобработки после получения 词元-level 向量模型 с помощью
--pooling none. Точно так же, как мы делали с отделением MLP от графаllama.cpp, это не очень эффективно, но не требует перекомпиляции. Однако есть еще одно предостережение: поскольку v4 является причинной моделью, late chunking больше не будет двунаправленным — более ранние chunk 向量模型 не будут содержать контекстную информацию из последующих chunks. Помните, что в v3 каждый chunk 向量模型 имел глобальную контекстную информацию, потому что мы использовали двунаправленные маски внимания в transformer blocks. Внутри мы обсуждали, делает ли причинность late chunking устаревшим: некоторые утверждают, что «контекст также является причинным» — это означает, что читатель обрабатывает текст слева направо, поэтому контекст, необходимый для интерпретации предложения, должен исходить из предшествующего текста. Другие говорят, что ограничение late chunking до однонаправленного блокирует обмен контекстом между chunks. В любом случае, эффективность late chunking в v4 остается сомнительной и требует дальнейшего изучения.
tagЭффективное создание 向量模型 через llama-embedding
llama-embedding — это относительно простая C++ обертка поверх llama.cpp для встраивания текста с очень чистым вводом-выводом: stdin, stdout. Сейчас мы сосредоточены на улучшении этого, а не llama-server, потому что есть множество других проблем, таких как сетевая очередь, балансировка нагрузки, multi-tenancy и сериализация, которые, по нашему мнению, выходят за рамки данной статьи. Наш вопрос прост: какую скорость мы можем получить от графического процессора L4 24GB и каково пиковое использование VRAM для встраивания длинных документов?
Но почему L4? В основном потому, что GCP предлагает довольно удобные функции Cloud Run поверх него, и это наиболее широко доступный и экономичный тип графического процессора, который вы можете получить для API бессерверного вывода. GCP предлагает A100 и H100 на Cloud Run по запросу, и команда GCP время от времени предлагает нам использовать графические процессоры получше. Но наша философия проста: если нам нужны A100/H100 для обслуживания модели 3B, то это явно проблема с нашими навыками.
Для справки: в llama.cpp логический размер пакета (-b) представляет максимальное количество токенов, отправляемых модели за один вызов оценки. При обработке длинных входных данных они разбиваются на фрагменты до этого размера. Физический размер пакета (-ub) - это фактическое количество токенов, обрабатываемых одновременно за один прямой проход через оборудование, ограниченное доступной памятью. Обновления KV-кэша происходят после завершения каждого физического пакета. Окно контекста (-c) - это жесткое ограничение на количество токенов, которое модель может "видеть" одновременно - для моделей v4 это 32 000 токенов, что представляет собой максимальную продолжительность внимания модели. Все токены должны помещаться в это окно контекста для поддержания связного внимания ко всей последовательности. На следующем рисунке показаны их взаимосвязи.

tagНаши исправления
В нашем форке, указанном выше, мы внесли несколько оптимизаций, чтобы сделать llama-embedding более эффективным:
- Упрощенная обработка пакетов: Мы автоматически устанавливаем
-bравным-c, эффективно делая этот параметр устаревшим. Пользователям больше не нужно указывать-b, поскольку мы всегда используем полную длину контекста модели для логического пакетирования. - Гибкое управление памятью: В отличие от оригинальной реализации, где
-ubбыл принудительно равен-b(поскольку они предполагали, что модели векторных представлений не могут быть причинными), мы позволяем пользователям независимо устанавливать-ub. Это дает точный контроль над пиковым использованием VRAM при кодировании длинных контекстов - вы можете обрабатывать контекст 32K с небольшим физическим пакетом в 512 токенов, чтобы оставаться в пределах ограничений VRAM благодаря реализации KV-кэша. Обратите внимание, что это изменение корректно только для причинных моделей векторных представлений, таких как jina-embeddings-v4 - для архитектур только с кодировщиком, таких как v3, это была бы неправильная реализация. - Исправлено среднее объединение: Мы исправили вычисление среднего объединения для векторных представлений, когда
ub < b, которое ранее было сломано в оригинальной реализации.
Это изменение значительно упрощает работу с моделями векторных представлений с длинным контекстом, предназначенными только для декодирования, при эффективном управлении ограничениями памяти. Теперь пользователям нужно настроить только два параметра:
-c: Максимальная длина контекста (сколько токенов может обработать модель векторных представлений)-ub: Физический размер пакета (сколько токенов GPU обрабатывает одновременно)
Итак, точный код для запуска нашего форка на L4 выглядит следующим образом:
# Compile
git clone https://github.com/hanxiao/llama.cpp.git
cd llama.cpp
cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release -j 8
# Run
INPUT_PREFIX="Query: " # or "Passage: "
cat big_input.txt | sed "s/^/${INPUT_PREFIX}/" | \
./llama.cpp/build/bin/llama-embedding -f /dev/stdin \
-hf "jinaai/jina-embeddings-v4-text-retrieval-GGUF:FP16" \
--pooling mean \
--no-escape \
--embd-output-format array \
--ubatch-size 512 \
--ctx-size 8192 \
--flash-attn \
-ngl 99 \
> "embeddings.txt" 2> "error.log"Каждая строка в big_input.txt - это предложение, которое нужно представить в виде векторного представления. --no-escape следует установить, чтобы предотвратить интерпретацию \n в предложении как разделителей. --flash-attn и -ngl 99 следует установить для достижения наилучшей производительности на GPU L4.
tagБенчмарк
Мы хотим понять следующие вопросы с помощью бенчмаркинга:
- Насколько хороша наша квантизация по сравнению с оригинальной v4 Float16? В какой момент она ухудшается настолько, что нам было бы лучше просто использовать векторные представления v3?
- Как быстро каждая квантизация может работать на L4 и каково пиковое использование VRAM?
- Как физический размер пакета
-ubи длина контекста-cвлияют на скорость и пиковое использование VRAM?
Наборы данных, которые мы использовали в бенчмаркинге:
| Задача | Документы | Запросы | Релевантные пары | Средняя длина документа | Максимальная длина документа | Средняя длина запроса |
|---|---|---|---|---|---|---|
| NanoHotpotQA | 5,090 | 50 | 100 | 57.3 | 345 | 14.9 |
| NanoSciFact | 2,919 | 50 | 56 | 205.8 | 1524 | 13.5 |
| NanoArguAna | 3,635 | 50 | 50 | 164.5 | 1058 | 193.0 |
| NanoNFCorpus | 2,953 | 50 | 2,518 | 223.3 | 1460 | 3.3 |
| NanoFiQA2018 | 4,598 | 50 | 123 | 159.1 | 1882 | 10.2 |
Мы использовали нашу пользовательскую сборку llama-embedding для бенчмаркинга.
tagКачество квантизаций
Лучшая квантованная версия - IQ3_M (3.84 BPW) - квантизации ниже 2 бит работают хуже, чем v3, поэтому нет смысла их использовать.

| Quantization | NanoHotpotQA | NanoFiQA2018 | NanoArguAna | NanoNFCorpus | NanoSciFact |
|---|---|---|---|---|---|
| IQ1_S | 0.6369 | 0.3178 | 0.3798 | 0.2933 | 0.5934 |
| IQ1_M | 0.6316 | 0.3313 | 0.5167 | 0.3256 | 0.6114 |
| IQ2_XXS | 0.7236 | 0.4582 | 0.4584 | 0.4067 | 0.7392 |
| IQ2_M | 0.7427 | 0.5869 | 0.5090 | 0.4468 | 0.7880 |
| Q2_K | 0.7683 | 0.5744 | 0.5168 | 0.4183 | 0.7546 |
| IQ3_XXS | 0.7780 | 0.5991 | 0.4811 | 0.4267 | 0.7610 |
| IQ3_XS | 0.7727 | 0.5615 | 0.5195 | 0.4439 | 0.7726 |
| IQ3_S | 0.8002 | 0.5505 | 0.4886 | 0.4381 | 0.7690 |
| IQ3_M | 0.8106 | 0.5387 | 0.5091 | 0.4462 | 0.7760 |
| Q3_K_M | 0.7567 | 0.5267 | 0.4486 | 0.4092 | 0.7775 |
| IQ4_NL | 0.7930 | 0.5598 | 0.4911 | 0.4285 | 0.7794 |
| IQ4_XS | 0.7979 | 0.5627 | 0.4947 | 0.4258 | 0.7789 |
| Q4_K_M | 0.8029 | 0.5569 | 0.4883 | 0.4226 | 0.7877 |
| Q5_K_S | 0.7969 | 0.5581 | 0.4721 | 0.4288 | 0.7842 |
| Q5_K_M | 0.7927 | 0.5601 | 0.4745 | 0.4247 | 0.7873 |
| Q6_K | 0.7951 | 0.5636 | 0.4822 | 0.4337 | 0.7846 |
| Q8_0 | 0.7938 | 0.5687 | 0.4784 | 0.4335 | 0.7851 |
| F16 | 0.7940 | 0.5610 | 0.4931 | 0.4343 | 0.7963 |
| v3 (Transformers) | 0.7393 | 0.5144 | 0.4600 | 0.4068 | 0.7820 |
| v4 (Transformers) | 0.7977 | 0.5571 | 0.4844 | 0.4351 | 0.7963 |
tagСкорость и VRAM
Теперь зафиксируем эталонный набор данных на NanoHotpotQA и отобразим все квантования по их битам на вес в зависимости от скорости (измеряется в токенах в секунду) и потребления VRAM. Мы обнаружили, что версии GGUF немного быстрее, чем ванильная версия при FP16 (2023 против 1865 токенов/сек). Большинство квантований группируются около 2000-2100 токенов/сек. С включенным flash attention мы получаем ~77% прирост скорости для всех квантований (3000+ против 2000+ токенов/сек). Однако, наиболее эффективное квантование Q8_0 со скоростью около 3700 токенов в секунду все еще намного отстает от ванильной v3 (572M параметров), которая достигает 16000 токенов/сек. Квантованные версии значительно экономят VRAM и почти достигают уровня модели v3 FP16 с IQ3.

| Quantization | BPW | File Size (GB) | Peak VRAM (GB) | Token/s w FA | Token/s w/o FA |
|---|---|---|---|---|---|
| IQ1_S | 2.04 | 0.73 | 4.04 | 3625 | 2050 |
| IQ1_M | 2.19 | 0.79 | 4.09 | 3349 | 1997 |
| IQ2_XXS | 2.44 | 0.88 | 4.19 | 3701 | 2071 |
| IQ2_M | 2.94 | 1.06 | 4.37 | 3407 | 1989 |
| Q2_K | 3.29 | 1.18 | 4.49 | 3173 | 1905 |
| IQ3_XXS | 3.31 | 1.19 | 4.50 | 3668 | 2067 |
| IQ3_XS | 3.59 | 1.29 | 4.60 | 3604 | 2053 |
| IQ3_S | 3.76 | 1.35 | 4.66 | 3599 | 2049 |
| IQ3_M | 3.84 | 1.38 | 4.69 | 3603 | 2053 |
| Q3_K_M | 4.11 | 1.48 | 4.78 | 3450 | 2008 |
| IQ4_NL | 4.72 | 1.69 | 5.00 | 3571 | 2039 |
| IQ4_XS | 4.49 | 1.61 | 4.92 | 3585 | 2046 |
| Q4_K_M | 4.99 | 1.79 | 5.10 | 3558 | 2045 |
| Q5_K_S | 5.61 | 2.02 | 5.32 | 3567 | 2044 |
| Q5_K_M | 5.75 | 2.07 | 5.38 | 3528 | 2034 |
| Q6_K | 6.56 | 2.36 | 5.66 | 3334 | 1981 |
| Q8_0 | 8.50 | 3.05 | 6.36 | 3767 | 2101 |
| F16 | 16.00 | 5.75 | 9.70 | 3399 | 2023 |
| v3 (Transformers) | 16.00 | 1.10 | 2.82 | 16505 | |
| v4 (Transformers) | 16.00 | 7.40 | 14.45 | 1865 |
Нажмите, чтобы развернуть информацию о системе
load_tensors: loading model tensors, this can take a while... (mmap = true)
load_tensors: offloading 36 repeating layers to GPU
load_tensors: offloading output layer to GPU
load_tensors: offloaded 37/37 layers to GPU
load_tensors: CUDA0 model buffer size = 3127.61 MiB
load_tensors: CPU_Mapped model buffer size = 315.30 MiB
...................................................................................
llama_context: constructing llama_context
llama_context: n_seq_max = 1
llama_context: n_ctx = 4096
llama_context: n_ctx_per_seq = 4096
llama_context: n_batch = 4096
llama_context: n_ubatch = 4096
llama_context: causal_attn = 1
llama_context: flash_attn = 1 // 1 for w/ FA in the table; 0 for w/o FA
llama_context: kv_unified = true
llama_context: freq_base = 1000000.0
llama_context: freq_scale = 1
llama_context: n_ctx_per_seq (4096) < n_ctx_train (128000) -- the full capacity of the model will not be utilized
llama_context: CUDA_Host output buffer size = 0.59 MiB
llama_kv_cache_unified: CUDA0 KV buffer size = 144.00 MiB
llama_kv_cache_unified: size = 144.00 MiB ( 4096 cells, 36 layers, 1/1 seqs), K (f16): 72.00 MiB, V (f16): 72.00 MiB
llama_context: CUDA0 compute buffer size = 2470.16 MiB
llama_context: CUDA_Host compute buffer size = 96.17 MiB
llama_context: graph nodes = 1234
llama_context: graph splits = 2
common_init_from_params: added <|endoftext|> logit bias = -inf
common_init_from_params: added <|im_end|> logit bias = -inf
common_init_from_params: added <|fim_pad|> logit bias = -inf
common_init_from_params: added <|repo_name|> logit bias = -inf
common_init_from_params: added <|file_sep|> logit bias = -inf
common_init_from_params: setting dry_penalty_last_n to ctx_size = 4096
common_init_from_params: warming up the model with an empty run - please wait ... (--no-warmup to disable)
system_info: n_threads = 4 (n_threads_batch = 4) / 8 | CUDA : ARCHS = 890 | USE_GRAPHS = 1 | PEER_MAX_BATCH_SIZE = 128 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | AVX512 = 1 | AVX512_VNNI = 1 | LLAMAFILE = 1 | OPENMP = 1 | REPACK = 1 |
main: n_tokens in batch = 0
main: number of embeddings = 5090
tagОптимальный размер физического пакета и контекста
Теперь зафиксируем тип квантования на IQ3_S и изучим, как размер физического пакета (-ub) и размер контекста (-c) влияют на скорость и VRAM. Результаты на L4 GPU показывают, что -ub=512 с -c=2048 обеспечивает оптимальную конфигурацию, обеспечивая 4143 токена/сек при использовании 2025 МБ VRAM. Вывод интуитивно понятен: если вы знаете максимальную длину одного документа во входных данных, используйте меньший размер контекста, которого достаточно для его охвата. Для размера физического пакета 512 токенов, похоже, являются оптимальным вариантом на L4 GPU.

Производительность в токенах в секунду
| ubatch_size | ctx_size=64 | ctx_size=128 | ctx_size=256 | ctx_size=512 |
|---|---|---|---|---|
| 64 | 2233 | 2093 | 2128 | 2125 |
| 128 | N/A | 2866 | 2821 | 2877 |
| 256 | N/A | N/A | 3287 | 3349 |
| 512 | N/A | N/A | N/A | 3469 |
| ubatch_size | ctx_size=2048 | ctx_size=4096 | ctx_size=8192 | ctx_size=16384 |
|---|---|---|---|---|
| 256 | 3971 | 3630 | 3593 | 2766 |
| 512 | 4143 | 3797 | 3758 | 2852 |
| 1024 | 4059 | 3742 | 3707 | 2822 |
| 2048 | 3957 | 3631 | 3603 | 2762 |
| 4096 | N/A | 3450 | 3410 | 2625 |
Пиковое использование VRAM (МБ)
| ubatch_size | ctx_size=64 | ctx_size=128 | ctx_size=256 | ctx_size=512 |
|---|---|---|---|---|
| 64 | 1691 | 1689 | 1689 | 1697 |
| 128 | N/A | 1729 | 1727 | 1737 |
| 256 | N/A | N/A | 1803 | 1811 |
| 512 | N/A | N/A | N/A | 1963 |
| ubatch_size | ctx_size=2048 | ctx_size=4096 | ctx_size=8192 | ctx_size=16384 |
|---|---|---|---|---|
| 256 | 1885 | 1947 | 2099 | 2409 |
| 512 | 2025 | 2101 | 2257 | 2577 |
| 1024 | 2329 | 2407 | 2571 | 2917 |
| 2048 | 2933 | 3025 | 3203 | 3597 |
| 4096 | N/A | 4285 | 4497 | 4985 |
tagЗаключение
Для пользователей v4, которые хотят эффективно запускать квантованные GGUF на бюджетных GPU, выберите IQ3_S или IQ3_M с нашей пользовательской сборкой llama-embedding — это должно дать вам 4000 токенов/сек на обычных наборах данных (где длина предложения <2048 токенов). Для встраивания более длинных документов увеличьте размер контекста -c и контролируйте размер физического пакета -ub, чтобы уменьшить занимаемый объем VRAM. С нашей пользовательской сборкой вы можете кодировать очень длинные документы (>32K токенов), используя всего 3 ГБ VRAM, установив для -ub небольшое число, например 1024 — что было невозможно с исходной реализацией или стандартными трансформаторами.
Поиск оптимизации скорости никогда не заканчивается. Всегда есть место для более быстрой и экономичной реализации с более высокой пропускной способностью. 4000 токенов/сек, вероятно, не наш предел — предстоит еще много работы. Помимо исправления реализации qwen2.5-vl-3b mmproj/vision transformer в llama.cpp, мы также изучаем более глубокие оптимизации на уровне llama.graph и KV-cache, улучшаем логику пакетирования llama-serving и добавляем параметры потоковой передачи в API встраивания. Наша цель — сделать так, чтобы llama.cpp изначально поддерживал современные мультимодальные встраивания только для декодера для наших текущих и будущих выпусков reranker.






