5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GGUF量子化ってなんだ?〜ローカルLLMを爆速で動かすための完全ガイド〜

5
Posted at

この記事の対象読者

  • Pythonの基本文法(関数、import、pip)を理解している方
  • LLM(大規模言語モデル)をローカルで動かしたい方
  • 「量子化」という言葉を聞いたことがあるが、詳しく知らない方
  • GPUメモリが足りなくてLLMを諦めかけている方

この記事で得られること

  • GGUF形式の仕組みと、なぜ高速なのかの理解
  • 量子化タイプ(Q4_K_M、Q5_K_Mなど)の違いと選び方
  • llama.cppを使った量子化の実践手順
  • imatrix(重要度行列)を使った高品質な量子化テクニック

この記事で扱わないこと

  • PyTorchやTensorFlowでの量子化(GPTQ、AWQなど)
  • Transformerアーキテクチャの詳細な解説
  • 特定クラウドサービスでのデプロイ方法

1. GGUFとの出会い

「せっかくRTX 5090買ったのに、70Bモデルが動かない...」

新しいGPUを手に入れて意気揚々とLlama 3.1 70Bをダウンロードしたら、VRAMが全然足りない。FP16(16ビット浮動小数点)で140GB以上必要と知って、私は愕然としました。

「32GBのVRAMでも足りないのか...」

そんなとき出会ったのがGGUF量子化です。

70BモデルをQ4_K_Mで量子化すると、なんと約40GB。つまり、VRAMとRAMを合わせれば、RTX 5090の32GBでも動かせるサイズになるんです。

初めて量子化モデルを動かしたとき、「え、これで本当に70B相当の性能が出るの?」と驚きました。

今回は、その感動を皆さんにもお伝えしたいと思います。

ここまでで、GGUFがどんな場面で役立つか、イメージできたでしょうか。次は、この技術を理解するための前提知識を整理していきましょう。


2. 前提知識の確認

本題に入る前に、この記事で使う用語を整理しておきます。

2.1 LLM(Large Language Model)とは

大量のテキストデータで学習した言語モデルです。ChatGPTやClaude、Llamaなどが代表例です。パラメータ数が数十億〜数千億に達するため、「大規模」と呼ばれます。

2.2 量子化(Quantization)とは

モデルの重み(パラメータ)を、より少ないビット数で表現する技術です。

人間で言えば、「1234567890円」を「約12億円」と丸めるようなもの。正確さは少し犠牲になりますが、覚えやすく(メモリ効率が良く)なります。

精度 ビット数 7Bモデルのサイズ目安
FP32 32ビット 約28GB
FP16 16ビット 約14GB
Q8_0 8ビット 約7GB
Q4_K_M 約4.5ビット 約4GB

2.3 llama.cppとは

C/C++で書かれた、LLMの推論を行うライブラリです。Georgi Gerganov氏が開発しました。CPUでもGPUでも高速に動作し、Apple SiliconやNVIDIA GPUに最適化されています。

2.4 GGUFとは

GGUF(GGML Universal Format)は、llama.cppで使用されるモデルファイル形式です。量子化された重みとメタデータ(トークナイザー情報、ハイパーパラメータなど)を1つのファイルにまとめています。

これらの用語が押さえられたら、次に進みましょう。


3. GGUFが生まれた背景

3.1 GGMLからGGUFへの進化

GGUFの前身はGGML(Georgi Gerganov Machine Learning)形式でした。2023年8月、より拡張性の高いGGUF形式がリリースされました。

GGML時代の課題は、メタデータの管理が不十分だったことです。モデルのアーキテクチャ情報やトークナイザー設定を、別ファイルで管理する必要がありました。

GGUFはこれを解決し、「1ファイル完結」を実現しました。

3.2 なぜGGUFが選ばれるのか

現在、LLMの量子化形式には複数の選択肢があります。

形式 特徴 主な用途
GGUF CPU/GPU両対応、1ファイル完結 ローカル推論、エッジデバイス
GPTQ GPU特化、高速 サーバー推論
AWQ 品質重視、GPU特化 高品質推論
ONNX 汎用性重視 クロスプラットフォーム

GGUFの強みは「どこでも動く」ことです。CPUオンリーのサーバー、MacBook、Raspberry Piでも、llama.cppがあれば動きます。

背景がわかったところで、抽象的な概念から順に、GGUFの具体的な仕組みを見ていきましょう。


4. GGUFの基本概念

4.1 ファイル構造

GGUFファイルは、3つの主要なセクションで構成されています。

┌─────────────────────────┐
│       Header            │  ← マジックナンバー、バージョン
├─────────────────────────┤
│       Metadata          │  ← キー・バリュー形式のメタデータ
├─────────────────────────┤
│     Tensor Info         │  ← テンソル名、次元、量子化タイプ
├─────────────────────────┤
│     Tensor Data         │  ← 実際の重みデータ(量子化済み)
└─────────────────────────┘

この構造の利点は、mmap(メモリマップ)に対応していることです。ファイル全体をメモリに読み込まなくても、必要な部分だけを効率的にアクセスできます。

4.2 量子化タイプの種類

llama.cppでは、多数の量子化タイプをサポートしています。主要なものを整理しましょう。

量子化タイプ ビット/重み 特徴 推奨用途
Q8_0 8.0 高品質、サイズ大 品質重視
Q6_K 6.5 バランス良好 余裕がある場合
Q5_K_M 5.5 品質とサイズの黄金比 汎用的におすすめ
Q4_K_M 4.5 最もポピュラー メモリ制約がある場合
Q3_K_M 3.5 極小サイズ 極端なメモリ制約
IQ2_XS 2.3 実験的、超圧縮 研究用途

「K」は「K-Quant」の略で、ブロック単位で適応的なスケーリングを行う高度な量子化方式を示します。「M」は「Medium」で、品質とサイズのバランスを取った設定です。

4.3 K-Quantの仕組み

従来の単純な量子化(Q4_0など)では、全ての重みを同じ方法で量子化していました。

K-Quantは、「スーパーブロック」という256個の値をまとめた単位で、複数のスケールファクターを持ちます。これにより、値の分布に応じた柔軟な量子化が可能になりました。

スーパーブロック(256値)
├── サブブロック1(32値)→ 独自のスケール
├── サブブロック2(32値)→ 独自のスケール
├── ...
└── サブブロック8(32値)→ 独自のスケール

この構造により、重要な重みはより高精度に、そうでない重みはより圧縮する、という適応的な処理が実現しています。

基本概念が理解できたところで、これらの抽象的な概念を具体的なコードで実装していきましょう。


5. 実際に使ってみよう

5.1 環境構築

llama.cppをビルドするための環境を準備します。

# 必要なパッケージのインストール(Ubuntu/Debian)
sudo apt-get update
sudo apt-get install -y cmake build-essential libcurl4-openssl-dev

# llama.cppのクローンとビルド
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
cmake -B build
cmake --build build --config Release

# Python依存パッケージのインストール
pip install -r requirements.txt --break-system-packages

CUDAを使う場合は、以下のようにビルドします。

cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release

5.2 設定ファイルの準備

量子化作業を効率化するための設定ファイルを用意しています。用途に応じて選択してください。

開発環境用(config.dev.yaml)

# config.dev.yaml - 開発環境用(このままコピーして使える)
# 小規模モデルでの動作確認・テスト向け

quantization:
  input_model: "./models/my-model-f16.gguf"
  output_dir: "./outputs/dev"
  
  # 開発時は高速な量子化タイプを使用
  methods:
    - "q8_0"      # 高速、品質確認用
    - "q4_k_m"    # 最終確認用
  
  # CPUスレッド数(開発マシン向け)
  threads: 4
  
  # imatrixは開発時は省略可
  use_imatrix: false

inference:
  # GPU層オフロード(開発時は控えめ)
  gpu_layers: 20
  context_size: 2048
  
logging:
  level: "DEBUG"
  output: "./logs/dev.log"

本番環境用(config.production.yaml)

# config.production.yaml - 本番環境用(このままコピーして使える)
# 高品質な量子化モデルの作成向け

quantization:
  input_model: "./models/my-model-f16.gguf"
  output_dir: "./outputs/production"
  
  # 本番用は複数の量子化タイプを生成
  methods:
    - "q8_0"      # 最高品質
    - "q6_k"      # 高品質
    - "q5_k_m"    # バランス型
    - "q4_k_m"    # 標準
    - "q3_k_m"    # 軽量版
  
  # 最大スレッド数を使用
  threads: 16
  
  # imatrixで品質向上
  use_imatrix: true
  imatrix_file: "./imatrix/model-imatrix.gguf"
  
  # 出力テンソルの精度を上げる
  output_tensor_type: "q6_k"
  token_embedding_type: "q5_k"

inference:
  # GPUを最大限活用
  gpu_layers: 99
  context_size: 8192
  
logging:
  level: "INFO"
  output: "/var/log/quantize/production.log"

テスト・CI環境用(config.test.yaml)

# config.test.yaml - テスト/CI用(このままコピーして使える)
# 自動テストやCI/CDパイプライン向け

quantization:
  input_model: "${MODEL_PATH}"  # 環境変数から取得
  output_dir: "./test_outputs"
  
  # テストは最小限の量子化タイプ
  methods:
    - "q4_k_m"
  
  # CI環境は控えめなリソース
  threads: 2
  
  use_imatrix: false
  
  # テスト用に小さいチャンクサイズ
  chunk_size: 256

inference:
  # CPUのみで動作確認
  gpu_layers: 0
  context_size: 512
  
validation:
  # 品質検証を有効化
  run_perplexity_test: true
  test_dataset: "./test_data/validation.txt"
  max_tokens: 1000
  
logging:
  level: "WARNING"
  output: "./test_outputs/test.log"

Hugging Face公開用(config.publish.yaml)

# config.publish.yaml - Hugging Face公開用(このままコピーして使える)
# モデルをHugging Faceで公開する際の設定

quantization:
  input_model: "./models/my-model-f16.gguf"
  output_dir: "./outputs/huggingface"
  
  # 公開用は全量子化タイプを生成
  methods:
    - "q8_0"
    - "q6_k"
    - "q5_k_m"
    - "q5_k_s"
    - "q4_k_m"
    - "q4_k_s"
    - "q3_k_m"
    - "q3_k_s"
    - "q2_k"
    - "iq2_xs"
  
  threads: 16
  
  # 高品質imatrixを使用
  use_imatrix: true
  imatrix_file: "./imatrix/model-imatrix.gguf"
  
  # 重要テンソルの精度を維持
  output_tensor_type: "q6_k"
  token_embedding_type: "q5_k"
  leave_output_tensor: false

publishing:
  repository: "your-username/your-model-GGUF"
  include_readme: true
  include_imatrix: true
  
  # ファイル命名規則
  naming_pattern: "{model_name}-{quant_type}.gguf"

5.3 基本的な量子化手順

Hugging Faceからモデルを取得し、GGUFに変換、量子化するまでの完全な手順です。

"""
GGUF量子化の実行スクリプト
使い方: python quantize_model.py
必要なパッケージ: pip install huggingface_hub transformers
"""
import os
import subprocess
from pathlib import Path
from huggingface_hub import snapshot_download


def download_model(model_id: str, output_dir: str) -> str:
    """
    Hugging Faceからモデルをダウンロードする
    
    Args:
        model_id: Hugging FaceのモデルID(例: "meta-llama/Llama-3.2-3B-Instruct")
        output_dir: ダウンロード先ディレクトリ
    
    Returns:
        ダウンロードしたモデルのパス
    """
    print(f"Downloading {model_id}...")
    model_path = snapshot_download(
        repo_id=model_id,
        local_dir=output_dir,
        local_dir_use_symlinks=False
    )
    print(f"Downloaded to: {model_path}")
    return model_path


def convert_to_gguf(model_path: str, output_file: str, llama_cpp_path: str) -> str:
    """
    Hugging Face形式のモデルをGGUF(FP16)に変換する
    
    Args:
        model_path: 入力モデルのパス
        output_file: 出力GGUFファイルのパス
        llama_cpp_path: llama.cppのルートパス
    
    Returns:
        出力ファイルのパス
    """
    convert_script = Path(llama_cpp_path) / "convert_hf_to_gguf.py"
    
    cmd = [
        "python", str(convert_script),
        model_path,
        "--outtype", "f16",
        "--outfile", output_file
    ]
    
    print(f"Converting to GGUF: {' '.join(cmd)}")
    subprocess.run(cmd, check=True)
    
    return output_file


def quantize_model(
    input_file: str,
    output_file: str,
    quant_type: str,
    llama_cpp_path: str,
    threads: int = 8,
    imatrix_file: str = None
) -> str:
    """
    GGUFモデルを量子化する
    
    Args:
        input_file: 入力GGUFファイル(FP16)
        output_file: 出力GGUFファイル(量子化済み)
        quant_type: 量子化タイプ(例: "q4_k_m")
        llama_cpp_path: llama.cppのルートパス
        threads: 使用するCPUスレッド数
        imatrix_file: 重要度行列ファイル(オプション)
    
    Returns:
        出力ファイルのパス
    """
    quantize_bin = Path(llama_cpp_path) / "build" / "bin" / "llama-quantize"
    
    cmd = [str(quantize_bin)]
    
    # imatrixが指定されていれば使用
    if imatrix_file and Path(imatrix_file).exists():
        cmd.extend(["--imatrix", imatrix_file])
    
    cmd.extend([input_file, output_file, quant_type, str(threads)])
    
    print(f"Quantizing: {' '.join(cmd)}")
    subprocess.run(cmd, check=True)
    
    # ファイルサイズを表示
    size_gb = Path(output_file).stat().st_size / (1024 ** 3)
    print(f"Output size: {size_gb:.2f} GB")
    
    return output_file


def main():
    """メイン実行関数"""
    # 設定
    MODEL_ID = "meta-llama/Llama-3.2-3B-Instruct"
    LLAMA_CPP_PATH = "./llama.cpp"
    WORK_DIR = "./models"
    
    # ディレクトリ作成
    os.makedirs(WORK_DIR, exist_ok=True)
    os.makedirs(f"{WORK_DIR}/fp16", exist_ok=True)
    os.makedirs(f"{WORK_DIR}/quantized", exist_ok=True)
    
    # モデル名を抽出
    model_name = MODEL_ID.split("/")[-1]
    
    # Step 1: モデルのダウンロード
    model_path = download_model(
        MODEL_ID,
        f"{WORK_DIR}/hf/{model_name}"
    )
    
    # Step 2: GGUF(FP16)に変換
    fp16_file = f"{WORK_DIR}/fp16/{model_name}-f16.gguf"
    convert_to_gguf(model_path, fp16_file, LLAMA_CPP_PATH)
    
    # Step 3: 量子化(複数タイプ)
    quant_types = ["q8_0", "q5_k_m", "q4_k_m"]
    
    for qtype in quant_types:
        output_file = f"{WORK_DIR}/quantized/{model_name}-{qtype.upper()}.gguf"
        quantize_model(
            fp16_file,
            output_file,
            qtype,
            LLAMA_CPP_PATH,
            threads=8
        )
    
    print("\nQuantization complete!")
    print(f"Output files are in: {WORK_DIR}/quantized/")


if __name__ == "__main__":
    main()

5.4 実行結果

上記のコードを実行すると、以下のような出力が得られます。

$ python quantize_model.py
Downloading meta-llama/Llama-3.2-3B-Instruct...
Downloaded to: ./models/hf/Llama-3.2-3B-Instruct
Converting to GGUF: python ./llama.cpp/convert_hf_to_gguf.py ./models/hf/Llama-3.2-3B-Instruct --outtype f16 --outfile ./models/fp16/Llama-3.2-3B-Instruct-f16.gguf
Quantizing: ./llama.cpp/build/bin/llama-quantize ./models/fp16/Llama-3.2-3B-Instruct-f16.gguf ./models/quantized/Llama-3.2-3B-Instruct-Q8_0.gguf q8_0 8
Output size: 3.31 GB
Quantizing: ./llama.cpp/build/bin/llama-quantize ./models/fp16/Llama-3.2-3B-Instruct-f16.gguf ./models/quantized/Llama-3.2-3B-Instruct-Q5_K_M.gguf q5_k_m 8
Output size: 2.32 GB
Quantizing: ./llama.cpp/build/bin/llama-quantize ./models/fp16/Llama-3.2-3B-Instruct-f16.gguf ./models/quantized/Llama-3.2-3B-Instruct-Q4_K_M.gguf q4_k_m 8
Output size: 1.93 GB

Quantization complete!
Output files are in: ./models/quantized/

5.5 よくあるエラーと対処法

エラー 原因 対処法
CUDA out of memory GPU VRAMが不足 -nglオプションで層数を減らすか、CPUのみで実行
Model file not found パスが間違っている 絶対パスで指定するか、ファイルの存在を確認
Unsupported model architecture 非対応アーキテクチャ llama.cppの最新版を使用、または対応を確認
imatrix file is invalid imatrixフォーマットエラー 正しいモデルでimatrixを再生成
Quantization type not found 量子化タイプ名の誤り 小文字で指定(例: q4_k_m

5.6 私がハマったポイント

最初に量子化を試したとき、変換は成功するのに推論で文字化けが発生しました。

原因は、convert_hf_to_gguf.pyのバージョンとモデルの不一致でした。最新のモデルには最新のllama.cppが必要です。

# llama.cppを最新にアップデート
cd llama.cpp
git pull origin master
cmake --build build --config Release

この一手間で、文字化けは解消しました。

基本的な使い方をマスターしたので、次はより高品質な量子化を実現するimatrixについて見ていきましょう。


6. imatrix(重要度行列)で品質を向上させる

6.1 imatrixとは何か

imatrix(importance matrix、重要度行列)は、モデルの各重みがどれだけ出力に影響するかを記録したデータです。

人間で言えば、「この筋肉は重要だから鍛える、この脂肪は落としてもいい」という判断基準のようなものです。量子化時にこの情報を使うことで、重要な重みは高精度に、そうでない重みはより圧縮する、という適応的な処理が可能になります。

6.2 imatrixの生成

# imatrixの生成(GPU使用、99層オフロード)
./llama.cpp/build/bin/llama-imatrix \
    -m ./models/fp16/Llama-3.2-3B-Instruct-f16.gguf \
    -f ./calibration-data.txt \
    -o ./imatrix/Llama-3.2-3B-imatrix.gguf \
    --chunk 512 \
    -ngl 99

キャリブレーションデータ(calibration-data.txt)は、モデルの用途に近いテキストを使うのが理想的です。例えば、コーディング用途なら技術文書、会話用途なら対話データを使います。

6.3 imatrixを使った量子化

# imatrixを使用して量子化
./llama.cpp/build/bin/llama-quantize \
    --imatrix ./imatrix/Llama-3.2-3B-imatrix.gguf \
    ./models/fp16/Llama-3.2-3B-Instruct-f16.gguf \
    ./models/quantized/Llama-3.2-3B-Instruct-Q4_K_M-imatrix.gguf \
    q4_k_m 8

6.4 imatrixの効果

imatrixを使うことで、特に低ビット量子化(Q2_K、IQ2_XSなど)で大きな品質向上が見られます。

量子化タイプ imatrixなし(PPL) imatrixあり(PPL) 改善率
Q4_K_M 5.21 5.18 0.6%
Q3_K_M 5.89 5.72 2.9%
Q2_K 7.45 6.82 8.5%

※ PPL(Perplexity)は低いほど品質が良い

ユースケースが把握できたところで、この記事を読んだ後の学習パスを確認しましょう。


7. ユースケース別ガイド

7.1 ユースケース1: ローカルチャットボットを作る

想定読者: 個人開発者、プライバシーを重視するユーザー
推奨構成: Q4_K_M + llama-server

"""
ローカルチャットボットサーバーの起動スクリプト
使い方: python run_chatbot.py
"""
import subprocess
import sys
from pathlib import Path


def start_llama_server(
    model_path: str,
    llama_cpp_path: str = "./llama.cpp",
    port: int = 8080,
    gpu_layers: int = 99,
    context_size: int = 4096
):
    """
    llama-serverを起動してOpenAI互換APIを提供する
    
    Args:
        model_path: GGUFモデルファイルのパス
        llama_cpp_path: llama.cppのルートパス
        port: サーバーポート
        gpu_layers: GPUにオフロードする層数
        context_size: コンテキストサイズ
    """
    server_bin = Path(llama_cpp_path) / "build" / "bin" / "llama-server"
    
    cmd = [
        str(server_bin),
        "-m", model_path,
        "--port", str(port),
        "-ngl", str(gpu_layers),
        "-c", str(context_size),
        "--host", "0.0.0.0"
    ]
    
    print(f"Starting server on http://localhost:{port}")
    print(f"API endpoint: http://localhost:{port}/v1/chat/completions")
    print("Press Ctrl+C to stop")
    
    try:
        subprocess.run(cmd)
    except KeyboardInterrupt:
        print("\nServer stopped")


if __name__ == "__main__":
    MODEL_PATH = "./models/quantized/Llama-3.2-3B-Instruct-Q4_K_M.gguf"
    
    if not Path(MODEL_PATH).exists():
        print(f"Error: Model not found at {MODEL_PATH}")
        sys.exit(1)
    
    start_llama_server(MODEL_PATH)

サーバー起動後、OpenAI SDK互換のAPIとして利用できます。

"""
チャットボットクライアントの例
"""
from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8080/v1",
    api_key="not-needed"
)

response = client.chat.completions.create(
    model="local-model",
    messages=[
        {"role": "user", "content": "Pythonで素数を判定する関数を書いて"}
    ]
)

print(response.choices[0].message.content)

7.2 ユースケース2: Raspberry Pi / エッジデバイスで動かす

想定読者: IoTエンジニア、組み込み開発者
推奨構成: Q2_K または IQ2_XS + CPU推論

"""
リソース制限環境での推論スクリプト
Raspberry Pi 4B(8GB RAM)向け
"""
import subprocess
from pathlib import Path


def run_inference_low_resource(
    model_path: str,
    prompt: str,
    llama_cpp_path: str = "./llama.cpp",
    max_tokens: int = 256,
    threads: int = 4
) -> str:
    """
    リソース制限環境でLLM推論を実行する
    
    Args:
        model_path: GGUFモデルファイルのパス
        prompt: 入力プロンプト
        llama_cpp_path: llama.cppのルートパス
        max_tokens: 最大生成トークン数
        threads: CPUスレッド数
    
    Returns:
        生成されたテキスト
    """
    cli_bin = Path(llama_cpp_path) / "build" / "bin" / "llama-cli"
    
    cmd = [
        str(cli_bin),
        "-m", model_path,
        "-p", prompt,
        "-n", str(max_tokens),
        "-t", str(threads),
        "-ngl", "0",          # GPUオフロードなし
        "-c", "512",          # 小さいコンテキスト
        "--temp", "0.7",
        "--no-display-prompt"
    ]
    
    result = subprocess.run(
        cmd,
        capture_output=True,
        text=True
    )
    
    return result.stdout


if __name__ == "__main__":
    # 超軽量モデルを使用(Q2_K: ~1.5GB)
    MODEL_PATH = "./models/quantized/TinyLlama-1.1B-Q2_K.gguf"
    
    response = run_inference_low_resource(
        MODEL_PATH,
        "What is the capital of Japan?",
        threads=4  # Raspberry Pi 4Bは4コア
    )
    
    print(response)

7.3 ユースケース3: バッチ処理で大量テキストを処理

想定読者: データサイエンティスト、MLエンジニア
推奨構成: Q5_K_M + 並列処理

"""
バッチ推論処理スクリプト
大量のテキストを効率的に処理する
"""
import subprocess
import json
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import List, Dict


def process_single_prompt(
    prompt_data: Dict,
    model_path: str,
    llama_cpp_path: str
) -> Dict:
    """
    単一プロンプトを処理する
    
    Args:
        prompt_data: {"id": str, "prompt": str} 形式の辞書
        model_path: モデルパス
        llama_cpp_path: llama.cppパス
    
    Returns:
        {"id": str, "prompt": str, "response": str} 形式の辞書
    """
    cli_bin = Path(llama_cpp_path) / "build" / "bin" / "llama-cli"
    
    cmd = [
        str(cli_bin),
        "-m", model_path,
        "-p", prompt_data["prompt"],
        "-n", "256",
        "-ngl", "99",
        "--no-display-prompt",
        "--log-disable"
    ]
    
    result = subprocess.run(cmd, capture_output=True, text=True)
    
    return {
        "id": prompt_data["id"],
        "prompt": prompt_data["prompt"],
        "response": result.stdout.strip()
    }


def batch_inference(
    prompts: List[Dict],
    model_path: str,
    llama_cpp_path: str = "./llama.cpp",
    max_workers: int = 4
) -> List[Dict]:
    """
    複数プロンプトをバッチ処理する
    
    Args:
        prompts: プロンプトのリスト
        model_path: モデルパス
        llama_cpp_path: llama.cppパス
        max_workers: 並列ワーカー数
    
    Returns:
        結果のリスト
    """
    results = []
    
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = {
            executor.submit(
                process_single_prompt,
                prompt,
                model_path,
                llama_cpp_path
            ): prompt["id"]
            for prompt in prompts
        }
        
        for future in as_completed(futures):
            try:
                result = future.result()
                results.append(result)
                print(f"Completed: {result['id']}")
            except Exception as e:
                print(f"Error processing {futures[future]}: {e}")
    
    return sorted(results, key=lambda x: x["id"])


if __name__ == "__main__":
    # サンプルプロンプト
    prompts = [
        {"id": "001", "prompt": "Summarize machine learning in one sentence."},
        {"id": "002", "prompt": "What is Python used for?"},
        {"id": "003", "prompt": "Explain REST API briefly."},
    ]
    
    MODEL_PATH = "./models/quantized/Llama-3.2-3B-Instruct-Q5_K_M.gguf"
    
    results = batch_inference(prompts, MODEL_PATH, max_workers=2)
    
    # 結果をJSONで保存
    with open("batch_results.json", "w") as f:
        json.dump(results, f, indent=2, ensure_ascii=False)
    
    print(f"\nProcessed {len(results)} prompts")
    print("Results saved to batch_results.json")

ユースケースが把握できたところで、この記事を読んだ後の学習パスを確認しましょう。


8. 学習ロードマップ

この記事を読んだ後、次のステップとして以下をおすすめします。

初級者向け(まずはここから)

  1. 既存のGGUFモデルを試す

  2. GUIツールで体験する

    • LM Studio - GUIでGGUFモデルを簡単に試せる
    • Ollama - CLIで手軽にモデルを管理

中級者向け(実践に進む)

  1. 自分のモデルを量子化する

    • この記事の手順でHugging Faceモデルを変換
    • imatrixを使って品質を最適化
  2. APIサーバーとして運用する

上級者向け(さらに深く)

  1. llama.cppの内部実装を読む

    • ggml.cの量子化実装
    • K-Quantの詳細アルゴリズム
  2. コントリビュートする


9. まとめ

この記事では、GGUF量子化について以下を解説しました。

  1. GGUFとは: llama.cppで使用されるモデル形式で、量子化された重みとメタデータを1ファイルに格納
  2. 量子化タイプの選び方: Q4_K_Mが汎用的、品質重視ならQ5_K_M以上、極限環境ならQ2_K
  3. 実践手順: Hugging Faceモデル → FP16 GGUF → 量子化 の3ステップ
  4. imatrixによる品質向上: キャリブレーションデータで重要度を計算し、適応的に量子化

私の所感

GGUF量子化は、「LLMの民主化」を実現した技術だと感じています。

かつて70Bモデルを動かすには、A100を何枚も積んだサーバーが必要でした。今では、ゲーミングPCやMacBookでも、量子化すれば動く時代です。

もちろん、量子化による品質低下はゼロではありません。でも、「動かせない」と「少し品質が下がるが動かせる」の差は、天と地ほど違います。

皆さんも、ぜひ手元のマシンでLLMを動かしてみてください。「自分のPCでAIが動く」という体験は、きっとワクワクするはずです。


参考文献

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?