原创 Jina AI 2025-08-14 09:04 美国
家人们帮看下,3B向量模型每秒4000 token算快么?
jina-embeddings-v4 的 GGUF 格式及其多种动态量化版本。jina-embeddings-v4 原模型有 37.5 亿参数,在我们的 GCP G2 GPU 实例上直接运行时效率不高。因此,我们希望通过更小、更快的 GGUF 格式来加速推理。在转换和运行这些 GGUF 模型的过程中,我们踩坑很多也积累了一些小技巧。而目前 llama.cpp 开发者社区主要聚焦于大型语言模型,因此我们想从一个向量模型供应商的角度,简单来聊聊我们的经验。很多人可能没意识到,现在的向量模型和大型语言模型(LLM)在架构上基本一致。例如,jina-embeddings-v4 基于 Qwen2.5-VL-3B-instruct,而 jina-reranker-m0 是基于 Qwen2-VL-2B。唯一的本质区别在于输出:LLM 的输出是生成式的,而向量模型和重排器的输出是判别式的。这种一致性有好有坏:好的是,我们可以直接利用 llama.cpp 的高效实现(如 ubatch_size和kv cache)来运行模型;现实是:llama.cpp 中现有的向量功能实现,大多是围绕着旧式的 BERT/RoBERTa 的纯编码器 encoder-only 架构开发的,尚未适配现代纯解码器的 decoder-only 向量模型。本文将分享我们如何把纯解码器的向量模型适配到 GGUF 格式以及在 llama.cpp 工具链(如 llama-embedding 和 llama-serving)的优化实操经验。基础版与量化版 GGUFjina-embeddings-v4 基于 Qwen2.5-VL-3B-instruct 构建,并集成了三个 LoRA 适配器,分别针对:retrieval:文档检索任务text-matching:文本匹配任务code:代码检索任务模型本身也为视觉文档检索和多向量输出进行了深度训练。我们的思路是,复用 llama.cpp 中已有的 Qwen2.5-VL-3B 计算图,再通过 llama-embedding 执行推理。但我们一上来就发现,llama.cpp 的 mmproj(视觉 Transformer)的实现存在 bug。给定相同的图像输入,它生成的向量结果与 Qwen2.5-VL-3B 的 Torch 实现不一致。我们正在自己的分支(fork) 中修复这个问题。但在此期间,我们决定在目前发布的 GGUF 版本中移除视觉模块。我们向上游提交的bug报告:https://github.com/ggml-org/llama.cpp/discussions/14851多向量输出功能同样也没有原生支持,但这个问题大。多向量输出来自最后一个 Transformer 模块中的一个 MLP。因此,最差情况下,我们可以先导出这个 MLP,等 llama.cpp 输出 token 级向量后,再手动应用它。jina-reranker-m0-GGUF 就是采用这种方式实现的。这个方法虽然计算效率不高,但好在MLP比较小,且不用修改和重新编译 llama.cpp 就能工作。所以,为了完全兼容 llama.cpp 现有的 Qwen2.5-VL-3B 计算图,我们剥离了视觉 Transformer 和多向量投射器,然后将所有 LoRA 适配器合并回基础语言模型。最终,我们得到了三个针对特定任务的 v4 模型,每个模型的参数量从 37.5 亿减少到了 30.9 亿。我们针对不同任务,创建了三个 GGUF 仓库:HuggingFace 仓库
任务
jinaai/jina-embeddings-v4-text-retrieval-GGUF文本检索
jinaai/jina-embeddings-v4-text-code-GGUF代码检索
jinaai/jina-embeddings-v4-text-matching-GGUF句子相似度
calibration_data_v5_rc.txt 校准文件,为上述三个基础 GGUF 模型分别生成了 imatrix 重要性矩阵文件。然后,我们结合 imatrix 文件,调用 llama-quantize 工具将 float16 模型执行量化。具体命令如下:# 构建 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.sh jina-embeddings-v4-text-retrieval-F16.gguf retrieval-i3 imatrix-retrieval-512.dat jinaai/jina-embeddings-v4-text-retrieval-GGUF这里的 quantize.sh 脚本负责批量处理不同的量化类型:#!/bin/bashF16_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"# 定义量化类型数组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 8done最终,我们将所有量化模型上传至 HuggingFace。量化类型
BPW (每权重比特数)
文件大小 (GB)
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
llama-server 和 llama-embedding 来部署 GGUF 向量模型。Transformer 库允许我们灵活编写输入预处理代码。但在 llama.cpp 中,我们必须手动完成这一步,除非你打算重新编译 llama-server。为了确保 GGUF 模型的输出结果与原始 jina-embeddings-v4 模型完全一致,你 必须非常小心地 为输入内容手动添加前缀。参考以下表格:任务
prompt_name(Transformer 实现)
模型的实际输入
retrievalquery(默认)
Query: {原始文本}retrievalpassagePassage: {原始文本}text-matchingquery(默认)
Query: {原始文本}text-matchingpassageQuery: {原始文本}⚠️
codequery(默认)
Query: {原始文本}codepassagePassage: {原始文本}text-matching 任务中,即使指定 prompt_name='passage',输入前缀依然会被强制改为 "Query: "。这个设计其实很合理。因为 text-matching 是一个句子相似度任务,输入内容没有主次之分,两者是对称的。通过 llama-server 部署安装 llama.cpp 后,运行 llama-server 命令,即可将向量模型部署为一个兼容 OpenAI API 的 HTTP 服务。例如,要启动 text-matching 的 F16 模型,可以执行:llama-server -hf jinaai/jina-embeddings-v4-text-matching-GGUF:F16 --embedding --pooling mean -ub 8192必须添加 --pooling mean 参数,因为 v4 模型采用均值池化生成向量。然后,通过 curl 发送请求: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: 浜辺に沈む美しい夕日" ] }'通过 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注意事项小结在开始之前,需要了解使用 GGUF 模型时要注意的几个要点:必须手动添加前缀:你必须在输入文本前手动添加 Query: 或 Passage:。