生成AI入門 2026.04.19
量子化とは?AIモデルを1/4サイズにする仕組みとPythonコード【初心者向け】
量子化とは:数値精度を下げてサイズを圧縮する技術
32bit浮動小数点(fp32)の値:
3.14159265358979... ← 4バイト/数値
8bit整数(int8)に量子化後:
127 ← 1バイト/数値(75%削減)
4bit量子化(Q4)後:
8 ← 0.5バイト/数値(87.5%削減)
精度は落ちますが、大半のテキスト生成タスクでは実用上問題ありません。
精度とサイズのトレードオフ
| 精度 | バイト/数値 | 7Bモデルのサイズ | 精度低下 |
|---|---|---|---|
| fp32 | 4 byte | ~28GB | なし(基準) |
| fp16 / bf16 | 2 byte | ~14GB | ほぼなし |
| int8 | 1 byte | ~7GB | 1〜2% |
| Q4 (GGUF) | 0.5 byte | ~4GB | 2〜5% |
| Q2 (GGUF) | 0.25 byte | ~2GB | 5〜15%+ |
Pythonでの量子化の仕組みを理解する
# quantization_demo.py
# 量子化の数学的な仕組みを示すデモ
import numpy as np
def quantize_to_int8(weights: np.ndarray) -> tuple[np.ndarray, float, float]:
"""fp32の重みをint8に量子化する"""
# スケールとゼロ点を計算
w_min, w_max = weights.min(), weights.max()
scale = (w_max - w_min) / 255 # int8は-128〜127(256段階)
zero_point = -round(w_min / scale)
# 量子化
quantized = np.round(weights / scale + zero_point).astype(np.int8)
return quantized, scale, zero_point
def dequantize_from_int8(quantized: np.ndarray, scale: float, zero_point: float) -> np.ndarray:
"""int8から元の値を復元(誤差あり)"""
return (quantized.astype(np.float32) - zero_point) * scale
# 例:fp32の重みをint8に変換
original_weights = np.array([0.1234, -0.5678, 0.9012, -0.3456, 0.7890], dtype=np.float32)
print(f"元の重み: {original_weights}")
print(f"元のサイズ: {original_weights.nbytes} bytes")
quantized, scale, zp = quantize_to_int8(original_weights)
print(f"\n量子化後(int8): {quantized}")
print(f"サイズ: {quantized.nbytes} bytes({quantized.nbytes/original_weights.nbytes:.0%})")
# 復元して誤差を確認
restored = dequantize_from_int8(quantized, scale, zp)
error = np.abs(original_weights - restored)
print(f"\n復元値: {restored}")
print(f"誤差: {error}")
print(f"最大誤差: {error.max():.6f}")
実際のモデルをGGUF Q4に変換する(llama.cpp)
# llama.cppをビルド(Mac M1/M2の場合)
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make -j4 LLAMA_METAL=1 # Metal対応
# Hugging Faceからモデルをダウンロード
pip install huggingface_hub
python3 -c "
from huggingface_hub import snapshot_download
snapshot_download('meta-llama/Llama-3.2-1B-Instruct', local_dir='./llama-1b')
"
# fp16に変換(中間ステップ)
python3 convert_hf_to_gguf.py ./llama-1b \
--outfile ./llama-1b-f16.gguf \
--outtype f16
# Q4_K_M に量子化(推論精度と速度のバランスが良い設定)
./llama-quantize ./llama-1b-f16.gguf ./llama-1b-Q4_K_M.gguf Q4_K_M
# ファイルサイズ確認
ls -lh llama-1b-f16.gguf llama-1b-Q4_K_M.gguf
# -rw-r--r-- 2.5G llama-1b-f16.gguf
# -rw-r--r-- 770M llama-1b-Q4_K_M.gguf ← 70%削減
bitsandbytesで4bit量子化モデルをPythonからロード
# load_quantized.py
# pip install transformers bitsandbytes accelerate torch
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
# 4bit量子化設定
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.bfloat16, # 計算はbf16で高精度に
bnb_4bit_quant_type="nf4", # Normal Float 4(精度が良い)
bnb_4bit_use_double_quant=True, # 二重量子化でさらに圧縮
)
model_id = "meta-llama/Llama-3.2-3B-Instruct"
print("モデルをロード中(4bit量子化)...")
model = AutoModelForCausalLM.from_pretrained(
model_id,
quantization_config=quantization_config,
device_map="auto", # 利用可能なGPUを自動選択
)
tokenizer = AutoTokenizer.from_pretrained(model_id)
# メモリ使用量を確認
if torch.cuda.is_available():
mem_gb = torch.cuda.memory_allocated() / 1024**3
print(f"GPU メモリ使用量: {mem_gb:.1f} GB")
# テキスト生成
inputs = tokenizer("Pythonのデコレータとは何ですか?", return_tensors="pt")
outputs = model.generate(**inputs, max_new_tokens=200)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
Ollamaで量子化モデルを即座に実行
# Ollamaをインストール(最も簡単な方法)
brew install ollama # Mac
# Q4量子化済みのLlama 3.2をダウンロードして実行
ollama pull llama3.2:3b # 約2GB(Q4_K_M)
ollama run llama3.2:3b "量子化について200字で説明して"
# サイズ比較
ollama list
# NAME ID SIZE MODIFIED
# llama3.2:3b ... 2.0 GB ... ← Q4量子化済み
# (fp16なら ~6GB になるところを70%削減)
量子化レベルの選び方
目的: テキスト生成・チャット(精度より速度)
→ Q4_K_M(バランス最良・推奨)
目的: 高品質な文章・分析タスク
→ Q5_K_M または Q6_K
目的: 精度を最大化(VRAMに余裕あり)
→ Q8_0 または fp16
目的: とにかく軽く(2〜4GB GPUしかない)
→ Q2_K(精度低下を許容する場合のみ)
あわせて読みたい
- ローカルLLM運用で失敗しない|VRAM・トークン数の計算方法とPythonスクリプト
- 【2026年版】GoModelとLiteLLM比較|LLMプロキシ設定コードと使い分けガイド
- Claude Code制御ルールが機能しない原因と5つの検証方法