Instructions to use FastVideo/FastH3-Omni-FP8-Comfy with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusion Single File
How to use FastVideo/FastH3-Omni-FP8-Comfy with Diffusion Single File:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
File size: 10,914 Bytes
2de4138 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | #!/usr/bin/env python3
"""Quantize a native ComfyUI single-file OmniRef checkpoint to native FP8 or NVFP4.
The input is the native ComfyUI layout produced by ``convert_omniref_comfy.py``:
Q/K/V are already fused into ``attn.qkv_proj`` and the PDD output heads use the
native first-head-plus-offset representation. The quantizer therefore sees one
fused QKV tensor and applies one Comfy weight scale to it, exactly as the
pinned ComfyUI runtime expects.
Only the per-block attention/MLP projections are quantized. The token refiner,
AdaLN curves and projections, patch/condition/output projections, norms, RoPE,
and FP32 PDD heads stay dense. This matches the native ComfyUI MiniMax-H3
quantized-layer policy used by the released V2/Trim Comfy checkpoints.
Writes a JSON receipt with source/output hashes, the exact format markers,
per-linear reconstruction metrics, and runtime versions.
"""
from __future__ import annotations
import argparse
import datetime as dt
import hashlib
import importlib.metadata
import json
from pathlib import Path
import re
import sys
import time
import torch
# Native Comfy MiniMax-H3 block linears. qkv_proj is the fused Q/K/V module.
QUANT_LINEAR = re.compile(
r"^blocks\.\d+\.(?:attn\.(?:qkv_proj|out_proj|to_gate_compress)|mlp\.(?:fc1|fc2))\.weight$"
)
FORMATS = {
"fp8": {
"layout": "TensorCoreFP8Layout",
"tag": {"format": "float8_e4m3fn"},
"documented_name": "FP8 E4M3, per-tensor weight scale, dynamic per-tensor activation scale",
"activation": "dynamic-per-tensor",
},
"nvfp4": {
"layout": "TensorCoreNVFP4Layout",
"tag": {"format": "nvfp4"},
"documented_name": "NVFP4 E2M1, per-tensor global scale plus FP8 block scales (group 16), dynamic per-tensor activation scale",
"activation": "dynamic-per-tensor",
},
}
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
while True:
block = handle.read(64 * 1024 * 1024)
if not block:
break
digest.update(block)
return digest.hexdigest()
def tensor_digest(tensor: torch.Tensor) -> str:
return hashlib.sha256(tensor.detach().cpu().contiguous().view(torch.uint8).numpy().tobytes()).hexdigest()
def safetensors_header(path: Path) -> dict:
from safetensors import safe_open
with safe_open(str(path), framework="pt", device="cpu") as handle:
keys = list(handle.keys())
metadata = handle.metadata() or {}
return {"tensor_count": len(keys), "metadata": metadata, "keys": keys}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--src", required=True, type=Path, help="Native ComfyUI BF16 single-file checkpoint")
parser.add_argument("--dst", required=True, type=Path, help="Output quantized single-file checkpoint")
parser.add_argument("--format", required=True, choices=sorted(FORMATS))
parser.add_argument("--receipt", required=True, type=Path)
parser.add_argument("--comfyui-commit", default="b1a247c9cf9dbfda4b2c0a0c1d50950667eeaa0c")
parser.add_argument("--expect-tensors", type=int, default=None)
args = parser.parse_args()
if args.src == args.dst:
raise SystemExit("source and destination must differ")
if args.dst.exists():
raise SystemExit(f"refusing to overwrite existing output: {args.dst}")
if not torch.cuda.is_available():
raise SystemExit("CUDA is required for comfy-kitchen quantization")
import comfy_kitchen
import comfy_kitchen.tensor as ckt
from safetensors import safe_open
from safetensors.torch import load_file, save_file
started = time.time()
spec = FORMATS[args.format]
layout_cls = getattr(ckt, spec["layout"])
tag_bytes = json.dumps(spec["tag"], separators=(",", ":")).encode()
print(f"[quantize] format={args.format} src={args.src} dst={args.dst}", flush=True)
src_header = safetensors_header(args.src)
if args.expect_tensors is not None and src_header["tensor_count"] != args.expect_tensors:
raise SystemExit(f"expected {args.expect_tensors} tensors, found {src_header['tensor_count']}")
print(f"[quantize] source tensors={src_header['tensor_count']} hashing source", flush=True)
src_sha256 = sha256_file(args.src)
# The native Comfy BF16 export is ~44 GB. Keeping the pass-through tensors
# plus the quantized output in CPU RAM stays well under a GB200 node's RAM.
source = load_file(str(args.src), device="cpu")
metadata = dict(src_header["metadata"])
metadata["quantization"] = json.dumps(
{
"format": spec["tag"]["format"],
"layout": spec["layout"],
"weights": "per-tensor global scale" if args.format == "fp8" else "per-tensor global scale plus FP8 block scales, group 16",
"activations": spec["activation"],
"quantized_layers": "all 50 DiT blocks: fused qkv_proj, out_proj, to_gate_compress, mlp.fc1, mlp.fc2",
"dense_layers": "token_refiner, AdaLN, norms, embeddings, patch/condition projections, FP32 PDD heads",
"qkv_treatment": "fused Q/K/V quantized as one tensor under one weight scale",
"comfyui_commit": args.comfyui_commit,
},
separators=(",", ":"),
)
metadata["quantization_source_sha256"] = src_sha256
metadata["quantization_recipe"] = "omniref-comfy-ptq-v1"
output: dict[str, torch.Tensor] = {}
linears = []
for index, (name, tensor) in enumerate(source.items(), 1):
if not QUANT_LINEAR.match(name):
output[name] = tensor
continue
weight = tensor.to(device="cuda", dtype=torch.bfloat16).contiguous()
qdata, params = layout_cls.quantize(weight)
serialized = layout_cls.state_dict_tensors(qdata, params)
for suffix, value in serialized.items():
output[name + suffix] = value.detach().cpu().contiguous()
output[name[: -len(".weight")] + ".comfy_quant"] = torch.tensor(list(tag_bytes), dtype=torch.uint8)
dequantized = layout_cls.dequantize(qdata, params).float()
reference = weight.float()
diff = dequantized - reference
rel_l2 = float(diff.norm() / reference.norm().clamp(min=1e-30))
cosine = float(torch.nn.functional.cosine_similarity(dequantized.flatten(), reference.flatten(), dim=0))
entry = {
"name": name,
"logical_shape": list(tensor.shape),
"weight_storage_shape": list(serialized[""].shape),
"weight_storage_dtype": str(serialized[""].dtype).replace("torch.", ""),
"reconstruction_relative_l2": rel_l2,
"reconstruction_cosine": cosine,
"reconstruction_max_abs": float(diff.abs().max()),
"source_amax": float(reference.abs().max()),
}
if args.format == "fp8":
entry["weight_scale"] = float(params.scale.reshape(-1)[0])
else:
entry["weight_scale_2"] = float(params.scale.reshape(-1)[0])
entry["weight_scale_shape"] = list(serialized["_scale"].shape)
entry["weight_scale_dtype"] = str(serialized["_scale"].dtype).replace("torch.", "")
entry["block_scale_min"] = float(serialized["_scale"].to(torch.float32).min())
entry["block_scale_max"] = float(serialized["_scale"].to(torch.float32).max())
linears.append(entry)
print(
f"[quantize] {index:4d}/{len(source)} {name} relL2={rel_l2:.6f} cos={cosine:.8f}",
flush=True,
)
del weight, qdata, params, dequantized, reference, diff
torch.cuda.empty_cache()
expected_linears = 50 * 5
if len(linears) != expected_linears:
raise SystemExit(f"expected {expected_linears} quantized linears, got {len(linears)}")
print(f"[quantize] writing {len(output)} tensors to {args.dst}", flush=True)
args.dst.parent.mkdir(parents=True, exist_ok=True)
save_file(output, str(args.dst), metadata=metadata)
out_sha256 = sha256_file(args.dst)
out_header = safetensors_header(args.dst)
# Pass-through tensors must survive byte-for-byte. Compare every dense tensor
# from the written file against the source file, not against the in-memory copy.
pass_through = [name for name in source if not QUANT_LINEAR.match(name)]
identical = 0
with safe_open(str(args.src), framework="pt", device="cpu") as src_reader, safe_open(
str(args.dst), framework="pt", device="cpu"
) as dst_reader:
for name in pass_through:
left = src_reader.get_tensor(name)
right = dst_reader.get_tensor(name)
if left.dtype != right.dtype or left.shape != right.shape or not torch.equal(left, right):
raise SystemExit(f"pass-through tensor changed: {name}")
identical += 1
receipt = {
"schema": "omniref-comfy-quant-receipt-v1",
"created_utc": dt.datetime.now(dt.timezone.utc).isoformat(),
"format": args.format,
"format_description": spec["documented_name"],
"source": {
"path": str(args.src),
"bytes": args.src.stat().st_size,
"sha256": src_sha256,
"tensor_count": src_header["tensor_count"],
"metadata": src_header["metadata"],
},
"output": {
"path": str(args.dst),
"bytes": args.dst.stat().st_size,
"sha256": out_sha256,
"tensor_count": out_header["tensor_count"],
},
"runtime": {
"torch": torch.__version__,
"cuda": torch.version.cuda,
"gpu": torch.cuda.get_device_name(0),
"comfy_kitchen": importlib.metadata.version("comfy-kitchen"),
"comfyui_commit": args.comfyui_commit,
},
"policy": {
"quantized_linears": "all 50 DiT blocks: fused qkv_proj, out_proj, to_gate_compress, mlp.fc1, mlp.fc2",
"dense_linears": "token refiner, AdaLN, norms, embeddings, patch/condition projections, FP32 PDD heads",
"qkv": "fused by the native Comfy conversion, then quantized as one tensor with one weight scale",
"activation_quantization": spec["activation"],
"native_comfy_layout": spec["layout"],
},
"quantized_linear_count": len(linears),
"quantized_linears": linears,
"pass_through_tensor_count": identical,
"pass_through_byte_identical": True,
"elapsed_seconds": round(time.time() - started, 1),
}
args.receipt.parent.mkdir(parents=True, exist_ok=True)
args.receipt.write_text(json.dumps(receipt, indent=2) + "\n")
print(json.dumps({k: receipt[k] for k in ("format", "output", "quantized_linear_count", "elapsed_seconds")}, indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())
|