📁

RX7900XTX + WSL2 + ROCm + vLLMで量子化モデルを動かす

に公開

はじめに

本記事の内容

この記事は、前回の記事「RX7900XTX + WSL2 + ROCm + vLLMでローカルLLM環境を構築する」の続編として、
同じ環境で量子化モデルを使って 30B クラスのモデルを動かしてみた結果をまとめたものです。

特に、RX7900XTX + WSL2 + ROCm + vLLM という構成で、

  • どの量子化方式を選ぶべきか
  • なぜその選択になるのか
  • 実際に 30B モデルを動かしたときの VRAM 使用量とスループット

を、自分が実際に試した結果 + 調査した情報をあわせて整理しました。

この記事では、量子化方式の中でも特に**HuggingFace 上でモデル数が多い「AWQ」と「GGUF」**を中心に話を進めます。


結論を先に

  • この環境で性能を出したいなら、量子化方式は「AWQ 一択」です。
  • GGUF も動きますが、自分のテスト環境では **AWQ 30B ≒ 203 tok/s に対して、GGUF 30B ≒ 30 tok/s(約 1/7)と、「vLLM で使うには速度的に厳しい」**という印象です。
  • GGUF を活かしたいなら、vLLM ではなく llama.cpp / Ollama を使う方が◎

1. 量子化手法について

1-1. 量子化手法のざっくり比較

まずは、よく名前が挙がる量子化方式についてRX7900XTXの使用可否と特徴を簡単にまとめました。

量子化手法 ROCm
対応
dtype
設定
HuggingFace上のモデル数 精度保持率(ざっくり目安) 主な特徴 推奨度
AWQ ✅ 安定 float16 ⭐⭐⭐⭐⭐ 4bit: 95% 前後(タスク依存) vLLM で最適化済み、
高速推論、
幅広いアーキ対応
⭐⭐⭐
最推奨
GGUF ⚠️
条件付き
auto ⭐⭐⭐⭐⭐ Q4_K_M: 90% 前後
Q5_K_M: 95–98%
Llama/Qwen/Mistral系中心、
transformers から直接ロード不可
⭐⭐
エンジン依存
GPTQ ⚠️ 不安定 float16 ⭐⭐⭐⭐ 4bit: 92–95%
INT8: 95–98%
ROCm で文字化け報告あり、
INT8 は高精度

条件付き
BitsAndBytes 🔶 要検証(ROCm) auto ⭐⭐⭐ 4bit(NF4): 98–99%
8bit: 99.5%+
オンザフライ量子化、
QLoRA 対応、
やや遅い
⭐⭐
代替案
TorchAO 🔶 実験的 手法による ⭐⭐ 4bit: 85–90% PyTorch 公式、
まだ事例少ない

実験用
FP8 ❌ モデル依存・
限定的
- ⭐⭐ 8bit: 99%+ MI300/H100 向け、
RDNA3 では事例が少ない

本記事対象外
Marlin ❌ NVIDIA専用 - ⭐⭐ GPTQ 同等 GPTQ 高速化版、
ROCm 非対応

使用不可
BitBLAS ❌ NVIDIA専用 - 4bit: 85–90% 最速クラス、
ROCm 非対応

使用不可

1-2. HuggingFace 上のモデル数(入手性)

ざっくりとした入手性の目安はこんなイメージです。

  • ⭐⭐⭐⭐⭐ 非常に多数(数千以上、主要モデルのほぼ全てで何かしら量子化版がある)
  • ⭐⭐⭐⭐ 多数(数百以上、メジャーどころは大体ある)
  • ⭐⭐⭐ 一定数(数十以上、探せば見つかる)
  • ⭐⭐ 限定的(数個〜十数個)
  • ほぼ見かけない

AWQGGUF は、コミュニティによって幅広いモデルが量子化されており、
「とりあえず 30B 量子化モデルを探したい」という時に最も見つけやすいです。


2. RX7900XTX + WSL2 + ROCm + vLLM での 量子化モデル実測

実際に量子化モデルを動かしてみました。

2-1. 検証環境の再掲

前提となる環境は、前回の記事と同じです。

  • GPU: AMD Radeon RX7900XTX (24GB)
  • OS (ホスト): Windows 11
  • WSL2: Ubuntu 24.04
  • ROCm: 7.2
  • PyTorch: 2.9.1+rocm7.2.0
  • vLLM:
    • 検証時点では v0.15.0 を使用
    • v0.16.0 以降でも BUILD_FA=0 でビルドすれば同様の構成で動作可能

2-2. 30B クラスでの実測結果(AWQ vs GGUF)

同じ 30B クラスの Qwen3 Coder 系モデルを使って、
AWQ と GGUF をほぼ同条件で動かしてみました。

方式 モデル スループット(10プロンプト, max 512 tokens) VRAM(ロード時) VRAM(推論時 最大) 最大コンテキスト長(目安)
AWQ QuantTrio/Qwen3-Coder-30B-A3B-Instruct-AWQ 約 203 tok/s 約 16.3 GB 約 22.5 GB 40k くらいまでOK
GGUF unsloth/Qwen3-Coder-30B-A3B-Instruct Q4_K_M 約 30 tok/s 約 18.4 GB 約 22.6 GB 24k くらいまでOK

※コンテキスト長の「40k / 24k」は、KVキャッシュの量子化や最適化をしていない状態での最大なので実際にはもっと伸びます。

同じ 30B クラス・ほぼ同条件にもかかわらず、AWQ は GGUF の 6〜7 倍のスループットという結果になりました。


2-3. 他のベンチマークとの比較

他の人のベンチマーク(公式チームや EmbeddedLLM など)を見ると、

  • 専用の ROCm Docker(Navi 最適化版)
  • vLLM main ブランチ
  • Llama 3.1 8B Q5_K_M GGUF

といったかなり好条件の環境で、

  • 約 60 tok/s 前後

という数字が報告されています。

つまり、

  • 「ROCm + vLLM + GGUF の環境で必ず 30 tok/s」という結果になる訳ではない
  • ちゃんと環境を整えれば 60 tok/s 程度 は出る

一方で、

  • それでも 「AWQ 30B ≒ 200 tok/s」に比べると、8B GGUF が 60 tok/s というのはかなり見劣りする
  • 30B GGUF になると、自分の環境のように 30 tok/s 前後まで落ちる可能性はある

ということで、

「RX7900XTX + WSL2 + ROCm + vLLM で量子化モデルを使うなら、GGUF を頑張ってチューニングするより、最初から AWQ を選んだ方が幸せになれる」

というのが結論です。


2-4. ベンチマークに使用したスクリプト

前回記事でも使ったコードを流用し、dtypemodel だけ変えて測定しています。

benchmark_simple.py(クリックで開きます)
# benchmark_simple.py
import time
from vllm import LLM, SamplingParams

def benchmark_model(model_name, num_prompts=10, max_tokens=512):
    print("\n" + "="*60)
    print(f"Model: {model_name}")
    print("="*60)

    print("Loading model...")
    t0 = time.time()
    llm = LLM(
        model=model_name,
        trust_remote_code=True,
        gpu_memory_utilization=0.9,
        max_model_len=4086,
        dtype='float16'
    )
    load_time = time.time() - t0
    print(f"Load time: {load_time:.2f}s")

    prompts = [
        "Write a short story about artificial intelligence.",
        "Explain quantum computing in simple terms.",
        "What are the benefits of exercise?",
        "Describe the process of photosynthesis.",
        "Write a poem about nature.",
    ] * (num_prompts // 5)

    sampling_params = SamplingParams(
        temperature=0.8,
        top_p=0.95,
        max_tokens=max_tokens,
    )

    # Warmup
    _ = llm.generate(prompts[:1], sampling_params)

    print(f"\nRunning benchmark ({num_prompts} prompts, max {max_tokens} tokens)...")
    t1 = time.time()
    outputs = llm.generate(prompts, sampling_params)
    elapsed = time.time() - t1

    total_tokens = sum(len(output.outputs[0].token_ids) for output in outputs)
    avg_tokens = total_tokens / len(outputs)
    tps = total_tokens / elapsed

    print("\n" + "="*60)
    print("Results:")
    print(f"  Total time: {elapsed:.2f}s")
    print(f"  Total tokens generated: {total_tokens}")
    print(f"  Average tokens per output: {avg_tokens:.1f}")
    print(f"  Throughput: {tps:.2f} tokens/s")
    print("="*60)

    return {
        "model": model_name,
        "load_time": load_time,
        "total_time": elapsed,
        "total_tokens": total_tokens,
        "tps": tps,
    }

if __name__ == "__main__":
    models = [
        "QuantTrio/Qwen3-Coder-30B-A3B-Instruct-AWQ"
    ]
    for m in models:
        benchmark_model(m, num_prompts=10, max_tokens=512)
        input("\nPress Enter to continue...")

3. 【重要】dtype 設定について

vLLM v0.15.0 のデフォルト dtype は bfloat16 です。

そのままでも動くモデルもありますが、AWQ や GPTQ 等のモデルは float16 前提で作られています。

そのため、それらの量子化モデルを使うときは dtype="float16" を明示的に指定しておきましょう。

from vllm import LLM

llm = LLM(
    model="QuantTrio/Qwen3-Coder-30B-A3B-Instruct-AWQ",
    dtype="float16",  #float16を明示的に指定
    trust_remote_code=True,
    gpu_memory_utilization=0.9,
    max_model_len=4096,
)

4. vLLM で GGUF を使う場合の注意点

GGUF モデルは transformers から直接ロードできないので、以下の 2 点が必須です。

  1. GGUF ファイルをローカルにダウンロードしてパス指定
  2. 元モデルのトークナイザー(HuggingFace 上の非量子化モデル名)を指定

vLLM でのロード例

llm = LLM(
    model="/home/username/models/qwen3-30b-gguf/Qwen3-Coder-30B-A3B-Instruct-Q4_K_M.gguf", #モデル保存先パス指定
    tokenizer="Qwen/Qwen3-Coder-30B-Instruct",  #元モデルのトークナイザー名
    trust_remote_code=True,
    gpu_memory_utilization=0.9,
    max_model_len=4096,
)

5. GGUF を使うなら llama.cpp / Ollama を推奨

ここまでの結果から、

  • RX7900XTX + WSL2 + ROCm + vLLM で GGUF を動かす
     → 「動くが、AWQ と比べると明らかに遅い」
  • GGUF を使いたい最初から llama.cpp / Ollama を選んだ方が自然

という結論になりました。

ざっくりとした印象はこんなイメージです:

エンジン GGUF対応 RX 7900 XTX での性能イメージ 特徴
llama.cpp ◎ ネイティブ 80〜120 tok/s 単体バイナリで軽量、GGUF 本命
Ollama ◎ ネイティブ 70〜100 tok/s GUI/REST API 前提、扱いやすい
vLLM △ 実験的 30〜60 tok/s(今回は 30 tok/s) 本来は HF モデル + AWQ 向けの設計

まとめると:

「vLLM で速度と扱いやすさを取りたいなら AWQ。
GGUF を使いたいなら、vLLM ではなく llama.cpp / Ollama に任せる」

という棲み分けで良いのかなと感じています。

が・・・細かいことを言うと vLLM と ollama/llama.cpp には次のような特徴があるので、
やはり vLLM + AWQ が最適解なのかなあと思っています。

vLLM と Ollama/llama.cpp の根本的な違い(クリックで開きます)

実は、長い会話を続ける用途では vLLM の方が圧倒的に有利です。


PagedAttention の効果

vLLM の核心技術である PagedAttention は、
KVキャッシュを OS のメモリページングのようにページ単位で管理します。

vLLM(PagedAttention)の特徴:

  • KVキャッシュを小さなページ(ブロック)に分割して管理
  • 必要な部分だけをメモリに配置し、断片化を最小限に抑える
  • 会話ターン数が増えても メモリ効率が落ちにくい
  • 複数リクエストで KVキャッシュを共有できるため、バッチ処理に強い

Ollama/llama.cpp の特徴:

  • KVキャッシュを連続したメモリブロックとして確保(古典的方式)
  • 会話が長くなると:
    • より大きな連続メモリが必要になる
    • メモリの断片化が進む
    • KVキャッシュ全体の再確保やスワップが発生
  • ターン数が増えるほど速度が低下する

実際の挙動の違い(傾向)

エンジン 初期ターン 10ターン後 20ターン後 特徴
vLLM + AWQ 200 tok/s 190 tok/s 180 tok/s 速度が維持されやすい
Ollama/llama.cpp + GGUF 180 tok/s 120 tok/s 60 tok/s ターンが増えると急激に低下

※数値はあくまでイメージですが、実際の使用感としてもこのような傾向が確認できています


用途による使い分け

vLLM が向いている用途:

  • 長い対話を続ける(10ターン以上)
  • 複数ユーザーからのリクエストを同時処理(サーバー用途)
  • コンテキスト長が長い作業(数万トークン)
  • バッチ推論

Ollama/llama.cpp が向いている用途:

  • 短いやり取り(1〜5ターン程度)
  • 単発の推論
  • セットアップの手軽さ重視
  • GUI(Ollama の WebUI)から簡単に使いたい

結論

  • GGUF フォーマットのモデルを使いたい → llama.cpp / Ollama
  • 長い対話・サーバー用途・速度重視 → vLLM + AWQ
  • どちらも一長一短なので、用途で使い分けるのがベスト

6. その他の量子化手法について(調査ベース)

ここからは、自分ではまだ試せていないものの、調査した範囲でのメモです。

6-1. GPTQ

  • 4bit / INT8 ともにモデル数は多く、INT8 版は精度保持 95〜98% と優秀という報告が多い
  • 一方で、ROCm 環境では「文字化け」「意味の通らない出力」などの報告もあり、安定性にはやや不安あり
  • 使う場合は AWQ と同様、dtype="float16" を指定
  • 精度最重視なら INT8 GPTQ は候補になる

6-2. BitsAndBytes(BnB)

  • オンザフライ量子化(元の FP16/BF16 モデルをその場で 4bit/8bit 化)
  • QLoRA などのファインチューニングでもお馴染み
  • NF4 などの 4bit 量子化は 98〜99% の精度保持とかなり優秀という報告が多い
  • ただし、速度面では AWQ / 専用 4bit カーネルに劣るという評価がほとんど
  • ROCm には bitsandbytes-rocm のようなフォークも存在します

6-3. TorchAO / FP8 / その他

  • TorchAO: PyTorch 公式の量子化フレームワーク。現時点では対応モデルも少なく、実験用途。
  • FP8: MI300X / H100 など、大規模 GPU サーバ向けの話題が中心。
  • Marlin / BitBLAS: NVIDIA CUDA 前提の高速量子化で、ROCm では使用不可。

まとめ

この記事では、RX7900XTX + WSL2 + ROCm + vLLM という環境で、
量子化モデルを使って 30B クラスのモデルを動かした実験結果をまとめました。

改めてポイントを整理すると:

  • 量子化方式はいろいろあるが、この環境で現実的に使えるのは「AWQ 一択」と言ってよいレベル
    • AWQ 4bit は 精度 95% 前後を保ちつつ、30B モデルでも 200 tok/s 超えで動作
  • GGUF はモデル数こそ多いものの、
    • この環境下では 30B で ≒30 tok/s と実用には厳しい
    • 他のベンチでも、最適条件の 8B GGUF で ≒60 tok/s クラスが現実的な上限
  • dtype 設定は、AWQ / GPTQ は必ず dtype="float16" を指定しておく

ここまで読んでいただきありがとうございました。

量子化に関連して、

  • FP8 KVキャッシュや Prefix Caching などを使って、コンテキスト長をさらに伸ばす話
    はどこかで記事にしたいと思っています。

質問や、「この量子化方式も試してほしい」などあれば、コメントで教えてください。

Discussion