Add .safetensors checkpoint; README: Cube3D-style header, define HQQ
#3
by Ramkasuru - opened
- README.md +46 -10
- multi_part_dit_int4_hqq_g128.safetensors +3 -0
- quant_config.json +14 -2
README.md
CHANGED
|
@@ -15,16 +15,28 @@ tags:
|
|
| 15 |
- diffusion
|
| 16 |
---
|
| 17 |
|
| 18 |
-
#
|
| 19 |
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
-
|
| 25 |
-
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
## Efficiency
|
| 30 |
|
|
@@ -120,6 +132,29 @@ sd = torch.load("multi_part_dit_int4_hqq_g128.pt", map_location="cuda", weights_
|
|
| 120 |
pipe.system.diffusion_model.load_state_dict(sd)
|
| 121 |
```
|
| 122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
Generating parts from a mesh (this is the call used for every number above):
|
| 124 |
|
| 125 |
```python
|
|
@@ -144,15 +179,16 @@ part_meshes = pipe.input_to_part_shape(
|
|
| 144 |
|
| 145 |
Environment notes: set `TORCHDYNAMO_DISABLE=1` (CubePart's compiled path raises a Dynamo error in `hijack_qwenimage_multi.py`). On our setup `PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True` also avoided a Warp marching cubes out of memory error.
|
| 146 |
|
| 147 |
-
The checkpoint is a pickle and needs `weights_only=False` because torchao's INT4 tensors are tensor subclasses. Only load it from a source you trust.
|
| 148 |
|
| 149 |
-
Reload check: a fresh process that quantizes and then loads
|
| 150 |
|
| 151 |
## Files
|
| 152 |
|
| 153 |
| File | Size | Description |
|
| 154 |
|---|---|---|
|
| 155 |
-
| `multi_part_dit_int4_hqq_g128.pt` | 1.479 GB | INT4 `diffusion_model` state dict, saved after in place quantization |
|
|
|
|
| 156 |
| `quant_config.json` | small | Quantization settings and load recipe |
|
| 157 |
|
| 158 |
The shape VAE and the text encoder are unchanged, so they are not re-hosted. Use the base repository for them.
|
|
|
|
| 15 |
- diffusion
|
| 16 |
---
|
| 17 |
|
| 18 |
+
# ๐ First INT4 Quantized Efficient-CubePart3D - 83% Smaller Part Decomposition Model
|
| 19 |
|
| 20 |
+
-blueviolet)
|
| 21 |
+
-brightgreen)
|
| 22 |
+
-blue)
|
| 23 |
+
-orange)
|
| 24 |
|
| 25 |
+
Presenting the **first INT4 quantized efficient version** of [CubePart](https://huggingface.co/Roblox/cubepart) ([arXiv 2605.28763](https://arxiv.org/abs/2605.28763), SIGGRAPH 2026), an open-vocabulary, part-controllable 3D mesh generative model.
|
| 26 |
+
Quantized via **HQQ (Half-Quadratic Quantization)**, a calibration-free weight-only method, at **W4A16** (4-bit weights, 16-bit activations, group_size=128) using [torchao](https://github.com/pytorch/ao),
|
| 27 |
+
it cuts the diffusion transformer from **8.58 GB โ 1.48 GB (82.8%โ)** and peak VRAM from **33.71 GiB โ 27.09 GiB (19.6%โ)** while keeping part decomposition near lossless (median chamfer distance **0.0114** per part over 828 parts).
|
| 28 |
+
Companion to [TrNi/efficient-cube3d](https://huggingface.co/TrNi/efficient-cube3d), which does the same for Cube3D.
|
| 29 |
|
| 30 |
+
| | BF16 | **INT4 (HQQ W4A16)** |
|
| 31 |
+
|---|:-:|:-:|
|
| 32 |
+
| ๐พ Model size (diffusion transformer) | 8.58 GB | **1.48 GB (82.8%โ)** |
|
| 33 |
+
| ๐ฎ Peak VRAM | 33.71 GiB | **27.09 GiB (19.6%โ)** โจ |
|
| 34 |
+
| ๐ฆ Setup time | 161.8 s | **166.1 s** (BF16 load + 4.3 s quantize) |
|
| 35 |
+
| โฑ๏ธ Latency | 34.7 s | **66.5 s** (eager mode) |
|
| 36 |
+
| ๐ฏ Part chamfer distance vs BF16, median | n/a | **0.0114** |
|
| 37 |
+
|
| 38 |
+
<mark>๐ก The diffusion transformer is 82.8% smaller on disk and part decomposition stays near lossless on the typical part. Peak VRAM drops less (19.6%) because the Qwen3-VL text encoder and the shape VAE stay in BF16. INT4 is slower here because CubePart needs <code>torch.compile</code> disabled, so dequantization runs in eager mode.
|
| 39 |
+
</mark>
|
| 40 |
|
| 41 |
## Efficiency
|
| 42 |
|
|
|
|
| 132 |
pipe.system.diffusion_model.load_state_dict(sd)
|
| 133 |
```
|
| 134 |
|
| 135 |
+
Or load the `.safetensors` file instead (no pickle). safetensors stores plain tensors only, so each INT4 tensor was saved as its inner tensors under keys like `<param>::tensor_impl::packed_weight`. After the same `quantize_` call above, copy them back in:
|
| 136 |
+
|
| 137 |
+
```python
|
| 138 |
+
from safetensors.torch import load_file
|
| 139 |
+
|
| 140 |
+
def leaves(prefix, t, out):
|
| 141 |
+
# walk torchao tensor subclasses down to their plain inner tensors
|
| 142 |
+
if hasattr(t, "__tensor_flatten__"):
|
| 143 |
+
for name in t.__tensor_flatten__()[0]:
|
| 144 |
+
leaves(f"{prefix}::{name}", getattr(t, name), out)
|
| 145 |
+
else:
|
| 146 |
+
out[prefix] = t
|
| 147 |
+
|
| 148 |
+
saved = load_file("multi_part_dit_int4_hqq_g128.safetensors", device="cuda")
|
| 149 |
+
flat = {}
|
| 150 |
+
for k, v in pipe.system.diffusion_model.state_dict(keep_vars=True).items():
|
| 151 |
+
leaves(k, v, flat)
|
| 152 |
+
assert set(flat) == set(saved), "keys do not match: check the quantize_ settings"
|
| 153 |
+
with torch.no_grad():
|
| 154 |
+
for k, t in flat.items():
|
| 155 |
+
t.copy_(saved[k])
|
| 156 |
+
```
|
| 157 |
+
|
| 158 |
Generating parts from a mesh (this is the call used for every number above):
|
| 159 |
|
| 160 |
```python
|
|
|
|
| 179 |
|
| 180 |
Environment notes: set `TORCHDYNAMO_DISABLE=1` (CubePart's compiled path raises a Dynamo error in `hijack_qwenimage_multi.py`). On our setup `PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True` also avoided a Warp marching cubes out of memory error.
|
| 181 |
|
| 182 |
+
The `.pt` checkpoint is a pickle and needs `weights_only=False` because torchao's INT4 tensors are tensor subclasses. Only load it from a source you trust, or use the `.safetensors` file, which contains no code.
|
| 183 |
|
| 184 |
+
Reload check: for each file, a fresh process that quantizes and then loads it reproduces a live quantized model's output exactly (vertices and faces bit for bit on all 3 parts of a bar stool prompt). Both files hold the same weights.
|
| 185 |
|
| 186 |
## Files
|
| 187 |
|
| 188 |
| File | Size | Description |
|
| 189 |
|---|---|---|
|
| 190 |
+
| `multi_part_dit_int4_hqq_g128.pt` | 1.479 GB | INT4 `diffusion_model` state dict, saved after in place quantization (pickle) |
|
| 191 |
+
| `multi_part_dit_int4_hqq_g128.safetensors` | 1.478 GB | Same weights as plain tensors, 1163 entries (no pickle) |
|
| 192 |
| `quant_config.json` | small | Quantization settings and load recipe |
|
| 193 |
|
| 194 |
The shape VAE and the text encoder are unchanged, so they are not re-hosted. Use the base repository for them.
|
multi_part_dit_int4_hqq_g128.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c7e33a2ae0ae2ac708c4a9396c5762df3b4b5b0fbac01a93cd9772a543a68783
|
| 3 |
+
size 1478462752
|
quant_config.json
CHANGED
|
@@ -8,11 +8,23 @@
|
|
| 8 |
"config": "int4_weight_only(group_size=128, use_hqq=True)",
|
| 9 |
"group_size": 128,
|
| 10 |
"quantized_scope": "nn.Linear layers in transformer_blocks and multi_transformer_blocks of diffusion_model",
|
| 11 |
-
"skipped_name_substrings": [
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
"checkpoint_file": "multi_part_dit_int4_hqq_g128.pt",
|
|
|
|
| 14 |
"checkpoint_contents": "diffusion_model.state_dict() after in place quantization",
|
| 15 |
"load_recipe": "instantiate the pipeline, quantize_ diffusion_model with the same config and filter, then load_state_dict with defaults (no assign=True), weights_only=False",
|
|
|
|
| 16 |
"required_env": {
|
| 17 |
"TORCHDYNAMO_DISABLE": "1"
|
| 18 |
}
|
|
|
|
| 8 |
"config": "int4_weight_only(group_size=128, use_hqq=True)",
|
| 9 |
"group_size": 128,
|
| 10 |
"quantized_scope": "nn.Linear layers in transformer_blocks and multi_transformer_blocks of diffusion_model",
|
| 11 |
+
"skipped_name_substrings": [
|
| 12 |
+
"img_in",
|
| 13 |
+
"txt_in",
|
| 14 |
+
"proj_out",
|
| 15 |
+
"pos_embed",
|
| 16 |
+
"time_text_embed",
|
| 17 |
+
"norm"
|
| 18 |
+
],
|
| 19 |
+
"untouched_modules": [
|
| 20 |
+
"shape_model",
|
| 21 |
+
"base_model.text_encoder"
|
| 22 |
+
],
|
| 23 |
"checkpoint_file": "multi_part_dit_int4_hqq_g128.pt",
|
| 24 |
+
"checkpoint_file_safetensors": "multi_part_dit_int4_hqq_g128.safetensors",
|
| 25 |
"checkpoint_contents": "diffusion_model.state_dict() after in place quantization",
|
| 26 |
"load_recipe": "instantiate the pipeline, quantize_ diffusion_model with the same config and filter, then load_state_dict with defaults (no assign=True), weights_only=False",
|
| 27 |
+
"load_recipe_safetensors": "instantiate the pipeline, quantize_ diffusion_model with the same config and filter, flatten each state_dict tensor via __tensor_flatten__ into '<key>::<inner>' leaves, then copy_ each leaf from the file (see README)",
|
| 28 |
"required_env": {
|
| 29 |
"TORCHDYNAMO_DISABLE": "1"
|
| 30 |
}
|