llama.cpp
コミュニティのほとんどがLLMに焦点を当てているため、ベクトル模型プロバイダーの視点からこれを共有することは価値があると考えました。
特に重要なのは、今日のベクトル模型がLLMとほとんど同じであるということです。例えば、jina-embeddings-v4はQwen2.5-VL-3B-instruct
に基づいており、jina-reranker-m0はQwen2-VL-2B
に基づいています。唯一の本当の違いは**出力**です。LLMは**生成**的であり、ベクトル模型と重排器は**識別**的です。これにより、機会と課題の両方が生まれます。一方では、llama.cpp
の効率的な実装(例:ubatch_size
)を活用して、ベクトル模型/重排器を提供できます。他方では、llama.cpp
のベクトル模型実装は、ほとんどが古いエンコーダ専用のアーキテクチャ(RoBERTaベースのモデルなど)向けに開発されており、最新のデコーダ専用のベクトル模型/重排器モデルに完全には対応していません。この記事では、最新のベクトル模型をGGUF形式とllama.cpp
ツール(例:llama-embedding
やllama-serving
)で動作するように適応させる際に学んだことを共有します。
tagベースGGUFと量子化GGUF
jina-embeddings-v4は、3つのLoRAアダプター(retrieval
(検索タスク向けに最適化)、text-matching
(文の類似性タスク向けに最適化)、code
(コード検索タスク向けに最適化))を備えたQwen2.5-VL-3B-instruct
に基づいています。また、視覚的なドキュメント検索と、遅延インタラクションスタイルのマルチベクトル出力を目的としたトレーニングも集中的に行われています。ここでのアイデアは、llama.cpp
の既存のQwen2.5-VL-3B
のグラフ実装を活用し、llama-embedding
を推論に使用することです。
ただし、最初に気づいたのは、llama.cppのmmproj
またはビジョントランスフォーマーの実装におけるバグのある動作でした。これにより、同じ画像入力が与えられた場合、Qwen2.5-VL-3B
のtorch実装とは異なるベクトル模型が得られます。この問題を私たちのフォークで修正している間、当面はGGUFバージョンからビジョンタワーを除外することにしました。この議論の詳細については、こちらをご覧ください。
llama.cpp
からトークンレベルのベクトル模型を取得した後に適用することができます - これはjina-reranker-m0-GGUF
で行ったことです。 もちろん、あまり効率的ではありませんが、llama.cpp
を変更して再コンパイルする必要なく動作します。
ビジョントランスフォーマーとマルチベクトルプロジェクターを取り除き、F16で3つのベースGGUFモデルを取得しました。
llama.cpp
の既存のQwen2.5-VL-3B
のグラフ実装に完全に対応するために、ビジョントランスフォーマーと最後のTransformerブロックのマルチベクトルプロジェクターを取り除き、すべてのLoRAアダプターをベース言語モデルにマージしました。これにより、元のv4の37.5億パラメータから削減され、それぞれ30.9億パラメータの3つのタスク固有のv4モデルが得られました。
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
(こちらにあり、Unslothによって推奨されています)を使用して、3つのベースGGUFモデルを調整し、3つの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 | ファイルサイズ (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 |

tagUsage and Caveats
これで、llama-server
とllama-embedding
を使用して、埋め込み用のGGUFを処理できます。カスタム入力プリプロセッシングコードを記述できる柔軟性があるTransformerライブラリとは異なり、この部分は手動で処理する必要があります(llama-server
とllama-embedding
を再コンパイルしない限り)。具体的には、AutoModel.from_pretrained("jinaai/jina-embeddings-v4")...
を使用した場合と完全に一致する結果を得るには、プレフィックスに非常に注意し、それらを手動でGGUFモデルの入力に追加する必要があります。以下に参考表を示します。
タスク | Transformer実装のprompt_name |
モデルへの実際の入力 |
---|---|---|
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} |
一部のユーザーは、元のAutoModel.from_pretrained("jinaai/jina-embeddings-v4")....
でtext-matching
を使用すると、prompt_name='passage'
が"Query: "
にオーバーライドされることに驚くかもしれません。しかし、text-matching
は左右の役割がない文の類似性タスクであるため、これは実際には理にかなっています—入力は対称的です。
tagllama-server
経由
llama.cpp
をインストールしたら、llama-server
を実行して、埋め込みモデルをOpenAI API互換のHTTPサーバーとしてホストできます。たとえば、F16
でtext-matching
を使用するには、次のようにします。
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: 浜辺に沈む美しい夕日"
]
}'
tagllama-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:
を手動で追加する必要があります。 - 現在、GGUFモデルからビジョントランスフォーマーを削除したため、画像入力を処理できません。
Qwen2.5-vl-3b
のllama.cppのビジョントランスフォーマー/mmproj
実装のバグが原因で削除する必要がありました。このバグについては、アップストリームで修正に取り組んでいます。 llama.cpp
のQwen2.5-vl-3b
グラフ実装の一部ではないため、マルチベクトル埋め込みを出力できません。llama.cpp
を再コンパイルせずに最も簡単な回避策は、llama-embedding
で--pooling none
を設定してトークンレベルの埋め込みを取得した後、MLPをエクスポートして個別に実行することです。- v4はMatryoshka表現学習でトレーニングされており、GGUFに変換するとこの機能が保持されます。形状
NxD
の埋め込みを取得した場合、embeddings[:, :truncate_dim]
を使用するだけで、より小さい切り捨てられた埋め込みを取得できます。ただし、すべての次元がトレーニングされているわけではありません。v4では、これらの特定の値に対してtruncate_dim
をトレーニングしました:[128, 256, 512, 1024, 2048]
。これは、embeddings[:, :131]
の品質がembeddings[:, :128]
とembeddings[:, :256]
の品質の間のある種の補間になるのではなく、131次元はトレーニングされていないため、128次元または256次元の埋め込みよりも大幅に悪化することを意味します。 - v4は因果モデルであるため、
--pooling none
を介してトークンレベルの埋め込みを取得した後、後期チャンクは後処理の一部として引き続き機能します。llama.cpp
グラフからMLPを分離したのと同じように、これは非常に効率的ではありませんが、再コンパイルは必要ありません。ただし、もう1つの注意点があります。後期チャンクは双方向ではなくなります-以前のチャンク埋め込みには、後続のチャンクからの文脈情報が含まれなくなります。v3では、トランスフォーマーブロックで双方向アテンションマスクを使用したため、すべてのチャンク埋め込みにグローバルコンテキスト情報が含まれていたことを忘れないでください。内部的には、因果関係が後期チャンクを時代遅れにするかどうかについて議論しました。「コンテキストも因果的である」と主張する人もいます。つまり、読者はテキストを左から右に処理するため、文を解釈するために必要なコンテキストは先行するテキストから来るはずです。また、後期チャンクを単方向に制限すると、チャンク間のコンテキスト共有がブロックされると言う人もいます。いずれにせよ、v4での後期チャンクの有効性は依然として疑問視されており、さらなる研究が必要です。
tagllama-embedding
による効率的な埋め込み
llama-embedding
は、非常にクリーンなI/O(stdin、stdout)でテキストを埋め込むためのllama.cpp
上の比較的単純なC++ラッパーです。ネットワークキューイング、負荷分散、マルチテナンシー、シリアル化など、現時点で範囲外であると私たちが信じている他の多くの問題があるため、現時点ではllama-server
ではなく、この改善に焦点を当てています。私たちの質問は簡単です。L4 24GB GPUからどれだけの速度が得られるか、そして長いドキュメントを埋め込むためのピークVRAM使用量はどれくらいか?
しかし、なぜL4なのか?主に、GCPはそれをベースにした非常に便利なCloud Run関数を提供しており、サーバーレス推論APIで利用できる最も広く利用可能で経済的なGPUタイプであるためです。GCPはリクエストに応じてCloud RunでA100およびH100を提供しており、より優れたGPUを使用するようにGCPチームから時々提案されます。しかし、私たちの哲学は簡単です。3Bモデルを処理するためにA100/H100が必要な場合、それは明らかに私たちの側のスキルが問題です。
背景として、llama.cppでは、論理バッチサイズ (-b
) は、1回の評価呼び出しでモデルに送信される最大トークン数を表します。長い入力を処理する場合、これらはこのサイズまでのチャンクに分割されます。物理バッチサイズ (-ub
) は、利用可能なメモリによって制限される、ハードウェアを介した1回の順方向パスで同時に処理される実際のトークン数です。KVキャッシュは、各物理バッチが完了した後に更新されます。コンテキストウィンドウ (-c
) は、モデルが一度に「見ることができる」トークン数のハードリミットです。v4モデルの場合、これは32,000トークンであり、モデルの最大アテンションスパンを表します。すべてのトークンは、シーケンス全体で一貫したアテンションを維持するために、このコンテキストウィンドウ内に収まる必要があります。次の図は、それらの関係を示しています。

tag修正点
上記のフォークでは、llama-embedding
をより効率的にするために、いくつかの最適化を行いました。
- バッチ処理の簡略化:
-b
を-c
と等しくなるように自動的に設定し、このパラメータを事実上廃止しました。論理バッチ処理のためにモデルのフルコンテキスト長を常に活用するため、ユーザーは-b
を指定する必要がなくなりました。 - 柔軟なメモリ制御: 元の実装では
-ub
が-b
と等しくなるように強制されていましたが(埋め込みモデルは因果的ではないと想定していたため)、ユーザーが-ub
を個別に設定できるようにしました。これにより、長いコンテキストをエンコードする際のピークVRAM使用量を細かく制御できます。KVキャッシュの実装のおかげで、小さな512トークンの物理バッチで32Kコンテキストを処理して、VRAM制限内に収めることができます。この変更は、jina-embeddings-v4 のような因果的埋め込みモデルでのみ正しいことに注意してください。v3のようなエンコーダーのみのアーキテクチャの場合、これは間違った実装になります。 - 平均プーリングの修正:
ub < b
の場合の埋め込みの平均プーリング計算を修正しました。これは、元の実装では以前に壊れていました。
この変更により、メモリ制約を効果的に管理しながら、長コンテキストのデコーダーのみの埋め込みモデルを非常に簡単に操作できるようになります。ユーザーは、次の2つのパラメータのみを設定する必要があります。
-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
の各行は、埋め込む文です。文中の \n
が区切り文字として解釈されないように、--no-escape
を設定する必要があります。--flash-attn
と -ngl 99
は、L4 GPUで最高のパフォーマンスを得るために設定する必要があります。
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に固定し、すべての量子化を重みあたりのビット数と速度(1秒あたりのトークン数で測定)およびVRAM消費量によってプロットします。GGUFバージョンは、FP16のバニラバージョンよりもわずかに高速であることがわかりました(2023対1865トークン/秒)。ほとんどの量子化は、2000〜2100トークン/秒付近に集中しています。フラッシュアテンションを有効にすると、すべての量子化で約77%の高速化が得られます(3000+対2000+トークン/秒)。ただし、最高のパフォーマンスを発揮する量子化Q8_0
(約3700トークン/秒)は、依然としてバニラv3(572Mパラメータ)には遠く及びません。これは16000トークン/秒に達します。量子化されたバージョンはVRAMを大幅に節約し、IQ3を使用すると、v3 FP16モデルのレベルにほぼ近づきます。

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
が最適な構成であり、2,025MBのVRAMを使用しながら4,143 tokens/秒を提供することを示しています。重要なのは、入力内の単一ドキュメントの最大長がわかっている場合は、それをカバーするのに十分な小さいコンテキストサイズを使用することです。物理バッチサイズについては、512 tokensがL4 GPUでのスイートスポットのようです。

Tokens per Secondのパフォーマンス
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使用量 (MB)
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結論
予算の限られたGPUで量子化されたGGUFを効率的に実行したいv4ユーザーは、llama-embedding
のカスタムビルドでIQ3_S
またはIQ3_M
を選択してください。これにより、通常のデータセット(文の長さが<2048 tokensの場合)で4000 tokens/秒が得られるはずです。より長いドキュメントを埋め込むには、コンテキストサイズ-c
を増やし、物理バッチサイズ-ub
を制御してVRAMフットプリントを削減します。カスタムビルドを使用すると、-ub
を1024のような小さい数値に設定することで、わずか3GBのVRAMを使用して非常に長いドキュメント(>32K tokens)をエンコードできます。これは、元の実装またはバニラトランスフォーマーでは不可能でした。
速度最適化の探求に終わりはありません。常に、より高速で、より無駄のない実装と、より高いスループットの余地があります。4000 tokens/秒はおそらく私たちの天井ではありません。やるべきことはたくさんあります。llama.cpp
でのqwen2.5-vl-3b
mmproj/vision transformerの実装の修正に加えて、llama.graphとKVキャッシュレベルのより深い最適化、llama-serving
のバッチ処理ロジックの改善、埋め込みAPIへのストリーミングオプションの追加も検討しています。私たちの目標は、現在および将来の重排器リリースに向けて、llama.cpp
が最新のデコーダー専用マルチモーダル埋め込みをネイティブにサポートするようにすることです。