1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

科学と神々株式会社アドベントカレンダー 2025

LLM量子化 Day 6: データモデルの設計

データモデルとは何か

量子化ツールを作る際、最初に考えるべきことは「どんなデータを扱うか」です。

データモデルとは、システムが扱う情報の構造を定義したものです。適切なデータモデルがあれば、コードは自然と整理され、バグも減ります。

量子化の3つの登場人物

llm-quantizeでは、量子化プロセスを3つの主要なデータモデルで表現しています:

SourceModel          QuantizationConfig         QuantizedModel
(入力)        +     (設定)           →       (出力)

映画制作に例えると

  • SourceModel(原作): 映画化される元の小説。どんな作品で、何ページあるのか
  • QuantizationConfig(脚本・演出方針): どう映画化するか。上映時間、どの場面を残すか
  • QuantizedModel(完成映画): 出来上がった作品。何分の映画で、どれくらいの圧縮率か

SourceModel:入力モデルの表現

SourceModelは「量子化される前のモデル」を表現します。

どんな情報を持つか

SourceModel
├── model_path       モデルのパス(HuggingFace IDまたはローカルパス)
├── model_type       ソースの種類(HF_HUB / LOCAL_DIR)
├── architecture     アーキテクチャ(LlamaForCausalLM等)
├── parameter_count  パラメータ数(70億など)
├── dtype            元の精度(float16, bfloat16等)
├── hidden_size      隠れ層のサイズ
├── num_layers       レイヤー数
└── ...

設計上の工夫

1. 二つの入力ソースへの対応

モデルは「HuggingFace Hub」からも「ローカルディレクトリ」からも読み込めます。この違いを明示的に区別することで、処理の分岐が明確になります。

# パスから自動判別
source = SourceModel.from_path("meta-llama/Llama-2-7b-hf")  # → HF_HUB
source = SourceModel.from_path("./my-local-model")          # → LOCAL_DIR

ユーザーは「パスを渡すだけ」で、内部では適切に処理されます。

2. 便利なプロパティ

よく使う条件分岐をプロパティ化しておくと、コードが読みやすくなります:

if source.is_local:
    # ローカルモデル固有の処理
elif source.is_hub:
    # HuggingFace Hub固有の処理

3. バリデーションの組み込み

「作成したら必ず正しい」とは限りません。バリデーションメソッドを用意しておくと、問題を早期に発見できます:

errors = source.validate()
if errors:
    print(f"問題があります: {errors}")

QuantizationConfig:設定の表現

QuantizationConfigは「どのように量子化するか」を定義します。

どんな情報を持つか

QuantizationConfig
├── target_format        出力形式(GGUF / AWQ / GPTQ)
├── quantization_level   量子化レベル(Q4_K_M, 4bit等)
├── output_dir           出力ディレクトリ
├── calibration_samples  キャリブレーションサンプル数(デフォルト256)
├── group_size           グループサイズ(デフォルト128)
├── enable_checkpoints   チェックポイント有効化
└── ...

設計上の工夫

1. Enumによる型安全性

文字列ではなくEnumを使うことで、タイプミスによるバグを防ぎます:

# 悪い例:文字列を使う
config = QuantizationConfig(target_format="gguf")  # "gguF" と間違えるかも

# 良い例:Enumを使う
config = QuantizationConfig(target_format=OutputFormat.GGUF)  # コンパイル時にチェック

2. デフォルト値の明示

多くのオプションには「賢明なデフォルト値」があります。これを明示することで、ユーザーは最小限の設定で始められます:

# 最小限の設定
config = QuantizationConfig(
    target_format=OutputFormat.GGUF,
    quantization_level="Q4_K_M"
)
# → output_dir=".", calibration_samples=256, group_size=128 が自動設定

3. 形式ごとのバリデーション

GGUFとAWQでは、有効な量子化レベルが異なります。これをバリデーションで検証します:

# GGUFでは "Q4_K_M" は有効
# AWQでは "4bit" のみ有効

errors = config.validate()
# target_format=AWQ, quantization_level="Q4_K_M" の場合
# → ["Invalid AWQ quantization level: Q4_K_M. Valid options: 4bit"]

4. 出力パスの自動生成

ユーザーが出力ファイル名を指定しなくても、適切な名前を自動生成します:

入力: meta-llama/Llama-2-7b-hf, GGUF, Q4_K_M
出力: Llama-2-7b-hf-Q4_K_M.gguf

入力: my-model, AWQ, 4bit
出力: my-model-awq-4bit/

QuantizedModel:出力の表現

QuantizedModelは「量子化の結果」を表現します。

どんな情報を持つか

QuantizedModel
├── output_path          出力ファイルパス
├── format               形式
├── file_size            ファイルサイズ(バイト)
├── compression_ratio    圧縮率(0.0〜1.0)
├── validation_status    検証状態(VALID/INVALID/UNCHECKED)
├── duration_seconds     処理時間
├── peak_memory_bytes    ピークメモリ使用量
└── ...

設計上の工夫

1. メタデータの保持

後から「この量子化ファイルは何から作られたか」を追跡できるよう、元モデルの情報を保持します:

result.source_model_path  # → "meta-llama/Llama-2-7b-hf"
result.original_dtype     # → "float16"
result.original_size      # → 14000000000 (約14GB)

2. 圧縮率の計算

「どれくらい小さくなったか」は最も重要な指標の一つです:

compression_ratio = file_size / original_size
# 例:4GB / 14GB = 0.28 (72%の削減)

3. 検証状態の追跡

量子化が「成功した」と言えるためには、出力ファイルが正しく読み込めることを確認すべきです:

class ValidationStatus(Enum):
    VALID = "valid"       # 検証成功
    INVALID = "invalid"   # 検証失敗
    UNCHECKED = "unchecked"  # 未検証

4. JSON出力のサポート

自動化やログ記録のために、結果をJSON形式で出力できます:

{
    "status": "success",
    "output_path": "/output/llama-2-7b-Q4_K_M.gguf",
    "format": "gguf",
    "file_size": 4000000000,
    "compression_ratio": 0.28,
    "duration_seconds": 1800.5,
    "validation_status": "valid"
}

データフロー全体像

3つのモデルがどのように連携するか、全体の流れを見てみましょう:

1. ユーザー入力
   └── CLIコマンド: llm-quantize quantize meta-llama/... gguf -q Q4_K_M

2. SourceModel作成
   └── SourceModel.from_path() でモデル情報を取得
   └── validate() でエラーチェック

3. QuantizationConfig作成
   └── CLIオプションから設定を構築
   └── validate() でエラーチェック

4. 量子化実行
   └── Quantizer(source, config).quantize()

5. QuantizedModel作成
   └── 結果を構造化された形で返す
   └── validate() で出力を検証

6. 結果表示
   └── Human/JSON形式で結果を表示

Tips: データモデル設計のポイント

1. dataclassを活用する

Pythonの@dataclassは、データモデルの実装に最適です:

  • 自動的に__init____repr____eq__が生成される
  • 型ヒントが強制される
  • デフォルト値を簡単に設定できる
@dataclass
class QuantizationConfig:
    target_format: OutputFormat  # 必須
    quantization_level: str      # 必須
    output_dir: str = "."        # オプション(デフォルトあり)

2. バリデーションは作成時に近い場所で

問題は早期に発見するほど、デバッグが容易です。データモデルにバリデーションを組み込むことで、問題のある設定が後段まで流れることを防ぎます。

3. 不変性を意識する

設定が途中で変わると、バグの原因になります。可能であれば、作成後は変更できないようにしましょう:

@dataclass(frozen=True)  # frozen=Trueで不変に
class ImmutableConfig:
    target_format: OutputFormat

4. 関連情報はまとめる

「このデータは常にセットで使われる」なら、それを一つの構造にまとめます。これにより、データの受け渡しが簡潔になり、関連性も明確になります。

次回予告

Day 7では「抽象化の力」について解説します。BaseQuantizerという共通インターフェースを通じて、GGUF、AWQ、GPTQという異なる量子化器を統一的に扱う方法を学びます。


データモデルは「設計図」です。しっかりとした設計図があれば、建物(コード)は自然と美しく建ちます。逆に、設計図がいい加減だと、どんなに腕の良い職人(プログラマー)でも良い建物は建てられません。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?