Spaces:
Running on Zero
Running on Zero
Download toolkit/util/comfy_quant_export.py from HTBris/zerogpu-sdxlTrainer: direct link, hf CLI and curl.
- Browser
- Download file 4.54 kB
-
https://huggingface.co/spaces/HTBris/zerogpu-sdxlTrainer/resolve/main/toolkit/util/comfy_quant_export.py
- Command line
-
hf download hf://spaces/HTBris/zerogpu-sdxlTrainer/toolkit/util/comfy_quant_export.py
-
curl -L -o comfy_quant_export.py https://huggingface.co/spaces/HTBris/zerogpu-sdxlTrainer/resolve/main/toolkit/util/comfy_quant_export.py
4.54 kB
| """Export toolkit-quantized modules into ComfyUI ``comfy_quant`` checkpoints — | |
| the inverse of toolkit/util/comfy_quant_import.py. | |
| Every quantized OstrisLinear whose backend has a comfy storage format emits | |
| its quantized tensors plus the ``<prefix>.comfy_quant`` uint8 JSON marker: | |
| - convrot8 (int8_tensorwise + convrot): weight int8 [out, in], fp32 | |
| weight_scale [out, 1] (comfy_kitchen's per-channel convention) | |
| - nvfp4: high-nibble-first packed fp4 pairs, e4m3 block scales re-swizzled | |
| to the cuBLAS 128x4 tile layout, fp32 weight_scale_2 per-tensor scale and | |
| optional AWQ pre_quant_scale | |
| - float8_e4m3fn: fp8_e4m3 weight + one fp32 per-tensor weight_scale | |
| - convrotcomfyw4a4: via convrot_quant.export_comfy_convrot_w4a4 | |
| Biases are NOT emitted here — they are ordinary parameters and flow through | |
| the regular state_dict path. | |
| """ | |
| import json | |
| from typing import Dict, List, Tuple | |
| import torch | |
| from toolkit.util.ostris_quant import OstrisLinear | |
| def comfy_quant_marker(conf: dict) -> torch.Tensor: | |
| return torch.tensor(list(json.dumps(conf).encode("utf-8")), dtype=torch.uint8) | |
| def export_comfy_quantized_layers( | |
| root: torch.nn.Module, | |
| ) -> Tuple[Dict[str, torch.Tensor], List[str], List[str]]: | |
| """Comfy-format state-dict entries for every quantized OstrisLinear in | |
| ``root``. Returns ``(entries, exported_names, unexportable_names)`` — | |
| entries are keyed by module path in root's layout (run | |
| convert_state_dict_on_save afterwards for the checkpoint layout); | |
| unexportable_names lists quantized modules whose backend has no comfy | |
| storage format (the caller decides whether to dequantize instead).""" | |
| from toolkit.util.convrot_quant import ( | |
| ConvRotComfyW4A4Quantizer, | |
| export_comfy_convrot_w4a4, | |
| ) | |
| from toolkit.util.nvfp4_quant import swap_nvfp4_nibbles, swizzle_nvfp4_scales | |
| entries: Dict[str, torch.Tensor] = {} | |
| exported: List[str] = [] | |
| unexportable: List[str] = [] | |
| for name, module in root.named_modules(): | |
| if not isinstance(module, OstrisLinear): | |
| continue | |
| if hasattr(module, "cr8_qdata"): | |
| rot = int(getattr(module, "cr8_rot_size", 1) or 1) | |
| conf = {"format": "int8_tensorwise"} | |
| if rot > 1: | |
| conf.update({"convrot": True, "convrot_groupsize": rot}) | |
| entries[f"{name}.weight"] = module.cr8_qdata.detach().cpu().contiguous() | |
| entries[f"{name}.weight_scale"] = ( | |
| module.cr8_scales.view(torch.float32) | |
| .detach() | |
| .cpu() | |
| .reshape(module.out_features, 1) | |
| .contiguous() | |
| ) | |
| entries[f"{name}.comfy_quant"] = comfy_quant_marker(conf) | |
| elif hasattr(module, "nv4_qdata"): | |
| entries[f"{name}.weight"] = swap_nvfp4_nibbles( | |
| module.nv4_qdata.detach().cpu() | |
| ) | |
| scales = module.nv4_scales.view(torch.float8_e4m3fn).detach().cpu() | |
| entries[f"{name}.weight_scale"] = swizzle_nvfp4_scales( | |
| scales.reshape(module.out_features, module.in_features // 16) | |
| ).view(torch.float8_e4m3fn) | |
| entries[f"{name}.weight_scale_2"] = ( | |
| module.nv4_pts.view(torch.float32).detach().cpu().reshape(()) | |
| ) | |
| if hasattr(module, "nv4_pre_scale"): | |
| entries[f"{name}.pre_quant_scale"] = ( | |
| module.nv4_pre_scale.view(torch.float32).detach().cpu().contiguous() | |
| ) | |
| entries[f"{name}.comfy_quant"] = comfy_quant_marker({"format": "nvfp4"}) | |
| elif hasattr(module, "f8_qdata"): | |
| entries[f"{name}.weight"] = module.f8_qdata.detach().cpu().contiguous() | |
| entries[f"{name}.weight_scale"] = ( | |
| module.f8_scale.view(torch.float32).detach().cpu().reshape(()) | |
| ) | |
| entries[f"{name}.comfy_quant"] = comfy_quant_marker( | |
| {"format": "float8_e4m3fn", "full_precision_matrix_mult": True} | |
| ) | |
| elif isinstance(module.ostris_quantizer, ConvRotComfyW4A4Quantizer): | |
| layer_entries = export_comfy_convrot_w4a4(module, f"{name}.") | |
| layer_entries.pop(f"{name}.bias", None) | |
| entries.update( | |
| {k: v.detach().cpu() if torch.is_tensor(v) else v for k, v in layer_entries.items()} | |
| ) | |
| else: | |
| unexportable.append(name) | |
| continue | |
| exported.append(name) | |
| return entries, exported, unexportable | |