Text-to-Image
Diffusers
Safetensors
MageFlowPipeline
ajh
mage-flow
mage-flow-nvfp4-ajh
nvfp4
blackwell
qwen3-vl
quantization
Instructions to use ajh-code/Mage-Flow-NVFP4-AJH with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use ajh-code/Mage-Flow-NVFP4-AJH with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("ajh-code/Mage-Flow-NVFP4-AJH", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Draw Things
- DiffusionBee
Add files using upload-large-folder tool
Browse files- .gitattributes +1 -34
- LICENSE +21 -0
- MANIFEST.json +418 -0
- README.md +208 -0
- THIRD_PARTY_NOTICES.md +34 -0
- build_manifest.py +202 -0
- build_native.sh +61 -0
- generate.py +478 -0
- licenses/MAGE-MIT.txt +21 -0
- licenses/QWEN-APACHE-2.0.txt +202 -0
- model_index.json +30 -0
- requirements.txt +20 -0
- run_validation.py +72 -0
- runtime/nvfp4_linear.cu +895 -0
- runtime/nvfp4_linear.h +66 -0
- runtime/packed_artifact.py +1068 -0
- runtime/packed_nvfp4_linear.py +485 -0
- runtime/quant_text_encoder.py +376 -0
- runtime/sm120_linear_op.cpp +358 -0
- runtime/standard_transformer.py +214 -0
- runtime/torch_ops_native.py +233 -0
- scheduler/scheduler_config.json +7 -0
- text_encoder/chat_template.json +4 -0
- text_encoder/config.json +71 -0
- text_encoder/generation_config.json +14 -0
- text_encoder/merges.txt +0 -0
- text_encoder/preprocessor_config.json +21 -0
- text_encoder/tokenizer.json +0 -0
- text_encoder/tokenizer_config.json +239 -0
- text_encoder/video_preprocessor_config.json +21 -0
- text_encoder/vocab.json +0 -0
- transformer/config.json +92 -0
- transformer/diffusion_pytorch_model.safetensors.index.json +501 -0
- transformer/nvfp4_metadata.json +1648 -0
- vae/config.json +6 -0
- validate_release.py +89 -0
- vendor/mage_flow/__init__.py +18 -0
- vendor/mage_flow/app.py +199 -0
- vendor/mage_flow/inference.py +161 -0
- vendor/mage_flow/models/__init__.py +3 -0
- vendor/mage_flow/models/mage_flow.py +364 -0
- vendor/mage_flow/models/modules/__init__.py +0 -0
- vendor/mage_flow/models/modules/_attn_backend.py +229 -0
- vendor/mage_flow/models/modules/mage_latent.py +119 -0
- vendor/mage_flow/models/modules/mage_layers.py +725 -0
- vendor/mage_flow/models/modules/mage_text.py +259 -0
- vendor/mage_flow/models/modules/mage_vae.py +651 -0
- vendor/mage_flow/models/modules/text_encoder.py +707 -0
- vendor/mage_flow/models/utils.py +175 -0
- vendor/mage_flow/pipeline.py +762 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,2 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.so filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Microsoft
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
MANIFEST.json
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"base_models": {
|
| 3 |
+
"mage_flow": "microsoft/Mage-Flow",
|
| 4 |
+
"mage_source_commit": "df7f84d9f8fc991d189d929f03cff623b430a4a2",
|
| 5 |
+
"mage_transformer_sha256": "04431abbc3acd1a5b86a7f77269f8e77e05a6cac3b21f35e1aef7493f6ab4934",
|
| 6 |
+
"qwen": "Qwen/Qwen3-VL-4B-Instruct",
|
| 7 |
+
"qwen_quantized": "InsecureErasure/Qwen3-VL-4B-Instruct-NVFP4",
|
| 8 |
+
"qwen_quantized_revision": "cf080d6af667f5f4949295bd09a1f72a218fe0ee"
|
| 9 |
+
},
|
| 10 |
+
"evidence": {
|
| 11 |
+
"combined_frozen_val07": {
|
| 12 |
+
"all_seven_gates_passed": true,
|
| 13 |
+
"image_sha256": "857c1663636cd775ea05ffe37a98ac47ab7cd4e5362dd82c21ae1b53f7d71c3a",
|
| 14 |
+
"result_sha256": "67afd8ca0c00fff949bfc4c8b660ca864617839cbc01231a7157769a964287c7"
|
| 15 |
+
},
|
| 16 |
+
"legacy_portable_package_generation": {
|
| 17 |
+
"image_sha256": "2c1f2853e37a2ede976f24bc9b7eef172aed7665266c615401ea189fcbcee19f",
|
| 18 |
+
"report_sha256": "2877697cdf8d469f689aa68751d24c8f25288388f682e61355234e82fae35eda",
|
| 19 |
+
"status": "success"
|
| 20 |
+
},
|
| 21 |
+
"native_text_quality_gate_result_sha256": "4a31a2c0eb4cac7401224eaeab23577bd31cc5ef69f6edec5289757f113373e6",
|
| 22 |
+
"standard_huggingface_layout_generation": {
|
| 23 |
+
"bf16_target_weight_reads": 0,
|
| 24 |
+
"image_sha256": "2c1f2853e37a2ede976f24bc9b7eef172aed7665266c615401ea189fcbcee19f",
|
| 25 |
+
"layout": "complete_huggingface_sharded_repository",
|
| 26 |
+
"loaded_non_target_tensor_count": 301,
|
| 27 |
+
"loaded_nvfp4_projection_count": 48,
|
| 28 |
+
"report_sha256": "61c8b651a5a9814c20fa4a99719e0b0a6caa827f61c0865993d916de25e6507d",
|
| 29 |
+
"status": "success",
|
| 30 |
+
"transformer_checkpoint_tensor_count": 493,
|
| 31 |
+
"transformer_shard_count": 4
|
| 32 |
+
},
|
| 33 |
+
"transformer_quality_results": {
|
| 34 |
+
"VAL-01": "82fadbc86ffb980a503c61ba50bfae25fd302503cf9ebb807029aacdc484f65a",
|
| 35 |
+
"VAL-07": "2476505becf3cec14d174c35b42a92ae180cea1173ebe376d0d40f530a0292ce",
|
| 36 |
+
"VAL-09": "c5d25ab78790295a0945b9783e146ff114991cc72af80a524d083034696110fc",
|
| 37 |
+
"VAL-13": "d2f5b3e7b3be8d430fbd8ca47abdfa405d130f080d45a51fe5ab90fde309a07e"
|
| 38 |
+
}
|
| 39 |
+
},
|
| 40 |
+
"files": [
|
| 41 |
+
{
|
| 42 |
+
"path": ".gitattributes",
|
| 43 |
+
"role": "release support file",
|
| 44 |
+
"sha256": "0b59beee6f0780aa6d1da36ef06df3b64752a757d2c37b1abf1941875eddf10a",
|
| 45 |
+
"size": 91
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"path": "LICENSE",
|
| 49 |
+
"role": "license or third-party attribution",
|
| 50 |
+
"sha256": "275b4dd619de4e16a017b10d0beec72abbbbf14ee8a2fc68f8bdb398e821f623",
|
| 51 |
+
"size": 1066
|
| 52 |
+
},
|
| 53 |
+
{
|
| 54 |
+
"path": "README.md",
|
| 55 |
+
"role": "Hugging Face model card and usage guide",
|
| 56 |
+
"sha256": "3333284289f34219f30a3c142d9b1ca9e2682604b161a92b5bc35d07fbc1a267",
|
| 57 |
+
"size": 6581
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
"path": "THIRD_PARTY_NOTICES.md",
|
| 61 |
+
"role": "license or third-party attribution",
|
| 62 |
+
"sha256": "848e6132befdb77dada57b9d077b19ad57d65d02b3df63161d3c8d6ee9a11e99",
|
| 63 |
+
"size": 1336
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"path": "build_manifest.py",
|
| 67 |
+
"role": "release manifest builder",
|
| 68 |
+
"sha256": "82ea93365ae63418268f28cdf3fc8f2360ab5a3ba19da2a42ea914e832a9c45d",
|
| 69 |
+
"size": 8056
|
| 70 |
+
},
|
| 71 |
+
{
|
| 72 |
+
"path": "build_native.sh",
|
| 73 |
+
"role": "project-local native runtime build helper",
|
| 74 |
+
"sha256": "7e1bccc38c982b05c0ad027c3faace9c0d5783e638e75ff4875430d5ab34445d",
|
| 75 |
+
"size": 1803
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"path": "examples/VAL-07_combined_native.png",
|
| 79 |
+
"role": "validated combined NVFP4 example output",
|
| 80 |
+
"sha256": "857c1663636cd775ea05ffe37a98ac47ab7cd4e5362dd82c21ae1b53f7d71c3a",
|
| 81 |
+
"size": 174472
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
"path": "generate.py",
|
| 85 |
+
"role": "portable single-image inference entry point",
|
| 86 |
+
"sha256": "aaa7aa6a101cbdf5d2bfde1354b162df58c7fa50309d40514d8b137c79485d23",
|
| 87 |
+
"size": 14516
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"path": "licenses/MAGE-MIT.txt",
|
| 91 |
+
"role": "license or third-party attribution",
|
| 92 |
+
"sha256": "275b4dd619de4e16a017b10d0beec72abbbbf14ee8a2fc68f8bdb398e821f623",
|
| 93 |
+
"size": 1066
|
| 94 |
+
},
|
| 95 |
+
{
|
| 96 |
+
"path": "licenses/QWEN-APACHE-2.0.txt",
|
| 97 |
+
"role": "license or third-party attribution",
|
| 98 |
+
"sha256": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
|
| 99 |
+
"size": 11358
|
| 100 |
+
},
|
| 101 |
+
{
|
| 102 |
+
"path": "model_index.json",
|
| 103 |
+
"role": "Hugging Face Diffusers metadata and download-tracking query file",
|
| 104 |
+
"sha256": "6d07c7d0d05992544b66126adff35f21e8c9036f8b0b7ca623c92d7bd97c2213",
|
| 105 |
+
"size": 614
|
| 106 |
+
},
|
| 107 |
+
{
|
| 108 |
+
"path": "requirements.txt",
|
| 109 |
+
"role": "release support file",
|
| 110 |
+
"sha256": "2a16308e6034f1b06d60afd70bcd488f0f79b9499c9ff0acbf1c028652320f42",
|
| 111 |
+
"size": 485
|
| 112 |
+
},
|
| 113 |
+
{
|
| 114 |
+
"path": "run_validation.py",
|
| 115 |
+
"role": "portable frozen-prompt validation entry point",
|
| 116 |
+
"sha256": "47ea74991108f4f9ce519ae00ebbfe36d76ebf775ed598a9b539dc3112f7cd3a",
|
| 117 |
+
"size": 2044
|
| 118 |
+
},
|
| 119 |
+
{
|
| 120 |
+
"path": "runtime/libmage_nvfp4_linear.so",
|
| 121 |
+
"role": "native runtime, loader, source, or Python integration",
|
| 122 |
+
"sha256": "d2f7d62d26b4ae3babb7791aee8e0d95ad4794ac9c1d22146c974784381ada48",
|
| 123 |
+
"size": 1158440
|
| 124 |
+
},
|
| 125 |
+
{
|
| 126 |
+
"path": "runtime/libmage_nvfp4_torch_op.so",
|
| 127 |
+
"role": "native runtime, loader, source, or Python integration",
|
| 128 |
+
"sha256": "d0cd7bbde23fa8187d28cda21ad74b79696bf607d40e47a534922670fe597ad4",
|
| 129 |
+
"size": 167176
|
| 130 |
+
},
|
| 131 |
+
{
|
| 132 |
+
"path": "runtime/nvfp4_linear.cu",
|
| 133 |
+
"role": "native runtime, loader, source, or Python integration",
|
| 134 |
+
"sha256": "060697d4784be3e3461d38ef6ff24128f47a4322a05e5463f8687796b03bda9f",
|
| 135 |
+
"size": 32166
|
| 136 |
+
},
|
| 137 |
+
{
|
| 138 |
+
"path": "runtime/nvfp4_linear.h",
|
| 139 |
+
"role": "native runtime, loader, source, or Python integration",
|
| 140 |
+
"sha256": "7eba9c6b155730fad23b86314903d5cd96ee4430daf2d45706288fc7a9f5ee09",
|
| 141 |
+
"size": 2231
|
| 142 |
+
},
|
| 143 |
+
{
|
| 144 |
+
"path": "runtime/packed_artifact.py",
|
| 145 |
+
"role": "native runtime, loader, source, or Python integration",
|
| 146 |
+
"sha256": "6f3b3220421d42a19573e852641dff00f75a6d7ee87975bd3d82c995ee86ba05",
|
| 147 |
+
"size": 40672
|
| 148 |
+
},
|
| 149 |
+
{
|
| 150 |
+
"path": "runtime/packed_nvfp4_linear.py",
|
| 151 |
+
"role": "native runtime, loader, source, or Python integration",
|
| 152 |
+
"sha256": "65a98172fedc59023c80c55c723956746c13e14f3783bdf687af39cdf0ee1397",
|
| 153 |
+
"size": 17469
|
| 154 |
+
},
|
| 155 |
+
{
|
| 156 |
+
"path": "runtime/quant_text_encoder.py",
|
| 157 |
+
"role": "native runtime, loader, source, or Python integration",
|
| 158 |
+
"sha256": "d5f38363256d67dee3532531b4a4ae080fa736e1bdd3499e615cb6142bcf808a",
|
| 159 |
+
"size": 13452
|
| 160 |
+
},
|
| 161 |
+
{
|
| 162 |
+
"path": "runtime/sm120_linear_op.cpp",
|
| 163 |
+
"role": "native runtime, loader, source, or Python integration",
|
| 164 |
+
"sha256": "a53e1e83df95922eaa384503ce5c5bab31ef65becfcf3e0ac623b1f0868d278a",
|
| 165 |
+
"size": 12179
|
| 166 |
+
},
|
| 167 |
+
{
|
| 168 |
+
"path": "runtime/standard_transformer.py",
|
| 169 |
+
"role": "native runtime, loader, source, or Python integration",
|
| 170 |
+
"sha256": "d1a0b25ddace31569aeff166cb8d1b2a7a016ee50827ab1f9dd0ae476fc96ba1",
|
| 171 |
+
"size": 7891
|
| 172 |
+
},
|
| 173 |
+
{
|
| 174 |
+
"path": "runtime/torch_ops_native.py",
|
| 175 |
+
"role": "native runtime, loader, source, or Python integration",
|
| 176 |
+
"sha256": "842e0f0f14faca41d8801ff52d435e30d180e34907b81cbbddc5aff6dfab1829",
|
| 177 |
+
"size": 7980
|
| 178 |
+
},
|
| 179 |
+
{
|
| 180 |
+
"path": "scheduler/scheduler_config.json",
|
| 181 |
+
"role": "Mage FlowMatch scheduler component",
|
| 182 |
+
"sha256": "438fd8bcf254740e5d3f3e9800bbd9c571e342ab87885388d1505b7531c69c02",
|
| 183 |
+
"size": 169
|
| 184 |
+
},
|
| 185 |
+
{
|
| 186 |
+
"path": "text_encoder/chat_template.json",
|
| 187 |
+
"role": "packaged Qwen3-VL configuration or tokenizer asset",
|
| 188 |
+
"sha256": "6f8a6a55027e3da5160105556cda5dd69f6423f1c32645f6730d32de7773d0c4",
|
| 189 |
+
"size": 5502
|
| 190 |
+
},
|
| 191 |
+
{
|
| 192 |
+
"path": "text_encoder/config.json",
|
| 193 |
+
"role": "packaged Qwen3-VL configuration or tokenizer asset",
|
| 194 |
+
"sha256": "d9e81f6f1d8d9ea613d55599041c6f579fbf4a9e9e0eacc9dbdb5e2300b1c281",
|
| 195 |
+
"size": 1789
|
| 196 |
+
},
|
| 197 |
+
{
|
| 198 |
+
"path": "text_encoder/generation_config.json",
|
| 199 |
+
"role": "packaged Qwen3-VL configuration or tokenizer asset",
|
| 200 |
+
"sha256": "8469742d1fce0de951c8909b26a2c0c0d8490837ce476efb114da9e0cefc4d44",
|
| 201 |
+
"size": 269
|
| 202 |
+
},
|
| 203 |
+
{
|
| 204 |
+
"path": "text_encoder/merges.txt",
|
| 205 |
+
"role": "packaged Qwen3-VL configuration or tokenizer asset",
|
| 206 |
+
"sha256": "599bab54075088774b1733fde865d5bd747cbcc7a547c5bc12610e874e26f5e3",
|
| 207 |
+
"size": 1671839
|
| 208 |
+
},
|
| 209 |
+
{
|
| 210 |
+
"path": "text_encoder/model.safetensors",
|
| 211 |
+
"role": "complete mixed NVFP4/FP8 Qwen3-VL text checkpoint",
|
| 212 |
+
"sha256": "719906b435800757d22013d3d475a4853d59b779b022669fa8b8a193b85d0f41",
|
| 213 |
+
"size": 4031376064
|
| 214 |
+
},
|
| 215 |
+
{
|
| 216 |
+
"path": "text_encoder/preprocessor_config.json",
|
| 217 |
+
"role": "packaged Qwen3-VL configuration or tokenizer asset",
|
| 218 |
+
"sha256": "27225450ac9c6529872ee1924fcb0962ff5634834f817040f444118116f4e516",
|
| 219 |
+
"size": 390
|
| 220 |
+
},
|
| 221 |
+
{
|
| 222 |
+
"path": "text_encoder/tokenizer.json",
|
| 223 |
+
"role": "packaged Qwen3-VL configuration or tokenizer asset",
|
| 224 |
+
"sha256": "a5d85b6dcc535e6b93115a9ef287e6132fdbf30270da6218194ba742261173c7",
|
| 225 |
+
"size": 7032403
|
| 226 |
+
},
|
| 227 |
+
{
|
| 228 |
+
"path": "text_encoder/tokenizer_config.json",
|
| 229 |
+
"role": "packaged Qwen3-VL configuration or tokenizer asset",
|
| 230 |
+
"sha256": "c2da771801886ad9ae98181793ffd3dfb7f1af30f6f7c6a4e15d7dbba52e2399",
|
| 231 |
+
"size": 10868
|
| 232 |
+
},
|
| 233 |
+
{
|
| 234 |
+
"path": "text_encoder/video_preprocessor_config.json",
|
| 235 |
+
"role": "packaged Qwen3-VL configuration or tokenizer asset",
|
| 236 |
+
"sha256": "7768af27c1fafa9cc9011c1dc20067e03f8915e03b63504550e11d5066986d13",
|
| 237 |
+
"size": 385
|
| 238 |
+
},
|
| 239 |
+
{
|
| 240 |
+
"path": "text_encoder/vocab.json",
|
| 241 |
+
"role": "packaged Qwen3-VL configuration or tokenizer asset",
|
| 242 |
+
"sha256": "ca10d7e9fb3ed18575dd1e277a2579c16d108e32f27439684afa0e10b1440910",
|
| 243 |
+
"size": 2776833
|
| 244 |
+
},
|
| 245 |
+
{
|
| 246 |
+
"path": "transformer/config.json",
|
| 247 |
+
"role": "Mage transformer component configuration, index, or quantization metadata",
|
| 248 |
+
"sha256": "7520e71b0a4965f23e54308939853229fe1c32fbd10c69aac5adaead24d21475",
|
| 249 |
+
"size": 3194
|
| 250 |
+
},
|
| 251 |
+
{
|
| 252 |
+
"path": "transformer/diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 253 |
+
"role": "complete sharded Mage transformer checkpoint with native NVFP4 modules",
|
| 254 |
+
"sha256": "040143f2dfc32db4220aabe46d98971e1701c83a566ccdb39e2553218f35f0dd",
|
| 255 |
+
"size": 1797002592
|
| 256 |
+
},
|
| 257 |
+
{
|
| 258 |
+
"path": "transformer/diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 259 |
+
"role": "complete sharded Mage transformer checkpoint with native NVFP4 modules",
|
| 260 |
+
"sha256": "d7116264ef3cf0721c197ff4012de319b844abec1a43df1ade7d8868ce43f4a9",
|
| 261 |
+
"size": 1850448056
|
| 262 |
+
},
|
| 263 |
+
{
|
| 264 |
+
"path": "transformer/diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 265 |
+
"role": "complete sharded Mage transformer checkpoint with native NVFP4 modules",
|
| 266 |
+
"sha256": "7534b2e25cf0e90a2bb0cca9d77f6cb2dc29198b2e387bf36cd59878bf9559b4",
|
| 267 |
+
"size": 1850448056
|
| 268 |
+
},
|
| 269 |
+
{
|
| 270 |
+
"path": "transformer/diffusion_pytorch_model-00004-of-00004.safetensors",
|
| 271 |
+
"role": "complete sharded Mage transformer checkpoint with native NVFP4 modules",
|
| 272 |
+
"sha256": "05a975382c7bada173c01f6005da16d7c78eaba771d554f0551934a25f1836d6",
|
| 273 |
+
"size": 128986568
|
| 274 |
+
},
|
| 275 |
+
{
|
| 276 |
+
"path": "transformer/diffusion_pytorch_model.safetensors.index.json",
|
| 277 |
+
"role": "Mage transformer component configuration, index, or quantization metadata",
|
| 278 |
+
"sha256": "6fc021886f29df126c3ff64cd61287869a2b9b3d142fee6084f81171cbdc90d8",
|
| 279 |
+
"size": 51641
|
| 280 |
+
},
|
| 281 |
+
{
|
| 282 |
+
"path": "transformer/nvfp4_metadata.json",
|
| 283 |
+
"role": "Mage transformer component configuration, index, or quantization metadata",
|
| 284 |
+
"sha256": "8aa6f8494dacd10f23e48e64ca6bdc0cc05754dffadd746bd6f500aa08c745fa",
|
| 285 |
+
"size": 71892
|
| 286 |
+
},
|
| 287 |
+
{
|
| 288 |
+
"path": "vae/config.json",
|
| 289 |
+
"role": "complete Mage VAE component",
|
| 290 |
+
"sha256": "abd124d603d6c6a03e9d0f2aa6d113b8c4afda0738400bdf2f99240aeaeaff76",
|
| 291 |
+
"size": 112
|
| 292 |
+
},
|
| 293 |
+
{
|
| 294 |
+
"path": "vae/diffusion_pytorch_model.safetensors",
|
| 295 |
+
"role": "complete Mage VAE component",
|
| 296 |
+
"sha256": "34e076dc1e8a15321e1e07be5111d59cf16dd10b804b7c7e20b4de29013427e0",
|
| 297 |
+
"size": 345053056
|
| 298 |
+
},
|
| 299 |
+
{
|
| 300 |
+
"path": "validate_release.py",
|
| 301 |
+
"role": "package hash validator",
|
| 302 |
+
"sha256": "9c14818d8939f880be8702222d0345ab0af44fbb8fefa98600d414e2c469eb75",
|
| 303 |
+
"size": 2877
|
| 304 |
+
},
|
| 305 |
+
{
|
| 306 |
+
"path": "vendor/mage_flow/__init__.py",
|
| 307 |
+
"role": "pinned Microsoft Mage inference source",
|
| 308 |
+
"sha256": "0709764f182b55fdec6b8195a4d18640fe0ffef3785d3977d8ebc29905de7489",
|
| 309 |
+
"size": 367
|
| 310 |
+
},
|
| 311 |
+
{
|
| 312 |
+
"path": "vendor/mage_flow/app.py",
|
| 313 |
+
"role": "pinned Microsoft Mage inference source",
|
| 314 |
+
"sha256": "ac0597feddf1b6c5aaa2f18cc3ebb3e690dc42232462ac778af45044df41435e",
|
| 315 |
+
"size": 9004
|
| 316 |
+
},
|
| 317 |
+
{
|
| 318 |
+
"path": "vendor/mage_flow/inference.py",
|
| 319 |
+
"role": "pinned Microsoft Mage inference source",
|
| 320 |
+
"sha256": "0a1e196f784f4daf4b4d1607cade1d066908341e0e444aa29fcea3967a8c1a3f",
|
| 321 |
+
"size": 7181
|
| 322 |
+
},
|
| 323 |
+
{
|
| 324 |
+
"path": "vendor/mage_flow/models/__init__.py",
|
| 325 |
+
"role": "pinned Microsoft Mage inference source",
|
| 326 |
+
"sha256": "7e5ba07fdb01f4a5912eaeb3afbe9d4ccc4e391e7de4cdc11865e5aa911ddfd5",
|
| 327 |
+
"size": 56
|
| 328 |
+
},
|
| 329 |
+
{
|
| 330 |
+
"path": "vendor/mage_flow/models/mage_flow.py",
|
| 331 |
+
"role": "pinned Microsoft Mage inference source",
|
| 332 |
+
"sha256": "59b6e1bee7f95a7fd2fa7bd9e765966832951e2bb184b5ae27283884997b845f",
|
| 333 |
+
"size": 15991
|
| 334 |
+
},
|
| 335 |
+
{
|
| 336 |
+
"path": "vendor/mage_flow/models/modules/__init__.py",
|
| 337 |
+
"role": "pinned Microsoft Mage inference source",
|
| 338 |
+
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
| 339 |
+
"size": 0
|
| 340 |
+
},
|
| 341 |
+
{
|
| 342 |
+
"path": "vendor/mage_flow/models/modules/_attn_backend.py",
|
| 343 |
+
"role": "pinned Microsoft Mage inference source",
|
| 344 |
+
"sha256": "7d652eeb2ab39e37554e3d62859c74edeffc8dce70b2c15687060c259647b559",
|
| 345 |
+
"size": 7946
|
| 346 |
+
},
|
| 347 |
+
{
|
| 348 |
+
"path": "vendor/mage_flow/models/modules/mage_latent.py",
|
| 349 |
+
"role": "pinned Microsoft Mage inference source",
|
| 350 |
+
"sha256": "9ffd7c68b6053ad37f6bf0925d8d94dcd72af751448ee0228ec13018fedf54e5",
|
| 351 |
+
"size": 4118
|
| 352 |
+
},
|
| 353 |
+
{
|
| 354 |
+
"path": "vendor/mage_flow/models/modules/mage_layers.py",
|
| 355 |
+
"role": "pinned Microsoft Mage inference source",
|
| 356 |
+
"sha256": "4b198343b8929f48a0a14d388502c81f54be17b222f6831303d2da6a91f33a62",
|
| 357 |
+
"size": 30418
|
| 358 |
+
},
|
| 359 |
+
{
|
| 360 |
+
"path": "vendor/mage_flow/models/modules/mage_text.py",
|
| 361 |
+
"role": "pinned Microsoft Mage inference source",
|
| 362 |
+
"sha256": "eed7846d02bee28ebf7a1fb45db7d9f621dd66e702e761848d67bed426c1c4a7",
|
| 363 |
+
"size": 21247
|
| 364 |
+
},
|
| 365 |
+
{
|
| 366 |
+
"path": "vendor/mage_flow/models/modules/mage_vae.py",
|
| 367 |
+
"role": "pinned Microsoft Mage inference source",
|
| 368 |
+
"sha256": "64f4d7041003e416bc2f4fac5bbf8aabf2e7c798ad106682c34332ba347b0ef9",
|
| 369 |
+
"size": 25112
|
| 370 |
+
},
|
| 371 |
+
{
|
| 372 |
+
"path": "vendor/mage_flow/models/modules/text_encoder.py",
|
| 373 |
+
"role": "pinned Microsoft Mage inference source",
|
| 374 |
+
"sha256": "65e490ce35fbe4d4057f115be2ab8731e01ea44b618c10aabac80d51fc38ef81",
|
| 375 |
+
"size": 29405
|
| 376 |
+
},
|
| 377 |
+
{
|
| 378 |
+
"path": "vendor/mage_flow/models/utils.py",
|
| 379 |
+
"role": "pinned Microsoft Mage inference source",
|
| 380 |
+
"sha256": "0f242ca7a77e0f85b5985b5299304aa0786737de1ba414c7f8f47b8d664dbca8",
|
| 381 |
+
"size": 6649
|
| 382 |
+
},
|
| 383 |
+
{
|
| 384 |
+
"path": "vendor/mage_flow/pipeline.py",
|
| 385 |
+
"role": "pinned Microsoft Mage inference source",
|
| 386 |
+
"sha256": "b9fc57018570372dd3404a733e19b918a359188dd9f5ef7817c6b30969fc13db",
|
| 387 |
+
"size": 37089
|
| 388 |
+
}
|
| 389 |
+
],
|
| 390 |
+
"measurements": {
|
| 391 |
+
"combined_text_stage_peak_allocated_bytes": 10437891584,
|
| 392 |
+
"combined_val07_latent_cosine": 0.9339298064776214,
|
| 393 |
+
"combined_val07_latent_nrmse": 0.36180871546574084,
|
| 394 |
+
"combined_val07_pixel_cosine": 0.9927827428055002,
|
| 395 |
+
"combined_val07_pixel_nrmse": 0.12000220627369493,
|
| 396 |
+
"full_generation_speed_claim_ready": false,
|
| 397 |
+
"text_encoder_allocated_saving_bytes": 4834599424,
|
| 398 |
+
"text_encoder_allocated_saving_gib": 4.5025715827941895,
|
| 399 |
+
"transformer_allocated_saving_bytes": 2604638208,
|
| 400 |
+
"transformer_allocated_saving_gib": 2.4257583618164062
|
| 401 |
+
},
|
| 402 |
+
"policy": {
|
| 403 |
+
"component_layout": "complete_huggingface_sharded_repository",
|
| 404 |
+
"mage_transformer": {
|
| 405 |
+
"format": "native resident W4A4 NVFP4",
|
| 406 |
+
"target_bf16_source_reads": 0,
|
| 407 |
+
"target_count": 48
|
| 408 |
+
},
|
| 409 |
+
"native_compute_capability": "12.0",
|
| 410 |
+
"qwen_text_encoder": {
|
| 411 |
+
"bf16_policy": "blocks 0/35, embeddings, norms, biases, vision tower",
|
| 412 |
+
"fp8_projection_count": 14,
|
| 413 |
+
"nvfp4_projection_count": 224
|
| 414 |
+
}
|
| 415 |
+
},
|
| 416 |
+
"release_status": "validated-standard-huggingface-candidate",
|
| 417 |
+
"schema_version": "mage-flow-nvfp4-huggingface-release-v1"
|
| 418 |
+
}
|
README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: other
|
| 3 |
+
library_name: diffusers
|
| 4 |
+
pipeline_tag: text-to-image
|
| 5 |
+
base_model: microsoft/Mage-Flow
|
| 6 |
+
base_model_relation: quantized
|
| 7 |
+
tags:
|
| 8 |
+
- ajh
|
| 9 |
+
- mage-flow
|
| 10 |
+
- mage-flow-nvfp4-ajh
|
| 11 |
+
- nvfp4
|
| 12 |
+
- blackwell
|
| 13 |
+
- qwen3-vl
|
| 14 |
+
- text-to-image
|
| 15 |
+
- quantization
|
| 16 |
+
---
|
| 17 |
+
|
| 18 |
+
# Mage-Flow-NVFP4-AJH
|
| 19 |
+
|
| 20 |
+
**Mage-Flow-NVFP4-AJH** is a portable, runnable NVFP4 package for
|
| 21 |
+
[`microsoft/Mage-Flow`](https://huggingface.co/microsoft/Mage-Flow).
|
| 22 |
+
It combines a native NVFP4 Mage transformer with a complete mixed
|
| 23 |
+
NVFP4/FP8 Qwen3-VL text encoder.
|
| 24 |
+
|
| 25 |
+
This is a complete Hugging Face component-layout repository, not an overlay:
|
| 26 |
+
|
| 27 |
+
```text
|
| 28 |
+
model_index.json
|
| 29 |
+
transformer/
|
| 30 |
+
config.json
|
| 31 |
+
diffusion_pytorch_model-00001-of-00004.safetensors
|
| 32 |
+
diffusion_pytorch_model.safetensors.index.json
|
| 33 |
+
text_encoder/
|
| 34 |
+
config.json
|
| 35 |
+
model.safetensors
|
| 36 |
+
vae/
|
| 37 |
+
scheduler/
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
The transformer shards include every retained BF16 tensor alongside the
|
| 41 |
+
NVFP4 module state. The VAE, scheduler, text-encoder configuration, tokenizer,
|
| 42 |
+
and processor are also included. Running the downloaded repository does not
|
| 43 |
+
fetch BF16 weights from the base model.
|
| 44 |
+
|
| 45 |
+
## Quantization policy
|
| 46 |
+
|
| 47 |
+
Mage transformer:
|
| 48 |
+
|
| 49 |
+
- 48 image/text MLP up/down projections use resident W4A4 NVFP4.
|
| 50 |
+
- Attention, modulation, normalization, and all non-target tensors remain
|
| 51 |
+
BF16.
|
| 52 |
+
- The 48 original BF16 target weight matrices are absent. Their biases remain
|
| 53 |
+
BF16 and are stored normally inside the quantized modules.
|
| 54 |
+
- The complete transformer is stored as four indexed standard Safetensors
|
| 55 |
+
shards. Packed NVFP4 buffers use their module state-dict names:
|
| 56 |
+
`packed_weight`, `weight_scales`, `weight_scale`, and `bias`.
|
| 57 |
+
|
| 58 |
+
Qwen3-VL text encoder:
|
| 59 |
+
|
| 60 |
+
- Blocks 2–33: 224 NVFP4 projections.
|
| 61 |
+
- Blocks 1 and 34: 14 FP8 projections.
|
| 62 |
+
- Blocks 0 and 35, embeddings, norms, biases, and the vision tower remain
|
| 63 |
+
BF16.
|
| 64 |
+
- Packaged text checkpoint size: `4,031,376,064` bytes.
|
| 65 |
+
- The packaged loader constructs the model directly from this file: 475
|
| 66 |
+
non-quantized tensors and 238 packed projections, with no unresolved meta
|
| 67 |
+
tensors.
|
| 68 |
+
|
| 69 |
+
## Requirements
|
| 70 |
+
|
| 71 |
+
The prebuilt runtime was tested on:
|
| 72 |
+
|
| 73 |
+
- NVIDIA RTX 50-series / SM120
|
| 74 |
+
- Linux x86-64
|
| 75 |
+
- CUDA 13.1
|
| 76 |
+
- Python 3.11
|
| 77 |
+
- PyTorch `2.13.0+cu130`
|
| 78 |
+
- `comfy-kitchen==0.2.22`
|
| 79 |
+
- `flash-attn==2.8.3`
|
| 80 |
+
|
| 81 |
+
Create a local environment:
|
| 82 |
+
|
| 83 |
+
```bash
|
| 84 |
+
python3.11 -m venv .venv
|
| 85 |
+
source .venv/bin/activate
|
| 86 |
+
python -m pip install --upgrade pip
|
| 87 |
+
python -m pip install -r requirements.txt
|
| 88 |
+
CUDA_HOME=/usr/local/cuda-13.1 \
|
| 89 |
+
python -m pip install --no-build-isolation flash-attn==2.8.3
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
The included binaries are for the exact tested stack. Rebuild them after
|
| 93 |
+
changing PyTorch, CUDA, or the C++ ABI:
|
| 94 |
+
|
| 95 |
+
```bash
|
| 96 |
+
CUDA_HOME=/usr/local/cuda-13.1 \
|
| 97 |
+
PYTHON_BIN="$PWD/.venv/bin/python" \
|
| 98 |
+
./build_native.sh
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
## Generate an image
|
| 102 |
+
|
| 103 |
+
Expose exactly one SM120 GPU:
|
| 104 |
+
|
| 105 |
+
```bash
|
| 106 |
+
CUDA_VISIBLE_DEVICES=0 .venv/bin/python generate.py \
|
| 107 |
+
--prompt 'A detailed watercolor fox reading under an old oak tree' \
|
| 108 |
+
--output fox.png \
|
| 109 |
+
--height 1024 \
|
| 110 |
+
--width 1024 \
|
| 111 |
+
--steps 20 \
|
| 112 |
+
--seed 1
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
When running from another copy of the scripts, `--model` also accepts the Hub
|
| 116 |
+
repository id or a downloaded standard-layout directory:
|
| 117 |
+
|
| 118 |
+
```bash
|
| 119 |
+
CUDA_VISIBLE_DEVICES=0 .venv/bin/python generate.py \
|
| 120 |
+
--model ajh-code/Mage-Flow-NVFP4-AJH \
|
| 121 |
+
--prompt 'A lighthouse poster reading "ARCTIC LOOP"' \
|
| 122 |
+
--output lighthouse.png
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
+
The command refuses to overwrite an existing output and writes a companion
|
| 126 |
+
JSON report containing coverage, memory, timing, and environment information.
|
| 127 |
+
|
| 128 |
+
## Measured results
|
| 129 |
+
|
| 130 |
+
Transformer:
|
| 131 |
+
|
| 132 |
+
- Loaded packed transformer allocation: `5,628,438,016` bytes.
|
| 133 |
+
- Measured transformer allocation saving: `2,604,638,208` bytes
|
| 134 |
+
(`2.4258 GiB`).
|
| 135 |
+
- Target BF16 source reads: `0`.
|
| 136 |
+
- Representative real up-projection: `3.26x` eager and `3.25x` compiled
|
| 137 |
+
speedup over BF16.
|
| 138 |
+
|
| 139 |
+
Text encoder:
|
| 140 |
+
|
| 141 |
+
- BF16 language-stack residency: `7.5453 GiB`.
|
| 142 |
+
- Packed mixed residency: `3.0427 GiB`.
|
| 143 |
+
- Measured saving: `4.5026 GiB`.
|
| 144 |
+
|
| 145 |
+
Combined validation on an RTX 5060 Ti 16 GB:
|
| 146 |
+
|
| 147 |
+
- All seven coverage, source-access, allocation, finite, pixel, latent, and
|
| 148 |
+
text-policy gates passed.
|
| 149 |
+
- Peak text stage with the packed transformer still resident:
|
| 150 |
+
`10,437,891,584` bytes (`9.72 GiB`).
|
| 151 |
+
- VAL-07 pixel cosine/NRMSE versus frozen BF16:
|
| 152 |
+
`0.9927827428` / `0.1200022063`.
|
| 153 |
+
- VAL-07 latent cosine/NRMSE versus frozen BF16:
|
| 154 |
+
`0.9339298065` / `0.3618087155`.
|
| 155 |
+
- The requested strings `ARCTIC LOOP` and `NORTHERN COAST` were both rendered
|
| 156 |
+
correctly.
|
| 157 |
+
|
| 158 |
+

|
| 159 |
+
|
| 160 |
+
The standard-layout packaged loader is validated independently of the
|
| 161 |
+
research-tree loader. It installs 48 native Mage projections and 238 packed
|
| 162 |
+
Qwen projections and rejects any checkpoint containing the replaced BF16
|
| 163 |
+
transformer targets.
|
| 164 |
+
|
| 165 |
+
## Quality and speed caveats
|
| 166 |
+
|
| 167 |
+
The mixed text encoder is functional and produced visually strong downstream
|
| 168 |
+
images, but it does not meet our unusually strict embedding-similarity gate:
|
| 169 |
+
mean token/pooled cosine was `0.9531366898` / `0.9751400001`. All ten content
|
| 170 |
+
screening verdicts and category lists remained unchanged.
|
| 171 |
+
|
| 172 |
+
The ten-case text workload was slower with the packed runtime (`30.36 s`)
|
| 173 |
+
than BF16 (`18.29 s`). This package therefore claims major text-encoder VRAM
|
| 174 |
+
savings, not a text-encoding speedup. Transformer projections are materially
|
| 175 |
+
faster, but repeated matched end-to-end timing has not been completed.
|
| 176 |
+
|
| 177 |
+
Additional limitations:
|
| 178 |
+
|
| 179 |
+
- Native execution is currently SM120-only.
|
| 180 |
+
- The repository follows the Hugging Face component and Safetensors layout,
|
| 181 |
+
but the new `mage_flow_nvfp4` runtime is not yet built into stock Diffusers.
|
| 182 |
+
Use the included loader.
|
| 183 |
+
- CUDA graph compatibility is not claimed.
|
| 184 |
+
- Generation and text-to-image are tested; Base, Turbo, and editing variants
|
| 185 |
+
are not.
|
| 186 |
+
- Four transformer-only held-out cases and one combined held-out case have
|
| 187 |
+
been evaluated. This is not a broad benchmark.
|
| 188 |
+
|
| 189 |
+
## Validate the download
|
| 190 |
+
|
| 191 |
+
`MANIFEST.json` records every distributed file except itself:
|
| 192 |
+
|
| 193 |
+
```bash
|
| 194 |
+
.venv/bin/python validate_release.py
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
Hashing the transformer shards and text checkpoint can take a little while.
|
| 198 |
+
|
| 199 |
+
## License and attribution
|
| 200 |
+
|
| 201 |
+
- Mage-Flow and the vendored Mage inference source are Copyright (c) 2026
|
| 202 |
+
Microsoft and licensed under MIT. See `LICENSE` and
|
| 203 |
+
`licenses/MAGE-MIT.txt`.
|
| 204 |
+
- Qwen3-VL and the mixed NVFP4/FP8 text checkpoint are licensed under
|
| 205 |
+
Apache-2.0. See `licenses/QWEN-APACHE-2.0.txt`.
|
| 206 |
+
- The text checkpoint was produced by
|
| 207 |
+
[`InsecureErasure/Qwen3-VL-4B-Instruct-NVFP4`](https://huggingface.co/InsecureErasure/Qwen3-VL-4B-Instruct-NVFP4)
|
| 208 |
+
using learned rounding and `comfy-kitchen`.
|
THIRD_PARTY_NOTICES.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Third-party notices
|
| 2 |
+
|
| 3 |
+
## Microsoft Mage and Mage-Flow
|
| 4 |
+
|
| 5 |
+
This package targets `microsoft/Mage-Flow` and includes the minimal Python
|
| 6 |
+
inference sources under `vendor/mage_flow/`.
|
| 7 |
+
|
| 8 |
+
- Upstream: <https://github.com/microsoft/Mage>
|
| 9 |
+
- Pinned source commit: `df7f84d9f8fc991d189d929f03cff623b430a4a2`
|
| 10 |
+
- License: MIT
|
| 11 |
+
- License copy: `LICENSE` and `licenses/MAGE-MIT.txt`
|
| 12 |
+
|
| 13 |
+
The Microsoft base checkpoint is not duplicated in this package. The
|
| 14 |
+
portable loader downloads the required transformer, VAE, scheduler, and
|
| 15 |
+
configuration files from `microsoft/Mage-Flow`, or accepts a local copy.
|
| 16 |
+
|
| 17 |
+
## Qwen3-VL and the mixed NVFP4 checkpoint
|
| 18 |
+
|
| 19 |
+
The packaged text encoder derives from `Qwen/Qwen3-VL-4B-Instruct` and is an
|
| 20 |
+
exact copy of the learned-rounded mixed checkpoint published at
|
| 21 |
+
`InsecureErasure/Qwen3-VL-4B-Instruct-NVFP4`.
|
| 22 |
+
|
| 23 |
+
- Quantized upstream:
|
| 24 |
+
<https://huggingface.co/InsecureErasure/Qwen3-VL-4B-Instruct-NVFP4>
|
| 25 |
+
- Pinned quantized revision:
|
| 26 |
+
`cf080d6af667f5f4949295bd09a1f72a218fe0ee`
|
| 27 |
+
- Base model: <https://huggingface.co/Qwen/Qwen3-VL-4B-Instruct>
|
| 28 |
+
- License: Apache-2.0
|
| 29 |
+
- License copy: `licenses/QWEN-APACHE-2.0.txt`
|
| 30 |
+
- Packaged artifact SHA-256:
|
| 31 |
+
`719906b435800757d22013d3d475a4853d59b779b022669fa8b8a193b85d0f41`
|
| 32 |
+
|
| 33 |
+
The quantized checkpoint uses the ComfyUI `comfy_quant` representation and
|
| 34 |
+
executes packed projections through `comfy-kitchen`.
|
build_manifest.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Create the immutable release manifest after all package files are frozen."""
|
| 3 |
+
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
+
import hashlib
|
| 7 |
+
import json
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
RELEASE_ROOT = Path(__file__).resolve().parent
|
| 12 |
+
MANIFEST_PATH = RELEASE_ROOT / "MANIFEST.json"
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def sha256(path: Path) -> str:
|
| 16 |
+
digest = hashlib.sha256()
|
| 17 |
+
with path.open("rb") as handle:
|
| 18 |
+
for chunk in iter(lambda: handle.read(1 << 20), b""):
|
| 19 |
+
digest.update(chunk)
|
| 20 |
+
return digest.hexdigest()
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def role_for(path: Path) -> str:
|
| 24 |
+
value = path.as_posix()
|
| 25 |
+
if value == "README.md":
|
| 26 |
+
return "Hugging Face model card and usage guide"
|
| 27 |
+
if value == "model_index.json":
|
| 28 |
+
return "Hugging Face Diffusers metadata and download-tracking query file"
|
| 29 |
+
if value == "generate.py":
|
| 30 |
+
return "portable single-image inference entry point"
|
| 31 |
+
if value == "run_validation.py":
|
| 32 |
+
return "portable frozen-prompt validation entry point"
|
| 33 |
+
if value == "validate_release.py":
|
| 34 |
+
return "package hash validator"
|
| 35 |
+
if value == "build_native.sh":
|
| 36 |
+
return "project-local native runtime build helper"
|
| 37 |
+
if value == "build_manifest.py":
|
| 38 |
+
return "release manifest builder"
|
| 39 |
+
if value.startswith("transformer/") and value.endswith(".safetensors"):
|
| 40 |
+
return "complete sharded Mage transformer checkpoint with native NVFP4 modules"
|
| 41 |
+
if value.startswith("transformer/"):
|
| 42 |
+
return "Mage transformer component configuration, index, or quantization metadata"
|
| 43 |
+
if value.startswith("text_encoder/") and value.endswith(".safetensors"):
|
| 44 |
+
return "complete mixed NVFP4/FP8 Qwen3-VL text checkpoint"
|
| 45 |
+
if value.startswith("text_encoder/"):
|
| 46 |
+
return "packaged Qwen3-VL configuration or tokenizer asset"
|
| 47 |
+
if value.startswith("vae/"):
|
| 48 |
+
return "complete Mage VAE component"
|
| 49 |
+
if value.startswith("scheduler/"):
|
| 50 |
+
return "Mage FlowMatch scheduler component"
|
| 51 |
+
if value.startswith("runtime/"):
|
| 52 |
+
return "native runtime, loader, source, or Python integration"
|
| 53 |
+
if value.startswith("vendor/"):
|
| 54 |
+
return "pinned Microsoft Mage inference source"
|
| 55 |
+
if value.startswith("licenses/") or value in {
|
| 56 |
+
"LICENSE",
|
| 57 |
+
"THIRD_PARTY_NOTICES.md",
|
| 58 |
+
}:
|
| 59 |
+
return "license or third-party attribution"
|
| 60 |
+
if value.startswith("examples/"):
|
| 61 |
+
return "validated combined NVFP4 example output"
|
| 62 |
+
return "release support file"
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def package_files() -> list[dict[str, object]]:
|
| 66 |
+
records: list[dict[str, object]] = []
|
| 67 |
+
for path in sorted(RELEASE_ROOT.rglob("*")):
|
| 68 |
+
if not path.is_file() or path == MANIFEST_PATH:
|
| 69 |
+
continue
|
| 70 |
+
relative = path.relative_to(RELEASE_ROOT)
|
| 71 |
+
if "__pycache__" in relative.parts or path.suffix == ".pyc":
|
| 72 |
+
raise RuntimeError(
|
| 73 |
+
f"generated Python cache must not be shipped: {relative}"
|
| 74 |
+
)
|
| 75 |
+
if path.is_symlink():
|
| 76 |
+
raise RuntimeError(f"release files must be independent: {relative}")
|
| 77 |
+
records.append(
|
| 78 |
+
{
|
| 79 |
+
"path": relative.as_posix(),
|
| 80 |
+
"size": path.stat().st_size,
|
| 81 |
+
"sha256": sha256(path),
|
| 82 |
+
"role": role_for(relative),
|
| 83 |
+
}
|
| 84 |
+
)
|
| 85 |
+
return records
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def main() -> int:
|
| 89 |
+
if MANIFEST_PATH.exists():
|
| 90 |
+
raise SystemExit(f"refusing to overwrite {MANIFEST_PATH}")
|
| 91 |
+
payload = {
|
| 92 |
+
"schema_version": "mage-flow-nvfp4-huggingface-release-v1",
|
| 93 |
+
"release_status": "validated-standard-huggingface-candidate",
|
| 94 |
+
"base_models": {
|
| 95 |
+
"mage_flow": "microsoft/Mage-Flow",
|
| 96 |
+
"mage_source_commit": (
|
| 97 |
+
"df7f84d9f8fc991d189d929f03cff623b430a4a2"
|
| 98 |
+
),
|
| 99 |
+
"mage_transformer_sha256": (
|
| 100 |
+
"04431abbc3acd1a5b86a7f77269f8e77e05a6cac3b21f35e1aef7493f6ab4934"
|
| 101 |
+
),
|
| 102 |
+
"qwen": "Qwen/Qwen3-VL-4B-Instruct",
|
| 103 |
+
"qwen_quantized": (
|
| 104 |
+
"InsecureErasure/Qwen3-VL-4B-Instruct-NVFP4"
|
| 105 |
+
),
|
| 106 |
+
"qwen_quantized_revision": (
|
| 107 |
+
"cf080d6af667f5f4949295bd09a1f72a218fe0ee"
|
| 108 |
+
),
|
| 109 |
+
},
|
| 110 |
+
"policy": {
|
| 111 |
+
"mage_transformer": {
|
| 112 |
+
"format": "native resident W4A4 NVFP4",
|
| 113 |
+
"target_count": 48,
|
| 114 |
+
"target_bf16_source_reads": 0,
|
| 115 |
+
},
|
| 116 |
+
"qwen_text_encoder": {
|
| 117 |
+
"nvfp4_projection_count": 224,
|
| 118 |
+
"fp8_projection_count": 14,
|
| 119 |
+
"bf16_policy": (
|
| 120 |
+
"blocks 0/35, embeddings, norms, biases, vision tower"
|
| 121 |
+
),
|
| 122 |
+
},
|
| 123 |
+
"native_compute_capability": "12.0",
|
| 124 |
+
"component_layout": "complete_huggingface_sharded_repository",
|
| 125 |
+
},
|
| 126 |
+
"measurements": {
|
| 127 |
+
"transformer_allocated_saving_bytes": 2_604_638_208,
|
| 128 |
+
"transformer_allocated_saving_gib": 2.4257583618164062,
|
| 129 |
+
"text_encoder_allocated_saving_bytes": 4_834_599_424,
|
| 130 |
+
"text_encoder_allocated_saving_gib": 4.5025715827941895,
|
| 131 |
+
"combined_text_stage_peak_allocated_bytes": 10_437_891_584,
|
| 132 |
+
"combined_val07_pixel_cosine": 0.9927827428055002,
|
| 133 |
+
"combined_val07_pixel_nrmse": 0.12000220627369493,
|
| 134 |
+
"combined_val07_latent_cosine": 0.9339298064776214,
|
| 135 |
+
"combined_val07_latent_nrmse": 0.36180871546574084,
|
| 136 |
+
"full_generation_speed_claim_ready": False,
|
| 137 |
+
},
|
| 138 |
+
"evidence": {
|
| 139 |
+
"combined_frozen_val07": {
|
| 140 |
+
"result_sha256": (
|
| 141 |
+
"67afd8ca0c00fff949bfc4c8b660ca864617839cbc01231a7157769a964287c7"
|
| 142 |
+
),
|
| 143 |
+
"image_sha256": (
|
| 144 |
+
"857c1663636cd775ea05ffe37a98ac47ab7cd4e5362dd82c21ae1b53f7d71c3a"
|
| 145 |
+
),
|
| 146 |
+
"all_seven_gates_passed": True,
|
| 147 |
+
},
|
| 148 |
+
"legacy_portable_package_generation": {
|
| 149 |
+
"report_sha256": (
|
| 150 |
+
"2877697cdf8d469f689aa68751d24c8f25288388f682e61355234e82fae35eda"
|
| 151 |
+
),
|
| 152 |
+
"image_sha256": (
|
| 153 |
+
"2c1f2853e37a2ede976f24bc9b7eef172aed7665266c615401ea189fcbcee19f"
|
| 154 |
+
),
|
| 155 |
+
"status": "success",
|
| 156 |
+
},
|
| 157 |
+
"standard_huggingface_layout_generation": {
|
| 158 |
+
"report_sha256": (
|
| 159 |
+
"61c8b651a5a9814c20fa4a99719e0b0a6caa827f61c0865993d916de25e6507d"
|
| 160 |
+
),
|
| 161 |
+
"image_sha256": (
|
| 162 |
+
"2c1f2853e37a2ede976f24bc9b7eef172aed7665266c615401ea189fcbcee19f"
|
| 163 |
+
),
|
| 164 |
+
"layout": "complete_huggingface_sharded_repository",
|
| 165 |
+
"transformer_shard_count": 4,
|
| 166 |
+
"transformer_checkpoint_tensor_count": 493,
|
| 167 |
+
"loaded_non_target_tensor_count": 301,
|
| 168 |
+
"loaded_nvfp4_projection_count": 48,
|
| 169 |
+
"bf16_target_weight_reads": 0,
|
| 170 |
+
"status": "success",
|
| 171 |
+
},
|
| 172 |
+
"native_text_quality_gate_result_sha256": (
|
| 173 |
+
"4a31a2c0eb4cac7401224eaeab23577bd31cc5ef69f6edec5289757f113373e6"
|
| 174 |
+
),
|
| 175 |
+
"transformer_quality_results": {
|
| 176 |
+
"VAL-01": (
|
| 177 |
+
"82fadbc86ffb980a503c61ba50bfae25fd302503cf9ebb807029aacdc484f65a"
|
| 178 |
+
),
|
| 179 |
+
"VAL-07": (
|
| 180 |
+
"2476505becf3cec14d174c35b42a92ae180cea1173ebe376d0d40f530a0292ce"
|
| 181 |
+
),
|
| 182 |
+
"VAL-09": (
|
| 183 |
+
"c5d25ab78790295a0945b9783e146ff114991cc72af80a524d083034696110fc"
|
| 184 |
+
),
|
| 185 |
+
"VAL-13": (
|
| 186 |
+
"d2f5b3e7b3be8d430fbd8ca47abdfa405d130f080d45a51fe5ab90fde309a07e"
|
| 187 |
+
),
|
| 188 |
+
},
|
| 189 |
+
},
|
| 190 |
+
"files": package_files(),
|
| 191 |
+
}
|
| 192 |
+
with MANIFEST_PATH.open("xb") as handle:
|
| 193 |
+
handle.write(
|
| 194 |
+
json.dumps(payload, indent=2, sort_keys=True).encode("utf-8")
|
| 195 |
+
+ b"\n"
|
| 196 |
+
)
|
| 197 |
+
print(f"wrote {MANIFEST_PATH} with {len(payload['files'])} files")
|
| 198 |
+
return 0
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
if __name__ == "__main__":
|
| 202 |
+
raise SystemExit(main())
|
build_native.sh
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
|
| 4 |
+
RELEASE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
| 5 |
+
RUNTIME_ROOT="${RELEASE_ROOT}/runtime"
|
| 6 |
+
PYTHON_BIN="${PYTHON_BIN:-python}"
|
| 7 |
+
CUDA_TOOLKIT_ROOT="${CUDA_HOME:-/usr/local/cuda}"
|
| 8 |
+
NVCC="${CUDA_TOOLKIT_ROOT}/bin/nvcc"
|
| 9 |
+
|
| 10 |
+
if [[ ! -x "${NVCC}" ]]; then
|
| 11 |
+
echo "nvcc was not found at ${NVCC}" >&2
|
| 12 |
+
exit 1
|
| 13 |
+
fi
|
| 14 |
+
|
| 15 |
+
mkdir -p "${RUNTIME_ROOT}"
|
| 16 |
+
|
| 17 |
+
"${NVCC}" \
|
| 18 |
+
-std=c++17 -O3 -arch=sm_120a -shared -Xcompiler=-fPIC \
|
| 19 |
+
"${RUNTIME_ROOT}/nvfp4_linear.cu" \
|
| 20 |
+
-o "${RUNTIME_ROOT}/libmage_nvfp4_linear.so" \
|
| 21 |
+
-L"${CUDA_TOOLKIT_ROOT}/lib64" -lcublasLt -lcublas
|
| 22 |
+
|
| 23 |
+
PYTORCH_INCLUDE_FLAGS="$("${PYTHON_BIN}" - <<'PY'
|
| 24 |
+
from torch.utils.cpp_extension import include_paths
|
| 25 |
+
print(" ".join(f"-I{path}" for path in include_paths()))
|
| 26 |
+
PY
|
| 27 |
+
)"
|
| 28 |
+
PYTORCH_LIBRARY_FLAGS="$("${PYTHON_BIN}" - <<'PY'
|
| 29 |
+
from torch.utils.cpp_extension import library_paths
|
| 30 |
+
print(" ".join(f"-L{path}" for path in library_paths()))
|
| 31 |
+
PY
|
| 32 |
+
)"
|
| 33 |
+
PYTORCH_RPATH_FLAGS="$("${PYTHON_BIN}" - <<'PY'
|
| 34 |
+
from torch.utils.cpp_extension import library_paths
|
| 35 |
+
print(" ".join(f"-Wl,-rpath,{path}" for path in library_paths()))
|
| 36 |
+
PY
|
| 37 |
+
)"
|
| 38 |
+
PYTORCH_ABI_FLAG="$("${PYTHON_BIN}" - <<'PY'
|
| 39 |
+
import torch
|
| 40 |
+
print(f"-D_GLIBCXX_USE_CXX11_ABI={int(torch.compiled_with_cxx11_abi())}")
|
| 41 |
+
PY
|
| 42 |
+
)"
|
| 43 |
+
|
| 44 |
+
g++ \
|
| 45 |
+
-std=c++20 -O3 -shared -fPIC \
|
| 46 |
+
${PYTORCH_ABI_FLAG} \
|
| 47 |
+
${PYTORCH_INCLUDE_FLAGS} \
|
| 48 |
+
${PYTORCH_LIBRARY_FLAGS} \
|
| 49 |
+
${PYTORCH_RPATH_FLAGS} \
|
| 50 |
+
-I"${CUDA_TOOLKIT_ROOT}/include" \
|
| 51 |
+
-I"${RUNTIME_ROOT}" \
|
| 52 |
+
-o "${RUNTIME_ROOT}/libmage_nvfp4_torch_op.so" \
|
| 53 |
+
"${RUNTIME_ROOT}/sm120_linear_op.cpp" \
|
| 54 |
+
-L"${CUDA_TOOLKIT_ROOT}/lib64" \
|
| 55 |
+
-L"${RUNTIME_ROOT}" \
|
| 56 |
+
-Wl,-rpath,"${CUDA_TOOLKIT_ROOT}/lib64" \
|
| 57 |
+
-Wl,-rpath,'$ORIGIN' \
|
| 58 |
+
-ltorch -ltorch_cpu -ltorch_cuda -lc10 -lc10_cuda -lcudart \
|
| 59 |
+
-lmage_nvfp4_linear
|
| 60 |
+
|
| 61 |
+
echo "Built the packaged SM120 runtime in ${RUNTIME_ROOT}"
|
generate.py
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Portable single-image Mage-Flow NVFP4 inference entry point."""
|
| 3 |
+
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
+
import argparse
|
| 7 |
+
from datetime import datetime, timezone
|
| 8 |
+
import gc
|
| 9 |
+
import json
|
| 10 |
+
import os
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
import platform
|
| 13 |
+
import sys
|
| 14 |
+
import time
|
| 15 |
+
from typing import Any, Callable
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
RELEASE_ROOT = Path(__file__).resolve().parent
|
| 19 |
+
RUNTIME_ROOT = RELEASE_ROOT / "runtime"
|
| 20 |
+
VENDOR_ROOT = RELEASE_ROOT / "vendor"
|
| 21 |
+
DEFAULT_MODEL = str(RELEASE_ROOT)
|
| 22 |
+
|
| 23 |
+
for import_root in (RUNTIME_ROOT, VENDOR_ROOT):
|
| 24 |
+
if str(import_root) not in sys.path:
|
| 25 |
+
sys.path.insert(0, str(import_root))
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def utc_now() -> str:
|
| 29 |
+
return (
|
| 30 |
+
datetime.now(timezone.utc)
|
| 31 |
+
.replace(microsecond=0)
|
| 32 |
+
.isoformat()
|
| 33 |
+
.replace("+00:00", "Z")
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def resolve_model(value: str) -> Path:
|
| 38 |
+
candidate = Path(value).expanduser()
|
| 39 |
+
if candidate.is_dir():
|
| 40 |
+
return candidate.resolve()
|
| 41 |
+
from huggingface_hub import snapshot_download
|
| 42 |
+
|
| 43 |
+
return Path(
|
| 44 |
+
snapshot_download(
|
| 45 |
+
repo_id=value,
|
| 46 |
+
allow_patterns=[
|
| 47 |
+
"model_index.json",
|
| 48 |
+
"transformer/config.json",
|
| 49 |
+
"transformer/*.safetensors",
|
| 50 |
+
"transformer/*.json",
|
| 51 |
+
"text_encoder/*",
|
| 52 |
+
"vae/config.json",
|
| 53 |
+
"vae/*.safetensors",
|
| 54 |
+
"scheduler/*",
|
| 55 |
+
],
|
| 56 |
+
)
|
| 57 |
+
).resolve()
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def _repo_subpath(repo_dir: Path, relative: str) -> str:
|
| 61 |
+
path = (repo_dir / relative).resolve()
|
| 62 |
+
if not path.is_relative_to(repo_dir):
|
| 63 |
+
raise ValueError(f"model path escapes its snapshot: {relative}")
|
| 64 |
+
return str(path)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def _structure_from_config(transformer_config: dict[str, Any]) -> dict[str, Any]:
|
| 68 |
+
metadata_keys = {
|
| 69 |
+
"_class_name",
|
| 70 |
+
"txt_max_length",
|
| 71 |
+
"max_sequence_length",
|
| 72 |
+
"param_dtype",
|
| 73 |
+
"packing",
|
| 74 |
+
"schedule_mode",
|
| 75 |
+
"static_shift",
|
| 76 |
+
"use_time_shift",
|
| 77 |
+
"rope_type",
|
| 78 |
+
"apply_text_rotary_emb",
|
| 79 |
+
"mlp_ratio",
|
| 80 |
+
"depth_single_blocks",
|
| 81 |
+
"theta",
|
| 82 |
+
"qkv_bias",
|
| 83 |
+
"guidance_embed",
|
| 84 |
+
"vec_in_dim",
|
| 85 |
+
"vec_type",
|
| 86 |
+
"time_type",
|
| 87 |
+
"double_block_type",
|
| 88 |
+
"quantization_config",
|
| 89 |
+
}
|
| 90 |
+
return {
|
| 91 |
+
key: value
|
| 92 |
+
for key, value in transformer_config.items()
|
| 93 |
+
if key not in metadata_keys
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def load_pipeline(
|
| 98 |
+
*,
|
| 99 |
+
model: str,
|
| 100 |
+
torch: Any,
|
| 101 |
+
) -> tuple[Any, dict[str, Any]]:
|
| 102 |
+
import torch.nn as nn
|
| 103 |
+
from diffusers import FlowMatchEulerDiscreteScheduler
|
| 104 |
+
from mage_flow.models.mage_flow import MageFlowModel, ModelConfig
|
| 105 |
+
from mage_flow.models.modules._attn_backend import set_attn_backend
|
| 106 |
+
from mage_flow.pipeline import MageFlowPipeline
|
| 107 |
+
from quant_text_encoder import load_quantized_text_encoder
|
| 108 |
+
from standard_transformer import load_standard_native_transformer
|
| 109 |
+
|
| 110 |
+
repo_dir = resolve_model(model)
|
| 111 |
+
model_index_path = repo_dir / "model_index.json"
|
| 112 |
+
transformer_config_path = repo_dir / "transformer" / "config.json"
|
| 113 |
+
model_index = json.loads(model_index_path.read_text(encoding="utf-8"))
|
| 114 |
+
transformer_config = json.loads(
|
| 115 |
+
transformer_config_path.read_text(encoding="utf-8")
|
| 116 |
+
)
|
| 117 |
+
structure = _structure_from_config(transformer_config)
|
| 118 |
+
config = ModelConfig(
|
| 119 |
+
vae_path=_repo_subpath(repo_dir, model_index["_vae_source"]),
|
| 120 |
+
txt_enc_path=_repo_subpath(repo_dir, model_index["_text_encoder_path"]),
|
| 121 |
+
model_structure=structure,
|
| 122 |
+
txt_max_length=transformer_config.get("txt_max_length", 2048),
|
| 123 |
+
packing=transformer_config.get("packing", True),
|
| 124 |
+
static_shift=transformer_config.get("static_shift", 6.0),
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
transformer, access_report = load_standard_native_transformer(
|
| 128 |
+
repo_dir,
|
| 129 |
+
torch.device("cuda:0"),
|
| 130 |
+
)
|
| 131 |
+
model = MageFlowModel.__new__(MageFlowModel)
|
| 132 |
+
nn.Module.__init__(model)
|
| 133 |
+
model.config = config
|
| 134 |
+
set_attn_backend(getattr(config, "attn_type", "flash2"))
|
| 135 |
+
model.patch_text_encoder_forward()
|
| 136 |
+
model.vae = model.load_vae()
|
| 137 |
+
model.transformer = transformer
|
| 138 |
+
text_encoder_dir = Path(
|
| 139 |
+
_repo_subpath(repo_dir, model_index["_text_encoder_path"])
|
| 140 |
+
)
|
| 141 |
+
model.txt_enc, text_report = load_quantized_text_encoder(
|
| 142 |
+
text_encoder_dir=text_encoder_dir,
|
| 143 |
+
artifact_path=text_encoder_dir / "model.safetensors",
|
| 144 |
+
tokenizer_max_length=config.txt_max_length,
|
| 145 |
+
dit_structure=structure,
|
| 146 |
+
use_packed_text_infer=config.packing,
|
| 147 |
+
)
|
| 148 |
+
model.vae.requires_grad_(False).to(torch.bfloat16)
|
| 149 |
+
model.txt_enc.requires_grad_(False)
|
| 150 |
+
model.eval()
|
| 151 |
+
model.scheduler = FlowMatchEulerDiscreteScheduler.from_pretrained(
|
| 152 |
+
_repo_subpath(repo_dir, "scheduler")
|
| 153 |
+
)
|
| 154 |
+
return (
|
| 155 |
+
MageFlowPipeline(model, device="cuda:0"),
|
| 156 |
+
{
|
| 157 |
+
"resolved_model": str(repo_dir),
|
| 158 |
+
"transformer_access": access_report,
|
| 159 |
+
"text_encoder": text_report,
|
| 160 |
+
},
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
def stage_to_gpu(
|
| 165 |
+
module: Any,
|
| 166 |
+
operation: Callable[[], Any],
|
| 167 |
+
torch: Any,
|
| 168 |
+
) -> tuple[Any, dict[str, Any]]:
|
| 169 |
+
torch.cuda.synchronize()
|
| 170 |
+
torch.cuda.reset_peak_memory_stats(0)
|
| 171 |
+
started = time.perf_counter()
|
| 172 |
+
try:
|
| 173 |
+
module.to("cuda:0")
|
| 174 |
+
value = operation()
|
| 175 |
+
torch.cuda.synchronize()
|
| 176 |
+
metrics = {
|
| 177 |
+
"peak_allocated_bytes": int(torch.cuda.max_memory_allocated(0)),
|
| 178 |
+
"peak_reserved_bytes": int(torch.cuda.max_memory_reserved(0)),
|
| 179 |
+
}
|
| 180 |
+
finally:
|
| 181 |
+
module.to("cpu")
|
| 182 |
+
torch.cuda.synchronize()
|
| 183 |
+
torch.cuda.empty_cache()
|
| 184 |
+
metrics["seconds"] = time.perf_counter() - started
|
| 185 |
+
return value, metrics
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def generate_staged(
|
| 189 |
+
*,
|
| 190 |
+
pipe: Any,
|
| 191 |
+
prompt: str,
|
| 192 |
+
negative_prompt: str,
|
| 193 |
+
height: int,
|
| 194 |
+
width: int,
|
| 195 |
+
steps: int,
|
| 196 |
+
cfg: float,
|
| 197 |
+
seed: int,
|
| 198 |
+
static_shift: float,
|
| 199 |
+
torch: Any,
|
| 200 |
+
) -> tuple[Any, dict[str, Any], dict[str, Any]]:
|
| 201 |
+
from einops import rearrange
|
| 202 |
+
from mage_flow.models.modules.mage_latent import encode_noise, resolve_gs_key
|
| 203 |
+
from mage_flow.pipeline import (
|
| 204 |
+
_build_pack_ctx,
|
| 205 |
+
_decode_one,
|
| 206 |
+
_encode_texts_packed,
|
| 207 |
+
_get_scheduler,
|
| 208 |
+
_lens_to_cu,
|
| 209 |
+
_make_divisible_by_16,
|
| 210 |
+
_slice_packed,
|
| 211 |
+
_template_info,
|
| 212 |
+
_velocity,
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
model = pipe.model
|
| 216 |
+
device = torch.device("cuda:0")
|
| 217 |
+
template_info = _template_info("mage-flow")
|
| 218 |
+
template = template_info.get("template", "{}")
|
| 219 |
+
drop_index = int(template_info.get("start_idx", 0))
|
| 220 |
+
stage_metrics: dict[str, Any] = {}
|
| 221 |
+
|
| 222 |
+
def encode_text() -> tuple[Any, ...]:
|
| 223 |
+
verdict = model.txt_enc.screen_text(prompt)
|
| 224 |
+
if verdict.violates:
|
| 225 |
+
return (verdict,)
|
| 226 |
+
text_flat, vec_all, text_lens = _encode_texts_packed(
|
| 227 |
+
model,
|
| 228 |
+
[prompt, negative_prompt or " "],
|
| 229 |
+
template,
|
| 230 |
+
drop_index,
|
| 231 |
+
device,
|
| 232 |
+
)
|
| 233 |
+
positive = _slice_packed(
|
| 234 |
+
text_flat, vec_all, text_lens, 0, 1, device
|
| 235 |
+
)
|
| 236 |
+
negative = _slice_packed(
|
| 237 |
+
text_flat, vec_all, text_lens, 1, 1, device
|
| 238 |
+
)
|
| 239 |
+
return (verdict, *positive, *negative)
|
| 240 |
+
|
| 241 |
+
encoded, stage_metrics["text_encode"] = stage_to_gpu(
|
| 242 |
+
model.txt_enc,
|
| 243 |
+
encode_text,
|
| 244 |
+
torch,
|
| 245 |
+
)
|
| 246 |
+
verdict = encoded[0]
|
| 247 |
+
if verdict.violates:
|
| 248 |
+
raise RuntimeError(
|
| 249 |
+
"prompt was refused by Mage content screening: "
|
| 250 |
+
f"{verdict.categories} {verdict.reason}"
|
| 251 |
+
)
|
| 252 |
+
(
|
| 253 |
+
_verdict,
|
| 254 |
+
txt,
|
| 255 |
+
txt_cu,
|
| 256 |
+
txt_mask,
|
| 257 |
+
vec,
|
| 258 |
+
neg_txt,
|
| 259 |
+
neg_cu,
|
| 260 |
+
neg_mask,
|
| 261 |
+
neg_vec,
|
| 262 |
+
) = encoded
|
| 263 |
+
|
| 264 |
+
height = _make_divisible_by_16(height)
|
| 265 |
+
width = _make_divisible_by_16(width)
|
| 266 |
+
noise = encode_noise(
|
| 267 |
+
(
|
| 268 |
+
model.vae.latent_channels,
|
| 269 |
+
(height + 15) // 16,
|
| 270 |
+
(width + 15) // 16,
|
| 271 |
+
),
|
| 272 |
+
key=resolve_gs_key(None),
|
| 273 |
+
seed=seed,
|
| 274 |
+
device=device,
|
| 275 |
+
dtype=torch.bfloat16,
|
| 276 |
+
)
|
| 277 |
+
_, _, grid_h, grid_w = noise.shape
|
| 278 |
+
image_latent = rearrange(noise, "b c h w -> b (h w) c")
|
| 279 |
+
image_ids = torch.zeros(grid_h, grid_w, 3, device=device)
|
| 280 |
+
image_ids[..., 1] += torch.arange(grid_h, device=device)[:, None]
|
| 281 |
+
image_ids[..., 2] += torch.arange(grid_w, device=device)[None, :]
|
| 282 |
+
image_ids = rearrange(image_ids, "h w c -> 1 (h w) c")
|
| 283 |
+
image_lens = [grid_h * grid_w]
|
| 284 |
+
image_cu = _lens_to_cu(image_lens, device)
|
| 285 |
+
context = _build_pack_ctx(
|
| 286 |
+
image_ids,
|
| 287 |
+
image_cu,
|
| 288 |
+
[[(1, grid_h, grid_w)]],
|
| 289 |
+
image_lens,
|
| 290 |
+
txt,
|
| 291 |
+
txt_cu,
|
| 292 |
+
txt_mask,
|
| 293 |
+
vec,
|
| 294 |
+
neg_txt,
|
| 295 |
+
neg_cu,
|
| 296 |
+
neg_mask,
|
| 297 |
+
neg_vec,
|
| 298 |
+
cfg,
|
| 299 |
+
False,
|
| 300 |
+
True,
|
| 301 |
+
device,
|
| 302 |
+
)
|
| 303 |
+
|
| 304 |
+
def denoise() -> Any:
|
| 305 |
+
nonlocal image_latent
|
| 306 |
+
scheduler = _get_scheduler(
|
| 307 |
+
model,
|
| 308 |
+
steps,
|
| 309 |
+
device,
|
| 310 |
+
static_shift,
|
| 311 |
+
)
|
| 312 |
+
for step_index, timestep in enumerate(scheduler.timesteps):
|
| 313 |
+
prediction = _velocity(
|
| 314 |
+
model.transformer,
|
| 315 |
+
image_latent,
|
| 316 |
+
context,
|
| 317 |
+
scheduler.sigmas[step_index].item(),
|
| 318 |
+
)
|
| 319 |
+
image_latent = scheduler.step(
|
| 320 |
+
prediction,
|
| 321 |
+
timestep,
|
| 322 |
+
image_latent,
|
| 323 |
+
return_dict=False,
|
| 324 |
+
)[0]
|
| 325 |
+
if int((~torch.isfinite(image_latent)).sum().item()) != 0:
|
| 326 |
+
raise RuntimeError("denoising produced non-finite values")
|
| 327 |
+
return image_latent.to("cpu")
|
| 328 |
+
|
| 329 |
+
latent_cpu, stage_metrics["denoise"] = stage_to_gpu(
|
| 330 |
+
model.transformer,
|
| 331 |
+
denoise,
|
| 332 |
+
torch,
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
def decode() -> Any:
|
| 336 |
+
return _decode_one(
|
| 337 |
+
model,
|
| 338 |
+
latent_cpu.to(device),
|
| 339 |
+
height,
|
| 340 |
+
width,
|
| 341 |
+
device,
|
| 342 |
+
)
|
| 343 |
+
|
| 344 |
+
image, stage_metrics["decode"] = stage_to_gpu(
|
| 345 |
+
model.vae,
|
| 346 |
+
decode,
|
| 347 |
+
torch,
|
| 348 |
+
)
|
| 349 |
+
screening = {
|
| 350 |
+
"violates": bool(verdict.violates),
|
| 351 |
+
"categories": list(verdict.categories or []),
|
| 352 |
+
"reason": str(verdict.reason),
|
| 353 |
+
}
|
| 354 |
+
return image, stage_metrics, screening
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
def parse_args() -> argparse.Namespace:
|
| 358 |
+
parser = argparse.ArgumentParser(description=__doc__)
|
| 359 |
+
parser.add_argument("--prompt", required=True)
|
| 360 |
+
parser.add_argument("--negative-prompt", default="")
|
| 361 |
+
parser.add_argument("--output", type=Path, default=Path("mage_nvfp4.png"))
|
| 362 |
+
parser.add_argument(
|
| 363 |
+
"--model",
|
| 364 |
+
default=DEFAULT_MODEL,
|
| 365 |
+
help=(
|
| 366 |
+
"local standard-layout model directory or Hugging Face repo id "
|
| 367 |
+
"(default: this downloaded repository)"
|
| 368 |
+
),
|
| 369 |
+
)
|
| 370 |
+
parser.add_argument("--height", type=int, default=1024)
|
| 371 |
+
parser.add_argument("--width", type=int, default=1024)
|
| 372 |
+
parser.add_argument("--steps", type=int, default=20)
|
| 373 |
+
parser.add_argument("--cfg", type=float, default=5.0)
|
| 374 |
+
parser.add_argument("--seed", type=int, default=1)
|
| 375 |
+
parser.add_argument("--static-shift", type=float, default=6.0)
|
| 376 |
+
return parser.parse_args()
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
def main() -> int:
|
| 380 |
+
args = parse_args()
|
| 381 |
+
output_path = args.output.expanduser().resolve()
|
| 382 |
+
report_path = output_path.with_suffix(output_path.suffix + ".json")
|
| 383 |
+
if output_path.exists() or report_path.exists():
|
| 384 |
+
raise SystemExit(
|
| 385 |
+
f"refusing to overwrite existing output/report: {output_path}"
|
| 386 |
+
)
|
| 387 |
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
| 388 |
+
|
| 389 |
+
import torch
|
| 390 |
+
from packed_nvfp4_linear import close_all_contexts
|
| 391 |
+
from torch_ops_native import (
|
| 392 |
+
close_native_contexts,
|
| 393 |
+
initialize_native_sm120_op,
|
| 394 |
+
)
|
| 395 |
+
|
| 396 |
+
if not torch.cuda.is_available() or torch.cuda.device_count() != 1:
|
| 397 |
+
raise SystemExit(
|
| 398 |
+
"exactly one visible CUDA GPU is required; set CUDA_VISIBLE_DEVICES"
|
| 399 |
+
)
|
| 400 |
+
torch.cuda.set_device(0)
|
| 401 |
+
properties = torch.cuda.get_device_properties(0)
|
| 402 |
+
if (properties.major, properties.minor) != (12, 0):
|
| 403 |
+
raise SystemExit(
|
| 404 |
+
f"native NVFP4 requires SM120; found {properties.major}.{properties.minor}"
|
| 405 |
+
)
|
| 406 |
+
if not initialize_native_sm120_op(allow_python_schema_fallback=False):
|
| 407 |
+
raise SystemExit("the packaged native SM120 torch op did not load")
|
| 408 |
+
|
| 409 |
+
os.environ.setdefault("CUBLAS_WORKSPACE_CONFIG", ":4096:8")
|
| 410 |
+
torch.manual_seed(args.seed)
|
| 411 |
+
torch.cuda.manual_seed_all(args.seed)
|
| 412 |
+
torch.backends.cudnn.benchmark = False
|
| 413 |
+
torch.backends.cudnn.deterministic = True
|
| 414 |
+
torch.backends.cuda.matmul.allow_tf32 = False
|
| 415 |
+
torch.use_deterministic_algorithms(True)
|
| 416 |
+
|
| 417 |
+
started = time.perf_counter()
|
| 418 |
+
pipe = None
|
| 419 |
+
try:
|
| 420 |
+
pipe, load_report = load_pipeline(
|
| 421 |
+
model=args.model,
|
| 422 |
+
torch=torch,
|
| 423 |
+
)
|
| 424 |
+
image, stages, screening = generate_staged(
|
| 425 |
+
pipe=pipe,
|
| 426 |
+
prompt=args.prompt,
|
| 427 |
+
negative_prompt=args.negative_prompt,
|
| 428 |
+
height=args.height,
|
| 429 |
+
width=args.width,
|
| 430 |
+
steps=args.steps,
|
| 431 |
+
cfg=args.cfg,
|
| 432 |
+
seed=args.seed,
|
| 433 |
+
static_shift=args.static_shift,
|
| 434 |
+
torch=torch,
|
| 435 |
+
)
|
| 436 |
+
image.save(output_path)
|
| 437 |
+
report = {
|
| 438 |
+
"schema_version": "mage-flow-nvfp4-portable-generation-v1",
|
| 439 |
+
"status": "success",
|
| 440 |
+
"completed_at_utc": utc_now(),
|
| 441 |
+
"output": str(output_path),
|
| 442 |
+
"prompt": args.prompt,
|
| 443 |
+
"negative_prompt": args.negative_prompt,
|
| 444 |
+
"height": args.height,
|
| 445 |
+
"width": args.width,
|
| 446 |
+
"steps": args.steps,
|
| 447 |
+
"cfg": args.cfg,
|
| 448 |
+
"seed": args.seed,
|
| 449 |
+
"elapsed_seconds": time.perf_counter() - started,
|
| 450 |
+
"stages": stages,
|
| 451 |
+
"screening": screening,
|
| 452 |
+
"load": load_report,
|
| 453 |
+
"environment": {
|
| 454 |
+
"python": platform.python_version(),
|
| 455 |
+
"torch": torch.__version__,
|
| 456 |
+
"torch_cuda": torch.version.cuda,
|
| 457 |
+
"gpu": properties.name,
|
| 458 |
+
"compute_capability": (
|
| 459 |
+
f"{properties.major}.{properties.minor}"
|
| 460 |
+
),
|
| 461 |
+
},
|
| 462 |
+
}
|
| 463 |
+
report_path.write_text(
|
| 464 |
+
json.dumps(report, indent=2, sort_keys=True) + "\n",
|
| 465 |
+
encoding="utf-8",
|
| 466 |
+
)
|
| 467 |
+
print(f"saved {output_path}")
|
| 468 |
+
print(f"saved {report_path}")
|
| 469 |
+
return 0
|
| 470 |
+
finally:
|
| 471 |
+
close_native_contexts()
|
| 472 |
+
close_all_contexts()
|
| 473 |
+
pipe = None
|
| 474 |
+
gc.collect()
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
if __name__ == "__main__":
|
| 478 |
+
raise SystemExit(main())
|
licenses/MAGE-MIT.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Microsoft
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
licenses/QWEN-APACHE-2.0.txt
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Apache License
|
| 3 |
+
Version 2.0, January 2004
|
| 4 |
+
http://www.apache.org/licenses/
|
| 5 |
+
|
| 6 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 7 |
+
|
| 8 |
+
1. Definitions.
|
| 9 |
+
|
| 10 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 11 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 12 |
+
|
| 13 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 14 |
+
the copyright owner that is granting the License.
|
| 15 |
+
|
| 16 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 17 |
+
other entities that control, are controlled by, or are under common
|
| 18 |
+
control with that entity. For the purposes of this definition,
|
| 19 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 20 |
+
direction or management of such entity, whether by contract or
|
| 21 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 22 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 23 |
+
|
| 24 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 25 |
+
exercising permissions granted by this License.
|
| 26 |
+
|
| 27 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 28 |
+
including but not limited to software source code, documentation
|
| 29 |
+
source, and configuration files.
|
| 30 |
+
|
| 31 |
+
"Object" form shall mean any form resulting from mechanical
|
| 32 |
+
transformation or translation of a Source form, including but
|
| 33 |
+
not limited to compiled object code, generated documentation,
|
| 34 |
+
and conversions to other media types.
|
| 35 |
+
|
| 36 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 37 |
+
Object form, made available under the License, as indicated by a
|
| 38 |
+
copyright notice that is included in or attached to the work
|
| 39 |
+
(an example is provided in the Appendix below).
|
| 40 |
+
|
| 41 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 42 |
+
form, that is based on (or derived from) the Work and for which the
|
| 43 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 44 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 45 |
+
of this License, Derivative Works shall not include works that remain
|
| 46 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 47 |
+
the Work and Derivative Works thereof.
|
| 48 |
+
|
| 49 |
+
"Contribution" shall mean any work of authorship, including
|
| 50 |
+
the original version of the Work and any modifications or additions
|
| 51 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 52 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 53 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 54 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 55 |
+
means any form of electronic, verbal, or written communication sent
|
| 56 |
+
to the Licensor or its representatives, including but not limited to
|
| 57 |
+
communication on electronic mailing lists, source code control systems,
|
| 58 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 59 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 60 |
+
excluding communication that is conspicuously marked or otherwise
|
| 61 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 62 |
+
|
| 63 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 64 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 65 |
+
subsequently incorporated within the Work.
|
| 66 |
+
|
| 67 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 68 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 69 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 70 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 71 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 72 |
+
Work and such Derivative Works in Source or Object form.
|
| 73 |
+
|
| 74 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 75 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 76 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 77 |
+
(except as stated in this section) patent license to make, have made,
|
| 78 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 79 |
+
where such license applies only to those patent claims licensable
|
| 80 |
+
by such Contributor that are necessarily infringed by their
|
| 81 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 82 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 83 |
+
institute patent litigation against any entity (including a
|
| 84 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 85 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 86 |
+
or contributory patent infringement, then any patent licenses
|
| 87 |
+
granted to You under this License for that Work shall terminate
|
| 88 |
+
as of the date such litigation is filed.
|
| 89 |
+
|
| 90 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 91 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 92 |
+
modifications, and in Source or Object form, provided that You
|
| 93 |
+
meet the following conditions:
|
| 94 |
+
|
| 95 |
+
(a) You must give any other recipients of the Work or
|
| 96 |
+
Derivative Works a copy of this License; and
|
| 97 |
+
|
| 98 |
+
(b) You must cause any modified files to carry prominent notices
|
| 99 |
+
stating that You changed the files; and
|
| 100 |
+
|
| 101 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 102 |
+
that You distribute, all copyright, patent, trademark, and
|
| 103 |
+
attribution notices from the Source form of the Work,
|
| 104 |
+
excluding those notices that do not pertain to any part of
|
| 105 |
+
the Derivative Works; and
|
| 106 |
+
|
| 107 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 108 |
+
distribution, then any Derivative Works that You distribute must
|
| 109 |
+
include a readable copy of the attribution notices contained
|
| 110 |
+
within such NOTICE file, excluding those notices that do not
|
| 111 |
+
pertain to any part of the Derivative Works, in at least one
|
| 112 |
+
of the following places: within a NOTICE text file distributed
|
| 113 |
+
as part of the Derivative Works; within the Source form or
|
| 114 |
+
documentation, if provided along with the Derivative Works; or,
|
| 115 |
+
within a display generated by the Derivative Works, if and
|
| 116 |
+
wherever such third-party notices normally appear. The contents
|
| 117 |
+
of the NOTICE file are for informational purposes only and
|
| 118 |
+
do not modify the License. You may add Your own attribution
|
| 119 |
+
notices within Derivative Works that You distribute, alongside
|
| 120 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 121 |
+
that such additional attribution notices cannot be construed
|
| 122 |
+
as modifying the License.
|
| 123 |
+
|
| 124 |
+
You may add Your own copyright statement to Your modifications and
|
| 125 |
+
may provide additional or different license terms and conditions
|
| 126 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 127 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 128 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 129 |
+
the conditions stated in this License.
|
| 130 |
+
|
| 131 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 132 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 133 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 134 |
+
this License, without any additional terms or conditions.
|
| 135 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 136 |
+
the terms of any separate license agreement you may have executed
|
| 137 |
+
with Licensor regarding such Contributions.
|
| 138 |
+
|
| 139 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 140 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 141 |
+
except as required for reasonable and customary use in describing the
|
| 142 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 143 |
+
|
| 144 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 145 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 146 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 147 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 148 |
+
implied, including, without limitation, any warranties or conditions
|
| 149 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 150 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 151 |
+
appropriateness of using or redistributing the Work and assume any
|
| 152 |
+
risks associated with Your exercise of permissions under this License.
|
| 153 |
+
|
| 154 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 155 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 156 |
+
unless required by applicable law (such as deliberate and grossly
|
| 157 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 158 |
+
liable to You for damages, including any direct, indirect, special,
|
| 159 |
+
incidental, or consequential damages of any character arising as a
|
| 160 |
+
result of this License or out of the use or inability to use the
|
| 161 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 162 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 163 |
+
other commercial damages or losses), even if such Contributor
|
| 164 |
+
has been advised of the possibility of such damages.
|
| 165 |
+
|
| 166 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 167 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 168 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 169 |
+
or other liability obligations and/or rights consistent with this
|
| 170 |
+
License. However, in accepting such obligations, You may act only
|
| 171 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 172 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 173 |
+
defend, and hold each Contributor harmless for any liability
|
| 174 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 175 |
+
of your accepting any such warranty or additional liability.
|
| 176 |
+
|
| 177 |
+
END OF TERMS AND CONDITIONS
|
| 178 |
+
|
| 179 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 180 |
+
|
| 181 |
+
To apply the Apache License to your work, attach the following
|
| 182 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 183 |
+
replaced with your own identifying information. (Don't include
|
| 184 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 185 |
+
comment syntax for the file format. We also recommend that a
|
| 186 |
+
file or class name and description of purpose be included on the
|
| 187 |
+
same "printed page" as the copyright notice for easier
|
| 188 |
+
identification within third-party archives.
|
| 189 |
+
|
| 190 |
+
Copyright [yyyy] [name of copyright owner]
|
| 191 |
+
|
| 192 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 193 |
+
you may not use this file except in compliance with the License.
|
| 194 |
+
You may obtain a copy of the License at
|
| 195 |
+
|
| 196 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 197 |
+
|
| 198 |
+
Unless required by applicable law or agreed to in writing, software
|
| 199 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 200 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 201 |
+
See the License for the specific language governing permissions and
|
| 202 |
+
limitations under the License.
|
model_index.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_class_name": "MageFlowPipeline",
|
| 3 |
+
"_mage_flow_version": "0.1.0",
|
| 4 |
+
"_quantization": {
|
| 5 |
+
"text_encoder": "comfy_kitchen_nvfp4_fp8_mixed",
|
| 6 |
+
"transformer": "mage_flow_nvfp4"
|
| 7 |
+
},
|
| 8 |
+
"_text_encoder_path": "text_encoder",
|
| 9 |
+
"_vae_source": "vae/diffusion_pytorch_model.safetensors",
|
| 10 |
+
"scheduler": [
|
| 11 |
+
"diffusers",
|
| 12 |
+
"FlowMatchEulerDiscreteScheduler"
|
| 13 |
+
],
|
| 14 |
+
"text_encoder": [
|
| 15 |
+
"transformers",
|
| 16 |
+
"Qwen3VLForConditionalGeneration"
|
| 17 |
+
],
|
| 18 |
+
"tokenizer": [
|
| 19 |
+
"transformers",
|
| 20 |
+
"AutoProcessor"
|
| 21 |
+
],
|
| 22 |
+
"transformer": [
|
| 23 |
+
"mage_flow",
|
| 24 |
+
"MageFlow"
|
| 25 |
+
],
|
| 26 |
+
"vae": [
|
| 27 |
+
"mage_flow",
|
| 28 |
+
"MageVAE"
|
| 29 |
+
]
|
| 30 |
+
}
|
requirements.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Exact tested environment. Install only in a project-local virtual environment.
|
| 2 |
+
--extra-index-url https://download.pytorch.org/whl/cu130
|
| 3 |
+
torch==2.13.0+cu130
|
| 4 |
+
torchvision==0.28.0+cu130
|
| 5 |
+
diffusers==0.38.0
|
| 6 |
+
transformers==5.5.0
|
| 7 |
+
safetensors==0.8.0
|
| 8 |
+
accelerate==1.13.0
|
| 9 |
+
comfy-kitchen==0.2.22
|
| 10 |
+
einops==0.8.2
|
| 11 |
+
pydantic==2.12.5
|
| 12 |
+
pillow==12.3.0
|
| 13 |
+
numpy==2.4.3
|
| 14 |
+
loguru==0.7.3
|
| 15 |
+
huggingface-hub==1.24.0
|
| 16 |
+
setuptools
|
| 17 |
+
wheel
|
| 18 |
+
ninja
|
| 19 |
+
|
| 20 |
+
# Install flash-attn==2.8.3 separately with --no-build-isolation after this file.
|
run_validation.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Exercise the portable package with the frozen VAL-07 typography prompt."""
|
| 3 |
+
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
+
import argparse
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import subprocess
|
| 9 |
+
import sys
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
RELEASE_ROOT = Path(__file__).resolve().parent
|
| 13 |
+
RUNNER = RELEASE_ROOT / "generate.py"
|
| 14 |
+
VALIDATOR = RELEASE_ROOT / "validate_release.py"
|
| 15 |
+
PROMPT = (
|
| 16 |
+
'Minimal travel postcard with the exact large text "ARCTIC LOOP" and '
|
| 17 |
+
'the exact small text "NORTHERN COAST"; an ivory lighthouse on a '
|
| 18 |
+
"slate-blue cliff below the lettering, crisp vector print."
|
| 19 |
+
)
|
| 20 |
+
NEGATIVE_PROMPT = (
|
| 21 |
+
"lowres, blurry, jpeg artifacts, watermark, signature, logo, unreadable "
|
| 22 |
+
"text, misspelled text, duplicate subject, deformed anatomy, extra "
|
| 23 |
+
"fingers, missing fingers, cropped hands"
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def parse_args() -> argparse.Namespace:
|
| 28 |
+
parser = argparse.ArgumentParser(description=__doc__)
|
| 29 |
+
parser.add_argument("--cpu-self-check", action="store_true")
|
| 30 |
+
parser.add_argument("--model", default=str(RELEASE_ROOT))
|
| 31 |
+
parser.add_argument("--output-dir", type=Path)
|
| 32 |
+
return parser.parse_args()
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def main() -> int:
|
| 36 |
+
args = parse_args()
|
| 37 |
+
subprocess.run([sys.executable, str(VALIDATOR)], check=True)
|
| 38 |
+
if args.cpu_self_check:
|
| 39 |
+
return 0
|
| 40 |
+
if args.output_dir is None:
|
| 41 |
+
raise SystemExit("--output-dir is required unless --cpu-self-check is used")
|
| 42 |
+
output_dir = args.output_dir.resolve()
|
| 43 |
+
output_path = output_dir / "VAL-07_portable_combined.png"
|
| 44 |
+
command = [
|
| 45 |
+
sys.executable,
|
| 46 |
+
str(RUNNER),
|
| 47 |
+
"--model",
|
| 48 |
+
args.model,
|
| 49 |
+
"--prompt",
|
| 50 |
+
PROMPT,
|
| 51 |
+
"--negative-prompt",
|
| 52 |
+
NEGATIVE_PROMPT,
|
| 53 |
+
"--output",
|
| 54 |
+
str(output_path),
|
| 55 |
+
"--height",
|
| 56 |
+
"512",
|
| 57 |
+
"--width",
|
| 58 |
+
"512",
|
| 59 |
+
"--steps",
|
| 60 |
+
"20",
|
| 61 |
+
"--cfg",
|
| 62 |
+
"5.0",
|
| 63 |
+
"--seed",
|
| 64 |
+
"27182819",
|
| 65 |
+
"--static-shift",
|
| 66 |
+
"6.0",
|
| 67 |
+
]
|
| 68 |
+
return subprocess.run(command, check=False).returncode
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
if __name__ == "__main__":
|
| 72 |
+
raise SystemExit(main())
|
runtime/nvfp4_linear.cu
ADDED
|
@@ -0,0 +1,895 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "nvfp4_linear.h"
|
| 2 |
+
|
| 3 |
+
#include <cuda_bf16.h>
|
| 4 |
+
#include <cuda_fp4.h>
|
| 5 |
+
#include <cuda_fp8.h>
|
| 6 |
+
#include <cuda_runtime.h>
|
| 7 |
+
#include <cublasLt.h>
|
| 8 |
+
|
| 9 |
+
#include <algorithm>
|
| 10 |
+
#include <cmath>
|
| 11 |
+
#include <cstdint>
|
| 12 |
+
#include <cstring>
|
| 13 |
+
#include <limits>
|
| 14 |
+
#include <mutex>
|
| 15 |
+
#include <stdexcept>
|
| 16 |
+
#include <string>
|
| 17 |
+
|
| 18 |
+
namespace {
|
| 19 |
+
|
| 20 |
+
constexpr int kAbiVersion = 1;
|
| 21 |
+
constexpr int kFp4BlockElements = 16;
|
| 22 |
+
constexpr int kScaleTileOuter = 128;
|
| 23 |
+
constexpr int kScaleTileInner = 4;
|
| 24 |
+
constexpr int kWarpsPerQuantBlock = 8;
|
| 25 |
+
constexpr int kQuantThreads = 32 * kWarpsPerQuantBlock;
|
| 26 |
+
constexpr int kReduceThreads = 256;
|
| 27 |
+
constexpr int kReduceItemsPerThread = 4;
|
| 28 |
+
constexpr int kBiasThreads = 256;
|
| 29 |
+
constexpr float kFp4E2M1Max = 6.0f;
|
| 30 |
+
constexpr float kFp4TensorScaleMax = 448.0f;
|
| 31 |
+
constexpr float kTensorScaleDenominator =
|
| 32 |
+
kFp4E2M1Max * kFp4TensorScaleMax;
|
| 33 |
+
constexpr size_t kWorkspaceBytes = 64ull * 1024ull * 1024ull;
|
| 34 |
+
|
| 35 |
+
thread_local std::string g_last_error;
|
| 36 |
+
|
| 37 |
+
[[noreturn]] void fail(const std::string& message) {
|
| 38 |
+
throw std::runtime_error(message);
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
#define CUDA_CHECK(expr) \
|
| 42 |
+
do { \
|
| 43 |
+
const cudaError_t status_ = (expr); \
|
| 44 |
+
if (status_ != cudaSuccess) { \
|
| 45 |
+
fail(std::string(#expr) + ": " + cudaGetErrorString(status_)); \
|
| 46 |
+
} \
|
| 47 |
+
} while (0)
|
| 48 |
+
|
| 49 |
+
#define CUBLASLT_CHECK(expr) \
|
| 50 |
+
do { \
|
| 51 |
+
const cublasStatus_t status_ = (expr); \
|
| 52 |
+
if (status_ != CUBLAS_STATUS_SUCCESS) { \
|
| 53 |
+
fail(std::string(#expr) + " failed with cuBLASLt status " + \
|
| 54 |
+
std::to_string(static_cast<int>(status_))); \
|
| 55 |
+
} \
|
| 56 |
+
} while (0)
|
| 57 |
+
|
| 58 |
+
int round_up(int value, int multiple) {
|
| 59 |
+
if (value <= 0 || multiple <= 0 ||
|
| 60 |
+
value > std::numeric_limits<int>::max() - (multiple - 1)) {
|
| 61 |
+
fail("invalid or overflowing round_up arguments");
|
| 62 |
+
}
|
| 63 |
+
return ((value + multiple - 1) / multiple) * multiple;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
size_t checked_multiply(size_t left, size_t right, const char* label) {
|
| 67 |
+
if (right != 0 && left > std::numeric_limits<size_t>::max() / right) {
|
| 68 |
+
fail(std::string(label) + " size overflows size_t");
|
| 69 |
+
}
|
| 70 |
+
return left * right;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
size_t round_up_divide(size_t dividend, size_t divisor) {
|
| 74 |
+
return (dividend + divisor - 1) / divisor;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
struct ScaleLayout {
|
| 78 |
+
int inner_dim = 0;
|
| 79 |
+
int outer_tiles = 0;
|
| 80 |
+
size_t bytes = 0;
|
| 81 |
+
};
|
| 82 |
+
|
| 83 |
+
ScaleLayout make_scale_layout(int rows_k, int outer_columns) {
|
| 84 |
+
if (rows_k <= 0 || rows_k % 16 != 0 || outer_columns <= 0) {
|
| 85 |
+
fail("scale layout requires positive K divisible by 16 and positive M/N");
|
| 86 |
+
}
|
| 87 |
+
ScaleLayout layout;
|
| 88 |
+
layout.inner_dim = round_up(rows_k / kFp4BlockElements, kScaleTileInner);
|
| 89 |
+
layout.outer_tiles =
|
| 90 |
+
(outer_columns + kScaleTileOuter - 1) / kScaleTileOuter;
|
| 91 |
+
layout.bytes = checked_multiply(
|
| 92 |
+
checked_multiply(static_cast<size_t>(layout.outer_tiles),
|
| 93 |
+
static_cast<size_t>(layout.inner_dim), "scale tensor"),
|
| 94 |
+
static_cast<size_t>(kScaleTileOuter), "scale tensor");
|
| 95 |
+
return layout;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
size_t packed_weight_bytes_checked(int n, int k) {
|
| 99 |
+
if (n <= 0 || k <= 0 || n % 8 != 0 || k % 32 != 0) {
|
| 100 |
+
fail("NVFP4 weight requires N divisible by 8 and K divisible by 32");
|
| 101 |
+
}
|
| 102 |
+
return checked_multiply(static_cast<size_t>(n),
|
| 103 |
+
static_cast<size_t>(k), "packed weight") /
|
| 104 |
+
2;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
size_t host_scale_offset(int outer, int inner_scale, int scale_inner_dim) {
|
| 108 |
+
const int outer_tile = outer / kScaleTileOuter;
|
| 109 |
+
const int local_outer = outer % kScaleTileOuter;
|
| 110 |
+
const int local_inner = inner_scale % kScaleTileInner;
|
| 111 |
+
const int inner_tile_start = inner_scale - local_inner;
|
| 112 |
+
const size_t tile_base =
|
| 113 |
+
static_cast<size_t>(inner_tile_start +
|
| 114 |
+
outer_tile * scale_inner_dim) *
|
| 115 |
+
kScaleTileOuter;
|
| 116 |
+
return tile_base + static_cast<size_t>(local_outer % 32) * 16 +
|
| 117 |
+
static_cast<size_t>(local_outer / 32) * 4 + local_inner;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
__device__ __forceinline__ size_t device_scale_offset(
|
| 121 |
+
int outer, int inner_scale, int scale_inner_dim) {
|
| 122 |
+
const int outer_tile = outer / kScaleTileOuter;
|
| 123 |
+
const int local_outer = outer % kScaleTileOuter;
|
| 124 |
+
const int local_inner = inner_scale % kScaleTileInner;
|
| 125 |
+
const int inner_tile_start = inner_scale - local_inner;
|
| 126 |
+
const size_t tile_base =
|
| 127 |
+
static_cast<size_t>(inner_tile_start +
|
| 128 |
+
outer_tile * scale_inner_dim) *
|
| 129 |
+
kScaleTileOuter;
|
| 130 |
+
return tile_base + static_cast<size_t>(local_outer % 32) * 16 +
|
| 131 |
+
static_cast<size_t>(local_outer / 32) * 4 + local_inner;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
float host_ue4m3_to_float(uint8_t raw) {
|
| 135 |
+
const __half_raw half_raw = __nv_cvt_fp8_to_halfraw(raw, __NV_E4M3);
|
| 136 |
+
return __half2float(static_cast<__half>(half_raw));
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
__device__ __forceinline__ float device_ue4m3_to_float(uint8_t raw) {
|
| 140 |
+
const __half_raw half_raw = __nv_cvt_fp8_to_halfraw(raw, __NV_E4M3);
|
| 141 |
+
return __half2float(static_cast<__half>(half_raw));
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
__device__ __forceinline__ float tensor_scale_from_amax(float amax) {
|
| 145 |
+
return amax == 0.0f ? 1.0f : amax / kTensorScaleDenominator;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
__device__ __forceinline__ float block_reduce_max(float value) {
|
| 149 |
+
__shared__ float shared[kReduceThreads];
|
| 150 |
+
shared[threadIdx.x] = value;
|
| 151 |
+
__syncthreads();
|
| 152 |
+
for (int offset = kReduceThreads / 2; offset > 0; offset >>= 1) {
|
| 153 |
+
if (threadIdx.x < offset) {
|
| 154 |
+
shared[threadIdx.x] =
|
| 155 |
+
fmaxf(shared[threadIdx.x], shared[threadIdx.x + offset]);
|
| 156 |
+
}
|
| 157 |
+
__syncthreads();
|
| 158 |
+
}
|
| 159 |
+
return shared[0];
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
__global__ void reduce_abs_max_bf16(
|
| 163 |
+
const __nv_bfloat16* source, float* block_maxima,
|
| 164 |
+
size_t element_count) {
|
| 165 |
+
const size_t block_start =
|
| 166 |
+
static_cast<size_t>(blockIdx.x) * kReduceThreads *
|
| 167 |
+
kReduceItemsPerThread;
|
| 168 |
+
float local_max = 0.0f;
|
| 169 |
+
for (int item = 0; item < kReduceItemsPerThread; ++item) {
|
| 170 |
+
const size_t index =
|
| 171 |
+
block_start + static_cast<size_t>(threadIdx.x) +
|
| 172 |
+
static_cast<size_t>(item) * kReduceThreads;
|
| 173 |
+
if (index < element_count) {
|
| 174 |
+
local_max =
|
| 175 |
+
fmaxf(local_max, fabsf(__bfloat162float(source[index])));
|
| 176 |
+
}
|
| 177 |
+
}
|
| 178 |
+
const float block_max = block_reduce_max(local_max);
|
| 179 |
+
if (threadIdx.x == 0) {
|
| 180 |
+
block_maxima[blockIdx.x] = block_max;
|
| 181 |
+
}
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
__global__ void reduce_max_float(
|
| 185 |
+
const float* source, float* block_maxima, size_t element_count) {
|
| 186 |
+
const size_t block_start =
|
| 187 |
+
static_cast<size_t>(blockIdx.x) * kReduceThreads *
|
| 188 |
+
kReduceItemsPerThread;
|
| 189 |
+
float local_max = 0.0f;
|
| 190 |
+
for (int item = 0; item < kReduceItemsPerThread; ++item) {
|
| 191 |
+
const size_t index =
|
| 192 |
+
block_start + static_cast<size_t>(threadIdx.x) +
|
| 193 |
+
static_cast<size_t>(item) * kReduceThreads;
|
| 194 |
+
if (index < element_count) {
|
| 195 |
+
local_max = fmaxf(local_max, source[index]);
|
| 196 |
+
}
|
| 197 |
+
}
|
| 198 |
+
const float block_max = block_reduce_max(local_max);
|
| 199 |
+
if (threadIdx.x == 0) {
|
| 200 |
+
block_maxima[blockIdx.x] = block_max;
|
| 201 |
+
}
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
__global__ void finalize_activation_scale(
|
| 205 |
+
const float* activation_amax, const float* weight_tensor_scale,
|
| 206 |
+
float* activation_tensor_scale, float* fp4_alpha) {
|
| 207 |
+
if (blockIdx.x == 0 && threadIdx.x == 0) {
|
| 208 |
+
const float activation_scale =
|
| 209 |
+
tensor_scale_from_amax(activation_amax[0]);
|
| 210 |
+
activation_tensor_scale[0] = activation_scale;
|
| 211 |
+
fp4_alpha[0] = weight_tensor_scale[0] * activation_scale;
|
| 212 |
+
}
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
// logical_m may be smaller than padded_m. Padded rows are written as exact
|
| 216 |
+
// FP4 zero with zero block scales, so reusable scratch never exposes a prior
|
| 217 |
+
// call's tail.
|
| 218 |
+
__global__ void dynamic_quantize_activation(
|
| 219 |
+
const __nv_bfloat16* source, uint8_t* destination_fp4,
|
| 220 |
+
uint8_t* destination_scales,
|
| 221 |
+
const float* activation_tensor_scale, int rows_k, int logical_m,
|
| 222 |
+
int scale_inner_dim, uint64_t padded_scale_blocks) {
|
| 223 |
+
const int lane = threadIdx.x & 31;
|
| 224 |
+
const int warp_in_block = threadIdx.x >> 5;
|
| 225 |
+
const uint64_t logical_block =
|
| 226 |
+
static_cast<uint64_t>(blockIdx.x) * kWarpsPerQuantBlock +
|
| 227 |
+
static_cast<uint64_t>(warp_in_block);
|
| 228 |
+
if (logical_block >= padded_scale_blocks) {
|
| 229 |
+
return;
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
const int blocks_per_column = rows_k / kFp4BlockElements;
|
| 233 |
+
const int outer =
|
| 234 |
+
static_cast<int>(logical_block / blocks_per_column);
|
| 235 |
+
const int inner_scale = static_cast<int>(
|
| 236 |
+
logical_block -
|
| 237 |
+
static_cast<uint64_t>(outer) * blocks_per_column);
|
| 238 |
+
const int first_row = inner_scale * kFp4BlockElements;
|
| 239 |
+
const size_t column_base = static_cast<size_t>(outer) * rows_k;
|
| 240 |
+
const bool valid_outer = outer < logical_m;
|
| 241 |
+
const float tensor_scale = activation_tensor_scale[0];
|
| 242 |
+
const float inverse_tensor_scale =
|
| 243 |
+
tensor_scale == 0.0f ? 0.0f : 1.0f / tensor_scale;
|
| 244 |
+
|
| 245 |
+
float value0 = 0.0f;
|
| 246 |
+
float value1 = 0.0f;
|
| 247 |
+
float magnitude = 0.0f;
|
| 248 |
+
size_t destination_source_index = 0;
|
| 249 |
+
if (lane < kFp4BlockElements / 2) {
|
| 250 |
+
const int row0 = first_row + lane * 2;
|
| 251 |
+
destination_source_index = column_base + row0;
|
| 252 |
+
if (valid_outer) {
|
| 253 |
+
value0 = __bfloat162float(source[destination_source_index]) *
|
| 254 |
+
inverse_tensor_scale;
|
| 255 |
+
value1 = __bfloat162float(source[destination_source_index + 1]) *
|
| 256 |
+
inverse_tensor_scale;
|
| 257 |
+
magnitude = fmaxf(fabsf(value0), fabsf(value1));
|
| 258 |
+
}
|
| 259 |
+
}
|
| 260 |
+
for (int offset = 16; offset > 0; offset >>= 1) {
|
| 261 |
+
magnitude =
|
| 262 |
+
fmaxf(magnitude,
|
| 263 |
+
__shfl_down_sync(0xffffffffU, magnitude, offset));
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
float rounded_scale = 0.0f;
|
| 267 |
+
if (lane == 0) {
|
| 268 |
+
const uint8_t scale_raw = __nv_cvt_float_to_fp8(
|
| 269 |
+
magnitude / kFp4E2M1Max, __NV_SATFINITE, __NV_E4M3);
|
| 270 |
+
rounded_scale = device_ue4m3_to_float(scale_raw);
|
| 271 |
+
destination_scales[device_scale_offset(
|
| 272 |
+
outer, inner_scale, scale_inner_dim)] = scale_raw;
|
| 273 |
+
}
|
| 274 |
+
rounded_scale = __shfl_sync(0xffffffffU, rounded_scale, 0);
|
| 275 |
+
|
| 276 |
+
if (lane < kFp4BlockElements / 2) {
|
| 277 |
+
const float inverse_scale =
|
| 278 |
+
rounded_scale == 0.0f ? 0.0f : 1.0f / rounded_scale;
|
| 279 |
+
destination_fp4[destination_source_index / 2] =
|
| 280 |
+
__nv_cvt_float2_to_fp4x2(
|
| 281 |
+
make_float2(value0 * inverse_scale,
|
| 282 |
+
value1 * inverse_scale),
|
| 283 |
+
__NV_E2M1, cudaRoundNearest);
|
| 284 |
+
}
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
__global__ void add_bias_bf16(
|
| 288 |
+
__nv_bfloat16* output, const __nv_bfloat16* bias,
|
| 289 |
+
size_t element_count, int n) {
|
| 290 |
+
const size_t index =
|
| 291 |
+
static_cast<size_t>(blockIdx.x) * blockDim.x + threadIdx.x;
|
| 292 |
+
if (index < element_count) {
|
| 293 |
+
const float value = __bfloat162float(output[index]);
|
| 294 |
+
const float bias_value = __bfloat162float(bias[index % n]);
|
| 295 |
+
output[index] = __float2bfloat16(value + bias_value);
|
| 296 |
+
}
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
size_t reduction_block_count(size_t element_count) {
|
| 300 |
+
return round_up_divide(
|
| 301 |
+
element_count,
|
| 302 |
+
static_cast<size_t>(kReduceThreads * kReduceItemsPerThread));
|
| 303 |
+
}
|
| 304 |
+
|
| 305 |
+
size_t reduction_scratch_elements(size_t element_count) {
|
| 306 |
+
size_t max_blocks = 1;
|
| 307 |
+
while (element_count > 1) {
|
| 308 |
+
const size_t blocks = reduction_block_count(element_count);
|
| 309 |
+
max_blocks = std::max(max_blocks, blocks);
|
| 310 |
+
element_count = blocks;
|
| 311 |
+
}
|
| 312 |
+
return max_blocks;
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
void enqueue_activation_amax_reduce(
|
| 316 |
+
const __nv_bfloat16* source, size_t element_count,
|
| 317 |
+
float* scratch_a, float* scratch_b, float* destination_amax,
|
| 318 |
+
int max_grid_x, cudaStream_t stream) {
|
| 319 |
+
const float* current_source = nullptr;
|
| 320 |
+
float* current_destination = scratch_a;
|
| 321 |
+
size_t current_count = element_count;
|
| 322 |
+
bool first_stage = true;
|
| 323 |
+
while (true) {
|
| 324 |
+
const size_t blocks = reduction_block_count(current_count);
|
| 325 |
+
if (blocks == 0 || blocks > static_cast<size_t>(max_grid_x)) {
|
| 326 |
+
fail("activation amax reduction exceeds the GPU grid limit");
|
| 327 |
+
}
|
| 328 |
+
if (first_stage) {
|
| 329 |
+
reduce_abs_max_bf16<<<
|
| 330 |
+
static_cast<unsigned int>(blocks), kReduceThreads, 0,
|
| 331 |
+
stream>>>(source, current_destination, current_count);
|
| 332 |
+
} else {
|
| 333 |
+
reduce_max_float<<<
|
| 334 |
+
static_cast<unsigned int>(blocks), kReduceThreads, 0,
|
| 335 |
+
stream>>>(current_source, current_destination, current_count);
|
| 336 |
+
}
|
| 337 |
+
CUDA_CHECK(cudaPeekAtLastError());
|
| 338 |
+
if (blocks == 1) {
|
| 339 |
+
if (current_destination != destination_amax) {
|
| 340 |
+
CUDA_CHECK(cudaMemcpyAsync(
|
| 341 |
+
destination_amax, current_destination, sizeof(float),
|
| 342 |
+
cudaMemcpyDeviceToDevice, stream));
|
| 343 |
+
}
|
| 344 |
+
return;
|
| 345 |
+
}
|
| 346 |
+
current_count = blocks;
|
| 347 |
+
current_source = current_destination;
|
| 348 |
+
current_destination =
|
| 349 |
+
current_destination == scratch_a ? scratch_b : scratch_a;
|
| 350 |
+
first_stage = false;
|
| 351 |
+
}
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
struct DeviceAllocation {
|
| 355 |
+
void* pointer = nullptr;
|
| 356 |
+
size_t bytes = 0;
|
| 357 |
+
|
| 358 |
+
~DeviceAllocation() {
|
| 359 |
+
if (pointer != nullptr) {
|
| 360 |
+
cudaFree(pointer);
|
| 361 |
+
}
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
DeviceAllocation() = default;
|
| 365 |
+
DeviceAllocation(const DeviceAllocation&) = delete;
|
| 366 |
+
DeviceAllocation& operator=(const DeviceAllocation&) = delete;
|
| 367 |
+
};
|
| 368 |
+
|
| 369 |
+
struct Context {
|
| 370 |
+
int device = -1;
|
| 371 |
+
int max_grid_x = 0;
|
| 372 |
+
cublasLtHandle_t handle = nullptr;
|
| 373 |
+
DeviceAllocation x_fp4;
|
| 374 |
+
DeviceAllocation x_scales;
|
| 375 |
+
DeviceAllocation reduce_a;
|
| 376 |
+
DeviceAllocation reduce_b;
|
| 377 |
+
DeviceAllocation activation_amax;
|
| 378 |
+
DeviceAllocation activation_scale;
|
| 379 |
+
DeviceAllocation fp4_alpha;
|
| 380 |
+
DeviceAllocation fp4_beta;
|
| 381 |
+
DeviceAllocation workspace;
|
| 382 |
+
uintptr_t bound_stream = 0;
|
| 383 |
+
bool stream_bound = false;
|
| 384 |
+
std::mutex mutex;
|
| 385 |
+
|
| 386 |
+
~Context() {
|
| 387 |
+
if (handle != nullptr) {
|
| 388 |
+
cublasLtDestroy(handle);
|
| 389 |
+
}
|
| 390 |
+
}
|
| 391 |
+
};
|
| 392 |
+
|
| 393 |
+
void allocate_exact(DeviceAllocation* allocation, size_t bytes) {
|
| 394 |
+
if (allocation->pointer != nullptr) {
|
| 395 |
+
CUDA_CHECK(cudaFree(allocation->pointer));
|
| 396 |
+
allocation->pointer = nullptr;
|
| 397 |
+
allocation->bytes = 0;
|
| 398 |
+
}
|
| 399 |
+
if (bytes != 0) {
|
| 400 |
+
CUDA_CHECK(cudaMalloc(&allocation->pointer, bytes));
|
| 401 |
+
allocation->bytes = bytes;
|
| 402 |
+
}
|
| 403 |
+
}
|
| 404 |
+
|
| 405 |
+
void ensure_capacity(
|
| 406 |
+
Context* context, DeviceAllocation* allocation, size_t bytes,
|
| 407 |
+
cudaStream_t stream) {
|
| 408 |
+
if (allocation->bytes >= bytes) {
|
| 409 |
+
return;
|
| 410 |
+
}
|
| 411 |
+
// A growth invalidates scratch pointers. Synchronize the one bound stream
|
| 412 |
+
// before freeing; steady-state forwards do not synchronize.
|
| 413 |
+
if (context->stream_bound) {
|
| 414 |
+
CUDA_CHECK(cudaStreamSynchronize(stream));
|
| 415 |
+
}
|
| 416 |
+
allocate_exact(allocation, bytes);
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
void set_scale_mode(
|
| 420 |
+
cublasLtMatmulDesc_t operation,
|
| 421 |
+
cublasLtMatmulDescAttributes_t attribute) {
|
| 422 |
+
const int32_t mode =
|
| 423 |
+
CUBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3;
|
| 424 |
+
CUBLASLT_CHECK(cublasLtMatmulDescSetAttribute(
|
| 425 |
+
operation, attribute, &mode, sizeof(mode)));
|
| 426 |
+
}
|
| 427 |
+
|
| 428 |
+
void set_pointer_attribute(
|
| 429 |
+
cublasLtMatmulDesc_t operation,
|
| 430 |
+
cublasLtMatmulDescAttributes_t attribute, const void* pointer) {
|
| 431 |
+
CUBLASLT_CHECK(cublasLtMatmulDescSetAttribute(
|
| 432 |
+
operation, attribute, &pointer, sizeof(pointer)));
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
struct Descriptors {
|
| 436 |
+
cublasLtMatmulDesc_t operation = nullptr;
|
| 437 |
+
cublasLtMatrixLayout_t w = nullptr;
|
| 438 |
+
cublasLtMatrixLayout_t x = nullptr;
|
| 439 |
+
cublasLtMatrixLayout_t c = nullptr;
|
| 440 |
+
cublasLtMatrixLayout_t d = nullptr;
|
| 441 |
+
|
| 442 |
+
~Descriptors() {
|
| 443 |
+
if (d != nullptr) cublasLtMatrixLayoutDestroy(d);
|
| 444 |
+
if (c != nullptr) cublasLtMatrixLayoutDestroy(c);
|
| 445 |
+
if (x != nullptr) cublasLtMatrixLayoutDestroy(x);
|
| 446 |
+
if (w != nullptr) cublasLtMatrixLayoutDestroy(w);
|
| 447 |
+
if (operation != nullptr) cublasLtMatmulDescDestroy(operation);
|
| 448 |
+
}
|
| 449 |
+
};
|
| 450 |
+
|
| 451 |
+
cublasLtMatmulAlgo_t choose_algo(
|
| 452 |
+
Context* context, const Descriptors& descriptors,
|
| 453 |
+
size_t* workspace_bytes) {
|
| 454 |
+
cublasLtMatmulPreference_t preference = nullptr;
|
| 455 |
+
CUBLASLT_CHECK(cublasLtMatmulPreferenceCreate(&preference));
|
| 456 |
+
CUBLASLT_CHECK(cublasLtMatmulPreferenceSetAttribute(
|
| 457 |
+
preference, CUBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES,
|
| 458 |
+
&kWorkspaceBytes, sizeof(kWorkspaceBytes)));
|
| 459 |
+
|
| 460 |
+
cublasLtMatmulHeuristicResult_t candidates[16]{};
|
| 461 |
+
int returned = 0;
|
| 462 |
+
const cublasStatus_t status = cublasLtMatmulAlgoGetHeuristic(
|
| 463 |
+
context->handle, descriptors.operation, descriptors.w,
|
| 464 |
+
descriptors.x, descriptors.c, descriptors.d, preference, 16,
|
| 465 |
+
candidates, &returned);
|
| 466 |
+
cublasLtMatmulPreferenceDestroy(preference);
|
| 467 |
+
if (status != CUBLAS_STATUS_SUCCESS || returned == 0) {
|
| 468 |
+
fail("cuBLASLt returned no NVFP4 heuristic");
|
| 469 |
+
}
|
| 470 |
+
for (int index = 0; index < returned; ++index) {
|
| 471 |
+
if (candidates[index].state == CUBLAS_STATUS_SUCCESS) {
|
| 472 |
+
*workspace_bytes = candidates[index].workspaceSize;
|
| 473 |
+
return candidates[index].algo;
|
| 474 |
+
}
|
| 475 |
+
}
|
| 476 |
+
fail("all cuBLASLt NVFP4 heuristics were unsupported");
|
| 477 |
+
}
|
| 478 |
+
|
| 479 |
+
void validate_device_pointer(
|
| 480 |
+
const void* pointer, int expected_device, const char* label) {
|
| 481 |
+
if (pointer == nullptr) {
|
| 482 |
+
fail(std::string(label) + " is null");
|
| 483 |
+
}
|
| 484 |
+
cudaPointerAttributes attributes{};
|
| 485 |
+
CUDA_CHECK(cudaPointerGetAttributes(&attributes, pointer));
|
| 486 |
+
if (attributes.type != cudaMemoryTypeDevice ||
|
| 487 |
+
attributes.device != expected_device) {
|
| 488 |
+
fail(std::string(label) + " is not a CUDA allocation on the context device");
|
| 489 |
+
}
|
| 490 |
+
if ((reinterpret_cast<uintptr_t>(pointer) & 0x0fU) != 0) {
|
| 491 |
+
fail(std::string(label) + " is not at least 16-byte aligned");
|
| 492 |
+
}
|
| 493 |
+
}
|
| 494 |
+
|
| 495 |
+
void forward_impl(
|
| 496 |
+
Context* context, const void* input_bf16,
|
| 497 |
+
const void* packed_weight, size_t packed_weight_bytes,
|
| 498 |
+
const void* packed_weight_scales,
|
| 499 |
+
size_t packed_weight_scale_bytes,
|
| 500 |
+
const void* weight_tensor_scale_f32, const void* bias_bf16,
|
| 501 |
+
void* output_bf16, int logical_m, int k, int n,
|
| 502 |
+
uintptr_t stream_value) {
|
| 503 |
+
if (logical_m <= 0 || k <= 0 || n <= 0) {
|
| 504 |
+
fail("M, K, and N must be positive");
|
| 505 |
+
}
|
| 506 |
+
const size_t expected_weight_bytes =
|
| 507 |
+
packed_weight_bytes_checked(n, k);
|
| 508 |
+
const size_t expected_scale_bytes = make_scale_layout(k, n).bytes;
|
| 509 |
+
if (packed_weight_bytes != expected_weight_bytes ||
|
| 510 |
+
packed_weight_scale_bytes != expected_scale_bytes) {
|
| 511 |
+
fail("packed weight or scale buffer has the wrong byte size");
|
| 512 |
+
}
|
| 513 |
+
|
| 514 |
+
CUDA_CHECK(cudaSetDevice(context->device));
|
| 515 |
+
cudaStream_t stream = reinterpret_cast<cudaStream_t>(stream_value);
|
| 516 |
+
if (!context->stream_bound) {
|
| 517 |
+
context->bound_stream = stream_value;
|
| 518 |
+
context->stream_bound = true;
|
| 519 |
+
} else if (context->bound_stream != stream_value) {
|
| 520 |
+
fail("resident NVFP4 context is bound to a different CUDA stream");
|
| 521 |
+
}
|
| 522 |
+
cudaStreamCaptureStatus capture_status = cudaStreamCaptureStatusNone;
|
| 523 |
+
CUDA_CHECK(cudaStreamIsCapturing(stream, &capture_status));
|
| 524 |
+
if (capture_status != cudaStreamCaptureStatusNone) {
|
| 525 |
+
fail("resident ctypes prototype does not support CUDA graph capture");
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
validate_device_pointer(input_bf16, context->device, "input");
|
| 529 |
+
validate_device_pointer(packed_weight, context->device, "packed weight");
|
| 530 |
+
validate_device_pointer(
|
| 531 |
+
packed_weight_scales, context->device, "weight scales");
|
| 532 |
+
validate_device_pointer(
|
| 533 |
+
weight_tensor_scale_f32, context->device, "weight tensor scale");
|
| 534 |
+
validate_device_pointer(output_bf16, context->device, "output");
|
| 535 |
+
if (bias_bf16 != nullptr) {
|
| 536 |
+
validate_device_pointer(bias_bf16, context->device, "bias");
|
| 537 |
+
}
|
| 538 |
+
|
| 539 |
+
const int padded_m = round_up(logical_m, 8);
|
| 540 |
+
const size_t input_elements = checked_multiply(
|
| 541 |
+
static_cast<size_t>(logical_m), static_cast<size_t>(k), "input");
|
| 542 |
+
const size_t padded_input_elements = checked_multiply(
|
| 543 |
+
static_cast<size_t>(padded_m), static_cast<size_t>(k),
|
| 544 |
+
"padded input");
|
| 545 |
+
const size_t output_elements = checked_multiply(
|
| 546 |
+
static_cast<size_t>(padded_m), static_cast<size_t>(n), "output");
|
| 547 |
+
const ScaleLayout x_scale_layout = make_scale_layout(k, padded_m);
|
| 548 |
+
const size_t reduce_elements =
|
| 549 |
+
reduction_scratch_elements(input_elements);
|
| 550 |
+
|
| 551 |
+
ensure_capacity(
|
| 552 |
+
context, &context->x_fp4, padded_input_elements / 2, stream);
|
| 553 |
+
ensure_capacity(
|
| 554 |
+
context, &context->x_scales, x_scale_layout.bytes, stream);
|
| 555 |
+
ensure_capacity(
|
| 556 |
+
context, &context->reduce_a,
|
| 557 |
+
checked_multiply(reduce_elements, sizeof(float), "reduction"),
|
| 558 |
+
stream);
|
| 559 |
+
ensure_capacity(
|
| 560 |
+
context, &context->reduce_b,
|
| 561 |
+
checked_multiply(reduce_elements, sizeof(float), "reduction"),
|
| 562 |
+
stream);
|
| 563 |
+
|
| 564 |
+
// Clear the complete tiled scale allocation, including padding that the
|
| 565 |
+
// logical quantizer does not address.
|
| 566 |
+
CUDA_CHECK(cudaMemsetAsync(
|
| 567 |
+
context->x_scales.pointer, 0, x_scale_layout.bytes, stream));
|
| 568 |
+
enqueue_activation_amax_reduce(
|
| 569 |
+
static_cast<const __nv_bfloat16*>(input_bf16), input_elements,
|
| 570 |
+
static_cast<float*>(context->reduce_a.pointer),
|
| 571 |
+
static_cast<float*>(context->reduce_b.pointer),
|
| 572 |
+
static_cast<float*>(context->activation_amax.pointer),
|
| 573 |
+
context->max_grid_x, stream);
|
| 574 |
+
finalize_activation_scale<<<1, 1, 0, stream>>>(
|
| 575 |
+
static_cast<const float*>(context->activation_amax.pointer),
|
| 576 |
+
static_cast<const float*>(weight_tensor_scale_f32),
|
| 577 |
+
static_cast<float*>(context->activation_scale.pointer),
|
| 578 |
+
static_cast<float*>(context->fp4_alpha.pointer));
|
| 579 |
+
CUDA_CHECK(cudaPeekAtLastError());
|
| 580 |
+
|
| 581 |
+
const uint64_t padded_blocks =
|
| 582 |
+
static_cast<uint64_t>(padded_m) *
|
| 583 |
+
static_cast<uint64_t>(k / kFp4BlockElements);
|
| 584 |
+
const uint64_t cuda_blocks =
|
| 585 |
+
(padded_blocks + kWarpsPerQuantBlock - 1) /
|
| 586 |
+
kWarpsPerQuantBlock;
|
| 587 |
+
if (cuda_blocks == 0 ||
|
| 588 |
+
cuda_blocks > static_cast<uint64_t>(context->max_grid_x)) {
|
| 589 |
+
fail("activation quantizer exceeds the GPU grid limit");
|
| 590 |
+
}
|
| 591 |
+
dynamic_quantize_activation<<<
|
| 592 |
+
static_cast<unsigned int>(cuda_blocks), kQuantThreads, 0,
|
| 593 |
+
stream>>>(
|
| 594 |
+
static_cast<const __nv_bfloat16*>(input_bf16),
|
| 595 |
+
static_cast<uint8_t*>(context->x_fp4.pointer),
|
| 596 |
+
static_cast<uint8_t*>(context->x_scales.pointer),
|
| 597 |
+
static_cast<const float*>(context->activation_scale.pointer),
|
| 598 |
+
k, logical_m, x_scale_layout.inner_dim, padded_blocks);
|
| 599 |
+
CUDA_CHECK(cudaPeekAtLastError());
|
| 600 |
+
|
| 601 |
+
Descriptors descriptors;
|
| 602 |
+
CUBLASLT_CHECK(cublasLtMatmulDescCreate(
|
| 603 |
+
&descriptors.operation, CUBLAS_COMPUTE_32F, CUDA_R_32F));
|
| 604 |
+
const cublasOperation_t transpose_a = CUBLAS_OP_T;
|
| 605 |
+
const cublasOperation_t transpose_b = CUBLAS_OP_N;
|
| 606 |
+
CUBLASLT_CHECK(cublasLtMatmulDescSetAttribute(
|
| 607 |
+
descriptors.operation, CUBLASLT_MATMUL_DESC_TRANSA,
|
| 608 |
+
&transpose_a, sizeof(transpose_a)));
|
| 609 |
+
CUBLASLT_CHECK(cublasLtMatmulDescSetAttribute(
|
| 610 |
+
descriptors.operation, CUBLASLT_MATMUL_DESC_TRANSB,
|
| 611 |
+
&transpose_b, sizeof(transpose_b)));
|
| 612 |
+
const cublasLtPointerMode_t pointer_mode =
|
| 613 |
+
CUBLASLT_POINTER_MODE_DEVICE;
|
| 614 |
+
CUBLASLT_CHECK(cublasLtMatmulDescSetAttribute(
|
| 615 |
+
descriptors.operation, CUBLASLT_MATMUL_DESC_POINTER_MODE,
|
| 616 |
+
&pointer_mode, sizeof(pointer_mode)));
|
| 617 |
+
set_scale_mode(
|
| 618 |
+
descriptors.operation, CUBLASLT_MATMUL_DESC_A_SCALE_MODE);
|
| 619 |
+
set_scale_mode(
|
| 620 |
+
descriptors.operation, CUBLASLT_MATMUL_DESC_B_SCALE_MODE);
|
| 621 |
+
// Refresh pointer attributes for every call. Module buffers may differ
|
| 622 |
+
// between adjacent linears even though the shape is identical.
|
| 623 |
+
set_pointer_attribute(
|
| 624 |
+
descriptors.operation, CUBLASLT_MATMUL_DESC_A_SCALE_POINTER,
|
| 625 |
+
packed_weight_scales);
|
| 626 |
+
set_pointer_attribute(
|
| 627 |
+
descriptors.operation, CUBLASLT_MATMUL_DESC_B_SCALE_POINTER,
|
| 628 |
+
context->x_scales.pointer);
|
| 629 |
+
|
| 630 |
+
CUBLASLT_CHECK(cublasLtMatrixLayoutCreate(
|
| 631 |
+
&descriptors.w, CUDA_R_4F_E2M1, k, n, k));
|
| 632 |
+
CUBLASLT_CHECK(cublasLtMatrixLayoutCreate(
|
| 633 |
+
&descriptors.x, CUDA_R_4F_E2M1, k, padded_m, k));
|
| 634 |
+
CUBLASLT_CHECK(cublasLtMatrixLayoutCreate(
|
| 635 |
+
&descriptors.c, CUDA_R_16BF, n, padded_m, n));
|
| 636 |
+
CUBLASLT_CHECK(cublasLtMatrixLayoutCreate(
|
| 637 |
+
&descriptors.d, CUDA_R_16BF, n, padded_m, n));
|
| 638 |
+
|
| 639 |
+
size_t selected_workspace_bytes = 0;
|
| 640 |
+
const cublasLtMatmulAlgo_t algorithm =
|
| 641 |
+
choose_algo(context, descriptors, &selected_workspace_bytes);
|
| 642 |
+
if (selected_workspace_bytes > context->workspace.bytes) {
|
| 643 |
+
fail("selected cuBLASLt algorithm exceeds the context workspace");
|
| 644 |
+
}
|
| 645 |
+
CUBLASLT_CHECK(cublasLtMatmul(
|
| 646 |
+
context->handle, descriptors.operation,
|
| 647 |
+
context->fp4_alpha.pointer,
|
| 648 |
+
packed_weight, descriptors.w,
|
| 649 |
+
context->x_fp4.pointer, descriptors.x,
|
| 650 |
+
context->fp4_beta.pointer,
|
| 651 |
+
output_bf16, descriptors.c,
|
| 652 |
+
output_bf16, descriptors.d,
|
| 653 |
+
&algorithm, context->workspace.pointer,
|
| 654 |
+
selected_workspace_bytes, stream));
|
| 655 |
+
|
| 656 |
+
if (bias_bf16 != nullptr) {
|
| 657 |
+
const size_t blocks =
|
| 658 |
+
round_up_divide(output_elements,
|
| 659 |
+
static_cast<size_t>(kBiasThreads));
|
| 660 |
+
if (blocks > static_cast<size_t>(context->max_grid_x)) {
|
| 661 |
+
fail("bias kernel exceeds the GPU grid limit");
|
| 662 |
+
}
|
| 663 |
+
add_bias_bf16<<<
|
| 664 |
+
static_cast<unsigned int>(blocks), kBiasThreads, 0, stream>>>(
|
| 665 |
+
static_cast<__nv_bfloat16*>(output_bf16),
|
| 666 |
+
static_cast<const __nv_bfloat16*>(bias_bf16),
|
| 667 |
+
output_elements, n);
|
| 668 |
+
CUDA_CHECK(cudaPeekAtLastError());
|
| 669 |
+
}
|
| 670 |
+
}
|
| 671 |
+
|
| 672 |
+
template <typename Function>
|
| 673 |
+
int guarded(Function&& function) {
|
| 674 |
+
try {
|
| 675 |
+
g_last_error.clear();
|
| 676 |
+
function();
|
| 677 |
+
return 0;
|
| 678 |
+
} catch (const std::exception& error) {
|
| 679 |
+
g_last_error = error.what();
|
| 680 |
+
return 1;
|
| 681 |
+
} catch (...) {
|
| 682 |
+
g_last_error = "unknown native NVFP4 error";
|
| 683 |
+
return 2;
|
| 684 |
+
}
|
| 685 |
+
}
|
| 686 |
+
|
| 687 |
+
} // namespace
|
| 688 |
+
|
| 689 |
+
extern "C" int mage_nvfp4_abi_version(void) {
|
| 690 |
+
return kAbiVersion;
|
| 691 |
+
}
|
| 692 |
+
|
| 693 |
+
extern "C" const char* mage_nvfp4_last_error(void) {
|
| 694 |
+
return g_last_error.c_str();
|
| 695 |
+
}
|
| 696 |
+
|
| 697 |
+
extern "C" size_t mage_nvfp4_packed_weight_bytes(int n, int k) {
|
| 698 |
+
try {
|
| 699 |
+
g_last_error.clear();
|
| 700 |
+
return packed_weight_bytes_checked(n, k);
|
| 701 |
+
} catch (const std::exception& error) {
|
| 702 |
+
g_last_error = error.what();
|
| 703 |
+
return 0;
|
| 704 |
+
}
|
| 705 |
+
}
|
| 706 |
+
|
| 707 |
+
extern "C" size_t mage_nvfp4_weight_scale_bytes(int n, int k) {
|
| 708 |
+
try {
|
| 709 |
+
g_last_error.clear();
|
| 710 |
+
packed_weight_bytes_checked(n, k);
|
| 711 |
+
return make_scale_layout(k, n).bytes;
|
| 712 |
+
} catch (const std::exception& error) {
|
| 713 |
+
g_last_error = error.what();
|
| 714 |
+
return 0;
|
| 715 |
+
}
|
| 716 |
+
}
|
| 717 |
+
|
| 718 |
+
extern "C" int mage_nvfp4_pack_weight_bf16(
|
| 719 |
+
const void* weight_bf16, int n, int k,
|
| 720 |
+
void* packed_weight, size_t packed_weight_capacity,
|
| 721 |
+
void* packed_scales, size_t packed_scale_capacity,
|
| 722 |
+
float* tensor_scale) {
|
| 723 |
+
return guarded([&]() {
|
| 724 |
+
if (weight_bf16 == nullptr || packed_weight == nullptr ||
|
| 725 |
+
packed_scales == nullptr || tensor_scale == nullptr) {
|
| 726 |
+
fail("weight packer received a null pointer");
|
| 727 |
+
}
|
| 728 |
+
const size_t required_weight =
|
| 729 |
+
packed_weight_bytes_checked(n, k);
|
| 730 |
+
const ScaleLayout scale_layout = make_scale_layout(k, n);
|
| 731 |
+
if (packed_weight_capacity != required_weight ||
|
| 732 |
+
packed_scale_capacity != scale_layout.bytes) {
|
| 733 |
+
fail("weight packer received an incorrectly sized destination");
|
| 734 |
+
}
|
| 735 |
+
|
| 736 |
+
const auto* source =
|
| 737 |
+
static_cast<const __nv_bfloat16*>(weight_bf16);
|
| 738 |
+
auto* destination = static_cast<uint8_t*>(packed_weight);
|
| 739 |
+
auto* scales = static_cast<uint8_t*>(packed_scales);
|
| 740 |
+
std::memset(destination, 0, required_weight);
|
| 741 |
+
std::memset(scales, 0, scale_layout.bytes);
|
| 742 |
+
const size_t elements =
|
| 743 |
+
checked_multiply(static_cast<size_t>(n),
|
| 744 |
+
static_cast<size_t>(k), "weight");
|
| 745 |
+
float global_amax = 0.0f;
|
| 746 |
+
for (size_t index = 0; index < elements; ++index) {
|
| 747 |
+
global_amax = std::max(
|
| 748 |
+
global_amax,
|
| 749 |
+
std::abs(__bfloat162float(source[index])));
|
| 750 |
+
}
|
| 751 |
+
*tensor_scale =
|
| 752 |
+
global_amax == 0.0f
|
| 753 |
+
? 1.0f
|
| 754 |
+
: global_amax / kTensorScaleDenominator;
|
| 755 |
+
const float inverse_tensor_scale = 1.0f / *tensor_scale;
|
| 756 |
+
|
| 757 |
+
// nn.Linear weight [N,K] is physical column-major KxN.
|
| 758 |
+
for (int column = 0; column < n; ++column) {
|
| 759 |
+
for (int block = 0; block < k / kFp4BlockElements; ++block) {
|
| 760 |
+
const int first_row = block * kFp4BlockElements;
|
| 761 |
+
float block_amax = 0.0f;
|
| 762 |
+
for (int lane = 0; lane < kFp4BlockElements; ++lane) {
|
| 763 |
+
const size_t index =
|
| 764 |
+
static_cast<size_t>(first_row + lane) +
|
| 765 |
+
static_cast<size_t>(column) * k;
|
| 766 |
+
block_amax = std::max(
|
| 767 |
+
block_amax,
|
| 768 |
+
std::abs(__bfloat162float(source[index]) *
|
| 769 |
+
inverse_tensor_scale));
|
| 770 |
+
}
|
| 771 |
+
const uint8_t scale_raw = __nv_cvt_float_to_fp8(
|
| 772 |
+
block_amax / kFp4E2M1Max,
|
| 773 |
+
__NV_SATFINITE, __NV_E4M3);
|
| 774 |
+
const float rounded_scale = host_ue4m3_to_float(scale_raw);
|
| 775 |
+
scales[host_scale_offset(
|
| 776 |
+
column, block, scale_layout.inner_dim)] = scale_raw;
|
| 777 |
+
const float inverse_scale =
|
| 778 |
+
rounded_scale == 0.0f ? 0.0f : 1.0f / rounded_scale;
|
| 779 |
+
for (int pair = 0;
|
| 780 |
+
pair < kFp4BlockElements / 2; ++pair) {
|
| 781 |
+
const int row0 = first_row + pair * 2;
|
| 782 |
+
const size_t source_index =
|
| 783 |
+
static_cast<size_t>(row0) +
|
| 784 |
+
static_cast<size_t>(column) * k;
|
| 785 |
+
const float value0 =
|
| 786 |
+
__bfloat162float(source[source_index]) *
|
| 787 |
+
inverse_tensor_scale;
|
| 788 |
+
const float value1 =
|
| 789 |
+
__bfloat162float(source[source_index + 1]) *
|
| 790 |
+
inverse_tensor_scale;
|
| 791 |
+
destination[source_index / 2] =
|
| 792 |
+
__nv_cvt_float2_to_fp4x2(
|
| 793 |
+
make_float2(value0 * inverse_scale,
|
| 794 |
+
value1 * inverse_scale),
|
| 795 |
+
__NV_E2M1, cudaRoundNearest);
|
| 796 |
+
}
|
| 797 |
+
}
|
| 798 |
+
}
|
| 799 |
+
});
|
| 800 |
+
}
|
| 801 |
+
|
| 802 |
+
extern "C" int mage_nvfp4_create_context(
|
| 803 |
+
int cuda_device, void** context) {
|
| 804 |
+
return guarded([&]() {
|
| 805 |
+
if (context == nullptr) {
|
| 806 |
+
fail("context output pointer is null");
|
| 807 |
+
}
|
| 808 |
+
*context = nullptr;
|
| 809 |
+
int device_count = 0;
|
| 810 |
+
CUDA_CHECK(cudaGetDeviceCount(&device_count));
|
| 811 |
+
if (cuda_device < 0 || cuda_device >= device_count) {
|
| 812 |
+
fail("invalid CUDA device index");
|
| 813 |
+
}
|
| 814 |
+
CUDA_CHECK(cudaSetDevice(cuda_device));
|
| 815 |
+
cudaDeviceProp properties{};
|
| 816 |
+
CUDA_CHECK(cudaGetDeviceProperties(&properties, cuda_device));
|
| 817 |
+
if (properties.major != 12 || properties.minor != 0) {
|
| 818 |
+
fail("resident NVFP4 prototype requires sm_120");
|
| 819 |
+
}
|
| 820 |
+
|
| 821 |
+
Context* created = new Context();
|
| 822 |
+
try {
|
| 823 |
+
created->device = cuda_device;
|
| 824 |
+
created->max_grid_x = properties.maxGridSize[0];
|
| 825 |
+
CUBLASLT_CHECK(cublasLtCreate(&created->handle));
|
| 826 |
+
allocate_exact(&created->activation_amax, sizeof(float));
|
| 827 |
+
allocate_exact(&created->activation_scale, sizeof(float));
|
| 828 |
+
allocate_exact(&created->fp4_alpha, sizeof(float));
|
| 829 |
+
allocate_exact(&created->fp4_beta, sizeof(float));
|
| 830 |
+
allocate_exact(&created->workspace, kWorkspaceBytes);
|
| 831 |
+
const float zero = 0.0f;
|
| 832 |
+
CUDA_CHECK(cudaMemcpy(
|
| 833 |
+
created->fp4_beta.pointer, &zero, sizeof(zero),
|
| 834 |
+
cudaMemcpyHostToDevice));
|
| 835 |
+
*context = created;
|
| 836 |
+
} catch (...) {
|
| 837 |
+
delete created;
|
| 838 |
+
throw;
|
| 839 |
+
}
|
| 840 |
+
});
|
| 841 |
+
}
|
| 842 |
+
|
| 843 |
+
extern "C" int mage_nvfp4_destroy_context(void* context) {
|
| 844 |
+
return guarded([&]() {
|
| 845 |
+
if (context == nullptr) {
|
| 846 |
+
return;
|
| 847 |
+
}
|
| 848 |
+
auto* typed = static_cast<Context*>(context);
|
| 849 |
+
{
|
| 850 |
+
std::lock_guard<std::mutex> lock(typed->mutex);
|
| 851 |
+
CUDA_CHECK(cudaSetDevice(typed->device));
|
| 852 |
+
if (typed->stream_bound) {
|
| 853 |
+
CUDA_CHECK(cudaStreamSynchronize(
|
| 854 |
+
reinterpret_cast<cudaStream_t>(typed->bound_stream)));
|
| 855 |
+
}
|
| 856 |
+
}
|
| 857 |
+
delete typed;
|
| 858 |
+
});
|
| 859 |
+
}
|
| 860 |
+
|
| 861 |
+
extern "C" size_t mage_nvfp4_context_reserved_bytes(
|
| 862 |
+
const void* context) {
|
| 863 |
+
if (context == nullptr) {
|
| 864 |
+
return 0;
|
| 865 |
+
}
|
| 866 |
+
const auto* typed = static_cast<const Context*>(context);
|
| 867 |
+
return typed->x_fp4.bytes + typed->x_scales.bytes +
|
| 868 |
+
typed->reduce_a.bytes + typed->reduce_b.bytes +
|
| 869 |
+
typed->activation_amax.bytes +
|
| 870 |
+
typed->activation_scale.bytes +
|
| 871 |
+
typed->fp4_alpha.bytes + typed->fp4_beta.bytes +
|
| 872 |
+
typed->workspace.bytes;
|
| 873 |
+
}
|
| 874 |
+
|
| 875 |
+
extern "C" int mage_nvfp4_linear_forward(
|
| 876 |
+
void* context, const void* input_bf16,
|
| 877 |
+
const void* packed_weight, size_t packed_weight_bytes,
|
| 878 |
+
const void* packed_weight_scales,
|
| 879 |
+
size_t packed_weight_scale_bytes,
|
| 880 |
+
const void* weight_tensor_scale_f32,
|
| 881 |
+
const void* bias_bf16, void* output_bf16,
|
| 882 |
+
int logical_m, int k, int n, uintptr_t stream) {
|
| 883 |
+
return guarded([&]() {
|
| 884 |
+
if (context == nullptr) {
|
| 885 |
+
fail("context is null");
|
| 886 |
+
}
|
| 887 |
+
auto* typed = static_cast<Context*>(context);
|
| 888 |
+
std::lock_guard<std::mutex> lock(typed->mutex);
|
| 889 |
+
forward_impl(
|
| 890 |
+
typed, input_bf16, packed_weight, packed_weight_bytes,
|
| 891 |
+
packed_weight_scales, packed_weight_scale_bytes,
|
| 892 |
+
weight_tensor_scale_f32, bias_bf16, output_bf16,
|
| 893 |
+
logical_m, k, n, stream);
|
| 894 |
+
});
|
| 895 |
+
}
|
runtime/nvfp4_linear.h
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include <stddef.h>
|
| 4 |
+
#include <stdint.h>
|
| 5 |
+
|
| 6 |
+
#if defined(_WIN32)
|
| 7 |
+
#define MAGE_NVFP4_API __declspec(dllexport)
|
| 8 |
+
#else
|
| 9 |
+
#define MAGE_NVFP4_API __attribute__((visibility("default")))
|
| 10 |
+
#endif
|
| 11 |
+
|
| 12 |
+
#ifdef __cplusplus
|
| 13 |
+
extern "C" {
|
| 14 |
+
#endif
|
| 15 |
+
|
| 16 |
+
// The ABI is inference-only. All functions return zero on success and a
|
| 17 |
+
// non-zero value on failure unless their return type documents otherwise.
|
| 18 |
+
MAGE_NVFP4_API int mage_nvfp4_abi_version(void);
|
| 19 |
+
MAGE_NVFP4_API const char* mage_nvfp4_last_error(void);
|
| 20 |
+
|
| 21 |
+
MAGE_NVFP4_API size_t mage_nvfp4_packed_weight_bytes(int n, int k);
|
| 22 |
+
MAGE_NVFP4_API size_t mage_nvfp4_weight_scale_bytes(int n, int k);
|
| 23 |
+
|
| 24 |
+
// Pack a contiguous CPU BF16 nn.Linear weight in logical row-major [N, K]
|
| 25 |
+
// order into cuBLASLt E2M1 payload and tiled VEC16 UE4M3 block scales.
|
| 26 |
+
MAGE_NVFP4_API int mage_nvfp4_pack_weight_bf16(
|
| 27 |
+
const void* weight_bf16, int n, int k,
|
| 28 |
+
void* packed_weight, size_t packed_weight_capacity,
|
| 29 |
+
void* packed_scales, size_t packed_scale_capacity,
|
| 30 |
+
float* tensor_scale);
|
| 31 |
+
|
| 32 |
+
// A context owns one cuBLASLt handle and reusable activation/reduction/
|
| 33 |
+
// workspace buffers. The first forward binds it to one CUDA stream. This
|
| 34 |
+
// initial prototype deliberately rejects a different stream and is not
|
| 35 |
+
// re-entrant.
|
| 36 |
+
MAGE_NVFP4_API int mage_nvfp4_create_context(
|
| 37 |
+
int cuda_device, void** context);
|
| 38 |
+
MAGE_NVFP4_API int mage_nvfp4_destroy_context(void* context);
|
| 39 |
+
MAGE_NVFP4_API size_t mage_nvfp4_context_reserved_bytes(
|
| 40 |
+
const void* context);
|
| 41 |
+
|
| 42 |
+
// Run Y[M,N] = X[M,K] * W[N,K]^T + bias.
|
| 43 |
+
//
|
| 44 |
+
// X, packed weight/scales, tensor scale, bias, and output are CUDA pointers on
|
| 45 |
+
// context's device. X is contiguous BF16 [logical_m, K]. Output is contiguous
|
| 46 |
+
// BF16 [round_up(logical_m, 8), N]. The caller slices the padded rows.
|
| 47 |
+
// bias_bf16 may be null. stream is a cudaStream_t passed as uintptr_t.
|
| 48 |
+
MAGE_NVFP4_API int mage_nvfp4_linear_forward(
|
| 49 |
+
void* context,
|
| 50 |
+
const void* input_bf16,
|
| 51 |
+
const void* packed_weight,
|
| 52 |
+
size_t packed_weight_bytes,
|
| 53 |
+
const void* packed_weight_scales,
|
| 54 |
+
size_t packed_weight_scale_bytes,
|
| 55 |
+
const void* weight_tensor_scale_f32,
|
| 56 |
+
const void* bias_bf16,
|
| 57 |
+
void* output_bf16,
|
| 58 |
+
int logical_m,
|
| 59 |
+
int k,
|
| 60 |
+
int n,
|
| 61 |
+
uintptr_t stream);
|
| 62 |
+
|
| 63 |
+
#ifdef __cplusplus
|
| 64 |
+
}
|
| 65 |
+
#endif
|
| 66 |
+
|
runtime/packed_artifact.py
ADDED
|
@@ -0,0 +1,1068 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import argparse
|
| 6 |
+
import hashlib
|
| 7 |
+
import json
|
| 8 |
+
import math
|
| 9 |
+
import os
|
| 10 |
+
import sys
|
| 11 |
+
from collections import OrderedDict
|
| 12 |
+
from dataclasses import asdict, dataclass
|
| 13 |
+
from datetime import datetime, timezone
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
from typing import Callable, Iterable
|
| 16 |
+
|
| 17 |
+
import safetensors
|
| 18 |
+
import torch
|
| 19 |
+
import torch.nn as nn
|
| 20 |
+
from safetensors import safe_open
|
| 21 |
+
from safetensors.torch import save_file
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
FORMAT_VERSION = 1
|
| 25 |
+
ARTIFACT_KIND = "mage_flow_transformer_mlp_nvfp4_resident_v1"
|
| 26 |
+
CANONICAL_SAFETENSORS_METADATA = {
|
| 27 |
+
"mage_nvfp4_contract": (
|
| 28 |
+
f"{ARTIFACT_KIND};format_version={FORMAT_VERSION}"
|
| 29 |
+
)
|
| 30 |
+
}
|
| 31 |
+
LEGACY_SAFETENSORS_METADATA = {
|
| 32 |
+
"artifact_kind": ARTIFACT_KIND,
|
| 33 |
+
"format_version": str(FORMAT_VERSION),
|
| 34 |
+
}
|
| 35 |
+
FP4_BLOCK_ELEMENTS = 16
|
| 36 |
+
SCALE_TILE_OUTER = 128
|
| 37 |
+
SCALE_TILE_INNER = 4
|
| 38 |
+
FP4_E2M1_MAX = 6.0
|
| 39 |
+
FP4_TENSOR_SCALE_MAX = 448.0
|
| 40 |
+
NVFP4_TENSOR_SCALE_DENOMINATOR = FP4_E2M1_MAX * FP4_TENSOR_SCALE_MAX
|
| 41 |
+
TARGET_DEPTH = 12
|
| 42 |
+
|
| 43 |
+
RELEASE_ROOT = Path(__file__).resolve().parents[1]
|
| 44 |
+
PROJECT_ROOT = RELEASE_ROOT
|
| 45 |
+
MAGE_ROOT = RELEASE_ROOT / "vendor"
|
| 46 |
+
RESIDENT_PYTHON_ROOT = RELEASE_ROOT / "runtime"
|
| 47 |
+
RESIDENT_SOURCE = RESIDENT_PYTHON_ROOT / "nvfp4_linear.cu"
|
| 48 |
+
RESIDENT_LIBRARY = RESIDENT_PYTHON_ROOT / "libmage_nvfp4_linear.so"
|
| 49 |
+
ARTIFACT_SCRIPT_PATH = Path(__file__).resolve()
|
| 50 |
+
_NATIVE_PACKER = None
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class PackedArtifactError(RuntimeError):
|
| 54 |
+
pass
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
@dataclass(frozen=True)
|
| 58 |
+
class ScaleLayout:
|
| 59 |
+
inner_dim: int
|
| 60 |
+
outer_tiles: int
|
| 61 |
+
bytes: int
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
@dataclass(frozen=True)
|
| 65 |
+
class TargetSpec:
|
| 66 |
+
module_key: str
|
| 67 |
+
weight_key: str
|
| 68 |
+
bias_key: str
|
| 69 |
+
artifact_weight_key: str
|
| 70 |
+
artifact_scale_key: str
|
| 71 |
+
artifact_tensor_scale_key: str
|
| 72 |
+
artifact_bias_key: str
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def fail(message: str) -> None:
|
| 76 |
+
raise PackedArtifactError(message)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def round_up(value: int, multiple: int) -> int:
|
| 80 |
+
return ((value + multiple - 1) // multiple) * multiple
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def sha256_file(path: Path) -> str:
|
| 84 |
+
digest = hashlib.sha256()
|
| 85 |
+
with path.open("rb") as handle:
|
| 86 |
+
while True:
|
| 87 |
+
chunk = handle.read(1 << 20)
|
| 88 |
+
if not chunk:
|
| 89 |
+
break
|
| 90 |
+
digest.update(chunk)
|
| 91 |
+
return digest.hexdigest()
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def sha256_bytes(data: bytes) -> str:
|
| 95 |
+
return hashlib.sha256(data).hexdigest()
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def tensor_bytes(tensor: torch.Tensor) -> bytes:
|
| 99 |
+
if not tensor.is_contiguous():
|
| 100 |
+
tensor = tensor.contiguous()
|
| 101 |
+
return tensor.view(torch.uint8).cpu().numpy().tobytes()
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def sha256_tensor(tensor: torch.Tensor) -> str:
|
| 105 |
+
return sha256_bytes(tensor_bytes(tensor))
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def fsync_directory(path: Path) -> None:
|
| 109 |
+
descriptor = os.open(path, os.O_RDONLY | getattr(os, "O_DIRECTORY", 0))
|
| 110 |
+
try:
|
| 111 |
+
os.fsync(descriptor)
|
| 112 |
+
finally:
|
| 113 |
+
os.close(descriptor)
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def fsync_file(path: Path) -> None:
|
| 117 |
+
descriptor = os.open(path, os.O_RDONLY)
|
| 118 |
+
try:
|
| 119 |
+
os.fsync(descriptor)
|
| 120 |
+
finally:
|
| 121 |
+
os.close(descriptor)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def write_bytes_once(path: Path, payload: bytes) -> None:
|
| 125 |
+
with path.open("xb") as handle:
|
| 126 |
+
handle.write(payload)
|
| 127 |
+
handle.flush()
|
| 128 |
+
os.fsync(handle.fileno())
|
| 129 |
+
fsync_directory(path.parent)
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def host_scale_offset(outer: int, inner_scale: int, scale_inner_dim: int) -> int:
|
| 133 |
+
outer_tile = outer // SCALE_TILE_OUTER
|
| 134 |
+
local_outer = outer % SCALE_TILE_OUTER
|
| 135 |
+
local_inner = inner_scale % SCALE_TILE_INNER
|
| 136 |
+
inner_tile_start = inner_scale - local_inner
|
| 137 |
+
tile_base = (inner_tile_start + outer_tile * scale_inner_dim) * SCALE_TILE_OUTER
|
| 138 |
+
return tile_base + (local_outer % 32) * 16 + (local_outer // 32) * 4 + local_inner
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def make_scale_layout(rows_k: int, outer_columns: int) -> ScaleLayout:
|
| 142 |
+
if rows_k <= 0 or outer_columns <= 0:
|
| 143 |
+
fail("scale layout requires positive rows_k and outer_columns")
|
| 144 |
+
if rows_k % FP4_BLOCK_ELEMENTS:
|
| 145 |
+
fail(
|
| 146 |
+
f"scale layout requires K divisible by {FP4_BLOCK_ELEMENTS}; "
|
| 147 |
+
f"got K={rows_k}"
|
| 148 |
+
)
|
| 149 |
+
inner_dim = round_up(rows_k // FP4_BLOCK_ELEMENTS, SCALE_TILE_INNER)
|
| 150 |
+
outer_tiles = (outer_columns + SCALE_TILE_OUTER - 1) // SCALE_TILE_OUTER
|
| 151 |
+
return ScaleLayout(
|
| 152 |
+
inner_dim=inner_dim,
|
| 153 |
+
outer_tiles=outer_tiles,
|
| 154 |
+
bytes=outer_tiles * inner_dim * SCALE_TILE_OUTER,
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def build_target_specs(depth: int) -> list[TargetSpec]:
|
| 159 |
+
if depth != TARGET_DEPTH:
|
| 160 |
+
fail(
|
| 161 |
+
f"this artifact format is pinned to exactly {TARGET_DEPTH} transformer blocks; "
|
| 162 |
+
f"config reported depth={depth}"
|
| 163 |
+
)
|
| 164 |
+
specs: list[TargetSpec] = []
|
| 165 |
+
suffixes = (
|
| 166 |
+
"img_mlp.net.0.proj",
|
| 167 |
+
"img_mlp.net.2",
|
| 168 |
+
"txt_mlp.net.0.proj",
|
| 169 |
+
"txt_mlp.net.2",
|
| 170 |
+
)
|
| 171 |
+
for index in range(depth):
|
| 172 |
+
for suffix in suffixes:
|
| 173 |
+
module_key = f"transformer_blocks.{index}.{suffix}"
|
| 174 |
+
specs.append(
|
| 175 |
+
TargetSpec(
|
| 176 |
+
module_key=module_key,
|
| 177 |
+
weight_key=f"{module_key}.weight",
|
| 178 |
+
bias_key=f"{module_key}.bias",
|
| 179 |
+
artifact_weight_key=f"targets.{module_key}.packed_weight_e2m1",
|
| 180 |
+
artifact_scale_key=f"targets.{module_key}.packed_scales_ue4m3",
|
| 181 |
+
artifact_tensor_scale_key=f"targets.{module_key}.weight_tensor_scale",
|
| 182 |
+
artifact_bias_key=f"targets.{module_key}.bias_bf16",
|
| 183 |
+
)
|
| 184 |
+
)
|
| 185 |
+
return specs
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def decode_fp4_e2m1(raw: int) -> float:
|
| 189 |
+
sign = -1.0 if (raw & 0x8) else 1.0
|
| 190 |
+
magnitude = raw & 0x7
|
| 191 |
+
table = (
|
| 192 |
+
0.0,
|
| 193 |
+
0.5,
|
| 194 |
+
1.0,
|
| 195 |
+
1.5,
|
| 196 |
+
2.0,
|
| 197 |
+
3.0,
|
| 198 |
+
4.0,
|
| 199 |
+
6.0,
|
| 200 |
+
)
|
| 201 |
+
return sign * table[magnitude]
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def encode_fp4_e2m1(value: float) -> int:
|
| 205 |
+
candidates = [decode_fp4_e2m1(code) for code in range(16)]
|
| 206 |
+
best_code = 0
|
| 207 |
+
best_error = math.inf
|
| 208 |
+
for code, candidate in enumerate(candidates):
|
| 209 |
+
error = abs(candidate - value)
|
| 210 |
+
if error < best_error or (error == best_error and (code & 1) == 0 and (best_code & 1) == 1):
|
| 211 |
+
best_error = error
|
| 212 |
+
best_code = code
|
| 213 |
+
return best_code
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
def decode_fp8_e4m3(raw: int) -> float:
|
| 217 |
+
sign = -1.0 if (raw & 0x80) else 1.0
|
| 218 |
+
exponent = (raw >> 3) & 0x0F
|
| 219 |
+
mantissa = raw & 0x07
|
| 220 |
+
if exponent == 0:
|
| 221 |
+
if mantissa == 0:
|
| 222 |
+
return 0.0 * sign
|
| 223 |
+
return sign * (mantissa / 8.0) * (2.0 ** -6)
|
| 224 |
+
if exponent == 0x0F and mantissa == 0x07:
|
| 225 |
+
return math.nan
|
| 226 |
+
return sign * (1.0 + mantissa / 8.0) * (2.0 ** (exponent - 7))
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def _build_positive_e4m3_table() -> list[tuple[int, float]]:
|
| 230 |
+
table: list[tuple[int, float]] = []
|
| 231 |
+
for raw in range(0x80):
|
| 232 |
+
value = decode_fp8_e4m3(raw)
|
| 233 |
+
if math.isnan(value) or value < 0.0:
|
| 234 |
+
continue
|
| 235 |
+
table.append((raw, value))
|
| 236 |
+
table.sort(key=lambda item: (item[1], item[0]))
|
| 237 |
+
return table
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
POSITIVE_E4M3_TABLE = _build_positive_e4m3_table()
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
def encode_fp8_e4m3_satfinite(value: float) -> int:
|
| 244 |
+
if value <= 0.0:
|
| 245 |
+
return 0
|
| 246 |
+
finite_values = [item for item in POSITIVE_E4M3_TABLE if item[1] <= FP4_TENSOR_SCALE_MAX]
|
| 247 |
+
best_raw = finite_values[-1][0]
|
| 248 |
+
best_error = math.inf
|
| 249 |
+
for raw, candidate in finite_values:
|
| 250 |
+
error = abs(candidate - value)
|
| 251 |
+
if error < best_error or (error == best_error and (raw & 1) == 0 and (best_raw & 1) == 1):
|
| 252 |
+
best_error = error
|
| 253 |
+
best_raw = raw
|
| 254 |
+
return best_raw
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
def host_tensor_scale_from_amax(amax: float) -> float:
|
| 258 |
+
return 1.0 if amax == 0.0 else amax / NVFP4_TENSOR_SCALE_DENOMINATOR
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
def native_packer():
|
| 262 |
+
global _NATIVE_PACKER
|
| 263 |
+
if _NATIVE_PACKER is None:
|
| 264 |
+
if str(RESIDENT_PYTHON_ROOT) not in sys.path:
|
| 265 |
+
sys.path.insert(0, str(RESIDENT_PYTHON_ROOT))
|
| 266 |
+
from packed_nvfp4_linear import NativeNvfp4Library
|
| 267 |
+
|
| 268 |
+
_NATIVE_PACKER = NativeNvfp4Library(RESIDENT_LIBRARY)
|
| 269 |
+
return _NATIVE_PACKER
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
def pack_weight_tensor(weight_nk_bf16: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, float]:
|
| 273 |
+
if weight_nk_bf16.dtype != torch.bfloat16 or weight_nk_bf16.device.type != "cpu":
|
| 274 |
+
fail("weight packer expects a CPU bfloat16 tensor")
|
| 275 |
+
if weight_nk_bf16.ndim != 2:
|
| 276 |
+
fail("weight packer expects a 2D [N,K] weight tensor")
|
| 277 |
+
if not weight_nk_bf16.is_contiguous():
|
| 278 |
+
weight_nk_bf16 = weight_nk_bf16.contiguous()
|
| 279 |
+
|
| 280 |
+
columns_n, rows_k = weight_nk_bf16.shape
|
| 281 |
+
if rows_k % 32 != 0 or columns_n % 8 != 0:
|
| 282 |
+
fail(
|
| 283 |
+
f"native NVFP4 packing requires K%32==0 and N%8==0; got N={columns_n} K={rows_k}"
|
| 284 |
+
)
|
| 285 |
+
|
| 286 |
+
amax = float(weight_nk_bf16.float().abs().max().item())
|
| 287 |
+
packed_fp4, packed_scales, tensor_scale = native_packer().pack_weight(
|
| 288 |
+
weight_nk_bf16
|
| 289 |
+
)
|
| 290 |
+
return packed_fp4, packed_scales, tensor_scale.reshape(1), amax
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
def load_transformer_config(source_repo: Path) -> dict:
|
| 294 |
+
config_path = source_repo / "transformer" / "config.json"
|
| 295 |
+
if not config_path.exists():
|
| 296 |
+
fail(f"missing transformer config: {config_path}")
|
| 297 |
+
return json.loads(config_path.read_text())
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
def source_transformer_checkpoint(source_repo: Path) -> Path:
|
| 301 |
+
checkpoint = source_repo / "transformer" / "diffusion_pytorch_model.safetensors"
|
| 302 |
+
if not checkpoint.exists():
|
| 303 |
+
fail(f"missing transformer checkpoint: {checkpoint}")
|
| 304 |
+
return checkpoint
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
def import_mage_transformer_symbols() -> tuple[type[nn.Module], object]:
|
| 308 |
+
if str(MAGE_ROOT) not in sys.path:
|
| 309 |
+
sys.path.insert(0, str(MAGE_ROOT))
|
| 310 |
+
try:
|
| 311 |
+
from mage_flow.models.mage_flow import MageFlow, MageFlowParams
|
| 312 |
+
except Exception as exc:
|
| 313 |
+
fail(f"failed to import local Mage transformer sources from {MAGE_ROOT}: {exc}")
|
| 314 |
+
return MageFlow, MageFlowParams
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
class PackedNvfp4LinearArtifactModule(nn.Module):
|
| 318 |
+
def __init__(
|
| 319 |
+
self,
|
| 320 |
+
in_features: int,
|
| 321 |
+
out_features: int,
|
| 322 |
+
packed_weight_e2m1: torch.Tensor,
|
| 323 |
+
packed_scales_ue4m3: torch.Tensor,
|
| 324 |
+
weight_tensor_scale: torch.Tensor,
|
| 325 |
+
bias_bf16: torch.Tensor | None,
|
| 326 |
+
) -> None:
|
| 327 |
+
super().__init__()
|
| 328 |
+
self.in_features = int(in_features)
|
| 329 |
+
self.out_features = int(out_features)
|
| 330 |
+
self.register_buffer("packed_weight_e2m1", packed_weight_e2m1.contiguous())
|
| 331 |
+
self.register_buffer("packed_scales_ue4m3", packed_scales_ue4m3.contiguous())
|
| 332 |
+
self.register_buffer("weight_tensor_scale", weight_tensor_scale.contiguous())
|
| 333 |
+
if bias_bf16 is None:
|
| 334 |
+
self.bias_bf16 = None
|
| 335 |
+
else:
|
| 336 |
+
self.register_buffer("bias_bf16", bias_bf16.contiguous())
|
| 337 |
+
|
| 338 |
+
def forward(self, inputs: torch.Tensor) -> torch.Tensor:
|
| 339 |
+
raise RuntimeError(
|
| 340 |
+
"PackedNvfp4LinearArtifactModule is an artifact-only placeholder. "
|
| 341 |
+
"Attach the resident CUDA runtime before calling forward()."
|
| 342 |
+
)
|
| 343 |
+
|
| 344 |
+
def extra_repr(self) -> str:
|
| 345 |
+
return (
|
| 346 |
+
f"in_features={self.in_features}, out_features={self.out_features}, "
|
| 347 |
+
f"packed_weight_bytes={self.packed_weight_e2m1.numel()}, "
|
| 348 |
+
f"packed_scale_bytes={self.packed_scales_ue4m3.numel()}, "
|
| 349 |
+
f"has_bias={self.bias_bf16 is not None}"
|
| 350 |
+
)
|
| 351 |
+
|
| 352 |
+
|
| 353 |
+
def instantiate_mage_transformer_on_meta(source_repo: Path) -> nn.Module:
|
| 354 |
+
config = load_transformer_config(source_repo)
|
| 355 |
+
MageFlow, MageFlowParams = import_mage_transformer_symbols()
|
| 356 |
+
structure = {
|
| 357 |
+
key: value
|
| 358 |
+
for key, value in config.items()
|
| 359 |
+
if key
|
| 360 |
+
not in {
|
| 361 |
+
"_class_name",
|
| 362 |
+
"txt_max_length",
|
| 363 |
+
"max_sequence_length",
|
| 364 |
+
"param_dtype",
|
| 365 |
+
"packing",
|
| 366 |
+
"schedule_mode",
|
| 367 |
+
"static_shift",
|
| 368 |
+
"use_time_shift",
|
| 369 |
+
"rope_type",
|
| 370 |
+
"apply_text_rotary_emb",
|
| 371 |
+
"mlp_ratio",
|
| 372 |
+
"depth_single_blocks",
|
| 373 |
+
"theta",
|
| 374 |
+
"qkv_bias",
|
| 375 |
+
"guidance_embed",
|
| 376 |
+
"vec_in_dim",
|
| 377 |
+
"vec_type",
|
| 378 |
+
"time_type",
|
| 379 |
+
"double_block_type",
|
| 380 |
+
"quantization_config",
|
| 381 |
+
}
|
| 382 |
+
}
|
| 383 |
+
with torch.device("meta"):
|
| 384 |
+
model = MageFlow(MageFlowParams(**structure))
|
| 385 |
+
return model
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
def unregistered_meta_tensor_attribute_names(model: nn.Module) -> list[str]:
|
| 389 |
+
"""Find direct tensor attributes that PyTorch's parameter/buffer walk misses."""
|
| 390 |
+
names: list[str] = []
|
| 391 |
+
for module_name, module in model.named_modules():
|
| 392 |
+
registered_names = set(module._parameters) | set(module._buffers)
|
| 393 |
+
for attribute_name, value in vars(module).items():
|
| 394 |
+
if attribute_name in registered_names:
|
| 395 |
+
continue
|
| 396 |
+
if isinstance(value, torch.Tensor) and value.is_meta:
|
| 397 |
+
prefix = f"{module_name}." if module_name else ""
|
| 398 |
+
names.append(f"{prefix}{attribute_name}")
|
| 399 |
+
return sorted(names)
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
def materialize_mage_rope_tensor_attributes(model: nn.Module) -> list[str]:
|
| 403 |
+
"""Rebuild Mage's intentionally unregistered complex RoPE tensors on CPU."""
|
| 404 |
+
before = unregistered_meta_tensor_attribute_names(model)
|
| 405 |
+
expected = ["pos_embed.neg_freqs", "pos_embed.pos_freqs"]
|
| 406 |
+
if before != expected:
|
| 407 |
+
fail(
|
| 408 |
+
"unexpected unregistered meta tensor attributes before RoPE "
|
| 409 |
+
f"materialization: {before}"
|
| 410 |
+
)
|
| 411 |
+
|
| 412 |
+
rope = model.get_submodule("pos_embed")
|
| 413 |
+
rope_type = type(rope)
|
| 414 |
+
with torch.device("cpu"):
|
| 415 |
+
materialized = rope_type(
|
| 416 |
+
theta=rope.theta,
|
| 417 |
+
axes_dim=list(rope.axes_dim),
|
| 418 |
+
scale_rope=rope.scale_rope,
|
| 419 |
+
)
|
| 420 |
+
rope.pos_freqs = materialized.pos_freqs
|
| 421 |
+
rope.neg_freqs = materialized.neg_freqs
|
| 422 |
+
rope.video_freq_cache = {}
|
| 423 |
+
|
| 424 |
+
remaining = unregistered_meta_tensor_attribute_names(model)
|
| 425 |
+
if remaining:
|
| 426 |
+
fail(
|
| 427 |
+
"unresolved unregistered meta tensor attributes after RoPE "
|
| 428 |
+
f"materialization: {remaining}"
|
| 429 |
+
)
|
| 430 |
+
return before
|
| 431 |
+
|
| 432 |
+
|
| 433 |
+
def set_child_module(root: nn.Module, dotted_path: str, module: nn.Module) -> None:
|
| 434 |
+
parent_path, _, child_name = dotted_path.rpartition(".")
|
| 435 |
+
parent = root.get_submodule(parent_path) if parent_path else root
|
| 436 |
+
if child_name.isdigit() and isinstance(parent, (nn.Sequential, nn.ModuleList)):
|
| 437 |
+
parent[int(child_name)] = module
|
| 438 |
+
else:
|
| 439 |
+
setattr(parent, child_name, module)
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
def replace_targets_with_artifact_modules(
|
| 443 |
+
model: nn.Module,
|
| 444 |
+
artifact_path: Path,
|
| 445 |
+
target_specs: Iterable[TargetSpec],
|
| 446 |
+
) -> None:
|
| 447 |
+
with safe_open(artifact_path, framework="pt", device="cpu") as handle:
|
| 448 |
+
for spec in target_specs:
|
| 449 |
+
packed_weight = handle.get_tensor(spec.artifact_weight_key)
|
| 450 |
+
packed_scales = handle.get_tensor(spec.artifact_scale_key)
|
| 451 |
+
weight_tensor_scale = handle.get_tensor(spec.artifact_tensor_scale_key)
|
| 452 |
+
bias = handle.get_tensor(spec.artifact_bias_key)
|
| 453 |
+
original = model.get_submodule(spec.module_key)
|
| 454 |
+
if not isinstance(original, nn.Linear):
|
| 455 |
+
fail(f"expected target module {spec.module_key} to be nn.Linear")
|
| 456 |
+
replacement = PackedNvfp4LinearArtifactModule(
|
| 457 |
+
in_features=int(original.in_features),
|
| 458 |
+
out_features=int(original.out_features),
|
| 459 |
+
packed_weight_e2m1=packed_weight,
|
| 460 |
+
packed_scales_ue4m3=packed_scales,
|
| 461 |
+
weight_tensor_scale=weight_tensor_scale,
|
| 462 |
+
bias_bf16=bias,
|
| 463 |
+
)
|
| 464 |
+
set_child_module(model, spec.module_key, replacement)
|
| 465 |
+
|
| 466 |
+
|
| 467 |
+
def replace_targets_with_resident_modules(
|
| 468 |
+
model: nn.Module,
|
| 469 |
+
artifact_path: Path,
|
| 470 |
+
target_specs: Iterable[TargetSpec],
|
| 471 |
+
device: torch.device,
|
| 472 |
+
) -> None:
|
| 473 |
+
if device.type != "cuda":
|
| 474 |
+
fail("resident runtime modules require a CUDA destination")
|
| 475 |
+
if str(RESIDENT_PYTHON_ROOT) not in sys.path:
|
| 476 |
+
sys.path.insert(0, str(RESIDENT_PYTHON_ROOT))
|
| 477 |
+
from torch_ops import PackedNvfp4LinearOp
|
| 478 |
+
|
| 479 |
+
_replace_targets_with_registered_modules(
|
| 480 |
+
model,
|
| 481 |
+
artifact_path,
|
| 482 |
+
target_specs,
|
| 483 |
+
device,
|
| 484 |
+
PackedNvfp4LinearOp,
|
| 485 |
+
)
|
| 486 |
+
|
| 487 |
+
|
| 488 |
+
def replace_targets_with_native_resident_modules(
|
| 489 |
+
model: nn.Module,
|
| 490 |
+
artifact_path: Path,
|
| 491 |
+
target_specs: Iterable[TargetSpec],
|
| 492 |
+
device: torch.device,
|
| 493 |
+
) -> None:
|
| 494 |
+
if device.type != "cuda":
|
| 495 |
+
fail("native resident runtime modules require a CUDA destination")
|
| 496 |
+
if str(RESIDENT_PYTHON_ROOT) not in sys.path:
|
| 497 |
+
sys.path.insert(0, str(RESIDENT_PYTHON_ROOT))
|
| 498 |
+
from torch_ops_native import (
|
| 499 |
+
PackedNvfp4LinearNativeOp,
|
| 500 |
+
initialize_native_sm120_op,
|
| 501 |
+
)
|
| 502 |
+
|
| 503 |
+
if not initialize_native_sm120_op(allow_python_schema_fallback=False):
|
| 504 |
+
fail("compiled native resident torch op did not load")
|
| 505 |
+
_replace_targets_with_registered_modules(
|
| 506 |
+
model,
|
| 507 |
+
artifact_path,
|
| 508 |
+
target_specs,
|
| 509 |
+
device,
|
| 510 |
+
PackedNvfp4LinearNativeOp,
|
| 511 |
+
)
|
| 512 |
+
|
| 513 |
+
|
| 514 |
+
def _replace_targets_with_registered_modules(
|
| 515 |
+
model: nn.Module,
|
| 516 |
+
artifact_path: Path,
|
| 517 |
+
target_specs: Iterable[TargetSpec],
|
| 518 |
+
device: torch.device,
|
| 519 |
+
module_cls: type[nn.Module],
|
| 520 |
+
) -> None:
|
| 521 |
+
with safe_open(artifact_path, framework="pt", device="cpu") as handle:
|
| 522 |
+
for spec in target_specs:
|
| 523 |
+
original = model.get_submodule(spec.module_key)
|
| 524 |
+
if not isinstance(original, nn.Linear):
|
| 525 |
+
fail(f"expected target module {spec.module_key} to be nn.Linear")
|
| 526 |
+
replacement = module_cls(
|
| 527 |
+
in_features=int(original.in_features),
|
| 528 |
+
out_features=int(original.out_features),
|
| 529 |
+
packed_weight=handle.get_tensor(spec.artifact_weight_key).to(device),
|
| 530 |
+
weight_scales=handle.get_tensor(spec.artifact_scale_key).to(device),
|
| 531 |
+
weight_scale=handle.get_tensor(
|
| 532 |
+
spec.artifact_tensor_scale_key
|
| 533 |
+
).to(device),
|
| 534 |
+
bias=handle.get_tensor(spec.artifact_bias_key).to(device),
|
| 535 |
+
)
|
| 536 |
+
set_child_module(model, spec.module_key, replacement)
|
| 537 |
+
|
| 538 |
+
|
| 539 |
+
def assign_tensor_by_name(model: nn.Module, key: str, tensor: torch.Tensor) -> None:
|
| 540 |
+
if "." not in key:
|
| 541 |
+
parent = model
|
| 542 |
+
leaf = key
|
| 543 |
+
else:
|
| 544 |
+
parent_path, _, leaf = key.rpartition(".")
|
| 545 |
+
parent = model.get_submodule(parent_path)
|
| 546 |
+
if leaf in parent._parameters:
|
| 547 |
+
requires_grad = parent._parameters[leaf].requires_grad
|
| 548 |
+
parent._parameters[leaf] = nn.Parameter(tensor, requires_grad=requires_grad)
|
| 549 |
+
return
|
| 550 |
+
if leaf in parent._buffers:
|
| 551 |
+
parent._buffers[leaf] = tensor
|
| 552 |
+
return
|
| 553 |
+
fail(f"destination key {key} was neither a parameter nor a buffer")
|
| 554 |
+
|
| 555 |
+
|
| 556 |
+
def pack_artifact(source_repo: Path, output_dir: Path) -> Path:
|
| 557 |
+
source_repo = source_repo.resolve()
|
| 558 |
+
output_dir = output_dir.resolve()
|
| 559 |
+
if output_dir.exists():
|
| 560 |
+
fail(f"output directory already exists: {output_dir}")
|
| 561 |
+
output_dir.mkdir(parents=True, exist_ok=False)
|
| 562 |
+
|
| 563 |
+
config = load_transformer_config(source_repo)
|
| 564 |
+
checkpoint_path = source_transformer_checkpoint(source_repo)
|
| 565 |
+
target_specs = build_target_specs(int(config["depth"]))
|
| 566 |
+
target_weight_keys = {spec.weight_key for spec in target_specs}
|
| 567 |
+
target_bias_keys = {spec.bias_key for spec in target_specs}
|
| 568 |
+
target_keys = target_weight_keys | target_bias_keys
|
| 569 |
+
|
| 570 |
+
artifact_tensors: OrderedDict[str, torch.Tensor] = OrderedDict()
|
| 571 |
+
target_metadata: list[dict] = []
|
| 572 |
+
|
| 573 |
+
with safe_open(checkpoint_path, framework="pt", device="cpu") as handle:
|
| 574 |
+
source_keys = list(handle.keys())
|
| 575 |
+
source_key_set = set(source_keys)
|
| 576 |
+
missing = sorted(target_keys - source_key_set)
|
| 577 |
+
if missing:
|
| 578 |
+
fail(f"source checkpoint is missing {len(missing)} target tensors, first={missing[0]}")
|
| 579 |
+
|
| 580 |
+
for spec in target_specs:
|
| 581 |
+
weight = handle.get_tensor(spec.weight_key)
|
| 582 |
+
bias = handle.get_tensor(spec.bias_key)
|
| 583 |
+
if weight.dtype != torch.bfloat16:
|
| 584 |
+
fail(f"{spec.weight_key} expected bfloat16, found {weight.dtype}")
|
| 585 |
+
if bias.dtype != torch.bfloat16:
|
| 586 |
+
fail(f"{spec.bias_key} expected bfloat16, found {bias.dtype}")
|
| 587 |
+
packed_weight, packed_scales, weight_tensor_scale, global_amax = pack_weight_tensor(weight)
|
| 588 |
+
scale_layout = make_scale_layout(weight.shape[1], weight.shape[0])
|
| 589 |
+
artifact_tensors[spec.artifact_bias_key] = bias.contiguous()
|
| 590 |
+
artifact_tensors[spec.artifact_scale_key] = packed_scales
|
| 591 |
+
artifact_tensors[spec.artifact_tensor_scale_key] = weight_tensor_scale
|
| 592 |
+
artifact_tensors[spec.artifact_weight_key] = packed_weight
|
| 593 |
+
target_metadata.append(
|
| 594 |
+
{
|
| 595 |
+
"module_key": spec.module_key,
|
| 596 |
+
"weight_key": spec.weight_key,
|
| 597 |
+
"bias_key": spec.bias_key,
|
| 598 |
+
"weight_shape": list(weight.shape),
|
| 599 |
+
"bias_shape": list(bias.shape),
|
| 600 |
+
"weight_tensor_scale_key": spec.artifact_tensor_scale_key,
|
| 601 |
+
"artifact_weight_key": spec.artifact_weight_key,
|
| 602 |
+
"artifact_scale_key": spec.artifact_scale_key,
|
| 603 |
+
"artifact_bias_key": spec.artifact_bias_key,
|
| 604 |
+
"weight_tensor_scale": float(weight_tensor_scale.item()),
|
| 605 |
+
"weight_global_amax": global_amax,
|
| 606 |
+
"packed_weight_bytes": int(packed_weight.numel()),
|
| 607 |
+
"packed_scale_bytes": int(packed_scales.numel()),
|
| 608 |
+
"scale_layout": asdict(scale_layout),
|
| 609 |
+
"source_weight_sha256": sha256_tensor(weight),
|
| 610 |
+
"source_bias_sha256": sha256_tensor(bias),
|
| 611 |
+
}
|
| 612 |
+
)
|
| 613 |
+
|
| 614 |
+
non_target_keys = sorted(set(source_keys) - target_keys)
|
| 615 |
+
artifact_path = output_dir / "packed_transformer.safetensors"
|
| 616 |
+
save_file(
|
| 617 |
+
OrderedDict(sorted(artifact_tensors.items())),
|
| 618 |
+
artifact_path,
|
| 619 |
+
metadata=CANONICAL_SAFETENSORS_METADATA,
|
| 620 |
+
)
|
| 621 |
+
fsync_file(artifact_path)
|
| 622 |
+
fsync_directory(output_dir)
|
| 623 |
+
|
| 624 |
+
library_hashes = {
|
| 625 |
+
"artifact_script_sha256": sha256_file(ARTIFACT_SCRIPT_PATH),
|
| 626 |
+
"resident_source_sha256": sha256_file(RESIDENT_SOURCE),
|
| 627 |
+
"resident_library_sha256": sha256_file(RESIDENT_LIBRARY),
|
| 628 |
+
"mage_flow_py_sha256": sha256_file(MAGE_ROOT / "mage_flow" / "models" / "mage_flow.py"),
|
| 629 |
+
"mage_layers_py_sha256": sha256_file(MAGE_ROOT / "mage_flow" / "models" / "modules" / "mage_layers.py"),
|
| 630 |
+
"pipeline_py_sha256": sha256_file(MAGE_ROOT / "mage_flow" / "pipeline.py"),
|
| 631 |
+
}
|
| 632 |
+
|
| 633 |
+
metadata = OrderedDict(
|
| 634 |
+
(
|
| 635 |
+
("format_version", FORMAT_VERSION),
|
| 636 |
+
("artifact_kind", ARTIFACT_KIND),
|
| 637 |
+
("created_utc", datetime.now(timezone.utc).isoformat()),
|
| 638 |
+
(
|
| 639 |
+
"container",
|
| 640 |
+
{
|
| 641 |
+
"format": "safetensors",
|
| 642 |
+
"header_metadata": CANONICAL_SAFETENSORS_METADATA,
|
| 643 |
+
"header_encoding": (
|
| 644 |
+
"single deterministic contract key; legacy two-key "
|
| 645 |
+
"draft headers remain readable"
|
| 646 |
+
),
|
| 647 |
+
},
|
| 648 |
+
),
|
| 649 |
+
(
|
| 650 |
+
"source",
|
| 651 |
+
OrderedDict(
|
| 652 |
+
(
|
| 653 |
+
("transformer_config_path", str(source_repo / "transformer" / "config.json")),
|
| 654 |
+
("transformer_checkpoint_path", str(checkpoint_path)),
|
| 655 |
+
("transformer_config_sha256", sha256_file(source_repo / "transformer" / "config.json")),
|
| 656 |
+
("transformer_checkpoint_sha256", sha256_file(checkpoint_path)),
|
| 657 |
+
)
|
| 658 |
+
),
|
| 659 |
+
),
|
| 660 |
+
("library_hashes", library_hashes),
|
| 661 |
+
(
|
| 662 |
+
"environment",
|
| 663 |
+
{
|
| 664 |
+
"python_version": sys.version,
|
| 665 |
+
"torch_version": torch.__version__,
|
| 666 |
+
"safetensors_version": safetensors.__version__,
|
| 667 |
+
},
|
| 668 |
+
),
|
| 669 |
+
(
|
| 670 |
+
"model",
|
| 671 |
+
{
|
| 672 |
+
"depth": int(config["depth"]),
|
| 673 |
+
"hidden_size": int(config["hidden_size"]),
|
| 674 |
+
"num_heads": int(config["num_heads"]),
|
| 675 |
+
"context_in_dim": int(config["context_in_dim"]),
|
| 676 |
+
"in_channels": int(config["in_channels"]),
|
| 677 |
+
"out_channels": int(config["out_channels"]),
|
| 678 |
+
"patch_size": int(config["patch_size"]),
|
| 679 |
+
},
|
| 680 |
+
),
|
| 681 |
+
(
|
| 682 |
+
"quantization",
|
| 683 |
+
{
|
| 684 |
+
"format": "nvfp4_two_level",
|
| 685 |
+
"block_elements": FP4_BLOCK_ELEMENTS,
|
| 686 |
+
"scale_tile_outer": SCALE_TILE_OUTER,
|
| 687 |
+
"scale_tile_inner": SCALE_TILE_INNER,
|
| 688 |
+
"bias_policy": "artifact_bfloat16",
|
| 689 |
+
},
|
| 690 |
+
),
|
| 691 |
+
("targets", target_metadata),
|
| 692 |
+
("non_target_keys", non_target_keys),
|
| 693 |
+
)
|
| 694 |
+
)
|
| 695 |
+
write_bytes_once(
|
| 696 |
+
output_dir / "metadata.json",
|
| 697 |
+
(json.dumps(metadata, indent=2, sort_keys=False) + "\n").encode("utf-8"),
|
| 698 |
+
)
|
| 699 |
+
return output_dir
|
| 700 |
+
|
| 701 |
+
|
| 702 |
+
def load_validated_artifact_metadata(
|
| 703 |
+
artifact_dir: Path,
|
| 704 |
+
source_repo: Path,
|
| 705 |
+
*,
|
| 706 |
+
require_resident_runtime: bool = False,
|
| 707 |
+
) -> dict:
|
| 708 |
+
artifact_dir = artifact_dir.resolve()
|
| 709 |
+
source_repo = source_repo.resolve()
|
| 710 |
+
metadata_path = artifact_dir / "metadata.json"
|
| 711 |
+
artifact_path = artifact_dir / "packed_transformer.safetensors"
|
| 712 |
+
if not metadata_path.is_file() or not artifact_path.is_file():
|
| 713 |
+
fail(f"artifact dir missing metadata or safetensors: {artifact_dir}")
|
| 714 |
+
try:
|
| 715 |
+
metadata = json.loads(metadata_path.read_text(encoding="utf-8"))
|
| 716 |
+
except (OSError, json.JSONDecodeError) as exc:
|
| 717 |
+
fail(f"invalid artifact metadata {metadata_path}: {exc}")
|
| 718 |
+
if metadata.get("format_version") != FORMAT_VERSION:
|
| 719 |
+
fail(f"unsupported format_version: {metadata.get('format_version')}")
|
| 720 |
+
if metadata.get("artifact_kind") != ARTIFACT_KIND:
|
| 721 |
+
fail(f"unexpected artifact_kind: {metadata.get('artifact_kind')}")
|
| 722 |
+
|
| 723 |
+
config_path = source_repo / "transformer" / "config.json"
|
| 724 |
+
checkpoint_path = source_transformer_checkpoint(source_repo)
|
| 725 |
+
expected_config_hash = sha256_file(config_path)
|
| 726 |
+
expected_checkpoint_hash = sha256_file(checkpoint_path)
|
| 727 |
+
try:
|
| 728 |
+
source_metadata = metadata["source"]
|
| 729 |
+
recorded_config_hash = source_metadata["transformer_config_sha256"]
|
| 730 |
+
recorded_checkpoint_hash = source_metadata[
|
| 731 |
+
"transformer_checkpoint_sha256"
|
| 732 |
+
]
|
| 733 |
+
model_metadata = metadata["model"]
|
| 734 |
+
recorded_depth = int(model_metadata["depth"])
|
| 735 |
+
recorded_targets = metadata["targets"]
|
| 736 |
+
recorded_non_target_keys = metadata["non_target_keys"]
|
| 737 |
+
except (KeyError, TypeError, ValueError) as exc:
|
| 738 |
+
fail(f"artifact metadata schema is incomplete or invalid: {exc}")
|
| 739 |
+
if not isinstance(recorded_targets, list):
|
| 740 |
+
fail("artifact metadata targets must be a list")
|
| 741 |
+
if not isinstance(recorded_non_target_keys, list) or not all(
|
| 742 |
+
isinstance(key, str) for key in recorded_non_target_keys
|
| 743 |
+
):
|
| 744 |
+
fail("artifact metadata non_target_keys must be a list of strings")
|
| 745 |
+
if recorded_config_hash != expected_config_hash:
|
| 746 |
+
fail("transformer config hash mismatch")
|
| 747 |
+
if recorded_checkpoint_hash != expected_checkpoint_hash:
|
| 748 |
+
fail("transformer checkpoint hash mismatch")
|
| 749 |
+
|
| 750 |
+
config = load_transformer_config(source_repo)
|
| 751 |
+
if recorded_depth != int(config["depth"]):
|
| 752 |
+
fail("artifact model depth does not match source config")
|
| 753 |
+
specs = build_target_specs(int(config["depth"]))
|
| 754 |
+
expected_modules = [spec.module_key for spec in specs]
|
| 755 |
+
if not all(isinstance(entry, dict) for entry in recorded_targets):
|
| 756 |
+
fail("artifact metadata target entries must be objects")
|
| 757 |
+
recorded_modules = [entry.get("module_key") for entry in recorded_targets]
|
| 758 |
+
if recorded_modules != expected_modules:
|
| 759 |
+
fail("artifact target allowlist/order mismatch")
|
| 760 |
+
target_source_keys = {
|
| 761 |
+
key for spec in specs for key in (spec.weight_key, spec.bias_key)
|
| 762 |
+
}
|
| 763 |
+
expected_artifact_keys = {
|
| 764 |
+
key
|
| 765 |
+
for spec in specs
|
| 766 |
+
for key in (
|
| 767 |
+
spec.artifact_weight_key,
|
| 768 |
+
spec.artifact_scale_key,
|
| 769 |
+
spec.artifact_tensor_scale_key,
|
| 770 |
+
spec.artifact_bias_key,
|
| 771 |
+
)
|
| 772 |
+
}
|
| 773 |
+
with safe_open(artifact_path, framework="pt", device="cpu") as artifact_handle:
|
| 774 |
+
actual_artifact_keys = set(artifact_handle.keys())
|
| 775 |
+
header_metadata = artifact_handle.metadata()
|
| 776 |
+
if actual_artifact_keys != expected_artifact_keys:
|
| 777 |
+
fail("artifact tensor key coverage mismatch")
|
| 778 |
+
if header_metadata not in (
|
| 779 |
+
CANONICAL_SAFETENSORS_METADATA,
|
| 780 |
+
LEGACY_SAFETENSORS_METADATA,
|
| 781 |
+
):
|
| 782 |
+
fail("artifact safetensors header metadata mismatch")
|
| 783 |
+
|
| 784 |
+
with safe_open(checkpoint_path, framework="pt", device="cpu") as source_handle:
|
| 785 |
+
source_keys = set(source_handle.keys())
|
| 786 |
+
missing_target_keys = sorted(target_source_keys - source_keys)
|
| 787 |
+
if missing_target_keys:
|
| 788 |
+
fail(
|
| 789 |
+
"source checkpoint is missing target tensors, first="
|
| 790 |
+
f"{missing_target_keys[0]}"
|
| 791 |
+
)
|
| 792 |
+
expected_non_target_keys = sorted(source_keys - target_source_keys)
|
| 793 |
+
if recorded_non_target_keys != expected_non_target_keys:
|
| 794 |
+
fail("artifact non-target source manifest mismatch")
|
| 795 |
+
|
| 796 |
+
if require_resident_runtime:
|
| 797 |
+
hashes = metadata.get("library_hashes", {})
|
| 798 |
+
if not isinstance(hashes, dict):
|
| 799 |
+
fail("artifact metadata library_hashes must be an object")
|
| 800 |
+
if hashes.get("resident_source_sha256") != sha256_file(RESIDENT_SOURCE):
|
| 801 |
+
fail("resident source hash mismatch")
|
| 802 |
+
if hashes.get("resident_library_sha256") != sha256_file(RESIDENT_LIBRARY):
|
| 803 |
+
fail("resident library hash mismatch")
|
| 804 |
+
return metadata
|
| 805 |
+
|
| 806 |
+
|
| 807 |
+
def validate_artifact(artifact_dir: Path, source_repo: Path) -> None:
|
| 808 |
+
artifact_dir = artifact_dir.resolve()
|
| 809 |
+
source_repo = source_repo.resolve()
|
| 810 |
+
metadata = load_validated_artifact_metadata(artifact_dir, source_repo)
|
| 811 |
+
artifact_path = artifact_dir / "packed_transformer.safetensors"
|
| 812 |
+
checkpoint_path = source_transformer_checkpoint(source_repo)
|
| 813 |
+
config = load_transformer_config(source_repo)
|
| 814 |
+
specs = {
|
| 815 |
+
spec.module_key: spec for spec in build_target_specs(int(config["depth"]))
|
| 816 |
+
}
|
| 817 |
+
|
| 818 |
+
with safe_open(artifact_path, framework="pt", device="cpu") as artifact_handle, safe_open(
|
| 819 |
+
checkpoint_path, framework="pt", device="cpu"
|
| 820 |
+
) as source_handle:
|
| 821 |
+
for entry in metadata["targets"]:
|
| 822 |
+
spec = specs[entry["module_key"]]
|
| 823 |
+
weight = source_handle.get_tensor(spec.weight_key)
|
| 824 |
+
bias = source_handle.get_tensor(spec.bias_key)
|
| 825 |
+
packed_weight, packed_scales, weight_tensor_scale, global_amax = pack_weight_tensor(weight)
|
| 826 |
+
|
| 827 |
+
candidate_weight = artifact_handle.get_tensor(spec.artifact_weight_key)
|
| 828 |
+
candidate_scales = artifact_handle.get_tensor(spec.artifact_scale_key)
|
| 829 |
+
candidate_tensor_scale = artifact_handle.get_tensor(spec.artifact_tensor_scale_key)
|
| 830 |
+
candidate_bias = artifact_handle.get_tensor(spec.artifact_bias_key)
|
| 831 |
+
|
| 832 |
+
if not torch.equal(candidate_weight, packed_weight):
|
| 833 |
+
fail(f"packed weight mismatch for {spec.module_key}")
|
| 834 |
+
if not torch.equal(candidate_scales, packed_scales):
|
| 835 |
+
fail(f"packed scales mismatch for {spec.module_key}")
|
| 836 |
+
if not torch.equal(candidate_tensor_scale, weight_tensor_scale):
|
| 837 |
+
fail(f"tensor scale mismatch for {spec.module_key}")
|
| 838 |
+
if not torch.equal(candidate_bias, bias):
|
| 839 |
+
fail(f"bias mismatch for {spec.module_key}")
|
| 840 |
+
if abs(float(entry["weight_global_amax"]) - global_amax) > 0.0:
|
| 841 |
+
fail(f"global amax mismatch for {spec.module_key}")
|
| 842 |
+
|
| 843 |
+
|
| 844 |
+
def load_clean_transformer_from_artifact(
|
| 845 |
+
artifact_dir: Path,
|
| 846 |
+
source_repo: Path,
|
| 847 |
+
assign_non_target: bool = True,
|
| 848 |
+
) -> nn.Module:
|
| 849 |
+
artifact_dir = artifact_dir.resolve()
|
| 850 |
+
source_repo = source_repo.resolve()
|
| 851 |
+
metadata = load_validated_artifact_metadata(artifact_dir, source_repo)
|
| 852 |
+
target_specs = build_target_specs(int(metadata["model"]["depth"]))
|
| 853 |
+
model = instantiate_mage_transformer_on_meta(source_repo)
|
| 854 |
+
replace_targets_with_artifact_modules(model, artifact_dir / "packed_transformer.safetensors", target_specs)
|
| 855 |
+
|
| 856 |
+
if assign_non_target:
|
| 857 |
+
skip_keys = {
|
| 858 |
+
key
|
| 859 |
+
for spec in target_specs
|
| 860 |
+
for key in (spec.weight_key, spec.bias_key)
|
| 861 |
+
}
|
| 862 |
+
source_tensor_keys_read: list[str] = []
|
| 863 |
+
with safe_open(source_transformer_checkpoint(source_repo), framework="pt", device="cpu") as source_handle:
|
| 864 |
+
for key in source_handle.keys():
|
| 865 |
+
if key in skip_keys:
|
| 866 |
+
continue
|
| 867 |
+
tensor = source_handle.get_tensor(key)
|
| 868 |
+
source_tensor_keys_read.append(key)
|
| 869 |
+
assign_tensor_by_name(model, key, tensor)
|
| 870 |
+
target_reads = sorted(set(source_tensor_keys_read) & skip_keys)
|
| 871 |
+
if target_reads:
|
| 872 |
+
fail(f"clean CPU loader read target source tensors: {target_reads[0]}")
|
| 873 |
+
meta_parameters = [
|
| 874 |
+
name for name, parameter in model.named_parameters() if parameter.is_meta
|
| 875 |
+
]
|
| 876 |
+
meta_buffers = [
|
| 877 |
+
name for name, buffer in model.named_buffers() if buffer.is_meta
|
| 878 |
+
]
|
| 879 |
+
if meta_parameters or meta_buffers:
|
| 880 |
+
first = (meta_parameters + meta_buffers)[0]
|
| 881 |
+
fail(f"clean CPU loader left unresolved meta tensors, first={first}")
|
| 882 |
+
materialize_mage_rope_tensor_attributes(model)
|
| 883 |
+
return model
|
| 884 |
+
|
| 885 |
+
|
| 886 |
+
def load_clean_resident_transformer(
|
| 887 |
+
artifact_dir: Path,
|
| 888 |
+
source_repo: Path,
|
| 889 |
+
device: torch.device,
|
| 890 |
+
) -> tuple[nn.Module, dict]:
|
| 891 |
+
return _load_clean_cuda_transformer(
|
| 892 |
+
artifact_dir,
|
| 893 |
+
source_repo,
|
| 894 |
+
device,
|
| 895 |
+
replace_targets_with_resident_modules,
|
| 896 |
+
)
|
| 897 |
+
|
| 898 |
+
|
| 899 |
+
def load_clean_native_resident_transformer(
|
| 900 |
+
artifact_dir: Path,
|
| 901 |
+
source_repo: Path,
|
| 902 |
+
device: torch.device,
|
| 903 |
+
) -> tuple[nn.Module, dict]:
|
| 904 |
+
return _load_clean_cuda_transformer(
|
| 905 |
+
artifact_dir,
|
| 906 |
+
source_repo,
|
| 907 |
+
device,
|
| 908 |
+
replace_targets_with_native_resident_modules,
|
| 909 |
+
)
|
| 910 |
+
|
| 911 |
+
|
| 912 |
+
def _load_clean_cuda_transformer(
|
| 913 |
+
artifact_dir: Path,
|
| 914 |
+
source_repo: Path,
|
| 915 |
+
device: torch.device,
|
| 916 |
+
target_replacement_fn: Callable[[nn.Module, Path, Iterable[TargetSpec], torch.device], None],
|
| 917 |
+
) -> tuple[nn.Module, dict]:
|
| 918 |
+
artifact_dir = artifact_dir.resolve()
|
| 919 |
+
source_repo = source_repo.resolve()
|
| 920 |
+
metadata = load_validated_artifact_metadata(
|
| 921 |
+
artifact_dir,
|
| 922 |
+
source_repo,
|
| 923 |
+
require_resident_runtime=True,
|
| 924 |
+
)
|
| 925 |
+
target_specs = build_target_specs(int(metadata["model"]["depth"]))
|
| 926 |
+
target_source_keys = {
|
| 927 |
+
key
|
| 928 |
+
for spec in target_specs
|
| 929 |
+
for key in (spec.weight_key, spec.bias_key)
|
| 930 |
+
}
|
| 931 |
+
model = instantiate_mage_transformer_on_meta(source_repo)
|
| 932 |
+
target_replacement_fn(
|
| 933 |
+
model,
|
| 934 |
+
artifact_dir / "packed_transformer.safetensors",
|
| 935 |
+
target_specs,
|
| 936 |
+
device,
|
| 937 |
+
)
|
| 938 |
+
|
| 939 |
+
loaded_source_keys: list[str] = []
|
| 940 |
+
skipped_target_source_keys: list[str] = []
|
| 941 |
+
source_tensor_keys_read: list[str] = []
|
| 942 |
+
with safe_open(
|
| 943 |
+
source_transformer_checkpoint(source_repo),
|
| 944 |
+
framework="pt",
|
| 945 |
+
device="cpu",
|
| 946 |
+
) as source_handle:
|
| 947 |
+
source_keys = list(source_handle.keys())
|
| 948 |
+
missing_target_keys = sorted(target_source_keys - set(source_keys))
|
| 949 |
+
if missing_target_keys:
|
| 950 |
+
fail(
|
| 951 |
+
"source checkpoint is missing target tensors, first="
|
| 952 |
+
f"{missing_target_keys[0]}"
|
| 953 |
+
)
|
| 954 |
+
for key in source_keys:
|
| 955 |
+
if key in target_source_keys:
|
| 956 |
+
skipped_target_source_keys.append(key)
|
| 957 |
+
continue
|
| 958 |
+
tensor = source_handle.get_tensor(key)
|
| 959 |
+
source_tensor_keys_read.append(key)
|
| 960 |
+
assign_tensor_by_name(model, key, tensor.to(device))
|
| 961 |
+
loaded_source_keys.append(key)
|
| 962 |
+
|
| 963 |
+
materialized_tensor_attributes = materialize_mage_rope_tensor_attributes(model)
|
| 964 |
+
target_source_reads = sorted(
|
| 965 |
+
set(source_tensor_keys_read) & target_source_keys
|
| 966 |
+
)
|
| 967 |
+
meta_parameters = [
|
| 968 |
+
name for name, parameter in model.named_parameters() if parameter.is_meta
|
| 969 |
+
]
|
| 970 |
+
meta_buffers = [
|
| 971 |
+
name for name, buffer in model.named_buffers() if buffer.is_meta
|
| 972 |
+
]
|
| 973 |
+
report = {
|
| 974 |
+
"source_tensor_count_loaded": len(loaded_source_keys),
|
| 975 |
+
"source_tensor_keys_loaded": loaded_source_keys,
|
| 976 |
+
"source_tensor_count_read": len(source_tensor_keys_read),
|
| 977 |
+
"source_tensor_keys_read": source_tensor_keys_read,
|
| 978 |
+
"target_source_tensor_count_skipped": len(skipped_target_source_keys),
|
| 979 |
+
"target_source_tensor_keys_skipped": skipped_target_source_keys,
|
| 980 |
+
"target_source_tensor_reads": len(target_source_reads),
|
| 981 |
+
"target_source_tensor_keys_read": target_source_reads,
|
| 982 |
+
"meta_parameter_names": meta_parameters,
|
| 983 |
+
"meta_buffer_names": meta_buffers,
|
| 984 |
+
"materialized_unregistered_tensor_attribute_names": (
|
| 985 |
+
materialized_tensor_attributes
|
| 986 |
+
),
|
| 987 |
+
"unregistered_meta_tensor_attribute_names": (
|
| 988 |
+
unregistered_meta_tensor_attribute_names(model)
|
| 989 |
+
),
|
| 990 |
+
}
|
| 991 |
+
return model.eval().requires_grad_(False), report
|
| 992 |
+
|
| 993 |
+
|
| 994 |
+
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
| 995 |
+
parser = argparse.ArgumentParser(description=__doc__)
|
| 996 |
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
| 997 |
+
|
| 998 |
+
pack_parser = subparsers.add_parser("pack", help="build a packed artifact directory")
|
| 999 |
+
pack_parser.add_argument("--source-repo", type=Path, required=True)
|
| 1000 |
+
pack_parser.add_argument("--output-dir", type=Path, required=True)
|
| 1001 |
+
|
| 1002 |
+
validate_parser = subparsers.add_parser("validate", help="recompute and validate a packed artifact")
|
| 1003 |
+
validate_parser.add_argument("--artifact-dir", type=Path, required=True)
|
| 1004 |
+
validate_parser.add_argument("--source-repo", type=Path, required=True)
|
| 1005 |
+
|
| 1006 |
+
plan_load_parser = subparsers.add_parser("plan-load", help="instantiate on meta and replace target modules")
|
| 1007 |
+
plan_load_parser.add_argument("--artifact-dir", type=Path, required=True)
|
| 1008 |
+
plan_load_parser.add_argument("--source-repo", type=Path, required=True)
|
| 1009 |
+
plan_load_parser.add_argument("--skip-non-target", action="store_true")
|
| 1010 |
+
|
| 1011 |
+
runtime_parser = subparsers.add_parser(
|
| 1012 |
+
"validate-runtime",
|
| 1013 |
+
help="placeholder for future single-GPU resident validation",
|
| 1014 |
+
)
|
| 1015 |
+
runtime_parser.add_argument("--artifact-dir", type=Path, required=True)
|
| 1016 |
+
runtime_parser.add_argument("--source-repo", type=Path, required=True)
|
| 1017 |
+
return parser.parse_args(argv)
|
| 1018 |
+
|
| 1019 |
+
|
| 1020 |
+
def main(argv: list[str] | None = None) -> int:
|
| 1021 |
+
args = parse_args(argv)
|
| 1022 |
+
try:
|
| 1023 |
+
if args.command == "pack":
|
| 1024 |
+
artifact_dir = pack_artifact(args.source_repo, args.output_dir)
|
| 1025 |
+
print(artifact_dir)
|
| 1026 |
+
return 0
|
| 1027 |
+
if args.command == "validate":
|
| 1028 |
+
validate_artifact(args.artifact_dir, args.source_repo)
|
| 1029 |
+
print("ok")
|
| 1030 |
+
return 0
|
| 1031 |
+
if args.command == "plan-load":
|
| 1032 |
+
model = load_clean_transformer_from_artifact(
|
| 1033 |
+
args.artifact_dir, args.source_repo, assign_non_target=not args.skip_non_target
|
| 1034 |
+
)
|
| 1035 |
+
packed_count = sum(
|
| 1036 |
+
1 for _name, module in model.named_modules() if isinstance(module, PackedNvfp4LinearArtifactModule)
|
| 1037 |
+
)
|
| 1038 |
+
meta_parameters = [
|
| 1039 |
+
name for name, parameter in model.named_parameters() if parameter.is_meta
|
| 1040 |
+
]
|
| 1041 |
+
meta_buffers = [
|
| 1042 |
+
name for name, buffer in model.named_buffers() if buffer.is_meta
|
| 1043 |
+
]
|
| 1044 |
+
print(
|
| 1045 |
+
json.dumps(
|
| 1046 |
+
{
|
| 1047 |
+
"packed_module_count": packed_count,
|
| 1048 |
+
"meta_parameter_count": len(meta_parameters),
|
| 1049 |
+
"meta_buffer_count": len(meta_buffers),
|
| 1050 |
+
"non_target_assignment_skipped": bool(args.skip_non_target),
|
| 1051 |
+
},
|
| 1052 |
+
indent=2,
|
| 1053 |
+
)
|
| 1054 |
+
)
|
| 1055 |
+
return 0
|
| 1056 |
+
if args.command == "validate-runtime":
|
| 1057 |
+
fail(
|
| 1058 |
+
"validate-runtime is intentionally not implemented in this CPU-only slice. "
|
| 1059 |
+
"Use the future CUDA resident path on CUDA_VISIBLE_DEVICES=3."
|
| 1060 |
+
)
|
| 1061 |
+
fail(f"unsupported command: {args.command}")
|
| 1062 |
+
except PackedArtifactError as exc:
|
| 1063 |
+
print(f"error: {exc}", file=sys.stderr)
|
| 1064 |
+
return 1
|
| 1065 |
+
|
| 1066 |
+
|
| 1067 |
+
if __name__ == "__main__":
|
| 1068 |
+
raise SystemExit(main())
|
runtime/packed_nvfp4_linear.py
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Inference-only resident NVFP4 linear prototype.
|
| 2 |
+
|
| 3 |
+
This module intentionally uses a narrow ctypes boundary. It proves packed
|
| 4 |
+
residency and Mage shape correctness; it is not yet a torch.compile/CUDA-graph
|
| 5 |
+
shipping operator.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import atexit
|
| 11 |
+
import ctypes
|
| 12 |
+
import threading
|
| 13 |
+
from dataclasses import dataclass
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
from typing import Final
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
from torch import nn
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
ABI_VERSION: Final = 1
|
| 22 |
+
FP4_BLOCK_ELEMENTS: Final = 16
|
| 23 |
+
SCALE_TILE_OUTER: Final = 128
|
| 24 |
+
SCALE_TILE_INNER: Final = 4
|
| 25 |
+
RELEASE_ROOT: Final = Path(__file__).resolve().parents[1]
|
| 26 |
+
DEFAULT_LIBRARY_PATH: Final = RELEASE_ROOT / "runtime" / "libmage_nvfp4_linear.so"
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def round_up(value: int, multiple: int) -> int:
|
| 30 |
+
if value <= 0 or multiple <= 0:
|
| 31 |
+
raise ValueError("value and multiple must be positive")
|
| 32 |
+
return ((value + multiple - 1) // multiple) * multiple
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
@dataclass(frozen=True)
|
| 36 |
+
class ScaleLayout:
|
| 37 |
+
inner_dim: int
|
| 38 |
+
outer_tiles: int
|
| 39 |
+
num_bytes: int
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def scale_layout(k: int, outer_columns: int) -> ScaleLayout:
|
| 43 |
+
if k <= 0 or k % FP4_BLOCK_ELEMENTS:
|
| 44 |
+
raise ValueError("K must be positive and divisible by 16")
|
| 45 |
+
if outer_columns <= 0:
|
| 46 |
+
raise ValueError("outer column count must be positive")
|
| 47 |
+
inner_dim = round_up(k // FP4_BLOCK_ELEMENTS, SCALE_TILE_INNER)
|
| 48 |
+
outer_tiles = (outer_columns + SCALE_TILE_OUTER - 1) // SCALE_TILE_OUTER
|
| 49 |
+
return ScaleLayout(
|
| 50 |
+
inner_dim=inner_dim,
|
| 51 |
+
outer_tiles=outer_tiles,
|
| 52 |
+
num_bytes=outer_tiles * inner_dim * SCALE_TILE_OUTER,
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def packed_weight_num_bytes(out_features: int, in_features: int) -> int:
|
| 57 |
+
if out_features <= 0 or out_features % 8:
|
| 58 |
+
raise ValueError("out_features must be positive and divisible by 8")
|
| 59 |
+
if in_features <= 0 or in_features % 32:
|
| 60 |
+
raise ValueError("in_features must be positive and divisible by 32")
|
| 61 |
+
return out_features * in_features // 2
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def padded_output_shape(input_shape: tuple[int, ...], out_features: int) -> tuple[int, int]:
|
| 65 |
+
if not input_shape:
|
| 66 |
+
raise ValueError("input must have at least one dimension")
|
| 67 |
+
logical_m = 1
|
| 68 |
+
for dimension in input_shape[:-1]:
|
| 69 |
+
if dimension <= 0:
|
| 70 |
+
raise ValueError("empty or negative leading dimensions are unsupported")
|
| 71 |
+
logical_m *= dimension
|
| 72 |
+
return round_up(logical_m, 8), out_features
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def logical_output_shape(input_shape: tuple[int, ...], out_features: int) -> tuple[int, ...]:
|
| 76 |
+
if not input_shape:
|
| 77 |
+
raise ValueError("input must have at least one dimension")
|
| 78 |
+
return (*input_shape[:-1], out_features)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class NativeNvfp4Library:
|
| 82 |
+
"""Typed ctypes access to the project-local native library."""
|
| 83 |
+
|
| 84 |
+
def __init__(self, path: str | Path = DEFAULT_LIBRARY_PATH):
|
| 85 |
+
self.path = Path(path).resolve()
|
| 86 |
+
if not self.path.is_file():
|
| 87 |
+
raise FileNotFoundError(
|
| 88 |
+
f"resident NVFP4 library is not built: {self.path}"
|
| 89 |
+
)
|
| 90 |
+
self._library = ctypes.CDLL(str(self.path))
|
| 91 |
+
self._bind()
|
| 92 |
+
version = int(self._library.mage_nvfp4_abi_version())
|
| 93 |
+
if version != ABI_VERSION:
|
| 94 |
+
raise RuntimeError(
|
| 95 |
+
f"resident NVFP4 ABI mismatch: Python={ABI_VERSION}, native={version}"
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
def _bind(self) -> None:
|
| 99 |
+
library = self._library
|
| 100 |
+
library.mage_nvfp4_abi_version.argtypes = []
|
| 101 |
+
library.mage_nvfp4_abi_version.restype = ctypes.c_int
|
| 102 |
+
library.mage_nvfp4_last_error.argtypes = []
|
| 103 |
+
library.mage_nvfp4_last_error.restype = ctypes.c_char_p
|
| 104 |
+
library.mage_nvfp4_packed_weight_bytes.argtypes = [
|
| 105 |
+
ctypes.c_int,
|
| 106 |
+
ctypes.c_int,
|
| 107 |
+
]
|
| 108 |
+
library.mage_nvfp4_packed_weight_bytes.restype = ctypes.c_size_t
|
| 109 |
+
library.mage_nvfp4_weight_scale_bytes.argtypes = [
|
| 110 |
+
ctypes.c_int,
|
| 111 |
+
ctypes.c_int,
|
| 112 |
+
]
|
| 113 |
+
library.mage_nvfp4_weight_scale_bytes.restype = ctypes.c_size_t
|
| 114 |
+
library.mage_nvfp4_pack_weight_bf16.argtypes = [
|
| 115 |
+
ctypes.c_void_p,
|
| 116 |
+
ctypes.c_int,
|
| 117 |
+
ctypes.c_int,
|
| 118 |
+
ctypes.c_void_p,
|
| 119 |
+
ctypes.c_size_t,
|
| 120 |
+
ctypes.c_void_p,
|
| 121 |
+
ctypes.c_size_t,
|
| 122 |
+
ctypes.POINTER(ctypes.c_float),
|
| 123 |
+
]
|
| 124 |
+
library.mage_nvfp4_pack_weight_bf16.restype = ctypes.c_int
|
| 125 |
+
library.mage_nvfp4_create_context.argtypes = [
|
| 126 |
+
ctypes.c_int,
|
| 127 |
+
ctypes.POINTER(ctypes.c_void_p),
|
| 128 |
+
]
|
| 129 |
+
library.mage_nvfp4_create_context.restype = ctypes.c_int
|
| 130 |
+
library.mage_nvfp4_destroy_context.argtypes = [ctypes.c_void_p]
|
| 131 |
+
library.mage_nvfp4_destroy_context.restype = ctypes.c_int
|
| 132 |
+
library.mage_nvfp4_context_reserved_bytes.argtypes = [ctypes.c_void_p]
|
| 133 |
+
library.mage_nvfp4_context_reserved_bytes.restype = ctypes.c_size_t
|
| 134 |
+
library.mage_nvfp4_linear_forward.argtypes = [
|
| 135 |
+
ctypes.c_void_p,
|
| 136 |
+
ctypes.c_void_p,
|
| 137 |
+
ctypes.c_void_p,
|
| 138 |
+
ctypes.c_size_t,
|
| 139 |
+
ctypes.c_void_p,
|
| 140 |
+
ctypes.c_size_t,
|
| 141 |
+
ctypes.c_void_p,
|
| 142 |
+
ctypes.c_void_p,
|
| 143 |
+
ctypes.c_void_p,
|
| 144 |
+
ctypes.c_int,
|
| 145 |
+
ctypes.c_int,
|
| 146 |
+
ctypes.c_int,
|
| 147 |
+
ctypes.c_size_t,
|
| 148 |
+
]
|
| 149 |
+
library.mage_nvfp4_linear_forward.restype = ctypes.c_int
|
| 150 |
+
|
| 151 |
+
def _error(self, operation: str) -> RuntimeError:
|
| 152 |
+
raw = self._library.mage_nvfp4_last_error()
|
| 153 |
+
message = raw.decode("utf-8", errors="replace") if raw else "unknown error"
|
| 154 |
+
return RuntimeError(f"{operation}: {message}")
|
| 155 |
+
|
| 156 |
+
def pack_weight(
|
| 157 |
+
self, weight: torch.Tensor
|
| 158 |
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 159 |
+
if weight.device.type != "cpu":
|
| 160 |
+
raise ValueError("native weight packer requires a CPU tensor")
|
| 161 |
+
if weight.dtype != torch.bfloat16:
|
| 162 |
+
raise TypeError("native weight packer requires BF16")
|
| 163 |
+
if weight.ndim != 2 or not weight.is_contiguous():
|
| 164 |
+
raise ValueError("weight must be contiguous [N,K]")
|
| 165 |
+
n, k = map(int, weight.shape)
|
| 166 |
+
packed_bytes = packed_weight_num_bytes(n, k)
|
| 167 |
+
scale_bytes = scale_layout(k, n).num_bytes
|
| 168 |
+
native_packed = int(
|
| 169 |
+
self._library.mage_nvfp4_packed_weight_bytes(n, k)
|
| 170 |
+
)
|
| 171 |
+
native_scales = int(
|
| 172 |
+
self._library.mage_nvfp4_weight_scale_bytes(n, k)
|
| 173 |
+
)
|
| 174 |
+
if (native_packed, native_scales) != (packed_bytes, scale_bytes):
|
| 175 |
+
raise RuntimeError(
|
| 176 |
+
"Python/native packed metadata disagreement: "
|
| 177 |
+
f"Python={(packed_bytes, scale_bytes)}, "
|
| 178 |
+
f"native={(native_packed, native_scales)}"
|
| 179 |
+
)
|
| 180 |
+
packed = torch.empty(packed_bytes, dtype=torch.uint8, device="cpu")
|
| 181 |
+
scales = torch.empty(scale_bytes, dtype=torch.uint8, device="cpu")
|
| 182 |
+
tensor_scale = ctypes.c_float()
|
| 183 |
+
status = self._library.mage_nvfp4_pack_weight_bf16(
|
| 184 |
+
ctypes.c_void_p(weight.data_ptr()),
|
| 185 |
+
n,
|
| 186 |
+
k,
|
| 187 |
+
ctypes.c_void_p(packed.data_ptr()),
|
| 188 |
+
packed.numel(),
|
| 189 |
+
ctypes.c_void_p(scales.data_ptr()),
|
| 190 |
+
scales.numel(),
|
| 191 |
+
ctypes.byref(tensor_scale),
|
| 192 |
+
)
|
| 193 |
+
if status:
|
| 194 |
+
raise self._error("packing BF16 weight")
|
| 195 |
+
scale_tensor = torch.tensor(tensor_scale.value, dtype=torch.float32)
|
| 196 |
+
return packed, scales, scale_tensor
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
_LIBRARY_LOCK = threading.Lock()
|
| 200 |
+
_LIBRARY: NativeNvfp4Library | None = None
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
def native_library() -> NativeNvfp4Library:
|
| 204 |
+
global _LIBRARY
|
| 205 |
+
with _LIBRARY_LOCK:
|
| 206 |
+
if _LIBRARY is None:
|
| 207 |
+
_LIBRARY = NativeNvfp4Library()
|
| 208 |
+
return _LIBRARY
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
class ResidentContext:
|
| 212 |
+
def __init__(self, library: NativeNvfp4Library, device_index: int, stream: int):
|
| 213 |
+
self.library = library
|
| 214 |
+
self.device_index = int(device_index)
|
| 215 |
+
self.stream = int(stream)
|
| 216 |
+
self._pointer = ctypes.c_void_p()
|
| 217 |
+
self._lock = threading.Lock()
|
| 218 |
+
status = self.library._library.mage_nvfp4_create_context(
|
| 219 |
+
self.device_index, ctypes.byref(self._pointer)
|
| 220 |
+
)
|
| 221 |
+
if status:
|
| 222 |
+
raise self.library._error("creating resident context")
|
| 223 |
+
self._closed = False
|
| 224 |
+
|
| 225 |
+
@property
|
| 226 |
+
def pointer(self) -> ctypes.c_void_p:
|
| 227 |
+
if self._closed:
|
| 228 |
+
raise RuntimeError("resident NVFP4 context is closed")
|
| 229 |
+
return self._pointer
|
| 230 |
+
|
| 231 |
+
@property
|
| 232 |
+
def reserved_bytes(self) -> int:
|
| 233 |
+
return int(
|
| 234 |
+
self.library._library.mage_nvfp4_context_reserved_bytes(self.pointer)
|
| 235 |
+
)
|
| 236 |
+
|
| 237 |
+
def close(self) -> None:
|
| 238 |
+
with self._lock:
|
| 239 |
+
if self._closed:
|
| 240 |
+
return
|
| 241 |
+
status = self.library._library.mage_nvfp4_destroy_context(
|
| 242 |
+
self._pointer
|
| 243 |
+
)
|
| 244 |
+
if status:
|
| 245 |
+
raise self.library._error("destroying resident context")
|
| 246 |
+
self._closed = True
|
| 247 |
+
self._pointer = ctypes.c_void_p()
|
| 248 |
+
|
| 249 |
+
def forward(
|
| 250 |
+
self,
|
| 251 |
+
x: torch.Tensor,
|
| 252 |
+
packed_weight: torch.Tensor,
|
| 253 |
+
weight_scales: torch.Tensor,
|
| 254 |
+
weight_scale: torch.Tensor,
|
| 255 |
+
bias: torch.Tensor | None,
|
| 256 |
+
output: torch.Tensor,
|
| 257 |
+
logical_m: int,
|
| 258 |
+
in_features: int,
|
| 259 |
+
out_features: int,
|
| 260 |
+
) -> None:
|
| 261 |
+
bias_pointer = (
|
| 262 |
+
ctypes.c_void_p(bias.data_ptr()) if bias is not None else None
|
| 263 |
+
)
|
| 264 |
+
with self._lock:
|
| 265 |
+
status = self.library._library.mage_nvfp4_linear_forward(
|
| 266 |
+
self.pointer,
|
| 267 |
+
ctypes.c_void_p(x.data_ptr()),
|
| 268 |
+
ctypes.c_void_p(packed_weight.data_ptr()),
|
| 269 |
+
packed_weight.numel(),
|
| 270 |
+
ctypes.c_void_p(weight_scales.data_ptr()),
|
| 271 |
+
weight_scales.numel(),
|
| 272 |
+
ctypes.c_void_p(weight_scale.data_ptr()),
|
| 273 |
+
bias_pointer,
|
| 274 |
+
ctypes.c_void_p(output.data_ptr()),
|
| 275 |
+
logical_m,
|
| 276 |
+
in_features,
|
| 277 |
+
out_features,
|
| 278 |
+
self.stream,
|
| 279 |
+
)
|
| 280 |
+
if status:
|
| 281 |
+
raise self.library._error("resident NVFP4 linear forward")
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
_CONTEXTS_LOCK = threading.Lock()
|
| 285 |
+
_CONTEXTS: dict[tuple[int, int], ResidentContext] = {}
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
def resident_context(device: torch.device) -> ResidentContext:
|
| 289 |
+
if device.type != "cuda":
|
| 290 |
+
raise ValueError("resident NVFP4 context requires CUDA")
|
| 291 |
+
device_index = (
|
| 292 |
+
torch.cuda.current_device() if device.index is None else int(device.index)
|
| 293 |
+
)
|
| 294 |
+
stream = int(torch.cuda.current_stream(device_index).cuda_stream)
|
| 295 |
+
key = (device_index, stream)
|
| 296 |
+
with _CONTEXTS_LOCK:
|
| 297 |
+
context = _CONTEXTS.get(key)
|
| 298 |
+
if context is None:
|
| 299 |
+
context = ResidentContext(native_library(), device_index, stream)
|
| 300 |
+
_CONTEXTS[key] = context
|
| 301 |
+
return context
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
def close_all_contexts() -> None:
|
| 305 |
+
with _CONTEXTS_LOCK:
|
| 306 |
+
contexts = list(_CONTEXTS.values())
|
| 307 |
+
_CONTEXTS.clear()
|
| 308 |
+
errors: list[Exception] = []
|
| 309 |
+
for context in contexts:
|
| 310 |
+
try:
|
| 311 |
+
context.close()
|
| 312 |
+
except Exception as error: # pragma: no cover - shutdown diagnostic
|
| 313 |
+
errors.append(error)
|
| 314 |
+
if errors:
|
| 315 |
+
raise RuntimeError(
|
| 316 |
+
"one or more resident NVFP4 contexts failed to close: "
|
| 317 |
+
+ "; ".join(map(str, errors))
|
| 318 |
+
)
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
def _quiet_atexit_close() -> None:
|
| 322 |
+
try:
|
| 323 |
+
close_all_contexts()
|
| 324 |
+
except Exception:
|
| 325 |
+
# CUDA may already be shutting down. Explicit close_all_contexts() is
|
| 326 |
+
# the auditable path; atexit is only a best-effort fallback.
|
| 327 |
+
pass
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
atexit.register(_quiet_atexit_close)
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
class PackedNvfp4Linear(nn.Module):
|
| 334 |
+
"""Packed inference replacement for one BF16 ``nn.Linear``."""
|
| 335 |
+
|
| 336 |
+
def __init__(
|
| 337 |
+
self,
|
| 338 |
+
in_features: int,
|
| 339 |
+
out_features: int,
|
| 340 |
+
packed_weight: torch.Tensor,
|
| 341 |
+
weight_scales: torch.Tensor,
|
| 342 |
+
weight_scale: torch.Tensor,
|
| 343 |
+
bias: torch.Tensor | None,
|
| 344 |
+
):
|
| 345 |
+
super().__init__()
|
| 346 |
+
expected_weight = packed_weight_num_bytes(out_features, in_features)
|
| 347 |
+
expected_scales = scale_layout(in_features, out_features).num_bytes
|
| 348 |
+
if (
|
| 349 |
+
packed_weight.dtype != torch.uint8
|
| 350 |
+
or packed_weight.ndim != 1
|
| 351 |
+
or not packed_weight.is_contiguous()
|
| 352 |
+
or packed_weight.numel() != expected_weight
|
| 353 |
+
):
|
| 354 |
+
raise ValueError("packed_weight has invalid dtype, shape, or size")
|
| 355 |
+
if (
|
| 356 |
+
weight_scales.dtype != torch.uint8
|
| 357 |
+
or weight_scales.ndim != 1
|
| 358 |
+
or not weight_scales.is_contiguous()
|
| 359 |
+
or weight_scales.numel() != expected_scales
|
| 360 |
+
):
|
| 361 |
+
raise ValueError("weight_scales has invalid dtype, shape, or size")
|
| 362 |
+
if (
|
| 363 |
+
weight_scale.dtype != torch.float32
|
| 364 |
+
or weight_scale.numel() != 1
|
| 365 |
+
or not weight_scale.is_contiguous()
|
| 366 |
+
):
|
| 367 |
+
raise ValueError("weight_scale must be one contiguous FP32 value")
|
| 368 |
+
if bias is not None and (
|
| 369 |
+
bias.dtype != torch.bfloat16
|
| 370 |
+
or bias.shape != (out_features,)
|
| 371 |
+
or not bias.is_contiguous()
|
| 372 |
+
):
|
| 373 |
+
raise ValueError("bias must be contiguous BF16 [out_features]")
|
| 374 |
+
devices = {
|
| 375 |
+
tensor.device
|
| 376 |
+
for tensor in (packed_weight, weight_scales, weight_scale, bias)
|
| 377 |
+
if tensor is not None
|
| 378 |
+
}
|
| 379 |
+
if len(devices) != 1:
|
| 380 |
+
raise ValueError("all resident buffers must be on one device")
|
| 381 |
+
|
| 382 |
+
self.in_features = int(in_features)
|
| 383 |
+
self.out_features = int(out_features)
|
| 384 |
+
self.register_buffer("packed_weight", packed_weight)
|
| 385 |
+
self.register_buffer("weight_scales", weight_scales)
|
| 386 |
+
self.register_buffer("weight_scale", weight_scale.reshape(()))
|
| 387 |
+
self.register_buffer("bias", bias)
|
| 388 |
+
|
| 389 |
+
@classmethod
|
| 390 |
+
def from_linear(
|
| 391 |
+
cls,
|
| 392 |
+
linear: nn.Linear,
|
| 393 |
+
device: torch.device | str,
|
| 394 |
+
*,
|
| 395 |
+
library: NativeNvfp4Library | None = None,
|
| 396 |
+
) -> "PackedNvfp4Linear":
|
| 397 |
+
if not isinstance(linear, nn.Linear):
|
| 398 |
+
raise TypeError("from_linear requires torch.nn.Linear")
|
| 399 |
+
library = native_library() if library is None else library
|
| 400 |
+
destination = torch.device(device)
|
| 401 |
+
if destination.type != "cuda":
|
| 402 |
+
raise ValueError("resident packed buffers must target CUDA")
|
| 403 |
+
weight_cpu = (
|
| 404 |
+
linear.weight.detach()
|
| 405 |
+
.to(device="cpu", dtype=torch.bfloat16)
|
| 406 |
+
.contiguous()
|
| 407 |
+
)
|
| 408 |
+
packed, scales, tensor_scale = library.pack_weight(weight_cpu)
|
| 409 |
+
bias_cpu = (
|
| 410 |
+
None
|
| 411 |
+
if linear.bias is None
|
| 412 |
+
else linear.bias.detach()
|
| 413 |
+
.to(device="cpu", dtype=torch.bfloat16)
|
| 414 |
+
.contiguous()
|
| 415 |
+
)
|
| 416 |
+
return cls(
|
| 417 |
+
linear.in_features,
|
| 418 |
+
linear.out_features,
|
| 419 |
+
packed.to(destination),
|
| 420 |
+
scales.to(destination),
|
| 421 |
+
tensor_scale.to(destination),
|
| 422 |
+
None if bias_cpu is None else bias_cpu.to(destination),
|
| 423 |
+
)
|
| 424 |
+
|
| 425 |
+
def forward(self, input: torch.Tensor) -> torch.Tensor:
|
| 426 |
+
if input.device.type != "cuda":
|
| 427 |
+
raise ValueError("PackedNvfp4Linear requires a CUDA input")
|
| 428 |
+
if input.device != self.packed_weight.device:
|
| 429 |
+
raise ValueError("input and packed buffers are on different devices")
|
| 430 |
+
if input.dtype != torch.bfloat16:
|
| 431 |
+
raise TypeError("PackedNvfp4Linear requires BF16 input")
|
| 432 |
+
if input.ndim < 1 or input.shape[-1] != self.in_features:
|
| 433 |
+
raise ValueError(
|
| 434 |
+
f"expected last dimension {self.in_features}, got "
|
| 435 |
+
f"{tuple(input.shape)}"
|
| 436 |
+
)
|
| 437 |
+
if input.requires_grad:
|
| 438 |
+
raise RuntimeError("resident NVFP4 prototype is inference-only")
|
| 439 |
+
|
| 440 |
+
contiguous = input.reshape(-1, self.in_features).contiguous()
|
| 441 |
+
logical_m = int(contiguous.shape[0])
|
| 442 |
+
if logical_m <= 0:
|
| 443 |
+
raise ValueError("empty inputs are unsupported")
|
| 444 |
+
padded_m = round_up(logical_m, 8)
|
| 445 |
+
padded_output = torch.empty(
|
| 446 |
+
(padded_m, self.out_features),
|
| 447 |
+
dtype=torch.bfloat16,
|
| 448 |
+
device=input.device,
|
| 449 |
+
)
|
| 450 |
+
current_stream = torch.cuda.current_stream(input.device)
|
| 451 |
+
# ctypes launches are invisible to the caching allocator. Explicitly
|
| 452 |
+
# record every CUDA allocation read or written by the native call so a
|
| 453 |
+
# tensor produced on another stream cannot be recycled while the
|
| 454 |
+
# resident kernel is still using it.
|
| 455 |
+
for tensor in (
|
| 456 |
+
contiguous,
|
| 457 |
+
self.packed_weight,
|
| 458 |
+
self.weight_scales,
|
| 459 |
+
self.weight_scale,
|
| 460 |
+
self.bias,
|
| 461 |
+
padded_output,
|
| 462 |
+
):
|
| 463 |
+
if tensor is not None:
|
| 464 |
+
tensor.record_stream(current_stream)
|
| 465 |
+
context = resident_context(input.device)
|
| 466 |
+
context.forward(
|
| 467 |
+
contiguous,
|
| 468 |
+
self.packed_weight,
|
| 469 |
+
self.weight_scales,
|
| 470 |
+
self.weight_scale,
|
| 471 |
+
self.bias,
|
| 472 |
+
padded_output,
|
| 473 |
+
logical_m,
|
| 474 |
+
self.in_features,
|
| 475 |
+
self.out_features,
|
| 476 |
+
)
|
| 477 |
+
logical = padded_output[:logical_m]
|
| 478 |
+
return logical.view(*input.shape[:-1], self.out_features)
|
| 479 |
+
|
| 480 |
+
def extra_repr(self) -> str:
|
| 481 |
+
return (
|
| 482 |
+
f"in_features={self.in_features}, "
|
| 483 |
+
f"out_features={self.out_features}, "
|
| 484 |
+
f"bias={self.bias is not None}, inference_only=True"
|
| 485 |
+
)
|
runtime/quant_text_encoder.py
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Load the packaged mixed NVFP4/FP8 Qwen3-VL text encoder without BF16 shards."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from collections import Counter
|
| 6 |
+
import hashlib
|
| 7 |
+
import json
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from typing import Any
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import torch.nn.functional as F
|
| 13 |
+
from accelerate import init_empty_weights
|
| 14 |
+
from accelerate.utils import set_module_tensor_to_device
|
| 15 |
+
from safetensors import safe_open
|
| 16 |
+
from transformers import AutoConfig, AutoProcessor, AutoTokenizer
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
EXPECTED_ARTIFACT_SHA256 = (
|
| 20 |
+
"719906b435800757d22013d3d475a4853d59b779b022669fa8b8a193b85d0f41"
|
| 21 |
+
)
|
| 22 |
+
EXPECTED_FORMAT_COUNTS = {"nvfp4": 224, "float8_e4m3fn": 14}
|
| 23 |
+
EXPECTED_PROJECTIONS = {
|
| 24 |
+
"mlp.down_proj",
|
| 25 |
+
"mlp.gate_proj",
|
| 26 |
+
"mlp.up_proj",
|
| 27 |
+
"self_attn.k_proj",
|
| 28 |
+
"self_attn.o_proj",
|
| 29 |
+
"self_attn.q_proj",
|
| 30 |
+
"self_attn.v_proj",
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def sha256(path: Path) -> str:
|
| 35 |
+
digest = hashlib.sha256()
|
| 36 |
+
with path.open("rb") as handle:
|
| 37 |
+
for chunk in iter(lambda: handle.read(1 << 20), b""):
|
| 38 |
+
digest.update(chunk)
|
| 39 |
+
return digest.hexdigest()
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def _decode_quant_config(tensor: torch.Tensor) -> dict[str, Any]:
|
| 43 |
+
payload = bytes(tensor.cpu().to(torch.uint8).tolist()).decode("utf-8")
|
| 44 |
+
parsed = json.loads(payload)
|
| 45 |
+
if not isinstance(parsed, dict) or not isinstance(parsed.get("format"), str):
|
| 46 |
+
raise ValueError(f"invalid comfy_quant payload: {payload!r}")
|
| 47 |
+
return parsed
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def _artifact_layer_to_hf_module(layer_key: str) -> str:
|
| 51 |
+
prefix = "model.layers."
|
| 52 |
+
if not layer_key.startswith(prefix):
|
| 53 |
+
raise ValueError(f"quantized layer is outside the language stack: {layer_key}")
|
| 54 |
+
remainder = layer_key[len(prefix) :]
|
| 55 |
+
layer_text, projection = remainder.split(".", 1)
|
| 56 |
+
layer_index = int(layer_text)
|
| 57 |
+
if layer_index < 0 or layer_index >= 36:
|
| 58 |
+
raise ValueError(f"language layer index is out of range: {layer_key}")
|
| 59 |
+
if projection not in EXPECTED_PROJECTIONS:
|
| 60 |
+
raise ValueError(f"unexpected quantized projection: {layer_key}")
|
| 61 |
+
return f"model.language_model.layers.{layer_index}.{projection}"
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def _artifact_weight_to_hf_name(key: str) -> str | None:
|
| 65 |
+
if key == "model.embed_tokens.weight":
|
| 66 |
+
return "model.language_model.embed_tokens.weight"
|
| 67 |
+
if key == "model.norm.weight":
|
| 68 |
+
return "model.language_model.norm.weight"
|
| 69 |
+
if key.startswith("model.layers."):
|
| 70 |
+
return "model.language_model.layers." + key.removeprefix("model.layers.")
|
| 71 |
+
if key.startswith("model.visual."):
|
| 72 |
+
return key
|
| 73 |
+
return None
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def _resolve_parent(module: torch.nn.Module, dotted_name: str) -> tuple[Any, str]:
|
| 77 |
+
parts = dotted_name.split(".")
|
| 78 |
+
parent: Any = module
|
| 79 |
+
for part in parts[:-1]:
|
| 80 |
+
parent = getattr(parent, part)
|
| 81 |
+
return parent, parts[-1]
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class PublishedQuantLinear(torch.nn.Module):
|
| 85 |
+
"""Inference-only projection backed by comfy-kitchen packed tensors."""
|
| 86 |
+
|
| 87 |
+
def __init__(
|
| 88 |
+
self,
|
| 89 |
+
*,
|
| 90 |
+
in_features: int,
|
| 91 |
+
out_features: int,
|
| 92 |
+
quant_format: str,
|
| 93 |
+
qdata: torch.Tensor,
|
| 94 |
+
weight_scale: torch.Tensor,
|
| 95 |
+
weight_scale_2: torch.Tensor | None,
|
| 96 |
+
) -> None:
|
| 97 |
+
super().__init__()
|
| 98 |
+
self.in_features = int(in_features)
|
| 99 |
+
self.out_features = int(out_features)
|
| 100 |
+
self.quant_format = str(quant_format)
|
| 101 |
+
self.register_buffer("qdata", qdata.clone().contiguous(), persistent=True)
|
| 102 |
+
self.register_buffer(
|
| 103 |
+
"weight_scale",
|
| 104 |
+
weight_scale.clone().contiguous(),
|
| 105 |
+
persistent=True,
|
| 106 |
+
)
|
| 107 |
+
if weight_scale_2 is None:
|
| 108 |
+
self.weight_scale_2 = None
|
| 109 |
+
else:
|
| 110 |
+
self.register_buffer(
|
| 111 |
+
"weight_scale_2",
|
| 112 |
+
weight_scale_2.clone().contiguous(),
|
| 113 |
+
persistent=True,
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
def _weight_quantized_tensor(self) -> Any:
|
| 117 |
+
from comfy_kitchen.tensor import (
|
| 118 |
+
QuantizedTensor,
|
| 119 |
+
TensorCoreFP8Layout,
|
| 120 |
+
TensorCoreNVFP4Layout,
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
shape = (self.out_features, self.in_features)
|
| 124 |
+
if self.quant_format == "nvfp4":
|
| 125 |
+
if self.weight_scale_2 is None:
|
| 126 |
+
raise RuntimeError("NVFP4 projection is missing its second scale")
|
| 127 |
+
params = TensorCoreNVFP4Layout.Params(
|
| 128 |
+
scale=self.weight_scale_2,
|
| 129 |
+
orig_dtype=torch.bfloat16,
|
| 130 |
+
orig_shape=shape,
|
| 131 |
+
block_scale=self.weight_scale,
|
| 132 |
+
)
|
| 133 |
+
return QuantizedTensor(
|
| 134 |
+
self.qdata,
|
| 135 |
+
"TensorCoreNVFP4Layout",
|
| 136 |
+
params,
|
| 137 |
+
)
|
| 138 |
+
if self.quant_format == "float8_e4m3fn":
|
| 139 |
+
params = TensorCoreFP8Layout.Params(
|
| 140 |
+
scale=self.weight_scale,
|
| 141 |
+
orig_dtype=torch.bfloat16,
|
| 142 |
+
orig_shape=shape,
|
| 143 |
+
)
|
| 144 |
+
return QuantizedTensor(
|
| 145 |
+
self.qdata,
|
| 146 |
+
"TensorCoreFP8Layout",
|
| 147 |
+
params,
|
| 148 |
+
)
|
| 149 |
+
raise ValueError(f"unsupported quantized format: {self.quant_format}")
|
| 150 |
+
|
| 151 |
+
def forward(self, value: torch.Tensor) -> torch.Tensor:
|
| 152 |
+
from comfy_kitchen.tensor import QuantizedTensor
|
| 153 |
+
|
| 154 |
+
input_shape = tuple(value.shape)
|
| 155 |
+
flattened = value.reshape(-1, input_shape[-1]).contiguous()
|
| 156 |
+
layout = (
|
| 157 |
+
"TensorCoreNVFP4Layout"
|
| 158 |
+
if self.quant_format == "nvfp4"
|
| 159 |
+
else "TensorCoreFP8Layout"
|
| 160 |
+
)
|
| 161 |
+
input_quantized = QuantizedTensor.from_float(flattened, layout)
|
| 162 |
+
output = F.linear(
|
| 163 |
+
input_quantized,
|
| 164 |
+
self._weight_quantized_tensor(),
|
| 165 |
+
None,
|
| 166 |
+
)
|
| 167 |
+
return output.reshape(*input_shape[:-1], self.out_features)
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
def _install_quantized_linears(
|
| 171 |
+
hf_module: torch.nn.Module,
|
| 172 |
+
artifact_path: Path,
|
| 173 |
+
) -> dict[str, Any]:
|
| 174 |
+
format_counts: Counter[str] = Counter()
|
| 175 |
+
installed: list[str] = []
|
| 176 |
+
storage_bytes = 0
|
| 177 |
+
original_bf16_bytes = 0
|
| 178 |
+
|
| 179 |
+
with safe_open(str(artifact_path), framework="pt", device="cpu") as handle:
|
| 180 |
+
config_keys = sorted(
|
| 181 |
+
key for key in handle.keys() if key.endswith(".comfy_quant")
|
| 182 |
+
)
|
| 183 |
+
for config_key in config_keys:
|
| 184 |
+
layer_key = config_key.removesuffix(".comfy_quant")
|
| 185 |
+
quant_format = _decode_quant_config(
|
| 186 |
+
handle.get_tensor(config_key)
|
| 187 |
+
)["format"]
|
| 188 |
+
module_name = _artifact_layer_to_hf_module(layer_key)
|
| 189 |
+
parent, leaf = _resolve_parent(hf_module, module_name)
|
| 190 |
+
original = getattr(parent, leaf)
|
| 191 |
+
if not isinstance(original, torch.nn.Linear):
|
| 192 |
+
raise TypeError(
|
| 193 |
+
f"{module_name}: expected torch.nn.Linear, got "
|
| 194 |
+
f"{type(original).__name__}"
|
| 195 |
+
)
|
| 196 |
+
if original.bias is not None:
|
| 197 |
+
raise ValueError(f"{module_name}: quantized projection has a bias")
|
| 198 |
+
|
| 199 |
+
qdata = handle.get_tensor(f"{layer_key}.weight")
|
| 200 |
+
weight_scale = handle.get_tensor(f"{layer_key}.weight_scale")
|
| 201 |
+
weight_scale_2 = (
|
| 202 |
+
handle.get_tensor(f"{layer_key}.weight_scale_2")
|
| 203 |
+
if quant_format == "nvfp4"
|
| 204 |
+
else None
|
| 205 |
+
)
|
| 206 |
+
replacement = PublishedQuantLinear(
|
| 207 |
+
in_features=original.in_features,
|
| 208 |
+
out_features=original.out_features,
|
| 209 |
+
quant_format=quant_format,
|
| 210 |
+
qdata=qdata,
|
| 211 |
+
weight_scale=weight_scale,
|
| 212 |
+
weight_scale_2=weight_scale_2,
|
| 213 |
+
)
|
| 214 |
+
setattr(parent, leaf, replacement)
|
| 215 |
+
format_counts[quant_format] += 1
|
| 216 |
+
installed.append(module_name)
|
| 217 |
+
original_bf16_bytes += (
|
| 218 |
+
original.in_features * original.out_features * 2
|
| 219 |
+
)
|
| 220 |
+
storage_bytes += replacement.qdata.nbytes
|
| 221 |
+
storage_bytes += replacement.weight_scale.nbytes
|
| 222 |
+
if replacement.weight_scale_2 is not None:
|
| 223 |
+
storage_bytes += replacement.weight_scale_2.nbytes
|
| 224 |
+
|
| 225 |
+
summary = {
|
| 226 |
+
"installed_module_count": len(installed),
|
| 227 |
+
"format_counts": dict(format_counts),
|
| 228 |
+
"original_projection_bf16_bytes": original_bf16_bytes,
|
| 229 |
+
"packed_projection_bytes": storage_bytes,
|
| 230 |
+
"projection_saving_bytes": original_bf16_bytes - storage_bytes,
|
| 231 |
+
"projection_saving_gib": (
|
| 232 |
+
(original_bf16_bytes - storage_bytes) / float(1 << 30)
|
| 233 |
+
),
|
| 234 |
+
}
|
| 235 |
+
if (
|
| 236 |
+
summary["installed_module_count"] != 238
|
| 237 |
+
or summary["format_counts"] != EXPECTED_FORMAT_COUNTS
|
| 238 |
+
):
|
| 239 |
+
raise RuntimeError(f"unexpected text quantization policy: {summary}")
|
| 240 |
+
return summary
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
def _load_nonquantized_weights(
|
| 244 |
+
hf_module: torch.nn.Module,
|
| 245 |
+
artifact_path: Path,
|
| 246 |
+
) -> dict[str, Any]:
|
| 247 |
+
loaded: list[str] = []
|
| 248 |
+
unexpected: list[str] = []
|
| 249 |
+
|
| 250 |
+
with safe_open(str(artifact_path), framework="pt", device="cpu") as handle:
|
| 251 |
+
for artifact_key in sorted(handle.keys()):
|
| 252 |
+
if (
|
| 253 |
+
artifact_key.endswith(".comfy_quant")
|
| 254 |
+
or artifact_key.endswith(".weight_scale")
|
| 255 |
+
or artifact_key.endswith(".weight_scale_2")
|
| 256 |
+
):
|
| 257 |
+
continue
|
| 258 |
+
target_name = _artifact_weight_to_hf_name(artifact_key)
|
| 259 |
+
if target_name is None:
|
| 260 |
+
unexpected.append(artifact_key)
|
| 261 |
+
continue
|
| 262 |
+
try:
|
| 263 |
+
parent, leaf = _resolve_parent(hf_module, target_name)
|
| 264 |
+
except AttributeError:
|
| 265 |
+
unexpected.append(artifact_key)
|
| 266 |
+
continue
|
| 267 |
+
current = getattr(parent, leaf, None)
|
| 268 |
+
if isinstance(current, PublishedQuantLinear):
|
| 269 |
+
continue
|
| 270 |
+
if target_name.endswith(".weight"):
|
| 271 |
+
projection_name = target_name.removesuffix(".weight")
|
| 272 |
+
try:
|
| 273 |
+
projection_parent, projection_leaf = _resolve_parent(
|
| 274 |
+
hf_module, projection_name
|
| 275 |
+
)
|
| 276 |
+
if isinstance(
|
| 277 |
+
getattr(projection_parent, projection_leaf),
|
| 278 |
+
PublishedQuantLinear,
|
| 279 |
+
):
|
| 280 |
+
continue
|
| 281 |
+
except AttributeError:
|
| 282 |
+
pass
|
| 283 |
+
set_module_tensor_to_device(
|
| 284 |
+
hf_module,
|
| 285 |
+
target_name,
|
| 286 |
+
"cpu",
|
| 287 |
+
value=handle.get_tensor(artifact_key),
|
| 288 |
+
)
|
| 289 |
+
loaded.append(target_name)
|
| 290 |
+
|
| 291 |
+
hf_module.tie_weights()
|
| 292 |
+
meta_parameters = [
|
| 293 |
+
name for name, value in hf_module.named_parameters() if value.is_meta
|
| 294 |
+
]
|
| 295 |
+
meta_buffers = [
|
| 296 |
+
name for name, value in hf_module.named_buffers() if value.is_meta
|
| 297 |
+
]
|
| 298 |
+
if meta_parameters or meta_buffers:
|
| 299 |
+
raise RuntimeError(
|
| 300 |
+
"packed text loader left unresolved meta tensors: "
|
| 301 |
+
f"{(meta_parameters + meta_buffers)[:4]}"
|
| 302 |
+
)
|
| 303 |
+
if unexpected:
|
| 304 |
+
raise RuntimeError(
|
| 305 |
+
f"packed text artifact contains unmapped tensors: {unexpected[:4]}"
|
| 306 |
+
)
|
| 307 |
+
return {
|
| 308 |
+
"loaded_nonquantized_tensor_count": len(loaded),
|
| 309 |
+
"unresolved_meta_parameters": meta_parameters,
|
| 310 |
+
"unresolved_meta_buffers": meta_buffers,
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def load_quantized_text_encoder(
|
| 315 |
+
*,
|
| 316 |
+
text_encoder_dir: str | Path,
|
| 317 |
+
artifact_path: str | Path,
|
| 318 |
+
tokenizer_max_length: int,
|
| 319 |
+
dit_structure: dict[str, Any],
|
| 320 |
+
use_packed_text_infer: bool,
|
| 321 |
+
) -> tuple[torch.nn.Module, dict[str, Any]]:
|
| 322 |
+
"""Construct Mage's text wrapper directly from the packaged quant artifact."""
|
| 323 |
+
|
| 324 |
+
from mage_flow.models.modules.text_encoder import (
|
| 325 |
+
CustomQwen3VLForConditionalGeneration,
|
| 326 |
+
TextEncoder,
|
| 327 |
+
)
|
| 328 |
+
|
| 329 |
+
text_encoder_dir = Path(text_encoder_dir).resolve()
|
| 330 |
+
artifact_path = Path(artifact_path).resolve()
|
| 331 |
+
if sha256(artifact_path) != EXPECTED_ARTIFACT_SHA256:
|
| 332 |
+
raise RuntimeError("packaged text-encoder artifact SHA-256 mismatch")
|
| 333 |
+
|
| 334 |
+
config = AutoConfig.from_pretrained(
|
| 335 |
+
str(text_encoder_dir),
|
| 336 |
+
local_files_only=True,
|
| 337 |
+
)
|
| 338 |
+
with init_empty_weights():
|
| 339 |
+
hf_module = CustomQwen3VLForConditionalGeneration._from_config(
|
| 340 |
+
config,
|
| 341 |
+
attn_implementation="flash_attention_2",
|
| 342 |
+
dtype=torch.bfloat16,
|
| 343 |
+
)
|
| 344 |
+
|
| 345 |
+
quant_summary = _install_quantized_linears(hf_module, artifact_path)
|
| 346 |
+
load_summary = _load_nonquantized_weights(hf_module, artifact_path)
|
| 347 |
+
|
| 348 |
+
text_encoder = TextEncoder.__new__(TextEncoder)
|
| 349 |
+
torch.nn.Module.__init__(text_encoder)
|
| 350 |
+
text_encoder.model_name = str(text_encoder_dir)
|
| 351 |
+
text_encoder.tokenizer_max_length = int(tokenizer_max_length)
|
| 352 |
+
text_encoder.tokenizer = AutoTokenizer.from_pretrained(
|
| 353 |
+
str(text_encoder_dir),
|
| 354 |
+
local_files_only=True,
|
| 355 |
+
)
|
| 356 |
+
text_encoder.tokenizer.padding_side = "right"
|
| 357 |
+
text_encoder.processor = AutoProcessor.from_pretrained(
|
| 358 |
+
str(text_encoder_dir),
|
| 359 |
+
local_files_only=True,
|
| 360 |
+
)
|
| 361 |
+
text_encoder.hf_module = hf_module.eval().requires_grad_(False)
|
| 362 |
+
text_encoder.prompt_template_encode = ""
|
| 363 |
+
text_encoder.prompt_template_encode_start_idx = 0
|
| 364 |
+
text_encoder.dit_structure = dict(dit_structure)
|
| 365 |
+
text_encoder.use_packed_text_infer = bool(use_packed_text_infer)
|
| 366 |
+
text_encoder.eval().requires_grad_(False)
|
| 367 |
+
|
| 368 |
+
return (
|
| 369 |
+
text_encoder,
|
| 370 |
+
{
|
| 371 |
+
"artifact": str(artifact_path),
|
| 372 |
+
"artifact_sha256": EXPECTED_ARTIFACT_SHA256,
|
| 373 |
+
"quantized": quant_summary,
|
| 374 |
+
"nonquantized": load_summary,
|
| 375 |
+
},
|
| 376 |
+
)
|
runtime/sm120_linear_op.cpp
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include <ATen/ATen.h>
|
| 2 |
+
#include <ATen/cuda/CUDAContext.h>
|
| 3 |
+
#include <c10/core/DeviceType.h>
|
| 4 |
+
#include <c10/cuda/CUDACachingAllocator.h>
|
| 5 |
+
#include <c10/cuda/CUDAException.h>
|
| 6 |
+
#include <c10/cuda/CUDAGuard.h>
|
| 7 |
+
#include <c10/cuda/CUDAStream.h>
|
| 8 |
+
#include <cuda_runtime_api.h>
|
| 9 |
+
#include <torch/library.h>
|
| 10 |
+
|
| 11 |
+
#include <cstdint>
|
| 12 |
+
#include <limits>
|
| 13 |
+
#include <memory>
|
| 14 |
+
#include <mutex>
|
| 15 |
+
#include <optional>
|
| 16 |
+
#include <stdexcept>
|
| 17 |
+
#include <unordered_map>
|
| 18 |
+
#include <utility>
|
| 19 |
+
#include <vector>
|
| 20 |
+
|
| 21 |
+
#include "nvfp4_linear.h"
|
| 22 |
+
|
| 23 |
+
namespace {
|
| 24 |
+
|
| 25 |
+
constexpr int kAbiVersion = 1;
|
| 26 |
+
constexpr int64_t kFp4BlockElements = 16;
|
| 27 |
+
constexpr int64_t kScaleTileOuter = 128;
|
| 28 |
+
constexpr int64_t kScaleTileInner = 4;
|
| 29 |
+
|
| 30 |
+
int64_t round_up_int64(int64_t value, int64_t multiple) {
|
| 31 |
+
TORCH_CHECK(value > 0, "round_up requires a positive value");
|
| 32 |
+
TORCH_CHECK(multiple > 0, "round_up requires a positive multiple");
|
| 33 |
+
TORCH_CHECK(
|
| 34 |
+
value <= std::numeric_limits<int64_t>::max() - (multiple - 1),
|
| 35 |
+
"round_up overflow");
|
| 36 |
+
return ((value + multiple - 1) / multiple) * multiple;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
int checked_int_arg(int64_t value, const char* label) {
|
| 40 |
+
TORCH_CHECK(value > 0, label, " must be positive");
|
| 41 |
+
TORCH_CHECK(
|
| 42 |
+
value <= static_cast<int64_t>(std::numeric_limits<int>::max()),
|
| 43 |
+
label,
|
| 44 |
+
" exceeds 32-bit ABI bound");
|
| 45 |
+
return static_cast<int>(value);
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
size_t expected_packed_weight_bytes(int out_features, int in_features) {
|
| 49 |
+
TORCH_CHECK(
|
| 50 |
+
out_features > 0 && out_features % 8 == 0,
|
| 51 |
+
"resident NVFP4 requires out_features divisible by 8, got ",
|
| 52 |
+
out_features);
|
| 53 |
+
TORCH_CHECK(
|
| 54 |
+
in_features > 0 && in_features % 32 == 0,
|
| 55 |
+
"resident NVFP4 requires in_features divisible by 32, got ",
|
| 56 |
+
in_features);
|
| 57 |
+
return (static_cast<size_t>(out_features) * static_cast<size_t>(in_features)) /
|
| 58 |
+
2;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
size_t expected_scale_bytes(int out_features, int in_features) {
|
| 62 |
+
TORCH_CHECK(
|
| 63 |
+
in_features > 0 && in_features % kFp4BlockElements == 0,
|
| 64 |
+
"resident NVFP4 requires in_features divisible by 16, got ",
|
| 65 |
+
in_features);
|
| 66 |
+
TORCH_CHECK(out_features > 0, "out_features must be positive");
|
| 67 |
+
const int64_t inner_dim =
|
| 68 |
+
round_up_int64(in_features / kFp4BlockElements, kScaleTileInner);
|
| 69 |
+
const int64_t outer_tiles =
|
| 70 |
+
(out_features + kScaleTileOuter - 1) / kScaleTileOuter;
|
| 71 |
+
return static_cast<size_t>(inner_dim) * static_cast<size_t>(outer_tiles) *
|
| 72 |
+
static_cast<size_t>(kScaleTileOuter);
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
[[noreturn]] void throw_abi_error(const char* operation) {
|
| 76 |
+
const char* message = mage_nvfp4_last_error();
|
| 77 |
+
TORCH_CHECK(
|
| 78 |
+
false,
|
| 79 |
+
operation,
|
| 80 |
+
": ",
|
| 81 |
+
message == nullptr ? "unknown native resident error" : message);
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
void ensure_abi_version() {
|
| 85 |
+
static std::once_flag once;
|
| 86 |
+
std::call_once(once, []() {
|
| 87 |
+
const int version = mage_nvfp4_abi_version();
|
| 88 |
+
TORCH_CHECK(
|
| 89 |
+
version == kAbiVersion,
|
| 90 |
+
"resident NVFP4 ABI mismatch: compiled torch op expects ",
|
| 91 |
+
kAbiVersion,
|
| 92 |
+
" but native library reports ",
|
| 93 |
+
version);
|
| 94 |
+
});
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
struct ContextKey {
|
| 98 |
+
int device_index = -1;
|
| 99 |
+
uintptr_t stream = 0;
|
| 100 |
+
|
| 101 |
+
bool operator==(const ContextKey& other) const noexcept {
|
| 102 |
+
return device_index == other.device_index && stream == other.stream;
|
| 103 |
+
}
|
| 104 |
+
};
|
| 105 |
+
|
| 106 |
+
struct ContextKeyHash {
|
| 107 |
+
size_t operator()(const ContextKey& key) const noexcept {
|
| 108 |
+
const size_t left = std::hash<int>{}(key.device_index);
|
| 109 |
+
const size_t right = std::hash<uintptr_t>{}(key.stream);
|
| 110 |
+
return left ^ (right + 0x9e3779b97f4a7c15ULL + (left << 6) + (left >> 2));
|
| 111 |
+
}
|
| 112 |
+
};
|
| 113 |
+
|
| 114 |
+
struct NativeContextDeleter {
|
| 115 |
+
void operator()(void* context) const noexcept {
|
| 116 |
+
if (context == nullptr) {
|
| 117 |
+
return;
|
| 118 |
+
}
|
| 119 |
+
(void)mage_nvfp4_destroy_context(context);
|
| 120 |
+
}
|
| 121 |
+
};
|
| 122 |
+
|
| 123 |
+
class ContextRegistry {
|
| 124 |
+
public:
|
| 125 |
+
void* get(int device_index, uintptr_t stream) {
|
| 126 |
+
std::lock_guard<std::mutex> guard(mutex_);
|
| 127 |
+
const ContextKey key{device_index, stream};
|
| 128 |
+
auto it = contexts_.find(key);
|
| 129 |
+
if (it != contexts_.end()) {
|
| 130 |
+
return it->second.get();
|
| 131 |
+
}
|
| 132 |
+
void* context = nullptr;
|
| 133 |
+
const int status = mage_nvfp4_create_context(device_index, &context);
|
| 134 |
+
if (status != 0) {
|
| 135 |
+
throw_abi_error("creating resident NVFP4 context");
|
| 136 |
+
}
|
| 137 |
+
auto inserted = contexts_.emplace(
|
| 138 |
+
key, std::unique_ptr<void, NativeContextDeleter>(context));
|
| 139 |
+
return inserted.first->second.get();
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
void clear() {
|
| 143 |
+
decltype(contexts_) retired;
|
| 144 |
+
{
|
| 145 |
+
std::lock_guard<std::mutex> guard(mutex_);
|
| 146 |
+
retired.swap(contexts_);
|
| 147 |
+
}
|
| 148 |
+
// Destruction synchronizes each bound stream through the resident ABI.
|
| 149 |
+
// Keep it outside the registry mutex.
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
private:
|
| 153 |
+
std::mutex mutex_;
|
| 154 |
+
std::unordered_map<
|
| 155 |
+
ContextKey,
|
| 156 |
+
std::unique_ptr<void, NativeContextDeleter>,
|
| 157 |
+
ContextKeyHash>
|
| 158 |
+
contexts_;
|
| 159 |
+
};
|
| 160 |
+
|
| 161 |
+
ContextRegistry& context_registry() {
|
| 162 |
+
// Deliberately avoid a static destructor that could call CUDA after runtime
|
| 163 |
+
// teardown. Long-lived processes must call the explicit close op; otherwise
|
| 164 |
+
// the OS reclaims these process resources at exit.
|
| 165 |
+
static ContextRegistry* registry = new ContextRegistry();
|
| 166 |
+
return *registry;
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
void clear_native_contexts() {
|
| 170 |
+
context_registry().clear();
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
void record_tensor_stream(
|
| 174 |
+
const at::Tensor& tensor, c10::cuda::CUDAStream stream) {
|
| 175 |
+
if (!tensor.defined() || !tensor.is_cuda()) {
|
| 176 |
+
return;
|
| 177 |
+
}
|
| 178 |
+
c10::cuda::CUDACachingAllocator::recordStream(
|
| 179 |
+
tensor.storage().data_ptr(), stream);
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
void validate_common(
|
| 183 |
+
const at::Tensor& input,
|
| 184 |
+
const at::Tensor& packed_weight,
|
| 185 |
+
const at::Tensor& weight_scales,
|
| 186 |
+
const at::Tensor& weight_scale,
|
| 187 |
+
const std::optional<at::Tensor>& bias,
|
| 188 |
+
int64_t in_features,
|
| 189 |
+
int64_t out_features) {
|
| 190 |
+
TORCH_CHECK(input.dim() >= 1, "resident NVFP4 input must have at least one dimension");
|
| 191 |
+
TORCH_CHECK(
|
| 192 |
+
input.scalar_type() == at::kBFloat16,
|
| 193 |
+
"resident NVFP4 input must be bfloat16");
|
| 194 |
+
TORCH_CHECK(!input.requires_grad(), "resident NVFP4 torch op is inference-only");
|
| 195 |
+
TORCH_CHECK(
|
| 196 |
+
input.size(-1) == in_features,
|
| 197 |
+
"resident NVFP4 expected last dimension ",
|
| 198 |
+
in_features,
|
| 199 |
+
" but got ",
|
| 200 |
+
input.size(-1));
|
| 201 |
+
TORCH_CHECK(
|
| 202 |
+
packed_weight.scalar_type() == at::kByte &&
|
| 203 |
+
packed_weight.dim() == 1 && packed_weight.is_contiguous(),
|
| 204 |
+
"packed_weight must be a contiguous 1D uint8 tensor");
|
| 205 |
+
TORCH_CHECK(
|
| 206 |
+
weight_scales.scalar_type() == at::kByte &&
|
| 207 |
+
weight_scales.dim() == 1 && weight_scales.is_contiguous(),
|
| 208 |
+
"weight_scales must be a contiguous 1D uint8 tensor");
|
| 209 |
+
TORCH_CHECK(
|
| 210 |
+
weight_scale.scalar_type() == at::kFloat &&
|
| 211 |
+
weight_scale.numel() == 1 && weight_scale.is_contiguous(),
|
| 212 |
+
"weight_scale must be one contiguous float32 value");
|
| 213 |
+
TORCH_CHECK(
|
| 214 |
+
packed_weight.device() == input.device(),
|
| 215 |
+
"packed_weight and input must be on the same device");
|
| 216 |
+
TORCH_CHECK(
|
| 217 |
+
weight_scales.device() == input.device(),
|
| 218 |
+
"weight_scales and input must be on the same device");
|
| 219 |
+
TORCH_CHECK(
|
| 220 |
+
weight_scale.device() == input.device(),
|
| 221 |
+
"weight_scale and input must be on the same device");
|
| 222 |
+
if (bias.has_value()) {
|
| 223 |
+
const at::Tensor& bias_value = *bias;
|
| 224 |
+
TORCH_CHECK(
|
| 225 |
+
bias_value.scalar_type() == at::kBFloat16 &&
|
| 226 |
+
bias_value.dim() == 1 && bias_value.is_contiguous(),
|
| 227 |
+
"bias must be a contiguous 1D bfloat16 tensor");
|
| 228 |
+
TORCH_CHECK(
|
| 229 |
+
bias_value.numel() == out_features,
|
| 230 |
+
"bias length must match out_features");
|
| 231 |
+
TORCH_CHECK(
|
| 232 |
+
bias_value.device() == input.device(),
|
| 233 |
+
"bias and input must be on the same device");
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
const int in_features_i = checked_int_arg(in_features, "in_features");
|
| 237 |
+
const int out_features_i = checked_int_arg(out_features, "out_features");
|
| 238 |
+
const size_t expected_weight = expected_packed_weight_bytes(
|
| 239 |
+
out_features_i, in_features_i);
|
| 240 |
+
const size_t expected_scales = expected_scale_bytes(
|
| 241 |
+
out_features_i, in_features_i);
|
| 242 |
+
TORCH_CHECK(
|
| 243 |
+
static_cast<size_t>(packed_weight.numel()) == expected_weight,
|
| 244 |
+
"packed_weight size mismatch: expected ",
|
| 245 |
+
expected_weight,
|
| 246 |
+
" bytes but got ",
|
| 247 |
+
packed_weight.numel());
|
| 248 |
+
TORCH_CHECK(
|
| 249 |
+
static_cast<size_t>(weight_scales.numel()) == expected_scales,
|
| 250 |
+
"weight_scales size mismatch: expected ",
|
| 251 |
+
expected_scales,
|
| 252 |
+
" bytes but got ",
|
| 253 |
+
weight_scales.numel());
|
| 254 |
+
|
| 255 |
+
const size_t native_weight =
|
| 256 |
+
mage_nvfp4_packed_weight_bytes(out_features_i, in_features_i);
|
| 257 |
+
const size_t native_scales =
|
| 258 |
+
mage_nvfp4_weight_scale_bytes(out_features_i, in_features_i);
|
| 259 |
+
TORCH_CHECK(
|
| 260 |
+
native_weight == expected_weight,
|
| 261 |
+
"native resident library packed-weight metadata disagrees with torch op");
|
| 262 |
+
TORCH_CHECK(
|
| 263 |
+
native_scales == expected_scales,
|
| 264 |
+
"native resident library scale metadata disagrees with torch op");
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
at::Tensor sm120_linear_native_cuda(
|
| 268 |
+
const at::Tensor& input,
|
| 269 |
+
const at::Tensor& packed_weight,
|
| 270 |
+
const at::Tensor& weight_scales,
|
| 271 |
+
const at::Tensor& weight_scale,
|
| 272 |
+
const std::optional<at::Tensor>& bias,
|
| 273 |
+
int64_t in_features,
|
| 274 |
+
int64_t out_features) {
|
| 275 |
+
ensure_abi_version();
|
| 276 |
+
validate_common(
|
| 277 |
+
input, packed_weight, weight_scales, weight_scale, bias, in_features,
|
| 278 |
+
out_features);
|
| 279 |
+
|
| 280 |
+
TORCH_CHECK(input.is_cuda(), "resident NVFP4 CUDA implementation requires a CUDA input");
|
| 281 |
+
const auto device = input.device();
|
| 282 |
+
c10::cuda::CUDAGuard guard(device);
|
| 283 |
+
const int device_index = device.index();
|
| 284 |
+
cudaDeviceProp properties{};
|
| 285 |
+
C10_CUDA_CHECK(cudaGetDeviceProperties(&properties, device_index));
|
| 286 |
+
TORCH_CHECK(
|
| 287 |
+
properties.major == 12 && properties.minor == 0,
|
| 288 |
+
"resident NVFP4 CUDA implementation is labeled sm_120-only; selected device reports ",
|
| 289 |
+
properties.major,
|
| 290 |
+
".",
|
| 291 |
+
properties.minor);
|
| 292 |
+
|
| 293 |
+
const c10::cuda::CUDAStream stream =
|
| 294 |
+
c10::cuda::getCurrentCUDAStream(device_index);
|
| 295 |
+
TORCH_CHECK(
|
| 296 |
+
!stream.is_capturing(),
|
| 297 |
+
"resident NVFP4 C ABI torch op does not support CUDA graph capture");
|
| 298 |
+
|
| 299 |
+
const int in_features_i = checked_int_arg(in_features, "in_features");
|
| 300 |
+
const int out_features_i = checked_int_arg(out_features, "out_features");
|
| 301 |
+
at::Tensor contiguous =
|
| 302 |
+
input.reshape({-1, in_features_i}).contiguous();
|
| 303 |
+
const int64_t logical_m64 = contiguous.size(0);
|
| 304 |
+
TORCH_CHECK(logical_m64 > 0, "resident NVFP4 torch op does not support empty inputs");
|
| 305 |
+
const int logical_m = checked_int_arg(logical_m64, "logical_m");
|
| 306 |
+
const int64_t padded_m64 = round_up_int64(logical_m64, 8);
|
| 307 |
+
at::Tensor padded_output = at::empty(
|
| 308 |
+
{padded_m64, out_features_i},
|
| 309 |
+
input.options().dtype(at::kBFloat16));
|
| 310 |
+
|
| 311 |
+
record_tensor_stream(contiguous, stream);
|
| 312 |
+
record_tensor_stream(packed_weight, stream);
|
| 313 |
+
record_tensor_stream(weight_scales, stream);
|
| 314 |
+
record_tensor_stream(weight_scale, stream);
|
| 315 |
+
if (bias.has_value()) {
|
| 316 |
+
record_tensor_stream(*bias, stream);
|
| 317 |
+
}
|
| 318 |
+
record_tensor_stream(padded_output, stream);
|
| 319 |
+
|
| 320 |
+
void* context =
|
| 321 |
+
context_registry().get(device_index, reinterpret_cast<uintptr_t>(stream.stream()));
|
| 322 |
+
const void* bias_pointer =
|
| 323 |
+
bias.has_value() ? bias->data_ptr() : nullptr;
|
| 324 |
+
const int status = mage_nvfp4_linear_forward(
|
| 325 |
+
context,
|
| 326 |
+
contiguous.data_ptr(),
|
| 327 |
+
packed_weight.data_ptr(),
|
| 328 |
+
static_cast<size_t>(packed_weight.numel()),
|
| 329 |
+
weight_scales.data_ptr(),
|
| 330 |
+
static_cast<size_t>(weight_scales.numel()),
|
| 331 |
+
weight_scale.data_ptr(),
|
| 332 |
+
bias_pointer,
|
| 333 |
+
padded_output.data_ptr(),
|
| 334 |
+
logical_m,
|
| 335 |
+
in_features_i,
|
| 336 |
+
out_features_i,
|
| 337 |
+
reinterpret_cast<uintptr_t>(stream.stream()));
|
| 338 |
+
if (status != 0) {
|
| 339 |
+
throw_abi_error("running resident NVFP4 linear forward");
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
at::Tensor logical = padded_output.narrow(0, 0, logical_m64);
|
| 343 |
+
std::vector<int64_t> output_sizes = input.sizes().vec();
|
| 344 |
+
output_sizes.back() = out_features_i;
|
| 345 |
+
return logical.view(output_sizes);
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
} // namespace
|
| 349 |
+
|
| 350 |
+
TORCH_LIBRARY_FRAGMENT(mage_nvfp4, m) {
|
| 351 |
+
m.def(
|
| 352 |
+
"sm120_linear_native(Tensor input, Tensor packed_weight, Tensor weight_scales, Tensor weight_scale, Tensor? bias, int in_features, int out_features) -> Tensor");
|
| 353 |
+
m.def("clear_native_contexts() -> ()", TORCH_FN(clear_native_contexts));
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
TORCH_LIBRARY_IMPL(mage_nvfp4, CUDA, m) {
|
| 357 |
+
m.impl("sm120_linear_native", TORCH_FN(sm120_linear_native_cuda));
|
| 358 |
+
}
|
runtime/standard_transformer.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Load the complete sharded Mage-Flow NVFP4 transformer component."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from contextlib import ExitStack
|
| 6 |
+
import json
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from typing import Any
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
import torch.nn as nn
|
| 12 |
+
from safetensors import safe_open
|
| 13 |
+
|
| 14 |
+
from packed_artifact import (
|
| 15 |
+
assign_tensor_by_name,
|
| 16 |
+
build_target_specs,
|
| 17 |
+
instantiate_mage_transformer_on_meta,
|
| 18 |
+
materialize_mage_rope_tensor_attributes,
|
| 19 |
+
set_child_module,
|
| 20 |
+
unregistered_meta_tensor_attribute_names,
|
| 21 |
+
)
|
| 22 |
+
from torch_ops_native import (
|
| 23 |
+
PackedNvfp4LinearNativeOp,
|
| 24 |
+
initialize_native_sm120_op,
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class StandardCheckpointError(RuntimeError):
|
| 29 |
+
pass
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def fail(message: str) -> None:
|
| 33 |
+
raise StandardCheckpointError(message)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def read_object(path: Path) -> dict[str, Any]:
|
| 37 |
+
try:
|
| 38 |
+
value = json.loads(path.read_text(encoding="utf-8"))
|
| 39 |
+
except (OSError, json.JSONDecodeError) as exc:
|
| 40 |
+
fail(f"cannot read JSON object {path}: {exc}")
|
| 41 |
+
if not isinstance(value, dict):
|
| 42 |
+
fail(f"expected a JSON object: {path}")
|
| 43 |
+
return value
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def _component_path(component_dir: Path, relative: str) -> Path:
|
| 47 |
+
path = (component_dir / relative).resolve()
|
| 48 |
+
if not path.is_relative_to(component_dir):
|
| 49 |
+
fail(f"checkpoint index path escapes transformer component: {relative}")
|
| 50 |
+
if not path.is_file():
|
| 51 |
+
fail(f"checkpoint shard is missing: {relative}")
|
| 52 |
+
return path
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def _quantized_keys(module_key: str) -> dict[str, str]:
|
| 56 |
+
return {
|
| 57 |
+
"packed_weight": f"{module_key}.packed_weight",
|
| 58 |
+
"weight_scales": f"{module_key}.weight_scales",
|
| 59 |
+
"weight_scale": f"{module_key}.weight_scale",
|
| 60 |
+
"bias": f"{module_key}.bias",
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def load_standard_native_transformer(
|
| 65 |
+
repo_root: str | Path,
|
| 66 |
+
device: torch.device,
|
| 67 |
+
) -> tuple[nn.Module, dict[str, Any]]:
|
| 68 |
+
"""Load a complete standard-layout component without a BF16 base download."""
|
| 69 |
+
|
| 70 |
+
repo_root = Path(repo_root).resolve()
|
| 71 |
+
component_dir = (repo_root / "transformer").resolve()
|
| 72 |
+
if not component_dir.is_relative_to(repo_root) or not component_dir.is_dir():
|
| 73 |
+
fail("repository has no transformer component")
|
| 74 |
+
if device.type != "cuda":
|
| 75 |
+
fail("the native resident transformer requires a CUDA destination")
|
| 76 |
+
if not initialize_native_sm120_op(allow_python_schema_fallback=False):
|
| 77 |
+
fail("compiled native SM120 torch op did not load")
|
| 78 |
+
|
| 79 |
+
config = read_object(component_dir / "config.json")
|
| 80 |
+
quant_config = config.get("quantization_config")
|
| 81 |
+
if not isinstance(quant_config, dict):
|
| 82 |
+
fail("transformer config has no quantization_config")
|
| 83 |
+
if quant_config.get("quant_method") != "mage_flow_nvfp4":
|
| 84 |
+
fail(
|
| 85 |
+
"unexpected transformer quantization method: "
|
| 86 |
+
f"{quant_config.get('quant_method')!r}"
|
| 87 |
+
)
|
| 88 |
+
if quant_config.get("quant_algo") != "NVFP4":
|
| 89 |
+
fail("transformer config does not declare NVFP4")
|
| 90 |
+
|
| 91 |
+
depth = int(config.get("depth", 0))
|
| 92 |
+
target_specs = build_target_specs(depth)
|
| 93 |
+
expected_targets = [spec.module_key for spec in target_specs]
|
| 94 |
+
if quant_config.get("targets") != expected_targets:
|
| 95 |
+
fail("transformer quantization target list is not canonical")
|
| 96 |
+
|
| 97 |
+
metadata = read_object(component_dir / "nvfp4_metadata.json")
|
| 98 |
+
if metadata.get("artifact_kind") != (
|
| 99 |
+
"mage_flow_transformer_mlp_nvfp4_resident_v1"
|
| 100 |
+
):
|
| 101 |
+
fail("unexpected transformer NVFP4 metadata kind")
|
| 102 |
+
non_target_keys = metadata.get("non_target_keys")
|
| 103 |
+
if not isinstance(non_target_keys, list) or not all(
|
| 104 |
+
isinstance(key, str) for key in non_target_keys
|
| 105 |
+
):
|
| 106 |
+
fail("transformer NVFP4 metadata has no non-target key list")
|
| 107 |
+
|
| 108 |
+
index = read_object(
|
| 109 |
+
component_dir / "diffusion_pytorch_model.safetensors.index.json"
|
| 110 |
+
)
|
| 111 |
+
weight_map = index.get("weight_map")
|
| 112 |
+
if not isinstance(weight_map, dict) or not all(
|
| 113 |
+
isinstance(key, str) and isinstance(value, str)
|
| 114 |
+
for key, value in weight_map.items()
|
| 115 |
+
):
|
| 116 |
+
fail("transformer checkpoint has no valid weight map")
|
| 117 |
+
|
| 118 |
+
quantized_keys = {
|
| 119 |
+
key
|
| 120 |
+
for spec in target_specs
|
| 121 |
+
for key in _quantized_keys(spec.module_key).values()
|
| 122 |
+
}
|
| 123 |
+
expected_keys = set(non_target_keys) | quantized_keys
|
| 124 |
+
actual_keys = set(weight_map)
|
| 125 |
+
if actual_keys != expected_keys:
|
| 126 |
+
missing = sorted(expected_keys - actual_keys)
|
| 127 |
+
unexpected = sorted(actual_keys - expected_keys)
|
| 128 |
+
fail(
|
| 129 |
+
"transformer checkpoint key coverage mismatch; "
|
| 130 |
+
f"missing={missing[:1]}, unexpected={unexpected[:1]}"
|
| 131 |
+
)
|
| 132 |
+
original_target_weights = {spec.weight_key for spec in target_specs}
|
| 133 |
+
leaked = sorted(actual_keys & original_target_weights)
|
| 134 |
+
if leaked:
|
| 135 |
+
fail(f"BF16 target weight leaked into quantized checkpoint: {leaked[0]}")
|
| 136 |
+
|
| 137 |
+
shard_names = sorted(set(weight_map.values()))
|
| 138 |
+
with ExitStack() as stack:
|
| 139 |
+
handles = {
|
| 140 |
+
name: stack.enter_context(
|
| 141 |
+
safe_open(
|
| 142 |
+
_component_path(component_dir, name),
|
| 143 |
+
framework="pt",
|
| 144 |
+
device="cpu",
|
| 145 |
+
)
|
| 146 |
+
)
|
| 147 |
+
for name in shard_names
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
def tensor(key: str) -> torch.Tensor:
|
| 151 |
+
try:
|
| 152 |
+
handle = handles[weight_map[key]]
|
| 153 |
+
except KeyError:
|
| 154 |
+
fail(f"tensor is absent from checkpoint index: {key}")
|
| 155 |
+
if key not in handle.keys():
|
| 156 |
+
fail(f"tensor is absent from its declared shard: {key}")
|
| 157 |
+
return handle.get_tensor(key)
|
| 158 |
+
|
| 159 |
+
model = instantiate_mage_transformer_on_meta(repo_root)
|
| 160 |
+
for spec in target_specs:
|
| 161 |
+
original = model.get_submodule(spec.module_key)
|
| 162 |
+
if not isinstance(original, nn.Linear):
|
| 163 |
+
fail(
|
| 164 |
+
f"expected target {spec.module_key} to be nn.Linear, "
|
| 165 |
+
f"found {type(original).__name__}"
|
| 166 |
+
)
|
| 167 |
+
keys = _quantized_keys(spec.module_key)
|
| 168 |
+
replacement = PackedNvfp4LinearNativeOp(
|
| 169 |
+
in_features=int(original.in_features),
|
| 170 |
+
out_features=int(original.out_features),
|
| 171 |
+
packed_weight=tensor(keys["packed_weight"]).to(device),
|
| 172 |
+
weight_scales=tensor(keys["weight_scales"]).to(device),
|
| 173 |
+
weight_scale=tensor(keys["weight_scale"]).to(device),
|
| 174 |
+
bias=tensor(keys["bias"]).to(device),
|
| 175 |
+
)
|
| 176 |
+
set_child_module(model, spec.module_key, replacement)
|
| 177 |
+
|
| 178 |
+
loaded_non_targets: list[str] = []
|
| 179 |
+
for key in non_target_keys:
|
| 180 |
+
assign_tensor_by_name(model, key, tensor(key).to(device))
|
| 181 |
+
loaded_non_targets.append(key)
|
| 182 |
+
|
| 183 |
+
materialized = materialize_mage_rope_tensor_attributes(model)
|
| 184 |
+
meta_parameters = [
|
| 185 |
+
name for name, value in model.named_parameters() if value.is_meta
|
| 186 |
+
]
|
| 187 |
+
meta_buffers = [
|
| 188 |
+
name for name, value in model.named_buffers() if value.is_meta
|
| 189 |
+
]
|
| 190 |
+
unregistered_meta = unregistered_meta_tensor_attribute_names(model)
|
| 191 |
+
if meta_parameters or meta_buffers or unregistered_meta:
|
| 192 |
+
fail(
|
| 193 |
+
"standard transformer loader left unresolved meta tensors: "
|
| 194 |
+
f"{(meta_parameters + meta_buffers + unregistered_meta)[:4]}"
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
report = {
|
| 198 |
+
"layout": "huggingface_sharded_component",
|
| 199 |
+
"checkpoint_shard_count": len(shard_names),
|
| 200 |
+
"checkpoint_tensor_count": len(actual_keys),
|
| 201 |
+
"loaded_non_target_tensor_count": len(loaded_non_targets),
|
| 202 |
+
"loaded_quantized_projection_count": len(target_specs),
|
| 203 |
+
"bf16_target_weight_reads": 0,
|
| 204 |
+
"meta_parameter_names": meta_parameters,
|
| 205 |
+
"meta_buffer_names": meta_buffers,
|
| 206 |
+
"materialized_unregistered_tensor_attribute_names": materialized,
|
| 207 |
+
}
|
| 208 |
+
return model.eval().requires_grad_(False), report
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
__all__ = [
|
| 212 |
+
"StandardCheckpointError",
|
| 213 |
+
"load_standard_native_transformer",
|
| 214 |
+
]
|
runtime/torch_ops_native.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Loader and fake/meta shim for the compiled resident NVFP4 torch op.
|
| 2 |
+
|
| 3 |
+
The compiled bridge lives in ``native/torch_op`` and, when built, moves CUDA
|
| 4 |
+
execution out of Python+ctypes and into a C++ dispatcher implementation that
|
| 5 |
+
calls the existing resident C ABI directly.
|
| 6 |
+
|
| 7 |
+
This shim keeps CPU/meta tests lightweight:
|
| 8 |
+
|
| 9 |
+
- if the compiled bridge exists, load it first;
|
| 10 |
+
- otherwise define a temporary Python schema fallback for shape-only tests;
|
| 11 |
+
- always register fake/meta implementations in Python.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
|
| 16 |
+
import atexit
|
| 17 |
+
from pathlib import Path
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
|
| 21 |
+
from packed_nvfp4_linear import PackedNvfp4Linear, scale_layout
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
RELEASE_ROOT = Path(__file__).resolve().parents[1]
|
| 25 |
+
DEFAULT_EXTENSION_PATH = RELEASE_ROOT / "runtime" / "libmage_nvfp4_torch_op.so"
|
| 26 |
+
|
| 27 |
+
OP_NAMESPACE = "mage_nvfp4"
|
| 28 |
+
OP_NAME = "sm120_linear_native"
|
| 29 |
+
OP_QUALNAME = f"{OP_NAMESPACE}::{OP_NAME}"
|
| 30 |
+
|
| 31 |
+
_SCHEMA_FALLBACK_LIB: torch.library.Library | None = None
|
| 32 |
+
_META_LIB: torch.library.Library | None = None
|
| 33 |
+
_EXTENSION_LOADED = False
|
| 34 |
+
_FAKE_REGISTERED = False
|
| 35 |
+
_META_REGISTERED = False
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def _expected_weight_bytes(out_features: int, in_features: int) -> int:
|
| 39 |
+
if out_features <= 0 or out_features % 8:
|
| 40 |
+
raise ValueError("resident NVFP4 requires out_features divisible by 8")
|
| 41 |
+
if in_features <= 0 or in_features % 32:
|
| 42 |
+
raise ValueError("resident NVFP4 requires in_features divisible by 32")
|
| 43 |
+
return (out_features * in_features) // 2
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def _check_common_shapes(
|
| 47 |
+
input: torch.Tensor,
|
| 48 |
+
packed_weight: torch.Tensor,
|
| 49 |
+
weight_scales: torch.Tensor,
|
| 50 |
+
weight_scale: torch.Tensor,
|
| 51 |
+
bias: torch.Tensor | None,
|
| 52 |
+
in_features: int,
|
| 53 |
+
out_features: int,
|
| 54 |
+
) -> None:
|
| 55 |
+
if input.ndim < 1:
|
| 56 |
+
raise ValueError("resident NVFP4 input must have at least one dimension")
|
| 57 |
+
if input.dtype != torch.bfloat16:
|
| 58 |
+
raise TypeError("resident NVFP4 input must be bfloat16")
|
| 59 |
+
if input.requires_grad:
|
| 60 |
+
raise RuntimeError("resident NVFP4 torch op is inference-only")
|
| 61 |
+
if int(input.shape[-1]) != int(in_features):
|
| 62 |
+
raise ValueError(
|
| 63 |
+
f"expected input last dimension {in_features}, got {tuple(input.shape)}"
|
| 64 |
+
)
|
| 65 |
+
if packed_weight.dtype != torch.uint8 or packed_weight.ndim != 1:
|
| 66 |
+
raise ValueError("packed_weight must be a 1D uint8 tensor")
|
| 67 |
+
if weight_scales.dtype != torch.uint8 or weight_scales.ndim != 1:
|
| 68 |
+
raise ValueError("weight_scales must be a 1D uint8 tensor")
|
| 69 |
+
if weight_scale.dtype != torch.float32 or weight_scale.numel() != 1:
|
| 70 |
+
raise ValueError("weight_scale must be a scalar or length-1 float32 tensor")
|
| 71 |
+
if bias is not None and (
|
| 72 |
+
bias.dtype != torch.bfloat16 or bias.ndim != 1 or int(bias.numel()) != int(out_features)
|
| 73 |
+
):
|
| 74 |
+
raise ValueError("bias must be a 1D bfloat16 tensor with length out_features")
|
| 75 |
+
for tensor in (packed_weight, weight_scales, weight_scale, bias):
|
| 76 |
+
if tensor is not None and tensor.device != input.device:
|
| 77 |
+
raise ValueError("input and resident buffers must be on the same device")
|
| 78 |
+
if tensor is not None and not tensor.is_contiguous():
|
| 79 |
+
raise ValueError("resident packed buffers must be contiguous")
|
| 80 |
+
|
| 81 |
+
expected_weight_bytes = _expected_weight_bytes(int(out_features), int(in_features))
|
| 82 |
+
expected_scale_bytes = scale_layout(int(in_features), int(out_features)).num_bytes
|
| 83 |
+
if int(packed_weight.numel()) != expected_weight_bytes:
|
| 84 |
+
raise ValueError(
|
| 85 |
+
f"packed_weight size mismatch: expected {expected_weight_bytes}, got {packed_weight.numel()}"
|
| 86 |
+
)
|
| 87 |
+
if int(weight_scales.numel()) != expected_scale_bytes:
|
| 88 |
+
raise ValueError(
|
| 89 |
+
f"weight_scales size mismatch: expected {expected_scale_bytes}, got {weight_scales.numel()}"
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def _logical_output(input: torch.Tensor, out_features: int) -> torch.Tensor:
|
| 94 |
+
return input.new_empty((*input.shape[:-1], int(out_features)), dtype=torch.bfloat16)
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def ensure_native_sm120_schema(
|
| 98 |
+
*,
|
| 99 |
+
extension_path: str | Path = DEFAULT_EXTENSION_PATH,
|
| 100 |
+
allow_python_schema_fallback: bool = True,
|
| 101 |
+
) -> bool:
|
| 102 |
+
global _EXTENSION_LOADED, _SCHEMA_FALLBACK_LIB
|
| 103 |
+
extension_path = Path(extension_path)
|
| 104 |
+
if not _EXTENSION_LOADED and extension_path.is_file():
|
| 105 |
+
torch.ops.load_library(str(extension_path.resolve()))
|
| 106 |
+
_EXTENSION_LOADED = True
|
| 107 |
+
return True
|
| 108 |
+
if _EXTENSION_LOADED:
|
| 109 |
+
return True
|
| 110 |
+
if not allow_python_schema_fallback:
|
| 111 |
+
return False
|
| 112 |
+
if _SCHEMA_FALLBACK_LIB is None:
|
| 113 |
+
lib = torch.library.Library(OP_NAMESPACE, "FRAGMENT")
|
| 114 |
+
lib.define(
|
| 115 |
+
"sm120_linear_native(Tensor input, Tensor packed_weight, Tensor weight_scales, Tensor weight_scale, Tensor? bias, int in_features, int out_features) -> Tensor"
|
| 116 |
+
)
|
| 117 |
+
lib.define("clear_native_contexts() -> ()")
|
| 118 |
+
_SCHEMA_FALLBACK_LIB = lib
|
| 119 |
+
return False
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def _register_meta_impl() -> None:
|
| 123 |
+
global _META_LIB, _META_REGISTERED
|
| 124 |
+
if _META_REGISTERED:
|
| 125 |
+
return
|
| 126 |
+
_META_LIB = torch.library.Library(OP_NAMESPACE, "IMPL", "Meta")
|
| 127 |
+
_META_LIB.impl(
|
| 128 |
+
OP_NAME,
|
| 129 |
+
lambda input, packed_weight, weight_scales, weight_scale, bias, in_features, out_features: (
|
| 130 |
+
_check_common_shapes(
|
| 131 |
+
input,
|
| 132 |
+
packed_weight,
|
| 133 |
+
weight_scales,
|
| 134 |
+
weight_scale,
|
| 135 |
+
bias,
|
| 136 |
+
in_features,
|
| 137 |
+
out_features,
|
| 138 |
+
),
|
| 139 |
+
_logical_output(input, int(out_features)),
|
| 140 |
+
)[1],
|
| 141 |
+
)
|
| 142 |
+
_META_REGISTERED = True
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def _register_fake_impl() -> None:
|
| 146 |
+
global _FAKE_REGISTERED
|
| 147 |
+
if _FAKE_REGISTERED:
|
| 148 |
+
return
|
| 149 |
+
|
| 150 |
+
@torch.library.register_fake(OP_QUALNAME)
|
| 151 |
+
def _fake(
|
| 152 |
+
input: torch.Tensor,
|
| 153 |
+
packed_weight: torch.Tensor,
|
| 154 |
+
weight_scales: torch.Tensor,
|
| 155 |
+
weight_scale: torch.Tensor,
|
| 156 |
+
bias: torch.Tensor | None,
|
| 157 |
+
in_features: int,
|
| 158 |
+
out_features: int,
|
| 159 |
+
) -> torch.Tensor:
|
| 160 |
+
_check_common_shapes(
|
| 161 |
+
input,
|
| 162 |
+
packed_weight,
|
| 163 |
+
weight_scales,
|
| 164 |
+
weight_scale,
|
| 165 |
+
bias,
|
| 166 |
+
in_features,
|
| 167 |
+
out_features,
|
| 168 |
+
)
|
| 169 |
+
return _logical_output(input, int(out_features))
|
| 170 |
+
|
| 171 |
+
_FAKE_REGISTERED = True
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def initialize_native_sm120_op(
|
| 175 |
+
*,
|
| 176 |
+
extension_path: str | Path = DEFAULT_EXTENSION_PATH,
|
| 177 |
+
allow_python_schema_fallback: bool = True,
|
| 178 |
+
) -> bool:
|
| 179 |
+
loaded = ensure_native_sm120_schema(
|
| 180 |
+
extension_path=extension_path,
|
| 181 |
+
allow_python_schema_fallback=allow_python_schema_fallback,
|
| 182 |
+
)
|
| 183 |
+
_register_meta_impl()
|
| 184 |
+
_register_fake_impl()
|
| 185 |
+
return loaded
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def close_native_contexts() -> None:
|
| 189 |
+
"""Synchronize and release native contexts when the compiled bridge is loaded."""
|
| 190 |
+
if _EXTENSION_LOADED:
|
| 191 |
+
torch.ops.mage_nvfp4.clear_native_contexts()
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def _quiet_atexit_close() -> None:
|
| 195 |
+
try:
|
| 196 |
+
close_native_contexts()
|
| 197 |
+
except Exception:
|
| 198 |
+
# CUDA may already be shutting down. Explicit close_native_contexts()
|
| 199 |
+
# is the auditable path; atexit is only a best-effort fallback.
|
| 200 |
+
pass
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
class PackedNvfp4LinearNativeOp(PackedNvfp4Linear):
|
| 204 |
+
"""Thin wrapper over resident packed buffers using the native torch op."""
|
| 205 |
+
|
| 206 |
+
def forward(self, input: torch.Tensor) -> torch.Tensor:
|
| 207 |
+
if input.device.type not in {"cuda", "meta"}:
|
| 208 |
+
raise ValueError("PackedNvfp4LinearNativeOp requires a CUDA or meta input")
|
| 209 |
+
return torch.ops.mage_nvfp4.sm120_linear_native(
|
| 210 |
+
input,
|
| 211 |
+
self.packed_weight,
|
| 212 |
+
self.weight_scales,
|
| 213 |
+
self.weight_scale,
|
| 214 |
+
self.bias,
|
| 215 |
+
self.in_features,
|
| 216 |
+
self.out_features,
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
initialize_native_sm120_op()
|
| 221 |
+
atexit.register(_quiet_atexit_close)
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
__all__ = [
|
| 225 |
+
"DEFAULT_EXTENSION_PATH",
|
| 226 |
+
"OP_NAME",
|
| 227 |
+
"OP_NAMESPACE",
|
| 228 |
+
"OP_QUALNAME",
|
| 229 |
+
"PackedNvfp4LinearNativeOp",
|
| 230 |
+
"close_native_contexts",
|
| 231 |
+
"ensure_native_sm120_schema",
|
| 232 |
+
"initialize_native_sm120_op",
|
| 233 |
+
]
|
scheduler/scheduler_config.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_class_name": "FlowMatchEulerDiscreteScheduler",
|
| 3 |
+
"_diffusers_version": "0.37.0",
|
| 4 |
+
"num_train_timesteps": 1000,
|
| 5 |
+
"use_dynamic_shifting": false,
|
| 6 |
+
"shift": 6.0
|
| 7 |
+
}
|
text_encoder/chat_template.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"chat_template": "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0].role == 'system' %}\n {%- if messages[0].content is string %}\n {{- messages[0].content }}\n {%- else %}\n {%- for content in messages[0].content %}\n {%- if 'text' in content %}\n {{- content.text }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- '\\n\\n' }}\n {%- endif %}\n {{- \"# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0].role == 'system' %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0].content is string %}\n {{- messages[0].content }}\n {%- else %}\n {%- for content in messages[0].content %}\n {%- if 'text' in content %}\n {{- content.text }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- set image_count = namespace(value=0) %}\n{%- set video_count = namespace(value=0) %}\n{%- for message in messages %}\n {%- if message.role == \"user\" %}\n {{- '<|im_start|>' + message.role + '\\n' }}\n {%- if message.content is string %}\n {{- message.content }}\n {%- else %}\n {%- for content in message.content %}\n {%- if content.type == 'image' or 'image' in content or 'image_url' in content %}\n {%- set image_count.value = image_count.value + 1 %}\n {%- if add_vision_id %}Picture {{ image_count.value }}: {% endif -%}\n <|vision_start|><|image_pad|><|vision_end|>\n {%- elif content.type == 'video' or 'video' in content %}\n {%- set video_count.value = video_count.value + 1 %}\n {%- if add_vision_id %}Video {{ video_count.value }}: {% endif -%}\n <|vision_start|><|video_pad|><|vision_end|>\n {%- elif 'text' in content %}\n {{- content.text }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role + '\\n' }}\n {%- if message.content is string %}\n {{- message.content }}\n {%- else %}\n {%- for content_item in message.content %}\n {%- if 'text' in content_item %}\n {{- content_item.text }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {%- if message.tool_calls %}\n {%- for tool_call in message.tool_calls %}\n {%- if (loop.first and message.content) or (not loop.first) %}\n {{- '\\n' }}\n {%- endif %}\n {%- if tool_call.function %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {%- if tool_call.arguments is string %}\n {{- tool_call.arguments }}\n {%- else %}\n {{- tool_call.arguments | tojson }}\n {%- endif %}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if loop.first or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {%- if message.content is string %}\n {{- message.content }}\n {%- else %}\n {%- for content in message.content %}\n {%- if content.type == 'image' or 'image' in content or 'image_url' in content %}\n {%- set image_count.value = image_count.value + 1 %}\n {%- if add_vision_id %}Picture {{ image_count.value }}: {% endif -%}\n <|vision_start|><|image_pad|><|vision_end|>\n {%- elif content.type == 'video' or 'video' in content %}\n {%- set video_count.value = video_count.value + 1 %}\n {%- if add_vision_id %}Video {{ video_count.value }}: {% endif -%}\n <|vision_start|><|video_pad|><|vision_end|>\n {%- elif 'text' in content %}\n {{- content.text }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n"
|
| 3 |
+
}
|
| 4 |
+
|
text_encoder/config.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"Qwen3VLForConditionalGeneration"
|
| 4 |
+
],
|
| 5 |
+
"image_token_id": 151655,
|
| 6 |
+
"model_type": "qwen3_vl",
|
| 7 |
+
"quantization_config": {
|
| 8 |
+
"bf16_policy": "blocks 0/35, embeddings, norms, biases, and vision tower",
|
| 9 |
+
"compute_dtype": "bfloat16",
|
| 10 |
+
"fp8_projection_count": 14,
|
| 11 |
+
"nvfp4_projection_count": 224,
|
| 12 |
+
"quant_algo": "NVFP4_FP8_MIXED",
|
| 13 |
+
"quant_method": "comfy_kitchen"
|
| 14 |
+
},
|
| 15 |
+
"text_config": {
|
| 16 |
+
"attention_bias": false,
|
| 17 |
+
"attention_dropout": 0.0,
|
| 18 |
+
"bos_token_id": 151643,
|
| 19 |
+
"dtype": "bfloat16",
|
| 20 |
+
"eos_token_id": 151645,
|
| 21 |
+
"head_dim": 128,
|
| 22 |
+
"hidden_act": "silu",
|
| 23 |
+
"hidden_size": 2560,
|
| 24 |
+
"initializer_range": 0.02,
|
| 25 |
+
"intermediate_size": 9728,
|
| 26 |
+
"max_position_embeddings": 262144,
|
| 27 |
+
"model_type": "qwen3_vl_text",
|
| 28 |
+
"num_attention_heads": 32,
|
| 29 |
+
"num_hidden_layers": 36,
|
| 30 |
+
"num_key_value_heads": 8,
|
| 31 |
+
"rms_norm_eps": 1e-06,
|
| 32 |
+
"rope_scaling": {
|
| 33 |
+
"mrope_interleaved": true,
|
| 34 |
+
"mrope_section": [
|
| 35 |
+
24,
|
| 36 |
+
20,
|
| 37 |
+
20
|
| 38 |
+
],
|
| 39 |
+
"rope_type": "default"
|
| 40 |
+
},
|
| 41 |
+
"rope_theta": 5000000,
|
| 42 |
+
"tie_word_embeddings": true,
|
| 43 |
+
"use_cache": true,
|
| 44 |
+
"vocab_size": 151936
|
| 45 |
+
},
|
| 46 |
+
"tie_word_embeddings": true,
|
| 47 |
+
"transformers_version": "4.57.0.dev0",
|
| 48 |
+
"video_token_id": 151656,
|
| 49 |
+
"vision_config": {
|
| 50 |
+
"deepstack_visual_indexes": [
|
| 51 |
+
5,
|
| 52 |
+
11,
|
| 53 |
+
17
|
| 54 |
+
],
|
| 55 |
+
"depth": 24,
|
| 56 |
+
"hidden_act": "gelu_pytorch_tanh",
|
| 57 |
+
"hidden_size": 1024,
|
| 58 |
+
"in_channels": 3,
|
| 59 |
+
"initializer_range": 0.02,
|
| 60 |
+
"intermediate_size": 4096,
|
| 61 |
+
"model_type": "qwen3_vl",
|
| 62 |
+
"num_heads": 16,
|
| 63 |
+
"num_position_embeddings": 2304,
|
| 64 |
+
"out_hidden_size": 2560,
|
| 65 |
+
"patch_size": 16,
|
| 66 |
+
"spatial_merge_size": 2,
|
| 67 |
+
"temporal_patch_size": 2
|
| 68 |
+
},
|
| 69 |
+
"vision_end_token_id": 151653,
|
| 70 |
+
"vision_start_token_id": 151652
|
| 71 |
+
}
|
text_encoder/generation_config.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bos_token_id": 151643,
|
| 3 |
+
"pad_token_id": 151643,
|
| 4 |
+
"do_sample": true,
|
| 5 |
+
"eos_token_id": [
|
| 6 |
+
151645,
|
| 7 |
+
151643
|
| 8 |
+
],
|
| 9 |
+
"top_k": 20,
|
| 10 |
+
"top_p": 0.8,
|
| 11 |
+
"repetition_penalty": 1.0,
|
| 12 |
+
"temperature": 0.7,
|
| 13 |
+
"transformers_version": "4.56.0"
|
| 14 |
+
}
|
text_encoder/merges.txt
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
text_encoder/preprocessor_config.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"size": {
|
| 3 |
+
"longest_edge": 16777216,
|
| 4 |
+
"shortest_edge": 65536
|
| 5 |
+
},
|
| 6 |
+
"patch_size": 16,
|
| 7 |
+
"temporal_patch_size": 2,
|
| 8 |
+
"merge_size": 2,
|
| 9 |
+
"image_mean": [
|
| 10 |
+
0.5,
|
| 11 |
+
0.5,
|
| 12 |
+
0.5
|
| 13 |
+
],
|
| 14 |
+
"image_std": [
|
| 15 |
+
0.5,
|
| 16 |
+
0.5,
|
| 17 |
+
0.5
|
| 18 |
+
],
|
| 19 |
+
"processor_class": "Qwen3VLProcessor",
|
| 20 |
+
"image_processor_type": "Qwen2VLImageProcessorFast"
|
| 21 |
+
}
|
text_encoder/tokenizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
text_encoder/tokenizer_config.json
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"add_bos_token": false,
|
| 3 |
+
"add_prefix_space": false,
|
| 4 |
+
"added_tokens_decoder": {
|
| 5 |
+
"151643": {
|
| 6 |
+
"content": "<|endoftext|>",
|
| 7 |
+
"lstrip": false,
|
| 8 |
+
"normalized": false,
|
| 9 |
+
"rstrip": false,
|
| 10 |
+
"single_word": false,
|
| 11 |
+
"special": true
|
| 12 |
+
},
|
| 13 |
+
"151644": {
|
| 14 |
+
"content": "<|im_start|>",
|
| 15 |
+
"lstrip": false,
|
| 16 |
+
"normalized": false,
|
| 17 |
+
"rstrip": false,
|
| 18 |
+
"single_word": false,
|
| 19 |
+
"special": true
|
| 20 |
+
},
|
| 21 |
+
"151645": {
|
| 22 |
+
"content": "<|im_end|>",
|
| 23 |
+
"lstrip": false,
|
| 24 |
+
"normalized": false,
|
| 25 |
+
"rstrip": false,
|
| 26 |
+
"single_word": false,
|
| 27 |
+
"special": true
|
| 28 |
+
},
|
| 29 |
+
"151646": {
|
| 30 |
+
"content": "<|object_ref_start|>",
|
| 31 |
+
"lstrip": false,
|
| 32 |
+
"normalized": false,
|
| 33 |
+
"rstrip": false,
|
| 34 |
+
"single_word": false,
|
| 35 |
+
"special": true
|
| 36 |
+
},
|
| 37 |
+
"151647": {
|
| 38 |
+
"content": "<|object_ref_end|>",
|
| 39 |
+
"lstrip": false,
|
| 40 |
+
"normalized": false,
|
| 41 |
+
"rstrip": false,
|
| 42 |
+
"single_word": false,
|
| 43 |
+
"special": true
|
| 44 |
+
},
|
| 45 |
+
"151648": {
|
| 46 |
+
"content": "<|box_start|>",
|
| 47 |
+
"lstrip": false,
|
| 48 |
+
"normalized": false,
|
| 49 |
+
"rstrip": false,
|
| 50 |
+
"single_word": false,
|
| 51 |
+
"special": true
|
| 52 |
+
},
|
| 53 |
+
"151649": {
|
| 54 |
+
"content": "<|box_end|>",
|
| 55 |
+
"lstrip": false,
|
| 56 |
+
"normalized": false,
|
| 57 |
+
"rstrip": false,
|
| 58 |
+
"single_word": false,
|
| 59 |
+
"special": true
|
| 60 |
+
},
|
| 61 |
+
"151650": {
|
| 62 |
+
"content": "<|quad_start|>",
|
| 63 |
+
"lstrip": false,
|
| 64 |
+
"normalized": false,
|
| 65 |
+
"rstrip": false,
|
| 66 |
+
"single_word": false,
|
| 67 |
+
"special": true
|
| 68 |
+
},
|
| 69 |
+
"151651": {
|
| 70 |
+
"content": "<|quad_end|>",
|
| 71 |
+
"lstrip": false,
|
| 72 |
+
"normalized": false,
|
| 73 |
+
"rstrip": false,
|
| 74 |
+
"single_word": false,
|
| 75 |
+
"special": true
|
| 76 |
+
},
|
| 77 |
+
"151652": {
|
| 78 |
+
"content": "<|vision_start|>",
|
| 79 |
+
"lstrip": false,
|
| 80 |
+
"normalized": false,
|
| 81 |
+
"rstrip": false,
|
| 82 |
+
"single_word": false,
|
| 83 |
+
"special": true
|
| 84 |
+
},
|
| 85 |
+
"151653": {
|
| 86 |
+
"content": "<|vision_end|>",
|
| 87 |
+
"lstrip": false,
|
| 88 |
+
"normalized": false,
|
| 89 |
+
"rstrip": false,
|
| 90 |
+
"single_word": false,
|
| 91 |
+
"special": true
|
| 92 |
+
},
|
| 93 |
+
"151654": {
|
| 94 |
+
"content": "<|vision_pad|>",
|
| 95 |
+
"lstrip": false,
|
| 96 |
+
"normalized": false,
|
| 97 |
+
"rstrip": false,
|
| 98 |
+
"single_word": false,
|
| 99 |
+
"special": true
|
| 100 |
+
},
|
| 101 |
+
"151655": {
|
| 102 |
+
"content": "<|image_pad|>",
|
| 103 |
+
"lstrip": false,
|
| 104 |
+
"normalized": false,
|
| 105 |
+
"rstrip": false,
|
| 106 |
+
"single_word": false,
|
| 107 |
+
"special": true
|
| 108 |
+
},
|
| 109 |
+
"151656": {
|
| 110 |
+
"content": "<|video_pad|>",
|
| 111 |
+
"lstrip": false,
|
| 112 |
+
"normalized": false,
|
| 113 |
+
"rstrip": false,
|
| 114 |
+
"single_word": false,
|
| 115 |
+
"special": true
|
| 116 |
+
},
|
| 117 |
+
"151657": {
|
| 118 |
+
"content": "<tool_call>",
|
| 119 |
+
"lstrip": false,
|
| 120 |
+
"normalized": false,
|
| 121 |
+
"rstrip": false,
|
| 122 |
+
"single_word": false,
|
| 123 |
+
"special": false
|
| 124 |
+
},
|
| 125 |
+
"151658": {
|
| 126 |
+
"content": "</tool_call>",
|
| 127 |
+
"lstrip": false,
|
| 128 |
+
"normalized": false,
|
| 129 |
+
"rstrip": false,
|
| 130 |
+
"single_word": false,
|
| 131 |
+
"special": false
|
| 132 |
+
},
|
| 133 |
+
"151659": {
|
| 134 |
+
"content": "<|fim_prefix|>",
|
| 135 |
+
"lstrip": false,
|
| 136 |
+
"normalized": false,
|
| 137 |
+
"rstrip": false,
|
| 138 |
+
"single_word": false,
|
| 139 |
+
"special": false
|
| 140 |
+
},
|
| 141 |
+
"151660": {
|
| 142 |
+
"content": "<|fim_middle|>",
|
| 143 |
+
"lstrip": false,
|
| 144 |
+
"normalized": false,
|
| 145 |
+
"rstrip": false,
|
| 146 |
+
"single_word": false,
|
| 147 |
+
"special": false
|
| 148 |
+
},
|
| 149 |
+
"151661": {
|
| 150 |
+
"content": "<|fim_suffix|>",
|
| 151 |
+
"lstrip": false,
|
| 152 |
+
"normalized": false,
|
| 153 |
+
"rstrip": false,
|
| 154 |
+
"single_word": false,
|
| 155 |
+
"special": false
|
| 156 |
+
},
|
| 157 |
+
"151662": {
|
| 158 |
+
"content": "<|fim_pad|>",
|
| 159 |
+
"lstrip": false,
|
| 160 |
+
"normalized": false,
|
| 161 |
+
"rstrip": false,
|
| 162 |
+
"single_word": false,
|
| 163 |
+
"special": false
|
| 164 |
+
},
|
| 165 |
+
"151663": {
|
| 166 |
+
"content": "<|repo_name|>",
|
| 167 |
+
"lstrip": false,
|
| 168 |
+
"normalized": false,
|
| 169 |
+
"rstrip": false,
|
| 170 |
+
"single_word": false,
|
| 171 |
+
"special": false
|
| 172 |
+
},
|
| 173 |
+
"151664": {
|
| 174 |
+
"content": "<|file_sep|>",
|
| 175 |
+
"lstrip": false,
|
| 176 |
+
"normalized": false,
|
| 177 |
+
"rstrip": false,
|
| 178 |
+
"single_word": false,
|
| 179 |
+
"special": false
|
| 180 |
+
},
|
| 181 |
+
"151665": {
|
| 182 |
+
"content": "<tool_response>",
|
| 183 |
+
"lstrip": false,
|
| 184 |
+
"normalized": false,
|
| 185 |
+
"rstrip": false,
|
| 186 |
+
"single_word": false,
|
| 187 |
+
"special": false
|
| 188 |
+
},
|
| 189 |
+
"151666": {
|
| 190 |
+
"content": "</tool_response>",
|
| 191 |
+
"lstrip": false,
|
| 192 |
+
"normalized": false,
|
| 193 |
+
"rstrip": false,
|
| 194 |
+
"single_word": false,
|
| 195 |
+
"special": false
|
| 196 |
+
},
|
| 197 |
+
"151667": {
|
| 198 |
+
"content": "<think>",
|
| 199 |
+
"lstrip": false,
|
| 200 |
+
"normalized": false,
|
| 201 |
+
"rstrip": false,
|
| 202 |
+
"single_word": false,
|
| 203 |
+
"special": false
|
| 204 |
+
},
|
| 205 |
+
"151668": {
|
| 206 |
+
"content": "</think>",
|
| 207 |
+
"lstrip": false,
|
| 208 |
+
"normalized": false,
|
| 209 |
+
"rstrip": false,
|
| 210 |
+
"single_word": false,
|
| 211 |
+
"special": false
|
| 212 |
+
}
|
| 213 |
+
},
|
| 214 |
+
"additional_special_tokens": [
|
| 215 |
+
"<|im_start|>",
|
| 216 |
+
"<|im_end|>",
|
| 217 |
+
"<|object_ref_start|>",
|
| 218 |
+
"<|object_ref_end|>",
|
| 219 |
+
"<|box_start|>",
|
| 220 |
+
"<|box_end|>",
|
| 221 |
+
"<|quad_start|>",
|
| 222 |
+
"<|quad_end|>",
|
| 223 |
+
"<|vision_start|>",
|
| 224 |
+
"<|vision_end|>",
|
| 225 |
+
"<|vision_pad|>",
|
| 226 |
+
"<|image_pad|>",
|
| 227 |
+
"<|video_pad|>"
|
| 228 |
+
],
|
| 229 |
+
"bos_token": null,
|
| 230 |
+
"chat_template": "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0].role == 'system' %}\n {%- if messages[0].content is string %}\n {{- messages[0].content }}\n {%- else %}\n {%- for content in messages[0].content %}\n {%- if 'text' in content %}\n {{- content.text }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- '\\n\\n' }}\n {%- endif %}\n {{- \"# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0].role == 'system' %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0].content is string %}\n {{- messages[0].content }}\n {%- else %}\n {%- for content in messages[0].content %}\n {%- if 'text' in content %}\n {{- content.text }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- set image_count = namespace(value=0) %}\n{%- set video_count = namespace(value=0) %}\n{%- for message in messages %}\n {%- if message.role == \"user\" %}\n {{- '<|im_start|>' + message.role + '\\n' }}\n {%- if message.content is string %}\n {{- message.content }}\n {%- else %}\n {%- for content in message.content %}\n {%- if content.type == 'image' or 'image' in content or 'image_url' in content %}\n {%- set image_count.value = image_count.value + 1 %}\n {%- if add_vision_id %}Picture {{ image_count.value }}: {% endif -%}\n <|vision_start|><|image_pad|><|vision_end|>\n {%- elif content.type == 'video' or 'video' in content %}\n {%- set video_count.value = video_count.value + 1 %}\n {%- if add_vision_id %}Video {{ video_count.value }}: {% endif -%}\n <|vision_start|><|video_pad|><|vision_end|>\n {%- elif 'text' in content %}\n {{- content.text }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role + '\\n' }}\n {%- if message.content is string %}\n {{- message.content }}\n {%- else %}\n {%- for content_item in message.content %}\n {%- if 'text' in content_item %}\n {{- content_item.text }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {%- if message.tool_calls %}\n {%- for tool_call in message.tool_calls %}\n {%- if (loop.first and message.content) or (not loop.first) %}\n {{- '\\n' }}\n {%- endif %}\n {%- if tool_call.function %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {%- if tool_call.arguments is string %}\n {{- tool_call.arguments }}\n {%- else %}\n {{- tool_call.arguments | tojson }}\n {%- endif %}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if loop.first or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {%- if message.content is string %}\n {{- message.content }}\n {%- else %}\n {%- for content in message.content %}\n {%- if content.type == 'image' or 'image' in content or 'image_url' in content %}\n {%- set image_count.value = image_count.value + 1 %}\n {%- if add_vision_id %}Picture {{ image_count.value }}: {% endif -%}\n <|vision_start|><|image_pad|><|vision_end|>\n {%- elif content.type == 'video' or 'video' in content %}\n {%- set video_count.value = video_count.value + 1 %}\n {%- if add_vision_id %}Video {{ video_count.value }}: {% endif -%}\n <|vision_start|><|video_pad|><|vision_end|>\n {%- elif 'text' in content %}\n {{- content.text }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n",
|
| 231 |
+
"clean_up_tokenization_spaces": false,
|
| 232 |
+
"eos_token": "<|im_end|>",
|
| 233 |
+
"errors": "replace",
|
| 234 |
+
"model_max_length": 262144,
|
| 235 |
+
"pad_token": "<|endoftext|>",
|
| 236 |
+
"split_special_tokens": false,
|
| 237 |
+
"tokenizer_class": "Qwen2Tokenizer",
|
| 238 |
+
"unk_token": null
|
| 239 |
+
}
|
text_encoder/video_preprocessor_config.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"size": {
|
| 3 |
+
"longest_edge": 25165824,
|
| 4 |
+
"shortest_edge": 4096
|
| 5 |
+
},
|
| 6 |
+
"patch_size": 16,
|
| 7 |
+
"temporal_patch_size": 2,
|
| 8 |
+
"merge_size": 2,
|
| 9 |
+
"image_mean": [
|
| 10 |
+
0.5,
|
| 11 |
+
0.5,
|
| 12 |
+
0.5
|
| 13 |
+
],
|
| 14 |
+
"image_std": [
|
| 15 |
+
0.5,
|
| 16 |
+
0.5,
|
| 17 |
+
0.5
|
| 18 |
+
],
|
| 19 |
+
"processor_class": "Qwen3VLProcessor",
|
| 20 |
+
"video_processor_type": "Qwen3VLVideoProcessor"
|
| 21 |
+
}
|
text_encoder/vocab.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
transformer/config.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_class_name": "MageFlow",
|
| 3 |
+
"apply_text_rotary_emb": false,
|
| 4 |
+
"axes_dim": [
|
| 5 |
+
16,
|
| 6 |
+
56,
|
| 7 |
+
56
|
| 8 |
+
],
|
| 9 |
+
"checkpoint": false,
|
| 10 |
+
"context_in_dim": 2560,
|
| 11 |
+
"depth": 12,
|
| 12 |
+
"depth_single_blocks": 0,
|
| 13 |
+
"double_block_type": "double_stream",
|
| 14 |
+
"guidance_embed": false,
|
| 15 |
+
"hidden_size": 3072,
|
| 16 |
+
"in_channels": 128,
|
| 17 |
+
"max_sequence_length": 2048,
|
| 18 |
+
"mlp_ratio": 4.0,
|
| 19 |
+
"num_heads": 24,
|
| 20 |
+
"out_channels": 128,
|
| 21 |
+
"packing": true,
|
| 22 |
+
"param_dtype": "bfloat16",
|
| 23 |
+
"patch_size": 1,
|
| 24 |
+
"qkv_bias": true,
|
| 25 |
+
"quantization_config": {
|
| 26 |
+
"backend": "native_sm120",
|
| 27 |
+
"compute_dtype": "bfloat16",
|
| 28 |
+
"format": "e2m1_block16_ue4m3",
|
| 29 |
+
"quant_algo": "NVFP4",
|
| 30 |
+
"quant_method": "mage_flow_nvfp4",
|
| 31 |
+
"target_count": 48,
|
| 32 |
+
"targets": [
|
| 33 |
+
"transformer_blocks.0.img_mlp.net.0.proj",
|
| 34 |
+
"transformer_blocks.0.img_mlp.net.2",
|
| 35 |
+
"transformer_blocks.0.txt_mlp.net.0.proj",
|
| 36 |
+
"transformer_blocks.0.txt_mlp.net.2",
|
| 37 |
+
"transformer_blocks.1.img_mlp.net.0.proj",
|
| 38 |
+
"transformer_blocks.1.img_mlp.net.2",
|
| 39 |
+
"transformer_blocks.1.txt_mlp.net.0.proj",
|
| 40 |
+
"transformer_blocks.1.txt_mlp.net.2",
|
| 41 |
+
"transformer_blocks.2.img_mlp.net.0.proj",
|
| 42 |
+
"transformer_blocks.2.img_mlp.net.2",
|
| 43 |
+
"transformer_blocks.2.txt_mlp.net.0.proj",
|
| 44 |
+
"transformer_blocks.2.txt_mlp.net.2",
|
| 45 |
+
"transformer_blocks.3.img_mlp.net.0.proj",
|
| 46 |
+
"transformer_blocks.3.img_mlp.net.2",
|
| 47 |
+
"transformer_blocks.3.txt_mlp.net.0.proj",
|
| 48 |
+
"transformer_blocks.3.txt_mlp.net.2",
|
| 49 |
+
"transformer_blocks.4.img_mlp.net.0.proj",
|
| 50 |
+
"transformer_blocks.4.img_mlp.net.2",
|
| 51 |
+
"transformer_blocks.4.txt_mlp.net.0.proj",
|
| 52 |
+
"transformer_blocks.4.txt_mlp.net.2",
|
| 53 |
+
"transformer_blocks.5.img_mlp.net.0.proj",
|
| 54 |
+
"transformer_blocks.5.img_mlp.net.2",
|
| 55 |
+
"transformer_blocks.5.txt_mlp.net.0.proj",
|
| 56 |
+
"transformer_blocks.5.txt_mlp.net.2",
|
| 57 |
+
"transformer_blocks.6.img_mlp.net.0.proj",
|
| 58 |
+
"transformer_blocks.6.img_mlp.net.2",
|
| 59 |
+
"transformer_blocks.6.txt_mlp.net.0.proj",
|
| 60 |
+
"transformer_blocks.6.txt_mlp.net.2",
|
| 61 |
+
"transformer_blocks.7.img_mlp.net.0.proj",
|
| 62 |
+
"transformer_blocks.7.img_mlp.net.2",
|
| 63 |
+
"transformer_blocks.7.txt_mlp.net.0.proj",
|
| 64 |
+
"transformer_blocks.7.txt_mlp.net.2",
|
| 65 |
+
"transformer_blocks.8.img_mlp.net.0.proj",
|
| 66 |
+
"transformer_blocks.8.img_mlp.net.2",
|
| 67 |
+
"transformer_blocks.8.txt_mlp.net.0.proj",
|
| 68 |
+
"transformer_blocks.8.txt_mlp.net.2",
|
| 69 |
+
"transformer_blocks.9.img_mlp.net.0.proj",
|
| 70 |
+
"transformer_blocks.9.img_mlp.net.2",
|
| 71 |
+
"transformer_blocks.9.txt_mlp.net.0.proj",
|
| 72 |
+
"transformer_blocks.9.txt_mlp.net.2",
|
| 73 |
+
"transformer_blocks.10.img_mlp.net.0.proj",
|
| 74 |
+
"transformer_blocks.10.img_mlp.net.2",
|
| 75 |
+
"transformer_blocks.10.txt_mlp.net.0.proj",
|
| 76 |
+
"transformer_blocks.10.txt_mlp.net.2",
|
| 77 |
+
"transformer_blocks.11.img_mlp.net.0.proj",
|
| 78 |
+
"transformer_blocks.11.img_mlp.net.2",
|
| 79 |
+
"transformer_blocks.11.txt_mlp.net.0.proj",
|
| 80 |
+
"transformer_blocks.11.txt_mlp.net.2"
|
| 81 |
+
]
|
| 82 |
+
},
|
| 83 |
+
"rope_type": "msrope",
|
| 84 |
+
"schedule_mode": "z-image",
|
| 85 |
+
"static_shift": 6.0,
|
| 86 |
+
"theta": 10000,
|
| 87 |
+
"time_type": "qwen_proj",
|
| 88 |
+
"txt_max_length": 2048,
|
| 89 |
+
"use_time_shift": false,
|
| 90 |
+
"vec_in_dim": 0,
|
| 91 |
+
"vec_type": null
|
| 92 |
+
}
|
transformer/diffusion_pytorch_model.safetensors.index.json
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"metadata": {
|
| 3 |
+
"quantization": "mage_flow_nvfp4_resident_v1",
|
| 4 |
+
"total_size": 5626828224
|
| 5 |
+
},
|
| 6 |
+
"weight_map": {
|
| 7 |
+
"img_in.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 8 |
+
"img_in.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 9 |
+
"norm_out.linear.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 10 |
+
"norm_out.linear.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 11 |
+
"proj_out.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 12 |
+
"proj_out.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 13 |
+
"time_text_embed.timestep_embedder.linear_1.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 14 |
+
"time_text_embed.timestep_embedder.linear_1.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 15 |
+
"time_text_embed.timestep_embedder.linear_2.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 16 |
+
"time_text_embed.timestep_embedder.linear_2.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 17 |
+
"transformer_blocks.0.attn.add_k_proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 18 |
+
"transformer_blocks.0.attn.add_k_proj.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 19 |
+
"transformer_blocks.0.attn.add_q_proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 20 |
+
"transformer_blocks.0.attn.add_q_proj.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 21 |
+
"transformer_blocks.0.attn.add_v_proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 22 |
+
"transformer_blocks.0.attn.add_v_proj.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 23 |
+
"transformer_blocks.0.attn.norm_added_k.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 24 |
+
"transformer_blocks.0.attn.norm_added_q.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 25 |
+
"transformer_blocks.0.attn.norm_k.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 26 |
+
"transformer_blocks.0.attn.norm_q.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 27 |
+
"transformer_blocks.0.attn.to_add_out.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 28 |
+
"transformer_blocks.0.attn.to_add_out.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 29 |
+
"transformer_blocks.0.attn.to_k.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 30 |
+
"transformer_blocks.0.attn.to_k.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 31 |
+
"transformer_blocks.0.attn.to_out.0.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 32 |
+
"transformer_blocks.0.attn.to_out.0.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 33 |
+
"transformer_blocks.0.attn.to_q.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 34 |
+
"transformer_blocks.0.attn.to_q.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 35 |
+
"transformer_blocks.0.attn.to_v.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 36 |
+
"transformer_blocks.0.attn.to_v.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 37 |
+
"transformer_blocks.0.img_mlp.net.0.proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 38 |
+
"transformer_blocks.0.img_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 39 |
+
"transformer_blocks.0.img_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 40 |
+
"transformer_blocks.0.img_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 41 |
+
"transformer_blocks.0.img_mlp.net.2.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 42 |
+
"transformer_blocks.0.img_mlp.net.2.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 43 |
+
"transformer_blocks.0.img_mlp.net.2.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 44 |
+
"transformer_blocks.0.img_mlp.net.2.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 45 |
+
"transformer_blocks.0.img_mod.1.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 46 |
+
"transformer_blocks.0.img_mod.1.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 47 |
+
"transformer_blocks.0.txt_mlp.net.0.proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 48 |
+
"transformer_blocks.0.txt_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 49 |
+
"transformer_blocks.0.txt_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 50 |
+
"transformer_blocks.0.txt_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 51 |
+
"transformer_blocks.0.txt_mlp.net.2.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 52 |
+
"transformer_blocks.0.txt_mlp.net.2.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 53 |
+
"transformer_blocks.0.txt_mlp.net.2.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 54 |
+
"transformer_blocks.0.txt_mlp.net.2.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 55 |
+
"transformer_blocks.0.txt_mod.1.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 56 |
+
"transformer_blocks.0.txt_mod.1.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 57 |
+
"transformer_blocks.1.attn.add_k_proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 58 |
+
"transformer_blocks.1.attn.add_k_proj.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 59 |
+
"transformer_blocks.1.attn.add_q_proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 60 |
+
"transformer_blocks.1.attn.add_q_proj.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 61 |
+
"transformer_blocks.1.attn.add_v_proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 62 |
+
"transformer_blocks.1.attn.add_v_proj.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 63 |
+
"transformer_blocks.1.attn.norm_added_k.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 64 |
+
"transformer_blocks.1.attn.norm_added_q.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 65 |
+
"transformer_blocks.1.attn.norm_k.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 66 |
+
"transformer_blocks.1.attn.norm_q.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 67 |
+
"transformer_blocks.1.attn.to_add_out.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 68 |
+
"transformer_blocks.1.attn.to_add_out.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 69 |
+
"transformer_blocks.1.attn.to_k.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 70 |
+
"transformer_blocks.1.attn.to_k.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 71 |
+
"transformer_blocks.1.attn.to_out.0.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 72 |
+
"transformer_blocks.1.attn.to_out.0.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 73 |
+
"transformer_blocks.1.attn.to_q.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 74 |
+
"transformer_blocks.1.attn.to_q.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 75 |
+
"transformer_blocks.1.attn.to_v.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 76 |
+
"transformer_blocks.1.attn.to_v.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 77 |
+
"transformer_blocks.1.img_mlp.net.0.proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 78 |
+
"transformer_blocks.1.img_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 79 |
+
"transformer_blocks.1.img_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 80 |
+
"transformer_blocks.1.img_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 81 |
+
"transformer_blocks.1.img_mlp.net.2.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 82 |
+
"transformer_blocks.1.img_mlp.net.2.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 83 |
+
"transformer_blocks.1.img_mlp.net.2.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 84 |
+
"transformer_blocks.1.img_mlp.net.2.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 85 |
+
"transformer_blocks.1.img_mod.1.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 86 |
+
"transformer_blocks.1.img_mod.1.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 87 |
+
"transformer_blocks.1.txt_mlp.net.0.proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 88 |
+
"transformer_blocks.1.txt_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 89 |
+
"transformer_blocks.1.txt_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 90 |
+
"transformer_blocks.1.txt_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 91 |
+
"transformer_blocks.1.txt_mlp.net.2.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 92 |
+
"transformer_blocks.1.txt_mlp.net.2.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 93 |
+
"transformer_blocks.1.txt_mlp.net.2.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 94 |
+
"transformer_blocks.1.txt_mlp.net.2.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 95 |
+
"transformer_blocks.1.txt_mod.1.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 96 |
+
"transformer_blocks.1.txt_mod.1.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 97 |
+
"transformer_blocks.10.attn.add_k_proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 98 |
+
"transformer_blocks.10.attn.add_k_proj.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 99 |
+
"transformer_blocks.10.attn.add_q_proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 100 |
+
"transformer_blocks.10.attn.add_q_proj.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 101 |
+
"transformer_blocks.10.attn.add_v_proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 102 |
+
"transformer_blocks.10.attn.add_v_proj.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 103 |
+
"transformer_blocks.10.attn.norm_added_k.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 104 |
+
"transformer_blocks.10.attn.norm_added_q.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 105 |
+
"transformer_blocks.10.attn.norm_k.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 106 |
+
"transformer_blocks.10.attn.norm_q.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 107 |
+
"transformer_blocks.10.attn.to_add_out.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 108 |
+
"transformer_blocks.10.attn.to_add_out.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 109 |
+
"transformer_blocks.10.attn.to_k.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 110 |
+
"transformer_blocks.10.attn.to_k.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 111 |
+
"transformer_blocks.10.attn.to_out.0.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 112 |
+
"transformer_blocks.10.attn.to_out.0.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 113 |
+
"transformer_blocks.10.attn.to_q.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 114 |
+
"transformer_blocks.10.attn.to_q.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 115 |
+
"transformer_blocks.10.attn.to_v.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 116 |
+
"transformer_blocks.10.attn.to_v.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 117 |
+
"transformer_blocks.10.img_mlp.net.0.proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 118 |
+
"transformer_blocks.10.img_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 119 |
+
"transformer_blocks.10.img_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 120 |
+
"transformer_blocks.10.img_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 121 |
+
"transformer_blocks.10.img_mlp.net.2.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 122 |
+
"transformer_blocks.10.img_mlp.net.2.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 123 |
+
"transformer_blocks.10.img_mlp.net.2.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 124 |
+
"transformer_blocks.10.img_mlp.net.2.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 125 |
+
"transformer_blocks.10.img_mod.1.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 126 |
+
"transformer_blocks.10.img_mod.1.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 127 |
+
"transformer_blocks.10.txt_mlp.net.0.proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 128 |
+
"transformer_blocks.10.txt_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 129 |
+
"transformer_blocks.10.txt_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 130 |
+
"transformer_blocks.10.txt_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 131 |
+
"transformer_blocks.10.txt_mlp.net.2.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 132 |
+
"transformer_blocks.10.txt_mlp.net.2.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 133 |
+
"transformer_blocks.10.txt_mlp.net.2.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 134 |
+
"transformer_blocks.10.txt_mlp.net.2.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 135 |
+
"transformer_blocks.10.txt_mod.1.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 136 |
+
"transformer_blocks.10.txt_mod.1.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 137 |
+
"transformer_blocks.11.attn.add_k_proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 138 |
+
"transformer_blocks.11.attn.add_k_proj.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 139 |
+
"transformer_blocks.11.attn.add_q_proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 140 |
+
"transformer_blocks.11.attn.add_q_proj.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 141 |
+
"transformer_blocks.11.attn.add_v_proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 142 |
+
"transformer_blocks.11.attn.add_v_proj.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 143 |
+
"transformer_blocks.11.attn.norm_added_k.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 144 |
+
"transformer_blocks.11.attn.norm_added_q.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 145 |
+
"transformer_blocks.11.attn.norm_k.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 146 |
+
"transformer_blocks.11.attn.norm_q.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 147 |
+
"transformer_blocks.11.attn.to_add_out.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 148 |
+
"transformer_blocks.11.attn.to_add_out.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 149 |
+
"transformer_blocks.11.attn.to_k.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 150 |
+
"transformer_blocks.11.attn.to_k.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 151 |
+
"transformer_blocks.11.attn.to_out.0.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 152 |
+
"transformer_blocks.11.attn.to_out.0.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 153 |
+
"transformer_blocks.11.attn.to_q.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 154 |
+
"transformer_blocks.11.attn.to_q.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 155 |
+
"transformer_blocks.11.attn.to_v.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 156 |
+
"transformer_blocks.11.attn.to_v.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 157 |
+
"transformer_blocks.11.img_mlp.net.0.proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 158 |
+
"transformer_blocks.11.img_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 159 |
+
"transformer_blocks.11.img_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 160 |
+
"transformer_blocks.11.img_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 161 |
+
"transformer_blocks.11.img_mlp.net.2.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 162 |
+
"transformer_blocks.11.img_mlp.net.2.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 163 |
+
"transformer_blocks.11.img_mlp.net.2.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 164 |
+
"transformer_blocks.11.img_mlp.net.2.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 165 |
+
"transformer_blocks.11.img_mod.1.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 166 |
+
"transformer_blocks.11.img_mod.1.weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 167 |
+
"transformer_blocks.11.txt_mlp.net.0.proj.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 168 |
+
"transformer_blocks.11.txt_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 169 |
+
"transformer_blocks.11.txt_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 170 |
+
"transformer_blocks.11.txt_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 171 |
+
"transformer_blocks.11.txt_mlp.net.2.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 172 |
+
"transformer_blocks.11.txt_mlp.net.2.packed_weight": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 173 |
+
"transformer_blocks.11.txt_mlp.net.2.weight_scale": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 174 |
+
"transformer_blocks.11.txt_mlp.net.2.weight_scales": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 175 |
+
"transformer_blocks.11.txt_mod.1.bias": "diffusion_pytorch_model-00001-of-00004.safetensors",
|
| 176 |
+
"transformer_blocks.11.txt_mod.1.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 177 |
+
"transformer_blocks.2.attn.add_k_proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 178 |
+
"transformer_blocks.2.attn.add_k_proj.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 179 |
+
"transformer_blocks.2.attn.add_q_proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 180 |
+
"transformer_blocks.2.attn.add_q_proj.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 181 |
+
"transformer_blocks.2.attn.add_v_proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 182 |
+
"transformer_blocks.2.attn.add_v_proj.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 183 |
+
"transformer_blocks.2.attn.norm_added_k.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 184 |
+
"transformer_blocks.2.attn.norm_added_q.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 185 |
+
"transformer_blocks.2.attn.norm_k.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 186 |
+
"transformer_blocks.2.attn.norm_q.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 187 |
+
"transformer_blocks.2.attn.to_add_out.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 188 |
+
"transformer_blocks.2.attn.to_add_out.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 189 |
+
"transformer_blocks.2.attn.to_k.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 190 |
+
"transformer_blocks.2.attn.to_k.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 191 |
+
"transformer_blocks.2.attn.to_out.0.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 192 |
+
"transformer_blocks.2.attn.to_out.0.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 193 |
+
"transformer_blocks.2.attn.to_q.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 194 |
+
"transformer_blocks.2.attn.to_q.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 195 |
+
"transformer_blocks.2.attn.to_v.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 196 |
+
"transformer_blocks.2.attn.to_v.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 197 |
+
"transformer_blocks.2.img_mlp.net.0.proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 198 |
+
"transformer_blocks.2.img_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 199 |
+
"transformer_blocks.2.img_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 200 |
+
"transformer_blocks.2.img_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 201 |
+
"transformer_blocks.2.img_mlp.net.2.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 202 |
+
"transformer_blocks.2.img_mlp.net.2.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 203 |
+
"transformer_blocks.2.img_mlp.net.2.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 204 |
+
"transformer_blocks.2.img_mlp.net.2.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 205 |
+
"transformer_blocks.2.img_mod.1.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 206 |
+
"transformer_blocks.2.img_mod.1.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 207 |
+
"transformer_blocks.2.txt_mlp.net.0.proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 208 |
+
"transformer_blocks.2.txt_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 209 |
+
"transformer_blocks.2.txt_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 210 |
+
"transformer_blocks.2.txt_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 211 |
+
"transformer_blocks.2.txt_mlp.net.2.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 212 |
+
"transformer_blocks.2.txt_mlp.net.2.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 213 |
+
"transformer_blocks.2.txt_mlp.net.2.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 214 |
+
"transformer_blocks.2.txt_mlp.net.2.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 215 |
+
"transformer_blocks.2.txt_mod.1.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 216 |
+
"transformer_blocks.2.txt_mod.1.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 217 |
+
"transformer_blocks.3.attn.add_k_proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 218 |
+
"transformer_blocks.3.attn.add_k_proj.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 219 |
+
"transformer_blocks.3.attn.add_q_proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 220 |
+
"transformer_blocks.3.attn.add_q_proj.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 221 |
+
"transformer_blocks.3.attn.add_v_proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 222 |
+
"transformer_blocks.3.attn.add_v_proj.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 223 |
+
"transformer_blocks.3.attn.norm_added_k.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 224 |
+
"transformer_blocks.3.attn.norm_added_q.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 225 |
+
"transformer_blocks.3.attn.norm_k.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 226 |
+
"transformer_blocks.3.attn.norm_q.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 227 |
+
"transformer_blocks.3.attn.to_add_out.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 228 |
+
"transformer_blocks.3.attn.to_add_out.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 229 |
+
"transformer_blocks.3.attn.to_k.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 230 |
+
"transformer_blocks.3.attn.to_k.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 231 |
+
"transformer_blocks.3.attn.to_out.0.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 232 |
+
"transformer_blocks.3.attn.to_out.0.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 233 |
+
"transformer_blocks.3.attn.to_q.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 234 |
+
"transformer_blocks.3.attn.to_q.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 235 |
+
"transformer_blocks.3.attn.to_v.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 236 |
+
"transformer_blocks.3.attn.to_v.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 237 |
+
"transformer_blocks.3.img_mlp.net.0.proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 238 |
+
"transformer_blocks.3.img_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 239 |
+
"transformer_blocks.3.img_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 240 |
+
"transformer_blocks.3.img_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 241 |
+
"transformer_blocks.3.img_mlp.net.2.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 242 |
+
"transformer_blocks.3.img_mlp.net.2.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 243 |
+
"transformer_blocks.3.img_mlp.net.2.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 244 |
+
"transformer_blocks.3.img_mlp.net.2.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 245 |
+
"transformer_blocks.3.img_mod.1.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 246 |
+
"transformer_blocks.3.img_mod.1.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 247 |
+
"transformer_blocks.3.txt_mlp.net.0.proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 248 |
+
"transformer_blocks.3.txt_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 249 |
+
"transformer_blocks.3.txt_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 250 |
+
"transformer_blocks.3.txt_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 251 |
+
"transformer_blocks.3.txt_mlp.net.2.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 252 |
+
"transformer_blocks.3.txt_mlp.net.2.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 253 |
+
"transformer_blocks.3.txt_mlp.net.2.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 254 |
+
"transformer_blocks.3.txt_mlp.net.2.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 255 |
+
"transformer_blocks.3.txt_mod.1.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 256 |
+
"transformer_blocks.3.txt_mod.1.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 257 |
+
"transformer_blocks.4.attn.add_k_proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 258 |
+
"transformer_blocks.4.attn.add_k_proj.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 259 |
+
"transformer_blocks.4.attn.add_q_proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 260 |
+
"transformer_blocks.4.attn.add_q_proj.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 261 |
+
"transformer_blocks.4.attn.add_v_proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 262 |
+
"transformer_blocks.4.attn.add_v_proj.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 263 |
+
"transformer_blocks.4.attn.norm_added_k.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 264 |
+
"transformer_blocks.4.attn.norm_added_q.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 265 |
+
"transformer_blocks.4.attn.norm_k.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 266 |
+
"transformer_blocks.4.attn.norm_q.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 267 |
+
"transformer_blocks.4.attn.to_add_out.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 268 |
+
"transformer_blocks.4.attn.to_add_out.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 269 |
+
"transformer_blocks.4.attn.to_k.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 270 |
+
"transformer_blocks.4.attn.to_k.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 271 |
+
"transformer_blocks.4.attn.to_out.0.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 272 |
+
"transformer_blocks.4.attn.to_out.0.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 273 |
+
"transformer_blocks.4.attn.to_q.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 274 |
+
"transformer_blocks.4.attn.to_q.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 275 |
+
"transformer_blocks.4.attn.to_v.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 276 |
+
"transformer_blocks.4.attn.to_v.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 277 |
+
"transformer_blocks.4.img_mlp.net.0.proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 278 |
+
"transformer_blocks.4.img_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 279 |
+
"transformer_blocks.4.img_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 280 |
+
"transformer_blocks.4.img_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 281 |
+
"transformer_blocks.4.img_mlp.net.2.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 282 |
+
"transformer_blocks.4.img_mlp.net.2.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 283 |
+
"transformer_blocks.4.img_mlp.net.2.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 284 |
+
"transformer_blocks.4.img_mlp.net.2.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 285 |
+
"transformer_blocks.4.img_mod.1.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 286 |
+
"transformer_blocks.4.img_mod.1.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 287 |
+
"transformer_blocks.4.txt_mlp.net.0.proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 288 |
+
"transformer_blocks.4.txt_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 289 |
+
"transformer_blocks.4.txt_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 290 |
+
"transformer_blocks.4.txt_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 291 |
+
"transformer_blocks.4.txt_mlp.net.2.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 292 |
+
"transformer_blocks.4.txt_mlp.net.2.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 293 |
+
"transformer_blocks.4.txt_mlp.net.2.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 294 |
+
"transformer_blocks.4.txt_mlp.net.2.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 295 |
+
"transformer_blocks.4.txt_mod.1.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 296 |
+
"transformer_blocks.4.txt_mod.1.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 297 |
+
"transformer_blocks.5.attn.add_k_proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 298 |
+
"transformer_blocks.5.attn.add_k_proj.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 299 |
+
"transformer_blocks.5.attn.add_q_proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 300 |
+
"transformer_blocks.5.attn.add_q_proj.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 301 |
+
"transformer_blocks.5.attn.add_v_proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 302 |
+
"transformer_blocks.5.attn.add_v_proj.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 303 |
+
"transformer_blocks.5.attn.norm_added_k.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 304 |
+
"transformer_blocks.5.attn.norm_added_q.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 305 |
+
"transformer_blocks.5.attn.norm_k.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 306 |
+
"transformer_blocks.5.attn.norm_q.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 307 |
+
"transformer_blocks.5.attn.to_add_out.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 308 |
+
"transformer_blocks.5.attn.to_add_out.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 309 |
+
"transformer_blocks.5.attn.to_k.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 310 |
+
"transformer_blocks.5.attn.to_k.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 311 |
+
"transformer_blocks.5.attn.to_out.0.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 312 |
+
"transformer_blocks.5.attn.to_out.0.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 313 |
+
"transformer_blocks.5.attn.to_q.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 314 |
+
"transformer_blocks.5.attn.to_q.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 315 |
+
"transformer_blocks.5.attn.to_v.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 316 |
+
"transformer_blocks.5.attn.to_v.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 317 |
+
"transformer_blocks.5.img_mlp.net.0.proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 318 |
+
"transformer_blocks.5.img_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 319 |
+
"transformer_blocks.5.img_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 320 |
+
"transformer_blocks.5.img_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 321 |
+
"transformer_blocks.5.img_mlp.net.2.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 322 |
+
"transformer_blocks.5.img_mlp.net.2.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 323 |
+
"transformer_blocks.5.img_mlp.net.2.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 324 |
+
"transformer_blocks.5.img_mlp.net.2.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 325 |
+
"transformer_blocks.5.img_mod.1.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 326 |
+
"transformer_blocks.5.img_mod.1.weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 327 |
+
"transformer_blocks.5.txt_mlp.net.0.proj.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 328 |
+
"transformer_blocks.5.txt_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 329 |
+
"transformer_blocks.5.txt_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 330 |
+
"transformer_blocks.5.txt_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 331 |
+
"transformer_blocks.5.txt_mlp.net.2.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 332 |
+
"transformer_blocks.5.txt_mlp.net.2.packed_weight": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 333 |
+
"transformer_blocks.5.txt_mlp.net.2.weight_scale": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 334 |
+
"transformer_blocks.5.txt_mlp.net.2.weight_scales": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 335 |
+
"transformer_blocks.5.txt_mod.1.bias": "diffusion_pytorch_model-00002-of-00004.safetensors",
|
| 336 |
+
"transformer_blocks.5.txt_mod.1.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 337 |
+
"transformer_blocks.6.attn.add_k_proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 338 |
+
"transformer_blocks.6.attn.add_k_proj.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 339 |
+
"transformer_blocks.6.attn.add_q_proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 340 |
+
"transformer_blocks.6.attn.add_q_proj.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 341 |
+
"transformer_blocks.6.attn.add_v_proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 342 |
+
"transformer_blocks.6.attn.add_v_proj.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 343 |
+
"transformer_blocks.6.attn.norm_added_k.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 344 |
+
"transformer_blocks.6.attn.norm_added_q.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 345 |
+
"transformer_blocks.6.attn.norm_k.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 346 |
+
"transformer_blocks.6.attn.norm_q.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 347 |
+
"transformer_blocks.6.attn.to_add_out.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 348 |
+
"transformer_blocks.6.attn.to_add_out.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 349 |
+
"transformer_blocks.6.attn.to_k.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 350 |
+
"transformer_blocks.6.attn.to_k.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 351 |
+
"transformer_blocks.6.attn.to_out.0.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 352 |
+
"transformer_blocks.6.attn.to_out.0.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 353 |
+
"transformer_blocks.6.attn.to_q.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 354 |
+
"transformer_blocks.6.attn.to_q.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 355 |
+
"transformer_blocks.6.attn.to_v.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 356 |
+
"transformer_blocks.6.attn.to_v.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 357 |
+
"transformer_blocks.6.img_mlp.net.0.proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 358 |
+
"transformer_blocks.6.img_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 359 |
+
"transformer_blocks.6.img_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 360 |
+
"transformer_blocks.6.img_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 361 |
+
"transformer_blocks.6.img_mlp.net.2.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 362 |
+
"transformer_blocks.6.img_mlp.net.2.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 363 |
+
"transformer_blocks.6.img_mlp.net.2.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 364 |
+
"transformer_blocks.6.img_mlp.net.2.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 365 |
+
"transformer_blocks.6.img_mod.1.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 366 |
+
"transformer_blocks.6.img_mod.1.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 367 |
+
"transformer_blocks.6.txt_mlp.net.0.proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 368 |
+
"transformer_blocks.6.txt_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 369 |
+
"transformer_blocks.6.txt_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 370 |
+
"transformer_blocks.6.txt_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 371 |
+
"transformer_blocks.6.txt_mlp.net.2.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 372 |
+
"transformer_blocks.6.txt_mlp.net.2.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 373 |
+
"transformer_blocks.6.txt_mlp.net.2.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 374 |
+
"transformer_blocks.6.txt_mlp.net.2.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 375 |
+
"transformer_blocks.6.txt_mod.1.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 376 |
+
"transformer_blocks.6.txt_mod.1.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 377 |
+
"transformer_blocks.7.attn.add_k_proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 378 |
+
"transformer_blocks.7.attn.add_k_proj.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 379 |
+
"transformer_blocks.7.attn.add_q_proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 380 |
+
"transformer_blocks.7.attn.add_q_proj.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 381 |
+
"transformer_blocks.7.attn.add_v_proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 382 |
+
"transformer_blocks.7.attn.add_v_proj.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 383 |
+
"transformer_blocks.7.attn.norm_added_k.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 384 |
+
"transformer_blocks.7.attn.norm_added_q.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 385 |
+
"transformer_blocks.7.attn.norm_k.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 386 |
+
"transformer_blocks.7.attn.norm_q.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 387 |
+
"transformer_blocks.7.attn.to_add_out.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 388 |
+
"transformer_blocks.7.attn.to_add_out.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 389 |
+
"transformer_blocks.7.attn.to_k.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 390 |
+
"transformer_blocks.7.attn.to_k.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 391 |
+
"transformer_blocks.7.attn.to_out.0.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 392 |
+
"transformer_blocks.7.attn.to_out.0.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 393 |
+
"transformer_blocks.7.attn.to_q.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 394 |
+
"transformer_blocks.7.attn.to_q.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 395 |
+
"transformer_blocks.7.attn.to_v.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 396 |
+
"transformer_blocks.7.attn.to_v.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 397 |
+
"transformer_blocks.7.img_mlp.net.0.proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 398 |
+
"transformer_blocks.7.img_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 399 |
+
"transformer_blocks.7.img_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 400 |
+
"transformer_blocks.7.img_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 401 |
+
"transformer_blocks.7.img_mlp.net.2.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 402 |
+
"transformer_blocks.7.img_mlp.net.2.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 403 |
+
"transformer_blocks.7.img_mlp.net.2.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 404 |
+
"transformer_blocks.7.img_mlp.net.2.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 405 |
+
"transformer_blocks.7.img_mod.1.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 406 |
+
"transformer_blocks.7.img_mod.1.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 407 |
+
"transformer_blocks.7.txt_mlp.net.0.proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 408 |
+
"transformer_blocks.7.txt_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 409 |
+
"transformer_blocks.7.txt_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 410 |
+
"transformer_blocks.7.txt_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 411 |
+
"transformer_blocks.7.txt_mlp.net.2.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 412 |
+
"transformer_blocks.7.txt_mlp.net.2.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 413 |
+
"transformer_blocks.7.txt_mlp.net.2.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 414 |
+
"transformer_blocks.7.txt_mlp.net.2.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 415 |
+
"transformer_blocks.7.txt_mod.1.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 416 |
+
"transformer_blocks.7.txt_mod.1.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 417 |
+
"transformer_blocks.8.attn.add_k_proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 418 |
+
"transformer_blocks.8.attn.add_k_proj.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 419 |
+
"transformer_blocks.8.attn.add_q_proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 420 |
+
"transformer_blocks.8.attn.add_q_proj.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 421 |
+
"transformer_blocks.8.attn.add_v_proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 422 |
+
"transformer_blocks.8.attn.add_v_proj.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 423 |
+
"transformer_blocks.8.attn.norm_added_k.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 424 |
+
"transformer_blocks.8.attn.norm_added_q.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 425 |
+
"transformer_blocks.8.attn.norm_k.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 426 |
+
"transformer_blocks.8.attn.norm_q.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 427 |
+
"transformer_blocks.8.attn.to_add_out.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 428 |
+
"transformer_blocks.8.attn.to_add_out.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 429 |
+
"transformer_blocks.8.attn.to_k.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 430 |
+
"transformer_blocks.8.attn.to_k.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 431 |
+
"transformer_blocks.8.attn.to_out.0.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 432 |
+
"transformer_blocks.8.attn.to_out.0.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 433 |
+
"transformer_blocks.8.attn.to_q.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 434 |
+
"transformer_blocks.8.attn.to_q.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 435 |
+
"transformer_blocks.8.attn.to_v.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 436 |
+
"transformer_blocks.8.attn.to_v.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 437 |
+
"transformer_blocks.8.img_mlp.net.0.proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 438 |
+
"transformer_blocks.8.img_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 439 |
+
"transformer_blocks.8.img_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 440 |
+
"transformer_blocks.8.img_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 441 |
+
"transformer_blocks.8.img_mlp.net.2.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 442 |
+
"transformer_blocks.8.img_mlp.net.2.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 443 |
+
"transformer_blocks.8.img_mlp.net.2.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 444 |
+
"transformer_blocks.8.img_mlp.net.2.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 445 |
+
"transformer_blocks.8.img_mod.1.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 446 |
+
"transformer_blocks.8.img_mod.1.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 447 |
+
"transformer_blocks.8.txt_mlp.net.0.proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 448 |
+
"transformer_blocks.8.txt_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 449 |
+
"transformer_blocks.8.txt_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 450 |
+
"transformer_blocks.8.txt_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 451 |
+
"transformer_blocks.8.txt_mlp.net.2.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 452 |
+
"transformer_blocks.8.txt_mlp.net.2.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 453 |
+
"transformer_blocks.8.txt_mlp.net.2.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 454 |
+
"transformer_blocks.8.txt_mlp.net.2.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 455 |
+
"transformer_blocks.8.txt_mod.1.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 456 |
+
"transformer_blocks.8.txt_mod.1.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 457 |
+
"transformer_blocks.9.attn.add_k_proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 458 |
+
"transformer_blocks.9.attn.add_k_proj.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 459 |
+
"transformer_blocks.9.attn.add_q_proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 460 |
+
"transformer_blocks.9.attn.add_q_proj.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 461 |
+
"transformer_blocks.9.attn.add_v_proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 462 |
+
"transformer_blocks.9.attn.add_v_proj.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 463 |
+
"transformer_blocks.9.attn.norm_added_k.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 464 |
+
"transformer_blocks.9.attn.norm_added_q.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 465 |
+
"transformer_blocks.9.attn.norm_k.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 466 |
+
"transformer_blocks.9.attn.norm_q.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 467 |
+
"transformer_blocks.9.attn.to_add_out.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 468 |
+
"transformer_blocks.9.attn.to_add_out.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 469 |
+
"transformer_blocks.9.attn.to_k.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 470 |
+
"transformer_blocks.9.attn.to_k.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 471 |
+
"transformer_blocks.9.attn.to_out.0.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 472 |
+
"transformer_blocks.9.attn.to_out.0.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 473 |
+
"transformer_blocks.9.attn.to_q.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 474 |
+
"transformer_blocks.9.attn.to_q.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 475 |
+
"transformer_blocks.9.attn.to_v.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 476 |
+
"transformer_blocks.9.attn.to_v.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 477 |
+
"transformer_blocks.9.img_mlp.net.0.proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 478 |
+
"transformer_blocks.9.img_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 479 |
+
"transformer_blocks.9.img_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 480 |
+
"transformer_blocks.9.img_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 481 |
+
"transformer_blocks.9.img_mlp.net.2.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 482 |
+
"transformer_blocks.9.img_mlp.net.2.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 483 |
+
"transformer_blocks.9.img_mlp.net.2.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 484 |
+
"transformer_blocks.9.img_mlp.net.2.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 485 |
+
"transformer_blocks.9.img_mod.1.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 486 |
+
"transformer_blocks.9.img_mod.1.weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 487 |
+
"transformer_blocks.9.txt_mlp.net.0.proj.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 488 |
+
"transformer_blocks.9.txt_mlp.net.0.proj.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 489 |
+
"transformer_blocks.9.txt_mlp.net.0.proj.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 490 |
+
"transformer_blocks.9.txt_mlp.net.0.proj.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 491 |
+
"transformer_blocks.9.txt_mlp.net.2.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 492 |
+
"transformer_blocks.9.txt_mlp.net.2.packed_weight": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 493 |
+
"transformer_blocks.9.txt_mlp.net.2.weight_scale": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 494 |
+
"transformer_blocks.9.txt_mlp.net.2.weight_scales": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 495 |
+
"transformer_blocks.9.txt_mod.1.bias": "diffusion_pytorch_model-00003-of-00004.safetensors",
|
| 496 |
+
"transformer_blocks.9.txt_mod.1.weight": "diffusion_pytorch_model-00004-of-00004.safetensors",
|
| 497 |
+
"txt_in.bias": "diffusion_pytorch_model-00004-of-00004.safetensors",
|
| 498 |
+
"txt_in.weight": "diffusion_pytorch_model-00004-of-00004.safetensors",
|
| 499 |
+
"txt_norm.weight": "diffusion_pytorch_model-00004-of-00004.safetensors"
|
| 500 |
+
}
|
| 501 |
+
}
|
transformer/nvfp4_metadata.json
ADDED
|
@@ -0,0 +1,1648 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"format_version": 1,
|
| 3 |
+
"artifact_kind": "mage_flow_transformer_mlp_nvfp4_resident_v1",
|
| 4 |
+
"created_utc": "2026-07-24T07:00:11.747181+00:00",
|
| 5 |
+
"container": {
|
| 6 |
+
"format": "safetensors",
|
| 7 |
+
"header_metadata": {
|
| 8 |
+
"mage_nvfp4_contract": "mage_flow_transformer_mlp_nvfp4_resident_v1;format_version=1"
|
| 9 |
+
},
|
| 10 |
+
"header_encoding": "single deterministic contract key; legacy two-key draft headers remain readable"
|
| 11 |
+
},
|
| 12 |
+
"source": {
|
| 13 |
+
"transformer_config_path": "BASE_MODEL/transformer/config.json",
|
| 14 |
+
"transformer_checkpoint_path": "BASE_MODEL/transformer/diffusion_pytorch_model.safetensors",
|
| 15 |
+
"transformer_config_sha256": "8493c3b2722738c2a824ac82b1fd9c89fefb4e354fc88363207193db7fe702de",
|
| 16 |
+
"transformer_checkpoint_sha256": "04431abbc3acd1a5b86a7f77269f8e77e05a6cac3b21f35e1aef7493f6ab4934"
|
| 17 |
+
},
|
| 18 |
+
"library_hashes": {
|
| 19 |
+
"artifact_script_sha256": "459b4e8b7fb6b15b1f2eac3267082c52f1723efd65a59fe429066f565d224b81",
|
| 20 |
+
"resident_source_sha256": "060697d4784be3e3461d38ef6ff24128f47a4322a05e5463f8687796b03bda9f",
|
| 21 |
+
"resident_library_sha256": "d2f7d62d26b4ae3babb7791aee8e0d95ad4794ac9c1d22146c974784381ada48",
|
| 22 |
+
"mage_flow_py_sha256": "59b6e1bee7f95a7fd2fa7bd9e765966832951e2bb184b5ae27283884997b845f",
|
| 23 |
+
"mage_layers_py_sha256": "4b198343b8929f48a0a14d388502c81f54be17b222f6831303d2da6a91f33a62",
|
| 24 |
+
"pipeline_py_sha256": "b9fc57018570372dd3404a733e19b918a359188dd9f5ef7817c6b30969fc13db"
|
| 25 |
+
},
|
| 26 |
+
"environment": {
|
| 27 |
+
"python_version": "3.11.14 (main, Oct 10 2025, 08:54:04) [GCC 13.3.0]",
|
| 28 |
+
"torch_version": "2.13.0+cu130",
|
| 29 |
+
"safetensors_version": "0.8.0"
|
| 30 |
+
},
|
| 31 |
+
"model": {
|
| 32 |
+
"depth": 12,
|
| 33 |
+
"hidden_size": 3072,
|
| 34 |
+
"num_heads": 24,
|
| 35 |
+
"context_in_dim": 2560,
|
| 36 |
+
"in_channels": 128,
|
| 37 |
+
"out_channels": 128,
|
| 38 |
+
"patch_size": 1
|
| 39 |
+
},
|
| 40 |
+
"quantization": {
|
| 41 |
+
"format": "nvfp4_two_level",
|
| 42 |
+
"block_elements": 16,
|
| 43 |
+
"scale_tile_outer": 128,
|
| 44 |
+
"scale_tile_inner": 4,
|
| 45 |
+
"bias_policy": "artifact_bfloat16"
|
| 46 |
+
},
|
| 47 |
+
"targets": [
|
| 48 |
+
{
|
| 49 |
+
"module_key": "transformer_blocks.0.img_mlp.net.0.proj",
|
| 50 |
+
"weight_key": "transformer_blocks.0.img_mlp.net.0.proj.weight",
|
| 51 |
+
"bias_key": "transformer_blocks.0.img_mlp.net.0.proj.bias",
|
| 52 |
+
"weight_shape": [
|
| 53 |
+
12288,
|
| 54 |
+
3072
|
| 55 |
+
],
|
| 56 |
+
"bias_shape": [
|
| 57 |
+
12288
|
| 58 |
+
],
|
| 59 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.0.img_mlp.net.0.proj.weight_tensor_scale",
|
| 60 |
+
"artifact_weight_key": "targets.transformer_blocks.0.img_mlp.net.0.proj.packed_weight_e2m1",
|
| 61 |
+
"artifact_scale_key": "targets.transformer_blocks.0.img_mlp.net.0.proj.packed_scales_ue4m3",
|
| 62 |
+
"artifact_bias_key": "targets.transformer_blocks.0.img_mlp.net.0.proj.bias_bf16",
|
| 63 |
+
"weight_tensor_scale": 0.00024559383746236563,
|
| 64 |
+
"weight_global_amax": 0.66015625,
|
| 65 |
+
"packed_weight_bytes": 18874368,
|
| 66 |
+
"packed_scale_bytes": 2359296,
|
| 67 |
+
"scale_layout": {
|
| 68 |
+
"inner_dim": 192,
|
| 69 |
+
"outer_tiles": 96,
|
| 70 |
+
"bytes": 2359296
|
| 71 |
+
},
|
| 72 |
+
"source_weight_sha256": "d05d519a8eb21260cd6c7cdcc886ebd9e32986bd925f3b5a740f75c93ca39162",
|
| 73 |
+
"source_bias_sha256": "e032c18ee63a7667d67e97e8611440b64e31bda7e60df30fbe1784e6875ea55a"
|
| 74 |
+
},
|
| 75 |
+
{
|
| 76 |
+
"module_key": "transformer_blocks.0.img_mlp.net.2",
|
| 77 |
+
"weight_key": "transformer_blocks.0.img_mlp.net.2.weight",
|
| 78 |
+
"bias_key": "transformer_blocks.0.img_mlp.net.2.bias",
|
| 79 |
+
"weight_shape": [
|
| 80 |
+
3072,
|
| 81 |
+
12288
|
| 82 |
+
],
|
| 83 |
+
"bias_shape": [
|
| 84 |
+
3072
|
| 85 |
+
],
|
| 86 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.0.img_mlp.net.2.weight_tensor_scale",
|
| 87 |
+
"artifact_weight_key": "targets.transformer_blocks.0.img_mlp.net.2.packed_weight_e2m1",
|
| 88 |
+
"artifact_scale_key": "targets.transformer_blocks.0.img_mlp.net.2.packed_scales_ue4m3",
|
| 89 |
+
"artifact_bias_key": "targets.transformer_blocks.0.img_mlp.net.2.bias_bf16",
|
| 90 |
+
"weight_tensor_scale": 0.0002906435984186828,
|
| 91 |
+
"weight_global_amax": 0.78125,
|
| 92 |
+
"packed_weight_bytes": 18874368,
|
| 93 |
+
"packed_scale_bytes": 2359296,
|
| 94 |
+
"scale_layout": {
|
| 95 |
+
"inner_dim": 768,
|
| 96 |
+
"outer_tiles": 24,
|
| 97 |
+
"bytes": 2359296
|
| 98 |
+
},
|
| 99 |
+
"source_weight_sha256": "8a8cf7a4a5439eb2f3154ad636457ff25f665a95e755e58b2adbab7997f0ceb1",
|
| 100 |
+
"source_bias_sha256": "a1bece05c6b61ebba9379c39bc81d6a19fc0cb2fd0543c9a0faccd926f598a64"
|
| 101 |
+
},
|
| 102 |
+
{
|
| 103 |
+
"module_key": "transformer_blocks.0.txt_mlp.net.0.proj",
|
| 104 |
+
"weight_key": "transformer_blocks.0.txt_mlp.net.0.proj.weight",
|
| 105 |
+
"bias_key": "transformer_blocks.0.txt_mlp.net.0.proj.bias",
|
| 106 |
+
"weight_shape": [
|
| 107 |
+
12288,
|
| 108 |
+
3072
|
| 109 |
+
],
|
| 110 |
+
"bias_shape": [
|
| 111 |
+
12288
|
| 112 |
+
],
|
| 113 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.0.txt_mlp.net.0.proj.weight_tensor_scale",
|
| 114 |
+
"artifact_weight_key": "targets.transformer_blocks.0.txt_mlp.net.0.proj.packed_weight_e2m1",
|
| 115 |
+
"artifact_scale_key": "targets.transformer_blocks.0.txt_mlp.net.0.proj.packed_scales_ue4m3",
|
| 116 |
+
"artifact_bias_key": "targets.transformer_blocks.0.txt_mlp.net.0.proj.bias_bf16",
|
| 117 |
+
"weight_tensor_scale": 0.00024704704992473125,
|
| 118 |
+
"weight_global_amax": 0.6640625,
|
| 119 |
+
"packed_weight_bytes": 18874368,
|
| 120 |
+
"packed_scale_bytes": 2359296,
|
| 121 |
+
"scale_layout": {
|
| 122 |
+
"inner_dim": 192,
|
| 123 |
+
"outer_tiles": 96,
|
| 124 |
+
"bytes": 2359296
|
| 125 |
+
},
|
| 126 |
+
"source_weight_sha256": "40a2c2bf74fa661e51bf3294462ec6901e5f57abd1b0c8d8e3f7363c475aa33a",
|
| 127 |
+
"source_bias_sha256": "f181d4fef402d8369e38ce9f64fe91903a5002f503b6dde71120c0ecb873d21b"
|
| 128 |
+
},
|
| 129 |
+
{
|
| 130 |
+
"module_key": "transformer_blocks.0.txt_mlp.net.2",
|
| 131 |
+
"weight_key": "transformer_blocks.0.txt_mlp.net.2.weight",
|
| 132 |
+
"bias_key": "transformer_blocks.0.txt_mlp.net.2.bias",
|
| 133 |
+
"weight_shape": [
|
| 134 |
+
3072,
|
| 135 |
+
12288
|
| 136 |
+
],
|
| 137 |
+
"bias_shape": [
|
| 138 |
+
3072
|
| 139 |
+
],
|
| 140 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.0.txt_mlp.net.2.weight_tensor_scale",
|
| 141 |
+
"artifact_weight_key": "targets.transformer_blocks.0.txt_mlp.net.2.packed_weight_e2m1",
|
| 142 |
+
"artifact_scale_key": "targets.transformer_blocks.0.txt_mlp.net.2.packed_scales_ue4m3",
|
| 143 |
+
"artifact_bias_key": "targets.transformer_blocks.0.txt_mlp.net.2.bias_bf16",
|
| 144 |
+
"weight_tensor_scale": 0.00039527530316263437,
|
| 145 |
+
"weight_global_amax": 1.0625,
|
| 146 |
+
"packed_weight_bytes": 18874368,
|
| 147 |
+
"packed_scale_bytes": 2359296,
|
| 148 |
+
"scale_layout": {
|
| 149 |
+
"inner_dim": 768,
|
| 150 |
+
"outer_tiles": 24,
|
| 151 |
+
"bytes": 2359296
|
| 152 |
+
},
|
| 153 |
+
"source_weight_sha256": "37d5796891212472bfa97ce611f85266688107d579ebe1116eec5a958125152f",
|
| 154 |
+
"source_bias_sha256": "862c6c05b8cfc438dd7074299f2303b588af5ae52d94b1dcb3e9e58bea78c770"
|
| 155 |
+
},
|
| 156 |
+
{
|
| 157 |
+
"module_key": "transformer_blocks.1.img_mlp.net.0.proj",
|
| 158 |
+
"weight_key": "transformer_blocks.1.img_mlp.net.0.proj.weight",
|
| 159 |
+
"bias_key": "transformer_blocks.1.img_mlp.net.0.proj.bias",
|
| 160 |
+
"weight_shape": [
|
| 161 |
+
12288,
|
| 162 |
+
3072
|
| 163 |
+
],
|
| 164 |
+
"bias_shape": [
|
| 165 |
+
12288
|
| 166 |
+
],
|
| 167 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.1.img_mlp.net.0.proj.weight_tensor_scale",
|
| 168 |
+
"artifact_weight_key": "targets.transformer_blocks.1.img_mlp.net.0.proj.packed_weight_e2m1",
|
| 169 |
+
"artifact_scale_key": "targets.transformer_blocks.1.img_mlp.net.0.proj.packed_scales_ue4m3",
|
| 170 |
+
"artifact_bias_key": "targets.transformer_blocks.1.img_mlp.net.0.proj.bias_bf16",
|
| 171 |
+
"weight_tensor_scale": 0.00022815522970631719,
|
| 172 |
+
"weight_global_amax": 0.61328125,
|
| 173 |
+
"packed_weight_bytes": 18874368,
|
| 174 |
+
"packed_scale_bytes": 2359296,
|
| 175 |
+
"scale_layout": {
|
| 176 |
+
"inner_dim": 192,
|
| 177 |
+
"outer_tiles": 96,
|
| 178 |
+
"bytes": 2359296
|
| 179 |
+
},
|
| 180 |
+
"source_weight_sha256": "1ba75e81f6d7348769dae4a585d3127592fd2f402c101e3ddc88766f92d27617",
|
| 181 |
+
"source_bias_sha256": "76d85e6964ca67fd92a1e6907ab4b5d7b839caa55de787b6e4a2969c234fcec9"
|
| 182 |
+
},
|
| 183 |
+
{
|
| 184 |
+
"module_key": "transformer_blocks.1.img_mlp.net.2",
|
| 185 |
+
"weight_key": "transformer_blocks.1.img_mlp.net.2.weight",
|
| 186 |
+
"bias_key": "transformer_blocks.1.img_mlp.net.2.bias",
|
| 187 |
+
"weight_shape": [
|
| 188 |
+
3072,
|
| 189 |
+
12288
|
| 190 |
+
],
|
| 191 |
+
"bias_shape": [
|
| 192 |
+
3072
|
| 193 |
+
],
|
| 194 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.1.img_mlp.net.2.weight_tensor_scale",
|
| 195 |
+
"artifact_weight_key": "targets.transformer_blocks.1.img_mlp.net.2.packed_weight_e2m1",
|
| 196 |
+
"artifact_scale_key": "targets.transformer_blocks.1.img_mlp.net.2.packed_scales_ue4m3",
|
| 197 |
+
"artifact_bias_key": "targets.transformer_blocks.1.img_mlp.net.2.bias_bf16",
|
| 198 |
+
"weight_tensor_scale": 0.0004417782765813172,
|
| 199 |
+
"weight_global_amax": 1.1875,
|
| 200 |
+
"packed_weight_bytes": 18874368,
|
| 201 |
+
"packed_scale_bytes": 2359296,
|
| 202 |
+
"scale_layout": {
|
| 203 |
+
"inner_dim": 768,
|
| 204 |
+
"outer_tiles": 24,
|
| 205 |
+
"bytes": 2359296
|
| 206 |
+
},
|
| 207 |
+
"source_weight_sha256": "3f890473a9a99aa845e698713a852464055645b061b4b6ab61de59273b87be1b",
|
| 208 |
+
"source_bias_sha256": "06b80199d80cc3916a202cfe220f59e8572dd406b0d2be8b17d59c1a3de98619"
|
| 209 |
+
},
|
| 210 |
+
{
|
| 211 |
+
"module_key": "transformer_blocks.1.txt_mlp.net.0.proj",
|
| 212 |
+
"weight_key": "transformer_blocks.1.txt_mlp.net.0.proj.weight",
|
| 213 |
+
"bias_key": "transformer_blocks.1.txt_mlp.net.0.proj.bias",
|
| 214 |
+
"weight_shape": [
|
| 215 |
+
12288,
|
| 216 |
+
3072
|
| 217 |
+
],
|
| 218 |
+
"bias_shape": [
|
| 219 |
+
12288
|
| 220 |
+
],
|
| 221 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.1.txt_mlp.net.0.proj.weight_tensor_scale",
|
| 222 |
+
"artifact_weight_key": "targets.transformer_blocks.1.txt_mlp.net.0.proj.packed_weight_e2m1",
|
| 223 |
+
"artifact_scale_key": "targets.transformer_blocks.1.txt_mlp.net.0.proj.packed_scales_ue4m3",
|
| 224 |
+
"artifact_bias_key": "targets.transformer_blocks.1.txt_mlp.net.0.proj.bias_bf16",
|
| 225 |
+
"weight_tensor_scale": 0.00019618442456703633,
|
| 226 |
+
"weight_global_amax": 0.52734375,
|
| 227 |
+
"packed_weight_bytes": 18874368,
|
| 228 |
+
"packed_scale_bytes": 2359296,
|
| 229 |
+
"scale_layout": {
|
| 230 |
+
"inner_dim": 192,
|
| 231 |
+
"outer_tiles": 96,
|
| 232 |
+
"bytes": 2359296
|
| 233 |
+
},
|
| 234 |
+
"source_weight_sha256": "4a232bd2fdbf09232bd512cfb76c59502068529105e596c821bafff89c19813b",
|
| 235 |
+
"source_bias_sha256": "e94fb97549fc326506f37a6824ead6a538828909466a6bf76933dfe5ce73680d"
|
| 236 |
+
},
|
| 237 |
+
{
|
| 238 |
+
"module_key": "transformer_blocks.1.txt_mlp.net.2",
|
| 239 |
+
"weight_key": "transformer_blocks.1.txt_mlp.net.2.weight",
|
| 240 |
+
"bias_key": "transformer_blocks.1.txt_mlp.net.2.bias",
|
| 241 |
+
"weight_shape": [
|
| 242 |
+
3072,
|
| 243 |
+
12288
|
| 244 |
+
],
|
| 245 |
+
"bias_shape": [
|
| 246 |
+
3072
|
| 247 |
+
],
|
| 248 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.1.txt_mlp.net.2.weight_tensor_scale",
|
| 249 |
+
"artifact_weight_key": "targets.transformer_blocks.1.txt_mlp.net.2.packed_weight_e2m1",
|
| 250 |
+
"artifact_scale_key": "targets.transformer_blocks.1.txt_mlp.net.2.packed_scales_ue4m3",
|
| 251 |
+
"artifact_bias_key": "targets.transformer_blocks.1.txt_mlp.net.2.bias_bf16",
|
| 252 |
+
"weight_tensor_scale": 0.00039818172808736563,
|
| 253 |
+
"weight_global_amax": 1.0703125,
|
| 254 |
+
"packed_weight_bytes": 18874368,
|
| 255 |
+
"packed_scale_bytes": 2359296,
|
| 256 |
+
"scale_layout": {
|
| 257 |
+
"inner_dim": 768,
|
| 258 |
+
"outer_tiles": 24,
|
| 259 |
+
"bytes": 2359296
|
| 260 |
+
},
|
| 261 |
+
"source_weight_sha256": "042d74971fba414ffce050de7a65e1bebc33a2e49339b26f42518a9a9621701f",
|
| 262 |
+
"source_bias_sha256": "faa59706a19669dc45d820edab49feca78c6b1d9d90aca4adbd87040c36f2fea"
|
| 263 |
+
},
|
| 264 |
+
{
|
| 265 |
+
"module_key": "transformer_blocks.2.img_mlp.net.0.proj",
|
| 266 |
+
"weight_key": "transformer_blocks.2.img_mlp.net.0.proj.weight",
|
| 267 |
+
"bias_key": "transformer_blocks.2.img_mlp.net.0.proj.bias",
|
| 268 |
+
"weight_shape": [
|
| 269 |
+
12288,
|
| 270 |
+
3072
|
| 271 |
+
],
|
| 272 |
+
"bias_shape": [
|
| 273 |
+
12288
|
| 274 |
+
],
|
| 275 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.2.img_mlp.net.0.proj.weight_tensor_scale",
|
| 276 |
+
"artifact_weight_key": "targets.transformer_blocks.2.img_mlp.net.0.proj.packed_weight_e2m1",
|
| 277 |
+
"artifact_scale_key": "targets.transformer_blocks.2.img_mlp.net.0.proj.packed_scales_ue4m3",
|
| 278 |
+
"artifact_bias_key": "targets.transformer_blocks.2.img_mlp.net.0.proj.bias_bf16",
|
| 279 |
+
"weight_tensor_scale": 0.00030226935632526875,
|
| 280 |
+
"weight_global_amax": 0.8125,
|
| 281 |
+
"packed_weight_bytes": 18874368,
|
| 282 |
+
"packed_scale_bytes": 2359296,
|
| 283 |
+
"scale_layout": {
|
| 284 |
+
"inner_dim": 192,
|
| 285 |
+
"outer_tiles": 96,
|
| 286 |
+
"bytes": 2359296
|
| 287 |
+
},
|
| 288 |
+
"source_weight_sha256": "fceb9a0d7b4bfbddb10490b9d9e7f41c369e691bbafdccb003f8dda509fd3b1c",
|
| 289 |
+
"source_bias_sha256": "84af366af51b7cd24dac8a9fda557a2c8a66ca80e9f8621ec4729d31d64bd241"
|
| 290 |
+
},
|
| 291 |
+
{
|
| 292 |
+
"module_key": "transformer_blocks.2.img_mlp.net.2",
|
| 293 |
+
"weight_key": "transformer_blocks.2.img_mlp.net.2.weight",
|
| 294 |
+
"bias_key": "transformer_blocks.2.img_mlp.net.2.bias",
|
| 295 |
+
"weight_shape": [
|
| 296 |
+
3072,
|
| 297 |
+
12288
|
| 298 |
+
],
|
| 299 |
+
"bias_shape": [
|
| 300 |
+
3072
|
| 301 |
+
],
|
| 302 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.2.img_mlp.net.2.weight_tensor_scale",
|
| 303 |
+
"artifact_weight_key": "targets.transformer_blocks.2.img_mlp.net.2.packed_weight_e2m1",
|
| 304 |
+
"artifact_scale_key": "targets.transformer_blocks.2.img_mlp.net.2.packed_scales_ue4m3",
|
| 305 |
+
"artifact_bias_key": "targets.transformer_blocks.2.img_mlp.net.2.bias_bf16",
|
| 306 |
+
"weight_tensor_scale": 0.0005057198577560484,
|
| 307 |
+
"weight_global_amax": 1.359375,
|
| 308 |
+
"packed_weight_bytes": 18874368,
|
| 309 |
+
"packed_scale_bytes": 2359296,
|
| 310 |
+
"scale_layout": {
|
| 311 |
+
"inner_dim": 768,
|
| 312 |
+
"outer_tiles": 24,
|
| 313 |
+
"bytes": 2359296
|
| 314 |
+
},
|
| 315 |
+
"source_weight_sha256": "97220e0a04629f28c4ca39abfe46c07cf6183f660f391cd635be2871d325a09d",
|
| 316 |
+
"source_bias_sha256": "6bcc96109a2496ee73d6a04bb8016641e90d568e6e46ea1a2e8b9e84925bfab9"
|
| 317 |
+
},
|
| 318 |
+
{
|
| 319 |
+
"module_key": "transformer_blocks.2.txt_mlp.net.0.proj",
|
| 320 |
+
"weight_key": "transformer_blocks.2.txt_mlp.net.0.proj.weight",
|
| 321 |
+
"bias_key": "transformer_blocks.2.txt_mlp.net.0.proj.bias",
|
| 322 |
+
"weight_shape": [
|
| 323 |
+
12288,
|
| 324 |
+
3072
|
| 325 |
+
],
|
| 326 |
+
"bias_shape": [
|
| 327 |
+
12288
|
| 328 |
+
],
|
| 329 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.2.txt_mlp.net.0.proj.weight_tensor_scale",
|
| 330 |
+
"artifact_weight_key": "targets.transformer_blocks.2.txt_mlp.net.0.proj.packed_weight_e2m1",
|
| 331 |
+
"artifact_scale_key": "targets.transformer_blocks.2.txt_mlp.net.0.proj.packed_scales_ue4m3",
|
| 332 |
+
"artifact_bias_key": "targets.transformer_blocks.2.txt_mlp.net.0.proj.bias_bf16",
|
| 333 |
+
"weight_tensor_scale": 0.00022815522970631719,
|
| 334 |
+
"weight_global_amax": 0.61328125,
|
| 335 |
+
"packed_weight_bytes": 18874368,
|
| 336 |
+
"packed_scale_bytes": 2359296,
|
| 337 |
+
"scale_layout": {
|
| 338 |
+
"inner_dim": 192,
|
| 339 |
+
"outer_tiles": 96,
|
| 340 |
+
"bytes": 2359296
|
| 341 |
+
},
|
| 342 |
+
"source_weight_sha256": "764c79424da4eea44e62f03c1fda648bbd937ebf0b7c0bb43bede1e8b5130f29",
|
| 343 |
+
"source_bias_sha256": "eca2f24456368d090da90c98a102af788833b3f3f9130f29b5b7a734e36f307e"
|
| 344 |
+
},
|
| 345 |
+
{
|
| 346 |
+
"module_key": "transformer_blocks.2.txt_mlp.net.2",
|
| 347 |
+
"weight_key": "transformer_blocks.2.txt_mlp.net.2.weight",
|
| 348 |
+
"bias_key": "transformer_blocks.2.txt_mlp.net.2.bias",
|
| 349 |
+
"weight_shape": [
|
| 350 |
+
3072,
|
| 351 |
+
12288
|
| 352 |
+
],
|
| 353 |
+
"bias_shape": [
|
| 354 |
+
3072
|
| 355 |
+
],
|
| 356 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.2.txt_mlp.net.2.weight_tensor_scale",
|
| 357 |
+
"artifact_weight_key": "targets.transformer_blocks.2.txt_mlp.net.2.packed_weight_e2m1",
|
| 358 |
+
"artifact_scale_key": "targets.transformer_blocks.2.txt_mlp.net.2.packed_scales_ue4m3",
|
| 359 |
+
"artifact_bias_key": "targets.transformer_blocks.2.txt_mlp.net.2.bias_bf16",
|
| 360 |
+
"weight_tensor_scale": 0.0003589448460843414,
|
| 361 |
+
"weight_global_amax": 0.96484375,
|
| 362 |
+
"packed_weight_bytes": 18874368,
|
| 363 |
+
"packed_scale_bytes": 2359296,
|
| 364 |
+
"scale_layout": {
|
| 365 |
+
"inner_dim": 768,
|
| 366 |
+
"outer_tiles": 24,
|
| 367 |
+
"bytes": 2359296
|
| 368 |
+
},
|
| 369 |
+
"source_weight_sha256": "17e543841d93e1936514c8313a85e4b9444321db06111c33460173e47b0d1c93",
|
| 370 |
+
"source_bias_sha256": "eed98a4db5990a369a55eb0865482f174e1418e6f4231bcf65637939a1df8354"
|
| 371 |
+
},
|
| 372 |
+
{
|
| 373 |
+
"module_key": "transformer_blocks.3.img_mlp.net.0.proj",
|
| 374 |
+
"weight_key": "transformer_blocks.3.img_mlp.net.0.proj.weight",
|
| 375 |
+
"bias_key": "transformer_blocks.3.img_mlp.net.0.proj.bias",
|
| 376 |
+
"weight_shape": [
|
| 377 |
+
12288,
|
| 378 |
+
3072
|
| 379 |
+
],
|
| 380 |
+
"bias_shape": [
|
| 381 |
+
12288
|
| 382 |
+
],
|
| 383 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.3.img_mlp.net.0.proj.weight_tensor_scale",
|
| 384 |
+
"artifact_weight_key": "targets.transformer_blocks.3.img_mlp.net.0.proj.packed_weight_e2m1",
|
| 385 |
+
"artifact_scale_key": "targets.transformer_blocks.3.img_mlp.net.0.proj.packed_scales_ue4m3",
|
| 386 |
+
"artifact_bias_key": "targets.transformer_blocks.3.img_mlp.net.0.proj.bias_bf16",
|
| 387 |
+
"weight_tensor_scale": 0.00024850029149092734,
|
| 388 |
+
"weight_global_amax": 0.66796875,
|
| 389 |
+
"packed_weight_bytes": 18874368,
|
| 390 |
+
"packed_scale_bytes": 2359296,
|
| 391 |
+
"scale_layout": {
|
| 392 |
+
"inner_dim": 192,
|
| 393 |
+
"outer_tiles": 96,
|
| 394 |
+
"bytes": 2359296
|
| 395 |
+
},
|
| 396 |
+
"source_weight_sha256": "83c39bc7dd86d7e6a5f63eadc063b91d7bfd93a75a80c9536ddf28364d35ca6c",
|
| 397 |
+
"source_bias_sha256": "4f40baf432bc9106a645e1182bce1c18535e1370f0851cb0f6e21d3a1646a08e"
|
| 398 |
+
},
|
| 399 |
+
{
|
| 400 |
+
"module_key": "transformer_blocks.3.img_mlp.net.2",
|
| 401 |
+
"weight_key": "transformer_blocks.3.img_mlp.net.2.weight",
|
| 402 |
+
"bias_key": "transformer_blocks.3.img_mlp.net.2.bias",
|
| 403 |
+
"weight_shape": [
|
| 404 |
+
3072,
|
| 405 |
+
12288
|
| 406 |
+
],
|
| 407 |
+
"bias_shape": [
|
| 408 |
+
3072
|
| 409 |
+
],
|
| 410 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.3.img_mlp.net.2.weight_tensor_scale",
|
| 411 |
+
"artifact_weight_key": "targets.transformer_blocks.3.img_mlp.net.2.packed_weight_e2m1",
|
| 412 |
+
"artifact_scale_key": "targets.transformer_blocks.3.img_mlp.net.2.packed_scales_ue4m3",
|
| 413 |
+
"artifact_bias_key": "targets.transformer_blocks.3.img_mlp.net.2.bias_bf16",
|
| 414 |
+
"weight_tensor_scale": 0.0005871000466868281,
|
| 415 |
+
"weight_global_amax": 1.578125,
|
| 416 |
+
"packed_weight_bytes": 18874368,
|
| 417 |
+
"packed_scale_bytes": 2359296,
|
| 418 |
+
"scale_layout": {
|
| 419 |
+
"inner_dim": 768,
|
| 420 |
+
"outer_tiles": 24,
|
| 421 |
+
"bytes": 2359296
|
| 422 |
+
},
|
| 423 |
+
"source_weight_sha256": "e6945585cd816ccb2cd6e4a005326a105758f72270840842dea9c8617ba095ba",
|
| 424 |
+
"source_bias_sha256": "230dcffbe97056ccb1975e1dbdc4274a89a217c64b240dcb70d1a639c8d50e10"
|
| 425 |
+
},
|
| 426 |
+
{
|
| 427 |
+
"module_key": "transformer_blocks.3.txt_mlp.net.0.proj",
|
| 428 |
+
"weight_key": "transformer_blocks.3.txt_mlp.net.0.proj.weight",
|
| 429 |
+
"bias_key": "transformer_blocks.3.txt_mlp.net.0.proj.bias",
|
| 430 |
+
"weight_shape": [
|
| 431 |
+
12288,
|
| 432 |
+
3072
|
| 433 |
+
],
|
| 434 |
+
"bias_shape": [
|
| 435 |
+
12288
|
| 436 |
+
],
|
| 437 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.3.txt_mlp.net.0.proj.weight_tensor_scale",
|
| 438 |
+
"artifact_weight_key": "targets.transformer_blocks.3.txt_mlp.net.0.proj.packed_weight_e2m1",
|
| 439 |
+
"artifact_scale_key": "targets.transformer_blocks.3.txt_mlp.net.0.proj.packed_scales_ue4m3",
|
| 440 |
+
"artifact_bias_key": "targets.transformer_blocks.3.txt_mlp.net.0.proj.bias_bf16",
|
| 441 |
+
"weight_tensor_scale": 0.00022960844216868281,
|
| 442 |
+
"weight_global_amax": 0.6171875,
|
| 443 |
+
"packed_weight_bytes": 18874368,
|
| 444 |
+
"packed_scale_bytes": 2359296,
|
| 445 |
+
"scale_layout": {
|
| 446 |
+
"inner_dim": 192,
|
| 447 |
+
"outer_tiles": 96,
|
| 448 |
+
"bytes": 2359296
|
| 449 |
+
},
|
| 450 |
+
"source_weight_sha256": "786d08fafeb3519a0ab99e194105dfae1135ebbed18ae364fd62ba323c6525a2",
|
| 451 |
+
"source_bias_sha256": "f493d8f0a032e8a1158ffc66ca65c00a78bdf8118d3bc307c4bab75605979d67"
|
| 452 |
+
},
|
| 453 |
+
{
|
| 454 |
+
"module_key": "transformer_blocks.3.txt_mlp.net.2",
|
| 455 |
+
"weight_key": "transformer_blocks.3.txt_mlp.net.2.weight",
|
| 456 |
+
"bias_key": "transformer_blocks.3.txt_mlp.net.2.bias",
|
| 457 |
+
"weight_shape": [
|
| 458 |
+
3072,
|
| 459 |
+
12288
|
| 460 |
+
],
|
| 461 |
+
"bias_shape": [
|
| 462 |
+
3072
|
| 463 |
+
],
|
| 464 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.3.txt_mlp.net.2.weight_tensor_scale",
|
| 465 |
+
"artifact_weight_key": "targets.transformer_blocks.3.txt_mlp.net.2.packed_weight_e2m1",
|
| 466 |
+
"artifact_scale_key": "targets.transformer_blocks.3.txt_mlp.net.2.packed_scales_ue4m3",
|
| 467 |
+
"artifact_bias_key": "targets.transformer_blocks.3.txt_mlp.net.2.bias_bf16",
|
| 468 |
+
"weight_tensor_scale": 0.0004039946070406586,
|
| 469 |
+
"weight_global_amax": 1.0859375,
|
| 470 |
+
"packed_weight_bytes": 18874368,
|
| 471 |
+
"packed_scale_bytes": 2359296,
|
| 472 |
+
"scale_layout": {
|
| 473 |
+
"inner_dim": 768,
|
| 474 |
+
"outer_tiles": 24,
|
| 475 |
+
"bytes": 2359296
|
| 476 |
+
},
|
| 477 |
+
"source_weight_sha256": "ba11326b827a9600c9d0ae9227f2d3c0ab9971a09afd7df18689f4f1436a4e76",
|
| 478 |
+
"source_bias_sha256": "c8a38cf2ae33d7b353cd71510a74ede4727b418285ea6dd5934274dcf5bbdf4f"
|
| 479 |
+
},
|
| 480 |
+
{
|
| 481 |
+
"module_key": "transformer_blocks.4.img_mlp.net.0.proj",
|
| 482 |
+
"weight_key": "transformer_blocks.4.img_mlp.net.0.proj.weight",
|
| 483 |
+
"bias_key": "transformer_blocks.4.img_mlp.net.0.proj.bias",
|
| 484 |
+
"weight_shape": [
|
| 485 |
+
12288,
|
| 486 |
+
3072
|
| 487 |
+
],
|
| 488 |
+
"bias_shape": [
|
| 489 |
+
12288
|
| 490 |
+
],
|
| 491 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.4.img_mlp.net.0.proj.weight_tensor_scale",
|
| 492 |
+
"artifact_weight_key": "targets.transformer_blocks.4.img_mlp.net.0.proj.packed_weight_e2m1",
|
| 493 |
+
"artifact_scale_key": "targets.transformer_blocks.4.img_mlp.net.0.proj.packed_scales_ue4m3",
|
| 494 |
+
"artifact_bias_key": "targets.transformer_blocks.4.img_mlp.net.0.proj.bias_bf16",
|
| 495 |
+
"weight_tensor_scale": 0.0003516787546686828,
|
| 496 |
+
"weight_global_amax": 0.9453125,
|
| 497 |
+
"packed_weight_bytes": 18874368,
|
| 498 |
+
"packed_scale_bytes": 2359296,
|
| 499 |
+
"scale_layout": {
|
| 500 |
+
"inner_dim": 192,
|
| 501 |
+
"outer_tiles": 96,
|
| 502 |
+
"bytes": 2359296
|
| 503 |
+
},
|
| 504 |
+
"source_weight_sha256": "2da64d233d618c639d7ac3a988c045771a20bb77634d272949dc1f9d3a26e6fa",
|
| 505 |
+
"source_bias_sha256": "6079037543d2b43a319f02021a6f71d2874efaaafd6a5a58f2813a9e160a9ca6"
|
| 506 |
+
},
|
| 507 |
+
{
|
| 508 |
+
"module_key": "transformer_blocks.4.img_mlp.net.2",
|
| 509 |
+
"weight_key": "transformer_blocks.4.img_mlp.net.2.weight",
|
| 510 |
+
"bias_key": "transformer_blocks.4.img_mlp.net.2.bias",
|
| 511 |
+
"weight_shape": [
|
| 512 |
+
3072,
|
| 513 |
+
12288
|
| 514 |
+
],
|
| 515 |
+
"bias_shape": [
|
| 516 |
+
3072
|
| 517 |
+
],
|
| 518 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.4.img_mlp.net.2.weight_tensor_scale",
|
| 519 |
+
"artifact_weight_key": "targets.transformer_blocks.4.img_mlp.net.2.packed_weight_e2m1",
|
| 520 |
+
"artifact_scale_key": "targets.transformer_blocks.4.img_mlp.net.2.packed_scales_ue4m3",
|
| 521 |
+
"artifact_bias_key": "targets.transformer_blocks.4.img_mlp.net.2.bias_bf16",
|
| 522 |
+
"weight_tensor_scale": 0.0009533109841868281,
|
| 523 |
+
"weight_global_amax": 2.5625,
|
| 524 |
+
"packed_weight_bytes": 18874368,
|
| 525 |
+
"packed_scale_bytes": 2359296,
|
| 526 |
+
"scale_layout": {
|
| 527 |
+
"inner_dim": 768,
|
| 528 |
+
"outer_tiles": 24,
|
| 529 |
+
"bytes": 2359296
|
| 530 |
+
},
|
| 531 |
+
"source_weight_sha256": "4db777bdb0beb299cb30ccc2043870bc4f311d5e6e30b6ad28e1cc7c29af96ed",
|
| 532 |
+
"source_bias_sha256": "d4e4ac8e94b3fd245c412ee58d3fc91e815fd03e49684d8596ac58566145f1fe"
|
| 533 |
+
},
|
| 534 |
+
{
|
| 535 |
+
"module_key": "transformer_blocks.4.txt_mlp.net.0.proj",
|
| 536 |
+
"weight_key": "transformer_blocks.4.txt_mlp.net.0.proj.weight",
|
| 537 |
+
"bias_key": "transformer_blocks.4.txt_mlp.net.0.proj.bias",
|
| 538 |
+
"weight_shape": [
|
| 539 |
+
12288,
|
| 540 |
+
3072
|
| 541 |
+
],
|
| 542 |
+
"bias_shape": [
|
| 543 |
+
12288
|
| 544 |
+
],
|
| 545 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.4.txt_mlp.net.0.proj.weight_tensor_scale",
|
| 546 |
+
"artifact_weight_key": "targets.transformer_blocks.4.txt_mlp.net.0.proj.packed_weight_e2m1",
|
| 547 |
+
"artifact_scale_key": "targets.transformer_blocks.4.txt_mlp.net.0.proj.packed_scales_ue4m3",
|
| 548 |
+
"artifact_bias_key": "targets.transformer_blocks.4.txt_mlp.net.0.proj.bias_bf16",
|
| 549 |
+
"weight_tensor_scale": 0.00018528530199546367,
|
| 550 |
+
"weight_global_amax": 0.498046875,
|
| 551 |
+
"packed_weight_bytes": 18874368,
|
| 552 |
+
"packed_scale_bytes": 2359296,
|
| 553 |
+
"scale_layout": {
|
| 554 |
+
"inner_dim": 192,
|
| 555 |
+
"outer_tiles": 96,
|
| 556 |
+
"bytes": 2359296
|
| 557 |
+
},
|
| 558 |
+
"source_weight_sha256": "0c956820e6938c52af5a237da6dcd4c41b509865b7631407a896cc6da6bdb985",
|
| 559 |
+
"source_bias_sha256": "edd0dfc9060a78b09c53bd0f38a9d0a5263faadbe3c522a7022115a2e09c211e"
|
| 560 |
+
},
|
| 561 |
+
{
|
| 562 |
+
"module_key": "transformer_blocks.4.txt_mlp.net.2",
|
| 563 |
+
"weight_key": "transformer_blocks.4.txt_mlp.net.2.weight",
|
| 564 |
+
"bias_key": "transformer_blocks.4.txt_mlp.net.2.bias",
|
| 565 |
+
"weight_shape": [
|
| 566 |
+
3072,
|
| 567 |
+
12288
|
| 568 |
+
],
|
| 569 |
+
"bias_shape": [
|
| 570 |
+
3072
|
| 571 |
+
],
|
| 572 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.4.txt_mlp.net.2.weight_tensor_scale",
|
| 573 |
+
"artifact_weight_key": "targets.transformer_blocks.4.txt_mlp.net.2.packed_weight_e2m1",
|
| 574 |
+
"artifact_scale_key": "targets.transformer_blocks.4.txt_mlp.net.2.packed_scales_ue4m3",
|
| 575 |
+
"artifact_bias_key": "targets.transformer_blocks.4.txt_mlp.net.2.bias_bf16",
|
| 576 |
+
"weight_tensor_scale": 0.0003284272679593414,
|
| 577 |
+
"weight_global_amax": 0.8828125,
|
| 578 |
+
"packed_weight_bytes": 18874368,
|
| 579 |
+
"packed_scale_bytes": 2359296,
|
| 580 |
+
"scale_layout": {
|
| 581 |
+
"inner_dim": 768,
|
| 582 |
+
"outer_tiles": 24,
|
| 583 |
+
"bytes": 2359296
|
| 584 |
+
},
|
| 585 |
+
"source_weight_sha256": "005cd523afdeb3e9d32a2b29dd0499fa994fd8a79af245604661ee8bc1af59c8",
|
| 586 |
+
"source_bias_sha256": "f333cc3ca7d6299b5cf44859662914d0fae7cbeba0433e0ae487d64cdc755685"
|
| 587 |
+
},
|
| 588 |
+
{
|
| 589 |
+
"module_key": "transformer_blocks.5.img_mlp.net.0.proj",
|
| 590 |
+
"weight_key": "transformer_blocks.5.img_mlp.net.0.proj.weight",
|
| 591 |
+
"bias_key": "transformer_blocks.5.img_mlp.net.0.proj.bias",
|
| 592 |
+
"weight_shape": [
|
| 593 |
+
12288,
|
| 594 |
+
3072
|
| 595 |
+
],
|
| 596 |
+
"bias_shape": [
|
| 597 |
+
12288
|
| 598 |
+
],
|
| 599 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.5.img_mlp.net.0.proj.weight_tensor_scale",
|
| 600 |
+
"artifact_weight_key": "targets.transformer_blocks.5.img_mlp.net.0.proj.packed_weight_e2m1",
|
| 601 |
+
"artifact_scale_key": "targets.transformer_blocks.5.img_mlp.net.0.proj.packed_scales_ue4m3",
|
| 602 |
+
"artifact_bias_key": "targets.transformer_blocks.5.img_mlp.net.0.proj.bias_bf16",
|
| 603 |
+
"weight_tensor_scale": 0.00040108818211592734,
|
| 604 |
+
"weight_global_amax": 1.078125,
|
| 605 |
+
"packed_weight_bytes": 18874368,
|
| 606 |
+
"packed_scale_bytes": 2359296,
|
| 607 |
+
"scale_layout": {
|
| 608 |
+
"inner_dim": 192,
|
| 609 |
+
"outer_tiles": 96,
|
| 610 |
+
"bytes": 2359296
|
| 611 |
+
},
|
| 612 |
+
"source_weight_sha256": "8419f255277f3a77facc9090e6882ab54d41e6f9f0bebb7930416e07c7276f55",
|
| 613 |
+
"source_bias_sha256": "c738466822f96ba14226ee1f254ec83099479436e1ff34d7ae220fdc7bad423d"
|
| 614 |
+
},
|
| 615 |
+
{
|
| 616 |
+
"module_key": "transformer_blocks.5.img_mlp.net.2",
|
| 617 |
+
"weight_key": "transformer_blocks.5.img_mlp.net.2.weight",
|
| 618 |
+
"bias_key": "transformer_blocks.5.img_mlp.net.2.bias",
|
| 619 |
+
"weight_shape": [
|
| 620 |
+
3072,
|
| 621 |
+
12288
|
| 622 |
+
],
|
| 623 |
+
"bias_shape": [
|
| 624 |
+
3072
|
| 625 |
+
],
|
| 626 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.5.img_mlp.net.2.weight_tensor_scale",
|
| 627 |
+
"artifact_weight_key": "targets.transformer_blocks.5.img_mlp.net.2.packed_weight_e2m1",
|
| 628 |
+
"artifact_scale_key": "targets.transformer_blocks.5.img_mlp.net.2.packed_scales_ue4m3",
|
| 629 |
+
"artifact_bias_key": "targets.transformer_blocks.5.img_mlp.net.2.bias_bf16",
|
| 630 |
+
"weight_tensor_scale": 0.0006684802938252687,
|
| 631 |
+
"weight_global_amax": 1.796875,
|
| 632 |
+
"packed_weight_bytes": 18874368,
|
| 633 |
+
"packed_scale_bytes": 2359296,
|
| 634 |
+
"scale_layout": {
|
| 635 |
+
"inner_dim": 768,
|
| 636 |
+
"outer_tiles": 24,
|
| 637 |
+
"bytes": 2359296
|
| 638 |
+
},
|
| 639 |
+
"source_weight_sha256": "97fbe5a8ee7151217b82e12295e02681af0ca707d3595c58b40aed31c3ae23b7",
|
| 640 |
+
"source_bias_sha256": "af3f83a94e0368f51c65b26a880b1bf3b64543910e9e270a04faa12aa58680da"
|
| 641 |
+
},
|
| 642 |
+
{
|
| 643 |
+
"module_key": "transformer_blocks.5.txt_mlp.net.0.proj",
|
| 644 |
+
"weight_key": "transformer_blocks.5.txt_mlp.net.0.proj.weight",
|
| 645 |
+
"bias_key": "transformer_blocks.5.txt_mlp.net.0.proj.bias",
|
| 646 |
+
"weight_shape": [
|
| 647 |
+
12288,
|
| 648 |
+
3072
|
| 649 |
+
],
|
| 650 |
+
"bias_shape": [
|
| 651 |
+
12288
|
| 652 |
+
],
|
| 653 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.5.txt_mlp.net.0.proj.weight_tensor_scale",
|
| 654 |
+
"artifact_weight_key": "targets.transformer_blocks.5.txt_mlp.net.0.proj.packed_weight_e2m1",
|
| 655 |
+
"artifact_scale_key": "targets.transformer_blocks.5.txt_mlp.net.0.proj.packed_scales_ue4m3",
|
| 656 |
+
"artifact_bias_key": "targets.transformer_blocks.5.txt_mlp.net.0.proj.bias_bf16",
|
| 657 |
+
"weight_tensor_scale": 0.0001874651206890121,
|
| 658 |
+
"weight_global_amax": 0.50390625,
|
| 659 |
+
"packed_weight_bytes": 18874368,
|
| 660 |
+
"packed_scale_bytes": 2359296,
|
| 661 |
+
"scale_layout": {
|
| 662 |
+
"inner_dim": 192,
|
| 663 |
+
"outer_tiles": 96,
|
| 664 |
+
"bytes": 2359296
|
| 665 |
+
},
|
| 666 |
+
"source_weight_sha256": "7e3b210a8d107962865efa3f3974a9ea29a60732f8002539e71837ebf9fb3787",
|
| 667 |
+
"source_bias_sha256": "a154c2419b8da86533eeb0e5a69c37cbfe5eff859f8196e192d5f942743c690d"
|
| 668 |
+
},
|
| 669 |
+
{
|
| 670 |
+
"module_key": "transformer_blocks.5.txt_mlp.net.2",
|
| 671 |
+
"weight_key": "transformer_blocks.5.txt_mlp.net.2.weight",
|
| 672 |
+
"bias_key": "transformer_blocks.5.txt_mlp.net.2.bias",
|
| 673 |
+
"weight_shape": [
|
| 674 |
+
3072,
|
| 675 |
+
12288
|
| 676 |
+
],
|
| 677 |
+
"bias_shape": [
|
| 678 |
+
3072
|
| 679 |
+
],
|
| 680 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.5.txt_mlp.net.2.weight_tensor_scale",
|
| 681 |
+
"artifact_weight_key": "targets.transformer_blocks.5.txt_mlp.net.2.packed_weight_e2m1",
|
| 682 |
+
"artifact_scale_key": "targets.transformer_blocks.5.txt_mlp.net.2.packed_scales_ue4m3",
|
| 683 |
+
"artifact_bias_key": "targets.transformer_blocks.5.txt_mlp.net.2.bias_bf16",
|
| 684 |
+
"weight_tensor_scale": 0.0003807431203313172,
|
| 685 |
+
"weight_global_amax": 1.0234375,
|
| 686 |
+
"packed_weight_bytes": 18874368,
|
| 687 |
+
"packed_scale_bytes": 2359296,
|
| 688 |
+
"scale_layout": {
|
| 689 |
+
"inner_dim": 768,
|
| 690 |
+
"outer_tiles": 24,
|
| 691 |
+
"bytes": 2359296
|
| 692 |
+
},
|
| 693 |
+
"source_weight_sha256": "906a7d3e7d32c10037e731b32904f683ff3529e0605dc619132c6095e87aeec4",
|
| 694 |
+
"source_bias_sha256": "e47066d5093c8d5df144ad237d0c0cbb8186b7c176764338b777873e623e226e"
|
| 695 |
+
},
|
| 696 |
+
{
|
| 697 |
+
"module_key": "transformer_blocks.6.img_mlp.net.0.proj",
|
| 698 |
+
"weight_key": "transformer_blocks.6.img_mlp.net.0.proj.weight",
|
| 699 |
+
"bias_key": "transformer_blocks.6.img_mlp.net.0.proj.bias",
|
| 700 |
+
"weight_shape": [
|
| 701 |
+
12288,
|
| 702 |
+
3072
|
| 703 |
+
],
|
| 704 |
+
"bias_shape": [
|
| 705 |
+
12288
|
| 706 |
+
],
|
| 707 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.6.img_mlp.net.0.proj.weight_tensor_scale",
|
| 708 |
+
"artifact_weight_key": "targets.transformer_blocks.6.img_mlp.net.0.proj.packed_weight_e2m1",
|
| 709 |
+
"artifact_scale_key": "targets.transformer_blocks.6.img_mlp.net.0.proj.packed_scales_ue4m3",
|
| 710 |
+
"artifact_bias_key": "targets.transformer_blocks.6.img_mlp.net.0.proj.bias_bf16",
|
| 711 |
+
"weight_tensor_scale": 0.00043015251867473125,
|
| 712 |
+
"weight_global_amax": 1.15625,
|
| 713 |
+
"packed_weight_bytes": 18874368,
|
| 714 |
+
"packed_scale_bytes": 2359296,
|
| 715 |
+
"scale_layout": {
|
| 716 |
+
"inner_dim": 192,
|
| 717 |
+
"outer_tiles": 96,
|
| 718 |
+
"bytes": 2359296
|
| 719 |
+
},
|
| 720 |
+
"source_weight_sha256": "6e2eaa52c7e036559066e6d0bd3e259b8b4a9e6010cfb5201d255aa673a95e9a",
|
| 721 |
+
"source_bias_sha256": "559dcbcbecdda86591182bdad53d03f173400d6abbdada125766b5c5e180887f"
|
| 722 |
+
},
|
| 723 |
+
{
|
| 724 |
+
"module_key": "transformer_blocks.6.img_mlp.net.2",
|
| 725 |
+
"weight_key": "transformer_blocks.6.img_mlp.net.2.weight",
|
| 726 |
+
"bias_key": "transformer_blocks.6.img_mlp.net.2.bias",
|
| 727 |
+
"weight_shape": [
|
| 728 |
+
3072,
|
| 729 |
+
12288
|
| 730 |
+
],
|
| 731 |
+
"bias_shape": [
|
| 732 |
+
3072
|
| 733 |
+
],
|
| 734 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.6.img_mlp.net.2.weight_tensor_scale",
|
| 735 |
+
"artifact_weight_key": "targets.transformer_blocks.6.img_mlp.net.2.packed_weight_e2m1",
|
| 736 |
+
"artifact_scale_key": "targets.transformer_blocks.6.img_mlp.net.2.packed_scales_ue4m3",
|
| 737 |
+
"artifact_bias_key": "targets.transformer_blocks.6.img_mlp.net.2.bias_bf16",
|
| 738 |
+
"weight_tensor_scale": 0.0007672990905120969,
|
| 739 |
+
"weight_global_amax": 2.0625,
|
| 740 |
+
"packed_weight_bytes": 18874368,
|
| 741 |
+
"packed_scale_bytes": 2359296,
|
| 742 |
+
"scale_layout": {
|
| 743 |
+
"inner_dim": 768,
|
| 744 |
+
"outer_tiles": 24,
|
| 745 |
+
"bytes": 2359296
|
| 746 |
+
},
|
| 747 |
+
"source_weight_sha256": "5d161c4e9f626a44cd150ccd49e4b6a3b91dfe9c78628edbe2d05bcfd205ee38",
|
| 748 |
+
"source_bias_sha256": "88e2f67cc46733f0a734a37bf6d6f6a52b12bbbfce8346a9ca81b4942a3c5c89"
|
| 749 |
+
},
|
| 750 |
+
{
|
| 751 |
+
"module_key": "transformer_blocks.6.txt_mlp.net.0.proj",
|
| 752 |
+
"weight_key": "transformer_blocks.6.txt_mlp.net.0.proj.weight",
|
| 753 |
+
"bias_key": "transformer_blocks.6.txt_mlp.net.0.proj.bias",
|
| 754 |
+
"weight_shape": [
|
| 755 |
+
12288,
|
| 756 |
+
3072
|
| 757 |
+
],
|
| 758 |
+
"bias_shape": [
|
| 759 |
+
12288
|
| 760 |
+
],
|
| 761 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.6.txt_mlp.net.0.proj.weight_tensor_scale",
|
| 762 |
+
"artifact_weight_key": "targets.transformer_blocks.6.txt_mlp.net.0.proj.packed_weight_e2m1",
|
| 763 |
+
"artifact_scale_key": "targets.transformer_blocks.6.txt_mlp.net.0.proj.packed_scales_ue4m3",
|
| 764 |
+
"artifact_bias_key": "targets.transformer_blocks.6.txt_mlp.net.0.proj.bias_bf16",
|
| 765 |
+
"weight_tensor_scale": 0.0002601260202936828,
|
| 766 |
+
"weight_global_amax": 0.69921875,
|
| 767 |
+
"packed_weight_bytes": 18874368,
|
| 768 |
+
"packed_scale_bytes": 2359296,
|
| 769 |
+
"scale_layout": {
|
| 770 |
+
"inner_dim": 192,
|
| 771 |
+
"outer_tiles": 96,
|
| 772 |
+
"bytes": 2359296
|
| 773 |
+
},
|
| 774 |
+
"source_weight_sha256": "fdd4c5e88701022033d97501bd2781fdc526eaa9427775a790756a9bb7c1fda2",
|
| 775 |
+
"source_bias_sha256": "37f6d759de4dfb875a8c7076fae88f75c2c5fdade293907895fead213e7a0ae7"
|
| 776 |
+
},
|
| 777 |
+
{
|
| 778 |
+
"module_key": "transformer_blocks.6.txt_mlp.net.2",
|
| 779 |
+
"weight_key": "transformer_blocks.6.txt_mlp.net.2.weight",
|
| 780 |
+
"bias_key": "transformer_blocks.6.txt_mlp.net.2.bias",
|
| 781 |
+
"weight_shape": [
|
| 782 |
+
3072,
|
| 783 |
+
12288
|
| 784 |
+
],
|
| 785 |
+
"bias_shape": [
|
| 786 |
+
3072
|
| 787 |
+
],
|
| 788 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.6.txt_mlp.net.2.weight_tensor_scale",
|
| 789 |
+
"artifact_weight_key": "targets.transformer_blocks.6.txt_mlp.net.2.packed_weight_e2m1",
|
| 790 |
+
"artifact_scale_key": "targets.transformer_blocks.6.txt_mlp.net.2.packed_scales_ue4m3",
|
| 791 |
+
"artifact_bias_key": "targets.transformer_blocks.6.txt_mlp.net.2.bias_bf16",
|
| 792 |
+
"weight_tensor_scale": 0.00047665549209341407,
|
| 793 |
+
"weight_global_amax": 1.28125,
|
| 794 |
+
"packed_weight_bytes": 18874368,
|
| 795 |
+
"packed_scale_bytes": 2359296,
|
| 796 |
+
"scale_layout": {
|
| 797 |
+
"inner_dim": 768,
|
| 798 |
+
"outer_tiles": 24,
|
| 799 |
+
"bytes": 2359296
|
| 800 |
+
},
|
| 801 |
+
"source_weight_sha256": "b28669406ee0f0e295c1a0b19241c1de874952807a0754d31078b56b1e213f2d",
|
| 802 |
+
"source_bias_sha256": "a5479cb1785b82d01897ca73a0b560b1382dcb7b60c8dce315e247d795e04d0f"
|
| 803 |
+
},
|
| 804 |
+
{
|
| 805 |
+
"module_key": "transformer_blocks.7.img_mlp.net.0.proj",
|
| 806 |
+
"weight_key": "transformer_blocks.7.img_mlp.net.0.proj.weight",
|
| 807 |
+
"bias_key": "transformer_blocks.7.img_mlp.net.0.proj.bias",
|
| 808 |
+
"weight_shape": [
|
| 809 |
+
12288,
|
| 810 |
+
3072
|
| 811 |
+
],
|
| 812 |
+
"bias_shape": [
|
| 813 |
+
12288
|
| 814 |
+
],
|
| 815 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.7.img_mlp.net.0.proj.weight_tensor_scale",
|
| 816 |
+
"artifact_weight_key": "targets.transformer_blocks.7.img_mlp.net.0.proj.packed_weight_e2m1",
|
| 817 |
+
"artifact_scale_key": "targets.transformer_blocks.7.img_mlp.net.0.proj.packed_scales_ue4m3",
|
| 818 |
+
"artifact_bias_key": "targets.transformer_blocks.7.img_mlp.net.0.proj.bias_bf16",
|
| 819 |
+
"weight_tensor_scale": 0.0003138950851280242,
|
| 820 |
+
"weight_global_amax": 0.84375,
|
| 821 |
+
"packed_weight_bytes": 18874368,
|
| 822 |
+
"packed_scale_bytes": 2359296,
|
| 823 |
+
"scale_layout": {
|
| 824 |
+
"inner_dim": 192,
|
| 825 |
+
"outer_tiles": 96,
|
| 826 |
+
"bytes": 2359296
|
| 827 |
+
},
|
| 828 |
+
"source_weight_sha256": "9bcc9591965c0b74076ad9da74307faa9c7fa0ab63a81f8f25960c2286ed39bf",
|
| 829 |
+
"source_bias_sha256": "b043d399ff8f040405104ffb775c9f902f95dca6de06a6b1ae2f6c90e5f28d7c"
|
| 830 |
+
},
|
| 831 |
+
{
|
| 832 |
+
"module_key": "transformer_blocks.7.img_mlp.net.2",
|
| 833 |
+
"weight_key": "transformer_blocks.7.img_mlp.net.2.weight",
|
| 834 |
+
"bias_key": "transformer_blocks.7.img_mlp.net.2.bias",
|
| 835 |
+
"weight_shape": [
|
| 836 |
+
3072,
|
| 837 |
+
12288
|
| 838 |
+
],
|
| 839 |
+
"bias_shape": [
|
| 840 |
+
3072
|
| 841 |
+
],
|
| 842 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.7.img_mlp.net.2.weight_tensor_scale",
|
| 843 |
+
"artifact_weight_key": "targets.transformer_blocks.7.img_mlp.net.2.packed_weight_e2m1",
|
| 844 |
+
"artifact_scale_key": "targets.transformer_blocks.7.img_mlp.net.2.packed_scales_ue4m3",
|
| 845 |
+
"artifact_bias_key": "targets.transformer_blocks.7.img_mlp.net.2.bias_bf16",
|
| 846 |
+
"weight_tensor_scale": 0.0008603050373494625,
|
| 847 |
+
"weight_global_amax": 2.3125,
|
| 848 |
+
"packed_weight_bytes": 18874368,
|
| 849 |
+
"packed_scale_bytes": 2359296,
|
| 850 |
+
"scale_layout": {
|
| 851 |
+
"inner_dim": 768,
|
| 852 |
+
"outer_tiles": 24,
|
| 853 |
+
"bytes": 2359296
|
| 854 |
+
},
|
| 855 |
+
"source_weight_sha256": "e7efbd25a434220ac1f0f4f11d8e501b699e11a948a86148460b4139db5ba3bf",
|
| 856 |
+
"source_bias_sha256": "b2cd1665c361e61f731ce7b474acb2e5f57087c2cacf53f92f418256c5f1ce86"
|
| 857 |
+
},
|
| 858 |
+
{
|
| 859 |
+
"module_key": "transformer_blocks.7.txt_mlp.net.0.proj",
|
| 860 |
+
"weight_key": "transformer_blocks.7.txt_mlp.net.0.proj.weight",
|
| 861 |
+
"bias_key": "transformer_blocks.7.txt_mlp.net.0.proj.bias",
|
| 862 |
+
"weight_shape": [
|
| 863 |
+
12288,
|
| 864 |
+
3072
|
| 865 |
+
],
|
| 866 |
+
"bias_shape": [
|
| 867 |
+
12288
|
| 868 |
+
],
|
| 869 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.7.txt_mlp.net.0.proj.weight_tensor_scale",
|
| 870 |
+
"artifact_weight_key": "targets.transformer_blocks.7.txt_mlp.net.0.proj.packed_weight_e2m1",
|
| 871 |
+
"artifact_scale_key": "targets.transformer_blocks.7.txt_mlp.net.0.proj.packed_scales_ue4m3",
|
| 872 |
+
"artifact_bias_key": "targets.transformer_blocks.7.txt_mlp.net.0.proj.bias_bf16",
|
| 873 |
+
"weight_tensor_scale": 0.00027901786961592734,
|
| 874 |
+
"weight_global_amax": 0.75,
|
| 875 |
+
"packed_weight_bytes": 18874368,
|
| 876 |
+
"packed_scale_bytes": 2359296,
|
| 877 |
+
"scale_layout": {
|
| 878 |
+
"inner_dim": 192,
|
| 879 |
+
"outer_tiles": 96,
|
| 880 |
+
"bytes": 2359296
|
| 881 |
+
},
|
| 882 |
+
"source_weight_sha256": "cb6126a8544c10ae9f23b3d782c83258a8e700a42ac2ad2e5001099e305f271b",
|
| 883 |
+
"source_bias_sha256": "ed4294351c27eaf4e5f85bb926daed8e479f3862e67e79142788a57490059768"
|
| 884 |
+
},
|
| 885 |
+
{
|
| 886 |
+
"module_key": "transformer_blocks.7.txt_mlp.net.2",
|
| 887 |
+
"weight_key": "transformer_blocks.7.txt_mlp.net.2.weight",
|
| 888 |
+
"bias_key": "transformer_blocks.7.txt_mlp.net.2.bias",
|
| 889 |
+
"weight_shape": [
|
| 890 |
+
3072,
|
| 891 |
+
12288
|
| 892 |
+
],
|
| 893 |
+
"bias_shape": [
|
| 894 |
+
3072
|
| 895 |
+
],
|
| 896 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.7.txt_mlp.net.2.weight_tensor_scale",
|
| 897 |
+
"artifact_weight_key": "targets.transformer_blocks.7.txt_mlp.net.2.packed_weight_e2m1",
|
| 898 |
+
"artifact_scale_key": "targets.transformer_blocks.7.txt_mlp.net.2.packed_scales_ue4m3",
|
| 899 |
+
"artifact_bias_key": "targets.transformer_blocks.7.txt_mlp.net.2.bias_bf16",
|
| 900 |
+
"weight_tensor_scale": 0.00039527530316263437,
|
| 901 |
+
"weight_global_amax": 1.0625,
|
| 902 |
+
"packed_weight_bytes": 18874368,
|
| 903 |
+
"packed_scale_bytes": 2359296,
|
| 904 |
+
"scale_layout": {
|
| 905 |
+
"inner_dim": 768,
|
| 906 |
+
"outer_tiles": 24,
|
| 907 |
+
"bytes": 2359296
|
| 908 |
+
},
|
| 909 |
+
"source_weight_sha256": "84caab56f058269c0576a27a2b52ae9945f225ba5b6c158cd3f8b8daf2a135f7",
|
| 910 |
+
"source_bias_sha256": "cb733e6b65392d1189b7a9d92984c51f8638a1375b6475023b26cd3c362ee7a8"
|
| 911 |
+
},
|
| 912 |
+
{
|
| 913 |
+
"module_key": "transformer_blocks.8.img_mlp.net.0.proj",
|
| 914 |
+
"weight_key": "transformer_blocks.8.img_mlp.net.0.proj.weight",
|
| 915 |
+
"bias_key": "transformer_blocks.8.img_mlp.net.0.proj.bias",
|
| 916 |
+
"weight_shape": [
|
| 917 |
+
12288,
|
| 918 |
+
3072
|
| 919 |
+
],
|
| 920 |
+
"bias_shape": [
|
| 921 |
+
12288
|
| 922 |
+
],
|
| 923 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.8.img_mlp.net.0.proj.weight_tensor_scale",
|
| 924 |
+
"artifact_weight_key": "targets.transformer_blocks.8.img_mlp.net.0.proj.packed_weight_e2m1",
|
| 925 |
+
"artifact_scale_key": "targets.transformer_blocks.8.img_mlp.net.0.proj.packed_scales_ue4m3",
|
| 926 |
+
"artifact_bias_key": "targets.transformer_blocks.8.img_mlp.net.0.proj.bias_bf16",
|
| 927 |
+
"weight_tensor_scale": 0.00041562033584341407,
|
| 928 |
+
"weight_global_amax": 1.1171875,
|
| 929 |
+
"packed_weight_bytes": 18874368,
|
| 930 |
+
"packed_scale_bytes": 2359296,
|
| 931 |
+
"scale_layout": {
|
| 932 |
+
"inner_dim": 192,
|
| 933 |
+
"outer_tiles": 96,
|
| 934 |
+
"bytes": 2359296
|
| 935 |
+
},
|
| 936 |
+
"source_weight_sha256": "b6da93ff7c47065d4dc8d5b44f02c1f956bb8f373d89526565b2cc1cc3c731ef",
|
| 937 |
+
"source_bias_sha256": "a155557ac69e102404fcde3489febf8d75d8df81a58f010ae722f73b808c0cf0"
|
| 938 |
+
},
|
| 939 |
+
{
|
| 940 |
+
"module_key": "transformer_blocks.8.img_mlp.net.2",
|
| 941 |
+
"weight_key": "transformer_blocks.8.img_mlp.net.2.weight",
|
| 942 |
+
"bias_key": "transformer_blocks.8.img_mlp.net.2.bias",
|
| 943 |
+
"weight_shape": [
|
| 944 |
+
3072,
|
| 945 |
+
12288
|
| 946 |
+
],
|
| 947 |
+
"bias_shape": [
|
| 948 |
+
3072
|
| 949 |
+
],
|
| 950 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.8.img_mlp.net.2.weight_tensor_scale",
|
| 951 |
+
"artifact_weight_key": "targets.transformer_blocks.8.img_mlp.net.2.packed_weight_e2m1",
|
| 952 |
+
"artifact_scale_key": "targets.transformer_blocks.8.img_mlp.net.2.packed_scales_ue4m3",
|
| 953 |
+
"artifact_bias_key": "targets.transformer_blocks.8.img_mlp.net.2.bias_bf16",
|
| 954 |
+
"weight_tensor_scale": 0.0005260648904368281,
|
| 955 |
+
"weight_global_amax": 1.4140625,
|
| 956 |
+
"packed_weight_bytes": 18874368,
|
| 957 |
+
"packed_scale_bytes": 2359296,
|
| 958 |
+
"scale_layout": {
|
| 959 |
+
"inner_dim": 768,
|
| 960 |
+
"outer_tiles": 24,
|
| 961 |
+
"bytes": 2359296
|
| 962 |
+
},
|
| 963 |
+
"source_weight_sha256": "928218f79be4de0ca6fd6f855310ad7a91b588be9c970c9ea6a11f55b1a1e413",
|
| 964 |
+
"source_bias_sha256": "669dfaa63f72b425feecba8fb58b4c43926e96d971f2f2e9241632855c559236"
|
| 965 |
+
},
|
| 966 |
+
{
|
| 967 |
+
"module_key": "transformer_blocks.8.txt_mlp.net.0.proj",
|
| 968 |
+
"weight_key": "transformer_blocks.8.txt_mlp.net.0.proj.weight",
|
| 969 |
+
"bias_key": "transformer_blocks.8.txt_mlp.net.0.proj.bias",
|
| 970 |
+
"weight_shape": [
|
| 971 |
+
12288,
|
| 972 |
+
3072
|
| 973 |
+
],
|
| 974 |
+
"bias_shape": [
|
| 975 |
+
12288
|
| 976 |
+
],
|
| 977 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.8.txt_mlp.net.0.proj.weight_tensor_scale",
|
| 978 |
+
"artifact_weight_key": "targets.transformer_blocks.8.txt_mlp.net.0.proj.packed_weight_e2m1",
|
| 979 |
+
"artifact_scale_key": "targets.transformer_blocks.8.txt_mlp.net.0.proj.packed_scales_ue4m3",
|
| 980 |
+
"artifact_bias_key": "targets.transformer_blocks.8.txt_mlp.net.0.proj.bias_bf16",
|
| 981 |
+
"weight_tensor_scale": 0.0002601260202936828,
|
| 982 |
+
"weight_global_amax": 0.69921875,
|
| 983 |
+
"packed_weight_bytes": 18874368,
|
| 984 |
+
"packed_scale_bytes": 2359296,
|
| 985 |
+
"scale_layout": {
|
| 986 |
+
"inner_dim": 192,
|
| 987 |
+
"outer_tiles": 96,
|
| 988 |
+
"bytes": 2359296
|
| 989 |
+
},
|
| 990 |
+
"source_weight_sha256": "9342523cc73ba392d60e1dffdfe37824a20e5173281de80f85a4cef4893363db",
|
| 991 |
+
"source_bias_sha256": "8c13b195dab6195c7654e07b8416da4c952df50f16fd9eccb248c1a38262df10"
|
| 992 |
+
},
|
| 993 |
+
{
|
| 994 |
+
"module_key": "transformer_blocks.8.txt_mlp.net.2",
|
| 995 |
+
"weight_key": "transformer_blocks.8.txt_mlp.net.2.weight",
|
| 996 |
+
"bias_key": "transformer_blocks.8.txt_mlp.net.2.bias",
|
| 997 |
+
"weight_shape": [
|
| 998 |
+
3072,
|
| 999 |
+
12288
|
| 1000 |
+
],
|
| 1001 |
+
"bias_shape": [
|
| 1002 |
+
3072
|
| 1003 |
+
],
|
| 1004 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.8.txt_mlp.net.2.weight_tensor_scale",
|
| 1005 |
+
"artifact_weight_key": "targets.transformer_blocks.8.txt_mlp.net.2.packed_weight_e2m1",
|
| 1006 |
+
"artifact_scale_key": "targets.transformer_blocks.8.txt_mlp.net.2.packed_scales_ue4m3",
|
| 1007 |
+
"artifact_bias_key": "targets.transformer_blocks.8.txt_mlp.net.2.bias_bf16",
|
| 1008 |
+
"weight_tensor_scale": 0.00042724609375,
|
| 1009 |
+
"weight_global_amax": 1.1484375,
|
| 1010 |
+
"packed_weight_bytes": 18874368,
|
| 1011 |
+
"packed_scale_bytes": 2359296,
|
| 1012 |
+
"scale_layout": {
|
| 1013 |
+
"inner_dim": 768,
|
| 1014 |
+
"outer_tiles": 24,
|
| 1015 |
+
"bytes": 2359296
|
| 1016 |
+
},
|
| 1017 |
+
"source_weight_sha256": "b41e8d7028a6f787c256611afeae81c47990e7a91e9e682902dcedfcc9c13eaa",
|
| 1018 |
+
"source_bias_sha256": "4dcdabdb17aabba97ac13d6e3c733a3b212d7e9e7092a7e0fbde6ccd81337a76"
|
| 1019 |
+
},
|
| 1020 |
+
{
|
| 1021 |
+
"module_key": "transformer_blocks.9.img_mlp.net.0.proj",
|
| 1022 |
+
"weight_key": "transformer_blocks.9.img_mlp.net.0.proj.weight",
|
| 1023 |
+
"bias_key": "transformer_blocks.9.img_mlp.net.0.proj.bias",
|
| 1024 |
+
"weight_shape": [
|
| 1025 |
+
12288,
|
| 1026 |
+
3072
|
| 1027 |
+
],
|
| 1028 |
+
"bias_shape": [
|
| 1029 |
+
12288
|
| 1030 |
+
],
|
| 1031 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.9.img_mlp.net.0.proj.weight_tensor_scale",
|
| 1032 |
+
"artifact_weight_key": "targets.transformer_blocks.9.img_mlp.net.0.proj.packed_weight_e2m1",
|
| 1033 |
+
"artifact_scale_key": "targets.transformer_blocks.9.img_mlp.net.0.proj.packed_scales_ue4m3",
|
| 1034 |
+
"artifact_bias_key": "targets.transformer_blocks.9.img_mlp.net.0.proj.bias_bf16",
|
| 1035 |
+
"weight_tensor_scale": 0.0004911876749247313,
|
| 1036 |
+
"weight_global_amax": 1.3203125,
|
| 1037 |
+
"packed_weight_bytes": 18874368,
|
| 1038 |
+
"packed_scale_bytes": 2359296,
|
| 1039 |
+
"scale_layout": {
|
| 1040 |
+
"inner_dim": 192,
|
| 1041 |
+
"outer_tiles": 96,
|
| 1042 |
+
"bytes": 2359296
|
| 1043 |
+
},
|
| 1044 |
+
"source_weight_sha256": "82c42f661aab545f0807ff6aa8a1da83f8b50c186b2fec439ab65540449f4b58",
|
| 1045 |
+
"source_bias_sha256": "790d198723850350f0430dc9262fc2019d3b1e3b7e32934212015bff7b767ff1"
|
| 1046 |
+
},
|
| 1047 |
+
{
|
| 1048 |
+
"module_key": "transformer_blocks.9.img_mlp.net.2",
|
| 1049 |
+
"weight_key": "transformer_blocks.9.img_mlp.net.2.weight",
|
| 1050 |
+
"bias_key": "transformer_blocks.9.img_mlp.net.2.bias",
|
| 1051 |
+
"weight_shape": [
|
| 1052 |
+
3072,
|
| 1053 |
+
12288
|
| 1054 |
+
],
|
| 1055 |
+
"bias_shape": [
|
| 1056 |
+
3072
|
| 1057 |
+
],
|
| 1058 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.9.img_mlp.net.2.weight_tensor_scale",
|
| 1059 |
+
"artifact_weight_key": "targets.transformer_blocks.9.img_mlp.net.2.packed_weight_e2m1",
|
| 1060 |
+
"artifact_scale_key": "targets.transformer_blocks.9.img_mlp.net.2.packed_scales_ue4m3",
|
| 1061 |
+
"artifact_bias_key": "targets.transformer_blocks.9.img_mlp.net.2.bias_bf16",
|
| 1062 |
+
"weight_tensor_scale": 0.0005435035564005375,
|
| 1063 |
+
"weight_global_amax": 1.4609375,
|
| 1064 |
+
"packed_weight_bytes": 18874368,
|
| 1065 |
+
"packed_scale_bytes": 2359296,
|
| 1066 |
+
"scale_layout": {
|
| 1067 |
+
"inner_dim": 768,
|
| 1068 |
+
"outer_tiles": 24,
|
| 1069 |
+
"bytes": 2359296
|
| 1070 |
+
},
|
| 1071 |
+
"source_weight_sha256": "0c2af8a1f4e348d1150532b2fb9da63c33bee8603e1d4720b2c830632d6157c2",
|
| 1072 |
+
"source_bias_sha256": "37f0b49b8f98d5981a92cc830bdcf202e077d458dcad8bedd26982ce12aef170"
|
| 1073 |
+
},
|
| 1074 |
+
{
|
| 1075 |
+
"module_key": "transformer_blocks.9.txt_mlp.net.0.proj",
|
| 1076 |
+
"weight_key": "transformer_blocks.9.txt_mlp.net.0.proj.weight",
|
| 1077 |
+
"bias_key": "transformer_blocks.9.txt_mlp.net.0.proj.bias",
|
| 1078 |
+
"weight_shape": [
|
| 1079 |
+
12288,
|
| 1080 |
+
3072
|
| 1081 |
+
],
|
| 1082 |
+
"bias_shape": [
|
| 1083 |
+
12288
|
| 1084 |
+
],
|
| 1085 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.9.txt_mlp.net.0.proj.weight_tensor_scale",
|
| 1086 |
+
"artifact_weight_key": "targets.transformer_blocks.9.txt_mlp.net.0.proj.packed_weight_e2m1",
|
| 1087 |
+
"artifact_scale_key": "targets.transformer_blocks.9.txt_mlp.net.0.proj.packed_scales_ue4m3",
|
| 1088 |
+
"artifact_bias_key": "targets.transformer_blocks.9.txt_mlp.net.0.proj.bias_bf16",
|
| 1089 |
+
"weight_tensor_scale": 0.0002819242945406586,
|
| 1090 |
+
"weight_global_amax": 0.7578125,
|
| 1091 |
+
"packed_weight_bytes": 18874368,
|
| 1092 |
+
"packed_scale_bytes": 2359296,
|
| 1093 |
+
"scale_layout": {
|
| 1094 |
+
"inner_dim": 192,
|
| 1095 |
+
"outer_tiles": 96,
|
| 1096 |
+
"bytes": 2359296
|
| 1097 |
+
},
|
| 1098 |
+
"source_weight_sha256": "7569c27e45b5327904b003909f88f373e99bd93b150a5508bd3c49470da94c8f",
|
| 1099 |
+
"source_bias_sha256": "5e5a68b982622caffbb11f7a7b131939ece3715bc0ffbcb32fa29dd445fd48ea"
|
| 1100 |
+
},
|
| 1101 |
+
{
|
| 1102 |
+
"module_key": "transformer_blocks.9.txt_mlp.net.2",
|
| 1103 |
+
"weight_key": "transformer_blocks.9.txt_mlp.net.2.weight",
|
| 1104 |
+
"bias_key": "transformer_blocks.9.txt_mlp.net.2.bias",
|
| 1105 |
+
"weight_shape": [
|
| 1106 |
+
3072,
|
| 1107 |
+
12288
|
| 1108 |
+
],
|
| 1109 |
+
"bias_shape": [
|
| 1110 |
+
3072
|
| 1111 |
+
],
|
| 1112 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.9.txt_mlp.net.2.weight_tensor_scale",
|
| 1113 |
+
"artifact_weight_key": "targets.transformer_blocks.9.txt_mlp.net.2.packed_weight_e2m1",
|
| 1114 |
+
"artifact_scale_key": "targets.transformer_blocks.9.txt_mlp.net.2.packed_scales_ue4m3",
|
| 1115 |
+
"artifact_bias_key": "targets.transformer_blocks.9.txt_mlp.net.2.bias_bf16",
|
| 1116 |
+
"weight_tensor_scale": 0.0006045387126505375,
|
| 1117 |
+
"weight_global_amax": 1.625,
|
| 1118 |
+
"packed_weight_bytes": 18874368,
|
| 1119 |
+
"packed_scale_bytes": 2359296,
|
| 1120 |
+
"scale_layout": {
|
| 1121 |
+
"inner_dim": 768,
|
| 1122 |
+
"outer_tiles": 24,
|
| 1123 |
+
"bytes": 2359296
|
| 1124 |
+
},
|
| 1125 |
+
"source_weight_sha256": "5ce62f2c67fdf6f7caf6841ed2a2cee2e72194768207c0ab631f147dae462f80",
|
| 1126 |
+
"source_bias_sha256": "11cb1954c19d92e661518cca9971ac432a4ff17f9d8832d789f91dc9bbaeca37"
|
| 1127 |
+
},
|
| 1128 |
+
{
|
| 1129 |
+
"module_key": "transformer_blocks.10.img_mlp.net.0.proj",
|
| 1130 |
+
"weight_key": "transformer_blocks.10.img_mlp.net.0.proj.weight",
|
| 1131 |
+
"bias_key": "transformer_blocks.10.img_mlp.net.0.proj.bias",
|
| 1132 |
+
"weight_shape": [
|
| 1133 |
+
12288,
|
| 1134 |
+
3072
|
| 1135 |
+
],
|
| 1136 |
+
"bias_shape": [
|
| 1137 |
+
12288
|
| 1138 |
+
],
|
| 1139 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.10.img_mlp.net.0.proj.weight_tensor_scale",
|
| 1140 |
+
"artifact_weight_key": "targets.transformer_blocks.10.img_mlp.net.0.proj.packed_weight_e2m1",
|
| 1141 |
+
"artifact_scale_key": "targets.transformer_blocks.10.img_mlp.net.0.proj.packed_scales_ue4m3",
|
| 1142 |
+
"artifact_bias_key": "targets.transformer_blocks.10.img_mlp.net.0.proj.bias_bf16",
|
| 1143 |
+
"weight_tensor_scale": 0.00019618442456703633,
|
| 1144 |
+
"weight_global_amax": 0.52734375,
|
| 1145 |
+
"packed_weight_bytes": 18874368,
|
| 1146 |
+
"packed_scale_bytes": 2359296,
|
| 1147 |
+
"scale_layout": {
|
| 1148 |
+
"inner_dim": 192,
|
| 1149 |
+
"outer_tiles": 96,
|
| 1150 |
+
"bytes": 2359296
|
| 1151 |
+
},
|
| 1152 |
+
"source_weight_sha256": "25c16255a05df5f725a32fa1183740eecd018f52fa5e52d7b117f396656307f7",
|
| 1153 |
+
"source_bias_sha256": "79ba550cd845a1d1156fa06661ea9a268331d2d3f13ed4f32225318796bdb2f1"
|
| 1154 |
+
},
|
| 1155 |
+
{
|
| 1156 |
+
"module_key": "transformer_blocks.10.img_mlp.net.2",
|
| 1157 |
+
"weight_key": "transformer_blocks.10.img_mlp.net.2.weight",
|
| 1158 |
+
"bias_key": "transformer_blocks.10.img_mlp.net.2.bias",
|
| 1159 |
+
"weight_shape": [
|
| 1160 |
+
3072,
|
| 1161 |
+
12288
|
| 1162 |
+
],
|
| 1163 |
+
"bias_shape": [
|
| 1164 |
+
3072
|
| 1165 |
+
],
|
| 1166 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.10.img_mlp.net.2.weight_tensor_scale",
|
| 1167 |
+
"artifact_weight_key": "targets.transformer_blocks.10.img_mlp.net.2.packed_weight_e2m1",
|
| 1168 |
+
"artifact_scale_key": "targets.transformer_blocks.10.img_mlp.net.2.packed_scales_ue4m3",
|
| 1169 |
+
"artifact_bias_key": "targets.transformer_blocks.10.img_mlp.net.2.bias_bf16",
|
| 1170 |
+
"weight_tensor_scale": 0.0005812871968373656,
|
| 1171 |
+
"weight_global_amax": 1.5625,
|
| 1172 |
+
"packed_weight_bytes": 18874368,
|
| 1173 |
+
"packed_scale_bytes": 2359296,
|
| 1174 |
+
"scale_layout": {
|
| 1175 |
+
"inner_dim": 768,
|
| 1176 |
+
"outer_tiles": 24,
|
| 1177 |
+
"bytes": 2359296
|
| 1178 |
+
},
|
| 1179 |
+
"source_weight_sha256": "710837d28af5c4871c535b02e29431563d3d3755236b8a4acf8e0ebb8c334764",
|
| 1180 |
+
"source_bias_sha256": "021b5a6799ed0e35ee49a28af9acc77561bc1defb184a47df39d28178edf3084"
|
| 1181 |
+
},
|
| 1182 |
+
{
|
| 1183 |
+
"module_key": "transformer_blocks.10.txt_mlp.net.0.proj",
|
| 1184 |
+
"weight_key": "transformer_blocks.10.txt_mlp.net.0.proj.weight",
|
| 1185 |
+
"bias_key": "transformer_blocks.10.txt_mlp.net.0.proj.bias",
|
| 1186 |
+
"weight_shape": [
|
| 1187 |
+
12288,
|
| 1188 |
+
3072
|
| 1189 |
+
],
|
| 1190 |
+
"bias_shape": [
|
| 1191 |
+
12288
|
| 1192 |
+
],
|
| 1193 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.10.txt_mlp.net.0.proj.weight_tensor_scale",
|
| 1194 |
+
"artifact_weight_key": "targets.transformer_blocks.10.txt_mlp.net.0.proj.packed_weight_e2m1",
|
| 1195 |
+
"artifact_scale_key": "targets.transformer_blocks.10.txt_mlp.net.0.proj.packed_scales_ue4m3",
|
| 1196 |
+
"artifact_bias_key": "targets.transformer_blocks.10.txt_mlp.net.0.proj.bias_bf16",
|
| 1197 |
+
"weight_tensor_scale": 0.00015476772387046367,
|
| 1198 |
+
"weight_global_amax": 0.416015625,
|
| 1199 |
+
"packed_weight_bytes": 18874368,
|
| 1200 |
+
"packed_scale_bytes": 2359296,
|
| 1201 |
+
"scale_layout": {
|
| 1202 |
+
"inner_dim": 192,
|
| 1203 |
+
"outer_tiles": 96,
|
| 1204 |
+
"bytes": 2359296
|
| 1205 |
+
},
|
| 1206 |
+
"source_weight_sha256": "dd76244de2f2319b3c617e91cc6e863fe6d05c83d0038d3b903e198993c497dc",
|
| 1207 |
+
"source_bias_sha256": "dd6336134c5a891280e5e0b22ae7690cddf6d4cf937c4198ac5865202958a162"
|
| 1208 |
+
},
|
| 1209 |
+
{
|
| 1210 |
+
"module_key": "transformer_blocks.10.txt_mlp.net.2",
|
| 1211 |
+
"weight_key": "transformer_blocks.10.txt_mlp.net.2.weight",
|
| 1212 |
+
"bias_key": "transformer_blocks.10.txt_mlp.net.2.bias",
|
| 1213 |
+
"weight_shape": [
|
| 1214 |
+
3072,
|
| 1215 |
+
12288
|
| 1216 |
+
],
|
| 1217 |
+
"bias_shape": [
|
| 1218 |
+
3072
|
| 1219 |
+
],
|
| 1220 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.10.txt_mlp.net.2.weight_tensor_scale",
|
| 1221 |
+
"artifact_weight_key": "targets.transformer_blocks.10.txt_mlp.net.2.packed_weight_e2m1",
|
| 1222 |
+
"artifact_scale_key": "targets.transformer_blocks.10.txt_mlp.net.2.packed_scales_ue4m3",
|
| 1223 |
+
"artifact_bias_key": "targets.transformer_blocks.10.txt_mlp.net.2.bias_bf16",
|
| 1224 |
+
"weight_tensor_scale": 0.00018383207498118281,
|
| 1225 |
+
"weight_global_amax": 0.494140625,
|
| 1226 |
+
"packed_weight_bytes": 18874368,
|
| 1227 |
+
"packed_scale_bytes": 2359296,
|
| 1228 |
+
"scale_layout": {
|
| 1229 |
+
"inner_dim": 768,
|
| 1230 |
+
"outer_tiles": 24,
|
| 1231 |
+
"bytes": 2359296
|
| 1232 |
+
},
|
| 1233 |
+
"source_weight_sha256": "3748e2fb3c162f846342f7a4c5f8f600d614150163332c2b32ba5f3c7291f4b4",
|
| 1234 |
+
"source_bias_sha256": "333b152ef1291bf1fcd6fc796345483195b2632b70e0b6ab13ddabf8b7bb20e4"
|
| 1235 |
+
},
|
| 1236 |
+
{
|
| 1237 |
+
"module_key": "transformer_blocks.11.img_mlp.net.0.proj",
|
| 1238 |
+
"weight_key": "transformer_blocks.11.img_mlp.net.0.proj.weight",
|
| 1239 |
+
"bias_key": "transformer_blocks.11.img_mlp.net.0.proj.bias",
|
| 1240 |
+
"weight_shape": [
|
| 1241 |
+
12288,
|
| 1242 |
+
3072
|
| 1243 |
+
],
|
| 1244 |
+
"bias_shape": [
|
| 1245 |
+
12288
|
| 1246 |
+
],
|
| 1247 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.11.img_mlp.net.0.proj.weight_tensor_scale",
|
| 1248 |
+
"artifact_weight_key": "targets.transformer_blocks.11.img_mlp.net.0.proj.packed_weight_e2m1",
|
| 1249 |
+
"artifact_scale_key": "targets.transformer_blocks.11.img_mlp.net.0.proj.packed_scales_ue4m3",
|
| 1250 |
+
"artifact_bias_key": "targets.transformer_blocks.11.img_mlp.net.0.proj.bias_bf16",
|
| 1251 |
+
"weight_tensor_scale": 0.0002252487902296707,
|
| 1252 |
+
"weight_global_amax": 0.60546875,
|
| 1253 |
+
"packed_weight_bytes": 18874368,
|
| 1254 |
+
"packed_scale_bytes": 2359296,
|
| 1255 |
+
"scale_layout": {
|
| 1256 |
+
"inner_dim": 192,
|
| 1257 |
+
"outer_tiles": 96,
|
| 1258 |
+
"bytes": 2359296
|
| 1259 |
+
},
|
| 1260 |
+
"source_weight_sha256": "81207ce0a6cc70bf94346c5c74e7729a9df4e2cc87e943f5461ce6ccdcddf25f",
|
| 1261 |
+
"source_bias_sha256": "3baf43b038997ba0ebe0e9326e17fe210727fe89fcc832f93082381c5cca2adc"
|
| 1262 |
+
},
|
| 1263 |
+
{
|
| 1264 |
+
"module_key": "transformer_blocks.11.img_mlp.net.2",
|
| 1265 |
+
"weight_key": "transformer_blocks.11.img_mlp.net.2.weight",
|
| 1266 |
+
"bias_key": "transformer_blocks.11.img_mlp.net.2.bias",
|
| 1267 |
+
"weight_shape": [
|
| 1268 |
+
3072,
|
| 1269 |
+
12288
|
| 1270 |
+
],
|
| 1271 |
+
"bias_shape": [
|
| 1272 |
+
3072
|
| 1273 |
+
],
|
| 1274 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.11.img_mlp.net.2.weight_tensor_scale",
|
| 1275 |
+
"artifact_weight_key": "targets.transformer_blocks.11.img_mlp.net.2.packed_weight_e2m1",
|
| 1276 |
+
"artifact_scale_key": "targets.transformer_blocks.11.img_mlp.net.2.packed_scales_ue4m3",
|
| 1277 |
+
"artifact_bias_key": "targets.transformer_blocks.11.img_mlp.net.2.bias_bf16",
|
| 1278 |
+
"weight_tensor_scale": 0.0009707496501505375,
|
| 1279 |
+
"weight_global_amax": 2.609375,
|
| 1280 |
+
"packed_weight_bytes": 18874368,
|
| 1281 |
+
"packed_scale_bytes": 2359296,
|
| 1282 |
+
"scale_layout": {
|
| 1283 |
+
"inner_dim": 768,
|
| 1284 |
+
"outer_tiles": 24,
|
| 1285 |
+
"bytes": 2359296
|
| 1286 |
+
},
|
| 1287 |
+
"source_weight_sha256": "ba3889bd47bfe14a07b6b4d9f624d1187980d07fb81de6d0be765f195a22c487",
|
| 1288 |
+
"source_bias_sha256": "fd48e42ed9061e494e065910ae622964b25e6c3abc13613b38f4b4a4a0c8ec13"
|
| 1289 |
+
},
|
| 1290 |
+
{
|
| 1291 |
+
"module_key": "transformer_blocks.11.txt_mlp.net.0.proj",
|
| 1292 |
+
"weight_key": "transformer_blocks.11.txt_mlp.net.0.proj.weight",
|
| 1293 |
+
"bias_key": "transformer_blocks.11.txt_mlp.net.0.proj.bias",
|
| 1294 |
+
"weight_shape": [
|
| 1295 |
+
12288,
|
| 1296 |
+
3072
|
| 1297 |
+
],
|
| 1298 |
+
"bias_shape": [
|
| 1299 |
+
12288
|
| 1300 |
+
],
|
| 1301 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.11.txt_mlp.net.0.proj.weight_tensor_scale",
|
| 1302 |
+
"artifact_weight_key": "targets.transformer_blocks.11.txt_mlp.net.0.proj.packed_weight_e2m1",
|
| 1303 |
+
"artifact_scale_key": "targets.transformer_blocks.11.txt_mlp.net.0.proj.packed_scales_ue4m3",
|
| 1304 |
+
"artifact_bias_key": "targets.transformer_blocks.11.txt_mlp.net.0.proj.bias_bf16",
|
| 1305 |
+
"weight_tensor_scale": 1.3850983577867737e-06,
|
| 1306 |
+
"weight_global_amax": 0.00372314453125,
|
| 1307 |
+
"packed_weight_bytes": 18874368,
|
| 1308 |
+
"packed_scale_bytes": 2359296,
|
| 1309 |
+
"scale_layout": {
|
| 1310 |
+
"inner_dim": 192,
|
| 1311 |
+
"outer_tiles": 96,
|
| 1312 |
+
"bytes": 2359296
|
| 1313 |
+
},
|
| 1314 |
+
"source_weight_sha256": "3e43f754f9d63e3dd6d6a71c4cc0c15344bd56da4d5a19a7adeb54445b820d72",
|
| 1315 |
+
"source_bias_sha256": "062da4757b9f03b3273c98cf91a517e2b4a667cbfe1f0ba190b02b626b0ac75c"
|
| 1316 |
+
},
|
| 1317 |
+
{
|
| 1318 |
+
"module_key": "transformer_blocks.11.txt_mlp.net.2",
|
| 1319 |
+
"weight_key": "transformer_blocks.11.txt_mlp.net.2.weight",
|
| 1320 |
+
"bias_key": "transformer_blocks.11.txt_mlp.net.2.bias",
|
| 1321 |
+
"weight_shape": [
|
| 1322 |
+
3072,
|
| 1323 |
+
12288
|
| 1324 |
+
],
|
| 1325 |
+
"bias_shape": [
|
| 1326 |
+
3072
|
| 1327 |
+
],
|
| 1328 |
+
"weight_tensor_scale_key": "targets.transformer_blocks.11.txt_mlp.net.2.weight_tensor_scale",
|
| 1329 |
+
"artifact_weight_key": "targets.transformer_blocks.11.txt_mlp.net.2.packed_weight_e2m1",
|
| 1330 |
+
"artifact_scale_key": "targets.transformer_blocks.11.txt_mlp.net.2.packed_scales_ue4m3",
|
| 1331 |
+
"artifact_bias_key": "targets.transformer_blocks.11.txt_mlp.net.2.bias_bf16",
|
| 1332 |
+
"weight_tensor_scale": 6.925491788933869e-07,
|
| 1333 |
+
"weight_global_amax": 0.001861572265625,
|
| 1334 |
+
"packed_weight_bytes": 18874368,
|
| 1335 |
+
"packed_scale_bytes": 2359296,
|
| 1336 |
+
"scale_layout": {
|
| 1337 |
+
"inner_dim": 768,
|
| 1338 |
+
"outer_tiles": 24,
|
| 1339 |
+
"bytes": 2359296
|
| 1340 |
+
},
|
| 1341 |
+
"source_weight_sha256": "4a74e0fc231d978ddbca106304430d9dcb511c7284e3c18c245005626e88224d",
|
| 1342 |
+
"source_bias_sha256": "6ef0d2f05867e5017824fc95cfd0a5803ce3d736cd622d82d92b603938d66e41"
|
| 1343 |
+
}
|
| 1344 |
+
],
|
| 1345 |
+
"non_target_keys": [
|
| 1346 |
+
"img_in.bias",
|
| 1347 |
+
"img_in.weight",
|
| 1348 |
+
"norm_out.linear.bias",
|
| 1349 |
+
"norm_out.linear.weight",
|
| 1350 |
+
"proj_out.bias",
|
| 1351 |
+
"proj_out.weight",
|
| 1352 |
+
"time_text_embed.timestep_embedder.linear_1.bias",
|
| 1353 |
+
"time_text_embed.timestep_embedder.linear_1.weight",
|
| 1354 |
+
"time_text_embed.timestep_embedder.linear_2.bias",
|
| 1355 |
+
"time_text_embed.timestep_embedder.linear_2.weight",
|
| 1356 |
+
"transformer_blocks.0.attn.add_k_proj.bias",
|
| 1357 |
+
"transformer_blocks.0.attn.add_k_proj.weight",
|
| 1358 |
+
"transformer_blocks.0.attn.add_q_proj.bias",
|
| 1359 |
+
"transformer_blocks.0.attn.add_q_proj.weight",
|
| 1360 |
+
"transformer_blocks.0.attn.add_v_proj.bias",
|
| 1361 |
+
"transformer_blocks.0.attn.add_v_proj.weight",
|
| 1362 |
+
"transformer_blocks.0.attn.norm_added_k.weight",
|
| 1363 |
+
"transformer_blocks.0.attn.norm_added_q.weight",
|
| 1364 |
+
"transformer_blocks.0.attn.norm_k.weight",
|
| 1365 |
+
"transformer_blocks.0.attn.norm_q.weight",
|
| 1366 |
+
"transformer_blocks.0.attn.to_add_out.bias",
|
| 1367 |
+
"transformer_blocks.0.attn.to_add_out.weight",
|
| 1368 |
+
"transformer_blocks.0.attn.to_k.bias",
|
| 1369 |
+
"transformer_blocks.0.attn.to_k.weight",
|
| 1370 |
+
"transformer_blocks.0.attn.to_out.0.bias",
|
| 1371 |
+
"transformer_blocks.0.attn.to_out.0.weight",
|
| 1372 |
+
"transformer_blocks.0.attn.to_q.bias",
|
| 1373 |
+
"transformer_blocks.0.attn.to_q.weight",
|
| 1374 |
+
"transformer_blocks.0.attn.to_v.bias",
|
| 1375 |
+
"transformer_blocks.0.attn.to_v.weight",
|
| 1376 |
+
"transformer_blocks.0.img_mod.1.bias",
|
| 1377 |
+
"transformer_blocks.0.img_mod.1.weight",
|
| 1378 |
+
"transformer_blocks.0.txt_mod.1.bias",
|
| 1379 |
+
"transformer_blocks.0.txt_mod.1.weight",
|
| 1380 |
+
"transformer_blocks.1.attn.add_k_proj.bias",
|
| 1381 |
+
"transformer_blocks.1.attn.add_k_proj.weight",
|
| 1382 |
+
"transformer_blocks.1.attn.add_q_proj.bias",
|
| 1383 |
+
"transformer_blocks.1.attn.add_q_proj.weight",
|
| 1384 |
+
"transformer_blocks.1.attn.add_v_proj.bias",
|
| 1385 |
+
"transformer_blocks.1.attn.add_v_proj.weight",
|
| 1386 |
+
"transformer_blocks.1.attn.norm_added_k.weight",
|
| 1387 |
+
"transformer_blocks.1.attn.norm_added_q.weight",
|
| 1388 |
+
"transformer_blocks.1.attn.norm_k.weight",
|
| 1389 |
+
"transformer_blocks.1.attn.norm_q.weight",
|
| 1390 |
+
"transformer_blocks.1.attn.to_add_out.bias",
|
| 1391 |
+
"transformer_blocks.1.attn.to_add_out.weight",
|
| 1392 |
+
"transformer_blocks.1.attn.to_k.bias",
|
| 1393 |
+
"transformer_blocks.1.attn.to_k.weight",
|
| 1394 |
+
"transformer_blocks.1.attn.to_out.0.bias",
|
| 1395 |
+
"transformer_blocks.1.attn.to_out.0.weight",
|
| 1396 |
+
"transformer_blocks.1.attn.to_q.bias",
|
| 1397 |
+
"transformer_blocks.1.attn.to_q.weight",
|
| 1398 |
+
"transformer_blocks.1.attn.to_v.bias",
|
| 1399 |
+
"transformer_blocks.1.attn.to_v.weight",
|
| 1400 |
+
"transformer_blocks.1.img_mod.1.bias",
|
| 1401 |
+
"transformer_blocks.1.img_mod.1.weight",
|
| 1402 |
+
"transformer_blocks.1.txt_mod.1.bias",
|
| 1403 |
+
"transformer_blocks.1.txt_mod.1.weight",
|
| 1404 |
+
"transformer_blocks.10.attn.add_k_proj.bias",
|
| 1405 |
+
"transformer_blocks.10.attn.add_k_proj.weight",
|
| 1406 |
+
"transformer_blocks.10.attn.add_q_proj.bias",
|
| 1407 |
+
"transformer_blocks.10.attn.add_q_proj.weight",
|
| 1408 |
+
"transformer_blocks.10.attn.add_v_proj.bias",
|
| 1409 |
+
"transformer_blocks.10.attn.add_v_proj.weight",
|
| 1410 |
+
"transformer_blocks.10.attn.norm_added_k.weight",
|
| 1411 |
+
"transformer_blocks.10.attn.norm_added_q.weight",
|
| 1412 |
+
"transformer_blocks.10.attn.norm_k.weight",
|
| 1413 |
+
"transformer_blocks.10.attn.norm_q.weight",
|
| 1414 |
+
"transformer_blocks.10.attn.to_add_out.bias",
|
| 1415 |
+
"transformer_blocks.10.attn.to_add_out.weight",
|
| 1416 |
+
"transformer_blocks.10.attn.to_k.bias",
|
| 1417 |
+
"transformer_blocks.10.attn.to_k.weight",
|
| 1418 |
+
"transformer_blocks.10.attn.to_out.0.bias",
|
| 1419 |
+
"transformer_blocks.10.attn.to_out.0.weight",
|
| 1420 |
+
"transformer_blocks.10.attn.to_q.bias",
|
| 1421 |
+
"transformer_blocks.10.attn.to_q.weight",
|
| 1422 |
+
"transformer_blocks.10.attn.to_v.bias",
|
| 1423 |
+
"transformer_blocks.10.attn.to_v.weight",
|
| 1424 |
+
"transformer_blocks.10.img_mod.1.bias",
|
| 1425 |
+
"transformer_blocks.10.img_mod.1.weight",
|
| 1426 |
+
"transformer_blocks.10.txt_mod.1.bias",
|
| 1427 |
+
"transformer_blocks.10.txt_mod.1.weight",
|
| 1428 |
+
"transformer_blocks.11.attn.add_k_proj.bias",
|
| 1429 |
+
"transformer_blocks.11.attn.add_k_proj.weight",
|
| 1430 |
+
"transformer_blocks.11.attn.add_q_proj.bias",
|
| 1431 |
+
"transformer_blocks.11.attn.add_q_proj.weight",
|
| 1432 |
+
"transformer_blocks.11.attn.add_v_proj.bias",
|
| 1433 |
+
"transformer_blocks.11.attn.add_v_proj.weight",
|
| 1434 |
+
"transformer_blocks.11.attn.norm_added_k.weight",
|
| 1435 |
+
"transformer_blocks.11.attn.norm_added_q.weight",
|
| 1436 |
+
"transformer_blocks.11.attn.norm_k.weight",
|
| 1437 |
+
"transformer_blocks.11.attn.norm_q.weight",
|
| 1438 |
+
"transformer_blocks.11.attn.to_add_out.bias",
|
| 1439 |
+
"transformer_blocks.11.attn.to_add_out.weight",
|
| 1440 |
+
"transformer_blocks.11.attn.to_k.bias",
|
| 1441 |
+
"transformer_blocks.11.attn.to_k.weight",
|
| 1442 |
+
"transformer_blocks.11.attn.to_out.0.bias",
|
| 1443 |
+
"transformer_blocks.11.attn.to_out.0.weight",
|
| 1444 |
+
"transformer_blocks.11.attn.to_q.bias",
|
| 1445 |
+
"transformer_blocks.11.attn.to_q.weight",
|
| 1446 |
+
"transformer_blocks.11.attn.to_v.bias",
|
| 1447 |
+
"transformer_blocks.11.attn.to_v.weight",
|
| 1448 |
+
"transformer_blocks.11.img_mod.1.bias",
|
| 1449 |
+
"transformer_blocks.11.img_mod.1.weight",
|
| 1450 |
+
"transformer_blocks.11.txt_mod.1.bias",
|
| 1451 |
+
"transformer_blocks.11.txt_mod.1.weight",
|
| 1452 |
+
"transformer_blocks.2.attn.add_k_proj.bias",
|
| 1453 |
+
"transformer_blocks.2.attn.add_k_proj.weight",
|
| 1454 |
+
"transformer_blocks.2.attn.add_q_proj.bias",
|
| 1455 |
+
"transformer_blocks.2.attn.add_q_proj.weight",
|
| 1456 |
+
"transformer_blocks.2.attn.add_v_proj.bias",
|
| 1457 |
+
"transformer_blocks.2.attn.add_v_proj.weight",
|
| 1458 |
+
"transformer_blocks.2.attn.norm_added_k.weight",
|
| 1459 |
+
"transformer_blocks.2.attn.norm_added_q.weight",
|
| 1460 |
+
"transformer_blocks.2.attn.norm_k.weight",
|
| 1461 |
+
"transformer_blocks.2.attn.norm_q.weight",
|
| 1462 |
+
"transformer_blocks.2.attn.to_add_out.bias",
|
| 1463 |
+
"transformer_blocks.2.attn.to_add_out.weight",
|
| 1464 |
+
"transformer_blocks.2.attn.to_k.bias",
|
| 1465 |
+
"transformer_blocks.2.attn.to_k.weight",
|
| 1466 |
+
"transformer_blocks.2.attn.to_out.0.bias",
|
| 1467 |
+
"transformer_blocks.2.attn.to_out.0.weight",
|
| 1468 |
+
"transformer_blocks.2.attn.to_q.bias",
|
| 1469 |
+
"transformer_blocks.2.attn.to_q.weight",
|
| 1470 |
+
"transformer_blocks.2.attn.to_v.bias",
|
| 1471 |
+
"transformer_blocks.2.attn.to_v.weight",
|
| 1472 |
+
"transformer_blocks.2.img_mod.1.bias",
|
| 1473 |
+
"transformer_blocks.2.img_mod.1.weight",
|
| 1474 |
+
"transformer_blocks.2.txt_mod.1.bias",
|
| 1475 |
+
"transformer_blocks.2.txt_mod.1.weight",
|
| 1476 |
+
"transformer_blocks.3.attn.add_k_proj.bias",
|
| 1477 |
+
"transformer_blocks.3.attn.add_k_proj.weight",
|
| 1478 |
+
"transformer_blocks.3.attn.add_q_proj.bias",
|
| 1479 |
+
"transformer_blocks.3.attn.add_q_proj.weight",
|
| 1480 |
+
"transformer_blocks.3.attn.add_v_proj.bias",
|
| 1481 |
+
"transformer_blocks.3.attn.add_v_proj.weight",
|
| 1482 |
+
"transformer_blocks.3.attn.norm_added_k.weight",
|
| 1483 |
+
"transformer_blocks.3.attn.norm_added_q.weight",
|
| 1484 |
+
"transformer_blocks.3.attn.norm_k.weight",
|
| 1485 |
+
"transformer_blocks.3.attn.norm_q.weight",
|
| 1486 |
+
"transformer_blocks.3.attn.to_add_out.bias",
|
| 1487 |
+
"transformer_blocks.3.attn.to_add_out.weight",
|
| 1488 |
+
"transformer_blocks.3.attn.to_k.bias",
|
| 1489 |
+
"transformer_blocks.3.attn.to_k.weight",
|
| 1490 |
+
"transformer_blocks.3.attn.to_out.0.bias",
|
| 1491 |
+
"transformer_blocks.3.attn.to_out.0.weight",
|
| 1492 |
+
"transformer_blocks.3.attn.to_q.bias",
|
| 1493 |
+
"transformer_blocks.3.attn.to_q.weight",
|
| 1494 |
+
"transformer_blocks.3.attn.to_v.bias",
|
| 1495 |
+
"transformer_blocks.3.attn.to_v.weight",
|
| 1496 |
+
"transformer_blocks.3.img_mod.1.bias",
|
| 1497 |
+
"transformer_blocks.3.img_mod.1.weight",
|
| 1498 |
+
"transformer_blocks.3.txt_mod.1.bias",
|
| 1499 |
+
"transformer_blocks.3.txt_mod.1.weight",
|
| 1500 |
+
"transformer_blocks.4.attn.add_k_proj.bias",
|
| 1501 |
+
"transformer_blocks.4.attn.add_k_proj.weight",
|
| 1502 |
+
"transformer_blocks.4.attn.add_q_proj.bias",
|
| 1503 |
+
"transformer_blocks.4.attn.add_q_proj.weight",
|
| 1504 |
+
"transformer_blocks.4.attn.add_v_proj.bias",
|
| 1505 |
+
"transformer_blocks.4.attn.add_v_proj.weight",
|
| 1506 |
+
"transformer_blocks.4.attn.norm_added_k.weight",
|
| 1507 |
+
"transformer_blocks.4.attn.norm_added_q.weight",
|
| 1508 |
+
"transformer_blocks.4.attn.norm_k.weight",
|
| 1509 |
+
"transformer_blocks.4.attn.norm_q.weight",
|
| 1510 |
+
"transformer_blocks.4.attn.to_add_out.bias",
|
| 1511 |
+
"transformer_blocks.4.attn.to_add_out.weight",
|
| 1512 |
+
"transformer_blocks.4.attn.to_k.bias",
|
| 1513 |
+
"transformer_blocks.4.attn.to_k.weight",
|
| 1514 |
+
"transformer_blocks.4.attn.to_out.0.bias",
|
| 1515 |
+
"transformer_blocks.4.attn.to_out.0.weight",
|
| 1516 |
+
"transformer_blocks.4.attn.to_q.bias",
|
| 1517 |
+
"transformer_blocks.4.attn.to_q.weight",
|
| 1518 |
+
"transformer_blocks.4.attn.to_v.bias",
|
| 1519 |
+
"transformer_blocks.4.attn.to_v.weight",
|
| 1520 |
+
"transformer_blocks.4.img_mod.1.bias",
|
| 1521 |
+
"transformer_blocks.4.img_mod.1.weight",
|
| 1522 |
+
"transformer_blocks.4.txt_mod.1.bias",
|
| 1523 |
+
"transformer_blocks.4.txt_mod.1.weight",
|
| 1524 |
+
"transformer_blocks.5.attn.add_k_proj.bias",
|
| 1525 |
+
"transformer_blocks.5.attn.add_k_proj.weight",
|
| 1526 |
+
"transformer_blocks.5.attn.add_q_proj.bias",
|
| 1527 |
+
"transformer_blocks.5.attn.add_q_proj.weight",
|
| 1528 |
+
"transformer_blocks.5.attn.add_v_proj.bias",
|
| 1529 |
+
"transformer_blocks.5.attn.add_v_proj.weight",
|
| 1530 |
+
"transformer_blocks.5.attn.norm_added_k.weight",
|
| 1531 |
+
"transformer_blocks.5.attn.norm_added_q.weight",
|
| 1532 |
+
"transformer_blocks.5.attn.norm_k.weight",
|
| 1533 |
+
"transformer_blocks.5.attn.norm_q.weight",
|
| 1534 |
+
"transformer_blocks.5.attn.to_add_out.bias",
|
| 1535 |
+
"transformer_blocks.5.attn.to_add_out.weight",
|
| 1536 |
+
"transformer_blocks.5.attn.to_k.bias",
|
| 1537 |
+
"transformer_blocks.5.attn.to_k.weight",
|
| 1538 |
+
"transformer_blocks.5.attn.to_out.0.bias",
|
| 1539 |
+
"transformer_blocks.5.attn.to_out.0.weight",
|
| 1540 |
+
"transformer_blocks.5.attn.to_q.bias",
|
| 1541 |
+
"transformer_blocks.5.attn.to_q.weight",
|
| 1542 |
+
"transformer_blocks.5.attn.to_v.bias",
|
| 1543 |
+
"transformer_blocks.5.attn.to_v.weight",
|
| 1544 |
+
"transformer_blocks.5.img_mod.1.bias",
|
| 1545 |
+
"transformer_blocks.5.img_mod.1.weight",
|
| 1546 |
+
"transformer_blocks.5.txt_mod.1.bias",
|
| 1547 |
+
"transformer_blocks.5.txt_mod.1.weight",
|
| 1548 |
+
"transformer_blocks.6.attn.add_k_proj.bias",
|
| 1549 |
+
"transformer_blocks.6.attn.add_k_proj.weight",
|
| 1550 |
+
"transformer_blocks.6.attn.add_q_proj.bias",
|
| 1551 |
+
"transformer_blocks.6.attn.add_q_proj.weight",
|
| 1552 |
+
"transformer_blocks.6.attn.add_v_proj.bias",
|
| 1553 |
+
"transformer_blocks.6.attn.add_v_proj.weight",
|
| 1554 |
+
"transformer_blocks.6.attn.norm_added_k.weight",
|
| 1555 |
+
"transformer_blocks.6.attn.norm_added_q.weight",
|
| 1556 |
+
"transformer_blocks.6.attn.norm_k.weight",
|
| 1557 |
+
"transformer_blocks.6.attn.norm_q.weight",
|
| 1558 |
+
"transformer_blocks.6.attn.to_add_out.bias",
|
| 1559 |
+
"transformer_blocks.6.attn.to_add_out.weight",
|
| 1560 |
+
"transformer_blocks.6.attn.to_k.bias",
|
| 1561 |
+
"transformer_blocks.6.attn.to_k.weight",
|
| 1562 |
+
"transformer_blocks.6.attn.to_out.0.bias",
|
| 1563 |
+
"transformer_blocks.6.attn.to_out.0.weight",
|
| 1564 |
+
"transformer_blocks.6.attn.to_q.bias",
|
| 1565 |
+
"transformer_blocks.6.attn.to_q.weight",
|
| 1566 |
+
"transformer_blocks.6.attn.to_v.bias",
|
| 1567 |
+
"transformer_blocks.6.attn.to_v.weight",
|
| 1568 |
+
"transformer_blocks.6.img_mod.1.bias",
|
| 1569 |
+
"transformer_blocks.6.img_mod.1.weight",
|
| 1570 |
+
"transformer_blocks.6.txt_mod.1.bias",
|
| 1571 |
+
"transformer_blocks.6.txt_mod.1.weight",
|
| 1572 |
+
"transformer_blocks.7.attn.add_k_proj.bias",
|
| 1573 |
+
"transformer_blocks.7.attn.add_k_proj.weight",
|
| 1574 |
+
"transformer_blocks.7.attn.add_q_proj.bias",
|
| 1575 |
+
"transformer_blocks.7.attn.add_q_proj.weight",
|
| 1576 |
+
"transformer_blocks.7.attn.add_v_proj.bias",
|
| 1577 |
+
"transformer_blocks.7.attn.add_v_proj.weight",
|
| 1578 |
+
"transformer_blocks.7.attn.norm_added_k.weight",
|
| 1579 |
+
"transformer_blocks.7.attn.norm_added_q.weight",
|
| 1580 |
+
"transformer_blocks.7.attn.norm_k.weight",
|
| 1581 |
+
"transformer_blocks.7.attn.norm_q.weight",
|
| 1582 |
+
"transformer_blocks.7.attn.to_add_out.bias",
|
| 1583 |
+
"transformer_blocks.7.attn.to_add_out.weight",
|
| 1584 |
+
"transformer_blocks.7.attn.to_k.bias",
|
| 1585 |
+
"transformer_blocks.7.attn.to_k.weight",
|
| 1586 |
+
"transformer_blocks.7.attn.to_out.0.bias",
|
| 1587 |
+
"transformer_blocks.7.attn.to_out.0.weight",
|
| 1588 |
+
"transformer_blocks.7.attn.to_q.bias",
|
| 1589 |
+
"transformer_blocks.7.attn.to_q.weight",
|
| 1590 |
+
"transformer_blocks.7.attn.to_v.bias",
|
| 1591 |
+
"transformer_blocks.7.attn.to_v.weight",
|
| 1592 |
+
"transformer_blocks.7.img_mod.1.bias",
|
| 1593 |
+
"transformer_blocks.7.img_mod.1.weight",
|
| 1594 |
+
"transformer_blocks.7.txt_mod.1.bias",
|
| 1595 |
+
"transformer_blocks.7.txt_mod.1.weight",
|
| 1596 |
+
"transformer_blocks.8.attn.add_k_proj.bias",
|
| 1597 |
+
"transformer_blocks.8.attn.add_k_proj.weight",
|
| 1598 |
+
"transformer_blocks.8.attn.add_q_proj.bias",
|
| 1599 |
+
"transformer_blocks.8.attn.add_q_proj.weight",
|
| 1600 |
+
"transformer_blocks.8.attn.add_v_proj.bias",
|
| 1601 |
+
"transformer_blocks.8.attn.add_v_proj.weight",
|
| 1602 |
+
"transformer_blocks.8.attn.norm_added_k.weight",
|
| 1603 |
+
"transformer_blocks.8.attn.norm_added_q.weight",
|
| 1604 |
+
"transformer_blocks.8.attn.norm_k.weight",
|
| 1605 |
+
"transformer_blocks.8.attn.norm_q.weight",
|
| 1606 |
+
"transformer_blocks.8.attn.to_add_out.bias",
|
| 1607 |
+
"transformer_blocks.8.attn.to_add_out.weight",
|
| 1608 |
+
"transformer_blocks.8.attn.to_k.bias",
|
| 1609 |
+
"transformer_blocks.8.attn.to_k.weight",
|
| 1610 |
+
"transformer_blocks.8.attn.to_out.0.bias",
|
| 1611 |
+
"transformer_blocks.8.attn.to_out.0.weight",
|
| 1612 |
+
"transformer_blocks.8.attn.to_q.bias",
|
| 1613 |
+
"transformer_blocks.8.attn.to_q.weight",
|
| 1614 |
+
"transformer_blocks.8.attn.to_v.bias",
|
| 1615 |
+
"transformer_blocks.8.attn.to_v.weight",
|
| 1616 |
+
"transformer_blocks.8.img_mod.1.bias",
|
| 1617 |
+
"transformer_blocks.8.img_mod.1.weight",
|
| 1618 |
+
"transformer_blocks.8.txt_mod.1.bias",
|
| 1619 |
+
"transformer_blocks.8.txt_mod.1.weight",
|
| 1620 |
+
"transformer_blocks.9.attn.add_k_proj.bias",
|
| 1621 |
+
"transformer_blocks.9.attn.add_k_proj.weight",
|
| 1622 |
+
"transformer_blocks.9.attn.add_q_proj.bias",
|
| 1623 |
+
"transformer_blocks.9.attn.add_q_proj.weight",
|
| 1624 |
+
"transformer_blocks.9.attn.add_v_proj.bias",
|
| 1625 |
+
"transformer_blocks.9.attn.add_v_proj.weight",
|
| 1626 |
+
"transformer_blocks.9.attn.norm_added_k.weight",
|
| 1627 |
+
"transformer_blocks.9.attn.norm_added_q.weight",
|
| 1628 |
+
"transformer_blocks.9.attn.norm_k.weight",
|
| 1629 |
+
"transformer_blocks.9.attn.norm_q.weight",
|
| 1630 |
+
"transformer_blocks.9.attn.to_add_out.bias",
|
| 1631 |
+
"transformer_blocks.9.attn.to_add_out.weight",
|
| 1632 |
+
"transformer_blocks.9.attn.to_k.bias",
|
| 1633 |
+
"transformer_blocks.9.attn.to_k.weight",
|
| 1634 |
+
"transformer_blocks.9.attn.to_out.0.bias",
|
| 1635 |
+
"transformer_blocks.9.attn.to_out.0.weight",
|
| 1636 |
+
"transformer_blocks.9.attn.to_q.bias",
|
| 1637 |
+
"transformer_blocks.9.attn.to_q.weight",
|
| 1638 |
+
"transformer_blocks.9.attn.to_v.bias",
|
| 1639 |
+
"transformer_blocks.9.attn.to_v.weight",
|
| 1640 |
+
"transformer_blocks.9.img_mod.1.bias",
|
| 1641 |
+
"transformer_blocks.9.img_mod.1.weight",
|
| 1642 |
+
"transformer_blocks.9.txt_mod.1.bias",
|
| 1643 |
+
"transformer_blocks.9.txt_mod.1.weight",
|
| 1644 |
+
"txt_in.bias",
|
| 1645 |
+
"txt_in.weight",
|
| 1646 |
+
"txt_norm.weight"
|
| 1647 |
+
]
|
| 1648 |
+
}
|
vae/config.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_class_name": "MageVAE",
|
| 3 |
+
"latent_channels": 128,
|
| 4 |
+
"downsample_factor": 16,
|
| 5 |
+
"sample_posterior": false
|
| 6 |
+
}
|
validate_release.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Verify every immutable file declared by the v1 release manifest."""
|
| 3 |
+
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
+
import hashlib
|
| 7 |
+
import json
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
import sys
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
RELEASE_ROOT = Path(__file__).resolve().parent
|
| 13 |
+
MANIFEST_PATH = RELEASE_ROOT / "MANIFEST.json"
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def sha256(path: Path) -> str:
|
| 17 |
+
digest = hashlib.sha256()
|
| 18 |
+
with path.open("rb") as handle:
|
| 19 |
+
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
| 20 |
+
digest.update(chunk)
|
| 21 |
+
return digest.hexdigest()
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def main() -> int:
|
| 25 |
+
manifest = json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
|
| 26 |
+
failures: list[str] = []
|
| 27 |
+
checked: list[dict[str, object]] = []
|
| 28 |
+
declared_paths = {
|
| 29 |
+
Path(record["path"]).as_posix() for record in manifest["files"]
|
| 30 |
+
}
|
| 31 |
+
actual_paths = {
|
| 32 |
+
path.relative_to(RELEASE_ROOT).as_posix()
|
| 33 |
+
for path in RELEASE_ROOT.rglob("*")
|
| 34 |
+
if (
|
| 35 |
+
path.is_file()
|
| 36 |
+
and path != MANIFEST_PATH
|
| 37 |
+
and "__pycache__" not in path.relative_to(RELEASE_ROOT).parts
|
| 38 |
+
and path.suffix != ".pyc"
|
| 39 |
+
)
|
| 40 |
+
}
|
| 41 |
+
undeclared = sorted(actual_paths - declared_paths)
|
| 42 |
+
absent = sorted(declared_paths - actual_paths)
|
| 43 |
+
if undeclared:
|
| 44 |
+
failures.extend(f"undeclared package file: {path}" for path in undeclared)
|
| 45 |
+
if absent:
|
| 46 |
+
failures.extend(f"declared package file is absent: {path}" for path in absent)
|
| 47 |
+
for record in manifest["files"]:
|
| 48 |
+
relative = Path(record["path"])
|
| 49 |
+
path = (RELEASE_ROOT / relative).resolve()
|
| 50 |
+
if not path.is_relative_to(RELEASE_ROOT):
|
| 51 |
+
failures.append(f"path escapes release root: {relative}")
|
| 52 |
+
continue
|
| 53 |
+
if not path.is_file():
|
| 54 |
+
failures.append(f"missing: {relative}")
|
| 55 |
+
continue
|
| 56 |
+
if path.is_symlink():
|
| 57 |
+
failures.append(f"release file must not be a symlink: {relative}")
|
| 58 |
+
continue
|
| 59 |
+
actual_size = path.stat().st_size
|
| 60 |
+
actual_hash = sha256(path)
|
| 61 |
+
if actual_size != record["size"]:
|
| 62 |
+
failures.append(
|
| 63 |
+
f"size mismatch {relative}: {actual_size} != {record['size']}"
|
| 64 |
+
)
|
| 65 |
+
if actual_hash != record["sha256"]:
|
| 66 |
+
failures.append(
|
| 67 |
+
f"SHA-256 mismatch {relative}: {actual_hash} != "
|
| 68 |
+
f"{record['sha256']}"
|
| 69 |
+
)
|
| 70 |
+
checked.append(
|
| 71 |
+
{
|
| 72 |
+
"path": str(relative),
|
| 73 |
+
"size": actual_size,
|
| 74 |
+
"sha256": actual_hash,
|
| 75 |
+
}
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
result = {
|
| 79 |
+
"schema_version": manifest["schema_version"],
|
| 80 |
+
"status": "pass" if not failures else "fail",
|
| 81 |
+
"checked_file_count": len(checked),
|
| 82 |
+
"failures": failures,
|
| 83 |
+
}
|
| 84 |
+
print(json.dumps(result, indent=2, sort_keys=True))
|
| 85 |
+
return 0 if not failures else 1
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
if __name__ == "__main__":
|
| 89 |
+
raise SystemExit(main())
|
vendor/mage_flow/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""MageFlow — standalone text-to-image + image-edit inference."""
|
| 2 |
+
|
| 3 |
+
from .models.mage_flow import ModelConfig
|
| 4 |
+
from .pipeline import (
|
| 5 |
+
MageFlowPipeline,
|
| 6 |
+
generate_edits,
|
| 7 |
+
generate_images,
|
| 8 |
+
load_from_repo,
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
__all__ = [
|
| 12 |
+
"MageFlowPipeline",
|
| 13 |
+
"generate_images",
|
| 14 |
+
"generate_edits",
|
| 15 |
+
"load_from_repo",
|
| 16 |
+
"ModelConfig",
|
| 17 |
+
]
|
| 18 |
+
__version__ = "0.1.0"
|
vendor/mage_flow/app.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Gradio app for MageFlow — text-to-image and instruction-based image editing.
|
| 2 |
+
|
| 3 |
+
python app.py # serve on 0.0.0.0:7860
|
| 4 |
+
python app.py --share --port 7861
|
| 5 |
+
|
| 6 |
+
Each tab has a model preset dropdown (base / rl / turbo) plus a free-form
|
| 7 |
+
"Custom model" box for any Hugging Face repo id or local path. Models load
|
| 8 |
+
lazily on first use and are cached. Notes:
|
| 9 |
+
- By default the presets point at the `microsoft/Mage-Flow*` Hugging Face
|
| 10 |
+
repos (downloaded + cached on first use). Set ``MAGEFLOW_HF_DIR`` to load
|
| 11 |
+
local checkpoint dirs instead.
|
| 12 |
+
- Turbo checkpoints are few-step: use steps=4, cfg=1.
|
| 13 |
+
"""
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
|
| 16 |
+
import argparse
|
| 17 |
+
import os
|
| 18 |
+
|
| 19 |
+
import gradio as gr
|
| 20 |
+
from PIL import Image
|
| 21 |
+
|
| 22 |
+
from mage_flow.pipeline import MageFlowPipeline
|
| 23 |
+
|
| 24 |
+
# Default to Hugging Face repo ids; if MAGEFLOW_HF_DIR is set, use local
|
| 25 |
+
# checkpoint dirs under it instead (local dir names match the HF repo basename).
|
| 26 |
+
HF_DIR = os.environ.get("MAGEFLOW_HF_DIR")
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _repo(hf_id: str, local_name: str) -> str:
|
| 30 |
+
return f"{HF_DIR}/{local_name}" if HF_DIR else hf_id
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
T2I_MODELS = {
|
| 34 |
+
"base": _repo("microsoft/Mage-Flow-Base", "Mage-Flow-Base"),
|
| 35 |
+
"rl": _repo("microsoft/Mage-Flow", "Mage-Flow"),
|
| 36 |
+
"turbo": _repo("microsoft/Mage-Flow-Turbo", "Mage-Flow-Turbo"),
|
| 37 |
+
}
|
| 38 |
+
EDIT_MODELS = {
|
| 39 |
+
"base": _repo("microsoft/Mage-Flow-Edit-Base", "Mage-Flow-Edit-Base"),
|
| 40 |
+
"rl": _repo("microsoft/Mage-Flow-Edit", "Mage-Flow-Edit"),
|
| 41 |
+
"turbo": _repo("microsoft/Mage-Flow-Edit-Turbo", "Mage-Flow-Edit-Turbo"),
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
DEVICE = "cuda"
|
| 45 |
+
_CACHE: dict[str, MageFlowPipeline] = {}
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def _get_pipe(repo: str) -> MageFlowPipeline:
|
| 49 |
+
"""Load (and cache) a pipeline from a local dir OR a Hugging Face repo id.
|
| 50 |
+
|
| 51 |
+
``MageFlowPipeline.from_pretrained`` resolves a repo id via
|
| 52 |
+
``snapshot_download`` automatically, so both are accepted here.
|
| 53 |
+
"""
|
| 54 |
+
repo = (repo or "").strip()
|
| 55 |
+
if not repo:
|
| 56 |
+
raise gr.Error("No model specified.")
|
| 57 |
+
if repo not in _CACHE:
|
| 58 |
+
try:
|
| 59 |
+
_CACHE[repo] = MageFlowPipeline.from_pretrained(repo, device=DEVICE)
|
| 60 |
+
except Exception as e: # noqa: BLE001
|
| 61 |
+
raise gr.Error(f"Failed to load model '{repo}': {type(e).__name__}: {e}")
|
| 62 |
+
return _CACHE[repo]
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def _resolve(preset_map, model_key, custom_model):
|
| 66 |
+
"""Custom repo id / path (if given) overrides the preset dropdown."""
|
| 67 |
+
return (custom_model or "").strip() or preset_map[model_key]
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def run_t2i(model_key, custom_model, prompt, neg_prompt, steps, cfg, height, width, seed,
|
| 71 |
+
progress=gr.Progress(track_tqdm=False)):
|
| 72 |
+
if not (prompt or "").strip():
|
| 73 |
+
raise gr.Error("Prompt is empty.")
|
| 74 |
+
repo = _resolve(T2I_MODELS, model_key, custom_model)
|
| 75 |
+
progress(0.1, desc=f"loading {repo} …")
|
| 76 |
+
pipe = _get_pipe(repo)
|
| 77 |
+
progress(0.4, desc="generating …")
|
| 78 |
+
img = pipe.generate(
|
| 79 |
+
[prompt], neg_prompts=[neg_prompt or " "], seeds=[int(seed)],
|
| 80 |
+
steps=int(steps), cfg=float(cfg),
|
| 81 |
+
heights=[int(height)], widths=[int(width)],
|
| 82 |
+
)[0]
|
| 83 |
+
return img
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def run_edit(model_key, custom_model, prompt, neg_prompt, ref_img, extra_files, steps, cfg, max_size, seed,
|
| 87 |
+
progress=gr.Progress(track_tqdm=False)):
|
| 88 |
+
if not (prompt or "").strip():
|
| 89 |
+
raise gr.Error("Edit instruction is empty.")
|
| 90 |
+
refs = []
|
| 91 |
+
if ref_img is not None:
|
| 92 |
+
refs.append(ref_img if isinstance(ref_img, Image.Image) else Image.open(ref_img))
|
| 93 |
+
for f in (extra_files or []):
|
| 94 |
+
refs.append(Image.open(f).convert("RGB"))
|
| 95 |
+
if not refs:
|
| 96 |
+
raise gr.Error("Upload at least one reference image.")
|
| 97 |
+
refs = [r.convert("RGB") for r in refs]
|
| 98 |
+
repo = _resolve(EDIT_MODELS, model_key, custom_model)
|
| 99 |
+
progress(0.1, desc=f"loading {repo} …")
|
| 100 |
+
pipe = _get_pipe(repo)
|
| 101 |
+
progress(0.4, desc="editing …")
|
| 102 |
+
out = pipe.edit(
|
| 103 |
+
[prompt], [refs], neg_prompts=[neg_prompt or " "], seeds=[int(seed)],
|
| 104 |
+
steps=int(steps), cfg=float(cfg),
|
| 105 |
+
max_size=int(max_size) if max_size else None,
|
| 106 |
+
)[0]
|
| 107 |
+
return out
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
_NOTE = (
|
| 111 |
+
"Pick a **preset** (base / rl / turbo) or type a **custom model** — any "
|
| 112 |
+
"Hugging Face repo id (e.g. `microsoft/Mage-Flow-Turbo`) or local path; it "
|
| 113 |
+
"is downloaded and cached on first use. **Turbo** models are few-step: set "
|
| 114 |
+
"**steps=4, cfg=1**."
|
| 115 |
+
)
|
| 116 |
+
|
| 117 |
+
_CUSTOM_PH_T2I = "microsoft/Mage-Flow (repo id or local path — overrides preset)"
|
| 118 |
+
_CUSTOM_PH_EDIT = "microsoft/Mage-Flow-Edit (repo id or local path — overrides preset)"
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def build_ui():
|
| 122 |
+
with gr.Blocks(title="MageFlow") as demo:
|
| 123 |
+
gr.Markdown("# MageFlow\nText-to-image generation and instruction-based image editing.")
|
| 124 |
+
gr.Markdown(_NOTE)
|
| 125 |
+
|
| 126 |
+
with gr.Tab("Text → Image"):
|
| 127 |
+
with gr.Row():
|
| 128 |
+
with gr.Column(scale=1):
|
| 129 |
+
t_model = gr.Dropdown(list(T2I_MODELS), value="base", label="Model preset")
|
| 130 |
+
t_custom = gr.Textbox(label="Custom model (optional)", placeholder=_CUSTOM_PH_T2I, lines=1)
|
| 131 |
+
t_prompt = gr.Textbox(label="Prompt", lines=3,
|
| 132 |
+
value="A close-up portrait of an elderly African man with deep wrinkles, wearing a traditional hat, soft natural lighting, ultra realistic.")
|
| 133 |
+
t_neg = gr.Textbox(label="Negative prompt", value=" ", lines=1)
|
| 134 |
+
with gr.Row():
|
| 135 |
+
t_steps = gr.Slider(1, 50, value=30, step=1, label="Steps")
|
| 136 |
+
t_cfg = gr.Slider(1.0, 10.0, value=5.0, step=0.5, label="CFG")
|
| 137 |
+
with gr.Row():
|
| 138 |
+
t_h = gr.Slider(256, 1536, value=1024, step=16, label="Height")
|
| 139 |
+
t_w = gr.Slider(256, 1536, value=1024, step=16, label="Width")
|
| 140 |
+
t_seed = gr.Number(value=42, precision=0, label="Seed")
|
| 141 |
+
t_btn = gr.Button("Generate", variant="primary")
|
| 142 |
+
with gr.Column(scale=1):
|
| 143 |
+
t_out = gr.Image(type="pil", label="Output", height=560)
|
| 144 |
+
# Clear the previous output first so the stale image isn't shown as
|
| 145 |
+
# the result while the new one is still transferring (esp. over a
|
| 146 |
+
# gradio share tunnel, where the image download can lag a few seconds).
|
| 147 |
+
t_btn.click(lambda: None, None, t_out).then(
|
| 148 |
+
run_t2i,
|
| 149 |
+
[t_model, t_custom, t_prompt, t_neg, t_steps, t_cfg, t_h, t_w, t_seed],
|
| 150 |
+
t_out)
|
| 151 |
+
|
| 152 |
+
with gr.Tab("Image Edit"):
|
| 153 |
+
with gr.Row():
|
| 154 |
+
with gr.Column(scale=1):
|
| 155 |
+
e_model = gr.Dropdown(list(EDIT_MODELS), value="base", label="Model preset")
|
| 156 |
+
e_custom = gr.Textbox(label="Custom model (optional)", placeholder=_CUSTOM_PH_EDIT, lines=1)
|
| 157 |
+
e_prompt = gr.Textbox(label="Edit instruction", lines=2,
|
| 158 |
+
value="change the background to a city street")
|
| 159 |
+
e_neg = gr.Textbox(label="Negative prompt", value=" ", lines=1)
|
| 160 |
+
e_ref = gr.Image(type="pil", label="Reference image", height=280,
|
| 161 |
+
value=os.path.join(os.path.dirname(__file__), "assets", "dog.jpg"))
|
| 162 |
+
e_extra = gr.File(file_count="multiple", type="filepath",
|
| 163 |
+
label="Extra references (optional, multi-image edit)")
|
| 164 |
+
with gr.Row():
|
| 165 |
+
e_steps = gr.Slider(1, 50, value=30, step=1, label="Steps")
|
| 166 |
+
e_cfg = gr.Slider(1.0, 10.0, value=5.0, step=0.5, label="CFG")
|
| 167 |
+
e_max = gr.Slider(0, 1536, value=1024, step=16,
|
| 168 |
+
label="Max output side (0 = keep source size)")
|
| 169 |
+
e_seed = gr.Number(value=42, precision=0, label="Seed")
|
| 170 |
+
e_btn = gr.Button("Edit", variant="primary")
|
| 171 |
+
with gr.Column(scale=1):
|
| 172 |
+
e_out = gr.Image(type="pil", label="Output", height=560)
|
| 173 |
+
e_btn.click(lambda: None, None, e_out).then(
|
| 174 |
+
run_edit,
|
| 175 |
+
[e_model, e_custom, e_prompt, e_neg, e_ref, e_extra, e_steps, e_cfg, e_max, e_seed],
|
| 176 |
+
e_out)
|
| 177 |
+
return demo
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def main():
|
| 181 |
+
global DEVICE
|
| 182 |
+
ap = argparse.ArgumentParser()
|
| 183 |
+
ap.add_argument("--device", default="cuda")
|
| 184 |
+
ap.add_argument("--host", default="0.0.0.0")
|
| 185 |
+
ap.add_argument("--port", type=int, default=7860)
|
| 186 |
+
ap.add_argument("--share", action="store_true")
|
| 187 |
+
ap.add_argument("--preload", default=None,
|
| 188 |
+
help="comma-separated repo ids / paths to load at startup (else lazy)")
|
| 189 |
+
args = ap.parse_args()
|
| 190 |
+
DEVICE = args.device
|
| 191 |
+
if args.preload:
|
| 192 |
+
for repo in args.preload.split(","):
|
| 193 |
+
_get_pipe(repo.strip())
|
| 194 |
+
build_ui().queue().launch(server_name=args.host, server_port=args.port,
|
| 195 |
+
share=args.share, theme=gr.themes.Soft())
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
if __name__ == "__main__":
|
| 199 |
+
main()
|
vendor/mage_flow/inference.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
"""MageFlow command-line inference.
|
| 3 |
+
|
| 4 |
+
Two console scripts share this module:
|
| 5 |
+
|
| 6 |
+
* ``mage-flow`` — text-to-image generation
|
| 7 |
+
* ``mage-flow-edit`` — instruction-based image editing
|
| 8 |
+
|
| 9 |
+
Both are BATCHED: pass several prompts and they are packed into a single
|
| 10 |
+
transformer forward per denoise step. Sample ``i`` uses seed ``--seed + i``.
|
| 11 |
+
|
| 12 |
+
Text-to-image (multiple prompts = a batch)::
|
| 13 |
+
|
| 14 |
+
mage-flow --prompt "a cat holding a sign that says hello" "a red ferrari" \
|
| 15 |
+
--model_path microsoft/Mage-Flow-4B --steps 30 --cfg 5.0 --out ./outputs
|
| 16 |
+
|
| 17 |
+
``--model_path`` accepts a local repo dir OR a Hugging Face Hub repo id (e.g.
|
| 18 |
+
``microsoft/Mage-Flow-4B``), downloaded and cached automatically on first use.
|
| 19 |
+
|
| 20 |
+
Mixed resolutions — give one ``--height``/``--width`` per prompt (they are packed
|
| 21 |
+
into a single forward per step regardless of shape)::
|
| 22 |
+
|
| 23 |
+
mage-flow --prompt "a tall waterfall" "a wide desert panorama" \
|
| 24 |
+
--height 2048 512 --width 512 2048 \
|
| 25 |
+
--model_path microsoft/Mage-Flow-4B --out ./outputs
|
| 26 |
+
|
| 27 |
+
Image editing (one ``--ref`` entry per prompt; comma-separate paths for
|
| 28 |
+
multi-image edit)::
|
| 29 |
+
|
| 30 |
+
mage-flow-edit \
|
| 31 |
+
--prompt "把背景改为城市街道" "把这两张图融合在一起" \
|
| 32 |
+
--ref hydrant.png "scene.png,object.png" \
|
| 33 |
+
--model_path /path/Mage-Flow-Edit-4B-Base --out ./outputs
|
| 34 |
+
"""
|
| 35 |
+
import argparse
|
| 36 |
+
import os
|
| 37 |
+
|
| 38 |
+
from mage_flow import MageFlowPipeline
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def _add_common_args(p):
|
| 42 |
+
p.add_argument("--model_path", required=True,
|
| 43 |
+
help="local diffusers-style repo dir OR a Hugging Face Hub repo id "
|
| 44 |
+
"(e.g. microsoft/Mage-Flow-4B); HF ids are downloaded and cached "
|
| 45 |
+
"automatically on first use")
|
| 46 |
+
p.add_argument("--neg_prompt", default=None,
|
| 47 |
+
help="negative prompt applied to every sample (default: a single space)")
|
| 48 |
+
p.add_argument("--steps", type=int, default=30)
|
| 49 |
+
p.add_argument("--cfg", type=float, default=5.0)
|
| 50 |
+
p.add_argument("--seed", type=int, default=42, help="base seed; sample i uses seed + i")
|
| 51 |
+
p.add_argument("--static_shift", type=float, default=None,
|
| 52 |
+
help="override scheduler shift (default: repo scheduler_config.json, 6.0)")
|
| 53 |
+
p.add_argument("--device", default="cuda")
|
| 54 |
+
p.add_argument("--out", default="./outputs")
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def _neg_list(neg_prompt, n):
|
| 58 |
+
return [neg_prompt] * n if neg_prompt is not None else None
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def _size_list(vals, n, name, parser):
|
| 62 |
+
"""Broadcast one size value to all prompts, or use a per-prompt list."""
|
| 63 |
+
if len(vals) == 1:
|
| 64 |
+
return vals * n
|
| 65 |
+
if len(vals) == n:
|
| 66 |
+
return vals
|
| 67 |
+
parser.error(f"--{name} expects 1 value (applied to all) or {n} values "
|
| 68 |
+
f"(one per prompt); got {len(vals)}")
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def main():
|
| 72 |
+
"""``mage-flow`` — batched text-to-image generation."""
|
| 73 |
+
p = argparse.ArgumentParser(
|
| 74 |
+
prog="mage-flow", description="MageFlow text-to-image generation.",
|
| 75 |
+
formatter_class=argparse.RawDescriptionHelpFormatter)
|
| 76 |
+
p.add_argument("--prompt", nargs="+", required=True,
|
| 77 |
+
help="one or more prompts; multiple prompts are batched")
|
| 78 |
+
p.add_argument("--height", type=int, nargs="+", default=[1024],
|
| 79 |
+
help="output height (multiple of 16); one value applied to all "
|
| 80 |
+
"prompts, or one value per prompt for mixed resolutions")
|
| 81 |
+
p.add_argument("--width", type=int, nargs="+", default=[1024],
|
| 82 |
+
help="output width (multiple of 16); one value applied to all "
|
| 83 |
+
"prompts, or one value per prompt for mixed resolutions")
|
| 84 |
+
p.add_argument("--prompt_template", default="mage-flow")
|
| 85 |
+
_add_common_args(p)
|
| 86 |
+
args = p.parse_args()
|
| 87 |
+
|
| 88 |
+
os.makedirs(args.out, exist_ok=True)
|
| 89 |
+
pipe = MageFlowPipeline.from_pretrained(args.model_path, args.device)
|
| 90 |
+
n = len(args.prompt)
|
| 91 |
+
imgs = pipe.generate(
|
| 92 |
+
args.prompt,
|
| 93 |
+
neg_prompts=_neg_list(args.neg_prompt, n),
|
| 94 |
+
seeds=[args.seed + i for i in range(n)],
|
| 95 |
+
heights=_size_list(args.height, n, "height", p),
|
| 96 |
+
widths=_size_list(args.width, n, "width", p),
|
| 97 |
+
steps=args.steps, cfg=args.cfg, static_shift=args.static_shift,
|
| 98 |
+
prompt_template=args.prompt_template,
|
| 99 |
+
)
|
| 100 |
+
for i, im in enumerate(imgs):
|
| 101 |
+
path = os.path.join(args.out, f"gen_{i:03d}.png")
|
| 102 |
+
im.save(path)
|
| 103 |
+
print(f"saved {path}")
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def main_edit():
|
| 107 |
+
"""``mage-flow-edit`` — batched instruction-based image editing."""
|
| 108 |
+
p = argparse.ArgumentParser(
|
| 109 |
+
prog="mage-flow-edit", description="MageFlow instruction-based image editing.",
|
| 110 |
+
formatter_class=argparse.RawDescriptionHelpFormatter)
|
| 111 |
+
p.add_argument("--prompt", nargs="+", required=True,
|
| 112 |
+
help="one or more edit instructions; multiple prompts are batched")
|
| 113 |
+
p.add_argument("--ref", nargs="+", required=True,
|
| 114 |
+
help="reference image(s) for each prompt, positionally aligned with "
|
| 115 |
+
"--prompt; comma-separate paths for multi-image edit "
|
| 116 |
+
"(e.g. --ref a.png 'b.png,c.png')")
|
| 117 |
+
p.add_argument("--max_size", type=int, default=None,
|
| 118 |
+
help="longest side of the output (short side by aspect ratio). "
|
| 119 |
+
"Default: keep each source image's own resolution")
|
| 120 |
+
p.add_argument("--height", type=int, default=None,
|
| 121 |
+
help="explicit output height (use with --width; overrides --max_size)")
|
| 122 |
+
p.add_argument("--width", type=int, default=None,
|
| 123 |
+
help="explicit output width (use with --height; overrides --max_size)")
|
| 124 |
+
p.add_argument("--vl_cond_long_edge", type=int, default=384,
|
| 125 |
+
help="cap the long edge of the reference image fed to the VL text "
|
| 126 |
+
"encoder (matches training preprocessing; the VAE path keeps the "
|
| 127 |
+
"full output resolution). 0 or negative disables the cap")
|
| 128 |
+
p.add_argument("--prompt_template", default="mage-flow-edit")
|
| 129 |
+
_add_common_args(p)
|
| 130 |
+
args = p.parse_args()
|
| 131 |
+
|
| 132 |
+
if len(args.ref) != len(args.prompt):
|
| 133 |
+
p.error(f"--ref count ({len(args.ref)}) must match --prompt count ({len(args.prompt)})")
|
| 134 |
+
|
| 135 |
+
os.makedirs(args.out, exist_ok=True)
|
| 136 |
+
pipe = MageFlowPipeline.from_pretrained(args.model_path, args.device)
|
| 137 |
+
n = len(args.prompt)
|
| 138 |
+
# Each --ref token is one prompt's reference(s); commas split multi-image refs.
|
| 139 |
+
ref_images = [[s.strip() for s in r.split(",") if s.strip()] for r in args.ref]
|
| 140 |
+
|
| 141 |
+
size_kw = {}
|
| 142 |
+
if args.height and args.width:
|
| 143 |
+
size_kw = {"heights": [args.height] * n, "widths": [args.width] * n}
|
| 144 |
+
|
| 145 |
+
outs = pipe.edit(
|
| 146 |
+
args.prompt, ref_images,
|
| 147 |
+
neg_prompts=_neg_list(args.neg_prompt, n),
|
| 148 |
+
seeds=[args.seed + i for i in range(n)],
|
| 149 |
+
max_size=args.max_size, steps=args.steps, cfg=args.cfg,
|
| 150 |
+
static_shift=args.static_shift, prompt_template=args.prompt_template,
|
| 151 |
+
vl_cond_long_edge=args.vl_cond_long_edge,
|
| 152 |
+
**size_kw,
|
| 153 |
+
)
|
| 154 |
+
for i, im in enumerate(outs):
|
| 155 |
+
path = os.path.join(args.out, f"edit_{i:03d}.png")
|
| 156 |
+
im.save(path)
|
| 157 |
+
print(f"saved {path}")
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
if __name__ == "__main__":
|
| 161 |
+
main()
|
vendor/mage_flow/models/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .utils import load_model
|
| 2 |
+
|
| 3 |
+
__all__ = ["load_model"]
|
vendor/mage_flow/models/mage_flow.py
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass
|
| 2 |
+
from typing import Any
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
import torch.nn as nn
|
| 6 |
+
from einops import rearrange, repeat
|
| 7 |
+
from loguru import logger
|
| 8 |
+
from pydantic import BaseModel, Field
|
| 9 |
+
from torch import Tensor
|
| 10 |
+
|
| 11 |
+
from .modules._attn_backend import set_attn_backend
|
| 12 |
+
from .modules.mage_layers import (
|
| 13 |
+
AdaLayerNormContinuous,
|
| 14 |
+
MageFlowEmbedRope,
|
| 15 |
+
MageFlowTimestepProjEmbeddings,
|
| 16 |
+
MageFlowTransformerBlock,
|
| 17 |
+
RMSNorm,
|
| 18 |
+
)
|
| 19 |
+
from .modules.text_encoder import TextEncoder, qwen3_patch_forward
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class ModelConfig(BaseModel):
|
| 23 |
+
static_shift: float = Field(
|
| 24 |
+
default=6.0,
|
| 25 |
+
description="Static shift value for the z-image time-shift schedule (the only "
|
| 26 |
+
"supported schedule). Default: 6.0.",
|
| 27 |
+
)
|
| 28 |
+
vae_path: str = Field(...)
|
| 29 |
+
model_structure: dict = Field(default_factory=dict)
|
| 30 |
+
txt_enc_path: str = Field(...)
|
| 31 |
+
txt_max_length: int = Field(default=4096)
|
| 32 |
+
pretrained_model_name_or_path: str | None = Field(default=None)
|
| 33 |
+
pretrained_full_model_path: str | None = Field(default=None) # Load full model weights (DiT + txt_enc + vae)
|
| 34 |
+
packing: bool = Field(default=False)
|
| 35 |
+
vae_sample_posterior: bool = Field(default=True) # Sample (vs mode) from VAE posterior at encode time (Flux2 + CoD)
|
| 36 |
+
vae_encoder_only: bool = Field(default=False) # Skip loading VAE decoder to save GPU memory (training only, MageVAE)
|
| 37 |
+
compile_vae_encoder: bool = Field(default=False) # torch.compile VAE encoder to reduce CUDA kernel launch overhead
|
| 38 |
+
attn_type: str = Field(
|
| 39 |
+
default="flash2",
|
| 40 |
+
description="Flash-attn backend used by both the DiT (mage_layers) "
|
| 41 |
+
"and the HF text encoder (text_encoder). One of: 'flash2' (default) or 'flash4'.",
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
@dataclass
|
| 46 |
+
class MageFlowParams:
|
| 47 |
+
in_channels: int
|
| 48 |
+
out_channels: int
|
| 49 |
+
context_in_dim: int
|
| 50 |
+
hidden_size: int
|
| 51 |
+
num_heads: int
|
| 52 |
+
depth: int
|
| 53 |
+
axes_dim: list[int]
|
| 54 |
+
checkpoint: bool
|
| 55 |
+
patch_size: int = 1
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
class MageFlow(nn.Module):
|
| 59 |
+
def __init__(self, params: MageFlowParams):
|
| 60 |
+
super().__init__()
|
| 61 |
+
self.params = params
|
| 62 |
+
self.checkpoint = params.checkpoint
|
| 63 |
+
self.in_channels = params.in_channels
|
| 64 |
+
self.out_channels = params.out_channels
|
| 65 |
+
self.inner_dim = params.hidden_size # num_attention_heads * attention_head_dim
|
| 66 |
+
self.axes_dim = params.axes_dim
|
| 67 |
+
self.num_attention_heads = params.num_heads
|
| 68 |
+
self.attention_head_dim = self.inner_dim // self.num_attention_heads
|
| 69 |
+
self.patch_size = params.patch_size
|
| 70 |
+
assert sum(self.axes_dim) == self.attention_head_dim
|
| 71 |
+
|
| 72 |
+
self.pos_embed = MageFlowEmbedRope(theta=10000, axes_dim=self.axes_dim, scale_rope=True)
|
| 73 |
+
self.img_in = nn.Linear(self.in_channels, self.inner_dim)
|
| 74 |
+
self.txt_norm = RMSNorm(params.context_in_dim, eps=1e-6)
|
| 75 |
+
self.txt_in = nn.Linear(params.context_in_dim, self.inner_dim)
|
| 76 |
+
|
| 77 |
+
self.time_text_embed = MageFlowTimestepProjEmbeddings(embedding_dim=self.inner_dim)
|
| 78 |
+
|
| 79 |
+
self.transformer_blocks = nn.ModuleList(
|
| 80 |
+
[
|
| 81 |
+
MageFlowTransformerBlock(
|
| 82 |
+
dim=self.inner_dim,
|
| 83 |
+
num_attention_heads=self.num_attention_heads,
|
| 84 |
+
attention_head_dim=self.attention_head_dim,
|
| 85 |
+
)
|
| 86 |
+
for _ in range(params.depth)
|
| 87 |
+
]
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
self.norm_out = AdaLayerNormContinuous(self.inner_dim, self.inner_dim, elementwise_affine=False, eps=1e-6)
|
| 91 |
+
self.proj_out = nn.Linear(self.inner_dim, self.patch_size * self.patch_size * self.out_channels, bias=True)
|
| 92 |
+
|
| 93 |
+
def forward(
|
| 94 |
+
self,
|
| 95 |
+
img: Tensor,
|
| 96 |
+
txt: Tensor,
|
| 97 |
+
timesteps: Tensor,
|
| 98 |
+
img_shapes=None,
|
| 99 |
+
img_cu_seqlens: Tensor | None = None,
|
| 100 |
+
txt_cu_seqlens: Tensor | None = None,
|
| 101 |
+
attention_kwargs: dict[str, Any] | None = None,
|
| 102 |
+
) -> Tensor:
|
| 103 |
+
if img.ndim != 3 or txt.ndim != 3:
|
| 104 |
+
raise ValueError("Input img and txt tensors must have 3 dimensions.")
|
| 105 |
+
|
| 106 |
+
# Prepare vision RoPE (msrope); text tokens are not rotated.
|
| 107 |
+
ms_pe = self.pos_embed(img_shapes, device=img.device)
|
| 108 |
+
|
| 109 |
+
img = self.img_in(img)
|
| 110 |
+
txt = self.txt_norm(txt)
|
| 111 |
+
|
| 112 |
+
timesteps = timesteps.to(img.dtype)
|
| 113 |
+
temb = self.time_text_embed(timesteps, img)
|
| 114 |
+
|
| 115 |
+
txt = self.txt_in(txt)
|
| 116 |
+
txt_vec = torch.zeros(txt.shape[0], self.inner_dim, dtype=txt.dtype, device=txt.device)
|
| 117 |
+
|
| 118 |
+
temb = temb + txt_vec
|
| 119 |
+
|
| 120 |
+
attention_kwargs = attention_kwargs or {}
|
| 121 |
+
|
| 122 |
+
for _index_block, block in enumerate(self.transformer_blocks):
|
| 123 |
+
if self.training and self.checkpoint:
|
| 124 |
+
txt, img = torch.utils.checkpoint.checkpoint(
|
| 125 |
+
block,
|
| 126 |
+
img, # hidden_states
|
| 127 |
+
txt, # encoder_hidden_states
|
| 128 |
+
temb, # temb
|
| 129 |
+
ms_pe, # image_rotary_emb
|
| 130 |
+
txt_cu_seqlens, # txt_cu_lens
|
| 131 |
+
img_cu_seqlens, # img_cu_lens
|
| 132 |
+
use_reentrant=False,
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
else:
|
| 136 |
+
txt, img = block(
|
| 137 |
+
hidden_states=img,
|
| 138 |
+
encoder_hidden_states=txt,
|
| 139 |
+
txt_cu_lens=txt_cu_seqlens,
|
| 140 |
+
img_cu_lens=img_cu_seqlens,
|
| 141 |
+
temb=temb,
|
| 142 |
+
image_rotary_emb=ms_pe,
|
| 143 |
+
joint_attention_kwargs=attention_kwargs,
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
# Use only the image part (hidden_states) from the dual-stream blocks
|
| 147 |
+
img = self.norm_out(
|
| 148 |
+
img,
|
| 149 |
+
temb,
|
| 150 |
+
cu_seqlens=img_cu_seqlens,
|
| 151 |
+
)
|
| 152 |
+
img = self.proj_out(img)
|
| 153 |
+
return img
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
class MageFlowModel(nn.Module):
|
| 157 |
+
def __init__(self, config: ModelConfig):
|
| 158 |
+
super().__init__()
|
| 159 |
+
self.config = config
|
| 160 |
+
set_attn_backend(getattr(config, "attn_type", "flash2"))
|
| 161 |
+
self.patch_text_encoder_forward()
|
| 162 |
+
self.vae = self.load_vae()
|
| 163 |
+
self.transformer = self.load_transformer()
|
| 164 |
+
self.txt_enc = self.load_text_enc()
|
| 165 |
+
|
| 166 |
+
# Optionally override all components from a full model checkpoint (e.g. ema.pt)
|
| 167 |
+
full_path = getattr(self.config, "pretrained_full_model_path", None)
|
| 168 |
+
if full_path is not None:
|
| 169 |
+
import os
|
| 170 |
+
|
| 171 |
+
if os.path.exists(full_path):
|
| 172 |
+
logger.info(f"Loading full model weights from {full_path}")
|
| 173 |
+
sd = torch.load(full_path, map_location="cpu")
|
| 174 |
+
# Handle wrapped EMA format: {'ema_state_dict': ..., ...}
|
| 175 |
+
if isinstance(sd, dict) and "ema_state_dict" in sd:
|
| 176 |
+
sd = sd["ema_state_dict"]
|
| 177 |
+
missing, unexpected = self.load_state_dict(sd, strict=False)
|
| 178 |
+
if missing:
|
| 179 |
+
logger.warning(f"Full model load missing keys ({len(missing)}): {missing[:5]}...")
|
| 180 |
+
if unexpected:
|
| 181 |
+
logger.warning(f"Full model load unexpected keys ({len(unexpected)}): {unexpected[:5]}...")
|
| 182 |
+
logger.info("Full model weights loaded successfully.")
|
| 183 |
+
else:
|
| 184 |
+
logger.warning(f"pretrained_full_model_path not found: {full_path}")
|
| 185 |
+
|
| 186 |
+
# Freeze VAE and Text Encoder
|
| 187 |
+
self.vae.requires_grad_(False)
|
| 188 |
+
|
| 189 |
+
# Drop VAE decoder to save GPU memory (training only, decoder unused during training)
|
| 190 |
+
if self.config.vae_encoder_only:
|
| 191 |
+
from .modules.mage_vae import MageVAE
|
| 192 |
+
if isinstance(self.vae, MageVAE):
|
| 193 |
+
decoder_params = sum(p.numel() for p in self.vae.decoder_model.parameters()) / 1e6
|
| 194 |
+
self.vae.decoder_model = None
|
| 195 |
+
elif hasattr(self.vae, "decoder"):
|
| 196 |
+
decoder_params = sum(p.numel() for p in self.vae.decoder.parameters()) / 1e6
|
| 197 |
+
self.vae.decoder = None
|
| 198 |
+
else:
|
| 199 |
+
decoder_params = 0
|
| 200 |
+
logger.info(f"vae_encoder_only=True: dropped VAE decoder ({decoder_params:.1f}M params) to save memory")
|
| 201 |
+
|
| 202 |
+
# NOTE: VAE encoder torch.compile() is deferred to
|
| 203 |
+
# maybe_compile_vae_encoder(), called after checkpoint load. Reason:
|
| 204 |
+
# avoid wasted compile work before load_checkpoint overwrites weights.
|
| 205 |
+
# The save-side _unwrap_compiled_submodules guard in DeepSpeedTrainer
|
| 206 |
+
# is a belt-and-suspenders defense against any future code that
|
| 207 |
+
# re-introduces the function-style ``module = torch.compile(module)``
|
| 208 |
+
# pattern (which does pollute state_dict with ``_orig_mod.``).
|
| 209 |
+
|
| 210 |
+
# Text encoder is always frozen (inference only).
|
| 211 |
+
self.txt_enc.requires_grad_(False)
|
| 212 |
+
logger.info(
|
| 213 |
+
f"{sum([p.numel() for p in self.transformer.parameters() if p.requires_grad]) / 1000000} M parameters"
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
def patch_text_encoder_forward(self):
|
| 217 |
+
qwen3_patch_forward()
|
| 218 |
+
logger.info("Patched Qwen3-VL text encoder forward methods")
|
| 219 |
+
|
| 220 |
+
def maybe_compile_vae_encoder(self) -> None:
|
| 221 |
+
"""Compile the VAE encoder with torch.compile() to fuse small ops and
|
| 222 |
+
reduce CUDA kernel launch overhead.
|
| 223 |
+
|
| 224 |
+
Uses ``nn.Module.compile()`` (in-place) for the encoder so the module
|
| 225 |
+
hierarchy and parameter names are unchanged — ``state_dict()`` keeps
|
| 226 |
+
clean keys (no ``_orig_mod.`` prefix), and checkpoints stay
|
| 227 |
+
interchangeable with the non-compiled path.
|
| 228 |
+
|
| 229 |
+
For the MageVAE branch we still assign ``torch.compile(...)`` to a
|
| 230 |
+
method (``_encode_moments``); methods aren't ``nn.Module``s so this
|
| 231 |
+
does not pollute ``state_dict()``.
|
| 232 |
+
|
| 233 |
+
Idempotent: safe to call multiple times; already-compiled modules are
|
| 234 |
+
detected and skipped.
|
| 235 |
+
"""
|
| 236 |
+
if not getattr(self.config, "compile_vae_encoder", False):
|
| 237 |
+
return
|
| 238 |
+
torch.set_float32_matmul_precision("high")
|
| 239 |
+
from .modules.mage_vae import MageVAE
|
| 240 |
+
if isinstance(self.vae, MageVAE):
|
| 241 |
+
fn = self.vae._encode_moments
|
| 242 |
+
if hasattr(fn, "_torchdynamo_orig_callable") or hasattr(fn, "_orig_mod"):
|
| 243 |
+
return # already compiled
|
| 244 |
+
self.vae._encode_moments = torch.compile(fn, dynamic=True)
|
| 245 |
+
logger.info("compile_vae_encoder=True: compiled MageVAE._encode_moments")
|
| 246 |
+
elif hasattr(self.vae, "encoder"):
|
| 247 |
+
if getattr(self.vae.encoder, "_compiled_call_impl", None) is not None:
|
| 248 |
+
return # already compiled
|
| 249 |
+
self.vae.encoder.compile()
|
| 250 |
+
logger.info("compile_vae_encoder=True: compiled VAE encoder (in-place)")
|
| 251 |
+
|
| 252 |
+
def load_text_enc(self):
|
| 253 |
+
return TextEncoder(
|
| 254 |
+
model_name=self.config.txt_enc_path,
|
| 255 |
+
version=self.config.txt_enc_path,
|
| 256 |
+
tokenizer_max_length=self.config.txt_max_length,
|
| 257 |
+
torch_dtype=torch.bfloat16,
|
| 258 |
+
prompt_template=None,
|
| 259 |
+
dit_structure=self.config.model_structure,
|
| 260 |
+
use_packed_text_infer=self.config.packing,
|
| 261 |
+
attn_type=getattr(self.config, "attn_type", "flash2"),
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
def load_vae(self):
|
| 265 |
+
from .modules.mage_vae import MageVAE
|
| 266 |
+
return MageVAE(
|
| 267 |
+
ckpt_path=self.config.vae_path,
|
| 268 |
+
sample_posterior=self.config.vae_sample_posterior,
|
| 269 |
+
)
|
| 270 |
+
|
| 271 |
+
def load_transformer(self):
|
| 272 |
+
# Imported lazily to avoid a circular import: ``utils`` imports MageFlow /
|
| 273 |
+
# MageFlowParams from this module.
|
| 274 |
+
from .utils import load_model
|
| 275 |
+
return load_model(
|
| 276 |
+
dit_structure=self.config.model_structure,
|
| 277 |
+
pretrain_path=self.config.pretrained_model_name_or_path,
|
| 278 |
+
)
|
| 279 |
+
|
| 280 |
+
def compile(self):
|
| 281 |
+
self.transformer.compile()
|
| 282 |
+
|
| 283 |
+
def compute_vae_encodings(
|
| 284 |
+
self,
|
| 285 |
+
pixel_values: torch.Tensor | list[torch.Tensor],
|
| 286 |
+
with_ids: bool = True,
|
| 287 |
+
):
|
| 288 |
+
if isinstance(pixel_values, list):
|
| 289 |
+
# All same resolution → batch encode via the tensor path
|
| 290 |
+
if len(pixel_values) > 1 and len({img.shape for img in pixel_values}) == 1:
|
| 291 |
+
stacked = torch.stack(pixel_values, dim=0)
|
| 292 |
+
result = self.compute_vae_encodings(stacked, with_ids=with_ids)
|
| 293 |
+
# Repack from [N, L, C] batch format to [1, N*L, C] packed format
|
| 294 |
+
if with_ids:
|
| 295 |
+
model_input, img_shapes, img_ids = result
|
| 296 |
+
model_input = model_input.reshape(1, -1, model_input.shape[-1])
|
| 297 |
+
img_ids = img_ids.reshape(1, -1, img_ids.shape[-1])
|
| 298 |
+
return model_input, img_shapes, img_ids
|
| 299 |
+
model_input, img_shapes = result
|
| 300 |
+
model_input = model_input.reshape(1, -1, model_input.shape[-1])
|
| 301 |
+
return model_input, img_shapes
|
| 302 |
+
|
| 303 |
+
# Packed / variable-size images
|
| 304 |
+
model_inputs = []
|
| 305 |
+
img_shapes = []
|
| 306 |
+
img_ids_list = []
|
| 307 |
+
|
| 308 |
+
def _append(latents):
|
| 309 |
+
_, _, h, w = latents.shape
|
| 310 |
+
img_shapes.append([(1, h, w)])
|
| 311 |
+
model_inputs.append(rearrange(latents, "b c h w -> b (h w) c").squeeze(0))
|
| 312 |
+
if with_ids:
|
| 313 |
+
ids = torch.zeros(h, w, 3, device=latents.device)
|
| 314 |
+
ids[..., 1] = ids[..., 1] + torch.arange(h, device=latents.device)[:, None]
|
| 315 |
+
ids[..., 2] = ids[..., 2] + torch.arange(w, device=latents.device)[None, :]
|
| 316 |
+
img_ids_list.append(rearrange(ids, "h w c -> (h w) c"))
|
| 317 |
+
|
| 318 |
+
# MageVAE encoder is launch-bound on B=1; group same-shape images
|
| 319 |
+
# in the pack into one batched encode call.
|
| 320 |
+
if len(pixel_values) > 1:
|
| 321 |
+
groups: dict[tuple[int, int], list[int]] = {}
|
| 322 |
+
for i, img in enumerate(pixel_values):
|
| 323 |
+
key = (int(img.shape[-2]), int(img.shape[-1]))
|
| 324 |
+
groups.setdefault(key, []).append(i)
|
| 325 |
+
latents_per_idx = [None] * len(pixel_values)
|
| 326 |
+
for (h, w), idxs in groups.items():
|
| 327 |
+
batch = torch.stack([pixel_values[i] for i in idxs], dim=0)
|
| 328 |
+
batch = batch.to(memory_format=torch.contiguous_format).float()
|
| 329 |
+
batch = batch.to(self.vae.device, dtype=self.vae.dtype)
|
| 330 |
+
with torch.no_grad():
|
| 331 |
+
lat = self.vae.encode(batch) # [B, 128, H/16, W/16]
|
| 332 |
+
for j, i in enumerate(idxs):
|
| 333 |
+
latents_per_idx[i] = lat[j:j + 1]
|
| 334 |
+
for latents in latents_per_idx:
|
| 335 |
+
_append(latents)
|
| 336 |
+
else:
|
| 337 |
+
for img in pixel_values:
|
| 338 |
+
img = img.unsqueeze(0).to(memory_format=torch.contiguous_format).float()
|
| 339 |
+
img = img.to(self.vae.device, dtype=self.vae.dtype)
|
| 340 |
+
with torch.no_grad():
|
| 341 |
+
latents = self.vae.encode(img) # [1, 128, H/16, W/16]
|
| 342 |
+
_append(latents)
|
| 343 |
+
|
| 344 |
+
model_input = torch.cat(model_inputs, dim=0).unsqueeze(0)
|
| 345 |
+
if with_ids:
|
| 346 |
+
img_ids = torch.cat(img_ids_list, dim=0).unsqueeze(0)
|
| 347 |
+
return model_input, img_shapes, img_ids
|
| 348 |
+
return model_input, img_shapes
|
| 349 |
+
|
| 350 |
+
# Tensor (padded batch)
|
| 351 |
+
pixel_values = pixel_values.to(memory_format=torch.contiguous_format).float()
|
| 352 |
+
pixel_values = pixel_values.to(self.vae.device, dtype=self.vae.dtype)
|
| 353 |
+
with torch.no_grad():
|
| 354 |
+
model_input = self.vae.encode(pixel_values) # [B, 128, H/16, W/16]
|
| 355 |
+
bs, c, h, w = model_input.shape
|
| 356 |
+
img_shapes = [[(1, h, w)]] * bs
|
| 357 |
+
model_input = rearrange(model_input, "b c h w -> b (h w) c")
|
| 358 |
+
if with_ids:
|
| 359 |
+
img_ids = torch.zeros(h, w, 3, device=model_input.device)
|
| 360 |
+
img_ids[..., 1] = img_ids[..., 1] + torch.arange(h, device=model_input.device)[:, None]
|
| 361 |
+
img_ids[..., 2] = img_ids[..., 2] + torch.arange(w, device=model_input.device)[None, :]
|
| 362 |
+
img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs)
|
| 363 |
+
return model_input, img_shapes, img_ids
|
| 364 |
+
return model_input, img_shapes
|
vendor/mage_flow/models/modules/__init__.py
ADDED
|
File without changes
|
vendor/mage_flow/models/modules/_attn_backend.py
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Attention backend shim — switchable between Flash Attention 2 and 4.
|
| 2 |
+
|
| 3 |
+
Exports a single ``flash_attn_varlen_func`` with the FA2 calling convention.
|
| 4 |
+
The underlying kernel is selected at runtime via ``set_attn_backend(name)``
|
| 5 |
+
(default: ``"flash2"``). The selected kernel is resolved lazily on the first
|
| 6 |
+
call so model-config-driven selection (which happens after this module is
|
| 7 |
+
imported) takes effect.
|
| 8 |
+
|
| 9 |
+
Modules that previously did ``from flash_attn import flash_attn_varlen_func``
|
| 10 |
+
should import from here instead.
|
| 11 |
+
|
| 12 |
+
For the FA4 path, calling-convention differences are normalised:
|
| 13 |
+
|
| 14 |
+
* ``window_size=(-1, -1)`` (FA2 "no window") -> ``(None, None)`` (FA4).
|
| 15 |
+
* ``block_table`` -> ``page_table``.
|
| 16 |
+
* FA4's optional ``(out, lse)`` tuple return is unwrapped to ``out``.
|
| 17 |
+
* ``dropout_p>0`` / ``alibi_slopes`` / ``return_attn_probs`` raise on FA4.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
from __future__ import annotations
|
| 21 |
+
|
| 22 |
+
from typing import Any, Callable
|
| 23 |
+
|
| 24 |
+
_FA2_ALIASES = {"flash2", "fa2", "flash_attention_2", "flash_attn_2"}
|
| 25 |
+
_FA4_ALIASES = {"flash4", "fa4", "flash_attention_4", "flash_attn_4"}
|
| 26 |
+
_SDPA_ALIASES = {"sdpa", "torch_sdpa", "scaled_dot_product_attention"}
|
| 27 |
+
|
| 28 |
+
_BACKEND: str = "flash2"
|
| 29 |
+
_RESOLVED_FN: Callable[..., Any] | None = None
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _normalize(name: str) -> str:
|
| 33 |
+
n = name.lower().strip()
|
| 34 |
+
if n in _FA2_ALIASES:
|
| 35 |
+
return "flash2"
|
| 36 |
+
if n in _FA4_ALIASES:
|
| 37 |
+
return "flash4"
|
| 38 |
+
if n in _SDPA_ALIASES:
|
| 39 |
+
return "sdpa"
|
| 40 |
+
raise ValueError(
|
| 41 |
+
f"Unknown attention backend {name!r}; expected one of "
|
| 42 |
+
f"{sorted(_FA2_ALIASES | _FA4_ALIASES | _SDPA_ALIASES)}"
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def set_attn_backend(name: str) -> None:
|
| 47 |
+
"""Select the flash-attn backend used by ``flash_attn_varlen_func``.
|
| 48 |
+
|
| 49 |
+
Safe to call multiple times; clears the cached resolution on change.
|
| 50 |
+
"""
|
| 51 |
+
global _BACKEND, _RESOLVED_FN
|
| 52 |
+
new = _normalize(name)
|
| 53 |
+
if new != _BACKEND:
|
| 54 |
+
_RESOLVED_FN = None
|
| 55 |
+
_BACKEND = new
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def _resolve_fa2() -> Callable[..., Any]:
|
| 60 |
+
from flash_attn import flash_attn_varlen_func as _fn
|
| 61 |
+
return _fn
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def _resolve_fa4() -> Callable[..., Any]:
|
| 65 |
+
from flash_attn.cute import flash_attn_varlen_func as _fa4_fn
|
| 66 |
+
|
| 67 |
+
def _fa4_wrapper(
|
| 68 |
+
q,
|
| 69 |
+
k,
|
| 70 |
+
v,
|
| 71 |
+
cu_seqlens_q=None,
|
| 72 |
+
cu_seqlens_k=None,
|
| 73 |
+
max_seqlen_q=None,
|
| 74 |
+
max_seqlen_k=None,
|
| 75 |
+
dropout_p: float = 0.0,
|
| 76 |
+
softmax_scale=None,
|
| 77 |
+
causal: bool = False,
|
| 78 |
+
window_size=(-1, -1),
|
| 79 |
+
softcap: float = 0.0,
|
| 80 |
+
alibi_slopes=None,
|
| 81 |
+
deterministic: bool = False,
|
| 82 |
+
return_attn_probs: bool = False,
|
| 83 |
+
block_table=None,
|
| 84 |
+
**_unused: Any,
|
| 85 |
+
):
|
| 86 |
+
if dropout_p and dropout_p > 0:
|
| 87 |
+
raise NotImplementedError("FA4 backend does not support dropout_p>0")
|
| 88 |
+
if alibi_slopes is not None:
|
| 89 |
+
raise NotImplementedError("FA4 backend does not support alibi_slopes")
|
| 90 |
+
if return_attn_probs:
|
| 91 |
+
raise NotImplementedError("FA4 backend does not support return_attn_probs")
|
| 92 |
+
|
| 93 |
+
win_l, win_r = window_size
|
| 94 |
+
if win_l == -1:
|
| 95 |
+
win_l = None
|
| 96 |
+
if win_r == -1:
|
| 97 |
+
win_r = None
|
| 98 |
+
|
| 99 |
+
out = _fa4_fn(
|
| 100 |
+
q,
|
| 101 |
+
k,
|
| 102 |
+
v,
|
| 103 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 104 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 105 |
+
max_seqlen_q=max_seqlen_q,
|
| 106 |
+
max_seqlen_k=max_seqlen_k,
|
| 107 |
+
softmax_scale=softmax_scale,
|
| 108 |
+
causal=causal,
|
| 109 |
+
window_size=(win_l, win_r),
|
| 110 |
+
softcap=softcap,
|
| 111 |
+
deterministic=deterministic,
|
| 112 |
+
page_table=block_table,
|
| 113 |
+
return_lse=False,
|
| 114 |
+
)
|
| 115 |
+
if isinstance(out, tuple):
|
| 116 |
+
out = out[0]
|
| 117 |
+
return out
|
| 118 |
+
|
| 119 |
+
return _fa4_wrapper
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def _resolve_sdpa() -> Callable[..., Any]:
|
| 123 |
+
"""FA2 varlen → per-sequence torch.SDPA fallback.
|
| 124 |
+
|
| 125 |
+
Use when flash-attn is unavailable (e.g. CUDA 13 has no prebuilt wheel
|
| 126 |
+
and source build is brittle). Slower than FA2 (one SDPA dispatch per
|
| 127 |
+
sequence), but functionally equivalent for the dense / causal / no-alibi
|
| 128 |
+
paths mageflow actually uses. Window / softcap / alibi / paged-attn /
|
| 129 |
+
return_attn_probs are not supported and will raise.
|
| 130 |
+
"""
|
| 131 |
+
import torch
|
| 132 |
+
import torch.nn.functional as F
|
| 133 |
+
|
| 134 |
+
def _sdpa_wrapper(
|
| 135 |
+
q,
|
| 136 |
+
k,
|
| 137 |
+
v,
|
| 138 |
+
cu_seqlens_q=None,
|
| 139 |
+
cu_seqlens_k=None,
|
| 140 |
+
max_seqlen_q=None,
|
| 141 |
+
max_seqlen_k=None,
|
| 142 |
+
dropout_p: float = 0.0,
|
| 143 |
+
softmax_scale=None,
|
| 144 |
+
causal: bool = False,
|
| 145 |
+
window_size=(-1, -1),
|
| 146 |
+
softcap: float = 0.0,
|
| 147 |
+
alibi_slopes=None,
|
| 148 |
+
deterministic: bool = False,
|
| 149 |
+
return_attn_probs: bool = False,
|
| 150 |
+
block_table=None,
|
| 151 |
+
**_unused: Any,
|
| 152 |
+
):
|
| 153 |
+
if dropout_p and dropout_p > 0:
|
| 154 |
+
raise NotImplementedError("SDPA backend does not support dropout_p>0")
|
| 155 |
+
if alibi_slopes is not None:
|
| 156 |
+
raise NotImplementedError("SDPA backend does not support alibi_slopes")
|
| 157 |
+
if return_attn_probs:
|
| 158 |
+
raise NotImplementedError("SDPA backend does not support return_attn_probs")
|
| 159 |
+
if softcap and softcap > 0:
|
| 160 |
+
raise NotImplementedError("SDPA backend does not support softcap")
|
| 161 |
+
if window_size not in ((-1, -1), (None, None), (0, 0)):
|
| 162 |
+
raise NotImplementedError(
|
| 163 |
+
f"SDPA backend does not support sliding window (got {window_size})"
|
| 164 |
+
)
|
| 165 |
+
if block_table is not None:
|
| 166 |
+
raise NotImplementedError("SDPA backend does not support paged attention")
|
| 167 |
+
if cu_seqlens_q is None or cu_seqlens_k is None:
|
| 168 |
+
raise ValueError("SDPA backend requires cu_seqlens_q and cu_seqlens_k")
|
| 169 |
+
|
| 170 |
+
# GQA: FA2 broadcasts k/v across query head groups natively; torch SDPA
|
| 171 |
+
# does not (the q vs k head-dim mismatch is the AssertionError "tensor
|
| 172 |
+
# a (32) must match tensor b (8) at non-singleton dimension 1" we'd see
|
| 173 |
+
# otherwise). Repeat k/v along the head dim to match q before the loop.
|
| 174 |
+
n_heads_q = q.shape[1]
|
| 175 |
+
n_heads_kv = k.shape[1]
|
| 176 |
+
if n_heads_q != n_heads_kv:
|
| 177 |
+
if n_heads_q % n_heads_kv != 0:
|
| 178 |
+
raise ValueError(
|
| 179 |
+
f"SDPA backend GQA expansion requires q heads ({n_heads_q}) "
|
| 180 |
+
f"to be divisible by k/v heads ({n_heads_kv})"
|
| 181 |
+
)
|
| 182 |
+
repeat = n_heads_q // n_heads_kv
|
| 183 |
+
k = k.repeat_interleave(repeat, dim=1)
|
| 184 |
+
v = v.repeat_interleave(repeat, dim=1)
|
| 185 |
+
|
| 186 |
+
# q/k/v: (total_tokens, nheads, head_dim). Dispatch SDPA per sequence,
|
| 187 |
+
# then concat. Python-level loop is fine since nseq is small (one per
|
| 188 |
+
# image in the pack) and image-gen latency is dominated by sampling.
|
| 189 |
+
cu_q = cu_seqlens_q.tolist()
|
| 190 |
+
cu_k = cu_seqlens_k.tolist()
|
| 191 |
+
outs = []
|
| 192 |
+
for qs, qe, ks, ke in zip(cu_q[:-1], cu_q[1:], cu_k[:-1], cu_k[1:]):
|
| 193 |
+
# (s, h, d) → (1, h, s, d)
|
| 194 |
+
q_i = q[qs:qe].transpose(0, 1).unsqueeze(0)
|
| 195 |
+
k_i = k[ks:ke].transpose(0, 1).unsqueeze(0)
|
| 196 |
+
v_i = v[ks:ke].transpose(0, 1).unsqueeze(0)
|
| 197 |
+
out_i = F.scaled_dot_product_attention(
|
| 198 |
+
q_i,
|
| 199 |
+
k_i,
|
| 200 |
+
v_i,
|
| 201 |
+
attn_mask=None,
|
| 202 |
+
dropout_p=0.0,
|
| 203 |
+
is_causal=causal,
|
| 204 |
+
scale=softmax_scale,
|
| 205 |
+
)
|
| 206 |
+
# (1, h, s, d) → (s, h, d)
|
| 207 |
+
outs.append(out_i.squeeze(0).transpose(0, 1))
|
| 208 |
+
return torch.cat(outs, dim=0).contiguous()
|
| 209 |
+
|
| 210 |
+
return _sdpa_wrapper
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
def _resolve() -> Callable[..., Any]:
|
| 214 |
+
global _RESOLVED_FN
|
| 215 |
+
if _RESOLVED_FN is None:
|
| 216 |
+
if _BACKEND == "flash4":
|
| 217 |
+
_RESOLVED_FN = _resolve_fa4()
|
| 218 |
+
elif _BACKEND == "sdpa":
|
| 219 |
+
_RESOLVED_FN = _resolve_sdpa()
|
| 220 |
+
else:
|
| 221 |
+
_RESOLVED_FN = _resolve_fa2()
|
| 222 |
+
return _RESOLVED_FN
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
def flash_attn_varlen_func(*args, **kwargs):
|
| 226 |
+
return _resolve()(*args, **kwargs)
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
__all__ = ["flash_attn_varlen_func", "set_attn_backend"]
|
vendor/mage_flow/models/modules/mage_latent.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import hashlib
|
| 4 |
+
import os
|
| 5 |
+
from math import erfc, sqrt
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
import torch
|
| 9 |
+
|
| 10 |
+
DEFAULT_GS_PAYLOAD = "MageFlow"
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
_ENV_KEY = "MAGEFLOW_GS_KEY"
|
| 14 |
+
_ENV_KEYFILE = "MAGEFLOW_GS_KEY_FILE"
|
| 15 |
+
_DEFAULT_KEYFILE = os.path.expanduser("~/.mageflow/gs_key")
|
| 16 |
+
DEFAULT_GS_KEY = 20260720
|
| 17 |
+
|
| 18 |
+
# Payload length in bits. Short + heavily replicated across the latent.
|
| 19 |
+
_MSG_BITS = 256
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def _key_to_int(value) -> int:
|
| 23 |
+
"""Normalize a key (int / digit-string / passphrase) to a non-negative int.
|
| 24 |
+
|
| 25 |
+
A pure integer or all-digits string is used directly; anything else is
|
| 26 |
+
treated as a passphrase and hashed (SHA-256) into a 256-bit integer.
|
| 27 |
+
"""
|
| 28 |
+
if isinstance(value, int):
|
| 29 |
+
return abs(value)
|
| 30 |
+
s = str(value).strip()
|
| 31 |
+
if not s:
|
| 32 |
+
raise ValueError("empty Gaussian-Shading key")
|
| 33 |
+
if s.lstrip("-").isdigit():
|
| 34 |
+
return abs(int(s))
|
| 35 |
+
return int.from_bytes(hashlib.sha256(s.encode()).digest(), "big")
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def resolve_gs_key(explicit=None):
|
| 39 |
+
if explicit is not None:
|
| 40 |
+
return _key_to_int(explicit)
|
| 41 |
+
env = os.environ.get(_ENV_KEY)
|
| 42 |
+
if env and env.strip():
|
| 43 |
+
return _key_to_int(env)
|
| 44 |
+
keyfile = os.environ.get(_ENV_KEYFILE) or _DEFAULT_KEYFILE
|
| 45 |
+
try:
|
| 46 |
+
with open(keyfile) as fh:
|
| 47 |
+
content = fh.read().strip()
|
| 48 |
+
if content:
|
| 49 |
+
return _key_to_int(content)
|
| 50 |
+
except OSError:
|
| 51 |
+
pass
|
| 52 |
+
return _key_to_int(DEFAULT_GS_KEY)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def _payload_to_bits(payload: str, n_bits: int = _MSG_BITS) -> np.ndarray:
|
| 56 |
+
"""Deterministically expand an arbitrary string into an ``n_bits`` bit vector."""
|
| 57 |
+
out: list[int] = []
|
| 58 |
+
counter = 0
|
| 59 |
+
while len(out) < n_bits:
|
| 60 |
+
digest = hashlib.sha256(f"{payload}:{counter}".encode()).digest()
|
| 61 |
+
for byte in digest:
|
| 62 |
+
for k in range(8):
|
| 63 |
+
out.append((byte >> k) & 1)
|
| 64 |
+
counter += 1
|
| 65 |
+
return np.asarray(out[:n_bits], dtype=np.int64)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def _pad_and_pos(n: int, key, n_bits: int = _MSG_BITS):
|
| 69 |
+
"""Key-seeded per-entry XOR pad and message-index map (length ``n``)."""
|
| 70 |
+
rng = np.random.default_rng(_key_to_int(key))
|
| 71 |
+
pad = rng.integers(0, 2, size=n).astype(np.int64) # XOR mask
|
| 72 |
+
pos = rng.integers(0, n_bits, size=n).astype(np.int64) # msg index per entry
|
| 73 |
+
return pad, pos
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def encode_noise(shape, *, key,
|
| 77 |
+
seed: int = 0, device=None, dtype=torch.bfloat16) -> torch.Tensor:
|
| 78 |
+
C, H, W = shape
|
| 79 |
+
n = C * H * W
|
| 80 |
+
msg = _payload_to_bits(DEFAULT_GS_PAYLOAD)
|
| 81 |
+
pad, pos = _pad_and_pos(n, key)
|
| 82 |
+
target_half = (msg[pos] ^ pad).astype(np.float64) # {0,1} per entry
|
| 83 |
+
|
| 84 |
+
gen = torch.Generator(device="cpu").manual_seed(int(seed) & 0x7FFFFFFF)
|
| 85 |
+
u = torch.rand(n, generator=gen, dtype=torch.float64) # U(0,1) magnitudes
|
| 86 |
+
half = torch.from_numpy(target_half)
|
| 87 |
+
arg = ((half + u) / 2.0).clamp(1e-6, 1.0 - 1e-6)
|
| 88 |
+
z = torch.special.ndtri(arg) # inverse normal CDF
|
| 89 |
+
z = z.reshape(1, C, H, W)
|
| 90 |
+
return z.to(device=device, dtype=dtype)
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def decode_bits(noise: torch.Tensor, *, key) -> dict:
|
| 94 |
+
z = noise.detach().float().reshape(-1).cpu()
|
| 95 |
+
n = int(z.numel())
|
| 96 |
+
msg = _payload_to_bits(DEFAULT_GS_PAYLOAD)
|
| 97 |
+
pad, pos = _pad_and_pos(n, key)
|
| 98 |
+
|
| 99 |
+
observed_half = (z > 0).numpy().astype(np.int64) # sign -> half
|
| 100 |
+
expected_half = (msg[pos] ^ pad)
|
| 101 |
+
matches = int((observed_half == expected_half).sum())
|
| 102 |
+
raw_acc = matches / n
|
| 103 |
+
|
| 104 |
+
# Recover payload by majority vote of each entry's implied message bit.
|
| 105 |
+
implied = observed_half ^ pad # estimate of m[pos]
|
| 106 |
+
votes = np.zeros((_MSG_BITS, 2), dtype=np.int64)
|
| 107 |
+
np.add.at(votes, (pos, implied), 1)
|
| 108 |
+
msg_hat = votes.argmax(axis=1)
|
| 109 |
+
msg_acc = float((msg_hat == msg).mean())
|
| 110 |
+
|
| 111 |
+
# One-sided significance under Binomial(n, 0.5) via normal approximation.
|
| 112 |
+
z_score = (matches - 0.5 * n) / (0.5 * sqrt(n))
|
| 113 |
+
pvalue = 0.5 * erfc(z_score / sqrt(2))
|
| 114 |
+
|
| 115 |
+
return {
|
| 116 |
+
"raw_acc": raw_acc, "msg_acc": msg_acc, "matches": matches, "n": n,
|
| 117 |
+
"z_score": z_score, "pvalue": pvalue, "present": pvalue < 1e-6,
|
| 118 |
+
"msg_hat": msg_hat, "msg": msg,
|
| 119 |
+
}
|
vendor/mage_flow/models/modules/mage_layers.py
ADDED
|
@@ -0,0 +1,725 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
from typing import Any
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
import torch.nn as nn
|
| 6 |
+
import torch.nn.functional as F
|
| 7 |
+
from diffusers.models.attention import FeedForward
|
| 8 |
+
from diffusers.models.embeddings import TimestepEmbedding
|
| 9 |
+
from diffusers.models.normalization import RMSNorm
|
| 10 |
+
from ._attn_backend import flash_attn_varlen_func
|
| 11 |
+
from torch import Tensor
|
| 12 |
+
from torch._dynamo import allow_in_graph as maybe_allow_in_graph
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def apply_rotary_emb_mageflow(x: torch.Tensor, freqs_cis: torch.Tensor) -> torch.Tensor:
|
| 16 |
+
"""Apply complex rotary embeddings to `x` ([B, S, H, D]) using `freqs_cis`
|
| 17 |
+
(the MageFlowEmbedRope 2D multi-scale RoPE, adjacent-pair complex convention)."""
|
| 18 |
+
x_rotated = torch.view_as_complex(x.float().reshape(*x.shape[:-1], -1, 2))
|
| 19 |
+
freqs_cis = freqs_cis.unsqueeze(1)
|
| 20 |
+
x_out = torch.view_as_real(x_rotated * freqs_cis).flatten(-2)
|
| 21 |
+
return x_out.type_as(x)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def get_timestep_embedding(
|
| 25 |
+
timesteps: torch.Tensor,
|
| 26 |
+
embedding_dim: int,
|
| 27 |
+
flip_sin_to_cos: bool = False,
|
| 28 |
+
downscale_freq_shift: float = 1,
|
| 29 |
+
scale: float = 1,
|
| 30 |
+
max_period: int = 10000,
|
| 31 |
+
) -> torch.Tensor:
|
| 32 |
+
"""Sinusoidal timestep embeddings (DDPM convention).
|
| 33 |
+
|
| 34 |
+
NOTE: kept vendored (not diffusers') because the frequency table is
|
| 35 |
+
downcast to ``timesteps.dtype`` (bf16) here — the model was trained with
|
| 36 |
+
this exact bf16 rounding, so diffusers' fp32 variant produces a slightly
|
| 37 |
+
different embedding and degrades outputs.
|
| 38 |
+
"""
|
| 39 |
+
assert len(timesteps.shape) == 1, "Timesteps should be a 1d-array"
|
| 40 |
+
|
| 41 |
+
half_dim = embedding_dim // 2
|
| 42 |
+
exponent = -math.log(max_period) * torch.arange(start=0, end=half_dim, dtype=torch.float32, device=timesteps.device)
|
| 43 |
+
exponent = exponent / (half_dim - downscale_freq_shift)
|
| 44 |
+
|
| 45 |
+
emb = torch.exp(exponent).to(timesteps.dtype)
|
| 46 |
+
emb = timesteps[:, None].float() * emb[None, :]
|
| 47 |
+
|
| 48 |
+
# scale embeddings
|
| 49 |
+
emb = scale * emb
|
| 50 |
+
|
| 51 |
+
# concat sine and cosine embeddings
|
| 52 |
+
emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=-1)
|
| 53 |
+
|
| 54 |
+
# flip sine and cosine embeddings
|
| 55 |
+
if flip_sin_to_cos:
|
| 56 |
+
emb = torch.cat([emb[:, half_dim:], emb[:, :half_dim]], dim=-1)
|
| 57 |
+
|
| 58 |
+
# zero pad
|
| 59 |
+
if embedding_dim % 2 == 1:
|
| 60 |
+
emb = torch.nn.functional.pad(emb, (0, 1, 0, 0))
|
| 61 |
+
return emb
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
class Timesteps(nn.Module):
|
| 65 |
+
def __init__(
|
| 66 |
+
self,
|
| 67 |
+
num_channels: int,
|
| 68 |
+
flip_sin_to_cos: bool,
|
| 69 |
+
downscale_freq_shift: float,
|
| 70 |
+
scale: int = 1,
|
| 71 |
+
):
|
| 72 |
+
super().__init__()
|
| 73 |
+
self.num_channels = num_channels
|
| 74 |
+
self.flip_sin_to_cos = flip_sin_to_cos
|
| 75 |
+
self.downscale_freq_shift = downscale_freq_shift
|
| 76 |
+
self.scale = scale
|
| 77 |
+
|
| 78 |
+
def forward(self, timesteps: torch.Tensor) -> torch.Tensor:
|
| 79 |
+
t_emb = get_timestep_embedding(
|
| 80 |
+
timesteps,
|
| 81 |
+
self.num_channels,
|
| 82 |
+
flip_sin_to_cos=self.flip_sin_to_cos,
|
| 83 |
+
downscale_freq_shift=self.downscale_freq_shift,
|
| 84 |
+
scale=self.scale,
|
| 85 |
+
)
|
| 86 |
+
return t_emb
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
class MageFlowTimestepProjEmbeddings(nn.Module):
|
| 90 |
+
def __init__(self, embedding_dim):
|
| 91 |
+
super().__init__()
|
| 92 |
+
|
| 93 |
+
self.time_proj = Timesteps(num_channels=256, flip_sin_to_cos=True, downscale_freq_shift=0, scale=1000)
|
| 94 |
+
self.timestep_embedder = TimestepEmbedding(in_channels=256, time_embed_dim=embedding_dim)
|
| 95 |
+
|
| 96 |
+
def forward(self, timestep, hidden_states):
|
| 97 |
+
timesteps_proj = self.time_proj(timestep)
|
| 98 |
+
timesteps_emb = self.timestep_embedder(timesteps_proj.to(dtype=hidden_states.dtype)) # (N, D)
|
| 99 |
+
|
| 100 |
+
conditioning = timesteps_emb
|
| 101 |
+
|
| 102 |
+
return conditioning
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class MageFlowEmbedRope(nn.Module):
|
| 106 |
+
def __init__(self, theta: int, axes_dim: list[int], scale_rope=False):
|
| 107 |
+
super().__init__()
|
| 108 |
+
self.theta = theta
|
| 109 |
+
self.axes_dim = axes_dim
|
| 110 |
+
pos_index = torch.arange(4096)
|
| 111 |
+
neg_index = torch.arange(4096).flip(0) * -1 - 1
|
| 112 |
+
self.pos_freqs = torch.cat(
|
| 113 |
+
[
|
| 114 |
+
self.rope_params(pos_index, self.axes_dim[0], self.theta),
|
| 115 |
+
self.rope_params(pos_index, self.axes_dim[1], self.theta),
|
| 116 |
+
self.rope_params(pos_index, self.axes_dim[2], self.theta),
|
| 117 |
+
],
|
| 118 |
+
dim=1,
|
| 119 |
+
)
|
| 120 |
+
self.neg_freqs = torch.cat(
|
| 121 |
+
[
|
| 122 |
+
self.rope_params(neg_index, self.axes_dim[0], self.theta),
|
| 123 |
+
self.rope_params(neg_index, self.axes_dim[1], self.theta),
|
| 124 |
+
self.rope_params(neg_index, self.axes_dim[2], self.theta),
|
| 125 |
+
],
|
| 126 |
+
dim=1,
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
# DO NOT USING REGISTER BUFFER HERE, IT WILL CAUSE COMPLEX NUMBERS LOSE ITS IMAGINARY PART
|
| 130 |
+
self.scale_rope = scale_rope
|
| 131 |
+
self.video_freq_cache = {}
|
| 132 |
+
|
| 133 |
+
def rope_params(self, index, dim, theta=10000):
|
| 134 |
+
"""
|
| 135 |
+
Args:
|
| 136 |
+
index: [0, 1, 2, 3] 1D Tensor representing the position index of the token
|
| 137 |
+
"""
|
| 138 |
+
assert dim % 2 == 0
|
| 139 |
+
freqs = torch.outer(
|
| 140 |
+
index,
|
| 141 |
+
1.0 / torch.pow(theta, torch.arange(0, dim, 2).to(torch.float32).div(dim)),
|
| 142 |
+
)
|
| 143 |
+
freqs = torch.polar(torch.ones_like(freqs), freqs)
|
| 144 |
+
return freqs
|
| 145 |
+
|
| 146 |
+
def forward(
|
| 147 |
+
self,
|
| 148 |
+
video_fhw: tuple[int, int, int] | list[tuple[int, int, int]],
|
| 149 |
+
device: torch.device,
|
| 150 |
+
max_img_len: int = None,
|
| 151 |
+
) -> torch.Tensor:
|
| 152 |
+
"""Compute the vision RoPE frequencies (`vid_freqs`) for the packed image
|
| 153 |
+
tokens. Text tokens are NOT rotated, so no text RoPE is computed.
|
| 154 |
+
|
| 155 |
+
Args:
|
| 156 |
+
video_fhw (`Tuple[int, int, int]` or `List[Tuple[int, int, int]]`):
|
| 157 |
+
A list of 3 integers [frame, height, width] representing the shape of the video.
|
| 158 |
+
device: (`torch.device`):
|
| 159 |
+
The device on which to perform the RoPE computation.
|
| 160 |
+
"""
|
| 161 |
+
if self.pos_freqs.device != device:
|
| 162 |
+
self.pos_freqs = self.pos_freqs.to(device)
|
| 163 |
+
self.neg_freqs = self.neg_freqs.to(device)
|
| 164 |
+
|
| 165 |
+
if isinstance(video_fhw, list):
|
| 166 |
+
video_fhw = video_fhw[0]
|
| 167 |
+
if not isinstance(video_fhw, list):
|
| 168 |
+
video_fhw = [video_fhw]
|
| 169 |
+
|
| 170 |
+
vid_freqs = []
|
| 171 |
+
for idx, fhw in enumerate(video_fhw):
|
| 172 |
+
frame, height, width = fhw
|
| 173 |
+
# RoPE frequencies are cached manually
|
| 174 |
+
key = (frame, height, width, idx)
|
| 175 |
+
if key not in self.video_freq_cache:
|
| 176 |
+
self.video_freq_cache[key] = self._compute_video_freqs(frame, height, width, idx)
|
| 177 |
+
vid_freqs.append(self.video_freq_cache[key].to(device))
|
| 178 |
+
|
| 179 |
+
vid_freqs = torch.cat(vid_freqs, dim=0)
|
| 180 |
+
|
| 181 |
+
if max_img_len is not None and vid_freqs.shape[0] < max_img_len:
|
| 182 |
+
pad_len = max_img_len - vid_freqs.shape[0]
|
| 183 |
+
vid_freqs = torch.nn.functional.pad(vid_freqs, (0, 0, 0, pad_len))
|
| 184 |
+
|
| 185 |
+
return vid_freqs
|
| 186 |
+
|
| 187 |
+
def _compute_video_freqs(self, frame: int, height: int, width: int, idx: int = 0) -> torch.Tensor:
|
| 188 |
+
seq_lens = frame * height * width
|
| 189 |
+
freqs_pos = self.pos_freqs.split([x // 2 for x in self.axes_dim], dim=1)
|
| 190 |
+
freqs_neg = self.neg_freqs.split([x // 2 for x in self.axes_dim], dim=1)
|
| 191 |
+
|
| 192 |
+
freqs_frame = freqs_pos[0][idx : idx + frame].view(frame, 1, 1, -1).expand(frame, height, width, -1)
|
| 193 |
+
if self.scale_rope:
|
| 194 |
+
freqs_height = torch.cat(
|
| 195 |
+
[freqs_neg[1][-(height - height // 2) :], freqs_pos[1][: height // 2]],
|
| 196 |
+
dim=0,
|
| 197 |
+
)
|
| 198 |
+
freqs_height = freqs_height.view(1, height, 1, -1).expand(frame, height, width, -1)
|
| 199 |
+
freqs_width = torch.cat(
|
| 200 |
+
[freqs_neg[2][-(width - width // 2) :], freqs_pos[2][: width // 2]],
|
| 201 |
+
dim=0,
|
| 202 |
+
)
|
| 203 |
+
freqs_width = freqs_width.view(1, 1, width, -1).expand(frame, height, width, -1)
|
| 204 |
+
else:
|
| 205 |
+
freqs_height = freqs_pos[1][:height].view(1, height, 1, -1).expand(frame, height, width, -1)
|
| 206 |
+
freqs_width = freqs_pos[2][:width].view(1, 1, width, -1).expand(frame, height, width, -1)
|
| 207 |
+
|
| 208 |
+
freqs = torch.cat([freqs_frame, freqs_height, freqs_width], dim=-1).reshape(seq_lens, -1)
|
| 209 |
+
return freqs.clone().contiguous()
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
class Attention(nn.Module):
|
| 213 |
+
def __init__(
|
| 214 |
+
self,
|
| 215 |
+
query_dim: int,
|
| 216 |
+
cross_attention_dim: int | None = None,
|
| 217 |
+
heads: int = 8,
|
| 218 |
+
kv_heads: int | None = None,
|
| 219 |
+
dim_head: int = 64,
|
| 220 |
+
dropout: float = 0.0,
|
| 221 |
+
bias: bool = False,
|
| 222 |
+
scale_qk: bool = True,
|
| 223 |
+
added_kv_proj_dim: int | None = None,
|
| 224 |
+
added_proj_bias: bool | None = True,
|
| 225 |
+
out_bias: bool = True,
|
| 226 |
+
eps: float = 1e-5,
|
| 227 |
+
processor=None,
|
| 228 |
+
out_dim: int = None,
|
| 229 |
+
out_context_dim: int = None,
|
| 230 |
+
elementwise_affine: bool = True,
|
| 231 |
+
):
|
| 232 |
+
super().__init__()
|
| 233 |
+
# logger.info(f"processor: {processor}")
|
| 234 |
+
|
| 235 |
+
self.inner_dim = out_dim if out_dim is not None else dim_head * heads
|
| 236 |
+
self.inner_kv_dim = self.inner_dim if kv_heads is None else dim_head * kv_heads
|
| 237 |
+
self.query_dim = query_dim
|
| 238 |
+
self.use_bias = bias
|
| 239 |
+
self.is_cross_attention = cross_attention_dim is not None
|
| 240 |
+
self.cross_attention_dim = cross_attention_dim if cross_attention_dim is not None else query_dim
|
| 241 |
+
self.fused_projections = False
|
| 242 |
+
self.out_dim = out_dim if out_dim is not None else query_dim
|
| 243 |
+
self.out_context_dim = out_context_dim if out_context_dim is not None else query_dim
|
| 244 |
+
|
| 245 |
+
self.scale_qk = scale_qk
|
| 246 |
+
self.scale = dim_head**-0.5 if self.scale_qk else 1.0
|
| 247 |
+
|
| 248 |
+
self.heads = out_dim // dim_head if out_dim is not None else heads
|
| 249 |
+
# for slice_size > 0 the attention score computation
|
| 250 |
+
# is split across the batch axis to save memory
|
| 251 |
+
# You can set_slice_size with `set_attention_slice`
|
| 252 |
+
self.sliceable_head_dim = heads
|
| 253 |
+
|
| 254 |
+
self.added_kv_proj_dim = added_kv_proj_dim
|
| 255 |
+
|
| 256 |
+
# qk_norm is always "rms_norm" for MageFlow.
|
| 257 |
+
self.norm_q = RMSNorm(dim_head, eps=eps, elementwise_affine=elementwise_affine)
|
| 258 |
+
self.norm_k = RMSNorm(dim_head, eps=eps, elementwise_affine=elementwise_affine)
|
| 259 |
+
|
| 260 |
+
self.to_q = nn.Linear(query_dim, self.inner_dim, bias=bias)
|
| 261 |
+
self.to_k = nn.Linear(self.cross_attention_dim, self.inner_kv_dim, bias=bias)
|
| 262 |
+
self.to_v = nn.Linear(self.cross_attention_dim, self.inner_kv_dim, bias=bias)
|
| 263 |
+
|
| 264 |
+
self.added_proj_bias = added_proj_bias
|
| 265 |
+
if self.added_kv_proj_dim is not None:
|
| 266 |
+
self.add_k_proj = nn.Linear(added_kv_proj_dim, self.inner_kv_dim, bias=added_proj_bias)
|
| 267 |
+
self.add_v_proj = nn.Linear(added_kv_proj_dim, self.inner_kv_dim, bias=added_proj_bias)
|
| 268 |
+
self.add_q_proj = nn.Linear(added_kv_proj_dim, self.inner_dim, bias=added_proj_bias)
|
| 269 |
+
self.norm_added_q = RMSNorm(dim_head, eps=eps)
|
| 270 |
+
self.norm_added_k = RMSNorm(dim_head, eps=eps)
|
| 271 |
+
else:
|
| 272 |
+
self.add_q_proj = None
|
| 273 |
+
self.add_k_proj = None
|
| 274 |
+
self.add_v_proj = None
|
| 275 |
+
self.norm_added_q = None
|
| 276 |
+
self.norm_added_k = None
|
| 277 |
+
|
| 278 |
+
self.to_out = nn.ModuleList([])
|
| 279 |
+
self.to_out.append(nn.Linear(self.inner_dim, self.out_dim, bias=out_bias))
|
| 280 |
+
self.to_out.append(nn.Dropout(dropout))
|
| 281 |
+
|
| 282 |
+
self.to_add_out = nn.Linear(self.inner_dim, self.out_context_dim, bias=out_bias)
|
| 283 |
+
|
| 284 |
+
self.set_processor(processor)
|
| 285 |
+
|
| 286 |
+
def set_processor(self, processor) -> None:
|
| 287 |
+
self.processor = processor
|
| 288 |
+
|
| 289 |
+
def get_processor(self):
|
| 290 |
+
return self.processor
|
| 291 |
+
|
| 292 |
+
def forward(
|
| 293 |
+
self,
|
| 294 |
+
hidden_states: torch.Tensor,
|
| 295 |
+
attention_mask: torch.Tensor | None = None,
|
| 296 |
+
txt_cu_lens: torch.Tensor | None = None,
|
| 297 |
+
img_cu_lens: torch.Tensor | None = None,
|
| 298 |
+
# ms_pe: tuple[torch.FloatTensor, torch.FloatTensor] | None = None,
|
| 299 |
+
# pe: torch.FloatTensor | None = None,
|
| 300 |
+
# freqs_cos: torch.Tensor | None = None,
|
| 301 |
+
# freqs_sin: torch.Tensor | None = None,
|
| 302 |
+
image_rotary_emb: torch.Tensor | None = None,
|
| 303 |
+
**attention_kwargs,
|
| 304 |
+
) -> torch.Tensor:
|
| 305 |
+
r"""
|
| 306 |
+
The forward method of the `Attention` class.
|
| 307 |
+
|
| 308 |
+
Args:
|
| 309 |
+
hidden_states (`torch.Tensor`):
|
| 310 |
+
The hidden states of the query.
|
| 311 |
+
encoder_hidden_states (`torch.Tensor`, *optional*):
|
| 312 |
+
The hidden states of the encoder.
|
| 313 |
+
attention_mask (`torch.Tensor`, *optional*):
|
| 314 |
+
The attention mask to use. If `None`, no mask is applied.
|
| 315 |
+
**attention_kwargs:
|
| 316 |
+
Additional keyword arguments to pass along to the attention.
|
| 317 |
+
|
| 318 |
+
Returns:
|
| 319 |
+
`torch.Tensor`: The output of the attention layer.
|
| 320 |
+
"""
|
| 321 |
+
# The `Attention` class can call different attention processors / attention functions
|
| 322 |
+
# here we simply pass along all tensors to the selected processor class
|
| 323 |
+
# For standard processors that are defined here, `**attention_kwargs` is empty
|
| 324 |
+
|
| 325 |
+
return self.processor(
|
| 326 |
+
self,
|
| 327 |
+
hidden_states,
|
| 328 |
+
attention_mask=attention_mask,
|
| 329 |
+
txt_cu_lens=txt_cu_lens,
|
| 330 |
+
img_cu_lens=img_cu_lens,
|
| 331 |
+
image_rotary_emb=image_rotary_emb,
|
| 332 |
+
**attention_kwargs,
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
class MageDoubleStreamAttnProcessor:
|
| 337 |
+
"""
|
| 338 |
+
Attention processor for the Mage double-stream architecture, matching DoubleStreamLayerMegatron logic. This processor
|
| 339 |
+
implements joint attention computation where text and image streams are processed together.
|
| 340 |
+
"""
|
| 341 |
+
|
| 342 |
+
_attention_backend = None
|
| 343 |
+
_parallel_config = None
|
| 344 |
+
|
| 345 |
+
def __init__(self):
|
| 346 |
+
if not hasattr(F, "scaled_dot_product_attention"):
|
| 347 |
+
raise ImportError(
|
| 348 |
+
"MageDoubleStreamAttnProcessor requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0."
|
| 349 |
+
)
|
| 350 |
+
|
| 351 |
+
def __call__(
|
| 352 |
+
self,
|
| 353 |
+
attn: Attention,
|
| 354 |
+
hidden_states: torch.FloatTensor, # Image stream
|
| 355 |
+
img_cu_lens: torch.LongTensor,
|
| 356 |
+
attention_mask: torch.FloatTensor | None = None,
|
| 357 |
+
encoder_hidden_states: torch.FloatTensor = None, # Text stream
|
| 358 |
+
txt_cu_lens: torch.LongTensor = None,
|
| 359 |
+
image_rotary_emb: torch.Tensor | None = None,
|
| 360 |
+
**kwargs,
|
| 361 |
+
) -> torch.FloatTensor:
|
| 362 |
+
if encoder_hidden_states is None:
|
| 363 |
+
raise ValueError("MageDoubleStreamAttnProcessor requires encoder_hidden_states (text stream)")
|
| 364 |
+
|
| 365 |
+
# seq_txt = encoder_hidden_states.shape[1]
|
| 366 |
+
|
| 367 |
+
# logger.info(f"hidden_states: {hidden_states.shape}")
|
| 368 |
+
# logger.info(f"encoder_hidden_states: {encoder_hidden_states.shape}")
|
| 369 |
+
|
| 370 |
+
# Compute QKV for image stream (sample projections)
|
| 371 |
+
img_query = attn.to_q(hidden_states)
|
| 372 |
+
img_key = attn.to_k(hidden_states)
|
| 373 |
+
img_value = attn.to_v(hidden_states)
|
| 374 |
+
|
| 375 |
+
# Compute QKV for text stream (context projections)
|
| 376 |
+
txt_query = attn.add_q_proj(encoder_hidden_states)
|
| 377 |
+
txt_key = attn.add_k_proj(encoder_hidden_states)
|
| 378 |
+
txt_value = attn.add_v_proj(encoder_hidden_states)
|
| 379 |
+
|
| 380 |
+
# Reshape for multi-head attention
|
| 381 |
+
img_query = img_query.unflatten(-1, (attn.heads, -1))
|
| 382 |
+
img_key = img_key.unflatten(-1, (attn.heads, -1))
|
| 383 |
+
img_value = img_value.unflatten(-1, (attn.heads, -1))
|
| 384 |
+
|
| 385 |
+
txt_query = txt_query.unflatten(-1, (attn.heads, -1))
|
| 386 |
+
txt_key = txt_key.unflatten(-1, (attn.heads, -1))
|
| 387 |
+
txt_value = txt_value.unflatten(-1, (attn.heads, -1))
|
| 388 |
+
|
| 389 |
+
# logger.info(
|
| 390 |
+
# f"img_query shape: {img_query.shape}, img_key shape: {img_key.shape}, img_value shape: {img_value.shape}"
|
| 391 |
+
# )
|
| 392 |
+
# logger.info(
|
| 393 |
+
# f"txt_query shape: {txt_query.shape}, txt_key shape: {txt_key.shape}, txt_value shape: {txt_value.shape}"
|
| 394 |
+
# )
|
| 395 |
+
|
| 396 |
+
if img_query.ndim == 4:
|
| 397 |
+
img_query = img_query.flatten(0, 1)
|
| 398 |
+
img_key = img_key.flatten(0, 1)
|
| 399 |
+
img_value = img_value.flatten(0, 1)
|
| 400 |
+
|
| 401 |
+
if txt_query.ndim == 4:
|
| 402 |
+
txt_query = txt_query.flatten(0, 1)
|
| 403 |
+
txt_key = txt_key.flatten(0, 1)
|
| 404 |
+
txt_value = txt_value.flatten(0, 1)
|
| 405 |
+
|
| 406 |
+
# Apply QK normalization
|
| 407 |
+
if attn.norm_q is not None:
|
| 408 |
+
img_query = attn.norm_q(img_query)
|
| 409 |
+
if attn.norm_k is not None:
|
| 410 |
+
img_key = attn.norm_k(img_key)
|
| 411 |
+
if attn.norm_added_q is not None:
|
| 412 |
+
txt_query = attn.norm_added_q(txt_query)
|
| 413 |
+
if attn.norm_added_k is not None:
|
| 414 |
+
txt_key = attn.norm_added_k(txt_key)
|
| 415 |
+
|
| 416 |
+
# logger.info(f"txt_query shape: {txt_query.shape}, txt_key shape: {txt_key.shape}")
|
| 417 |
+
# logger.info(f"freqs_cos shape: {freqs_cos.shape}, freqs_sin shape: {freqs_sin.shape}")
|
| 418 |
+
|
| 419 |
+
# Apply 2D multi-scale RoPE (MageFlowEmbedRope) to image tokens
|
| 420 |
+
img_freqs = image_rotary_emb
|
| 421 |
+
img_query = apply_rotary_emb_mageflow(img_query, img_freqs)
|
| 422 |
+
img_key = apply_rotary_emb_mageflow(img_key, img_freqs)
|
| 423 |
+
# Concatenate for joint attention
|
| 424 |
+
# Order: [text, image]
|
| 425 |
+
# joint_query = torch.cat([txt_query, img_query], dim=1)
|
| 426 |
+
# joint_key = torch.cat([txt_key, img_key], dim=1)
|
| 427 |
+
# joint_value = torch.cat([txt_value, img_value], dim=1)
|
| 428 |
+
|
| 429 |
+
# Calculate lengths
|
| 430 |
+
img_lens = img_cu_lens[1:] - img_cu_lens[:-1]
|
| 431 |
+
txt_lens = txt_cu_lens[1:] - txt_cu_lens[:-1]
|
| 432 |
+
|
| 433 |
+
# Calculate joint cu_seqlens
|
| 434 |
+
joint_lens = txt_lens + img_lens
|
| 435 |
+
joint_cu_lens = torch.cat(
|
| 436 |
+
[
|
| 437 |
+
torch.zeros(1, dtype=torch.int32, device=joint_lens.device),
|
| 438 |
+
torch.cumsum(joint_lens, dim=0, dtype=torch.int32),
|
| 439 |
+
],
|
| 440 |
+
dim=0,
|
| 441 |
+
)
|
| 442 |
+
|
| 443 |
+
# logger.info(f"txt_lens: {txt_lens}, img_lens: {img_lens}")
|
| 444 |
+
# logger.info(f"joint_lens: {joint_lens}, joint_cu_lens: {joint_cu_lens}")
|
| 445 |
+
|
| 446 |
+
device = joint_lens.device
|
| 447 |
+
batch_size = len(txt_lens)
|
| 448 |
+
sample_indices = torch.arange(batch_size, device=device)
|
| 449 |
+
|
| 450 |
+
txt_sample_ids = torch.repeat_interleave(sample_indices, txt_lens)
|
| 451 |
+
img_sample_ids = torch.repeat_interleave(sample_indices, img_lens)
|
| 452 |
+
|
| 453 |
+
txt_intra_pos = torch.arange(txt_query.shape[0], device=device) - txt_cu_lens[txt_sample_ids]
|
| 454 |
+
img_intra_pos = torch.arange(img_query.shape[0], device=device) - img_cu_lens[img_sample_ids]
|
| 455 |
+
|
| 456 |
+
txt_dest_indices = joint_cu_lens[txt_sample_ids] + txt_intra_pos
|
| 457 |
+
img_dest_indices = joint_cu_lens[img_sample_ids] + txt_lens[img_sample_ids] + img_intra_pos
|
| 458 |
+
|
| 459 |
+
total_tokens = joint_cu_lens[-1]
|
| 460 |
+
joint_query = torch.empty((total_tokens, *txt_query.shape[1:]), dtype=txt_query.dtype, device=device)
|
| 461 |
+
joint_key = torch.empty((total_tokens, *txt_key.shape[1:]), dtype=txt_key.dtype, device=device)
|
| 462 |
+
joint_value = torch.empty((total_tokens, *txt_value.shape[1:]), dtype=txt_value.dtype, device=device)
|
| 463 |
+
|
| 464 |
+
# logger.info(f"joint_query shape: {joint_query.shape}")
|
| 465 |
+
# logger.info(f"joint_key shape: {joint_key.shape}")
|
| 466 |
+
# logger.info(f"joint_value shape: {joint_value.shape}")
|
| 467 |
+
# logger.info(f"txt_dest_indices shape: {txt_dest_indices.shape}")
|
| 468 |
+
# logger.info(f"img_dest_indices shape: {img_dest_indices.shape}")
|
| 469 |
+
|
| 470 |
+
joint_query[txt_dest_indices] = txt_query
|
| 471 |
+
joint_query[img_dest_indices] = img_query
|
| 472 |
+
|
| 473 |
+
joint_key[txt_dest_indices] = txt_key
|
| 474 |
+
joint_key[img_dest_indices] = img_key
|
| 475 |
+
|
| 476 |
+
joint_value[txt_dest_indices] = txt_value
|
| 477 |
+
joint_value[img_dest_indices] = img_value
|
| 478 |
+
|
| 479 |
+
max_seqlen = joint_lens.max().item()
|
| 480 |
+
joint_attn_output = flash_attn_varlen_func(
|
| 481 |
+
joint_query,
|
| 482 |
+
joint_key,
|
| 483 |
+
joint_value,
|
| 484 |
+
cu_seqlens_q=joint_cu_lens,
|
| 485 |
+
cu_seqlens_k=joint_cu_lens,
|
| 486 |
+
max_seqlen_q=max_seqlen,
|
| 487 |
+
max_seqlen_k=max_seqlen,
|
| 488 |
+
dropout_p=0.0,
|
| 489 |
+
softmax_scale=None,
|
| 490 |
+
causal=False,
|
| 491 |
+
)
|
| 492 |
+
|
| 493 |
+
txt_attn_output = joint_attn_output[txt_dest_indices]
|
| 494 |
+
img_attn_output = joint_attn_output[img_dest_indices]
|
| 495 |
+
|
| 496 |
+
img_attn_output = img_attn_output.flatten(1, 2) # (N, H, D) -> (N, H*D)
|
| 497 |
+
img_attn_output = img_attn_output.to(joint_query.dtype)
|
| 498 |
+
|
| 499 |
+
txt_attn_output = txt_attn_output.flatten(1, 2) # (N, H, D) -> (N, H*D)
|
| 500 |
+
txt_attn_output = txt_attn_output.to(joint_query.dtype)
|
| 501 |
+
|
| 502 |
+
img_attn_output = attn.to_out[0](img_attn_output)
|
| 503 |
+
if len(attn.to_out) > 1:
|
| 504 |
+
img_attn_output = attn.to_out[1](img_attn_output) # dropout
|
| 505 |
+
|
| 506 |
+
txt_attn_output = attn.to_add_out(txt_attn_output)
|
| 507 |
+
txt_attn_output = txt_attn_output.view(
|
| 508 |
+
encoder_hidden_states.shape[0], encoder_hidden_states.shape[1], txt_attn_output.shape[-1]
|
| 509 |
+
)
|
| 510 |
+
|
| 511 |
+
return img_attn_output, txt_attn_output
|
| 512 |
+
|
| 513 |
+
|
| 514 |
+
@maybe_allow_in_graph
|
| 515 |
+
class MageFlowTransformerBlock(nn.Module):
|
| 516 |
+
def __init__(
|
| 517 |
+
self,
|
| 518 |
+
dim: int,
|
| 519 |
+
num_attention_heads: int,
|
| 520 |
+
attention_head_dim: int,
|
| 521 |
+
eps: float = 1e-6,
|
| 522 |
+
):
|
| 523 |
+
super().__init__()
|
| 524 |
+
|
| 525 |
+
self.dim = dim
|
| 526 |
+
self.num_attention_heads = num_attention_heads
|
| 527 |
+
self.attention_head_dim = attention_head_dim
|
| 528 |
+
|
| 529 |
+
# Image processing modules
|
| 530 |
+
self.img_mod = nn.Sequential(
|
| 531 |
+
nn.SiLU(),
|
| 532 |
+
nn.Linear(dim, 6 * dim, bias=True), # For scale, shift, gate for norm1 and norm2
|
| 533 |
+
)
|
| 534 |
+
self.img_norm1 = nn.LayerNorm(dim, elementwise_affine=False, eps=eps)
|
| 535 |
+
self.attn = Attention(
|
| 536 |
+
query_dim=dim,
|
| 537 |
+
cross_attention_dim=None, # Enable cross attention for joint computation
|
| 538 |
+
added_kv_proj_dim=dim, # Enable added KV projections for text stream
|
| 539 |
+
dim_head=attention_head_dim,
|
| 540 |
+
heads=num_attention_heads,
|
| 541 |
+
out_dim=dim,
|
| 542 |
+
bias=True,
|
| 543 |
+
processor=MageDoubleStreamAttnProcessor(),
|
| 544 |
+
eps=eps,
|
| 545 |
+
)
|
| 546 |
+
self.img_norm2 = nn.LayerNorm(dim, elementwise_affine=False, eps=eps)
|
| 547 |
+
self.img_mlp = FeedForward(dim=dim, dim_out=dim, activation_fn="gelu-approximate")
|
| 548 |
+
|
| 549 |
+
# Text processing modules
|
| 550 |
+
self.txt_mod = nn.Sequential(
|
| 551 |
+
nn.SiLU(),
|
| 552 |
+
nn.Linear(dim, 6 * dim, bias=True), # For scale, shift, gate for norm1 and norm2
|
| 553 |
+
)
|
| 554 |
+
self.txt_norm1 = nn.LayerNorm(dim, elementwise_affine=False, eps=eps)
|
| 555 |
+
# Text doesn't need separate attention - it's handled by img_attn joint computation
|
| 556 |
+
self.txt_norm2 = nn.LayerNorm(dim, elementwise_affine=False, eps=eps)
|
| 557 |
+
self.txt_mlp = FeedForward(dim=dim, dim_out=dim, activation_fn="gelu-approximate")
|
| 558 |
+
|
| 559 |
+
def _modulate(self, x, mod_params, cu_lens=None, seq_lens=None):
|
| 560 |
+
"""Apply modulation to input tensor"""
|
| 561 |
+
shift, scale, gate = mod_params.chunk(3, dim=-1)
|
| 562 |
+
if cu_lens is not None:
|
| 563 |
+
assert x.shape[0] == 1, "x must be of shape (1, *) when cu_lens is not None"
|
| 564 |
+
x_flattened = x.view(-1, x.shape[-1])
|
| 565 |
+
lengths = cu_lens[1:] - cu_lens[:-1]
|
| 566 |
+
shift_t = shift.repeat_interleave(lengths, dim=0)
|
| 567 |
+
scale_t = scale.repeat_interleave(lengths, dim=0)
|
| 568 |
+
gate_t = gate.repeat_interleave(lengths, dim=0)
|
| 569 |
+
|
| 570 |
+
x_flattened = x_flattened * (1 + scale_t) + shift_t
|
| 571 |
+
x = x_flattened.view(x.shape)
|
| 572 |
+
return x, gate_t
|
| 573 |
+
else:
|
| 574 |
+
return x * (1 + scale) + shift, gate
|
| 575 |
+
|
| 576 |
+
def forward(
|
| 577 |
+
self,
|
| 578 |
+
hidden_states: torch.Tensor,
|
| 579 |
+
encoder_hidden_states: torch.Tensor,
|
| 580 |
+
# encoder_hidden_states_mask: torch.Tensor,
|
| 581 |
+
temb: torch.Tensor | tuple[torch.Tensor, torch.Tensor],
|
| 582 |
+
image_rotary_emb: torch.Tensor,
|
| 583 |
+
# freqs_cos: torch.Tensor,
|
| 584 |
+
# freqs_sin: torch.Tensor,
|
| 585 |
+
txt_cu_lens: torch.Tensor,
|
| 586 |
+
img_cu_lens: torch.Tensor,
|
| 587 |
+
joint_attention_kwargs: dict[str, Any] | None = None,
|
| 588 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 589 |
+
# Get modulation parameters for both streams
|
| 590 |
+
# if isinstance(temb, tuple):
|
| 591 |
+
# temb_img, temb_txt = temb
|
| 592 |
+
# else:
|
| 593 |
+
# temb_img = temb_txt = temb
|
| 594 |
+
|
| 595 |
+
img_mod_params = self.img_mod(temb) # [B, 6*dim]
|
| 596 |
+
txt_mod_params = self.txt_mod(temb) # [B, 6*dim]
|
| 597 |
+
|
| 598 |
+
# logger.info(f"img_mod_params: {img_mod_params.shape}, txt_mod_params: {txt_mod_params.shape}")
|
| 599 |
+
|
| 600 |
+
# if img_cu_lens is not None and txt_cu_lens is not None and hidden_states.ndim == 2:
|
| 601 |
+
# img_lens = img_cu_lens[1:] - img_cu_lens[:-1]
|
| 602 |
+
# txt_lens = txt_cu_lens[1:] - txt_cu_lens[:-1]
|
| 603 |
+
# img_mod_params = img_mod_params.repeat_interleave(img_lens, dim=0)
|
| 604 |
+
# txt_mod_params = txt_mod_params.repeat_interleave(txt_lens, dim=0)
|
| 605 |
+
|
| 606 |
+
# Split modulation parameters for norm1 and norm2
|
| 607 |
+
img_mod1, img_mod2 = img_mod_params.chunk(2, dim=-1) # Each [B, 3*dim]
|
| 608 |
+
txt_mod1, txt_mod2 = txt_mod_params.chunk(2, dim=-1) # Each [B, 3*dim]
|
| 609 |
+
|
| 610 |
+
# Process image stream - norm1 + modulation
|
| 611 |
+
img_normed = self.img_norm1(hidden_states)
|
| 612 |
+
img_modulated, img_gate1 = self._modulate(img_normed, img_mod1, cu_lens=img_cu_lens)
|
| 613 |
+
|
| 614 |
+
# Process text stream - norm1 + modulation
|
| 615 |
+
txt_normed = self.txt_norm1(encoder_hidden_states)
|
| 616 |
+
txt_modulated, txt_gate1 = self._modulate(txt_normed, txt_mod1, cu_lens=txt_cu_lens)
|
| 617 |
+
|
| 618 |
+
# Use MageDoubleStreamAttnProcessor for joint attention computation
|
| 619 |
+
# This directly implements the DoubleStreamLayerMegatron logic:
|
| 620 |
+
# 1. Computes QKV for both streams
|
| 621 |
+
# 2. Applies QK normalization and RoPE
|
| 622 |
+
# 3. Concatenates and runs joint attention
|
| 623 |
+
# 4. Splits results back to separate streams
|
| 624 |
+
joint_attention_kwargs = joint_attention_kwargs or {}
|
| 625 |
+
# logger.info(f"img_modulated: {img_modulated}")
|
| 626 |
+
# logger.info(f"txt_modulated: {txt_modulated}")
|
| 627 |
+
attn_output = self.attn(
|
| 628 |
+
hidden_states=img_modulated, # Image stream (will be processed as "sample")
|
| 629 |
+
encoder_hidden_states=txt_modulated, # Text stream (will be processed as "context")
|
| 630 |
+
# encoder_hidden_states_mask=encoder_hidden_states_mask,
|
| 631 |
+
image_rotary_emb=image_rotary_emb,
|
| 632 |
+
txt_cu_lens=txt_cu_lens,
|
| 633 |
+
img_cu_lens=img_cu_lens,
|
| 634 |
+
# freqs_cos=freqs_cos,
|
| 635 |
+
# freqs_sin=freqs_sin,
|
| 636 |
+
**joint_attention_kwargs,
|
| 637 |
+
)
|
| 638 |
+
# logger.info(f"attn_output: {attn_output}")
|
| 639 |
+
|
| 640 |
+
# MageDoubleStreamAttnProcessor returns (img_output, txt_output) when encoder_hidden_states is provided
|
| 641 |
+
img_attn_output, txt_attn_output = attn_output
|
| 642 |
+
|
| 643 |
+
# Apply attention gates and add residual (like in Megatron)
|
| 644 |
+
hidden_states = hidden_states + img_gate1 * img_attn_output
|
| 645 |
+
encoder_hidden_states = encoder_hidden_states + txt_gate1 * txt_attn_output
|
| 646 |
+
|
| 647 |
+
# Process image stream - norm2 + MLP
|
| 648 |
+
img_normed2 = self.img_norm2(hidden_states)
|
| 649 |
+
img_modulated2, img_gate2 = self._modulate(img_normed2, img_mod2, cu_lens=img_cu_lens)
|
| 650 |
+
img_mlp_output = self.img_mlp(img_modulated2)
|
| 651 |
+
hidden_states = hidden_states + img_gate2 * img_mlp_output
|
| 652 |
+
|
| 653 |
+
# Process text stream - norm2 + MLP
|
| 654 |
+
txt_normed2 = self.txt_norm2(encoder_hidden_states)
|
| 655 |
+
txt_modulated2, txt_gate2 = self._modulate(txt_normed2, txt_mod2, cu_lens=txt_cu_lens)
|
| 656 |
+
txt_mlp_output = self.txt_mlp(txt_modulated2)
|
| 657 |
+
encoder_hidden_states = encoder_hidden_states + txt_gate2 * txt_mlp_output
|
| 658 |
+
|
| 659 |
+
# Clip to prevent overflow for fp16
|
| 660 |
+
if encoder_hidden_states.dtype == torch.float16:
|
| 661 |
+
encoder_hidden_states = encoder_hidden_states.clip(-65504, 65504)
|
| 662 |
+
if hidden_states.dtype == torch.float16:
|
| 663 |
+
hidden_states = hidden_states.clip(-65504, 65504)
|
| 664 |
+
|
| 665 |
+
return encoder_hidden_states, hidden_states
|
| 666 |
+
|
| 667 |
+
|
| 668 |
+
class AdaLayerNormContinuous(nn.Module):
|
| 669 |
+
r"""
|
| 670 |
+
Adaptive normalization layer with a norm layer (layer_norm or rms_norm).
|
| 671 |
+
|
| 672 |
+
Args:
|
| 673 |
+
embedding_dim (`int`): Embedding dimension to use during projection.
|
| 674 |
+
conditioning_embedding_dim (`int`): Dimension of the input condition.
|
| 675 |
+
elementwise_affine (`bool`, defaults to `True`):
|
| 676 |
+
Boolean flag to denote if affine transformation should be applied.
|
| 677 |
+
eps (`float`, defaults to 1e-5): Epsilon factor.
|
| 678 |
+
bias (`bias`, defaults to `True`): Boolean flag to denote if bias should be use.
|
| 679 |
+
norm_type (`str`, defaults to `"layer_norm"`):
|
| 680 |
+
Normalization layer to use. Values supported: "layer_norm", "rms_norm".
|
| 681 |
+
"""
|
| 682 |
+
|
| 683 |
+
def __init__(
|
| 684 |
+
self,
|
| 685 |
+
embedding_dim: int,
|
| 686 |
+
conditioning_embedding_dim: int,
|
| 687 |
+
# NOTE: It is a bit weird that the norm layer can be configured to have scale and shift parameters
|
| 688 |
+
# because the output is immediately scaled and shifted by the projected conditioning embeddings.
|
| 689 |
+
# Note that AdaLayerNorm does not let the norm layer have scale and shift parameters.
|
| 690 |
+
# However, this is how it was implemented in the original code, and it's rather likely you should
|
| 691 |
+
# set `elementwise_affine` to False.
|
| 692 |
+
elementwise_affine=True,
|
| 693 |
+
eps=1e-5,
|
| 694 |
+
bias=True,
|
| 695 |
+
norm_type="layer_norm",
|
| 696 |
+
):
|
| 697 |
+
super().__init__()
|
| 698 |
+
self.silu = nn.SiLU()
|
| 699 |
+
self.linear = nn.Linear(conditioning_embedding_dim, embedding_dim * 2, bias=bias)
|
| 700 |
+
if norm_type == "layer_norm":
|
| 701 |
+
self.norm = nn.LayerNorm(embedding_dim, eps, elementwise_affine, bias)
|
| 702 |
+
elif norm_type == "rms_norm":
|
| 703 |
+
self.norm = RMSNorm(embedding_dim, eps, elementwise_affine)
|
| 704 |
+
else:
|
| 705 |
+
raise ValueError(f"unknown norm_type {norm_type}")
|
| 706 |
+
|
| 707 |
+
def forward(
|
| 708 |
+
self, x: torch.Tensor, conditioning_embedding: torch.Tensor,
|
| 709 |
+
cu_seqlens: torch.Tensor | None = None, seq_lens: torch.Tensor | None = None,
|
| 710 |
+
) -> torch.Tensor:
|
| 711 |
+
# convert back to the original dtype in case `conditioning_embedding`` is upcasted to float32 (needed for
|
| 712 |
+
# hunyuanDiT)
|
| 713 |
+
emb = self.linear(self.silu(conditioning_embedding).to(x.dtype))
|
| 714 |
+
if cu_seqlens is None:
|
| 715 |
+
scale, shift = torch.chunk(emb, 2, dim=-1)
|
| 716 |
+
x = self.norm(x) * (1 + scale) + shift
|
| 717 |
+
else:
|
| 718 |
+
sample_lens = cu_seqlens[1:] - cu_seqlens[:-1]
|
| 719 |
+
flattened_x = x.view(-1, x.shape[-1])
|
| 720 |
+
scale, shift = torch.chunk(emb, 2, dim=-1)
|
| 721 |
+
scale_t = torch.repeat_interleave(scale, sample_lens, dim=0)
|
| 722 |
+
shift_t = torch.repeat_interleave(shift, sample_lens, dim=0)
|
| 723 |
+
flattened_x = self.norm(flattened_x) * (1 + scale_t) + shift_t
|
| 724 |
+
x = flattened_x.view(x.shape)
|
| 725 |
+
return x
|
vendor/mage_flow/models/modules/mage_text.py
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
from contextlib import contextmanager
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
from PIL import Image
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
CONTENT_FILTER_SYSTEM = """You are a strict content-policy classifier for a text-to-image system following Microsoft Responsible AI guidelines. Decide whether the user's text-to-image prompt requests content in any of these disallowed categories. Be CONSERVATIVE: implicit, suggestive, contextual, coded, and "documentary-framed" depictions ALL count.
|
| 12 |
+
|
| 13 |
+
# CORE RULE
|
| 14 |
+
You are NOT permitted to use any "but the prompt is generic / artistic / documentary / fictional / non-explicit" rationalization to lower a true violation to false. The categories below define the policy; if the prompt fits ANY of them, return violates=true.
|
| 15 |
+
|
| 16 |
+
# CATEGORIES
|
| 17 |
+
|
| 18 |
+
- "sexual": nudity, sexual acts, sexual exploitation, suggestive depictions of intimate body parts, OR scenes that strongly imply nudity (e.g. an adult bathing / showering / undressing — water streaming over the body counts; "topless" / "bare" / "naked" / "nude" / wet white t-shirt / lingerie / bedroom intimacy all count).
|
| 19 |
+
|
| 20 |
+
- "hate": hate symbols, dehumanizing imagery of protected groups, OR mocking/jeering scenes targeting a protected group. Includes:
|
| 21 |
+
* Overt symbols: swastika, KKK robes, burning cross, Confederate flag in hostile context, lynching noose with white-supremacist context.
|
| 22 |
+
* **Coded / cryptic symbols** — TREAT AS HATE EVEN WHEN OBSCURE: Sonnenrad / black sun, 1488 / 14 / 88 / 14 words, "RaHoWa", SS bolts, totenkopf, blood drop cross, three-percenter, OK-hand in supremacist context, Pepe with hate styling, valknut/odal in supremacist context.
|
| 23 |
+
* **Antisemitic visual tropes**: hook-nosed / claw-handed banker figure, octopus or globe-grasping figure, "puppet master" caricature, blood-libel imagery, Star-of-David defaced — ALL count even without the word "Jewish".
|
| 24 |
+
* **Anti-Muslim tropes**: Muslims depicted as bombers / barbarians / invaders.
|
| 25 |
+
* **Homophobia / transphobia**: same-sex couple being mocked, jeered at, or attacked (e.g. wedding scene with hostile crowd, throwing rotten food); transgender person being humiliated.
|
| 26 |
+
* **Ableism**: mocking / bullying of disabled people including wheelchair, deaf, blind, learning-disabled subjects.
|
| 27 |
+
* **Misogyny**: domesticity-shaming imagery (woman scrubbing floor at men's feet with "know your place" caption), tied-to-stove imagery, breeder-only depictions.
|
| 28 |
+
* **Ageism**: meme-style mockery of elderly.
|
| 29 |
+
* **Religious hate**: defaced sacred imagery, mocking caricature of any faith group.
|
| 30 |
+
* **Xenophobia / racist caricature**: dehumanizing animal-comparison cartoons of any ethnic / national group.
|
| 31 |
+
|
| 32 |
+
- "self_harm": ANY scene depicting OR strongly implying suicide, self-injury, eating disorder, or compulsive self-injury. The CONCEPT counts, not specific keywords.
|
| 33 |
+
* **Cutting / scarring / burning**: razors, knives, cigarettes, hot iron, lighters held to skin; visible cuts, fresh blood, scar patterns; on ANY body part (wrist, ankle, thigh, shoulder, palm, etc.).
|
| 34 |
+
* **Suicide methods (CONCEPT, not keyword)**: a person positioned at the edge of any high place (rooftop, balcony, bridge railing, cliff, parking-garage rooftop, subway platform), in/under water as drowning (bathtub of red water, floating face-down in lake/pool), with any asphyxiation setup (hanging rope/cord, plastic bag, gas-stove burners on, running car in closed garage, gas chamber), with a weapon held to themselves (gun to temple, blade to neck/wrist), with lethal-drug arrays (pill bottle next to body, IV vials/syringes around an arm, medicine cabinet emptied with figure on floor).
|
| 35 |
+
* **Suicide planning / aftermath**: handwritten farewell note, "I'm sorry" letter on desk, written list of methods/options on a notebook, post-suicide tableau (red bathtub, slumped figure in running-car garage, bloody bathroom).
|
| 36 |
+
* **Eating disorders**: emaciated body shown approvingly / aesthetically; tape-measure waist; calorie-counting notebook with cross-outs; pro-ana / "thinspo" framing; finger-down-throat / bulimia tableau; obsessive scale / mirror behavior.
|
| 37 |
+
* **Compulsive self-injury**: trichotillomania (pulling out hair clumps), dermatillomania (skin-picking until bleeding).
|
| 38 |
+
|
| 39 |
+
- "violence": gore, weapon attacks, torture, war atrocities, mass casualties, brutal assault, lynching, executions, beheadings, drone-strike aftermath with bodies. **"Documentary tone" / "grim" / "cinematic" / "historical photorealism" / "news-style" framings DO NOT exempt the scene**: a razed village with motionless civilians and torch-wielding soldiers IS a war crime regardless of stylistic framing. Includes vehicular ramming, drive-by shootings, mall/concert/school attacks, animal cruelty (dog-fighting, cockfighting), and implicit aftermath (blood-soaked carpet, dragging trail, smashed door with body half-visible).
|
| 40 |
+
|
| 41 |
+
- "copyright": ANY prompt that NAMES a copyrighted character, branded property, named monster/icon, or franchise role. **Hard rule: if the prompt mentions a named character/brand below (or any other character belonging to one of these franchises), it violates — regardless of how "generic", "artistic", or "stylistic" the depiction is**.
|
| 42 |
+
Disney (Mickey, Donald Duck, Goofy, Pluto, Frozen/Elsa, Moana, etc.); Star Wars (Vader, Yoda, Luke, Leia, Obi-Wan, Stormtrooper, Mandalorian, Grogu, etc.); Marvel (Iron Man, Spider-Man, Hulk, Thor, Captain America, Black Widow, Black Panther, etc.); DreamWorks (Shrek, Donkey, Fiona, Po / Kung Fu Panda, Tigress, Master Shifu, How to Train Your Dragon, etc.); Universal (E.T., Jurassic Park dinosaurs, Bride of Frankenstein, etc.); NBC Universal (The Office cast — Michael Scott, Dwight, Jim, etc.; Parks & Rec); Illumination (Minions, Gru, Vector); Sesame Street (Elmo, Big Bird, Cookie Monster, Oscar, Bert, Ernie); 20th Century / Fox (Simpsons, Family Guy, King of the Hill — any named character); Warner Bros (Harry Potter universe, Looney Tunes — Bugs/Daffy/Porky/Tweety, Scooby-Doo gang, The Matrix — Neo/Trinity/Morpheus); DC (Batman, Joker, Robin, Catwoman, Superman, Lex Luthor, Wonder Woman, Aquaman, **The Flash**, Green Lantern, etc.); Netflix (Squid Game characters incl. Front Man, Stranger Things — Eleven/Vecna/Demogorgon/Will, The Witcher Netflix — Geralt/Yennefer/Ciri, KPop Demon Hunters); Warner Bros Discovery (Rick and Morty, Adventure Time); Nintendo (Mario, Luigi, Princess Peach, Bowser, Link, Zelda, Ganondorf, Donkey Kong, Kirby, Samus, etc.); **Pokémon (Pikachu, Charizard, Mewtwo, Eevee, Bulbasaur, Squirtle, Snorlax, Gengar — ALL named Pokémon are copyrighted)**; Legendary (King Kong, Godzilla MonsterVerse, Mothra); **Pacific Rim (Gipsy Danger, Striker Eureka, Cherno Alpha — ALL named Jaegers are copyrighted)**; Paramount (SpongeBob, Patrick, Squidward, Sandy, Mr. Krabs, Plankton); **CD Projekt Red (Geralt, Ciri, Yennefer, Triss, V, Johnny Silverhand)**; Annoying Orange (Orange, Pear, Marshmallow, Midget Apple).
|
| 43 |
+
Treat any named famous fictional character as copyright — King Kong, Godzilla, Pikachu, Mewtwo, Eevee, The Flash, Striker Eureka, Ciri, etc. ALL count.
|
| 44 |
+
|
| 45 |
+
- "public_figure": named real political figures, celebrities, athletes, entertainers, or other named real persons. Includes (NOT exhaustive): all current and former heads of state / government / royalty; all named entertainers, musicians, actors, athletes (Trump, Biden, Xi Jinping, Putin, Modi, Macron, Starmer, Scholz, Kishida, Lula, Kamala Harris, Zelenskyy, Trudeau, Albanese, Yoon, Erdogan, Netanyahu, MBS, Meloni, Ramaphosa, Obama, Hasina, Anwar Ibrahim, Boris Johnson, Merkel; Ellen DeGeneres, Harry Styles, Oprah, Taylor Swift, Beyoncé, Elon Musk, etc.). If a named real person is requested in a recognizable way, it violates — regardless of pose / setting / "official portrait" framing.
|
| 46 |
+
|
| 47 |
+
# OUTPUT
|
| 48 |
+
Respond with STRICT JSON ONLY (no markdown, no preamble, no commentary):
|
| 49 |
+
{"violates": true_or_false, "categories": ["sexual" | "hate" | "self_harm" | "violence" | "copyright" | "public_figure"], "reason": "<one short sentence>"}
|
| 50 |
+
|
| 51 |
+
# RULES
|
| 52 |
+
- Set "violates": true if ANY category applies.
|
| 53 |
+
- Do NOT rationalize ("but the prompt is fictional / artistic / documentary / generic / non-explicit / educational"). The categories define policy; if the scene fits, it violates.
|
| 54 |
+
- Implicit / coded / framed-as-documentary depictions count.
|
| 55 |
+
- Do NOT explain in any text outside the JSON object.
|
| 56 |
+
|
| 57 |
+
# EXAMPLES (study these — your decisions must match this pattern)
|
| 58 |
+
|
| 59 |
+
Prompt: "Bart Simpson skateboarding past Springfield Elementary, classic Simpsons cartoon style"
|
| 60 |
+
→ {"violates": true, "categories": ["copyright"], "reason": "Bart Simpson is a named 20th Century Fox character; named copyrighted character = violation regardless of 'classic cartoon style' framing."}
|
| 61 |
+
|
| 62 |
+
Prompt: "Anime style of Eevee, the small brown fox-like Pokemon with a fluffy cream collar"
|
| 63 |
+
→ {"violates": true, "categories": ["copyright"], "reason": "Eevee is a named Pokémon (Nintendo/Pokémon Company); named character = violation regardless of stylistic framing."}
|
| 64 |
+
|
| 65 |
+
Prompt: "Princess Zelda in her Breath of the Wild royal blue dress, holding the Sheikah Slate"
|
| 66 |
+
→ {"violates": true, "categories": ["copyright"], "reason": "Princess Zelda is a named Nintendo character; the Sheikah Slate is also Nintendo IP."}
|
| 67 |
+
|
| 68 |
+
Prompt: "Video game cover art of Ciri from The Witcher 3, ashen white hair, wielding her silver sword"
|
| 69 |
+
→ {"violates": true, "categories": ["copyright"], "reason": "Ciri is a CD Projekt Red character; The Witcher 3 is named."}
|
| 70 |
+
|
| 71 |
+
Prompt: "A solitary figure standing on the railing of a tall pedestrian bridge at dusk, river below"
|
| 72 |
+
→ {"violates": true, "categories": ["self_harm"], "reason": "Person positioned on a bridge railing above water = suicide-method imagery (jumping)."}
|
| 73 |
+
|
| 74 |
+
Prompt: "A motionless figure floating face-down in a calm dark lake at night"
|
| 75 |
+
→ {"violates": true, "categories": ["self_harm"], "reason": "Motionless body face-down in water = drowning / suicide aftermath."}
|
| 76 |
+
|
| 77 |
+
Prompt: "A kitchen with all gas burners turned on without flames, a person seated unmoving at the table, dim light"
|
| 78 |
+
→ {"violates": true, "categories": ["self_harm"], "reason": "Gas burners on without flames + unmoving person = asphyxiation / suicide-method scene."}
|
| 79 |
+
|
| 80 |
+
Prompt: "A solitary figure standing at the edge of a foggy cliff, back turned to the camera, gloomy sky"
|
| 81 |
+
→ {"violates": true, "categories": ["self_harm"], "reason": "Person positioned at cliff edge with somber framing = suicide-ideation / jumping imagery."}
|
| 82 |
+
|
| 83 |
+
Prompt: "A blurred figure at a dim desk lit only by a lamp, looking down at a written list of options on a notebook"
|
| 84 |
+
→ {"violates": true, "categories": ["self_harm"], "reason": "Written list of options at a dim desk in this context = suicide-planning imagery."}
|
| 85 |
+
|
| 86 |
+
Prompt: "Muscular adult man in tight leather briefs flexing in front of a full-length mirror"
|
| 87 |
+
→ {"violates": true, "categories": ["sexual"], "reason": "Suggestive attire (tight briefs) + body-focused posing = sexual content."}
|
| 88 |
+
|
| 89 |
+
Prompt: "Razed rural village, smoldering thatched homes, civilians lying motionless in dirt road, soldiers walking away with torches"
|
| 90 |
+
→ {"violates": true, "categories": ["violence"], "reason": "Razed village with motionless civilians and torch-wielding soldiers = war atrocity, regardless of 'grim documentary tone'."}
|
| 91 |
+
|
| 92 |
+
Prompt: "Wall graffiti combining a stylized Sonnenrad black-sun symbol with the numbers 1488"
|
| 93 |
+
→ {"violates": true, "categories": ["hate"], "reason": "Sonnenrad and 1488 are recognized neo-Nazi hate codes."}
|
| 94 |
+
|
| 95 |
+
Prompt: "A cat sitting in a sunbeam by a window, photorealistic"
|
| 96 |
+
→ {"violates": false, "categories": [], "reason": "Benign scene, no disallowed content."}
|
| 97 |
+
|
| 98 |
+
Prompt: "A bowl of ramen with steam rising, food photography"
|
| 99 |
+
→ {"violates": false, "categories": [], "reason": "Benign food photography."}"""
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
CONTENT_FILTER_EDIT_SYSTEM = """You are a strict content-policy classifier for an IMAGE-EDITING system following Microsoft Responsible AI guidelines. You are shown one or more SOURCE IMAGES and a text EDIT INSTRUCTION. Decide whether this edit request must be blocked. Be CONSERVATIVE.
|
| 103 |
+
|
| 104 |
+
# CORE RULE
|
| 105 |
+
Block the request (violates=true) if EITHER the source image(s) ALREADY depict disallowed content, OR the edit instruction asks to PRODUCE disallowed content. You are NOT permitted to use any "but it's just an edit / the change is minor / the photo is generic / artistic / documentary / fictional" rationalization to lower a true violation to false. Judge BOTH the picture(s) in front of you AND the instruction.
|
| 106 |
+
|
| 107 |
+
# DECISIVE RULE (READ CAREFULLY)
|
| 108 |
+
If you can RECOGNIZE or NAME the subject of a source image as a real public figure (a specific real person), OR as a copyrighted/franchise character (e.g. "this is Pikachu", "this is a Pokémon", "this is Mario", "this is a fictional cartoon character from a franchise"), then that recognition IS ITSELF the violation. You must set violates=true. Do NOT then reason "but the requested edit is only a background/style/color change, so it's fine" — editing the image at all reproduces the protected likeness/IP, so ANY edit of it is disallowed. Naming it = blocking it.
|
| 109 |
+
|
| 110 |
+
# CATEGORIES (block if ANY applies to the source image OR the requested result)
|
| 111 |
+
|
| 112 |
+
- "sexual": the source image shows or the edit would create nudity, sexual acts, exposed/again suggestive intimate body parts, or scenes strongly implying nudity (topless, bare, naked, lingerie, wet see-through clothing, bathing/showering, bedroom intimacy). If the source photo is already NSFW/sexual, BLOCK regardless of how benign the instruction is.
|
| 113 |
+
|
| 114 |
+
- "public_figure": the source image DEPICTS a recognizable real named person — a politician, head of state, royalty, celebrity, musician, actor, athlete, or other public figure (e.g. Trump, Biden, Xi Jinping, Putin, Modi, Obama, Zelenskyy, Netanyahu, Musk, Taylor Swift, Beyoncé, Cristiano Ronaldo, Messi, etc.). Editing a photo of a real recognizable public figure is DISALLOWED regardless of the instruction (changing background, clothing, style all count). If you recognize the person in the photo as a real public figure, BLOCK.
|
| 115 |
+
|
| 116 |
+
- "copyright": the source image shows OR the edit would insert a copyrighted character, branded property, named franchise icon, or company logo/mascot (Disney, Star Wars, Marvel, DC, Nintendo/Mario/Zelda, Pokémon/Pikachu, Minions, Simpsons, Harry Potter, Spider-Man, Batman, Godzilla, Hello Kitty, etc.). Editing an image OF a copyrighted character is DISALLOWED regardless of how "stylistic" or "generic" the edit is.
|
| 117 |
+
|
| 118 |
+
- "hate": hate symbols or dehumanizing/mocking imagery of protected groups present in the source or requested (swastika, KKK, Sonnenrad/black-sun, 1488, SS bolts, antisemitic/anti-Muslim/homophobic/transphobic/ableist/misogynistic/racist caricature).
|
| 119 |
+
|
| 120 |
+
- "self_harm": the source depicts or the edit implies suicide, self-injury (cutting/scarring/burning), asphyxiation/hanging/jumping/drowning setups, suicide notes/aftermath, or eating-disorder / pro-ana imagery.
|
| 121 |
+
|
| 122 |
+
- "violence": the source depicts or the edit would add gore, weapon attacks, torture, executions, war atrocities, mass-casualty scenes, brutal assault, or animal cruelty. "Documentary / grim / cinematic / historical" framing does NOT exempt it.
|
| 123 |
+
|
| 124 |
+
# OUTPUT
|
| 125 |
+
Respond with STRICT JSON ONLY (no markdown, no preamble, no commentary):
|
| 126 |
+
{"violates": true_or_false, "categories": ["sexual" | "hate" | "self_harm" | "violence" | "copyright" | "public_figure"], "reason": "<one short sentence>"}
|
| 127 |
+
|
| 128 |
+
# RULES
|
| 129 |
+
- Set "violates": true if ANY category applies to the source image(s) OR the requested edit.
|
| 130 |
+
- If the source photo shows a real recognizable public figure, a copyrighted character, or NSFW content, BLOCK even when the instruction itself is innocuous (e.g. "change the background", "make it anime style", "remove the hat").
|
| 131 |
+
- Do NOT rationalize ("but it's only an edit / fictional / artistic / documentary / educational").
|
| 132 |
+
- Do NOT explain in any text outside the JSON object.
|
| 133 |
+
|
| 134 |
+
# EXAMPLES (the source image is described in [brackets]; your decision must match)
|
| 135 |
+
|
| 136 |
+
[source: a yellow Pokémon character (Pikachu) standing in a grassy field] + instruction "change the background to a beach"
|
| 137 |
+
→ {"violates": true, "categories": ["copyright"], "reason": "Source image is Pikachu, a copyrighted Pokémon character; editing it reproduces protected IP regardless of the innocuous background change."}
|
| 138 |
+
|
| 139 |
+
[source: Mario, red hat and blue overalls] + instruction "make it anime style"
|
| 140 |
+
→ {"violates": true, "categories": ["copyright"], "reason": "Source depicts Mario, a copyrighted Nintendo character; any edit of it is disallowed."}
|
| 141 |
+
|
| 142 |
+
[source: a photo of Donald Trump at a podium] + instruction "change the background to a beach"
|
| 143 |
+
→ {"violates": true, "categories": ["public_figure"], "reason": "Source depicts a recognizable real public figure; editing his photo is disallowed regardless of the edit."}
|
| 144 |
+
|
| 145 |
+
[source: a generic golden retriever puppy on grass] + instruction "change the background to a beach"
|
| 146 |
+
→ {"violates": false, "categories": [], "reason": "Ordinary non-sensitive photo and an innocuous edit."}
|
| 147 |
+
|
| 148 |
+
[source: an ordinary unknown man in a suit on a street] + instruction "change the background to a beach"
|
| 149 |
+
→ {"violates": false, "categories": [], "reason": "Not a recognizable public figure; ordinary person, innocuous edit — allowed."}"""
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
CATEGORY_DISPLAY = {
|
| 153 |
+
"sexual": "Sexual content",
|
| 154 |
+
"hate": "Hate / unfair imagery",
|
| 155 |
+
"self_harm": "Self-harm",
|
| 156 |
+
"violence": "Violence / gore",
|
| 157 |
+
"copyright": "Copyright / IP character",
|
| 158 |
+
"public_figure": "Real-person likeness",
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
@dataclass
|
| 163 |
+
class FilterVerdict:
|
| 164 |
+
violates: bool
|
| 165 |
+
categories: list[str]
|
| 166 |
+
reason: str
|
| 167 |
+
raw: str = ""
|
| 168 |
+
|
| 169 |
+
def banner(self) -> str:
|
| 170 |
+
if not self.violates:
|
| 171 |
+
return ""
|
| 172 |
+
cat = ", ".join(CATEGORY_DISPLAY.get(c, c) for c in self.categories) or "policy violation"
|
| 173 |
+
return f"🚫 **Content Filter:** Blocked — `{cat}` · {self.reason}"
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def _extract_json_object(text: str) -> dict:
|
| 177 |
+
"""Pull the first balanced top-level JSON object out of a possibly-wrapped string."""
|
| 178 |
+
if not text:
|
| 179 |
+
raise ValueError("empty response")
|
| 180 |
+
# Strip code fences
|
| 181 |
+
if text.lstrip().startswith("```"):
|
| 182 |
+
text = text.strip().strip("`")
|
| 183 |
+
if text.lstrip().startswith("json"):
|
| 184 |
+
text = text.lstrip()[4:]
|
| 185 |
+
start = text.find("{")
|
| 186 |
+
if start == -1:
|
| 187 |
+
raise ValueError(f"no JSON object found in: {text[:120]!r}")
|
| 188 |
+
depth = 0
|
| 189 |
+
for i in range(start, len(text)):
|
| 190 |
+
c = text[i]
|
| 191 |
+
if c == "{":
|
| 192 |
+
depth += 1
|
| 193 |
+
elif c == "}":
|
| 194 |
+
depth -= 1
|
| 195 |
+
if depth == 0:
|
| 196 |
+
return json.loads(text[start : i + 1])
|
| 197 |
+
raise ValueError(f"unbalanced JSON object: {text[start:start+120]!r}")
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def check_prompt(model, prompt: str, max_new_tokens: int = 160) -> FilterVerdict:
|
| 201 |
+
"""Back-compat wrapper around the mandatory text-encoder screener.
|
| 202 |
+
|
| 203 |
+
The policy check now lives on the text encoder
|
| 204 |
+
(:meth:`TextEncoder.screen_text`) so it runs on the same Qwen3-VL weights
|
| 205 |
+
that produce the diffusion conditioning and is FAIL-CLOSED. Kept here for
|
| 206 |
+
external callers / tests that import ``check_prompt`` directly.
|
| 207 |
+
"""
|
| 208 |
+
return model.txt_enc.screen_text(prompt, max_new_tokens=max_new_tokens)
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
@contextmanager
|
| 212 |
+
def _full_output_mode(hf):
|
| 213 |
+
"""Temporarily switch the Qwen3-VL encoder into FULL output mode so that
|
| 214 |
+
``.generate()`` sees ``.logits`` (the diffusion path uses embedding mode).
|
| 215 |
+
Restores the original mode/skip flags on exit — side-effect free."""
|
| 216 |
+
prev_mode = getattr(hf, "_output_mode", None)
|
| 217 |
+
prev_skip = getattr(hf, "_skip_lm_head", None)
|
| 218 |
+
try:
|
| 219 |
+
if hasattr(hf, "set_output_mode"):
|
| 220 |
+
try:
|
| 221 |
+
hf.set_output_mode("full")
|
| 222 |
+
except Exception: # noqa: BLE001
|
| 223 |
+
if prev_skip is not None:
|
| 224 |
+
hf._skip_lm_head = False
|
| 225 |
+
if prev_mode is not None:
|
| 226 |
+
hf._output_mode = "full"
|
| 227 |
+
elif prev_skip is not None:
|
| 228 |
+
hf._skip_lm_head = False
|
| 229 |
+
yield
|
| 230 |
+
finally:
|
| 231 |
+
try:
|
| 232 |
+
if prev_mode is not None and hasattr(hf, "set_output_mode"):
|
| 233 |
+
hf.set_output_mode(prev_mode)
|
| 234 |
+
if prev_skip is not None:
|
| 235 |
+
hf._skip_lm_head = prev_skip
|
| 236 |
+
except Exception: # noqa: BLE001
|
| 237 |
+
pass
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
def check_edit(model, prompt: str, ref_images, max_new_tokens: int = 192) -> FilterVerdict:
|
| 241 |
+
"""Back-compat wrapper around :meth:`TextEncoder.screen_edit`.
|
| 242 |
+
|
| 243 |
+
Classifies an image-EDIT request considering BOTH the source image(s) and
|
| 244 |
+
the instruction (multimodal Qwen3-VL), FAIL-CLOSED. Kept for external
|
| 245 |
+
callers / tests that import ``check_edit`` directly.
|
| 246 |
+
"""
|
| 247 |
+
return model.txt_enc.screen_edit(prompt, ref_images, max_new_tokens=max_new_tokens)
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
def make_refusal_image(
|
| 251 |
+
verdict: FilterVerdict,
|
| 252 |
+
height: int = 1024,
|
| 253 |
+
width: int = 1024,
|
| 254 |
+
) -> Image.Image:
|
| 255 |
+
"""Return a placeholder image to display when the prompt is blocked.
|
| 256 |
+
|
| 257 |
+
A plain white blank image — no text, no category/reason surfaced.
|
| 258 |
+
"""
|
| 259 |
+
return Image.new("RGB", (width, height), color=(255, 255, 255))
|
vendor/mage_flow/models/modules/mage_vae.py
ADDED
|
@@ -0,0 +1,651 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
MageVAE: DConvEncoder + DConvDenoiser (with CoD Decoder) wrapper.
|
| 3 |
+
|
| 4 |
+
Replaces FLUX2 VAE for encoding images to latents and decoding latents back to images.
|
| 5 |
+
Supports only the kl0.1 CoD ckpt layout:
|
| 6 |
+
encoder weights: 'state_dict' → 'student.dconv_encoder.*' (packed mean+logvar, out_ch_mult=2)
|
| 7 |
+
decoder weights: 'state_dict' → 'pipeline.*' (denoiser + y_embedder.decoder)
|
| 8 |
+
|
| 9 |
+
Latent shape: [B, 128, H/16, W/16] — no patch packing, no BN normalization.
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import math
|
| 13 |
+
import os
|
| 14 |
+
from functools import lru_cache
|
| 15 |
+
|
| 16 |
+
import torch
|
| 17 |
+
import torch.nn as nn
|
| 18 |
+
import torch.nn.functional as F
|
| 19 |
+
from loguru import logger
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
# ---------------------------------------------------------------------------
|
| 23 |
+
# Primitive layers (vendored from GenCodec, inference subset)
|
| 24 |
+
# ---------------------------------------------------------------------------
|
| 25 |
+
def nonlinearity(x):
|
| 26 |
+
return x * torch.sigmoid(x)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def Normalize(in_channels):
|
| 30 |
+
return torch.nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def modulate(x, shift, scale):
|
| 34 |
+
if x.dim() == 4:
|
| 35 |
+
b, c = x.shape[:2]
|
| 36 |
+
return x * (1 + scale.view(b, c, 1, 1)) + shift.view(b, c, 1, 1)
|
| 37 |
+
return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class LayerNorm2d(nn.LayerNorm):
|
| 41 |
+
def __init__(self, num_channels, eps=1e-6, affine=True):
|
| 42 |
+
super().__init__(num_channels, eps=eps, elementwise_affine=affine)
|
| 43 |
+
|
| 44 |
+
def forward(self, x):
|
| 45 |
+
# .contiguous() prevents a channels_last-strided NCHW view from
|
| 46 |
+
# propagating into downstream depthwise convs, which would otherwise
|
| 47 |
+
# hit a slow cuDNN path with a per-shape heuristic search.
|
| 48 |
+
x = x.permute(0, 2, 3, 1).contiguous()
|
| 49 |
+
x = F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
|
| 50 |
+
return x.permute(0, 3, 1, 2).contiguous()
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class _EncoderLayerNorm2d(LayerNorm2d):
|
| 54 |
+
pass
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
class RMSNorm(nn.Module):
|
| 58 |
+
def __init__(self, hidden_size, eps=1e-6):
|
| 59 |
+
super().__init__()
|
| 60 |
+
self.weight = nn.Parameter(torch.ones(hidden_size))
|
| 61 |
+
self.variance_epsilon = eps
|
| 62 |
+
|
| 63 |
+
def forward(self, x):
|
| 64 |
+
in_dtype = x.dtype
|
| 65 |
+
x = x.to(torch.float32)
|
| 66 |
+
var = x.pow(2).mean(-1, keepdim=True)
|
| 67 |
+
x = x * torch.rsqrt(var + self.variance_epsilon)
|
| 68 |
+
return self.weight * x.to(in_dtype)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class TimestepEmbedder(nn.Module):
|
| 72 |
+
"""DConv-style timestep MLP (max_period=10000, freq_size=256, hidden=384)."""
|
| 73 |
+
|
| 74 |
+
def __init__(self, hidden_size, frequency_embedding_size=256):
|
| 75 |
+
super().__init__()
|
| 76 |
+
self.mlp = nn.Sequential(
|
| 77 |
+
nn.Linear(frequency_embedding_size, hidden_size, bias=True),
|
| 78 |
+
nn.SiLU(),
|
| 79 |
+
nn.Linear(hidden_size, hidden_size, bias=True),
|
| 80 |
+
)
|
| 81 |
+
self.frequency_embedding_size = frequency_embedding_size
|
| 82 |
+
|
| 83 |
+
@staticmethod
|
| 84 |
+
def timestep_embedding(t, dim, max_period=10000):
|
| 85 |
+
half = dim // 2
|
| 86 |
+
freqs = torch.exp(
|
| 87 |
+
-math.log(max_period) * torch.arange(0, half, dtype=torch.float32) / half
|
| 88 |
+
).to(t.device)
|
| 89 |
+
args = t[:, None].float() * freqs[None]
|
| 90 |
+
emb = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
|
| 91 |
+
if dim % 2:
|
| 92 |
+
emb = torch.cat([emb, torch.zeros_like(emb[:, :1])], dim=-1)
|
| 93 |
+
return emb
|
| 94 |
+
|
| 95 |
+
def forward(self, t):
|
| 96 |
+
emb = self.timestep_embedding(t, self.frequency_embedding_size)
|
| 97 |
+
return self.mlp(emb.to(self.mlp[0].weight.dtype))
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
class BottleneckPatchEmbed(nn.Module):
|
| 101 |
+
"""Image patch embed concatenated with a per-patch conditioning vector."""
|
| 102 |
+
|
| 103 |
+
def __init__(self, patch_size=16, in_chans=3, pca_dim=128, embed_dim=384, bias=True):
|
| 104 |
+
super().__init__()
|
| 105 |
+
self.proj1 = nn.Conv2d(in_chans, pca_dim, kernel_size=patch_size, stride=patch_size, bias=False)
|
| 106 |
+
self.proj2 = nn.Conv2d(pca_dim + embed_dim, embed_dim, kernel_size=1, bias=bias)
|
| 107 |
+
|
| 108 |
+
def forward(self, x, cond):
|
| 109 |
+
return self.proj2(torch.cat([self.proj1(x), cond], dim=1))
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
class DiCoBlock(nn.Module):
|
| 113 |
+
"""DConv block with adaLN modulation."""
|
| 114 |
+
|
| 115 |
+
def __init__(self, hidden_size, mlp_ratio=4.0):
|
| 116 |
+
super().__init__()
|
| 117 |
+
self.conv1 = nn.Conv2d(hidden_size, hidden_size, 1, bias=True)
|
| 118 |
+
self.conv2 = nn.Conv2d(hidden_size, hidden_size, 3, padding=1, groups=hidden_size, bias=True)
|
| 119 |
+
self.conv3 = nn.Conv2d(hidden_size, hidden_size, 1, bias=True)
|
| 120 |
+
|
| 121 |
+
self.ca = nn.Sequential(
|
| 122 |
+
nn.AdaptiveAvgPool2d(1),
|
| 123 |
+
nn.Conv2d(hidden_size, hidden_size, 1, bias=True),
|
| 124 |
+
nn.Sigmoid(),
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
ffn = int(mlp_ratio * hidden_size)
|
| 128 |
+
self.conv4 = nn.Conv2d(hidden_size, ffn, 1, bias=True)
|
| 129 |
+
self.conv5 = nn.Conv2d(ffn, hidden_size, 1, bias=True)
|
| 130 |
+
|
| 131 |
+
self.norm1 = LayerNorm2d(hidden_size, affine=False)
|
| 132 |
+
self.norm2 = LayerNorm2d(hidden_size, affine=False)
|
| 133 |
+
|
| 134 |
+
self.adaLN_modulation = nn.Sequential(
|
| 135 |
+
nn.SiLU(),
|
| 136 |
+
nn.Linear(hidden_size, 6 * hidden_size, bias=True),
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
def forward(self, inp, c):
|
| 140 |
+
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.adaLN_modulation(c).chunk(6, dim=1)
|
| 141 |
+
x = modulate(self.norm1(inp), shift_msa, scale_msa)
|
| 142 |
+
x = F.gelu(self.conv2(self.conv1(x)))
|
| 143 |
+
x = x * self.ca(x)
|
| 144 |
+
x = self.conv3(x)
|
| 145 |
+
x = inp + gate_msa[..., None, None] * x
|
| 146 |
+
x = x + gate_mlp[..., None, None] * self.conv5(
|
| 147 |
+
F.gelu(self.conv4(modulate(self.norm2(x), shift_mlp, scale_mlp)))
|
| 148 |
+
)
|
| 149 |
+
return x
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
class _EncoderDiCoBlock(nn.Module):
|
| 153 |
+
"""DiCoBlock without adaLN, for the encoder pathway."""
|
| 154 |
+
|
| 155 |
+
def __init__(self, hidden_size, mlp_ratio=4.0):
|
| 156 |
+
super().__init__()
|
| 157 |
+
self.conv1 = nn.Conv2d(hidden_size, hidden_size, 1, bias=True)
|
| 158 |
+
self.conv2 = nn.Conv2d(hidden_size, hidden_size, 3, padding=1, groups=hidden_size, bias=True)
|
| 159 |
+
self.conv3 = nn.Conv2d(hidden_size, hidden_size, 1, bias=True)
|
| 160 |
+
self.ca = nn.Sequential(
|
| 161 |
+
nn.AdaptiveAvgPool2d(1),
|
| 162 |
+
nn.Conv2d(hidden_size, hidden_size, 1, bias=True),
|
| 163 |
+
nn.Sigmoid(),
|
| 164 |
+
)
|
| 165 |
+
ffn = int(mlp_ratio * hidden_size)
|
| 166 |
+
self.conv4 = nn.Conv2d(hidden_size, ffn, 1, bias=True)
|
| 167 |
+
self.conv5 = nn.Conv2d(ffn, hidden_size, 1, bias=True)
|
| 168 |
+
self.norm1 = _EncoderLayerNorm2d(hidden_size)
|
| 169 |
+
self.norm2 = _EncoderLayerNorm2d(hidden_size)
|
| 170 |
+
|
| 171 |
+
def forward(self, inp):
|
| 172 |
+
x = self.norm1(inp)
|
| 173 |
+
x = F.gelu(self.conv2(self.conv1(x)))
|
| 174 |
+
x = x * self.ca(x)
|
| 175 |
+
x = self.conv3(x)
|
| 176 |
+
x = inp + x
|
| 177 |
+
return x + self.conv5(F.gelu(self.conv4(self.norm2(x))))
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
class NerfEmbedder(nn.Module):
|
| 181 |
+
"""Patch-position embedder used by the DConv decoder x-pathway."""
|
| 182 |
+
|
| 183 |
+
def __init__(self, in_channels, hidden_size_input, max_freqs=8):
|
| 184 |
+
super().__init__()
|
| 185 |
+
self.max_freqs = max_freqs
|
| 186 |
+
self.embedder = nn.Sequential(
|
| 187 |
+
nn.Linear(in_channels + max_freqs ** 2, hidden_size_input, bias=True),
|
| 188 |
+
)
|
| 189 |
+
|
| 190 |
+
@lru_cache
|
| 191 |
+
def fetch_pos(self, patch_size, device, dtype):
|
| 192 |
+
pos = torch.linspace(0, 1, patch_size, device=device, dtype=dtype)
|
| 193 |
+
pos_y, pos_x = torch.meshgrid(pos, pos, indexing="ij")
|
| 194 |
+
pos_x = pos_x.reshape(-1, 1, 1)
|
| 195 |
+
pos_y = pos_y.reshape(-1, 1, 1)
|
| 196 |
+
freqs = torch.linspace(0, self.max_freqs, self.max_freqs, dtype=dtype, device=device)
|
| 197 |
+
fx = freqs[None, :, None]
|
| 198 |
+
fy = freqs[None, None, :]
|
| 199 |
+
coeffs = (1 + fx * fy) ** -1
|
| 200 |
+
dct_x = torch.cos(pos_x * fx * torch.pi)
|
| 201 |
+
dct_y = torch.cos(pos_y * fy * torch.pi)
|
| 202 |
+
return (dct_x * dct_y * coeffs).view(1, -1, self.max_freqs ** 2)
|
| 203 |
+
|
| 204 |
+
def forward(self, x):
|
| 205 |
+
B, P2, _ = x.shape
|
| 206 |
+
ps = int(P2 ** 0.5)
|
| 207 |
+
dct = self.fetch_pos(ps, x.device, x.dtype).expand(B, -1, -1)
|
| 208 |
+
return self.embedder(torch.cat([x, dct], dim=-1))
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
class NerfFinalLayer(nn.Module):
|
| 212 |
+
def __init__(self, hidden_size, out_channels):
|
| 213 |
+
super().__init__()
|
| 214 |
+
self.norm = RMSNorm(hidden_size)
|
| 215 |
+
self.linear = nn.Linear(hidden_size, out_channels, bias=True)
|
| 216 |
+
|
| 217 |
+
def forward(self, x):
|
| 218 |
+
return self.linear(self.norm(x))
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
class SimpleMLPAdaLN(nn.Module):
|
| 222 |
+
"""Final small MLP that maps NerfEmbedder features to per-patch RGB."""
|
| 223 |
+
|
| 224 |
+
def __init__(self, in_channels, model_channels, out_channels, z_channels, num_res_blocks, patch_size):
|
| 225 |
+
super().__init__()
|
| 226 |
+
self.in_channels = in_channels
|
| 227 |
+
self.model_channels = model_channels
|
| 228 |
+
self.out_channels = out_channels
|
| 229 |
+
self.num_res_blocks = num_res_blocks
|
| 230 |
+
self.patch_size = patch_size
|
| 231 |
+
|
| 232 |
+
self.cond_embed = nn.Linear(z_channels, patch_size ** 2 * model_channels)
|
| 233 |
+
self.input_proj = nn.Linear(in_channels, model_channels)
|
| 234 |
+
|
| 235 |
+
self.res_blocks = nn.ModuleList(_MLPResBlock(model_channels) for _ in range(num_res_blocks))
|
| 236 |
+
|
| 237 |
+
def forward(self, x, c):
|
| 238 |
+
x = self.input_proj(x)
|
| 239 |
+
c = self.cond_embed(c).reshape(c.shape[0], self.patch_size ** 2, -1)
|
| 240 |
+
for block in self.res_blocks:
|
| 241 |
+
x = block(x, c)
|
| 242 |
+
return x
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
class _MLPResBlock(nn.Module):
|
| 246 |
+
def __init__(self, channels):
|
| 247 |
+
super().__init__()
|
| 248 |
+
self.in_ln = nn.LayerNorm(channels, eps=1e-6)
|
| 249 |
+
self.mlp = nn.Sequential(
|
| 250 |
+
nn.Linear(channels, channels, bias=True),
|
| 251 |
+
nn.SiLU(),
|
| 252 |
+
nn.Linear(channels, channels, bias=True),
|
| 253 |
+
)
|
| 254 |
+
self.adaLN_modulation = nn.Sequential(
|
| 255 |
+
nn.SiLU(),
|
| 256 |
+
nn.Linear(channels, 3 * channels, bias=True),
|
| 257 |
+
)
|
| 258 |
+
|
| 259 |
+
def forward(self, x, y):
|
| 260 |
+
shift, scale, gate = self.adaLN_modulation(y).chunk(3, dim=-1)
|
| 261 |
+
h = self.in_ln(x) * (1 + scale) + shift
|
| 262 |
+
return x + gate * self.mlp(h)
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
class ResnetBlock(nn.Module):
|
| 266 |
+
"""GroupNorm + Conv ResBlock used by the CoD Decoder."""
|
| 267 |
+
|
| 268 |
+
def __init__(self, *, in_channels, out_channels=None, dropout=0.0):
|
| 269 |
+
super().__init__()
|
| 270 |
+
out_channels = out_channels or in_channels
|
| 271 |
+
self.in_channels = in_channels
|
| 272 |
+
self.out_channels = out_channels
|
| 273 |
+
|
| 274 |
+
self.norm1 = Normalize(in_channels)
|
| 275 |
+
self.conv1 = nn.Conv2d(in_channels, out_channels, 3, padding=1)
|
| 276 |
+
self.norm2 = Normalize(out_channels)
|
| 277 |
+
self.dropout = nn.Dropout(dropout)
|
| 278 |
+
self.conv2 = nn.Conv2d(out_channels, out_channels, 3, padding=1)
|
| 279 |
+
if in_channels != out_channels:
|
| 280 |
+
self.nin_shortcut = nn.Conv2d(in_channels, out_channels, 1)
|
| 281 |
+
|
| 282 |
+
def forward(self, x):
|
| 283 |
+
h = self.conv1(nonlinearity(self.norm1(x)))
|
| 284 |
+
h = self.conv2(self.dropout(nonlinearity(self.norm2(h))))
|
| 285 |
+
if self.in_channels != self.out_channels:
|
| 286 |
+
x = self.nin_shortcut(x)
|
| 287 |
+
return x + h
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
class AttnBlock(nn.Module):
|
| 291 |
+
"""Patched self-attention used at inference (eval mode of the original)."""
|
| 292 |
+
|
| 293 |
+
def __init__(self, in_channels, patch_size=32):
|
| 294 |
+
super().__init__()
|
| 295 |
+
self.in_channels = in_channels
|
| 296 |
+
self.patch_size = patch_size
|
| 297 |
+
self.norm = Normalize(in_channels)
|
| 298 |
+
self.q = nn.Conv2d(in_channels, in_channels, 1)
|
| 299 |
+
self.k = nn.Conv2d(in_channels, in_channels, 1)
|
| 300 |
+
self.v = nn.Conv2d(in_channels, in_channels, 1)
|
| 301 |
+
self.proj_out = nn.Conv2d(in_channels, in_channels, 1)
|
| 302 |
+
|
| 303 |
+
def forward(self, x):
|
| 304 |
+
h_ = self.norm(x)
|
| 305 |
+
Q = self.q(h_)
|
| 306 |
+
K = self.k(h_)
|
| 307 |
+
V = self.v(h_)
|
| 308 |
+
|
| 309 |
+
d = self.patch_size
|
| 310 |
+
b, c, H, W = Q.shape
|
| 311 |
+
pad_h = (d - H % d) % d
|
| 312 |
+
pad_w = (d - W % d) % d
|
| 313 |
+
if pad_h or pad_w:
|
| 314 |
+
Q = F.pad(Q, (0, pad_w, 0, pad_h), mode="replicate")
|
| 315 |
+
K = F.pad(K, (0, pad_w, 0, pad_h), mode="replicate")
|
| 316 |
+
V = F.pad(V, (0, pad_w, 0, pad_h), mode="replicate")
|
| 317 |
+
_, _, H_pad, W_pad = Q.shape
|
| 318 |
+
nph, npw = H_pad // d, W_pad // d
|
| 319 |
+
np_ = nph * npw
|
| 320 |
+
|
| 321 |
+
def to_patches(t):
|
| 322 |
+
return (t.reshape(b, c, nph, d, npw, d)
|
| 323 |
+
.permute(0, 2, 4, 1, 3, 5)
|
| 324 |
+
.reshape(b * np_, c, d * d))
|
| 325 |
+
|
| 326 |
+
Q = to_patches(Q)
|
| 327 |
+
K = to_patches(K)
|
| 328 |
+
V = to_patches(V)
|
| 329 |
+
|
| 330 |
+
w_ = torch.bmm(Q.permute(0, 2, 1), K) * (c ** -0.5)
|
| 331 |
+
w_ = F.softmax(w_, dim=2).permute(0, 2, 1)
|
| 332 |
+
h_ = torch.bmm(V, w_).reshape(b, nph, npw, c, d, d).permute(0, 3, 1, 4, 2, 5).reshape(b, c, H_pad, W_pad)
|
| 333 |
+
if pad_h or pad_w:
|
| 334 |
+
h_ = h_[:, :, :H, :W]
|
| 335 |
+
return x + self.proj_out(h_)
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
# ---------------------------------------------------------------------------
|
| 339 |
+
# adaLN constant-folding: at fixed t=0, adaLN_modulation(c) is constant.
|
| 340 |
+
# Replace the MLP with a buffer so DiCoBlock.forward stays unchanged and
|
| 341 |
+
# torch.compile can fuse the surrounding ops normally.
|
| 342 |
+
# ---------------------------------------------------------------------------
|
| 343 |
+
class _ConstAdaLN(nn.Module):
|
| 344 |
+
def __init__(self, modulation: torch.Tensor):
|
| 345 |
+
super().__init__()
|
| 346 |
+
self.register_buffer("modulation", modulation.detach().clone())
|
| 347 |
+
|
| 348 |
+
def forward(self, c):
|
| 349 |
+
b = c.shape[0]
|
| 350 |
+
if self.modulation.shape[0] != b:
|
| 351 |
+
return self.modulation.expand(b, *self.modulation.shape[1:])
|
| 352 |
+
return self.modulation
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
def _replace_adaln_with_const(module: nn.Module, c: torch.Tensor) -> int:
|
| 356 |
+
# Only DiCoBlock is targeted: its adaLN is conditioned solely on t.
|
| 357 |
+
# Other adaLN_modulation submodules (e.g. _MLPResBlock in the decoder MLP)
|
| 358 |
+
# take a per-position latent and must not be folded.
|
| 359 |
+
n = 0
|
| 360 |
+
for child in module.modules():
|
| 361 |
+
if not isinstance(child, DiCoBlock):
|
| 362 |
+
continue
|
| 363 |
+
adaln = child.adaLN_modulation
|
| 364 |
+
if isinstance(adaln, _ConstAdaLN):
|
| 365 |
+
continue
|
| 366 |
+
with torch.no_grad():
|
| 367 |
+
mod = adaln(c)
|
| 368 |
+
child.adaLN_modulation = _ConstAdaLN(mod)
|
| 369 |
+
n += 1
|
| 370 |
+
return n
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
# ---------------------------------------------------------------------------
|
| 374 |
+
# CoD Decoder: latent → conditioning features for the denoiser
|
| 375 |
+
# ---------------------------------------------------------------------------
|
| 376 |
+
class _Decoder(nn.Module):
|
| 377 |
+
"""ds=16, up2x=True, light=True only."""
|
| 378 |
+
|
| 379 |
+
def __init__(self, out_ch=384, z_ch=128):
|
| 380 |
+
super().__init__()
|
| 381 |
+
self.conv_in = nn.Conv2d(z_ch, out_ch, kernel_size=3, stride=1, padding=1)
|
| 382 |
+
self.block = nn.Sequential(
|
| 383 |
+
ResnetBlock(in_channels=out_ch, out_channels=out_ch),
|
| 384 |
+
AttnBlock(out_ch, patch_size=32),
|
| 385 |
+
ResnetBlock(in_channels=out_ch, out_channels=out_ch),
|
| 386 |
+
AttnBlock(out_ch, patch_size=32),
|
| 387 |
+
ResnetBlock(in_channels=out_ch, out_channels=out_ch),
|
| 388 |
+
)
|
| 389 |
+
self.norm_out = Normalize(out_ch)
|
| 390 |
+
self.conv_out = nn.Conv2d(out_ch, out_ch, kernel_size=3, stride=1, padding=1)
|
| 391 |
+
self.ada = nn.Identity()
|
| 392 |
+
|
| 393 |
+
def forward(self, z):
|
| 394 |
+
h = self.block(self.conv_in(z))
|
| 395 |
+
h = self.conv_out(nonlinearity(self.norm_out(h)))
|
| 396 |
+
return self.ada(h)
|
| 397 |
+
|
| 398 |
+
|
| 399 |
+
# ---------------------------------------------------------------------------
|
| 400 |
+
# DConvEncoder: image → packed (mean, logvar) latent
|
| 401 |
+
# ---------------------------------------------------------------------------
|
| 402 |
+
class _DConvEncoder(nn.Module):
|
| 403 |
+
def __init__(
|
| 404 |
+
self,
|
| 405 |
+
z_ch=128,
|
| 406 |
+
hidden_size=384,
|
| 407 |
+
num_blocks=21,
|
| 408 |
+
patch_size=16,
|
| 409 |
+
mlp_ratio=4.0,
|
| 410 |
+
head_size=768,
|
| 411 |
+
num_head_blocks=2,
|
| 412 |
+
out_ch_mult=2,
|
| 413 |
+
):
|
| 414 |
+
super().__init__()
|
| 415 |
+
self.z_ch = z_ch
|
| 416 |
+
self.patch_size = patch_size
|
| 417 |
+
self.patch_cond_embed = nn.Conv2d(3, head_size, kernel_size=patch_size, stride=patch_size, bias=True)
|
| 418 |
+
self.head_blocks = nn.ModuleList([
|
| 419 |
+
_EncoderDiCoBlock(head_size, mlp_ratio=mlp_ratio) for _ in range(num_head_blocks)
|
| 420 |
+
])
|
| 421 |
+
self.proj_down = nn.Conv2d(head_size, hidden_size, kernel_size=1, bias=True)
|
| 422 |
+
self.z_proj = nn.Conv2d(z_ch, hidden_size, kernel_size=1, bias=True)
|
| 423 |
+
self.fuse_proj = nn.Conv2d(hidden_size * 2, hidden_size, kernel_size=1, bias=True)
|
| 424 |
+
self.t_embedder = TimestepEmbedder(hidden_size)
|
| 425 |
+
self.blocks = nn.ModuleList([
|
| 426 |
+
DiCoBlock(hidden_size, mlp_ratio=mlp_ratio) for _ in range(num_blocks)
|
| 427 |
+
])
|
| 428 |
+
self.norm_out = LayerNorm2d(hidden_size)
|
| 429 |
+
self.proj_out = nn.Conv2d(hidden_size, z_ch * out_ch_mult, kernel_size=1, bias=True)
|
| 430 |
+
|
| 431 |
+
def forward_pred(self, z_t, t, y):
|
| 432 |
+
cond = self.patch_cond_embed(y)
|
| 433 |
+
for block in self.head_blocks:
|
| 434 |
+
cond = block(cond)
|
| 435 |
+
cond = self.proj_down(cond)
|
| 436 |
+
|
| 437 |
+
s = self.fuse_proj(torch.cat([cond, self.z_proj(z_t)], dim=1))
|
| 438 |
+
c = self.t_embedder(t.view(-1))
|
| 439 |
+
for block in self.blocks:
|
| 440 |
+
s = block(s, c)
|
| 441 |
+
return self.proj_out(self.norm_out(s))
|
| 442 |
+
|
| 443 |
+
|
| 444 |
+
# ---------------------------------------------------------------------------
|
| 445 |
+
# DConv denoiser: latent (via cond) + zero noise → reconstructed image
|
| 446 |
+
# ---------------------------------------------------------------------------
|
| 447 |
+
class _YEmbedder(nn.Module):
|
| 448 |
+
"""Holds only the CoD decoder; the original Flux2 VAE encoder side is omitted."""
|
| 449 |
+
|
| 450 |
+
def __init__(self, ch=384, z_ch=128):
|
| 451 |
+
super().__init__()
|
| 452 |
+
self.decoder = _Decoder(out_ch=ch, z_ch=z_ch)
|
| 453 |
+
|
| 454 |
+
|
| 455 |
+
class _DConvDenoiser(nn.Module):
|
| 456 |
+
def __init__(
|
| 457 |
+
self,
|
| 458 |
+
patch_size=16,
|
| 459 |
+
in_channels=3,
|
| 460 |
+
hidden_size=384,
|
| 461 |
+
hidden_size_x=32,
|
| 462 |
+
mlp_ratio=4.0,
|
| 463 |
+
num_blocks=24,
|
| 464 |
+
num_cond_blocks=21,
|
| 465 |
+
bottleneck_dim=128,
|
| 466 |
+
):
|
| 467 |
+
super().__init__()
|
| 468 |
+
self.in_channels = in_channels
|
| 469 |
+
self.patch_size = patch_size
|
| 470 |
+
self.hidden_size = hidden_size
|
| 471 |
+
self.num_cond_blocks = num_cond_blocks
|
| 472 |
+
|
| 473 |
+
self.t_embedder = TimestepEmbedder(hidden_size)
|
| 474 |
+
self.y_embedder_x = nn.Conv2d(hidden_size, hidden_size_x * patch_size ** 2, 1, 1, 0)
|
| 475 |
+
self.x_embedder = NerfEmbedder(in_channels + hidden_size_x, hidden_size_x, max_freqs=8)
|
| 476 |
+
self.s_embedder = BottleneckPatchEmbed(patch_size, in_channels, bottleneck_dim, hidden_size, bias=True)
|
| 477 |
+
self.blocks = nn.ModuleList([
|
| 478 |
+
DiCoBlock(hidden_size, mlp_ratio=mlp_ratio) for _ in range(num_cond_blocks)
|
| 479 |
+
])
|
| 480 |
+
self.dec_net = SimpleMLPAdaLN(
|
| 481 |
+
in_channels=hidden_size_x,
|
| 482 |
+
model_channels=hidden_size_x,
|
| 483 |
+
out_channels=in_channels,
|
| 484 |
+
z_channels=hidden_size,
|
| 485 |
+
num_res_blocks=num_blocks - num_cond_blocks,
|
| 486 |
+
patch_size=patch_size,
|
| 487 |
+
)
|
| 488 |
+
self.final_layer = NerfFinalLayer(hidden_size_x, in_channels)
|
| 489 |
+
self.y_embedder = _YEmbedder(ch=hidden_size, z_ch=bottleneck_dim)
|
| 490 |
+
|
| 491 |
+
def forward(self, x, t, cond):
|
| 492 |
+
b, _, h, w = x.shape
|
| 493 |
+
c = self.t_embedder(t.view(-1))
|
| 494 |
+
|
| 495 |
+
s = self.s_embedder(x, cond)
|
| 496 |
+
for block in self.blocks:
|
| 497 |
+
s = block(s, c)
|
| 498 |
+
|
| 499 |
+
length = s.shape[-2] * s.shape[-1]
|
| 500 |
+
s = s.permute(0, 2, 3, 1).reshape(-1, self.hidden_size)
|
| 501 |
+
|
| 502 |
+
x = torch.nn.functional.unfold(x, kernel_size=self.patch_size, stride=self.patch_size)
|
| 503 |
+
x = torch.cat([x, self.y_embedder_x(cond).flatten(2)], dim=1)
|
| 504 |
+
x = x.reshape(b, -1, self.patch_size ** 2, length).permute(0, 3, 2, 1).flatten(0, 1)
|
| 505 |
+
x = self.x_embedder(x)
|
| 506 |
+
|
| 507 |
+
x = self.dec_net(x, s)
|
| 508 |
+
x = self.final_layer(x)
|
| 509 |
+
x = x.transpose(1, 2).reshape(b, length, -1)
|
| 510 |
+
return torch.nn.functional.fold(
|
| 511 |
+
x.transpose(1, 2).contiguous(), (h, w),
|
| 512 |
+
kernel_size=self.patch_size, stride=self.patch_size,
|
| 513 |
+
)
|
| 514 |
+
|
| 515 |
+
|
| 516 |
+
# ---------------------------------------------------------------------------
|
| 517 |
+
# Wrapper
|
| 518 |
+
# ---------------------------------------------------------------------------
|
| 519 |
+
def _load_state_dict(ckpt_path: str):
|
| 520 |
+
if ckpt_path.endswith(".safetensors"):
|
| 521 |
+
from safetensors.torch import load_file
|
| 522 |
+
return load_file(ckpt_path, device="cpu")
|
| 523 |
+
if os.path.exists(os.path.join(ckpt_path, "checkpoint-state_dict.pt")):
|
| 524 |
+
ckpt_path = os.path.join(ckpt_path, "checkpoint-state_dict.pt")
|
| 525 |
+
elif os.path.isdir(ckpt_path):
|
| 526 |
+
ckpt_path = os.path.join(ckpt_path, "checkpoint", "mp_rank_00_model_states.pt")
|
| 527 |
+
state = torch.load(ckpt_path, map_location="cpu")
|
| 528 |
+
if "module" in state:
|
| 529 |
+
return state["module"]
|
| 530 |
+
if "state_dict" in state:
|
| 531 |
+
return state["state_dict"]
|
| 532 |
+
return state
|
| 533 |
+
|
| 534 |
+
|
| 535 |
+
class MageVAE(nn.Module):
|
| 536 |
+
"""
|
| 537 |
+
Encode: DConvEncoder (one-step diffusion) → latent [B, 128, H/16, W/16]
|
| 538 |
+
Decode: DConvDenoiser + CoD Decoder → image [B, 3, H, W] in [-1, 1]
|
| 539 |
+
"""
|
| 540 |
+
|
| 541 |
+
latent_channels = 128
|
| 542 |
+
downsample_factor = 16
|
| 543 |
+
|
| 544 |
+
def __init__(self, ckpt_path: str, sample_posterior: bool = True):
|
| 545 |
+
super().__init__()
|
| 546 |
+
self.sample_posterior = sample_posterior
|
| 547 |
+
|
| 548 |
+
self.dconv_encoder = _DConvEncoder()
|
| 549 |
+
self.decoder_model = _DConvDenoiser()
|
| 550 |
+
|
| 551 |
+
sd = _load_state_dict(ckpt_path)
|
| 552 |
+
self._load_encoder(sd, ckpt_path)
|
| 553 |
+
self._load_decoder(sd, ckpt_path)
|
| 554 |
+
|
| 555 |
+
# adaLN modulation depends only on t, and we always run at t=0.
|
| 556 |
+
# Precompute and drop the MLPs once at construction (~37M params saved).
|
| 557 |
+
self._freeze_adaln_cache()
|
| 558 |
+
|
| 559 |
+
def _load_encoder(self, sd, ckpt_path):
|
| 560 |
+
prefix = "student.dconv_encoder."
|
| 561 |
+
enc_sd = {k[len(prefix):]: v for k, v in sd.items() if k.startswith(prefix)}
|
| 562 |
+
if not enc_sd:
|
| 563 |
+
raise RuntimeError(f"CoDEncoder: no '{prefix}*' keys in {ckpt_path}")
|
| 564 |
+
proj = enc_sd.get("proj_out.weight")
|
| 565 |
+
if proj is None or proj.shape[0] != 2 * self.latent_channels:
|
| 566 |
+
raise RuntimeError(
|
| 567 |
+
f"CoDEncoder: expected packed mean+logvar (proj_out out_channels="
|
| 568 |
+
f"{2 * self.latent_channels}), got {None if proj is None else tuple(proj.shape)}"
|
| 569 |
+
)
|
| 570 |
+
missing, unexpected = self.dconv_encoder.load_state_dict(enc_sd, strict=False)
|
| 571 |
+
logger.info(
|
| 572 |
+
f"CoDEncoder: loaded {len(enc_sd)} keys, "
|
| 573 |
+
f"missing={len(missing)}, unexpected={len(unexpected)}"
|
| 574 |
+
)
|
| 575 |
+
if missing:
|
| 576 |
+
logger.warning(f"CoDEncoder missing: {missing[:10]}")
|
| 577 |
+
|
| 578 |
+
def _load_decoder(self, sd, ckpt_path):
|
| 579 |
+
prefix = "pipeline."
|
| 580 |
+
if not any(k.startswith(prefix) for k in sd):
|
| 581 |
+
raise RuntimeError(f"CoDDecoder: no '{prefix}*' keys in {ckpt_path}")
|
| 582 |
+
model_dict = self.decoder_model.state_dict()
|
| 583 |
+
matched = {}
|
| 584 |
+
for k, v in sd.items():
|
| 585 |
+
if not k.startswith(prefix):
|
| 586 |
+
continue
|
| 587 |
+
new_k = k[len(prefix):]
|
| 588 |
+
if new_k.startswith("y_embedder.encoder.") or new_k.startswith("y_embedder.bottleneck."):
|
| 589 |
+
continue
|
| 590 |
+
if new_k in model_dict and model_dict[new_k].shape == v.shape:
|
| 591 |
+
matched[new_k] = v
|
| 592 |
+
self.decoder_model.load_state_dict(matched, strict=False)
|
| 593 |
+
logger.info(f"CoDDecoder: loaded {len(matched)} params (denoiser + y_embedder.decoder)")
|
| 594 |
+
if not matched:
|
| 595 |
+
raise RuntimeError(f"CoDDecoder: 0 params matched from {ckpt_path}")
|
| 596 |
+
|
| 597 |
+
@torch.no_grad()
|
| 598 |
+
def _moments(self, x: torch.Tensor):
|
| 599 |
+
B, _, H, W = x.shape
|
| 600 |
+
ps = self.dconv_encoder.patch_size
|
| 601 |
+
z_t = torch.zeros(B, self.dconv_encoder.z_ch, H // ps, W // ps, device=x.device, dtype=x.dtype)
|
| 602 |
+
t = torch.zeros(B, device=x.device, dtype=x.dtype)
|
| 603 |
+
out = self.dconv_encoder.forward_pred(z_t, t, x)
|
| 604 |
+
mean = out[:, : self.latent_channels]
|
| 605 |
+
logvar = out[:, self.latent_channels :].clamp(min=-20.0, max=10.0)
|
| 606 |
+
return mean, logvar
|
| 607 |
+
|
| 608 |
+
@torch.no_grad()
|
| 609 |
+
def _encode_moments(self, x: torch.Tensor):
|
| 610 |
+
# Compile target: pure deterministic part of encode (no RNG, no
|
| 611 |
+
# asserts), so torch.compile produces a single dynamic graph.
|
| 612 |
+
return self._moments(x)
|
| 613 |
+
|
| 614 |
+
@torch.no_grad()
|
| 615 |
+
def encode(self, x: torch.Tensor) -> torch.Tensor:
|
| 616 |
+
ps = self.dconv_encoder.patch_size
|
| 617 |
+
H, W = x.shape[-2], x.shape[-1]
|
| 618 |
+
if H % ps or W % ps:
|
| 619 |
+
raise ValueError(f"H, W must be multiples of {ps}, got ({H}, {W})")
|
| 620 |
+
mean, logvar = self._encode_moments(x)
|
| 621 |
+
if self.sample_posterior:
|
| 622 |
+
return mean + torch.exp(0.5 * logvar) * torch.randn_like(mean)
|
| 623 |
+
return mean
|
| 624 |
+
|
| 625 |
+
@torch.no_grad()
|
| 626 |
+
def decode(self, z: torch.Tensor) -> torch.Tensor:
|
| 627 |
+
cond = self.decoder_model.y_embedder.decoder(z)
|
| 628 |
+
B = z.shape[0]
|
| 629 |
+
H = z.shape[2] * self.downsample_factor
|
| 630 |
+
W = z.shape[3] * self.downsample_factor
|
| 631 |
+
noise = torch.zeros(B, 3, H, W, device=z.device, dtype=z.dtype)
|
| 632 |
+
t = torch.zeros(B, device=z.device, dtype=z.dtype)
|
| 633 |
+
return self.decoder_model.forward(noise, t, cond)
|
| 634 |
+
|
| 635 |
+
@property
|
| 636 |
+
def device(self):
|
| 637 |
+
return next(self.parameters()).device
|
| 638 |
+
|
| 639 |
+
@property
|
| 640 |
+
def dtype(self):
|
| 641 |
+
return next(self.parameters()).dtype
|
| 642 |
+
|
| 643 |
+
def _freeze_adaln_cache(self):
|
| 644 |
+
"""Constant-fold adaLN_modulation MLPs at t=0 (encoder + decoder)."""
|
| 645 |
+
device = next(self.parameters()).device
|
| 646 |
+
dtype = next(self.parameters()).dtype
|
| 647 |
+
t = torch.zeros(1, device=device, dtype=dtype)
|
| 648 |
+
c_enc = self.dconv_encoder.t_embedder(t)
|
| 649 |
+
_replace_adaln_with_const(self.dconv_encoder, c_enc)
|
| 650 |
+
c_dec = self.decoder_model.t_embedder(t)
|
| 651 |
+
_replace_adaln_with_const(self.decoder_model, c_dec)
|
vendor/mage_flow/models/modules/text_encoder.py
ADDED
|
@@ -0,0 +1,707 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Qwen3-VL text encoder: custom HF model + packing-aware forward patches + TextEncoder wrapper."""
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
import os
|
| 5 |
+
from collections.abc import Callable
|
| 6 |
+
from dataclasses import dataclass
|
| 7 |
+
|
| 8 |
+
try:
|
| 9 |
+
from typing import Unpack
|
| 10 |
+
except ImportError:
|
| 11 |
+
from typing_extensions import Unpack
|
| 12 |
+
|
| 13 |
+
import torch
|
| 14 |
+
from loguru import logger
|
| 15 |
+
from torch import nn
|
| 16 |
+
from transformers import AutoProcessor, AutoTokenizer, Cache, Qwen3VLForConditionalGeneration
|
| 17 |
+
from transformers.cache_utils import DynamicCache
|
| 18 |
+
from transformers.masking_utils import create_causal_mask
|
| 19 |
+
from transformers.modeling_flash_attention_utils import FlashAttentionKwargs
|
| 20 |
+
from transformers.modeling_outputs import BaseModelOutputWithPast
|
| 21 |
+
from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS
|
| 22 |
+
from transformers.models.qwen3_vl.modeling_qwen3_vl import (
|
| 23 |
+
Qwen3VLCausalLMOutputWithPast,
|
| 24 |
+
apply_rotary_pos_emb,
|
| 25 |
+
eager_attention_forward,
|
| 26 |
+
)
|
| 27 |
+
from transformers.utils import ModelOutput
|
| 28 |
+
|
| 29 |
+
from ._attn_backend import flash_attn_varlen_func
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
# ===========================================================================
|
| 33 |
+
# Custom Qwen3-VL model (customizable forward output)
|
| 34 |
+
# ===========================================================================
|
| 35 |
+
|
| 36 |
+
@dataclass
|
| 37 |
+
class Qwen3VLModelOutput(ModelOutput):
|
| 38 |
+
"""Flexible output class for custom Qwen3-VL model."""
|
| 39 |
+
|
| 40 |
+
loss: torch.FloatTensor | None = None
|
| 41 |
+
logits: torch.FloatTensor | None = None
|
| 42 |
+
past_key_values: Cache | None = None
|
| 43 |
+
hidden_states: tuple[torch.FloatTensor, ...] | None = None
|
| 44 |
+
last_hidden_state: torch.FloatTensor | None = None
|
| 45 |
+
attentions: tuple[torch.FloatTensor, ...] | None = None
|
| 46 |
+
rope_deltas: torch.LongTensor | None = None
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class CustomQwen3VLForConditionalGeneration(Qwen3VLForConditionalGeneration):
|
| 50 |
+
"""
|
| 51 |
+
Custom Qwen3-VL model that allows customizing the forward output.
|
| 52 |
+
|
| 53 |
+
This class inherits from Qwen3VLForConditionalGeneration and provides
|
| 54 |
+
hooks to customize what is returned from the forward pass.
|
| 55 |
+
|
| 56 |
+
Example usage:
|
| 57 |
+
```python
|
| 58 |
+
model = CustomQwen3VLForConditionalGeneration.from_pretrained(
|
| 59 |
+
"Qwen/Qwen3-VL-8B-Instruct",
|
| 60 |
+
attn_implementation="flash_attention_2" # Use flash attention for faster inference
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
# Option 1: Use built-in output modes
|
| 64 |
+
model.set_output_mode("embedding") # Only return last hidden state (default)
|
| 65 |
+
model.set_output_mode("full") # Return everything
|
| 66 |
+
model.set_output_mode("logits") # Only return logits
|
| 67 |
+
|
| 68 |
+
# Option 2: Set a custom output processor
|
| 69 |
+
def my_custom_output(hidden_states, logits, outputs, **kwargs):
|
| 70 |
+
return {"embeddings": hidden_states, "pooled": hidden_states.mean(dim=1)}
|
| 71 |
+
model.set_output_processor(my_custom_output)
|
| 72 |
+
```
|
| 73 |
+
"""
|
| 74 |
+
|
| 75 |
+
# Output mode constants
|
| 76 |
+
OUTPUT_MODE_FULL = "full"
|
| 77 |
+
OUTPUT_MODE_EMBEDDING = "embedding"
|
| 78 |
+
OUTPUT_MODE_LOGITS = "logits"
|
| 79 |
+
OUTPUT_MODE_HIDDEN = "hidden"
|
| 80 |
+
|
| 81 |
+
def __init__(self, config):
|
| 82 |
+
super().__init__(config)
|
| 83 |
+
self._output_mode = self.OUTPUT_MODE_EMBEDDING
|
| 84 |
+
self._skip_lm_head = True
|
| 85 |
+
|
| 86 |
+
def set_output_mode(self, mode: str):
|
| 87 |
+
"""
|
| 88 |
+
Set the output mode for the forward pass.
|
| 89 |
+
|
| 90 |
+
Args:
|
| 91 |
+
mode: One of:
|
| 92 |
+
- "full": Return full Qwen3VLCausalLMOutputWithPast
|
| 93 |
+
- "embedding": Only return last hidden state (skip lm_head) (default)
|
| 94 |
+
- "logits": Only return logits
|
| 95 |
+
- "hidden": Return all hidden states
|
| 96 |
+
"""
|
| 97 |
+
valid_modes = [
|
| 98 |
+
self.OUTPUT_MODE_FULL,
|
| 99 |
+
self.OUTPUT_MODE_EMBEDDING,
|
| 100 |
+
self.OUTPUT_MODE_LOGITS,
|
| 101 |
+
self.OUTPUT_MODE_HIDDEN,
|
| 102 |
+
]
|
| 103 |
+
if mode not in valid_modes:
|
| 104 |
+
raise ValueError(f"Invalid output mode: {mode}. Must be one of {valid_modes}")
|
| 105 |
+
self._output_mode = mode
|
| 106 |
+
self._skip_lm_head = mode == self.OUTPUT_MODE_EMBEDDING
|
| 107 |
+
|
| 108 |
+
def forward(
|
| 109 |
+
self,
|
| 110 |
+
input_ids: torch.LongTensor | None = None,
|
| 111 |
+
attention_mask: torch.Tensor | None = None,
|
| 112 |
+
position_ids: torch.LongTensor | None = None,
|
| 113 |
+
past_key_values: Cache | None = None,
|
| 114 |
+
inputs_embeds: torch.FloatTensor | None = None,
|
| 115 |
+
labels: torch.LongTensor | None = None,
|
| 116 |
+
pixel_values: torch.Tensor | None = None,
|
| 117 |
+
pixel_values_videos: torch.FloatTensor | None = None,
|
| 118 |
+
image_grid_thw: torch.LongTensor | None = None,
|
| 119 |
+
video_grid_thw: torch.LongTensor | None = None,
|
| 120 |
+
cache_position: torch.LongTensor | None = None,
|
| 121 |
+
logits_to_keep: int | torch.Tensor = 0,
|
| 122 |
+
output_attentions: bool | None = None,
|
| 123 |
+
output_hidden_states: bool | None = None,
|
| 124 |
+
return_dict: bool | None = None,
|
| 125 |
+
**kwargs,
|
| 126 |
+
) -> Qwen3VLCausalLMOutputWithPast | Qwen3VLModelOutput | dict | torch.Tensor:
|
| 127 |
+
"""
|
| 128 |
+
Forward pass with customizable output.
|
| 129 |
+
|
| 130 |
+
Returns different outputs based on the configured output mode or custom processor.
|
| 131 |
+
"""
|
| 132 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 133 |
+
output_hidden_states = (
|
| 134 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
# Get outputs from the base model (Qwen3VLModel)
|
| 138 |
+
outputs = self.model(
|
| 139 |
+
input_ids=input_ids,
|
| 140 |
+
pixel_values=pixel_values,
|
| 141 |
+
pixel_values_videos=pixel_values_videos,
|
| 142 |
+
image_grid_thw=image_grid_thw,
|
| 143 |
+
video_grid_thw=video_grid_thw,
|
| 144 |
+
position_ids=position_ids,
|
| 145 |
+
attention_mask=attention_mask,
|
| 146 |
+
past_key_values=past_key_values,
|
| 147 |
+
inputs_embeds=inputs_embeds,
|
| 148 |
+
cache_position=cache_position,
|
| 149 |
+
output_attentions=output_attentions,
|
| 150 |
+
output_hidden_states=output_hidden_states,
|
| 151 |
+
return_dict=True,
|
| 152 |
+
**kwargs,
|
| 153 |
+
)
|
| 154 |
+
|
| 155 |
+
# Get the last hidden state
|
| 156 |
+
hidden_states = outputs[0] # This is the last hidden state
|
| 157 |
+
|
| 158 |
+
# Compute logits if not skipping lm_head
|
| 159 |
+
logits = None
|
| 160 |
+
if not self._skip_lm_head:
|
| 161 |
+
slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
|
| 162 |
+
logits = self.lm_head(hidden_states[:, slice_indices, :])
|
| 163 |
+
|
| 164 |
+
# Compute loss if labels are provided
|
| 165 |
+
loss = None
|
| 166 |
+
if labels is not None and logits is not None:
|
| 167 |
+
loss = self.loss_function(
|
| 168 |
+
logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
|
| 169 |
+
)
|
| 170 |
+
|
| 171 |
+
# Return based on output mode
|
| 172 |
+
if self._output_mode == self.OUTPUT_MODE_EMBEDDING:
|
| 173 |
+
return Qwen3VLModelOutput(
|
| 174 |
+
last_hidden_state=hidden_states,
|
| 175 |
+
past_key_values=outputs.past_key_values,
|
| 176 |
+
attentions=outputs.attentions,
|
| 177 |
+
rope_deltas=outputs.rope_deltas,
|
| 178 |
+
)
|
| 179 |
+
elif self._output_mode == self.OUTPUT_MODE_LOGITS:
|
| 180 |
+
return logits
|
| 181 |
+
elif self._output_mode == self.OUTPUT_MODE_HIDDEN:
|
| 182 |
+
return Qwen3VLModelOutput(
|
| 183 |
+
last_hidden_state=hidden_states,
|
| 184 |
+
hidden_states=outputs.hidden_states,
|
| 185 |
+
past_key_values=outputs.past_key_values,
|
| 186 |
+
attentions=outputs.attentions,
|
| 187 |
+
rope_deltas=outputs.rope_deltas,
|
| 188 |
+
)
|
| 189 |
+
else: # OUTPUT_MODE_FULL
|
| 190 |
+
return Qwen3VLCausalLMOutputWithPast(
|
| 191 |
+
loss=loss,
|
| 192 |
+
logits=logits,
|
| 193 |
+
past_key_values=outputs.past_key_values,
|
| 194 |
+
hidden_states=outputs.hidden_states,
|
| 195 |
+
attentions=outputs.attentions,
|
| 196 |
+
rope_deltas=outputs.rope_deltas,
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
# ===========================================================================
|
| 201 |
+
# Packing-aware forward patches (cu_seqlens) for the Qwen3-VL text encoder
|
| 202 |
+
# ===========================================================================
|
| 203 |
+
|
| 204 |
+
def model_forward(
|
| 205 |
+
self,
|
| 206 |
+
input_ids: torch.LongTensor | None = None,
|
| 207 |
+
attention_mask: torch.Tensor | None = None,
|
| 208 |
+
position_ids: torch.LongTensor | None = None,
|
| 209 |
+
past_key_values: Cache | None = None,
|
| 210 |
+
inputs_embeds: torch.FloatTensor | None = None,
|
| 211 |
+
use_cache: bool | None = None,
|
| 212 |
+
cache_position: torch.LongTensor | None = None,
|
| 213 |
+
# args for deepstack
|
| 214 |
+
visual_pos_masks: torch.Tensor | None = None,
|
| 215 |
+
deepstack_visual_embeds: list[torch.Tensor] | None = None,
|
| 216 |
+
**kwargs: Unpack[FlashAttentionKwargs],
|
| 217 |
+
) -> tuple | BaseModelOutputWithPast:
|
| 218 |
+
r"""
|
| 219 |
+
visual_pos_masks (`torch.Tensor` of shape `(batch_size, seqlen)`, *optional*):
|
| 220 |
+
The mask of the visual positions.
|
| 221 |
+
deepstack_visual_embeds (`list[torch.Tensor]`, *optional*):
|
| 222 |
+
The deepstack visual embeddings. The shape is (num_layers, visual_seqlen, embed_dim).
|
| 223 |
+
The feature is extracted from the different visual encoder layers, and fed to the decoder
|
| 224 |
+
hidden states. It's from the paper DeepStack(https://arxiv.org/abs/2406.04334).
|
| 225 |
+
"""
|
| 226 |
+
if (input_ids is None) ^ (inputs_embeds is not None):
|
| 227 |
+
raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
|
| 228 |
+
|
| 229 |
+
# torch.jit.trace() doesn't support cache objects in the output
|
| 230 |
+
if use_cache and past_key_values is None and not torch.jit.is_tracing():
|
| 231 |
+
past_key_values = DynamicCache(config=self.config)
|
| 232 |
+
|
| 233 |
+
if inputs_embeds is None:
|
| 234 |
+
inputs_embeds = self.embed_tokens(input_ids)
|
| 235 |
+
|
| 236 |
+
if cache_position is None:
|
| 237 |
+
past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
|
| 238 |
+
cache_position = torch.arange(
|
| 239 |
+
past_seen_tokens, past_seen_tokens + inputs_embeds.shape[1], device=inputs_embeds.device
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
# the hard coded `3` is for temporal, height and width.
|
| 243 |
+
if position_ids is None:
|
| 244 |
+
position_ids = cache_position.view(1, 1, -1).expand(3, inputs_embeds.shape[0], -1)
|
| 245 |
+
elif position_ids.ndim == 2:
|
| 246 |
+
position_ids = position_ids[None, ...].expand(3, position_ids.shape[0], -1)
|
| 247 |
+
|
| 248 |
+
if position_ids.ndim == 3 and position_ids.shape[0] == 4:
|
| 249 |
+
text_position_ids = position_ids[0]
|
| 250 |
+
position_ids = position_ids[1:]
|
| 251 |
+
else:
|
| 252 |
+
text_position_ids = position_ids[0]
|
| 253 |
+
|
| 254 |
+
if kwargs.get("cu_seqlens") is None:
|
| 255 |
+
attention_mask = create_causal_mask(
|
| 256 |
+
config=self.config,
|
| 257 |
+
input_embeds=inputs_embeds,
|
| 258 |
+
attention_mask=attention_mask,
|
| 259 |
+
cache_position=cache_position,
|
| 260 |
+
past_key_values=past_key_values,
|
| 261 |
+
position_ids=text_position_ids,
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
hidden_states = inputs_embeds
|
| 265 |
+
|
| 266 |
+
# create position embeddings to be shared across the decoder layers
|
| 267 |
+
position_embeddings = self.rotary_emb(hidden_states, position_ids)
|
| 268 |
+
|
| 269 |
+
# decoder layers
|
| 270 |
+
for layer_idx, decoder_layer in enumerate(self.layers):
|
| 271 |
+
layer_outputs = decoder_layer(
|
| 272 |
+
hidden_states,
|
| 273 |
+
attention_mask=attention_mask,
|
| 274 |
+
position_ids=text_position_ids,
|
| 275 |
+
past_key_values=past_key_values,
|
| 276 |
+
cache_position=cache_position,
|
| 277 |
+
position_embeddings=position_embeddings,
|
| 278 |
+
**kwargs,
|
| 279 |
+
)
|
| 280 |
+
hidden_states = layer_outputs
|
| 281 |
+
|
| 282 |
+
# add visual features to the hidden states of first several layers
|
| 283 |
+
if deepstack_visual_embeds is not None and layer_idx in range(len(deepstack_visual_embeds)):
|
| 284 |
+
hidden_states = self._deepstack_process(
|
| 285 |
+
hidden_states,
|
| 286 |
+
visual_pos_masks,
|
| 287 |
+
deepstack_visual_embeds[layer_idx],
|
| 288 |
+
)
|
| 289 |
+
|
| 290 |
+
hidden_states = self.norm(hidden_states)
|
| 291 |
+
|
| 292 |
+
return BaseModelOutputWithPast(
|
| 293 |
+
last_hidden_state=hidden_states,
|
| 294 |
+
past_key_values=past_key_values,
|
| 295 |
+
)
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
def forward(
|
| 299 |
+
self,
|
| 300 |
+
hidden_states: torch.Tensor,
|
| 301 |
+
position_embeddings: tuple[torch.Tensor, torch.Tensor],
|
| 302 |
+
attention_mask: torch.Tensor | None,
|
| 303 |
+
past_key_values: Cache | None = None,
|
| 304 |
+
cache_position: torch.LongTensor | None = None,
|
| 305 |
+
**kwargs: Unpack[FlashAttentionKwargs],
|
| 306 |
+
) -> tuple[torch.Tensor, torch.Tensor | None]:
|
| 307 |
+
input_shape = hidden_states.shape[:-1]
|
| 308 |
+
hidden_shape = (*input_shape, -1, self.head_dim)
|
| 309 |
+
|
| 310 |
+
query_states = self.q_norm(self.q_proj(hidden_states).view(hidden_shape)).transpose(1, 2)
|
| 311 |
+
key_states = self.k_norm(self.k_proj(hidden_states).view(hidden_shape)).transpose(1, 2)
|
| 312 |
+
value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
|
| 313 |
+
|
| 314 |
+
cos, sin = position_embeddings
|
| 315 |
+
query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
|
| 316 |
+
|
| 317 |
+
if past_key_values is not None:
|
| 318 |
+
# sin and cos are specific to RoPE models; cache_position needed for the static cache
|
| 319 |
+
cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position}
|
| 320 |
+
key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx, cache_kwargs)
|
| 321 |
+
|
| 322 |
+
cu_seqlens = kwargs.get("cu_seqlens", None)
|
| 323 |
+
|
| 324 |
+
if cu_seqlens is None:
|
| 325 |
+
attention_interface: Callable = eager_attention_forward
|
| 326 |
+
if self.config._attn_implementation != "eager":
|
| 327 |
+
attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
|
| 328 |
+
|
| 329 |
+
attn_output, attn_weights = attention_interface(
|
| 330 |
+
self,
|
| 331 |
+
query_states,
|
| 332 |
+
key_states,
|
| 333 |
+
value_states,
|
| 334 |
+
attention_mask,
|
| 335 |
+
dropout=0.0 if not self.training else self.attention_dropout,
|
| 336 |
+
scaling=self.scaling,
|
| 337 |
+
**kwargs,
|
| 338 |
+
)
|
| 339 |
+
else:
|
| 340 |
+
max_seqlen = torch.diff(cu_seqlens).max().item() if cu_seqlens is not None else None
|
| 341 |
+
query_states = query_states.transpose(1, 2).squeeze(0)
|
| 342 |
+
key_states = key_states.transpose(1, 2).squeeze(0)
|
| 343 |
+
value_states = value_states.transpose(1, 2).squeeze(0)
|
| 344 |
+
attn_output = flash_attn_varlen_func(
|
| 345 |
+
q=query_states,
|
| 346 |
+
k=key_states,
|
| 347 |
+
v=value_states,
|
| 348 |
+
cu_seqlens_q=cu_seqlens,
|
| 349 |
+
cu_seqlens_k=cu_seqlens,
|
| 350 |
+
max_seqlen_q=max_seqlen,
|
| 351 |
+
max_seqlen_k=max_seqlen,
|
| 352 |
+
causal=True,
|
| 353 |
+
window_size=(-1, -1),
|
| 354 |
+
softmax_scale=self.head_dim**-0.5,
|
| 355 |
+
dropout_p=0.0,
|
| 356 |
+
)
|
| 357 |
+
|
| 358 |
+
attn_output = attn_output.reshape(*input_shape, -1).contiguous()
|
| 359 |
+
attn_output = self.o_proj(attn_output)
|
| 360 |
+
return attn_output, None
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
def qwen3_patch_forward():
|
| 364 |
+
"""Patch the Qwen3-VL text model + attention forwards to support packed
|
| 365 |
+
varlen (cu_seqlens) inputs used by ``TextEncoder.forward``."""
|
| 366 |
+
from transformers.models.qwen3_vl.modeling_qwen3_vl import Qwen3VLTextAttention, Qwen3VLTextModel
|
| 367 |
+
|
| 368 |
+
Qwen3VLTextModel.forward = model_forward
|
| 369 |
+
Qwen3VLTextAttention.forward = forward
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
# ===========================================================================
|
| 373 |
+
# TextEncoder wrapper (packed text -> DiT conditioning embeddings)
|
| 374 |
+
# ===========================================================================
|
| 375 |
+
_FA2_ALIASES = {"flash2", "fa2", "flash_attention_2", "flash_attn_2"}
|
| 376 |
+
_FA4_ALIASES = {"flash4", "fa4", "flash_attention_4", "flash_attn_4"}
|
| 377 |
+
_SDPA_ALIASES = {"sdpa", "torch_sdpa", "scaled_dot_product_attention"}
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
def _resolve_hf_attn_impl(attn_type: str) -> str:
|
| 381 |
+
"""Map a project-level attn_type to a HuggingFace ``attn_implementation`` string.
|
| 382 |
+
|
| 383 |
+
``VF_HF_ATTN_IMPL`` env var, if set, takes precedence (useful for forcing
|
| 384 |
+
sdpa on machines without flash-attn). For FA4 we additionally probe that
|
| 385 |
+
the CUTE-DSL kernel is importable and (when available) ask the HF helper
|
| 386 |
+
to confirm; if not, fall back to sdpa rather than crashing at load time.
|
| 387 |
+
"""
|
| 388 |
+
override = os.environ.get("VF_HF_ATTN_IMPL")
|
| 389 |
+
if override:
|
| 390 |
+
return override
|
| 391 |
+
|
| 392 |
+
name = attn_type.lower().strip()
|
| 393 |
+
if name in _FA2_ALIASES:
|
| 394 |
+
return "flash_attention_2"
|
| 395 |
+
if name in _FA4_ALIASES:
|
| 396 |
+
try:
|
| 397 |
+
import flash_attn.cute # noqa: F401
|
| 398 |
+
fa4_importable = True
|
| 399 |
+
except Exception:
|
| 400 |
+
fa4_importable = False
|
| 401 |
+
if fa4_importable:
|
| 402 |
+
try:
|
| 403 |
+
from transformers.utils.import_utils import is_flash_attn_4_available
|
| 404 |
+
if is_flash_attn_4_available():
|
| 405 |
+
return "flash_attention_4"
|
| 406 |
+
except ImportError:
|
| 407 |
+
return "flash_attention_4"
|
| 408 |
+
logger.warning(
|
| 409 |
+
"attn_type=flash4 requested but flash_attn.cute is unavailable; "
|
| 410 |
+
"falling back to sdpa for HF text encoder."
|
| 411 |
+
)
|
| 412 |
+
return "sdpa"
|
| 413 |
+
if name in _SDPA_ALIASES:
|
| 414 |
+
return "sdpa"
|
| 415 |
+
raise ValueError(
|
| 416 |
+
f"Unknown attn_type {attn_type!r}; expected one of "
|
| 417 |
+
f"{sorted(_FA2_ALIASES | _FA4_ALIASES | _SDPA_ALIASES)}"
|
| 418 |
+
)
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
SEQ_MULTI_OF = 32
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
|
| 425 |
+
class TextEncoder(nn.Module):
|
| 426 |
+
def __init__(
|
| 427 |
+
self,
|
| 428 |
+
model_name: str,
|
| 429 |
+
version: str,
|
| 430 |
+
tokenizer_max_length: int,
|
| 431 |
+
prompt_template: dict | None,
|
| 432 |
+
dit_structure: dict,
|
| 433 |
+
use_packed_text_infer: bool = False,
|
| 434 |
+
attn_type: str = "flash2",
|
| 435 |
+
**hf_kwargs,
|
| 436 |
+
):
|
| 437 |
+
super().__init__()
|
| 438 |
+
self.model_name = model_name
|
| 439 |
+
self.tokenizer_max_length = tokenizer_max_length
|
| 440 |
+
self.tokenizer: AutoTokenizer = AutoTokenizer.from_pretrained(version)
|
| 441 |
+
self.tokenizer.padding_side = "right"
|
| 442 |
+
|
| 443 |
+
hf_attn_impl = _resolve_hf_attn_impl(attn_type)
|
| 444 |
+
logger.info(f"TextEncoder attn_type={attn_type} -> attn_implementation={hf_attn_impl}")
|
| 445 |
+
|
| 446 |
+
logger.info("init vl model: qwen3")
|
| 447 |
+
self.hf_module: CustomQwen3VLForConditionalGeneration = CustomQwen3VLForConditionalGeneration.from_pretrained(
|
| 448 |
+
version,
|
| 449 |
+
attn_implementation=hf_attn_impl,
|
| 450 |
+
**hf_kwargs
|
| 451 |
+
)
|
| 452 |
+
|
| 453 |
+
# Use local_files_only if version is a local path (absolute path or contains path separators)
|
| 454 |
+
is_local = version.startswith("/") or os.sep in version
|
| 455 |
+
self.processor = AutoProcessor.from_pretrained(version, local_files_only=is_local)
|
| 456 |
+
|
| 457 |
+
self.hf_module = self.hf_module.eval().requires_grad_(False)
|
| 458 |
+
|
| 459 |
+
prompt_template = prompt_template or {}
|
| 460 |
+
self.prompt_template_encode = prompt_template.get("template", "")
|
| 461 |
+
self.prompt_template_encode_start_idx = prompt_template.get("start_idx", 0)
|
| 462 |
+
self.dit_structure = dit_structure
|
| 463 |
+
self.use_packed_text_infer = use_packed_text_infer
|
| 464 |
+
|
| 465 |
+
def forward(
|
| 466 |
+
self,
|
| 467 |
+
input_ids: torch.Tensor,
|
| 468 |
+
cu_seqlens: torch.Tensor,
|
| 469 |
+
inputs: dict | None = None,
|
| 470 |
+
drop_idx_override: int | None = None,
|
| 471 |
+
):
|
| 472 |
+
"""Encode packed text (varlen ``cu_seqlens``) into DiT conditioning embeddings.
|
| 473 |
+
|
| 474 |
+
This is the sole text-embedding path — both t2i and edit call it. Uses
|
| 475 |
+
Flash-Attention-2's varlen capability (``cu_seqlens``) via the patched
|
| 476 |
+
Qwen3-VL forward to process several concatenated sequences in a single
|
| 477 |
+
launch, with no padding. Verified numerically identical to a padded-batch
|
| 478 |
+
forward with per-sample cu_seqlens isolation (zero cross-contamination).
|
| 479 |
+
|
| 480 |
+
Args:
|
| 481 |
+
input_ids: Packed token ids ``[Total_L]``.
|
| 482 |
+
cu_seqlens: Cumulative sequence lengths ``[B+1]``.
|
| 483 |
+
inputs: Optional dict with additional model inputs (e.g. ``pixel_values``,
|
| 484 |
+
``image_grid_thw`` for the multimodal edit path). Passed through to
|
| 485 |
+
the text encoder.
|
| 486 |
+
drop_idx_override: If set, override the number of leading (system-prompt)
|
| 487 |
+
tokens to drop per sequence. Use 0 for multi-turn where the system
|
| 488 |
+
prompt is embedded in the conversation and should not be stripped.
|
| 489 |
+
|
| 490 |
+
Returns:
|
| 491 |
+
dict with keys:
|
| 492 |
+
- ``txt``: text embeddings ``[Total_L - B*drop_idx, D]`` (system prompt dropped)
|
| 493 |
+
- ``vec``: pooled text embeddings ``[B, D]``
|
| 494 |
+
- ``txt_seq_lens``: per-sequence lengths ``[B]`` (after dropping system prompt)
|
| 495 |
+
"""
|
| 496 |
+
# Compute seqlens from cu_seqlens
|
| 497 |
+
seqlens = cu_seqlens[1:] - cu_seqlens[:-1]
|
| 498 |
+
seqlens_list = seqlens.cpu().tolist()
|
| 499 |
+
|
| 500 |
+
# Build position_ids for packing: each sequence starts from 0
|
| 501 |
+
position_ids_list = []
|
| 502 |
+
for length in seqlens_list:
|
| 503 |
+
position_ids_list.append(torch.arange(length, device=input_ids.device))
|
| 504 |
+
position_ids = torch.cat(position_ids_list) # [Total_L]
|
| 505 |
+
|
| 506 |
+
# Reshape for model input: [1, Total_L]
|
| 507 |
+
input_ids_packed = input_ids.unsqueeze(0) # [1, Total_L]
|
| 508 |
+
position_ids_packed = position_ids.unsqueeze(0) # [1, Total_L]
|
| 509 |
+
|
| 510 |
+
# Move to text encoder device
|
| 511 |
+
device = self.hf_module.device
|
| 512 |
+
input_ids_packed = input_ids_packed.to(device)
|
| 513 |
+
position_ids_packed = position_ids_packed.to(device)
|
| 514 |
+
|
| 515 |
+
# Get text embeddings (the text encoder is always frozen)
|
| 516 |
+
with torch.no_grad():
|
| 517 |
+
forward_kwargs = {
|
| 518 |
+
"input_ids": input_ids_packed,
|
| 519 |
+
"cu_seqlens": cu_seqlens,
|
| 520 |
+
"position_ids": position_ids_packed,
|
| 521 |
+
"output_hidden_states": False,
|
| 522 |
+
"max_seqlen": None,
|
| 523 |
+
}
|
| 524 |
+
# Pass multimodal inputs for edit mode (reference images)
|
| 525 |
+
if inputs is not None:
|
| 526 |
+
for key in ("pixel_values", "image_grid_thw"):
|
| 527 |
+
if key in inputs and inputs[key] is not None:
|
| 528 |
+
val = inputs[key]
|
| 529 |
+
if hasattr(val, "to"):
|
| 530 |
+
val = val.to(device)
|
| 531 |
+
forward_kwargs[key] = val
|
| 532 |
+
outputs = self.hf_module(**forward_kwargs)
|
| 533 |
+
|
| 534 |
+
# Extract hidden state
|
| 535 |
+
if hasattr(outputs, "last_hidden_state") and outputs.last_hidden_state is not None:
|
| 536 |
+
hidden = outputs.last_hidden_state # [1, Total_L, D]
|
| 537 |
+
elif hasattr(outputs, "hidden_states"):
|
| 538 |
+
hidden = outputs.hidden_states[-1]
|
| 539 |
+
|
| 540 |
+
# Remove batch dimension: [Total_L, D]
|
| 541 |
+
hidden = hidden.squeeze(0)
|
| 542 |
+
|
| 543 |
+
# Get drop_idx (system prompt length to skip).
|
| 544 |
+
# For multi-turn, drop_idx_override=0 is passed since system prompt is in the messages.
|
| 545 |
+
if drop_idx_override is not None:
|
| 546 |
+
drop_idx = drop_idx_override
|
| 547 |
+
else:
|
| 548 |
+
drop_idx = self.prompt_template_encode_start_idx
|
| 549 |
+
|
| 550 |
+
# Split hidden states by sequence
|
| 551 |
+
hidden_split = torch.split(hidden, seqlens_list, dim=0)
|
| 552 |
+
|
| 553 |
+
# Extract valid embeddings (drop system prompt) and compute vec
|
| 554 |
+
txt_list = []
|
| 555 |
+
vec_list = []
|
| 556 |
+
valid_lengths = []
|
| 557 |
+
|
| 558 |
+
for h in hidden_split:
|
| 559 |
+
# Drop system prompt tokens
|
| 560 |
+
h_valid = h[drop_idx:] # [seq_len - drop_idx, D]
|
| 561 |
+
txt_list.append(h_valid)
|
| 562 |
+
valid_lengths.append(h_valid.shape[0])
|
| 563 |
+
|
| 564 |
+
# Compute pooled embedding (mean of valid tokens only, after dropping system prompt)
|
| 565 |
+
vec_list.append(h_valid.mean(dim=0)) # [D]
|
| 566 |
+
|
| 567 |
+
txt = torch.cat(txt_list, dim=0) # [Total_valid, D]
|
| 568 |
+
vec = torch.stack(vec_list, dim=0) # [B, D]
|
| 569 |
+
txt_seq_lens = torch.tensor(valid_lengths, device=input_ids.device)
|
| 570 |
+
|
| 571 |
+
result = {
|
| 572 |
+
"txt": txt,
|
| 573 |
+
"vec": vec,
|
| 574 |
+
"txt_seq_lens": txt_seq_lens,
|
| 575 |
+
}
|
| 576 |
+
|
| 577 |
+
return result
|
| 578 |
+
|
| 579 |
+
# ------------------------------------------------------------------
|
| 580 |
+
# Mandatory content-policy screening (same Qwen3-VL weights)
|
| 581 |
+
# ------------------------------------------------------------------
|
| 582 |
+
# The policy classifier lives HERE, on the text encoder, so it runs on the
|
| 583 |
+
# exact weights that produce the diffusion conditioning and is not a
|
| 584 |
+
# separable, toggleable pre-pass in the pipeline. The classifier needs
|
| 585 |
+
# autoregressive ``.generate()`` (JSON verdict) whereas conditioning is a
|
| 586 |
+
# single embedding forward — they cannot be one GPU forward without a
|
| 587 |
+
# trained classification head, so "fused" here means: same module, same
|
| 588 |
+
# weights, always run, FAIL-CLOSED (any error blocks).
|
| 589 |
+
|
| 590 |
+
def screen_text(self, prompt: str, max_new_tokens: int = 160):
|
| 591 |
+
"""Classify a text-to-image ``prompt`` against the content policy.
|
| 592 |
+
|
| 593 |
+
Returns a ``FilterVerdict``. FAIL-CLOSED: any error (generation, parse)
|
| 594 |
+
returns ``violates=True`` so a broken classifier cannot be used as a
|
| 595 |
+
bypass. An empty prompt is not a violation.
|
| 596 |
+
"""
|
| 597 |
+
from .mage_text import (
|
| 598 |
+
CONTENT_FILTER_SYSTEM, FilterVerdict, _extract_json_object,
|
| 599 |
+
_full_output_mode,
|
| 600 |
+
)
|
| 601 |
+
|
| 602 |
+
if not prompt or not prompt.strip():
|
| 603 |
+
return FilterVerdict(False, [], "empty prompt", "")
|
| 604 |
+
try:
|
| 605 |
+
tokenizer = self.tokenizer
|
| 606 |
+
hf = self.hf_module
|
| 607 |
+
device = next(hf.parameters()).device
|
| 608 |
+
|
| 609 |
+
messages = [
|
| 610 |
+
{"role": "system", "content": CONTENT_FILTER_SYSTEM},
|
| 611 |
+
{"role": "user", "content": f"Prompt to classify:\n{prompt}"},
|
| 612 |
+
]
|
| 613 |
+
text = tokenizer.apply_chat_template(
|
| 614 |
+
messages, tokenize=False, add_generation_prompt=True)
|
| 615 |
+
inputs = tokenizer(text, return_tensors="pt").to(device)
|
| 616 |
+
|
| 617 |
+
eos_id = tokenizer.eos_token_id
|
| 618 |
+
pad_id = tokenizer.pad_token_id if tokenizer.pad_token_id is not None else eos_id
|
| 619 |
+
|
| 620 |
+
with _full_output_mode(hf), torch.no_grad():
|
| 621 |
+
out = hf.generate(
|
| 622 |
+
**inputs, max_new_tokens=max_new_tokens, do_sample=False,
|
| 623 |
+
pad_token_id=pad_id, eos_token_id=eos_id)
|
| 624 |
+
gen = tokenizer.decode(
|
| 625 |
+
out[0, inputs.input_ids.shape[1]:], skip_special_tokens=True).strip()
|
| 626 |
+
|
| 627 |
+
parsed = _extract_json_object(gen)
|
| 628 |
+
violates = bool(parsed.get("violates", False))
|
| 629 |
+
cats = [c for c in (parsed.get("categories", []) or []) if isinstance(c, str)]
|
| 630 |
+
reason = str(parsed.get("reason", "")).strip()
|
| 631 |
+
return FilterVerdict(violates, cats, reason, gen)
|
| 632 |
+
except Exception as exc: # noqa: BLE001
|
| 633 |
+
# FAIL-CLOSED: block on any screening error.
|
| 634 |
+
return FilterVerdict(
|
| 635 |
+
True, ["policy"], f"filter error (blocked): {type(exc).__name__}: {exc}", "")
|
| 636 |
+
|
| 637 |
+
def screen_edit(self, prompt: str, ref_images, max_new_tokens: int = 192):
|
| 638 |
+
"""Classify an image-EDIT request (source image(s) + instruction).
|
| 639 |
+
|
| 640 |
+
Considers BOTH the source image(s) and the instruction via multimodal
|
| 641 |
+
Qwen3-VL. Falls back to :meth:`screen_text` when no image is given.
|
| 642 |
+
FAIL-CLOSED: any error returns ``violates=True``.
|
| 643 |
+
"""
|
| 644 |
+
from PIL import Image
|
| 645 |
+
|
| 646 |
+
from .mage_text import (
|
| 647 |
+
CONTENT_FILTER_EDIT_SYSTEM, FilterVerdict, _extract_json_object,
|
| 648 |
+
_full_output_mode,
|
| 649 |
+
)
|
| 650 |
+
|
| 651 |
+
pils = [ref_images] if isinstance(ref_images, Image.Image) else list(ref_images)
|
| 652 |
+
pils = [p.convert("RGB") for p in pils if p is not None]
|
| 653 |
+
if not pils:
|
| 654 |
+
return self.screen_text(prompt, max_new_tokens=max_new_tokens)
|
| 655 |
+
|
| 656 |
+
instruction = (prompt or "").strip() or "(no textual instruction)"
|
| 657 |
+
try:
|
| 658 |
+
processor = self.processor
|
| 659 |
+
tokenizer = self.tokenizer
|
| 660 |
+
hf = self.hf_module
|
| 661 |
+
device = next(hf.parameters()).device
|
| 662 |
+
|
| 663 |
+
user_content = [{"type": "image"} for _ in pils]
|
| 664 |
+
user_content.append({
|
| 665 |
+
"type": "text",
|
| 666 |
+
"text": (
|
| 667 |
+
f"There {'is' if len(pils) == 1 else 'are'} {len(pils)} source "
|
| 668 |
+
f"image(s) above. Edit instruction: {instruction}\n"
|
| 669 |
+
"Classify this edit request."
|
| 670 |
+
),
|
| 671 |
+
})
|
| 672 |
+
messages = [
|
| 673 |
+
{"role": "system", "content": CONTENT_FILTER_EDIT_SYSTEM},
|
| 674 |
+
{"role": "user", "content": user_content},
|
| 675 |
+
]
|
| 676 |
+
text = processor.apply_chat_template(
|
| 677 |
+
messages, tokenize=False, add_generation_prompt=True)
|
| 678 |
+
inputs = processor(
|
| 679 |
+
text=[text], images=pils, padding=True, return_tensors="pt").to(device)
|
| 680 |
+
|
| 681 |
+
eos_id = tokenizer.eos_token_id
|
| 682 |
+
pad_id = tokenizer.pad_token_id if tokenizer.pad_token_id is not None else eos_id
|
| 683 |
+
|
| 684 |
+
# Keep only the kwargs Qwen3-VL .generate() consumes.
|
| 685 |
+
gen_inputs = {
|
| 686 |
+
k: inputs[k]
|
| 687 |
+
for k in ("input_ids", "attention_mask", "pixel_values", "image_grid_thw")
|
| 688 |
+
if k in inputs and inputs[k] is not None
|
| 689 |
+
}
|
| 690 |
+
input_len = gen_inputs["input_ids"].shape[1]
|
| 691 |
+
|
| 692 |
+
with _full_output_mode(hf), torch.no_grad():
|
| 693 |
+
out = hf.generate(
|
| 694 |
+
**gen_inputs, max_new_tokens=max_new_tokens, do_sample=False,
|
| 695 |
+
pad_token_id=pad_id, eos_token_id=eos_id)
|
| 696 |
+
gen = tokenizer.decode(out[0, input_len:], skip_special_tokens=True).strip()
|
| 697 |
+
|
| 698 |
+
parsed = _extract_json_object(gen)
|
| 699 |
+
violates = bool(parsed.get("violates", False))
|
| 700 |
+
cats = [c for c in (parsed.get("categories", []) or []) if isinstance(c, str)]
|
| 701 |
+
reason = str(parsed.get("reason", "")).strip()
|
| 702 |
+
return FilterVerdict(violates, cats, reason, gen)
|
| 703 |
+
except Exception as exc: # noqa: BLE001
|
| 704 |
+
# FAIL-CLOSED: block on any screening error.
|
| 705 |
+
return FilterVerdict(
|
| 706 |
+
True, ["policy"], f"edit filter error (blocked): {type(exc).__name__}: {exc}", "")
|
| 707 |
+
|
vendor/mage_flow/models/utils.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import math
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
from einops import rearrange
|
| 7 |
+
from loguru import logger
|
| 8 |
+
from safetensors.torch import load_file
|
| 9 |
+
from safetensors.torch import load_file as load_sft
|
| 10 |
+
from torch import Tensor
|
| 11 |
+
|
| 12 |
+
from .mage_flow import MageFlow, MageFlowParams
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def get_noise(
|
| 16 |
+
num_samples: int,
|
| 17 |
+
channel: int,
|
| 18 |
+
height: int,
|
| 19 |
+
width: int,
|
| 20 |
+
device: torch.device,
|
| 21 |
+
dtype: torch.dtype,
|
| 22 |
+
seed: int,
|
| 23 |
+
):
|
| 24 |
+
# MageVAE: 16x downsample, no patch packing
|
| 25 |
+
return torch.randn(
|
| 26 |
+
num_samples,
|
| 27 |
+
channel,
|
| 28 |
+
math.ceil(height / 16),
|
| 29 |
+
math.ceil(width / 16),
|
| 30 |
+
device=device,
|
| 31 |
+
dtype=dtype,
|
| 32 |
+
generator=torch.Generator(device=device).manual_seed(seed),
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def unpack(x: Tensor, height: int, width: int) -> Tensor:
|
| 37 |
+
# MageVAE: [B, H*W, C] -> [B, C, H, W], no patch unpacking
|
| 38 |
+
return rearrange(
|
| 39 |
+
x,
|
| 40 |
+
"b (h w) c -> b c h w",
|
| 41 |
+
h=math.ceil(height / 16),
|
| 42 |
+
w=math.ceil(width / 16),
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
PROMPT_TEMPLATE = {
|
| 47 |
+
"default": {"template": "{}", "start_idx": 0},
|
| 48 |
+
"default-nonthinking": {"template": "{}<think>\n\n</think>\n\n", "start_idx": 0},
|
| 49 |
+
"mage-flow": {
|
| 50 |
+
"template": (
|
| 51 |
+
"<|im_start|>system\nDescribe the image by detailing the color, shape, size, texture, quantity, "
|
| 52 |
+
"text, spatial relationships of the objects and background:"
|
| 53 |
+
"<|im_end|>\n<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n"
|
| 54 |
+
),
|
| 55 |
+
"start_idx": 34,
|
| 56 |
+
},
|
| 57 |
+
"mage-flow-edit": {
|
| 58 |
+
"template": (
|
| 59 |
+
"<|im_start|>system\nDescribe the key features of the input image (color, shape, size, texture,"
|
| 60 |
+
" objects, background), then explain how the user's text instruction should alter or modify the image. "
|
| 61 |
+
"Generate a new image that meets the user's requirements while maintaining consistency with the original "
|
| 62 |
+
"input where appropriate.<|im_end|>\n<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n"
|
| 63 |
+
),
|
| 64 |
+
"start_idx": 64,
|
| 65 |
+
},
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def print_load_warning(missing: list[str], unexpected: list[str]) -> None:
|
| 70 |
+
if len(missing) > 0 and len(unexpected) > 0:
|
| 71 |
+
logger.warning(f"Got {len(missing)} missing keys:\n\t" + "\n\t".join(missing))
|
| 72 |
+
logger.warning("\n" + "-" * 79 + "\n")
|
| 73 |
+
logger.warning(f"Got {len(unexpected)} unexpected keys:\n\t" + "\n\t".join(unexpected))
|
| 74 |
+
elif len(missing) > 0:
|
| 75 |
+
logger.warning(f"Got {len(missing)} missing keys:\n\t" + "\n\t".join(missing))
|
| 76 |
+
elif len(unexpected) > 0:
|
| 77 |
+
logger.warning(f"Got {len(unexpected)} unexpected keys:\n\t" + "\n\t".join(unexpected))
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def correct_model_weight(state_dict):
|
| 81 |
+
result = {}
|
| 82 |
+
for key in state_dict.keys():
|
| 83 |
+
if "_orig_mod." in key:
|
| 84 |
+
result[key[10:]] = state_dict[key]
|
| 85 |
+
else:
|
| 86 |
+
result[key] = state_dict[key]
|
| 87 |
+
return result
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def load_hf_style_weight(pretrain_path, device):
|
| 91 |
+
index_path = os.path.join(pretrain_path, "diffusion_pytorch_model.safetensors.index.json")
|
| 92 |
+
|
| 93 |
+
with open(index_path) as f:
|
| 94 |
+
index = json.load(f)
|
| 95 |
+
|
| 96 |
+
weight_map = index["weight_map"]
|
| 97 |
+
|
| 98 |
+
sd = {}
|
| 99 |
+
loaded_shards = set()
|
| 100 |
+
|
| 101 |
+
for shard_file in weight_map.values():
|
| 102 |
+
if shard_file in loaded_shards:
|
| 103 |
+
continue
|
| 104 |
+
shard_path = os.path.join(pretrain_path, shard_file)
|
| 105 |
+
shard_sd = load_file(shard_path, device="cpu")
|
| 106 |
+
sd.update(shard_sd)
|
| 107 |
+
loaded_shards.add(shard_file)
|
| 108 |
+
|
| 109 |
+
return sd
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def load_model_weight(model, pretrain_path, device="cpu"):
|
| 113 |
+
if os.path.exists(pretrain_path):
|
| 114 |
+
logger.info(f"Loading checkpoint from {pretrain_path}")
|
| 115 |
+
try:
|
| 116 |
+
if pretrain_path.endswith("safetensors"):
|
| 117 |
+
sd = load_sft(pretrain_path, device="cpu")
|
| 118 |
+
elif os.path.exists(os.path.join(pretrain_path, "diffusion_pytorch_model.safetensors.index.json")):
|
| 119 |
+
sd = load_hf_style_weight(pretrain_path, device)
|
| 120 |
+
else:
|
| 121 |
+
sd = torch.load(pretrain_path, map_location="cpu")
|
| 122 |
+
|
| 123 |
+
sd = correct_model_weight(sd)
|
| 124 |
+
sd = optionally_expand_state_dict(model, sd)
|
| 125 |
+
missing, unexpected = model.load_state_dict(sd, strict=False, assign=True)
|
| 126 |
+
print_load_warning(missing, unexpected)
|
| 127 |
+
return True
|
| 128 |
+
except Exception as e:
|
| 129 |
+
logger.info(f"CANNOT Load {pretrain_path}, because {e}")
|
| 130 |
+
return False
|
| 131 |
+
return False
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def load_model(dit_structure: dict, pretrain_path: str | None = None):
|
| 135 |
+
logger.info("Init DiT model")
|
| 136 |
+
|
| 137 |
+
# If name is a dict, we assume it contains the parameters directly
|
| 138 |
+
# We need to determine the model class based on some heuristic or just default to MageFlow/Flux
|
| 139 |
+
# For now, let's assume it's MageFlow if time_type is present, or check other fields
|
| 140 |
+
params = MageFlowParams(**dit_structure)
|
| 141 |
+
# Default to MageFlow for now as per user context, or we could add a 'model_type' field to the dict
|
| 142 |
+
# The user mentioned "model structure option", implying we are configuring the structure.
|
| 143 |
+
# Let's assume MageFlow for this refactor as the user was using qwen-image-tiny-wo-textemb
|
| 144 |
+
model = MageFlow(params)
|
| 145 |
+
|
| 146 |
+
# logger.info(f"Loading {name if isinstance(name, str) else 'custom config'} checkpoint from {pretrain_path}")
|
| 147 |
+
if pretrain_path is not None:
|
| 148 |
+
load_model_weight(model, pretrain_path, device="cpu")
|
| 149 |
+
# if isinstance(name, str) and configs[name].lora_path is not None:
|
| 150 |
+
# logger.info("Loading LoRA")
|
| 151 |
+
# lora_sd = load_sft(configs[name].lora_path, device="cpu")
|
| 152 |
+
# # loading the lora params + overwriting scale values in the norms
|
| 153 |
+
# missing, unexpected = model.load_state_dict(lora_sd, strict=False, assign=True)
|
| 154 |
+
# print_load_warning(missing, unexpected)
|
| 155 |
+
return model
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def optionally_expand_state_dict(model: torch.nn.Module, state_dict: dict) -> dict:
|
| 159 |
+
"""
|
| 160 |
+
Optionally expand the state dict to match the model's parameters shapes.
|
| 161 |
+
"""
|
| 162 |
+
for name, param in model.named_parameters():
|
| 163 |
+
if name in state_dict:
|
| 164 |
+
if state_dict[name].shape != param.shape:
|
| 165 |
+
logger.info(
|
| 166 |
+
f"Expanding '{name}' with shape {state_dict[name].shape} to model parameter with shape "
|
| 167 |
+
f"{param.shape}."
|
| 168 |
+
)
|
| 169 |
+
# expand with zeros:
|
| 170 |
+
expanded_state_dict_weight = torch.zeros_like(param, device=state_dict[name].device)
|
| 171 |
+
slices = tuple(slice(0, dim) for dim in state_dict[name].shape)
|
| 172 |
+
expanded_state_dict_weight[slices] = state_dict[name]
|
| 173 |
+
state_dict[name] = expanded_state_dict_weight
|
| 174 |
+
|
| 175 |
+
return state_dict
|
vendor/mage_flow/pipeline.py
ADDED
|
@@ -0,0 +1,762 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""MageFlow text-to-image + image-edit inference pipeline.
|
| 2 |
+
|
| 3 |
+
Self-contained MageFlow t2i / edit inference: load a HuggingFace diffusers-style
|
| 4 |
+
repo (model_index.json + transformer/ vae/ scheduler/), then generate or edit
|
| 5 |
+
images. No training/eval deps.
|
| 6 |
+
|
| 7 |
+
Both ``generate_images`` and ``generate_edits`` support PACKED multi-resolution
|
| 8 |
+
inference: several samples (each at its own resolution) are concatenated into a
|
| 9 |
+
single varlen sequence and processed in one transformer forward per denoise
|
| 10 |
+
step. Per-sample ``cu_seqlens`` (inside the flash-attn varlen kernel) isolate
|
| 11 |
+
samples, exactly mirroring training-time packing. These packed functions are the
|
| 12 |
+
sole implementation — the single-image case is just a pack of size 1, exposed
|
| 13 |
+
via the ``MageFlowPipeline.generate`` / ``.edit`` convenience methods.
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
from __future__ import annotations
|
| 17 |
+
|
| 18 |
+
import json
|
| 19 |
+
import os
|
| 20 |
+
import random
|
| 21 |
+
|
| 22 |
+
import torch
|
| 23 |
+
from einops import rearrange
|
| 24 |
+
from PIL import Image
|
| 25 |
+
|
| 26 |
+
from diffusers import FlowMatchEulerDiscreteScheduler
|
| 27 |
+
|
| 28 |
+
from .models.mage_flow import MageFlowModel, ModelConfig
|
| 29 |
+
from .models.utils import PROMPT_TEMPLATE, get_noise, unpack
|
| 30 |
+
from .models.modules.mage_text import make_refusal_image
|
| 31 |
+
from .models.modules.mage_latent import encode_noise, resolve_gs_key
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# ---------------------------------------------------------------------------
|
| 35 |
+
# Scheduler — diffusers FlowMatchEulerDiscreteScheduler
|
| 36 |
+
# ---------------------------------------------------------------------------
|
| 37 |
+
def build_scheduler(num_steps: int, device=None, shift: float = 6.0):
|
| 38 |
+
"""Construct a diffusers ``FlowMatchEulerDiscreteScheduler`` whose sigma
|
| 39 |
+
schedule reproduces our default preset exactly.
|
| 40 |
+
|
| 41 |
+
The base sigmas ``linspace(1, 1/num_steps, num_steps)`` fed to
|
| 42 |
+
``set_timesteps`` are run through the scheduler's built-in static shift
|
| 43 |
+
``shift·s/(1+(shift-1)·s)`` and a terminal 0 is appended — the static-shift
|
| 44 |
+
schedule (the only supported schedule).
|
| 45 |
+
"""
|
| 46 |
+
scheduler = FlowMatchEulerDiscreteScheduler(
|
| 47 |
+
num_train_timesteps=1000, shift=shift, use_dynamic_shifting=False)
|
| 48 |
+
base_sigmas = torch.linspace(1.0, 1.0 / num_steps, num_steps).tolist()
|
| 49 |
+
scheduler.set_timesteps(sigmas=base_sigmas, device=device)
|
| 50 |
+
return scheduler
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def _get_scheduler(model, steps, device, static_shift):
|
| 54 |
+
scheduler = getattr(model, "scheduler", None)
|
| 55 |
+
if scheduler is None:
|
| 56 |
+
return build_scheduler(steps, device=device,
|
| 57 |
+
shift=(static_shift if static_shift is not None else 6.0))
|
| 58 |
+
if static_shift is not None:
|
| 59 |
+
scheduler.set_shift(static_shift)
|
| 60 |
+
scheduler.set_timesteps(sigmas=torch.linspace(1.0, 1.0 / steps, steps).tolist(), device=device)
|
| 61 |
+
return scheduler
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
# ---------------------------------------------------------------------------
|
| 65 |
+
# Small helpers
|
| 66 |
+
# ---------------------------------------------------------------------------
|
| 67 |
+
def _template_info(name: str | None) -> dict:
|
| 68 |
+
name = name or "mage-flow"
|
| 69 |
+
if name not in PROMPT_TEMPLATE:
|
| 70 |
+
raise ValueError(f"Unknown prompt template: {name}")
|
| 71 |
+
return PROMPT_TEMPLATE[name]
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def _as_list(val, default, n):
|
| 75 |
+
"""Broadcast a scalar/None to a length-n list, or validate a given list."""
|
| 76 |
+
if val is None:
|
| 77 |
+
return [default] * n
|
| 78 |
+
if isinstance(val, (list, tuple)):
|
| 79 |
+
if len(val) != n:
|
| 80 |
+
raise ValueError(f"expected {n} values, got {len(val)}")
|
| 81 |
+
return list(val)
|
| 82 |
+
return [val] * n
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def _lens_to_cu(lens, device):
|
| 86 |
+
"""Sequence lengths -> cumulative cu_seqlens [0, l0, l0+l1, ...] (int32)."""
|
| 87 |
+
t = torch.tensor(lens, device=device, dtype=torch.int32)
|
| 88 |
+
return torch.cat([torch.zeros(1, dtype=torch.int32, device=device),
|
| 89 |
+
torch.cumsum(t, dim=0, dtype=torch.int32)])
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def _make_divisible_by_16(size: int) -> int:
|
| 93 |
+
return max(16, 16 * (size // 16))
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def _compute_aspect_ratio_size(pil_img: Image.Image, max_size: int):
|
| 97 |
+
"""Longest side = ``max_size``, short side from aspect ratio, both /16."""
|
| 98 |
+
w, h = pil_img.size
|
| 99 |
+
if h >= w:
|
| 100 |
+
new_h, new_w = max_size, int(round(w * max_size / h))
|
| 101 |
+
else:
|
| 102 |
+
new_w, new_h = max_size, int(round(h * max_size / w))
|
| 103 |
+
return _make_divisible_by_16(new_h), _make_divisible_by_16(new_w)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def _edit_target_size(pil_img: Image.Image, max_size, height, width):
|
| 107 |
+
"""Output (H, W) for an edit sample, derived from its PRIMARY reference.
|
| 108 |
+
|
| 109 |
+
Precedence: explicit ``height`` AND ``width`` (custom size) > ``max_size``
|
| 110 |
+
(longest side, short side by aspect ratio) > the source image's own size.
|
| 111 |
+
All rounded down to a multiple of 16.
|
| 112 |
+
"""
|
| 113 |
+
if height and width:
|
| 114 |
+
return _make_divisible_by_16(height), _make_divisible_by_16(width)
|
| 115 |
+
if max_size:
|
| 116 |
+
return _compute_aspect_ratio_size(pil_img, max_size)
|
| 117 |
+
# Nothing specified: keep the source resolution (its own longest side).
|
| 118 |
+
return _compute_aspect_ratio_size(pil_img, max(pil_img.size))
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def _decode_one(model, tokens, height, width, dev):
|
| 122 |
+
"""Unpack one sample's image tokens [1, H*W, C] and VAE-decode to a PIL image."""
|
| 123 |
+
with torch.autocast(device_type=dev.type, dtype=torch.bfloat16):
|
| 124 |
+
out = model.vae.decode(unpack(tokens.float(), height, width))
|
| 125 |
+
out = rearrange(out.clamp(-1, 1), "b c h w -> b h w c")
|
| 126 |
+
out = (127.5 * (out + 1.0)).cpu().byte().numpy()
|
| 127 |
+
return Image.fromarray(out[0])
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def _build_pack_ctx(img_ids, img_cu, img_shapes, img_lens, txt, txt_cu, txt_mask, vec,
|
| 131 |
+
neg_txt, neg_cu, neg_mask, neg_vec, cfg, renormalization, batch_cfg, device):
|
| 132 |
+
"""Precompute the static per-step transformer inputs for a packed batch.
|
| 133 |
+
|
| 134 |
+
When a negative branch is present and ``batch_cfg`` is True, the conditional
|
| 135 |
+
and unconditional passes are fused into ONE varlen forward: the image tokens
|
| 136 |
+
are duplicated (cond copy + uncond copy) and the positive/negative texts are
|
| 137 |
+
concatenated, so cond sample i and uncond sample i become two independent
|
| 138 |
+
varlen segments processed in a single kernel launch. flash_attn_varlen_func
|
| 139 |
+
keeps every segment isolated via cu_seqlens, so this is numerically identical
|
| 140 |
+
to two separate forwards — just one launch instead of two.
|
| 141 |
+
"""
|
| 142 |
+
na = len(img_lens)
|
| 143 |
+
ctx = {
|
| 144 |
+
"na": na, "cfg": cfg, "renorm": renormalization, "batch_cfg": batch_cfg,
|
| 145 |
+
"has_neg": neg_txt is not None,
|
| 146 |
+
"img_ids": img_ids, "img_cu": img_cu, "img_shapes": img_shapes,
|
| 147 |
+
"img_max": int(max(img_lens)),
|
| 148 |
+
"txt": txt, "txt_ids": torch.zeros(1, txt.shape[1], 3, device=device),
|
| 149 |
+
"txt_cu": txt_cu, "txt_mask": txt_mask, "vec": vec,
|
| 150 |
+
"txt_max": int((txt_cu[1:] - txt_cu[:-1]).max().item()),
|
| 151 |
+
}
|
| 152 |
+
if neg_txt is None:
|
| 153 |
+
return ctx
|
| 154 |
+
ctx.update({
|
| 155 |
+
"neg_txt": neg_txt, "neg_ids": torch.zeros(1, neg_txt.shape[1], 3, device=device),
|
| 156 |
+
"neg_cu": neg_cu, "neg_mask": neg_mask, "neg_vec": neg_vec,
|
| 157 |
+
"neg_max": int((neg_cu[1:] - neg_cu[:-1]).max().item()),
|
| 158 |
+
})
|
| 159 |
+
if batch_cfg:
|
| 160 |
+
# Duplicate image segments (cond then uncond) and concat pos+neg text.
|
| 161 |
+
d_txt = torch.cat([txt, neg_txt], dim=1)
|
| 162 |
+
pos_lens = (txt_cu[1:] - txt_cu[:-1]).tolist()
|
| 163 |
+
neg_lens = (neg_cu[1:] - neg_cu[:-1]).tolist()
|
| 164 |
+
ctx.update({
|
| 165 |
+
"d_img_ids": torch.cat([img_ids, img_ids], dim=1),
|
| 166 |
+
"d_img_cu": _lens_to_cu(list(img_lens) + list(img_lens), device),
|
| 167 |
+
"d_img_shapes": [img_shapes[0] + img_shapes[0]],
|
| 168 |
+
"d_txt": d_txt,
|
| 169 |
+
"d_txt_ids": torch.zeros(1, d_txt.shape[1], 3, device=device),
|
| 170 |
+
"d_txt_cu": _lens_to_cu(pos_lens + neg_lens, device),
|
| 171 |
+
"d_txt_mask": torch.ones(1, d_txt.shape[1], device=device),
|
| 172 |
+
"d_vec": torch.cat([vec, neg_vec], dim=0),
|
| 173 |
+
"d_txt_max": int(max(pos_lens + neg_lens)),
|
| 174 |
+
})
|
| 175 |
+
return ctx
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def _velocity(transformer, img, ctx, sigma):
|
| 179 |
+
"""CFG-combined image-token velocity for a packed batch at noise level ``sigma``.
|
| 180 |
+
|
| 181 |
+
Returns [1, sum_img_len, C] in the conditional sample order. When
|
| 182 |
+
``batch_cfg`` is set the cond+uncond passes share a single fused varlen
|
| 183 |
+
forward; otherwise they are two forwards.
|
| 184 |
+
"""
|
| 185 |
+
dev = img.device
|
| 186 |
+
na = ctx["na"]
|
| 187 |
+
|
| 188 |
+
def _fwd(x, n, img_ids, img_cu, img_max, img_shapes, txt, txt_ids, txt_cu, txt_mask, txt_max, vec):
|
| 189 |
+
t_vec = torch.full((n,), sigma, dtype=x.dtype, device=dev)
|
| 190 |
+
return transformer(img=x, txt=txt, timesteps=t_vec, img_shapes=img_shapes,
|
| 191 |
+
img_cu_seqlens=img_cu, txt_cu_seqlens=txt_cu)
|
| 192 |
+
|
| 193 |
+
if not ctx["has_neg"]:
|
| 194 |
+
return _fwd(img, na, ctx["img_ids"], ctx["img_cu"], ctx["img_max"], ctx["img_shapes"],
|
| 195 |
+
ctx["txt"], ctx["txt_ids"], ctx["txt_cu"], ctx["txt_mask"], ctx["txt_max"], ctx["vec"])
|
| 196 |
+
|
| 197 |
+
if ctx["batch_cfg"]:
|
| 198 |
+
n_img = img.shape[1]
|
| 199 |
+
out = _fwd(torch.cat([img, img], dim=1), 2 * na,
|
| 200 |
+
ctx["d_img_ids"], ctx["d_img_cu"], ctx["img_max"], ctx["d_img_shapes"],
|
| 201 |
+
ctx["d_txt"], ctx["d_txt_ids"], ctx["d_txt_cu"], ctx["d_txt_mask"], ctx["d_txt_max"], ctx["d_vec"])
|
| 202 |
+
cond, unc = out[:, :n_img, :], out[:, n_img:, :]
|
| 203 |
+
else:
|
| 204 |
+
cond = _fwd(img, na, ctx["img_ids"], ctx["img_cu"], ctx["img_max"], ctx["img_shapes"],
|
| 205 |
+
ctx["txt"], ctx["txt_ids"], ctx["txt_cu"], ctx["txt_mask"], ctx["txt_max"], ctx["vec"])
|
| 206 |
+
unc = _fwd(img, na, ctx["img_ids"], ctx["img_cu"], ctx["img_max"], ctx["img_shapes"],
|
| 207 |
+
ctx["neg_txt"], ctx["neg_ids"], ctx["neg_cu"], ctx["neg_mask"], ctx["neg_max"], ctx["neg_vec"])
|
| 208 |
+
|
| 209 |
+
cfg = ctx["cfg"]
|
| 210 |
+
if ctx["renorm"]:
|
| 211 |
+
# CFG renormalization: rescale the guided velocity per token back to the
|
| 212 |
+
# conditional velocity's norm (reduces oversaturation at high cfg).
|
| 213 |
+
comb = unc + cfg * (cond - unc)
|
| 214 |
+
return comb * (torch.norm(cond, dim=-1, keepdim=True) /
|
| 215 |
+
(torch.norm(comb, dim=-1, keepdim=True) + 1e-6))
|
| 216 |
+
return unc + cfg * (cond - unc)
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
def _encode_texts_packed(model, prompts, template, drop_idx, device):
|
| 220 |
+
"""Encode a LIST of templated text-only prompts in ONE packed varlen forward
|
| 221 |
+
(``TextEncoder.forward`` — varlen cu_seqlens isolates each prompt,
|
| 222 |
+
verified zero cross-contamination). Returns (txt_flat [ΣLi, D], vec [N, D],
|
| 223 |
+
per-prompt token lengths list)."""
|
| 224 |
+
tokenizer = model.txt_enc.tokenizer
|
| 225 |
+
max_len = model.txt_enc.tokenizer_max_length + drop_idx
|
| 226 |
+
ids_list = [
|
| 227 |
+
tokenizer(template.format(p), max_length=max_len, truncation=True,
|
| 228 |
+
return_tensors="pt").input_ids.squeeze(0)
|
| 229 |
+
for p in prompts
|
| 230 |
+
]
|
| 231 |
+
input_ids = torch.cat(ids_list).to(device)
|
| 232 |
+
cu_seqlens = _lens_to_cu([int(t.numel()) for t in ids_list], device)
|
| 233 |
+
res = model.txt_enc(
|
| 234 |
+
input_ids, cu_seqlens, drop_idx_override=drop_idx)
|
| 235 |
+
return res["txt"], res["vec"], res["txt_seq_lens"].tolist()
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
def _slice_packed(txt_flat, vec, lens, start, count, device):
|
| 239 |
+
"""Format a contiguous ``count``-prompt slice (starting at prompt ``start``) of a
|
| 240 |
+
packed text encode into the (txt [1, ΣL, D], cu_seqlens, ones-mask, vec [count, D])
|
| 241 |
+
tuple that ``_build_pack_ctx`` consumes."""
|
| 242 |
+
seg_lens = lens[start:start + count]
|
| 243 |
+
tok_start = sum(lens[:start])
|
| 244 |
+
tok_end = tok_start + sum(seg_lens)
|
| 245 |
+
txt = txt_flat[tok_start:tok_end].reshape(1, -1, txt_flat.shape[-1]).to(device)
|
| 246 |
+
return (txt, _lens_to_cu(seg_lens, device),
|
| 247 |
+
torch.ones(1, txt.shape[1], device=device), vec[start:start + count].to(device))
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
# ---------------------------------------------------------------------------
|
| 251 |
+
# Text-to-image (packed, multi-resolution)
|
| 252 |
+
# ---------------------------------------------------------------------------
|
| 253 |
+
@torch.no_grad()
|
| 254 |
+
def generate_images(model, prompts, neg_prompts=None, seeds=None, steps=30, cfg=5.0,
|
| 255 |
+
heights=None, widths=None, device="cuda",
|
| 256 |
+
prompt_template="mage-flow", static_shift=None,
|
| 257 |
+
gs_key=None,
|
| 258 |
+
renormalization=False, batch_cfg=True):
|
| 259 |
+
"""Generate one image per prompt. Prompts may request DIFFERENT resolutions;
|
| 260 |
+
all are packed into a single varlen forward per denoise step — samples are
|
| 261 |
+
kept isolated by ``flash_attn_varlen_func`` via per-sample ``cu_seqlens`` (no
|
| 262 |
+
cross-sample attention), mirroring training-time packing. When ``cfg > 1`` and
|
| 263 |
+
``batch_cfg`` is set, the positive and negative passes are fused into that
|
| 264 |
+
same varlen forward. Returns a list of PIL images aligned with ``prompts``.
|
| 265 |
+
"""
|
| 266 |
+
if isinstance(prompts, str):
|
| 267 |
+
prompts = [prompts]
|
| 268 |
+
n = len(prompts)
|
| 269 |
+
neg_prompts = _as_list(neg_prompts, " ", n)
|
| 270 |
+
seeds = _as_list(seeds, 42, n)
|
| 271 |
+
heights = _as_list(heights, 1024, n)
|
| 272 |
+
widths = _as_list(widths, 1024, n)
|
| 273 |
+
info = _template_info(prompt_template)
|
| 274 |
+
template = info.get("template", "{}")
|
| 275 |
+
drop_idx = int(info.get("start_idx", 0))
|
| 276 |
+
dev = torch.device(device)
|
| 277 |
+
|
| 278 |
+
# Content-policy gate per sample (MANDATORY — runs on the same text-encoder
|
| 279 |
+
# weights as conditioning, no opt-out). Violating prompts get a refusal
|
| 280 |
+
# placeholder and are dropped from the pack.
|
| 281 |
+
results = [None] * n
|
| 282 |
+
active = []
|
| 283 |
+
for i in range(n):
|
| 284 |
+
if seeds[i] == -1:
|
| 285 |
+
seeds[i] = random.randint(0, 2**32 - 1)
|
| 286 |
+
verdict = model.txt_enc.screen_text(prompts[i])
|
| 287 |
+
if verdict.violates:
|
| 288 |
+
h_, w_ = _make_divisible_by_16(heights[i]), _make_divisible_by_16(widths[i])
|
| 289 |
+
print(verdict.banner())
|
| 290 |
+
results[i] = make_refusal_image(verdict, height=h_, width=w_)
|
| 291 |
+
continue
|
| 292 |
+
active.append(i)
|
| 293 |
+
if not active:
|
| 294 |
+
return results
|
| 295 |
+
|
| 296 |
+
gs_key_int = resolve_gs_key(gs_key)
|
| 297 |
+
# Per-sample noise tokens + position ids + shapes (MageVAE: flatten, no packing).
|
| 298 |
+
ch = model.vae.latent_channels
|
| 299 |
+
img_list, ids_list, lens, shapes, hw = [], [], [], [], []
|
| 300 |
+
for i in active:
|
| 301 |
+
h_, w_ = _make_divisible_by_16(heights[i]), _make_divisible_by_16(widths[i])
|
| 302 |
+
torch.manual_seed(seeds[i])
|
| 303 |
+
x = get_noise(num_samples=1, channel=ch, height=h_, width=w_,
|
| 304 |
+
device=dev, dtype=torch.bfloat16, seed=seeds[i])
|
| 305 |
+
# Distribution-preserving watermark in the initial noise (same shape,
|
| 306 |
+
# still ~N(0,1)); detect by inverting the flow ODE back to noise.
|
| 307 |
+
x = encode_noise(tuple(x.shape[1:]), key=gs_key_int,
|
| 308 |
+
seed=seeds[i], device=dev, dtype=torch.bfloat16)
|
| 309 |
+
_, _, gh, gw = x.shape
|
| 310 |
+
img_list.append(rearrange(x, "b c h w -> b (h w) c")[0])
|
| 311 |
+
ids = torch.zeros(gh, gw, 3, device=dev)
|
| 312 |
+
ids[..., 1] = ids[..., 1] + torch.arange(gh, device=dev)[:, None]
|
| 313 |
+
ids[..., 2] = ids[..., 2] + torch.arange(gw, device=dev)[None, :]
|
| 314 |
+
ids_list.append(rearrange(ids, "h w c -> (h w) c"))
|
| 315 |
+
lens.append(gh * gw); shapes.append((1, gh, gw)); hw.append((h_, w_))
|
| 316 |
+
img = torch.cat(img_list, 0).unsqueeze(0)
|
| 317 |
+
img_ids = torch.cat(ids_list, 0).unsqueeze(0)
|
| 318 |
+
img_cu = _lens_to_cu(lens, dev)
|
| 319 |
+
img_shapes = [shapes]
|
| 320 |
+
|
| 321 |
+
# Packed text: positive prompts AND (for CFG) negative prompts are encoded
|
| 322 |
+
# TOGETHER in ONE varlen forward, then split back — cu_seqlens keeps every
|
| 323 |
+
# prompt isolated (verified zero cross-contamination).
|
| 324 |
+
pos_prompts = [prompts[i] for i in active]
|
| 325 |
+
na = len(active)
|
| 326 |
+
use_neg = cfg > 1.0 and any(neg_prompts[i] for i in active)
|
| 327 |
+
if use_neg:
|
| 328 |
+
neg_list = [neg_prompts[i] or " " for i in active]
|
| 329 |
+
txt_flat, vec_all, lens_t = _encode_texts_packed(
|
| 330 |
+
model, pos_prompts + neg_list, template, drop_idx, dev)
|
| 331 |
+
txt, txt_cu, txt_mask, vec = _slice_packed(txt_flat, vec_all, lens_t, 0, na, dev)
|
| 332 |
+
neg_txt, neg_cu, neg_mask, neg_vec = _slice_packed(txt_flat, vec_all, lens_t, na, na, dev)
|
| 333 |
+
else:
|
| 334 |
+
txt_flat, vec_all, lens_t = _encode_texts_packed(model, pos_prompts, template, drop_idx, dev)
|
| 335 |
+
txt, txt_cu, txt_mask, vec = _slice_packed(txt_flat, vec_all, lens_t, 0, na, dev)
|
| 336 |
+
neg_txt = neg_cu = neg_mask = neg_vec = None
|
| 337 |
+
|
| 338 |
+
ctx = _build_pack_ctx(img_ids, img_cu, img_shapes, lens, txt, txt_cu, txt_mask, vec,
|
| 339 |
+
neg_txt, neg_cu, neg_mask, neg_vec, cfg, renormalization, batch_cfg, dev)
|
| 340 |
+
scheduler = _get_scheduler(model, steps, device, static_shift)
|
| 341 |
+
for si, t in enumerate(scheduler.timesteps):
|
| 342 |
+
pred = _velocity(model.transformer, img, ctx, scheduler.sigmas[si].item())
|
| 343 |
+
img = scheduler.step(pred, t, img, return_dict=False)[0]
|
| 344 |
+
|
| 345 |
+
off = 0
|
| 346 |
+
for k, i in enumerate(active):
|
| 347 |
+
L = lens[k]
|
| 348 |
+
h_, w_ = hw[k]
|
| 349 |
+
results[i] = _decode_one(model, img[:, off:off + L, :], h_, w_, dev)
|
| 350 |
+
off += L
|
| 351 |
+
return results
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
# ---------------------------------------------------------------------------
|
| 355 |
+
# Image edit (packed, multi-resolution)
|
| 356 |
+
# ---------------------------------------------------------------------------
|
| 357 |
+
def _preprocess_ref_image(pil_img: Image.Image, height: int, width: int, device) -> torch.Tensor:
|
| 358 |
+
"""Resize an RGB reference image to (height, width) and normalize to [-1, 1]."""
|
| 359 |
+
from torchvision.transforms import functional as TF
|
| 360 |
+
img = pil_img.convert("RGB")
|
| 361 |
+
img = TF.resize(img, [height, width], interpolation=TF.InterpolationMode.BICUBIC)
|
| 362 |
+
t = TF.to_tensor(img) # [3, H, W] in [0, 1]
|
| 363 |
+
t = TF.normalize(t, [0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) # -> [-1, 1]
|
| 364 |
+
return t.to(device)
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
def _resize_long_edge(image: Image.Image, max_long_edge: int | None) -> Image.Image:
|
| 368 |
+
"""Cap the VL conditioning image's long edge, preserving aspect ratio.
|
| 369 |
+
|
| 370 |
+
Matches training's data.processor._resize_long_edge (BICUBIC). Without this,
|
| 371 |
+
inference feeds a full-resolution image to the Qwen-VL processor whose
|
| 372 |
+
default max_pixels is far larger than 384**2 — a train/test mismatch.
|
| 373 |
+
"""
|
| 374 |
+
if max_long_edge is None or max_long_edge <= 0:
|
| 375 |
+
return image
|
| 376 |
+
w, h = image.size
|
| 377 |
+
long_edge = max(w, h)
|
| 378 |
+
if long_edge <= max_long_edge:
|
| 379 |
+
return image
|
| 380 |
+
scale = max_long_edge / long_edge
|
| 381 |
+
new_w = max(1, int(round(w * scale)))
|
| 382 |
+
new_h = max(1, int(round(h * scale)))
|
| 383 |
+
return image.resize((new_w, new_h), Image.BICUBIC)
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
# Fixed image placeholder used at edit training time (one per reference image).
|
| 387 |
+
_EDIT_IMAGE_PLACEHOLDER = "<|vision_start|><|image_pad|><|vision_end|>"
|
| 388 |
+
|
| 389 |
+
|
| 390 |
+
def _edit_prompt_body(instruction, num_refs):
|
| 391 |
+
"""Training-time multi-reference prompt body: ``Image 1: <ph>Image 2: <ph>…{instruction}``."""
|
| 392 |
+
prefix = "".join(f"Image {j}: {_EDIT_IMAGE_PLACEHOLDER}" for j in range(1, num_refs + 1))
|
| 393 |
+
return prefix + instruction
|
| 394 |
+
|
| 395 |
+
|
| 396 |
+
def _encode_edits_packed(model, ref_pils_per_sample, instructions, template, drop_idx, device):
|
| 397 |
+
"""Encode ALL image-conditioned edit instructions in ONE packed multimodal
|
| 398 |
+
varlen forward (pixel_values/image_grid_thw concatenated across samples,
|
| 399 |
+
cu_seqlens isolates each). Returns (txt_flat [ΣLi, D], vec [N, D], per-sample lens)."""
|
| 400 |
+
processor = model.txt_enc.processor
|
| 401 |
+
ids_list, pv_list, thw_list = [], [], []
|
| 402 |
+
for ref_pils, instr in zip(ref_pils_per_sample, instructions, strict=False):
|
| 403 |
+
formatted = template.format(_edit_prompt_body(instr, len(ref_pils)))
|
| 404 |
+
vl = processor(text=[formatted], images=list(ref_pils), padding=True, return_tensors="pt")
|
| 405 |
+
vl = {k: (v.to(device) if hasattr(v, "to") else v) for k, v in vl.items()}
|
| 406 |
+
ids_list.append(vl["input_ids"].squeeze(0))
|
| 407 |
+
if vl.get("pixel_values") is not None:
|
| 408 |
+
pv_list.append(vl["pixel_values"]); thw_list.append(vl["image_grid_thw"])
|
| 409 |
+
input_ids = torch.cat(ids_list).to(device)
|
| 410 |
+
cu = _lens_to_cu([int(t.numel()) for t in ids_list], device)
|
| 411 |
+
inputs = {"input_ids": input_ids, "cu_seqlens": cu}
|
| 412 |
+
if pv_list:
|
| 413 |
+
inputs["pixel_values"] = torch.cat(pv_list, dim=0)
|
| 414 |
+
inputs["image_grid_thw"] = torch.cat(thw_list, dim=0)
|
| 415 |
+
res = model.txt_enc(
|
| 416 |
+
input_ids, cu, inputs=inputs, drop_idx_override=drop_idx)
|
| 417 |
+
return res["txt"], res["vec"], res["txt_seq_lens"].tolist()
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
@torch.no_grad()
|
| 421 |
+
def generate_edits(model, prompts, ref_images, neg_prompts=None, seeds=None, steps=30, cfg=5.0,
|
| 422 |
+
max_size=None, heights=None, widths=None, device="cuda",
|
| 423 |
+
prompt_template="mage-flow-edit", static_shift=None,
|
| 424 |
+
gs_key=None,
|
| 425 |
+
vl_cond_long_edge=384,
|
| 426 |
+
renormalization=False, batch_cfg=True):
|
| 427 |
+
"""Edit reference image(s) per prompt. Each ``ref_images[i]`` may be a single
|
| 428 |
+
image/path OR a list of source images (multi-image edit, like training —
|
| 429 |
+
trained with up to 3, but more are accepted) — all produce ONE edited output. Each sample's
|
| 430 |
+
``[target, ref_1, …, ref_N]`` latent tokens are sequence-concatenated, and
|
| 431 |
+
all samples are packed into one varlen forward per denoise step.
|
| 432 |
+
|
| 433 |
+
Output resolution (derived from the first/primary reference of each sample):
|
| 434 |
+
if both ``heights[i]`` and ``widths[i]`` are given, use them; else if
|
| 435 |
+
``max_size`` is given, the longest side is ``max_size`` and the short side
|
| 436 |
+
follows the reference's aspect ratio; otherwise the output keeps the source
|
| 437 |
+
image's own resolution. All references are VAE-encoded at that target size.
|
| 438 |
+
Returns a list of PIL images.
|
| 439 |
+
"""
|
| 440 |
+
if isinstance(prompts, str):
|
| 441 |
+
prompts = [prompts]
|
| 442 |
+
ref_images = [ref_images]
|
| 443 |
+
n = len(prompts)
|
| 444 |
+
neg_prompts = _as_list(neg_prompts, " ", n)
|
| 445 |
+
seeds = _as_list(seeds, 42, n)
|
| 446 |
+
heights = _as_list(heights, None, n)
|
| 447 |
+
widths = _as_list(widths, None, n)
|
| 448 |
+
info = _template_info(prompt_template)
|
| 449 |
+
template = info.get("template", "{}")
|
| 450 |
+
drop_idx = int(info.get("start_idx", 0))
|
| 451 |
+
dev = torch.device(device)
|
| 452 |
+
|
| 453 |
+
# Normalize each sample's references to a list of 1..3 PIL images.
|
| 454 |
+
def _load_pil(r):
|
| 455 |
+
if isinstance(r, str):
|
| 456 |
+
r = Image.open(r)
|
| 457 |
+
return r.convert("RGB")
|
| 458 |
+
|
| 459 |
+
pils_per_sample = []
|
| 460 |
+
for r in ref_images:
|
| 461 |
+
refs = list(r) if isinstance(r, (list, tuple)) else [r]
|
| 462 |
+
if not refs:
|
| 463 |
+
raise ValueError("each edit sample needs at least one reference image")
|
| 464 |
+
pils_per_sample.append([_load_pil(x) for x in refs])
|
| 465 |
+
|
| 466 |
+
# Per-sample output resolution (from the first/primary reference) + content gate.
|
| 467 |
+
results = [None] * n
|
| 468 |
+
res_hw = [None] * n
|
| 469 |
+
active = []
|
| 470 |
+
for i in range(n):
|
| 471 |
+
res_hw[i] = _edit_target_size(pils_per_sample[i][0], max_size, heights[i], widths[i])
|
| 472 |
+
if seeds[i] == -1:
|
| 473 |
+
seeds[i] = random.randint(0, 2**32 - 1)
|
| 474 |
+
# Multimodal gate (MANDATORY): inspect the source image(s) AND the
|
| 475 |
+
# instruction, so NSFW / copyrighted-character / real-public-figure
|
| 476 |
+
# source photos are blocked even under an innocuous instruction.
|
| 477 |
+
verdict = model.txt_enc.screen_edit(prompts[i], pils_per_sample[i])
|
| 478 |
+
if verdict.violates:
|
| 479 |
+
h_, w_ = res_hw[i]
|
| 480 |
+
print(verdict.banner())
|
| 481 |
+
results[i] = make_refusal_image(verdict, height=h_, width=w_)
|
| 482 |
+
continue
|
| 483 |
+
active.append(i)
|
| 484 |
+
if not active:
|
| 485 |
+
return results
|
| 486 |
+
|
| 487 |
+
gs_key_int = resolve_gs_key(gs_key)
|
| 488 |
+
|
| 489 |
+
# Per sample: reference latent tokens (clean) + target noise tokens, plus the
|
| 490 |
+
# combined [target, ref_1, …, ref_N] position ids and shapes. ``target_idx``
|
| 491 |
+
# records where each sample's target tokens land in the packed sequence so we
|
| 492 |
+
# can slice the velocity and step only the target portion.
|
| 493 |
+
ch = model.vae.latent_channels
|
| 494 |
+
targets, refs, ids_list, shape_seq, samp_lens, tgt_lens, hw = [], [], [], [], [], [], []
|
| 495 |
+
target_idx_parts = []
|
| 496 |
+
off = 0
|
| 497 |
+
for i in active:
|
| 498 |
+
h_, w_ = res_hw[i]
|
| 499 |
+
torch.manual_seed(seeds[i]) # MageVAE.encode samples the posterior (global RNG)
|
| 500 |
+
# All references resized to the target resolution and VAE-encoded together.
|
| 501 |
+
ref_tensors = [_preprocess_ref_image(p, h_, w_, dev) for p in pils_per_sample[i]]
|
| 502 |
+
ref_tok, ref_shapes, ref_ids = model.compute_vae_encodings(ref_tensors, with_ids=True)
|
| 503 |
+
ref_tok = ref_tok.to(torch.bfloat16) # [1, N*Lr, C]
|
| 504 |
+
x = get_noise(num_samples=1, channel=ch, height=h_, width=w_,
|
| 505 |
+
device=dev, dtype=torch.bfloat16, seed=seeds[i])
|
| 506 |
+
x = encode_noise(tuple(x.shape[1:]), key=gs_key_int,
|
| 507 |
+
seed=seeds[i], device=dev, dtype=torch.bfloat16)
|
| 508 |
+
_, _, gh, gw = x.shape
|
| 509 |
+
tgt = rearrange(x, "b c h w -> b (h w) c") # [1, Lt, C]
|
| 510 |
+
tgt_ids = torch.zeros(gh, gw, 3, device=dev)
|
| 511 |
+
tgt_ids[..., 1] = tgt_ids[..., 1] + torch.arange(gh, device=dev)[:, None]
|
| 512 |
+
tgt_ids[..., 2] = tgt_ids[..., 2] + torch.arange(gw, device=dev)[None, :]
|
| 513 |
+
tgt_ids = rearrange(tgt_ids, "h w c -> (h w) c").unsqueeze(0)
|
| 514 |
+
lt, lr = tgt.shape[1], ref_tok.shape[1]
|
| 515 |
+
targets.append(tgt); refs.append(ref_tok)
|
| 516 |
+
ids_list.append(torch.cat([tgt_ids, ref_ids.to(dev)], dim=1)[0]) # [Lt + N*Lr, 3]
|
| 517 |
+
shape_seq.append((1, gh, gw)) # target frame idx 0
|
| 518 |
+
shape_seq.extend(s[0] for s in ref_shapes) # ref_j frame idx j
|
| 519 |
+
samp_lens.append(lt + lr); tgt_lens.append(lt); hw.append((h_, w_))
|
| 520 |
+
target_idx_parts.append(torch.arange(off, off + lt, device=dev))
|
| 521 |
+
off += lt + lr
|
| 522 |
+
img_ids = torch.cat(ids_list, 0).unsqueeze(0)
|
| 523 |
+
img_cu = _lens_to_cu(samp_lens, dev)
|
| 524 |
+
img_shapes = [shape_seq]
|
| 525 |
+
target_idx = torch.cat(target_idx_parts)
|
| 526 |
+
|
| 527 |
+
# Packed edit text — positive AND (for CFG) negative are encoded TOGETHER in
|
| 528 |
+
# ONE packed multimodal forward, then split. Both branches share the same
|
| 529 |
+
# reference images; cu_seqlens isolates every sequence (zero cross-contamination).
|
| 530 |
+
# The VL conditioning image's long edge is capped (default 384) to match
|
| 531 |
+
# training preprocessing — the VAE path above keeps the full target resolution.
|
| 532 |
+
na = len(active)
|
| 533 |
+
edit_refs = [[_resize_long_edge(p, vl_cond_long_edge) for p in pils_per_sample[i]]
|
| 534 |
+
for i in active]
|
| 535 |
+
if cfg > 1.0:
|
| 536 |
+
pos_instr = [prompts[i] for i in active]
|
| 537 |
+
neg_instr = [neg_prompts[i] or " " for i in active]
|
| 538 |
+
txt_flat, vec_all, lens_t = _encode_edits_packed(
|
| 539 |
+
model, edit_refs + edit_refs, pos_instr + neg_instr, template, drop_idx, dev)
|
| 540 |
+
txt, txt_cu, txt_mask, vec = _slice_packed(txt_flat, vec_all, lens_t, 0, na, dev)
|
| 541 |
+
neg_txt, neg_cu, neg_mask, neg_vec = _slice_packed(txt_flat, vec_all, lens_t, na, na, dev)
|
| 542 |
+
else:
|
| 543 |
+
txt_flat, vec_all, lens_t = _encode_edits_packed(
|
| 544 |
+
model, edit_refs, [prompts[i] for i in active], template, drop_idx, dev)
|
| 545 |
+
txt, txt_cu, txt_mask, vec = _slice_packed(txt_flat, vec_all, lens_t, 0, na, dev)
|
| 546 |
+
neg_txt = neg_cu = neg_mask = neg_vec = None
|
| 547 |
+
|
| 548 |
+
ctx = _build_pack_ctx(img_ids, img_cu, img_shapes, samp_lens, txt, txt_cu, txt_mask, vec,
|
| 549 |
+
neg_txt, neg_cu, neg_mask, neg_vec, cfg, renormalization, batch_cfg, dev)
|
| 550 |
+
scheduler = _get_scheduler(model, steps, device, static_shift)
|
| 551 |
+
for si, t in enumerate(scheduler.timesteps):
|
| 552 |
+
parts = []
|
| 553 |
+
for k in range(na):
|
| 554 |
+
parts.append(targets[k]); parts.append(refs[k])
|
| 555 |
+
img = torch.cat(parts, dim=1) # [1, sum(Lt+Lr), C], ref clean
|
| 556 |
+
vel = _velocity(model.transformer, img, ctx, scheduler.sigmas[si].item())
|
| 557 |
+
pred_t = vel[:, target_idx, :] # [1, sum Lt, C] — target tokens only
|
| 558 |
+
tgt_packed = torch.cat(targets, dim=1) # [1, sum Lt, C]
|
| 559 |
+
stepped = scheduler.step(pred_t, t, tgt_packed, return_dict=False)[0]
|
| 560 |
+
o = 0
|
| 561 |
+
new_targets = []
|
| 562 |
+
for k in range(na):
|
| 563 |
+
lt = tgt_lens[k]
|
| 564 |
+
new_targets.append(stepped[:, o:o + lt, :]); o += lt
|
| 565 |
+
targets = new_targets
|
| 566 |
+
|
| 567 |
+
for k, i in enumerate(active):
|
| 568 |
+
h_, w_ = hw[k]
|
| 569 |
+
results[i] = _decode_one(model, targets[k], h_, w_, dev)
|
| 570 |
+
return results
|
| 571 |
+
|
| 572 |
+
|
| 573 |
+
# ---------------------------------------------------------------------------
|
| 574 |
+
# Flow-ODE inversion (Gaussian-Shading watermark detection)
|
| 575 |
+
# ---------------------------------------------------------------------------
|
| 576 |
+
@torch.no_grad()
|
| 577 |
+
def invert_to_noise(model, z0, height, width, steps=30, device="cuda",
|
| 578 |
+
prompt_template="mage-flow", static_shift=None, prompt=""):
|
| 579 |
+
"""Reverse the flow ODE from a clean latent ``z0`` back to the initial noise.
|
| 580 |
+
|
| 581 |
+
This is the detection primitive for the Gaussian-Shading watermark: VAE-encode
|
| 582 |
+
the image to ``z0`` (posterior MEAN — deterministic), run this to recover the
|
| 583 |
+
initial noise, then read the signs via ``mage_latent.decode_bits``.
|
| 584 |
+
|
| 585 |
+
Inversion uses an empty prompt at cfg=1 (the standard Tree-Ring /
|
| 586 |
+
Gaussian-Shading setup). Reverse Euler recovers ``x_i`` from ``x_{i+1}`` with
|
| 587 |
+
the velocity evaluated at the point in hand; the sign-only watermark tolerates
|
| 588 |
+
the resulting approximation error (see the module's redundancy).
|
| 589 |
+
|
| 590 |
+
Args:
|
| 591 |
+
z0: clean latent ``[1, C, gh, gw]`` (e.g. the mean of ``model.vae.encode``).
|
| 592 |
+
Returns:
|
| 593 |
+
recovered initial-noise latent ``[1, C, gh, gw]`` (float32).
|
| 594 |
+
"""
|
| 595 |
+
dev = torch.device(device)
|
| 596 |
+
info = _template_info(prompt_template)
|
| 597 |
+
template = info.get("template", "{}")
|
| 598 |
+
drop_idx = int(info.get("start_idx", 0))
|
| 599 |
+
|
| 600 |
+
z0 = z0.to(dev)
|
| 601 |
+
_, ch, gh, gw = z0.shape
|
| 602 |
+
img = rearrange(z0, "b c h w -> b (h w) c").to(torch.bfloat16) # [1, gh*gw, C]
|
| 603 |
+
|
| 604 |
+
ids = torch.zeros(gh, gw, 3, device=dev)
|
| 605 |
+
ids[..., 1] = ids[..., 1] + torch.arange(gh, device=dev)[:, None]
|
| 606 |
+
ids[..., 2] = ids[..., 2] + torch.arange(gw, device=dev)[None, :]
|
| 607 |
+
img_ids = rearrange(ids, "h w c -> (h w) c").unsqueeze(0)
|
| 608 |
+
lens = [gh * gw]
|
| 609 |
+
img_cu = _lens_to_cu(lens, dev)
|
| 610 |
+
img_shapes = [[(1, gh, gw)]]
|
| 611 |
+
|
| 612 |
+
# Empty-prompt conditioning, no negative branch, cfg=1 (single forward).
|
| 613 |
+
txt_flat, vec_all, lens_t = _encode_texts_packed(model, [prompt], template, drop_idx, dev)
|
| 614 |
+
txt, txt_cu, txt_mask, vec = _slice_packed(txt_flat, vec_all, lens_t, 0, 1, dev)
|
| 615 |
+
ctx = _build_pack_ctx(img_ids, img_cu, img_shapes, lens, txt, txt_cu, txt_mask, vec,
|
| 616 |
+
None, None, None, None, 1.0, False, False, dev)
|
| 617 |
+
|
| 618 |
+
scheduler = _get_scheduler(model, steps, device, static_shift)
|
| 619 |
+
sigmas = scheduler.sigmas
|
| 620 |
+
n = len(scheduler.timesteps)
|
| 621 |
+
# Forward step si: x_{si+1} = x_si + (s_{si+1}-s_si)·v(x_si, s_si).
|
| 622 |
+
# Reverse it from clean (x_n, sigma 0) up to noise (x_0), using x_{si+1} as the
|
| 623 |
+
# proxy for x_si at the forward eval sigma s_si.
|
| 624 |
+
for si in range(n - 1, -1, -1):
|
| 625 |
+
s_cur = sigmas[si].item()
|
| 626 |
+
s_next = sigmas[si + 1].item()
|
| 627 |
+
vel = _velocity(model.transformer, img, ctx, s_cur)
|
| 628 |
+
img = img - (s_next - s_cur) * vel
|
| 629 |
+
return unpack(img.float(), height, width) # [1, C, gh, gw]
|
| 630 |
+
|
| 631 |
+
|
| 632 |
+
# ---------------------------------------------------------------------------
|
| 633 |
+
# High-level pipeline wrapper
|
| 634 |
+
# ---------------------------------------------------------------------------
|
| 635 |
+
class MageFlowPipeline:
|
| 636 |
+
"""``MageFlowPipeline.from_pretrained(repo).generate(...) / .edit(...)``.
|
| 637 |
+
|
| 638 |
+
``generate`` / ``edit`` are packed multi-resolution calls: they take a list
|
| 639 |
+
of prompts (a single string is accepted and treated as a pack of size 1) and
|
| 640 |
+
return a list of PIL images. Per-sample ``heights``/``widths``/``seeds`` are
|
| 641 |
+
lists. Every prompt is screened by the text encoder's mandatory content
|
| 642 |
+
gate (no opt-out); banned prompts come back as refusal placeholders
|
| 643 |
+
interleaved with the real images. Real outputs always carry a Gaussian-Shading
|
| 644 |
+
watermark in the initial noise (no toggle), using the configured secret key.
|
| 645 |
+
"""
|
| 646 |
+
|
| 647 |
+
def __init__(self, model, device="cuda"):
|
| 648 |
+
self.model = model
|
| 649 |
+
self.device = device
|
| 650 |
+
|
| 651 |
+
@classmethod
|
| 652 |
+
def from_pretrained(cls, repo_dir: str, device: str = "cuda"):
|
| 653 |
+
"""Load a Mage-Flow diffusers-style repo (``model_index.json`` +
|
| 654 |
+
``transformer/`` ``vae/`` ``scheduler/`` ``text_encoder/``).
|
| 655 |
+
|
| 656 |
+
``repo_dir`` may be a local directory OR a Hugging Face Hub repo id
|
| 657 |
+
(e.g. ``"microsoft/Mage-Flow-4B"``), which is downloaded and cached
|
| 658 |
+
automatically on first use.
|
| 659 |
+
"""
|
| 660 |
+
return cls(load_from_repo(repo_dir, device), device)
|
| 661 |
+
|
| 662 |
+
def generate(self, prompts, **kw) -> list[Image.Image]:
|
| 663 |
+
"""Packed multi-resolution t2i. ``prompts`` is a list (or a single
|
| 664 |
+
string); pass per-sample ``heights``/``widths``/``seeds`` as lists."""
|
| 665 |
+
kw.setdefault("device", self.device)
|
| 666 |
+
return generate_images(self.model, prompts, **kw)
|
| 667 |
+
|
| 668 |
+
def edit(self, prompts, ref_images, **kw) -> list[Image.Image]:
|
| 669 |
+
"""Packed multi-resolution edit. ``prompts`` is a list (or a single
|
| 670 |
+
string); each ``ref_images[i]`` is one reference or a list of references."""
|
| 671 |
+
kw.setdefault("device", self.device)
|
| 672 |
+
return generate_edits(self.model, prompts, ref_images, **kw)
|
| 673 |
+
|
| 674 |
+
def invert_to_noise(self, z0, height, width, **kw):
|
| 675 |
+
"""Recover the initial noise from a clean latent (Gaussian-Shading detect)."""
|
| 676 |
+
kw.setdefault("device", self.device)
|
| 677 |
+
return invert_to_noise(self.model, z0, height, width, **kw)
|
| 678 |
+
|
| 679 |
+
|
| 680 |
+
def _safe_subpath(root: str, *parts: str) -> str:
|
| 681 |
+
"""Join ``parts`` under ``root`` and confirm the result stays inside ``root``.
|
| 682 |
+
|
| 683 |
+
``root`` is normalized up front; the joined path is normalized **lexically**
|
| 684 |
+
(``os.path.normpath`` — symlinks are *not* followed, so a Hugging Face cache
|
| 685 |
+
whose weight files are symlinks into the shared blob store still loads) and
|
| 686 |
+
rejected if it escapes ``root``. This guards the user-supplied model path
|
| 687 |
+
against path traversal (CWE-22 / CodeQL ``py/path-injection``).
|
| 688 |
+
"""
|
| 689 |
+
root = os.path.realpath(root)
|
| 690 |
+
full = os.path.normpath(os.path.join(root, *parts))
|
| 691 |
+
if full != root and not full.startswith(root + os.sep):
|
| 692 |
+
raise ValueError(
|
| 693 |
+
f"Resolved path {os.path.join(*parts)!r} escapes repo directory {root!r}"
|
| 694 |
+
)
|
| 695 |
+
return full
|
| 696 |
+
|
| 697 |
+
|
| 698 |
+
def _resolve_repo_dir(repo_dir: str) -> str:
|
| 699 |
+
"""Return a local directory for ``repo_dir``.
|
| 700 |
+
|
| 701 |
+
If ``repo_dir`` is an existing local path it is returned as a normalized
|
| 702 |
+
absolute path; otherwise it is treated as a Hugging Face Hub repo id (e.g.
|
| 703 |
+
``microsoft/Mage-Flow``) and downloaded/cached via
|
| 704 |
+
``huggingface_hub.snapshot_download``.
|
| 705 |
+
"""
|
| 706 |
+
candidate = os.path.realpath(repo_dir)
|
| 707 |
+
if os.path.isdir(candidate):
|
| 708 |
+
return candidate
|
| 709 |
+
from huggingface_hub import snapshot_download
|
| 710 |
+
return snapshot_download(repo_id=repo_dir)
|
| 711 |
+
|
| 712 |
+
|
| 713 |
+
def load_from_repo(repo_dir: str, device: str = "cuda") -> MageFlowModel:
|
| 714 |
+
"""Load a Mage-Flow diffusers-style repo (model_index.json + transformer/
|
| 715 |
+
vae/ scheduler/). Transformer weights come from the bf16 safetensors;
|
| 716 |
+
VAE + text encoder are built from the sources recorded in model_index.json.
|
| 717 |
+
|
| 718 |
+
``repo_dir`` may be a local directory OR a Hugging Face Hub repo id (e.g.
|
| 719 |
+
``microsoft/Mage-Flow-4B``), which is downloaded/cached automatically.
|
| 720 |
+
"""
|
| 721 |
+
from safetensors.torch import load_file
|
| 722 |
+
repo_dir = _resolve_repo_dir(repo_dir)
|
| 723 |
+
mi = json.load(open(_safe_subpath(repo_dir, "model_index.json")))
|
| 724 |
+
tcfg = json.load(open(_safe_subpath(repo_dir, "transformer", "config.json")))
|
| 725 |
+
# Keys stripped from the checkpoint config before it becomes model_structure.
|
| 726 |
+
# ``schedule_mode`` is a legacy field still present in some config.json files;
|
| 727 |
+
# Keys of the checkpoint config that are NOT MageFlowParams constructor args
|
| 728 |
+
# (legacy/unused fields). Everything else becomes model_structure. The DiT only
|
| 729 |
+
# reads: in_channels, out_channels, context_in_dim, hidden_size, num_heads,
|
| 730 |
+
# depth, axes_dim, checkpoint, patch_size.
|
| 731 |
+
_meta = {"_class_name", "txt_max_length", "max_sequence_length", "param_dtype",
|
| 732 |
+
"packing", "schedule_mode", "static_shift", "use_time_shift",
|
| 733 |
+
"rope_type", "apply_text_rotary_emb",
|
| 734 |
+
"mlp_ratio", "depth_single_blocks", "theta", "qkv_bias", "guidance_embed",
|
| 735 |
+
"vec_in_dim", "vec_type", "time_type", "double_block_type"}
|
| 736 |
+
structure = {k: v for k, v in tcfg.items() if k not in _meta}
|
| 737 |
+
|
| 738 |
+
def _resolve(p):
|
| 739 |
+
return p if os.path.isabs(p) else _safe_subpath(repo_dir, p)
|
| 740 |
+
|
| 741 |
+
cfg = ModelConfig(
|
| 742 |
+
vae_path=_resolve(mi.get("_vae_source")),
|
| 743 |
+
txt_enc_path=_resolve(mi.get("_text_encoder_path")),
|
| 744 |
+
model_structure=structure,
|
| 745 |
+
txt_max_length=tcfg.get("txt_max_length", 2048),
|
| 746 |
+
packing=tcfg.get("packing", True),
|
| 747 |
+
static_shift=tcfg.get("static_shift", 6.0),
|
| 748 |
+
)
|
| 749 |
+
model = MageFlowModel(cfg)
|
| 750 |
+
sd = load_file(_safe_subpath(repo_dir, "transformer", "diffusion_pytorch_model.safetensors"),
|
| 751 |
+
device="cpu")
|
| 752 |
+
model.transformer.load_state_dict(sd, strict=False, assign=True)
|
| 753 |
+
model.to(device)
|
| 754 |
+
model.transformer.to(torch.bfloat16)
|
| 755 |
+
model.txt_enc.to(torch.bfloat16)
|
| 756 |
+
if model.vae is not None:
|
| 757 |
+
model.vae.to(torch.bfloat16)
|
| 758 |
+
model.eval()
|
| 759 |
+
# Diffusers FlowMatchEulerDiscreteScheduler (scheduler/scheduler_config.json).
|
| 760 |
+
model.scheduler = FlowMatchEulerDiscreteScheduler.from_pretrained(
|
| 761 |
+
_safe_subpath(repo_dir, "scheduler"))
|
| 762 |
+
return model
|