科学と神々株式会社アドベントカレンダー 2025
LLM量子化 Day 12: 出力の検証
「できた」だけでは不十分
量子化処理が終わり、ファイルが生成された。しかし、それで本当に「成功」と言えるでしょうか?
- ファイルが途中で切れていないか?
- 正しいGGUF形式になっているか?
- 実際にllama.cppで読み込めるか?
これらを確認するのが「検証」です。
検証が必要なシナリオ
よくある問題
1. ディスク容量不足
→ ファイルが途中で切れる
→ 開こうとするとクラッシュ
2. 変換中のエラー
→ 不完全なGGUF構造
→ 一部のテンソルが欠落
3. 電源断・強制終了
→ ファイル破損
→ メタデータの不整合
これらの問題は、ファイルが「存在する」だけでは検出できません。
検証の階層
llm-quantizeは、複数の検証レベルを提供します:
検証レベル:
├── Level 1: 存在確認
│ └── ファイルが存在するか
│
├── Level 2: 形式確認
│ ├── 拡張子が正しいか
│ ├── ファイルサイズが0でないか
│ └── マジックバイトが正しいか
│
└── Level 3: 構造確認(将来拡張)
├── バージョン番号が有効か
├── テンソル数が妥当か
└── メタデータが読み込めるか
検証結果の表現
検証結果は、シンプルなデータ構造で表現されます:
@dataclass
class ValidationResult:
is_valid: bool # 検証に成功したか
error_message: str = "" # エラーの詳細(失敗時のみ)
@classmethod
def valid(cls):
"""検証成功を表す"""
return cls(is_valid=True)
@classmethod
def invalid(cls, message: str):
"""検証失敗を表す"""
return cls(is_valid=False, error_message=message)
使用例:
# 検証成功
result = ValidationResult.valid()
print(result.is_valid) # True
# 検証失敗
result = ValidationResult.invalid("ファイルが空です")
print(result.is_valid) # False
print(result.error_message) # "ファイルが空です"
GGUF固有の検証
GGUFファイルには特有の検証ポイントがあります:
1. ファイル形式の確認
def _validate_gguf(path: Path) -> ValidationResult:
# ファイルであることを確認(ディレクトリではない)
if not path.is_file():
return ValidationResult.invalid(f"Must be a file: {path}")
# 拡張子の確認
if path.suffix.lower() != ".gguf":
return ValidationResult.invalid(f"Must have .gguf extension: {path}")
2. ファイルサイズの確認
# 空ファイルでないことを確認
file_size = path.stat().st_size
if file_size == 0:
return ValidationResult.invalid(f"File is empty: {path}")
3. マジックバイトの確認
GGUFファイルの最初の4バイトは必ず「GGUF」(0x47475546)です:
# マジックバイトの確認
with open(path, "rb") as f:
magic = f.read(4)
if magic != b"GGUF":
return ValidationResult.invalid(
f"Invalid magic bytes: {magic!r}, expected b'GGUF'"
)
return ValidationResult.valid()
なぜマジックバイトが重要か
マジックバイト(ファイルシグネチャ)は、ファイル形式を識別するための「指紋」です。
ファイル形式とマジックバイト:
├── GGUF: "GGUF" (0x47 0x47 0x55 0x46)
├── PNG: 0x89 0x50 0x4E 0x47
├── PDF: "%PDF"
└── ZIP: "PK" (0x50 0x4B)
マジックバイトを確認することで、以下の問題を検出できます:
- 拡張子だけ変えた別形式のファイル
- 完全に破損したファイル
- 意図しない上書き
検証ステータスの追跡
量子化結果には、検証状態が記録されます:
class ValidationStatus(Enum):
VALID = "valid" # 検証成功
INVALID = "invalid" # 検証失敗
UNCHECKED = "unchecked" # 未検証
状態遷移
量子化直後:
status = UNCHECKED
│
▼ 検証実行
│
├── 成功 → status = VALID
│
└── 失敗 → status = INVALID
error_message = "詳細なエラー情報"
検証のタイミング
検証はいつ行うべきでしょうか?
量子化完了直後(推奨)
result = quantizer.quantize()
result = update_validation_status(result, OutputFormat.GGUF)
if result.validation_status == ValidationStatus.INVALID:
print(f"警告: 検証に失敗しました")
使用前
# 既存のGGUFファイルを使う前に
result = validate_output("model.gguf", "gguf")
if not result.is_valid:
print(f"エラー: {result.error_message}")
sys.exit(1)
CLIでの検証統合
llm-quantizeでは、量子化完了時に自動的に検証が行われます:
$ llm-quantize quantize model gguf -q Q4_K_M
Quantizing to GGUF Q4_K_M...
[████████████████████████████████] 100%
✓ Quantization complete
✓ Validation passed
Output: model-Q4_K_M.gguf
Size: 4.0 GB
Compression: 72%
検証に失敗した場合:
$ llm-quantize quantize model gguf -q Q4_K_M
Quantizing to GGUF Q4_K_M...
[████████████████████████████████] 100%
✓ Quantization complete
✗ Validation failed: Invalid GGUF magic bytes: b'\x00\x00\x00\x00'
Warning: The output file may be corrupted.
検証の限界
現在の検証は「ファイルとして正しいか」を確認します。しかし、以下は確認できません:
- 品質: 量子化後のモデルの推論品質
- 完全性: すべてのテンソルが正しく量子化されているか
- 互換性: 特定のllama.cppバージョンで動作するか
より深い検証を行うには、実際にモデルを読み込んで推論を試す必要があります。
Tips: 検証のベストプラクティス
自動検証を有効にする
量子化後は必ず検証を行いましょう。llm-quantizeではデフォルトで有効です。
検証失敗時の対処
1. ディスク空き容量を確認
df -h
2. 量子化をやり直す
rm model-Q4_K_M.gguf
llm-quantize quantize model gguf -q Q4_K_M
3. それでも失敗する場合
- 別の量子化レベルを試す
- llama.cppを最新版に更新
- モデルの互換性を確認
手動での検証
GGUFファイルを手動で確認するには:
# マジックバイトを確認
xxd -l 4 model.gguf
# 出力: 00000000: 4747 5546 GGUF
# ファイルサイズを確認
ls -lh model.gguf
# 出力: -rw-r--r-- 1 user staff 4.0G Dec 12 12:00 model.gguf
# llama.cppで読み込みを試す
llama-cli -m model.gguf -p "Hello" -n 10
次回予告
Day 13からは「AWQ/GPTQ形式編」として、GPU推論に最適化された量子化形式について6日間にわたって解説します。
「信頼するが検証せよ(Trust, but verify)」——これはソフトウェア開発の基本原則です。自分の書いたコードも、外部ツールの出力も、必ず検証しましょう。