Chuyển đến nội dung chính
友田 陽大
Quantized LLMs & self-hosting
Qwen
AWQ
量子化
vLLM
生成AI
セルフホスト
Python

Qwen3-8B-AWQ 実践ガイド:4bit量子化で“思考するLLM”をGPU1枚にセルフホストする

Qwen3-8B-AWQを公式ドキュメントに忠実に解説。AWQ 4bit量子化で重みを約6GBに圧縮し24GBのGPU1枚で本番運用。ハイブリッド思考(thinking/non-thinking)の切替、vLLMでのOpenAI互換サーブ、モード別の推奨サンプリング、YaRNで131K拡張、ツール呼び出し、量子化特有の落とし穴(presence_penalty/greedy禁止)まで実コードで。

Published
Last updated
Updated
Reading time
16 min read
Author
友田 陽大
Chia sẻ

この記事のゴール

オープンウェイトLLMを本番投入する全体像で「自前運用なら vLLM」「原価を決めるのは量子化と稼働率」と書きました。本稿はその具体例の決定版です。題材は Qwen3-8B-AWQ——アリババ Qwen チームの 8B モデルを AWQ で 4bit に量子化した、Apache-2.0 のオープンウェイトです。

このモデルが面白いのは、**「小さくて安いのに“考える”」**という一点に尽きます。AWQ で重みが約 1/3 に圧縮されるので 24GB の GPU 1枚に載り、しかも 思考(reasoning)モードと高速対話モードを 1 モデルで切り替えられる。つまり「o1 系のような段階推論を、自分のサーバで、安く」が現実になります。

ゴールは、vllm serve を打てることではなく、公式ドキュメントに忠実なまま、どの場面でどう使い、何を外すと品質が崩れるかまで分かった状態になることです。コードはすべて手元で動く形で示します。

信頼性の開示:GPU で LLM を本番運用する難しさ(VRAM・スループット・回復性)は、動画AIローカライズ基盤で実際に踏んだ領域です。本稿のスペック・コマンド・サンプリング値は、すべて公式一次情報——Qwen3-8B-AWQ モデルカードQwen 公式ドキュメントvLLM ドキュメント——に基づきます。VRAM とスループットの実数は環境依存・要ベンチであることを明記します。


30秒の結論:いつ Qwen3-8B-AWQ を選ぶか

Qwen3-8B-AWQ(4bit)Qwen3-8B(FP16/BF16)クローズドAPI
重みの VRAM≈6GB(要実測)≈16GB0(自前不要)
載るGPU24GBクラス1枚(L4 / A10 / RTX 4090 等)24GB〜
思考モードあり(切替可)ありモデル依存
ライセンスApache-2.0(商用可)Apache-2.0利用規約
データ主権自前で完結自前で完結外部送出
運用自前(本記事)自前ほぼゼロ

本質はこの一行:Qwen3-8B-AWQ は「思考できる小型モデルを、データを外に出さず、1枚のGPUで安く回す」ための選択肢です。

  • 向く:オンプレ/VPC内での推論、機微データを外に出せない案件、RAG やエージェントの“推論役”を原価最適化したい、検証〜小規模本番を 1 GPU で回したい。
  • 向かない:とにかく最高品質が要る(より大きいモデルへ)/運用ゼロで始めたい(まずはAPI)。判断軸の全体像はAPI vs セルフホストの損益分岐に揃えてください。

Qwen3-8B-AWQ とは(公式スペックを正確に)

まず公式の事実を、盛らずに置きます(出典:モデルカード)。

項目
総パラメータ8.2B(うち非埋め込み 6.95B)
レイヤー数36
アテンションGQA:Q ヘッド 32 / KV ヘッド 8
ネイティブ文脈長32,768 トークン
拡張文脈長131,072 トークン(YaRN 使用時)
量子化AWQ 4bit
ライセンスApache-2.0
引用Qwen3 Technical Report(arXiv:2505.09388)

公式は、Qwen3 が推論能力で過去の QwQ・Qwen2.5 系を上回り、創作・ロールプレイ・指示追従での人間嗜好との整合性も向上したと述べています(具体スコアは技術報告書を参照)。本稿はベンチ数値の主張ではなく、**「公式仕様どおりに本番で動かす」**ことに集中します。

🔎 派生に注意:Hugging Face には Qwen/Qwen3-8B(フル精度)、Qwen/Qwen3-8B-FP8Qwen/Qwen3-8B-Base、コミュニティの MLX 版などが並びます。本稿が扱うのは公式 Qwen/Qwen3-8B-AWQ(4bit)です。GQA は Q32/KV8——KV ヘッドが少ない設計は、KVキャッシュが軽く、同時実行を載せやすい=セルフホスト向きという含意があります。


AWQ(4bit量子化)とは:なぜ“1枚のGPU”に載るのか

AWQ = Activation-aware Weight Quantization。公式は「ハードウェアに優しい低ビット重み量子化」と説明します。重要なのは activation-aware——「出力に効く重みほど壊さない」ように量子化するため、4bit でも品質劣化を抑えられます。AutoAWQ の実装では FP16 比で約3倍のメモリ削減・約3倍の高速化が示されています(AWQ ドキュメント)。

VRAM の概算で“なぜ1枚に載るか”が腑に落ちます。

FP16 の重み  ≈ 8.2B × 2 byte ≈ 16.4 GB
AWQ 4bit     ≈ 8.2B × 0.5 byte ≈ 4.1 GB(+ スケール/ゼロ点で実測 6GB 前後)

つまり重みは概ね 6GB 前後。残りの VRAM を **KVキャッシュ(同時実行と文脈長を決める)**に回せます。24GB のGPU 1枚なら、重み 6GB + KV 余裕十分、という構図です(実際の同時実行数とスループットは必ず負荷試験で確定してください)。

量子化重みVRAM品質主用途
FP16/BF16≈16GB基準余裕がある時の最高品質
AWQ 4bit≈6GB実用上わずかな劣化1GPUセルフホスト・原価最適化
FP8≈8GBほぼ無劣化対応GPU(H100等)で速度重視

💡 使い分け:H100 のような FP8 対応GPUがあるなら FP8 も有力です(Llama を FP8 でサーブする回参照)。一方、L4 / A10 / RTX 4090 のような 24GB GPU で“とにかく載せて安く回す”なら AWQ 4bit が王道です。AWQ・GPTQ・FP8・GGUF の選び分けは量子化方式の選び方で詳説しています。


キラー機能:ハイブリッド思考(thinking / non-thinking)

Qwen3 最大の差別化が、1つのモデルで「考える」と「即答する」を切り替えられることです。

  • 思考モード(thinking)<think> … </think> の中で段階推論してから答える。数学・コード・複雑な判断に強い。
  • 非思考モード(non-thinking):推論ブロックを出さず即答。分類・抽出・定型対話など速度と原価が効く用途向け。

切り替えは2通り(公式仕様)

  1. ハードスイッチapply_chat_template(..., enable_thinking=True/False)(API では chat_template_kwargs)。
  2. ソフトスイッチenable_thinking=True の状態で、ユーザー発話に /think / /no_think を書くとターン単位で上書きできる。

モード別の推奨サンプリング(外すと壊れる)

公式がモードごとに別のサンプリングを指定している点が要注意です。

パラメータ思考モード非思考モード
Temperature0.60.7
TopP0.950.8
TopK2020
MinP00
greedy decoding禁止

⚠️ 公式の明確な警告:思考モードで greedy decoding(temperature=0 相当)を使ってはいけません。無限ループ・繰り返し・性能劣化を招きます。「reasoning は決定的に出したいから temp=0」は逆効果です。

出力長の目安も公式にあります:ほとんどのクエリは 32,768 トークン、数学やプログラミングのような複雑問題は 38,912 トークンまで見込む。思考モードは答えの前に長く“考える”ため、max_tokens をケチると結論に到達する前に切れます


動かす①:transformers で最短確認(開発用)

まずはローカルで最小確認。AWQ 重みの実行には awq カーネルが要るので、autoawq を入れます(本番は後述の vLLM 推奨)。

# pip install -U transformers accelerate autoawq
from transformers import AutoModelForCausalLM, AutoTokenizer

model_name = "Qwen/Qwen3-8B-AWQ"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype="auto", device_map="auto")

messages = [{"role": "user", "content": "9.11 と 9.9 はどちらが大きい?理由も。"}]

# enable_thinking=True で <think>...</think> による段階推論を有効化(ハードスイッチ)
text = tokenizer.apply_chat_template(
    messages, tokenize=False, add_generation_prompt=True, enable_thinking=True,
)
inputs = tokenizer(text, return_tensors="pt").to(model.device)

# 思考モードの公式推奨サンプリング(greedy は使わない)
generated = model.generate(
    **inputs, max_new_tokens=32768,
    temperature=0.6, top_p=0.95, top_k=20,
)
output_ids = generated[0][len(inputs.input_ids[0]):].tolist()
print(tokenizer.decode(output_ids, skip_special_tokens=True))

開発の単機・お試しはこれで十分。ただし transformersgenerate連続バッチを持たないため、本番の同時多数リクエストには向きません。そこで vLLM に移ります。


動かす②:vLLM で本番サーブ(OpenAI互換)

本番は vLLM。連続バッチと PagedAttention で GPU を遊ばせず、OpenAI 互換エンドポイントを出すので、アプリ側のコードを変えずに向け先だけ差し替えられます(基盤づくりの全体はvLLM 本番セルフホスト運用記に集約)。

# Qwen3-8B-AWQ を OpenAI 互換でサーブ。思考の分離パースを有効化
vllm serve Qwen/Qwen3-8B-AWQ \
  --reasoning-parser qwen3 \
  --max-model-len 32768 \
  --gpu-memory-utilization 0.90 \
  --port 8000
  • --reasoning-parser qwen3vLLM 0.9.0 以降の指定。<think> ブロックを本文と分離し、レスポンスに reasoning_content フィールドを足してくれる。古い vLLM では --enable-reasoning --reasoning-parser deepseek_r1 を使います。
  • --max-model-len 32768:ネイティブ上限。実際に必要な長さに絞るのが原価とレイテンシの鉄則(青天井にしない)。
  • --gpu-memory-utilization 0.90:確保率。上げすぎ=OOM、低すぎ=同時実行が減る。

立ち上がれば、OpenAI クライアントでそのまま叩けます。思考のオン/オフは extra_body.chat_template_kwargs.enable_thinking で制御します。

from openai import OpenAI
client = OpenAI(api_key="EMPTY", base_url="http://localhost:8000/v1")

resp = client.chat.completions.create(
    model="Qwen/Qwen3-8B-AWQ",
    messages=[{"role": "user", "content": "在庫が負になりうる箇所をこのSQLから指摘して"}],
    temperature=0.6, top_p=0.95, extra_body={
        "top_k": 20,
        "chat_template_kwargs": {"enable_thinking": True},  # 思考モード
        # 量子化モデルで“終わらない繰り返し”が出たら presence_penalty を 0〜2 で(推奨 1.5)
        "presence_penalty": 1.5,
    },
)
msg = resp.choices[0].message
print("思考:", getattr(msg, "reasoning_content", None))  # <think> の中身(ログ/監査用)
print("回答:", msg.content)                               # ユーザーに返す最終回答

reasoning_content(思考過程)と content(最終回答)が構造的に分かれるのが本番で効きます。ユーザーには content だけ返し、reasoning_content は監査・評価用に内部で扱う——この分離が後述の型安全・可観測性の土台になります。


長文脈:YaRN で 32K → 131K(必要な時だけ)

ネイティブは 32,768。それ以上の文脈が要るときだけ YaRN で拡張します。CLI でも宣言できます。

vllm serve Qwen/Qwen3-8B-AWQ \
  --reasoning-parser qwen3 \
  --rope-scaling '{"rope_type":"yarn","factor":4.0,"original_max_position_embeddings":32768}' \
  --max-model-len 131072

config.json に同等を書く形(factor:4.0, original_max_position_embeddings:32768)も公式に示されています。

⚠️ 常時 YaRN を有効化しない。公式は「必要な時だけ」と明言しています。短い入力にまで長文脈スケーリングを掛けると、短文の品質が落ちる副作用があります。32K で足りるなら YaRN は外す——「念のため最大」は逆効果です。長文脈ほど KVキャッシュが膨らみ VRAM とレイテンシも増えるので、max-model-len実需に合わせて設定してください。


ツール呼び出し / エージェント

Qwen3 は関数呼び出し(tool use)に対応します。vLLM ではフラグで有効化します。

vllm serve Qwen/Qwen3-8B-AWQ \
  --reasoning-parser qwen3 \
  --enable-auto-tool-choice \
  --tool-call-parser hermes

あとは OpenAI 互換の tools を渡せば、モデルが必要に応じて関数呼び出しを返します。エージェント的なツール連携を組むなら、公式の Qwen-Agent(ツール定義とパースのテンプレートが揃う)を使うと、tool-call の取り回しを自前で書かずに済みます。引数のZod検証・反復上限・冪等な副作用まで含む安全なループの実装はQwen3 のエージェント化で具体化します。設計の勘所——「LLMに任せる判断決定的コードに寄せる実行を分ける」——はツール使用・関数呼び出しの設計に揃えてください。


どの場面で使うか:型安全な本番クライアント(応用)

ここからが応用です。Qwen3-8B-AWQ の強みは「思考できる」「安い」「自前」。これを型安全・冪等・回復性で包むと、本番で稼げるコンポーネントになります。代表的な応用は、機微データを外に出さない自前RAGです。

設計方針(CLAUDE.md の原則どおり、SRP / KISS / 型安全境界を効かせます):

  1. モードを難度でルーティング——簡単なタスクは非思考(速い・安い)、難所だけ思考(原価最適化の本丸)。
  2. 思考過程と回答を分離——reasoning_content はログ、content だけ検証して返す。
  3. 構造化出力は境界で Zod 検証——LLM 出力は外部入力。parse で不正を弾く(型安全の規律)。生成段階で不正JSONを作らせない guided decoding との二段構えは型安全な構造化出力へ。
  4. タイムアウト+冪等キャッシュ——同じ入力を二度生成しない=原価も安定。
// lib/qwen-client.ts — 思考/非思考をルーティングし、出力を境界で型検証する薄いクライアント
import OpenAI from "openai";
import { z } from "zod";
import { createHash } from "node:crypto";

const client = new OpenAI({
  baseURL: process.env.QWEN_BASE_URL, // 例: http://qwen-internal:8000/v1(privateで公開しない)
  apiKey: "internal",
  timeout: 60_000, // 思考モードは長い。短すぎる timeout は“正常な熟考”を切る
});

/** タスク難度 → モードと公式推奨サンプリングを決める(SSoT) */
const MODE = {
  fast: { enable_thinking: false, temperature: 0.7, top_p: 0.8, top_k: 20 },
  think: { enable_thinking: true, temperature: 0.6, top_p: 0.95, top_k: 20 },
} as const;
type Difficulty = keyof typeof MODE;

/** 期待する構造化出力。LLMの“それっぽい文字列”を信用せず、ここで弾く */
const RiskFinding = z.object({
  hasRisk: z.boolean(),
  severity: z.enum(["low", "medium", "high"]),
  reason: z.string().min(1),
});
type RiskFinding = z.infer<typeof RiskFinding>;

const cache = new Map<string, RiskFinding>(); // 実運用は Redis 等に置換

/** 入力が同じなら生成も同じ(冪等)。連打・リトライ・重複依頼でコストを無駄にしない */
const keyOf = (prompt: string, d: Difficulty) =>
  createHash("sha256").update(`qwen3-8b-awq:${d}:${prompt}`).digest("hex");

export async function assessRisk(prompt: string, difficulty: Difficulty = "think"): Promise<RiskFinding> {
  const key = keyOf(prompt, difficulty);
  const hit = cache.get(key);
  if (hit) return hit;

  const m = MODE[difficulty];
  const resp = await client.chat.completions.create({
    model: "Qwen/Qwen3-8B-AWQ",
    messages: [
      { role: "system", content: 'JSONのみで返答: {"hasRisk":bool,"severity":"low|medium|high","reason":string}' },
      { role: "user", content: prompt },
    ],
    temperature: m.temperature,
    top_p: m.top_p,
    response_format: { type: "json_object" },
    presence_penalty: 1.5, // 量子化モデルの繰り返し対策(公式推奨レンジ 0〜2・OpenAI互換)
    // vLLM拡張はトップレベルで送る。Node SDKは未知キーも本文へ転送し、spreadなので型エラーも出ない。
    // ※ Python SDK の extra_body は TS SDK には存在しないので使わない。
    ...{ top_k: m.top_k, chat_template_kwargs: { enable_thinking: m.enable_thinking } },
  });

  // content だけを検証対象に。reasoning_content(思考過程)は監査ログへ(PIIは載せない)
  const raw = resp.choices[0]?.message.content ?? "{}";
  const finding = RiskFinding.parse(JSON.parse(raw)); // 不正な形なら throw → 上位で握る

  cache.set(key, finding);
  return finding;
}

このクライアントは、“思考するLLM”の出力を決して鵜呑みにしない点が肝です。reasoning_content は人間の監査と評価のために残し、ユーザーに返すのは Zod を通った型だけ。さらに enable_thinking をタスク難度で出し入れすることで、簡単8割を非思考で安く・難所2割だけ思考で正確に——という原価設計が、1つのモデルで成立します。


本番の作り込み(重複は避け、要点だけ)

可観測性・オートスケール・グレースフルドレイン・回復性・ネットワーク隔離は、モデルが Qwen でも Llama でも同じ作法です。詳細は重複させず、vLLM 本番セルフホスト運用記に委ねます。Qwen3-8B-AWQ で特に効くポイントだけ:

  • 可観測性:vLLM の /metrics(Prometheus)で num_requests_waiting / gpu_cache_usage_perc / TTFT を監視。思考モードは出力が長くなりがちなので、reasoning のトークン消費を用途別に可視化すると原価の当たりがつきます(OpenTelemetry の相関設計)。
  • 回復性:自前ノードの障害を全体障害にしない。タイムアウト+リトライ+フォールバック。8B が落ちたら別ノード/上位モデルへ逃がす(リトライ・サーキットブレーカー)。
  • セキュリティ:vLLM の OpenAI 互換サーバはそれ自体に強い認証がないインターネットに直接晒さない。private VPC に置き、前段の API Gateway で認証・レート制限・監査。プロンプト本文(PII)はログに残さない——そもそも「外に出さない」がセルフホストの動機です。

ハマりどころ & 公式ベストプラクティス

公式ドキュメントから、外すと品質が崩れるものを抜き出します。

  • 🔴 思考モードで greedy decoding 禁止。temp=0 にしない。繰り返し・無限ループの原因。
  • 🟠 量子化モデルの繰り返し対策に presence_penalty ≈ 1.5(レンジ 0〜2)。ただし上げすぎると稀に言語混在やわずかな性能低下があるため、出たら効かせる程度に。
  • 🟠 マルチターンで履歴に思考内容(<think>)を残さない。次ターンの入力には最終回答だけを積む。思考を積み続けると文脈が汚れ、品質・原価とも悪化。
  • 🟠 出力長を絞りすぎない。思考は答えの前に長い。通常 32,768 / 複雑問題 38,912 トークンを目安に max_tokens を確保。
  • 🟢 数学は出力フォーマットを指定:「Please reason step by step, and put your final answer within \boxed{}.」で最終解が機械抽出しやすくなる。
  • 🟢 YaRN は必要な時だけ。常時有効化は短文の品質を落とす。
  • 🟢 AWQ は transformers だと awq カーネル依存。本番スループットは vLLM(連続バッチ)に寄せる。

まとめ

Qwen3-8B-AWQ は、「思考できる小型モデルを、データを外に出さず、1枚のGPUで安く回す」という、これまで両立しにくかった要件を素直に満たします。

  1. AWQ 4bit で重み ≈6GB → 24GB GPU 1枚に載る(Apache-2.0・商用可)。
  2. ハイブリッド思考を難度でルーティング——簡単は非思考で安く、難所は思考で正確に。
  3. vLLM で OpenAI 互換サーブ--reasoning-parser qwen3)、reasoning_content で思考と回答を分離。
  4. 公式サンプリングを厳守(思考: 0.6/0.95/20、greedy禁止、presence_penalty≈1.5、履歴に思考を残さない)。
  5. 出力は境界で型検証(Zod)+冪等キャッシュ。本番の作法はvLLM 運用記に集約。

オンプレ/VPC内で「思考するLLM」を、可観測・回復性・型安全まで含めて構築します。GPU 本番運用の実績をご覧のうえ、モデル選定・原価設計・セルフホスト移行までご相談ください。一人 × 生成AIで、速く・安く・安全に。

出典・公式リソース

※ スペック・フラグ・サンプリング推奨は更新されます。VRAM とスループットは環境依存・要ベンチです。実装前に一次情報と自社ベンチで必ず確認してください。

Câu hỏi thường gặp

Qwen3-8B-AWQ と FP8 版、どちらを使う?
24GB級の汎用GPU(L4/A10/RTX 4090)で“載せて安く回す”なら AWQ 4bit。H100 など FP8 対応GPUで速度最優先なら FP8。重みは AWQ≈6GB / FP8≈8GB / FP16≈16GB が目安です(要実測)。
“思考”は常にオンにすべき?
いいえ。分類・抽出・定型対話は非思考(速い・安い)で十分。数学・コード・複雑な判断だけ思考に。タスク難度でルーティングするのが原価設計の本丸です(本文のクライアント例)。
greedy(temp=0)で再現性を出したいのですが?
思考モードでは公式が禁止しています。決定的な出力が要るなら、reasoning ではなく最終回答のスキーマ(Zod)と冪等キャッシュで再現性を担保してください。サンプリング自体を殺すのは逆効果です。
何 GPU 要りますか?
まず 1 枚。重み≈6GB なので 24GB 1枚で動きます。同時実行とスループットは KVキャッシュ次第なので、負荷試験で飽和点を測り、そこから台数を逆算します。
Ollama でも動く?
ローカル開発の単機お試しには手軽です。ただし本番の高スループット(連続バッチ)は vLLM。開発は手軽な方、本番は vLLM、と使い分けます。
ライセンスは商用利用できる?
Apache-2.0 です。重みを所有し、改造し、自社環境で動かせます。Llama の「700M MAU 制限/Built with Llama 表記」のような制約は無く、ライセンス面はよりシンプルです。
友田

友田 陽大

Người phát triển sản phẩm đoạt Giải thưởng Bộ trưởng (METI). Với TypeScript + Python + AWS, tôi một mình triển khai trọn gói SaaS, chuyển đổi số công nghiệp và AI tạo sinh (RAG) sẵn sàng cho production — từ phân tích yêu cầu đến hạ tầng và vận hành.

I can take on the implementation from this article as an engagement

Self-hosting infrastructure for open-weight LLMs — build, production operations, and cost optimization

I've run Qwen3-8B-AWQ and 4-bit quantized Llama 3 in production on vLLM, operated as a reproducible inference stack on spot GPUs (Tesla T4) with Terraform. Quantization choice, VRAM and throughput sizing, swappable models behind a provider abstraction, and resumption after forced preemption — I design and implement inference infrastructure that stays up.

Nhận cả dự án trọn gói lẫn vai trò cố vấn kỹ thuật. Hãy bắt đầu bằng buổi tư vấn miễn phí 30 phút.

最短ルート:カレンダーから直接予約

相談内容が固まっている方は、フォーム送信よりその場で日程を確定する方がスムーズです。下記から空き時間をお選びください。

  • 30分のオンライン無料相談
  • Google Meet / Zoom / Microsoft Teams
  • NDA 商談前締結可・無理な営業はいたしません
無料相談の空き枠を予約する

Cũng đáng đọc