Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +13 -0
- LICENSE.txt +62 -0
- README.md +295 -0
- README_CN.md +284 -0
- assets/I2V_exp.png +3 -0
- assets/T2V_exp.png +3 -0
- assets/efficiency.png +3 -0
- assets/logo.svg +72 -0
- assets/method.png +3 -0
- assets/teaser.jpg +3 -0
- assets/videos/more/109_seed_677347.jpg +3 -0
- assets/videos/more/14_seed_876367.jpg +3 -0
- assets/videos/more/artlist_video_60ca5873a5c21ff4e4785b1997239d87_seed_561368.jpg +0 -0
- assets/videos/more/real_1246_seed_277973.jpg +0 -0
- configs/infer_pavrm_i2v_720.yaml +100 -0
- configs/pre_480.yaml +22 -0
- configs/pre_720.yaml +22 -0
- configs/train_pavrm_bt_i2v_720.yaml +103 -0
- configs/train_pavrm_i2v_480.yaml +102 -0
- configs/train_pavrm_i2v_720.yaml +102 -0
- configs/train_pavrm_t2v_480.yaml +102 -0
- configs/train_pavrm_t2v_720.yaml +102 -0
- configs/train_prfl_i2v_480.yaml +95 -0
- configs/train_prfl_i2v_720.yaml +96 -0
- configs/train_prfl_t2v_480.yaml +95 -0
- configs/train_prfl_t2v_720.yaml +96 -0
- diffusers_lite.egg-info/PKG-INFO +26 -0
- diffusers_lite.egg-info/SOURCES.txt +7 -0
- diffusers_lite.egg-info/dependency_links.txt +1 -0
- diffusers_lite.egg-info/requires.txt +20 -0
- diffusers_lite.egg-info/top_level.txt +1 -0
- diffusers_lite/__init__.py +0 -0
- diffusers_lite/arguments.py +216 -0
- diffusers_lite/constants.py +9 -0
- diffusers_lite/datasets/image2video_dataset.py +448 -0
- diffusers_lite/schedulers/__init__.py +1 -0
- diffusers_lite/schedulers/scheduling_flow_match_discrete.py +275 -0
- diffusers_lite/utils/communication.py +691 -0
- diffusers_lite/utils/data_utils.py +542 -0
- diffusers_lite/utils/diffusion_utils.py +395 -0
- diffusers_lite/utils/distill_utils.py +136 -0
- diffusers_lite/utils/fsdp_utils.py +168 -0
- diffusers_lite/utils/load.py +12 -0
- diffusers_lite/utils/model_utils.py +175 -0
- diffusers_lite/utils/network.py +217 -0
- diffusers_lite/utils/parallel_states.py +141 -0
- diffusers_lite/utils/torch_utils.py +59 -0
- diffusers_lite/wan/__init__.py +4 -0
- diffusers_lite/wan/configs/__init__.py +49 -0
- 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>  
|
| 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>  
|
| 18 |
+
<a href="https://arxiv.org/pdf/2511.21541"><img src="https://img.shields.io/badge/ArXiv-2511.21541-red"></a>  
|
| 19 |
+
</div>
|
| 20 |
+
|
| 21 |
+
<br>
|
| 22 |
+
|
| 23 |
+

|
| 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 |
+

|
| 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 |
+

|
| 89 |
+
|
| 90 |
+
#### Image-to-Video Results
|
| 91 |
+

|
| 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>  
|
| 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>  
|
| 15 |
+
<a href="https://arxiv.org/pdf/2511.21541"><img src="https://img.shields.io/badge/ArXiv-2511.21541-red"></a>  
|
| 16 |
+
</div>
|
| 17 |
+
|
| 18 |
+
<br>
|
| 19 |
+
|
| 20 |
+

|
| 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 |
+

|
| 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 |
+

|
| 82 |
+
|
| 83 |
+
#### 图像生成视频结果
|
| 84 |
+

|
| 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
|
assets/T2V_exp.png
ADDED
|
Git LFS Details
|
assets/efficiency.png
ADDED
|
Git LFS Details
|
assets/logo.svg
ADDED
|
|
assets/method.png
ADDED
|
Git LFS Details
|
assets/teaser.jpg
ADDED
|
Git LFS Details
|
assets/videos/more/109_seed_677347.jpg
ADDED
|
Git LFS Details
|
assets/videos/more/14_seed_876367.jpg
ADDED
|
Git LFS Details
|
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压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走'
|