GGUF逆量子化とroundtripの監査設計

CHATGPT / AIChatGPTなどの生成AIを活用して運営・記事制作・更新を行っています。制作方針

Transformers v5.15.0とllama.cpp b10418を固定し、GGUF読込時のpeak RAM、Hugging Face形式保存、再変換後のmetadata・tokenizer・出力差を監査する机上検証計画です。

読込・保存・再検証をつなぎ、metadataとtokenizerと出力の三点でroundtripを判定する図

先に結論

GGUF roundtripの合格条件は、再変換fileが生成できることでも、元fileとhashが一致することでもありません。Transformersは入力GGUFを指定dtypeのPyTorch weightへ展開し、llama.cpp converterは保存済みHugging Face directoryから新しいGGUFを構築します。source revision、tokenizer、metadata、tensor dtype、固定prompt出力を両端で照合し、load時peak RAMと再変換による差を一つの監査記録へ結び付けます。

確認日時: 2026年8月14日(Asia/Tokyo)
固定version: Transformers v5.15.0/commit 5eddc12、llama.cpp b10418/commit a97123e、各revisionに対応する`gguf`とPyTorch。
検証区分: 公式documentation、release、sourceに基づく机上調査・実験計画です。model download、逆量子化、保存、再変換、推論、benchmarkは実行しておらず、RAM、時間、品質の実測値は示しません。
適用外: v5.15.0 integrationに未登録のarchitecture、元tokenizerやsource revisionを特定できないartifact、training済みweight、再量子化後の品質、multimodal sidecarや分割GGUFの単純な一般化。

比較単位をroundtrip全体で固定する

GGUFはtensorだけでなく、architecture、context、tokenizer、chat templateなどのmetadataを持つ拡張可能な形式です。Transformers integrationはmetadata keyをPyTorch configへmappingしますが、すべてのarchitectureと独自keyを無条件に保存する保証ではありません。fileを開けた事実と、意味が保存された事実を分けます。

固定する値記録する理由
input GGUFrepository、revision、filename、SHA-256、quant type、license元artifactの同一性
loaderTransformers 5.15.05eddc12gguf、PyTorch、Pythonmappingと逆量子化差
dtypetorch.float32をbaseline、別runでfp16/bf16展開後memoryと数値差
tokenizervocab、special token ID、chat template、tokenizer class文字列だけでは見えない互換性
converterllama.cpp b10418a97123econvert_hf_to_gguf.py出力metadataと対応architecture
workloadprompt ID、tokenized input、seed、sampling、生成上限出力比較の再現性
observationwall time、peak RSS、disk、error、hashresourceと失敗を同じrunへ接続

元GGUFが量子化されている場合、fp32へ展開した時点で失われていた量子化前情報が復元されるわけではありません。さらにf16 GGUFへ再変換すれば、量子化値から復元した近似値をf16へ格納する別artifactです。元の高精度checkpointと同等、または元GGUFより高品質になったとは扱いません。

逆量子化と保存を一つのrunにする

最初のrunは編集も学習もせず、loadと保存だけにします。入力のhashとmanifestを先に確定し、出力directoryは空の専用pathを使います。

import json
import os
from pathlib import Path

import torch
import transformers
from transformers import AutoModelForCausalLM, AutoTokenizer

MODEL_ID = os.environ["MODEL_ID"]
GGUF_FILE = os.environ["GGUF_FILE"]
OUT_DIR = Path("roundtrip-hf-fp32")

if OUT_DIR.exists():
    raise SystemExit(f"refusing to overwrite: {OUT_DIR}")

tokenizer = AutoTokenizer.from_pretrained(
    MODEL_ID,
    gguf_file=GGUF_FILE,
)
model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID,
    gguf_file=GGUF_FILE,
    dtype=torch.float32,
)

OUT_DIR.mkdir()
tokenizer.save_pretrained(OUT_DIR)
model.save_pretrained(OUT_DIR, safe_serialization=True)

manifest = {
    "transformers": transformers.__version__,
    "dtype": str(next(model.parameters()).dtype),
    "model_id": MODEL_ID,
    "gguf_file": GGUF_FILE,
}
(OUT_DIR / "kfb-roundtrip-manifest.json").write_text(
    json.dumps(manifest, ensure_ascii=False, indent=2) + "\n",
    encoding="utf-8",
)

これは構文例であり、この制作環境では実行していません。実験ではMODEL_ID、revision、入力hash、dependency lockをmanifestへ追加します。trust_remote_code=Trueを安易に足さず、v5.15.0本体の対応範囲で読めないmodelは別security reviewへ分離します。

load前、load後、保存中のpeak RSSを外側から同じ間隔で測ります。元GGUF sizeと保存後Safetensors sizeだけを比較せず、process peak、一時file、page cacheを区別します。fp16/bf16はbaseline完了後の別runとし、同じprocessで順番にloadしてcache効果を混ぜません。

llama.cppで再変換する

converterはb10418 checkoutのfileを使い、--helpを保存してから実行します。公式source上の--outtypef32f16bf16q8_0tq1_0tq2_0autoを受け付けます。roundtripの最初は量子化を追加せず、明示したf16またはbf16へ変換します。

LLAMA_CPP_DIR="/absolute/path/to/llama.cpp-b10418"
HF_DIR="/absolute/path/to/roundtrip-hf-fp32"
OUT_GGUF="/absolute/path/to/roundtrip-f16.gguf"

python "$LLAMA_CPP_DIR/convert_hf_to_gguf.py" --help
python "$LLAMA_CPP_DIR/convert_hf_to_gguf.py" \
  "$HF_DIR" \
  --outfile "$OUT_GGUF" \
  --outtype f16
shasum -a 256 "$OUT_GGUF"

出力先が存在する場合は実験wrapper側で拒否し、入力や過去結果を上書きしません。converterがModel ... is not supportedで止まった場合、近いarchitecture名へ偽装せず、そのversionでは変換不可として記録します。autoはsource weightに応じて出力typeを選ぶため、比較runでは明示typeを優先します。

再量子化まで調べる場合は、f16/bf16の再変換GGUFを新しいbaselineとして、llama-quantizeを別stageで実行します。元の量子化GGUFを直接さらに量子化する条件とは分け、品質差を実測するまで採用しません。

metadata・tokenizer・出力を三つのgateにする

1. metadata gate

元GGUFと再変換GGUFから、少なくとも次を機械抽出して比較します。

  • GGUF format version、tensor count、metadata key count
  • general.architecture、file type、model名とsource情報
  • context length、embedding length、layer数、attention head数
  • tokenizer model、vocab size、BOS/EOS/PAD ID、chat template
  • tensor名、shape、dtypeの集合

key名はarchitectureで異なり、独自metadataがroundtripで保存されるとは限りません。消失・追加・値変更を差分として残し、未知keyを黙って無視しません。file hashは変わるのが通常なので同一性gateには使わず、各artifactを識別するIDとして使います。

2. tokenizer gate

固定文字列setを両端でtokenizeし、token ID列、decode、special token、chat template適用後の文字列を比較します。日本語、ASCII、改行、code、絵文字、空文字、長い入力を分けます。生成文が似ていてもtoken IDが変われば、weight以外の差として失敗扱いにします。

3. output gate

元GGUFは固定したllama.cpp、保存済みHugging Face形式はTransformers、再変換GGUFは同じllama.cpp buildで実行します。共通にできる範囲でprompt、tokenized input、seed、temperature 0、生成上限、stop条件を固定し、次を保存します。

  • input token IDとoutput token ID
  • first mismatch位置と終了理由
  • error、NaN、空出力、異常反復
  • 実行engine、dtype、backend、thread/GPU条件

異なるengineの浮動小数演算は完全一致しない場合があるため、文字列一致率だけで合否を決めません。まずtokenizer一致を必須にし、task別の固定rubricと許容差を実験前に定義します。都合の良いpromptだけを後から選びません。

resourceとtrade-offを別軸で読む

観測確認したいこと誤解を防ぐ判断
input GGUF size配布・保管量load時RAMとは分ける
load peak RSS逆量子化と一時領域steady RAMだけで見ない
saved Safetensors size指定dtypeのfull weight元GGUFとの単純倍率を一般化しない
conversion wall timedisk/CPUを含む工程時間model間の性能順位にしない
output GGUF sizeouttypeとmetadataの結果品質指標にしない
token/task差roundtrip後の挙動file生成成功と分ける

この計画から、特定quant typeの必要RAM、roundtripの品質劣化率、最適dtype、変換時間は導けません。model size、architecture、storage、RAM bandwidth、converter更新で変わるためです。実測値を公開する場合はhardware、OS、filesystem、cold/warm、反復数を併記します。

未検証範囲と採用gate

roundtripを採用するのは、次を満たすartifactだけです。

  • 入力revision、SHA-256、license、architectureを固定できる。
  • v5.15.0の対応sourceでloadでき、peak RAMとdiskに安全余裕がある。
  • tokenizer gateが一致し、metadata差分を説明できる。
  • 固定test setで未許容の出力差、NaN、errorがない。
  • 元GGUF、Hugging Face保存物、再変換GGUFを別pathとhashで保持する。
  • converter、dependency、command、失敗log、rollbackを再現できる。

multimodal projector、MTP sidecar、split GGUF、custom tokenizer、remote code、学習で変更したweightは別matrixにします。training後のartifactは単なるformat roundtripではなくmodel変更を含むため、元GGUFとの比較だけでは品質を帰属できません。

入門記事

初めて試す場合は、TransformersでGGUFを読み込む入門で、対応済みの小型artifact、明示dtype、短い決定的生成から入口とmemory境界を確認してください。

既存のGGUF load error切り分けはllama.cppでの実行失敗、Transformers 4-bit・8-bit量子化入門はbitsandbytesによる別の量子化経路を扱います。本稿はGGUFからPyTorchへ戻し、Hugging Face形式を経てGGUFへ再変換する監査へ範囲を限定しています。

参考資料