From dffa75b95270ef3075c0d4660fbbabc71ecbe7cd Mon Sep 17 00:00:00 2001 From: molbal Date: Fri, 12 Jun 2026 15:20:00 +0200 Subject: [PATCH 01/10] . --- dequant.py | 3 + editor.md | 60 ++ fp8/transformer/config.json | 19 + ...usion_pytorch_model.safetensors.index.json | 1 + fp8/unconditional_transformer/config.json | 19 + ...usion_pytorch_model.safetensors.index.json | 1 + loader.py | 37 +- ops.py | 40 +- pr.md | 42 + tools/convert.py | 15 +- ui.html | 995 ++++++++++++++++++ 11 files changed, 1222 insertions(+), 10 deletions(-) create mode 100644 editor.md create mode 100644 fp8/transformer/config.json create mode 100644 fp8/transformer/diffusion_pytorch_model.safetensors.index.json create mode 100644 fp8/unconditional_transformer/config.json create mode 100644 fp8/unconditional_transformer/diffusion_pytorch_model.safetensors.index.json create mode 100644 pr.md create mode 100644 ui.html diff --git a/dequant.py b/dequant.py index 78f5f26..809db16 100644 --- a/dequant.py +++ b/dequant.py @@ -18,6 +18,9 @@ def dequantize_tensor(tensor, dtype=None, dequant_dtype=None): if qtype in TORCH_COMPATIBLE_QTYPES: return tensor.to(dtype) + elif qtype == gguf.GGMLQuantizationType.BF16: + tensor = torch.Tensor(tensor.data.view(torch.bfloat16).reshape(oshape)) + return tensor if dtype is None or dtype == torch.bfloat16 else tensor.to(dtype) elif qtype in dequantize_functions: dequant_dtype = dtype if dequant_dtype == "target" else dequant_dtype return dequantize(tensor.data, qtype, oshape, dtype=dequant_dtype).to(dtype) diff --git a/editor.md b/editor.md new file mode 100644 index 0000000..870afe8 --- /dev/null +++ b/editor.md @@ -0,0 +1,60 @@ +# Prompt Canvas Editor + +A single-file browser editor for building Ideogram-style structured JSON prompts with canvas-based bounding boxes. The app lives entirely in `ui.html`; there is no build step, package manager, or local server requirement. + +## Features + +- Set canvas width and height with sliders or by double-clicking the displayed values. +- Draw, move, resize, delete, and edit bounding boxes directly on the canvas. +- Cycle through selected boxes with the `<-` and `->` controls when boxes overlap. +- Edit global prompt fields, including high-level description, aesthetics, lighting, medium, style/photo mode, background, and color palette. +- Edit per-box mode, description, optional text content, and per-box color palette. +- Generate formatted JSON from the current canvas and form state. +- Paste existing prompt JSON into the JSON box and load it back into the editable canvas. + +## Usage + +Open `ui.html` directly in a modern browser. + +The Tailwind design system is loaded from the Tailwind CDN, so the page needs internet access for styling. The editor logic itself is plain HTML, CSS, and JavaScript. + +## Basic Workflow + +1. Set the canvas size. +2. Draw boxes on the canvas by clicking and dragging. +3. Select a box and edit its properties in the right panel. +4. Fill in the global prompt settings. +5. Click `Generate JSON` to write the prompt JSON into the textarea. +6. Copy or save the generated JSON wherever your workflow needs it. + +To edit an existing prompt, paste the JSON into the textarea and click `Load JSON`. The editor will rebuild the canvas boxes and form fields from the prompt. + +## JSON Shape + +The editor expects prompt JSON in this general form: + +```json +{ + "high_level_description": "", + "style_description": { + "aesthetics": "", + "lighting": "", + "medium": "", + "art_style": "", + "color_palette": [] + }, + "compositional_deconstruction": { + "background": "", + "elements": [ + { + "type": "obj", + "bbox": [0, 0, 1000, 1000], + "desc": "", + "color_palette": [] + } + ] + } +} +``` + +Bounding boxes use normalized coordinates from `0` to `1000` in `[y1, x1, y2, x2]` order. The editor converts those coordinates to the current canvas size when loading JSON, then converts them back to normalized coordinates when generating JSON. \ No newline at end of file diff --git a/fp8/transformer/config.json b/fp8/transformer/config.json new file mode 100644 index 0000000..da6e1e8 --- /dev/null +++ b/fp8/transformer/config.json @@ -0,0 +1,19 @@ +{ + "_class_name": "Ideogram4Transformer2DModel", + "_diffusers_version": "0.39.0.dev0", + "_name_or_path": "/home/jinli/.cache/huggingface/hub/models--ideogram-ai--debug-ideogram-v4/snapshots/41af6183c9fd9b6254864b0720319ef984535bfc/transformer", + "adaln_dim": 512, + "attention_head_dim": 256, + "in_channels": 128, + "intermediate_size": 12288, + "llm_features_dim": 53248, + "mrope_section": [ + 24, + 20, + 20 + ], + "norm_eps": 1e-05, + "num_attention_heads": 18, + "num_layers": 34, + "rope_theta": 5000000 +} diff --git a/fp8/transformer/diffusion_pytorch_model.safetensors.index.json b/fp8/transformer/diffusion_pytorch_model.safetensors.index.json new file mode 100644 index 0000000..ba1f6d8 --- /dev/null +++ b/fp8/transformer/diffusion_pytorch_model.safetensors.index.json @@ -0,0 +1 @@ +{"metadata": {"total_size": 9289722624}, "weight_map": {"adaln_proj.bias": "diffusion_pytorch_model.safetensors", "adaln_proj.weight": "diffusion_pytorch_model.safetensors", "adaln_proj.weight_scale": "diffusion_pytorch_model.safetensors", "embed_image_indicator.weight": "diffusion_pytorch_model.safetensors", "final_layer.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "final_layer.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "final_layer.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "final_layer.linear.bias": "diffusion_pytorch_model.safetensors", "final_layer.linear.weight": "diffusion_pytorch_model.safetensors", "final_layer.linear.weight_scale": "diffusion_pytorch_model.safetensors", "input_proj.bias": "diffusion_pytorch_model.safetensors", "input_proj.weight": "diffusion_pytorch_model.safetensors", "input_proj.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.0.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.0.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.0.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.1.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.1.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.1.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.1.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.10.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.10.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.10.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.10.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.11.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.11.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.11.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.11.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.12.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.12.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.12.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.12.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.13.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.13.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.13.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.13.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.14.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.14.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.14.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.14.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.15.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.15.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.15.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.15.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.16.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.16.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.16.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.16.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.17.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.17.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.17.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.17.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.18.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.18.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.18.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.18.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.19.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.19.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.19.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.19.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.2.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.2.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.2.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.2.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.20.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.20.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.20.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.20.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.21.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.21.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.21.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.21.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.22.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.22.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.22.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.22.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.23.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.23.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.23.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.23.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.24.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.24.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.24.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.24.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.25.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.25.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.25.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.25.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.26.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.26.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.26.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.26.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.27.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.27.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.27.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.27.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.28.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.28.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.28.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.28.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.29.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.29.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.29.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.29.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.3.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.3.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.3.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.3.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.30.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.30.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.30.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.30.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.31.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.31.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.31.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.31.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.32.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.32.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.32.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.32.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.33.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.33.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.33.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.33.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.4.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.4.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.4.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.4.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.5.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.5.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.5.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.5.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.6.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.6.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.6.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.6.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.7.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.7.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.7.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.7.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.8.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.8.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.8.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.8.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.9.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.9.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.9.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.9.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "llm_cond_norm.weight": "diffusion_pytorch_model.safetensors", "llm_cond_proj.bias": "diffusion_pytorch_model.safetensors", "llm_cond_proj.weight": "diffusion_pytorch_model.safetensors", "llm_cond_proj.weight_scale": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_in.bias": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_in.weight": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_in.weight_scale": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_out.bias": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_out.weight": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_out.weight_scale": "diffusion_pytorch_model.safetensors"}} \ No newline at end of file diff --git a/fp8/unconditional_transformer/config.json b/fp8/unconditional_transformer/config.json new file mode 100644 index 0000000..f40a02c --- /dev/null +++ b/fp8/unconditional_transformer/config.json @@ -0,0 +1,19 @@ +{ + "_class_name": "Ideogram4Transformer2DModel", + "_diffusers_version": "0.39.0.dev0", + "_name_or_path": "/home/jinli/.cache/huggingface/hub/models--ideogram-ai--debug-ideogram-v4/snapshots/41af6183c9fd9b6254864b0720319ef984535bfc/unconditional_transformer", + "adaln_dim": 512, + "attention_head_dim": 256, + "in_channels": 128, + "intermediate_size": 12288, + "llm_features_dim": 53248, + "mrope_section": [ + 24, + 20, + 20 + ], + "norm_eps": 1e-05, + "num_attention_heads": 18, + "num_layers": 34, + "rope_theta": 5000000 +} diff --git a/fp8/unconditional_transformer/diffusion_pytorch_model.safetensors.index.json b/fp8/unconditional_transformer/diffusion_pytorch_model.safetensors.index.json new file mode 100644 index 0000000..ba1f6d8 --- /dev/null +++ b/fp8/unconditional_transformer/diffusion_pytorch_model.safetensors.index.json @@ -0,0 +1 @@ +{"metadata": {"total_size": 9289722624}, "weight_map": {"adaln_proj.bias": "diffusion_pytorch_model.safetensors", "adaln_proj.weight": "diffusion_pytorch_model.safetensors", "adaln_proj.weight_scale": "diffusion_pytorch_model.safetensors", "embed_image_indicator.weight": "diffusion_pytorch_model.safetensors", "final_layer.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "final_layer.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "final_layer.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "final_layer.linear.bias": "diffusion_pytorch_model.safetensors", "final_layer.linear.weight": "diffusion_pytorch_model.safetensors", "final_layer.linear.weight_scale": "diffusion_pytorch_model.safetensors", "input_proj.bias": "diffusion_pytorch_model.safetensors", "input_proj.weight": "diffusion_pytorch_model.safetensors", "input_proj.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.0.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.0.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.0.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.1.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.1.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.1.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.1.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.10.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.10.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.10.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.10.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.11.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.11.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.11.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.11.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.12.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.12.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.12.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.12.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.13.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.13.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.13.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.13.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.14.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.14.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.14.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.14.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.15.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.15.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.15.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.15.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.16.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.16.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.16.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.16.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.17.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.17.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.17.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.17.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.18.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.18.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.18.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.18.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.19.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.19.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.19.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.19.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.2.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.2.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.2.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.2.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.20.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.20.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.20.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.20.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.21.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.21.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.21.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.21.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.22.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.22.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.22.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.22.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.23.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.23.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.23.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.23.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.24.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.24.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.24.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.24.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.25.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.25.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.25.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.25.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.26.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.26.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.26.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.26.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.27.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.27.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.27.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.27.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.28.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.28.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.28.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.28.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.29.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.29.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.29.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.29.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.3.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.3.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.3.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.3.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.30.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.30.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.30.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.30.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.31.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.31.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.31.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.31.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.32.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.32.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.32.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.32.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.33.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.33.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.33.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.33.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.4.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.4.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.4.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.4.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.5.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.5.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.5.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.5.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.6.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.6.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.6.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.6.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.7.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.7.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.7.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.7.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.8.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.8.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.8.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.8.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.9.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.9.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.9.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.9.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "llm_cond_norm.weight": "diffusion_pytorch_model.safetensors", "llm_cond_proj.bias": "diffusion_pytorch_model.safetensors", "llm_cond_proj.weight": "diffusion_pytorch_model.safetensors", "llm_cond_proj.weight_scale": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_in.bias": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_in.weight": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_in.weight_scale": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_out.bias": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_out.weight": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_out.weight_scale": "diffusion_pytorch_model.safetensors"}} \ No newline at end of file diff --git a/loader.py b/loader.py index 7cefb11..64177f8 100644 --- a/loader.py +++ b/loader.py @@ -9,10 +9,23 @@ from .ops import GGMLTensor from .dequant import is_quantized, dequantize_tensor -IMG_ARCH_LIST = {"flux", "sd1", "sdxl", "sd3", "aura", "hidream", "cosmos", "ltxv", "hyvid", "wan", "lumina2", "qwen_image"} +IMG_ARCH_LIST = {"flux", "sd1", "sdxl", "sd3", "aura", "hidream", "cosmos", "ltxv", "hyvid", "wan", "lumina2", "qwen_image", "ideogram"} TXT_ARCH_LIST = {"t5", "t5encoder", "llama", "qwen2vl", "qwen3", "qwen3vl", "gemma3"} VIS_TYPE_LIST = {"clip-vision", "mmproj"} +def device_supports_bf16(): + """ + Return True if the active torch device can run bf16 natively. On devices + without native bf16 support, computation silently falls back to fp32 which + is very slow, so callers should load tensors as fp16 instead. + """ + try: + import comfy.model_management + return comfy.model_management.should_use_bf16(comfy.model_management.get_torch_device()) + except Exception: + # If support can't be determined, keep the previous bf16 behavior. + return True + def get_orig_shape(reader, tensor_name): field_key = f"comfy.gguf.orig_shape.{tensor_name}" field = reader.get_field(field_key) @@ -113,6 +126,9 @@ def gguf_sd_loader(path, handle_prefix="model.diffusion_model.", is_text_model=F logging.warning(f"Warning: This gguf model file is loaded in compatibility mode '{compat}' [arch:{arch_str}]") # main loading loop + # Devices without native bf16 fall back to slow fp32 compute, so load the + # full-precision BF16 storage tensors as fp16 there instead. + bf16_storage_dtype = torch.bfloat16 if device_supports_bf16() else torch.float16 state_dict = {} qtype_dict = {} for sd_key, tensor in tensors: @@ -138,9 +154,10 @@ def gguf_sd_loader(path, handle_prefix="model.diffusion_model.", is_text_model=F torch_tensor = torch_tensor.view(*shape) state_dict[sd_key] = GGMLTensor(torch_tensor, tensor_type=tensor.tensor_type, tensor_shape=shape) - # 1D tensors shouldn't be quantized, this is a fix for BF16 - if len(shape) <= 1 and tensor.tensor_type == gguf.GGMLQuantizationType.BF16: - state_dict[sd_key] = dequantize_tensor(state_dict[sd_key], dtype=torch.float32) + # BF16 GGUF tensors are full-precision storage, not compressed quants. + if tensor.tensor_type == gguf.GGMLQuantizationType.BF16: + dtype = torch.float32 if len(shape) <= 1 else bf16_storage_dtype + state_dict[sd_key] = dequantize_tensor(state_dict[sd_key], dtype=dtype) # keep track of loaded tensor types tensor_type_str = getattr(tensor.tensor_type, "name", repr(tensor.tensor_type)) @@ -501,6 +518,18 @@ def gguf_clip_loader(path): if arch == "qwen2vl": vsd = gguf_mmproj_loader(path) sd.update(vsd) + elif arch == "ideogram": + # Dequantize Ideogram model for inference + logging.info("Dequantizing Ideogram model for inference...") + # Use BF16 to save VRAM while maintaining quality, but fall back to FP16 + # on devices that don't support bf16 (avoids slow fp32 compute fallback). + target_dtype = torch.bfloat16 if device_supports_bf16() else torch.float16 + dequantized_count = 0 + for key in list(sd.keys()): + if is_quantized(sd[key]): + sd[key] = dequantize_tensor(sd[key], dtype=target_dtype) + dequantized_count += 1 + logging.info(f"Dequantized {dequantized_count} tensors for Ideogram model ({target_dtype})") else: pass return sd diff --git a/ops.py b/ops.py index 88a352e..9c26052 100644 --- a/ops.py +++ b/ops.py @@ -8,6 +8,18 @@ import comfy.model_management from .dequant import dequantize_tensor, is_quantized +def _valid_compute_dtype(dtype): + return dtype in {torch.float16, torch.bfloat16, torch.float32, torch.float64} + +def _infer_compute_dtype(tensor_type, fallback=None): + if _valid_compute_dtype(fallback): + return fallback + if tensor_type == gguf.GGMLQuantizationType.BF16: + return torch.bfloat16 + if tensor_type == gguf.GGMLQuantizationType.F32: + return torch.float32 + return torch.float16 + def chained_hasattr(obj, chained_attr): probe = obj for attr in chained_attr.split('.'): @@ -45,13 +57,14 @@ class GGMLTensor(torch.Tensor): """ Main tensor-like class for storing quantized weights """ - def __init__(self, *args, tensor_type, tensor_shape, patches=[], **kwargs): + def __init__(self, *args, tensor_type, tensor_shape, patches=[], compute_dtype=None, **kwargs): super().__init__() self.tensor_type = tensor_type self.tensor_shape = tensor_shape self.patches = patches + self.compute_dtype = compute_dtype - def __new__(cls, *args, tensor_type, tensor_shape, patches=[], **kwargs): + def __new__(cls, *args, tensor_type, tensor_shape, patches=[], compute_dtype=None, **kwargs): return super().__new__(cls, *args, **kwargs) def to(self, *args, **kwargs): @@ -59,6 +72,7 @@ def to(self, *args, **kwargs): new.tensor_type = getattr(self, "tensor_type", None) new.tensor_shape = getattr(self, "tensor_shape", new.data.shape) new.patches = getattr(self, "patches", []).copy() + new.compute_dtype = getattr(self, "compute_dtype", None) return new def clone(self, *args, **kwargs): @@ -81,9 +95,17 @@ def new_empty(self, size, *args, **kwargs): new_tensor, tensor_type = getattr(self, "tensor_type", None), tensor_shape = size, - patches = getattr(self, "patches", []).copy() + patches = getattr(self, "patches", []).copy(), + compute_dtype = getattr(self, "compute_dtype", None), ) + @property + def dtype(self): + qtype = getattr(self, "tensor_type", None) + if qtype in GGMLLayer.torch_compatible_tensor_types: + return torch.Tensor(self).dtype + return _infer_compute_dtype(qtype, getattr(self, "compute_dtype", None)) + @property def shape(self): if not hasattr(self, "tensor_shape"): @@ -121,8 +143,12 @@ def ggml_load_from_state_dict(self, state_dict, prefix, local_metadata, strict, prefix_len = len(prefix) for k,v in state_dict.items(): if k[prefix_len:] == "weight": + if isinstance(v, GGMLTensor): + v.compute_dtype = self._ggml_compute_dtype(v, "weight") self.weight = torch.nn.Parameter(v, requires_grad=False) elif k[prefix_len:] == "bias" and v is not None: + if isinstance(v, GGMLTensor): + v.compute_dtype = self._ggml_compute_dtype(v, "bias") self.bias = torch.nn.Parameter(v, requires_grad=False) else: unexpected_keys.append(k) @@ -137,6 +163,12 @@ def ggml_load_from_state_dict(self, state_dict, prefix, local_metadata, strict, if getattr(self.weight, "is_largest_weight", False): self.largest_layer = True + def _ggml_compute_dtype(self, tensor, param_name): + if self.dequant_dtype is not None and self.dequant_dtype != "target": + return self.dequant_dtype + model_dtype = getattr(self, f"{param_name}_comfy_model_dtype", None) + return _infer_compute_dtype(getattr(tensor, "tensor_type", None), model_dtype) + def _save_to_state_dict(self, *args, **kwargs): if self.is_ggml_quantized(): return self.ggml_save_to_state_dict(*args, **kwargs) @@ -238,6 +270,8 @@ def __init__(self, in_features, out_features, bias=True, device=None, dtype=None self.out_features = out_features self.weight = None self.bias = None + self.weight_comfy_model_dtype = dtype + self.bias_comfy_model_dtype = dtype def forward_ggml_cast_weights(self, input): weight, bias = self.cast_bias_weight(input) diff --git a/pr.md b/pr.md new file mode 100644 index 0000000..7ea8184 --- /dev/null +++ b/pr.md @@ -0,0 +1,42 @@ +# Summary + +This adds support for Ideogram GGUF models. + +# What Changed + +- Added `ideogram` to the supported image GGUF architectures. +- Added Ideogram model detection to the converter. +- Added GGUF dtype handling needed by Ideogram inference. +- Fixed the Ideogram inference failure where a packed GGUF weight dtype caused a byte tensor to reach CUDA linear. +- Adjusted BF16 GGUF loading so Ideogram can start inference faster. + +# Error Fixed + +Before this change, Ideogram GGUF models could load but failed during sampling with: + +```text +"addmm_cuda" not implemented for 'Byte' +``` + +# Tests + +Ran Python compile checks: + +```text +python -m py_compile dequant.py ops.py loader.py nodes.py tools\convert.py +``` + +Checked a synthetic GGUF linear forward: + +- packed storage stayed `torch.uint8` +- reported dtype was `torch.bfloat16` +- input dtype was `torch.bfloat16` +- output dtype was `torch.bfloat16` + +Checked BF16 loading behavior with finite values. + +# Notes + +Full ComfyUI inference was not run from this environment. + +Other GGUF quant types still use the existing dequant paths. diff --git a/tools/convert.py b/tools/convert.py index 5029c87..a9b76ea 100644 --- a/tools/convert.py +++ b/tools/convert.py @@ -145,8 +145,18 @@ class ModelLumina2(ModelTemplate): ("cap_embedder.1.weight", "context_refiner.0.attention.qkv.weight") ] +class ModelIdeogram(ModelTemplate): + arch = "ideogram" + keys_detect = [ + ( + "t_embedding.mlp_in.weight", + "layers.0.attention.qkv.weight", + "final_layer.linear.weight", + ) + ] + arch_list = [ModelFlux, ModelSD3, ModelAura, ModelHiDream, CosmosPredict2, - ModelLTXV, ModelHyVid, ModelWan, ModelSDXL, ModelSD1, ModelLumina2] + ModelLTXV, ModelHyVid, ModelWan, ModelSDXL, ModelSD1, ModelLumina2, ModelIdeogram] def is_model_arch(model, state_dict): # check if model is correct @@ -361,5 +371,4 @@ def convert_file(path, dst_path=None, interact=True, overwrite=False): if __name__ == "__main__": args = parse_args() - convert_file(args.src, args.dst) - + convert_file(args.src, args.dst) \ No newline at end of file diff --git a/ui.html b/ui.html new file mode 100644 index 0000000..740d44a --- /dev/null +++ b/ui.html @@ -0,0 +1,995 @@ + + + + + + + JSON Prompt Generation Tool + + + + + + + +
+
+
+
+
+ + 1024 +
+ +
+
+
+ + 1024 +
+ +
+ +
+
+ +
+
+
+
+

Prompt Canvas

+ 1024 x 1024 +
+
+
+
+
+
+
+
+
+ +
+
+
+

Prompt JSON

+
+ + +
+
+ +
+
+
+
+ + +
+
+ + + + + + From 16ddaa43a1a25b66e9ee7f02e9a4d97e759cba63 Mon Sep 17 00:00:00 2001 From: molbal Date: Wed, 24 Jun 2026 23:27:17 +0200 Subject: [PATCH 02/10] Add Krea-2 model support and batch converter for GGUF files - Updated IMG_ARCH_LIST to include 'krea2'. - Introduced ModelKrea2 class with architecture details and tensor handling. - Enhanced convert_file function to support quantization types. - Added tools/convert_krea2_gguf.py for batch conversion of Krea-2 models to multiple GGUF quant levels. --- loader.py | 2 +- tools/convert.py | 117 +++++++++++++++++++++++------ tools/convert_krea2_gguf.py | 144 ++++++++++++++++++++++++++++++++++++ tools/krea2_gguf_readme.md | 100 +++++++++++++++++++++++++ 4 files changed, 339 insertions(+), 24 deletions(-) create mode 100644 tools/convert_krea2_gguf.py create mode 100644 tools/krea2_gguf_readme.md diff --git a/loader.py b/loader.py index 64177f8..88f4713 100644 --- a/loader.py +++ b/loader.py @@ -9,7 +9,7 @@ from .ops import GGMLTensor from .dequant import is_quantized, dequantize_tensor -IMG_ARCH_LIST = {"flux", "sd1", "sdxl", "sd3", "aura", "hidream", "cosmos", "ltxv", "hyvid", "wan", "lumina2", "qwen_image", "ideogram"} +IMG_ARCH_LIST = {"flux", "sd1", "sdxl", "sd3", "aura", "hidream", "cosmos", "ltxv", "hyvid", "wan", "lumina2", "qwen_image", "ideogram", "krea2"} TXT_ARCH_LIST = {"t5", "t5encoder", "llama", "qwen2vl", "qwen3", "qwen3vl", "gemma3"} VIS_TYPE_LIST = {"clip-vision", "mmproj"} diff --git a/tools/convert.py b/tools/convert.py index a9b76ea..a79a395 100644 --- a/tools/convert.py +++ b/tools/convert.py @@ -155,8 +155,49 @@ class ModelIdeogram(ModelTemplate): ) ] -arch_list = [ModelFlux, ModelSD3, ModelAura, ModelHiDream, CosmosPredict2, - ModelLTXV, ModelHyVid, ModelWan, ModelSDXL, ModelSD1, ModelLumina2, ModelIdeogram] +class ModelKrea2(ModelTemplate): + """ + Krea-2 is a novel architecture from krea.ai — NOT Ideogram4. + Key structure (verified from Krea2_Turbo_fp8mixed.safetensors header): + blocks.N.attn.{wq,wk,wv,wo,gate} — separate Q/K/V/O projections + gating + blocks.N.attn.qknorm.{qnorm,knorm} — Q/K norms + blocks.N.mlp.{up,gate,down} — SwiGLU-style MLP + blocks.N.mod.lin — per-block modulation + blocks.N.{pre,post}norm.scale — RMSNorm scales + txtfusion.layerwise_blocks.N.* — layerwise text-image cross-attention + txtfusion.refiner_blocks.N.* — refiner text-image cross-attention + txtfusion.projector — text projector + first.weight / last.* — input / output projections + tmlp.N / tproj.N — timestep MLP / projection + + NOTE: ComfyUI core must have krea2 diffusion model support + (i.e. a detection branch for 'blocks.0.attn.wq.weight' in model_detection.py + and a matching supported_models entry) for the GGUF to load correctly. + Krea-2 was released 2026-06-22; verify your ComfyUI build is up to date. + """ + arch = "krea2" + keys_detect = [ + ( + "blocks.0.attn.wq.weight", + "txtfusion.projector.weight", + "first.weight", + ), + ( + "blocks.0.attn.wq.weight", + "txtfusion.layerwise_blocks.0.attn.wq.weight", + "last.linear.weight", + ), + ] + keys_hiprec = [ + "blocks.0.mod.lin", # modulation parameters — keep at full precision + "last.modulation.lin", + "tmlp.", + "tproj.", + ] + +arch_list = [ModelFlux, ModelSD3, ModelAura, ModelHiDream, CosmosPredict2, + ModelLTXV, ModelHyVid, ModelWan, ModelSDXL, ModelSD1, ModelLumina2, + ModelKrea2, ModelIdeogram] def is_model_arch(model, state_dict): # check if model is correct @@ -179,10 +220,30 @@ def detect_arch(state_dict): assert model_arch is not None, "Unknown model architecture!" return model_arch +QUANT_TYPE_MAP = { + "F16": (gguf.GGMLQuantizationType.F16, gguf.LlamaFileType.MOSTLY_F16), + "BF16": (gguf.GGMLQuantizationType.BF16, gguf.LlamaFileType.MOSTLY_BF16), + "Q8_0": (gguf.GGMLQuantizationType.Q8_0, gguf.LlamaFileType.MOSTLY_Q8_0), + "Q5_1": (gguf.GGMLQuantizationType.Q5_1, gguf.LlamaFileType.MOSTLY_Q5_1), + "Q5_0": (gguf.GGMLQuantizationType.Q5_0, gguf.LlamaFileType.MOSTLY_Q5_0), + "Q4_1": (gguf.GGMLQuantizationType.Q4_1, gguf.LlamaFileType.MOSTLY_Q4_1), + "Q4_0": (gguf.GGMLQuantizationType.Q4_0, gguf.LlamaFileType.MOSTLY_Q4_0), +} + def parse_args(): - parser = argparse.ArgumentParser(description="Generate F16 GGUF files from single UNET") - parser.add_argument("--src", required=True, help="Source model ckpt file.") - parser.add_argument("--dst", help="Output unet gguf file.") + parser = argparse.ArgumentParser( + description="Convert diffusion model safetensors/ckpt to GGUF." + " By default produces an F16/BF16 GGUF; use --quant-type to quantize." + ) + parser.add_argument("--src", required=True, help="Source model ckpt/safetensors file.") + parser.add_argument("--dst", help="Output GGUF file path.") + parser.add_argument( + "--quant-type", + choices=list(QUANT_TYPE_MAP.keys()), + default=None, + help="Target quantization type for eligible 2-D+ tensors " + "(1-D biases/scales stay F32). Defaults to F16/BF16 matching the source dtype.", + ) args = parser.parse_args() if not os.path.isfile(args.src): @@ -234,7 +295,7 @@ def load_state_dict(path): return strip_prefix(state_dict) -def handle_tensors(writer, state_dict, model_arch): +def handle_tensors(writer, state_dict, model_arch, quant_type=None): name_lengths = tuple(sorted( ((key, len(key)) for key in state_dict.keys()), key=lambda item: item[1], @@ -257,7 +318,7 @@ def handle_tensors(writer, state_dict, model_arch): data = data.to(torch.float32).numpy() # this is so we don't break torch 2.0.X elif data.dtype in [getattr(torch, "float8_e4m3fn", "_invalid"), getattr(torch, "float8_e5m2", "_invalid")]: - data = data.to(torch.float16).numpy() + data = data.to(torch.float32).numpy() # cast to f32 for correct downstream quant else: data = data.numpy() @@ -294,6 +355,10 @@ def handle_tensors(writer, state_dict, model_arch): # tensors that require max precision data_qtype = gguf.GGMLQuantizationType.F32 + elif quant_type is not None: + # caller-specified quantization type for all eligible tensors + data_qtype = quant_type + if (model_arch.shape_fix # NEVER reshape for models such as flux and n_dims > 1 # Skip one-dimensional tensors and n_params >= REARRANGE_THRESHOLD # Only rearrange tensors meeting the size requirement @@ -318,26 +383,32 @@ def handle_tensors(writer, state_dict, model_arch): writer.add_tensor(new_name, data, raw_dtype=data_qtype) -def convert_file(path, dst_path=None, interact=True, overwrite=False): +def convert_file(path, dst_path=None, interact=True, overwrite=False, quant_type_name=None): # load & run model detection logic state_dict = load_state_dict(path) model_arch = detect_arch(state_dict) logging.info(f"* Architecture detected from input: {model_arch.arch}") - # detect & set dtype for output file - dtypes = [x.dtype for x in state_dict.values()] - dtypes = {x:dtypes.count(x) for x in set(dtypes)} - main_dtype = max(dtypes, key=dtypes.get) - - if main_dtype == torch.bfloat16: - ftype_name = "BF16" - ftype_gguf = gguf.LlamaFileType.MOSTLY_BF16 - # elif main_dtype == torch.float32: - # ftype_name = "F32" - # ftype_gguf = None + # resolve quant type from name if provided + quant_type = None + if quant_type_name is not None and quant_type_name in QUANT_TYPE_MAP: + quant_type, ftype_gguf = QUANT_TYPE_MAP[quant_type_name] + ftype_name = quant_type_name else: - ftype_name = "F16" - ftype_gguf = gguf.LlamaFileType.MOSTLY_F16 + # detect & set dtype from source file + dtypes = [x.dtype for x in state_dict.values()] + dtypes = {x: dtypes.count(x) for x in set(dtypes)} + main_dtype = max(dtypes, key=dtypes.get) + + if main_dtype == torch.bfloat16: + ftype_name = "BF16" + ftype_gguf = gguf.LlamaFileType.MOSTLY_BF16 + # elif main_dtype == torch.float32: + # ftype_name = "F32" + # ftype_gguf = None + else: + ftype_name = "F16" + ftype_gguf = gguf.LlamaFileType.MOSTLY_F16 if dst_path is None: dst_path = f"{os.path.splitext(path)[0]}-{ftype_name}.gguf" @@ -356,7 +427,7 @@ def convert_file(path, dst_path=None, interact=True, overwrite=False): if ftype_gguf is not None: writer.add_file_type(ftype_gguf) - handle_tensors(writer, state_dict, model_arch) + handle_tensors(writer, state_dict, model_arch, quant_type=quant_type) writer.write_header_to_file(path=dst_path) writer.write_kv_data_to_file() writer.write_tensors_to_file(progress=True) @@ -371,4 +442,4 @@ def convert_file(path, dst_path=None, interact=True, overwrite=False): if __name__ == "__main__": args = parse_args() - convert_file(args.src, args.dst) \ No newline at end of file + convert_file(args.src, args.dst, quant_type_name=args.quant_type) \ No newline at end of file diff --git a/tools/convert_krea2_gguf.py b/tools/convert_krea2_gguf.py new file mode 100644 index 0000000..8e6fb4b --- /dev/null +++ b/tools/convert_krea2_gguf.py @@ -0,0 +1,144 @@ +""" +Batch GGUF converter for Krea-2 (Base and Turbo) models. + +Produces Q4_0, Q4_1, Q5_0, Q5_1, and Q8_0 GGUF files from a single +BF16/F16 safetensors source file. The converter auto-detects architecture +(ModelKrea2 or ModelIdeogram) from the tensor keys in the source file. + +Usage +----- +# Convert all five quant levels from a BF16 safetensors: +python tools/convert_krea2_gguf.py --src krea2_base_bf16.safetensors + +# Convert specific quant levels only: +python tools/convert_krea2_gguf.py --src krea2_turbo_bf16.safetensors --quant Q5_0 Q8_0 + +# Explicit output directory: +python tools/convert_krea2_gguf.py --src krea2_base_bf16.safetensors --outdir /path/to/output + +Notes +----- +- 1-D tensors (biases, norms, scales) are always kept in F32 regardless of + the requested quant type. +- Tensors with fewer than 1024 elements are kept in F32. +- No model weights are downloaded by this script. You must supply the source + safetensors file yourself. +- This script calls convert_file() from tools/convert.py; both files must be + on the Python path (they are when run from the repo root or from tools/). + +Approximate output sizes per quant level (Krea-2 ~24 B parameters): + Q4_0 ~ 14 GB Q4_1 ~ 15 GB Q5_0 ~ 17 GB Q5_1 ~ 18 GB Q8_0 ~ 26 GB +""" + +import os +import sys +import logging +import argparse +from tqdm import tqdm + +# Allow running directly from tools/ or from the repo root. +_HERE = os.path.dirname(os.path.abspath(__file__)) +_REPO = os.path.dirname(_HERE) +for _p in (_HERE, _REPO): + if _p not in sys.path: + sys.path.insert(0, _p) + +from convert import convert_file, QUANT_TYPE_MAP # noqa: E402 (tools/convert.py) + +logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") + +DEFAULT_QUANTS = ["Q4_0", "Q4_1", "Q5_0", "Q5_1", "Q8_0"] + + +def build_dst_path(src: str, quant: str, outdir: str | None) -> str: + """Derive an output path like -Q4_0.gguf next to the source.""" + stem = os.path.splitext(os.path.basename(src))[0] + filename = f"{stem}-{quant}.gguf" + directory = outdir if outdir else os.path.dirname(os.path.abspath(src)) + return os.path.join(directory, filename) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Batch-convert a Krea-2 safetensors file to multiple GGUF quant levels.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__doc__, + ) + parser.add_argument( + "--src", + required=True, + help="Path to the source BF16/F16 safetensors (or ckpt) file.", + ) + parser.add_argument( + "--outdir", + default=None, + help="Directory to write GGUF files into. Defaults to the same directory as --src.", + ) + parser.add_argument( + "--quant", + nargs="+", + choices=list(QUANT_TYPE_MAP.keys()), + default=DEFAULT_QUANTS, + metavar="QUANT", + help=( + "One or more quantization levels to produce. " + f"Choices: {list(QUANT_TYPE_MAP.keys())}. " + f"Default: {DEFAULT_QUANTS}" + ), + ) + parser.add_argument( + "--overwrite", + action="store_true", + help="Overwrite existing output files without prompting.", + ) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + + if not os.path.isfile(args.src): + logging.error(f"Source file not found: {args.src}") + sys.exit(1) + + if args.outdir: + os.makedirs(args.outdir, exist_ok=True) + + results: list[tuple[str, str, bool]] = [] # (quant, dst_path, success) + + for quant in tqdm(args.quant, desc="GGUF quant levels", unit="quant"): + dst = build_dst_path(args.src, quant, args.outdir) + logging.info(f"\n{'='*60}") + logging.info(f"Converting {os.path.basename(args.src)} → {os.path.basename(dst)}") + logging.info(f"Quant type : {quant}") + logging.info(f"Output : {dst}") + logging.info(f"{'='*60}") + + try: + out_path, model_arch = convert_file( + args.src, + dst_path=dst, + interact=False, + overwrite=args.overwrite, + quant_type_name=quant, + ) + logging.info(f"[OK] {quant} → {out_path} (arch={model_arch.arch})") + results.append((quant, out_path, True)) + except Exception as exc: + logging.error(f"[FAILED] {quant}: {exc}") + results.append((quant, dst, False)) + + # Summary + print(f"\n{'='*60}") + print("Conversion summary:") + for quant, path, ok in results: + status = "OK " if ok else "FAIL" + print(f" [{status}] {quant:6s} {path}") + print(f"{'='*60}") + + if not all(ok for _, _, ok in results): + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/tools/krea2_gguf_readme.md b/tools/krea2_gguf_readme.md new file mode 100644 index 0000000..5321d57 --- /dev/null +++ b/tools/krea2_gguf_readme.md @@ -0,0 +1,100 @@ +--- +license: other +license_name: krea-2-community-license +license_link: https://huggingface.co/krea/Krea-2-Turbo/blob/main/LICENSE +pipeline_tag: text-to-image +tags: + - image-generation + - diffusion + - flow-matching + - dit + - krea2 + - quantization + - comfyui +library_name: gguf +base_model: + - krea/Krea-2-Raw + - krea/Krea-2-Turbo +--- + +# Krea 2 GGUF + +Quantized GGUF diffusion transformer weights for [Krea 2](https://huggingface.co/krea/Krea-2-Turbo), +converted from the original BF16 releases for use with ComfyUI GGUF loader nodes. + +This repository provides GGUF files for two checkpoints of the Krea 2 model family: + +- `krea2_raw_bf16-*.gguf` — converted from [krea/Krea-2-Raw](https://huggingface.co/krea/Krea-2-Raw), the base release checkpoint. +- `krea2_turbo_bf16-*.gguf` — converted from [krea/Krea-2-Turbo](https://huggingface.co/krea/Krea-2-Turbo), the post-trained checkpoint with additional fine-tuning and distillation. + +Krea 2 is a 12-billion parameter Diffusion Transformer with a novel architecture featuring layerwise and refiner text-fusion blocks. It is not based on Flux or any prior open-weight architecture. + +These files are not a complete standalone Krea 2 package. Your workflow still needs the text encoder and VAE components. + +## ComfyUI Support + +Use these models with the ComfyUI nodes from [molbal/ComfyUI-GGUF](https://github.com/molbal/ComfyUI-GGUF). +Install that custom node repository into your ComfyUI `custom_nodes` folder, then restart ComfyUI. + +> **Important:** This repository requires [molbal/ComfyUI-GGUF](https://github.com/molbal/ComfyUI-GGUF), +> which is a fork of [city96/ComfyUI-GGUF](https://github.com/city96/ComfyUI-GGUF) with added support +> for the Krea 2 architecture. The original city96 plugin does **not** support these files. (as of 2026-06-24) + +Place the downloaded `.gguf` files in one of ComfyUI's diffusion model folders: + +``` +ComfyUI/models/diffusion_models/ +ComfyUI/models/unet/ +``` + +Load the file with `Unet Loader (GGUF)` in a Krea 2 workflow. Krea 2 uses a single transformer +(unlike Ideogram 4, there is no separate unconditional transformer component). + +## Files + +| Quant | Raw (base) | Turbo | Size | +|-------|-----------|-------------------|------| +| Q4_0 | krea2_raw_bf16-Q4_0.gguf | krea2_turbo_bf16-Q4_0.gguf | 7.74 GB | +| Q4_1 | krea2_raw_bf16-Q4_1.gguf | krea2_turbo_bf16-Q4_1.gguf | 8.47 GB | +| Q5_0 | krea2_raw_bf16-Q5_0.gguf | krea2_turbo_bf16-Q5_0.gguf | 9.20 GB | +| Q5_1 | krea2_raw_bf16-Q5_1.gguf | krea2_turbo_bf16-Q5_1.gguf | 9.93 GB | +| Q8_0 | krea2_raw_bf16-Q8_0.gguf | krea2_turbo_bf16-Q8_0.gguf | 13.56 GB | + +Choose either the Raw or Turbo variant depending on your workflow; they are not paired with each other. + +## Which Checkpoint to Use + +| Checkpoint | Steps | CFG | Notes | +|------------|-------|-----|-------| +| **Turbo** | 4–8 | 0.0 | Distilled; CFG-free. Fast, good for most use cases. | +| **Raw** | 20–30 | 3.0–7.0 | Full CFG; more controllable, higher inference cost. | + +The Turbo checkpoint has been post-trained with distillation and runs well at 8 steps with `CFG=1`. The Raw checkpoint behaves like a standard flow-matching DiT and benefits from more steps and positive CFG. + +## When GGUFs Are Worth the Tradeoff + +The BF16 source weights for Krea 2 are 26.6 GB each — far beyond what most consumer GPUs can hold entirely in VRAM. GGUFs make sense when: + +- **Limited VRAM:** Q4_0 at 7.74 GB fits entirely in an 8 GB GPU; Q5_1 at 9.93 GB targets 10–12 GB cards. Running BF16 on these GPUs would require heavy CPU offloading and become impractically slow. +- **CPU offload workflows:** If you are already offloading model layers to RAM, GGUF reduces the RAM footprint proportionally alongside VRAM, which is often the actual bottleneck. +- **Acceptable quality loss at Q5+:** At Q5_0 and above the visual output of Krea 2 is very close to BF16. Q4 levels show mild softening on fine detail but remain usable for most creative tasks. + +GGUFs are generally **not** worth it if you have a 24 GB+ GPU and want maximum fidelity — load the FP8 or BF16 source directly in that case. + +## Download + +Download the file you want from the Files tab, or use the Hugging Face CLI. For example: + +``` +huggingface-cli download molbal/krea2-gguf krea2_turbo_bf16-Q5_1.gguf --local-dir ComfyUI/models/diffusion_models +``` + +## Compatibility Notes + +These are non-K GGUF quantizations intended for PyTorch dequantization in ComfyUI. K-quants are not included because this ComfyUI loading path does not use fused quantized linear kernels. + +Krea 2 GGUF support requires ComfyUI to have the `krea2` architecture registered in its model detection system. If your ComfyUI installation does not recognise the checkpoint, update ComfyUI core and [molbal/ComfyUI-GGUF](https://github.com/molbal/ComfyUI-GGUF) to their latest versions. + +## License + +These files are derived from [krea/Krea-2-Raw](https://huggingface.co/krea/Krea-2-Raw) and [krea/Krea-2-Turbo](https://huggingface.co/krea/Krea-2-Turbo) and follow the [Krea 2 Community License](https://huggingface.co/krea/Krea-2-Turbo/blob/main/LICENSE). From cd2c9bdca2ee0f83760abd2822d9525b30c4fe6d Mon Sep 17 00:00:00 2001 From: molbal Date: Thu, 25 Jun 2026 11:06:51 +0200 Subject: [PATCH 03/10] Update README and loader.py for Krea-2 and Ideogram 4 GGUF support --- README.md | 5 +++++ loader.py | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/README.md b/README.md index 6915927..db4f912 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # ComfyUI-GGUF GGUF Quantization support for native ComfyUI models +> [!NOTE] +> This is a fork of the original nodes, updated to support loading Ideogram 4 GGUFs and Krea 2 GGUFs. +> To use it, clone `https://github.com/city96/ComfyUI-GGUF`and not the original repo. + + This is currently very much WIP. These custom nodes provide support for model files stored in the GGUF format popularized by [llama.cpp](https://github.com/ggerganov/llama.cpp). While quantization wasn't feasible for regular UNET models (conv2d), transformer/DiT models such as flux seem less affected by quantization. This allows running it in much lower bits per weight variable bitrate quants on low-end GPUs. For further VRAM savings, a node to load a quantized version of the T5 text encoder is also included. diff --git a/loader.py b/loader.py index 88f4713..62a00a2 100644 --- a/loader.py +++ b/loader.py @@ -518,6 +518,23 @@ def gguf_clip_loader(path): if arch == "qwen2vl": vsd = gguf_mmproj_loader(path) sd.update(vsd) + if arch == "qwen3vl" and "model.visual.deepstack_merger_list.0.norm.weight" not in sd: + # Standard llama.cpp Qwen3-VL GGUFs omit the visual tower. Without it, + # detect_te_model() mis-classifies the state dict as QWEN3_4B/8B (Qwen3 LM) + # instead of QWEN3VL_4B/8B, so clip_type=KREA2 never selects the 12-layer + # tap encoder and conditioning has shape (B, seq, 2560) instead of (B, seq, 30720). + # Inject zero sentinel tensors with shapes that exactly match the model + # parameters so that load_state_dict(strict=False) doesn't raise a size + # mismatch error while still satisfying detect_te_model()'s key checks. + # deepstack_merger_list.0.norm -> LayerNorm(vis_hidden * 4) shape [merge_dim] + # merger.linear_fc2 -> Linear(merge_dim, lm_hidden) shape [lm_hidden, merge_dim] + ln_key = "model.layers.0.input_layernorm.weight" + lm_hidden = int(sd[ln_key].shape[0]) if ln_key in sd else 2560 + vis_hidden = 1024 if lm_hidden == 2560 else 1152 # Qwen3-VL-4B vs 8B + merge_dim = vis_hidden * 4 # spatial_merge_size=2 + sd["model.visual.deepstack_merger_list.0.norm.weight"] = torch.zeros(merge_dim) + sd["model.visual.merger.linear_fc2.weight"] = torch.zeros(lm_hidden, merge_dim) + logging.info(f"qwen3vl GGUF: injected visual marker tensors (lm_hidden={lm_hidden}, merge_dim={merge_dim}) for model type detection") elif arch == "ideogram": # Dequantize Ideogram model for inference logging.info("Dequantizing Ideogram model for inference...") From e532b7d77ab14860301f93d388105d0587fcf0b7 Mon Sep 17 00:00:00 2001 From: molbal Date: Thu, 25 Jun 2026 20:34:23 +0200 Subject: [PATCH 04/10] Update README to include new pre-quantized models and clarify quantization support limitations --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index db4f912..6ff410c 100644 --- a/README.md +++ b/README.md @@ -40,15 +40,22 @@ Simply use the GGUF Unet loader found under the `bootleg` category. Place the .g LoRA loading is experimental but it should work with just the built-in LoRA loader node(s). -Pre-quantized models: +Pre-quantized models (🍴 icon on ones added by this fork): - [flux1-dev GGUF](https://huggingface.co/city96/FLUX.1-dev-gguf) - [flux1-schnell GGUF](https://huggingface.co/city96/FLUX.1-schnell-gguf) - [stable-diffusion-3.5-large GGUF](https://huggingface.co/city96/stable-diffusion-3.5-large-gguf) - [stable-diffusion-3.5-large-turbo GGUF](https://huggingface.co/city96/stable-diffusion-3.5-large-turbo-gguf) +- [Krea 2 (Both Turbo and Raw)](https://huggingface.co/molbal/krea2-gguf) 🍴 +- [Ideogram 4](https://huggingface.co/molbal/ideogram-4-gguff) 🍴 + + +> [!IMPORTANT] +> Please note, that this fork does not support _K quants on diffusion models, only on text encoders. They may or may not load, but inference speed may be very slow. There may be other forks, or other custom nodes with better support for these quantization types. Initial support for quantizing T5 has also been added recently, these can be used using the various `*CLIPLoader (gguf)` nodes which can be used inplace of the regular ones. For the CLIP model, use whatever model you were using before for CLIP. The loader can handle both types of files - `gguf` and regular `safetensors`/`bin`. - [t5_v1.1-xxl GGUF](https://huggingface.co/city96/t5-v1_1-xxl-encoder-gguf) +- [Qwen3-VL-4B-Instruct-GGUF](https://huggingface.co/Qwen/Qwen3-VL-4B-Instruct-GGUF)🍴 See the instructions in the [tools](https://github.com/city96/ComfyUI-GGUF/tree/main/tools) folder for how to create your own quants. From f65d0d9e4f048583f740d82b2c8815a57474f760 Mon Sep 17 00:00:00 2001 From: molbal Date: Thu, 25 Jun 2026 22:30:26 +0200 Subject: [PATCH 05/10] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ff410c..9024650 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Pre-quantized models (🍴 icon on ones added by this fork): - [stable-diffusion-3.5-large GGUF](https://huggingface.co/city96/stable-diffusion-3.5-large-gguf) - [stable-diffusion-3.5-large-turbo GGUF](https://huggingface.co/city96/stable-diffusion-3.5-large-turbo-gguf) - [Krea 2 (Both Turbo and Raw)](https://huggingface.co/molbal/krea2-gguf) 🍴 -- [Ideogram 4](https://huggingface.co/molbal/ideogram-4-gguff) 🍴 +- [Ideogram 4](https://huggingface.co/molbal/ideogram-4-gguf) 🍴 > [!IMPORTANT] From 73439d2120c2e465fbdf7eb9e98c17ef592e5da3 Mon Sep 17 00:00:00 2001 From: molbal Date: Sat, 27 Jun 2026 23:57:06 +0200 Subject: [PATCH 06/10] Enhance tensor handling in convert.py to drop incompatible FP8 scale tensors and skip 0-dim scalars during GGUF conversion --- tools/convert.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/tools/convert.py b/tools/convert.py index a79a395..8faa0b2 100644 --- a/tools/convert.py +++ b/tools/convert.py @@ -296,6 +296,17 @@ def load_state_dict(path): return strip_prefix(state_dict) def handle_tensors(writer, state_dict, model_arch, quant_type=None): + # Pre-collect per-tensor FP8 scales (0-dim float32 tensors named "{key}_scale"). + # These must be applied to their FP8 weight tensors before GGUF quantization. + # The actual weight value is fp8_value * scale; ignoring scale produces wrong magnitudes. + fp8_scales = { + k[:-len("_scale")]: v.item() + for k, v in state_dict.items() + if k.endswith("_scale") and len(v.shape) == 0 and v.dtype == torch.float32 + } + if fp8_scales: + tqdm.write(f"Found {len(fp8_scales)} FP8 per-tensor scale(s); will apply before quantization.") + name_lengths = tuple(sorted( ((key, len(key)) for key in state_dict.keys()), key=lambda item: item[1], @@ -314,11 +325,27 @@ def handle_tensors(writer, state_dict, model_arch, quant_type=None): tqdm.write(f"Filtering ignored key: '{key}'") continue + # comfy_quant tensors are FP8 scale factors specific to ComfyUI's custom FP8 format. + # weight_scale tensors are 0-dim per-tensor FP8 scales (e.g. from torchao/fp8 fine-tunes). + # Both are meaningless after GGUF re-quantization and must be dropped so the loader + # does not try to apply them to already-GGUF-dequantized weights. + if key.endswith(".comfy_quant") or key.endswith("_scale") and len(data.shape) == 0: + tqdm.write(f"Dropping FP8 scale tensor: '{key}'") + continue + + # 0-dim (scalar) tensors cannot be stored in GGUF and have no meaningful weight data. + if len(data.shape) == 0: + tqdm.write(f"Skipping 0-dim scalar tensor: '{key}'") + continue + if data.dtype == torch.bfloat16: data = data.to(torch.float32).numpy() # this is so we don't break torch 2.0.X elif data.dtype in [getattr(torch, "float8_e4m3fn", "_invalid"), getattr(torch, "float8_e5m2", "_invalid")]: - data = data.to(torch.float32).numpy() # cast to f32 for correct downstream quant + data = data.to(torch.float32) + if key in fp8_scales: + data = data * fp8_scales[key] # apply per-tensor dequantization scale + data = data.numpy() else: data = data.numpy() @@ -341,7 +368,8 @@ def handle_tensors(writer, state_dict, model_arch, quant_type=None): for dim_size in data_shape: n_params *= dim_size - if old_dtype in (torch.float32, torch.bfloat16): + _FP8_DTYPES = {getattr(torch, "float8_e4m3fn", None), getattr(torch, "float8_e5m2", None)} - {None} + if old_dtype in (torch.float32, torch.bfloat16) or old_dtype in _FP8_DTYPES: if n_dims == 1: # one-dimensional tensors should be kept in F32 # also speeds up inference due to not dequantizing From 3551793a7b5d4bc203464655d5476c50a686504e Mon Sep 17 00:00:00 2001 From: molbal Date: Sun, 26 Jul 2026 18:18:25 +0200 Subject: [PATCH 07/10] Adds Q8_CR quant type --- README.md | 20 +++ fp8/transformer/config.json | 19 --- ...usion_pytorch_model.safetensors.index.json | 1 - fp8/unconditional_transformer/config.json | 19 --- ...usion_pytorch_model.safetensors.index.json | 1 - loader.py | 74 ++++++++- nodes.py | 17 +- ops.py | 150 ++++++++++++++++++ tools/convert.py | 130 +++++++++++++-- 9 files changed, 371 insertions(+), 60 deletions(-) delete mode 100644 fp8/transformer/config.json delete mode 100644 fp8/transformer/diffusion_pytorch_model.safetensors.index.json delete mode 100644 fp8/unconditional_transformer/config.json delete mode 100644 fp8/unconditional_transformer/diffusion_pytorch_model.safetensors.index.json diff --git a/README.md b/README.md index 9024650..6e099b5 100644 --- a/README.md +++ b/README.md @@ -59,3 +59,23 @@ Initial support for quantizing T5 has also been added recently, these can be use - [Qwen3-VL-4B-Instruct-GGUF](https://huggingface.co/Qwen/Qwen3-VL-4B-Instruct-GGUF)🍴 See the instructions in the [tools](https://github.com/city96/ComfyUI-GGUF/tree/main/tools) folder for how to create your own quants. + +## Native weight-only quantization + +The converter supports two custom, global quantization modes for DiT/transformer +UNets: + +- `Q8_CR` stores eligible 2-D Linear weights as per-row INT8 ConvRot. It uses + ComfyUI's native `TensorWiseINT8Layout` path, so weights remain INT8 during + inference. +- `Q4_PT` stores eligible 2-D Linear weights as group-size-64 INT4 and uses + PyTorch's CUDA `_weight_int4pack_mm` path. The first use of each layer creates + the device-specific packed representation; weights are not dequantized. + +Both modes keep 1-D, small, and architecture-designated high-precision tensors +in FP32. Conv2d weights remain FP16 because these modes accelerate Linear +matrix multiplication only. `Q4_PT` requires CUDA with FP16 or BF16 +activations; it intentionally has no CPU or dequantization fallback. + +Reconvert any `Q8_CR` GGUF created before ConvRot weights were marked as +pre-rotated. Older files load safely with native non-rotated INT8 instead. diff --git a/fp8/transformer/config.json b/fp8/transformer/config.json deleted file mode 100644 index da6e1e8..0000000 --- a/fp8/transformer/config.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "_class_name": "Ideogram4Transformer2DModel", - "_diffusers_version": "0.39.0.dev0", - "_name_or_path": "/home/jinli/.cache/huggingface/hub/models--ideogram-ai--debug-ideogram-v4/snapshots/41af6183c9fd9b6254864b0720319ef984535bfc/transformer", - "adaln_dim": 512, - "attention_head_dim": 256, - "in_channels": 128, - "intermediate_size": 12288, - "llm_features_dim": 53248, - "mrope_section": [ - 24, - 20, - 20 - ], - "norm_eps": 1e-05, - "num_attention_heads": 18, - "num_layers": 34, - "rope_theta": 5000000 -} diff --git a/fp8/transformer/diffusion_pytorch_model.safetensors.index.json b/fp8/transformer/diffusion_pytorch_model.safetensors.index.json deleted file mode 100644 index ba1f6d8..0000000 --- a/fp8/transformer/diffusion_pytorch_model.safetensors.index.json +++ /dev/null @@ -1 +0,0 @@ -{"metadata": {"total_size": 9289722624}, "weight_map": {"adaln_proj.bias": "diffusion_pytorch_model.safetensors", "adaln_proj.weight": "diffusion_pytorch_model.safetensors", "adaln_proj.weight_scale": "diffusion_pytorch_model.safetensors", "embed_image_indicator.weight": "diffusion_pytorch_model.safetensors", "final_layer.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "final_layer.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "final_layer.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "final_layer.linear.bias": "diffusion_pytorch_model.safetensors", "final_layer.linear.weight": "diffusion_pytorch_model.safetensors", "final_layer.linear.weight_scale": "diffusion_pytorch_model.safetensors", "input_proj.bias": "diffusion_pytorch_model.safetensors", "input_proj.weight": "diffusion_pytorch_model.safetensors", "input_proj.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.0.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.0.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.0.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.1.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.1.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.1.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.1.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.10.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.10.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.10.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.10.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.11.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.11.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.11.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.11.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.12.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.12.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.12.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.12.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.13.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.13.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.13.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.13.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.14.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.14.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.14.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.14.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.15.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.15.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.15.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.15.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.16.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.16.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.16.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.16.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.17.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.17.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.17.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.17.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.18.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.18.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.18.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.18.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.19.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.19.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.19.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.19.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.2.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.2.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.2.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.2.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.20.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.20.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.20.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.20.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.21.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.21.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.21.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.21.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.22.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.22.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.22.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.22.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.23.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.23.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.23.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.23.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.24.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.24.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.24.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.24.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.25.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.25.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.25.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.25.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.26.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.26.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.26.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.26.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.27.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.27.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.27.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.27.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.28.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.28.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.28.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.28.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.29.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.29.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.29.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.29.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.3.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.3.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.3.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.3.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.30.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.30.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.30.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.30.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.31.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.31.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.31.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.31.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.32.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.32.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.32.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.32.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.33.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.33.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.33.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.33.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.4.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.4.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.4.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.4.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.5.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.5.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.5.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.5.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.6.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.6.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.6.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.6.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.7.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.7.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.7.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.7.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.8.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.8.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.8.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.8.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.9.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.9.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.9.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.9.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "llm_cond_norm.weight": "diffusion_pytorch_model.safetensors", "llm_cond_proj.bias": "diffusion_pytorch_model.safetensors", "llm_cond_proj.weight": "diffusion_pytorch_model.safetensors", "llm_cond_proj.weight_scale": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_in.bias": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_in.weight": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_in.weight_scale": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_out.bias": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_out.weight": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_out.weight_scale": "diffusion_pytorch_model.safetensors"}} \ No newline at end of file diff --git a/fp8/unconditional_transformer/config.json b/fp8/unconditional_transformer/config.json deleted file mode 100644 index f40a02c..0000000 --- a/fp8/unconditional_transformer/config.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "_class_name": "Ideogram4Transformer2DModel", - "_diffusers_version": "0.39.0.dev0", - "_name_or_path": "/home/jinli/.cache/huggingface/hub/models--ideogram-ai--debug-ideogram-v4/snapshots/41af6183c9fd9b6254864b0720319ef984535bfc/unconditional_transformer", - "adaln_dim": 512, - "attention_head_dim": 256, - "in_channels": 128, - "intermediate_size": 12288, - "llm_features_dim": 53248, - "mrope_section": [ - 24, - 20, - 20 - ], - "norm_eps": 1e-05, - "num_attention_heads": 18, - "num_layers": 34, - "rope_theta": 5000000 -} diff --git a/fp8/unconditional_transformer/diffusion_pytorch_model.safetensors.index.json b/fp8/unconditional_transformer/diffusion_pytorch_model.safetensors.index.json deleted file mode 100644 index ba1f6d8..0000000 --- a/fp8/unconditional_transformer/diffusion_pytorch_model.safetensors.index.json +++ /dev/null @@ -1 +0,0 @@ -{"metadata": {"total_size": 9289722624}, "weight_map": {"adaln_proj.bias": "diffusion_pytorch_model.safetensors", "adaln_proj.weight": "diffusion_pytorch_model.safetensors", "adaln_proj.weight_scale": "diffusion_pytorch_model.safetensors", "embed_image_indicator.weight": "diffusion_pytorch_model.safetensors", "final_layer.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "final_layer.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "final_layer.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "final_layer.linear.bias": "diffusion_pytorch_model.safetensors", "final_layer.linear.weight": "diffusion_pytorch_model.safetensors", "final_layer.linear.weight_scale": "diffusion_pytorch_model.safetensors", "input_proj.bias": "diffusion_pytorch_model.safetensors", "input_proj.weight": "diffusion_pytorch_model.safetensors", "input_proj.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.0.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.0.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.0.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.0.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.0.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.0.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.1.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.1.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.1.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.1.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.1.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.1.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.1.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.10.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.10.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.10.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.10.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.10.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.10.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.10.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.11.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.11.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.11.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.11.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.11.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.11.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.11.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.12.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.12.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.12.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.12.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.12.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.12.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.12.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.13.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.13.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.13.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.13.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.13.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.13.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.13.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.14.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.14.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.14.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.14.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.14.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.14.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.14.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.15.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.15.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.15.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.15.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.15.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.15.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.15.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.16.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.16.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.16.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.16.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.16.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.16.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.16.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.17.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.17.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.17.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.17.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.17.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.17.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.17.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.18.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.18.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.18.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.18.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.18.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.18.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.18.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.19.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.19.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.19.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.19.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.19.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.19.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.19.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.2.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.2.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.2.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.2.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.2.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.2.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.2.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.20.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.20.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.20.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.20.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.20.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.20.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.20.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.21.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.21.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.21.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.21.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.21.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.21.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.21.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.22.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.22.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.22.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.22.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.22.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.22.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.22.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.23.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.23.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.23.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.23.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.23.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.23.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.23.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.24.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.24.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.24.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.24.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.24.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.24.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.24.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.25.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.25.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.25.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.25.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.25.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.25.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.25.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.26.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.26.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.26.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.26.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.26.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.26.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.26.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.27.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.27.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.27.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.27.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.27.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.27.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.27.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.28.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.28.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.28.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.28.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.28.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.28.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.28.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.29.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.29.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.29.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.29.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.29.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.29.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.29.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.3.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.3.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.3.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.3.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.3.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.3.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.3.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.30.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.30.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.30.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.30.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.30.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.30.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.30.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.31.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.31.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.31.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.31.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.31.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.31.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.31.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.32.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.32.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.32.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.32.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.32.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.32.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.32.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.33.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.33.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.33.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.33.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.33.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.33.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.33.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.4.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.4.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.4.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.4.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.4.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.4.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.4.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.5.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.5.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.5.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.5.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.5.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.5.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.5.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.6.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.6.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.6.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.6.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.6.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.6.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.6.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.7.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.7.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.7.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.7.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.7.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.7.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.7.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.8.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.8.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.8.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.8.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.8.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.8.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.8.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.9.adaln_modulation.bias": "diffusion_pytorch_model.safetensors", "layers.9.adaln_modulation.weight": "diffusion_pytorch_model.safetensors", "layers.9.adaln_modulation.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.attention.norm_k.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.norm_q.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.o.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.o.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.attention.qkv.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention.qkv.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.attention_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.9.attention_norm2.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w1.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w1.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w2.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w2.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w3.weight": "diffusion_pytorch_model.safetensors", "layers.9.feed_forward.w3.weight_scale": "diffusion_pytorch_model.safetensors", "layers.9.ffn_norm1.weight": "diffusion_pytorch_model.safetensors", "layers.9.ffn_norm2.weight": "diffusion_pytorch_model.safetensors", "llm_cond_norm.weight": "diffusion_pytorch_model.safetensors", "llm_cond_proj.bias": "diffusion_pytorch_model.safetensors", "llm_cond_proj.weight": "diffusion_pytorch_model.safetensors", "llm_cond_proj.weight_scale": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_in.bias": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_in.weight": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_in.weight_scale": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_out.bias": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_out.weight": "diffusion_pytorch_model.safetensors", "t_embedding.mlp_out.weight_scale": "diffusion_pytorch_model.safetensors"}} \ No newline at end of file diff --git a/loader.py b/loader.py index 62a00a2..657ddd8 100644 --- a/loader.py +++ b/loader.py @@ -3,9 +3,9 @@ import logging import torch import gguf +import json import re import os - from .ops import GGMLTensor from .dequant import is_quantized, dequantize_tensor @@ -177,6 +177,78 @@ def gguf_sd_loader(path, handle_prefix="model.diffusion_model.", is_text_model=F "arch_str": arch_str, "metadata": get_gguf_metadata(reader) } + + # Detect custom ComfyUI native quantization metadata + warned_unrotated_convrot = False + for field_name in reader.fields: + if not field_name.startswith("comfy.gguf.quant."): + continue + key = field_name[len("comfy.gguf.quant."):] + field = reader.get_field(field_name) + quant_conf = json.loads(str(field.parts[field.data[-1]], "utf-8")) + fmt = quant_conf.get("format") + + weight_key = key + scale_key = f"{key}_scale" + if weight_key not in state_dict or scale_key not in state_dict: + logging.warning(f"Missing custom quant tensors for {weight_key}") + continue + + weight_ggml = state_dict[weight_key] + scale_ggml = state_dict[scale_key] + + if fmt == "int8_tensorwise": + if quant_conf.get("convrot") and not quant_conf.get("weight_rotated", False): + if not warned_unrotated_convrot: + logging.warning( + "Disabling ConvRot because this GGUF does not mark its weights " + "as pre-rotated. Reconvert with the current converter to enable ConvRot." + ) + warned_unrotated_convrot = True + quant_conf["convrot"] = False + quant_conf.pop("convrot_groupsize", None) + elif quant_conf.get("convrot"): + groupsize = quant_conf.get("convrot_groupsize", 256) + if weight_ggml.tensor_shape[-1] % groupsize != 0: + logging.warning( + "Disabling ConvRot for %s because %d input features are not " + "divisible by group size %d.", + weight_key, + weight_ggml.tensor_shape[-1], + groupsize, + ) + quant_conf["convrot"] = False + quant_conf.pop("convrot_groupsize", None) + + # Convert to ComfyUI native state-dict layout + weight = weight_ggml.data.view(torch.int8).reshape(weight_ggml.tensor_shape) + scale = scale_ggml.data.view(torch.float32).reshape(scale_ggml.tensor_shape) + + state_dict[weight_key] = torch.nn.Parameter(weight, requires_grad=False) + state_dict[scale_key] = torch.nn.Parameter(scale, requires_grad=False) + + layer_prefix = weight_key[:weight_key.rfind("weight")] + quant_json = json.dumps(quant_conf) + state_dict[f"{layer_prefix}comfy_quant"] = torch.nn.Parameter( + torch.tensor(list(quant_json.encode("utf-8")), dtype=torch.uint8), + requires_grad=False, + ) + extra["gguf_quant_mode"] = "int8_convrot" + + elif fmt == "int4_pytorch": + # Keep serialized INT4 weights; custom ops pack them for the CUDA kernel. + weight = weight_ggml.data.view(torch.uint8).reshape(weight_ggml.tensor_shape) + scale = scale_ggml.data.view(torch.float32).reshape(scale_ggml.tensor_shape) + + state_dict[weight_key] = torch.nn.Parameter(weight, requires_grad=False) + state_dict[scale_key] = torch.nn.Parameter(scale, requires_grad=False) + layer_prefix = weight_key[:weight_key.rfind("weight")] + state_dict[f"{layer_prefix}comfy_quant"] = torch.nn.Parameter( + torch.tensor(list(json.dumps(quant_conf).encode("utf-8")), dtype=torch.uint8), + requires_grad=False, + ) + extra["gguf_quant_mode"] = "int4_pytorch" + return (state_dict, extra) # for remapping llama.cpp -> original key names diff --git a/nodes.py b/nodes.py index 4683514..fab0749 100644 --- a/nodes.py +++ b/nodes.py @@ -13,7 +13,7 @@ import comfy.model_management import folder_paths -from .ops import GGMLOps, move_patch_to_device +from .ops import GGMLOps, GGUFQ4Ops, get_gguf_q8_ops, move_patch_to_device from .loader import gguf_sd_loader, gguf_clip_loader from .dequant import is_quantized, is_torch_compatible @@ -148,7 +148,18 @@ def INPUT_TYPES(s): TITLE = "Unet Loader (GGUF)" def load_unet(self, unet_name, dequant_dtype=None, patch_dtype=None, patch_on_device=None): - ops = GGMLOps() + unet_path = folder_paths.get_full_path("unet", unet_name) + sd, extra = gguf_sd_loader(unet_path) + + mode = extra.get("gguf_quant_mode") + if mode == "int8_convrot": + # Use ComfyUI native INT8 path (weights stay INT8) + ops = get_gguf_q8_ops(compute_dtype=torch.bfloat16)() + elif mode == "int4_pytorch": + # Use custom INT4 ops class (currently dequant fallback) + ops = GGUFQ4Ops() + else: + ops = GGMLOps() if dequant_dtype in ("default", None): ops.Linear.dequant_dtype = None @@ -165,8 +176,6 @@ def load_unet(self, unet_name, dequant_dtype=None, patch_dtype=None, patch_on_de ops.Linear.patch_dtype = getattr(torch, patch_dtype) # init model - unet_path = folder_paths.get_full_path("unet", unet_name) - sd, extra = gguf_sd_loader(unet_path) kwargs = {} valid_params = inspect.signature(comfy.sd.load_diffusion_model_state_dict).parameters diff --git a/ops.py b/ops.py index 9c26052..5b0c839 100644 --- a/ops.py +++ b/ops.py @@ -1,5 +1,6 @@ # (c) City96 || Apache-2.0 (apache.org/licenses/LICENSE-2.0) import gguf +import json import torch import logging @@ -313,3 +314,152 @@ def move_patch_to_device(item, device): return [move_patch_to_device(x, device) for x in item] else: return item + +def get_gguf_q8_ops(compute_dtype=torch.bfloat16, full_precision_mm=False): + """ + Factory for an ops class that uses ComfyUI's native mixed_precision_ops INT8 path. + Weights are kept as INT8 and matmul uses comfy_kitchen's TensorWiseINT8Layout. + """ + BaseOps = comfy.ops.mixed_precision_ops( + quant_config={}, + compute_dtype=compute_dtype, + full_precision_mm=full_precision_mm, + ) + + class GGUFQ8Ops(BaseOps): + class Linear(BaseOps.Linear): + def __init__(self, in_features, out_features, bias=True, device=None, dtype=None): + # Lazy init: don't allocate weight here; it will be loaded from state dict + torch.nn.Module.__init__(self) + self.factory_kwargs = {"device": device, "dtype": BaseOps._compute_dtype} + self.in_features = in_features + self.out_features = out_features + self.weight = None + if bias: + self.bias = torch.nn.Parameter(torch.empty(out_features, **self.factory_kwargs)) + else: + self.register_parameter("bias", None) + self._orig_shape = (out_features, in_features) + self.tensor_class = None + self._full_precision_mm = BaseOps._full_precision_mm + self._full_precision_mm_config = False + + def _load_from_state_dict(self, *args): + return comfy.ops._load_quantized_module( + self, + torch.nn.Module._load_from_state_dict.__get__(self, type(self)), + *args, + load_extra_params=True, + ) + + return GGUFQ8Ops + + +class GGUFQ4Ops(comfy.ops.manual_cast): + """ + Ops class for PyTorch's CUDA INT4 matrix multiplication. + + GGUF stores a portable nibble-packed representation. The first forward pass + for each layer converts it to PyTorch's hardware-specific packed layout on + the active CUDA device, and subsequent forwards reuse that packed tensor. + """ + class Linear(torch.nn.Module, comfy.ops.CastWeightBiasOp): + def __init__(self, in_features, out_features, bias=True, device=None, dtype=None): + torch.nn.Module.__init__(self) + self.in_features = in_features + self.out_features = out_features + self.weight = None + self.register_parameter("bias", None) + self._orig_shape = (out_features, in_features) + self._group_size = None + self._pad = 0 + self._orig_in_features = in_features + self._int4_cache = {} + self._is_int4 = False + + def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs): + weight_key = f"{prefix}weight" + scale_key = f"{prefix}weight_scale" + quant_key = f"{prefix}comfy_quant" + + weight = state_dict.pop(weight_key, None) + scale = state_dict.pop(scale_key, None) + quant_raw = state_dict.pop(quant_key, None) + + bias_key = f"{prefix}bias" + bias = state_dict.pop(bias_key, None) + if quant_raw is None: + if weight is None: + missing_keys.append(weight_key) + return + self.weight = torch.nn.Parameter(torch.Tensor(weight), requires_grad=False) + if bias is not None: + self.bias = torch.nn.Parameter(torch.Tensor(bias), requires_grad=False) + self._is_int4 = False + return + + if weight is None or scale is None: + raise RuntimeError(f"Missing INT4 tensors for {prefix}") + + quant_conf = json.loads(bytes(quant_raw.tolist()).decode("utf-8")) + if quant_conf.get("format") != "int4_pytorch": + raise ValueError(f"Unsupported INT4 format for {prefix}") + self._group_size = quant_conf["group_size"] + self._pad = quant_conf.get("pad", 0) + orig_shape = tuple(quant_conf["orig_shape"]) + self._orig_in_features = orig_shape[1] + self._orig_shape = orig_shape + self.out_features = orig_shape[0] + + self.weight = torch.nn.Parameter(weight, requires_grad=False) + self.weight_scale = torch.nn.Parameter(scale, requires_grad=False) + self._is_int4 = True + + if bias is not None: + self.bias = torch.nn.Parameter(bias, requires_grad=False) + for key in (weight_key, scale_key, quant_key, bias_key): + if key in missing_keys: + missing_keys.remove(key) + + @staticmethod + def _inner_k_tiles(): + return 2 + + def _packed_weight(self, device, dtype): + if device.type != "cuda": + raise RuntimeError("Q4_PT requires CUDA; no dequantization fallback is available.") + if dtype not in (torch.float16, torch.bfloat16): + raise RuntimeError(f"Q4_PT requires FP16 or BF16 activations, got {dtype}.") + + cache_key = (device, dtype) + cached = self._int4_cache.get(cache_key) + if cached is not None: + return cached + + serialized = self.weight.to(device=device, dtype=torch.uint8, non_blocking=True) + packed = torch._convert_weight_to_int4pack(serialized, self._inner_k_tiles()) + scale_and_offset = self.weight_scale.to(device=device, dtype=dtype, non_blocking=True) + self._int4_cache[cache_key] = (packed, scale_and_offset) + return packed, scale_and_offset + + def forward(self, input): + if self.weight is None: + raise RuntimeError("Q4_PT weight was not loaded.") + if not self._is_int4: + weight = self.weight.to(device=input.device, dtype=input.dtype) + bias = self.bias.to(device=input.device, dtype=input.dtype) if self.bias is not None else None + return torch.nn.functional.linear(input, weight, bias) + if self.weight_function or self.bias_function: + raise RuntimeError("Q4_PT does not support weight patches or LoRAs without dequantization.") + input_shape = input.shape + input_2d = input.reshape(-1, input_shape[-1]) + if self._pad: + input_2d = torch.nn.functional.pad(input_2d, (0, self._pad)) + + packed, scale_and_offset = self._packed_weight(input.device, input.dtype) + output = torch._weight_int4pack_mm( + input_2d, packed, self._group_size, scale_and_offset + ) + if self.bias is not None: + output = output + self.bias.to(device=input.device, dtype=input.dtype) + return output.reshape(*input_shape[:-1], self.out_features) diff --git a/tools/convert.py b/tools/convert.py index 8faa0b2..f2813c2 100644 --- a/tools/convert.py +++ b/tools/convert.py @@ -1,6 +1,7 @@ # (c) City96 || Apache-2.0 (apache.org/licenses/LICENSE-2.0) import os import gguf +import json import torch import logging import argparse @@ -228,8 +229,84 @@ def detect_arch(state_dict): "Q5_0": (gguf.GGMLQuantizationType.Q5_0, gguf.LlamaFileType.MOSTLY_Q5_0), "Q4_1": (gguf.GGMLQuantizationType.Q4_1, gguf.LlamaFileType.MOSTLY_Q4_1), "Q4_0": (gguf.GGMLQuantizationType.Q4_0, gguf.LlamaFileType.MOSTLY_Q4_0), + "Q8_CR": (gguf.GGMLQuantizationType.I8, None), # INT8 ConvRot (ComfyUI native) + "Q4_PT": (gguf.GGMLQuantizationType.I8, None), # INT4 PyTorch (packed as raw bytes) } + +def quantize_int8_convrot(weight, convrot_groupsize=256): + """ + Quantize a 2D Linear weight to INT8 with ConvRot grouping. + Uses per-output-channel scales to match ComfyUI's TensorWiseINT8Layout. + """ + weight = weight.to(torch.float32) + orig_shape = tuple(weight.shape) + groupsize = next( + ( + size + for size in (convrot_groupsize, 64, 16, 4) + if size <= weight.shape[1] and weight.shape[1] % size == 0 + ), + None, + ) + if groupsize is not None: + from comfy_kitchen.tensor.int8_utils import _build_hadamard, _rotate_weight + + hadamard = _build_hadamard(groupsize, device=weight.device, dtype=weight.dtype) + weight = _rotate_weight(weight, hadamard, groupsize) + + scale = weight.abs().amax(dim=1, keepdim=True).clamp_min(1e-9) / 127.0 + qdata = (weight / scale).round().clamp(-128, 127).to(torch.int8) + quant_conf = { + "format": "int8_tensorwise", + "convrot": groupsize is not None, + "weight_rotated": groupsize is not None, + "per_row": True, + } + if groupsize is not None: + quant_conf["convrot_groupsize"] = groupsize + return qdata, scale, quant_conf, orig_shape + + +def quantize_int4_pytorch(weight, group_size=64): + """ + Quantize a 2D Linear weight for PyTorch's native INT4 kernel. + Weights are serialized as packed uint8 [n, k//2]. The runtime converts this + portable representation to PyTorch's device-specific INT4 layout. + """ + orig_shape = tuple(weight.shape) + n, k = orig_shape + weight = weight.to(torch.float32) + + pad = 0 + if k % group_size != 0: + pad = group_size - (k % group_size) + weight = torch.nn.functional.pad(weight, (0, pad)) + k = k + pad + + w_grouped = weight.reshape(n, k // group_size, group_size) + w_min = w_grouped.amin(dim=-1, keepdim=True) + w_max = w_grouped.amax(dim=-1, keepdim=True) + scale = (w_max - w_min) / 15.0 + scale = scale.clamp_min(1e-9) + q = ((w_grouped - w_min) / scale).round().clamp(0, 15).to(torch.uint8) + + q_flat = q.reshape(n, k) + packed = ((q_flat[:, 0::2] << 4) | q_flat[:, 1::2]).to(torch.uint8) + + qsz = torch.zeros(k // group_size, n, 2, dtype=torch.float32) + qsz[:, :, 0] = scale.reshape(n, k // group_size).t() + # _weight_int4pack_mm dequantizes as (q - 8) * scale + offset. + qsz[:, :, 1] = (w_min + 8 * scale).reshape(n, k // group_size).t() + + quant_conf = { + "format": "int4_pytorch", + "group_size": group_size, + "orig_shape": orig_shape, + "pad": pad, + } + return packed, qsz, quant_conf, orig_shape + def parse_args(): parser = argparse.ArgumentParser( description="Convert diffusion model safetensors/ckpt to GGUF." @@ -295,7 +372,7 @@ def load_state_dict(path): return strip_prefix(state_dict) -def handle_tensors(writer, state_dict, model_arch, quant_type=None): +def handle_tensors(writer, state_dict, model_arch, quant_type=None, quant_type_name=None): # Pre-collect per-tensor FP8 scales (0-dim float32 tensors named "{key}_scale"). # These must be applied to their FP8 weight tensors before GGUF quantization. # The actual weight value is fp8_value * scale; ignoring scale produces wrong magnitudes. @@ -358,35 +435,58 @@ def handle_tensors(writer, state_dict, model_arch, quant_type=None): else: data_qtype = gguf.GGMLQuantizationType.F16 - # The max no. of dimensions that can be handled by the quantization code is 4 - if len(data.shape) > MAX_TENSOR_DIMS: + # The max no. of dimensions that can be handled by the quantization code is 4. + if n_dims > MAX_TENSOR_DIMS: model_arch.handle_nd_tensor(key, data) - continue # needs to be added back later + continue - # get number of parameters (AKA elements) in this tensor n_params = 1 for dim_size in data_shape: n_params *= dim_size _FP8_DTYPES = {getattr(torch, "float8_e4m3fn", None), getattr(torch, "float8_e5m2", None)} - {None} - if old_dtype in (torch.float32, torch.bfloat16) or old_dtype in _FP8_DTYPES: + apply_quantization_rules = ( + quant_type_name in ("Q8_CR", "Q4_PT") + or old_dtype in (torch.float32, torch.bfloat16) + or old_dtype in _FP8_DTYPES + ) + if apply_quantization_rules: if n_dims == 1: - # one-dimensional tensors should be kept in F32 - # also speeds up inference due to not dequantizing + # One-dimensional tensors should be kept in F32. data_qtype = gguf.GGMLQuantizationType.F32 - elif n_params <= QUANTIZATION_THRESHOLD: - # very small tensors data_qtype = gguf.GGMLQuantizationType.F32 - elif any(x in key for x in model_arch.keys_hiprec): - # tensors that require max precision data_qtype = gguf.GGMLQuantizationType.F32 - + elif n_dims == 4 and "conv" in key.lower(): + # Native quantized paths are Linear-only. + data_qtype = gguf.GGMLQuantizationType.F16 elif quant_type is not None: - # caller-specified quantization type for all eligible tensors data_qtype = quant_type + if quant_type_name in ("Q8_CR", "Q4_PT") and n_dims > 1 and n_dims != 2: + # Custom native layouts only represent Linear matrices. + data_qtype = gguf.GGMLQuantizationType.F16 + elif quant_type_name == "Q4_PT" and data_shape[0] % 8 != 0: + # PyTorch's CUDA INT4 layout has an eight-output-channel tile. + data_qtype = gguf.GGMLQuantizationType.F16 + + # Custom quantization paths for Q8_CR and Q4_PT + if quant_type_name in ("Q8_CR", "Q4_PT") and data_qtype == quant_type and n_dims == 2: + if quant_type_name == "Q8_CR": + qdata, scale, quant_conf, orig_shape = quantize_int8_convrot(torch.from_numpy(data)) + writer.add_tensor(key, qdata.numpy(), raw_dtype=gguf.GGMLQuantizationType.I8) + writer.add_tensor(f"{key}_scale", scale.numpy(), raw_dtype=gguf.GGMLQuantizationType.F32) + writer.add_string(f"comfy.gguf.quant.{key}", json.dumps(quant_conf)) + continue + + elif quant_type_name == "Q4_PT": + packed, qsz, quant_conf, orig_shape = quantize_int4_pytorch(torch.from_numpy(data)) + writer.add_tensor(key, packed.numpy(), raw_dtype=gguf.GGMLQuantizationType.I8) + writer.add_tensor(f"{key}_scale", qsz.numpy(), raw_dtype=gguf.GGMLQuantizationType.F32) + writer.add_string(f"comfy.gguf.quant.{key}", json.dumps(quant_conf)) + continue + if (model_arch.shape_fix # NEVER reshape for models such as flux and n_dims > 1 # Skip one-dimensional tensors and n_params >= REARRANGE_THRESHOLD # Only rearrange tensors meeting the size requirement @@ -455,7 +555,7 @@ def convert_file(path, dst_path=None, interact=True, overwrite=False, quant_type if ftype_gguf is not None: writer.add_file_type(ftype_gguf) - handle_tensors(writer, state_dict, model_arch, quant_type=quant_type) + handle_tensors(writer, state_dict, model_arch, quant_type=quant_type, quant_type_name=quant_type_name) writer.write_header_to_file(path=dst_path) writer.write_kv_data_to_file() writer.write_tensors_to_file(progress=True) From 89f20c88d544cdb8f7f9bf310292344517effe22 Mon Sep 17 00:00:00 2001 From: molbal Date: Sun, 26 Jul 2026 22:04:46 +0200 Subject: [PATCH 08/10] Q8_CR follow-up work --- README.md | 36 +- loader.py | 1263 +++++++++++++++++++++++----------------------- nodes.py | 677 ++++++++++++------------- ops.py | 950 +++++++++++++++++----------------- tools/convert.py | 1139 +++++++++++++++++++++-------------------- 5 files changed, 2049 insertions(+), 2016 deletions(-) diff --git a/README.md b/README.md index 6e099b5..97c152a 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,27 @@ # ComfyUI-GGUF -GGUF Quantization support for native ComfyUI models +GGUF Quantization support for native ComfyUI models including the custom Q8_CR > [!NOTE] > This is a fork of the original nodes, updated to support loading Ideogram 4 GGUFs and Krea 2 GGUFs. > To use it, clone `https://github.com/city96/ComfyUI-GGUF`and not the original repo. - -This is currently very much WIP. These custom nodes provide support for model files stored in the GGUF format popularized by [llama.cpp](https://github.com/ggerganov/llama.cpp). - While quantization wasn't feasible for regular UNET models (conv2d), transformer/DiT models such as flux seem less affected by quantization. This allows running it in much lower bits per weight variable bitrate quants on low-end GPUs. For further VRAM savings, a node to load a quantized version of the T5 text encoder is also included. -![Comfy_Flux1_dev_Q4_0_GGUF_1024](https://github.com/user-attachments/assets/70d16d97-c522-4ef4-9435-633f128644c8) - -Note: The "Force/Set CLIP Device" is **NOT** part of this node pack. Do not install it if you only have one GPU. Do not set it to cuda:0 then complain about OOM errors if you do not undestand what it is for. There is not need to copy the workflow above, just use your own workflow and replace the stock "Load Diffusion Model" with the "Unet Loader (GGUF)" node. - ## Installation > [!IMPORTANT] -> Make sure your ComfyUI is on a recent-enough version to support custom ops when loading the UNET-only. +> Make sure your ComfyUI is on v0.27.0 or later. To install the custom node normally, git clone this repository into your custom nodes folder (`ComfyUI/custom_nodes`) and install the only dependency for inference (`pip install --upgrade gguf`) ``` -git clone https://github.com/city96/ComfyUI-GGUF +git clone https://github.com/molbal/ComfyUI-GGUF ``` To install the custom node on a standalone ComfyUI release, open a CMD inside the "ComfyUI_windows_portable" folder (where your `run_nvidia_gpu.bat` file is) and use the following commands: ``` -git clone https://github.com/city96/ComfyUI-GGUF ComfyUI/custom_nodes/ComfyUI-GGUF +git clone https://github.com/molbal/ComfyUI-GGUF ComfyUI/custom_nodes/ComfyUI-GGUF .\python_embeded\python.exe -s -m pip install -r .\ComfyUI\custom_nodes\ComfyUI-GGUF\requirements.txt ``` @@ -68,14 +61,23 @@ UNets: - `Q8_CR` stores eligible 2-D Linear weights as per-row INT8 ConvRot. It uses ComfyUI's native `TensorWiseINT8Layout` path, so weights remain INT8 during inference. -- `Q4_PT` stores eligible 2-D Linear weights as group-size-64 INT4 and uses - PyTorch's CUDA `_weight_int4pack_mm` path. The first use of each layer creates - the device-specific packed representation; weights are not dequantized. -Both modes keep 1-D, small, and architecture-designated high-precision tensors +Q8_CR keeps 1-D, small, and architecture-designated high-precision tensors in FP32. Conv2d weights remain FP16 because these modes accelerate Linear -matrix multiplication only. `Q4_PT` requires CUDA with FP16 or BF16 -activations; it intentionally has no CPU or dequantization fallback. +matrix multiplication only. + +### Q8_CR platform support + +Q8_CR does not require CUDA. It uses ComfyUI's `comfy_kitchen` layout backend: + +- NVIDIA CUDA uses ComfyUI's optimized native INT8 backend when available. +- Linux and non-CUDA environments use the `comfy_kitchen` eager backend. +- CPU Q8_CR loading and inference are supported, but naturally slower than + optimized CUDA inference. + +Q4_PT is retired from conversion and loading until a performant W4A16 backend +is available. Its experimental implementation remains in the source for future +work, but it is no longer selectable or loadable. Reconvert any `Q8_CR` GGUF created before ConvRot weights were marked as pre-rotated. Older files load safely with native non-rotated INT8 instead. diff --git a/loader.py b/loader.py index 657ddd8..e23fef6 100644 --- a/loader.py +++ b/loader.py @@ -1,624 +1,639 @@ -# (c) City96 || Apache-2.0 (apache.org/licenses/LICENSE-2.0) -import warnings -import logging -import torch -import gguf -import json -import re -import os -from .ops import GGMLTensor -from .dequant import is_quantized, dequantize_tensor - -IMG_ARCH_LIST = {"flux", "sd1", "sdxl", "sd3", "aura", "hidream", "cosmos", "ltxv", "hyvid", "wan", "lumina2", "qwen_image", "ideogram", "krea2"} -TXT_ARCH_LIST = {"t5", "t5encoder", "llama", "qwen2vl", "qwen3", "qwen3vl", "gemma3"} -VIS_TYPE_LIST = {"clip-vision", "mmproj"} - -def device_supports_bf16(): - """ - Return True if the active torch device can run bf16 natively. On devices - without native bf16 support, computation silently falls back to fp32 which - is very slow, so callers should load tensors as fp16 instead. - """ - try: - import comfy.model_management - return comfy.model_management.should_use_bf16(comfy.model_management.get_torch_device()) - except Exception: - # If support can't be determined, keep the previous bf16 behavior. - return True - -def get_orig_shape(reader, tensor_name): - field_key = f"comfy.gguf.orig_shape.{tensor_name}" - field = reader.get_field(field_key) - if field is None: - return None - # Has original shape metadata, so we try to decode it. - if len(field.types) != 2 or field.types[0] != gguf.GGUFValueType.ARRAY or field.types[1] != gguf.GGUFValueType.INT32: - raise TypeError(f"Bad original shape metadata for {field_key}: Expected ARRAY of INT32, got {field.types}") - return torch.Size(tuple(int(field.parts[part_idx][0]) for part_idx in field.data)) - -def get_field(reader, field_name, field_type): - field = reader.get_field(field_name) - if field is None: - return None - elif field_type == str: - # extra check here as this is used for checking arch string - if len(field.types) != 1 or field.types[0] != gguf.GGUFValueType.STRING: - raise TypeError(f"Bad type for GGUF {field_name} key: expected string, got {field.types!r}") - return str(field.parts[field.data[-1]], encoding="utf-8") - elif field_type in [int, float, bool]: - return field_type(field.parts[field.data[-1]].item()) - else: - raise TypeError(f"Unknown field type {field_type}") - -def get_list_field(reader, field_name, field_type): - field = reader.get_field(field_name) - if field is None: - return None - elif field_type == str: - return tuple(str(field.parts[part_idx], encoding="utf-8") for part_idx in field.data) - elif field_type in [int, float, bool]: - return tuple(field_type(field.parts[part_idx][0]) for part_idx in field.data) - else: - raise TypeError(f"Unknown field type {field_type}") - -def get_gguf_metadata(reader): - """Extract all simple metadata fields like safetensors""" - metadata = {} - for field_name in reader.fields: - try: - field = reader.get_field(field_name) - if len(field.types) == 1: # Simple scalar fields only - if field.types[0] == gguf.GGUFValueType.STRING: - metadata[field_name] = str(field.parts[field.data[-1]], "utf-8") - elif field.types[0] == gguf.GGUFValueType.INT32: - metadata[field_name] = int(field.parts[field.data[-1]]) - elif field.types[0] == gguf.GGUFValueType.F32: - metadata[field_name] = float(field.parts[field.data[-1]]) - elif field.types[0] == gguf.GGUFValueType.BOOL: - metadata[field_name] = bool(field.parts[field.data[-1]]) - except: - continue - return metadata - -def gguf_sd_loader(path, handle_prefix="model.diffusion_model.", is_text_model=False): - """ - Read state dict as fake tensors - """ - reader = gguf.GGUFReader(path) - - # filter and strip prefix - has_prefix = False - if handle_prefix is not None: - prefix_len = len(handle_prefix) - tensor_names = set(tensor.name for tensor in reader.tensors) - has_prefix = any(s.startswith(handle_prefix) for s in tensor_names) - - tensors = [] - for tensor in reader.tensors: - sd_key = tensor_name = tensor.name - if has_prefix: - if not tensor_name.startswith(handle_prefix): - continue - sd_key = tensor_name[prefix_len:] - tensors.append((sd_key, tensor)) - - # detect and verify architecture - compat = None - arch_str = get_field(reader, "general.architecture", str) - type_str = get_field(reader, "general.type", str) - if arch_str in [None, "pig", "cow"]: - if is_text_model: - raise ValueError(f"This gguf file is incompatible with llama.cpp!\nConsider using safetensors or a compatible gguf file\n({path})") - compat = "sd.cpp" if arch_str is None else arch_str - # import here to avoid changes to convert.py breaking regular models - from .tools.convert import detect_arch - try: - arch_str = detect_arch(set(val[0] for val in tensors)).arch - except Exception as e: - raise ValueError(f"This model is not currently supported - ({e})") - elif arch_str not in TXT_ARCH_LIST and is_text_model: - if type_str not in VIS_TYPE_LIST: - raise ValueError(f"Unexpected text model architecture type in GGUF file: {arch_str!r}") - elif arch_str not in IMG_ARCH_LIST and not is_text_model: - raise ValueError(f"Unexpected architecture type in GGUF file: {arch_str!r}") - - if compat: - logging.warning(f"Warning: This gguf model file is loaded in compatibility mode '{compat}' [arch:{arch_str}]") - - # main loading loop - # Devices without native bf16 fall back to slow fp32 compute, so load the - # full-precision BF16 storage tensors as fp16 there instead. - bf16_storage_dtype = torch.bfloat16 if device_supports_bf16() else torch.float16 - state_dict = {} - qtype_dict = {} - for sd_key, tensor in tensors: - tensor_name = tensor.name - # torch_tensor = torch.from_numpy(tensor.data) # mmap - - # NOTE: line above replaced with this block to avoid persistent numpy warning about mmap - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", message="The given NumPy array is not writable") - torch_tensor = torch.from_numpy(tensor.data) # mmap - - shape = get_orig_shape(reader, tensor_name) - if shape is None: - shape = torch.Size(tuple(int(v) for v in reversed(tensor.shape))) - # Workaround for stable-diffusion.cpp SDXL detection. - if compat == "sd.cpp" and arch_str == "sdxl": - if any([tensor_name.endswith(x) for x in (".proj_in.weight", ".proj_out.weight")]): - while len(shape) > 2 and shape[-1] == 1: - shape = shape[:-1] - - # add to state dict - if tensor.tensor_type in {gguf.GGMLQuantizationType.F32, gguf.GGMLQuantizationType.F16}: - torch_tensor = torch_tensor.view(*shape) - state_dict[sd_key] = GGMLTensor(torch_tensor, tensor_type=tensor.tensor_type, tensor_shape=shape) - - # BF16 GGUF tensors are full-precision storage, not compressed quants. - if tensor.tensor_type == gguf.GGMLQuantizationType.BF16: - dtype = torch.float32 if len(shape) <= 1 else bf16_storage_dtype - state_dict[sd_key] = dequantize_tensor(state_dict[sd_key], dtype=dtype) - - # keep track of loaded tensor types - tensor_type_str = getattr(tensor.tensor_type, "name", repr(tensor.tensor_type)) - qtype_dict[tensor_type_str] = qtype_dict.get(tensor_type_str, 0) + 1 - - # print loaded tensor type counts - logging.info("gguf qtypes: " + ", ".join(f"{k} ({v})" for k, v in qtype_dict.items())) - - # mark largest tensor for vram estimation - qsd = {k:v for k,v in state_dict.items() if is_quantized(v)} - if len(qsd) > 0: - max_key = max(qsd.keys(), key=lambda k: qsd[k].numel()) - state_dict[max_key].is_largest_weight = True - - # extra info to return - extra = { - "arch_str": arch_str, - "metadata": get_gguf_metadata(reader) - } - - # Detect custom ComfyUI native quantization metadata - warned_unrotated_convrot = False - for field_name in reader.fields: - if not field_name.startswith("comfy.gguf.quant."): - continue - key = field_name[len("comfy.gguf.quant."):] - field = reader.get_field(field_name) - quant_conf = json.loads(str(field.parts[field.data[-1]], "utf-8")) - fmt = quant_conf.get("format") - - weight_key = key - scale_key = f"{key}_scale" - if weight_key not in state_dict or scale_key not in state_dict: - logging.warning(f"Missing custom quant tensors for {weight_key}") - continue - - weight_ggml = state_dict[weight_key] - scale_ggml = state_dict[scale_key] - - if fmt == "int8_tensorwise": - if quant_conf.get("convrot") and not quant_conf.get("weight_rotated", False): - if not warned_unrotated_convrot: - logging.warning( - "Disabling ConvRot because this GGUF does not mark its weights " - "as pre-rotated. Reconvert with the current converter to enable ConvRot." - ) - warned_unrotated_convrot = True - quant_conf["convrot"] = False - quant_conf.pop("convrot_groupsize", None) - elif quant_conf.get("convrot"): - groupsize = quant_conf.get("convrot_groupsize", 256) - if weight_ggml.tensor_shape[-1] % groupsize != 0: - logging.warning( - "Disabling ConvRot for %s because %d input features are not " - "divisible by group size %d.", - weight_key, - weight_ggml.tensor_shape[-1], - groupsize, - ) - quant_conf["convrot"] = False - quant_conf.pop("convrot_groupsize", None) - - # Convert to ComfyUI native state-dict layout - weight = weight_ggml.data.view(torch.int8).reshape(weight_ggml.tensor_shape) - scale = scale_ggml.data.view(torch.float32).reshape(scale_ggml.tensor_shape) - - state_dict[weight_key] = torch.nn.Parameter(weight, requires_grad=False) - state_dict[scale_key] = torch.nn.Parameter(scale, requires_grad=False) - - layer_prefix = weight_key[:weight_key.rfind("weight")] - quant_json = json.dumps(quant_conf) - state_dict[f"{layer_prefix}comfy_quant"] = torch.nn.Parameter( - torch.tensor(list(quant_json.encode("utf-8")), dtype=torch.uint8), - requires_grad=False, - ) - extra["gguf_quant_mode"] = "int8_convrot" - - elif fmt == "int4_pytorch": - # Keep serialized INT4 weights; custom ops pack them for the CUDA kernel. - weight = weight_ggml.data.view(torch.uint8).reshape(weight_ggml.tensor_shape) - scale = scale_ggml.data.view(torch.float32).reshape(scale_ggml.tensor_shape) - - state_dict[weight_key] = torch.nn.Parameter(weight, requires_grad=False) - state_dict[scale_key] = torch.nn.Parameter(scale, requires_grad=False) - layer_prefix = weight_key[:weight_key.rfind("weight")] - state_dict[f"{layer_prefix}comfy_quant"] = torch.nn.Parameter( - torch.tensor(list(json.dumps(quant_conf).encode("utf-8")), dtype=torch.uint8), - requires_grad=False, - ) - extra["gguf_quant_mode"] = "int4_pytorch" - - return (state_dict, extra) - -# for remapping llama.cpp -> original key names -T5_SD_MAP = { - "enc.": "encoder.", - ".blk.": ".block.", - "token_embd": "shared", - "output_norm": "final_layer_norm", - "attn_q": "layer.0.SelfAttention.q", - "attn_k": "layer.0.SelfAttention.k", - "attn_v": "layer.0.SelfAttention.v", - "attn_o": "layer.0.SelfAttention.o", - "attn_norm": "layer.0.layer_norm", - "attn_rel_b": "layer.0.SelfAttention.relative_attention_bias", - "ffn_up": "layer.1.DenseReluDense.wi_1", - "ffn_down": "layer.1.DenseReluDense.wo", - "ffn_gate": "layer.1.DenseReluDense.wi_0", - "ffn_norm": "layer.1.layer_norm", -} - -LLAMA_SD_MAP = { - "blk.": "model.layers.", - "attn_norm": "input_layernorm", - "attn_q_norm.": "self_attn.q_norm.", - "attn_k_norm.": "self_attn.k_norm.", - "attn_v_norm.": "self_attn.v_norm.", - "attn_q": "self_attn.q_proj", - "attn_k": "self_attn.k_proj", - "attn_v": "self_attn.v_proj", - "attn_output": "self_attn.o_proj", - "ffn_up": "mlp.up_proj", - "ffn_down": "mlp.down_proj", - "ffn_gate": "mlp.gate_proj", - "ffn_norm": "post_attention_layernorm", - "token_embd": "model.embed_tokens", - "output_norm": "model.norm", - "output.weight": "lm_head.weight", -} - -GEMMA3_SD_MAP = LLAMA_SD_MAP.copy() -GEMMA3_SD_MAP.update({ - "ffn_norm": "pre_feedforward_layernorm", - "post_ffw_norm": "post_feedforward_layernorm", - "post_attention_norm": "post_attention_layernorm", -}) - -CLIP_VISION_SD_MAP = { - "mm.": "visual.merger.mlp.", - "v.post_ln.": "visual.merger.ln_q.", - "v.patch_embd": "visual.patch_embed.proj", - "v.blk.": "visual.blocks.", - "ffn_up": "mlp.up_proj", - "ffn_down": "mlp.down_proj", - "ffn_gate": "mlp.gate_proj", - "attn_out.": "attn.proj.", - "ln1.": "norm1.", - "ln2.": "norm2.", -} - -def sd_map_replace(raw_sd, key_map): - sd = {} - for k,v in raw_sd.items(): - for s,d in key_map.items(): - k = k.replace(s,d) - sd[k] = v - return sd - -def llama_permute(raw_sd, n_head, n_head_kv): - # Reverse version of LlamaModel.permute in llama.cpp convert script - sd = {} - permute = lambda x,h: x.reshape(h, x.shape[0] // h // 2, 2, *x.shape[1:]).swapaxes(1, 2).reshape(x.shape) - for k,v in raw_sd.items(): - if k.endswith(("q_proj.weight", "q_proj.bias")): - v.data = permute(v.data, n_head) - if k.endswith(("k_proj.weight", "k_proj.bias")): - v.data = permute(v.data, n_head_kv) - sd[k] = v - return sd - -def gemma3_norm_corrections(sd): - # Reverse change from Gemma3Model modify_tensors in llama.cpp convert script - norm_patterns = [ - "input_layernorm.weight", - "post_attention_layernorm.weight", - "pre_feedforward_layernorm.weight", - "post_feedforward_layernorm.weight", - "self_attn.q_norm.weight", - "self_attn.k_norm.weight", - "model.norm.weight" - ] - corrected = 0 - for key in list(sd.keys()): - if any(p in key for p in norm_patterns): - if is_quantized(sd[key]): - sd[key] = dequantize_tensor(sd[key], dtype=torch.float32) - 1.0 - else: - sd[key] = sd[key].float() - 1.0 - corrected += 1 - #logging.info(f"Gemma3: Applied -1 norm correction to {corrected} tensors") - return sd - -def strip_quant_suffix(name): - pattern = r"[-_]?(?:ud-)?i?q[0-9]_[a-z0-9_\-]{1,8}$" - match = re.search(pattern, name, re.IGNORECASE) - if match: - name = name[:match.start()] - return name - -def gguf_mmproj_loader(path): - # Reverse version of Qwen2VLVisionModel.modify_tensors - logging.info("Attenpting to find mmproj file for text encoder...") - - # get name to match w/o quant suffix - tenc_fname = os.path.basename(path) - tenc = os.path.splitext(tenc_fname)[0].lower() - tenc = strip_quant_suffix(tenc) - - # try and find matching mmproj - target = [] - root = os.path.dirname(path) - for fname in os.listdir(root): - name, ext = os.path.splitext(fname) - if ext.lower() != ".gguf": - continue - if "mmproj" not in name.lower(): - continue - if tenc in name.lower(): - target.append(fname) - - if len(target) == 0: - logging.error(f"Error: Can't find mmproj file for '{tenc_fname}' (matching:'{tenc}')! Qwen-Image-Edit will be broken!") - return {} - if len(target) > 1: - logging.error(f"Ambiguous mmproj for text encoder '{tenc_fname}', will use first match.") - - logging.info(f"Using mmproj '{target[0]}' for text encoder '{tenc_fname}'.") - target = os.path.join(root, target[0]) - vsd, _ = gguf_sd_loader(target, is_text_model=True) - - # concat 4D to 5D - if "v.patch_embd.weight.1" in vsd: - w1 = dequantize_tensor(vsd.pop("v.patch_embd.weight"), dtype=torch.float32) - w2 = dequantize_tensor(vsd.pop("v.patch_embd.weight.1"), dtype=torch.float32) - vsd["v.patch_embd.weight"] = torch.stack([w1, w2], dim=2) - - # run main replacement - vsd = sd_map_replace(vsd, CLIP_VISION_SD_MAP) - - # handle split Q/K/V - if "visual.blocks.0.attn_q.weight" in vsd: - attns = {} - # filter out attentions + group - for k,v in vsd.items(): - if any(x in k for x in ["attn_q", "attn_k", "attn_v"]): - k_attn, k_name = k.rsplit(".attn_", 1) - k_attn += ".attn.qkv." + k_name.split(".")[-1] - if k_attn not in attns: - attns[k_attn] = {} - attns[k_attn][k_name] = dequantize_tensor( - v, dtype=(torch.bfloat16 if is_quantized(v) else torch.float16) - ) - - # recombine - for k,v in attns.items(): - suffix = k.split(".")[-1] - vsd[k] = torch.cat([ - v[f"q.{suffix}"], - v[f"k.{suffix}"], - v[f"v.{suffix}"], - ], dim=0) - del attns - - return vsd - -def gguf_tokenizer_loader(path, temb_shape): - # convert gguf tokenizer to spiece - logging.info("Attempting to recreate sentencepiece tokenizer from GGUF file metadata...") - try: - from sentencepiece import sentencepiece_model_pb2 as model - except ImportError: - raise ImportError("Please make sure sentencepiece and protobuf are installed.\npip install sentencepiece protobuf") - spm = model.ModelProto() - - reader = gguf.GGUFReader(path) - - if get_field(reader, "tokenizer.ggml.model", str) == "t5": - if temb_shape == (256384, 4096): # probably UMT5 - spm.trainer_spec.model_type == 1 # Unigram (do we have a T5 w/ BPE?) - else: - raise NotImplementedError("Unknown model, can't set tokenizer!") - else: - raise NotImplementedError("Unknown model, can't set tokenizer!") - - spm.normalizer_spec.add_dummy_prefix = get_field(reader, "tokenizer.ggml.add_space_prefix", bool) - spm.normalizer_spec.remove_extra_whitespaces = get_field(reader, "tokenizer.ggml.remove_extra_whitespaces", bool) - - tokens = get_list_field(reader, "tokenizer.ggml.tokens", str) - scores = get_list_field(reader, "tokenizer.ggml.scores", float) - toktypes = get_list_field(reader, "tokenizer.ggml.token_type", int) - - for idx, (token, score, toktype) in enumerate(zip(tokens, scores, toktypes)): - # # These aren't present in the original? - # if toktype == 5 and idx >= temb_shape[0]%1000): - # continue - - piece = spm.SentencePiece() - piece.piece = token - piece.score = score - piece.type = toktype - spm.pieces.append(piece) - - # unsure if any of these are correct - spm.trainer_spec.byte_fallback = True - spm.trainer_spec.vocab_size = len(tokens) # split off unused? - spm.trainer_spec.max_sentence_length = 4096 - spm.trainer_spec.eos_id = get_field(reader, "tokenizer.ggml.eos_token_id", int) - spm.trainer_spec.pad_id = get_field(reader, "tokenizer.ggml.padding_token_id", int) - - logging.info(f"Created tokenizer with vocab size of {len(spm.pieces)}") - del reader - return torch.ByteTensor(list(spm.SerializeToString())) - -def gguf_tekken_tokenizer_loader(path, temb_shape): - # convert ggml (hf) tokenizer metadata to tekken/comfy data - logging.info("Attempting to recreate tekken tokenizer from GGUF file metadata...") - import json - import base64 - from transformers.convert_slow_tokenizer import bytes_to_unicode - - reader = gguf.GGUFReader(path) - - model_str = get_field(reader, "tokenizer.ggml.model", str) - if model_str == "gpt2": - if temb_shape == (131072, 5120): # probably Mistral - data = { - "config": {"num_vocab_tokens": 150000, "default_vocab_size": 131072}, - "vocab": [], - "special_tokens": [], - } - else: - raise NotImplementedError("Unknown model, can't set tokenizer!") - else: - raise NotImplementedError("Unknown model, can't set tokenizer!") - - tokens = get_list_field(reader, "tokenizer.ggml.tokens", str) - toktypes = get_list_field(reader, "tokenizer.ggml.token_type", int) - - decoder = {v: k for k, v in bytes_to_unicode().items()} - for idx, (token, toktype) in enumerate(zip(tokens, toktypes)): - if toktype == 3: - data["special_tokens"].append( - {'rank': idx, 'token_str': token, 'is_control': True} - ) - else: - tok = bytes([decoder[char] for char in token]) - data["vocab"].append({ - "rank": len(data["vocab"]), - "token_bytes": base64.b64encode(tok).decode("ascii"), - "token_str": tok.decode("utf-8", errors="replace") # ? - }) - - logging.info(f"Created tekken tokenizer with vocab size of {len(data['vocab'])} (+{len(data['special_tokens'])})") - del reader - return torch.ByteTensor(list(json.dumps(data).encode('utf-8'))) - -def gguf_gemma3_tokenizer_loader(path): - #TODO: merge into gguf_tokenizer_loader - logging.info("Attempting to recreate sentencepiece tokenizer from GGUF file metadata...") - try: - from sentencepiece import sentencepiece_model_pb2 as model - except ImportError: - raise ImportError("Please install sentencepiece and protobuf.\npip install sentencepiece protobuf") - spm = model.ModelProto() - reader = gguf.GGUFReader(path) - - spm.normalizer_spec.name = "identity" - spm.normalizer_spec.add_dummy_prefix = False - spm.trainer_spec.model_type = 2 - spm.trainer_spec.input_format = "tsv" - spm.trainer_spec.byte_fallback = True - spm.trainer_spec.max_sentence_length = 4192 - spm.trainer_spec.bos_piece = "" - - tokens = get_list_field(reader, "tokenizer.ggml.tokens", str) - scores = get_list_field(reader, "tokenizer.ggml.scores", float) - toktype = get_list_field(reader, "tokenizer.ggml.token_type", int) - - if not tokens or not scores or not toktype: - raise ValueError("Missing tokenizer metadata") - - for idx in range(len(tokens)): - piece = spm.SentencePiece() - piece.piece = tokens[idx] - if idx == 3: # UNK position - piece.type = 2 # UNK Token - piece.score = 0.0 # UNK Score - else: - piece.type = toktype[idx] - piece.score = scores[idx] - spm.pieces.append(piece) - - spm.trainer_spec.vocab_size = len(spm.pieces) - logging.info(f"Created tokenizer with vocab size of {len(spm.pieces)}") - - del reader - return torch.ByteTensor(list(spm.SerializeToString())) - -def gguf_clip_loader(path): - sd, extra = gguf_sd_loader(path, is_text_model=True) - arch = extra.get("arch_str", None) - if arch in {"t5", "t5encoder"}: - temb_key = "token_embd.weight" - if temb_key in sd and sd[temb_key].shape == (256384, 4096): - # non-standard Comfy-Org tokenizer - sd["spiece_model"] = gguf_tokenizer_loader(path, sd[temb_key].shape) - # TODO: dequantizing token embed here is janky but otherwise we OOM due to tensor being massive. - logging.warning(f"Dequantizing {temb_key} to prevent runtime OOM.") - sd[temb_key] = dequantize_tensor(sd[temb_key], dtype=torch.float16) - sd = sd_map_replace(sd, T5_SD_MAP) - elif arch in {"llama", "qwen2vl", "qwen3", "qwen3vl", "gemma3"}: - # TODO: pass model_options["vocab_size"] to loader somehow - temb_key = "token_embd.weight" - if temb_key in sd and sd[temb_key].shape[0] >= (64 * 1024): - if arch == "llama" and sd[temb_key].shape == (131072, 5120): - # non-standard Comfy-Org tokenizer - sd["tekken_model"] = gguf_tekken_tokenizer_loader(path, sd[temb_key].shape) - elif arch == "gemma3": - sd["spiece_model"] = gguf_gemma3_tokenizer_loader(path) - # See note above for T5. - logging.warning(f"Dequantizing {temb_key} to prevent runtime OOM.") - sd[temb_key] = dequantize_tensor(sd[temb_key], dtype=torch.float16) - if arch == "gemma3": - sd = sd_map_replace(sd, GEMMA3_SD_MAP) - sd = gemma3_norm_corrections(sd) - else: - sd = sd_map_replace(sd, LLAMA_SD_MAP) - if arch == "llama": - sd = llama_permute(sd, 32, 8) # L3 / Mistral - if arch == "qwen2vl": - vsd = gguf_mmproj_loader(path) - sd.update(vsd) - if arch == "qwen3vl" and "model.visual.deepstack_merger_list.0.norm.weight" not in sd: - # Standard llama.cpp Qwen3-VL GGUFs omit the visual tower. Without it, - # detect_te_model() mis-classifies the state dict as QWEN3_4B/8B (Qwen3 LM) - # instead of QWEN3VL_4B/8B, so clip_type=KREA2 never selects the 12-layer - # tap encoder and conditioning has shape (B, seq, 2560) instead of (B, seq, 30720). - # Inject zero sentinel tensors with shapes that exactly match the model - # parameters so that load_state_dict(strict=False) doesn't raise a size - # mismatch error while still satisfying detect_te_model()'s key checks. - # deepstack_merger_list.0.norm -> LayerNorm(vis_hidden * 4) shape [merge_dim] - # merger.linear_fc2 -> Linear(merge_dim, lm_hidden) shape [lm_hidden, merge_dim] - ln_key = "model.layers.0.input_layernorm.weight" - lm_hidden = int(sd[ln_key].shape[0]) if ln_key in sd else 2560 - vis_hidden = 1024 if lm_hidden == 2560 else 1152 # Qwen3-VL-4B vs 8B - merge_dim = vis_hidden * 4 # spatial_merge_size=2 - sd["model.visual.deepstack_merger_list.0.norm.weight"] = torch.zeros(merge_dim) - sd["model.visual.merger.linear_fc2.weight"] = torch.zeros(lm_hidden, merge_dim) - logging.info(f"qwen3vl GGUF: injected visual marker tensors (lm_hidden={lm_hidden}, merge_dim={merge_dim}) for model type detection") - elif arch == "ideogram": - # Dequantize Ideogram model for inference - logging.info("Dequantizing Ideogram model for inference...") - # Use BF16 to save VRAM while maintaining quality, but fall back to FP16 - # on devices that don't support bf16 (avoids slow fp32 compute fallback). - target_dtype = torch.bfloat16 if device_supports_bf16() else torch.float16 - dequantized_count = 0 - for key in list(sd.keys()): - if is_quantized(sd[key]): - sd[key] = dequantize_tensor(sd[key], dtype=target_dtype) - dequantized_count += 1 - logging.info(f"Dequantized {dequantized_count} tensors for Ideogram model ({target_dtype})") - else: - pass - return sd +# (c) City96 || Apache-2.0 (apache.org/licenses/LICENSE-2.0) +import warnings +import logging +import torch +import gguf +import json +import re +import os +from .ops import GGMLTensor +from .dequant import is_quantized, dequantize_tensor + +IMG_ARCH_LIST = {"flux", "sd1", "sdxl", "sd3", "aura", "hidream", "cosmos", "ltxv", "hyvid", "wan", "lumina2", "qwen_image", "ideogram", "krea2"} +TXT_ARCH_LIST = {"t5", "t5encoder", "llama", "qwen2vl", "qwen3", "qwen3vl", "gemma3"} +VIS_TYPE_LIST = {"clip-vision", "mmproj"} + +def device_supports_bf16(): + """ + Return True if the active torch device can run bf16 natively. On devices + without native bf16 support, computation silently falls back to fp32 which + is very slow, so callers should load tensors as fp16 instead. + """ + try: + import comfy.model_management + return comfy.model_management.should_use_bf16(comfy.model_management.get_torch_device()) + except Exception: + # If support can't be determined, keep the previous bf16 behavior. + return True + +def get_orig_shape(reader, tensor_name): + field_key = f"comfy.gguf.orig_shape.{tensor_name}" + field = reader.get_field(field_key) + if field is None: + return None + # Has original shape metadata, so we try to decode it. + if len(field.types) != 2 or field.types[0] != gguf.GGUFValueType.ARRAY or field.types[1] != gguf.GGUFValueType.INT32: + raise TypeError(f"Bad original shape metadata for {field_key}: Expected ARRAY of INT32, got {field.types}") + return torch.Size(tuple(int(field.parts[part_idx][0]) for part_idx in field.data)) + +def get_field(reader, field_name, field_type): + field = reader.get_field(field_name) + if field is None: + return None + elif field_type == str: + # extra check here as this is used for checking arch string + if len(field.types) != 1 or field.types[0] != gguf.GGUFValueType.STRING: + raise TypeError(f"Bad type for GGUF {field_name} key: expected string, got {field.types!r}") + return str(field.parts[field.data[-1]], encoding="utf-8") + elif field_type in [int, float, bool]: + return field_type(field.parts[field.data[-1]].item()) + else: + raise TypeError(f"Unknown field type {field_type}") + +def get_list_field(reader, field_name, field_type): + field = reader.get_field(field_name) + if field is None: + return None + elif field_type == str: + return tuple(str(field.parts[part_idx], encoding="utf-8") for part_idx in field.data) + elif field_type in [int, float, bool]: + return tuple(field_type(field.parts[part_idx][0]) for part_idx in field.data) + else: + raise TypeError(f"Unknown field type {field_type}") + +def get_gguf_metadata(reader): + """Extract all simple metadata fields like safetensors""" + metadata = {} + for field_name in reader.fields: + try: + field = reader.get_field(field_name) + if len(field.types) == 1: # Simple scalar fields only + if field.types[0] == gguf.GGUFValueType.STRING: + metadata[field_name] = str(field.parts[field.data[-1]], "utf-8") + elif field.types[0] == gguf.GGUFValueType.INT32: + metadata[field_name] = int(field.parts[field.data[-1]]) + elif field.types[0] == gguf.GGUFValueType.F32: + metadata[field_name] = float(field.parts[field.data[-1]]) + elif field.types[0] == gguf.GGUFValueType.BOOL: + metadata[field_name] = bool(field.parts[field.data[-1]]) + except: + continue + return metadata + +def gguf_sd_loader(path, handle_prefix="model.diffusion_model.", is_text_model=False): + """ + Read state dict as fake tensors + """ + reader = gguf.GGUFReader(path) + + # filter and strip prefix + has_prefix = False + if handle_prefix is not None: + prefix_len = len(handle_prefix) + tensor_names = set(tensor.name for tensor in reader.tensors) + has_prefix = any(s.startswith(handle_prefix) for s in tensor_names) + + tensors = [] + for tensor in reader.tensors: + sd_key = tensor_name = tensor.name + if has_prefix: + if not tensor_name.startswith(handle_prefix): + continue + sd_key = tensor_name[prefix_len:] + tensors.append((sd_key, tensor)) + + # detect and verify architecture + compat = None + arch_str = get_field(reader, "general.architecture", str) + type_str = get_field(reader, "general.type", str) + if arch_str in [None, "pig", "cow"]: + if is_text_model: + raise ValueError(f"This gguf file is incompatible with llama.cpp!\nConsider using safetensors or a compatible gguf file\n({path})") + compat = "sd.cpp" if arch_str is None else arch_str + # import here to avoid changes to convert.py breaking regular models + from .tools.convert import detect_arch + try: + arch_str = detect_arch(set(val[0] for val in tensors)).arch + except Exception as e: + raise ValueError(f"This model is not currently supported - ({e})") + elif arch_str not in TXT_ARCH_LIST and is_text_model: + if type_str not in VIS_TYPE_LIST: + raise ValueError(f"Unexpected text model architecture type in GGUF file: {arch_str!r}") + elif arch_str not in IMG_ARCH_LIST and not is_text_model: + raise ValueError(f"Unexpected architecture type in GGUF file: {arch_str!r}") + + if compat: + logging.warning(f"Warning: This gguf model file is loaded in compatibility mode '{compat}' [arch:{arch_str}]") + + # main loading loop + # Devices without native bf16 fall back to slow fp32 compute, so load the + # full-precision BF16 storage tensors as fp16 there instead. + bf16_storage_dtype = torch.bfloat16 if device_supports_bf16() else torch.float16 + state_dict = {} + qtype_dict = {} + for sd_key, tensor in tensors: + tensor_name = tensor.name + # torch_tensor = torch.from_numpy(tensor.data) # mmap + + # NOTE: line above replaced with this block to avoid persistent numpy warning about mmap + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", message="The given NumPy array is not writable") + torch_tensor = torch.from_numpy(tensor.data) # mmap + + shape = get_orig_shape(reader, tensor_name) + if shape is None: + shape = torch.Size(tuple(int(v) for v in reversed(tensor.shape))) + # Workaround for stable-diffusion.cpp SDXL detection. + if compat == "sd.cpp" and arch_str == "sdxl": + if any([tensor_name.endswith(x) for x in (".proj_in.weight", ".proj_out.weight")]): + while len(shape) > 2 and shape[-1] == 1: + shape = shape[:-1] + + # add to state dict + if tensor.tensor_type in {gguf.GGMLQuantizationType.F32, gguf.GGMLQuantizationType.F16}: + torch_tensor = torch_tensor.view(*shape) + state_dict[sd_key] = GGMLTensor(torch_tensor, tensor_type=tensor.tensor_type, tensor_shape=shape) + + # BF16 GGUF tensors are full-precision storage, not compressed quants. + if tensor.tensor_type == gguf.GGMLQuantizationType.BF16: + dtype = torch.float32 if len(shape) <= 1 else bf16_storage_dtype + state_dict[sd_key] = dequantize_tensor(state_dict[sd_key], dtype=dtype) + + # keep track of loaded tensor types + tensor_type_str = getattr(tensor.tensor_type, "name", repr(tensor.tensor_type)) + qtype_dict[tensor_type_str] = qtype_dict.get(tensor_type_str, 0) + 1 + + # print loaded tensor type counts + logging.info("gguf qtypes: " + ", ".join(f"{k} ({v})" for k, v in qtype_dict.items())) + + # mark largest tensor for vram estimation + qsd = {k:v for k,v in state_dict.items() if is_quantized(v)} + if len(qsd) > 0: + max_key = max(qsd.keys(), key=lambda k: qsd[k].numel()) + state_dict[max_key].is_largest_weight = True + + # extra info to return + extra = { + "arch_str": arch_str, + "metadata": get_gguf_metadata(reader) + } + + # Detect custom ComfyUI native quantization metadata + warned_unrotated_convrot = False + for field_name in reader.fields: + if not field_name.startswith("comfy.gguf.quant."): + continue + key = field_name[len("comfy.gguf.quant."):] + field = reader.get_field(field_name) + quant_conf = json.loads(str(field.parts[field.data[-1]], "utf-8")) + fmt = quant_conf.get("format") + + if fmt in {"int4_compact_gemm", "int4_pytorch"}: + raise ValueError( + "Q4_PT GGUF files are retired because PyTorch's Ampere INT4 " + "kernel is not performance-competitive. Reconvert as Q8_CR." + ) + + weight_key = key + scale_key = f"{key}_scale" + if weight_key not in state_dict or scale_key not in state_dict: + logging.warning(f"Missing custom quant tensors for {weight_key}") + continue + + weight_ggml = state_dict[weight_key] + scale_ggml = state_dict[scale_key] + + if fmt == "int8_tensorwise": + if quant_conf.get("convrot") and not quant_conf.get("weight_rotated", False): + if not warned_unrotated_convrot: + logging.warning( + "Disabling ConvRot because this GGUF does not mark its weights " + "as pre-rotated. Reconvert with the current converter to enable ConvRot." + ) + warned_unrotated_convrot = True + quant_conf["convrot"] = False + quant_conf.pop("convrot_groupsize", None) + elif quant_conf.get("convrot"): + groupsize = quant_conf.get("convrot_groupsize", 256) + if weight_ggml.tensor_shape[-1] % groupsize != 0: + logging.warning( + "Disabling ConvRot for %s because %d input features are not " + "divisible by group size %d.", + weight_key, + weight_ggml.tensor_shape[-1], + groupsize, + ) + quant_conf["convrot"] = False + quant_conf.pop("convrot_groupsize", None) + + # Convert to ComfyUI native state-dict layout + weight = weight_ggml.data.view(torch.int8).reshape(weight_ggml.tensor_shape) + scale = scale_ggml.data.view(torch.float32).reshape(scale_ggml.tensor_shape) + + state_dict[weight_key] = torch.nn.Parameter(weight, requires_grad=False) + state_dict[scale_key] = torch.nn.Parameter(scale, requires_grad=False) + + layer_prefix = weight_key[:weight_key.rfind("weight")] + quant_json = json.dumps(quant_conf) + state_dict[f"{layer_prefix}comfy_quant"] = torch.nn.Parameter( + torch.tensor(list(quant_json.encode("utf-8")), dtype=torch.uint8), + requires_grad=False, + ) + extra["gguf_quant_mode"] = "int8_convrot" + + # Retained loader adaptation for the retired Q4_PT experiment. The + # explicit rejection above prevents this branch from being executed. + elif fmt in {"int4_compact_gemm", "int4_pytorch"}: + # Keep compact INT4 storage while exposing the original shape to + # ComfyUI's model detector. It uses first.weight.shape to infer + # Krea2's latent channel count before custom ops load the tensor. + orig_shape = torch.Size(tuple(quant_conf["orig_shape"])) + weight = GGMLTensor( + weight_ggml.data.view(torch.uint8).reshape(weight_ggml.tensor_shape), + tensor_type=weight_ggml.tensor_type, + tensor_shape=orig_shape, + ) + scale = scale_ggml.data.view(torch.float32).reshape(scale_ggml.tensor_shape) + + state_dict[weight_key] = torch.nn.Parameter(weight, requires_grad=False) + state_dict[scale_key] = torch.nn.Parameter(scale, requires_grad=False) + layer_prefix = weight_key[:weight_key.rfind("weight")] + state_dict[f"{layer_prefix}comfy_quant"] = torch.nn.Parameter( + torch.tensor(list(json.dumps(quant_conf).encode("utf-8")), dtype=torch.uint8), + requires_grad=False, + ) + extra["gguf_quant_mode"] = "int4_pytorch" + + return (state_dict, extra) + +# for remapping llama.cpp -> original key names +T5_SD_MAP = { + "enc.": "encoder.", + ".blk.": ".block.", + "token_embd": "shared", + "output_norm": "final_layer_norm", + "attn_q": "layer.0.SelfAttention.q", + "attn_k": "layer.0.SelfAttention.k", + "attn_v": "layer.0.SelfAttention.v", + "attn_o": "layer.0.SelfAttention.o", + "attn_norm": "layer.0.layer_norm", + "attn_rel_b": "layer.0.SelfAttention.relative_attention_bias", + "ffn_up": "layer.1.DenseReluDense.wi_1", + "ffn_down": "layer.1.DenseReluDense.wo", + "ffn_gate": "layer.1.DenseReluDense.wi_0", + "ffn_norm": "layer.1.layer_norm", +} + +LLAMA_SD_MAP = { + "blk.": "model.layers.", + "attn_norm": "input_layernorm", + "attn_q_norm.": "self_attn.q_norm.", + "attn_k_norm.": "self_attn.k_norm.", + "attn_v_norm.": "self_attn.v_norm.", + "attn_q": "self_attn.q_proj", + "attn_k": "self_attn.k_proj", + "attn_v": "self_attn.v_proj", + "attn_output": "self_attn.o_proj", + "ffn_up": "mlp.up_proj", + "ffn_down": "mlp.down_proj", + "ffn_gate": "mlp.gate_proj", + "ffn_norm": "post_attention_layernorm", + "token_embd": "model.embed_tokens", + "output_norm": "model.norm", + "output.weight": "lm_head.weight", +} + +GEMMA3_SD_MAP = LLAMA_SD_MAP.copy() +GEMMA3_SD_MAP.update({ + "ffn_norm": "pre_feedforward_layernorm", + "post_ffw_norm": "post_feedforward_layernorm", + "post_attention_norm": "post_attention_layernorm", +}) + +CLIP_VISION_SD_MAP = { + "mm.": "visual.merger.mlp.", + "v.post_ln.": "visual.merger.ln_q.", + "v.patch_embd": "visual.patch_embed.proj", + "v.blk.": "visual.blocks.", + "ffn_up": "mlp.up_proj", + "ffn_down": "mlp.down_proj", + "ffn_gate": "mlp.gate_proj", + "attn_out.": "attn.proj.", + "ln1.": "norm1.", + "ln2.": "norm2.", +} + +def sd_map_replace(raw_sd, key_map): + sd = {} + for k,v in raw_sd.items(): + for s,d in key_map.items(): + k = k.replace(s,d) + sd[k] = v + return sd + +def llama_permute(raw_sd, n_head, n_head_kv): + # Reverse version of LlamaModel.permute in llama.cpp convert script + sd = {} + permute = lambda x,h: x.reshape(h, x.shape[0] // h // 2, 2, *x.shape[1:]).swapaxes(1, 2).reshape(x.shape) + for k,v in raw_sd.items(): + if k.endswith(("q_proj.weight", "q_proj.bias")): + v.data = permute(v.data, n_head) + if k.endswith(("k_proj.weight", "k_proj.bias")): + v.data = permute(v.data, n_head_kv) + sd[k] = v + return sd + +def gemma3_norm_corrections(sd): + # Reverse change from Gemma3Model modify_tensors in llama.cpp convert script + norm_patterns = [ + "input_layernorm.weight", + "post_attention_layernorm.weight", + "pre_feedforward_layernorm.weight", + "post_feedforward_layernorm.weight", + "self_attn.q_norm.weight", + "self_attn.k_norm.weight", + "model.norm.weight" + ] + corrected = 0 + for key in list(sd.keys()): + if any(p in key for p in norm_patterns): + if is_quantized(sd[key]): + sd[key] = dequantize_tensor(sd[key], dtype=torch.float32) - 1.0 + else: + sd[key] = sd[key].float() - 1.0 + corrected += 1 + #logging.info(f"Gemma3: Applied -1 norm correction to {corrected} tensors") + return sd + +def strip_quant_suffix(name): + pattern = r"[-_]?(?:ud-)?i?q[0-9]_[a-z0-9_\-]{1,8}$" + match = re.search(pattern, name, re.IGNORECASE) + if match: + name = name[:match.start()] + return name + +def gguf_mmproj_loader(path): + # Reverse version of Qwen2VLVisionModel.modify_tensors + logging.info("Attenpting to find mmproj file for text encoder...") + + # get name to match w/o quant suffix + tenc_fname = os.path.basename(path) + tenc = os.path.splitext(tenc_fname)[0].lower() + tenc = strip_quant_suffix(tenc) + + # try and find matching mmproj + target = [] + root = os.path.dirname(path) + for fname in os.listdir(root): + name, ext = os.path.splitext(fname) + if ext.lower() != ".gguf": + continue + if "mmproj" not in name.lower(): + continue + if tenc in name.lower(): + target.append(fname) + + if len(target) == 0: + logging.error(f"Error: Can't find mmproj file for '{tenc_fname}' (matching:'{tenc}')! Qwen-Image-Edit will be broken!") + return {} + if len(target) > 1: + logging.error(f"Ambiguous mmproj for text encoder '{tenc_fname}', will use first match.") + + logging.info(f"Using mmproj '{target[0]}' for text encoder '{tenc_fname}'.") + target = os.path.join(root, target[0]) + vsd, _ = gguf_sd_loader(target, is_text_model=True) + + # concat 4D to 5D + if "v.patch_embd.weight.1" in vsd: + w1 = dequantize_tensor(vsd.pop("v.patch_embd.weight"), dtype=torch.float32) + w2 = dequantize_tensor(vsd.pop("v.patch_embd.weight.1"), dtype=torch.float32) + vsd["v.patch_embd.weight"] = torch.stack([w1, w2], dim=2) + + # run main replacement + vsd = sd_map_replace(vsd, CLIP_VISION_SD_MAP) + + # handle split Q/K/V + if "visual.blocks.0.attn_q.weight" in vsd: + attns = {} + # filter out attentions + group + for k,v in vsd.items(): + if any(x in k for x in ["attn_q", "attn_k", "attn_v"]): + k_attn, k_name = k.rsplit(".attn_", 1) + k_attn += ".attn.qkv." + k_name.split(".")[-1] + if k_attn not in attns: + attns[k_attn] = {} + attns[k_attn][k_name] = dequantize_tensor( + v, dtype=(torch.bfloat16 if is_quantized(v) else torch.float16) + ) + + # recombine + for k,v in attns.items(): + suffix = k.split(".")[-1] + vsd[k] = torch.cat([ + v[f"q.{suffix}"], + v[f"k.{suffix}"], + v[f"v.{suffix}"], + ], dim=0) + del attns + + return vsd + +def gguf_tokenizer_loader(path, temb_shape): + # convert gguf tokenizer to spiece + logging.info("Attempting to recreate sentencepiece tokenizer from GGUF file metadata...") + try: + from sentencepiece import sentencepiece_model_pb2 as model + except ImportError: + raise ImportError("Please make sure sentencepiece and protobuf are installed.\npip install sentencepiece protobuf") + spm = model.ModelProto() + + reader = gguf.GGUFReader(path) + + if get_field(reader, "tokenizer.ggml.model", str) == "t5": + if temb_shape == (256384, 4096): # probably UMT5 + spm.trainer_spec.model_type == 1 # Unigram (do we have a T5 w/ BPE?) + else: + raise NotImplementedError("Unknown model, can't set tokenizer!") + else: + raise NotImplementedError("Unknown model, can't set tokenizer!") + + spm.normalizer_spec.add_dummy_prefix = get_field(reader, "tokenizer.ggml.add_space_prefix", bool) + spm.normalizer_spec.remove_extra_whitespaces = get_field(reader, "tokenizer.ggml.remove_extra_whitespaces", bool) + + tokens = get_list_field(reader, "tokenizer.ggml.tokens", str) + scores = get_list_field(reader, "tokenizer.ggml.scores", float) + toktypes = get_list_field(reader, "tokenizer.ggml.token_type", int) + + for idx, (token, score, toktype) in enumerate(zip(tokens, scores, toktypes)): + # # These aren't present in the original? + # if toktype == 5 and idx >= temb_shape[0]%1000): + # continue + + piece = spm.SentencePiece() + piece.piece = token + piece.score = score + piece.type = toktype + spm.pieces.append(piece) + + # unsure if any of these are correct + spm.trainer_spec.byte_fallback = True + spm.trainer_spec.vocab_size = len(tokens) # split off unused? + spm.trainer_spec.max_sentence_length = 4096 + spm.trainer_spec.eos_id = get_field(reader, "tokenizer.ggml.eos_token_id", int) + spm.trainer_spec.pad_id = get_field(reader, "tokenizer.ggml.padding_token_id", int) + + logging.info(f"Created tokenizer with vocab size of {len(spm.pieces)}") + del reader + return torch.ByteTensor(list(spm.SerializeToString())) + +def gguf_tekken_tokenizer_loader(path, temb_shape): + # convert ggml (hf) tokenizer metadata to tekken/comfy data + logging.info("Attempting to recreate tekken tokenizer from GGUF file metadata...") + import json + import base64 + from transformers.convert_slow_tokenizer import bytes_to_unicode + + reader = gguf.GGUFReader(path) + + model_str = get_field(reader, "tokenizer.ggml.model", str) + if model_str == "gpt2": + if temb_shape == (131072, 5120): # probably Mistral + data = { + "config": {"num_vocab_tokens": 150000, "default_vocab_size": 131072}, + "vocab": [], + "special_tokens": [], + } + else: + raise NotImplementedError("Unknown model, can't set tokenizer!") + else: + raise NotImplementedError("Unknown model, can't set tokenizer!") + + tokens = get_list_field(reader, "tokenizer.ggml.tokens", str) + toktypes = get_list_field(reader, "tokenizer.ggml.token_type", int) + + decoder = {v: k for k, v in bytes_to_unicode().items()} + for idx, (token, toktype) in enumerate(zip(tokens, toktypes)): + if toktype == 3: + data["special_tokens"].append( + {'rank': idx, 'token_str': token, 'is_control': True} + ) + else: + tok = bytes([decoder[char] for char in token]) + data["vocab"].append({ + "rank": len(data["vocab"]), + "token_bytes": base64.b64encode(tok).decode("ascii"), + "token_str": tok.decode("utf-8", errors="replace") # ? + }) + + logging.info(f"Created tekken tokenizer with vocab size of {len(data['vocab'])} (+{len(data['special_tokens'])})") + del reader + return torch.ByteTensor(list(json.dumps(data).encode('utf-8'))) + +def gguf_gemma3_tokenizer_loader(path): + #TODO: merge into gguf_tokenizer_loader + logging.info("Attempting to recreate sentencepiece tokenizer from GGUF file metadata...") + try: + from sentencepiece import sentencepiece_model_pb2 as model + except ImportError: + raise ImportError("Please install sentencepiece and protobuf.\npip install sentencepiece protobuf") + spm = model.ModelProto() + reader = gguf.GGUFReader(path) + + spm.normalizer_spec.name = "identity" + spm.normalizer_spec.add_dummy_prefix = False + spm.trainer_spec.model_type = 2 + spm.trainer_spec.input_format = "tsv" + spm.trainer_spec.byte_fallback = True + spm.trainer_spec.max_sentence_length = 4192 + spm.trainer_spec.bos_piece = "" + + tokens = get_list_field(reader, "tokenizer.ggml.tokens", str) + scores = get_list_field(reader, "tokenizer.ggml.scores", float) + toktype = get_list_field(reader, "tokenizer.ggml.token_type", int) + + if not tokens or not scores or not toktype: + raise ValueError("Missing tokenizer metadata") + + for idx in range(len(tokens)): + piece = spm.SentencePiece() + piece.piece = tokens[idx] + if idx == 3: # UNK position + piece.type = 2 # UNK Token + piece.score = 0.0 # UNK Score + else: + piece.type = toktype[idx] + piece.score = scores[idx] + spm.pieces.append(piece) + + spm.trainer_spec.vocab_size = len(spm.pieces) + logging.info(f"Created tokenizer with vocab size of {len(spm.pieces)}") + + del reader + return torch.ByteTensor(list(spm.SerializeToString())) + +def gguf_clip_loader(path): + sd, extra = gguf_sd_loader(path, is_text_model=True) + arch = extra.get("arch_str", None) + if arch in {"t5", "t5encoder"}: + temb_key = "token_embd.weight" + if temb_key in sd and sd[temb_key].shape == (256384, 4096): + # non-standard Comfy-Org tokenizer + sd["spiece_model"] = gguf_tokenizer_loader(path, sd[temb_key].shape) + # TODO: dequantizing token embed here is janky but otherwise we OOM due to tensor being massive. + logging.warning(f"Dequantizing {temb_key} to prevent runtime OOM.") + sd[temb_key] = dequantize_tensor(sd[temb_key], dtype=torch.float16) + sd = sd_map_replace(sd, T5_SD_MAP) + elif arch in {"llama", "qwen2vl", "qwen3", "qwen3vl", "gemma3"}: + # TODO: pass model_options["vocab_size"] to loader somehow + temb_key = "token_embd.weight" + if temb_key in sd and sd[temb_key].shape[0] >= (64 * 1024): + if arch == "llama" and sd[temb_key].shape == (131072, 5120): + # non-standard Comfy-Org tokenizer + sd["tekken_model"] = gguf_tekken_tokenizer_loader(path, sd[temb_key].shape) + elif arch == "gemma3": + sd["spiece_model"] = gguf_gemma3_tokenizer_loader(path) + # See note above for T5. + logging.warning(f"Dequantizing {temb_key} to prevent runtime OOM.") + sd[temb_key] = dequantize_tensor(sd[temb_key], dtype=torch.float16) + if arch == "gemma3": + sd = sd_map_replace(sd, GEMMA3_SD_MAP) + sd = gemma3_norm_corrections(sd) + else: + sd = sd_map_replace(sd, LLAMA_SD_MAP) + if arch == "llama": + sd = llama_permute(sd, 32, 8) # L3 / Mistral + if arch == "qwen2vl": + vsd = gguf_mmproj_loader(path) + sd.update(vsd) + if arch == "qwen3vl" and "model.visual.deepstack_merger_list.0.norm.weight" not in sd: + # Standard llama.cpp Qwen3-VL GGUFs omit the visual tower. Without it, + # detect_te_model() mis-classifies the state dict as QWEN3_4B/8B (Qwen3 LM) + # instead of QWEN3VL_4B/8B, so clip_type=KREA2 never selects the 12-layer + # tap encoder and conditioning has shape (B, seq, 2560) instead of (B, seq, 30720). + # Inject zero sentinel tensors with shapes that exactly match the model + # parameters so that load_state_dict(strict=False) doesn't raise a size + # mismatch error while still satisfying detect_te_model()'s key checks. + # deepstack_merger_list.0.norm -> LayerNorm(vis_hidden * 4) shape [merge_dim] + # merger.linear_fc2 -> Linear(merge_dim, lm_hidden) shape [lm_hidden, merge_dim] + ln_key = "model.layers.0.input_layernorm.weight" + lm_hidden = int(sd[ln_key].shape[0]) if ln_key in sd else 2560 + vis_hidden = 1024 if lm_hidden == 2560 else 1152 # Qwen3-VL-4B vs 8B + merge_dim = vis_hidden * 4 # spatial_merge_size=2 + sd["model.visual.deepstack_merger_list.0.norm.weight"] = torch.zeros(merge_dim) + sd["model.visual.merger.linear_fc2.weight"] = torch.zeros(lm_hidden, merge_dim) + logging.info(f"qwen3vl GGUF: injected visual marker tensors (lm_hidden={lm_hidden}, merge_dim={merge_dim}) for model type detection") + elif arch == "ideogram": + # Dequantize Ideogram model for inference + logging.info("Dequantizing Ideogram model for inference...") + # Use BF16 to save VRAM while maintaining quality, but fall back to FP16 + # on devices that don't support bf16 (avoids slow fp32 compute fallback). + target_dtype = torch.bfloat16 if device_supports_bf16() else torch.float16 + dequantized_count = 0 + for key in list(sd.keys()): + if is_quantized(sd[key]): + sd[key] = dequantize_tensor(sd[key], dtype=target_dtype) + dequantized_count += 1 + logging.info(f"Dequantized {dequantized_count} tensors for Ideogram model ({target_dtype})") + else: + pass + return sd diff --git a/nodes.py b/nodes.py index fab0749..ce30888 100644 --- a/nodes.py +++ b/nodes.py @@ -1,338 +1,339 @@ -# (c) City96 || Apache-2.0 (apache.org/licenses/LICENSE-2.0) -import torch -import logging -import inspect -import collections - -import nodes -import comfy.sd -import comfy.lora -import comfy.float -import comfy.utils -import comfy.model_patcher -import comfy.model_management -import folder_paths - -from .ops import GGMLOps, GGUFQ4Ops, get_gguf_q8_ops, move_patch_to_device -from .loader import gguf_sd_loader, gguf_clip_loader -from .dequant import is_quantized, is_torch_compatible - -def update_folder_names_and_paths(key, targets=[]): - # check for existing key - base = folder_paths.folder_names_and_paths.get(key, ([], {})) - base = base[0] if isinstance(base[0], (list, set, tuple)) else [] - # find base key & add w/ fallback, sanity check + warning - target = next((x for x in targets if x in folder_paths.folder_names_and_paths), targets[0]) - orig, _ = folder_paths.folder_names_and_paths.get(target, ([], {})) - folder_paths.folder_names_and_paths[key] = (orig or base, {".gguf"}) - if base and base != orig: - logging.warning(f"Unknown file list already present on key {key}: {base}") - -# Add a custom keys for files ending in .gguf -update_folder_names_and_paths("unet_gguf", ["diffusion_models", "unet"]) -update_folder_names_and_paths("clip_gguf", ["text_encoders", "clip"]) - -class GGUFModelPatcher(comfy.model_patcher.ModelPatcher): - patch_on_device = False - - def patch_weight_to_device(self, key, device_to=None, inplace_update=False): - if key not in self.patches: - return - weight = comfy.utils.get_attr(self.model, key) - - patches = self.patches[key] - if is_quantized(weight): - out_weight = weight.to(device_to) - patches = move_patch_to_device(patches, self.load_device if self.patch_on_device else self.offload_device) - # TODO: do we ever have legitimate duplicate patches? (i.e. patch on top of patched weight) - out_weight.patches = [(patches, key)] - else: - inplace_update = self.weight_inplace_update or inplace_update - if key not in self.backup: - self.backup[key] = collections.namedtuple('Dimension', ['weight', 'inplace_update'])( - weight.to(device=self.offload_device, copy=inplace_update), inplace_update - ) - - if device_to is not None: - temp_weight = comfy.model_management.cast_to_device(weight, device_to, torch.float32, copy=True) - else: - temp_weight = weight.to(torch.float32, copy=True) - - out_weight = comfy.lora.calculate_weight(patches, temp_weight, key) - out_weight = comfy.float.stochastic_rounding(out_weight, weight.dtype) - - if inplace_update: - comfy.utils.copy_to_param(self.model, key, out_weight) - else: - comfy.utils.set_attr_param(self.model, key, out_weight) - - def unpatch_model(self, device_to=None, unpatch_weights=True): - if unpatch_weights: - for p in self.model.parameters(): - if is_torch_compatible(p): - continue - patches = getattr(p, "patches", []) - if len(patches) > 0: - p.patches = [] - # TODO: Find another way to not unload after patches - return super().unpatch_model(device_to=device_to, unpatch_weights=unpatch_weights) - - - def pin_weight_to_device(self, key): - op_key = key.rsplit('.', 1)[0] - if not self.mmap_released and op_key in self.named_modules_to_munmap: - # TODO: possible to OOM, find better way to detach - self.named_modules_to_munmap[op_key].to(self.load_device).to(self.offload_device) - del self.named_modules_to_munmap[op_key] - super().pin_weight_to_device(key) - - mmap_released = False - named_modules_to_munmap = {} - - def load(self, *args, force_patch_weights=False, **kwargs): - if not self.mmap_released: - self.named_modules_to_munmap = dict(self.model.named_modules()) - - # always call `patch_weight_to_device` even for lowvram - super().load(*args, force_patch_weights=True, **kwargs) - - # make sure nothing stays linked to mmap after first load - if not self.mmap_released: - linked = [] - if kwargs.get("lowvram_model_memory", 0) > 0: - for n, m in self.named_modules_to_munmap.items(): - if hasattr(m, "weight"): - device = getattr(m.weight, "device", None) - if device == self.offload_device: - linked.append((n, m)) - continue - if hasattr(m, "bias"): - device = getattr(m.bias, "device", None) - if device == self.offload_device: - linked.append((n, m)) - continue - if linked and self.load_device != self.offload_device: - logging.info(f"Attempting to release mmap ({len(linked)})") - for n, m in linked: - # TODO: possible to OOM, find better way to detach - m.to(self.load_device).to(self.offload_device) - self.mmap_released = True - self.named_modules_to_munmap = {} - - def clone(self, *args, **kwargs): - src_cls = self.__class__ - self.__class__ = GGUFModelPatcher - n = super().clone(*args, **kwargs) - n.__class__ = GGUFModelPatcher - self.__class__ = src_cls - # GGUF specific clone values below - n.patch_on_device = getattr(self, "patch_on_device", False) - n.mmap_released = getattr(self, "mmap_released", False) - if src_cls != GGUFModelPatcher: - n.size = 0 # force recalc - return n - -class UnetLoaderGGUF: - @classmethod - def INPUT_TYPES(s): - unet_names = [x for x in folder_paths.get_filename_list("unet_gguf")] - return { - "required": { - "unet_name": (unet_names,), - } - } - - RETURN_TYPES = ("MODEL",) - FUNCTION = "load_unet" - CATEGORY = "bootleg" - TITLE = "Unet Loader (GGUF)" - - def load_unet(self, unet_name, dequant_dtype=None, patch_dtype=None, patch_on_device=None): - unet_path = folder_paths.get_full_path("unet", unet_name) - sd, extra = gguf_sd_loader(unet_path) - - mode = extra.get("gguf_quant_mode") - if mode == "int8_convrot": - # Use ComfyUI native INT8 path (weights stay INT8) - ops = get_gguf_q8_ops(compute_dtype=torch.bfloat16)() - elif mode == "int4_pytorch": - # Use custom INT4 ops class (currently dequant fallback) - ops = GGUFQ4Ops() - else: - ops = GGMLOps() - - if dequant_dtype in ("default", None): - ops.Linear.dequant_dtype = None - elif dequant_dtype in ["target"]: - ops.Linear.dequant_dtype = dequant_dtype - else: - ops.Linear.dequant_dtype = getattr(torch, dequant_dtype) - - if patch_dtype in ("default", None): - ops.Linear.patch_dtype = None - elif patch_dtype in ["target"]: - ops.Linear.patch_dtype = patch_dtype - else: - ops.Linear.patch_dtype = getattr(torch, patch_dtype) - - # init model - - kwargs = {} - valid_params = inspect.signature(comfy.sd.load_diffusion_model_state_dict).parameters - if "metadata" in valid_params: - kwargs["metadata"] = extra.get("metadata", {}) - - model = comfy.sd.load_diffusion_model_state_dict( - sd, model_options={"custom_operations": ops}, **kwargs, - ) - if model is None: - logging.error("ERROR UNSUPPORTED UNET {}".format(unet_path)) - raise RuntimeError("ERROR: Could not detect model type of: {}".format(unet_path)) - model = GGUFModelPatcher.clone(model) - model.patch_on_device = patch_on_device - return (model,) - -class UnetLoaderGGUFAdvanced(UnetLoaderGGUF): - @classmethod - def INPUT_TYPES(s): - unet_names = [x for x in folder_paths.get_filename_list("unet_gguf")] - return { - "required": { - "unet_name": (unet_names,), - "dequant_dtype": (["default", "target", "float32", "float16", "bfloat16"], {"default": "default"}), - "patch_dtype": (["default", "target", "float32", "float16", "bfloat16"], {"default": "default"}), - "patch_on_device": ("BOOLEAN", {"default": False}), - } - } - TITLE = "Unet Loader (GGUF/Advanced)" - -class CLIPLoaderGGUF: - @classmethod - def INPUT_TYPES(s): - base = nodes.CLIPLoader.INPUT_TYPES() - return { - "required": { - "clip_name": (s.get_filename_list(),), - "type": base["required"]["type"], - } - } - - RETURN_TYPES = ("CLIP",) - FUNCTION = "load_clip" - CATEGORY = "bootleg" - TITLE = "CLIPLoader (GGUF)" - - @classmethod - def get_filename_list(s): - files = [] - files += folder_paths.get_filename_list("clip") - files += folder_paths.get_filename_list("clip_gguf") - return sorted(files) - - def load_data(self, ckpt_paths): - clip_data = [] - for p in ckpt_paths: - if p.endswith(".gguf"): - sd = gguf_clip_loader(p) - else: - sd = comfy.utils.load_torch_file(p, safe_load=True) - if "scaled_fp8" in sd: # NOTE: Scaled FP8 would require different custom ops, but only one can be active - raise NotImplementedError(f"Mixing scaled FP8 with GGUF is not supported! Use regular CLIP loader or switch model(s)\n({p})") - clip_data.append(sd) - return clip_data - - def load_patcher(self, clip_paths, clip_type, clip_data): - clip = comfy.sd.load_text_encoder_state_dicts( - clip_type = clip_type, - state_dicts = clip_data, - model_options = { - "custom_operations": GGMLOps, - "initial_device": comfy.model_management.text_encoder_offload_device() - }, - embedding_directory = folder_paths.get_folder_paths("embeddings"), - ) - clip.patcher = GGUFModelPatcher.clone(clip.patcher) - return clip - - def load_clip(self, clip_name, type="stable_diffusion"): - clip_path = folder_paths.get_full_path("clip", clip_name) - clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) - return (self.load_patcher([clip_path], clip_type, self.load_data([clip_path])),) - -class DualCLIPLoaderGGUF(CLIPLoaderGGUF): - @classmethod - def INPUT_TYPES(s): - base = nodes.DualCLIPLoader.INPUT_TYPES() - file_options = (s.get_filename_list(), ) - return { - "required": { - "clip_name1": file_options, - "clip_name2": file_options, - "type": base["required"]["type"], - } - } - - TITLE = "DualCLIPLoader (GGUF)" - - def load_clip(self, clip_name1, clip_name2, type): - clip_path1 = folder_paths.get_full_path("clip", clip_name1) - clip_path2 = folder_paths.get_full_path("clip", clip_name2) - clip_paths = (clip_path1, clip_path2) - clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) - return (self.load_patcher(clip_paths, clip_type, self.load_data(clip_paths)),) - -class TripleCLIPLoaderGGUF(CLIPLoaderGGUF): - @classmethod - def INPUT_TYPES(s): - file_options = (s.get_filename_list(), ) - return { - "required": { - "clip_name1": file_options, - "clip_name2": file_options, - "clip_name3": file_options, - } - } - - TITLE = "TripleCLIPLoader (GGUF)" - - def load_clip(self, clip_name1, clip_name2, clip_name3, type="sd3"): - clip_path1 = folder_paths.get_full_path("clip", clip_name1) - clip_path2 = folder_paths.get_full_path("clip", clip_name2) - clip_path3 = folder_paths.get_full_path("clip", clip_name3) - clip_paths = (clip_path1, clip_path2, clip_path3) - clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) - return (self.load_patcher(clip_paths, clip_type, self.load_data(clip_paths)),) - -class QuadrupleCLIPLoaderGGUF(CLIPLoaderGGUF): - @classmethod - def INPUT_TYPES(s): - file_options = (s.get_filename_list(), ) - return { - "required": { - "clip_name1": file_options, - "clip_name2": file_options, - "clip_name3": file_options, - "clip_name4": file_options, - } - } - - TITLE = "QuadrupleCLIPLoader (GGUF)" - - def load_clip(self, clip_name1, clip_name2, clip_name3, clip_name4, type="stable_diffusion"): - clip_path1 = folder_paths.get_full_path("clip", clip_name1) - clip_path2 = folder_paths.get_full_path("clip", clip_name2) - clip_path3 = folder_paths.get_full_path("clip", clip_name3) - clip_path4 = folder_paths.get_full_path("clip", clip_name4) - clip_paths = (clip_path1, clip_path2, clip_path3, clip_path4) - clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) - return (self.load_patcher(clip_paths, clip_type, self.load_data(clip_paths)),) - -NODE_CLASS_MAPPINGS = { - "UnetLoaderGGUF": UnetLoaderGGUF, - "CLIPLoaderGGUF": CLIPLoaderGGUF, - "DualCLIPLoaderGGUF": DualCLIPLoaderGGUF, - "TripleCLIPLoaderGGUF": TripleCLIPLoaderGGUF, - "QuadrupleCLIPLoaderGGUF": QuadrupleCLIPLoaderGGUF, - "UnetLoaderGGUFAdvanced": UnetLoaderGGUFAdvanced, -} - +# (c) City96 || Apache-2.0 (apache.org/licenses/LICENSE-2.0) +import torch +import logging +import inspect +import collections + +import nodes +import comfy.sd +import comfy.lora +import comfy.float +import comfy.utils +import comfy.model_patcher +import comfy.model_management +import folder_paths + +from .ops import GGMLOps, get_gguf_q8_ops, move_patch_to_device +from .loader import gguf_sd_loader, gguf_clip_loader +from .dequant import is_quantized, is_torch_compatible + +def update_folder_names_and_paths(key, targets=[]): + # check for existing key + base = folder_paths.folder_names_and_paths.get(key, ([], {})) + base = base[0] if isinstance(base[0], (list, set, tuple)) else [] + # find base key & add w/ fallback, sanity check + warning + target = next((x for x in targets if x in folder_paths.folder_names_and_paths), targets[0]) + orig, _ = folder_paths.folder_names_and_paths.get(target, ([], {})) + folder_paths.folder_names_and_paths[key] = (orig or base, {".gguf"}) + if base and base != orig: + logging.warning(f"Unknown file list already present on key {key}: {base}") + +# Add a custom keys for files ending in .gguf +update_folder_names_and_paths("unet_gguf", ["diffusion_models", "unet"]) +update_folder_names_and_paths("clip_gguf", ["text_encoders", "clip"]) + +class GGUFModelPatcher(comfy.model_patcher.ModelPatcher): + patch_on_device = False + + def patch_weight_to_device(self, key, device_to=None, inplace_update=False): + if key not in self.patches: + return + weight = comfy.utils.get_attr(self.model, key) + + patches = self.patches[key] + if is_quantized(weight): + out_weight = weight.to(device_to) + patches = move_patch_to_device(patches, self.load_device if self.patch_on_device else self.offload_device) + # TODO: do we ever have legitimate duplicate patches? (i.e. patch on top of patched weight) + out_weight.patches = [(patches, key)] + else: + inplace_update = self.weight_inplace_update or inplace_update + if key not in self.backup: + self.backup[key] = collections.namedtuple('Dimension', ['weight', 'inplace_update'])( + weight.to(device=self.offload_device, copy=inplace_update), inplace_update + ) + + if device_to is not None: + temp_weight = comfy.model_management.cast_to_device(weight, device_to, torch.float32, copy=True) + else: + temp_weight = weight.to(torch.float32, copy=True) + + out_weight = comfy.lora.calculate_weight(patches, temp_weight, key) + out_weight = comfy.float.stochastic_rounding(out_weight, weight.dtype) + + if inplace_update: + comfy.utils.copy_to_param(self.model, key, out_weight) + else: + comfy.utils.set_attr_param(self.model, key, out_weight) + + def unpatch_model(self, device_to=None, unpatch_weights=True): + if unpatch_weights: + for p in self.model.parameters(): + if is_torch_compatible(p): + continue + patches = getattr(p, "patches", []) + if len(patches) > 0: + p.patches = [] + # TODO: Find another way to not unload after patches + return super().unpatch_model(device_to=device_to, unpatch_weights=unpatch_weights) + + + def pin_weight_to_device(self, key): + op_key = key.rsplit('.', 1)[0] + if not self.mmap_released and op_key in self.named_modules_to_munmap: + # TODO: possible to OOM, find better way to detach + self.named_modules_to_munmap[op_key].to(self.load_device).to(self.offload_device) + del self.named_modules_to_munmap[op_key] + super().pin_weight_to_device(key) + + mmap_released = False + named_modules_to_munmap = {} + + def load(self, *args, force_patch_weights=False, **kwargs): + if not self.mmap_released: + self.named_modules_to_munmap = dict(self.model.named_modules()) + + # always call `patch_weight_to_device` even for lowvram + super().load(*args, force_patch_weights=True, **kwargs) + + # make sure nothing stays linked to mmap after first load + if not self.mmap_released: + linked = [] + if kwargs.get("lowvram_model_memory", 0) > 0: + for n, m in self.named_modules_to_munmap.items(): + if hasattr(m, "weight"): + device = getattr(m.weight, "device", None) + if device == self.offload_device: + linked.append((n, m)) + continue + if hasattr(m, "bias"): + device = getattr(m.bias, "device", None) + if device == self.offload_device: + linked.append((n, m)) + continue + if linked and self.load_device != self.offload_device: + logging.info(f"Attempting to release mmap ({len(linked)})") + for n, m in linked: + # TODO: possible to OOM, find better way to detach + m.to(self.load_device).to(self.offload_device) + self.mmap_released = True + self.named_modules_to_munmap = {} + + def clone(self, *args, **kwargs): + src_cls = self.__class__ + self.__class__ = GGUFModelPatcher + n = super().clone(*args, **kwargs) + n.__class__ = GGUFModelPatcher + self.__class__ = src_cls + # GGUF specific clone values below + n.patch_on_device = getattr(self, "patch_on_device", False) + n.mmap_released = getattr(self, "mmap_released", False) + if src_cls != GGUFModelPatcher: + n.size = 0 # force recalc + return n + +class UnetLoaderGGUF: + @classmethod + def INPUT_TYPES(s): + unet_names = [x for x in folder_paths.get_filename_list("unet_gguf")] + return { + "required": { + "unet_name": (unet_names,), + } + } + + RETURN_TYPES = ("MODEL",) + FUNCTION = "load_unet" + CATEGORY = "bootleg" + TITLE = "Unet Loader (GGUF)" + + def load_unet(self, unet_name, dequant_dtype=None, patch_dtype=None, patch_on_device=None): + unet_path = folder_paths.get_full_path("unet", unet_name) + sd, extra = gguf_sd_loader(unet_path) + + mode = extra.get("gguf_quant_mode") + if mode == "int8_convrot": + # Use ComfyUI native INT8 path (weights stay INT8) + ops = get_gguf_q8_ops(compute_dtype=torch.bfloat16)() + elif mode == "int4_pytorch": + raise RuntimeError( + "Q4_PT is retired because PyTorch's Ampere INT4 kernel is not " + "performance-competitive. Reconvert the model as Q8_CR." + ) + else: + ops = GGMLOps() + + if dequant_dtype in ("default", None): + ops.Linear.dequant_dtype = None + elif dequant_dtype in ["target"]: + ops.Linear.dequant_dtype = dequant_dtype + else: + ops.Linear.dequant_dtype = getattr(torch, dequant_dtype) + + if patch_dtype in ("default", None): + ops.Linear.patch_dtype = None + elif patch_dtype in ["target"]: + ops.Linear.patch_dtype = patch_dtype + else: + ops.Linear.patch_dtype = getattr(torch, patch_dtype) + + # init model + + kwargs = {} + valid_params = inspect.signature(comfy.sd.load_diffusion_model_state_dict).parameters + if "metadata" in valid_params: + kwargs["metadata"] = extra.get("metadata", {}) + + model = comfy.sd.load_diffusion_model_state_dict( + sd, model_options={"custom_operations": ops}, **kwargs, + ) + if model is None: + logging.error("ERROR UNSUPPORTED UNET {}".format(unet_path)) + raise RuntimeError("ERROR: Could not detect model type of: {}".format(unet_path)) + model = GGUFModelPatcher.clone(model) + model.patch_on_device = patch_on_device + return (model,) + +class UnetLoaderGGUFAdvanced(UnetLoaderGGUF): + @classmethod + def INPUT_TYPES(s): + unet_names = [x for x in folder_paths.get_filename_list("unet_gguf")] + return { + "required": { + "unet_name": (unet_names,), + "dequant_dtype": (["default", "target", "float32", "float16", "bfloat16"], {"default": "default"}), + "patch_dtype": (["default", "target", "float32", "float16", "bfloat16"], {"default": "default"}), + "patch_on_device": ("BOOLEAN", {"default": False}), + } + } + TITLE = "Unet Loader (GGUF/Advanced)" + +class CLIPLoaderGGUF: + @classmethod + def INPUT_TYPES(s): + base = nodes.CLIPLoader.INPUT_TYPES() + return { + "required": { + "clip_name": (s.get_filename_list(),), + "type": base["required"]["type"], + } + } + + RETURN_TYPES = ("CLIP",) + FUNCTION = "load_clip" + CATEGORY = "bootleg" + TITLE = "CLIPLoader (GGUF)" + + @classmethod + def get_filename_list(s): + files = [] + files += folder_paths.get_filename_list("clip") + files += folder_paths.get_filename_list("clip_gguf") + return sorted(files) + + def load_data(self, ckpt_paths): + clip_data = [] + for p in ckpt_paths: + if p.endswith(".gguf"): + sd = gguf_clip_loader(p) + else: + sd = comfy.utils.load_torch_file(p, safe_load=True) + if "scaled_fp8" in sd: # NOTE: Scaled FP8 would require different custom ops, but only one can be active + raise NotImplementedError(f"Mixing scaled FP8 with GGUF is not supported! Use regular CLIP loader or switch model(s)\n({p})") + clip_data.append(sd) + return clip_data + + def load_patcher(self, clip_paths, clip_type, clip_data): + clip = comfy.sd.load_text_encoder_state_dicts( + clip_type = clip_type, + state_dicts = clip_data, + model_options = { + "custom_operations": GGMLOps, + "initial_device": comfy.model_management.text_encoder_offload_device() + }, + embedding_directory = folder_paths.get_folder_paths("embeddings"), + ) + clip.patcher = GGUFModelPatcher.clone(clip.patcher) + return clip + + def load_clip(self, clip_name, type="stable_diffusion"): + clip_path = folder_paths.get_full_path("clip", clip_name) + clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) + return (self.load_patcher([clip_path], clip_type, self.load_data([clip_path])),) + +class DualCLIPLoaderGGUF(CLIPLoaderGGUF): + @classmethod + def INPUT_TYPES(s): + base = nodes.DualCLIPLoader.INPUT_TYPES() + file_options = (s.get_filename_list(), ) + return { + "required": { + "clip_name1": file_options, + "clip_name2": file_options, + "type": base["required"]["type"], + } + } + + TITLE = "DualCLIPLoader (GGUF)" + + def load_clip(self, clip_name1, clip_name2, type): + clip_path1 = folder_paths.get_full_path("clip", clip_name1) + clip_path2 = folder_paths.get_full_path("clip", clip_name2) + clip_paths = (clip_path1, clip_path2) + clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) + return (self.load_patcher(clip_paths, clip_type, self.load_data(clip_paths)),) + +class TripleCLIPLoaderGGUF(CLIPLoaderGGUF): + @classmethod + def INPUT_TYPES(s): + file_options = (s.get_filename_list(), ) + return { + "required": { + "clip_name1": file_options, + "clip_name2": file_options, + "clip_name3": file_options, + } + } + + TITLE = "TripleCLIPLoader (GGUF)" + + def load_clip(self, clip_name1, clip_name2, clip_name3, type="sd3"): + clip_path1 = folder_paths.get_full_path("clip", clip_name1) + clip_path2 = folder_paths.get_full_path("clip", clip_name2) + clip_path3 = folder_paths.get_full_path("clip", clip_name3) + clip_paths = (clip_path1, clip_path2, clip_path3) + clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) + return (self.load_patcher(clip_paths, clip_type, self.load_data(clip_paths)),) + +class QuadrupleCLIPLoaderGGUF(CLIPLoaderGGUF): + @classmethod + def INPUT_TYPES(s): + file_options = (s.get_filename_list(), ) + return { + "required": { + "clip_name1": file_options, + "clip_name2": file_options, + "clip_name3": file_options, + "clip_name4": file_options, + } + } + + TITLE = "QuadrupleCLIPLoader (GGUF)" + + def load_clip(self, clip_name1, clip_name2, clip_name3, clip_name4, type="stable_diffusion"): + clip_path1 = folder_paths.get_full_path("clip", clip_name1) + clip_path2 = folder_paths.get_full_path("clip", clip_name2) + clip_path3 = folder_paths.get_full_path("clip", clip_name3) + clip_path4 = folder_paths.get_full_path("clip", clip_name4) + clip_paths = (clip_path1, clip_path2, clip_path3, clip_path4) + clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) + return (self.load_patcher(clip_paths, clip_type, self.load_data(clip_paths)),) + +NODE_CLASS_MAPPINGS = { + "UnetLoaderGGUF": UnetLoaderGGUF, + "CLIPLoaderGGUF": CLIPLoaderGGUF, + "DualCLIPLoaderGGUF": DualCLIPLoaderGGUF, + "TripleCLIPLoaderGGUF": TripleCLIPLoaderGGUF, + "QuadrupleCLIPLoaderGGUF": QuadrupleCLIPLoaderGGUF, + "UnetLoaderGGUFAdvanced": UnetLoaderGGUFAdvanced, +} diff --git a/ops.py b/ops.py index 5b0c839..5904e62 100644 --- a/ops.py +++ b/ops.py @@ -1,465 +1,485 @@ -# (c) City96 || Apache-2.0 (apache.org/licenses/LICENSE-2.0) -import gguf -import json -import torch -import logging - -import comfy.ops -import comfy.lora -import comfy.model_management -from .dequant import dequantize_tensor, is_quantized - -def _valid_compute_dtype(dtype): - return dtype in {torch.float16, torch.bfloat16, torch.float32, torch.float64} - -def _infer_compute_dtype(tensor_type, fallback=None): - if _valid_compute_dtype(fallback): - return fallback - if tensor_type == gguf.GGMLQuantizationType.BF16: - return torch.bfloat16 - if tensor_type == gguf.GGMLQuantizationType.F32: - return torch.float32 - return torch.float16 - -def chained_hasattr(obj, chained_attr): - probe = obj - for attr in chained_attr.split('.'): - if hasattr(probe, attr): - probe = getattr(probe, attr) - else: - return False - return True - -# A bakcward and forward compatible way to get `torch.compiler.disable`. -def get_torch_compiler_disable_decorator(): - def dummy_decorator(*args, **kwargs): - def noop(x): - return x - return noop - - from packaging import version - - if not chained_hasattr(torch, "compiler.disable"): - logging.info("ComfyUI-GGUF: Torch too old for torch.compile - bypassing") - return dummy_decorator # torch too old - elif version.parse(torch.__version__) >= version.parse("2.8"): - logging.info("ComfyUI-GGUF: Allowing full torch compile") - return dummy_decorator # torch compile works - if chained_hasattr(torch, "_dynamo.config.nontraceable_tensor_subclasses"): - logging.info("ComfyUI-GGUF: Allowing full torch compile (nightly)") - return dummy_decorator # torch compile works, nightly before 2.8 release - else: - logging.info("ComfyUI-GGUF: Partial torch compile only, consider updating pytorch") - return torch.compiler.disable - -torch_compiler_disable = get_torch_compiler_disable_decorator() - -class GGMLTensor(torch.Tensor): - """ - Main tensor-like class for storing quantized weights - """ - def __init__(self, *args, tensor_type, tensor_shape, patches=[], compute_dtype=None, **kwargs): - super().__init__() - self.tensor_type = tensor_type - self.tensor_shape = tensor_shape - self.patches = patches - self.compute_dtype = compute_dtype - - def __new__(cls, *args, tensor_type, tensor_shape, patches=[], compute_dtype=None, **kwargs): - return super().__new__(cls, *args, **kwargs) - - def to(self, *args, **kwargs): - new = super().to(*args, **kwargs) - new.tensor_type = getattr(self, "tensor_type", None) - new.tensor_shape = getattr(self, "tensor_shape", new.data.shape) - new.patches = getattr(self, "patches", []).copy() - new.compute_dtype = getattr(self, "compute_dtype", None) - return new - - def clone(self, *args, **kwargs): - return self - - def detach(self, *args, **kwargs): - return self - - def copy_(self, *args, **kwargs): - # fixes .weight.copy_ in comfy/clip_model/CLIPTextModel - try: - return super().copy_(*args, **kwargs) - except Exception as e: - logging.warning(f"ignoring 'copy_' on tensor: {e}") - - def new_empty(self, size, *args, **kwargs): - # Intel Arc fix, ref#50 - new_tensor = super().new_empty(size, *args, **kwargs) - return GGMLTensor( - new_tensor, - tensor_type = getattr(self, "tensor_type", None), - tensor_shape = size, - patches = getattr(self, "patches", []).copy(), - compute_dtype = getattr(self, "compute_dtype", None), - ) - - @property - def dtype(self): - qtype = getattr(self, "tensor_type", None) - if qtype in GGMLLayer.torch_compatible_tensor_types: - return torch.Tensor(self).dtype - return _infer_compute_dtype(qtype, getattr(self, "compute_dtype", None)) - - @property - def shape(self): - if not hasattr(self, "tensor_shape"): - self.tensor_shape = self.size() - return self.tensor_shape - -class GGMLLayer(torch.nn.Module): - """ - This (should) be responsible for de-quantizing on the fly - """ - comfy_cast_weights = True - dequant_dtype = None - patch_dtype = None - largest_layer = False - torch_compatible_tensor_types = {None, gguf.GGMLQuantizationType.F32, gguf.GGMLQuantizationType.F16} - - def is_ggml_quantized(self, *, weight=None, bias=None): - if weight is None: - weight = self.weight - if bias is None: - bias = self.bias - return is_quantized(weight) or is_quantized(bias) - - def _load_from_state_dict(self, state_dict, prefix, *args, **kwargs): - weight, bias = state_dict.get(f"{prefix}weight"), state_dict.get(f"{prefix}bias") - # NOTE: using modified load for linear due to not initializing on creation, see GGMLOps todo - if self.is_ggml_quantized(weight=weight, bias=bias) or isinstance(self, torch.nn.Linear): - return self.ggml_load_from_state_dict(state_dict, prefix, *args, **kwargs) - # Not strictly required, but fixes embedding shape mismatch. Threshold set in loader.py - if isinstance(self, torch.nn.Embedding) and self.weight.shape[0] >= (64 * 1024): - return self.ggml_load_from_state_dict(state_dict, prefix, *args, **kwargs) - return super()._load_from_state_dict(state_dict, prefix, *args, **kwargs) - - def ggml_load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs): - prefix_len = len(prefix) - for k,v in state_dict.items(): - if k[prefix_len:] == "weight": - if isinstance(v, GGMLTensor): - v.compute_dtype = self._ggml_compute_dtype(v, "weight") - self.weight = torch.nn.Parameter(v, requires_grad=False) - elif k[prefix_len:] == "bias" and v is not None: - if isinstance(v, GGMLTensor): - v.compute_dtype = self._ggml_compute_dtype(v, "bias") - self.bias = torch.nn.Parameter(v, requires_grad=False) - else: - unexpected_keys.append(k) - - # For Linear layer with missing weight - if self.weight is None and isinstance(self, torch.nn.Linear): - v = torch.zeros(self.in_features, self.out_features) - self.weight = torch.nn.Parameter(v, requires_grad=False) - missing_keys.append(prefix+"weight") - - # for vram estimation (TODO: less fragile logic?) - if getattr(self.weight, "is_largest_weight", False): - self.largest_layer = True - - def _ggml_compute_dtype(self, tensor, param_name): - if self.dequant_dtype is not None and self.dequant_dtype != "target": - return self.dequant_dtype - model_dtype = getattr(self, f"{param_name}_comfy_model_dtype", None) - return _infer_compute_dtype(getattr(tensor, "tensor_type", None), model_dtype) - - def _save_to_state_dict(self, *args, **kwargs): - if self.is_ggml_quantized(): - return self.ggml_save_to_state_dict(*args, **kwargs) - return super()._save_to_state_dict(*args, **kwargs) - - def ggml_save_to_state_dict(self, destination, prefix, keep_vars): - # This is a fake state dict for vram estimation - weight = torch.zeros_like(self.weight, device=torch.device("meta")) - destination[prefix + "weight"] = weight - if self.bias is not None: - bias = torch.zeros_like(self.bias, device=torch.device("meta")) - destination[prefix + "bias"] = bias - - # Take into account space required for dequantizing the largest tensor - if self.largest_layer: - shape = getattr(self.weight, "tensor_shape", self.weight.shape) - dtype = self.dequant_dtype if self.dequant_dtype and self.dequant_dtype != "target" else torch.float16 - temp = torch.empty(*shape, device=torch.device("meta"), dtype=dtype) - destination[prefix + "temp.weight"] = temp - - return - # This would return the dequantized state dict - destination[prefix + "weight"] = self.get_weight(self.weight) - if bias is not None: - destination[prefix + "bias"] = self.get_weight(self.bias) - - def get_weight(self, tensor, dtype): - if tensor is None: - return - - # consolidate and load patches to GPU in async - patch_list = [] - device = tensor.device - for patches, key in getattr(tensor, "patches", []): - patch_list += move_patch_to_device(patches, device) - - # dequantize tensor while patches load - weight = dequantize_tensor(tensor, dtype, self.dequant_dtype) - - # prevent propagating custom tensor class - if isinstance(weight, GGMLTensor): - weight = torch.Tensor(weight) - - # apply patches - if len(patch_list) > 0: - if self.patch_dtype is None: - weight = comfy.lora.calculate_weight(patch_list, weight, key) - else: - # for testing, may degrade image quality - patch_dtype = dtype if self.patch_dtype == "target" else self.patch_dtype - weight = comfy.lora.calculate_weight(patch_list, weight, key, patch_dtype) - return weight - - @torch_compiler_disable() - def cast_bias_weight(s, input=None, dtype=None, device=None, bias_dtype=None): - if input is not None: - if dtype is None: - dtype = getattr(input, "dtype", torch.float32) - if bias_dtype is None: - bias_dtype = dtype - if device is None: - device = input.device - - bias = None - non_blocking = comfy.model_management.device_supports_non_blocking(device) - if s.bias is not None: - bias = s.get_weight(s.bias.to(device), dtype) - bias = comfy.ops.cast_to(bias, bias_dtype, device, non_blocking=non_blocking, copy=False) - - weight = s.get_weight(s.weight.to(device), dtype) - weight = comfy.ops.cast_to(weight, dtype, device, non_blocking=non_blocking, copy=False) - return weight, bias - - def forward_comfy_cast_weights(self, input, *args, **kwargs): - if self.is_ggml_quantized(): - out = self.forward_ggml_cast_weights(input, *args, **kwargs) - else: - out = super().forward_comfy_cast_weights(input, *args, **kwargs) - - # non-ggml forward might still propagate custom tensor class - if isinstance(out, GGMLTensor): - out = torch.Tensor(out) - return out - - def forward_ggml_cast_weights(self, input): - raise NotImplementedError - -class GGMLOps(comfy.ops.manual_cast): - """ - Dequantize weights on the fly before doing the compute - """ - class Linear(GGMLLayer, comfy.ops.manual_cast.Linear): - def __init__(self, in_features, out_features, bias=True, device=None, dtype=None): - torch.nn.Module.__init__(self) - # TODO: better workaround for reserved memory spike on windows - # Issue is with `torch.empty` still reserving the full memory for the layer - # Windows doesn't over-commit memory so without this 24GB+ of pagefile is used - self.in_features = in_features - self.out_features = out_features - self.weight = None - self.bias = None - self.weight_comfy_model_dtype = dtype - self.bias_comfy_model_dtype = dtype - - def forward_ggml_cast_weights(self, input): - weight, bias = self.cast_bias_weight(input) - return torch.nn.functional.linear(input, weight, bias) - - class Conv2d(GGMLLayer, comfy.ops.manual_cast.Conv2d): - def forward_ggml_cast_weights(self, input): - weight, bias = self.cast_bias_weight(input) - return self._conv_forward(input, weight, bias) - - class Embedding(GGMLLayer, comfy.ops.manual_cast.Embedding): - def forward_ggml_cast_weights(self, input, out_dtype=None): - output_dtype = out_dtype - if self.weight.dtype == torch.float16 or self.weight.dtype == torch.bfloat16: - out_dtype = None - weight, _bias = self.cast_bias_weight(self, device=input.device, dtype=out_dtype) - return torch.nn.functional.embedding( - input, weight, self.padding_idx, self.max_norm, self.norm_type, self.scale_grad_by_freq, self.sparse - ).to(dtype=output_dtype) - - class LayerNorm(GGMLLayer, comfy.ops.manual_cast.LayerNorm): - def forward_ggml_cast_weights(self, input): - if self.weight is None: - return super().forward_comfy_cast_weights(input) - weight, bias = self.cast_bias_weight(input) - return torch.nn.functional.layer_norm(input, self.normalized_shape, weight, bias, self.eps) - - class GroupNorm(GGMLLayer, comfy.ops.manual_cast.GroupNorm): - def forward_ggml_cast_weights(self, input): - weight, bias = self.cast_bias_weight(input) - return torch.nn.functional.group_norm(input, self.num_groups, weight, bias, self.eps) - -def move_patch_to_device(item, device): - if isinstance(item, torch.Tensor): - return item.to(device, non_blocking=True) - elif isinstance(item, tuple): - return tuple(move_patch_to_device(x, device) for x in item) - elif isinstance(item, list): - return [move_patch_to_device(x, device) for x in item] - else: - return item - -def get_gguf_q8_ops(compute_dtype=torch.bfloat16, full_precision_mm=False): - """ - Factory for an ops class that uses ComfyUI's native mixed_precision_ops INT8 path. - Weights are kept as INT8 and matmul uses comfy_kitchen's TensorWiseINT8Layout. - """ - BaseOps = comfy.ops.mixed_precision_ops( - quant_config={}, - compute_dtype=compute_dtype, - full_precision_mm=full_precision_mm, - ) - - class GGUFQ8Ops(BaseOps): - class Linear(BaseOps.Linear): - def __init__(self, in_features, out_features, bias=True, device=None, dtype=None): - # Lazy init: don't allocate weight here; it will be loaded from state dict - torch.nn.Module.__init__(self) - self.factory_kwargs = {"device": device, "dtype": BaseOps._compute_dtype} - self.in_features = in_features - self.out_features = out_features - self.weight = None - if bias: - self.bias = torch.nn.Parameter(torch.empty(out_features, **self.factory_kwargs)) - else: - self.register_parameter("bias", None) - self._orig_shape = (out_features, in_features) - self.tensor_class = None - self._full_precision_mm = BaseOps._full_precision_mm - self._full_precision_mm_config = False - - def _load_from_state_dict(self, *args): - return comfy.ops._load_quantized_module( - self, - torch.nn.Module._load_from_state_dict.__get__(self, type(self)), - *args, - load_extra_params=True, - ) - - return GGUFQ8Ops - - -class GGUFQ4Ops(comfy.ops.manual_cast): - """ - Ops class for PyTorch's CUDA INT4 matrix multiplication. - - GGUF stores a portable nibble-packed representation. The first forward pass - for each layer converts it to PyTorch's hardware-specific packed layout on - the active CUDA device, and subsequent forwards reuse that packed tensor. - """ - class Linear(torch.nn.Module, comfy.ops.CastWeightBiasOp): - def __init__(self, in_features, out_features, bias=True, device=None, dtype=None): - torch.nn.Module.__init__(self) - self.in_features = in_features - self.out_features = out_features - self.weight = None - self.register_parameter("bias", None) - self._orig_shape = (out_features, in_features) - self._group_size = None - self._pad = 0 - self._orig_in_features = in_features - self._int4_cache = {} - self._is_int4 = False - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs): - weight_key = f"{prefix}weight" - scale_key = f"{prefix}weight_scale" - quant_key = f"{prefix}comfy_quant" - - weight = state_dict.pop(weight_key, None) - scale = state_dict.pop(scale_key, None) - quant_raw = state_dict.pop(quant_key, None) - - bias_key = f"{prefix}bias" - bias = state_dict.pop(bias_key, None) - if quant_raw is None: - if weight is None: - missing_keys.append(weight_key) - return - self.weight = torch.nn.Parameter(torch.Tensor(weight), requires_grad=False) - if bias is not None: - self.bias = torch.nn.Parameter(torch.Tensor(bias), requires_grad=False) - self._is_int4 = False - return - - if weight is None or scale is None: - raise RuntimeError(f"Missing INT4 tensors for {prefix}") - - quant_conf = json.loads(bytes(quant_raw.tolist()).decode("utf-8")) - if quant_conf.get("format") != "int4_pytorch": - raise ValueError(f"Unsupported INT4 format for {prefix}") - self._group_size = quant_conf["group_size"] - self._pad = quant_conf.get("pad", 0) - orig_shape = tuple(quant_conf["orig_shape"]) - self._orig_in_features = orig_shape[1] - self._orig_shape = orig_shape - self.out_features = orig_shape[0] - - self.weight = torch.nn.Parameter(weight, requires_grad=False) - self.weight_scale = torch.nn.Parameter(scale, requires_grad=False) - self._is_int4 = True - - if bias is not None: - self.bias = torch.nn.Parameter(bias, requires_grad=False) - for key in (weight_key, scale_key, quant_key, bias_key): - if key in missing_keys: - missing_keys.remove(key) - - @staticmethod - def _inner_k_tiles(): - return 2 - - def _packed_weight(self, device, dtype): - if device.type != "cuda": - raise RuntimeError("Q4_PT requires CUDA; no dequantization fallback is available.") - if dtype not in (torch.float16, torch.bfloat16): - raise RuntimeError(f"Q4_PT requires FP16 or BF16 activations, got {dtype}.") - - cache_key = (device, dtype) - cached = self._int4_cache.get(cache_key) - if cached is not None: - return cached - - serialized = self.weight.to(device=device, dtype=torch.uint8, non_blocking=True) - packed = torch._convert_weight_to_int4pack(serialized, self._inner_k_tiles()) - scale_and_offset = self.weight_scale.to(device=device, dtype=dtype, non_blocking=True) - self._int4_cache[cache_key] = (packed, scale_and_offset) - return packed, scale_and_offset - - def forward(self, input): - if self.weight is None: - raise RuntimeError("Q4_PT weight was not loaded.") - if not self._is_int4: - weight = self.weight.to(device=input.device, dtype=input.dtype) - bias = self.bias.to(device=input.device, dtype=input.dtype) if self.bias is not None else None - return torch.nn.functional.linear(input, weight, bias) - if self.weight_function or self.bias_function: - raise RuntimeError("Q4_PT does not support weight patches or LoRAs without dequantization.") - input_shape = input.shape - input_2d = input.reshape(-1, input_shape[-1]) - if self._pad: - input_2d = torch.nn.functional.pad(input_2d, (0, self._pad)) - - packed, scale_and_offset = self._packed_weight(input.device, input.dtype) - output = torch._weight_int4pack_mm( - input_2d, packed, self._group_size, scale_and_offset - ) - if self.bias is not None: - output = output + self.bias.to(device=input.device, dtype=input.dtype) - return output.reshape(*input_shape[:-1], self.out_features) +# (c) City96 || Apache-2.0 (apache.org/licenses/LICENSE-2.0) +import gguf +import json +import torch +import logging + +import comfy.ops +import comfy.lora +import comfy.model_management +from .dequant import dequantize_tensor, is_quantized + +def _valid_compute_dtype(dtype): + return dtype in {torch.float16, torch.bfloat16, torch.float32, torch.float64} + +def _infer_compute_dtype(tensor_type, fallback=None): + if _valid_compute_dtype(fallback): + return fallback + if tensor_type == gguf.GGMLQuantizationType.BF16: + return torch.bfloat16 + if tensor_type == gguf.GGMLQuantizationType.F32: + return torch.float32 + return torch.float16 + +def chained_hasattr(obj, chained_attr): + probe = obj + for attr in chained_attr.split('.'): + if hasattr(probe, attr): + probe = getattr(probe, attr) + else: + return False + return True + +# A bakcward and forward compatible way to get `torch.compiler.disable`. +def get_torch_compiler_disable_decorator(): + def dummy_decorator(*args, **kwargs): + def noop(x): + return x + return noop + + from packaging import version + + if not chained_hasattr(torch, "compiler.disable"): + logging.info("ComfyUI-GGUF: Torch too old for torch.compile - bypassing") + return dummy_decorator # torch too old + elif version.parse(torch.__version__) >= version.parse("2.8"): + logging.info("ComfyUI-GGUF: Allowing full torch compile") + return dummy_decorator # torch compile works + if chained_hasattr(torch, "_dynamo.config.nontraceable_tensor_subclasses"): + logging.info("ComfyUI-GGUF: Allowing full torch compile (nightly)") + return dummy_decorator # torch compile works, nightly before 2.8 release + else: + logging.info("ComfyUI-GGUF: Partial torch compile only, consider updating pytorch") + return torch.compiler.disable + +torch_compiler_disable = get_torch_compiler_disable_decorator() + +class GGMLTensor(torch.Tensor): + """ + Main tensor-like class for storing quantized weights + """ + def __init__(self, *args, tensor_type, tensor_shape, patches=[], compute_dtype=None, **kwargs): + super().__init__() + self.tensor_type = tensor_type + self.tensor_shape = tensor_shape + self.patches = patches + self.compute_dtype = compute_dtype + + def __new__(cls, *args, tensor_type, tensor_shape, patches=[], compute_dtype=None, **kwargs): + return super().__new__(cls, *args, **kwargs) + + def to(self, *args, **kwargs): + new = super().to(*args, **kwargs) + new.tensor_type = getattr(self, "tensor_type", None) + new.tensor_shape = getattr(self, "tensor_shape", new.data.shape) + new.patches = getattr(self, "patches", []).copy() + new.compute_dtype = getattr(self, "compute_dtype", None) + return new + + def clone(self, *args, **kwargs): + return self + + def detach(self, *args, **kwargs): + return self + + def copy_(self, *args, **kwargs): + # fixes .weight.copy_ in comfy/clip_model/CLIPTextModel + try: + return super().copy_(*args, **kwargs) + except Exception as e: + logging.warning(f"ignoring 'copy_' on tensor: {e}") + + def new_empty(self, size, *args, **kwargs): + # Intel Arc fix, ref#50 + new_tensor = super().new_empty(size, *args, **kwargs) + return GGMLTensor( + new_tensor, + tensor_type = getattr(self, "tensor_type", None), + tensor_shape = size, + patches = getattr(self, "patches", []).copy(), + compute_dtype = getattr(self, "compute_dtype", None), + ) + + @property + def dtype(self): + qtype = getattr(self, "tensor_type", None) + if qtype in GGMLLayer.torch_compatible_tensor_types: + return torch.Tensor(self).dtype + return _infer_compute_dtype(qtype, getattr(self, "compute_dtype", None)) + + @property + def shape(self): + if not hasattr(self, "tensor_shape"): + self.tensor_shape = self.size() + return self.tensor_shape + +class GGMLLayer(torch.nn.Module): + """ + This (should) be responsible for de-quantizing on the fly + """ + comfy_cast_weights = True + dequant_dtype = None + patch_dtype = None + largest_layer = False + torch_compatible_tensor_types = {None, gguf.GGMLQuantizationType.F32, gguf.GGMLQuantizationType.F16} + + def is_ggml_quantized(self, *, weight=None, bias=None): + if weight is None: + weight = self.weight + if bias is None: + bias = self.bias + return is_quantized(weight) or is_quantized(bias) + + def _load_from_state_dict(self, state_dict, prefix, *args, **kwargs): + weight, bias = state_dict.get(f"{prefix}weight"), state_dict.get(f"{prefix}bias") + # NOTE: using modified load for linear due to not initializing on creation, see GGMLOps todo + if self.is_ggml_quantized(weight=weight, bias=bias) or isinstance(self, torch.nn.Linear): + return self.ggml_load_from_state_dict(state_dict, prefix, *args, **kwargs) + # Not strictly required, but fixes embedding shape mismatch. Threshold set in loader.py + if isinstance(self, torch.nn.Embedding) and self.weight.shape[0] >= (64 * 1024): + return self.ggml_load_from_state_dict(state_dict, prefix, *args, **kwargs) + return super()._load_from_state_dict(state_dict, prefix, *args, **kwargs) + + def ggml_load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs): + prefix_len = len(prefix) + for k,v in state_dict.items(): + if k[prefix_len:] == "weight": + if isinstance(v, GGMLTensor): + v.compute_dtype = self._ggml_compute_dtype(v, "weight") + self.weight = torch.nn.Parameter(v, requires_grad=False) + elif k[prefix_len:] == "bias" and v is not None: + if isinstance(v, GGMLTensor): + v.compute_dtype = self._ggml_compute_dtype(v, "bias") + self.bias = torch.nn.Parameter(v, requires_grad=False) + else: + unexpected_keys.append(k) + + # For Linear layer with missing weight + if self.weight is None and isinstance(self, torch.nn.Linear): + v = torch.zeros(self.in_features, self.out_features) + self.weight = torch.nn.Parameter(v, requires_grad=False) + missing_keys.append(prefix+"weight") + + # for vram estimation (TODO: less fragile logic?) + if getattr(self.weight, "is_largest_weight", False): + self.largest_layer = True + + def _ggml_compute_dtype(self, tensor, param_name): + if self.dequant_dtype is not None and self.dequant_dtype != "target": + return self.dequant_dtype + model_dtype = getattr(self, f"{param_name}_comfy_model_dtype", None) + return _infer_compute_dtype(getattr(tensor, "tensor_type", None), model_dtype) + + def _save_to_state_dict(self, *args, **kwargs): + if self.is_ggml_quantized(): + return self.ggml_save_to_state_dict(*args, **kwargs) + return super()._save_to_state_dict(*args, **kwargs) + + def ggml_save_to_state_dict(self, destination, prefix, keep_vars): + # This is a fake state dict for vram estimation + weight = torch.zeros_like(self.weight, device=torch.device("meta")) + destination[prefix + "weight"] = weight + if self.bias is not None: + bias = torch.zeros_like(self.bias, device=torch.device("meta")) + destination[prefix + "bias"] = bias + + # Take into account space required for dequantizing the largest tensor + if self.largest_layer: + shape = getattr(self.weight, "tensor_shape", self.weight.shape) + dtype = self.dequant_dtype if self.dequant_dtype and self.dequant_dtype != "target" else torch.float16 + temp = torch.empty(*shape, device=torch.device("meta"), dtype=dtype) + destination[prefix + "temp.weight"] = temp + + return + # This would return the dequantized state dict + destination[prefix + "weight"] = self.get_weight(self.weight) + if bias is not None: + destination[prefix + "bias"] = self.get_weight(self.bias) + + def get_weight(self, tensor, dtype): + if tensor is None: + return + + # consolidate and load patches to GPU in async + patch_list = [] + device = tensor.device + for patches, key in getattr(tensor, "patches", []): + patch_list += move_patch_to_device(patches, device) + + # dequantize tensor while patches load + weight = dequantize_tensor(tensor, dtype, self.dequant_dtype) + + # prevent propagating custom tensor class + if isinstance(weight, GGMLTensor): + weight = torch.Tensor(weight) + + # apply patches + if len(patch_list) > 0: + if self.patch_dtype is None: + weight = comfy.lora.calculate_weight(patch_list, weight, key) + else: + # for testing, may degrade image quality + patch_dtype = dtype if self.patch_dtype == "target" else self.patch_dtype + weight = comfy.lora.calculate_weight(patch_list, weight, key, patch_dtype) + return weight + + @torch_compiler_disable() + def cast_bias_weight(s, input=None, dtype=None, device=None, bias_dtype=None): + if input is not None: + if dtype is None: + dtype = getattr(input, "dtype", torch.float32) + if bias_dtype is None: + bias_dtype = dtype + if device is None: + device = input.device + + bias = None + non_blocking = comfy.model_management.device_supports_non_blocking(device) + if s.bias is not None: + bias = s.get_weight(s.bias.to(device), dtype) + bias = comfy.ops.cast_to(bias, bias_dtype, device, non_blocking=non_blocking, copy=False) + + weight = s.get_weight(s.weight.to(device), dtype) + weight = comfy.ops.cast_to(weight, dtype, device, non_blocking=non_blocking, copy=False) + return weight, bias + + def forward_comfy_cast_weights(self, input, *args, **kwargs): + if self.is_ggml_quantized(): + out = self.forward_ggml_cast_weights(input, *args, **kwargs) + else: + out = super().forward_comfy_cast_weights(input, *args, **kwargs) + + # non-ggml forward might still propagate custom tensor class + if isinstance(out, GGMLTensor): + out = torch.Tensor(out) + return out + + def forward_ggml_cast_weights(self, input): + raise NotImplementedError + +class GGMLOps(comfy.ops.manual_cast): + """ + Dequantize weights on the fly before doing the compute + """ + class Linear(GGMLLayer, comfy.ops.manual_cast.Linear): + def __init__(self, in_features, out_features, bias=True, device=None, dtype=None): + torch.nn.Module.__init__(self) + # TODO: better workaround for reserved memory spike on windows + # Issue is with `torch.empty` still reserving the full memory for the layer + # Windows doesn't over-commit memory so without this 24GB+ of pagefile is used + self.in_features = in_features + self.out_features = out_features + self.weight = None + self.bias = None + self.weight_comfy_model_dtype = dtype + self.bias_comfy_model_dtype = dtype + + def forward_ggml_cast_weights(self, input): + weight, bias = self.cast_bias_weight(input) + return torch.nn.functional.linear(input, weight, bias) + + class Conv2d(GGMLLayer, comfy.ops.manual_cast.Conv2d): + def forward_ggml_cast_weights(self, input): + weight, bias = self.cast_bias_weight(input) + return self._conv_forward(input, weight, bias) + + class Embedding(GGMLLayer, comfy.ops.manual_cast.Embedding): + def forward_ggml_cast_weights(self, input, out_dtype=None): + output_dtype = out_dtype + if self.weight.dtype == torch.float16 or self.weight.dtype == torch.bfloat16: + out_dtype = None + weight, _bias = self.cast_bias_weight(self, device=input.device, dtype=out_dtype) + return torch.nn.functional.embedding( + input, weight, self.padding_idx, self.max_norm, self.norm_type, self.scale_grad_by_freq, self.sparse + ).to(dtype=output_dtype) + + class LayerNorm(GGMLLayer, comfy.ops.manual_cast.LayerNorm): + def forward_ggml_cast_weights(self, input): + if self.weight is None: + return super().forward_comfy_cast_weights(input) + weight, bias = self.cast_bias_weight(input) + return torch.nn.functional.layer_norm(input, self.normalized_shape, weight, bias, self.eps) + + class GroupNorm(GGMLLayer, comfy.ops.manual_cast.GroupNorm): + def forward_ggml_cast_weights(self, input): + weight, bias = self.cast_bias_weight(input) + return torch.nn.functional.group_norm(input, self.num_groups, weight, bias, self.eps) + +def move_patch_to_device(item, device): + if isinstance(item, torch.Tensor): + return item.to(device, non_blocking=True) + elif isinstance(item, tuple): + return tuple(move_patch_to_device(x, device) for x in item) + elif isinstance(item, list): + return [move_patch_to_device(x, device) for x in item] + else: + return item + +def get_gguf_q8_ops(compute_dtype=torch.bfloat16, full_precision_mm=False): + """ + Factory for an ops class that uses ComfyUI's native mixed_precision_ops INT8 path. + Weights are kept as INT8 and matmul uses comfy_kitchen's TensorWiseINT8Layout. + """ + BaseOps = comfy.ops.mixed_precision_ops( + quant_config={}, + compute_dtype=compute_dtype, + full_precision_mm=full_precision_mm, + ) + + class GGUFQ8Ops(BaseOps): + class Linear(BaseOps.Linear): + def __init__(self, in_features, out_features, bias=True, device=None, dtype=None): + # Lazy init: don't allocate weight here; it will be loaded from state dict + torch.nn.Module.__init__(self) + self.factory_kwargs = {"device": device, "dtype": BaseOps._compute_dtype} + self.in_features = in_features + self.out_features = out_features + self.weight = None + if bias: + self.bias = torch.nn.Parameter(torch.empty(out_features, **self.factory_kwargs)) + else: + self.register_parameter("bias", None) + self._orig_shape = (out_features, in_features) + self.tensor_class = None + self._full_precision_mm = BaseOps._full_precision_mm + self._full_precision_mm_config = False + + def _load_from_state_dict(self, *args): + return comfy.ops._load_quantized_module( + self, + torch.nn.Module._load_from_state_dict.__get__(self, type(self)), + *args, + load_extra_params=True, + ) + + return GGUFQ8Ops + + +# Retired experimental Q4_PT implementation. No loader or node runtime path +# references this class until a performant W4A16 backend is available. +class RetiredGGUFQ4Ops(comfy.ops.manual_cast): + """ + Ops class for PyTorch's native compact INT4 GEMM. + + Packed weights are created transiently at invocation time. This keeps + low-VRAM offloading viable without retaining a second copy of all weights. + """ + class Linear(torch.nn.Module, comfy.ops.CastWeightBiasOp): + comfy_cast_weights = True + + def __init__(self, in_features, out_features, bias=True, device=None, dtype=None): + torch.nn.Module.__init__(self) + self.in_features = in_features + self.out_features = out_features + self.weight = None + self.register_parameter("bias", None) + self._orig_shape = (out_features, in_features) + self._group_size = None + self._pad = 0 + self._orig_in_features = in_features + self._is_int4 = False + + def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs): + weight_key = f"{prefix}weight" + scale_key = f"{prefix}weight_scale" + quant_key = f"{prefix}comfy_quant" + + weight = state_dict.pop(weight_key, None) + scale = state_dict.pop(scale_key, None) + quant_raw = state_dict.pop(quant_key, None) + + bias_key = f"{prefix}bias" + bias = state_dict.pop(bias_key, None) + if quant_raw is None: + if weight is None: + missing_keys.append(weight_key) + return + self.weight = torch.nn.Parameter(torch.Tensor(weight), requires_grad=False) + if bias is not None: + self.bias = torch.nn.Parameter(torch.Tensor(bias), requires_grad=False) + self._is_int4 = False + return + + if weight is None or scale is None: + raise RuntimeError(f"Missing INT4 tensors for {prefix}") + + quant_conf = json.loads(bytes(quant_raw.tolist()).decode("utf-8")) + if quant_conf.get("format") not in {"int4_compact_gemm", "int4_pytorch"}: + raise ValueError(f"Unsupported INT4 format for {prefix}") + self._group_size = quant_conf["group_size"] + self._pad = quant_conf.get("pad", 0) + orig_shape = tuple(quant_conf["orig_shape"]) + self._orig_in_features = orig_shape[1] + self._orig_shape = orig_shape + self.out_features = orig_shape[0] + + self.weight = torch.nn.Parameter(weight, requires_grad=False) + self.weight_scale = torch.nn.Parameter(scale, requires_grad=False) + self._is_int4 = True + + if bias is not None: + self.bias = torch.nn.Parameter(bias, requires_grad=False) + for key in (weight_key, scale_key, quant_key, bias_key): + if key in missing_keys: + missing_keys.remove(key) + + def forward(self, input): + if self.weight is None: + raise RuntimeError("Q4_PT weight was not loaded.") + if not self._is_int4: + weight = self.weight.to(device=input.device, dtype=input.dtype) + bias = self.bias.to(device=input.device, dtype=input.dtype) if self.bias is not None else None + return torch.nn.functional.linear(input, weight, bias) + if self.weight_function or self.bias_function: + raise RuntimeError("Q4_PT does not support weight patches or LoRAs without dequantization.") + input_shape = input.shape + input_2d = input.reshape(-1, input_shape[-1]) + if self._pad: + input_2d = torch.nn.functional.pad(input_2d, (0, self._pad)) + + if self._group_size != 64: + raise RuntimeError( + f"Q4_PT requires PyTorch's group-size-64 INT4 operator, got {self._group_size}." + ) + if input_2d.device.type != "cuda": + raise RuntimeError("Q4_PT requires a CUDA device.") + if input_2d.dtype != torch.bfloat16: + raise RuntimeError( + f"Q4_PT requires BF16 activations for PyTorch's native INT4 operator, got {input_2d.dtype}." + ) + + weight = torch.Tensor( + self.weight.to(device=input.device, dtype=torch.uint8, non_blocking=True) + ) + scale_and_offset = self.weight_scale.to( + device=input.device, + dtype=torch.bfloat16, + non_blocking=True, + ) + packed_features = weight.size(-1) * 2 + native_padding = (-packed_features) % 128 + if native_padding: + # PyTorch's INT4 packer needs K to be a multiple of 128. + # A zero-valued group and zero input features preserve the + # original result for K=64 projections such as Krea2's input. + if native_padding % self._group_size: + raise RuntimeError( + f"Cannot pad Q4_PT input width {packed_features} to PyTorch's INT4 tile size." + ) + input_2d = torch.nn.functional.pad(input_2d, (0, native_padding)) + weight = torch.nn.functional.pad(weight, (0, native_padding // 2)) + scale_and_offset = torch.nn.functional.pad( + scale_and_offset, + (0, 0, 0, 0, 0, native_padding // self._group_size), + ) + + packed_weight = torch._convert_weight_to_int4pack(weight, 8) + output = torch._weight_int4pack_mm( + input_2d, + packed_weight, + self._group_size, + scale_and_offset, + ) + if self.bias is not None: + output = output + self.bias.to(device=input.device, dtype=input.dtype) + return output.reshape(*input_shape[:-1], self.out_features) diff --git a/tools/convert.py b/tools/convert.py index f2813c2..2bd2993 100644 --- a/tools/convert.py +++ b/tools/convert.py @@ -1,573 +1,568 @@ -# (c) City96 || Apache-2.0 (apache.org/licenses/LICENSE-2.0) -import os -import gguf -import json -import torch -import logging -import argparse -from tqdm import tqdm -from safetensors.torch import load_file, save_file - -QUANTIZATION_THRESHOLD = 1024 -REARRANGE_THRESHOLD = 512 -MAX_TENSOR_NAME_LENGTH = 127 -MAX_TENSOR_DIMS = 4 - -class ModelTemplate: - arch = "invalid" # string describing architecture - shape_fix = False # whether to reshape tensors - keys_detect = [] # list of lists to match in state dict - keys_banned = [] # list of keys that should mark model as invalid for conversion - keys_hiprec = [] # list of keys that need to be kept in fp32 for some reason - keys_ignore = [] # list of strings to ignore keys by when found - - def handle_nd_tensor(self, key, data): - raise NotImplementedError(f"Tensor detected that exceeds dims supported by C++ code! ({key} @ {data.shape})") - -class ModelFlux(ModelTemplate): - arch = "flux" - keys_detect = [ - ("transformer_blocks.0.attn.norm_added_k.weight",), - ("double_blocks.0.img_attn.proj.weight",), - ] - keys_banned = ["transformer_blocks.0.attn.norm_added_k.weight",] - -class ModelSD3(ModelTemplate): - arch = "sd3" - keys_detect = [ - ("transformer_blocks.0.attn.add_q_proj.weight",), - ("joint_blocks.0.x_block.attn.qkv.weight",), - ] - keys_banned = ["transformer_blocks.0.attn.add_q_proj.weight",] - -class ModelAura(ModelTemplate): - arch = "aura" - keys_detect = [ - ("double_layers.3.modX.1.weight",), - ("joint_transformer_blocks.3.ff_context.out_projection.weight",), - ] - keys_banned = ["joint_transformer_blocks.3.ff_context.out_projection.weight",] - -class ModelHiDream(ModelTemplate): - arch = "hidream" - keys_detect = [ - ( - "caption_projection.0.linear.weight", - "double_stream_blocks.0.block.ff_i.shared_experts.w3.weight" - ) - ] - keys_hiprec = [ - # nn.parameter, can't load from BF16 ver - ".ff_i.gate.weight", - "img_emb.emb_pos" - ] - -class CosmosPredict2(ModelTemplate): - arch = "cosmos" - keys_detect = [ - ( - "blocks.0.mlp.layer1.weight", - "blocks.0.adaln_modulation_cross_attn.1.weight", - ) - ] - keys_hiprec = ["pos_embedder"] - keys_ignore = ["_extra_state", "accum_"] - -class ModelHyVid(ModelTemplate): - arch = "hyvid" - keys_detect = [ - ( - "double_blocks.0.img_attn_proj.weight", - "txt_in.individual_token_refiner.blocks.1.self_attn_qkv.weight", - ) - ] - - def handle_nd_tensor(self, key, data): - # hacky but don't have any better ideas - path = f"./fix_5d_tensors_{self.arch}.safetensors" # TODO: somehow get a path here?? - if os.path.isfile(path): - raise RuntimeError(f"5D tensor fix file already exists! {path}") - fsd = {key: torch.from_numpy(data)} - tqdm.write(f"5D key found in state dict! Manual fix required! - {key} {data.shape}") - save_file(fsd, path) - -class ModelWan(ModelHyVid): - arch = "wan" - keys_detect = [ - ( - "blocks.0.self_attn.norm_q.weight", - "text_embedding.2.weight", - "head.modulation", - ) - ] - keys_hiprec = [ - ".modulation" # nn.parameter, can't load from BF16 ver - ] - -class ModelLTXV(ModelTemplate): - arch = "ltxv" - keys_detect = [ - ( - "adaln_single.emb.timestep_embedder.linear_2.weight", - "transformer_blocks.27.scale_shift_table", - "caption_projection.linear_2.weight", - ) - ] - keys_hiprec = [ - "scale_shift_table" # nn.parameter, can't load from BF16 base quant - ] - -class ModelSDXL(ModelTemplate): - arch = "sdxl" - shape_fix = True - keys_detect = [ - ("down_blocks.0.downsamplers.0.conv.weight", "add_embedding.linear_1.weight",), - ( - "input_blocks.3.0.op.weight", "input_blocks.6.0.op.weight", - "output_blocks.2.2.conv.weight", "output_blocks.5.2.conv.weight", - ), # Non-diffusers - ("label_emb.0.0.weight",), - ] - -class ModelSD1(ModelTemplate): - arch = "sd1" - shape_fix = True - keys_detect = [ - ("down_blocks.0.downsamplers.0.conv.weight",), - ( - "input_blocks.3.0.op.weight", "input_blocks.6.0.op.weight", "input_blocks.9.0.op.weight", - "output_blocks.2.1.conv.weight", "output_blocks.5.2.conv.weight", "output_blocks.8.2.conv.weight" - ), # Non-diffusers - ] - -class ModelLumina2(ModelTemplate): - arch = "lumina2" - keys_detect = [ - ("cap_embedder.1.weight", "context_refiner.0.attention.qkv.weight") - ] - -class ModelIdeogram(ModelTemplate): - arch = "ideogram" - keys_detect = [ - ( - "t_embedding.mlp_in.weight", - "layers.0.attention.qkv.weight", - "final_layer.linear.weight", - ) - ] - -class ModelKrea2(ModelTemplate): - """ - Krea-2 is a novel architecture from krea.ai — NOT Ideogram4. - Key structure (verified from Krea2_Turbo_fp8mixed.safetensors header): - blocks.N.attn.{wq,wk,wv,wo,gate} — separate Q/K/V/O projections + gating - blocks.N.attn.qknorm.{qnorm,knorm} — Q/K norms - blocks.N.mlp.{up,gate,down} — SwiGLU-style MLP - blocks.N.mod.lin — per-block modulation - blocks.N.{pre,post}norm.scale — RMSNorm scales - txtfusion.layerwise_blocks.N.* — layerwise text-image cross-attention - txtfusion.refiner_blocks.N.* — refiner text-image cross-attention - txtfusion.projector — text projector - first.weight / last.* — input / output projections - tmlp.N / tproj.N — timestep MLP / projection - - NOTE: ComfyUI core must have krea2 diffusion model support - (i.e. a detection branch for 'blocks.0.attn.wq.weight' in model_detection.py - and a matching supported_models entry) for the GGUF to load correctly. - Krea-2 was released 2026-06-22; verify your ComfyUI build is up to date. - """ - arch = "krea2" - keys_detect = [ - ( - "blocks.0.attn.wq.weight", - "txtfusion.projector.weight", - "first.weight", - ), - ( - "blocks.0.attn.wq.weight", - "txtfusion.layerwise_blocks.0.attn.wq.weight", - "last.linear.weight", - ), - ] - keys_hiprec = [ - "blocks.0.mod.lin", # modulation parameters — keep at full precision - "last.modulation.lin", - "tmlp.", - "tproj.", - ] - -arch_list = [ModelFlux, ModelSD3, ModelAura, ModelHiDream, CosmosPredict2, - ModelLTXV, ModelHyVid, ModelWan, ModelSDXL, ModelSD1, ModelLumina2, - ModelKrea2, ModelIdeogram] - -def is_model_arch(model, state_dict): - # check if model is correct - matched = False - invalid = False - for match_list in model.keys_detect: - if all(key in state_dict for key in match_list): - matched = True - invalid = any(key in state_dict for key in model.keys_banned) - break - assert not invalid, "Model architecture not allowed for conversion! (i.e. reference VS diffusers format)" - return matched - -def detect_arch(state_dict): - model_arch = None - for arch in arch_list: - if is_model_arch(arch, state_dict): - model_arch = arch() - break - assert model_arch is not None, "Unknown model architecture!" - return model_arch - -QUANT_TYPE_MAP = { - "F16": (gguf.GGMLQuantizationType.F16, gguf.LlamaFileType.MOSTLY_F16), - "BF16": (gguf.GGMLQuantizationType.BF16, gguf.LlamaFileType.MOSTLY_BF16), - "Q8_0": (gguf.GGMLQuantizationType.Q8_0, gguf.LlamaFileType.MOSTLY_Q8_0), - "Q5_1": (gguf.GGMLQuantizationType.Q5_1, gguf.LlamaFileType.MOSTLY_Q5_1), - "Q5_0": (gguf.GGMLQuantizationType.Q5_0, gguf.LlamaFileType.MOSTLY_Q5_0), - "Q4_1": (gguf.GGMLQuantizationType.Q4_1, gguf.LlamaFileType.MOSTLY_Q4_1), - "Q4_0": (gguf.GGMLQuantizationType.Q4_0, gguf.LlamaFileType.MOSTLY_Q4_0), - "Q8_CR": (gguf.GGMLQuantizationType.I8, None), # INT8 ConvRot (ComfyUI native) - "Q4_PT": (gguf.GGMLQuantizationType.I8, None), # INT4 PyTorch (packed as raw bytes) -} - - -def quantize_int8_convrot(weight, convrot_groupsize=256): - """ - Quantize a 2D Linear weight to INT8 with ConvRot grouping. - Uses per-output-channel scales to match ComfyUI's TensorWiseINT8Layout. - """ - weight = weight.to(torch.float32) - orig_shape = tuple(weight.shape) - groupsize = next( - ( - size - for size in (convrot_groupsize, 64, 16, 4) - if size <= weight.shape[1] and weight.shape[1] % size == 0 - ), - None, - ) - if groupsize is not None: - from comfy_kitchen.tensor.int8_utils import _build_hadamard, _rotate_weight - - hadamard = _build_hadamard(groupsize, device=weight.device, dtype=weight.dtype) - weight = _rotate_weight(weight, hadamard, groupsize) - - scale = weight.abs().amax(dim=1, keepdim=True).clamp_min(1e-9) / 127.0 - qdata = (weight / scale).round().clamp(-128, 127).to(torch.int8) - quant_conf = { - "format": "int8_tensorwise", - "convrot": groupsize is not None, - "weight_rotated": groupsize is not None, - "per_row": True, - } - if groupsize is not None: - quant_conf["convrot_groupsize"] = groupsize - return qdata, scale, quant_conf, orig_shape - - -def quantize_int4_pytorch(weight, group_size=64): - """ - Quantize a 2D Linear weight for PyTorch's native INT4 kernel. - Weights are serialized as packed uint8 [n, k//2]. The runtime converts this - portable representation to PyTorch's device-specific INT4 layout. - """ - orig_shape = tuple(weight.shape) - n, k = orig_shape - weight = weight.to(torch.float32) - - pad = 0 - if k % group_size != 0: - pad = group_size - (k % group_size) - weight = torch.nn.functional.pad(weight, (0, pad)) - k = k + pad - - w_grouped = weight.reshape(n, k // group_size, group_size) - w_min = w_grouped.amin(dim=-1, keepdim=True) - w_max = w_grouped.amax(dim=-1, keepdim=True) - scale = (w_max - w_min) / 15.0 - scale = scale.clamp_min(1e-9) - q = ((w_grouped - w_min) / scale).round().clamp(0, 15).to(torch.uint8) - - q_flat = q.reshape(n, k) - packed = ((q_flat[:, 0::2] << 4) | q_flat[:, 1::2]).to(torch.uint8) - - qsz = torch.zeros(k // group_size, n, 2, dtype=torch.float32) - qsz[:, :, 0] = scale.reshape(n, k // group_size).t() - # _weight_int4pack_mm dequantizes as (q - 8) * scale + offset. - qsz[:, :, 1] = (w_min + 8 * scale).reshape(n, k // group_size).t() - - quant_conf = { - "format": "int4_pytorch", - "group_size": group_size, - "orig_shape": orig_shape, - "pad": pad, - } - return packed, qsz, quant_conf, orig_shape - -def parse_args(): - parser = argparse.ArgumentParser( - description="Convert diffusion model safetensors/ckpt to GGUF." - " By default produces an F16/BF16 GGUF; use --quant-type to quantize." - ) - parser.add_argument("--src", required=True, help="Source model ckpt/safetensors file.") - parser.add_argument("--dst", help="Output GGUF file path.") - parser.add_argument( - "--quant-type", - choices=list(QUANT_TYPE_MAP.keys()), - default=None, - help="Target quantization type for eligible 2-D+ tensors " - "(1-D biases/scales stay F32). Defaults to F16/BF16 matching the source dtype.", - ) - args = parser.parse_args() - - if not os.path.isfile(args.src): - parser.error("No input provided!") - - return args - -def strip_prefix(state_dict): - # prefix for mixed state dict - prefix = None - for pfx in ["model.diffusion_model.", "model."]: - if any([x.startswith(pfx) for x in state_dict.keys()]): - prefix = pfx - break - - # prefix for uniform state dict - if prefix is None: - for pfx in ["net."]: - if all([x.startswith(pfx) for x in state_dict.keys()]): - prefix = pfx - break - - # strip prefix if found - if prefix is not None: - logging.info(f"State dict prefix found: '{prefix}'") - sd = {} - for k, v in state_dict.items(): - if prefix not in k: - continue - k = k.replace(prefix, "") - sd[k] = v - else: - logging.debug("State dict has no prefix") - sd = state_dict - - return sd - -def load_state_dict(path): - if any(path.endswith(x) for x in [".ckpt", ".pt", ".bin", ".pth"]): - state_dict = torch.load(path, map_location="cpu", weights_only=True) - for subkey in ["model", "module"]: - if subkey in state_dict: - state_dict = state_dict[subkey] - break - if len(state_dict) < 20: - raise RuntimeError(f"pt subkey load failed: {state_dict.keys()}") - else: - state_dict = load_file(path) - - return strip_prefix(state_dict) - -def handle_tensors(writer, state_dict, model_arch, quant_type=None, quant_type_name=None): - # Pre-collect per-tensor FP8 scales (0-dim float32 tensors named "{key}_scale"). - # These must be applied to their FP8 weight tensors before GGUF quantization. - # The actual weight value is fp8_value * scale; ignoring scale produces wrong magnitudes. - fp8_scales = { - k[:-len("_scale")]: v.item() - for k, v in state_dict.items() - if k.endswith("_scale") and len(v.shape) == 0 and v.dtype == torch.float32 - } - if fp8_scales: - tqdm.write(f"Found {len(fp8_scales)} FP8 per-tensor scale(s); will apply before quantization.") - - name_lengths = tuple(sorted( - ((key, len(key)) for key in state_dict.keys()), - key=lambda item: item[1], - reverse=True, - )) - if not name_lengths: - return - max_name_len = name_lengths[0][1] - if max_name_len > MAX_TENSOR_NAME_LENGTH: - bad_list = ", ".join(f"{key!r} ({namelen})" for key, namelen in name_lengths if namelen > MAX_TENSOR_NAME_LENGTH) - raise ValueError(f"Can only handle tensor names up to {MAX_TENSOR_NAME_LENGTH} characters. Tensors exceeding the limit: {bad_list}") - for key, data in tqdm(state_dict.items()): - old_dtype = data.dtype - - if any(x in key for x in model_arch.keys_ignore): - tqdm.write(f"Filtering ignored key: '{key}'") - continue - - # comfy_quant tensors are FP8 scale factors specific to ComfyUI's custom FP8 format. - # weight_scale tensors are 0-dim per-tensor FP8 scales (e.g. from torchao/fp8 fine-tunes). - # Both are meaningless after GGUF re-quantization and must be dropped so the loader - # does not try to apply them to already-GGUF-dequantized weights. - if key.endswith(".comfy_quant") or key.endswith("_scale") and len(data.shape) == 0: - tqdm.write(f"Dropping FP8 scale tensor: '{key}'") - continue - - # 0-dim (scalar) tensors cannot be stored in GGUF and have no meaningful weight data. - if len(data.shape) == 0: - tqdm.write(f"Skipping 0-dim scalar tensor: '{key}'") - continue - - if data.dtype == torch.bfloat16: - data = data.to(torch.float32).numpy() - # this is so we don't break torch 2.0.X - elif data.dtype in [getattr(torch, "float8_e4m3fn", "_invalid"), getattr(torch, "float8_e5m2", "_invalid")]: - data = data.to(torch.float32) - if key in fp8_scales: - data = data * fp8_scales[key] # apply per-tensor dequantization scale - data = data.numpy() - else: - data = data.numpy() - - n_dims = len(data.shape) - data_shape = data.shape - if old_dtype == torch.bfloat16: - data_qtype = gguf.GGMLQuantizationType.BF16 - # elif old_dtype == torch.float32: - # data_qtype = gguf.GGMLQuantizationType.F32 - else: - data_qtype = gguf.GGMLQuantizationType.F16 - - # The max no. of dimensions that can be handled by the quantization code is 4. - if n_dims > MAX_TENSOR_DIMS: - model_arch.handle_nd_tensor(key, data) - continue - - n_params = 1 - for dim_size in data_shape: - n_params *= dim_size - - _FP8_DTYPES = {getattr(torch, "float8_e4m3fn", None), getattr(torch, "float8_e5m2", None)} - {None} - apply_quantization_rules = ( - quant_type_name in ("Q8_CR", "Q4_PT") - or old_dtype in (torch.float32, torch.bfloat16) - or old_dtype in _FP8_DTYPES - ) - if apply_quantization_rules: - if n_dims == 1: - # One-dimensional tensors should be kept in F32. - data_qtype = gguf.GGMLQuantizationType.F32 - elif n_params <= QUANTIZATION_THRESHOLD: - data_qtype = gguf.GGMLQuantizationType.F32 - elif any(x in key for x in model_arch.keys_hiprec): - data_qtype = gguf.GGMLQuantizationType.F32 - elif n_dims == 4 and "conv" in key.lower(): - # Native quantized paths are Linear-only. - data_qtype = gguf.GGMLQuantizationType.F16 - elif quant_type is not None: - data_qtype = quant_type - - if quant_type_name in ("Q8_CR", "Q4_PT") and n_dims > 1 and n_dims != 2: - # Custom native layouts only represent Linear matrices. - data_qtype = gguf.GGMLQuantizationType.F16 - elif quant_type_name == "Q4_PT" and data_shape[0] % 8 != 0: - # PyTorch's CUDA INT4 layout has an eight-output-channel tile. - data_qtype = gguf.GGMLQuantizationType.F16 - - # Custom quantization paths for Q8_CR and Q4_PT - if quant_type_name in ("Q8_CR", "Q4_PT") and data_qtype == quant_type and n_dims == 2: - if quant_type_name == "Q8_CR": - qdata, scale, quant_conf, orig_shape = quantize_int8_convrot(torch.from_numpy(data)) - writer.add_tensor(key, qdata.numpy(), raw_dtype=gguf.GGMLQuantizationType.I8) - writer.add_tensor(f"{key}_scale", scale.numpy(), raw_dtype=gguf.GGMLQuantizationType.F32) - writer.add_string(f"comfy.gguf.quant.{key}", json.dumps(quant_conf)) - continue - - elif quant_type_name == "Q4_PT": - packed, qsz, quant_conf, orig_shape = quantize_int4_pytorch(torch.from_numpy(data)) - writer.add_tensor(key, packed.numpy(), raw_dtype=gguf.GGMLQuantizationType.I8) - writer.add_tensor(f"{key}_scale", qsz.numpy(), raw_dtype=gguf.GGMLQuantizationType.F32) - writer.add_string(f"comfy.gguf.quant.{key}", json.dumps(quant_conf)) - continue - - if (model_arch.shape_fix # NEVER reshape for models such as flux - and n_dims > 1 # Skip one-dimensional tensors - and n_params >= REARRANGE_THRESHOLD # Only rearrange tensors meeting the size requirement - and (n_params / 256).is_integer() # Rearranging only makes sense if total elements is divisible by 256 - and not (data.shape[-1] / 256).is_integer() # Only need to rearrange if the last dimension is not divisible by 256 - ): - orig_shape = data.shape - data = data.reshape(n_params // 256, 256) - writer.add_array(f"comfy.gguf.orig_shape.{key}", tuple(int(dim) for dim in orig_shape)) - - try: - data = gguf.quants.quantize(data, data_qtype) - except (AttributeError, gguf.QuantError) as e: - tqdm.write(f"falling back to F16: {e}") - data_qtype = gguf.GGMLQuantizationType.F16 - data = gguf.quants.quantize(data, data_qtype) - - new_name = key # do we need to rename? - - shape_str = f"{{{', '.join(str(n) for n in reversed(data.shape))}}}" - tqdm.write(f"{f'%-{max_name_len + 4}s' % f'{new_name}'} {old_dtype} --> {data_qtype.name}, shape = {shape_str}") - - writer.add_tensor(new_name, data, raw_dtype=data_qtype) - -def convert_file(path, dst_path=None, interact=True, overwrite=False, quant_type_name=None): - # load & run model detection logic - state_dict = load_state_dict(path) - model_arch = detect_arch(state_dict) - logging.info(f"* Architecture detected from input: {model_arch.arch}") - - # resolve quant type from name if provided - quant_type = None - if quant_type_name is not None and quant_type_name in QUANT_TYPE_MAP: - quant_type, ftype_gguf = QUANT_TYPE_MAP[quant_type_name] - ftype_name = quant_type_name - else: - # detect & set dtype from source file - dtypes = [x.dtype for x in state_dict.values()] - dtypes = {x: dtypes.count(x) for x in set(dtypes)} - main_dtype = max(dtypes, key=dtypes.get) - - if main_dtype == torch.bfloat16: - ftype_name = "BF16" - ftype_gguf = gguf.LlamaFileType.MOSTLY_BF16 - # elif main_dtype == torch.float32: - # ftype_name = "F32" - # ftype_gguf = None - else: - ftype_name = "F16" - ftype_gguf = gguf.LlamaFileType.MOSTLY_F16 - - if dst_path is None: - dst_path = f"{os.path.splitext(path)[0]}-{ftype_name}.gguf" - elif "{ftype}" in dst_path: # lcpp logic - dst_path = dst_path.replace("{ftype}", ftype_name) - - if os.path.isfile(dst_path) and not overwrite: - if interact: - input("Output exists enter to continue or ctrl+c to abort!") - else: - raise OSError("Output exists and overwriting is disabled!") - - # handle actual file - writer = gguf.GGUFWriter(path=None, arch=model_arch.arch) - writer.add_quantization_version(gguf.GGML_QUANT_VERSION) - if ftype_gguf is not None: - writer.add_file_type(ftype_gguf) - - handle_tensors(writer, state_dict, model_arch, quant_type=quant_type, quant_type_name=quant_type_name) - writer.write_header_to_file(path=dst_path) - writer.write_kv_data_to_file() - writer.write_tensors_to_file(progress=True) - writer.close() - - fix = f"./fix_5d_tensors_{model_arch.arch}.safetensors" - if os.path.isfile(fix): - logging.warning(f"\n### Warning! Fix file found at '{fix}'") - logging.warning(" you most likely need to run 'fix_5d_tensors.py' after quantization.") - - return dst_path, model_arch - -if __name__ == "__main__": - args = parse_args() +# (c) City96 || Apache-2.0 (apache.org/licenses/LICENSE-2.0) +import os +import gguf +import json +import torch +import logging +import argparse +from tqdm import tqdm +from safetensors.torch import load_file, save_file + +QUANTIZATION_THRESHOLD = 1024 +REARRANGE_THRESHOLD = 512 +MAX_TENSOR_NAME_LENGTH = 127 +MAX_TENSOR_DIMS = 4 + +class ModelTemplate: + arch = "invalid" # string describing architecture + shape_fix = False # whether to reshape tensors + keys_detect = [] # list of lists to match in state dict + keys_banned = [] # list of keys that should mark model as invalid for conversion + keys_hiprec = [] # list of keys that need to be kept in fp32 for some reason + keys_ignore = [] # list of strings to ignore keys by when found + + def handle_nd_tensor(self, key, data): + raise NotImplementedError(f"Tensor detected that exceeds dims supported by C++ code! ({key} @ {data.shape})") + +class ModelFlux(ModelTemplate): + arch = "flux" + keys_detect = [ + ("transformer_blocks.0.attn.norm_added_k.weight",), + ("double_blocks.0.img_attn.proj.weight",), + ] + keys_banned = ["transformer_blocks.0.attn.norm_added_k.weight",] + +class ModelSD3(ModelTemplate): + arch = "sd3" + keys_detect = [ + ("transformer_blocks.0.attn.add_q_proj.weight",), + ("joint_blocks.0.x_block.attn.qkv.weight",), + ] + keys_banned = ["transformer_blocks.0.attn.add_q_proj.weight",] + +class ModelAura(ModelTemplate): + arch = "aura" + keys_detect = [ + ("double_layers.3.modX.1.weight",), + ("joint_transformer_blocks.3.ff_context.out_projection.weight",), + ] + keys_banned = ["joint_transformer_blocks.3.ff_context.out_projection.weight",] + +class ModelHiDream(ModelTemplate): + arch = "hidream" + keys_detect = [ + ( + "caption_projection.0.linear.weight", + "double_stream_blocks.0.block.ff_i.shared_experts.w3.weight" + ) + ] + keys_hiprec = [ + # nn.parameter, can't load from BF16 ver + ".ff_i.gate.weight", + "img_emb.emb_pos" + ] + +class CosmosPredict2(ModelTemplate): + arch = "cosmos" + keys_detect = [ + ( + "blocks.0.mlp.layer1.weight", + "blocks.0.adaln_modulation_cross_attn.1.weight", + ) + ] + keys_hiprec = ["pos_embedder"] + keys_ignore = ["_extra_state", "accum_"] + +class ModelHyVid(ModelTemplate): + arch = "hyvid" + keys_detect = [ + ( + "double_blocks.0.img_attn_proj.weight", + "txt_in.individual_token_refiner.blocks.1.self_attn_qkv.weight", + ) + ] + + def handle_nd_tensor(self, key, data): + # hacky but don't have any better ideas + path = f"./fix_5d_tensors_{self.arch}.safetensors" # TODO: somehow get a path here?? + if os.path.isfile(path): + raise RuntimeError(f"5D tensor fix file already exists! {path}") + fsd = {key: torch.from_numpy(data)} + tqdm.write(f"5D key found in state dict! Manual fix required! - {key} {data.shape}") + save_file(fsd, path) + +class ModelWan(ModelHyVid): + arch = "wan" + keys_detect = [ + ( + "blocks.0.self_attn.norm_q.weight", + "text_embedding.2.weight", + "head.modulation", + ) + ] + keys_hiprec = [ + ".modulation" # nn.parameter, can't load from BF16 ver + ] + +class ModelLTXV(ModelTemplate): + arch = "ltxv" + keys_detect = [ + ( + "adaln_single.emb.timestep_embedder.linear_2.weight", + "transformer_blocks.27.scale_shift_table", + "caption_projection.linear_2.weight", + ) + ] + keys_hiprec = [ + "scale_shift_table" # nn.parameter, can't load from BF16 base quant + ] + +class ModelSDXL(ModelTemplate): + arch = "sdxl" + shape_fix = True + keys_detect = [ + ("down_blocks.0.downsamplers.0.conv.weight", "add_embedding.linear_1.weight",), + ( + "input_blocks.3.0.op.weight", "input_blocks.6.0.op.weight", + "output_blocks.2.2.conv.weight", "output_blocks.5.2.conv.weight", + ), # Non-diffusers + ("label_emb.0.0.weight",), + ] + +class ModelSD1(ModelTemplate): + arch = "sd1" + shape_fix = True + keys_detect = [ + ("down_blocks.0.downsamplers.0.conv.weight",), + ( + "input_blocks.3.0.op.weight", "input_blocks.6.0.op.weight", "input_blocks.9.0.op.weight", + "output_blocks.2.1.conv.weight", "output_blocks.5.2.conv.weight", "output_blocks.8.2.conv.weight" + ), # Non-diffusers + ] + +class ModelLumina2(ModelTemplate): + arch = "lumina2" + keys_detect = [ + ("cap_embedder.1.weight", "context_refiner.0.attention.qkv.weight") + ] + +class ModelIdeogram(ModelTemplate): + arch = "ideogram" + keys_detect = [ + ( + "t_embedding.mlp_in.weight", + "layers.0.attention.qkv.weight", + "final_layer.linear.weight", + ) + ] + +class ModelKrea2(ModelTemplate): + """ + Krea-2 is a novel architecture from krea.ai — NOT Ideogram4. + Key structure (verified from Krea2_Turbo_fp8mixed.safetensors header): + blocks.N.attn.{wq,wk,wv,wo,gate} — separate Q/K/V/O projections + gating + blocks.N.attn.qknorm.{qnorm,knorm} — Q/K norms + blocks.N.mlp.{up,gate,down} — SwiGLU-style MLP + blocks.N.mod.lin — per-block modulation + blocks.N.{pre,post}norm.scale — RMSNorm scales + txtfusion.layerwise_blocks.N.* — layerwise text-image cross-attention + txtfusion.refiner_blocks.N.* — refiner text-image cross-attention + txtfusion.projector — text projector + first.weight / last.* — input / output projections + tmlp.N / tproj.N — timestep MLP / projection + + NOTE: ComfyUI core must have krea2 diffusion model support + (i.e. a detection branch for 'blocks.0.attn.wq.weight' in model_detection.py + and a matching supported_models entry) for the GGUF to load correctly. + Krea-2 was released 2026-06-22; verify your ComfyUI build is up to date. + """ + arch = "krea2" + keys_detect = [ + ( + "blocks.0.attn.wq.weight", + "txtfusion.projector.weight", + "first.weight", + ), + ( + "blocks.0.attn.wq.weight", + "txtfusion.layerwise_blocks.0.attn.wq.weight", + "last.linear.weight", + ), + ] + keys_hiprec = [ + "blocks.0.mod.lin", # modulation parameters — keep at full precision + "last.modulation.lin", + "tmlp.", + "tproj.", + ] + +arch_list = [ModelFlux, ModelSD3, ModelAura, ModelHiDream, CosmosPredict2, + ModelLTXV, ModelHyVid, ModelWan, ModelSDXL, ModelSD1, ModelLumina2, + ModelKrea2, ModelIdeogram] + +def is_model_arch(model, state_dict): + # check if model is correct + matched = False + invalid = False + for match_list in model.keys_detect: + if all(key in state_dict for key in match_list): + matched = True + invalid = any(key in state_dict for key in model.keys_banned) + break + assert not invalid, "Model architecture not allowed for conversion! (i.e. reference VS diffusers format)" + return matched + +def detect_arch(state_dict): + model_arch = None + for arch in arch_list: + if is_model_arch(arch, state_dict): + model_arch = arch() + break + assert model_arch is not None, "Unknown model architecture!" + return model_arch + +QUANT_TYPE_MAP = { + "F16": (gguf.GGMLQuantizationType.F16, gguf.LlamaFileType.MOSTLY_F16), + "BF16": (gguf.GGMLQuantizationType.BF16, gguf.LlamaFileType.MOSTLY_BF16), + "Q8_0": (gguf.GGMLQuantizationType.Q8_0, gguf.LlamaFileType.MOSTLY_Q8_0), + "Q5_1": (gguf.GGMLQuantizationType.Q5_1, gguf.LlamaFileType.MOSTLY_Q5_1), + "Q5_0": (gguf.GGMLQuantizationType.Q5_0, gguf.LlamaFileType.MOSTLY_Q5_0), + "Q4_1": (gguf.GGMLQuantizationType.Q4_1, gguf.LlamaFileType.MOSTLY_Q4_1), + "Q4_0": (gguf.GGMLQuantizationType.Q4_0, gguf.LlamaFileType.MOSTLY_Q4_0), + "Q8_CR": (gguf.GGMLQuantizationType.I8, None), # INT8 ConvRot (ComfyUI native) + # Q4_PT is retired pending a performant Ampere W4A16 backend. + # "Q4_PT": (gguf.GGMLQuantizationType.I8, None), +} + + +def quantize_int8_convrot(weight, convrot_groupsize=256): + """ + Quantize a 2D Linear weight to INT8 with ConvRot grouping. + Uses per-output-channel scales to match ComfyUI's TensorWiseINT8Layout. + """ + weight = weight.to(torch.float32) + orig_shape = tuple(weight.shape) + groupsize = next( + ( + size + for size in (convrot_groupsize, 64, 16, 4) + if size <= weight.shape[1] and weight.shape[1] % size == 0 + ), + None, + ) + if groupsize is not None: + from comfy_kitchen.tensor.int8_utils import _build_hadamard, _rotate_weight + + hadamard = _build_hadamard(groupsize, device=weight.device, dtype=weight.dtype) + weight = _rotate_weight(weight, hadamard, groupsize) + + scale = weight.abs().amax(dim=1, keepdim=True).clamp_min(1e-9) / 127.0 + qdata = (weight / scale).round().clamp(-128, 127).to(torch.int8) + quant_conf = { + "format": "int8_tensorwise", + "convrot": groupsize is not None, + "weight_rotated": groupsize is not None, + "per_row": True, + } + if groupsize is not None: + quant_conf["convrot_groupsize"] = groupsize + return qdata, scale, quant_conf, orig_shape + + +def retired_quantize_int4_pytorch(weight, group_size=64): + """ + Quantize a 2D Linear weight for PyTorch's native INT4 kernel. + Weights are serialized as packed uint8 [n, k//2]. The runtime converts this + portable representation to PyTorch's device-specific INT4 layout. + """ + orig_shape = tuple(weight.shape) + n, k = orig_shape + weight = weight.to(torch.float32) + + pad = 0 + if k % group_size != 0: + pad = group_size - (k % group_size) + weight = torch.nn.functional.pad(weight, (0, pad)) + k = k + pad + + w_grouped = weight.reshape(n, k // group_size, group_size) + w_min = w_grouped.amin(dim=-1, keepdim=True) + w_max = w_grouped.amax(dim=-1, keepdim=True) + scale = (w_max - w_min) / 15.0 + scale = scale.clamp_min(1e-9) + q = ((w_grouped - w_min) / scale).round().clamp(0, 15).to(torch.uint8) + + q_flat = q.reshape(n, k) + packed = ((q_flat[:, 0::2] << 4) | q_flat[:, 1::2]).to(torch.uint8) + + qsz = torch.zeros(k // group_size, n, 2, dtype=torch.float32) + qsz[:, :, 0] = scale.reshape(n, k // group_size).t() + # _weight_int4pack_mm dequantizes as (q - 8) * scale + offset. + qsz[:, :, 1] = (w_min + 8 * scale).reshape(n, k // group_size).t() + + quant_conf = { + "format": "int4_compact_gemm", + "group_size": group_size, + "orig_shape": orig_shape, + "pad": pad, + } + return packed, qsz, quant_conf, orig_shape + +def parse_args(): + parser = argparse.ArgumentParser( + description="Convert diffusion model safetensors/ckpt to GGUF." + " By default produces an F16/BF16 GGUF; use --quant-type to quantize." + ) + parser.add_argument("--src", required=True, help="Source model ckpt/safetensors file.") + parser.add_argument("--dst", help="Output GGUF file path.") + parser.add_argument( + "--quant-type", + choices=list(QUANT_TYPE_MAP.keys()), + default=None, + help="Target quantization type for eligible 2-D+ tensors " + "(1-D biases/scales stay F32). Defaults to F16/BF16 matching the source dtype.", + ) + args = parser.parse_args() + + if not os.path.isfile(args.src): + parser.error("No input provided!") + + return args + +def strip_prefix(state_dict): + # prefix for mixed state dict + prefix = None + for pfx in ["model.diffusion_model.", "model."]: + if any([x.startswith(pfx) for x in state_dict.keys()]): + prefix = pfx + break + + # prefix for uniform state dict + if prefix is None: + for pfx in ["net."]: + if all([x.startswith(pfx) for x in state_dict.keys()]): + prefix = pfx + break + + # strip prefix if found + if prefix is not None: + logging.info(f"State dict prefix found: '{prefix}'") + sd = {} + for k, v in state_dict.items(): + if prefix not in k: + continue + k = k.replace(prefix, "") + sd[k] = v + else: + logging.debug("State dict has no prefix") + sd = state_dict + + return sd + +def load_state_dict(path): + if any(path.endswith(x) for x in [".ckpt", ".pt", ".bin", ".pth"]): + state_dict = torch.load(path, map_location="cpu", weights_only=True) + for subkey in ["model", "module"]: + if subkey in state_dict: + state_dict = state_dict[subkey] + break + if len(state_dict) < 20: + raise RuntimeError(f"pt subkey load failed: {state_dict.keys()}") + else: + state_dict = load_file(path) + + return strip_prefix(state_dict) + +def handle_tensors(writer, state_dict, model_arch, quant_type=None, quant_type_name=None): + # Pre-collect per-tensor FP8 scales (0-dim float32 tensors named "{key}_scale"). + # These must be applied to their FP8 weight tensors before GGUF quantization. + # The actual weight value is fp8_value * scale; ignoring scale produces wrong magnitudes. + fp8_scales = { + k[:-len("_scale")]: v.item() + for k, v in state_dict.items() + if k.endswith("_scale") and len(v.shape) == 0 and v.dtype == torch.float32 + } + if fp8_scales: + tqdm.write(f"Found {len(fp8_scales)} FP8 per-tensor scale(s); will apply before quantization.") + + name_lengths = tuple(sorted( + ((key, len(key)) for key in state_dict.keys()), + key=lambda item: item[1], + reverse=True, + )) + if not name_lengths: + return + max_name_len = name_lengths[0][1] + if max_name_len > MAX_TENSOR_NAME_LENGTH: + bad_list = ", ".join(f"{key!r} ({namelen})" for key, namelen in name_lengths if namelen > MAX_TENSOR_NAME_LENGTH) + raise ValueError(f"Can only handle tensor names up to {MAX_TENSOR_NAME_LENGTH} characters. Tensors exceeding the limit: {bad_list}") + for key, data in tqdm(state_dict.items()): + old_dtype = data.dtype + + if any(x in key for x in model_arch.keys_ignore): + tqdm.write(f"Filtering ignored key: '{key}'") + continue + + # comfy_quant tensors are FP8 scale factors specific to ComfyUI's custom FP8 format. + # weight_scale tensors are 0-dim per-tensor FP8 scales (e.g. from torchao/fp8 fine-tunes). + # Both are meaningless after GGUF re-quantization and must be dropped so the loader + # does not try to apply them to already-GGUF-dequantized weights. + if key.endswith(".comfy_quant") or key.endswith("_scale") and len(data.shape) == 0: + tqdm.write(f"Dropping FP8 scale tensor: '{key}'") + continue + + # 0-dim (scalar) tensors cannot be stored in GGUF and have no meaningful weight data. + if len(data.shape) == 0: + tqdm.write(f"Skipping 0-dim scalar tensor: '{key}'") + continue + + if data.dtype == torch.bfloat16: + data = data.to(torch.float32).numpy() + # this is so we don't break torch 2.0.X + elif data.dtype in [getattr(torch, "float8_e4m3fn", "_invalid"), getattr(torch, "float8_e5m2", "_invalid")]: + data = data.to(torch.float32) + if key in fp8_scales: + data = data * fp8_scales[key] # apply per-tensor dequantization scale + data = data.numpy() + else: + data = data.numpy() + + n_dims = len(data.shape) + data_shape = data.shape + if old_dtype == torch.bfloat16: + data_qtype = gguf.GGMLQuantizationType.BF16 + # elif old_dtype == torch.float32: + # data_qtype = gguf.GGMLQuantizationType.F32 + else: + data_qtype = gguf.GGMLQuantizationType.F16 + + # The max no. of dimensions that can be handled by the quantization code is 4. + if n_dims > MAX_TENSOR_DIMS: + model_arch.handle_nd_tensor(key, data) + continue + + n_params = 1 + for dim_size in data_shape: + n_params *= dim_size + + _FP8_DTYPES = {getattr(torch, "float8_e4m3fn", None), getattr(torch, "float8_e5m2", None)} - {None} + apply_quantization_rules = ( + quant_type_name == "Q8_CR" + or old_dtype in (torch.float32, torch.bfloat16) + or old_dtype in _FP8_DTYPES + ) + if apply_quantization_rules: + if n_dims == 1: + # One-dimensional tensors should be kept in F32. + data_qtype = gguf.GGMLQuantizationType.F32 + elif n_params <= QUANTIZATION_THRESHOLD: + data_qtype = gguf.GGMLQuantizationType.F32 + elif any(x in key for x in model_arch.keys_hiprec): + data_qtype = gguf.GGMLQuantizationType.F32 + elif n_dims == 4 and "conv" in key.lower(): + # Native quantized paths are Linear-only. + data_qtype = gguf.GGMLQuantizationType.F16 + elif quant_type is not None: + data_qtype = quant_type + + if quant_type_name == "Q8_CR" and n_dims > 1 and n_dims != 2: + # Custom native layouts only represent Linear matrices. + data_qtype = gguf.GGMLQuantizationType.F16 + # Q4_PT layout restrictions are retained with + # retired_quantize_int4_pytorch and are intentionally not selectable. + + # Q8_CR is the supported custom quantization path. + if quant_type_name == "Q8_CR" and data_qtype == quant_type and n_dims == 2: + if quant_type_name == "Q8_CR": + qdata, scale, quant_conf, orig_shape = quantize_int8_convrot(torch.from_numpy(data)) + writer.add_tensor(key, qdata.numpy(), raw_dtype=gguf.GGMLQuantizationType.I8) + writer.add_tensor(f"{key}_scale", scale.numpy(), raw_dtype=gguf.GGMLQuantizationType.F32) + writer.add_string(f"comfy.gguf.quant.{key}", json.dumps(quant_conf)) + continue + + # Q4_PT emission is retired with its runtime backend. + + if (model_arch.shape_fix # NEVER reshape for models such as flux + and n_dims > 1 # Skip one-dimensional tensors + and n_params >= REARRANGE_THRESHOLD # Only rearrange tensors meeting the size requirement + and (n_params / 256).is_integer() # Rearranging only makes sense if total elements is divisible by 256 + and not (data.shape[-1] / 256).is_integer() # Only need to rearrange if the last dimension is not divisible by 256 + ): + orig_shape = data.shape + data = data.reshape(n_params // 256, 256) + writer.add_array(f"comfy.gguf.orig_shape.{key}", tuple(int(dim) for dim in orig_shape)) + + try: + data = gguf.quants.quantize(data, data_qtype) + except (AttributeError, gguf.QuantError) as e: + tqdm.write(f"falling back to F16: {e}") + data_qtype = gguf.GGMLQuantizationType.F16 + data = gguf.quants.quantize(data, data_qtype) + + new_name = key # do we need to rename? + + shape_str = f"{{{', '.join(str(n) for n in reversed(data.shape))}}}" + tqdm.write(f"{f'%-{max_name_len + 4}s' % f'{new_name}'} {old_dtype} --> {data_qtype.name}, shape = {shape_str}") + + writer.add_tensor(new_name, data, raw_dtype=data_qtype) + +def convert_file(path, dst_path=None, interact=True, overwrite=False, quant_type_name=None): + # load & run model detection logic + state_dict = load_state_dict(path) + model_arch = detect_arch(state_dict) + logging.info(f"* Architecture detected from input: {model_arch.arch}") + + # resolve quant type from name if provided + quant_type = None + if quant_type_name is not None and quant_type_name in QUANT_TYPE_MAP: + quant_type, ftype_gguf = QUANT_TYPE_MAP[quant_type_name] + ftype_name = quant_type_name + else: + # detect & set dtype from source file + dtypes = [x.dtype for x in state_dict.values()] + dtypes = {x: dtypes.count(x) for x in set(dtypes)} + main_dtype = max(dtypes, key=dtypes.get) + + if main_dtype == torch.bfloat16: + ftype_name = "BF16" + ftype_gguf = gguf.LlamaFileType.MOSTLY_BF16 + # elif main_dtype == torch.float32: + # ftype_name = "F32" + # ftype_gguf = None + else: + ftype_name = "F16" + ftype_gguf = gguf.LlamaFileType.MOSTLY_F16 + + if dst_path is None: + dst_path = f"{os.path.splitext(path)[0]}-{ftype_name}.gguf" + elif "{ftype}" in dst_path: # lcpp logic + dst_path = dst_path.replace("{ftype}", ftype_name) + + if os.path.isfile(dst_path) and not overwrite: + if interact: + input("Output exists enter to continue or ctrl+c to abort!") + else: + raise OSError("Output exists and overwriting is disabled!") + + # handle actual file + writer = gguf.GGUFWriter(path=None, arch=model_arch.arch) + writer.add_quantization_version(gguf.GGML_QUANT_VERSION) + if ftype_gguf is not None: + writer.add_file_type(ftype_gguf) + + handle_tensors(writer, state_dict, model_arch, quant_type=quant_type, quant_type_name=quant_type_name) + writer.write_header_to_file(path=dst_path) + writer.write_kv_data_to_file() + writer.write_tensors_to_file(progress=True) + writer.close() + + fix = f"./fix_5d_tensors_{model_arch.arch}.safetensors" + if os.path.isfile(fix): + logging.warning(f"\n### Warning! Fix file found at '{fix}'") + logging.warning(" you most likely need to run 'fix_5d_tensors.py' after quantization.") + + return dst_path, model_arch + +if __name__ == "__main__": + args = parse_args() convert_file(args.src, args.dst, quant_type_name=args.quant_type) \ No newline at end of file From 297672043c4b6e211aef47a160b08530f62af12f Mon Sep 17 00:00:00 2001 From: molbal Date: Sun, 26 Jul 2026 22:25:22 +0200 Subject: [PATCH 09/10] Adds docs --- README.md | 97 ++++++++++++++++++++++++++++- __init__.py | 4 +- web/docs/CLIPLoaderGGUF.md | 18 ++++++ web/docs/DualCLIPLoaderGGUF.md | 18 ++++++ web/docs/QuadrupleCLIPLoaderGGUF.md | 18 ++++++ web/docs/TripleCLIPLoaderGGUF.md | 17 +++++ web/docs/UnetLoaderGGUF.md | 20 ++++++ web/docs/UnetLoaderGGUFAdvanced.md | 26 ++++++++ 8 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 web/docs/CLIPLoaderGGUF.md create mode 100644 web/docs/DualCLIPLoaderGGUF.md create mode 100644 web/docs/QuadrupleCLIPLoaderGGUF.md create mode 100644 web/docs/TripleCLIPLoaderGGUF.md create mode 100644 web/docs/UnetLoaderGGUF.md create mode 100644 web/docs/UnetLoaderGGUFAdvanced.md diff --git a/README.md b/README.md index 97c152a..cd2307e 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,89 @@ Initial support for quantizing T5 has also been added recently, these can be use See the instructions in the [tools](https://github.com/city96/ComfyUI-GGUF/tree/main/tools) folder for how to create your own quants. +## Converting Krea 2 and Ideogram 4 models + +The converter detects supported Krea 2 and Ideogram 4 checkpoints directly. +Provide an existing `.safetensors`, `.ckpt`, `.pt`, `.pth`, or `.bin` diffusion +model file; no model-specific conversion script is required. + +Run these commands from the `ComfyUI-GGUF` directory, replacing the source and +destination paths with your model names: + +```bash +# Compact standard GGUF +python tools/convert.py --src /path/to/krea2_or_ideogram.safetensors \ + --dst /path/to/model-Q4_0.gguf --quant-type Q4_0 + +# Higher-quality standard GGUF +python tools/convert.py --src /path/to/krea2_or_ideogram.safetensors \ + --dst /path/to/model-Q8_0.gguf --quant-type Q8_0 + +# Recommended for RTX 30-series NVIDIA GPUs +python tools/convert.py --src /path/to/krea2_or_ideogram.safetensors \ + --dst /path/to/model-Q8_CR.gguf --quant-type Q8_CR +``` + +For the portable Windows distribution, use its embedded Python executable: + +```bat +.\python_embeded\python.exe .\ComfyUI\custom_nodes\ComfyUI-GGUF\tools\convert.py ^ + --src C:\path\to\krea2_or_ideogram.safetensors ^ + --dst C:\path\to\model-Q8_CR.gguf --quant-type Q8_CR +``` + +Place the resulting GGUF in `ComfyUI/models/unet` or +`ComfyUI/models/diffusion_models`, then load it with **Unet Loader (GGUF)**. + +## Supported conversion formats + +The standard GGUF formats use the package's normal GGML loader path. `Q8_CR` +is a custom ComfyUI-native INT8 layout for eligible Linear weights. + +| Format | Storage / execution | Recommended use | +| --- | --- | --- | +| `F16` | FP16 GGUF | Maximum compatibility with half-precision storage. | +| `BF16` | BF16 GGUF | Preserve BF16 source models where the target supports BF16. | +| `Q8_0` | Standard GGML 8-bit quantization | Excellent general-quality 8-bit GGUF; portable and straightforward to offload. | +| `Q5_1` | Standard GGML 5-bit quantization | Lower storage with a quality-oriented 5-bit format. | +| `Q5_0` | Standard GGML 5-bit quantization | Lower storage alternative to `Q5_1`. | +| `Q4_1` | Standard GGML 4-bit quantization | Smaller files when VRAM or RAM is constrained. | +| `Q4_0` | Standard GGML 4-bit quantization | Smallest supported standard diffusion-model format; expect the largest quality trade-off. | +| `Q8_CR` | Per-row INT8 ConvRot through ComfyUI native ops | **Maintainer recommendation for NVIDIA RTX 30-series systems.** | + + +> [!WARNING] +> `_K` quant formats are not supported for diffusion models; they are supported only for text encoders. + ## Native weight-only quantization -The converter supports two custom, global quantization modes for DiT/transformer -UNets: +The converter supports one custom global quantization mode for DiT/transformer +UNets: `Q8_CR`. + +### What Q8_CR is, why it exists, and how it works + +`Q8_CR` is an INT8 weight-only format designed for transformer-style diffusion +models. Its purpose is to reduce GGUF model storage and VRAM pressure while +preserving the fast native INT8 Linear operations available on supported NVIDIA +GPUs. It is especially useful when the full diffusion model does not fit in +VRAM and ComfyUI needs to offload weights to CPU memory. + +During conversion, the converter: + +1. Selects eligible 2-D Linear weights. One-dimensional tensors, small + tensors, and architecture-designated sensitive tensors stay FP32; Conv2d + weights stay FP16. +2. Applies the compatible ConvRot/Hadamard rotation to each eligible weight + matrix. +3. Quantizes the rotated weights to INT8 using an FP32 scale for every output + row. +4. Stores the INT8 payload, scales, and ConvRot metadata in the GGUF file. + +During loading, the GGUF loader recognizes this metadata and passes the raw +INT8 weights and row scales to ComfyUI's `TensorWiseINT8Layout`. On supported +CUDA systems, ComfyUI executes the native INT8/ConvRot Linear path directly; +it does not first expand the weight matrix to FP16 or BF16. The ordinary GGUF +container still provides memory-mapped loading and CPU offload behavior. - `Q8_CR` stores eligible 2-D Linear weights as per-row INT8 ConvRot. It uses ComfyUI's native `TensorWiseINT8Layout` path, so weights remain INT8 during @@ -66,6 +145,20 @@ Q8_CR keeps 1-D, small, and architecture-designated high-precision tensors in FP32. Conv2d weights remain FP16 because these modes accelerate Linear matrix multiplication only. +### Maintainer recommendation: Q8_CR on RTX 30-series + +For NVIDIA RTX 30-series systems, the maintainer recommends `Q8_CR` for Krea 2 +and Ideogram 4. It combines: + +- Fast native INT8 operations on these GPUs through ComfyUI's ConvRot backend. +- GGUF's convenient CPU offload and memory-mapped model storage behavior. +- The generally excellent image quality expected from 8-bit GGUF + quantization, while retaining selected sensitive tensors in higher precision. + +Use `Q8_0` instead when you need the conventional portable GGML 8-bit format. +Use `Q4_0` primarily when the smaller model footprint matters more than +quality or sampling speed. + ### Q8_CR platform support Q8_CR does not require CUDA. It uses ComfyUI's `comfy_kitchen` layout backend: diff --git a/__init__.py b/__init__.py index a03726e..9cd3727 100644 --- a/__init__.py +++ b/__init__.py @@ -1,3 +1,5 @@ +WEB_DIRECTORY = "./web" + # only import if running as a custom node try: import comfy.utils @@ -6,4 +8,4 @@ else: from .nodes import NODE_CLASS_MAPPINGS NODE_DISPLAY_NAME_MAPPINGS = {k:v.TITLE for k,v in NODE_CLASS_MAPPINGS.items()} - __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] + __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY'] diff --git a/web/docs/CLIPLoaderGGUF.md b/web/docs/CLIPLoaderGGUF.md new file mode 100644 index 0000000..940b4a1 --- /dev/null +++ b/web/docs/CLIPLoaderGGUF.md @@ -0,0 +1,18 @@ +# CLIP Loader (GGUF) + +Loads one text encoder from a GGUF model file and returns a ComfyUI `CLIP`. + +## Parameters + +- **clip_name**: A GGUF text encoder from `models/clip` or + `models/text_encoders`. Regular compatible checkpoint files are also listed. +- **type**: Select the model family expected by the workflow, such as + `stable_diffusion`, `sd3`, or `krea2`. + +## Usage + +Connect **CLIP** to `CLIP Text Encode` nodes. The selected **type** must match +the diffusion model; for example, Krea 2 workflows require the `krea2` type. + +GGUF text encoders are loaded with the package's GGML operations and may use +less VRAM than full-precision checkpoints. diff --git a/web/docs/DualCLIPLoaderGGUF.md b/web/docs/DualCLIPLoaderGGUF.md new file mode 100644 index 0000000..c55803f --- /dev/null +++ b/web/docs/DualCLIPLoaderGGUF.md @@ -0,0 +1,18 @@ +# Dual CLIP Loader (GGUF) + +Loads two text encoder files and returns one combined ComfyUI `CLIP` object. + +## Parameters + +- **clip_name1**: First encoder file. +- **clip_name2**: Second encoder file. +- **type**: The model family required by the downstream diffusion model. + +Files can be GGUF or compatible regular text-encoder checkpoints. Select the +same pair and model type normally used by the equivalent built-in Dual CLIP +loader. + +## Usage + +Connect the output to the workflow's text-encoding nodes. Both encoders must +match the selected model family. diff --git a/web/docs/QuadrupleCLIPLoaderGGUF.md b/web/docs/QuadrupleCLIPLoaderGGUF.md new file mode 100644 index 0000000..eb84fa6 --- /dev/null +++ b/web/docs/QuadrupleCLIPLoaderGGUF.md @@ -0,0 +1,18 @@ +# Quadruple CLIP Loader (GGUF) + +Loads four text encoder files and returns one combined ComfyUI `CLIP` object. + +## Parameters + +- **clip_name1** +- **clip_name2** +- **clip_name3** +- **clip_name4** + +Each input accepts a GGUF text encoder or a compatible regular checkpoint. +The default model type is `stable_diffusion`. + +## Usage + +Use this node only for workflows whose model family requires four text +encoders. Connect **CLIP** to the workflow's normal text-encoding node. diff --git a/web/docs/TripleCLIPLoaderGGUF.md b/web/docs/TripleCLIPLoaderGGUF.md new file mode 100644 index 0000000..e229f3c --- /dev/null +++ b/web/docs/TripleCLIPLoaderGGUF.md @@ -0,0 +1,17 @@ +# Triple CLIP Loader (GGUF) + +Loads three text encoder files and returns one combined ComfyUI `CLIP` object. + +## Parameters + +- **clip_name1** +- **clip_name2** +- **clip_name3** + +Each input accepts a GGUF text encoder or a compatible regular checkpoint. +This node uses the `sd3` model type by default. + +## Usage + +Use this node where an SD3-style workflow requires three text encoders. Connect +the resulting **CLIP** output to the normal text-encoding node. diff --git a/web/docs/UnetLoaderGGUF.md b/web/docs/UnetLoaderGGUF.md new file mode 100644 index 0000000..41e73f2 --- /dev/null +++ b/web/docs/UnetLoaderGGUF.md @@ -0,0 +1,20 @@ +# Unet Loader (GGUF) + +Loads a diffusion model stored in GGUF format and returns a ComfyUI `MODEL`. + +Place GGUF diffusion models in `ComfyUI/models/unet` or +`ComfyUI/models/diffusion_models`, then select the file with **unet_name**. + +## Q8_CR models + +`Q8_CR` GGUFs use ComfyUI's native INT8 ConvRot path. Eligible Linear weights +remain INT8 during inference; 1-D, small, and designated high-precision tensors +are retained in FP32, while convolution weights remain FP16. + +CUDA is optional. CUDA uses the optimized ComfyUI backend when available; +non-CUDA environments use the eager backend and are slower. + +## Usage + +Connect **MODEL** to a sampler such as `KSampler`. Use the normal model-family +workflow inputs, including the matching text encoder and VAE. \ No newline at end of file diff --git a/web/docs/UnetLoaderGGUFAdvanced.md b/web/docs/UnetLoaderGGUFAdvanced.md new file mode 100644 index 0000000..de2beb1 --- /dev/null +++ b/web/docs/UnetLoaderGGUFAdvanced.md @@ -0,0 +1,26 @@ +# Unet Loader (GGUF/Advanced) + +Loads a GGUF diffusion model with the same behavior as **Unet Loader (GGUF)**, +with additional controls for dequantization and LoRA patch handling. + +## Parameters + +- **unet_name**: GGUF diffusion model in `models/unet` or + `models/diffusion_models`. +- **dequant_dtype**: Dtype used when a conventional GGUF quant needs + dequantization. Use **default** unless troubleshooting a model. +- **patch_dtype**: Dtype used when applying weight patches such as LoRAs. Use + **default** unless a specific workflow requires another dtype. +- **patch_on_device**: Applies patches on the model load device. Leave this off + for the usual lower-VRAM behavior. + +## Q8_CR + +For `Q8_CR` files, the loader selects ComfyUI's native INT8 ConvRot path. +The advanced dtype controls do not convert native INT8 weights to floating +point. + +## Usage + +Connect **MODEL** to the same downstream nodes as the standard GGUF UNet loader. +Start with all advanced options set to **default**. From bcaa7545512a53c2c6f7b0729c1686cd51e06ce5 Mon Sep 17 00:00:00 2001 From: molbal Date: Wed, 29 Jul 2026 23:23:23 +0200 Subject: [PATCH 10/10] Add Dynamic VRAM support for GGUF loaders Add DynamicVRAM-aware GGUF loading and node variants. loader.py: attach mmap-backed file slices, preserve custom GGUF quant configs, and switch to dynamic handling (including BF16/quant paths). nodes.py: add DynamicVRAM loaders and GGUFModelPatcherDynamic, plus helpers to load Unet/CLIP via DynamicVRAM. quant_ops.py: implement GGML quantized tensor layout for Comfy's QuantizedTensor system. tools/convert.py: preserve safetensors metadata, introduce keys_noquant, and avoid quantizing specified small tensors. Add documentation pages for the new Dynamic VRAM loader nodes. --- loader.py | 96 +++++++-- nodes.py | 183 ++++++++++++++++++ quant_ops.py | 70 +++++++ tools/convert.py | 36 +++- web/docs/CLIPLoaderGGUFDynamicVRAM.md | 5 + web/docs/DualCLIPLoaderGGUFDynamicVRAM.md | 5 + .../QuadrupleCLIPLoaderGGUFDynamicVRAM.md | 5 + web/docs/TripleCLIPLoaderGGUFDynamicVRAM.md | 5 + web/docs/UnetLoaderGGUFDynamicVRAM.md | 7 + 9 files changed, 398 insertions(+), 14 deletions(-) create mode 100644 quant_ops.py create mode 100644 web/docs/CLIPLoaderGGUFDynamicVRAM.md create mode 100644 web/docs/DualCLIPLoaderGGUFDynamicVRAM.md create mode 100644 web/docs/QuadrupleCLIPLoaderGGUFDynamicVRAM.md create mode 100644 web/docs/TripleCLIPLoaderGGUFDynamicVRAM.md create mode 100644 web/docs/UnetLoaderGGUFDynamicVRAM.md diff --git a/loader.py b/loader.py index e23fef6..5dd8d8f 100644 --- a/loader.py +++ b/loader.py @@ -6,8 +6,11 @@ import json import re import os +import threading +import comfy.memory_management from .ops import GGMLTensor from .dequant import is_quantized, dequantize_tensor +from .quant_ops import make_quantized IMG_ARCH_LIST = {"flux", "sd1", "sdxl", "sd3", "aura", "hidream", "cosmos", "ltxv", "hyvid", "wan", "lumina2", "qwen_image", "ideogram", "krea2"} TXT_ARCH_LIST = {"t5", "t5encoder", "llama", "qwen2vl", "qwen3", "qwen3vl", "gemma3"} @@ -26,6 +29,32 @@ def device_supports_bf16(): # If support can't be determined, keep the previous bf16 behavior. return True + +def dynamic_gguf_file_slice(path): + """ + Create the file handle metadata used by DynamicVRAM to transfer a GGUF + tensor directly from its mmap-backed file to GPU memory. + """ + if not comfy.memory_management.aimdo_enabled: + return None + + import comfy_aimdo.model_mmap + + model_mmap = comfy_aimdo.model_mmap.ModelMMAP(path) + return model_mmap, threading.Lock() + + +def attach_dynamic_file_slice(torch_tensor, model_mmap, file_lock, offset, size): + storage = torch_tensor.untyped_storage() + storage._comfy_tensor_file_slice = comfy.memory_management.TensorFileSlice( + model_mmap.get_file_handle(), + file_lock, + offset, + size, + ) + # Keep the native mmap alive for the storage lifetime. + storage._comfy_tensor_mmap_refs = (model_mmap,) + def get_orig_shape(reader, tensor_name): field_key = f"comfy.gguf.orig_shape.{tensor_name}" field = reader.get_field(field_key) @@ -80,11 +109,12 @@ def get_gguf_metadata(reader): continue return metadata -def gguf_sd_loader(path, handle_prefix="model.diffusion_model.", is_text_model=False): +def gguf_sd_loader(path, handle_prefix="model.diffusion_model.", is_text_model=False, dynamic=False): """ Read state dict as fake tensors """ reader = gguf.GGUFReader(path) + dynamic_file_slice = dynamic_gguf_file_slice(path) if dynamic else None # filter and strip prefix has_prefix = False @@ -125,6 +155,22 @@ def gguf_sd_loader(path, handle_prefix="model.diffusion_model.", is_text_model=F if compat: logging.warning(f"Warning: This gguf model file is loaded in compatibility mode '{compat}' [arch:{arch_str}]") + # Q8_CR weights must use ComfyUI's native TensorWiseINT8Layout rather than + # the generic GGML layout so DynamicVRAM retains native INT8 ConvRot kernels. + custom_quant_configs = {} + for field_name in reader.fields: + if field_name.startswith("comfy.gguf.quant."): + key = field_name[len("comfy.gguf.quant."):] + field = reader.get_field(field_name) + custom_quant_configs[key] = json.loads(str(field.parts[field.data[-1]], "utf-8")) + + custom_quant_tensor_names = { + tensor_name + for key, quant_conf in custom_quant_configs.items() + if quant_conf.get("format") == "int8_tensorwise" + for tensor_name in (key, f"{key}_scale") + } + # main loading loop # Devices without native bf16 fall back to slow fp32 compute, so load the # full-precision BF16 storage tensors as fp16 there instead. @@ -139,6 +185,15 @@ def gguf_sd_loader(path, handle_prefix="model.diffusion_model.", is_text_model=F with warnings.catch_warnings(): warnings.filterwarnings("ignore", message="The given NumPy array is not writable") torch_tensor = torch.from_numpy(tensor.data) # mmap + if dynamic_file_slice is not None: + model_mmap, file_lock = dynamic_file_slice + attach_dynamic_file_slice( + torch_tensor, + model_mmap, + file_lock, + tensor.data_offset, + tensor.n_bytes, + ) shape = get_orig_shape(reader, tensor_name) if shape is None: @@ -150,12 +205,26 @@ def gguf_sd_loader(path, handle_prefix="model.diffusion_model.", is_text_model=F shape = shape[:-1] # add to state dict - if tensor.tensor_type in {gguf.GGMLQuantizationType.F32, gguf.GGMLQuantizationType.F16}: + if dynamic and sd_key not in custom_quant_tensor_names: + if tensor.tensor_type in { + gguf.GGMLQuantizationType.F32, + gguf.GGMLQuantizationType.F16, + }: + state_dict[sd_key] = torch_tensor.view(*shape) + elif tensor.tensor_type == gguf.GGMLQuantizationType.BF16: + state_dict[sd_key] = torch_tensor.view(torch.bfloat16).reshape(shape).to( + dtype=torch.float32 if len(shape) <= 1 else bf16_storage_dtype, + ) + else: + state_dict[sd_key] = make_quantized(torch_tensor, tensor.tensor_type, shape) + elif tensor.tensor_type in {gguf.GGMLQuantizationType.F32, gguf.GGMLQuantizationType.F16}: torch_tensor = torch_tensor.view(*shape) - state_dict[sd_key] = GGMLTensor(torch_tensor, tensor_type=tensor.tensor_type, tensor_shape=shape) + state_dict[sd_key] = GGMLTensor(torch_tensor, tensor_type=tensor.tensor_type, tensor_shape=shape) + else: + state_dict[sd_key] = GGMLTensor(torch_tensor, tensor_type=tensor.tensor_type, tensor_shape=shape) # BF16 GGUF tensors are full-precision storage, not compressed quants. - if tensor.tensor_type == gguf.GGMLQuantizationType.BF16: + if not dynamic and tensor.tensor_type == gguf.GGMLQuantizationType.BF16: dtype = torch.float32 if len(shape) <= 1 else bf16_storage_dtype state_dict[sd_key] = dequantize_tensor(state_dict[sd_key], dtype=dtype) @@ -185,7 +254,7 @@ def gguf_sd_loader(path, handle_prefix="model.diffusion_model.", is_text_model=F continue key = field_name[len("comfy.gguf.quant."):] field = reader.get_field(field_name) - quant_conf = json.loads(str(field.parts[field.data[-1]], "utf-8")) + quant_conf = custom_quant_configs[key] fmt = quant_conf.get("format") if fmt in {"int4_compact_gemm", "int4_pytorch"}: @@ -215,20 +284,25 @@ def gguf_sd_loader(path, handle_prefix="model.diffusion_model.", is_text_model=F quant_conf.pop("convrot_groupsize", None) elif quant_conf.get("convrot"): groupsize = quant_conf.get("convrot_groupsize", 256) - if weight_ggml.tensor_shape[-1] % groupsize != 0: + weight_shape = weight_ggml.shape if dynamic else weight_ggml.tensor_shape + if weight_shape[-1] % groupsize != 0: logging.warning( "Disabling ConvRot for %s because %d input features are not " "divisible by group size %d.", weight_key, - weight_ggml.tensor_shape[-1], + weight_shape[-1], groupsize, ) quant_conf["convrot"] = False quant_conf.pop("convrot_groupsize", None) # Convert to ComfyUI native state-dict layout - weight = weight_ggml.data.view(torch.int8).reshape(weight_ggml.tensor_shape) - scale = scale_ggml.data.view(torch.float32).reshape(scale_ggml.tensor_shape) + if dynamic: + weight = weight_ggml.view(torch.int8).reshape(weight_ggml.shape) + scale = scale_ggml.view(torch.float32).reshape(scale_ggml.shape) + else: + weight = weight_ggml.data.view(torch.int8).reshape(weight_ggml.tensor_shape) + scale = scale_ggml.data.view(torch.float32).reshape(scale_ggml.tensor_shape) state_dict[weight_key] = torch.nn.Parameter(weight, requires_grad=False) state_dict[scale_key] = torch.nn.Parameter(scale, requires_grad=False) @@ -571,8 +645,8 @@ def gguf_gemma3_tokenizer_loader(path): del reader return torch.ByteTensor(list(spm.SerializeToString())) -def gguf_clip_loader(path): - sd, extra = gguf_sd_loader(path, is_text_model=True) +def gguf_clip_loader(path, dynamic=False): + sd, extra = gguf_sd_loader(path, is_text_model=True, dynamic=dynamic) arch = extra.get("arch_str", None) if arch in {"t5", "t5encoder"}: temb_key = "token_embd.weight" diff --git a/nodes.py b/nodes.py index ce30888..0c672d7 100644 --- a/nodes.py +++ b/nodes.py @@ -11,6 +11,7 @@ import comfy.utils import comfy.model_patcher import comfy.model_management +import comfy.memory_management import folder_paths from .ops import GGMLOps, get_gguf_q8_ops, move_patch_to_device @@ -194,6 +195,95 @@ def load_unet(self, unet_name, dequant_dtype=None, patch_dtype=None, patch_on_de model.patch_on_device = patch_on_device return (model,) + +def _require_dynamic_vram(): + if not comfy.memory_management.aimdo_enabled: + raise RuntimeError( + "Dynamic VRAM is not enabled in this ComfyUI installation. " + "Start ComfyUI without --disable-dynamic-vram and use a build that supports DynamicVRAM." + ) + + +def _clone_as_dynamic_gguf_patcher(model_patcher): + source_class = model_patcher.__class__ + model_patcher.__class__ = GGUFModelPatcherDynamic + cloned = model_patcher.clone() + model_patcher.__class__ = source_class + return cloned + + +def _legacy_gguf_ops(extra): + mode = extra.get("gguf_quant_mode") + if mode == "int8_convrot": + return get_gguf_q8_ops(compute_dtype=torch.bfloat16)() + if mode == "int4_pytorch": + raise RuntimeError( + "Q4_PT is retired because PyTorch's Ampere INT4 kernel is not " + "performance-competitive. Reconvert the model as Q8_CR." + ) + return GGMLOps() + + +def _load_dynamic_gguf_unet(unet_path, disable_dynamic=False): + if not disable_dynamic: + _require_dynamic_vram() + sd, extra = gguf_sd_loader(unet_path, dynamic=not disable_dynamic) + + kwargs = {} + valid_params = inspect.signature(comfy.sd.load_diffusion_model_state_dict).parameters + if "metadata" in valid_params: + kwargs["metadata"] = extra.get("metadata", {}) + + model_options = {} if not disable_dynamic else { + "custom_operations": _legacy_gguf_ops(extra), + } + model = comfy.sd.load_diffusion_model_state_dict( + sd, + model_options=model_options, + disable_dynamic=disable_dynamic, + **kwargs, + ) + if model is None: + logging.error("ERROR UNSUPPORTED UNET {}".format(unet_path)) + raise RuntimeError("ERROR: Could not detect model type of: {}".format(unet_path)) + model = GGUFModelPatcher.clone(model) if disable_dynamic else _clone_as_dynamic_gguf_patcher(model) + model.cached_patcher_init = (_load_dynamic_gguf_unet, (unet_path,)) + return model + + +class GGUFModelPatcherDynamic(comfy.model_patcher.ModelPatcherDynamic): + def load(self, *args, **kwargs): + super().load(*args, **kwargs) + # GGML weights cannot be requantized after applying a LoRA patch. + for _, module in self.model.named_modules(): + for param_key in ("weight", "bias"): + attr = f"{param_key}_lowvram_function" + lowvram_function = getattr(module, attr, None) + if lowvram_function is not None: + setattr(module, attr, None) + functions = getattr(module, f"{param_key}_function", []) + functions.append(lowvram_function) + setattr(module, f"{param_key}_function", functions) + + def clone(self, disable_dynamic=False, model_override=None): + if disable_dynamic: + if model_override is None: + fallback = self.cached_patcher_init[0]( + *self.cached_patcher_init[1], + disable_dynamic=True, + ) + model_override = fallback.get_clone_model_override() + return GGUFModelPatcher.clone(self, model_override=model_override) + return super().clone(disable_dynamic=disable_dynamic, model_override=model_override) + + +class UnetLoaderGGUFDynamicVRAM(UnetLoaderGGUF): + TITLE = "Unet Loader (Dynamic VRAM)" + + def load_unet(self, unet_name, **kwargs): + unet_path = folder_paths.get_full_path("unet", unet_name) + return (_load_dynamic_gguf_unet(unet_path),) + class UnetLoaderGGUFAdvanced(UnetLoaderGGUF): @classmethod def INPUT_TYPES(s): @@ -261,6 +351,55 @@ def load_clip(self, clip_name, type="stable_diffusion"): clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) return (self.load_patcher([clip_path], clip_type, self.load_data([clip_path])),) + +def _load_dynamic_gguf_clip(clip_paths, clip_type, disable_dynamic=False): + if not disable_dynamic: + _require_dynamic_vram() + clip_data = [] + for path in clip_paths: + if path.endswith(".gguf"): + clip_data.append(gguf_clip_loader(path, dynamic=not disable_dynamic)) + else: + clip_data.append(comfy.utils.load_torch_file(path, safe_load=True)) + + model_options = { + "initial_device": comfy.model_management.text_encoder_offload_device(), + } + if disable_dynamic: + model_options["custom_operations"] = GGMLOps + + clip = comfy.sd.load_text_encoder_state_dicts( + clip_type=clip_type, + state_dicts=clip_data, + model_options=model_options, + embedding_directory=folder_paths.get_folder_paths("embeddings"), + disable_dynamic=disable_dynamic, + ) + clip.patcher = ( + GGUFModelPatcher.clone(clip.patcher) + if disable_dynamic + else _clone_as_dynamic_gguf_patcher(clip.patcher) + ) + clip.patcher.cached_patcher_init = (_load_dynamic_gguf_clip_patcher, (clip_paths, clip_type)) + return clip + + +def _load_dynamic_gguf_clip_patcher(clip_paths, clip_type, disable_dynamic=False): + return _load_dynamic_gguf_clip( + clip_paths, + clip_type, + disable_dynamic=disable_dynamic, + ).patcher + + +class CLIPLoaderGGUFDynamicVRAM(CLIPLoaderGGUF): + TITLE = "CLIPLoader (Dynamic VRAM)" + + def load_clip(self, clip_name, type="stable_diffusion"): + clip_path = folder_paths.get_full_path("clip", clip_name) + clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) + return (_load_dynamic_gguf_clip([clip_path], clip_type),) + class DualCLIPLoaderGGUF(CLIPLoaderGGUF): @classmethod def INPUT_TYPES(s): @@ -283,6 +422,18 @@ def load_clip(self, clip_name1, clip_name2, type): clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) return (self.load_patcher(clip_paths, clip_type, self.load_data(clip_paths)),) + +class DualCLIPLoaderGGUFDynamicVRAM(DualCLIPLoaderGGUF): + TITLE = "DualCLIPLoader (Dynamic VRAM)" + + def load_clip(self, clip_name1, clip_name2, type): + clip_paths = ( + folder_paths.get_full_path("clip", clip_name1), + folder_paths.get_full_path("clip", clip_name2), + ) + clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) + return (_load_dynamic_gguf_clip(clip_paths, clip_type),) + class TripleCLIPLoaderGGUF(CLIPLoaderGGUF): @classmethod def INPUT_TYPES(s): @@ -305,6 +456,19 @@ def load_clip(self, clip_name1, clip_name2, clip_name3, type="sd3"): clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) return (self.load_patcher(clip_paths, clip_type, self.load_data(clip_paths)),) + +class TripleCLIPLoaderGGUFDynamicVRAM(TripleCLIPLoaderGGUF): + TITLE = "TripleCLIPLoader (Dynamic VRAM)" + + def load_clip(self, clip_name1, clip_name2, clip_name3, type="sd3"): + clip_paths = ( + folder_paths.get_full_path("clip", clip_name1), + folder_paths.get_full_path("clip", clip_name2), + folder_paths.get_full_path("clip", clip_name3), + ) + clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) + return (_load_dynamic_gguf_clip(clip_paths, clip_type),) + class QuadrupleCLIPLoaderGGUF(CLIPLoaderGGUF): @classmethod def INPUT_TYPES(s): @@ -329,6 +493,20 @@ def load_clip(self, clip_name1, clip_name2, clip_name3, clip_name4, type="stable clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) return (self.load_patcher(clip_paths, clip_type, self.load_data(clip_paths)),) + +class QuadrupleCLIPLoaderGGUFDynamicVRAM(QuadrupleCLIPLoaderGGUF): + TITLE = "QuadrupleCLIPLoader (Dynamic VRAM)" + + def load_clip(self, clip_name1, clip_name2, clip_name3, clip_name4, type="stable_diffusion"): + clip_paths = ( + folder_paths.get_full_path("clip", clip_name1), + folder_paths.get_full_path("clip", clip_name2), + folder_paths.get_full_path("clip", clip_name3), + folder_paths.get_full_path("clip", clip_name4), + ) + clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) + return (_load_dynamic_gguf_clip(clip_paths, clip_type),) + NODE_CLASS_MAPPINGS = { "UnetLoaderGGUF": UnetLoaderGGUF, "CLIPLoaderGGUF": CLIPLoaderGGUF, @@ -336,4 +514,9 @@ def load_clip(self, clip_name1, clip_name2, clip_name3, clip_name4, type="stable "TripleCLIPLoaderGGUF": TripleCLIPLoaderGGUF, "QuadrupleCLIPLoaderGGUF": QuadrupleCLIPLoaderGGUF, "UnetLoaderGGUFAdvanced": UnetLoaderGGUFAdvanced, + "UnetLoaderGGUFDynamicVRAM": UnetLoaderGGUFDynamicVRAM, + "CLIPLoaderGGUFDynamicVRAM": CLIPLoaderGGUFDynamicVRAM, + "DualCLIPLoaderGGUFDynamicVRAM": DualCLIPLoaderGGUFDynamicVRAM, + "TripleCLIPLoaderGGUFDynamicVRAM": TripleCLIPLoaderGGUFDynamicVRAM, + "QuadrupleCLIPLoaderGGUFDynamicVRAM": QuadrupleCLIPLoaderGGUFDynamicVRAM, } diff --git a/quant_ops.py b/quant_ops.py new file mode 100644 index 0000000..80d1adb --- /dev/null +++ b/quant_ops.py @@ -0,0 +1,70 @@ +# GGML QuantizedTensor support for ComfyUI DynamicVRAM loading. +from dataclasses import dataclass + +import gguf +import torch + +from comfy_kitchen.tensor import ( + BaseLayoutParams, + QuantizedLayout, + QuantizedTensor, + register_layout_class, +) + +from .dequant import TORCH_COMPATIBLE_QTYPES, dequantize_functions + + +@dataclass(frozen=True) +class GGMLLayoutParams(BaseLayoutParams): + tensor_type: int + + +class GGMLLayout(QuantizedLayout): + Params = GGMLLayoutParams + + @classmethod + def quantize(cls, tensor, **kwargs): + raise NotImplementedError("Quantization to GGML format is not supported") + + @classmethod + def dequantize(cls, qdata, params): + qtype = gguf.GGMLQuantizationType(params.tensor_type) + orig_shape = params.orig_shape + + if qtype in TORCH_COMPATIBLE_QTYPES: + return qdata.reshape(orig_shape).to(params.orig_dtype) + + if qtype not in dequantize_functions: + dequantized = gguf.quants.dequantize(qdata.cpu().numpy(), qtype) + return torch.from_numpy(dequantized).reshape(orig_shape).to( + device=qdata.device, + dtype=params.orig_dtype, + ) + + _, type_size = gguf.GGML_QUANT_SIZES[qtype] + raw = qdata.reshape(-1).view(torch.uint8) + blocks = raw.reshape((raw.numel() // type_size, type_size)) + return dequantize_functions[qtype](blocks, *gguf.GGML_QUANT_SIZES[qtype], None).reshape( + orig_shape + ).to(params.orig_dtype) + + @classmethod + def get_plain_tensors(cls, qtensor): + return (qtensor._qdata,) + + @classmethod + def state_dict_tensors(cls, qdata, params): + return {"weight": qdata} + + +register_layout_class("GGMLLayout", GGMLLayout) + + +def make_quantized(qdata, tensor_type, tensor_shape, orig_dtype=torch.float16): + params = GGMLLayoutParams( + scale=torch.ones((), dtype=torch.float32), + orig_dtype=orig_dtype, + orig_shape=tuple(tensor_shape), + tensor_type=tensor_type.value if not isinstance(tensor_type, int) else tensor_type, + ) + return QuantizedTensor(qdata, "GGMLLayout", params) diff --git a/tools/convert.py b/tools/convert.py index 2bd2993..96d07ca 100644 --- a/tools/convert.py +++ b/tools/convert.py @@ -6,6 +6,7 @@ import logging import argparse from tqdm import tqdm +from safetensors import safe_open from safetensors.torch import load_file, save_file QUANTIZATION_THRESHOLD = 1024 @@ -19,6 +20,7 @@ class ModelTemplate: keys_detect = [] # list of lists to match in state dict keys_banned = [] # list of keys that should mark model as invalid for conversion keys_hiprec = [] # list of keys that need to be kept in fp32 for some reason + keys_noquant = [] # list of keys that must retain their source precision keys_ignore = [] # list of strings to ignore keys by when found def handle_nd_tensor(self, key, data): @@ -111,10 +113,27 @@ class ModelLTXV(ModelTemplate): "adaln_single.emb.timestep_embedder.linear_2.weight", "transformer_blocks.27.scale_shift_table", "caption_projection.linear_2.weight", - ) + ), + # LTX 2.3 audio-video checkpoints replace the video-only caption + # projection with audio/video connector modules. + ( + "adaln_single.emb.timestep_embedder.linear_2.weight", + "transformer_blocks.27.scale_shift_table", + "audio_adaln_single.linear.weight", + ), ] keys_hiprec = [ - "scale_shift_table" # nn.parameter, can't load from BF16 base quant + "scale_shift_table", # nn.Parameter, can't load from BF16 base quant + "learnable_registers", # Connector nn.Parameter, not a Linear weight + ] + # LTX's native INT8 ConvRot checkpoints intentionally leave these + # projections in BF16. The many 32-output gate logits are particularly + # small, so ConvRot dispatch overhead outweighs their INT8 benefit. + keys_noquant = [ + "adaln_single", + "patchify_proj", + "proj_out", + "to_gate_logits", ] class ModelSDXL(ModelTemplate): @@ -373,6 +392,12 @@ def load_state_dict(path): return strip_prefix(state_dict) +def load_safetensors_metadata(path): + if not path.endswith(".safetensors"): + return {} + with safe_open(path, framework="pt", device="cpu") as checkpoint: + return checkpoint.metadata() or {} + def handle_tensors(writer, state_dict, model_arch, quant_type=None, quant_type_name=None): # Pre-collect per-tensor FP8 scales (0-dim float32 tensors named "{key}_scale"). # These must be applied to their FP8 weight tensors before GGUF quantization. @@ -452,7 +477,9 @@ def handle_tensors(writer, state_dict, model_arch, quant_type=None, quant_type_n or old_dtype in _FP8_DTYPES ) if apply_quantization_rules: - if n_dims == 1: + if any(x in key for x in model_arch.keys_noquant): + pass + elif n_dims == 1: # One-dimensional tensors should be kept in F32. data_qtype = gguf.GGMLQuantizationType.F32 elif n_params <= QUANTIZATION_THRESHOLD: @@ -509,6 +536,7 @@ def handle_tensors(writer, state_dict, model_arch, quant_type=None, quant_type_n def convert_file(path, dst_path=None, interact=True, overwrite=False, quant_type_name=None): # load & run model detection logic state_dict = load_state_dict(path) + source_metadata = load_safetensors_metadata(path) model_arch = detect_arch(state_dict) logging.info(f"* Architecture detected from input: {model_arch.arch}") @@ -549,6 +577,8 @@ def convert_file(path, dst_path=None, interact=True, overwrite=False, quant_type writer.add_quantization_version(gguf.GGML_QUANT_VERSION) if ftype_gguf is not None: writer.add_file_type(ftype_gguf) + if "config" in source_metadata: + writer.add_string("config", source_metadata["config"]) handle_tensors(writer, state_dict, model_arch, quant_type=quant_type, quant_type_name=quant_type_name) writer.write_header_to_file(path=dst_path) diff --git a/web/docs/CLIPLoaderGGUFDynamicVRAM.md b/web/docs/CLIPLoaderGGUFDynamicVRAM.md new file mode 100644 index 0000000..6bf6bbe --- /dev/null +++ b/web/docs/CLIPLoaderGGUFDynamicVRAM.md @@ -0,0 +1,5 @@ +# CLIPLoader (Dynamic VRAM) + +Loads one text encoder through ComfyUI's DynamicVRAM path. + +Use this node when DynamicVRAM is enabled and the text encoder benefits from demand loading. It accepts GGUF and regular text-encoder files. diff --git a/web/docs/DualCLIPLoaderGGUFDynamicVRAM.md b/web/docs/DualCLIPLoaderGGUFDynamicVRAM.md new file mode 100644 index 0000000..db6baaa --- /dev/null +++ b/web/docs/DualCLIPLoaderGGUFDynamicVRAM.md @@ -0,0 +1,5 @@ +# DualCLIPLoader (Dynamic VRAM) + +Loads two text encoders through ComfyUI's DynamicVRAM path. + +Use this node when DynamicVRAM is enabled and your workflow requires two CLIP/text-encoder inputs. diff --git a/web/docs/QuadrupleCLIPLoaderGGUFDynamicVRAM.md b/web/docs/QuadrupleCLIPLoaderGGUFDynamicVRAM.md new file mode 100644 index 0000000..1fd2a8c --- /dev/null +++ b/web/docs/QuadrupleCLIPLoaderGGUFDynamicVRAM.md @@ -0,0 +1,5 @@ +# QuadrupleCLIPLoader (Dynamic VRAM) + +Loads four text encoders through ComfyUI's DynamicVRAM path. + +Use this node when DynamicVRAM is enabled and your workflow requires four CLIP/text-encoder inputs. diff --git a/web/docs/TripleCLIPLoaderGGUFDynamicVRAM.md b/web/docs/TripleCLIPLoaderGGUFDynamicVRAM.md new file mode 100644 index 0000000..c946c26 --- /dev/null +++ b/web/docs/TripleCLIPLoaderGGUFDynamicVRAM.md @@ -0,0 +1,5 @@ +# TripleCLIPLoader (Dynamic VRAM) + +Loads three text encoders through ComfyUI's DynamicVRAM path. + +Use this node when DynamicVRAM is enabled and your workflow requires three CLIP/text-encoder inputs. diff --git a/web/docs/UnetLoaderGGUFDynamicVRAM.md b/web/docs/UnetLoaderGGUFDynamicVRAM.md new file mode 100644 index 0000000..042b97b --- /dev/null +++ b/web/docs/UnetLoaderGGUFDynamicVRAM.md @@ -0,0 +1,7 @@ +# Unet Loader (Dynamic VRAM) + +Loads a GGUF diffusion model through ComfyUI's DynamicVRAM path. + +DynamicVRAM keeps quantized GGUF weights in host memory and loads them on demand. It requires a ComfyUI build with DynamicVRAM enabled; use the regular GGUF loader when DynamicVRAM is unavailable. + +For Q8_CR models, supported linear layers retain ComfyUI's native INT8 ConvRot layout.