メインコンテンツへスキップ

llama-perplexityでバグ発生コミットを特定してみた ~llama.cpp ROCmでLLM劣化はb10040?~

    こんにちはRcatです。
    下記の件で、現在私の環境ではROCmビルドのllama.cppでAIエージェントを実行すると出力が劣化して頭がすごく悪くなります。
    Vulkanに逃げることで暫定対策中ですが、パフォーマンスを考えるとROCmに軍配が上がります。

    そこで、そもそもこの違和感は数値として出せるものなのか?出たとしたら発生源を特定できるのか?
    ちょっと頑張ってみたので記事にします。


    はじめに

    利用規約

    情報や作品の活用時は事前に利用規約をご確認ください。

    コメントについて

    利用規約のガイドラインを確認の上コメントしてください。
    則っていないコメントは削除します。


    概要

    llama-perplexityとは

    llama-perplexityは、モデルを量子化した際にどの程度劣化したのかを数値で見るためのツールです。
    llama.cppをビルドすると入っています。
    元のBF16,量子化のQ8やQ4などで回してその数値を比較します。
    Geminiに聞いたところ、一般的に5~15程度の値になるそうです。

    試験内容としては、入力したテキストをどこまで正確に予測できるかだそうです。


    検証

    wikitext2-rawを手に入れる

    サンプリングデータとして、標準的なwikitext2-rawのテキストを取得します。

    llama.cppのフォルダの中にscriptsというフォルダがあり、その中の`get-wikitext-2.sh`を実行するだけで手に入ります。

    rcat@rcat-EVO-X2:~/tmp$ ./get-wikitext-2.sh
    画像

    ちなみにwgetかcurlが要るので、コンテナとして動かしている状態では無理だったので、スクリプトだけ引っこ抜いてホストマシンで走らせます。

    実行(ROCm)

    とりあえずROCmとVulkanでの差異を見たいので、モデルは軽量のやつで行きます。

    root@rcat-EVO-X2:/app/llama.cpp/build/bin# ./llama-perplexity -m /opt/models/Qwen3.6-35B-A3B-UD-IQ4_NL.gguf -f /opt/models/wikitext-2-raw/wiki.test.raw  -c 32768  -ngl 999
    0.02.814.025 I cmn          init: llama threadpool init, n_threads = 16
    0.02.905.619 I
    0.02.905.743 I system_info: n_threads = 16 (n_threads_batch = 16) / 32 | ROCm : NO_VMM = 1 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX_VNNI = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | AVX                          BF16 = 1 | LLAMAFILE = 1 | OPENMP = 1 | REPACK = 1 |
    0.02.905.765 I perplexity: tokenizing the input ..
    0.03.392.787 I perplexity: tokenization took 487.008 ms
    0.03.392.939 I perplexity: calculating perplexity over 9 chunks, n_ctx=32768, batch_size=2048, n_seq=1
    1.03.316.748 I perplexity: 59.92 seconds per pass - ETA 8.98 minutes
    [1]1890.2290,[2]2906.1400,[3]3002.3720,[4]3046.2892,[5]3111.8808,[6]3060.3074,[7]3062.7044,[8]3182.3452,[9]3236.7434,
    8.58.296.601 I Final estimate: PPL = 3236.7434 +/- 59.07391

    PPLが結果で、5~15が普通でしたっけ?
    さ ん ぜ ん っ て な に

    実行(Vulkan)

    とりあえず前の結果はガン無視して、Vulkanのコンテナに切り替えて再度実行

    root@rcat-EVO-X2:/app/llama.cpp/build/bin# ./llama-perplexity -m /opt/models/Qwen3.6-35B-A3B-UD-IQ4_NL.gguf -f /opt/models/wikitext-2-raw/wiki.test.raw  -c 32768   -ngl 999 --no-mmap
    0.00.043.262 W DEPRECATED: --mmap and --no-mmap are deprecated. use --load-mode mmap instead
    0.02.126.467 I cmn          init: llama threadpool init, n_threads = 16
    0.02.156.990 I
    0.02.157.059 I system_info: n_threads = 16 (n_threads_batch = 16) / 32 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | AVX512 = 1 | AVX512_VBMI = 1 | AVX512_VNNI = 1 | AVX512_BF16 = 1 | LLAMAFILE = 1 | OPENMP = 1 | REPACK = 1 |
    0.02.157.081 I perplexity: tokenizing the input ..
    0.02.619.763 I perplexity: tokenization took 462.669 ms
    0.02.619.891 I perplexity: calculating perplexity over 9 chunks, n_ctx=32768, batch_size=2048, n_seq=1
    1.02.094.974 I perplexity: 59.48 seconds per pass - ETA 8.92 minutes
    [1]6.1041,[2]6.0178,[3]6.3360,[4]6.0747,[5]6.0153,[6]6.3637,[7]6.3311,[8]6.2066,[9]6.1368,
    8.53.140.381 I Final estimate: PPL = 6.1368 +/- 0.03836

    こちらは6なので正常範囲内。おや? おやおや?

    もう一度

    IQ4という特殊な量子化が~とGeminiが言っていたので、とりあえずQ4K_Mの方も持ってきました。

    Vulkan

    また6ですね

    root@rcat-EVO-X2:/app/llama.cpp/build/bin# ./llama-perplexity -m /opt/models/Qwen3.6-35B-A3B-UD-Q4_K_M.gguf  -f /opt/models/wikitext-2-raw/wiki.test.raw  -c 32768   -ngl 999
    0.02.670.119 I cmn          init: llama threadpool init, n_threads = 16
    0.02.752.512 I
    0.02.752.587 I system_info: n_threads = 16 (n_threads_batch = 16) / 32 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | AVX512 = 1 | AVX512_VBMI = 1 | AVX512_VNNI = 1 | AVX512_BF16 = 1 | LLAMAFILE = 1 | OPENMP = 1 | REPACK = 1 |
    0.02.752.608 I perplexity: tokenizing the input ..
    0.03.225.597 I perplexity: tokenization took 472.975 ms
    0.03.225.729 I perplexity: calculating perplexity over 9 chunks, n_ctx=32768, batch_size=2048, n_seq=1
    1.02.977.945 I perplexity: 59.75 seconds per pass - ETA 8.95 minutes
    [1]6.0508,[2]5.9490,[3]6.2369,[4]5.9855,[5]5.9249,[6]6.2735,[7]6.2098,[8]6.0934,[9]6.0270,
    8.53.406.499 I Final estimate: PPL = 6.0270 +/- 0.03758

    ROCm

    さ ん ぜ ん っ て な に

    root@rcat-EVO-X2:/app/llama.cpp/build/bin# ./llama-perplexity -m /opt/models/Qwen3.6-35B-A3B-UD-Q4_K_M.gguf  -f /opt/models/wikitext-2-raw/wiki.test.raw  -c 32768   -ngl 999
    0.03.796.326 I cmn          init: llama threadpool init, n_threads = 16
    0.03.890.932 I
    0.03.891.061 I system_info: n_threads = 16 (n_threads_batch = 16) / 32 | ROCm : NO_VMM = 1 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX_VNNI = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | AVX512 = 1 | AVX512_VBMI = 1 | AVX512_VNNI = 1 | AVX512_BF16 = 1 | LLAMAFILE = 1 | OPENMP = 1 | REPACK = 1 |
    0.03.891.081 I perplexity: tokenizing the input ..
    0.04.371.301 I perplexity: tokenization took 480.206 ms
    0.04.371.436 I perplexity: calculating perplexity over 9 chunks, n_ctx=32768, batch_size=2048, n_seq=1
    1.03.418.629 I perplexity: 59.05 seconds per pass - ETA 8.85 minutes
    [1]1962.8753,[2]3092.0829,[3]3183.6765,[4]3191.3942,[5]3233.6071,[6]3193.9506,[7]3190.0359,[8]3317.2313,[9]3385.6207,
    8.51.056.923 I Final estimate: PPL = 3385.6207 +/- 62.36581

    変わらん。ROCmがおかしい

    この後、MoE以外のQwen3.5 9Bとかもっと古いQwen2.5とか持ってきましたが、変わらずROCmは3000台をたたき出します。

    希望の光が見えた?

    わからないので、ソースを全部AIに食わせて診断してもらっています。
    そのうちにこれやってみてと言われたコマンドがあります。

    コンテキスト長です。これを変えてやってみたところ、512の時だけ標準的な数値が出ました。これ以降大きくなるとまた壊れていきます。

    # for C in 512 2048 8192 16384 32768; do   echo "=== c=$C ===";   ./llama-perplexity -m $M -f $F -c $C -ngl 999 --chunks 1; done
    === c=512 ===
    0.01.330.984 I cmn          init: llama threadpool init, n_threads = 16
    0.01.406.503 I
    0.01.406.654 I system_info: n_threads = 16 (n_threads_batch = 16) / 32 | ROCm : NO_VMM = 1 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX_VNNI = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | AVX512 = 1 | AVX512_VBMI = 1 | AVX512_VNNI = 1 | AVX512_BF16 = 1 | LLAMAFILE = 1 | OPENMP = 1 | REPACK = 1 |
    0.01.406.676 I perplexity: tokenizing the input ..
    0.01.874.206 I perplexity: tokenization took 467.516 ms
    0.01.874.321 I perplexity: calculating perplexity over 1 chunks, n_ctx=512, batch_size=2048, n_seq=4
    0.02.200.636 I perplexity: 0.33 seconds per pass - ETA 0.00 minutes
    [1]6.1261,
    0.02.213.682 I Final estimate: PPL = 6.1261 +/- 1.03751

    もし壊れなかったら今度はバッチを見てみてねとも言われたので、いろいろやったところ、bとubを揃えた時は壊れた数値が出ないことがわかりました。

    # ./llama-perplexity -m $M -f $F -c 32768 -ngl 999 --chunks 1 -b 2048 -ub 2048
    0.01.469.298 I cmn          init: llama threadpool init, n_threads = 16
    0.01.540.822 I
    0.01.540.943 I system_info: n_threads = 16 (n_threads_batch = 16) / 32 | ROCm : NO_VMM = 1 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX_VNNI = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | AVX512 = 1 | AVX512_VBMI = 1 | AVX512_VNNI = 1 | AVX512_BF16 = 1 | LLAMAFILE = 1 | OPENMP = 1 | REPACK = 1 |
    0.01.540.964 I perplexity: tokenizing the input ..
    0.02.012.875 I perplexity: tokenization took 471.898 ms
    0.02.013.010 I perplexity: calculating perplexity over 1 chunks, n_ctx=32768, batch_size=2048, n_seq=1
    0.39.878.318 I perplexity: 37.87 seconds per pass - ETA 0.62 minutes
    [1]10.8289,
    0.40.613.967 I Final estimate: PPL = 10.8289 +/- 0.24102

    llama.cppはデフォルトだとub=512,b=2048で、バッチサイズを変えてる人でもubまではあんまり変えてないかと思います。
    つまりubとずれると。そして出力が壊れる。


    壊れたコミットの特定

    特定開始

    いろいろと試した結果、claudeは尽きたらしくコミットを特定しろと言ってきましたw
    下記をもらったので、これを回し続けてコミットを探します。

    cat > /app/bisect.sh <<'EOF'
    #!/bin/bash
    cd /app/llama.cpp
    cmake -B build-bs -DGGML_HIP=ON -DGPU_TARGETS=gfx1151 -DCMAKE_BUILD_TYPE=Release \
          -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache >/dev/null 2>&1 || exit 125
    cmake --build build-bs --target llama-perplexity -j$(nproc) >/dev/null 2>&1 || exit 125
    PPL=$(./build-bs/bin/llama-perplexity -m /opt/models/llama-3.2-3b-q4_0.gguf \
            -f /opt/models/wikitext-2-raw/wiki.test.raw -c 32768 -ngl 999 --chunks 1 2>&1 \
          | grep -oP 'Final estimate: PPL = \K[0-9.]+')
    echo "PPL=$PPL"
    [ -z "$PPL" ] && exit 125
    awk -v p="$PPL" 'BEGIN { exit (p < 100) ? 0 : 1 }'
    EOF
    chmod +x /app/bisect.sh

    大雑把な期間が判明

    7/15~7/21のどこかで壊れてる

    7/1
    /app/llama.cpp# git checkout b9850
    /app/llama.cpp# /app/bisect.sh
    PPL=7.7197
    
    8/1
    /app/llama.cpp# git checkout b10217
    /app/llama.cpp# /app/bisect.sh
    PPL=3008.9946
    
    7/15
    /app/llama.cpp# git checkout b10007
    /app/llama.cpp# /app/bisect.sh
    PPL=7.7241
    
    7/21
    /app/llama.cpp# git checkout b10075
    /app/llama.cpp# /app/bisect.sh
    PPL=3016.7824

    絞り込み

    というわけでコミットメッセージを食わせて怪しそうなコミットを絞ります。
    Qwen3.6に食わせてっと… 3.8?あれは重いから無理。

    ## b10007〜b10075 範囲のROCm関連コミット一覧
    
    ### 【最優先】ROCm/HIP直接関連
    
    | コミットハッシュ | 説明 |
    |------------------|------|
    | `c7d872292` | **ggml-cuda: restore prop.integrated on HIP builds (#24233)** — AMD APU/UMA PartsのHIPビルドで`prop.integrated`プロパティを復元。このコミットがないと、ROCmで統合GPUホストバッファ使用時に不具合が発生する |
    
    ### 【高優先度】CUDA系(HIPと共通部分が多い)
    
    | コミットハッシュ | 説明 |
    |------------------|------|
    | `79bba02a6` | CUDA: Support CUDA Virtual Devices (#25228) |
    | `5839ba352` | CUDA: dedup MoE gate/up activation quantization (#25441) |
    | `f6f12e43f` | CUDA: tighter MMQ src1 buffer size for native fp4 (#25613) |
    
    ### 【中優先度】演算・コア変更
    
    | コミットハッシュ | 説明 |
    |------------------|------|
    | `7d56da7e5` | sync : ggml (ggml/1568) — ggmlサブモジュールの同期 |
    | `372740406` | ggml: bump version to 0.17.0 (ggml/1568) |
    | `0dc74e332` | DeepseekV4: Add fused hyper-connection ops (#25585) |
    | `f955e394b` | ggml: add f16 out_prod support for CPU and out_prod op for Vulkan (#23997) |
    | `3f08ef2c5` | Enable CUDA graphs on volta+turing (#25749) |
    | `602f828b4` | cuda: extract Q1_0 elements via __byte_perm (#25628) |
    | `3b5321936` | CUDA: GGML_OP_LIGHTNING_INDEXER implementation (#25545) |
    | `a3e5b96ac` | cuda: relax tensor contiguity requirements for quantized concat (#25678) |
    | `56d6e9dde` | quant: allow using manual tensor types with --pure (#25716) |
    
    ### 【低優先度】OpenCL/その他のGPU関連
    
    | コミットハッシュ | 説明 |
    |------------------|------|
    | `b85833e93` | opencl: add ABS op (#25115) |
    | `76f46ad29` | hexagon: add CLAMP op (#25934) |
    
    ---
    
    ## 最も疑わしいコミット
    
    **[`c7d872292`](cc7d872292:ggml/src/ggml-cuda/ggml-cuda.cu)** — HIP/ROCmの`prop.integrated`プロパティに関する修正です。このコミットメッセージによると、それまでCUDA/HIPデバイスを区別せずに`integrated = false`としていたのが原因で、AMD APU/UMA Partsでホストバッファ使用時に不具合が発生していました。ROCmバックエンドで推論が壊れる症状に最も直結する可能性があります。
    
    次に疑わしいのは**[`7d56da7e5`](7d56da7e5)** のggml同期コミットです。サブモジュールの変更はコア演算に影響する可能性があります。

    ビンゴ!
    b10040コミットで壊れてる

    /app/llama.cpp# git checkout b10040
    /app/llama.cpp# /app/bisect.sh
    PPL=3024.3587
    
    /app/llama.cpp# git checkout b10038
    /app/llama.cpp# /app/bisect.sh
    PPL=7.7241
    
    #(b10039はないのでハッシュで)
    /app/llama.cpp# git checkout 5839ba352471b2a7b45e7ba401619a6896f10f8b
    /app/llama.cpp# /app/bisect.sh
    PPL=7.7241

    念のため今の段階で治ってるか見る
    →治ってない

    /app/llama.cpp# git switch master
    /app/llama.cpp# git pull
    /app/llama.cpp# git log
    commit bb4caa7540188872173c44d161602d9271386413 (HEAD -> master, origin/master, origin/HEAD)
    Author: Georgi Gerganov <ggerganov@gmail.com>
    Date:   Fri Aug 21 15:01:24 2026 +0300
    
        llama.cpp : bump version to 0.2.0 (#27498)
    
    ...
    
    /app/llama.cpp# /app/bisect.sh
    PPL=2177.3497

    まとめ

    今回は推論の違和感からバグ発生の特定までを行いました。
    ArduinoレベルしかCはできないのでこれ以上の追及は不可能ですが、ひとまず検証結果(b10040コミットで破損がある)ことは報告しておこうと思います。

    コード提供できないけど、症状とその問題が発生したコミットまで突き止めれば報告しても大丈夫だよね…? PPLっていう動かぬ数値もあるし。

    治ってくれるといいなぁ。


    リンク集

    感想投稿フォーム

    コメントは公開で恥ずかしい!! Rcatだけに送りたい人向け

    メンバーシップの説明

    【ローカルLLM】バイブコーディング完全ガイド

    【ローカルLLM】仕様駆動開発で、下流を丸投げ!

    【ローカルLLM】OpenHandsで丸投げ開発

    【ローカルLLM】自己成長AIエージェントHermesをホスト

    【ローカルLLM】FastFlowLMでRyzan AIのNPUを叩く

    【ローカルLLM】ROCmでLLMが劣化する!?

    ミニPCにLinuxをインストール


     
     
    モノづくりが趣味。 "売ってないなら自分で作ればいいじゃない"をコンセプトに、機械/電気/AI/プログラム/ネットワーク 全部やります。 記事執筆はAI不使用。 ご連絡や利用規約はプロフィール記事参照。

    あなたへのおすすめ