Andyson commited on
Commit
47165cf
·
verified ·
1 Parent(s): 8e256ec

Release WAN Fun Control LoRA: training step 1600, 4-step CFG-free inference

Browse files
README.md ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: other
3
+ base_model: alibaba-pai/Wan2.2-Fun-5B-Control
4
+ base_model_relation: adapter
5
+ library_name: peft
6
+ pipeline_tag: video-to-video
7
+ model_name: WAN Fun LoRA - 5B Control - train step 1600 - 4-step CFG-free (CFG 1)
8
+ language:
9
+ - en
10
+ - zh
11
+ datasets:
12
+ - Yanbin99/Depth-Normal-Videos-42K
13
+ - fal/cosmos-openvid-1m
14
+ tags:
15
+ - wan2.2
16
+ - wan-fun
17
+ - controlnet
18
+ - depth-control
19
+ - lora
20
+ - dmd
21
+ - non-ar
22
+ - four-step
23
+ - cfg-free
24
+ - cfg1
25
+ - step1600
26
+ - rank64
27
+ ---
28
+
29
+ # WAN Fun LoRA: 5B Control, step 1600, 4-step, CFG 1
30
+
31
+ This is the **training-step-1600 generator LoRA** distilled directly on
32
+ [alibaba-pai/Wan2.2-Fun-5B-Control](https://huggingface.co/alibaba-pai/Wan2.2-Fun-5B-Control)
33
+ using prompt + dynamic Depth video pairs. It is intended for **four denoising
34
+ steps with `guidance_scale=1.0`**, which disables classifier-free guidance.
35
+
36
+ 中文:这是专门在 **Wan2.2-Fun-5B-Control** 上训练的 DMD LoRA。发布训练
37
+ **1600 step** 检查点;推理 **4 steps、CFG=1(关闭 CFG)**。它就是本次
38
+ PAI-Bench-C Depth 评测使用的权重。训练时 generator、teacher、critic 均加载
39
+ 完整 Fun-Control 模型;发布文件仅包含推理所需的 generator LoRA。
40
+
41
+ | Setting | Value |
42
+ | --- | --- |
43
+ | Training checkpoint | **step 1600** |
44
+ | Inference denoising steps | **4** |
45
+ | Inference CFG | **1.0, conditional-only, no CFG branch** |
46
+ | LoRA strength | **1.0** |
47
+ | Rank / alpha | 64 / 64 |
48
+ | Scheduler | VideoX-Fun `FlowUniPCMultistepScheduler` |
49
+ | Scheduler configuration shift / runtime shift | 1.0 / 5.0 |
50
+ | Observed four timesteps | `[999, 937, 833, 624]` |
51
+ | TeaCache | Disabled for four-step inference |
52
+ | Base revision used | `b8bc1a65ab71d054ba4636dc0dac104aa4df2686` |
53
+
54
+ `step1600` describes the saved training iteration. `4Step` describes inference.
55
+ CFG and LoRA strength are separate settings. The LoRA residual is
56
+ `strength * (alpha/rank) * (B @ A)`.
57
+
58
+ During DMD training the teacher used `real_guidance_scale=3`, with the formula
59
+ `conditional + 3 * (conditional - unconditional)`. This corresponds to a
60
+ conventional teacher CFG scale of **4**, while the student/critic branch and
61
+ the released inference recipe use conventional **CFG 1**.
62
+
63
+ ## Training and provenance
64
+
65
+ The run used two nodes with eight H100 GPUs each, microbatch one per GPU,
66
+ gradient accumulation two, and effective global batch 32. It reused the exact
67
+ 5,000 prompt + dynamic-depth pairs from the preceding ControlNet Depth run:
68
+
69
+ - Depth videos: [Yanbin99/Depth-Normal-Videos-42K](https://huggingface.co/datasets/Yanbin99/Depth-Normal-Videos-42K), revision `9c5d205edd71077bbab7d96cae971a25a1fbf91f`.
70
+ - Corresponding captions: [fal/cosmos-openvid-1m](https://huggingface.co/datasets/fal/cosmos-openvid-1m), revision `10b41fc29006eff62ff64b8795b8ae8ef7ff9cde`.
71
+ - Training geometry: 832 x 480, 93 raw video frames, 24 latent frames.
72
+ - Generator, frozen teacher, and trainable critic each load the full Fun-Control DiT.
73
+ - Generator and critic have independent rank-64 adapters; the teacher is frozen.
74
+
75
+ The published adapter contains 600 FP32 tensors over 300 Linear layers in
76
+ 30 blocks: self-attention q/k/v/o, cross-attention q/k/v/o, and FFN 0/2.
77
+ There are 161,218,560 adapter parameters. The full Fun-Control backbone and
78
+ its learned control conditioning remain required.
79
+
80
+ The source training bundle SHA256 is
81
+ `47f94f7fc85ed53b5480b28c2bfa3a05659bcf4aeb1278004c0860224787e78a`.
82
+ Both exported formats were compared tensor-by-tensor to its `generator_lora`
83
+ state. All 3,600 LoRA video inference receipts from this evaluation record
84
+ the same source checksum, complete loading of 600 tensors / 300 target layers,
85
+ four denoising steps and CFG 1.
86
+
87
+ ## Loading
88
+
89
+ Use the native VideoX-Fun Wan2.2-Fun-5B-Control pipeline. The evaluated
90
+ VideoX-Fun source revision was `1fd9ed9208ba280f6b48c542a7739e636f4e2a14`.
91
+ Start from its [5B Control example](https://github.com/aigc-apps/VideoX-Fun/blob/1fd9ed9208ba280f6b48c542a7739e636f4e2a14/examples/wan2.2_fun/predict_v2v_control_5b.py).
92
+
93
+ Download the adapter and loader:
94
+
95
+ ```python
96
+ from huggingface_hub import snapshot_download
97
+
98
+ release = snapshot_download(
99
+ "Perflow-Shuai/WAN-Fun-LoRA-5B-Control-4Step-CFG1-step1600",
100
+ allow_patterns=["generator_lora.pt", "perflow_lora.py", "load_wan_fun_lora.py"],
101
+ )
102
+ ```
103
+
104
+ After the example loads the full Control transformer, and **before** applying
105
+ CPU/group offloading, attach this adapter once:
106
+
107
+ ```python
108
+ import sys
109
+ from pathlib import Path
110
+
111
+ sys.path.insert(0, release)
112
+ from load_wan_fun_lora import load_wan_fun_lora
113
+
114
+ receipt = load_wan_fun_lora(
115
+ transformer,
116
+ Path(release) / "generator_lora.pt",
117
+ strength=1.0,
118
+ )
119
+ assert receipt["loaded_targets"] == 300
120
+ assert receipt["loaded_tensors"] == 600
121
+ ```
122
+
123
+ In that example, set `sampler_name="Flow_Unipc"`, `num_inference_steps=4`,
124
+ `guidance_scale=1.0`, `shift=5`, and `enable_teacache=False`. Its UniPC branch
125
+ sets the scheduler constructor's shift to 1 and the pipeline call applies
126
+ runtime shift 5. Supply your prompt and dynamic depth video via its existing
127
+ control-video inputs. Preserve the model's depth/inpainting/reference
128
+ conditioning preprocessing, including the learned `ref_conv` behavior.
129
+
130
+ `adapter_model.safetensors` plus `adapter_config.json` is the equivalent
131
+ PEFT/native-Wan export. Keys use `blocks.*.self_attn.*`, `cross_attn.*`, and
132
+ `ffn.*`. Diffusers-specific key conversion and generic ComfyUI LoRA loading
133
+ were not validated for this release. The included loader takes
134
+ `generator_lora.pt`; its shared implementation is in `perflow_lora.py`.
135
+
136
+ ## PAI-Bench-C Depth evaluation
137
+
138
+ We evaluated 600 Depth cases, with six prompt variants per control for
139
+ diversity (3,600 generated videos per local model). Local outputs use 121
140
+ frames at 24 FPS and seed 43. The four-step variants use identical inputs,
141
+ resolution, seed and scheduler; their difference is this adapter at strength 1.
142
+
143
+ | Model | Blur SSIM ↑ | Edge F1 ↑ | Depth si-RMSE ↓ | Mask mIoU ↑ | Quality ↑ | Diversity ↑ |
144
+ | --- | ---: | ---: | ---: | ---: | ---: | ---: |
145
+ | Official reported Wan2.2-Fun-5B-Control | 0.556 | 0.106 | 1.819 | 0.615 | 9.317 | 0.481 |
146
+ | Local base, 40 steps, CFG 6, TeaCache | 0.557 | 0.099 | 4.836 | 0.600 | 8.962 | 0.474 |
147
+ | Local base, 4 steps, CFG 1 | 0.682 | 0.089 | 2.654 | 0.637 | 8.896 | 0.248 |
148
+ | **WAN Fun LoRA step1600, 4 steps, CFG 1** | **0.544** | **0.092** | **1.805** | **0.589** | **9.290** | **0.458** |
149
+
150
+ The official row is quoted from [PAI-Bench](https://github.com/SHI-Labs/physical-ai-bench)
151
+ and was not run by us. Local main metrics use its contemporaneous legacy
152
+ evaluator; diversity uses the current implementation, whose source is identical
153
+ across the two snapshots. Exact revisions and values are in `evaluation.json`.
154
+ The official run's private configuration is unavailable, so the local multistep
155
+ row is a best-effort reproduction. Legacy depth RMSE has outliers; these rows
156
+ should not be read as proof that the LoRA surpasses the official base overall.
157
+
158
+ Compared with direct four-step inference, this LoRA improves Depth, Quality,
159
+ Diversity and slightly Edge F1, while Blur SSIM and Mask mIoU decrease.
160
+ Blur SSIM measures similarity after blurring and is not a sharpness metric.
161
+ The adapter was trained for Depth; other control modalities and transfer to
162
+ unmodified Wan backbones are not established by these results.
163
+
164
+ ## Files and terms
165
+
166
+ - `generator_lora.pt`: generator-only payload with `generator_lora` and `step`.
167
+ - `adapter_model.safetensors`, `adapter_config.json`: equivalent FP32 adapter.
168
+ - `load_wan_fun_lora.py`, `perflow_lora.py`: strict native-Wan loader.
169
+ - `training_config.yaml`, `inference_config.yaml`: training and inference settings.
170
+ - `release_metadata.json`, `provenance.json`, `evaluation.json`, `SHA256SUMS`: provenance, measured results and checksums.
171
+
172
+ The upstream [Fun-Control model card](https://huggingface.co/alibaba-pai/Wan2.2-Fun-5B-Control)
173
+ declares Apache-2.0; the linked depth dataset declares MIT and the caption
174
+ dataset declares Apache-2.0. This release follows the preceding depth-conditioned
175
+ adapter's `license: other` designation because upstream source-video rights
176
+ are not established by those dataset labels alone. Consult the respective
177
+ upstream terms. Original training videos, captions, base-model weights and
178
+ critic weights are not redistributed here.
SHA256SUMS ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ f9497abecbce0e35c2a8242066adbbbb41b9b2a94829d8fdc4200cf6e6790a72 README.md
2
+ 13c103b922401eeb48aeb84d1c8533c3aa4a8a514aaad329d4f960ed7d01bd56 adapter_config.json
3
+ cb06b699fe5bdbb9e5750ca177bf61e74fbb32d150e4dcaf55f9141b0f169278 adapter_model.safetensors
4
+ e173b37254baa5945aa52d23dd2faf28f0f49a41f9c24e3864d9100ef9967150 evaluation.json
5
+ 12946fdfac2a99643f259e721764b728e6e905c1e838e104c24b87b0757facc8 generator_lora.pt
6
+ 6fafca458ab88bc032c17c8ade3a24e2861143a9cd0aab70f1e7cfb1cc0203f7 inference_config.yaml
7
+ 73748f6691dc826acaf87ef2bc1c0ae5b9dea79c0bdc3418f40bf480dded3be4 load_wan_fun_lora.py
8
+ 4ee88e7cfb4d803f97622809d5f483187a061b7f53d77748b5850e438dba08b8 perflow_lora.py
9
+ 7ba884eb930d768d626385261e7a8d6ab8c645ef064b9b07da57347d84c32a85 provenance.json
10
+ 7fe349696daf2c5574f2cc74615684bd9e74aa9f9ed44416cbc050a3fd905836 release_metadata.json
11
+ 2e7640e256ca8192cc4988ab19119f86d350440438cac2d3a782dc43a1166108 training_config.yaml
adapter_config.json ADDED
@@ -0,0 +1,317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "base_model_name_or_path": "alibaba-pai/Wan2.2-Fun-5B-Control",
3
+ "bias": "none",
4
+ "fan_in_fan_out": false,
5
+ "inference_mode": true,
6
+ "init_lora_weights": true,
7
+ "lora_alpha": 64,
8
+ "lora_dropout": 0.0,
9
+ "modules_to_save": null,
10
+ "peft_type": "LORA",
11
+ "r": 64,
12
+ "revision": "b8bc1a65ab71d054ba4636dc0dac104aa4df2686",
13
+ "target_modules": [
14
+ "blocks.0.cross_attn.k",
15
+ "blocks.0.cross_attn.o",
16
+ "blocks.0.cross_attn.q",
17
+ "blocks.0.cross_attn.v",
18
+ "blocks.0.ffn.0",
19
+ "blocks.0.ffn.2",
20
+ "blocks.0.self_attn.k",
21
+ "blocks.0.self_attn.o",
22
+ "blocks.0.self_attn.q",
23
+ "blocks.0.self_attn.v",
24
+ "blocks.1.cross_attn.k",
25
+ "blocks.1.cross_attn.o",
26
+ "blocks.1.cross_attn.q",
27
+ "blocks.1.cross_attn.v",
28
+ "blocks.1.ffn.0",
29
+ "blocks.1.ffn.2",
30
+ "blocks.1.self_attn.k",
31
+ "blocks.1.self_attn.o",
32
+ "blocks.1.self_attn.q",
33
+ "blocks.1.self_attn.v",
34
+ "blocks.10.cross_attn.k",
35
+ "blocks.10.cross_attn.o",
36
+ "blocks.10.cross_attn.q",
37
+ "blocks.10.cross_attn.v",
38
+ "blocks.10.ffn.0",
39
+ "blocks.10.ffn.2",
40
+ "blocks.10.self_attn.k",
41
+ "blocks.10.self_attn.o",
42
+ "blocks.10.self_attn.q",
43
+ "blocks.10.self_attn.v",
44
+ "blocks.11.cross_attn.k",
45
+ "blocks.11.cross_attn.o",
46
+ "blocks.11.cross_attn.q",
47
+ "blocks.11.cross_attn.v",
48
+ "blocks.11.ffn.0",
49
+ "blocks.11.ffn.2",
50
+ "blocks.11.self_attn.k",
51
+ "blocks.11.self_attn.o",
52
+ "blocks.11.self_attn.q",
53
+ "blocks.11.self_attn.v",
54
+ "blocks.12.cross_attn.k",
55
+ "blocks.12.cross_attn.o",
56
+ "blocks.12.cross_attn.q",
57
+ "blocks.12.cross_attn.v",
58
+ "blocks.12.ffn.0",
59
+ "blocks.12.ffn.2",
60
+ "blocks.12.self_attn.k",
61
+ "blocks.12.self_attn.o",
62
+ "blocks.12.self_attn.q",
63
+ "blocks.12.self_attn.v",
64
+ "blocks.13.cross_attn.k",
65
+ "blocks.13.cross_attn.o",
66
+ "blocks.13.cross_attn.q",
67
+ "blocks.13.cross_attn.v",
68
+ "blocks.13.ffn.0",
69
+ "blocks.13.ffn.2",
70
+ "blocks.13.self_attn.k",
71
+ "blocks.13.self_attn.o",
72
+ "blocks.13.self_attn.q",
73
+ "blocks.13.self_attn.v",
74
+ "blocks.14.cross_attn.k",
75
+ "blocks.14.cross_attn.o",
76
+ "blocks.14.cross_attn.q",
77
+ "blocks.14.cross_attn.v",
78
+ "blocks.14.ffn.0",
79
+ "blocks.14.ffn.2",
80
+ "blocks.14.self_attn.k",
81
+ "blocks.14.self_attn.o",
82
+ "blocks.14.self_attn.q",
83
+ "blocks.14.self_attn.v",
84
+ "blocks.15.cross_attn.k",
85
+ "blocks.15.cross_attn.o",
86
+ "blocks.15.cross_attn.q",
87
+ "blocks.15.cross_attn.v",
88
+ "blocks.15.ffn.0",
89
+ "blocks.15.ffn.2",
90
+ "blocks.15.self_attn.k",
91
+ "blocks.15.self_attn.o",
92
+ "blocks.15.self_attn.q",
93
+ "blocks.15.self_attn.v",
94
+ "blocks.16.cross_attn.k",
95
+ "blocks.16.cross_attn.o",
96
+ "blocks.16.cross_attn.q",
97
+ "blocks.16.cross_attn.v",
98
+ "blocks.16.ffn.0",
99
+ "blocks.16.ffn.2",
100
+ "blocks.16.self_attn.k",
101
+ "blocks.16.self_attn.o",
102
+ "blocks.16.self_attn.q",
103
+ "blocks.16.self_attn.v",
104
+ "blocks.17.cross_attn.k",
105
+ "blocks.17.cross_attn.o",
106
+ "blocks.17.cross_attn.q",
107
+ "blocks.17.cross_attn.v",
108
+ "blocks.17.ffn.0",
109
+ "blocks.17.ffn.2",
110
+ "blocks.17.self_attn.k",
111
+ "blocks.17.self_attn.o",
112
+ "blocks.17.self_attn.q",
113
+ "blocks.17.self_attn.v",
114
+ "blocks.18.cross_attn.k",
115
+ "blocks.18.cross_attn.o",
116
+ "blocks.18.cross_attn.q",
117
+ "blocks.18.cross_attn.v",
118
+ "blocks.18.ffn.0",
119
+ "blocks.18.ffn.2",
120
+ "blocks.18.self_attn.k",
121
+ "blocks.18.self_attn.o",
122
+ "blocks.18.self_attn.q",
123
+ "blocks.18.self_attn.v",
124
+ "blocks.19.cross_attn.k",
125
+ "blocks.19.cross_attn.o",
126
+ "blocks.19.cross_attn.q",
127
+ "blocks.19.cross_attn.v",
128
+ "blocks.19.ffn.0",
129
+ "blocks.19.ffn.2",
130
+ "blocks.19.self_attn.k",
131
+ "blocks.19.self_attn.o",
132
+ "blocks.19.self_attn.q",
133
+ "blocks.19.self_attn.v",
134
+ "blocks.2.cross_attn.k",
135
+ "blocks.2.cross_attn.o",
136
+ "blocks.2.cross_attn.q",
137
+ "blocks.2.cross_attn.v",
138
+ "blocks.2.ffn.0",
139
+ "blocks.2.ffn.2",
140
+ "blocks.2.self_attn.k",
141
+ "blocks.2.self_attn.o",
142
+ "blocks.2.self_attn.q",
143
+ "blocks.2.self_attn.v",
144
+ "blocks.20.cross_attn.k",
145
+ "blocks.20.cross_attn.o",
146
+ "blocks.20.cross_attn.q",
147
+ "blocks.20.cross_attn.v",
148
+ "blocks.20.ffn.0",
149
+ "blocks.20.ffn.2",
150
+ "blocks.20.self_attn.k",
151
+ "blocks.20.self_attn.o",
152
+ "blocks.20.self_attn.q",
153
+ "blocks.20.self_attn.v",
154
+ "blocks.21.cross_attn.k",
155
+ "blocks.21.cross_attn.o",
156
+ "blocks.21.cross_attn.q",
157
+ "blocks.21.cross_attn.v",
158
+ "blocks.21.ffn.0",
159
+ "blocks.21.ffn.2",
160
+ "blocks.21.self_attn.k",
161
+ "blocks.21.self_attn.o",
162
+ "blocks.21.self_attn.q",
163
+ "blocks.21.self_attn.v",
164
+ "blocks.22.cross_attn.k",
165
+ "blocks.22.cross_attn.o",
166
+ "blocks.22.cross_attn.q",
167
+ "blocks.22.cross_attn.v",
168
+ "blocks.22.ffn.0",
169
+ "blocks.22.ffn.2",
170
+ "blocks.22.self_attn.k",
171
+ "blocks.22.self_attn.o",
172
+ "blocks.22.self_attn.q",
173
+ "blocks.22.self_attn.v",
174
+ "blocks.23.cross_attn.k",
175
+ "blocks.23.cross_attn.o",
176
+ "blocks.23.cross_attn.q",
177
+ "blocks.23.cross_attn.v",
178
+ "blocks.23.ffn.0",
179
+ "blocks.23.ffn.2",
180
+ "blocks.23.self_attn.k",
181
+ "blocks.23.self_attn.o",
182
+ "blocks.23.self_attn.q",
183
+ "blocks.23.self_attn.v",
184
+ "blocks.24.cross_attn.k",
185
+ "blocks.24.cross_attn.o",
186
+ "blocks.24.cross_attn.q",
187
+ "blocks.24.cross_attn.v",
188
+ "blocks.24.ffn.0",
189
+ "blocks.24.ffn.2",
190
+ "blocks.24.self_attn.k",
191
+ "blocks.24.self_attn.o",
192
+ "blocks.24.self_attn.q",
193
+ "blocks.24.self_attn.v",
194
+ "blocks.25.cross_attn.k",
195
+ "blocks.25.cross_attn.o",
196
+ "blocks.25.cross_attn.q",
197
+ "blocks.25.cross_attn.v",
198
+ "blocks.25.ffn.0",
199
+ "blocks.25.ffn.2",
200
+ "blocks.25.self_attn.k",
201
+ "blocks.25.self_attn.o",
202
+ "blocks.25.self_attn.q",
203
+ "blocks.25.self_attn.v",
204
+ "blocks.26.cross_attn.k",
205
+ "blocks.26.cross_attn.o",
206
+ "blocks.26.cross_attn.q",
207
+ "blocks.26.cross_attn.v",
208
+ "blocks.26.ffn.0",
209
+ "blocks.26.ffn.2",
210
+ "blocks.26.self_attn.k",
211
+ "blocks.26.self_attn.o",
212
+ "blocks.26.self_attn.q",
213
+ "blocks.26.self_attn.v",
214
+ "blocks.27.cross_attn.k",
215
+ "blocks.27.cross_attn.o",
216
+ "blocks.27.cross_attn.q",
217
+ "blocks.27.cross_attn.v",
218
+ "blocks.27.ffn.0",
219
+ "blocks.27.ffn.2",
220
+ "blocks.27.self_attn.k",
221
+ "blocks.27.self_attn.o",
222
+ "blocks.27.self_attn.q",
223
+ "blocks.27.self_attn.v",
224
+ "blocks.28.cross_attn.k",
225
+ "blocks.28.cross_attn.o",
226
+ "blocks.28.cross_attn.q",
227
+ "blocks.28.cross_attn.v",
228
+ "blocks.28.ffn.0",
229
+ "blocks.28.ffn.2",
230
+ "blocks.28.self_attn.k",
231
+ "blocks.28.self_attn.o",
232
+ "blocks.28.self_attn.q",
233
+ "blocks.28.self_attn.v",
234
+ "blocks.29.cross_attn.k",
235
+ "blocks.29.cross_attn.o",
236
+ "blocks.29.cross_attn.q",
237
+ "blocks.29.cross_attn.v",
238
+ "blocks.29.ffn.0",
239
+ "blocks.29.ffn.2",
240
+ "blocks.29.self_attn.k",
241
+ "blocks.29.self_attn.o",
242
+ "blocks.29.self_attn.q",
243
+ "blocks.29.self_attn.v",
244
+ "blocks.3.cross_attn.k",
245
+ "blocks.3.cross_attn.o",
246
+ "blocks.3.cross_attn.q",
247
+ "blocks.3.cross_attn.v",
248
+ "blocks.3.ffn.0",
249
+ "blocks.3.ffn.2",
250
+ "blocks.3.self_attn.k",
251
+ "blocks.3.self_attn.o",
252
+ "blocks.3.self_attn.q",
253
+ "blocks.3.self_attn.v",
254
+ "blocks.4.cross_attn.k",
255
+ "blocks.4.cross_attn.o",
256
+ "blocks.4.cross_attn.q",
257
+ "blocks.4.cross_attn.v",
258
+ "blocks.4.ffn.0",
259
+ "blocks.4.ffn.2",
260
+ "blocks.4.self_attn.k",
261
+ "blocks.4.self_attn.o",
262
+ "blocks.4.self_attn.q",
263
+ "blocks.4.self_attn.v",
264
+ "blocks.5.cross_attn.k",
265
+ "blocks.5.cross_attn.o",
266
+ "blocks.5.cross_attn.q",
267
+ "blocks.5.cross_attn.v",
268
+ "blocks.5.ffn.0",
269
+ "blocks.5.ffn.2",
270
+ "blocks.5.self_attn.k",
271
+ "blocks.5.self_attn.o",
272
+ "blocks.5.self_attn.q",
273
+ "blocks.5.self_attn.v",
274
+ "blocks.6.cross_attn.k",
275
+ "blocks.6.cross_attn.o",
276
+ "blocks.6.cross_attn.q",
277
+ "blocks.6.cross_attn.v",
278
+ "blocks.6.ffn.0",
279
+ "blocks.6.ffn.2",
280
+ "blocks.6.self_attn.k",
281
+ "blocks.6.self_attn.o",
282
+ "blocks.6.self_attn.q",
283
+ "blocks.6.self_attn.v",
284
+ "blocks.7.cross_attn.k",
285
+ "blocks.7.cross_attn.o",
286
+ "blocks.7.cross_attn.q",
287
+ "blocks.7.cross_attn.v",
288
+ "blocks.7.ffn.0",
289
+ "blocks.7.ffn.2",
290
+ "blocks.7.self_attn.k",
291
+ "blocks.7.self_attn.o",
292
+ "blocks.7.self_attn.q",
293
+ "blocks.7.self_attn.v",
294
+ "blocks.8.cross_attn.k",
295
+ "blocks.8.cross_attn.o",
296
+ "blocks.8.cross_attn.q",
297
+ "blocks.8.cross_attn.v",
298
+ "blocks.8.ffn.0",
299
+ "blocks.8.ffn.2",
300
+ "blocks.8.self_attn.k",
301
+ "blocks.8.self_attn.o",
302
+ "blocks.8.self_attn.q",
303
+ "blocks.8.self_attn.v",
304
+ "blocks.9.cross_attn.k",
305
+ "blocks.9.cross_attn.o",
306
+ "blocks.9.cross_attn.q",
307
+ "blocks.9.cross_attn.v",
308
+ "blocks.9.ffn.0",
309
+ "blocks.9.ffn.2",
310
+ "blocks.9.self_attn.k",
311
+ "blocks.9.self_attn.o",
312
+ "blocks.9.self_attn.q",
313
+ "blocks.9.self_attn.v"
314
+ ],
315
+ "use_dora": false,
316
+ "use_rslora": false
317
+ }
adapter_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cb06b699fe5bdbb9e5750ca177bf61e74fbb32d150e4dcaf55f9141b0f169278
3
+ size 644949344
evaluation.json ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "benchmark": {
3
+ "comparability_note": "The official row predates the current deterministic/original-byte DOVER and capped depth-residual fixes; no official run-level artifact identifies the exact private evaluator or generation configuration.",
4
+ "contemporaneous_legacy": {
5
+ "commit": "1b84911a64d9d73c3267fa74baa640fab336b7e0",
6
+ "source_files": {
7
+ "conditional_generation/benchmark_pipelines/scores/transfer_bench/metrics_canny_blur_depth.py": {
8
+ "bytes": 11376,
9
+ "sha256": "63b9178580a72395be8ffdd841a5717a8d84f43e9f80e8736d6d802adc15bfb3"
10
+ },
11
+ "conditional_generation/compute_diversity_score.py": {
12
+ "bytes": 11408,
13
+ "sha256": "c3c48fefcf8fd3b31692869300d43feeca4b95a67a65f5b6a9039a7e9140b0f1"
14
+ },
15
+ "conditional_generation/compute_metrics.py": {
16
+ "bytes": 30773,
17
+ "sha256": "7cbb66e4c5fea2a4ac4a538614577c6ccc42f92f5f5c825fb86241f5538b0d13"
18
+ },
19
+ "conditional_generation/models/dover.py": {
20
+ "bytes": 8856,
21
+ "sha256": "8e0729e2d37b34f0ee4f1d055a0863c1f6fd49f2bea7b705c7843fb87b4b0ed2"
22
+ },
23
+ "conditional_generation/pyproject.toml": {
24
+ "bytes": 1206,
25
+ "sha256": "646ec4e39a9946eaab8e93f4aa4cf3fab10c9aea85e08257e920f1c255233b28"
26
+ }
27
+ }
28
+ },
29
+ "current": {
30
+ "commit": "2f3b687410029b98397fbc51fa4de36bfd45627d",
31
+ "source_files": {
32
+ "conditional_generation/benchmark_pipelines/scores/transfer_bench/metrics_canny_blur_depth.py": {
33
+ "bytes": 11616,
34
+ "sha256": "5aa1beabc56d7f0df23b4ef7b049d4adb41c4cab803de3cb620460827a283962"
35
+ },
36
+ "conditional_generation/compute_diversity_score.py": {
37
+ "bytes": 11408,
38
+ "sha256": "c3c48fefcf8fd3b31692869300d43feeca4b95a67a65f5b6a9039a7e9140b0f1"
39
+ },
40
+ "conditional_generation/compute_metrics.py": {
41
+ "bytes": 30882,
42
+ "sha256": "5001d3bdc81e579c7792b7f737e6620960e2057e638f2e75d419fb06d3d5556d"
43
+ },
44
+ "conditional_generation/models/dover.py": {
45
+ "bytes": 9354,
46
+ "sha256": "9473cbb64744a7436c377eab11fa779c52f85cd0782b2db8d7d29539307e9cb3"
47
+ },
48
+ "conditional_generation/pyproject.toml": {
49
+ "bytes": 1206,
50
+ "sha256": "646ec4e39a9946eaab8e93f4aa4cf3fab10c9aea85e08257e920f1c255233b28"
51
+ }
52
+ }
53
+ },
54
+ "repository": "https://github.com/SHI-Labs/physical-ai-bench"
55
+ },
56
+ "condition": "Depth",
57
+ "diversity_videos_per_variant": 3600,
58
+ "main_cases": 600,
59
+ "metric_definitions": {
60
+ "blur_ssim": "SSIM between bilaterally blurred source and generated RGB videos; higher is better and it is not a sharpness score",
61
+ "depth_si_rmse": "Median-scale-aligned depth RMSE per frame using Video Depth Anything estimates; lower is better",
62
+ "diversity": "Mean VGG-LPIPS over 30 ordered pairs from six prompts sharing one Depth control; higher means more variation",
63
+ "edge_f1": "F1 between Canny edge maps over all video pixels; higher is better",
64
+ "mask_miou": "Prompt-grounded SAM2 mask IoU after Hungarian matching; higher is better",
65
+ "quality_score": "DOVER technical video quality score; higher is better"
66
+ },
67
+ "protocol": {
68
+ "diversity_evaluator": "current",
69
+ "lora_checkpoint_sha256": "47f94f7fc85ed53b5480b28c2bfa3a05659bcf4aeb1278004c0860224787e78a",
70
+ "main_evaluator": "legacy",
71
+ "output_fps": 24,
72
+ "run_contract_sha256": "0fa06f59d3f8a54041ffd9d0982f4f3608fdd6d3fa024bc9d4e7dde403aaf489",
73
+ "shared_frames": 121,
74
+ "shared_seed": 43
75
+ },
76
+ "resolution_buckets": [
77
+ {
78
+ "count": 200,
79
+ "generation": "640x480",
80
+ "input": "640x480",
81
+ "source": "AgiBot"
82
+ },
83
+ {
84
+ "count": 195,
85
+ "generation": "1280x704",
86
+ "input": "1280x720",
87
+ "source": "OpenDV"
88
+ },
89
+ {
90
+ "count": 3,
91
+ "generation": "1280x672",
92
+ "input": "1920x1016",
93
+ "source": "OpenDV"
94
+ },
95
+ {
96
+ "count": 2,
97
+ "generation": "1280x544",
98
+ "input": "1920x808",
99
+ "source": "OpenDV"
100
+ },
101
+ {
102
+ "count": 200,
103
+ "generation": "1280x704",
104
+ "input": "1920x1080",
105
+ "source": "Ego-Exo4D"
106
+ }
107
+ ],
108
+ "rows": [
109
+ {
110
+ "classification": "official_quoted_not_locally_measured",
111
+ "id": "official_reported",
112
+ "label": "Wan2.2-Fun-5B-Control (official leaderboard)",
113
+ "metrics": {
114
+ "blur_ssim": 0.556,
115
+ "depth_si_rmse": 1.819,
116
+ "diversity": 0.481,
117
+ "edge_f1": 0.106,
118
+ "mask_miou": 0.615,
119
+ "quality_score": 9.317
120
+ },
121
+ "protocol": "\u5b98\u65b9 leaderboard\uff1b\u914d\u7f6e\u672a\u62ab\u9732"
122
+ },
123
+ {
124
+ "classification": "local_measured",
125
+ "id": "local_multistep",
126
+ "label": "Wan2.2-Fun-5B-Control (local multistep)",
127
+ "metrics": {
128
+ "blur_ssim": 0.5572094391287489,
129
+ "depth_si_rmse": 4.836493481093092,
130
+ "diversity": 0.4744977790738146,
131
+ "edge_f1": 0.09937129978006591,
132
+ "mask_miou": 0.6002169333646695,
133
+ "quality_score": 8.961508704821268
134
+ },
135
+ "protocol": "\u672c\u5730 40-step \u00b7 CFG 6 \u00b7 TeaCache"
136
+ },
137
+ {
138
+ "classification": "local_measured",
139
+ "id": "base4_cfgfree",
140
+ "label": "Wan2.2-Fun-5B-Control (local direct 4-step)",
141
+ "metrics": {
142
+ "blur_ssim": 0.6823773058925994,
143
+ "depth_si_rmse": 2.6539159381803015,
144
+ "diversity": 0.24838090770112145,
145
+ "edge_f1": 0.0889015064608313,
146
+ "mask_miou": 0.636834080417951,
147
+ "quality_score": 8.89601070801417
148
+ },
149
+ "protocol": "\u672c\u5730 4-step \u00b7 CFG-free \u00b7 no LoRA"
150
+ },
151
+ {
152
+ "classification": "local_measured",
153
+ "id": "lora1600_4step_cfgfree",
154
+ "label": "Wan2.2-Fun-5B-Control + DMD LoRA step 1600 (4-step)",
155
+ "metrics": {
156
+ "blur_ssim": 0.5439233481351533,
157
+ "depth_si_rmse": 1.8050484347511278,
158
+ "diversity": 0.4581396648420228,
159
+ "edge_f1": 0.09165980415187946,
160
+ "mask_miou": 0.5890168254077435,
161
+ "quality_score": 9.290268224477767
162
+ },
163
+ "protocol": "\u672c\u5730 4-step \u00b7 CFG-free \u00b7 LoRA step 1600"
164
+ }
165
+ ]
166
+ }
generator_lora.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:12946fdfac2a99643f259e721764b728e6e905c1e838e104c24b87b0757facc8
3
+ size 645089990
inference_config.yaml ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ base_model: alibaba-pai/Wan2.2-Fun-5B-Control
2
+ base_model_revision: b8bc1a65ab71d054ba4636dc0dac104aa4df2686
3
+ training_checkpoint_step: 1600
4
+ adapter_file: generator_lora.pt
5
+ num_inference_steps: 4
6
+ guidance_scale: 1.0
7
+ classifier_free_guidance: false
8
+ lora_strength: 1.0
9
+ lora_rank: 64
10
+ lora_alpha: 64
11
+ scheduler: FlowUniPCMultistepScheduler
12
+ scheduler_config_shift: 1.0
13
+ runtime_shift: 5.0
14
+ observed_timesteps:
15
+ - 999
16
+ - 937
17
+ - 833
18
+ - 624
19
+ enable_teacache: false
20
+ evaluation_frames: 121
21
+ evaluation_fps: 24
22
+ training_width: 832
23
+ training_height: 480
24
+ training_raw_frames: 93
load_wan_fun_lora.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Load the step-1600 Wan-Fun generator adapter through the evaluated loader."""
2
+
3
+ from perflow_lora import apply_adapter_strength, attach_wan_lora
4
+
5
+
6
+ def load_wan_fun_lora(transformer, checkpoint_path, strength=1.0):
7
+ """Attach once to a freshly loaded native VideoX-Fun 5B Control transformer.
8
+
9
+ ``checkpoint_path`` is the downloaded ``generator_lora.pt``. The returned
10
+ receipt proves exact target coverage. Call before CPU/group offloading.
11
+ Safetensors is a separate PEFT-format export, not this function's input.
12
+ """
13
+ name = "wan_fun_dmd_step1600"
14
+ if any(name in getattr(m, "lora_A", {}) for m in transformer.modules()):
15
+ raise ValueError("Adapter already installed; load a fresh transformer")
16
+ conv = transformer.patch_embedding
17
+ if conv.in_channels != 148:
18
+ raise ValueError("Expected the complete 148-channel Wan2.2-Fun-5B-Control transformer")
19
+ report = attach_wan_lora(
20
+ transformer, checkpoint_path, style="native", expected_rank=64,
21
+ lora_alpha=64, adapter_name=name, checkpoint_kind="wan_fun_control_dmd",
22
+ report_prefix="WAN_FUN_LORA", expected_step=1600, enabled=True,
23
+ )
24
+ report.update(apply_adapter_strength(transformer, adapter_name=name, strength=strength))
25
+ return report
perflow_lora.py ADDED
@@ -0,0 +1,546 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Strict loaders for Wan2.2-TI2V-5B four-step LoRA checkpoints.
3
+
4
+ Both the published Perflow checkpoint and the local non-AR DMD checkpoint are
5
+ torch ``model.pt`` files containing ``generator_lora`` and ``critic_lora``.
6
+ Inference must load only the former. This helper validates all 300 expected
7
+ Wan linear targets before installing a PEFT adapter, and supports both native
8
+ Wan and Diffusers module names.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import hashlib
15
+ import math
16
+ import os
17
+ import re
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+ import torch
22
+ from torch import nn
23
+
24
+
25
+ PERFLOW_ADAPTER_NAME = "perflow_step4"
26
+ LOCAL_NONAR_ADAPTER_NAME = "local_nonar_step4"
27
+ # Backward-compatible import for existing callers.
28
+ ADAPTER_NAME = PERFLOW_ADAPTER_NAME
29
+ EXPECTED_BLOCKS = tuple(range(30))
30
+ EXPECTED_SUFFIXES = (
31
+ "self_attn.q",
32
+ "self_attn.k",
33
+ "self_attn.v",
34
+ "self_attn.o",
35
+ "cross_attn.q",
36
+ "cross_attn.k",
37
+ "cross_attn.v",
38
+ "cross_attn.o",
39
+ "ffn.0",
40
+ "ffn.2",
41
+ )
42
+ _LORA_KEY = re.compile(
43
+ r"^(?:base_model\.model\.)?(blocks\.(\d+)\.(.+))\.lora_([AB])\.weight$"
44
+ )
45
+
46
+
47
+ def _sha256_file(path: Path) -> str:
48
+ digest = hashlib.sha256()
49
+ with path.open("rb") as handle:
50
+ for chunk in iter(lambda: handle.read(8 * 1024 * 1024), b""):
51
+ digest.update(chunk)
52
+ return digest.hexdigest()
53
+
54
+
55
+ def _target_name(source_name: str, style: str) -> str:
56
+ if style == "native":
57
+ return source_name
58
+ if style != "diffusers":
59
+ raise ValueError(f"Unknown Wan module style: {style!r}")
60
+
61
+ replacements = {
62
+ ".self_attn.q": ".attn1.to_q",
63
+ ".self_attn.k": ".attn1.to_k",
64
+ ".self_attn.v": ".attn1.to_v",
65
+ ".self_attn.o": ".attn1.to_out.0",
66
+ ".cross_attn.q": ".attn2.to_q",
67
+ ".cross_attn.k": ".attn2.to_k",
68
+ ".cross_attn.v": ".attn2.to_v",
69
+ ".cross_attn.o": ".attn2.to_out.0",
70
+ ".ffn.0": ".ffn.net.0.proj",
71
+ ".ffn.2": ".ffn.net.2",
72
+ }
73
+ for suffix, replacement in replacements.items():
74
+ if source_name.endswith(suffix):
75
+ return source_name[: -len(suffix)] + replacement
76
+ raise ValueError(f"Unsupported Wan LoRA target: {source_name}")
77
+
78
+
79
+ def inspect_wan_lora_checkpoint(
80
+ checkpoint_path: str | Path,
81
+ *,
82
+ expected_rank: int,
83
+ lora_alpha: int | None = None,
84
+ checkpoint_kind: str,
85
+ expected_step: int | None = None,
86
+ ) -> dict[str, Any]:
87
+ """Read and strictly validate one 30-block Wan generator LoRA."""
88
+ if expected_rank <= 0:
89
+ raise ValueError(f"expected_rank must be positive, got {expected_rank}")
90
+ alpha = expected_rank if lora_alpha is None else lora_alpha
91
+ if alpha <= 0:
92
+ raise ValueError(f"lora_alpha must be positive, got {alpha}")
93
+ checkpoint_path = Path(checkpoint_path).resolve()
94
+ payload = torch.load(
95
+ checkpoint_path,
96
+ map_location="cpu",
97
+ weights_only=True,
98
+ mmap=True,
99
+ )
100
+ if not isinstance(payload, dict):
101
+ raise TypeError(f"Expected a dict checkpoint, got {type(payload).__name__}")
102
+ if "generator_lora" not in payload:
103
+ raise KeyError(f"Missing generator_lora; top-level keys: {list(payload)}")
104
+ checkpoint_step = int(payload.get("step", -1))
105
+ if expected_step is not None and checkpoint_step != expected_step:
106
+ raise ValueError(
107
+ f"Expected step {expected_step} for {checkpoint_kind}, "
108
+ f"got {checkpoint_step}"
109
+ )
110
+ state = payload["generator_lora"]
111
+ if not isinstance(state, dict):
112
+ raise TypeError("generator_lora must be a state dict")
113
+
114
+ pairs: dict[str, dict[str, torch.Tensor]] = {}
115
+ bad_keys: list[str] = []
116
+ for key, tensor in state.items():
117
+ match = _LORA_KEY.match(key)
118
+ if match is None or not isinstance(tensor, torch.Tensor):
119
+ bad_keys.append(key)
120
+ continue
121
+ source_name, block_text, suffix, side = match.groups()
122
+ block = int(block_text)
123
+ if block not in EXPECTED_BLOCKS or suffix not in EXPECTED_SUFFIXES:
124
+ bad_keys.append(key)
125
+ continue
126
+ pairs.setdefault(source_name, {})[side] = tensor
127
+
128
+ expected_names = {
129
+ f"blocks.{block}.{suffix}"
130
+ for block in EXPECTED_BLOCKS
131
+ for suffix in EXPECTED_SUFFIXES
132
+ }
133
+ missing_names = sorted(expected_names - set(pairs))
134
+ extra_names = sorted(set(pairs) - expected_names)
135
+ incomplete = sorted(name for name, pair in pairs.items() if set(pair) != {"A", "B"})
136
+ if bad_keys or missing_names or extra_names or incomplete or len(state) != 600:
137
+ raise ValueError(
138
+ f"Invalid {checkpoint_kind} generator_lora structure: "
139
+ f"tensors={len(state)}, bad={len(bad_keys)}, missing={len(missing_names)}, "
140
+ f"extra={len(extra_names)}, incomplete={len(incomplete)}"
141
+ )
142
+
143
+ ranks = set()
144
+ parameter_count = 0
145
+ for name, pair in pairs.items():
146
+ a, b = pair["A"], pair["B"]
147
+ if a.ndim != 2 or b.ndim != 2 or a.shape[0] != b.shape[1]:
148
+ raise ValueError(f"Invalid A/B shapes for {name}: A={tuple(a.shape)}, B={tuple(b.shape)}")
149
+ ranks.add(int(a.shape[0]))
150
+ parameter_count += a.numel() + b.numel()
151
+ if ranks != {expected_rank}:
152
+ raise ValueError(
153
+ f"Expected rank {expected_rank} throughout for {checkpoint_kind}, "
154
+ f"got {sorted(ranks)}"
155
+ )
156
+
157
+ metadata = {
158
+ "checkpoint_kind": checkpoint_kind,
159
+ "checkpoint": str(checkpoint_path),
160
+ "checkpoint_bytes": checkpoint_path.stat().st_size,
161
+ "checkpoint_sha256": _sha256_file(checkpoint_path),
162
+ "top_level_keys": sorted(payload),
163
+ "step": checkpoint_step,
164
+ "selected_state": "generator_lora",
165
+ "ignored_state": "critic_lora" if "critic_lora" in payload else None,
166
+ "tensor_count": len(state),
167
+ "target_count": len(pairs),
168
+ "blocks": len(EXPECTED_BLOCKS),
169
+ "targets_per_block": len(EXPECTED_SUFFIXES),
170
+ "rank": expected_rank,
171
+ "alpha": alpha,
172
+ "scale": alpha / expected_rank,
173
+ "parameter_count": parameter_count,
174
+ }
175
+ return {"metadata": metadata, "pairs": pairs}
176
+
177
+
178
+ def inspect_perflow_checkpoint(checkpoint_path: str | Path) -> dict[str, Any]:
179
+ """Validate the published rank-128 Perflow generator LoRA."""
180
+ return inspect_wan_lora_checkpoint(
181
+ checkpoint_path,
182
+ expected_rank=128,
183
+ lora_alpha=128,
184
+ checkpoint_kind="perflow_step4",
185
+ expected_step=None,
186
+ )
187
+
188
+
189
+ def inspect_local_nonar_checkpoint(
190
+ checkpoint_path: str | Path,
191
+ *,
192
+ expected_step: int | None = 1600,
193
+ expected_rank: int | None = None,
194
+ ) -> dict[str, Any]:
195
+ """Validate a local non-AR generator LoRA with a uniform inferred rank.
196
+
197
+ ``1600`` remains the fail-closed default for the historical four-step DMD
198
+ artifact. Native-step CFG-distillation callers must pass their checkpoint
199
+ identity explicitly instead of being silently coupled to that old step.
200
+ Rank is inferred by default so matched rank sweeps are not incorrectly
201
+ rejected by the historical rank-64 assumption.
202
+ """
203
+ checkpoint_path = Path(checkpoint_path).resolve()
204
+ if expected_rank is None:
205
+ payload = torch.load(
206
+ checkpoint_path,
207
+ map_location="cpu",
208
+ weights_only=True,
209
+ mmap=True,
210
+ )
211
+ if not isinstance(payload, dict) or not isinstance(
212
+ payload.get("generator_lora"), dict
213
+ ):
214
+ raise TypeError(f"Expected generator_lora state dict in {checkpoint_path}")
215
+ state = payload["generator_lora"]
216
+ ranks = {
217
+ int(tensor.shape[0])
218
+ for key, tensor in state.items()
219
+ if key.endswith(".lora_A.weight")
220
+ and isinstance(tensor, torch.Tensor)
221
+ and tensor.ndim == 2
222
+ }
223
+ if len(ranks) != 1:
224
+ raise ValueError(
225
+ f"Expected one uniform LoRA rank in {checkpoint_path}, "
226
+ f"got {sorted(ranks)}"
227
+ )
228
+ expected_rank = next(iter(ranks))
229
+ del state, payload
230
+ return inspect_wan_lora_checkpoint(
231
+ checkpoint_path,
232
+ expected_rank=expected_rank,
233
+ lora_alpha=expected_rank,
234
+ checkpoint_kind="local_nonar_step4",
235
+ expected_step=expected_step,
236
+ )
237
+
238
+
239
+ def attach_wan_lora(
240
+ model: nn.Module,
241
+ checkpoint_path: str | Path,
242
+ *,
243
+ style: str,
244
+ expected_rank: int,
245
+ lora_alpha: int,
246
+ adapter_name: str,
247
+ checkpoint_kind: str,
248
+ report_prefix: str,
249
+ expected_step: int | None = None,
250
+ enabled: bool = False,
251
+ ) -> dict[str, Any]:
252
+ """Install the generator LoRA on a native-Wan or Diffusers Wan backbone.
253
+
254
+ Every source tensor and every destination module must match. Any missing or
255
+ shape-mismatched layer aborts the run instead of silently producing a partial
256
+ LoRA load.
257
+ """
258
+ inspected = inspect_wan_lora_checkpoint(
259
+ checkpoint_path,
260
+ expected_rank=expected_rank,
261
+ lora_alpha=lora_alpha,
262
+ checkpoint_kind=checkpoint_kind,
263
+ expected_step=expected_step,
264
+ )
265
+ metadata = inspected["metadata"]
266
+ pairs = inspected["pairs"]
267
+
268
+ destinations: dict[str, tuple[str, dict[str, torch.Tensor], nn.Linear]] = {}
269
+ for source_name, pair in pairs.items():
270
+ target_name = _target_name(source_name, style)
271
+ try:
272
+ target = model.get_submodule(target_name)
273
+ except AttributeError as exc:
274
+ raise ValueError(f"Missing LoRA destination {target_name} (from {source_name})") from exc
275
+ if not isinstance(target, nn.Linear):
276
+ raise TypeError(f"LoRA destination {target_name} is {type(target).__name__}, not nn.Linear")
277
+ a, b = pair["A"], pair["B"]
278
+ expected_a = (a.shape[0], target.in_features)
279
+ expected_b = (target.out_features, a.shape[0])
280
+ if tuple(a.shape) != expected_a or tuple(b.shape) != expected_b:
281
+ raise ValueError(
282
+ f"Shape mismatch for {target_name}: target={tuple(target.weight.shape)}, "
283
+ f"A={tuple(a.shape)} expected={expected_a}, B={tuple(b.shape)} expected={expected_b}"
284
+ )
285
+ destinations[target_name] = (source_name, pair, target)
286
+
287
+ if len(destinations) != 300:
288
+ raise ValueError(f"Expected 300 unique LoRA destinations, got {len(destinations)}")
289
+
290
+ # PEFT 0.19 probes torchao before checking whether a layer is quantized. The
291
+ # inherited environment has torchao 0.11, so explicitly disable that optional
292
+ # dispatcher; ordinary torch.nn.Linear layers use PEFT's native dispatcher.
293
+ import peft.tuners.lora.torchao as peft_torchao
294
+ from peft import LoraConfig, inject_adapter_in_model
295
+
296
+ peft_torchao.is_torchao_available = lambda: False
297
+ config = LoraConfig(
298
+ r=expected_rank,
299
+ lora_alpha=lora_alpha,
300
+ lora_dropout=0.0,
301
+ bias="none",
302
+ target_modules=sorted(destinations),
303
+ init_lora_weights=True,
304
+ )
305
+ inject_adapter_in_model(config, model, adapter_name=adapter_name)
306
+
307
+ copied_tensors = 0
308
+ nonzero_b = 0
309
+ adapter_dtypes: set[str] = set()
310
+ with torch.no_grad():
311
+ for target_name, (_source_name, pair, _old_target) in destinations.items():
312
+ layer = model.get_submodule(target_name)
313
+ if adapter_name not in layer.lora_A or adapter_name not in layer.lora_B:
314
+ raise RuntimeError(f"PEFT did not create adapter tensors for {target_name}")
315
+ a_param = layer.lora_A[adapter_name].weight
316
+ b_param = layer.lora_B[adapter_name].weight
317
+ a_param.copy_(pair["A"].to(device=a_param.device, dtype=a_param.dtype))
318
+ b_param.copy_(pair["B"].to(device=b_param.device, dtype=b_param.dtype))
319
+ copied_tensors += 2
320
+ nonzero_b += int(torch.count_nonzero(pair["B"]).item() > 0)
321
+ adapter_dtypes.add(str(a_param.dtype).replace("torch.", ""))
322
+ scale = float(layer.scaling[adapter_name])
323
+ expected_scale = lora_alpha / expected_rank
324
+ if scale != expected_scale:
325
+ raise RuntimeError(
326
+ f"Unexpected LoRA scale for {target_name}: {scale}; "
327
+ f"expected {expected_scale}"
328
+ )
329
+
330
+ toggled = _set_adapter_enabled(
331
+ model,
332
+ adapter_name=adapter_name,
333
+ enabled=enabled,
334
+ report_prefix=report_prefix,
335
+ allow_absent=False,
336
+ )
337
+ if toggled != 300 or copied_tensors != 600 or nonzero_b != 300:
338
+ raise RuntimeError(
339
+ f"Incomplete LoRA installation: layers={toggled}, tensors={copied_tensors}, "
340
+ f"nonzero_B={nonzero_b}"
341
+ )
342
+
343
+ report = {
344
+ **metadata,
345
+ "module_style": style,
346
+ "adapter_name": adapter_name,
347
+ "loaded_tensors": copied_tensors,
348
+ "loaded_targets": toggled,
349
+ "missing_targets": 0,
350
+ "shape_mismatches": 0,
351
+ "nonzero_B_targets": nonzero_b,
352
+ "adapter_dtypes": sorted(adapter_dtypes),
353
+ "enabled": bool(enabled),
354
+ }
355
+ print(report_prefix + "_LOAD_REPORT=" + json.dumps(report, sort_keys=True), flush=True)
356
+ return report
357
+
358
+
359
+ def attach_perflow_lora(
360
+ model: nn.Module,
361
+ checkpoint_path: str | Path,
362
+ *,
363
+ style: str,
364
+ enabled: bool = False,
365
+ ) -> dict[str, Any]:
366
+ """Install the published rank-128 Perflow generator LoRA."""
367
+ return attach_wan_lora(
368
+ model,
369
+ checkpoint_path,
370
+ style=style,
371
+ expected_rank=128,
372
+ lora_alpha=128,
373
+ adapter_name=PERFLOW_ADAPTER_NAME,
374
+ checkpoint_kind="perflow_step4",
375
+ report_prefix="PERFLOW_LORA",
376
+ expected_step=None,
377
+ enabled=enabled,
378
+ )
379
+
380
+
381
+ def apply_adapter_strength(
382
+ model: nn.Module,
383
+ *,
384
+ adapter_name: str,
385
+ strength: float,
386
+ expected_targets: int = 300,
387
+ ) -> dict[str, Any]:
388
+ """Apply one linear inference multiplier through PEFT's scaling value."""
389
+
390
+ strength = float(strength)
391
+ if not math.isfinite(strength) or strength <= 0:
392
+ raise ValueError(f"LoRA inference strength must be positive and finite, got {strength}")
393
+ base_scalings: set[float] = set()
394
+ effective_scalings: set[float] = set()
395
+ scaled_targets = 0
396
+ for layer in model.modules():
397
+ scaling = getattr(layer, "scaling", None)
398
+ if scaling is None or adapter_name not in scaling:
399
+ continue
400
+ base_scale = float(scaling[adapter_name])
401
+ effective_scale = base_scale * strength
402
+ scaling[adapter_name] = effective_scale
403
+ base_scalings.add(base_scale)
404
+ effective_scalings.add(effective_scale)
405
+ scaled_targets += 1
406
+ if scaled_targets != expected_targets:
407
+ raise RuntimeError(
408
+ "Incomplete LoRA inference-strength application: "
409
+ f"targets={scaled_targets}, expected={expected_targets}"
410
+ )
411
+ return {
412
+ "inference_strength": strength,
413
+ "base_adapter_scaling": sorted(base_scalings),
414
+ "effective_adapter_scaling": sorted(effective_scalings),
415
+ "scaled_targets": scaled_targets,
416
+ "scale_semantics": "delta_W = inference_strength * (B @ A) * alpha/r",
417
+ }
418
+
419
+
420
+ def attach_local_nonar_lora(
421
+ model: nn.Module,
422
+ checkpoint_path: str | Path,
423
+ *,
424
+ style: str,
425
+ enabled: bool = True,
426
+ strength: float | None = None,
427
+ ) -> dict[str, Any]:
428
+ """Install a local four-step non-AR DMD generator LoRA.
429
+
430
+ The transfer study compares independently trained WAN, SCOPE and TheDenk
431
+ adapters. They intentionally use different ranks and checkpoint steps, so
432
+ infer those provenance fields from the checkpoint while retaining the
433
+ strict 600-tensor / 300-target / destination-shape validation performed by
434
+ :func:`attach_wan_lora`.
435
+ """
436
+ checkpoint_path = Path(checkpoint_path).resolve()
437
+ payload = torch.load(
438
+ checkpoint_path,
439
+ map_location="cpu",
440
+ weights_only=True,
441
+ mmap=True,
442
+ )
443
+ if not isinstance(payload, dict) or not isinstance(payload.get("generator_lora"), dict):
444
+ raise TypeError(f"Expected generator_lora state dict in {checkpoint_path}")
445
+ state = payload["generator_lora"]
446
+ ranks = {
447
+ int(tensor.shape[0])
448
+ for key, tensor in state.items()
449
+ if key.endswith(".lora_A.weight") and isinstance(tensor, torch.Tensor) and tensor.ndim == 2
450
+ }
451
+ if len(ranks) != 1:
452
+ raise ValueError(f"Expected one uniform LoRA rank in {checkpoint_path}, got {sorted(ranks)}")
453
+ rank = next(iter(ranks))
454
+ step = payload.get("step")
455
+ expected_step = int(step) if step is not None else None
456
+ del state, payload
457
+ if strength is None:
458
+ strength = float(os.environ.get("LOCAL_NONAR_LORA_SCALE", "1"))
459
+ strength = float(strength)
460
+ if not math.isfinite(strength) or strength <= 0:
461
+ raise ValueError(f"LoRA inference strength must be positive and finite, got {strength}")
462
+
463
+ report = attach_wan_lora(
464
+ model,
465
+ checkpoint_path,
466
+ style=style,
467
+ expected_rank=rank,
468
+ lora_alpha=rank,
469
+ adapter_name=LOCAL_NONAR_ADAPTER_NAME,
470
+ checkpoint_kind="local_nonar_step4",
471
+ report_prefix="LOCAL_NONAR_LORA",
472
+ expected_step=expected_step,
473
+ enabled=enabled,
474
+ )
475
+ report = {
476
+ **report,
477
+ **apply_adapter_strength(
478
+ model,
479
+ adapter_name=LOCAL_NONAR_ADAPTER_NAME,
480
+ strength=strength,
481
+ expected_targets=300,
482
+ ),
483
+ }
484
+ report_path_raw = os.environ.get("LOCAL_NONAR_LORA_REPORT_PATH")
485
+ if report_path_raw:
486
+ report_path = Path(report_path_raw).resolve()
487
+ report_path.parent.mkdir(parents=True, exist_ok=True)
488
+ temporary = report_path.with_suffix(report_path.suffix + ".tmp")
489
+ temporary.write_text(
490
+ json.dumps(report, indent=2, sort_keys=True) + "\n",
491
+ encoding="utf-8",
492
+ )
493
+ temporary.replace(report_path)
494
+ print(
495
+ "LOCAL_NONAR_LORA_SCALE_REPORT="
496
+ + json.dumps(report, sort_keys=True),
497
+ flush=True,
498
+ )
499
+ return report
500
+
501
+
502
+ def _set_adapter_enabled(
503
+ model: nn.Module,
504
+ *,
505
+ adapter_name: str,
506
+ enabled: bool,
507
+ report_prefix: str,
508
+ allow_absent: bool,
509
+ ) -> int:
510
+ count = 0
511
+ for module in model.modules():
512
+ lora_a = getattr(module, "lora_A", None)
513
+ if lora_a is None or adapter_name not in lora_a:
514
+ continue
515
+ module.enable_adapters(enabled=enabled)
516
+ count += 1
517
+ expected_counts = (0, 300) if allow_absent else (300,)
518
+ if count not in expected_counts:
519
+ raise RuntimeError(
520
+ f"Expected {'0 or ' if allow_absent else ''}300 {adapter_name} layers "
521
+ f"while toggling, found {count}"
522
+ )
523
+ print(f"{report_prefix}_ENABLED={int(enabled)} layers={count}", flush=True)
524
+ return count
525
+
526
+
527
+ def set_perflow_enabled(model: nn.Module, enabled: bool) -> int:
528
+ """Enable/disable only the installed Perflow tuner layers."""
529
+ return _set_adapter_enabled(
530
+ model,
531
+ adapter_name=PERFLOW_ADAPTER_NAME,
532
+ enabled=enabled,
533
+ report_prefix="PERFLOW_LORA",
534
+ allow_absent=True,
535
+ )
536
+
537
+
538
+ def set_local_nonar_enabled(model: nn.Module, enabled: bool) -> int:
539
+ """Enable/disable only the installed local non-AR tuner layers."""
540
+ return _set_adapter_enabled(
541
+ model,
542
+ adapter_name=LOCAL_NONAR_ADAPTER_NAME,
543
+ enabled=enabled,
544
+ report_prefix="LOCAL_NONAR_LORA",
545
+ allow_absent=True,
546
+ )
provenance.json ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "artifacts": {
3
+ "adapter_model.safetensors": {
4
+ "bytes": 644949344,
5
+ "sha256": "cb06b699fe5bdbb9e5750ca177bf61e74fbb32d150e4dcaf55f9141b0f169278"
6
+ },
7
+ "generator_lora.pt": {
8
+ "bytes": 645089990,
9
+ "sha256": "12946fdfac2a99643f259e721764b728e6e905c1e838e104c24b87b0757facc8"
10
+ },
11
+ "perflow_lora.py": {
12
+ "bytes": 19487,
13
+ "sha256": "4ee88e7cfb4d803f97622809d5f483187a061b7f53d77748b5850e438dba08b8"
14
+ }
15
+ },
16
+ "base_model": {
17
+ "id": "alibaba-pai/Wan2.2-Fun-5B-Control",
18
+ "revision": "b8bc1a65ab71d054ba4636dc0dac104aa4df2686",
19
+ "weights_sha256": "ace4718a7c87ee3e5606a68ab79142c4395e81aece76b8120bc886f0fbbe1d16"
20
+ },
21
+ "evaluation_receipts": {
22
+ "all_600_tensors_loaded": true,
23
+ "all_fourstep_cfgfree": true,
24
+ "all_loaded_source_sha256": "47f94f7fc85ed53b5480b28c2bfa3a05659bcf4aeb1278004c0860224787e78a",
25
+ "count": 3600
26
+ },
27
+ "source_checkpoint": {
28
+ "bytes": 1290171290,
29
+ "relative_path": "wanfun_lora/experiments/dmd_4step/logs/checkpoint_model_001600/model.pt",
30
+ "sha256": "47f94f7fc85ed53b5480b28c2bfa3a05659bcf4aeb1278004c0860224787e78a",
31
+ "step": 1600
32
+ },
33
+ "source_metadata_sha256": "048f724b06350bbb0dfb1e65efe175182cad924cbab3691fa34d92c61e441b89",
34
+ "source_training_config_sha256": "4a2246e0186fcaceff47b860d262796078ba5ed19c3abdd51981e7409bd3d203",
35
+ "training_data": {
36
+ "caption_repository": "fal/cosmos-openvid-1m",
37
+ "caption_revision": "10b41fc29006eff62ff64b8795b8ae8ef7ff9cde",
38
+ "depth_repository": "Yanbin99/Depth-Normal-Videos-42K",
39
+ "depth_revision": "9c5d205edd71077bbab7d96cae971a25a1fbf91f",
40
+ "manifest_sha256": "53844eda18ca75ae22c377354cf37b5e968f47f4452a6130de9f5c18782e3f20",
41
+ "pair_count": 5000,
42
+ "reuses_previous_controlnet_depth_manifest": true
43
+ },
44
+ "training_source": {
45
+ "longlive_revision": "52e9fce478ef1c3eed4c3f918c63f9895c2c61e4",
46
+ "trainer_source_sha256": "7db33c34c1b48f8a14a5709f2420f73adeabf2e668b0af424addf11260e17f01",
47
+ "videox_revision": "1fd9ed9208ba280f6b48c542a7739e636f4e2a14",
48
+ "wrapper_source_sha256": "c27b7f63715af3a92c20fec8bf2d50d7b4019150ba9af6d3e5637556d88c58ee"
49
+ }
50
+ }
release_metadata.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "all_target_shapes_match_pinned_base": true,
3
+ "base_model": "alibaba-pai/Wan2.2-Fun-5B-Control",
4
+ "base_model_revision": "b8bc1a65ab71d054ba4636dc0dac104aa4df2686",
5
+ "classifier_free_guidance": false,
6
+ "display_name": "WAN Fun LoRA",
7
+ "effective_global_batch": 32,
8
+ "evaluation_video_receipts_checked": 3600,
9
+ "excluded_states": [
10
+ "critic_lora"
11
+ ],
12
+ "gpus_per_node": 8,
13
+ "inference_guidance_scale": 1.0,
14
+ "inference_steps": 4,
15
+ "lora_alpha": 64,
16
+ "lora_rank": 64,
17
+ "lora_strength": 1.0,
18
+ "parameter_count": 161218560,
19
+ "repo_id": "Perflow-Shuai/WAN-Fun-LoRA-5B-Control-4Step-CFG1-step1600",
20
+ "schema_version": 1,
21
+ "selected_state": "generator_lora",
22
+ "source_to_native_tensor_identity": true,
23
+ "source_to_safetensors_tensor_identity": true,
24
+ "storage_dtype": "float32",
25
+ "target_count": 300,
26
+ "tensor_count": 600,
27
+ "training_latent_frames": 24,
28
+ "training_nodes": 2,
29
+ "training_pair_count": 5000,
30
+ "training_raw_frames": 93,
31
+ "training_resolution": [
32
+ 832,
33
+ 480
34
+ ],
35
+ "training_step": 1600,
36
+ "training_teacher_conventional_cfg": 4.0,
37
+ "training_teacher_formula": "conditional + 3 * (conditional - unconditional)",
38
+ "training_teacher_real_guidance_scale": 3.0
39
+ }
training_config.yaml ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Training configuration; local asset paths normalized for publication.
2
+ # model_name selects shared VAE/text assets; fun_control_model_path selects the trained DiT.
3
+ infra:
4
+ expected_world_size: 16
5
+ expected_nodes: 2
6
+ expected_gpus_per_node: 8
7
+ required_gpu_name: H100
8
+ sharding_strategy: hybrid_full
9
+ mixed_precision: true
10
+ vae_dtype: float32
11
+ gradient_checkpointing: true
12
+ generator_fsdp_wrap_strategy: size
13
+ real_score_fsdp_wrap_strategy: size
14
+ fake_score_fsdp_wrap_strategy: size
15
+ text_encoder_fsdp_wrap_strategy: size
16
+ model_kwargs:
17
+ model_name: Wan2.2-TI2V-5B
18
+ fun_control_model_path: models/Wan2.2-Fun-5B-Control
19
+ timestep_shift: 5.0
20
+ num_frame_per_block: 8
21
+ local_attn_size: -1
22
+ control_raw_frames: 93
23
+ checkpoints:
24
+ generator_ckpt: null
25
+ real_score_ckpt: null
26
+ fake_score_ckpt: null
27
+ algorithm:
28
+ trainer: score_distillation
29
+ distribution_loss: dmd
30
+ generator_is_causal: false
31
+ real_score_is_causal: false
32
+ fake_score_is_causal: false
33
+ all_causal: false
34
+ backward_simulation: true
35
+ independent_first_frame: false
36
+ ts_schedule: false
37
+ real_guidance_scale: 3.0
38
+ fake_guidance_scale: 0.0
39
+ training:
40
+ lr: 1.0e-05
41
+ lr_critic: 2.0e-06
42
+ weight_decay: 0.0
43
+ beta1: 0.0
44
+ beta2: 0.999
45
+ beta1_critic: 0.0
46
+ beta2_critic: 0.999
47
+ batch_size: 1
48
+ gradient_accumulation_steps: 2
49
+ ema_weight: 0.99
50
+ ema_start_step: 200
51
+ log_iters: 100
52
+ max_checkpoints: 20
53
+ max_iters: 3000
54
+ gc_interval: 100
55
+ dfake_gen_update_ratio: 5
56
+ min_num_training_frames: 24
57
+ num_training_frames: 24
58
+ slice_last_frames: 24
59
+ data:
60
+ data_path: data/depth_video_5k/manifest.jsonl
61
+ eval_data_path: data/depth_video_5k/manifest.jsonl
62
+ control_manifest: data/depth_video_5k/manifest.jsonl
63
+ eval_control_manifest: data/depth_video_5k/manifest.jsonl
64
+ control_raw_frames: 93
65
+ control_min_temporal_difference: 0.0001
66
+ control_num_workers: 2
67
+ image_or_video_shape:
68
+ - 1
69
+ - 24
70
+ - 48
71
+ - 30
72
+ - 52
73
+ full_ref_mode: zero_through_ref_conv
74
+ inference:
75
+ sampling_steps: 4
76
+ guidance_scale: 1.0
77
+ inference_guidance_scale: 1.0
78
+ sink_size: 0
79
+ multi_shot_rope_offset: 0
80
+ evaluation:
81
+ interval: -1
82
+ num_frames: 24
83
+ use_ema: false
84
+ val_batch_size: 1
85
+ save_latents_only: true
86
+ adapter:
87
+ type: lora
88
+ rank: 64
89
+ alpha: 64
90
+ dropout: 0.0
91
+ apply_to_critic: true
92
+ verbose: false
93
+ logging:
94
+ seed: 17
95
+ wandb_entity: null
96
+ wandb_project: LongLive-WanFun5B-Control-DMD