Camellia997 commited on
Commit
e14f899
·
verified ·
1 Parent(s): c3a9448

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +13 -0
  2. LICENSE.txt +62 -0
  3. README.md +295 -0
  4. README_CN.md +284 -0
  5. assets/I2V_exp.png +3 -0
  6. assets/T2V_exp.png +3 -0
  7. assets/efficiency.png +3 -0
  8. assets/logo.svg +72 -0
  9. assets/method.png +3 -0
  10. assets/teaser.jpg +3 -0
  11. assets/videos/more/109_seed_677347.jpg +3 -0
  12. assets/videos/more/14_seed_876367.jpg +3 -0
  13. assets/videos/more/artlist_video_60ca5873a5c21ff4e4785b1997239d87_seed_561368.jpg +0 -0
  14. assets/videos/more/real_1246_seed_277973.jpg +0 -0
  15. configs/infer_pavrm_i2v_720.yaml +100 -0
  16. configs/pre_480.yaml +22 -0
  17. configs/pre_720.yaml +22 -0
  18. configs/train_pavrm_bt_i2v_720.yaml +103 -0
  19. configs/train_pavrm_i2v_480.yaml +102 -0
  20. configs/train_pavrm_i2v_720.yaml +102 -0
  21. configs/train_pavrm_t2v_480.yaml +102 -0
  22. configs/train_pavrm_t2v_720.yaml +102 -0
  23. configs/train_prfl_i2v_480.yaml +95 -0
  24. configs/train_prfl_i2v_720.yaml +96 -0
  25. configs/train_prfl_t2v_480.yaml +95 -0
  26. configs/train_prfl_t2v_720.yaml +96 -0
  27. diffusers_lite.egg-info/PKG-INFO +26 -0
  28. diffusers_lite.egg-info/SOURCES.txt +7 -0
  29. diffusers_lite.egg-info/dependency_links.txt +1 -0
  30. diffusers_lite.egg-info/requires.txt +20 -0
  31. diffusers_lite.egg-info/top_level.txt +1 -0
  32. diffusers_lite/__init__.py +0 -0
  33. diffusers_lite/arguments.py +216 -0
  34. diffusers_lite/constants.py +9 -0
  35. diffusers_lite/datasets/image2video_dataset.py +448 -0
  36. diffusers_lite/schedulers/__init__.py +1 -0
  37. diffusers_lite/schedulers/scheduling_flow_match_discrete.py +275 -0
  38. diffusers_lite/utils/communication.py +691 -0
  39. diffusers_lite/utils/data_utils.py +542 -0
  40. diffusers_lite/utils/diffusion_utils.py +395 -0
  41. diffusers_lite/utils/distill_utils.py +136 -0
  42. diffusers_lite/utils/fsdp_utils.py +168 -0
  43. diffusers_lite/utils/load.py +12 -0
  44. diffusers_lite/utils/model_utils.py +175 -0
  45. diffusers_lite/utils/network.py +217 -0
  46. diffusers_lite/utils/parallel_states.py +141 -0
  47. diffusers_lite/utils/torch_utils.py +59 -0
  48. diffusers_lite/wan/__init__.py +4 -0
  49. diffusers_lite/wan/configs/__init__.py +49 -0
  50. diffusers_lite/wan/configs/shared_config.py +19 -0
.gitattributes CHANGED
@@ -33,3 +33,16 @@ saved_model/**/* 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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
36
+ assets/I2V_exp.png filter=lfs diff=lfs merge=lfs -text
37
+ assets/T2V_exp.png filter=lfs diff=lfs merge=lfs -text
38
+ assets/efficiency.png filter=lfs diff=lfs merge=lfs -text
39
+ assets/method.png filter=lfs diff=lfs merge=lfs -text
40
+ assets/teaser.jpg filter=lfs diff=lfs merge=lfs -text
41
+ assets/videos/more/109_seed_677347.jpg filter=lfs diff=lfs merge=lfs -text
42
+ assets/videos/more/14_seed_876367.jpg filter=lfs diff=lfs merge=lfs -text
43
+ temp_data/ref_imgs/a[[:space:]]brown[[:space:]]bear[[:space:]]in[[:space:]]the[[:space:]]water[[:space:]]with[[:space:]]a[[:space:]]fish[[:space:]]in[[:space:]]its[[:space:]]mouth.jpg filter=lfs diff=lfs merge=lfs -text
44
+ temp_data/ref_imgs/a[[:space:]]close-up[[:space:]]of[[:space:]]a[[:space:]]hippopotamus[[:space:]]eating[[:space:]]grass[[:space:]]in[[:space:]]a[[:space:]]field.jpg filter=lfs diff=lfs merge=lfs -text
45
+ temp_data/ref_imgs/a[[:space:]]sea[[:space:]]turtle[[:space:]]swimming[[:space:]]in[[:space:]]the[[:space:]]ocean[[:space:]]under[[:space:]]the[[:space:]]water.jpg filter=lfs diff=lfs merge=lfs -text
46
+ temp_data/videos/0004e625d5bcb80130e1ea3d204e2488.mp4 filter=lfs diff=lfs merge=lfs -text
47
+ temp_data/videos/00086ac488a1ef8833bb6b0c6714f617.mp4 filter=lfs diff=lfs merge=lfs -text
48
+ temp_data/videos/0012a9d775ff5b5f9f8676a5970691fa.mp4 filter=lfs diff=lfs merge=lfs -text
LICENSE.txt ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Tencent is pleased to support the open source community by making prfl available.
2
+
3
+ Copyright (C) 2026 Tencent. All rights reserved.
4
+
5
+ prfl is licensed under Apache-2.0. prfl does not impose any additional restrictions beyond those specified in license.
6
+
7
+ Terms of the Apache-2.0 License:
8
+ --------------------------------------------------------------------
9
+ Apache License
10
+ Version 2.0, January 2004
11
+ http://www.apache.org/licenses/
12
+
13
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
14
+
15
+ 1. Definitions.
16
+
17
+ "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
18
+
19
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
20
+
21
+ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
24
+
25
+ "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
26
+
27
+ "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
28
+
29
+ "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
30
+
31
+ "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
32
+
33
+ "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
34
+
35
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
36
+
37
+ 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
38
+
39
+ 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
40
+
41
+ 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
42
+
43
+ You must give any other recipients of the Work or Derivative Works a copy of this License; and
44
+ You must cause any modified files to carry prominent notices stating that You changed the files; and
45
+ You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
46
+ If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
47
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
48
+
49
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
50
+
51
+ 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
52
+
53
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
54
+
55
+ 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
56
+
57
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
58
+
59
+ END OF TERMS AND CONDITIONS
60
+
61
+
62
+
README.md ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [中文文档](./README_CN.md)
2
+
3
+ # HY-Video-PRFL
4
+
5
+ <div align="center">
6
+ <img src="assets/logo.svg" height=100>
7
+
8
+ # ⚡ HY-Video-PRFL: Video Generation Models Are Good Latent Reward Models
9
+
10
+ </div>
11
+
12
+ Video generation models can both create and evaluate — we enable 14B models to complete full 720P×81-frame post-training within 67GB VRAM, achieving 1.5× faster speed and 56% improvement in motion quality over traditional methods.
13
+
14
+
15
+ <div align="center">
16
+ <a href="https://github.com/Tencent-Hunyuan/HY-Video-PRFL"><img src="https://img.shields.io/static/v1?label=HY-Video-PRFL%20Code&message=Github&color=blue"></a> &ensp;
17
+ <a href="https://hy-video-prfl.github.io/HY-VIDEO-PRFL/"><img src="https://img.shields.io/static/v1?label=Project%20Page&message=Web&color=green"></a> &ensp;
18
+ <a href="https://arxiv.org/pdf/2511.21541"><img src="https://img.shields.io/badge/ArXiv-2511.21541-red"></a> &ensp;
19
+ </div>
20
+
21
+ <br>
22
+
23
+ ![image](assets/teaser.jpg)
24
+
25
+ > [**HY-Video-PRFL: Video Generation Models Are Good Latent Reward Models**](https://arxiv.org/pdf/2511.21541) <be>
26
+
27
+ ## 🔥🔥🔥 News!!
28
+
29
+ * **Dec 07, 2025**: 👋 We release the training and inference code of HY-Video-PRFL.
30
+ * **Nov 26, 2025**: 👋 We release the paper and project page. [[Paper](https://arxiv.org/pdf/2511.21541)] [[Project Page](https://hy-video-prfl.github.io/HY-VIDEO-PRFL/)]
31
+ ## 📑 Open-source Plan
32
+
33
+ - HY-Video-PRFL
34
+ - [x] Training and inference code for PAVRM
35
+ - [x] Training and inference code for PRFL
36
+
37
+ ## 📋 Table of Contents
38
+
39
+ - [🔥🔥🔥 News!!](#-news)
40
+ - [📑 Open-source Plan](#-open-source-plan)
41
+ - [📖 Abstract](#-abstract)
42
+ - [🏗️ Model Architecture](#-model-architecture)
43
+ - [📊 Performance](#-performance)
44
+ - [🎬 Case Show](#-case-show)
45
+ - [📜 Requirements](#-requirements)
46
+ - [🛠️ Installation](#-installation)
47
+ - [🧱 Download Models](#-download-models)
48
+ - [🎓 Training](#-training)
49
+ - [🚀 Inference](#-inference)
50
+ - [📝 Citation](#-citation)
51
+ - [🙏 Acknowledgements](#-acknowledgements)
52
+
53
+
54
+
55
+ ## 📖 Abstract
56
+
57
+ Reward feedback learning (ReFL) has proven effective for aligning image generation with human preferences. However, its extension to video generation faces significant challenges. Existing video reward models rely on vision-language models designed for pixel-space inputs, confining ReFL optimization to near-complete denoising steps after computationally expensive VAE decoding.
58
+
59
+ **HY-Video-PRFL** introduces **Process Reward Feedback Learning (PRFL)**, a framework that conducts preference optimization entirely in latent space. We demonstrate that pre-trained video generation models are naturally suited for reward modeling in the noisy latent space, enabling efficient gradient backpropagation throughout the full denoising chain without VAE decoding.
60
+
61
+ **Key advantages:**
62
+ - ✅ Efficient latent-space optimization
63
+ - ✅ Significant memory savings
64
+ - ✅ 1.4X faster training compared to RGB ReFL
65
+ - ✅ Better alignment with human preferences
66
+
67
+
68
+ ## 🏗️ Model Architecture
69
+
70
+ ![image](assets/method.png)
71
+
72
+ **Traditional RGB ReFL** relies on vision-language models designed for pixel-space inputs, requiring expensive VAE decoding and confining optimization to late-stage denoising steps.
73
+
74
+ **Our PRFL approach** leverages pre-trained video generation models as reward models in the noisy latent space. This enables:
75
+ - Full-chain gradient backpropagation without VAE decoding
76
+ - Early-stage supervision for motion dynamics and structure coherence
77
+ - Substantial reductions in memory consumption and training time
78
+
79
+
80
+
81
+ ## 📊 Performance
82
+
83
+ ### Quantitative Results
84
+
85
+ Our experiments demonstrate that PRFL achieves substantial motion quality improvements (with +56.00 in dynamic degree, +21.52 in human anatomy and superior alignment with human preferences) as well as significant efficiency gains (with at least 1.4X faster training and notable memory savings).
86
+
87
+ #### Text-to-Video Results
88
+ ![image](assets/T2V_exp.png)
89
+
90
+ #### Image-to-Video Results
91
+ ![image](assets/I2V_exp.png)
92
+
93
+ #### Efficiency Comparison
94
+ <img src="assets/efficiency.png" width="50%">
95
+
96
+ ## 🎬 Case Show
97
+
98
+ ### Text-to-Video Generation
99
+
100
+ |480P Resolution|720P Resolution|
101
+ |---|---|
102
+ |<video src="https://github.com/user-attachments/assets/eed8d875-4b0d-43ec-b013-f0d10c2e107a" width="600" controls autoplay loop></video> <details><summary>📋 Show prompt</summary>```Two shirtless men with short dark hair are sparring in a dimly lit room. They are both wearing boxing gloves, one red and one black. One man is wearing white shorts while the other is wearing black shorts. There are several screens on the wall displaying images of buildings and people.```</details>|<video src="https://github.com/user-attachments/assets/4871a9f9-9b15-4065-8680-1c8059242707" width="600" controls autoplay loop></video> <details><summary>📋 Show prompt</summary>```A woman with fair skin, dark hair tied back, and wearing a light green t-shirt is visible against a gray background. She uses both hands to apply a white substance from below her eyes upward onto her face. Her mouth is slightly open as she spreads the cream.```</details>|
103
+ |<video src="https://github.com/user-attachments/assets/27296444-973b-4815-b103-5a2ee06404db" width="600" controls autoplay loop></video> <details><summary>📋 Show prompt</summary>```The woman has dark eyes and is holding a black smartphone to her ear with her right hand. She is typing on the keyboard of an open silver laptop computer with her left hand. Her fingers have blue nail polish. She is sitting in front of a window covered by sheer white curtains.```</details>|<video src="https://github.com/user-attachments/assets/430ff1ca-63ae-4a67-b6fb-b010c7ceec29" width="600" controls autoplay loop></video> <details><summary>📋 Show prompt</summary>```A light-skinned man with short hair wearing a yellow baseball cap, plaid shirt, and blue overalls stands in a field of sunflowers. He holds a cut sunflower head in his left hand and touches it with his right index finger. Several other sunflowers are visible in the background, some facing away from the camera.```</details>|
104
+
105
+ ### Image-to-Video Generation
106
+
107
+ |480P Resolution|720P Resolution|
108
+ |---|---|
109
+ |<img src="assets/videos/more/14_seed_876367.jpg" width="600">|<img src="assets/videos/more/109_seed_677347.jpg" width="600">|
110
+ |<video src="https://github.com/user-attachments/assets/956f5f64-1680-45a0-8666-9fda8e253017" width="600" controls autoplay loop></video> <details><summary>📋 Show prompt</summary>```A monochromatic video capturing a cat's gaze into the camera```</details>|<video src="https://github.com/user-attachments/assets/8dfeb1ae-8b9c-45aa-899a-3b48903629f9" width="600" controls autoplay loop></video> <details><summary>📋 Show prompt</summary>```A young boy is jumping in the mud```</details>|
111
+ |<img src="assets/videos/more/artlist_video_60ca5873a5c21ff4e4785b1997239d87_seed_561368.jpg" width="600">|<img src="assets/videos/more/real_1246_seed_277973.jpg" width="600">|
112
+ <video src="https://github.com/user-attachments/assets/d6d48d0c-cca5-4bdc-95c7-6a2858679111" width="600" controls autoplay loop></video> <details><summary>📋 Show prompt</summary>```A family of four eats fast food at a table.```</details>| <video src="https://github.com/user-attachments/assets/f39d3382-5430-412d-999e-c4c108835b6c" width="600" controls autoplay loop></video> <details><summary>📋 Show prompt</summary>```Normal speed, Medium shot, Eye level angle, Third person viewpoint, Static camera movement, Frame-within-frame composition, Shallow depth of field, Natural light, Cinematic style, Desaturated palette with slate blue, dusty rose, and dark wood tones color palette, Dramatic atmosphere. The scene is set on a patio or veranda, framed by a stone archway. In the back, there is a large, weathered wooden gate set into a stone wall. Six people are gathered on a stone patio in front of a large wooden gate. On the right, two men are seated at a dark wooden table. An older man in a grey traditional jacket holds a cane and gestures with his right hand while speaking. A younger man in a light grey suit sits beside him, listening. On the left side of the frame, a man in a dark suit stands with his back to the camera. Next to him, a woman in a pink patterned cheongsam and a woman in a grey skirt suit are standing close together, whispering. The women then turn and smile towards the men at the table. The man in the dark suit turns to face the group, revealing a newborn baby cradled in his arms, wrapped in a pink blanket. He takes a few steps forward, holding the baby. The women look at him and the infant. The older man at the table continues to talk, now gesturing towards the man with the baby. The man holding the baby looks down at the infant as he continues to walk slowly. The table is set with white cups, plates, fruit, and a dark wooden box.```</details>|
113
+
114
+
115
+
116
+ ## 📜 Requirements
117
+
118
+ ### Hardware Requirements
119
+
120
+ We recommend using GPUs with at least 80GB of memory for better generation quality.
121
+
122
+ ### Software Requirements
123
+
124
+ * **OS**: Linux
125
+ * **CUDA**: 12.4
126
+
127
+ ## 🛠️ Installation
128
+
129
+ ### Step 1: Clone Repository
130
+ ```bash
131
+ git clone https://github.com/Tencent-Hunyuan/HY-Video-PRFL.git
132
+ cd HY-Video-PRFL
133
+ ```
134
+
135
+ ### Step 2: Setup Environment
136
+
137
+ We recommend CUDA versions 12.4 for installation. Conda's installation instructions are available [here](https://www.anaconda.com/docs/main).
138
+ ```bash
139
+ # Create conda environment
140
+ conda create -n HY-Video-PRFL python==3.10
141
+
142
+ # Activate environment
143
+ conda activate HY-Video-PRFL
144
+
145
+ # Install PyTorch and dependencies (CUDA 12.4)
146
+ pip3 install torch==2.5.0 torchvision==0.20.0 torchaudio==2.5.0 --index-url https://download.pytorch.org/whl/cu121
147
+
148
+ # Install additional dependencies
149
+ pip3 install git+https://github.com/huggingface/transformers qwen-vl-utils[decord]
150
+ pip3 install git+https://github.com/huggingface/diffusers
151
+ pip3 install xfuser -i https://pypi.org/simple
152
+ pip3 install flash-attn==2.5.0 --no-build-isolation
153
+ pip3 install -e .
154
+ pip3 install nvidia-cublas-cu12==12.4.5.8
155
+
156
+ export PYTHONPATH=./
157
+ ```
158
+
159
+ ## 🧱 Download Models
160
+
161
+ Download the pretrained models before training or inference:
162
+
163
+ | Model | Resolution | Download Links | Notes |
164
+ |-------|-----------|----------------|-------|
165
+ | **Wan2.1-T2V-14B** | 480P & 720P | 🤗 [Huggingface](https://huggingface.co/Wan-AI/Wan2.1-T2V-14B) <br> 🤖 [ModelScope](https://www.modelscope.cn/models/Wan-AI/Wan2.1-T2V-14B) | Text-to-Video model |
166
+ | **Wan2.1-I2V-14B-720P** | 720P | 🤗 [Huggingface](https://huggingface.co/Wan-AI/Wan2.1-I2V-14B-720P) <br> 🤖 [ModelScope](https://www.modelscope.cn/models/Wan-AI/Wan2.1-I2V-14B-720P) | Image-to-Video (High-res) |
167
+ | **Wan2.1-I2V-14B-480P** | 480P | 🤗 [Huggingface](https://huggingface.co/Wan-AI/Wan2.1-I2V-14B-480P) <br> 🤖 [ModelScope](https://www.modelscope.cn/models/Wan-AI/Wan2.1-I2V-14B-480P) | Image-to-Video (Standard) |
168
+
169
+ First, make sure you have installed the huggingface CLI or modelscope CLI.
170
+ ```
171
+ pip install -U "huggingface_hub[cli]"
172
+ pip install modelscope
173
+ ```
174
+ Then, download the pretrained DiT and VAE checkpoints. For example, you can use the following command to download the WAN2.1 checkpoint of 720P I2V task to ```./weights``` by default.
175
+ ```
176
+ hf download Wan-AI/Wan2.1-I2V-14B-720P --local-dir ./weights
177
+ ```
178
+
179
+ ## 🎓 Training
180
+
181
+ ### 1️⃣ Data Preprocess on single GPU
182
+ ```bash
183
+ python3 scripts/preprocess/gen_wanx_latent.py --config configs/pre_480.yaml
184
+ ```
185
+
186
+ We provide several videos in ```temp_data/videos``` as template training data and an input json file ```temp_data/temp_input_data.json```template for preprocess. ```configs/pre_480.yaml``` is for 480P latent extraction and ```configs/pre_720.yaml``` is for 720P. The ```json_path``` and ```save_dir``` in config file can be customized with your own training data.
187
+
188
+ ### 2️⃣ Data Annotation and Format Conversion
189
+
190
+ The annotation for reward model (e.g. ```"physics_quality": 1, "human_quality": 1```) should be added in the data meta files (e.g. ```temp_data/480/meta_v1/0004e625d5bcb80130e1ea3d204e2488_meta_v1.json```). Thus we get meta file list ```temp_data/temp_data_480.list``` and ```temp_data/temp_data_720.list``` which can be used in PAVRM and PRFL training.
191
+
192
+ ### 3️⃣ Parallel PAVRM Training on Multiple GPUs
193
+
194
+ For example, to train PAVRM with 8 GPUs, you can use the following command.
195
+
196
+ ```bash
197
+ torchrun --nnodes=1 --nproc_per_node=8 --master_port 29500 scripts/pavrm/train_pavrm.py --config configs/train_pavrm_i2v_720.yaml
198
+ ```
199
+
200
+ The ```meta_file_list``` and ```val_meta_file_list``` in config file can be customized with your own training and validation data. We provide several config files for different settings t2v or i2v, 480P or 720P. To be noted that, we train PAVRM with ce loss. To train PAVRM with bt loss, you can use the config file of ```configs/train_pavrm_bt_i2v_720.yaml```.
201
+
202
+ ### 4️⃣ Parallel PRFL Training on Multiple GPUs
203
+
204
+ ```bash
205
+ torchrun --nnodes=1 --nproc_per_node=8 --master_port 29500 scripts/prfl/train_prfl.py --config configs/train_prfl_i2v_720.yaml
206
+ ```
207
+
208
+ The ```meta_file_list``` in config file can be customized with your own training data, ```lrm_transformer_path```, ```lrm_mlp_path``` and ```lrm_query_attention_path``` in config file are for your reward model obtained from the previous step. We provide several config files for different settings t2v or i2v, 480P or 720P.
209
+
210
+
211
+ ## 🚀 Inference
212
+
213
+ ### 1️⃣ Parallel PAVRM Inference on Multiple GPUs
214
+
215
+ ```bash
216
+ torchrun --nnodes=1 --nproc_per_node=8 --master_port 29500 scripts/pavrm/inference_pavrm.py --config configs/infer_pavrm_i2v_720.yaml
217
+ ```
218
+
219
+ The ```val_meta_file_list``` in config file can be customized with your own inference data, ```resume_transformer_path```, ```resume_mlp_path``` and ```resume_query_attention_path``` in config file are for your reward model to be tested.
220
+
221
+ ### 2️⃣ Parallel PRFL Inference on Multiple GPUs
222
+
223
+ The PRFL Inference is exactly same as its base model (e.g. Wan2.1).
224
+
225
+ ```bash
226
+ export negative_prompt="色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走"
227
+ torchrun --nnodes=1 --nproc_per_node=8 --master_port 29500 scripts/prfl/inference_prfl.py \
228
+ --dit_fsdp \
229
+ --t5_fsdp \
230
+ --ulysses_size 1 \
231
+ --task "i2v-14B"\
232
+ --ckpt_dir "weights/Wan2.1-I2V-14B-720P" \
233
+ --lora_path "" \
234
+ --lora_alpha 0 \
235
+ --dataset_path "temp_data/temp_prfl_infer_data.json" \
236
+ --negative_prompt "$negative_prompt" \
237
+ --size "1280*720" \
238
+ --frame_num 81 \
239
+ --sample_steps 40 \
240
+ --sample_guide_scale 5.0 \
241
+ --sample_shift 5.0 \
242
+ --teacache_thresh 0 \
243
+ --save_folder outputs/infer/prfl_i2v_720 \
244
+ --transformer_path <YOUR_CKPT_PATH> \
245
+ --offload_model False
246
+ ```
247
+
248
+ **Parameters:**
249
+ - `--dit_fsdp` `--t5_fsdp`: Enable FSDP for memory efficiency
250
+ - `--task`: "t2v-14B" or "i2v-14B"
251
+ - `--ckpt_dir`: Path to pretrained checkpoint file
252
+ - `--lora_path` `--lora_alpha`: Path and load weight ratio for LoRA checkpoint file
253
+ - `--dataset_path`: Path to inference dataset file
254
+ - `--size`: Output resolution ("1280\*720" or "832\*480")
255
+ - `--frame_num`: Number of frames to generate (default: 81)
256
+ - `--sample_steps`: Number of inference steps (default: 40)
257
+ - `--sample_guide_scale`: Classifier-free guidance scale (default: 5.0)
258
+ - `--sample_shift`: Flow shift (default: 5.0)
259
+ - `--save_folder`: Path to save generated videos
260
+ - `--teacache_thresh`: Enable teacache
261
+ - `--transformer_path`: Path to your PRFL checkpoint file
262
+ - `--offload_model`: Offload to CPU to save GPU memory
263
+
264
+ ## 📝 Citation
265
+
266
+ If you find **HY-Video-PRFL** useful for your research, please cite:
267
+ ```bibtex
268
+ @article{mi2025video,
269
+ title={Video Generation Models are Good Latent Reward Models},
270
+ author={Mi, Xiaoyue and Yu, Wenqing and Lian, Jiesong and Jie, Shibo and Zhong, Ruizhe and Liu, Zijun and Zhang, Guozhen and Zhou, Zixiang and Xu, Zhiyong and Zhou, Yuan and Lu, Qinglin and Tang, Fan},
271
+ journal={arXiv preprint arXiv:2511.21541},
272
+ year={2025}
273
+ }
274
+ ```
275
+
276
+
277
+ ## 🙏 Acknowledgements
278
+
279
+ We sincerely thank the contributors to the following projects:
280
+ - [HunyuanVideo](https://github.com/Tencent/HunyuanVideo)
281
+ - [Wan2.1](https://github.com/Wan-Video/Wan2.1)
282
+ - [ImageReward](https://github.com/THUDM/ImageReward)
283
+ - [Diffusers](https://github.com/huggingface/diffusers)
284
+ - [HuggingFace](https://huggingface.co)
285
+ - [DeepSpeed](https://github.com/deepspeedai/DeepSpeed)
286
+
287
+
288
+
289
+ ---
290
+
291
+ <div align="center">
292
+
293
+ **Star ⭐ this repo if you find it helpful!**
294
+
295
+ </div>
README_CN.md ADDED
@@ -0,0 +1,284 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HY-Video-PRFL
2
+
3
+ <div align="center">
4
+ <img src="assets/logo.svg" height=100>
5
+
6
+ # ⚡ HY-Video-PRFL: 视频生成模型是优秀的潜在奖励模型
7
+
8
+ </div>
9
+
10
+ 视频生成模型既能创造也能评估——我们使14B模型能够在67GB显存内完成完整的720P×81帧后训练,相比传统方法实现1.5倍的速度提升和56%的运动质量改进。
11
+
12
+ <div align="center">
13
+ <a href="https://github.com/Tencent-Hunyuan/HY-Video-PRFL"><img src="https://img.shields.io/static/v1?label=HY-Video-PRFL%20Code&message=Github&color=blue"></a> &ensp;
14
+ <a href="https://hy-video-prfl.github.io/HY-VIDEO-PRFL/"><img src="https://img.shields.io/static/v1?label=Project%20Page&message=Web&color=green"></a> &ensp;
15
+ <a href="https://arxiv.org/pdf/2511.21541"><img src="https://img.shields.io/badge/ArXiv-2511.21541-red"></a> &ensp;
16
+ </div>
17
+
18
+ <br>
19
+
20
+ ![image](assets/teaser.jpg)
21
+
22
+ > [**HY-Video-PRFL: 视频生成模型是优秀的潜在奖励模型**](https://arxiv.org/pdf/2511.21541)
23
+
24
+ ## 🔥🔥🔥 最新动态!
25
+
26
+ * **2025年12月7日**: 👋 我们发布了HY-Video-PRFL的训练和推理代码。
27
+ * **2025年11月26日**: 👋 我们发布了论文和项目主页。[[论文](https://arxiv.org/pdf/2511.21541)] [[项目主页](https://hy-video-prfl.github.io/HY-VIDEO-PRFL/)]
28
+
29
+ ## 📑 开源计划
30
+
31
+ - HY-Video-PRFL
32
+ - [x] PAVRM的训练和推理代码
33
+ - [x] PRFL的训练和推理代码
34
+
35
+ ## 📋 目录
36
+
37
+ - [🔥🔥🔥 最新动态!](#-最新动态)
38
+ - [📑 开源计划](#-开源计划)
39
+ - [📖 摘要](#-摘要)
40
+ - [🏗️ 模型架构](#️-模型架构)
41
+ - [📊 性能表现](#-性能表现)
42
+ - [🎬 案例展示](#-案例展示)
43
+ - [📜 环境要求](#-环境要求)
44
+ - [🛠️ 安装](#️-安装)
45
+ - [🧱 下载模型](#-下载模型)
46
+ - [🎓 训练](#-训练)
47
+ - [🚀 推理](#-推理)
48
+ - [📝 引用](#-引用)
49
+ - [🙏 致谢](#-致谢)
50
+
51
+ ## 📖 摘要
52
+
53
+ 奖励反馈学习(ReFL)已被证明能有效地使图像生成与人类偏好保持一致。然而,将其扩展到视频生成面临着重大挑战。现有的视频奖励模型依赖于为像素空间输入设计的视觉-语言模型,在计算成本高昂的VAE解码后,将ReFL优化限制在接近完成的去噪步骤。
54
+
55
+ **HY-Video-PRFL** 引入了**过程奖励反馈学习(PRFL)**,这是一个完全在潜在空间中进行偏好优化的框架。我们证明了预训练的视频生成模型天然适合在噪声潜在空间中进行奖励建模,使得能够在整个去噪链中进行高效的梯度反向传播,而无需VAE解码。
56
+
57
+ **核心优势:**
58
+ - ✅ 高效的潜在空间优化
59
+ - ✅ 显著的内存节省
60
+ - ✅ 相比RGB ReFL快1.4倍的训练速度
61
+ - ✅ 更好地与人类偏好保持一致
62
+
63
+ ## 🏗️ 模型架构
64
+
65
+ ![image](assets/method.png)
66
+
67
+ **传统的RGB ReFL** 依赖于为像素空间输入设计的视觉-语言模型,需要昂贵的VAE解码,并将优化限制在后期去噪步骤。
68
+
69
+ **我们的PRFL方法** 利用预训练的视频生成模型作为噪声潜在空间中的奖励模型。这实现了:
70
+ - 无需VAE解码的全链梯度反向传播
71
+ - 针对运动动态和结构一致性的早期监督
72
+ - 大幅减少内存消耗和训练时间
73
+
74
+ ## 📊 性能表现
75
+
76
+ ### 定量结果
77
+
78
+ 我们的实验表明,PRFL在运动质量方面实现了显著改进(动态度提升+56.00,人体解剖结构提升+21.52,并且与人类偏好有更好的对齐),同时在效率方面也取得了显著提升(至少快1.4倍的训练速度和显著的内存节省)。
79
+
80
+ #### 文本生成视频结果
81
+ ![image](assets/T2V_exp.png)
82
+
83
+ #### 图像生成视频结果
84
+ ![image](assets/I2V_exp.png)
85
+
86
+ #### 效率对比
87
+ <img src="assets/efficiency.png" width="50%">
88
+
89
+ ## 🎬 案例展示
90
+
91
+ ### 文本生成视频
92
+
93
+ |480P 分辨率|720P 分辨率|
94
+ |---|---|
95
+ |<video src="https://github.com/user-attachments/assets/eed8d875-4b0d-43ec-b013-f0d10c2e107a" width="600" controls autoplay loop></video> <details><summary>📋 展示提示词</summary>```Two shirtless men with short dark hair are sparring in a dimly lit room. They are both wearing boxing gloves, one red and one black. One man is wearing white shorts while the other is wearing black shorts. There are several screens on the wall displaying images of buildings and people.```</details>|<video src="https://github.com/user-attachments/assets/4871a9f9-9b15-4065-8680-1c8059242707" width="600" controls autoplay loop></video> <details><summary>📋 展示提示词</summary>```A woman with fair skin, dark hair tied back, and wearing a light green t-shirt is visible against a gray background. She uses both hands to apply a white substance from below her eyes upward onto her face. Her mouth is slightly open as she spreads the cream.```</details>|
96
+ |<video src="https://github.com/user-attachments/assets/27296444-973b-4815-b103-5a2ee06404db" width="600" controls autoplay loop></video> <details><summary>📋 展示提示词</summary>```The woman has dark eyes and is holding a black smartphone to her ear with her right hand. She is typing on the keyboard of an open silver laptop computer with her left hand. Her fingers have blue nail polish. She is sitting in front of a window covered by sheer white curtains.```</details>|<video src="https://github.com/user-attachments/assets/430ff1ca-63ae-4a67-b6fb-b010c7ceec29" width="600" controls autoplay loop></video> <details><summary>📋 展示提示词</summary>```A light-skinned man with short hair wearing a yellow baseball cap, plaid shirt, and blue overalls stands in a field of sunflowers. He holds a cut sunflower head in his left hand and touches it with his right index finger. Several other sunflowers are visible in the background, some facing away from the camera.```</details>|
97
+
98
+ ### 图像生成视频
99
+
100
+ |480P 分辨率|720P 分辨率|
101
+ |---|---|
102
+ |<img src="assets/videos/more/14_seed_876367.jpg" width="600">|<img src="assets/videos/more/109_seed_677347.jpg" width="600">|
103
+ |<video src="https://github.com/user-attachments/assets/956f5f64-1680-45a0-8666-9fda8e253017" width="600" controls autoplay loop></video> <details><summary>📋 展示提示词</summary>```A monochromatic video capturing a cat's gaze into the camera```</details>|<video src="https://github.com/user-attachments/assets/8dfeb1ae-8b9c-45aa-899a-3b48903629f9" width="600" controls autoplay loop></video> <details><summary>📋 展示提示词</summary>```A young boy is jumping in the mud```</details>|
104
+ |<img src="assets/videos/more/artlist_video_60ca5873a5c21ff4e4785b1997239d87_seed_561368.jpg" width="600">|<img src="assets/videos/more/real_1246_seed_277973.jpg" width="600">|
105
+ <video src="https://github.com/user-attachments/assets/d6d48d0c-cca5-4bdc-95c7-6a2858679111" width="600" controls autoplay loop></video> <details><summary>📋 展示提示词</summary>```A family of four eats fast food at a table.```</details>| <video src="https://github.com/user-attachments/assets/f39d3382-5430-412d-999e-c4c108835b6c" width="600" controls autoplay loop></video> <details><summary>📋 展示提示词</summary>```Normal speed, Medium shot, Eye level angle, Third person viewpoint, Static camera movement, Frame-within-frame composition, Shallow depth of field, Natural light, Cinematic style, Desaturated palette with slate blue, dusty rose, and dark wood tones color palette, Dramatic atmosphere. The scene is set on a patio or veranda, framed by a stone archway. In the back, there is a large, weathered wooden gate set into a stone wall. Six people are gathered on a stone patio in front of a large wooden gate. On the right, two men are seated at a dark wooden table. An older man in a grey traditional jacket holds a cane and gestures with his right hand while speaking. A younger man in a light grey suit sits beside him, listening. On the left side of the frame, a man in a dark suit stands with his back to the camera. Next to him, a woman in a pink patterned cheongsam and a woman in a grey skirt suit are standing close together, whispering. The women then turn and smile towards the men at the table. The man in the dark suit turns to face the group, revealing a newborn baby cradled in his arms, wrapped in a pink blanket. He takes a few steps forward, holding the baby. The women look at him and the infant. The older man at the table continues to talk, now gesturing towards the man with the baby. The man holding the baby looks down at the infant as he continues to walk slowly. The table is set with white cups, plates, fruit, and a dark wooden box.```</details>|
106
+
107
+
108
+ ## 📜 环境要求
109
+
110
+ ### 硬件要求
111
+
112
+ 我们建议使用至少80GB显存的GPU以获得更好的生成质量。
113
+
114
+ ### 软件要求
115
+
116
+ * **操作系统**: Linux
117
+ * **CUDA**: 12.4
118
+
119
+ ## 🛠️ 安装
120
+
121
+ ### 步骤1: 克隆仓库
122
+ ```bash
123
+ git clone https://github.com/Tencent-Hunyuan/HY-Video-PRFL.git
124
+ cd HY-Video-PRFL
125
+ ```
126
+
127
+ ### 步骤2: 设置环境
128
+
129
+ 我们推荐使用CUDA 12.4版本进行安装。Conda的安装说明可在[这里](https://www.anaconda.com/docs/main)找到。
130
+
131
+ ```bash
132
+ # 创建conda环境
133
+ conda create -n HY-Video-PRFL python==3.10
134
+
135
+ # 激活环境
136
+ conda activate HY-Video-PRFL
137
+
138
+ # 安装PyTorch和依赖项(CUDA 12.4)
139
+ pip3 install torch==2.5.0 torchvision==0.20.0 torchaudio==2.5.0 --index-url https://download.pytorch.org/whl/cu121
140
+
141
+ # 安装额外依赖项
142
+ pip3 install git+https://github.com/huggingface/transformers qwen-vl-utils[decord]
143
+ pip3 install git+https://github.com/huggingface/diffusers
144
+ pip3 install xfuser -i https://pypi.org/simple
145
+ pip3 install flash-attn==2.5.0 --no-build-isolation
146
+ pip3 install -e .
147
+ pip3 install nvidia-cublas-cu12==12.4.5.8
148
+
149
+ export PYTHONPATH=./
150
+ ```
151
+
152
+ ## 🧱 下载模型
153
+
154
+ 在训练或推理前下载预训练模型:
155
+
156
+ | 模型 | 分辨率 | 下载链接 | 说明 |
157
+ |-------|-----------|----------------|-------|
158
+ | **Wan2.1-T2V-14B** | 480P & 720P | 🤗 [Huggingface](https://huggingface.co/Wan-AI/Wan2.1-T2V-14B) <br> 🤖 [ModelScope](https://www.modelscope.cn/models/Wan-AI/Wan2.1-T2V-14B) | 文本生成视频模型 |
159
+ | **Wan2.1-I2V-14B-720P** | 720P | 🤗 [Huggingface](https://huggingface.co/Wan-AI/Wan2.1-I2V-14B-720P) <br> 🤖 [ModelScope](https://www.modelscope.cn/models/Wan-AI/Wan2.1-I2V-14B-720P) | 图像生成视频(高分辨率) |
160
+ | **Wan2.1-I2V-14B-480P** | 480P | 🤗 [Huggingface](https://huggingface.co/Wan-AI/Wan2.1-I2V-14B-480P) <br> 🤖 [ModelScope](https://www.modelscope.cn/models/Wan-AI/Wan2.1-I2V-14B-480P) | 图像生成视频(标准) |
161
+
162
+ 首先,确保已安装huggingface CLI或modelscope CLI。
163
+ ```
164
+ pip install -U "huggingface_hub[cli]"
165
+ pip install modelscope
166
+ ```
167
+ 然后,下载预训练的DiT和VAE检查点。例如,可以使用以下命令将720P I2V任务的WAN2.1检查点下载到默认的```./weights```目录。
168
+ ```
169
+ hf download Wan-AI/Wan2.1-I2V-14B-720P --local-dir ./weights
170
+ ```
171
+
172
+ ## 🎓 训练
173
+
174
+ ### 1️⃣ 单GPU数据预处理
175
+ ```bash
176
+ python3 scripts/preprocess/gen_wanx_latent.py --config configs/pre_480.yaml
177
+ ```
178
+
179
+ 我们在```temp_data/videos```中提供了几个视频作为模板训练数据,以及用于预处理的输入json文件```temp_data/temp_input_data.json```模板。```configs/pre_480.yaml```用于480P潜在提取,```configs/pre_720.yaml```用于720P。配置文件中的```json_path```和```save_dir```可以根据自己的训练数据自定义。
180
+
181
+ ### 2️⃣ 数据标注和格式转换
182
+
183
+ 奖励模型的标注(例如```"physics_quality": 1, "human_quality": 1```)应添加到数据元文件中(例如```temp_data/480/meta_v1/0004e625d5bcb80130e1ea3d204e2488_meta_v1.json```)。这样我们得到元文件列表```temp_data/temp_data_480.list```和```temp_data/temp_data_720.list```,可用于PAVRM和PRFL训练。
184
+
185
+ ### 3️⃣ 多GPU并行PAVRM训练
186
+
187
+ 例如,要使用8个GPU训练PAVRM,可以使用以下命令。
188
+
189
+ ```bash
190
+ torchrun --nnodes=1 --nproc_per_node=8 --master_port 29500 scripts/pavrm/train_pavrm.py --config configs/train_pavrm_i2v_720.yaml
191
+ ```
192
+
193
+ 配置文件中的```meta_file_list```和```val_meta_file_list```可以根据自己的训练和验证数据自定义。我们为不同设置(t2v或i2v,480P或720P)提供了几个配置文件。需要注意的是,我们使用ce损失训练PAVRM。要使用bt损失训练PAVRM,可以使用配置文件```configs/train_pavrm_bt_i2v_720.yaml```。
194
+
195
+ ### 4️⃣ 多GPU并行PRFL训练
196
+
197
+ ```bash
198
+ torchrun --nnodes=1 --nproc_per_node=8 --master_port 29500 scripts/prfl/train_prfl.py --config configs/train_prfl_i2v_720.yaml
199
+ ```
200
+
201
+ 配置文件中的```meta_file_list```可以根据自己的训练数据自定义,配置文件中的```lrm_transformer_path```、```lrm_mlp_path```和```lrm_query_attention_path```用于从上一步获得的奖励模型。我们为不同设置(t2v或i2v,480P或720P)提供了几个配置文件。
202
+
203
+ ## 🚀 推理
204
+
205
+ ### 1️⃣ 多GPU并行PAVRM推理
206
+
207
+ ```bash
208
+ torchrun --nnodes=1 --nproc_per_node=8 --master_port 29500 scripts/pavrm/inference_pavrm.py --config configs/infer_pavrm_i2v_720.yaml
209
+ ```
210
+
211
+ 配置文件中的```val_meta_file_list```可以根据自己的推理数据自定义,配置文件中的```resume_transformer_path```、```resume_mlp_path```和```resume_query_attention_path```用于待测试的奖励模型。
212
+
213
+ ### 2️⃣ 多GPU并行PRFL推理
214
+
215
+ PRFL推理与其基础模型(例如Wan2.1)完全相同。
216
+
217
+ ```bash
218
+ export negative_prompt="色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走"
219
+ torchrun --nnodes=1 --nproc_per_node=8 --master_port 29500 scripts/prfl/inference_prfl.py \
220
+ --dit_fsdp \
221
+ --t5_fsdp \
222
+ --ulysses_size 1 \
223
+ --task "i2v-14B"\
224
+ --ckpt_dir "weights/Wan2.1-I2V-14B-720P" \
225
+ --lora_path "" \
226
+ --lora_alpha 0 \
227
+ --dataset_path "temp_data/temp_prfl_infer_data.json" \
228
+ --negative_prompt "$negative_prompt" \
229
+ --size "1280*720" \
230
+ --frame_num 81 \
231
+ --sample_steps 40 \
232
+ --sample_guide_scale 5.0 \
233
+ --sample_shift 5.0 \
234
+ --teacache_thresh 0 \
235
+ --save_folder outputs/infer/prfl_i2v_720 \
236
+ --transformer_path <YOUR_CKPT_PATH> \
237
+ --offload_model False
238
+ ```
239
+
240
+ **参数说明:**
241
+ - `--dit_fsdp` `--t5_fsdp`: 启用FSDP以提高内存效率
242
+ - `--task`: "t2v-14B"或"i2v-14B"
243
+ - `--ckpt_dir`: 预训练检查点文件路径
244
+ - `--lora_path` `--lora_alpha`: LoRA检查点文件路径和加载权重比例
245
+ - `--dataset_path`: 推理数据集文件路径
246
+ - `--size`: 输出分辨率("1280\*720"或"832\*480")
247
+ - `--frame_num`: 生成的帧数(默认:81)
248
+ - `--sample_steps`: 推理步数(默认:40)
249
+ - `--sample_guide_scale`: 无分类器引导比例(默认:5.0)
250
+ - `--sample_shift`: 流偏移(默认:5.0)
251
+ - `--save_folder`: 保存生成视频的路径
252
+ - `--teacache_thresh`: 启用teacache
253
+ - `--transformer_path`: PRFL检查点文件路径
254
+ - `--offload_model`: 卸载到CPU以节省GPU内存
255
+
256
+ ## 📝 引用
257
+
258
+ 如果您发现**HY-Video-PRFL**对您的研究有用,请引用:
259
+ ```bibtex
260
+ @article{mi2025video,
261
+ title={Video Generation Models are Good Latent Reward Models},
262
+ author={Mi, Xiaoyue and Yu, Wenqing and Lian, Jiesong and Jie, Shibo and Zhong, Ruizhe and Liu, Zijun and Zhang, Guozhen and Zhou, Zixiang and Xu, Zhiyong and Zhou, Yuan and Lu, Qinglin and Tang, Fan},
263
+ journal={arXiv preprint arXiv:2511.21541},
264
+ year={2025}
265
+ }
266
+ ```
267
+
268
+ ## 🙏 致谢
269
+
270
+ 我们真诚感谢以下项目的贡献者:
271
+ - [HunyuanVideo](https://github.com/Tencent/HunyuanVideo)
272
+ - [Wan2.1](https://github.com/Wan-Video/Wan2.1)
273
+ - [ImageReward](https://github.com/THUDM/ImageReward)
274
+ - [Diffusers](https://github.com/huggingface/diffusers)
275
+ - [HuggingFace](https://huggingface.co)
276
+ - [DeepSpeed](https://github.com/deepspeedai/DeepSpeed)
277
+
278
+ ---
279
+
280
+ <div align="center">
281
+
282
+ **如果您觉得有帮助,请给这个仓库加星 ⭐!**
283
+
284
+ </div>
assets/I2V_exp.png ADDED

Git LFS Details

  • SHA256: 4a99564e5180fb7fd7b7594df70ba296e11ce2b2d619274b87ed301812cb4547
  • Pointer size: 131 Bytes
  • Size of remote file: 426 kB
assets/T2V_exp.png ADDED

Git LFS Details

  • SHA256: 584a4421383c137599ca5304bd6d7334376483fbe58f31d6a7bee617f0893ce2
  • Pointer size: 131 Bytes
  • Size of remote file: 523 kB
assets/efficiency.png ADDED

Git LFS Details

  • SHA256: bc4c7b60070fcc479bacdd47dd28946a0b505d8ed2ca237691b7ddf72f477a5e
  • Pointer size: 131 Bytes
  • Size of remote file: 174 kB
assets/logo.svg ADDED
assets/method.png ADDED

Git LFS Details

  • SHA256: 6c1fa1fac95659c70e134b24e3d58be3d24c98c902e58aeea682807d55cd279a
  • Pointer size: 131 Bytes
  • Size of remote file: 750 kB
assets/teaser.jpg ADDED

Git LFS Details

  • SHA256: a7146364ab134cb61ccf8541aab6f8296f32bd11da5a25cc8bb4ec5f97c07daf
  • Pointer size: 131 Bytes
  • Size of remote file: 841 kB
assets/videos/more/109_seed_677347.jpg ADDED

Git LFS Details

  • SHA256: f4f3d86494e82c0c5cc7f69734eb11a1e0eca5fc691a2e981f7c96e0c787f216
  • Pointer size: 132 Bytes
  • Size of remote file: 2.21 MB
assets/videos/more/14_seed_876367.jpg ADDED

Git LFS Details

  • SHA256: 3fba8138089c49acd66485a1cbb02b0d14d233214d0abee1e17fbb0574e7f744
  • Pointer size: 131 Bytes
  • Size of remote file: 251 kB
assets/videos/more/artlist_video_60ca5873a5c21ff4e4785b1997239d87_seed_561368.jpg ADDED
assets/videos/more/real_1246_seed_277973.jpg ADDED
configs/infer_pavrm_i2v_720.yaml ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ train_id: "pavrm_i2v_720"
2
+ task: "i2v-14b-720p" # t2v-1.3b, i2v-1.3b, t2v-14b, i2v-14b-480p, i2v-14b-720p
3
+
4
+ model:
5
+ base_path: "weights/Wan2.1-I2V-14B-720P"
6
+ init_transformer_path: null
7
+ resume_transformer_path: null
8
+ resume_mlp_path: null
9
+ resume_query_attention_path: null
10
+
11
+ patch_size: [1, 2, 2]
12
+ lora:
13
+ use_lora: false
14
+ lora_rank: 128
15
+ target_modules: ["q", "k", "v", "o"]
16
+ resume_lora_path: null # load lora ckpt if not empty
17
+ ema:
18
+ use_ema: false
19
+ ema_decay: 0.99
20
+ fsdp:
21
+ fsdp_sharding_startegy: full
22
+ use_cpu_offload: false
23
+ gradient_checkpointing: true
24
+ selective_checkpointing: 1.0
25
+
26
+ extra_model:
27
+ vae:
28
+ name: Wan2.1_VAE.pth
29
+ vae_stride: [4, 8, 8]
30
+ text_encoder:
31
+ t5_text_len: 512
32
+ t5_checkpoint: models_t5_umt5-xxl-enc-bf16.pth
33
+ t5_tokenizer: google/umt5-xxl
34
+ image_encoder:
35
+ clip_checkpoint: models_clip_open-clip-xlm-roberta-large-vit-huge-14.pth
36
+ clip_tokenizer: xlm-roberta-large
37
+ scheduler:
38
+ flow_shift: 5.0
39
+ num_train_timesteps: 1000
40
+ weighting_scheme: uniform # logit_normal, uniform
41
+ logit_mean: 0
42
+ logit_std: 1
43
+ mode_scale: 1.29
44
+
45
+ dataset:
46
+ val_meta_file_list:
47
+ - temp_data/temp_data_720.list
48
+
49
+ crop_ratio: [1, 1, 1] # width, height
50
+ crop_type: "random" # center, random
51
+ uncond_prob: [0.1, 0.0] # prompt, image
52
+ sp_size: 4
53
+ batch_size: 1 #
54
+ sp_batch_size: 1 #4
55
+ num_workers: 0
56
+ group_frame: null
57
+ group_resolution: null
58
+
59
+ optimizer:
60
+ learning_rate: 1e-6
61
+ learning_rate_mlp: 1e-5
62
+ adam_beta1: 0.9
63
+ adam_beta2: 0.999
64
+ weight_decay: 0.01
65
+ lr_scheduler: constant
66
+ lr_warmup_steps: 0
67
+ lr_num_cycles: 1
68
+ lr_power: 1.0
69
+ max_train_steps: 1000000
70
+
71
+ train:
72
+ seed: 110221
73
+ precision: bf16
74
+ extra_precision: bf16
75
+ allow_tf32: false
76
+ save_interval: 100
77
+ sanity_check_interval: 0
78
+ teacher_student_parallel: true
79
+ dpo_beta: 500
80
+ gradient_accumulation_steps: 1
81
+
82
+ eval:
83
+ seed: 0
84
+
85
+ save:
86
+ output_dir: "outputs/infer"
87
+ sanity_check_dir: null
88
+
89
+ lrm:
90
+ query_attention:
91
+ num_queries: 1
92
+ num_heads: 8
93
+ dropout: 0.
94
+ return_type: query
95
+ feature_layer: [8]
96
+ pool: q_attn
97
+ mlp_dim: 5120
98
+ loss: "ce"
99
+ task: "motion_quality"
100
+ trainable_blocks: [0, 1, 2, 3, 4, 5, 6, 7]
configs/pre_480.yaml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ start_idx: 0
2
+ end_idx: null
3
+ sample_n_frames: 81
4
+ aspect_ratio: 1.73
5
+ seed: 42
6
+ precision: 'bf16'
7
+ task: 'i2v'
8
+ base_dir: 'weights/Wan2.1-I2V-14B-480P'
9
+ resolution: [480]
10
+ num_frames: 81
11
+ batch_size: 1
12
+ fps: 16
13
+ json_path: temp_data/temp_input_data.json
14
+ save_dir: temp_data/480
15
+ extract_fps: 16
16
+ model_type: 'wanx'
17
+ vae_path: 'weights/Wan2.1-I2V-14B-480P/Wan2.1_VAE.pth'
18
+ image_processor_path: 'weights/Wan2.1-I2V-14B-480P/xlm-roberta-large'
19
+ image_encoder_path: 'weights/Wan2.1-I2V-14B-480P/models_clip_open-clip-xlm-roberta-large-vit-huge-14.pth'
20
+ tokenizer_path: 'weights/Wan2.1-I2V-14B-480P/google/umt5-xxl'
21
+ text_encoder_path: 'weights/Wan2.1-I2V-14B-480P/models_t5_umt5-xxl-enc-bf16.pth'
22
+ max_sequence_length: 512
configs/pre_720.yaml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ start_idx: 0
2
+ end_idx: null
3
+ sample_n_frames: 81
4
+ aspect_ratio: 1.81
5
+ seed: 42
6
+ precision: 'bf16'
7
+ task: 'i2v'
8
+ base_dir: 'weights/Wan2.1-I2V-14B-720P'
9
+ resolution: [704]
10
+ num_frames: 81
11
+ batch_size: 1
12
+ fps: 16
13
+ json_path: temp_data/temp_input_data.json
14
+ save_dir: temp_data/720
15
+ extract_fps: 16
16
+ model_type: 'wanx'
17
+ vae_path: 'weights/Wan2.1-I2V-14B-720P/Wan2.1_VAE.pth'
18
+ image_processor_path: 'weights/Wan2.1-I2V-14B-720P/xlm-roberta-large'
19
+ image_encoder_path: 'weights/Wan2.1-I2V-14B-720P/models_clip_open-clip-xlm-roberta-large-vit-huge-14.pth'
20
+ tokenizer_path: 'weights/Wan2.1-I2V-14B-720P/google/umt5-xxl'
21
+ text_encoder_path: 'weights/Wan2.1-I2V-14B-720P/models_t5_umt5-xxl-enc-bf16.pth'
22
+ max_sequence_length: 512
configs/train_pavrm_bt_i2v_720.yaml ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ train_id: "pavrm_bt_i2v_720"
2
+ task: "i2v-14b-720p" # t2v-1.3b, i2v-1.3b, t2v-14b, i2v-14b-480p, i2v-14b-720p
3
+
4
+ model:
5
+ base_path: "weights/Wan2.1-I2V-14B-720P"
6
+ init_transformer_path: null
7
+ resume_transformer_path: null
8
+ resume_mlp_path: null
9
+ patch_size: [1, 2, 2]
10
+ lora:
11
+ use_lora: false
12
+ lora_rank: 128
13
+ target_modules: ["q", "k", "v", "o"]
14
+ resume_lora_path: null # load lora ckpt if not empty
15
+ ema:
16
+ use_ema: false
17
+ ema_decay: 0.99
18
+ fsdp:
19
+ fsdp_sharding_startegy: full
20
+ use_cpu_offload: false
21
+ gradient_checkpointing: true
22
+ selective_checkpointing: 1.0
23
+
24
+ extra_model:
25
+ vae:
26
+ name: Wan2.1_VAE.pth
27
+ vae_stride: [4, 8, 8]
28
+ text_encoder:
29
+ t5_text_len: 512
30
+ t5_checkpoint: models_t5_umt5-xxl-enc-bf16.pth
31
+ t5_tokenizer: google/umt5-xxl
32
+ image_encoder:
33
+ clip_checkpoint: models_clip_open-clip-xlm-roberta-large-vit-huge-14.pth
34
+ clip_tokenizer: xlm-roberta-large
35
+ scheduler:
36
+ flow_shift: 5.0
37
+ num_train_timesteps: 1000
38
+ weighting_scheme: uniform # logit_normal, uniform
39
+ logit_mean: 0
40
+ logit_std: 1
41
+ mode_scale: 1.29
42
+
43
+ dataset:
44
+ meta_file_list:
45
+ - temp_data/temp_data_720.list
46
+ meta_file_lose_list:
47
+ - temp_data/temp_data_720.list
48
+ val_meta_file_list:
49
+ - temp_data/temp_data_720.list
50
+
51
+ crop_ratio: [1, 1, 1] # width, height
52
+ crop_type: "random" # center, random
53
+ uncond_prob: [0.1, 0.0] # prompt, image
54
+ sp_size: 4
55
+ batch_size: 1 #
56
+ sp_batch_size: 1 #4
57
+ num_workers: 8
58
+ group_frame: null
59
+ group_resolution: null
60
+
61
+ optimizer:
62
+ learning_rate: 1e-6
63
+ learning_rate_mlp: 1e-5
64
+ adam_beta1: 0.9
65
+ adam_beta2: 0.999
66
+ weight_decay: 0.01
67
+ lr_scheduler: constant
68
+ lr_warmup_steps: 0
69
+ lr_num_cycles: 1
70
+ lr_power: 1.0
71
+ max_train_steps: 1000000
72
+
73
+ train:
74
+ seed: 110221
75
+ precision: bf16
76
+ extra_precision: bf16
77
+ allow_tf32: false
78
+ save_interval: 100 #100
79
+ sanity_check_interval: 0
80
+ teacher_student_parallel: true
81
+ dpo_beta: 500
82
+ gradient_accumulation_steps: 1
83
+
84
+ eval:
85
+ seed: 0
86
+ timestep: [201, 400, 600, 800, 1000]
87
+
88
+ save:
89
+ output_dir: "outputs"
90
+ sanity_check_dir: null
91
+
92
+ lrm:
93
+ query_attention:
94
+ num_queries: 1
95
+ num_heads: 8
96
+ dropout: 0.
97
+ return_type: query
98
+ feature_layer: [8]
99
+ pool: q_attn
100
+ mlp_dim: 5120
101
+ loss: "bt"
102
+ task: "motion_quality"
103
+ trainable_blocks: [0, 1, 2, 3, 4, 5, 6, 7]
configs/train_pavrm_i2v_480.yaml ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ train_id: "pavrm_i2v_480"
2
+ task: "i2v-14b-480p" # t2v-1.3b, i2v-1.3b, t2v-14b, i2v-14b-480p, i2v-14b-720p
3
+
4
+ model:
5
+ base_path: "weights/Wan2.1-I2V-14B-480P"
6
+ init_transformer_path: null
7
+ resume_transformer_path: null
8
+ resume_mlp_path: null
9
+ resume_query_attention_path: null
10
+ patch_size: [1, 2, 2]
11
+ lora:
12
+ use_lora: false
13
+ lora_rank: 128
14
+ target_modules: ["q", "k", "v", "o"]
15
+ resume_lora_path: null # load lora ckpt if not empty
16
+ ema:
17
+ use_ema: false
18
+ ema_decay: 0.99
19
+ fsdp:
20
+ fsdp_sharding_startegy: full
21
+ use_cpu_offload: false
22
+ gradient_checkpointing: true
23
+ selective_checkpointing: 1.0
24
+
25
+ extra_model:
26
+ vae:
27
+ name: Wan2.1_VAE.pth
28
+ vae_stride: [4, 8, 8]
29
+ text_encoder:
30
+ t5_text_len: 512
31
+ t5_checkpoint: models_t5_umt5-xxl-enc-bf16.pth
32
+ t5_tokenizer: google/umt5-xxl
33
+ image_encoder:
34
+ clip_checkpoint: models_clip_open-clip-xlm-roberta-large-vit-huge-14.pth
35
+ clip_tokenizer: xlm-roberta-large
36
+ scheduler:
37
+ flow_shift: 5.0
38
+ num_train_timesteps: 1000
39
+ weighting_scheme: uniform # logit_normal, uniform
40
+ logit_mean: 0
41
+ logit_std: 1
42
+ mode_scale: 1.29
43
+
44
+ dataset:
45
+ meta_file_list:
46
+ - temp_data/temp_data_480.list
47
+ val_meta_file_list:
48
+ - temp_data/temp_data_480.list
49
+
50
+ crop_ratio: [1, 1, 1] # width, height
51
+ crop_type: "random" # center, random
52
+ uncond_prob: [0.1, 0.0] # prompt, image
53
+ sp_size: 4
54
+ batch_size: 1 #
55
+ sp_batch_size: 1 #4
56
+ num_workers: 8
57
+ group_frame: null
58
+ group_resolution: null
59
+
60
+ optimizer:
61
+ learning_rate: 1e-6
62
+ # learning_rate_mlp: 1e-5
63
+ adam_beta1: 0.9
64
+ adam_beta2: 0.999
65
+ weight_decay: 0.01
66
+ lr_scheduler: constant
67
+ lr_warmup_steps: 0
68
+ lr_num_cycles: 1
69
+ lr_power: 1.0
70
+ max_train_steps: 1000000
71
+
72
+ train:
73
+ seed: 110221
74
+ precision: bf16
75
+ extra_precision: bf16
76
+ allow_tf32: false
77
+ save_interval: 100
78
+ sanity_check_interval: 0
79
+ teacher_student_parallel: true
80
+ dpo_beta: 500
81
+ gradient_accumulation_steps: 1
82
+
83
+ eval:
84
+ seed: 0
85
+ timestep: [201, 400, 600, 800, 1000]
86
+
87
+ save:
88
+ output_dir: "outputs"
89
+ sanity_check_dir: null
90
+
91
+ lrm:
92
+ query_attention:
93
+ num_queries: 1
94
+ num_heads: 8
95
+ dropout: 0.
96
+ return_type: query
97
+ feature_layer: [8]
98
+ pool: q_attn
99
+ mlp_dim: 5120
100
+ loss: "ce"
101
+ task: "motion_quality"
102
+ trainable_blocks: [0, 1, 2, 3, 4, 5, 6, 7]
configs/train_pavrm_i2v_720.yaml ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ train_id: "pavrm_i2v_720"
2
+ task: "i2v-14b-720p" # t2v-1.3b, i2v-1.3b, t2v-14b, i2v-14b-480p, i2v-14b-720p
3
+
4
+ model:
5
+ base_path: "weights/Wan2.1-I2V-14B-720P"
6
+ init_transformer_path: null
7
+ resume_transformer_path: null
8
+ resume_mlp_path: null
9
+ resume_query_attention_path: null
10
+ patch_size: [1, 2, 2]
11
+ lora:
12
+ use_lora: false
13
+ lora_rank: 128
14
+ target_modules: ["q", "k", "v", "o"]
15
+ resume_lora_path: null # load lora ckpt if not empty
16
+ ema:
17
+ use_ema: false
18
+ ema_decay: 0.99
19
+ fsdp:
20
+ fsdp_sharding_startegy: full
21
+ use_cpu_offload: false
22
+ gradient_checkpointing: true
23
+ selective_checkpointing: 1.0
24
+
25
+ extra_model:
26
+ vae:
27
+ name: Wan2.1_VAE.pth
28
+ vae_stride: [4, 8, 8]
29
+ text_encoder:
30
+ t5_text_len: 512
31
+ t5_checkpoint: models_t5_umt5-xxl-enc-bf16.pth
32
+ t5_tokenizer: google/umt5-xxl
33
+ image_encoder:
34
+ clip_checkpoint: models_clip_open-clip-xlm-roberta-large-vit-huge-14.pth
35
+ clip_tokenizer: xlm-roberta-large
36
+ scheduler:
37
+ flow_shift: 5.0
38
+ num_train_timesteps: 1000
39
+ weighting_scheme: uniform # logit_normal, uniform
40
+ logit_mean: 0
41
+ logit_std: 1
42
+ mode_scale: 1.29
43
+
44
+ dataset:
45
+ meta_file_list:
46
+ - temp_data/temp_data_720.list
47
+ val_meta_file_list:
48
+ - temp_data/temp_data_720.list
49
+
50
+ crop_ratio: [1, 1, 1] # width, height
51
+ crop_type: "random" # center, random
52
+ uncond_prob: [0.1, 0.0] # prompt, image
53
+ sp_size: 4
54
+ batch_size: 1
55
+ sp_batch_size: 1
56
+ num_workers: 8
57
+ group_frame: null
58
+ group_resolution: null
59
+
60
+ optimizer:
61
+ learning_rate: 1e-6
62
+ # learning_rate_mlp: 1e-5
63
+ adam_beta1: 0.9
64
+ adam_beta2: 0.999
65
+ weight_decay: 0.01
66
+ lr_scheduler: constant
67
+ lr_warmup_steps: 0
68
+ lr_num_cycles: 1
69
+ lr_power: 1.0
70
+ max_train_steps: 1000000
71
+
72
+ train:
73
+ seed: 110221
74
+ precision: bf16
75
+ extra_precision: bf16
76
+ allow_tf32: false
77
+ save_interval: 100
78
+ sanity_check_interval: 0
79
+ teacher_student_parallel: true
80
+ dpo_beta: 500
81
+ gradient_accumulation_steps: 1
82
+
83
+ eval:
84
+ seed: 0
85
+ timestep: [201, 400, 600, 800, 1000]
86
+
87
+ save:
88
+ output_dir: "outputs"
89
+ sanity_check_dir: null
90
+
91
+ lrm:
92
+ query_attention:
93
+ num_queries: 1
94
+ num_heads: 8
95
+ dropout: 0.
96
+ return_type: query
97
+ feature_layer: [8]
98
+ pool: q_attn
99
+ mlp_dim: 5120
100
+ loss: "ce"
101
+ task: "motion_quality"
102
+ trainable_blocks: [0, 1, 2, 3, 4, 5, 6, 7]
configs/train_pavrm_t2v_480.yaml ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ train_id: "pavrm_t2v_480"
2
+ task: "t2v-14b" # t2v-1.3b, i2v-1.3b, t2v-14b, i2v-14b-480p, i2v-14b-720p
3
+
4
+ model:
5
+ base_path: "weights/Wan2.1-T2V-14B"
6
+ init_transformer_path: null
7
+ resume_transformer_path: null
8
+ resume_mlp_path: null
9
+ resume_query_attention_path: null
10
+ patch_size: [1, 2, 2]
11
+ lora:
12
+ use_lora: false
13
+ lora_rank: 128
14
+ target_modules: ["q", "k", "v", "o"]
15
+ resume_lora_path: null # load lora ckpt if not empty
16
+ ema:
17
+ use_ema: false
18
+ ema_decay: 0.99
19
+ fsdp:
20
+ fsdp_sharding_startegy: full
21
+ use_cpu_offload: false
22
+ gradient_checkpointing: true
23
+ selective_checkpointing: 1.0
24
+
25
+ extra_model:
26
+ vae:
27
+ name: Wan2.1_VAE.pth
28
+ vae_stride: [4, 8, 8]
29
+ text_encoder:
30
+ t5_text_len: 512
31
+ t5_checkpoint: models_t5_umt5-xxl-enc-bf16.pth
32
+ t5_tokenizer: google/umt5-xxl
33
+ image_encoder:
34
+ clip_checkpoint: models_clip_open-clip-xlm-roberta-large-vit-huge-14.pth
35
+ clip_tokenizer: xlm-roberta-large
36
+ scheduler:
37
+ flow_shift: 5.0
38
+ num_train_timesteps: 1000
39
+ weighting_scheme: uniform # logit_normal, uniform
40
+ logit_mean: 0
41
+ logit_std: 1
42
+ mode_scale: 1.29
43
+
44
+ dataset:
45
+ meta_file_list:
46
+ - temp_data/temp_data_480.list
47
+ val_meta_file_list:
48
+ - temp_data/temp_data_480.list
49
+
50
+ crop_ratio: [1, 1, 1] # width, height
51
+ crop_type: "random" # center, random
52
+ uncond_prob: [0.1, 0.0] # prompt, image
53
+ sp_size: 4
54
+ batch_size: 1 #
55
+ sp_batch_size: 1 #4
56
+ num_workers: 8
57
+ group_frame: null
58
+ group_resolution: null
59
+
60
+ optimizer:
61
+ learning_rate: 1e-6
62
+ # learning_rate_mlp: 1e-5
63
+ adam_beta1: 0.9
64
+ adam_beta2: 0.999
65
+ weight_decay: 0.01
66
+ lr_scheduler: constant
67
+ lr_warmup_steps: 0
68
+ lr_num_cycles: 1
69
+ lr_power: 1.0
70
+ max_train_steps: 1000000
71
+
72
+ train:
73
+ seed: 110221
74
+ precision: bf16
75
+ extra_precision: bf16
76
+ allow_tf32: false
77
+ save_interval: 100 #100
78
+ sanity_check_interval: 0
79
+ teacher_student_parallel: true
80
+ dpo_beta: 500
81
+ gradient_accumulation_steps: 1
82
+
83
+ eval:
84
+ seed: 0
85
+ timestep: [201, 400, 600, 800, 1000]
86
+
87
+ save:
88
+ output_dir: "outputs"
89
+ sanity_check_dir: null
90
+
91
+ lrm:
92
+ query_attention:
93
+ num_queries: 1
94
+ num_heads: 8
95
+ dropout: 0.
96
+ return_type: query
97
+ feature_layer: [8]
98
+ pool: q_attn
99
+ mlp_dim: 5120
100
+ loss: "ce"
101
+ task: "motion_quality"
102
+ trainable_blocks: [0, 1, 2, 3, 4, 5, 6, 7]
configs/train_pavrm_t2v_720.yaml ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ train_id: "pavrm_t2v_720"
2
+ task: "t2v-14b" # t2v-1.3b, i2v-1.3b, t2v-14b, i2v-14b-480p, i2v-14b-720p
3
+
4
+ model:
5
+ base_path: "weights/Wan2.1-T2V-14B"
6
+ init_transformer_path: null
7
+ resume_transformer_path: null
8
+ resume_mlp_path: null
9
+ resume_query_attention_path: null
10
+ patch_size: [1, 2, 2]
11
+ lora:
12
+ use_lora: false
13
+ lora_rank: 128
14
+ target_modules: ["q", "k", "v", "o"]
15
+ resume_lora_path: null # load lora ckpt if not empty
16
+ ema:
17
+ use_ema: false
18
+ ema_decay: 0.99
19
+ fsdp:
20
+ fsdp_sharding_startegy: full
21
+ use_cpu_offload: false
22
+ gradient_checkpointing: true
23
+ selective_checkpointing: 1.0
24
+
25
+ extra_model:
26
+ vae:
27
+ name: Wan2.1_VAE.pth
28
+ vae_stride: [4, 8, 8]
29
+ text_encoder:
30
+ t5_text_len: 512
31
+ t5_checkpoint: models_t5_umt5-xxl-enc-bf16.pth
32
+ t5_tokenizer: google/umt5-xxl
33
+ image_encoder:
34
+ clip_checkpoint: models_clip_open-clip-xlm-roberta-large-vit-huge-14.pth
35
+ clip_tokenizer: xlm-roberta-large
36
+ scheduler:
37
+ flow_shift: 5.0
38
+ num_train_timesteps: 1000
39
+ weighting_scheme: uniform # logit_normal, uniform
40
+ logit_mean: 0
41
+ logit_std: 1
42
+ mode_scale: 1.29
43
+
44
+ dataset:
45
+ meta_file_list:
46
+ - temp_data/temp_data_720.list
47
+ val_meta_file_list:
48
+ - temp_data/temp_data_720.list
49
+
50
+ crop_ratio: [1, 1, 1] # width, height
51
+ crop_type: "random" # center, random
52
+ uncond_prob: [0.1, 0.0] # prompt, image
53
+ sp_size: 4
54
+ batch_size: 1 #
55
+ sp_batch_size: 1 #4
56
+ num_workers: 8
57
+ group_frame: null
58
+ group_resolution: null
59
+
60
+ optimizer:
61
+ learning_rate: 1e-6
62
+ # learning_rate_mlp: 1e-5
63
+ adam_beta1: 0.9
64
+ adam_beta2: 0.999
65
+ weight_decay: 0.01
66
+ lr_scheduler: constant
67
+ lr_warmup_steps: 0
68
+ lr_num_cycles: 1
69
+ lr_power: 1.0
70
+ max_train_steps: 1000000
71
+
72
+ train:
73
+ seed: 110221
74
+ precision: bf16
75
+ extra_precision: bf16
76
+ allow_tf32: false
77
+ save_interval: 100 #100
78
+ sanity_check_interval: 0
79
+ teacher_student_parallel: true
80
+ dpo_beta: 500
81
+ gradient_accumulation_steps: 1
82
+
83
+ eval:
84
+ seed: 0
85
+ timestep: [201, 400, 600, 800, 1000]
86
+
87
+ save:
88
+ output_dir: "outputs"
89
+ sanity_check_dir: null
90
+
91
+ lrm:
92
+ query_attention:
93
+ num_queries: 1
94
+ num_heads: 8
95
+ dropout: 0.
96
+ return_type: query
97
+ feature_layer: [8]
98
+ pool: q_attn
99
+ mlp_dim: 5120
100
+ loss: "ce"
101
+ task: "motion_quality"
102
+ trainable_blocks: [0, 1, 2, 3, 4, 5, 6, 7]
configs/train_prfl_i2v_480.yaml ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ train_id: "prfl_i2v_480"
2
+ task: "i2v-14b-480p"
3
+
4
+ model:
5
+ base_path: weights/Wan2.1-I2V-14B-480P
6
+ init_transformer_path: null
7
+ lrm_transformer_path: null
8
+ lrm_mlp_path: null
9
+ lrm_query_attention_path: null
10
+ resume_transformer_path: null
11
+ patch_size: [1, 2, 2]
12
+ lora:
13
+ use_lora: false
14
+ lora_rank: 128
15
+ target_modules: ["q", "k", "v", "o"]
16
+ resume_lora_path: null # load lora ckpt if not empty
17
+ ema:
18
+ use_ema: false
19
+ ema_decay: 0.99
20
+ fsdp:
21
+ fsdp_sharding_startegy: full
22
+ use_cpu_offload: false
23
+ gradient_checkpointing: true
24
+ selective_checkpointing: 1.0
25
+
26
+ extra_model:
27
+ vae:
28
+ name: Wan2.1_VAE.pth
29
+ vae_stride: [4, 8, 8]
30
+ text_encoder:
31
+ t5_text_len: 512
32
+ t5_checkpoint: models_t5_umt5-xxl-enc-bf16.pth
33
+ t5_tokenizer: google/umt5-xxl
34
+ image_encoder:
35
+ clip_checkpoint: models_clip_open-clip-xlm-roberta-large-vit-huge-14.pth
36
+ clip_tokenizer: xlm-roberta-large
37
+ scheduler:
38
+ flow_shift: 5.0
39
+ num_train_timesteps: 1000
40
+ weighting_scheme: uniform # logit_normal, uniform
41
+ logit_mean: 0
42
+ logit_std: 1
43
+ mode_scale: 1.29
44
+
45
+ dataset:
46
+ meta_file_list:
47
+ - temp_data/temp_data_720.list
48
+ crop_ratio: [1, 1, 1] # width, height
49
+ crop_type: "random" # center, random
50
+ uncond_prob: [0.1, 0.0] # prompt, image
51
+ sp_size: 4
52
+ batch_size: 1
53
+ sp_batch_size: 1
54
+ num_workers: 8
55
+ group_frame: null
56
+ group_resolution: null
57
+ # negative_prompt:
58
+
59
+ optimizer:
60
+ learning_rate: 5e-6
61
+ adam_beta1: 0.9
62
+ adam_beta2: 0.999
63
+ weight_decay: 0.01
64
+ lr_scheduler: constant
65
+ lr_warmup_steps: 0
66
+ lr_num_cycles: 1
67
+ lr_power: 1.0
68
+ max_train_steps: 1000000
69
+
70
+ train:
71
+ seed: 110221
72
+ precision: bf16
73
+ extra_precision: bf16
74
+ allow_tf32: false
75
+ save_interval: 100 #100
76
+ sanity_check_interval: 100
77
+ teacher_student_parallel: true
78
+ dpo_beta: 500
79
+ gradient_accumulation_steps: 5.
80
+
81
+ save:
82
+ output_dir: "outputs"
83
+ log_dir: null
84
+ sanity_check_dir: null
85
+ lrm:
86
+ query_attention:
87
+ num_queries: 1
88
+ num_heads: 8
89
+ dropout: 0.
90
+ return_type: query
91
+ feature_layer: [8]
92
+ pool: q_attn
93
+ mlp_dim: 5120
94
+ task: "motion_quality"
95
+ trainable_blocks: [0, 1, 2, 3, 4, 5, 6, 7]
configs/train_prfl_i2v_720.yaml ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ train_id: "prfl_i2v_720"
2
+ # task: "i2v-14b-720p" # t2v-1.3b, i2v-1.3b, t2v-14b, i2v-14b-480p, i2v-14b-720p
3
+ task: "i2v-14b-720p"
4
+
5
+ model:
6
+ base_path: weights/Wan2.1-I2V-14B-720P
7
+ init_transformer_path: null
8
+ lrm_transformer_path: null
9
+ lrm_mlp_path: null
10
+ lrm_query_attention_path: null
11
+ resume_transformer_path: null
12
+ patch_size: [1, 2, 2]
13
+ lora:
14
+ use_lora: false
15
+ lora_rank: 128
16
+ target_modules: ["q", "k", "v", "o"]
17
+ resume_lora_path: null # load lora ckpt if not empty
18
+ ema:
19
+ use_ema: false
20
+ ema_decay: 0.99
21
+ fsdp:
22
+ fsdp_sharding_startegy: full
23
+ use_cpu_offload: false
24
+ gradient_checkpointing: true
25
+ selective_checkpointing: 1.0
26
+
27
+ extra_model:
28
+ vae:
29
+ name: Wan2.1_VAE.pth
30
+ vae_stride: [4, 8, 8]
31
+ text_encoder:
32
+ t5_text_len: 512
33
+ t5_checkpoint: models_t5_umt5-xxl-enc-bf16.pth
34
+ t5_tokenizer: google/umt5-xxl
35
+ image_encoder:
36
+ clip_checkpoint: models_clip_open-clip-xlm-roberta-large-vit-huge-14.pth
37
+ clip_tokenizer: xlm-roberta-large
38
+ scheduler:
39
+ flow_shift: 5.0
40
+ num_train_timesteps: 1000
41
+ weighting_scheme: uniform # logit_normal, uniform
42
+ logit_mean: 0
43
+ logit_std: 1
44
+ mode_scale: 1.29
45
+
46
+ dataset:
47
+ meta_file_list:
48
+ - temp_data/temp_data_720.list
49
+ crop_ratio: [1, 1, 1] # width, height
50
+ crop_type: "random" # center, random
51
+ uncond_prob: [0.1, 0.0] # prompt, image
52
+ sp_size: 4
53
+ batch_size: 1
54
+ sp_batch_size: 1
55
+ num_workers: 8
56
+ group_frame: null
57
+ group_resolution: null
58
+ # negative_prompt:
59
+
60
+ optimizer:
61
+ learning_rate: 5e-6
62
+ adam_beta1: 0.9
63
+ adam_beta2: 0.999
64
+ weight_decay: 0.01
65
+ lr_scheduler: constant
66
+ lr_warmup_steps: 0
67
+ lr_num_cycles: 1
68
+ lr_power: 1.0
69
+ max_train_steps: 1000000
70
+
71
+ train:
72
+ seed: 110221
73
+ precision: bf16
74
+ extra_precision: bf16
75
+ allow_tf32: false
76
+ save_interval: 100
77
+ sanity_check_interval: 100
78
+ teacher_student_parallel: true
79
+ dpo_beta: 500
80
+ gradient_accumulation_steps: 5.
81
+
82
+ save:
83
+ output_dir: "outputs"
84
+ log_dir: null
85
+ sanity_check_dir: null
86
+ lrm:
87
+ query_attention:
88
+ num_queries: 1
89
+ num_heads: 8
90
+ dropout: 0.
91
+ return_type: query
92
+ feature_layer: [8]
93
+ pool: q_attn
94
+ mlp_dim: 5120
95
+ task: "motion_quality"
96
+ trainable_blocks: [0, 1, 2, 3, 4, 5, 6, 7]
configs/train_prfl_t2v_480.yaml ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ train_id: "prfl_t2v_480"
2
+ task: "t2v-14b"
3
+
4
+ model:
5
+ base_path: weights/Wan2.1-T2V-14B
6
+ init_transformer_path: null
7
+ lrm_transformer_path: null
8
+ lrm_mlp_path: null
9
+ lrm_query_attention_path: null
10
+ resume_transformer_path: null
11
+ patch_size: [1, 2, 2]
12
+ lora:
13
+ use_lora: false
14
+ lora_rank: 128
15
+ target_modules: ["q", "k", "v", "o"]
16
+ resume_lora_path: null # load lora ckpt if not empty
17
+ ema:
18
+ use_ema: false
19
+ ema_decay: 0.99
20
+ fsdp:
21
+ fsdp_sharding_startegy: full
22
+ use_cpu_offload: false
23
+ gradient_checkpointing: true
24
+ selective_checkpointing: 1.0
25
+
26
+ extra_model:
27
+ vae:
28
+ name: Wan2.1_VAE.pth
29
+ vae_stride: [4, 8, 8]
30
+ text_encoder:
31
+ t5_text_len: 512
32
+ t5_checkpoint: models_t5_umt5-xxl-enc-bf16.pth
33
+ t5_tokenizer: google/umt5-xxl
34
+ image_encoder:
35
+ clip_checkpoint: models_clip_open-clip-xlm-roberta-large-vit-huge-14.pth
36
+ clip_tokenizer: xlm-roberta-large
37
+ scheduler:
38
+ flow_shift: 5.0
39
+ num_train_timesteps: 1000
40
+ weighting_scheme: uniform # logit_normal, uniform
41
+ logit_mean: 0
42
+ logit_std: 1
43
+ mode_scale: 1.29
44
+
45
+ dataset:
46
+ meta_file_list:
47
+ - temp_data/temp_data_480.list
48
+ crop_ratio: [1, 1, 1] # width, height
49
+ crop_type: "random" # center, random
50
+ uncond_prob: [0.1, 0.0] # prompt, image
51
+ sp_size: 4
52
+ batch_size: 1
53
+ sp_batch_size: 1
54
+ num_workers: 8
55
+ group_frame: null
56
+ group_resolution: null
57
+ # negative_prompt:
58
+
59
+ optimizer:
60
+ learning_rate: 5e-6
61
+ adam_beta1: 0.9
62
+ adam_beta2: 0.999
63
+ weight_decay: 0.01
64
+ lr_scheduler: constant
65
+ lr_warmup_steps: 0
66
+ lr_num_cycles: 1
67
+ lr_power: 1.0
68
+ max_train_steps: 1000000
69
+
70
+ train:
71
+ seed: 110221
72
+ precision: bf16
73
+ extra_precision: bf16
74
+ allow_tf32: false
75
+ save_interval: 100 #100
76
+ sanity_check_interval: 100
77
+ teacher_student_parallel: true
78
+ dpo_beta: 500
79
+ gradient_accumulation_steps: 5.
80
+
81
+ save:
82
+ output_dir: "outputs"
83
+ log_dir: null
84
+ sanity_check_dir: null
85
+ lrm:
86
+ query_attention:
87
+ num_queries: 1
88
+ num_heads: 8
89
+ dropout: 0.
90
+ return_type: query
91
+ feature_layer: [8]
92
+ pool: q_attn
93
+ mlp_dim: 5120
94
+ task: "motion_quality"
95
+ trainable_blocks: [0, 1, 2, 3, 4, 5, 6, 7]
configs/train_prfl_t2v_720.yaml ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ train_id: "prfl_t2v_720"
2
+ task: "t2v-14b"
3
+
4
+ model:
5
+ base_path: weights/Wan2.1-T2V-14B
6
+ init_transformer_path: null
7
+ lrm_transformer_path: null
8
+ lrm_mlp_path: null
9
+ lrm_query_attention_path: null
10
+ resume_transformer_path: null
11
+ patch_size: [1, 2, 2]
12
+ lora:
13
+ use_lora: false
14
+ lora_rank: 128
15
+ target_modules: ["q", "k", "v", "o"]
16
+ resume_lora_path: null # load lora ckpt if not empty
17
+ ema:
18
+ use_ema: false
19
+ ema_decay: 0.99
20
+ fsdp:
21
+ fsdp_sharding_startegy: full
22
+ use_cpu_offload: false
23
+ gradient_checkpointing: true
24
+ selective_checkpointing: 1.0
25
+
26
+ extra_model:
27
+ vae:
28
+ name: Wan2.1_VAE.pth
29
+ vae_stride: [4, 8, 8]
30
+ text_encoder:
31
+ t5_text_len: 512
32
+ t5_checkpoint: models_t5_umt5-xxl-enc-bf16.pth
33
+ t5_tokenizer: google/umt5-xxl
34
+ image_encoder:
35
+ clip_checkpoint: models_clip_open-clip-xlm-roberta-large-vit-huge-14.pth
36
+ clip_tokenizer: xlm-roberta-large
37
+ scheduler:
38
+ flow_shift: 5.0
39
+ num_train_timesteps: 1000
40
+ weighting_scheme: uniform # logit_normal, uniform
41
+ logit_mean: 0
42
+ logit_std: 1
43
+ mode_scale: 1.29
44
+
45
+ dataset:
46
+ meta_file_list:
47
+ - temp_data/temp_data_720.list
48
+ crop_ratio: [1, 1, 1] # width, height
49
+ crop_type: "random" # center, random
50
+ uncond_prob: [0.1, 0.0] # prompt, image
51
+ sp_size: 4
52
+ batch_size: 1
53
+ sp_batch_size: 1
54
+ num_workers: 8
55
+ group_frame: null
56
+ group_resolution: null
57
+ # negative_prompt:
58
+
59
+ optimizer:
60
+ learning_rate: 5e-6
61
+ adam_beta1: 0.9
62
+ adam_beta2: 0.999
63
+ weight_decay: 0.01
64
+ lr_scheduler: constant
65
+ lr_warmup_steps: 0
66
+ lr_num_cycles: 1
67
+ lr_power: 1.0
68
+ max_train_steps: 1000000
69
+
70
+ train:
71
+ seed: 110221
72
+ precision: bf16
73
+ extra_precision: bf16
74
+ allow_tf32: false
75
+ save_interval: 100
76
+ sanity_check_interval: 100
77
+ teacher_student_parallel: true
78
+ dpo_beta: 500
79
+ gradient_accumulation_steps: 5.
80
+
81
+ save:
82
+ output_dir: "outputs"
83
+ log_dir: null
84
+ sanity_check_dir: null
85
+
86
+ lrm:
87
+ query_attention:
88
+ num_queries: 1
89
+ num_heads: 8
90
+ dropout: 0.
91
+ return_type: query
92
+ feature_layer: [8]
93
+ pool: q_attn
94
+ mlp_dim: 5120
95
+ task: "motion_quality"
96
+ trainable_blocks: [0, 1, 2, 3, 4, 5, 6, 7]
diffusers_lite.egg-info/PKG-INFO ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.4
2
+ Name: diffusers_lite
3
+ Version: 0.0.1
4
+ Author: jaspercheng
5
+ Requires-Dist: torch>=2.4.0
6
+ Requires-Dist: torchvision>=0.19.0
7
+ Requires-Dist: opencv-python>=4.9.0.80
8
+ Requires-Dist: diffusers>=0.31.0
9
+ Requires-Dist: transformers>=4.49.0
10
+ Requires-Dist: tokenizers>=0.20.3
11
+ Requires-Dist: accelerate>=1.1.1
12
+ Requires-Dist: gradio>=5.0.0
13
+ Requires-Dist: tqdm
14
+ Requires-Dist: imageio
15
+ Requires-Dist: easydict
16
+ Requires-Dist: ftfy
17
+ Requires-Dist: dashscope
18
+ Requires-Dist: peft
19
+ Requires-Dist: imageio-ffmpeg
20
+ Requires-Dist: flash_attn
21
+ Requires-Dist: numpy
22
+ Requires-Dist: omegaconf
23
+ Requires-Dist: protobuf
24
+ Requires-Dist: matplotlib
25
+ Dynamic: author
26
+ Dynamic: requires-dist
diffusers_lite.egg-info/SOURCES.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ README.md
2
+ setup.py
3
+ diffusers_lite.egg-info/PKG-INFO
4
+ diffusers_lite.egg-info/SOURCES.txt
5
+ diffusers_lite.egg-info/dependency_links.txt
6
+ diffusers_lite.egg-info/requires.txt
7
+ diffusers_lite.egg-info/top_level.txt
diffusers_lite.egg-info/dependency_links.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
diffusers_lite.egg-info/requires.txt ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ torch>=2.4.0
2
+ torchvision>=0.19.0
3
+ opencv-python>=4.9.0.80
4
+ diffusers>=0.31.0
5
+ transformers>=4.49.0
6
+ tokenizers>=0.20.3
7
+ accelerate>=1.1.1
8
+ gradio>=5.0.0
9
+ tqdm
10
+ imageio
11
+ easydict
12
+ ftfy
13
+ dashscope
14
+ peft
15
+ imageio-ffmpeg
16
+ flash_attn
17
+ numpy
18
+ omegaconf
19
+ protobuf
20
+ matplotlib
diffusers_lite.egg-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
diffusers_lite/__init__.py ADDED
File without changes
diffusers_lite/arguments.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import random
3
+ import sys
4
+
5
+ from .wan.configs import WAN_CONFIGS
6
+ from .wan.utils.utils import str2bool
7
+
8
+
9
+ def args_init():
10
+ parser = argparse.ArgumentParser(description="diffusers lite script")
11
+ parser.add_argument("--seed", type=int, default=42)
12
+ parser.add_argument("--precision", type=str, default="bf16")
13
+ parser.add_argument("--task", type=str, default="i2v", choices=["i2v", "t2v", "flf2v"])
14
+
15
+ ### Inference ###
16
+ # model
17
+ parser.add_argument("--base-dir", type=str, default="")
18
+ parser.add_argument("--transformer_path", nargs="?", const="", default="")
19
+
20
+ # lora
21
+ parser.add_argument("--lora-path", nargs="?", const="", default="")
22
+ parser.add_argument("--lora-alpha", type=float, default=1.0)
23
+
24
+ # dataset
25
+ parser.add_argument("--dataset-path", type=str, default="")
26
+ parser.add_argument("--resolution", nargs="+", type=int, default=[512])
27
+ parser.add_argument("--num-frames", type=int, default=81)
28
+ parser.add_argument("--batch-size", type=int, default=1)
29
+
30
+ # inference
31
+ parser.add_argument("--cfg", type=float, default=5.0)
32
+ parser.add_argument("--shift", type=float, default=3.0)
33
+ parser.add_argument("--step", type=int, default=50)
34
+
35
+ # save
36
+ parser.add_argument("--fps", type=int, default=16)
37
+ parser.add_argument("--save-dir", type=str, default="")
38
+
39
+ ### Preprocess dataset ###
40
+ parser.add_argument("--json-paths", nargs="+", type=str, default=[""])
41
+ parser.add_argument("--video-dir", type=str, default="")
42
+ parser.add_argument("--image-dir", type=str, default="")
43
+ parser.add_argument("--extract-fps", type=int, default=24)
44
+ # model
45
+ parser.add_argument("--model-type", type=str, default="wanx", choices=["wanx", "ltx"])
46
+ parser.add_argument("--vae-path", type=str, default="")
47
+ parser.add_argument("--image-processor-path", type=str, default="")
48
+ parser.add_argument("--image-encoder-path", type=str, default="")
49
+ parser.add_argument("--tokenizer-path", type=str, default="")
50
+ parser.add_argument("--text-encoder-path", type=str, default="")
51
+ parser.add_argument("--max-sequence-length", type=int, default=512)
52
+ parser.add_argument("--vlm-type", type=str, default="qwenvl2", choices=["qwenvl2", "qwenvl2.5", "nocap"])
53
+ parser.add_argument("--vlm-path", nargs="?", const="", default="")
54
+ parser.add_argument("--max-new-tokens", type=int, default=256)
55
+ # prompt
56
+ parser.add_argument("--caption-template", type=str, default="")
57
+ parser.add_argument("--instruct-sentence", type=str, default="")
58
+ parser.add_argument("--negative-prompt", nargs="?", const="", default="")
59
+ parser.add_argument("--null-caption-length", type=int, default=0)
60
+ # save
61
+ parser.add_argument("--save-interval", type=int, default=100)
62
+ parser.add_argument("--save-path", type=str, default="")
63
+
64
+ args = parser.parse_args()
65
+ return args
66
+
67
+
68
+ def args_wan_init():
69
+ parser = argparse.ArgumentParser(
70
+ description="Generate a image or video from a text prompt or image using Wan"
71
+ )
72
+ parser.add_argument(
73
+ "--task",
74
+ type=str,
75
+ default="i2v-14B",
76
+ choices=list(WAN_CONFIGS.keys()),
77
+ help="The task to run.")
78
+ parser.add_argument(
79
+ "--size",
80
+ type=str,
81
+ default="1280*720",
82
+ # choices=list(SIZE_CONFIGS.keys()),
83
+ help="The area (width*height) of the generated video. For the I2V task, the aspect ratio of the output video will follow that of the input image."
84
+ )
85
+ parser.add_argument(
86
+ "--frame_num",
87
+ type=int,
88
+ default=81,
89
+ help="How many frames to sample from a image or video. The number should be 4n+1"
90
+ )
91
+ parser.add_argument(
92
+ "--ckpt_dir",
93
+ type=str,
94
+ default='',
95
+ help="The path to the checkpoint directory.")
96
+ parser.add_argument(
97
+ "--offload_model",
98
+ type=str2bool,
99
+ default=None,
100
+ help="Whether to offload the model to CPU after each model forward, reducing GPU memory usage."
101
+ )
102
+ parser.add_argument(
103
+ "--ulysses_size",
104
+ type=int,
105
+ default=1,
106
+ help="The size of the ulysses parallelism in DiT.")
107
+ parser.add_argument(
108
+ "--ring_size",
109
+ type=int,
110
+ default=1,
111
+ help="The size of the ring attention parallelism in DiT.")
112
+ parser.add_argument(
113
+ "--t5_fsdp",
114
+ action="store_true",
115
+ default=False,
116
+ help="Whether to use FSDP for T5.")
117
+ parser.add_argument(
118
+ "--t5_cpu",
119
+ action="store_true",
120
+ default=False,
121
+ help="Whether to place T5 model on CPU.")
122
+ parser.add_argument(
123
+ "--dit_fsdp",
124
+ action="store_true",
125
+ default=False,
126
+ help="Whether to use FSDP for DiT.")
127
+ parser.add_argument(
128
+ "--save_folder",
129
+ type=str,
130
+ default=None,
131
+ help="The folder to save the generated image or video to.")
132
+ parser.add_argument(
133
+ "--save_file",
134
+ type=str,
135
+ default=None,
136
+ help="The file to save the generated image or video to.")
137
+ parser.add_argument(
138
+ "--prompt",
139
+ type=str,
140
+ default=None,
141
+ help="The prompt to generate the image or video from.")
142
+ parser.add_argument(
143
+ "--base_seed",
144
+ type=int,
145
+ default=-1,
146
+ help="The seed to use for generating the image or video.")
147
+ parser.add_argument(
148
+ "--image",
149
+ type=str,
150
+ default=None,
151
+ help="The image to generate the video from.")
152
+ parser.add_argument(
153
+ "--sample_solver",
154
+ type=str,
155
+ default='unipc',
156
+ choices=['unipc', 'dpm++'],
157
+ help="The solver used to sample.")
158
+ parser.add_argument(
159
+ "--sample_steps", type=int, default=None, help="The sampling steps.")
160
+ parser.add_argument(
161
+ "--sample_shift",
162
+ type=float,
163
+ default=None,
164
+ help="Sampling shift factor for flow matching schedulers.")
165
+ parser.add_argument(
166
+ "--sample_guide_scale",
167
+ type=float,
168
+ default=6.0,
169
+ help="Classifier free guidance scale.")
170
+ parser.add_argument(
171
+ "--teacache_thresh",
172
+ type=float,
173
+ default=None,
174
+ help="The threshold for caching diffusion model steps.")
175
+
176
+ # NOTE: add by diffusers-lite to fill in blank args
177
+ parser.add_argument("--ddp_mode", type=bool, default=False)
178
+ # dataset
179
+ parser.add_argument("--dataset_path", type=str, default=None)
180
+ parser.add_argument("--resolution", nargs="+", type=int, default=[512])
181
+ parser.add_argument("--batch_size", type=int, default=1)
182
+ parser.add_argument("--negative_prompt", nargs="?", const="", default="")
183
+ # transformer
184
+ parser.add_argument("--transformer_path", nargs="?", const="", default="")
185
+ # lora
186
+ parser.add_argument("--lora_path", nargs="?", const="", default="")
187
+ parser.add_argument("--lora_alpha", type=float, default=1.0)
188
+ parser.add_argument("--distill_lora_path", nargs="?", const="", default="")
189
+ parser.add_argument("--distill_lora_alpha", type=float, default=1.0)
190
+
191
+ args = parser.parse_args()
192
+
193
+ assert args.ckpt_dir is not None, "Please specify the checkpoint directory."
194
+ assert args.task in WAN_CONFIGS, f"Unsupport task: {args.task}"
195
+
196
+ # The default sampling steps are 40 for image-to-video tasks and 50 for text-to-video tasks.
197
+ if args.sample_steps is None:
198
+ args.sample_steps = 40 if "i2v" in args.task else 50
199
+
200
+ if args.sample_shift is None:
201
+ args.sample_shift = 5.0
202
+ if "i2v" in args.task:# and args.size in ["832*480", "480*832"]
203
+ args.sample_shift = 3.0
204
+
205
+ # The default number of frames are 1 for text-to-image tasks and 81 for other tasks.
206
+ if args.frame_num is None:
207
+ args.frame_num = 1 if "t2i" in args.task else 81
208
+
209
+ # T2I frame_num check
210
+ if "t2i" in args.task:
211
+ assert args.frame_num == 1, f"Unsupport frame_num {args.frame_num} for task {args.task}"
212
+
213
+ args.base_seed = args.base_seed if args.base_seed >= 0 else random.randint(
214
+ 0, sys.maxsize)
215
+
216
+ return args
diffusers_lite/constants.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ PRECISION_TO_TYPE = {
4
+ 'fp32': torch.float32,
5
+ 'fp16': torch.float16,
6
+ 'bf16': torch.bfloat16,
7
+ }
8
+
9
+ NULL_DIR="temp_data/null"
diffusers_lite/datasets/image2video_dataset.py ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import random
4
+ import traceback
5
+ from PIL import Image
6
+
7
+ import torch
8
+ import numpy as np
9
+ from decord import VideoReader
10
+ from easydict import EasyDict
11
+ from einops import rearrange
12
+ from torch.utils.data import Dataset
13
+ from torchvision import transforms
14
+
15
+ from ..utils.data_utils import align_floor_to, align_ceil_to
16
+ from ..constants import NULL_DIR
17
+
18
+
19
+ class Image2VideoTrainDataset(Dataset):
20
+ def __init__(
21
+ self,
22
+ task="i2v-14b-480p",
23
+ dataset_type="wanx",
24
+ meta_file_list=[],
25
+ meta_file_lose_list=[],
26
+ uncond_prob=[0.0, 0.0],
27
+ sp_size=1,
28
+ patch_size=[1,2,2],
29
+ ):
30
+ self.task = task
31
+ self.dataset_type = dataset_type
32
+ self.uncond_prompt_prob = uncond_prob[0]
33
+ self.uncond_image_prob = uncond_prob[-1]
34
+ self.sp_size = sp_size
35
+ self.patch_size = patch_size
36
+ self.meta_paths = []
37
+
38
+ for meta_file in meta_file_list:
39
+ self.meta_paths.extend(
40
+ [line.strip() for line in open(meta_file, "r").readlines()]
41
+ )
42
+ if len(meta_file_lose_list) > 0:
43
+ self.meta_paths_lose = []
44
+ for meta_file in meta_file_lose_list:
45
+ self.meta_paths_lose.extend(
46
+ [line.strip() for line in open(meta_file, "r").readlines()]
47
+ )
48
+
49
+ def __len__(self):
50
+ return len(self.meta_paths)
51
+
52
+ def __getitem__(self, idx):
53
+ try_times = 100
54
+ for _ in range(try_times):
55
+ try:
56
+ if self.dataset_type in ["refl"]:
57
+ return self.get_batch_lrm_refl(idx)
58
+ elif self.dataset_type in ["lrm_ce"]:
59
+ return self.get_batch_lrm_ce(idx)
60
+ elif self.dataset_type in ["lrm_bt_online"]:
61
+ return self.get_batch_lrm_bt_online(idx)
62
+ except Exception as e:
63
+ print(
64
+ f"Error details: {str(e)}-{idx}-{self.meta_paths[idx]}-{traceback.format_exc()}\n"
65
+ )
66
+ idx = np.random.randint(len(self.meta_paths))
67
+
68
+ raise RuntimeError("Too many bad data.")
69
+
70
+ def get_batch_lrm_refl(self, idx):
71
+ data_json_path = self.meta_paths[idx]
72
+
73
+ with open(data_json_path, "r") as f:
74
+ data_dict = json.load(f)
75
+
76
+ # video
77
+ if 'video_vae_latent_path' in data_dict.keys():
78
+ latents_path = data_dict["video_vae_latent_path"]
79
+ elif 'vae_latent_path' in data_dict.keys():
80
+ latents_path = data_dict["vae_latent_path"]
81
+ else:
82
+ latents_path = data_dict["latents_path"]
83
+ latents = np.load(latents_path)[0]
84
+ latents = torch.from_numpy(latents)
85
+ frames = latents.shape[1]
86
+
87
+ # text states
88
+ if 'textshort_path' in data_dict and 'textlong_path' in data_dict:
89
+ text_states_path = data_dict["textshort_path"]
90
+ text_states_path_long = data_dict["textlong_path"]
91
+ prompt= data_dict["short_caption"]
92
+ if random.random() <= 0.7:
93
+ text_states_path = text_states_path_long
94
+ prompt=data_dict["long_caption"]
95
+ else:
96
+ text_states_path = data_dict["text_en_path"]
97
+ prompt= data_dict["prompt"]
98
+ text_states = np.load(text_states_path)[0]
99
+ text_states = torch.from_numpy(text_states)
100
+
101
+ # image embeds
102
+ image_embeds_path = data_dict["imgclip_path"]
103
+ image_embeds = torch.from_numpy(np.load(image_embeds_path))
104
+ image_embeds = rearrange(image_embeds, "b s d -> (b s) d")
105
+
106
+ # latents condition
107
+ if "f1_black_path" in data_dict.keys():
108
+ latents_condition_path = data_dict["f1_black_path"]
109
+ else:
110
+ latents_condition_path = data_dict["latents_condition_path"]
111
+ latents_condition = np.load(latents_condition_path)[0]
112
+ latents_condition = torch.from_numpy(latents_condition)
113
+
114
+ # distill prompts
115
+ if "flf2v" in self.task:
116
+ uncond_text_states_path = os.path.join(NULL_DIR, f"wanx/uncond_flf2v.npy")
117
+ else:
118
+ uncond_text_states_path = os.path.join(NULL_DIR, f"wanx/uncond.npy")
119
+ uncond_text_states = np.load(uncond_text_states_path)[0]
120
+ uncond_text_states = torch.from_numpy(uncond_text_states)
121
+
122
+ # drop prompts
123
+ random_number = random.random()
124
+ if random_number < self.uncond_prompt_prob:
125
+ null_text_states_path = os.path.join(NULL_DIR, f"wanx/null.npy")
126
+ null_text_states = np.load(null_text_states_path)[0]
127
+ text_states = torch.from_numpy(null_text_states)
128
+
129
+ return latents, text_states, uncond_text_states, image_embeds, latents_condition,prompt#,inference_data_dict
130
+
131
+ def get_batch_refl(self, idx):
132
+ data_json_path = self.meta_paths[idx]
133
+
134
+ with open(data_json_path, "r") as f:
135
+ data_dict = json.load(f)
136
+
137
+ # video
138
+ if 'video_vae_latent_path' in data_dict.keys():
139
+ latents_path = data_dict["video_vae_latent_path"]
140
+ elif 'vae_latent_path' in data_dict.keys():
141
+ latents_path = data_dict["vae_latent_path"]
142
+ else:
143
+ latents_path = data_dict["latents_path"]
144
+ latents = np.load(latents_path)[0]
145
+ latents = torch.from_numpy(latents)
146
+ frames = latents.shape[1]
147
+
148
+ # text states
149
+ text_states_path = data_dict["textshort_path"]
150
+ text_states_path_long = data_dict["textlong_path"]
151
+ prompt= data_dict["short_caption"]
152
+ if random.random() <= 0.7:
153
+ text_states_path = text_states_path_long
154
+ prompt=data_dict["long_caption"]
155
+ text_states = np.load(text_states_path)[0]
156
+ text_states = torch.from_numpy(text_states)
157
+
158
+ image_embeds_path = data_dict["imgclip_path"]
159
+ image_embeds = torch.from_numpy(np.load(image_embeds_path))
160
+ image_embeds = rearrange(image_embeds, "b s d -> (b s) d")
161
+
162
+ if "f1_black_path" in data_dict.keys():
163
+ latents_condition_path = data_dict["f1_black_path"]
164
+ else:
165
+ latents_condition_path = data_dict["latents_condition_path"]
166
+ latents_condition = np.load(latents_condition_path)[0]
167
+ latents_condition = torch.from_numpy(latents_condition)
168
+
169
+ if "flf2v" in self.task:
170
+ uncond_text_states_path = os.path.join(NULL_DIR, f"wanx/uncond_flf2v.npy")
171
+ else:
172
+ uncond_text_states_path = os.path.join(NULL_DIR, f"wanx/uncond.npy")
173
+ uncond_text_states = np.load(uncond_text_states_path)[0]
174
+ uncond_text_states = torch.from_numpy(uncond_text_states)
175
+
176
+ random_number = random.random()
177
+ if random_number < self.uncond_prompt_prob:
178
+ null_text_states_path = os.path.join(NULL_DIR, f"wanx/null.npy")
179
+ null_text_states = np.load(null_text_states_path)[0]
180
+ text_states = torch.from_numpy(null_text_states)
181
+
182
+ return latents, text_states, uncond_text_states, image_embeds, latents_condition, prompt # inference_data_dict
183
+
184
+ def get_batch_lrm_ce(self, idx):
185
+ data_json_path = self.meta_paths[idx]
186
+
187
+ with open(data_json_path, "r") as f:
188
+ data_dict = json.load(f)
189
+
190
+ source_id = data_dict["source_id"]
191
+
192
+ if 'video_vae_latent_path' in data_dict:
193
+ latents_path = data_dict["video_vae_latent_path"]
194
+ else:
195
+ latents_path = data_dict["vae_latent_path"]
196
+
197
+ latents = np.load(latents_path)[0]
198
+ latents = torch.from_numpy(latents)
199
+ frames = latents.shape[1]
200
+
201
+ if 'save_textshort_path' in data_dict:
202
+ text_states_path = data_dict["save_textshort_path"]
203
+ elif 'textshort_path' in data_dict:
204
+ text_states_path = data_dict["textshort_path"]
205
+ else:
206
+ text_states_path = data_dict["text_en_path"]
207
+
208
+ text_states = np.load(text_states_path)[0]
209
+ text_states = torch.from_numpy(text_states)
210
+
211
+ if "image_embeds" in data_dict:
212
+ image_embeds_path = data_dict["image_embeds"]
213
+ else:
214
+ image_embeds_path = data_dict["imgclip_path"]
215
+
216
+ image_embeds = torch.from_numpy(np.load(image_embeds_path))
217
+ image_embeds = rearrange(image_embeds, "b s d -> (b s) d")
218
+
219
+ if "f1_black_path" in data_dict:
220
+ latents_condition_path = data_dict["f1_black_path"]
221
+ else:
222
+ latents_condition_path = data_dict["latents_condition_path"]
223
+
224
+ latents_condition = np.load(latents_condition_path)[0]
225
+ latents_condition = torch.from_numpy(latents_condition)
226
+
227
+ if "flf2v" in self.task:
228
+ uncond_text_states_path = os.path.join(NULL_DIR, "wanx/uncond_flf2v.npy")
229
+ else:
230
+ uncond_text_states_path = os.path.join(NULL_DIR, "wanx/uncond.npy")
231
+
232
+ uncond_text_states = np.load(uncond_text_states_path)[0]
233
+ uncond_text_states = torch.from_numpy(uncond_text_states)
234
+
235
+ if "model" in data_dict:
236
+ data_from_model = data_dict["model"]
237
+ else:
238
+ data_from_model = ""
239
+ if "text_alignment" in data_dict:
240
+ text_alignment = data_dict["text_alignment"]
241
+ else:
242
+ text_alignment = 0
243
+ if "blur_quality" in data_dict:
244
+ blur_quality = data_dict["blur_quality"]
245
+ else:
246
+ blur_quality = 0
247
+ if "physics_quality" in data_dict:
248
+ physics_quality = data_dict["physics_quality"]
249
+ else:
250
+ physics_quality = 0
251
+ if "human_quality" in data_dict:
252
+ human_quality = data_dict["human_quality"]
253
+ else:
254
+ human_quality = 0
255
+
256
+ if text_alignment == "poor" or text_alignment is None: text_alignment = 0
257
+ if blur_quality == "poor" or blur_quality is None: blur_quality = 0
258
+ if physics_quality == "poor" or physics_quality is None: physics_quality = 0
259
+ if human_quality == "poor" or human_quality is None: human_quality = 0
260
+ if text_alignment == "good": text_alignment = 1
261
+ if blur_quality == "good": blur_quality = 1
262
+ if physics_quality == "good": physics_quality = 1
263
+ if human_quality == "good": human_quality = 1
264
+
265
+ return (latents, text_states, uncond_text_states, image_embeds, latents_condition,
266
+ data_from_model, text_alignment, blur_quality, physics_quality, human_quality)
267
+
268
+ def get_batch_lrm_bt_online(self, idx):
269
+ data_json_path = self.meta_paths[idx]
270
+
271
+ if self.meta_paths_lose is None or len(self.meta_paths_lose) == 0:
272
+ raise ValueError("meta_paths_lose is None or empty. Please ensure bt=True and meta_file_list_lose is provided.")
273
+
274
+ data_json_path_lose = self.meta_paths_lose[random.randint(0, len(self.meta_paths_lose)-1)]
275
+
276
+ with open(data_json_path, "r") as f:
277
+ data_dict = json.load(f)
278
+ with open(data_json_path_lose, "r") as f:
279
+ data_dict_lose = json.load(f)
280
+
281
+ if 'video_vae_latent_path' in data_dict:
282
+ latents_path = data_dict["video_vae_latent_path"]
283
+ latents_path_lose = data_dict_lose["video_vae_latent_path"]
284
+ else:
285
+ latents_path = data_dict["vae_latent_path"]
286
+ latents_path_lose = data_dict_lose["vae_latent_path"]
287
+
288
+ latents = np.load(latents_path)[0]
289
+ latents = torch.from_numpy(latents)
290
+ frames = latents.shape[1]
291
+ latents_lose = np.load(latents_path_lose)[0]
292
+ latents_lose = torch.from_numpy(latents_lose)
293
+ frames_lose = latents_lose.shape[1]
294
+ assert latents.shape == latents_lose.shape, f'latents.shape {latents.shape} != latents_lose.shape {latents_lose.shape}'
295
+
296
+ if 'save_textshort_path' in data_dict:
297
+ text_states_path = data_dict["save_textshort_path"]
298
+ text_states_path_lose = data_dict_lose["save_textshort_path"]
299
+ elif 'textshort_path' in data_dict:
300
+ text_states_path_lose = data_dict_lose["textshort_path"]
301
+ text_states_path = data_dict["textshort_path"]
302
+ else:
303
+ text_states_path = data_dict["text_en_path"]
304
+ text_states_path_lose = data_dict_lose["text_en_path"]
305
+
306
+ text_states = np.load(text_states_path)[0]
307
+ text_states = torch.from_numpy(text_states)
308
+ text_states_lose = np.load(text_states_path_lose)[0]
309
+ text_states_lose = torch.from_numpy(text_states_lose)
310
+
311
+ if "image_embeds" in data_dict:
312
+ image_embeds_path = data_dict["image_embeds"]
313
+ image_embeds_path_lose = data_dict_lose["image_embeds"]
314
+ else:
315
+ image_embeds_path = data_dict["imgclip_path"]
316
+ image_embeds_path_lose = data_dict_lose["imgclip_path"]
317
+
318
+ image_embeds = torch.from_numpy(np.load(image_embeds_path))
319
+ image_embeds = rearrange(image_embeds, "b s d -> (b s) d")
320
+ image_embeds_lose = torch.from_numpy(np.load(image_embeds_path_lose))
321
+ image_embeds_lose = rearrange(image_embeds_lose, "b s d -> (b s) d")
322
+
323
+ if "f1_black_path" in data_dict:
324
+ latents_condition_path = data_dict["f1_black_path"]
325
+ latents_condition_path_lose = data_dict_lose["f1_black_path"]
326
+ else:
327
+ latents_condition_path = data_dict["latents_condition_path"]
328
+ latents_condition_path_lose = data_dict_lose["latents_condition_path"]
329
+
330
+ latents_condition = np.load(latents_condition_path)[0]
331
+ latents_condition = torch.from_numpy(latents_condition)
332
+ latents_condition_lose = np.load(latents_condition_path_lose)[0]
333
+ latents_condition_lose = torch.from_numpy(latents_condition_lose)
334
+
335
+ if "flf2v" in self.task:
336
+ uncond_text_states_path = os.path.join(NULL_DIR, "wanx/uncond_flf2v.npy")
337
+ uncond_text_states_path_lose = os.path.join(NULL_DIR, "wanx/uncond_flf2v.npy")
338
+ else:
339
+ uncond_text_states_path = os.path.join(NULL_DIR, "wanx/uncond.npy")
340
+ uncond_text_states_path_lose = os.path.join(NULL_DIR, "wanx/uncond.npy")
341
+
342
+ uncond_text_states = np.load(uncond_text_states_path)[0]
343
+ uncond_text_states = torch.from_numpy(uncond_text_states)
344
+ uncond_text_states_lose = np.load(uncond_text_states_path_lose)[0]
345
+ uncond_text_states_lose = torch.from_numpy(uncond_text_states_lose)
346
+
347
+ return (latents, text_states, uncond_text_states, image_embeds, latents_condition,
348
+ latents_lose, text_states_lose, uncond_text_states_lose, image_embeds_lose, latents_condition_lose)
349
+
350
+
351
+ class Image2VideoEvalDataset(Dataset):
352
+ def __init__(self, file_path, resolution=(512,512), alignment=16, do_scale=True):
353
+ self.prompts = []
354
+ self.image_ids = []
355
+ self.image_paths = []
356
+ self.last_image_paths = []
357
+ self.seeds = []
358
+
359
+ if file_path.endswith(".txt"):
360
+ with open(file_path, "r") as file:
361
+ for line in file:
362
+ prompt = line.strip()
363
+ self.prompts.append(prompt)
364
+
365
+ elif file_path.endswith(".json"):
366
+ with open(file_path, "r") as f:
367
+ datas = json.load(f)
368
+ for data in datas:
369
+ self.prompts.append(data["caption"].strip())
370
+ if "image_id" in data.keys():
371
+ self.image_ids.append(data["image_id"])
372
+ if "image_path" in data.keys():
373
+ self.image_paths.append(data["image_path"])
374
+ if "last_image_path" in data.keys():
375
+ self.last_image_paths.append(data["last_image_path"])
376
+ if "seed" in data.keys():
377
+ self.seeds.append(data["seed"])
378
+
379
+ self.resolution = resolution
380
+ self.alignment = alignment
381
+ self.do_scale = do_scale
382
+
383
+ print(f"[INFO] Load text and image dataset done, total len {len(self.prompts)}")
384
+
385
+ def __len__(self):
386
+ return len(self.prompts)
387
+
388
+ def __getitem__(self, index):
389
+ prompt = self.prompts[index]
390
+
391
+ if len(self.image_paths) > 0:
392
+ image_path = self.image_paths[index]
393
+ image_id = image_path.split("/")[-1].split(".")[0]
394
+
395
+ # Load image
396
+ image = Image.open(image_path).convert("RGB")
397
+
398
+ # Resize image
399
+ width, height = image.size
400
+ scale = min(min(self.resolution) / min(width, height), max(self.resolution) / max(width, height))
401
+
402
+ width_scale = align_ceil_to(int(width * scale), self.alignment)
403
+ height_scale = align_ceil_to(int(height * scale), self.alignment)
404
+
405
+ if not self.do_scale:
406
+ width_scale = width
407
+ height_scale = height
408
+
409
+ transform = transforms.Compose(
410
+ [
411
+ transforms.Resize((height_scale, width_scale)),
412
+ transforms.ToTensor(),
413
+ ]
414
+ )
415
+
416
+ image = transform(image)
417
+ else:
418
+ image_path = ""
419
+ image = ""
420
+ image_id = str(index)
421
+
422
+ if len(self.image_ids) > 0:
423
+ image_id = self.image_ids[index]
424
+
425
+ # Load last image
426
+ if len(self.last_image_paths) > 0:
427
+ last_image_path = self.last_image_paths[index]
428
+ last_image = Image.open(last_image_path).convert("RGB")
429
+ last_image = transform(last_image)
430
+ else:
431
+ last_image = ""
432
+
433
+ if len(self.seeds) > 0:
434
+ seed = self.seeds[index]
435
+ image_id += f'_seed_{seed}'
436
+ else:
437
+ seed = 42
438
+
439
+ return {
440
+ "prompt": prompt,
441
+ "image": image,
442
+ "last_image": last_image,
443
+ "image_id": image_id,
444
+ "image_path": image_path,
445
+ "seed": seed,
446
+ }
447
+
448
+
diffusers_lite/schedulers/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .scheduling_flow_match_discrete import FlowMatchDiscreteScheduler
diffusers_lite/schedulers/scheduling_flow_match_discrete.py ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 Stability AI, Katherine Crowson and The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from dataclasses import dataclass
16
+ from typing import Optional, Tuple, Union
17
+
18
+ import numpy as np
19
+ import torch
20
+
21
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
22
+ from diffusers.utils import BaseOutput, logging
23
+ from diffusers.schedulers.scheduling_utils import SchedulerMixin
24
+
25
+
26
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
27
+
28
+
29
+ @dataclass
30
+ class FlowMatchDiscreteSchedulerOutput(BaseOutput):
31
+ prev_sample: torch.FloatTensor
32
+
33
+
34
+ class FlowMatchDiscreteScheduler(SchedulerMixin, ConfigMixin):
35
+ _compatibles = []
36
+ order = 1
37
+
38
+ @register_to_config
39
+ def __init__(
40
+ self,
41
+ num_train_timesteps: int = 1000,
42
+ shift: float = 1.0,
43
+ sigma_max=1.0,
44
+ reverse: bool = True,
45
+ solver: str = "euler",
46
+ ):
47
+ # sigmas = torch.linspace(1, 0, num_train_timesteps + 1)
48
+ sigmas = torch.linspace(sigma_max,0,num_train_timesteps+1)
49
+
50
+ if not reverse:
51
+ sigmas = sigmas.flip(0)
52
+
53
+ self.sigmas = sigmas
54
+ # the value fed to model
55
+ self.timesteps = (sigmas[:-1] * num_train_timesteps).to(dtype=torch.float32)
56
+
57
+ self._step_index = None
58
+ self._begin_index = None
59
+
60
+ self.supported_solver = ["euler"]
61
+ if solver not in self.supported_solver:
62
+ raise ValueError(
63
+ f"Solver {solver} not supported. Supported solvers: {self.supported_solver}"
64
+ )
65
+
66
+ self.sigma_max = sigma_max
67
+
68
+ @property
69
+ def step_index(self):
70
+ return self._step_index
71
+
72
+ @property
73
+ def begin_index(self):
74
+ return self._begin_index
75
+
76
+ # Copied from diffusers.schedulers.scheduling_dpmsolver_multistep.DPMSolverMultistepScheduler.set_begin_index
77
+ def set_begin_index(self, begin_index: int = 0):
78
+ self._begin_index = begin_index
79
+
80
+ def _sigma_to_t(self, sigma):
81
+ return sigma * self.config.num_train_timesteps
82
+
83
+ def set_timesteps(
84
+ self,
85
+ num_inference_steps: int,
86
+ device: Union[str, torch.device] = None,
87
+ dtype: torch.Tensor = torch.float32,
88
+ ):
89
+ self.num_inference_steps = num_inference_steps
90
+
91
+ sigmas = torch.linspace(self.sigma_max, 0, num_inference_steps + 1)
92
+ sigmas = (self.config.shift * sigmas) / (1 + (self.config.shift - 1) * sigmas)
93
+
94
+ if not self.config.reverse:
95
+ sigmas = 1 - sigmas
96
+
97
+ self.sigmas = sigmas
98
+ self.timesteps = (sigmas[:-1] * self.config.num_train_timesteps).to(
99
+ dtype=dtype, device=device
100
+ )
101
+
102
+ # Reset step index
103
+ self._step_index = None
104
+
105
+ def index_for_timestep(self, timestep, schedule_timesteps=None):
106
+ if schedule_timesteps is None:
107
+ schedule_timesteps = self.timesteps
108
+
109
+ indices = (schedule_timesteps == timestep).nonzero()
110
+ pos = 1 if len(indices) > 1 else 0
111
+
112
+ return indices[pos].item()
113
+
114
+ def _init_step_index(self, timestep):
115
+ if self.begin_index is None:
116
+ if isinstance(timestep, torch.Tensor):
117
+ timestep = timestep.to(self.timesteps.device)
118
+ self._step_index = self.index_for_timestep(timestep)
119
+ else:
120
+ self._step_index = self._begin_index
121
+
122
+ def scale_model_input(
123
+ self, sample: torch.Tensor, timestep: Optional[int] = None
124
+ ) -> torch.Tensor:
125
+ return sample
126
+
127
+ def step(
128
+ self,
129
+ model_output: torch.FloatTensor,
130
+ timestep: Union[float, torch.FloatTensor],
131
+ sample: torch.FloatTensor,
132
+ return_dict: bool = True,
133
+ ) -> Union[FlowMatchDiscreteSchedulerOutput, Tuple]:
134
+ if (
135
+ isinstance(timestep, int)
136
+ or isinstance(timestep, torch.IntTensor)
137
+ or isinstance(timestep, torch.LongTensor)
138
+ ):
139
+ raise ValueError(
140
+ (
141
+ "Passing integer indices (e.g. from `enumerate(timesteps)`) as timesteps to"
142
+ " `EulerDiscreteScheduler.step()` is not supported. Make sure to pass"
143
+ " one of the `scheduler.timesteps` as a timestep."
144
+ ),
145
+ )
146
+
147
+ if self.step_index is None:
148
+ self._init_step_index(timestep)
149
+
150
+ # Upcast to avoid precision issues when computing prev_sample
151
+ sample = sample.to(torch.float32)
152
+
153
+ sigma_ = self.sigmas[self.step_index + 1]
154
+ sigma = self.sigmas[self.step_index]
155
+ dt = sigma_ - sigma
156
+
157
+ if self.config.solver == "euler":
158
+ prev_sample = sample + model_output.to(torch.float32) * dt
159
+ else:
160
+ raise ValueError(
161
+ f"Solver {self.config.solver} not supported. Supported solvers: {self.supported_solver}"
162
+ )
163
+
164
+ # upon completion increase step index by one
165
+ self._step_index += 1
166
+
167
+ if not return_dict:
168
+ return (prev_sample,)
169
+
170
+ return FlowMatchDiscreteSchedulerOutput(prev_sample=prev_sample)
171
+
172
+ def __len__(self):
173
+ return self.config.num_train_timesteps
174
+
175
+ def get_train_timestep_and_sigma(
176
+ self,
177
+ weighting_scheme: str = "logit_normal", # logit_norm, uniform
178
+ batch_size: int = 1,
179
+ logit_mean: float = 0.0,
180
+ logit_std: float = 1.0,
181
+ device: Union[torch.device, str] = "cpu",
182
+ generator: Optional[torch.Generator] = None,
183
+ n_dim: int = 4,
184
+ ):
185
+ if weighting_scheme == "logit_normal":
186
+ # NOTE: sigma from 1 to 0.
187
+ u = torch.normal(mean=logit_mean, std=logit_std, size=(batch_size,), generator=generator)
188
+ u = torch.nn.functional.sigmoid(u)
189
+ else:
190
+ u = torch.rand(size=(batch_size,), generator=generator)
191
+
192
+ indices = (u * self.config.num_train_timesteps).long()
193
+ timestep = self.timesteps[indices].to(device=device)
194
+ sigma = self.sigmas[indices].to(device=device, dtype=torch.float32)
195
+
196
+ while len(sigma.shape) < n_dim:
197
+ sigma = sigma.unsqueeze(-1)
198
+
199
+ return timestep, sigma
200
+
201
+ def get_train_timestep(
202
+ self,
203
+ weighting_scheme: str = "logit_normal", # logit_norm, uniform
204
+ batch_size: int = 1,
205
+ logit_mean: float = 0.0,
206
+ logit_std: float = 1.0,
207
+ device: Union[torch.device, str] = "cpu",
208
+ generator: Optional[torch.Generator] = None,
209
+ ):
210
+ if weighting_scheme == "logit_normal":
211
+ # NOTE: sigma from 1 to 0.
212
+ u = torch.normal(mean=logit_mean, std=logit_std, size=(batch_size,), generator=generator)
213
+ u = torch.nn.functional.sigmoid(u)
214
+ else:
215
+ u = torch.rand(size=(batch_size,), generator=generator)
216
+
217
+ indices = (u * self.config.num_train_timesteps).long()
218
+ timestep = self.timesteps[indices].to(device=device)
219
+ return timestep
220
+
221
+ def get_train_sigma(
222
+ self,
223
+ timestep: Union[float, torch.FloatTensor],
224
+ n_dim: int = 4,
225
+ device: Union[str, torch.device] = "cpu",
226
+ dtype: torch.dtype = torch.float32,
227
+ ):
228
+ if isinstance(timestep, float):
229
+ timestep = torch.tensor([timestep], dtype=dtype)
230
+
231
+ sigmas = self.sigmas.to(device, dtype=dtype)
232
+ schedule_timesteps = self.timesteps.to(device)
233
+ timestep = timestep.to(device)
234
+
235
+ step_indices = [(schedule_timesteps == t).nonzero()[0].item() for t in timestep]
236
+
237
+ sigma = sigmas[step_indices].flatten()
238
+ while len(sigma.shape) < n_dim:
239
+ sigma = sigma.unsqueeze(-1)
240
+ return sigma
241
+
242
+ def add_noise(
243
+ self,
244
+ original_samples: torch.FloatTensor,
245
+ noise: torch.FloatTensor,
246
+ sigma: Union[float, torch.FloatTensor],
247
+ ) -> torch.FloatTensor:
248
+ sample = (1 - sigma) * original_samples + sigma * noise
249
+ return sample
250
+
251
+ def get_train_target(
252
+ self,
253
+ original_samples: torch.FloatTensor,
254
+ noise: torch.FloatTensor,
255
+ ):
256
+ target = noise - original_samples
257
+ return target
258
+
259
+ def get_train_loss_weighting(
260
+ self,
261
+ sigma: torch.FloatTensor,
262
+ ):
263
+ weighting = torch.ones_like(sigma)
264
+ return weighting
265
+
266
+ def get_x0(
267
+ self,
268
+ model_output: torch.FloatTensor,
269
+ sample: torch.FloatTensor,
270
+ sigma_t: torch.FloatTensor,
271
+ ):
272
+ sigma_0 = torch.zeros_like(sigma_t)
273
+ dt = sigma_0 - sigma_t
274
+ prev_sample = sample + model_output.to(torch.float32) * dt
275
+ return prev_sample
diffusers_lite/utils/communication.py ADDED
@@ -0,0 +1,691 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # DeepSpeed Team
5
+
6
+ from typing import Any, Tuple
7
+
8
+ import os
9
+ import torch
10
+ import functools
11
+ import torch.distributed as dist
12
+ from torch import Tensor
13
+
14
+ from ..utils.parallel_states import nccl_info, get_teacher_student_parallel_state
15
+
16
+
17
+ def broadcast(input_: torch.Tensor):
18
+ src = nccl_info.group_id * nccl_info.sp_size
19
+ dist.broadcast(input_, src=src, group=nccl_info.group)
20
+
21
+ def broadcast_within_ts_unit(input_):
22
+ src = nccl_info.ts_unit_group_id * nccl_info.ts_unit_size
23
+ dist.broadcast(input_, src=src, group=nccl_info.ts_unit_group)
24
+
25
+ def broadcast_global(input_: torch.Tensor):
26
+ dist.broadcast(input_, src=0, group=None)
27
+
28
+ def broadcast_dict(input_: dict):
29
+ src = nccl_info.group_id * nccl_info.sp_size
30
+ for k, v in input_.items():
31
+ if isinstance(input_[k], torch.Tensor):
32
+ dist.broadcast(input_[k], src=src, group=nccl_info.group)
33
+
34
+ def broadcast_dict_within_ts_unit(input_: dict):
35
+ src = nccl_info.ts_unit_group_id * nccl_info.ts_unit_size
36
+ for k, v in input_.items():
37
+ if isinstance(input_[k], torch.Tensor):
38
+ dist.broadcast(input_[k], src=src, group=nccl_info.ts_unit_group)
39
+
40
+ def _all_to_all_4D(
41
+ input: torch.tensor, scatter_idx: int = 2, gather_idx: int = 1, group=None
42
+ ) -> torch.tensor:
43
+ """
44
+ all-to-all for QKV
45
+
46
+ Args:
47
+ input (torch.tensor): a tensor sharded along dim scatter dim
48
+ scatter_idx (int): default 1
49
+ gather_idx (int): default 2
50
+ group : torch process group
51
+
52
+ Returns:
53
+ torch.tensor: resharded tensor (bs, seqlen/P, hc, hs)
54
+ """
55
+ assert (
56
+ input.dim() == 4
57
+ ), f"input must be 4D tensor, got {input.dim()} and shape {input.shape}"
58
+
59
+ seq_world_size = dist.get_world_size(group)
60
+
61
+ if scatter_idx == 2 and gather_idx == 1:
62
+ # input (torch.tensor): a tensor sharded along dim 1 (bs, seqlen/P, hc, hs) output: (bs, seqlen, hc/P, hs)
63
+ bs, shard_seqlen, hc, hs = input.shape
64
+ seqlen = shard_seqlen * seq_world_size
65
+ shard_hc = hc // seq_world_size
66
+
67
+ # transpose groups of heads with the seq-len parallel dimension, so that we can scatter them!
68
+ # (bs, seqlen/P, hc, hs) -reshape-> (bs, seq_len/P, P, hc/P, hs) -transpose(0,2)-> (P, seq_len/P, bs, hc/P, hs)
69
+ input_t = (
70
+ input.reshape(bs, shard_seqlen, seq_world_size, shard_hc, hs)
71
+ .transpose(0, 2)
72
+ .contiguous()
73
+ )
74
+
75
+ output = torch.empty_like(input_t)
76
+ # https://pytorch.org/docs/stable/distributed.html#torch.distributed.all_to_all_single
77
+ # (P, seq_len/P, bs, hc/P, hs) scatter seqlen -all2all-> (P, seq_len/P, bs, hc/P, hs) scatter head
78
+ if seq_world_size > 1:
79
+ dist.all_to_all_single(output, input_t, group=group)
80
+ torch.cuda.synchronize()
81
+ else:
82
+ output = input_t
83
+ # if scattering the seq-dim, transpose the heads back to the original dimension
84
+ output = output.reshape(seqlen, bs, shard_hc, hs)
85
+
86
+ # (seq_len, bs, hc/P, hs) -reshape-> (bs, seq_len, hc/P, hs)
87
+ output = output.transpose(0, 1).contiguous().reshape(bs, seqlen, shard_hc, hs)
88
+
89
+ return output
90
+
91
+ elif scatter_idx == 1 and gather_idx == 2:
92
+ # input (torch.tensor): a tensor sharded along dim 1 (bs, seqlen, hc/P, hs) output: (bs, seqlen/P, hc, hs)
93
+ bs, seqlen, shard_hc, hs = input.shape
94
+ hc = shard_hc * seq_world_size
95
+ shard_seqlen = seqlen // seq_world_size
96
+ seq_world_size = dist.get_world_size(group)
97
+
98
+ # transpose groups of heads with the seq-len parallel dimension, so that we can scatter them!
99
+ # (bs, seqlen, hc/P, hs) -reshape-> (bs, P, seq_len/P, hc/P, hs) -transpose(0, 3)-> (hc/P, P, seqlen/P, bs, hs) -transpose(0, 1) -> (P, hc/P, seqlen/P, bs, hs)
100
+ input_t = (
101
+ input.reshape(bs, seq_world_size, shard_seqlen, shard_hc, hs)
102
+ .transpose(0, 3)
103
+ .transpose(0, 1)
104
+ .contiguous()
105
+ .reshape(seq_world_size, shard_hc, shard_seqlen, bs, hs)
106
+ )
107
+
108
+ output = torch.empty_like(input_t)
109
+ # https://pytorch.org/docs/stable/distributed.html#torch.distributed.all_to_all_single
110
+ # (P, bs x hc/P, seqlen/P, hs) scatter seqlen -all2all-> (P, bs x seq_len/P, hc/P, hs) scatter head
111
+ if seq_world_size > 1:
112
+ dist.all_to_all_single(output, input_t, group=group)
113
+ torch.cuda.synchronize()
114
+ else:
115
+ output = input_t
116
+
117
+ # if scattering the seq-dim, transpose the heads back to the original dimension
118
+ output = output.reshape(hc, shard_seqlen, bs, hs)
119
+
120
+ # (hc, seqlen/N, bs, hs) -tranpose(0,2)-> (bs, seqlen/N, hc, hs)
121
+ output = output.transpose(0, 2).contiguous().reshape(bs, shard_seqlen, hc, hs)
122
+
123
+ return output
124
+ else:
125
+ raise RuntimeError("scatter_idx must be 1 or 2 and gather_idx must be 1 or 2")
126
+
127
+
128
+ class SeqAllToAll4D(torch.autograd.Function):
129
+ @staticmethod
130
+ def forward(
131
+ ctx: Any,
132
+ group: dist.ProcessGroup,
133
+ input: Tensor,
134
+ scatter_idx: int,
135
+ gather_idx: int,
136
+ ) -> Tensor:
137
+ ctx.group = group
138
+ ctx.scatter_idx = scatter_idx
139
+ ctx.gather_idx = gather_idx
140
+
141
+ return _all_to_all_4D(input, scatter_idx, gather_idx, group=group)
142
+
143
+ @staticmethod
144
+ def backward(ctx: Any, *grad_output: Tensor) -> Tuple[None, Tensor, None, None]:
145
+ return (
146
+ None,
147
+ SeqAllToAll4D.apply(
148
+ ctx.group, *grad_output, ctx.gather_idx, ctx.scatter_idx
149
+ ),
150
+ None,
151
+ None,
152
+ )
153
+
154
+
155
+ def all_to_all_4D(
156
+ input_: torch.Tensor,
157
+ scatter_dim: int = 2,
158
+ gather_dim: int = 1,
159
+ ):
160
+ return SeqAllToAll4D.apply(nccl_info.group, input_, scatter_dim, gather_dim)
161
+
162
+
163
+ def _all_to_all(
164
+ input_: torch.Tensor,
165
+ world_size: int,
166
+ group: dist.ProcessGroup,
167
+ scatter_dim: int,
168
+ gather_dim: int,
169
+ ):
170
+ input_list = [
171
+ t.contiguous() for t in torch.tensor_split(input_, world_size, scatter_dim)
172
+ ]
173
+ output_list = [torch.empty_like(input_list[0]) for _ in range(world_size)]
174
+ dist.all_to_all(output_list, input_list, group=group)
175
+ return torch.cat(output_list, dim=gather_dim).contiguous()
176
+
177
+
178
+ class _AllToAll(torch.autograd.Function):
179
+ """All-to-all communication.
180
+
181
+ Args:
182
+ input_: input matrix
183
+ process_group: communication group
184
+ scatter_dim: scatter dimension
185
+ gather_dim: gather dimension
186
+ """
187
+
188
+ @staticmethod
189
+ def forward(ctx, input_, process_group, scatter_dim, gather_dim):
190
+ ctx.process_group = process_group
191
+ ctx.scatter_dim = scatter_dim
192
+ ctx.gather_dim = gather_dim
193
+ ctx.world_size = dist.get_world_size(process_group)
194
+ output = _all_to_all(
195
+ input_, ctx.world_size, process_group, scatter_dim, gather_dim
196
+ )
197
+ return output
198
+
199
+ @staticmethod
200
+ def backward(ctx, grad_output):
201
+ grad_output = _all_to_all(
202
+ grad_output,
203
+ ctx.world_size,
204
+ ctx.process_group,
205
+ ctx.gather_dim,
206
+ ctx.scatter_dim,
207
+ )
208
+ return (
209
+ grad_output,
210
+ None,
211
+ None,
212
+ None,
213
+ )
214
+
215
+
216
+ def all_to_all(
217
+ input_: torch.Tensor,
218
+ scatter_dim: int = 2,
219
+ gather_dim: int = 1,
220
+ ):
221
+ return _AllToAll.apply(input_, nccl_info.group, scatter_dim, gather_dim)
222
+
223
+
224
+ class _AllGather(torch.autograd.Function):
225
+ """All-gather communication with autograd support.
226
+
227
+ Args:
228
+ input_: input tensor
229
+ dim: dimension along which to concatenate
230
+ """
231
+
232
+ @staticmethod
233
+ def forward(ctx, input_, dim):
234
+ ctx.dim = dim
235
+ world_size = nccl_info.sp_size
236
+ group = nccl_info.group
237
+ input_size = list(input_.size())
238
+
239
+ ctx.input_size = input_size[dim]
240
+
241
+ tensor_list = [torch.empty_like(input_) for _ in range(world_size)]
242
+ input_ = input_.contiguous()
243
+ dist.all_gather(tensor_list, input_, group=group)
244
+
245
+ output = torch.cat(tensor_list, dim=dim)
246
+ return output
247
+
248
+ @staticmethod
249
+ def backward(ctx, grad_output):
250
+ world_size = nccl_info.sp_size
251
+ rank = nccl_info.rank_within_group
252
+ dim = ctx.dim
253
+ input_size = ctx.input_size
254
+
255
+ sizes = [input_size] * world_size
256
+
257
+ grad_input_list = torch.split(grad_output, sizes, dim=dim)
258
+ grad_input = grad_input_list[rank]
259
+
260
+ return grad_input, None
261
+
262
+
263
+ def all_gather(input_: torch.Tensor, dim: int = 1):
264
+ """Performs an all-gather operation on the input tensor along the specified dimension.
265
+
266
+ Args:
267
+ input_ (torch.Tensor): Input tensor of shape [B, H, S, D].
268
+ dim (int, optional): Dimension along which to concatenate. Defaults to 1.
269
+
270
+ Returns:
271
+ torch.Tensor: Output tensor after all-gather operation, concatenated along 'dim'.
272
+ """
273
+ return _AllGather.apply(input_, dim)
274
+
275
+ class _AllGather_TeacherStudent(torch.autograd.Function):
276
+ """All-gather communication with autograd support.
277
+
278
+ Args:
279
+ input_: input tensor
280
+ dim: dimension along which to concatenate
281
+ """
282
+
283
+ @staticmethod
284
+ def forward(ctx, input_, dim):
285
+ ctx.dim = dim
286
+ world_size = nccl_info.ts_unit_size
287
+ group = nccl_info.ts_unit_group
288
+ input_size = list(input_.size())
289
+
290
+ ctx.input_size = input_size[dim]
291
+
292
+ tensor_list = [torch.empty_like(input_) for _ in range(world_size)]
293
+ input_ = input_.contiguous()
294
+ dist.all_gather(tensor_list, input_, group=group)
295
+
296
+ output = torch.cat(tensor_list, dim=dim)
297
+ return output
298
+
299
+ @staticmethod
300
+ def backward(ctx, grad_output):
301
+ world_size = nccl_info.ts_unit_size
302
+ rank = nccl_info.rank_within_ts_unit_group
303
+ dim = ctx.dim
304
+ input_size = ctx.input_size
305
+
306
+ sizes = [input_size] * world_size
307
+ grad_input_list = torch.split(grad_output, sizes, dim=dim)
308
+ grad_input = grad_input_list[rank]
309
+ return grad_input, None
310
+
311
+ def all_gather_ts(input_: torch.Tensor, dim: int = 1):
312
+ """Performs an all-gather operation on the input tensor along the specified dimension.
313
+
314
+ Args:
315
+ input_ (torch.Tensor): Input tensor of shape [B, H, S, D].
316
+ dim (int, optional): Dimension along which to concatenate. Defaults to 1.
317
+
318
+ Returns:
319
+ torch.Tensor: Output tensor after all-gather operation, concatenated along 'dim'.
320
+ """
321
+ return _AllGather_TeacherStudent.apply(input_, dim)
322
+
323
+
324
+ def prepare_sequence_parallel_data_wanx(
325
+ hidden_states, encoder_hidden_states, uncond_text_states, image_embeds, latents_condition
326
+ ):
327
+ if nccl_info.sp_size == 1:
328
+ return (
329
+ hidden_states,
330
+ encoder_hidden_states,
331
+ uncond_text_states,
332
+ image_embeds,
333
+ latents_condition,
334
+ )
335
+
336
+ def prepare(hidden_states, encoder_hidden_states, uncond_text_states, image_embeds, latents_condition):
337
+ hidden_states = all_to_all(hidden_states, scatter_dim=2, gather_dim=0)
338
+ encoder_hidden_states = all_to_all(
339
+ encoder_hidden_states, scatter_dim=1, gather_dim=0
340
+ )
341
+ uncond_text_states = all_to_all(
342
+ uncond_text_states, scatter_dim=1, gather_dim=0
343
+ )
344
+ image_embeds = all_to_all(image_embeds, scatter_dim=1, gather_dim=0)
345
+ latents_condition = all_to_all(latents_condition, scatter_dim=2, gather_dim=0)
346
+
347
+ return (
348
+ hidden_states,
349
+ encoder_hidden_states,
350
+ uncond_text_states,
351
+ image_embeds,
352
+ latents_condition,
353
+ )
354
+
355
+ sp_size = nccl_info.sp_size
356
+ frame = hidden_states.shape[2]
357
+ assert frame % sp_size == 0, "frame should be a multiple of sp_size"
358
+
359
+ (
360
+ hidden_states,
361
+ encoder_hidden_states,
362
+ uncond_text_states,
363
+ image_embeds,
364
+ latents_condition,
365
+ ) = prepare(
366
+ hidden_states,
367
+ encoder_hidden_states.repeat(1, sp_size, 1),
368
+ uncond_text_states.repeat(1, sp_size, 1),
369
+ image_embeds.repeat(1, sp_size, 1),
370
+ latents_condition,
371
+ )
372
+
373
+ return hidden_states, encoder_hidden_states, uncond_text_states, image_embeds, latents_condition
374
+
375
+
376
+ def sp_parallel_dataloader_wrapper_wanx(
377
+ dataloader, device, train_batch_size, sp_size, train_sp_batch_size
378
+ ):
379
+ while True:
380
+ for data_item in dataloader:
381
+ latents, text_states, uncond_text_states, image_embeds, latents_condition = data_item
382
+ latents = latents.to(device)
383
+ text_states = text_states.to(device)
384
+ uncond_text_states = uncond_text_states.to(device)
385
+ image_embeds = image_embeds.to(device)
386
+ latents_condition = latents_condition.to(device)
387
+ frame = latents.shape[2]
388
+ if frame == 1:
389
+ yield latents, text_states, uncond_text_states, image_embeds, latents_condition
390
+ else:
391
+ latents, text_states, uncond_text_states, image_embeds, latents_condition = (
392
+ prepare_sequence_parallel_data_wanx(
393
+ latents, text_states, uncond_text_states, image_embeds, latents_condition
394
+ )
395
+ )
396
+ assert (
397
+ train_batch_size * sp_size >= train_sp_batch_size
398
+ ), "train_batch_size * sp_size should be greater than train_sp_batch_size"
399
+ for iter in range(train_batch_size * sp_size // train_sp_batch_size):
400
+ st_idx = iter * train_sp_batch_size
401
+ ed_idx = (iter + 1) * train_sp_batch_size
402
+ yield (
403
+ latents[st_idx:ed_idx],
404
+ text_states[st_idx:ed_idx],
405
+ uncond_text_states[st_idx:ed_idx],
406
+ image_embeds[st_idx:ed_idx],
407
+ latents_condition[st_idx:ed_idx],
408
+ )
409
+
410
+ def prepare_sequence_parallel_data_wanx_dpo(
411
+ hidden_states, encoder_hidden_states, uncond_text_states, image_embeds, latents_condition,latents_lose
412
+ ):
413
+ if nccl_info.sp_size == 1:
414
+ return (
415
+ hidden_states,
416
+ encoder_hidden_states,
417
+ uncond_text_states,
418
+ image_embeds,
419
+ latents_condition,
420
+ latents_lose,
421
+ )
422
+
423
+ def prepare(hidden_states, encoder_hidden_states, uncond_text_states, image_embeds, latents_condition, latents_lose):
424
+ hidden_states = all_to_all(hidden_states, scatter_dim=2, gather_dim=0)
425
+ latents_lose = all_to_all(latents_lose, scatter_dim=2, gather_dim=0)
426
+ encoder_hidden_states = all_to_all(
427
+ encoder_hidden_states, scatter_dim=1, gather_dim=0
428
+ )
429
+ uncond_text_states = all_to_all(
430
+ uncond_text_states, scatter_dim=1, gather_dim=0
431
+ )
432
+ image_embeds = all_to_all(image_embeds, scatter_dim=1, gather_dim=0)
433
+ latents_condition = all_to_all(latents_condition, scatter_dim=2, gather_dim=0)
434
+
435
+ return (
436
+ hidden_states,
437
+ encoder_hidden_states,
438
+ uncond_text_states,
439
+ image_embeds,
440
+ latents_condition,
441
+ latents_lose,
442
+ )
443
+
444
+ sp_size = nccl_info.sp_size
445
+ frame = hidden_states.shape[2]
446
+ assert frame % sp_size == 0, "frame should be a multiple of sp_size"
447
+
448
+ (
449
+ hidden_states,
450
+ encoder_hidden_states,
451
+ uncond_text_states,
452
+ image_embeds,
453
+ latents_condition,latents_lose,
454
+ ) = prepare(
455
+ hidden_states,
456
+ encoder_hidden_states.repeat(1, sp_size, 1),
457
+ uncond_text_states.repeat(1, sp_size, 1),
458
+ image_embeds.repeat(1, sp_size, 1),
459
+ latents_condition,
460
+ latents_lose
461
+ )
462
+
463
+ return hidden_states, encoder_hidden_states, uncond_text_states, image_embeds, latents_condition,latents_lose
464
+
465
+ def sp_parallel_dataloader_wrapper_wanx_dpo(
466
+ dataloader, device, train_batch_size, sp_size, train_sp_batch_size
467
+ ):
468
+ while True:
469
+ for data_item in dataloader:
470
+ latents, text_states, uncond_text_states, image_embeds, latents_condition,latent_lose = data_item
471
+ latents = latents.to(device)
472
+ latents_lose = latents.to(device)
473
+ text_states = text_states.to(device)
474
+ uncond_text_states = uncond_text_states.to(device)
475
+ image_embeds = image_embeds.to(device)
476
+ latents_condition = latents_condition.to(device)
477
+ frame = latents.shape[2]
478
+ if frame == 1:
479
+ yield latents, text_states, uncond_text_states, image_embeds, latents_condition,latent_lose
480
+ else:
481
+ latents, text_states, uncond_text_states, image_embeds, latents_condition, latents_lose = (
482
+ prepare_sequence_parallel_data_wanx_dpo(
483
+ latents, text_states, uncond_text_states, image_embeds, latents_condition,latent_lose
484
+ )
485
+ )
486
+ assert (
487
+ train_batch_size * sp_size >= train_sp_batch_size
488
+ ), "train_batch_size * sp_size should be greater than train_sp_batch_size"
489
+ for iter in range(train_batch_size * sp_size // train_sp_batch_size):
490
+ st_idx = iter * train_sp_batch_size
491
+ ed_idx = (iter + 1) * train_sp_batch_size
492
+ yield (
493
+ latents[st_idx:ed_idx],
494
+ text_states[st_idx:ed_idx],
495
+ uncond_text_states[st_idx:ed_idx],
496
+ image_embeds[st_idx:ed_idx],
497
+ latents_condition[st_idx:ed_idx],
498
+ latents_lose[st_idx:ed_idx],
499
+ )
500
+
501
+ def prepare_sequence_parallel_data_ltx(
502
+ hidden_states, encoder_hidden_states, text_mask, uncond_text_states, uncond_text_mask
503
+ ):
504
+ if nccl_info.sp_size == 1:
505
+ return (
506
+ hidden_states,
507
+ encoder_hidden_states,
508
+ text_mask,
509
+ uncond_text_states,
510
+ uncond_text_mask,
511
+ )
512
+
513
+ def prepare(hidden_states, encoder_hidden_states, text_mask, uncond_text_states, uncond_text_mask):
514
+ hidden_states = all_to_all(hidden_states, scatter_dim=2, gather_dim=0)
515
+ encoder_hidden_states = all_to_all(
516
+ encoder_hidden_states, scatter_dim=1, gather_dim=0
517
+ )
518
+ text_mask = all_to_all(text_mask, scatter_dim=1, gather_dim=0)
519
+ uncond_text_states = all_to_all(
520
+ uncond_text_states, scatter_dim=1, gather_dim=0
521
+ )
522
+ uncond_text_mask = all_to_all(
523
+ uncond_text_mask, scatter_dim=1, gather_dim=0
524
+ )
525
+
526
+ return (
527
+ hidden_states,
528
+ encoder_hidden_states,
529
+ text_mask,
530
+ uncond_text_states,
531
+ uncond_text_mask,
532
+ )
533
+
534
+ sp_size = nccl_info.sp_size
535
+ frame = hidden_states.shape[2]
536
+ assert frame % sp_size == 0, "frame should be a multiple of sp_size"
537
+
538
+ (
539
+ hidden_states,
540
+ encoder_hidden_states,
541
+ text_mask,
542
+ uncond_text_states,
543
+ uncond_text_mask,
544
+ ) = prepare(
545
+ hidden_states,
546
+ encoder_hidden_states.repeat(1, sp_size, 1),
547
+ text_mask.repeat(1, sp_size),
548
+ uncond_text_states.repeat(1, sp_size, 1),
549
+ uncond_text_mask.repeat(1, sp_size)
550
+ )
551
+
552
+ return hidden_states, encoder_hidden_states, text_mask, uncond_text_states, uncond_text_mask
553
+
554
+
555
+ def sp_parallel_dataloader_wrapper_ltx(
556
+ dataloader, device, train_batch_size, sp_size, train_sp_batch_size
557
+ ):
558
+ while True:
559
+ for data_item in dataloader:
560
+ latents, text_states, text_mask, uncond_text_states, uncond_text_mask = data_item
561
+ latents = latents.to(device)
562
+ text_states = text_states.to(device)
563
+ text_mask = text_mask.to(device)
564
+ uncond_text_states = uncond_text_states.to(device)
565
+ uncond_text_mask = uncond_text_mask.to(device)
566
+ frame = latents.shape[2]
567
+ if frame == 1:
568
+ yield latents, text_states, text_mask, uncond_text_states, uncond_text_mask
569
+ else:
570
+ latents, text_states, text_mask, uncond_text_states, uncond_text_mask = (
571
+ prepare_sequence_parallel_data_ltx(
572
+ latents, text_states, text_mask, uncond_text_states, uncond_text_mask
573
+ )
574
+ )
575
+ assert (
576
+ train_batch_size * sp_size >= train_sp_batch_size
577
+ ), "train_batch_size * sp_size should be greater than train_sp_batch_size"
578
+ for iter in range(train_batch_size * sp_size // train_sp_batch_size):
579
+ st_idx = iter * train_sp_batch_size
580
+ ed_idx = (iter + 1) * train_sp_batch_size
581
+ yield (
582
+ latents[st_idx:ed_idx],
583
+ text_states[st_idx:ed_idx],
584
+ text_mask[st_idx:ed_idx],
585
+ uncond_text_states[st_idx:ed_idx],
586
+ uncond_text_mask[st_idx:ed_idx],
587
+ )
588
+
589
+
590
+ def parallelize_model(model):
591
+ original_forward = model.forward
592
+
593
+ @functools.wraps(model.__class__.forward)
594
+ def new_forward(
595
+ self,
596
+ hidden_states: torch.Tensor,
597
+ timestep: torch.LongTensor,
598
+ text_states: torch.Tensor,
599
+ text_states_2: torch.Tensor,
600
+ encoder_attention_mask: torch.Tensor,
601
+ output_features=False,
602
+ output_features_stride=8,
603
+ attention_kwargs=None,
604
+ freqs_cos=None,
605
+ freqs_sin=None,
606
+ return_dict=False,
607
+ guidance=None,
608
+ ):
609
+ x = hidden_states
610
+ sp_size = nccl_info.sp_size
611
+ sp_rank = nccl_info.rank_within_group
612
+
613
+ if x.shape[-2] // 2 % sp_size == 0:
614
+ # try to split x by height
615
+ split_dim = -2
616
+ elif x.shape[-1] // 2 % sp_size == 0:
617
+ # try to split x by width
618
+ split_dim = -1
619
+ else:
620
+ raise ValueError(f"Cannot split video sequence into ulysses_degree ({sp_size}) parts evenly")
621
+
622
+ _, _, ot, oh, ow = x.shape
623
+ tt, th, tw = (
624
+ ot // self.patch_size[0],
625
+ oh // self.patch_size[1],
626
+ ow // self.patch_size[2],
627
+ )
628
+ freqs_cos, freqs_sin = self.get_rotary_pos_embed((tt, th, tw))
629
+ # patch sizes for the temporal, height, and width dimensions are 1, 2, and 2.
630
+ temporal_size, h, w = x.shape[2], x.shape[3] // 2, x.shape[4] // 2
631
+
632
+ x = torch.chunk(x, sp_size,dim=split_dim)[sp_rank]
633
+
634
+ dim_thw = freqs_cos.shape[-1]
635
+ freqs_cos = freqs_cos.reshape(temporal_size, h, w, dim_thw)
636
+ freqs_cos = torch.chunk(freqs_cos, sp_size,dim=split_dim - 1)[sp_rank]
637
+ freqs_cos = freqs_cos.reshape(-1, dim_thw)
638
+ dim_thw = freqs_sin.shape[-1]
639
+ freqs_sin = freqs_sin.reshape(temporal_size, h, w, dim_thw)
640
+ freqs_sin = torch.chunk(freqs_sin, sp_size,dim=split_dim - 1)[sp_rank]
641
+ freqs_sin = freqs_sin.reshape(-1, dim_thw)
642
+
643
+ output = original_forward(
644
+ x,
645
+ timestep,
646
+ text_states,
647
+ text_states_2,
648
+ encoder_attention_mask,
649
+ output_features,
650
+ output_features_stride,
651
+ attention_kwargs,
652
+ freqs_cos,
653
+ freqs_sin,
654
+ return_dict,
655
+ guidance,
656
+ )
657
+
658
+ return_dict = not isinstance(output, tuple)
659
+ shape = (tt, th, tw)
660
+ if return_dict:
661
+ assert not output_features, "output_feature is not compatible with return_dict"
662
+ sample = output["x"]
663
+ sample = all_gather(sample, dim=split_dim)
664
+ output["x"] = sample
665
+ else:
666
+ sample = output[0]
667
+ sample = all_gather(sample, dim=split_dim)
668
+ if output_features:
669
+ features_list = output[1]
670
+ features_list = all_gather(features_list, dim=split_dim)
671
+ else:
672
+ features_list = None
673
+
674
+ output = (sample, features_list, shape)
675
+ return output
676
+
677
+ new_forward = new_forward.__get__(model)
678
+ model.forward = new_forward
679
+
680
+
681
+ def all_reduce_tensor_item(item):
682
+ world_size = int(os.environ["WORLD_SIZE"])
683
+ item = item.detach().clone()
684
+ dist.all_reduce(item, op=dist.ReduceOp.SUM)
685
+ item = item / nccl_info.ts_group_size if get_teacher_student_parallel_state() else item / world_size
686
+ return item
687
+
688
+ def broadcast_item(item, idx):
689
+ item_list = [item]
690
+ dist.broadcast_object_list(item_list, src=idx)
691
+ return item_list[0]
diffusers_lite/utils/data_utils.py ADDED
@@ -0,0 +1,542 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import math
3
+ import random
4
+ from collections import Counter
5
+ from typing import List, Optional
6
+
7
+ import imageio
8
+ import torch
9
+ import torchvision
10
+ import numpy as np
11
+ from einops import rearrange
12
+ from torch.utils.data import Sampler
13
+ from typing import Union, Optional, Iterator, List, Callable
14
+ import warnings
15
+ import logging
16
+
17
+ import torch
18
+ import torch.distributed as dist
19
+ from torch.utils.data.distributed import DistributedSampler as TorchDistributedSampler
20
+
21
+
22
+
23
+ def split_list(input_list, rank=0, num_process=8):
24
+
25
+ n = len(input_list)
26
+ base = n // num_process
27
+ remainder = n % num_process
28
+
29
+ if rank < remainder:
30
+ start = rank * (base + 1)
31
+ end = start + (base + 1)
32
+ else:
33
+ start = remainder * (base + 1) + (rank - remainder) * base
34
+ end = start + base
35
+
36
+ local_input_list = input_list[start:end]
37
+
38
+ return local_input_list
39
+
40
+
41
+ def align_floor_to(value, alignment):
42
+ return int(math.floor(value / alignment) * alignment)
43
+
44
+
45
+ def align_ceil_to(value, alignment):
46
+ return int(math.ceil(value / alignment) * alignment)
47
+
48
+
49
+ def crop_tensor(
50
+ latents,
51
+ image_latents=None,
52
+ crop_width_ratio=1.0,
53
+ crop_height_ratio=1.0,
54
+ crop_type="center",
55
+ crop_time_ratio=1.0,
56
+ ):
57
+ b, c, t, h, w = latents.shape
58
+ crop_h, crop_w = int(h * crop_height_ratio), int(w * crop_width_ratio)
59
+ crop_t = int(t * crop_time_ratio)
60
+
61
+ if crop_type == "center":
62
+ top = (h - crop_h) // 2
63
+ left = (w - crop_w) // 2
64
+ elif crop_type == "random":
65
+ top = random.randint(0, h - crop_h)
66
+ left = random.randint(0, w - crop_w)
67
+
68
+ crop_h = align_floor_to(crop_h, alignment=2)
69
+ crop_w = align_floor_to(crop_w, alignment=2)
70
+ crop_t = align_floor_to(crop_t, alignment=1)
71
+
72
+ if image_latents is not None:
73
+ return (
74
+ latents[:, :, :crop_t, top : top + crop_h, left : left + crop_w],
75
+ image_latents[:, :, :crop_t, top : top + crop_h, left : left + crop_w],
76
+ )
77
+ else:
78
+ return latents[:, :, :crop_t, top : top + crop_h, left : left + crop_w], image_latents
79
+
80
+ def crop_tensor_dpo(
81
+ latents,
82
+ latents_lose,
83
+ image_latents=None,
84
+ crop_width_ratio=1.0,
85
+ crop_height_ratio=1.0,
86
+ crop_type="center",
87
+ crop_time_ratio=1.0,
88
+ ):
89
+ b, c, t, h, w = latents.shape
90
+ crop_h, crop_w = int(h * crop_height_ratio), int(w * crop_width_ratio)
91
+ crop_t = int(t * crop_time_ratio)
92
+
93
+ if crop_type == "center":
94
+ top = (h - crop_h) // 2
95
+ left = (w - crop_w) // 2
96
+ elif crop_type == "random":
97
+ top = random.randint(0, h - crop_h)
98
+ left = random.randint(0, w - crop_w)
99
+
100
+ crop_h = align_floor_to(crop_h, alignment=2)
101
+ crop_w = align_floor_to(crop_w, alignment=2)
102
+ crop_t = align_floor_to(crop_t, alignment=1)
103
+
104
+ if image_latents is not None:
105
+ return (
106
+ latents[:, :, :crop_t, top : top + crop_h, left : left + crop_w],
107
+ image_latents[:, :, :crop_t, top : top + crop_h, left : left + crop_w],
108
+ latents_lose[:, :, :crop_t, top : top + crop_h, left : left + crop_w]
109
+ )
110
+ else:
111
+ return (latents[:, :, :crop_t, top : top + crop_h, left : left + crop_w],
112
+ image_latents,
113
+ latents_lose[:, :, :crop_t, top : top + crop_h, left : left + crop_w])
114
+
115
+
116
+ def megabatch_frame_alignment(megabatches, lengths):
117
+ aligned_magabatches = []
118
+ for _, megabatch in enumerate(megabatches):
119
+ assert len(megabatch) != 0
120
+ len_each_megabatch = [lengths[i] for i in megabatch]
121
+ idx_length_dict = dict([*zip(megabatch, len_each_megabatch)])
122
+ count_dict = Counter(len_each_megabatch)
123
+
124
+ # mixed frame length, align megabatch inside
125
+ if len(count_dict) != 1:
126
+ sorted_by_value = sorted(count_dict.items(), key=lambda item: item[1])
127
+ pick_length = sorted_by_value[-1][0] # the highest frequency
128
+ candidate_batch = [
129
+ idx for idx, length in idx_length_dict.items() if length == pick_length
130
+ ]
131
+ random_select_batch = [
132
+ random.choice(candidate_batch)
133
+ for i in range(len(idx_length_dict) - len(candidate_batch))
134
+ ]
135
+ aligned_magabatch = candidate_batch + random_select_batch
136
+ aligned_magabatches.append(aligned_magabatch)
137
+ # already aligned megabatches
138
+ else:
139
+ aligned_magabatches.append(megabatch)
140
+
141
+ return aligned_magabatches
142
+
143
+
144
+ def split_to_even_chunks(indices, lengths, num_chunks, batch_size):
145
+ """
146
+ Split a list of indices into `chunks` chunks of roughly equal lengths.
147
+ """
148
+
149
+ if len(indices) % num_chunks != 0:
150
+ chunks = [indices[i::num_chunks] for i in range(num_chunks)]
151
+ else:
152
+ num_indices_per_chunk = len(indices) // num_chunks
153
+
154
+ chunks = [[] for _ in range(num_chunks)]
155
+ chunks_lengths = [0 for _ in range(num_chunks)]
156
+ for index in indices:
157
+ shortest_chunk = chunks_lengths.index(min(chunks_lengths))
158
+ chunks[shortest_chunk].append(index)
159
+ chunks_lengths[shortest_chunk] += lengths[index]
160
+ if len(chunks[shortest_chunk]) == num_indices_per_chunk:
161
+ chunks_lengths[shortest_chunk] = float("inf")
162
+ # return chunks
163
+
164
+ pad_chunks = []
165
+ for idx, chunk in enumerate(chunks):
166
+ if batch_size != len(chunk):
167
+ assert batch_size > len(chunk)
168
+ if len(chunk) != 0:
169
+ chunk = chunk + [
170
+ random.choice(chunk) for _ in range(batch_size - len(chunk))
171
+ ]
172
+ else:
173
+ chunk = random.choice(pad_chunks)
174
+ print(chunks[idx], "->", chunk)
175
+ pad_chunks.append(chunk)
176
+ return pad_chunks
177
+
178
+
179
+ def group_frame_fun(indices, lengths):
180
+ # sort by num_frames
181
+ indices.sort(key=lambda i: lengths[i], reverse=True)
182
+ return indices
183
+
184
+
185
+ def get_length_grouped_indices(
186
+ lengths,
187
+ batch_size,
188
+ world_size,
189
+ generator=None,
190
+ group_frame=False,
191
+ group_resolution=False,
192
+ seed=42,
193
+ ):
194
+ # We need to use torch for the random part as a distributed sampler will set the random seed for torch.
195
+ if generator is None:
196
+ generator = torch.Generator().manual_seed(
197
+ seed
198
+ ) # every rank will generate a fixed order but random index
199
+
200
+ indices = torch.randperm(len(lengths), generator=generator).tolist()
201
+
202
+ # sort dataset according to frame
203
+ indices = group_frame_fun(indices, lengths)
204
+
205
+ # chunk dataset to megabatches
206
+ megabatch_size = world_size * batch_size
207
+ megabatches = [
208
+ indices[i : i + megabatch_size] for i in range(0, len(lengths), megabatch_size)
209
+ ]
210
+
211
+ # make sure the length in each magabatch is align with each other
212
+ megabatches = megabatch_frame_alignment(megabatches, lengths)
213
+
214
+ # aplit aligned megabatch into batches
215
+ megabatches = [
216
+ split_to_even_chunks(megabatch, lengths, world_size, batch_size)
217
+ for megabatch in megabatches
218
+ ]
219
+
220
+ # random megabatches to do video-image mix training
221
+ indices = torch.randperm(len(megabatches), generator=generator).tolist()
222
+ shuffled_megabatches = [megabatches[i] for i in indices]
223
+
224
+ # expand indices and return
225
+ return [
226
+ i for megabatch in shuffled_megabatches for batch in megabatch for i in batch
227
+ ]
228
+
229
+
230
+ class LengthGroupedSampler(Sampler):
231
+ r"""
232
+ Sampler that samples indices in a way that groups together features of the dataset of roughly the same length while
233
+ keeping a bit of randomness.
234
+ """
235
+
236
+ def __init__(
237
+ self,
238
+ batch_size: int,
239
+ rank: int,
240
+ world_size: int,
241
+ lengths: Optional[List[int]] = None,
242
+ group_frame=False,
243
+ group_resolution=False,
244
+ generator=None,
245
+ ):
246
+ if lengths is None:
247
+ raise ValueError("Lengths must be provided.")
248
+
249
+ self.batch_size = batch_size
250
+ self.rank = rank
251
+ self.world_size = world_size
252
+ self.lengths = lengths
253
+ self.group_frame = group_frame
254
+ self.group_resolution = group_resolution
255
+ self.generator = generator
256
+
257
+ def __len__(self):
258
+ return len(self.lengths)
259
+
260
+ def __iter__(self):
261
+ indices = get_length_grouped_indices(
262
+ self.lengths,
263
+ self.batch_size,
264
+ self.world_size,
265
+ group_frame=self.group_frame,
266
+ group_resolution=self.group_resolution,
267
+ generator=self.generator,
268
+ )
269
+
270
+ def distributed_sampler(lst, rank, batch_size, world_size):
271
+ result = []
272
+ index = rank * batch_size
273
+ while index < len(lst):
274
+ result.extend(lst[index : index + batch_size])
275
+ index += batch_size * world_size
276
+ return result
277
+
278
+ indices = distributed_sampler(
279
+ indices, self.rank, self.batch_size, self.world_size
280
+ )
281
+ return iter(indices)
282
+
283
+
284
+ def save_videos_grid(videos, path, rescale=False, n_rows=1, fps=24):
285
+ videos = rearrange(videos, "b c t h w -> t b c h w")
286
+ outputs = []
287
+ for x in videos:
288
+ x = torchvision.utils.make_grid(x, nrow=n_rows)
289
+ x = x.transpose(0, 1).transpose(1, 2).squeeze(-1)
290
+ if rescale:
291
+ x = (x + 1.0) / 2.0 # -1,1 -> 0,1
292
+ x = torch.clamp(x, 0, 1)
293
+ x = (x * 255).numpy().astype(np.uint8)
294
+ outputs.append(x)
295
+
296
+ os.makedirs(os.path.dirname(path), exist_ok=True)
297
+ imageio.mimsave(path, outputs, fps=fps)
298
+
299
+
300
+ class BlockDistributedSampler(TorchDistributedSampler):
301
+ def __init__(self, dataset, num_replicas=None, rank=None, shuffle=False, seed=0, drop_last=False,
302
+ batch_size=-1, start_index=0, align=1):
303
+ """
304
+ Args:
305
+ dataset: Dataset used for sampling.
306
+ num_replicas: Number of processes participating in distributed training.
307
+ rank: Rank of the current process within num_replicas.
308
+ shuffle: If True, the sampler will shuffle the indices.
309
+ seed: Random seed.
310
+ drop_last: If True, the sampler will drop the last batch if its size would be less than batch_size.
311
+ batch_size: Size of mini-batch. If callable, it should accept a tuple of (w, h) as input and return an integer
312
+ value as the batch size. It is useful for mix-scale(e.g., 256, 512, 1024) training.
313
+ start_index: Start index for the sampler.
314
+ align: Align the indices to the multiple of align for each dp.
315
+ """
316
+ super().__init__(dataset, num_replicas, rank, shuffle, seed, drop_last)
317
+ if num_replicas is None:
318
+ if not dist.is_available():
319
+ raise RuntimeError("Requires distributed package to be available")
320
+ num_replicas = dist.get_world_size()
321
+ if rank is None:
322
+ if not dist.is_available():
323
+ raise RuntimeError("Requires distributed package to be available")
324
+ rank = dist.get_rank()
325
+ if rank >= num_replicas or rank < 0:
326
+ raise ValueError(
327
+ "Invalid rank {}, rank should be in the interval"
328
+ " [0, {}]".format(rank, num_replicas - 1))
329
+ if batch_size != -1:
330
+ align = batch_size
331
+ warnings.warn("batch_size is deprecated, please use `align` instead.")
332
+ if align <= 0:
333
+ raise ValueError(f"align should be a positive integer, but got {align}.")
334
+
335
+ self.dataset = dataset
336
+ self.num_replicas = num_replicas
337
+ self.rank = rank
338
+ self.epoch = 0
339
+ self.drop_last = drop_last
340
+ self.shuffle = shuffle
341
+ self.seed = seed
342
+ self.batch_size = batch_size
343
+ self.align = align
344
+ self._start_index = start_index
345
+ self.recompute_sizes()
346
+
347
+ @property
348
+ def start_index(self):
349
+ return self._start_index
350
+
351
+ @start_index.setter
352
+ def start_index(self, value):
353
+ if self._start_index != value:
354
+ self._start_index = value
355
+ self.recompute_sizes()
356
+
357
+ def recompute_sizes(self):
358
+ self.num_samples = len(self.dataset) // self.align * self.align // self.num_replicas \
359
+ - self._start_index
360
+ self.total_size = self.num_samples * self.num_replicas
361
+
362
+ def __iter__(self):
363
+ if self.shuffle:
364
+ # deterministically shuffle based on epoch and seed
365
+ g = torch.Generator()
366
+ g.manual_seed(self.seed + self.epoch)
367
+ indices = torch.randperm(len(self.dataset), generator=g).tolist() # type: ignore[arg-type]
368
+ else:
369
+ indices = list(range(len(self.dataset))) # type: ignore[arg-type]
370
+ raw_num_samples = len(indices) // self.align * self.align // self.num_replicas
371
+ raw_total_size = raw_num_samples * self.num_replicas
372
+ indices = indices[:raw_total_size]
373
+
374
+ # subsample with start_index
375
+ indices = indices[self.rank * raw_num_samples + self.start_index:(self.rank + 1) * raw_num_samples]
376
+ assert len(indices) + self.start_index == raw_num_samples, \
377
+ f"{len(indices) + self.start_index} vs {raw_num_samples}"
378
+
379
+ # print(f"Iterator of BlockDistributedSampler created.")
380
+ # This is a sequential sampler. The shuffle operation is done by the dataset itself.
381
+ return iter(indices)
382
+
383
+
384
+ class DistributedSampler(TorchDistributedSampler):
385
+ def __init__(self, dataset, num_replicas=None, rank=None, shuffle=False, seed=0, drop_last=False,
386
+ start_index=0):
387
+ super().__init__(dataset, num_replicas, rank, shuffle, seed, drop_last)
388
+ if num_replicas is None:
389
+ if not dist.is_available():
390
+ raise RuntimeError("Requires distributed package to be available")
391
+ num_replicas = dist.get_world_size()
392
+ if rank is None:
393
+ if not dist.is_available():
394
+ raise RuntimeError("Requires distributed package to be available")
395
+ rank = dist.get_rank()
396
+ if rank >= num_replicas or rank < 0:
397
+ raise ValueError(
398
+ "Invalid rank {}, rank should be in the interval"
399
+ " [0, {}]".format(rank, num_replicas - 1))
400
+ self.dataset = dataset
401
+ self.num_replicas = num_replicas
402
+ self.rank = rank
403
+ self.epoch = 0
404
+ self.drop_last = drop_last
405
+ self._start_index = start_index
406
+ self.recompute_sizes()
407
+ self.shuffle = shuffle
408
+ self.seed = seed
409
+
410
+ @property
411
+ def start_index(self):
412
+ return self._start_index
413
+
414
+ @start_index.setter
415
+ def start_index(self, value):
416
+ self._start_index = value
417
+ self.recompute_sizes()
418
+
419
+ def recompute_sizes(self):
420
+ # If the dataset length is evenly divisible by # of replicas, then there
421
+ # is no need to drop any data, since the dataset will be split equally.
422
+ if self.drop_last and (len(self.dataset) - self._start_index) % self.num_replicas != 0: # type: ignore[arg-type]
423
+ # Split to nearest available length that is evenly divisible.
424
+ # This is to ensure each rank receives the same amount of data when
425
+ # using this Sampler.
426
+ self.num_samples = math.ceil(
427
+ ((len(self.dataset) - self._start_index) - self.num_replicas) / self.num_replicas # type: ignore[arg-type]
428
+ )
429
+ else:
430
+ self.num_samples = math.ceil((len(self.dataset) - self._start_index) / self.num_replicas) # type: ignore[arg-type]
431
+ self.total_size = self.num_samples * self.num_replicas
432
+
433
+ def __iter__(self):
434
+ if self.shuffle:
435
+ # deterministically shuffle based on epoch and seed
436
+ g = torch.Generator()
437
+ g.manual_seed(self.seed + self.epoch)
438
+ indices = torch.randperm(len(self.dataset), generator=g).tolist() # type: ignore[arg-type]
439
+ indices = indices[self._start_index:]
440
+ else:
441
+ indices = list(range(self._start_index, len(self.dataset))) # type: ignore[arg-type]
442
+
443
+ if not self.drop_last:
444
+ # add extra samples to make it evenly divisible
445
+ padding_size = self.total_size - len(indices)
446
+ if padding_size <= len(indices):
447
+ indices += indices[:padding_size]
448
+ else:
449
+ indices += (indices * math.ceil(padding_size / len(indices)))[:padding_size]
450
+ else:
451
+ # remove tail of data to make it evenly divisible.
452
+ indices = indices[:self.total_size]
453
+ assert len(indices) == self.total_size
454
+
455
+ # subsample with start_index
456
+ indices = indices[self.rank:self.total_size:self.num_replicas]
457
+ assert len(indices) == self.num_samples
458
+
459
+ print(f"Iterator of DistributedSamplerWithStartIndex created.")
460
+ return iter(indices)
461
+
462
+
463
+ # For backward compatibility
464
+ DistributedSamplerWithStartIndex = DistributedSampler
465
+
466
+
467
+ def cumsum(sequence):
468
+ r, s = [], 0
469
+ for e in sequence:
470
+ l = len(e)
471
+ r.append(l + s)
472
+ s += l
473
+ return r
474
+
475
+ def get_infinite_iterator(dataloader):
476
+ while True:
477
+ for batch in dataloader:
478
+ yield batch
479
+ dataloader.sampler.set_epoch(dataloader.sampler.epoch + 1)
480
+ print(f"epoch: {dataloader.sampler.epoch}, rank: {dataloader.sampler.rank}")
481
+
482
+
483
+ class VideoImageBatchIterator:
484
+ def __init__(self,
485
+ video_dataloader,
486
+ image_dataloader = None,
487
+ sp_size = 1,
488
+ ):
489
+ assert video_dataloader is not None or image_dataloader is not None
490
+ self.sp_size = sp_size
491
+ self.video_dataloader = video_dataloader
492
+ self.image_dataloader = image_dataloader
493
+ self.video_iterator = iter(self.video_dataloader) if video_dataloader is not None else None
494
+ self.image_iterator = iter(self.image_dataloader) if image_dataloader is not None else None
495
+
496
+ def get_image_batch(self):
497
+ try:
498
+ if self.sp_size > 1:
499
+ while True:
500
+ batch = next(self.image_iterator)
501
+ shape = batch[0].shape
502
+ if shape[-1]/16 * shape[-2]/16 % self.sp_size == 0:
503
+ break
504
+ else:
505
+ logging.warning(f"skipping one sample due to the shape {shape} and SP {self.sp_size} mismatching")
506
+ else:
507
+ batch = next(self.image_iterator)
508
+ return batch
509
+ except StopIteration:
510
+ logging.info(f"Image dataset start new epoch")
511
+ self.image_iterator = iter(self.image_dataloader)
512
+ raise StopIteration
513
+
514
+
515
+ def get_video_batch(self):
516
+ try:
517
+ if self.sp_size > 1:
518
+ while True:
519
+ batch = next(self.video_iterator)
520
+ shape = batch[0].shape # [B, C, T, H, W]
521
+ if (shape[-1]/2 * shape[-2]/2 * shape[-3] % self.sp_size == 0):
522
+ break
523
+ else:
524
+ logging.warning(f"skipping one sample due to the shape {shape} and SP {self.sp_size} mismatching")
525
+ else:
526
+ batch = next(self.video_iterator)
527
+
528
+ return batch
529
+ except StopIteration:
530
+ logging.info(f"Video dataset start new epoch")
531
+ self.video_iterator = iter(self.video_dataloader)
532
+ return next(self.video_iterator)
533
+
534
+
535
+ def __iter__(self):
536
+ return self
537
+
538
+ def __next__(self):
539
+ if self.video_iterator is None:
540
+ return self.get_image_batch()
541
+ if self.image_iterator is None:
542
+ return self.get_video_batch()
diffusers_lite/utils/diffusion_utils.py ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import torch
3
+ import torch.amp as amp
4
+ import torch.nn.functional as F
5
+ from einops import rearrange
6
+ from safetensors.torch import load_file
7
+
8
+
9
+ # tensor
10
+ def expand_tensor_dims(tensor, ndim):
11
+ while len(tensor.shape) < ndim:
12
+ tensor = tensor.unsqueeze(-1)
13
+ return tensor
14
+
15
+
16
+ # vae
17
+ def vae_encode(vae, images, dtype=torch.bfloat16, vae_type="wanx"):
18
+ if vae_type in ["wanx"]:
19
+ images = batch2list(images)
20
+ latents = vae.encode(images)
21
+ latents = list2batch(latents)
22
+ elif vae_type in ["ltx"]:
23
+ with amp.autocast("cuda", dtype=dtype):
24
+ latents = vae.encode(images).latent_dist.sample()
25
+
26
+ latents_mean = vae.latents_mean
27
+ latents_std = vae.latents_std
28
+ scaling_factor = 1.0
29
+
30
+ latents_mean = latents_mean.view(1, -1, 1, 1, 1).to(latents.device, latents.dtype)
31
+ latents_std = latents_std.view(1, -1, 1, 1, 1).to(latents.device, latents.dtype)
32
+ latents = (latents - latents_mean) * scaling_factor / latents_std
33
+
34
+ return latents
35
+
36
+ def vae_decode(vae, latents, dtype=torch.bfloat16, vae_type="wanx"):
37
+ if vae_type in ["wanx"]:
38
+ latents = batch2list(latents)
39
+ images = vae.decode(latents)
40
+ images = list2batch(images)
41
+
42
+ elif vae_type in ["ltx"]:
43
+ latents_mean = vae.latents_mean.view(1, -1, 1, 1, 1).to(latents.device, latents.dtype)
44
+ latents_std = vae.latents_std.view(1, -1, 1, 1, 1).to(latents.device, latents.dtype)
45
+ scaling_factor = 1.0
46
+ latents = latents * latents_std / scaling_factor + latents_mean
47
+
48
+ with amp.autocast("cuda", dtype=dtype):
49
+ images = vae.decode(latents, return_dict=False)[0]
50
+
51
+ return images
52
+
53
+
54
+ def image_encode(
55
+ image_encoder,
56
+ image,
57
+ last_image=None,
58
+ image_encoder_type="wanx"
59
+ ):
60
+ if image_encoder_type in ["wanx"]:
61
+ if image.ndim == 5:
62
+ image = image[:,:,0]
63
+ image = rearrange(image, "b c h w -> c b h w")
64
+
65
+ if last_image is not None:
66
+ if last_image.ndim == 5:
67
+ last_image = last_image[:,:,0]
68
+
69
+ last_image = rearrange(last_image, "b c h w -> c b h w")
70
+ image_embeds = image_encoder.visual([image, last_image])
71
+ else:
72
+ image_embeds = image_encoder.visual([image])
73
+
74
+ return image_embeds
75
+
76
+
77
+ def pack_latents(latents, patch_size=1, patch_size_t=1):
78
+ # Unpacked latents of shape are [B, C, F, H, W] are patched into tokens of shape [B, C, F // p_t, p_t, H // p, p, W // p, p].
79
+ # The patch dimensions are then permuted and collapsed into the channel dimension of shape:
80
+ # [B, F // p_t * H // p * W // p, C * p_t * p * p] (an ndim=3 tensor).
81
+ # dim=0 is the batch size, dim=1 is the effective video sequence length, dim=2 is the effective number of input features
82
+ batch_size, num_channels, num_frames, height, width = latents.shape
83
+ post_patch_num_frames = num_frames // patch_size_t
84
+ post_patch_height = height // patch_size
85
+ post_patch_width = width // patch_size
86
+ latents = latents.reshape(
87
+ batch_size,
88
+ -1,
89
+ post_patch_num_frames,
90
+ patch_size_t,
91
+ post_patch_height,
92
+ patch_size,
93
+ post_patch_width,
94
+ patch_size,
95
+ )
96
+ latents = latents.permute(0, 2, 4, 6, 1, 3, 5, 7).flatten(4, 7).flatten(1, 3)
97
+ latents = latents.contiguous()
98
+ return latents
99
+
100
+
101
+ def unpack_latents(latents, num_frames, height, width, patch_size=1, patch_size_t=1):
102
+ # Packed latents of shape [B, S, D] (S is the effective video sequence length, D is the effective feature dimensions)
103
+ # are unpacked and reshaped into a video tensor of shape [B, C, F, H, W]. This is the inverse operation of
104
+ # what happens in the `_pack_latents` method.
105
+ batch_size = latents.size(0)
106
+ latents = latents.reshape(
107
+ batch_size, num_frames, height, width, -1, patch_size_t, patch_size, patch_size
108
+ )
109
+ latents = (
110
+ latents.permute(0, 4, 1, 5, 2, 6, 3, 7)
111
+ .flatten(6, 7)
112
+ .flatten(4, 5)
113
+ .flatten(2, 3)
114
+ )
115
+ latents = latents.contiguous()
116
+ return latents
117
+
118
+
119
+ # text encoder
120
+ def prompt2states(
121
+ prompt,
122
+ text_encoder,
123
+ device="cuda:0",
124
+ tokenizer=None,
125
+ max_length=128,
126
+ text_encoder_type="wanx",
127
+ ):
128
+ if isinstance(prompt, str):
129
+ prompt = [prompt]
130
+
131
+ if text_encoder_type in ["wanx"]:
132
+ text_states = text_encoder(prompt, device)[0]
133
+ text_states = text_states.unsqueeze(0)
134
+ return text_states
135
+ elif text_encoder_type in ["ltx"]:
136
+ text_inputs = tokenizer(
137
+ prompt,
138
+ padding="max_length",
139
+ max_length=max_length,
140
+ truncation=True,
141
+ add_special_tokens=True,
142
+ return_tensors="pt",
143
+ )
144
+ text_ids = text_inputs.input_ids.to(device)
145
+ text_mask = text_inputs.attention_mask
146
+ text_mask = text_mask.bool().to(device)
147
+ text_states = text_encoder(text_ids)[0]
148
+
149
+ return text_states, text_mask
150
+
151
+
152
+ def load_lora_for_pipeline(
153
+ pipeline,
154
+ lora_path,
155
+ LORA_PREFIX_TRANSFORMER="",
156
+ LORA_PREFIX_TEXT_ENCODER="",
157
+ alpha=1.0,
158
+ rank=0,
159
+ ):
160
+ # load LoRA weight from .safetensors
161
+ state_dict = load_file(lora_path, device=rank)
162
+
163
+ visited = []
164
+
165
+ # directly update weight in diffusers model
166
+ for key in state_dict:
167
+ # it is suggested to print out the key, it usually will be something like below
168
+ # "lora_te_text_model_encoder_layers_0_self_attn_k_proj.lora_down.weight"
169
+
170
+ # as we have set the alpha beforehand, so just skip
171
+ if "alpha" in key or key in visited:
172
+ continue
173
+
174
+ if "text" in key:
175
+ layer_infos = (
176
+ key.split(".")[0].split(LORA_PREFIX_TEXT_ENCODER + "_")[-1].split("_")
177
+ )
178
+ curr_layer = pipeline.text_encoder
179
+ else:
180
+ layer_infos = (
181
+ key.split(".")[0].split(LORA_PREFIX_TRANSFORMER + "_")[-1].split("_")
182
+ )
183
+ curr_layer = pipeline.transformer
184
+
185
+ # find the target layer
186
+ temp_name = layer_infos.pop(0)
187
+ while len(layer_infos) > -1:
188
+ try:
189
+ curr_layer = curr_layer.__getattr__(temp_name)
190
+ if len(layer_infos) > 0:
191
+ temp_name = layer_infos.pop(0)
192
+ elif len(layer_infos) == 0:
193
+ break
194
+ except Exception:
195
+ if len(temp_name) > 0:
196
+ temp_name += "_" + layer_infos.pop(0)
197
+ else:
198
+ temp_name = layer_infos.pop(0)
199
+
200
+ pair_keys = []
201
+ if "lora_down" in key:
202
+ pair_keys.append(key.replace("lora_down", "lora_up"))
203
+ pair_keys.append(key)
204
+ else:
205
+ pair_keys.append(key)
206
+ pair_keys.append(key.replace("lora_up", "lora_down"))
207
+
208
+ # update weight
209
+ if len(state_dict[pair_keys[0]].shape) == 4:
210
+ weight_up = state_dict[pair_keys[0]].squeeze(3).squeeze(2).to(torch.float32)
211
+ weight_down = (
212
+ state_dict[pair_keys[1]].squeeze(3).squeeze(2).to(torch.float32)
213
+ )
214
+ curr_layer.weight.data += alpha * torch.mm(
215
+ weight_up, weight_down
216
+ ).unsqueeze(2).unsqueeze(3)
217
+ else:
218
+ weight_up = state_dict[pair_keys[0]].to(torch.float32)
219
+ weight_down = state_dict[pair_keys[1]].to(torch.float32)
220
+ curr_layer.weight.data += alpha * torch.mm(weight_up, weight_down)
221
+
222
+ # update visited list
223
+ for item in pair_keys:
224
+ visited.append(item)
225
+ del state_dict
226
+
227
+ return pipeline
228
+
229
+
230
+ def load_lora_for_model(
231
+ model,
232
+ lora_path,
233
+ LORA_PREFIX_TRANSFORMER="",
234
+ LORA_PREFIX_TEXT_ENCODER="",
235
+ alpha=1.0,
236
+ rank=0,
237
+ ):
238
+ # load LoRA weight from .safetensors
239
+ state_dict = load_file(lora_path, device="cpu")
240
+
241
+ visited = []
242
+
243
+ # directly update weight in diffusers model
244
+ for key in state_dict:
245
+ # it is suggested to print out the key, it usually will be something like below
246
+ # "lora_te_text_model_encoder_layers_0_self_attn_k_proj.lora_down.weight"
247
+
248
+ # as we have set the alpha beforehand, so just skip
249
+ if "alpha" in key or key in visited:
250
+ continue
251
+
252
+ layer_infos = (
253
+ key.split(".")[0].split(LORA_PREFIX_TRANSFORMER + "_")[-1].split("_")
254
+ )
255
+ curr_layer = model
256
+
257
+ # find the target layer
258
+ temp_name = layer_infos.pop(0)
259
+ while len(layer_infos) > -1:
260
+ try:
261
+ curr_layer = curr_layer.__getattr__(temp_name)
262
+ if len(layer_infos) > 0:
263
+ temp_name = layer_infos.pop(0)
264
+ elif len(layer_infos) == 0:
265
+ break
266
+ except Exception:
267
+ if len(temp_name) > 0:
268
+ temp_name += "_" + layer_infos.pop(0)
269
+ else:
270
+ temp_name = layer_infos.pop(0)
271
+
272
+ pair_keys = []
273
+ if "lora_down" in key:
274
+ pair_keys.append(key.replace("lora_down", "lora_up"))
275
+ pair_keys.append(key)
276
+ else:
277
+ pair_keys.append(key)
278
+ pair_keys.append(key.replace("lora_up", "lora_down"))
279
+
280
+ # update weight
281
+ if len(state_dict[pair_keys[0]].shape) == 4:
282
+ weight_up = state_dict[pair_keys[0]].squeeze(3).squeeze(2).to(torch.float32)
283
+ weight_down = (
284
+ state_dict[pair_keys[1]].squeeze(3).squeeze(2).to(torch.float32)
285
+ )
286
+ curr_layer.weight.data += alpha * torch.mm(
287
+ weight_up, weight_down
288
+ ).unsqueeze(2).unsqueeze(3)
289
+ else:
290
+ weight_up = state_dict[pair_keys[0]].to(torch.float32)
291
+ weight_down = state_dict[pair_keys[1]].to(torch.float32)
292
+ curr_layer.weight.data += alpha * torch.mm(weight_up, weight_down)
293
+
294
+ # update visited list
295
+ for item in pair_keys:
296
+ visited.append(item)
297
+ del state_dict
298
+
299
+ return model
300
+
301
+
302
+ def load_lora_state_dict(lora_dir):
303
+ lora_path = os.path.join(lora_dir, 'pytorch_lora_transformers_weights.safetensors')
304
+ lora_weights = load_file(lora_path)
305
+ load_lora_weights = {}
306
+ for key in lora_weights:
307
+ load_lora_weights[key.replace('.weight','.default.weight')] = lora_weights[key]
308
+
309
+ return load_lora_weights
310
+
311
+
312
+ def transformer_zero_init(transformer):
313
+ for p in transformer.parameters():
314
+ if p.dim() > 1:
315
+ torch.nn.init.zeros_(p.data)
316
+ else:
317
+ torch.nn.init.normal_(p.data)
318
+
319
+ return transformer
320
+
321
+
322
+ def prepare_video_condition_wanx(
323
+ vae,
324
+ video,
325
+ mask_strategy=[0.4, 0.25, 0.3, 0.05],
326
+ ):
327
+ # Get mask strategy
328
+ mask_id = torch.multinomial(torch.tensor(mask_strategy), num_samples=1).item()
329
+ bsz, _, num_frames, height, width = video.shape
330
+ latents_height, latents_width = height // 8, width // 8
331
+
332
+ # Get video mask
333
+ if mask_id == 0:
334
+ mask = torch.cat([
335
+ torch.ones(bsz, 1, 1, height, width),
336
+ torch.zeros(bsz, 1, num_frames-1, height, width)
337
+ ], dim=2)
338
+
339
+ elif mask_id == 1:
340
+ mid_frame = (num_frames - 1) // 2 + 1
341
+ mask = torch.cat([
342
+ torch.ones(bsz, 1, mid_frame, height, width),
343
+ torch.zeros(bsz, 1, num_frames-mid_frame, height, width)
344
+ ], dim=2)
345
+
346
+ elif mask_id == 2:
347
+ mask = torch.cat([
348
+ torch.ones(bsz, 1, 1, height, width),
349
+ torch.zeros(bsz, 1, num_frames-2, height, width),
350
+ torch.ones(bsz, 1, 1, height, width)
351
+ ], dim=2)
352
+
353
+ elif mask_id == 3:
354
+ num_masked = torch.randint(1, num_frames, (bsz,)).item()
355
+ indices = torch.randperm(num_frames)[:num_masked].sort().values
356
+ mask = torch.zeros(bsz, 1, num_frames, height, width)
357
+ mask[:,:, indices] = 1
358
+
359
+ # Encode video mask
360
+ mask = mask.to(video.device, dtype=video.dtype)
361
+ mask_lat_size = torch.cat([
362
+ torch.repeat_interleave(mask[:,:,:1,:,:], dim=2, repeats=4),
363
+ mask[:,:,1:,:,:],
364
+ ], dim=2)
365
+ mask_lat_size = mask_lat_size[:,:,:,::8,::8]
366
+ mask_lat_size = mask_lat_size.view(bsz, -1, 4, latents_height, latents_width).transpose(1,2)
367
+
368
+ # Encode video condition
369
+ video_condition = video * mask
370
+ latents_condition = torch.cat([
371
+ mask_lat_size,
372
+ vae_encode(vae, video_condition, "wanx")
373
+ ], dim=1)
374
+
375
+ return latents_condition
376
+
377
+
378
+ def batch2list(batch):
379
+ return [item for item in batch]
380
+
381
+ def list2batch(list):
382
+ return torch.stack(list)
383
+
384
+
385
+ def stable_mse_loss(model_pred, target, weighting=None, threshold=50):
386
+ if weighting is None:
387
+ weighting = torch.ones_like(target)
388
+
389
+ diff = model_pred - target
390
+ mask = (diff.abs() <= threshold).float()
391
+ loss = F.mse_loss(model_pred, target, reduction="none")
392
+ masked_loss = weighting * mask * loss
393
+ masked_loss = masked_loss.mean()
394
+
395
+ return masked_loss
diffusers_lite/utils/distill_utils.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import torch
3
+ import torch.nn as nn
4
+ from .diffusion_utils import list2batch
5
+
6
+
7
+ def extract_into_tensor(a, t, x_shape):
8
+ b, *_ = t.shape
9
+ out = a.gather(-1, t)
10
+ return out.reshape(b, *((1, ) * (len(x_shape) - 1)))
11
+
12
+ def get_phase_endpoint(index, num_teacher_timesteps=32, multiphase=8):
13
+ interval = num_teacher_timesteps // multiphase
14
+ max_endpoint = num_teacher_timesteps - interval
15
+
16
+ if index >= max_endpoint:
17
+ return max_endpoint
18
+
19
+ else:
20
+ quotient = index // interval
21
+ return quotient * interval
22
+
23
+ class EulerSolver:
24
+ def __init__(self, sigmas, timesteps=1000, euler_timesteps=50):
25
+ # sigmas: 0.0 -> 1.0, length = 1001
26
+ self.num_timesteps = timesteps
27
+
28
+ step_ratio = timesteps / euler_timesteps
29
+ euler_timesteps = np.round(np.arange(timesteps, 0, -step_ratio)).astype(np.int64) - 1 # 999,...,0
30
+ self.euler_timesteps = euler_timesteps[::-1].copy() + 1 # 1,...,1000
31
+
32
+ self.sigmas = sigmas[self.euler_timesteps] # 0.001,...,1.0
33
+ self.sigmas_prev = np.asarray(
34
+ [sigmas[0]] + sigmas[self.euler_timesteps[:-1]].tolist() # 0.000,...,0.999
35
+ )
36
+ self.sigmas_all = sigmas.copy()
37
+
38
+ self.euler_timesteps = torch.from_numpy(self.euler_timesteps).long()
39
+ self.sigmas = torch.from_numpy(self.sigmas)
40
+ self.sigmas_prev = torch.from_numpy(self.sigmas_prev)
41
+ self.sigmas_all = torch.from_numpy(self.sigmas_all)
42
+
43
+
44
+ def to(self, device):
45
+ self.euler_timesteps = self.euler_timesteps.to(device)
46
+ self.sigmas = self.sigmas.to(device)
47
+ self.sigmas_prev = self.sigmas_prev.to(device)
48
+ self.sigmas_all = self.sigmas_all.to(device)
49
+ return self
50
+
51
+ def euler_step(self, sample, model_pred, timestep_index):
52
+ sigma = extract_into_tensor(self.sigmas, timestep_index, model_pred.shape)
53
+ sigma_prev = extract_into_tensor(self.sigmas_prev, timestep_index, model_pred.shape)
54
+ x_prev = sample + (sigma_prev - sigma) * model_pred
55
+ return x_prev
56
+
57
+ def euler_step_to_target(self, sample, model_pred, timestep_index, target_timestep_index):
58
+ sigma = extract_into_tensor(self.sigmas, timestep_index, model_pred.shape)
59
+ sigma_target = extract_into_tensor(self.sigmas_prev, target_timestep_index, model_pred.shape)
60
+
61
+ x_target = sample + (sigma_target - sigma) * model_pred
62
+ return x_target
63
+
64
+
65
+ class DiscriminatorHead(nn.Module):
66
+ def __init__(self, in_channels=1280, reduced_channels=512):
67
+ super(DiscriminatorHead, self).__init__()
68
+
69
+ # Reduce channels using 1x1 convolution
70
+ self.reduce_ch_conv = nn.Conv3d(in_channels, reduced_channels, kernel_size=(1, 1, 1))
71
+
72
+ # Main convolutional layers
73
+ self.conv_layers = nn.Sequential(
74
+ nn.Conv3d(reduced_channels, reduced_channels * 2, kernel_size=(3, 3, 3), stride=(1, 2, 2)),
75
+ nn.LeakyReLU(0.2),
76
+ nn.Conv3d(reduced_channels * 2, reduced_channels * 4, kernel_size=(3, 3, 3), stride=(1, 2, 2)),
77
+ nn.LeakyReLU(0.2),
78
+ nn.Conv3d(reduced_channels * 4, reduced_channels * 8, kernel_size=(3, 3, 3), stride=(1, 2, 2)),
79
+ nn.LeakyReLU(0.2)
80
+ )
81
+
82
+ # Global pooling
83
+ self.global_pool = nn.AdaptiveAvgPool3d((1, 1, 1))
84
+
85
+ # Fully connected layer
86
+ self.fc = nn.Linear(reduced_channels * 8, 1)
87
+
88
+ def forward(self, feature):
89
+ # Reduce channels
90
+ reduced_feature = self.reduce_ch_conv(feature)
91
+
92
+ # Apply main convolutional layers
93
+ x = self.conv_layers(reduced_feature)
94
+
95
+ # Global pooling
96
+ x = self.global_pool(x)
97
+
98
+ # Fully connected layer
99
+ x = x.view(x.size(0), -1)
100
+ out = self.fc(x)
101
+
102
+ return out
103
+
104
+
105
+ class Discriminator(nn.Module):
106
+
107
+ def __init__(
108
+ self,
109
+ num_h_per_head=1,
110
+ selected_layers=[20,30,40],
111
+ adapter_channel_dims=[1280],
112
+ ):
113
+ super().__init__()
114
+ if isinstance(adapter_channel_dims, int):
115
+ adapter_channel_dims = [adapter_channel_dims]
116
+
117
+ adapter_channel_dims = adapter_channel_dims * len(selected_layers)
118
+ self.num_h_per_head = num_h_per_head
119
+ self.head_num = len(adapter_channel_dims)
120
+ self.heads = nn.ModuleList([
121
+ nn.ModuleList([DiscriminatorHead(adapter_channel) for _ in range(self.num_h_per_head)])
122
+ for adapter_channel in adapter_channel_dims
123
+ ])
124
+
125
+ def forward(self, features):
126
+ outputs = []
127
+ assert len(features) == len(self.heads)
128
+ for i in range(0, len(features)):
129
+ for h in self.heads[i]:
130
+ if isinstance(features[i], list):
131
+ input_features = list2batch(features[i])
132
+ else:
133
+ input_features = features[i]
134
+ out = h(input_features)
135
+ outputs.append(out)
136
+ return outputs
diffusers_lite/utils/fsdp_utils.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ruff: noqa: E731
2
+ import functools
3
+ from functools import partial
4
+
5
+ import torch
6
+ from peft.utils.other import fsdp_auto_wrap_policy
7
+ from torch.distributed.algorithms._checkpoint.checkpoint_wrapper import (
8
+ CheckpointImpl,
9
+ apply_activation_checkpointing,
10
+ checkpoint_wrapper,
11
+ )
12
+ from torch.distributed.fsdp import MixedPrecision, ShardingStrategy
13
+ from torch.distributed.fsdp.wrap import transformer_auto_wrap_policy
14
+
15
+ from .load import get_no_split_modules
16
+ from torch.distributed.fsdp import BackwardPrefetch
17
+ non_reentrant_wrapper = partial(
18
+ checkpoint_wrapper,
19
+ checkpoint_impl=CheckpointImpl.NO_REENTRANT,
20
+ )
21
+
22
+
23
+ def apply_fsdp_checkpointing(model, no_split_modules, p=1):
24
+ # https://github.com/foundation-model-stack/fms-fsdp/blob/408c7516d69ea9b6bcd4c0f5efab26c0f64b3c2d/fms_fsdp/policies/ac_handler.py#L16
25
+ """apply activation checkpointing to model
26
+ returns None as model is updated directly
27
+ """
28
+ print("--> applying fdsp activation checkpointing...")
29
+ block_idx = 0
30
+ cut_off = 1 / 2
31
+ # when passing p as a fraction number (e.g. 1/3), it will be interpreted
32
+ # as a string in argv, thus we need eval("1/3") here for fractions.
33
+ p = eval(p) if isinstance(p, str) else p
34
+
35
+ def selective_checkpointing(submodule):
36
+ nonlocal block_idx
37
+ nonlocal cut_off
38
+
39
+ if isinstance(submodule, no_split_modules):
40
+ block_idx += 1
41
+ if block_idx * p >= cut_off:
42
+ cut_off += 1
43
+ return True
44
+ return False
45
+
46
+ apply_activation_checkpointing(
47
+ model,
48
+ checkpoint_wrapper_fn=non_reentrant_wrapper,
49
+ check_fn=selective_checkpointing,
50
+ )
51
+
52
+
53
+ def get_mixed_precision(master_weight_type="fp32"):
54
+ weight_type = torch.float32 if master_weight_type == "fp32" else torch.bfloat16
55
+ mixed_precision = MixedPrecision(
56
+ param_dtype=weight_type,
57
+ # Gradient communication precision.
58
+ reduce_dtype=weight_type,
59
+ # Buffer precision.
60
+ buffer_dtype=weight_type,
61
+ cast_forward_inputs=False,
62
+ )
63
+ return mixed_precision
64
+
65
+
66
+ def get_dit_fsdp_kwargs(
67
+ transformer,
68
+ sharding_strategy,
69
+ use_lora=False,
70
+ cpu_offload=False,
71
+ master_weight_type="fp32",
72
+ ):
73
+ no_split_modules = get_no_split_modules(transformer)
74
+ if use_lora:
75
+ auto_wrap_policy = fsdp_auto_wrap_policy
76
+ else:
77
+ auto_wrap_policy = functools.partial(
78
+ transformer_auto_wrap_policy,
79
+ transformer_layer_cls=no_split_modules,
80
+ )
81
+
82
+ # we use float32 for fsdp but autocast during training
83
+ mixed_precision = get_mixed_precision(master_weight_type)
84
+
85
+ # NOTE: if no modules are split, we use NO_SHARD
86
+ if sharding_strategy == "full":
87
+ sharding_strategy = ShardingStrategy.FULL_SHARD
88
+ elif sharding_strategy == "hybrid_full":
89
+ sharding_strategy = ShardingStrategy.HYBRID_SHARD
90
+ elif sharding_strategy == "none":
91
+ sharding_strategy = ShardingStrategy.NO_SHARD
92
+ auto_wrap_policy = None
93
+ elif sharding_strategy == "hybrid_zero2":
94
+ sharding_strategy = ShardingStrategy._HYBRID_SHARD_ZERO2
95
+ elif sharding_strategy == 'shard_grad_op':
96
+ sharding_strategy = ShardingStrategy.SHARD_GRAD_OP
97
+
98
+ device_id = torch.cuda.current_device()
99
+ cpu_offload = (
100
+ torch.distributed.fsdp.CPUOffload(offload_params=True) if cpu_offload else None
101
+ )
102
+ fsdp_kwargs = {
103
+ "auto_wrap_policy": auto_wrap_policy,
104
+ "mixed_precision": mixed_precision,
105
+ "sharding_strategy": sharding_strategy,
106
+ "device_id": device_id,
107
+ "limit_all_gathers": True,
108
+ "cpu_offload": cpu_offload,
109
+ }
110
+
111
+ # Add LoRA-specific settings when LoRA is enabled
112
+ if len(no_split_modules) != 0 and use_lora:
113
+ fsdp_kwargs.update(
114
+ {
115
+ "use_orig_params": False, # Required for LoRA memory savings
116
+ "sync_module_states": True,
117
+ }
118
+ )
119
+ elif len(no_split_modules) == 0 and use_lora:
120
+ fsdp_kwargs.update({"use_orig_params": True})
121
+
122
+ return fsdp_kwargs, no_split_modules
123
+
124
+
125
+ def get_discriminator_fsdp_kwargs(master_weight_type="fp32"):
126
+ auto_wrap_policy = None
127
+
128
+ # Use existing mixed precision settings
129
+ mixed_precision = get_mixed_precision(master_weight_type)
130
+ sharding_strategy = ShardingStrategy.NO_SHARD
131
+ device_id = torch.cuda.current_device()
132
+ fsdp_kwargs = {
133
+ "auto_wrap_policy": auto_wrap_policy,
134
+ "mixed_precision": mixed_precision,
135
+ "sharding_strategy": sharding_strategy,
136
+ "device_id": device_id,
137
+ "limit_all_gathers": True,
138
+ }
139
+
140
+ return fsdp_kwargs
141
+ def get_vae_fsdp_kwargs(master_weight_type="fp32", cpu_offload=False):
142
+ auto_wrap_policy = None
143
+
144
+ # Use existing mixed precision settings
145
+ mixed_precision = get_mixed_precision(master_weight_type)
146
+ # sharding_strategy = ShardingStrategy.SHARD_GRAD_OP
147
+ sharding_strategy = ShardingStrategy.FULL_SHARD # 而不是SHARD_GRAD_OP
148
+
149
+
150
+ # sharding_strategy = ShardingStrategy.NO_SHARD # 注释掉的备用策略
151
+ device_id = torch.cuda.current_device()
152
+ cpu_offload = (
153
+ torch.distributed.fsdp.CPUOffload(offload_params=True) if cpu_offload else None
154
+ )
155
+
156
+ fsdp_kwargs = {
157
+ "auto_wrap_policy": auto_wrap_policy,
158
+ "mixed_precision": mixed_precision,
159
+ "sharding_strategy": sharding_strategy,
160
+ "device_id": device_id,
161
+ "limit_all_gathers": True,
162
+ "cpu_offload": cpu_offload, # 添加cpu_offload参数
163
+ "limit_all_gathers": True,
164
+ "use_orig_params": True, # 保持原始参数结构
165
+ # "backward_prefetch": BackwardPrefetch.BACKWARD_PRE,
166
+ }
167
+
168
+ return fsdp_kwargs
diffusers_lite/utils/load.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from peft import PeftModel
3
+ from diffusers_lite.wan.modules.model import WanModel, WanAttentionBlock
4
+
5
+
6
+ def get_no_split_modules(transformer):
7
+ while isinstance(transformer, PeftModel):
8
+ transformer = transformer.base_model.model
9
+ if isinstance(transformer, WanModel):
10
+ return (WanAttentionBlock, )
11
+ else:
12
+ raise ValueError(f"Unsupported transformer type: {type(transformer)}")
diffusers_lite/utils/model_utils.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import json
3
+ import os
4
+
5
+ import torch
6
+ from peft import get_peft_model_state_dict
7
+ from safetensors.torch import save_file, load_file
8
+ from tqdm import tqdm
9
+ from torch.distributed.fsdp import StateDictType
10
+ from torch.distributed.fsdp import ShardingStrategy
11
+ from torch.distributed.fsdp import FullOptimStateDictConfig, FullStateDictConfig
12
+ from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
13
+
14
+ from .torch_utils import set_logging
15
+
16
+
17
+ def get_kohya_state_dict(lora_layers, prefix="lora", dtype=torch.float32):
18
+ kohya_ss_state_dict = {}
19
+ for peft_key, weight in lora_layers.items():
20
+ kohya_key = peft_key.replace("base_model.model", prefix)
21
+ kohya_key = kohya_key.replace("lora_A", "lora_down")
22
+ kohya_key = kohya_key.replace("lora_B", "lora_up")
23
+ kohya_key = kohya_key.replace(".", "_", kohya_key.count(".") - 2)
24
+ kohya_ss_state_dict[kohya_key] = weight.to(dtype)
25
+
26
+ return kohya_ss_state_dict
27
+
28
+
29
+ def get_diffusers_state_dict(lora_layers, dtype=torch.float32):
30
+ diffusers_ss_state_dict = {}
31
+ for peft_key, weight in lora_layers.items():
32
+ diffusers_key = peft_key.replace("base_model.model", "diffusion_model")
33
+ diffusers_ss_state_dict[diffusers_key] = weight.to(dtype)
34
+
35
+ return diffusers_ss_state_dict
36
+
37
+
38
+ def save_lora_checkpoint(transformer, rank, output_dir, step, ema=False):
39
+ with FSDP.state_dict_type(
40
+ transformer,
41
+ StateDictType.FULL_STATE_DICT,
42
+ FullStateDictConfig(offload_to_cpu=True, rank0_only=True),
43
+ ):
44
+ full_state_dict = transformer.state_dict()
45
+
46
+ if rank <= 0:
47
+ if ema:
48
+ save_dir = os.path.join(output_dir, f"checkpoint-{step}-ema")
49
+ else:
50
+ save_dir = os.path.join(output_dir, f"checkpoint-{step}")
51
+ os.makedirs(save_dir, exist_ok=True)
52
+
53
+ # save lora weight
54
+ transformer_lora_layers = get_peft_model_state_dict(
55
+ model=transformer, state_dict=full_state_dict
56
+ )
57
+ kohya_ss_state_dict = get_kohya_state_dict(lora_layers=transformer_lora_layers)
58
+ diffusers_ss_state_dict = get_diffusers_state_dict(
59
+ lora_layers=transformer_lora_layers
60
+ )
61
+ save_transformer_name = "pytorch_lora_transformers_weights.safetensors"
62
+ save_kohya_name = "pytorch_lora_kohya_weights.safetensors"
63
+ save_diffusers_name = "pytorch_lora_diffusers_weights.safetensors"
64
+
65
+ save_file(transformer_lora_layers, os.path.join(save_dir, save_transformer_name))
66
+ save_file(kohya_ss_state_dict, os.path.join(save_dir, save_kohya_name))
67
+ save_file(diffusers_ss_state_dict, os.path.join(save_dir, save_diffusers_name))
68
+
69
+
70
+ def save_checkpoint(transformer, rank, output_dir, step, ema=False):
71
+ with FSDP.state_dict_type(
72
+ transformer,
73
+ StateDictType.FULL_STATE_DICT,
74
+ FullStateDictConfig(offload_to_cpu=True, rank0_only=True),
75
+ ):
76
+ cpu_state = transformer.state_dict()
77
+
78
+ if rank <= 0:
79
+ if ema:
80
+ save_dir = os.path.join(output_dir, f"checkpoint-{step}-ema")
81
+ else:
82
+ save_dir = os.path.join(output_dir, f"checkpoint-{step}")
83
+ os.makedirs(save_dir, exist_ok=True)
84
+
85
+ max_bytes = 5 * 1024 ** 3 # 5GB
86
+ total_bytes = sum(v.numel() * v.element_size() for v in cpu_state.values())
87
+
88
+ if total_bytes <= max_bytes:
89
+ save_name = "diffusion_pytorch_model.safetensors"
90
+ save_file(cpu_state, os.path.join(save_dir, save_name))
91
+ else:
92
+ shard, shards, current_size = {}, [], 0
93
+ for k, v in sorted(cpu_state.items()):
94
+ tensor_size = v.numel() * v.element_size()
95
+ if current_size + tensor_size > max_bytes and shard:
96
+ shards.append(shard)
97
+ shard, current_size = {}, 0
98
+
99
+ shard[k], current_size = v, current_size + tensor_size
100
+ if shard:
101
+ shards.append(shard)
102
+
103
+ index_data = {
104
+ "metadata": {
105
+ "total_size": total_bytes,
106
+ },
107
+ "weight_map": {}
108
+ }
109
+
110
+ for i, shard in enumerate(shards, start=1):
111
+ save_name = f"diffusion_pytorch_model-{i:05}-of-{len(shards):05}.safetensors"
112
+ save_file(shard, os.path.join(save_dir, save_name))
113
+ for key in shard.keys():
114
+ index_data["weight_map"][key] = save_name
115
+
116
+ with open(os.path.join(save_dir, "diffusion_pytorch_model.safetensors.index.json"), "w") as f:
117
+ json.dump(index_data, f, indent=2)
118
+
119
+ config_dict = dict(transformer.config)
120
+ if "dtype" in config_dict:
121
+ del config_dict["dtype"] # TODO
122
+ config_path = os.path.join(save_dir, "config.json")
123
+ # save dict as json
124
+ with open(config_path, "w") as f:
125
+ json.dump(config_dict, f, indent=4)
126
+
127
+ def load_state_dict(model_dir, postfix=".safetensors"):
128
+ chunk_path_list = [os.path.join(model_dir, name) for name in os.listdir(model_dir) if name.endswith(postfix)]
129
+ chunk_length = len(chunk_path_list)
130
+
131
+ state_dict = {}
132
+ for chunk_path in tqdm(chunk_path_list, total=chunk_length):
133
+ if postfix == ".safetensors":
134
+ chunk_state_dict = load_file(chunk_path, device="cpu")
135
+ else:
136
+ chunk_state_dict = torch.load(chunk_path, map_location="cpu")
137
+ if "module" in chunk_state_dict.keys():
138
+ chunk_state_dict = chunk_state_dict["module"]
139
+ state_dict.update(chunk_state_dict)
140
+
141
+ return state_dict
142
+
143
+
144
+ def print_parameters_information(model, name="Model name", rank=0):
145
+
146
+ def format_params(params):
147
+ if params < 1e6:
148
+ return f"{params} (less than 1M)"
149
+ elif params < 1e9:
150
+ return f"{params / 1e6:.2f}M"
151
+ else:
152
+ return f"{params / 1e9:.2f}B"
153
+
154
+ if model is None:
155
+ logging.info(f"name {name} is none objects.")
156
+ return
157
+
158
+ trainable_params = 0
159
+ all_param = 0
160
+ for _, param in model.named_parameters():
161
+ all_param += param.numel()
162
+ if param.requires_grad:
163
+ trainable_params += param.numel()
164
+
165
+ param = next(model.parameters())
166
+ logging.info(
167
+ f"name [{name}] trainable params: {format_params(trainable_params)} || all params: {format_params(all_param)} || trainable%: {100 * trainable_params / all_param:.2f} || device: {param.device}, dtype: {param.dtype}."
168
+ )
169
+ logging.info(f"name [{name}] device: {param.device} || dtype: {param.dtype}.")
170
+
171
+ @torch.no_grad
172
+ def update_ema_model(transformer, ema_transformer, ema_decay):
173
+ for p_averaged, p_model in zip(ema_transformer.parameters(), transformer.parameters()):
174
+ if p_model.requires_grad:
175
+ p_averaged.data.mul_(ema_decay).add_(p_model.data, alpha=1 - ema_decay)
diffusers_lite/utils/network.py ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.optim as optim
5
+ from sklearn.model_selection import train_test_split
6
+ from diffusers.models.normalization import FP32LayerNorm
7
+
8
+ class QueryAttention(nn.Module):
9
+ """
10
+ Query-based attention pooling module using PyTorch's built-in MultiheadAttention.
11
+ Uses learnable query vectors to attend to sequence features.
12
+ """
13
+ def __init__(self, feature_dim, num_queries=1, num_heads=8, dropout=0.1, layer_norm=False, return_type=None, product_text=False, text_dim=768):
14
+ super(QueryAttention, self).__init__()
15
+ self.feature_dim = feature_dim
16
+ self.num_queries = num_queries
17
+ self.num_heads = num_heads
18
+ self.layer_norm = layer_norm
19
+ self.return_type = return_type
20
+ self.product_text = product_text
21
+
22
+ # Use PyTorch's built-in MultiheadAttention
23
+ self.multihead_attn = nn.MultiheadAttention(
24
+ embed_dim=feature_dim,
25
+ num_heads=num_heads,
26
+ dropout=dropout,
27
+ batch_first=True # Use batch_first=True for easier handling
28
+ )
29
+ # Learnable query vectors
30
+ self.queries = nn.Parameter(torch.randn(num_queries, feature_dim))
31
+ # Initialize query parameters
32
+ nn.init.xavier_uniform_(self.queries)
33
+
34
+ if self.layer_norm:
35
+ self.norm = FP32LayerNorm(feature_dim, eps=1e-6, elementwise_affine=False)
36
+
37
+ if self.product_text:
38
+ self.text_proj = nn.Linear(text_dim, feature_dim)
39
+ nn.init.xavier_uniform_(self.text_proj.weight)
40
+ if self.text_proj.bias is not None:
41
+ nn.init.zeros_(self.text_proj.bias)
42
+
43
+
44
+ def forward(self, x, e = None, text = None):
45
+ """
46
+ Args:
47
+ x: Input tensor of shape [batch_size, seq_len, feature_dim] or [batch_size, feature_dim]
48
+ Returns:
49
+ Pooled features of shape [batch_size, feature_dim]
50
+ """
51
+
52
+ if self.layer_norm:
53
+ x = self.norm(x)
54
+
55
+ batch_size = x.shape[0]
56
+ original_shape = x.shape
57
+
58
+ # Handle different input shapes
59
+ if len(x.shape) == 2: # [batch_size, feature_dim]
60
+ # Add sequence dimension
61
+ x = x.unsqueeze(1) # [batch_size, 1, feature_dim]
62
+ seq_len = 1
63
+ elif len(x.shape) == 3: # [batch_size, seq_len, feature_dim]
64
+ seq_len = x.shape[1]
65
+ elif len(x.shape) == 4: # [sp_size, batch_size, seq_len, feature_dim]
66
+ # Handle sequence parallel case
67
+ sp_size, batch_size, seq_len, feature_dim = x.shape
68
+ x = x.view(sp_size * batch_size, seq_len, feature_dim)
69
+ batch_size = sp_size * batch_size
70
+ else:
71
+ raise ValueError(f"Unsupported input shape: {x.shape}")
72
+
73
+ # Expand queries to batch size
74
+ queries = self.queries.unsqueeze(0).expand(batch_size, -1, -1) # [batch_size, num_queries, feature_dim]
75
+ if e is not None:
76
+ queries = queries + e.unsqueeze(0).expand(batch_size, -1, -1)
77
+ # Use PyTorch's MultiheadAttention
78
+ # query: [batch_size, num_queries, feature_dim]
79
+ # key, value: [batch_size, seq_len, feature_dim]
80
+ attended, attention_weights = self.multihead_attn(
81
+ query=queries,
82
+ key=x,
83
+ value=x,
84
+ need_weights=False # We don't need attention weights for pooling
85
+ )
86
+
87
+ # attended: [batch_size, num_queries, feature_dim]
88
+
89
+ # If multiple queries, average them
90
+ if self.num_queries > 1:
91
+ output = attended.mean(dim=1) # [batch_size, feature_dim]
92
+ else:
93
+ output = attended.squeeze(1) # [batch_size, feature_dim]
94
+
95
+ # Handle sequence parallel case
96
+ if len(original_shape) == 4:
97
+ output = output.view(sp_size, batch_size // sp_size, -1)
98
+ output = output.mean(dim=0) # Average across SP devices
99
+
100
+ if self.layer_norm:
101
+ output = self.norm(output)
102
+
103
+ if self.return_type == 'query':
104
+ output = output + queries
105
+
106
+ if self.product_text and text is not None:
107
+ output_product_text = torch.mul(self.text_proj(text), output)
108
+ return output_product_text
109
+ else:
110
+ return output
111
+
112
+ class MLP(nn.Module):
113
+ def __init__(self, input_dim):
114
+ super(MLP, self).__init__()
115
+ self.fc1 = nn.Linear(input_dim, 1024) # First hidden layer
116
+ self.fc2 = nn.Linear(1024, 512) # Second hidden layer
117
+ self.fc3 = nn.Linear(512, 1) # Output layer (binary classification)
118
+
119
+ # 初始化权重,避免梯度消失
120
+ self._init_weights()
121
+
122
+ def _init_weights(self):
123
+ for m in self.modules():
124
+ if isinstance(m, nn.Linear):
125
+ # 使用Xavier初始化
126
+ nn.init.xavier_uniform_(m.weight)
127
+ if m.bias is not None:
128
+ nn.init.zeros_(m.bias)
129
+
130
+ def forward(self, x):
131
+ x = torch.relu(self.fc1(x))
132
+ x = torch.relu(self.fc2(x))
133
+ x = self.fc3(x) # 注意:这里不应用sigmoid,因为forward_siamese会处理
134
+ return x
135
+
136
+ class MultiHead(nn.Module):
137
+ def __init__(self, input_dim, num_heads = 3):
138
+ super().__init__()
139
+ self.num_heads = num_heads
140
+ self.mlps = torch.nn.ModuleList(
141
+ [MLP(input_dim) for _ in range(num_heads)]
142
+ )
143
+
144
+ def forward_mlp(self, head_idx, x):
145
+ return torch.sigmoid(self.mlps[head_idx](x))
146
+
147
+ def forward(self, x):
148
+ out = [self.forward_mlp(h, x) for h in range(self.num_heads)]
149
+ return torch.stack(out)
150
+
151
+ def forward_mlp(model, input):
152
+ return torch.sigmoid(model(input))
153
+
154
+ def forward_siamese(model, input1, input2):
155
+ # Pass both inputs through the same model (weight sharing)
156
+ reward1 = model(input1)
157
+ reward2 = model(input2)
158
+ # Compute the difference between the two embeddings
159
+ diff = reward1 - reward2
160
+
161
+ # Use this difference for binary prediction (preference/ranking)
162
+ return torch.sigmoid(diff)
163
+
164
+ def train_model(model, device, model_mode, X_train, y_train, X_test, y_test, epochs=3, lr=0.001, batch_size = 512, verbose=False, ealry_stopping_patience=3):
165
+ model = model.to(device) # Move model to GPU
166
+ criterion = nn.BCELoss() # Binary cross-entropy loss with logits for numerical stability
167
+ optimizer = optim.Adam(model.parameters(), lr=lr)
168
+ batch_size = min(batch_size, X_train.shape[0])
169
+ val_losses = []
170
+ for epoch in range(epochs):
171
+ for n_batch in range(0, X_train.shape[0], batch_size):
172
+
173
+ # randomly selecte batch
174
+ batch_idx = torch.randperm(X_train.shape[0])[:batch_size]
175
+ X_batch = X_train[batch_idx]
176
+ y_batch = y_train[batch_idx]
177
+
178
+ model.train()
179
+ optimizer.zero_grad()
180
+
181
+ # Forward pass
182
+ if model_mode == 'clf':
183
+ outputs = forward_mlp(model, X_batch)
184
+ elif model_mode == 'siamese':
185
+ outputs = forward_siamese(model, X_batch[:, 0], X_batch[:, 1])
186
+ loss = criterion(outputs, y_batch)
187
+
188
+ # Backward pass and optimization
189
+ loss.backward()
190
+ optimizer.step()
191
+
192
+ # Evaluate on validation set
193
+ model.eval()
194
+ with torch.no_grad():
195
+ if model_mode == 'clf':
196
+ val_outputs = forward_mlp(model, X_test)
197
+ elif model_mode == 'siamese':
198
+ val_outputs = forward_siamese(model, X_test[:, 0], X_test[:, 1])
199
+ # early stopping?
200
+ val_loss = criterion(val_outputs, y_test)
201
+ val_losses.append(val_loss.cpu().detach().item())
202
+ if len(val_losses) > ealry_stopping_patience:
203
+ if all(val_losses[-1] > x for x in val_losses[-(ealry_stopping_patience+1):-1]):
204
+ if verbose:
205
+ print(f"Early stopping at epoch {epoch+1}")
206
+ break
207
+ if verbose:
208
+ print(f"Epoch {epoch+1}/{epochs}, Loss: {loss.cpu().detach().item()}, Val Loss: {val_loss.cpu().detach().item()}")
209
+ # accuracy
210
+ val_outputs = val_outputs.cpu().detach().numpy()
211
+ val_pred = (val_outputs > 0.5).astype(int)
212
+ accuracy = (val_pred == y_test.cpu().detach().numpy()).mean()
213
+ if verbose:
214
+ print(f"Accuracy: {accuracy}")
215
+
216
+ def save_model(model, path):
217
+ torch.save(model.state_dict(), path)
diffusers_lite/utils/parallel_states.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import torch
3
+ import torch.distributed as dist
4
+ import os
5
+ import time
6
+ import random
7
+ import functools
8
+ from typing import List, Optional, Tuple, Union
9
+
10
+ class COMM_INFO:
11
+
12
+ def __init__(self):
13
+ self.group = None
14
+ self.sp_size = 1
15
+ self.global_rank = 0
16
+ self.rank_within_group = 0
17
+ self.group_id = 0
18
+
19
+ # the group info for teacher-student parallel
20
+ self.ts_group_size = 1
21
+ self.ts_group = None # for fsdp data parallel communication
22
+ self.ts_group_id = 0
23
+
24
+ # the group for teacher-student unit union
25
+ self.ts_unit_size = 1
26
+ self.ts_unit_group = None
27
+ self.rank_within_ts_unit_group = 0
28
+ self.ts_unit_group_id = 0
29
+
30
+
31
+ nccl_info = COMM_INFO()
32
+ _SEQUENCE_PARALLEL_STATE = False
33
+ _TEACHER_STUDENT_PARALLEL_STATE = False
34
+
35
+ def initialize_sequence_parallel_state(sequence_parallel_size):
36
+ global _SEQUENCE_PARALLEL_STATE
37
+ if sequence_parallel_size > 1:
38
+ _SEQUENCE_PARALLEL_STATE = True
39
+ initialize_sequence_parallel_group(sequence_parallel_size)
40
+ else:
41
+ nccl_info.sp_size = 1
42
+ nccl_info.global_rank = int(os.getenv("RANK", "0"))
43
+ nccl_info.rank_within_group = 0
44
+ nccl_info.group_id = int(os.getenv("RANK", "0"))
45
+
46
+
47
+ def set_sequence_parallel_state(state):
48
+ global _SEQUENCE_PARALLEL_STATE
49
+ _SEQUENCE_PARALLEL_STATE = state
50
+
51
+
52
+ def get_sequence_parallel_state():
53
+ return _SEQUENCE_PARALLEL_STATE
54
+
55
+
56
+ def initialize_sequence_parallel_group(sequence_parallel_size):
57
+ """Initialize the sequence parallel group."""
58
+ rank = int(os.getenv("RANK", "0"))
59
+ world_size = int(os.getenv("WORLD_SIZE", "1"))
60
+ assert (
61
+ world_size % sequence_parallel_size == 0
62
+ ), "world_size must be divisible by sequence_parallel_size, but got world_size: {}, sequence_parallel_size: {}".format(
63
+ world_size, sequence_parallel_size
64
+ )
65
+ nccl_info.sp_size = sequence_parallel_size #序列并行size
66
+ nccl_info.global_rank = rank #全局rank
67
+ num_sequence_parallel_groups: int = world_size // sequence_parallel_size
68
+ for i in range(num_sequence_parallel_groups):
69
+ ranks = range(i * sequence_parallel_size, (i + 1) * sequence_parallel_size)
70
+ group = dist.new_group(ranks)
71
+ if rank in ranks:
72
+ nccl_info.group = group
73
+ nccl_info.rank_within_group = rank - i * sequence_parallel_size #rank在序列并行group中的rank
74
+ nccl_info.group_id = i #sequence parallel group id
75
+
76
+ def get_sequence_parallel_state():
77
+ return _SEQUENCE_PARALLEL_STATE
78
+
79
+
80
+ def set_teacher_student_parallel_state(state):
81
+ global _TEACHER_STUDENT_PARALLEL_STATE
82
+ _TEACHER_STUDENT_PARALLEL_STATE = state
83
+
84
+
85
+ def get_teacher_student_parallel_state():
86
+ return _TEACHER_STUDENT_PARALLEL_STATE
87
+
88
+
89
+
90
+ def initialize_teacher_student_parallel_state(sequence_parallel_size):
91
+ global _TEACHER_STUDENT_PARALLEL_STATE
92
+ """Initialize the teacher-student parallel group."""
93
+ rank = int(os.getenv("RANK", "0"))
94
+ world_size = int(os.getenv("WORLD_SIZE", "1"))
95
+ assert (
96
+ world_size % (2 * sequence_parallel_size) == 0
97
+ ), "world_size must be divisible by 2 * sequence_parallel_size, but got world_size: {}, sequence_parallel_size: {}".format(
98
+ world_size, sequence_parallel_size
99
+ )
100
+ _TEACHER_STUDENT_PARALLEL_STATE = True
101
+ nccl_info.global_rank = rank
102
+ # init ts_unit_group and assign info
103
+ # teacher and student must have the same sp size temporally!
104
+ # In the unit, front is student, back is teacher
105
+ nccl_info.ts_unit_size = sequence_parallel_size * 2
106
+ num_teacher_student_union_groups = world_size // sequence_parallel_size // 2
107
+ for j in range(num_teacher_student_union_groups):
108
+ ts_unit_ranks = range(j * sequence_parallel_size * 2, (j+1) * sequence_parallel_size * 2)
109
+ ts_unit_group = dist.new_group(ts_unit_ranks)
110
+ if rank in ts_unit_ranks:
111
+ nccl_info.ts_unit_group = ts_unit_group
112
+ nccl_info.ts_unit_group_id = j
113
+ nccl_info.rank_within_ts_unit_group = rank - j * sequence_parallel_size * 2
114
+
115
+ # init ts_goup and assign info
116
+ nccl_info.ts_group_size = world_size // 2
117
+ for i in range(2):
118
+ ranks = []
119
+ for j in range(num_teacher_student_union_groups):
120
+ ranks += range((j*2+i) * sequence_parallel_size, (j*2+i+1) * sequence_parallel_size)
121
+
122
+ ts_group = dist.new_group(ranks)
123
+ if rank in ranks:
124
+ nccl_info.ts_group = ts_group
125
+ nccl_info.ts_group_id = i
126
+
127
+ def destroy_sequence_parallel_group():
128
+ """Destroy the sequence parallel group."""
129
+ dist.destroy_process_group()
130
+
131
+ def is_teacher_group():
132
+ if _TEACHER_STUDENT_PARALLEL_STATE:
133
+ return nccl_info.group_id % 2 == 1
134
+ else:
135
+ return True
136
+
137
+ def is_student_group():
138
+ if _TEACHER_STUDENT_PARALLEL_STATE:
139
+ return nccl_info.group_id % 2 == 0
140
+ else:
141
+ return True
diffusers_lite/utils/torch_utils.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gc
2
+ import random
3
+ import logging
4
+ import os
5
+ import sys
6
+ import numpy as np
7
+ import torch
8
+ import torch.distributed as dist
9
+ # from loguru import logger
10
+
11
+ def init_dist():
12
+ """Initializes distributed environment."""
13
+ rank = int(os.environ["RANK"])
14
+ num_gpus = torch.cuda.device_count()
15
+ local_rank = rank % num_gpus
16
+ torch.cuda.set_device(local_rank)
17
+ dist.init_process_group(backend="nccl")
18
+ return local_rank
19
+
20
+
21
+ def set_manual_seed(seed):
22
+ random.seed(seed)
23
+ np.random.seed(seed)
24
+ torch.manual_seed(seed)
25
+ torch.cuda.manual_seed_all(seed)
26
+
27
+
28
+ def make_contiguous(x):
29
+ if isinstance(x, torch.Tensor):
30
+ return x.contiguous()
31
+ elif isinstance(x, dict):
32
+ return {k: make_contiguous(v) for k, v in x.items()}
33
+ else:
34
+ return x
35
+
36
+
37
+ class set_worker_seed_builder():
38
+ def __init__(self, global_rank):
39
+ self.global_rank = global_rank
40
+
41
+ def __call__(self, worker_id):
42
+ set_manual_seed(torch.initial_seed() % (2 ** 32 - 1))
43
+
44
+ def free_memory():
45
+ if torch.cuda.is_available():
46
+ gc.collect()
47
+ torch.cuda.empty_cache()
48
+ torch.cuda.ipc_collect()
49
+
50
+
51
+ def set_logging(local_rank):
52
+ if local_rank == 0:
53
+ # set format
54
+ logging.basicConfig(
55
+ level=logging.INFO,
56
+ format="[%(asctime)s] %(levelname)s: %(message)s",
57
+ handlers=[logging.StreamHandler(stream=sys.stdout)])
58
+ else:
59
+ logging.basicConfig(level=logging.ERROR)
diffusers_lite/wan/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from . import configs, distributed, modules
2
+ from .image2video import WanI2V
3
+ from .text2video import WanT2V
4
+ from .first_last_frame2video import WanFLF2V
diffusers_lite/wan/configs/__init__.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved.
2
+ import copy
3
+ import os
4
+
5
+ os.environ['TOKENIZERS_PARALLELISM'] = 'false'
6
+
7
+ from .wan_i2v_14B import i2v_14B
8
+ from .wan_t2v_1_3B import t2v_1_3B
9
+ from .wan_t2v_14B import t2v_14B
10
+
11
+ # the config of t2i_14B is the same as t2v_14B
12
+ t2i_14B = copy.deepcopy(t2v_14B)
13
+ t2i_14B.__name__ = 'Config: Wan T2I 14B'
14
+
15
+ # the config of flf2v_14B is the same as i2v_14B
16
+ flf2v_14B = copy.deepcopy(i2v_14B)
17
+ flf2v_14B.__name__ = 'Config: Wan FLF2V 14B'
18
+ flf2v_14B.sample_neg_prompt = "镜头切换," + flf2v_14B.sample_neg_prompt
19
+
20
+ WAN_CONFIGS = {
21
+ 't2v-14B': t2v_14B,
22
+ 't2v-1.3B': t2v_1_3B,
23
+ 'i2v-14B': i2v_14B,
24
+ 't2i-14B': t2i_14B,
25
+ 'flf2v-14B': flf2v_14B
26
+ }
27
+
28
+ SIZE_CONFIGS = {
29
+ '720*1280': (720, 1280),
30
+ '1280*720': (1280, 720),
31
+ '480*832': (480, 832),
32
+ '832*480': (832, 480),
33
+ '1024*1024': (1024, 1024),
34
+ }
35
+
36
+ MAX_AREA_CONFIGS = {
37
+ '720*1280': 720 * 1280,
38
+ '1280*720': 1280 * 720,
39
+ '480*832': 480 * 832,
40
+ '832*480': 832 * 480,
41
+ }
42
+
43
+ SUPPORTED_SIZES = {
44
+ 't2v-14B': ('720*1280', '1280*720', '480*832', '832*480'),
45
+ 't2v-1.3B': ('480*832', '832*480'),
46
+ 'i2v-14B': ('720*1280', '1280*720', '480*832', '832*480'),
47
+ 'flf2v-14B': ('720*1280', '1280*720', '480*832', '832*480'),
48
+ 't2i-14B': tuple(SIZE_CONFIGS.keys()),
49
+ }
diffusers_lite/wan/configs/shared_config.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved.
2
+ import torch
3
+ from easydict import EasyDict
4
+
5
+ #------------------------ Wan shared config ------------------------#
6
+ wan_shared_cfg = EasyDict()
7
+
8
+ # t5
9
+ wan_shared_cfg.t5_model = 'umt5_xxl'
10
+ wan_shared_cfg.t5_dtype = torch.bfloat16
11
+ wan_shared_cfg.text_len = 512
12
+
13
+ # transformer
14
+ wan_shared_cfg.param_dtype = torch.bfloat16
15
+
16
+ # inference
17
+ wan_shared_cfg.num_train_timesteps = 1000
18
+ wan_shared_cfg.sample_fps = 16
19
+ wan_shared_cfg.sample_neg_prompt = '色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走'