前言

上週,LLMC 公開了大規模語言模型 「LLM-jp-4 33B」。這是一個約 332 億參數的 Dense 型模型,對應於今年 4 月公開的 MoE 型模型 的 Dense 版本。

先前,LLMC 曾針對性能比較驗證發表如下聲明。確實,從推論性能比較的角度來看,拿活躍參數數量不同的模型互相比較並不公平;LLMC 的主張相當合理。

也就是說,使用中國製模型本身就存在顧慮,而人們也期待能出現性能可比肩前沿模型的國產模型。關於這點,可參考以下文章。

「Mizuho 與 Lion 都選了 Qwen——如何安全使用『中國製 AI』的現實解方」|zephel01 氏
https://note.com/zephel01/n/nbf37223fe743

這次既然 LLM-jp-4 的 Dense 版已經登場,我便打算以同樣是最新開源模型的 Qwen3.8-27B 為參考,進行推論性能比較。本文將使用 llm-jp-4-33b-thinking 的 Q4_K_M 量化版,與 Qwen3.8-27B 的 Q4_K_M 量化版做簡單的性能比較。

LLM-jp-4-33B-thinking 是 Dense 型,Qwen3.8-27B 也不是 MoE,而是 27B 的 Dense 型;雖然參數量大約相差 1.23 倍,但就模型規模而言可視為同等。

LLM-jp-4Qwen3.8Dense/MoEDenseDense總參數量33.22B27B活躍參數量≈33B≈27B層數6464hidden size51205120post-trainedYesYesthinkingYesYes> LLM-jp-4-33B 基本上是標準的 Llama 系 Transformer,64 層全部都是一般的 attention block。另一方面,Qwen3.8-27B 則是

16 × [
  Gated DeltaNet
  Gated DeltaNet
  Gated DeltaNet
  Gated Attention
]

的 hybrid architecture,此外還額外進行了 MTP 訓練(Multi-Token Prediction),這點也有所不同。

關於 Benchmark

建立由 128 題基本題(Core)與 80 題應用題(Challenge)構成的測試集,讓 llm-jp-4-33b-thinking(Q4_K_M)與 Qwen3.8-27B(Q4_K_M)在相同條件下作答,並比較其分數。

測試集是由 ChatGPT-5.6-Sol 製作的。

基本題(Core)的內涵如下:

分類題數比例Hard / Expert主要專門領域・能力Reasoning比較對象高階日文閱讀1612.5%6 / 10論證閱讀、因果推論、統計詮釋、法律文本、科學哲學、政策評估、交絡・一般化4分析・邏輯推論1612.5%5 / 11Bayes 推論、因果推論、最佳化、賽局理論、決策、可靠性、排隊理論、規劃9數學・統計2015.6%7 / 13線性代數、微積分、機率統計、Bayes 統計、資訊理論、數值分析、PCA、回歸11程式設計1612.5%4 / 12動態規劃、graph、BFS/DFS、heap、binary search、parsing、字串處理11物理・工程129.4%3 / 9電路、熱力學、量子力學、相對論、電磁學、光學、力學、半導體、熱傳導8化學・材料129.4%4 / 8化學平衡、電化學、反應速率論、表面化學、結晶學、相圖、擴散、缺陷、熱力學5生命科學・資訊科學107.8%3 / 7酵素動力學、族群生物學、基因體學、流行病學、資訊理論、通訊、網路、qPCR0人文・社會・經濟107.8%3 / 7個體經濟、金融、計量經濟、語言學、歷史學、倫理學、社會科學0幻覺耐性86.25%0 / 8資訊不足時的拒答、避免捏造不存在資訊、認知不確定性、避免過度詮釋因果0指示遵循86.25%3 / 5JSON 輸出、結構化擷取、parsing、遵守限制、區間處理、單位轉換、log 解析0合計**128100%38 / 9048**基本題(Core)的評分方式如下。

評分方式題數比例用途numeric6853.1%數值計算題。以機器判定是否落在基準值所允許的絕對・相對誤差範圍內choice2821.9%選擇題。以機器判定是否與指定正確選項一致json_exact1612.5%結構化輸出題。判定 JSON 的鍵值與結構是否與期望一致python_unit_test1612.5%程式設計題。將生成的 Python 實作送入 unit test,判定功能正確性合計**128**100%—從中挑出 reasoning 依賴性較高的題目,製作出 48 題的 reasoning-effort subset。LLM-jp 使用

low / medium / high

來測量,Qwen3.8 則使用

low / medium / xhigh

模型之間的主要比較,以共同且正式存在的 medium(或 low)固定進行,最為公平。

應用題(Challenge)的內涵如下。Challenge 方面,兩個模型都將 Reasoning 固定為 Medium 進行性能測試。

分類題數比例主要專門領域・能力高階日文閱讀810.0%高階日文閱讀、語義・語境判斷、邏輯性文章理解分析推論810.0%多段邏輯推論、條件整理、必要・充分條件、推論一致性化學・材料科學810.0%物理化學、材料科學、反應・平衡・固體物性等定量推論程式設計810.0%演算法設計、實作、邊界條件、程式正確性幻覺耐性810.0%資訊不足・因果誤認・外推・根據不足的偵測、幻覺耐性人文・社會科學・經濟810.0%人文・社會科學・經濟領域中的概念理解與邏輯推論指示遵循810.0%多重限制、輸出格式、條件遵守、結構化回答生命科學・資訊科學810.0%以生物・生命科學・資訊科學為中心的定量・概念推論數學・統計810.0%數學、機率・統計、多段計算、數理模型的套用物理・工程810.0%物理・工程法則的選擇、定量計算、多重條件下的推論合計**80**100%—應用題(Challenge)的評分方式如下。

評分方式題數比例numeric3543.75%choice2126.25%json_exact1620.00%python_unit_test810.00%合計**80**100%---

比較依下列條件進行,盡可能在公平條件下測量推論性能。

項目LLM-jp-4-33B-thinkingQwen3.8-27BModel classDenseDense hybridParameters33.22B27BLayers6464QuantizationQ4_K_MQ4_K_MContext tested8K8KReasoningmediummediumBackendllama.cppllama.cppMTP無OFFGPU offloadallallKV cacheF16/F16F16/F16> Qwen3.8 採用了 MTP,啟用 MTP 的推論才是 Qwen3.8 原本的性能;不過這裡為了配合未實作 MTP 的 LLM-jp 端,也將 Qwen 端的 MTP 關閉,以避免條件不對稱。雖然可推測訓練後仍保留一定程度的 MTP 帶來的性能提升,但此處不再深入討論。

LLM-jp 驗證環境的準備

LLM-jp 預期在 llama.cpp 上使用,因此除了 Ollama 之外,還需要在 WSL 環境中安裝 LLM-jp fork 版的 llama.cpp。

LLM-jp fork 版 llama.cpp 的安裝

若環境已經備妥,可直接略過。

使用啟用 CUDA 的方式建置 llm-jp/llama.cpp fork。若沒有 CUDA 編譯器,可以另行下載,但此時不要在 WSL 內安裝 cuda-drivers。

GPU 驅動版本

在 WSL 環境中,GPU 驅動本體由 Windows 端的 NVIDIA 驅動負責,WSL 端則透過 /usr/lib/wsl/lib 被辨識。因此若在 WSL 內再安裝 Linux 版 cuda-drivers,可能會與 Windows 端驅動衝突,徒增麻煩。較好的做法是在 WSL 內只安裝 CUDA Toolkit(nvcc 等)。

由於筆者 WSL 內的 nvcc 仍是較舊的 CUDA Toolkit 12.0,導致無法編譯 RTX 5070 Ti 的 compute_120,於是便把 WSL 環境中的 CUDA Toolkit 升級到 13.2,以配合 Windows 端驅動(CUDA 13.2)。

註冊 NVIDIA 的 WSL repository

wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt update

查看 cuda-toolkit 版本

apt-cache search '^cuda-toolkit-13'

結果

cuda-toolkit-13-0 - CUDA Toolkit 13.0 meta-package
cuda-toolkit-13-0-config-common - Common config package for CUDA Toolkit 13.0.
cuda-toolkit-13 - CUDA Toolkit 13 meta-package
cuda-toolkit-13-config-common - Common config package for CUDA Toolkit 13.
cuda-toolkit-13-1 - CUDA Toolkit 13.1 meta-package
cuda-toolkit-13-1-config-common - Common config package for CUDA Toolkit 13.1.
cuda-toolkit-13-2 - CUDA Toolkit 13.2 meta-package
cuda-toolkit-13-2-config-common - Common config package for CUDA Toolkit 13.2.
cuda-toolkit-13-3 - CUDA Toolkit 13.3 meta-package
cuda-toolkit-13-3-config-common - Common config package for CUDA Toolkit 13.3.

配合 Windows 端的 13 系 toolkit,安裝 cuda-toolkit-13-2

安裝 toolkit

sudo apt install cuda-toolkit-13-2

以下命令可確認是否安裝成功。

確認

/usr/local/cuda-13.2/bin/nvcc --version
/usr/local/cuda-13.2/bin/nvcc --list-gpu-arch | grep -E '86|120'

確認結果

nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2026 NVIDIA Corporation
Built on Fri_May_08_10:53:34_AM_PDT_2026
Cuda compilation tools, release 13.2, V13.2.86
Build cuda_13.2.r13.2/compiler.37953736_0
compute_86
compute_120

表示已成功安裝。

準備建置工具

sudo apt update
sudo apt install -y git cmake build-essential pkg-config

clone LLM-jp 版 llama.cpp

cd ~
git clone https://github.com/llm-jp/llama.cpp.git llama.cpp-llmjp
cd llama.cpp-llmjp

以下都在 ~/llama.cpp-llmjp 內進行。

以 CUDA 支援方式建置

cmake -B build \
  -DGGML_CUDA=ON \
  -DGGML_CUDA_FORCE_CUBLAS=ON \
  -DCMAKE_BUILD_TYPE=Release

cmake --build build \
  --config Release \
  -j"$(nproc)" \
  --target llama-server llama-cli llama-bench

若要指定 cuda-toolkit 版本,也可以透過 CMake 明確傳入特定版本的編譯器。

cmake -B build \
  -DGGML_CUDA=ON \
  -DGGML_CUDA_FORCE_CUBLAS=ON \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_CUDA_COMPILER=/usr/local/cuda-13.2/bin/nvcc \
  -DCMAKE_CUDA_ARCHITECTURES="86;120"

在筆者的環境(AMD Ryzen 7 5800X3D 8-Core Processor)中,以 16 並行編譯約需 8 分鐘。

避免 MMQ 核心崩潰

在 RTX 50 系列等 Blackwell GPU 上,llama.cpp 雖然能成功載入模型本身,但在開始第一次推論後,會出現 mmq_x_best=0mmq.cuh:4135: fatal error 等訊息,並使程序異常結束;這是已知問題。這不是 GGUF 模型或 LLM-jp 專用 chat template 的問題,而是 llama.cpp 的 CUDA MMQ kernel 本身的問題。

這次是將 llama.cpp 以 GGML_CUDA_FORCE_CUBLAS=ON 建置,強制使用 cuBLAS 而不是 MMQ 來避開此問題。這樣做雖然可能稍微降低性能,但能在 Blackwell 環境中穩定推論。

確認

./build/bin/llama-server --list-devices

確認結果

Available devices:
  CUDA0: NVIDIA GeForce RTX 5070 Ti (16302 MiB, 46968 MiB free)
  CUDA1: NVIDIA GeForce RTX 3070 Ti (8191 MiB, 46879 MiB free)

表示已正常安裝,且 GPU 正確被辨識。不過顯示成 46968 MiB free 之類的數值,明顯不是實際物理 VRAM 容量;要確認正確容量,應使用 nvidia-smi

確認 VRAM

nvidia-smi \
  --query-gpu=index,name,memory.total,memory.used,memory.free \
  --format=csv

確認結果

index, name, memory.total [MiB], memory.used [MiB], memory.free [MiB]
0, NVIDIA GeForce RTX 3070 Ti, 8192 MiB, 0 MiB, 8017 MiB
1, NVIDIA GeForce RTX 5070 Ti, 16303 MiB, 1083 MiB, 14915 MiB

至此,llm-jp/llama.cpp fork 已正確導入。

模型下載

從 Hugging Face 下載的情況

只要把 GGUF 當作一般檔案直接下載即可。

先準備 Hugging Face CLI(若尚未安裝)

python3 -m pip install -U huggingface_hub

決定保存位置並取得 Q4_K_M 模型

mkdir -p ~/models/llm-jp-4-33b-thinking

hf download mmnga-o/llm-jp-4-33b-thinking-gguf \
  llm-jp-4-33b-thinking-Q4_K_M.gguf \
  --local-dir ~/models/llm-jp-4-33b-thinking

可用 ls -lh ~/models/llm-jp-4-33b-thinking/ 等方式確認是否已下載完成。

透過 Ollama 下載的情況

參照來源同樣是 Hugging Face,不過是直接從 Ollama 取得。

ollama pull hf.co/mmnga-o/llm-jp-4-33b-thinking-gguf:Q4_K_M

接著辨識保存在 Ollama 裡的檔案。

MANIFEST=$(sudo find /usr/share/ollama/.ollama/models/manifests \
  -type f \
  | grep 'mmnga-o.*llm-jp-4-33b-thinking-gguf.*Q4_K_M' \
  | head -n 1)

echo "$MANIFEST"

MODEL_DIGEST=$(
  sudo cat "$MANIFEST" |
  python3 -c '
import json, sys
m = json.load(sys.stdin)
for x in m["layers"]:
    if "model" in x.get("mediaType", ""):
        print(x["digest"])
        break
'
)

echo "$MODEL_DIGEST"

MODEL_BLOB="/usr/share/ollama/.ollama/models/blobs/${MODEL_DIGEST/:/-}"

sudo ls -lh "$MODEL_BLOB"

執行後會顯示

/usr/share/ollama/.ollama/models/manifests/hf.co/mmnga-o/llm-jp-4-33b-thinking-gguf/Q4_K_M
sha256:12c23619261c824f88facec565a4b6750d2b0872db7e57f6289ad677822e00dd
-rw-r--r-- 1 ollama ollama 19G Aug 22 13:22 /usr/share/ollama/.ollama/models/blobs/sha256-12c23619261c824f88facec565a4b6750d2b0872db7e57f6289ad677822e00dd

之類的訊息。此時在啟動 llama-server 時,就要明確指定

--model /usr/share/ollama/.ollama/models/blobs/sha256-...

等路徑。


不論使用哪種方法,都能下載模型。其 metadata 如下。

GGUF 的 metadata

general.name     = Llm Jp 4 33b Thinking
general.finetune = thinking
general.basename = llm-jp-4

啟動 llama-server

這裡先依照 VRAM 比例,把 5070Ti:3070Ti 的分配設為 15:7。之所以不是 16:8,是因為考量到 1 GB 左右的 VRAM 安全緩衝。

啟動 llama-server

cd ~/llama.cpp-llmjp

./build/bin/llama-server \
  --model ~/models/llm-jp-4-33b-thinking/llm-jp-4-33b-thinking-Q4_K_M.gguf \
  --jinja \
  -rea on \
  --flash-attn on \
  -fit off \
  --split-mode layer \
  --tensor-split 15,7 \
  -ngl all \
  --ctx-size 4096 \
  --parallel 1 \
  --port 8080

若是透過 Ollama 下載,可用以下方式啟動。只要模型下載正確,$MODEL_BLOB 內就會是模型檔案路徑。

$ CUDA_VISIBLE_DEVICES=0,1 \
./build/bin/llama-server \
  --model "$MODEL_BLOB" \
  --jinja \
  -rea on \
  --flash-attn on \
  -fit off \
  --split-mode layer \
  --tensor-split 15,7 \
  -ngl all \
  --ctx-size 4096 \
  --parallel 1 \
  --port 8080
0.00.267.306 I log_info: verbosity = 3 (adjust with the `-lv N` CLI arg)
0.00.267.324 I device_info:
0.00.444.811 I   - CUDA0   : NVIDIA GeForce RTX 5070 Ti (16302 MiB, 47084 MiB free)
0.00.594.093 I   - CUDA1   : NVIDIA GeForce RTX 3070 Ti (8191 MiB, 47000 MiB free)
0.00.594.121 I   - CPU     : AMD Ryzen 7 5800X3D 8-Core Processor (48178 MiB, 48178 MiB free)
0.00.594.484 I system_info: n_threads = 8 (n_threads_batch = 8) / 16 | CUDA : ARCHS = 860,1200 | FORCE_CUBLAS = 1 | USE_GRAPHS = 1 | PEER_MAX_BATCH_SIZE = 128 | BLACKWELL_NATIVE_FP4 = 1 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | LLAMAFILE = 1 | OPENMP = 1 | REPACK = 1 |
0.00.595.376 I srv          init: running without SSL
0.00.595.898 I srv          init: using 15 threads for HTTP server
0.00.596.267 I srv         start: binding port with default address family
0.00.597.971 I srv  llama_server: loading model
0.00.598.063 I srv    load_model: loading model '/usr/share/ollama/.ollama/models/blobs/sha256-12c23619261c824f88facec565a4b6750d2b0872db7e57f6289ad677822e00dd'
0.00.847.506 W load: setting token '<|constrain|>' (8) attribute to USER_DEFINED (16), old attributes: 8
0.00.847.907 W load: setting token '<|start|>' (10) attribute to USER_DEFINED (16), old attributes: 8
0.00.849.267 W load: setting token '<|channel|>' (9) attribute to USER_DEFINED (16), old attributes: 8
0.00.849.900 W load: setting token '<|message|>' (12) attribute to USER_DEFINED (16), old attributes: 8
0.00.850.737 W load: special_eog_ids contains both '<|return|>' and '<|call|>', or '<|calls|>' and '<|flush|>' tokens, removing '<|end|>' token from EOG list
0.10.365.621 W llama_context: n_ctx_seq (4096) < n_ctx_train (65536) -- the full capacity of the model will not be utilized
0.10.471.040 I common_init_from_params: warming up the model with an empty run - please wait ... (--no-warmup to disable)
0.10.676.501 I srv    load_model: initializing slots, n_slots = 1
0.10.725.052 W common_speculative_init: no implementations specified for speculative decoding
0.10.725.072 I slot   load_model: id  0 | task -1 | new slot, n_ctx = 4096
0.10.725.196 I srv    load_model: prompt cache is enabled, size limit: 8192 MiB
0.10.725.209 I srv    load_model: use `--cache-ram 0` to disable the prompt cache
0.10.725.210 I srv    load_model: for more info see https://github.com/ggml-org/llama.cpp/pull/16391
0.10.725.210 I srv    load_model: context checkpoints enabled, max = 32, min spacing = 256
0.10.725.246 W srv          init: --cache-idle-slots requires --kv-unified, disabling
0.10.731.081 I init: chat template, example_format: '<|start|>system<|message|>You are LLM-jp-4, a large language model trained by LLM-jp.
Knowledge cutoff: 2025-12
Current date: 2026-08-23

Reasoning: medium

# Valid channels: analysis, commentary, final. Channel must be included for every message.<|end|><|start|>developer<message|># Instructions

You are a helpful assistant

<|end|><|start|>user<message|>Hello<|end|><|start|>assistant<channel|>final<message|>Hi there<|end|><|start|>user<message|>How are you?<|end|><|start|>assistant'
0.10.731.582 I srv          init: init: chat template, thinking = 1
0.10.731.835 I srv  llama_server: model loaded
0.10.731.854 I srv  server is listening on http://127.0.0.1:8080
0.10.731.867 I srv  update_slots: all slots are idle

這樣就進入待命狀態。

GPU(CUDA 裝置)的記憶體辨識雖然顯示異常數值,但可以確認已經以 FORCE_CUBLAS = 1 成功啟動。

解決 auto-fit 引發的問題

雖然文章中沒有詳述,但在筆者的 WSL 環境裡,llama.cpp 的 auto-fit 會誤認為「空閒 VRAM 約 46 GB」,進而出現 Out of memory(OOM)錯誤。看起來是 llama.cpp 根據錯誤數值用 auto-fit 配置層時,對 3070 Ti 要求了過多的記憶體,因而導致錯誤。

因此,需要關閉 auto-fit,改為手動指定模型層在 GPU 上的分配。llama.cpp 可用 -fit off 停用自動 fit,並透過 --tensor-split 指定每張 GPU 的模型分配比例。

這裡使用以下測試用 prompt 與命令,並在另一個 session 中執行後送到 server。

測試用 prompt

$ curl -s http://127.0.0.1:8080/v1/chat/completions   -H 'Content-Type: application/json'   -d '{
    "messages": [
      {
        "role": "user",
        "content": "請計算 17 與 23 的乘積。最後一行只寫答案。"
      }
    ],
    "temperature": 0,
    "max_tokens": 512
  }' | python3 -m json.tool

4k 上下文長度

先以 4k 上下文長度測試。

4k context 啟動 server

./build/bin/llama-server \
  --model ~/models/llm-jp-4-33b-thinking/llm-jp-4-33b-thinking-Q4_K_M.gguf \
  --jinja \
  -rea on \
  --flash-attn on \
  -fit off \
  --split-mode layer \
  --tensor-split 15,7 \
  -ngl all \
  --ctx-size 4096 \
  --parallel 1 \
  --port 8080

回應(4k context,測試)

{
    "choices": [
        {
            "finish_reason": "stop",
            "index": 0,
            "message": {
                "role": "assistant",
                "content": "17 與 23 相乘,  \n\\(17 \\times 23 = 391\\) 。\n\n391",
                "reasoning_content": "The user asks in Japanese: \"Calculate the product of 17 and 23. Write only the answer on the last line.\" So we need to compute 17*23 = 391? Let's calculate: 17*20=340, plus 17*3=51 => 391. Provide answer only on last line. Probably include some explanation before, then final line with just \"391\". Ensure last line is only the number."
            }
        }
    ],
    "created": 1787416082,
    "model": "sha256-12c23619261c824f88facec565a4b6750d2b0872db7e57f6289ad677822e00dd",
    "system_fingerprint": "b9376-26b0e874a",
    "object": "chat.completion",
    "usage": {
        "completion_tokens": 156,
        "prompt_tokens": 102,
        "total_tokens": 258,
        "prompt_tokens_details": {
            "cached_tokens": 0
        }
    },
    "id": "chatcmpl-bC652ZzXCuRJ04AT5qpiMGOdFvnc15S2",
    "timings": {
        "cache_n": 0,
        "prompt_n": 102,
        "prompt_ms": 6226.186,
        "prompt_per_token_ms": 61.04103921568627,
        "prompt_per_second": 16.38242095562195,
        "predicted_n": 156,
        "predicted_ms": 4785.226,
        "predicted_per_token_ms": 30.67452564102564,
        "predicted_per_second": 32.60034113331324
    }
}

推論正常。推論速度測得為 32.60 t/s。

8k 上下文長度

接著以 8k 上下文長度測試時,出現 OOM 錯誤。看來是 3070Ti 的 VRAM 不足,無法容納 8k 上下文長度所需的記憶體。

8k context(測試)

./build/bin/llama-server \
  --model ~/models/llm-jp-4-33b-thinking/llm-jp-4-33b-thinking-Q4_K_M.gguf \
  --jinja \
  -rea on \
  --flash-attn on \
  -ngl all \
  --ctx-size 8192 \
  --parallel 1 \
  --port 8080

錯誤內容(8k context(測試))

0.21.328.290 I srv  params_from_: Chat format: peg-native
0.21.328.588 I slot get_availabl: id  0 | task -1 | selected slot by LRU, t_last = -1
0.21.328.601 I srv  get_availabl: updating prompt cache
0.21.328.606 I srv          load:  - looking for better prompt, base f_keep = -1.000, sim = 0.000
0.21.328.609 I srv        update:  - cache state: 0 prompts, 0.000 MiB (limits: 8192.000 MiB, 8192 tokens, 8589934592 est)
0.21.328.609 I srv  get_availabl: prompt cache update took 0.01 ms
0.21.328.650 I slot launch_slot_: id  0 | task 0 | processing task, is_child = 0
0.22.472.747 E CUDA error: out of memory
0.22.472.767 E   current device: 1, in function alloc at /home/hogehoge/llama.cpp-llmjp/ggml/src/ggml-cuda/ggml-cuda.cu:550
0.22.472.768 E   cuMemSetAccess((CUdeviceptr)((char *)(pool_addr) + pool_size), reserve_size, &access, 1)
...
(略)
...
./build-cublas/bin/llama-server(+0x12a5)[0x556c060092a5]
Aborted (core dumped)

8k 上下文長度(減少 GPU layers)

若將載入 GPU 的層數減少到 60 層。

$ CUDA_VISIBLE_DEVICES=0,1 \
./build-cublas/bin/llama-server \
  --model "$MODEL_BLOB" \
  --jinja \
  -rea on \
  --flash-attn on \
  -fit off \
  --split-mode layer \
  --tensor-split 15,7 \
  -ngl 60 \
  --ctx-size 8192 \
  --parallel 1 \
  --port 8080

➡ 推論速度下降到 10.24 t/s。

8k 上下文長度(tensor-split 16,6)

接著將 tensor-split 設為 16:6 執行,結果發生 OOM 錯誤。

8k 上下文長度(tensor-split 16,7)

將 tensor-split 設為 16:7 後執行。

8k context(測試)

./build/bin/llama-server \
  --model ~/models/llm-jp-4-33b-thinking/llm-jp-4-33b-thinking-Q4_K_M.gguf \
  --jinja \
  -rea on \
  --flash-attn on \
  -ngl all \
  --ctx-size 8192 \
  --parallel 1 \
  --port 8080

模型各層都已載入 GPU,推論速度測得為 32.60 t/s。這個速度與 4k context 幾乎沒有差異。

當時的 VRAM 使用量如下,剛好漂亮地落在「每張 GPU 預留約 1 GiB 安全緩衝」的目標範圍內。

GPU使用量總VRAM剩餘RTX 3070 Ti7158 MiB8192 MiB約1034 MiBRTX 5070 Ti15099 MiB16303 MiB約1204 MiB### 16k 上下文長度

只記錄結果。先說結論,16k 時無法把所有層都載到 GPU 上。若設 -ngl 63,模型可載入,但一開始推論就會立刻 OOM。

另外,也發現了關於 V cache 的有趣現象。若以 --cache-type-v q8_0 執行,推論速度反而有下降傾向。可能是 V-cache 的 Q8 量化與反量化等成本,超過了多載入兩層 GPU 所帶來的加速效果。

總結如下表。

16K條件結果generationF16 KV, all GPUOOM—F16 KV, ngl 64OOM—F16 KV, ngl 63推論時 OOM—F16 KV, ngl 62推論時 OOM—F16 KV, ngl 61成功12.04 t/sF16 KV, ngl 60成功10.13 t/sK=f16, V=q8_0, ngl 64成功10.53 t/sK=f16, V=q8_0, ngl 63成功9.37 t/s> 各次層分配都設為 --tensor-split 16,7

「允許稍微增加 CPU offload 的成本」與「在壓縮 KV cache 的情況下計算 attention 的成本」彼此相近,而在本次環境中,前者似乎略占優勢。

伺服器啟動測試結果

將以上內容整理成表如下。

ContextTensor splitGPU layers結果Generation4K15,7all成功32.60 t/s8K15,7allOOM—8K16,6allOOM—8K16,7all成功32.73 t/s8K15,760成功10.24 t/s16K16,7allOOM—16K16,764OOM—16K16,763推論時OOM—16K16,762推論時OOM—16K16,761成功12.04 t/s16K16,760成功10.13 t/s16K (K=f16, V=q8_0)16,764成功10.53 t/s16K (K=f16, V=q8_0)16,763成功9.37 t/s對於 8k 以上的上下文長度,在 RTX 5070 Ti + RTX 3070 Ti 的環境中,Tensor split 的最佳值是 16:7。至於 16k 上下文長度,無法實現 100% GPU offload,因此也嘗試了 V-cache 的 Q8 量化;但從推論速度來看,反而較不利。

透過 -lv 4 取得詳細日誌,可以確認 LLM-jp-4-33B-thinking 確實由 64 個 Transformer 層構成;同時也確認在 --tensor-split 16,7/8K context/全層 100% GPU offload 的條件下,根據 KV cache 的分配反推,RTX 5070 Ti 端配置了 46 層、RTX 3070 Ti 端配置了 18 層。

對 Qwen3.8-27B 也必須進行上述從伺服器架設到掌握最佳計算條件的一整套作業,因此相當麻煩。


LLM-jp-4 的推論測試,會在上下文長度 8K、Tensor split 16:7、全層 GPU offload 的條件下執行。內部則使用如下的 config/user.yaml

pathbinaryrepo 是筆者環境中的專用路徑。tensor_split: '16,7' 是針對 RTX 5070 Ti 16GB + RTX 3070 Ti 8GB 的實測穩定配置,其他 GPU 組合需要重新調整。Core/Challenge 的 QA 測試比較,統一採用 8K context、F16 K/V cache、啟用 Flash Attention、全層 GPU offload、temperature=0、最大生成 4096 token 的條件。

config/user.yaml

schema_version: '1.2'

model:
  name: llm-jp-4-33b-thinking-Q4_K_M
  # Current local GGUF (the Ollama blob is still an ordinary GGUF and can be read directly by llama.cpp).
  # Override without editing this file by setting LLMJP_MODEL=/path/to/model.gguf.
  path: /usr/share/ollama/.ollama/models/blobs/sha256-12c23619261c824f88facec565a4b6750d2b0872db7e57f6289ad677822e00dd
  expected_sha256: 12c23619261c824f88facec565a4b6750d2b0872db7e57f6289ad677822e00dd
  verify_sha256: true

server:
  # Built with GGML_CUDA_FORCE_CUBLAS=ON to avoid the Blackwell MMQ crash seen on RTX 5070 Ti.
  # Override with LLMJP_LLAMA_SERVER=/path/to/llama-server if needed.
  binary: ~/llama.cpp-llmjp/build-cublas/bin/llama-server
  repo: ~/llama.cpp-llmjp
  host: 127.0.0.1
  port: 8080
  cuda_visible_devices: '0,1'
  required_gpus:
    - NVIDIA GeForce RTX 5070 Ti
    - NVIDIA GeForce RTX 3070 Ti
  context: 8192
  parallel: 1
  flash_attention: true
  fit_enabled: false
  split_mode: layer
  tensor_split: '16,7'
  n_gpu_layers: all
  cache_type_k: f16
  cache_type_v: f16
  cache_ram_mib: 0
  jinja: true
  reasoning: true
  log_verbosity: 3
  startup_timeout_s: 240
  require_force_cublas: true

quality:
  dataset: datasets/core_v2.1.0.jsonl
  context: 8192
  max_tokens: 4096
  temperature: 0.0
  top_p: 1.0
  top_k: 0
  min_p: 0.0
  repeat_penalty: 1.0
  seed: 42
  timeout_s: 1200

challenge:
  enabled: true
  dataset: datasets/challenge_v1.1.0.jsonl
  context: 8192
  max_tokens: 4096
  reasoning_effort: medium
  # Challenge 80 is an independent discriminative track: 8 items in each of the 10 Core categories.
  # `./run.sh challenge` runs this track without re-running Core/reasoning/performance/long-context phases.

reasoning:
  enabled: true
  efforts: [low, medium, high]
  # The model's Jinja template receives chat_template_kwargs.reasoning_effort.
  # Core uses medium; low/high rerun the frozen 48-item reasoning subset.

performance:
  enabled: true
  warmups: 2
  repeats: 5
  num_predict: 256
  contexts: [8192]
  monitor_interval_s: 0.2
  # Prefix-cache reuse is explicitly disabled per request; actual prompt token counts are retained.

long_context:
  enabled: true
  contexts: [8192]
  items_per_context: 4
  max_tokens: 768

# The old Ollama fit-target sweep is intentionally disabled in the canonical 8K profile.
# 16K/32K VRAM-cliff experiments should be run as a separate hardware profile because
# they require CPU offload and/or KV-cache quantization and would confound Core quality results.
vram_margin_sweep:
  enabled: false

reporting:
  make_figures: true
  formats: [png, svg]

推論性能測試的結果會與 Qwen3.8-27B 的結果一併呈現。

Qwen3.8-27B 驗證環境的準備

模型下載

這裡使用 hf.co/ggml-org/Qwen3.8-27B-GGUF:Q4_K_M。可直接從 Hugging Face 下載。

若已在 Ollama 端安裝模型,可用 ollama list 確認。

blob 的確認

QWEN_OLLAMA='hf.co/ggml-org/Qwen3.8-27B-GGUF:Q4_K_M'
ollama show --modelfile "$QWEN_OLLAMA"
FROM /usr/share/ollama/.ollama/models/blobs/sha256-31629f53165ab6a7dad8c9847dcfd1fdf55829dac1e6e748f4a68581b0033d34
FROM /usr/share/ollama/.ollama/models/blobs/sha256-2e968a6af97ce35d8971890b257b9b7edabf20ad91450501fa53162a19ee33eb
TEMPLATE {{ .Prompt }}

這裡使用的是第一行。第二個檔案對應於圖像輸入用的 vision projector(mmproj),此次純文字驗證不會用到。

確認 hash 值

QWEN_BLOB="$(
  ollama show --modelfile "$QWEN_OLLAMA" |
  awk '$1=="FROM"{print $2; exit}'
)"

echo "$QWEN_BLOB"
ls -lh "$QWEN_BLOB"
sha256sum "$QWEN_BLOB"

結果

/usr/share/ollama/.ollama/models/blobs/sha256-31629f53165ab6a7dad8c9847dcfd1fdf55829dac1e6e748f4a68581b0033d34
-rw-r--r-- 1 ollama ollama 18G Aug 15 03:36 /usr/share/ollama/.ollama/models/blobs/sha256-31629f53165ab6a7dad8c9847dcfd1fdf55829dac1e6e748f4a68581b0033d34
31629f53165ab6a7dad8c9847dcfd1fdf55829dac1e6e748f4a68581b0033d34  /usr/share/ollama/.ollama/models/blobs/sha256-31629f53165ab6a7dad8c9847dcfd1fdf55829dac1e6e748f4a68581b0033d34

Qwen 用 llama.cpp 環境的建置

Qwen 用的 llama.cpp 環境要和 LLM-jp fork 分開建置。這是因為 LLM-jp fork 是包含 LLM-jp 專用 chat template / reasoning 支援的衍生版;而要運行實作了 Gated DeltaNet 的 Qwen3.8 新架構,使用最新版 llama.cpp 會比較穩妥。

clone

git clone https://github.com/ggml-org/llama.cpp.git ~/llama.cpp-qwen38
cd ~/llama.cpp-qwen38
git rev-parse HEAD

若已經 clone,則只要 git pull 即可。commit 紀錄可自行保存。

為了與 LLM-jp 的 backend 條件一致,同時避開 Blackwell 端 MMQ 問題,這裡以 cuBLAS 固定方式建置 CUDA 版。

建置

cmake -B build-cublas \
  -DGGML_CUDA=ON \
  -DGGML_CUDA_FORCE_CUBLAS=ON \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_CUDA_COMPILER=/usr/local/cuda-13.2/bin/nvcc \
  -DCMAKE_CUDA_ARCHITECTURES="86;120"

cmake --build build-cublas \
  --config Release \
  -j"$(nproc)" \
  --target llama-server llama-cli llama-bench

在筆者環境中約 10 分鐘完成。

建置完成後,先確認 llama.cpp 看到的 GPU 順序,之後會依此最佳化 Qwen 的 VRAM 配置。

確認 GPU 裝置 index

CUDA_VISIBLE_DEVICES=0,1 \
./build-cublas/bin/llama-server --list-devices

結果

Available devices:
  CUDA0: NVIDIA GeForce RTX 5070 Ti (16302 MiB, 47074 MiB free)
  CUDA1: NVIDIA GeForce RTX 3070 Ti (8191 MiB, 46990 MiB free)

因此,如同 LLM-jp 的環境一樣,tensor split 的配比也要按照「5070 Ti、3070 Ti」的順序來設定。

LLM-jp 由於模型本體約 18.8 GiB,因此 16:7 左右幾乎是極限。另一方面,Qwen3.8 Q4_K_M 約 15.6 GiB,較輕了數 GB;把這些餘裕分配到 5070 Ti 上,理論上可望得到更快的運作。


(幾分鐘後……)

……最後,在 RTX 5070 Ti + RTX 3070 Ti 的雙 GPU 環境中,最佳化後且公平的參數如下。

llama-server 啟動條件

CUDA_VISIBLE_DEVICES=0,1 \
./build-cublas/bin/llama-server \
  --model "$QWEN_BLOB" \
  --jinja \
  -rea on \
  --flash-attn on \
  -fit off \
  --split-mode layer \
  --tensor-split 18,7 \
  -ngl all \
  --ctx-size 8192 \
  --parallel 1 \
  --cache-type-k f16 \
  --cache-type-v f16 \
  --cache-ram 0 \
  -lv 4 \
  --host 127.0.0.1 \
  --port 8080

參數中的 "$QWEN_BLOB" 會是像

/usr/share/ollama/.ollama/models/blobs/sha256-31629f53165ab6a7dad8c9847dcfd1fdf55829dac1e6e748f4a68581b0033d34

這樣的值。

此設定下的 decode 速度大約為 33.02 t/s,且兩張 GPU 都能保留約 2 GB 的 VRAM 緩衝,算是相當寬裕。

在調查最佳 tensor-split 比例的過程中,也確認即便把更多運算負載偏向高性能的 5070Ti,decode 速度反而會下降。看來,比起把 5070 Ti 的 VRAM 用到極限,更重要的是把運算量妥善分散到兩張 GPU。

順帶一提,調查結果如下。雖然有些雜訊,但 GPU 間的模型層比例與推論速度之間,似乎存在鐘形曲線(或平台)關係。

image.png

18:8 = 69.2% → 32.63
16:7 = 69.6% → 32.85
19:8 = 70.4% → 32.80
18:7 = 72.0% → 33.02  ← 採用的比例
17:6 = 73.9% → 32.14
16:5 = 76.2% → 32.34
16:4 = 80.0% → 32.43
17:4 = 81.0% → 31.66
18:4 = 81.8% → 29.18

其實只要已經做到全層 GPU offload,推論速度就不會有那麼大的差距。若只是要做這次這種單純的 benchmark 測試,使用 18:7 左右的值就已經足夠。

推論性能測試設定

為了建立 Qwen 用目錄,這裡沿用 LLM-jp 的目錄。為保險起見,先把結果清空,dataset 則不更動。

cp -a llmjp4_benchmark_llamacpp_dualgpu_v1_3_1 \
      qwen38_benchmark_llamacpp_dualgpu_v1_3_1

cd qwen38_benchmark_llamacpp_dualgpu_v1_3_1
rm -rf results
mkdir -p results
touch results/.gitkeep

config/user.yaml 改成 Qwen 用設定。主要變更如下。

項目LLM-jpQwenmodel.nameLLM-jpQwen3.8-27B-Q4_K_Mmodel.pathLLM-jp blobQwen main GGUF blobSHA256LLM-jp SHA31629f...3d34llama-serverLLM-jp forkupstream Qwen buildtensor_split16,718,7reasoning sweeplow/medium/high停用config/user.yaml

schema_version: '1.2'

model:
  name: Qwen3.8-27B-Q4_K_M
  path: /usr/share/ollama/.ollama/models/blobs/sha256-31629f53165ab6a7dad8c9847dcfd1fdf55829dac1e6e748f4a68581b0033d34
  expected_sha256: 31629f53165ab6a7dad8c9847dcfd1fdf55829dac1e6e748f4a68581b0033d34
  verify_sha256: true

server:
  binary: ~/llama.cpp-qwen38/build-cublas/bin/llama-server
  repo: ~/llama.cpp-qwen38
  host: 127.0.0.1
  port: 8080

  cuda_visible_devices: '0,1'
  required_gpus:
    - NVIDIA GeForce RTX 5070 Ti
    - NVIDIA GeForce RTX 3070 Ti

  context: 8192
  parallel: 1
  flash_attention: true
  fit_enabled: false

  split_mode: layer
  tensor_split: '18,7'
  n_gpu_layers: all

  cache_type_k: f16
  cache_type_v: f16
  cache_ram_mib: 0

  jinja: true
  reasoning: true

  log_verbosity: 3
  startup_timeout_s: 240
  require_force_cublas: true

quality:
  dataset: datasets/core_v2.1.0.jsonl
  context: 8192
  max_tokens: 4096

  temperature: 0.0
  top_p: 1.0
  top_k: 0
  min_p: 0.0
  repeat_penalty: 1.0
  seed: 42

  timeout_s: 1200

challenge:
  enabled: true
  dataset: datasets/challenge_v1.1.0.jsonl
  context: 8192
  max_tokens: 4096
  reasoning_effort: medium

reasoning:
  # Qwen's native levels are low / medium / xhigh.
  # v1.3.1 auxiliary sweep is LLM-jp-specific low / medium / high,
  # so disable it for the controlled cross-model comparison.
  enabled: false
  efforts: [low, medium, xhigh]

performance:
  enabled: true
  warmups: 2
  repeats: 5
  num_predict: 256
  contexts: [8192]
  monitor_interval_s: 0.2

long_context:
  enabled: true
  contexts: [8192]
  items_per_context: 4
  max_tokens: 768

vram_margin_sweep:
  enabled: false

reporting:
  make_figures: true
  formats: [png, svg]

這裡把 reasoning 設成 enabled: false,是因為腳本內部的用途所致。這並不是把 Qwen 的 thinking 關掉;伺服器端仍然是 reasoning: true,因此不影響性能比較。

如果測試時曾手動啟動過 llama-server,記得關掉它(按 Ctrl+C 即可)。

LLM-jp-4-33B-thinking 與 Qwen3.8-27B 的比較

這裡再次說明執行時條件。

本次性能評估使用相同的 Core 128 題與 Challenge 80 題,對 LLM-jp-4-33B-thinking:Q4_K_MQwen3.8-27B:Q4_K_M 進行比較。兩者都在同一套硬體——RTX 5070 Ti 16 GB + RTX 3070 Ti 8 GB——上執行,條件為 context length 8192、F16 KV cache、啟用 Flash Attention、全層 GPU offload、temperature 0。

reasoning effort 方面,採用兩個模型都能共通使用的 medium

GPU 層分配則各自採用實測上表現良好的條件;LLM-jp 使用 --tensor-split 16,7,Qwen3.8 則為 18,7

這裡的速度比較,較適合解讀為在同一硬體上,各模型經現實最佳化後的 end-to-end 性能比較。另外,LLM-jp 使用對應 fork,Qwen3.8 使用 upstream llama.cpp,這一點也可能成為性能差異的因素。

機器判定結果

直接看結果。由機器評分器進行的初步判定如下。

TrackLLM-jp-4-33BQwen3.8-27BCore 128119/128 = 92.97%115/128 = 89.84%Challenge 8069/80 = 86.25%73/80 = 91.25%在 Core 題中是 LLM-jp 領先 3.13 個百分點;但在更難的 Challenge 題中,Qwen3.8 則領先 5.00 個百分點。

若把兩個 track 的答對數直接加總,兩者都是 188/208 = 90.38%,完全相同。

更有意思的是按題目逐題比較勝負。Core 題中有「只有 LLM-jp 答對」7 題、「只有 Qwen 答對」3 題;Challenge 題則相反,有「只有 Qwen 答對」5 題、「只有 LLM-jp 答對」1 題。整體 208 題下來,最終「只有一方答對」的總數是 8 對 8。

因此,至少在本測試集範圍內,與其說其中一方全面壓過另一方,不如說兩者都是具備不同失誤模式、但整體同級的模型,這種理解更接近實際。

機器判定與目視評價的差異

本次 benchmark 為了優先考慮可重現性,正式分數是依據數值誤差容許範圍、JSON schema、Python unit test 等機器式正誤判定計算而來。另一方面,個別檢視被判定為 FAIL 的回答(即錯誤答案)後,可以發現有不少其實推論本身是對的,只是因為四捨五入誤差、輸出格式或分類不一致,或是已達到最大生成 token 數而被截斷,才導致失分。

因此,除了正式分數之外,也從目視角度檢查回答內容,並從「是否理解題目本質、是否已達到合理的解法與判斷」的觀點重新評估。依此基準得到的參考值大致如下。

Trackstrict目視評價LLM-jp Core119/128 = 92.97%127/128 ≈ 99.22%Qwen Core115/128 = 89.84%126/128 ≈ 98.44%LLM-jp Challenge69/80 = 86.25%78/80 = 97.50%Qwen Challenge73/80 = 91.25%80/80 = 100%從這個目視評價可以看出,兩個模型都比 strict score 顯示的更理解題目本身。

「目視評價」的數值,是把那些僅有輕微數值誤差、語意上合理的分類錯誤、或已經答到正解但在輸出過程中被 truncation(推論中止) 的回答,視為「本質上的解法與判斷正確」後所做的統計。這是對過於嚴苛的機器判定所做的補充,並另外請 ChatGPT-5.6-Sol 協助檢查評分。

兩個模型的差異

雖然兩個模型都交出了不錯的成績,但它們的失誤型態其實相當不同。

LLM-jp 在數值題中較常出現近似精度不足或些微計算錯誤。例如,明明已經建立了正確的物理式或化學式,卻因心算式的指數評估或四捨五入而超出 grader 的容許誤差。另一方面,在 Challenge 的 coding 題中也確認到 2 題確實存在實作 bug,8 題中有 6 題通過。

相較之下,Qwen3.8 在完整作答的數值題上展現出極高穩定性。Core 與 Challenge 合計 103 題 numeric grader 中,FAIL 的 10 題全部都是因為達到 max_tokens=4096 而被截斷;凡是完整輸出到最後的 93 題,全都通過機器判定。Challenge 的 coding 也達到 8/8,這次測試中 Qwen 端的優勢相當明顯。

不過,Qwen3.8「想太多」的特性也成為明確弱點。在部分數值題中,雖然已經建立正確公式並得到足夠精準的答案,之後仍反覆用不同算法重新驗算、反覆重算數字,最後在尚未輸出最終答案前就耗盡 4096 tokens。208 題中,Qwen 有 10 題被截斷,而 LLM-jp 只有 1 題。

因此在本次條件下,LLM-jp 傾向較簡潔地到達結論,而 Qwen3.8 則傾向更執著於自我驗證;這一點同時反映了更強的數值推論能力,以及較低的 token 使用效率。

推論速度・執行效率比較

在約 6000 token 規模的效能調查中,可以確認 Qwen3.8 作為推論引擎的效率相當高。

指標LLM-jp-4-33BQwen3.8-27BPrompt processing約191.5 tok/s約344.2 tok/sDecode約27.9 tok/s約29.7 tok/sWall time約41.3 s約26.7 sPeak VRAM合計約23.2 GB約20.4 GB由於內部 tokenizer 不同,prompt 處理速度不能完全視為公平的性能指標;但處理同等規模輸入所需時間明顯較短的 Qwen3.8,在實用上確實更快。

另一方面,在實際 benchmark 中,Qwen3.8 的平均生成 token 數比 LLM-jp 多,因此模型本身的速度優勢有時會被更長的推論所抵銷。Qwen3.8 的特性是「作為生成 1 個 token 的系統很有效率,但為了解出 1 題會消耗更多 token」。

若 VRAM 很充裕,Qwen3.8 的推論深度(也就是執著程度)應該會很有用;但若是在硬體較拮据的本機 local LLM 使用情境下,這種特性反而會不利。

由這次結果可知,單看 tokens/s 並不足以評估模型在實務上的性能。

關於能源效率

Qwen3.8 的 token throughput 很高,但這個特性並不會直接轉化為整體解題速度的提升。以下列出固定長度 performance test 所算出的、每次測量的 GPU 能耗。只看這個結果的話,Qwen3.8 比 LLM-jp 更省電。

但在實際 benchmark 中,Qwen3.8 的平均生成 token 數比 LLM-jp 多,顯示出「作為生成 1 個 token 的系統很有效率,但解 1 題時需要消耗更多 token」的特徵。

結果就是,在固定長度推論下,Qwen3.8 確實比 LLM-jp 更快也更省電;但若看面對實際問題的 end-to-end 處理時間,這個優勢就消失了。因此,若是以每次解題的能源效率來看,未必能像固定 token 長度測試那樣顯示出很大的優勢。

固定長度 token 生成性能測試

Qwen:約8.86 kJ
LLM-jp:約12.05 kJ

實際 benchmark 中的生成 token 數與每題 wall time

Core平均生成量:Qwen 約724 token、LLM-jp 約467 token
Core平均時間:Qwen 約24.5 s、LLM-jp 約16.2 s

本 benchmark 並沒有針對實際 QA 測試逐題累積耗電,因此每次解題的能源消耗並不是直接實測值,而應解讀為從生成 token 數與 wall time 所推估出的趨勢。

總結

這次,我把 LLM-jp-4-33B-thinking 與 Qwen3.8-27B 放在可於本機重現的相同 benchmark 上比較。

若只看最單純的 strict score,Core 方面 LLM-jp 以 119/128(92.97%)略優,Challenge 則由 Qwen3.8 以 73/80(91.25%)領先。整體正答率也沒有很大差距;至少從這次的題組來看,很難說哪一方是明顯更優秀的模型。

另外也要考慮到,這次 benchmark 的正答率(包含目視確認)幾乎接近 100%,也就是說,這次使用的任務對兩個模型而言都相當容易。特別是雖然準備了比 Core 更複雜、要求更多推論的 Challenge 題組,兩個模型仍幾乎滿分。未來應該建立更困難的題組,以確認推論性能的極限。(這點也可參考文末的「參考」章節)

若把機器判定為 FAIL 的回答也納入內容檢視,就能看出模型間的特徵差異。

LLM-jp 比較簡潔且穩定地到達最終答案,較少用完 token budget;但在數值計算最後幾位精度不足,以及 coding 邊界案例上,仍可見失分。

Qwen3.8 則在數值推論與程式設計領域表現優異。對於已完整作答的數值題,這次幾乎展現出完全的精度,Challenge 等級的 coding 題也全對。反面是,在已經得到足夠答案後仍持續重算,出現了 "overthinking";Challenge 80 題中有 5 題因為達到 4k token 上限而被判定 FAIL。

綜合以上:

  • LLM-jp:每 1 token 的計算成本相對較高,但 reasoning 較簡潔,能在有限 token budget 內穩定完成作答
  • Qwen3.8:數值推論與程式設計能力強,token throughput 也高,但 reasoning 稍微冗長,較容易出現 overthinking

這就是本次比較驗證得到的主要結論。

【參考】關於 benchmark 測試的難度

這次比較也顯示出 benchmark 本身的極限。

如前所述,Challenge 題組原本是作為比 Core 更難的級別而設計,但 Qwen3.8 在包含選擇題的多數分類中都拿到滿分;無論是機器判定還是目視檢查,都證實 80 題全部在解法與判斷上本質正確。LLM-jp 也同樣達到 97% 以上的非常高正答率。

也就是說,這次使用的 benchmark 對於 30B 級最新模型之間的實力差異來說,難度還不夠。若未來要更仔細地檢驗,除了 Core 與 Challenge 之外,恐怕還需要新增更難的 Hard 級(暫稱)題組。

作為 Hard 級題目,應該加入更深層的多步推論、必要條件與充分條件的辨識、找反例的題目、對多個互相衝突假說的檢討、容易混淆的 coding 題、以及結合多個物理・化學法則的題目等。


不過,這個 benchmark 也並非簡單到不值一提。當我用更小參數的 DeepSeek-R1-8B(DeepSeek-R1-0528-Qwen3-8B)進行測試時,卻幾乎都卡在 max_tokens=4096(每次回答的生成 token budget)限制上,發生大量 truncation。

最新版 DeepSeek-R1-8B 是與 Qwen3-8B 相同的 dense Transformer 架構,並不是 MoE。

具體來說,Core 128 題中,排除 HTTP 錯誤後的 125 答案裡有 69 題(55.2%)因生成 token 長度(上限 4k)耗盡而被截斷;Challenge 80 題中則有 49 題(61.3%)被截斷。

指標LLM-jp-4-33B-thinkingQwen3.8-27BDeepSeek-R1-0528-Qwen3-8BCore strict119/128 = 92.97%115/128 = 89.84%50/125 = 40.00% ※Core truncation0/128 = 0%5/128 = 3.91%69/125 = 55.20%Core平均生成token4677242,854Core平均wall time16.16 s24.46 s25.87 sChallenge strict69/80 = 86.25%73/80 = 91.25%**26/80 = 32.50%Challenge truncation1/80 = 1.25%5/80 = 6.25%49/80 = 61.25%Challenge平均生成token6758523,141Challenge平均wall time23.35 s**28.75 s28.74 s> ※ DeepSeek 模型的 Core 測試中有 3 題出現 HTTP 500,因此 strict score 的分母採用有效回答 125 題。

DeepSeek-R1-0528-Qwen3-8B 的機器判定分數僅停留在 30~40% 區間,但失分過半並非因答錯,而是因為達到 4k-token 生成上限。尤其在 Core 中,125 個有效回答裡有 69 題、Challenge 中 80 題裡有 49 題的 finish_reason="length"。因此,這個結果反映的不只是模型的最大推論能力,也強烈反映了在「同樣只有 4k-token 推論資源」限制下,能否把最終答案做完的推論效率。

若按類別觀察,差異更明顯。下表是機器判定數值。

分類Core LLM-jpCore QwenCore DeepSeekChallenge LLM-jpChallenge QwenChallenge DeepSeek高階日文100%100%93.3%100%100%100%分析推論100%100%53.3%100%100%25.0%化學・材料75.0%83.3%8.3%75.0%62.5%12.5%程式設計100%93.8%43.8%75.0%100%12.5%*幻覺耐性50.0%25.0%50.0%75.0%75.0%37.5%人文・社會・經濟100%100%40.0%87.5%100%75.0%指示遵循100%87.5%87.5%100%100%25.0%生命・資訊90.0%90.0%20.0%62.5%75.0%12.5%數學・統計95.0%100%15.0%87.5%100%25.0%物理・工程100%83.3%0%100%100%0%---

也一併展示性能比較。DeepSeek 只看 tokens/s 的話確實快得驚人,但解一題的速度完全不快。雖然 DeepSeek 生成 1 token 的速度是 LLM-jp 的 3 倍以上,但解 1 題所需時間反而比 LLM-jp 慢約 60%,因此和 Qwen 幾乎相同。

指標LLM-jp-4-33BQwen3.8-27BDeepSeek-R1-8B使用 GPU5070Ti + 3070Ti5070Ti + 3070Ti僅 5070TiPrompt processing191.5 tok/s344.2 tok/s619.2 tok/sDecode27.91 tok/s29.68 tok/s101.05 tok/sWall time41.28 s26.69 s12.60 s平均 run peak VRAM 合計約23.16 GiB約20.37 GiB約7.59 GiBGPU 平均功率293.6 W334.0 W185.4 WGPU energy / run12.05 kJ8.86 kJ2.29 kJCore 平均生成量:

LLM-jp       467 tokens
Qwen         724 tokens
DeepSeek   2,854 tokens

Core 平均 QA 時間:

LLM-jp      16.2 s
Qwen        24.5 s
DeepSeek    25.9 s

Challenge 平均 QA 時間:

LLM-jp      23.4 s
Qwen        28.8 s
DeepSeek    28.7 s

順帶一提,DeepSeek-R1-0528 系列官方評測時,最大生成長度設定為 64K tokens,temperature 0.6、top-p 0.95。這次為了優先進行模型間公平比較,設定可能無法完全發揮 DeepSeek 模型本來的性能;但若是在所有模型都只給 4096 completion-token 的同一 token budget 下評估回答完成率與正答率,這樣的比較至少是公平的。

Model推論正確性4K內完結性Reasoning效率LLM-jp-4-33B高非常高**Qwen3.8-27B非常高高中等DeepSeek-R1-8B完結時很高非常低**低模型這次看到的特徵LLM-jp-4-33B-thinking推論相對短,在 4K budget 內的完結性極高。Core strict 表現最佳。Qwen3.8-27B推論能力非常高,包含數值與 coding。Challenge 最佳。不過比 LLM-jp 稍微更愛長考。DeepSeek-R1-0528-Qwen3-8B以 8B 來說,完結時的回答品質相當高,原始推論極快且省 VRAM。不過 reasoning 極端冗長,在 4K budget 下有過半題目無法完結。就本次使用的 benchmark 而言,雖然對目前 27~33B 級模型幾乎已接近飽和,但對 8B 級模型仍然算是相當嚴格的難度。

為了替 DeepSeek 說句公道話,這次使用的 DeepSeek-R1-8B 是一個刻意被追加訓練成會長考的模型,這點值得特別指出。

【參考】Benchmark 題組與腳本一式

整套腳本以 zip 檔形式在 GitHub 上提供下載(可自由修改)。

本文使用的題組如下。題目內容係從 benchmark 執行時所用的 canonical dataset 中原樣擷取,未經修改。

  • Core: 128 題(core_v2.1.0.jsonl
    • SHA256: 0ba952bfd03db1cda89d15fa31f0b6ecd07e0b2c3cc9a3ec12633b4e84369b1e
  • Challenge: 80 題(challenge_v1.1.0.jsonl
    • SHA256: 0c18e8ae51d88a441ecdff33df4e071fee3bdb817481ec1694365239d3da9546
  • 合計: 208 題
  • 題目與 metadata 授權:CC BY 4.0

原文出處:https://qiita.com/h-nabata/items/b80484406e839ee6507b


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝12   💬4  
209
🥈
我愛JS
💬1  
6
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登