Instructions to use alibaba-pai/MiniMax-H3-Acc-LoRAs with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- VideoX Fun
How to use alibaba-pai/MiniMax-H3-Acc-LoRAs with VideoX Fun:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
Upload MiniMax-H3 acceleration LoRAs and examples
Browse filesUpload the MiniMax-H3 FL2VA and Ref2VA acceleration LoRAs, inference examples, documentation, assets, and generated comparison videos. Excludes __pycache__ and samples.
- .gitattributes +21 -0
- MiniMax-H3-FL2VA-Acc-8Step.safetensors +3 -0
- MiniMax-H3-Ref2VA-Acc-8Step.safetensors +3 -0
- README.md +123 -0
- asset/ref2va_test_5_1.jpg +0 -0
- asset/ref2va_test_5_2.jpg +3 -0
- asset/ref2va_test_5_3.jpg +3 -0
- asset/ref2va_test_5_4.jpg +3 -0
- minimax_h3_pdd.py +290 -0
- predict_ref2v.py +51 -0
- predict_t2v.py +60 -0
- results/minimax_h3_fl2va_acc_8step_v1_1.mp4 +3 -0
- results/minimax_h3_fl2va_acc_8step_v1_2.mp4 +3 -0
- results/minimax_h3_fl2va_acc_8step_v1_3.mp4 +3 -0
- results/minimax_h3_fl2va_baseline_1.mp4 +3 -0
- results/minimax_h3_fl2va_baseline_2.mp4 +3 -0
- results/minimax_h3_fl2va_baseline_3.mp4 +3 -0
- results/minimax_h3_fl2va_turbo_4step_v1.1_768p_1.mp4 +3 -0
- results/minimax_h3_fl2va_turbo_4step_v1.1_768p_2.mp4 +3 -0
- results/minimax_h3_fl2va_turbo_4step_v1.1_768p_3.mp4 +3 -0
- results/minimax_h3_ref2va_acc_8step_v1_1.mp4 +3 -0
- results/minimax_h3_ref2va_acc_8step_v1_2.mp4 +3 -0
- results/minimax_h3_ref2va_acc_8step_v1_3.mp4 +3 -0
- results/minimax_h3_ref2va_baseline_1.mp4 +3 -0
- results/minimax_h3_ref2va_baseline_2.mp4 +3 -0
- results/minimax_h3_ref2va_baseline_3.mp4 +3 -0
- results/minimax_h3_ref2va_turbo_4step_v0.1_768p_1.mp4 +3 -0
- results/minimax_h3_ref2va_turbo_4step_v0.1_768p_2.mp4 +3 -0
- results/minimax_h3_ref2va_turbo_4step_v0.1_768p_3.mp4 +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,24 @@ 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 |
+
asset/ref2va_test_5_2.jpg filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
asset/ref2va_test_5_3.jpg filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
asset/ref2va_test_5_4.jpg filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
results/minimax_h3_fl2va_acc_8step_v1_1.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
results/minimax_h3_fl2va_acc_8step_v1_2.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
results/minimax_h3_fl2va_acc_8step_v1_3.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
results/minimax_h3_fl2va_baseline_1.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
results/minimax_h3_fl2va_baseline_2.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
results/minimax_h3_fl2va_baseline_3.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
results/minimax_h3_fl2va_turbo_4step_v1.1_768p_1.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
results/minimax_h3_fl2va_turbo_4step_v1.1_768p_2.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
results/minimax_h3_fl2va_turbo_4step_v1.1_768p_3.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
results/minimax_h3_ref2va_acc_8step_v1_1.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
results/minimax_h3_ref2va_acc_8step_v1_2.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
results/minimax_h3_ref2va_acc_8step_v1_3.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
results/minimax_h3_ref2va_baseline_1.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
results/minimax_h3_ref2va_baseline_2.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 53 |
+
results/minimax_h3_ref2va_baseline_3.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 54 |
+
results/minimax_h3_ref2va_turbo_4step_v0.1_768p_1.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 55 |
+
results/minimax_h3_ref2va_turbo_4step_v0.1_768p_2.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 56 |
+
results/minimax_h3_ref2va_turbo_4step_v0.1_768p_3.mp4 filter=lfs diff=lfs merge=lfs -text
|
MiniMax-H3-FL2VA-Acc-8Step.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0b29be7042d883970eb0c20774a9ba03d95669ed80a721bb4d21be8ea0d0a196
|
| 3 |
+
size 1372450680
|
MiniMax-H3-Ref2VA-Acc-8Step.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:111c82e669f6e20e628228172edf39395f1a9fc3ad049793895e542c0f55b18c
|
| 3 |
+
size 1372450680
|
README.md
CHANGED
|
@@ -1,3 +1,126 @@
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
| 3 |
+
base_model:
|
| 4 |
+
- alibaba-pai/MiniMax-H3-Acc-LoRAs
|
| 5 |
+
- MiniMaxAI/MiniMax-H3
|
| 6 |
+
library_name: videox_fun
|
| 7 |
+
pipeline_tag: text-to-video
|
| 8 |
---
|
| 9 |
+
# MiniMax-H3-Acc-LoRAs
|
| 10 |
+
## Introduction
|
| 11 |
+
We apply Parallel Decoding Distillation (PDD) <sup>[1](#ref1)</sup> to [MiniMax-H3](https://huggingface.co/MiniMaxAI/MiniMax-H3), enabling efficient video generation in only a few inference steps.
|
| 12 |
+
|
| 13 |
+
For more details, please refer to our [GitHub repo](https://github.com/aigc-apps/VideoX-Fun).
|
| 14 |
+
|
| 15 |
+
| Name | Base Model | Hugging Face | Description |
|
| 16 |
+
|--|--|--|--|
|
| 17 |
+
| MiniMax-H3-FL2VA-Acc-8Step.safetensors | [MiniMax-H3 (FL2VA)](https://huggingface.co/MiniMaxAI/MiniMax-H3/tree/main/FL2VA) | [🤗Link](https://huggingface.co/alibaba-pai/MiniMax-H3-Acc-LoRAs/resolve/main/MiniMax-H3-FL2VA-Acc-8Step.safetensors) | Official 8 Step Acc LoRA (`rank=64` and `network_alpha=64` in BF16) for MiniMax-H3 (FL2VA).|
|
| 18 |
+
| MiniMax-H3-Ref2VA-Acc-8Step.safetensors | [MiniMax-H3 (Ref2VA)](https://huggingface.co/MiniMaxAI/MiniMax-H3/tree/main/Ref2VA) | [🤗Link](https://huggingface.co/alibaba-pai/MiniMax-H3-Acc-LoRAs/resolve/main/MiniMax-H3-Ref2VA-Acc-8Step.safetensors) | Official 8 Step Acc LoRA (`rank=64` and `network_alpha=64` in BF16) for MiniMax-H3 (Ref2VA).|
|
| 19 |
+
|
| 20 |
+
## Demo
|
| 21 |
+
|
| 22 |
+
### FL2VA (768p)
|
| 23 |
+
|
| 24 |
+
<table border="0" style="width: 100%; text-align: center; margin-top: 20px;">
|
| 25 |
+
<thead>
|
| 26 |
+
<tr>
|
| 27 |
+
<th style="text-align: center;" width="33%">MiniMax-H3-FL2VA</th>
|
| 28 |
+
<th style="text-align: center;" width="33%"><a href="https://huggingface.co/lightx2v/Minimax-h3-Turbo/blob/main/minimax_h3_fl2v_turbo_4step_v1.1_768p_bf16.safetensors">Minimax-h3-Turbo <br> (fl2v_turbo_4step_v1.1_768p)</a></th>
|
| 29 |
+
<th style="text-align: center;" width="33%">MiniMax-H3-FL2VA-Acc-8Step </th>
|
| 30 |
+
</tr>
|
| 31 |
+
</thead>
|
| 32 |
+
<tr>
|
| 33 |
+
<td>
|
| 34 |
+
<video src="results/minimax_h3_fl2va_baseline_1.mp4" width="100%" controls autoplay loop></video>
|
| 35 |
+
</td>
|
| 36 |
+
<td>
|
| 37 |
+
<video src="results/minimax_h3_fl2va_turbo_4step_v1.1_768p_1.mp4" width="100%" controls autoplay loop></video>
|
| 38 |
+
</td>
|
| 39 |
+
<td>
|
| 40 |
+
<video src="results/minimax_h3_fl2va_acc_8step_v1_1.mp4" width="100%" controls autoplay loop></video>
|
| 41 |
+
</td>
|
| 42 |
+
</tr>
|
| 43 |
+
<tr>
|
| 44 |
+
<td>
|
| 45 |
+
<video src="results/minimax_h3_fl2va_baseline_2.mp4" width="100%" controls autoplay loop></video>
|
| 46 |
+
</td>
|
| 47 |
+
<td>
|
| 48 |
+
<video src="results/minimax_h3_fl2va_turbo_4step_v1.1_768p_2.mp4" width="100%" controls autoplay loop></video>
|
| 49 |
+
</td>
|
| 50 |
+
<td>
|
| 51 |
+
<video src="results/minimax_h3_fl2va_acc_8step_v1_2.mp4" width="100%" controls autoplay loop></video>
|
| 52 |
+
</td>
|
| 53 |
+
</tr>
|
| 54 |
+
<tr>
|
| 55 |
+
<td>
|
| 56 |
+
<video src="results/minimax_h3_fl2va_baseline_3.mp4" width="100%" controls autoplay loop></video>
|
| 57 |
+
</td>
|
| 58 |
+
<td>
|
| 59 |
+
<video src="results/minimax_h3_fl2va_turbo_4step_v1.1_768p_3.mp4" width="100%" controls autoplay loop></video>
|
| 60 |
+
</td>
|
| 61 |
+
<td>
|
| 62 |
+
<video src="results/minimax_h3_fl2va_acc_8step_v1_3.mp4" width="100%" controls autoplay loop></video>
|
| 63 |
+
</td>
|
| 64 |
+
</tr>
|
| 65 |
+
</table>
|
| 66 |
+
|
| 67 |
+
### Ref2VA
|
| 68 |
+
|
| 69 |
+
<table border="0" style="width: 100%; text-align: center; margin-top: 20px;">
|
| 70 |
+
<thead>
|
| 71 |
+
<tr>
|
| 72 |
+
<th style="text-align: center;" width="33%">MiniMax-H3-Ref2VA</th>
|
| 73 |
+
<th style="text-align: center;" width="33%"><a href="https://huggingface.co/lightx2v/Minimax-h3-Turbo/blob/main/minimax_h3_ref2v_turbo_4step_v0.1_bf16.safetensors">Minimax-h3-Turbo <br> (ref2v_turbo_4step_v0.1)</a></th>
|
| 74 |
+
<th style="text-align: center;" width="33%">MiniMax-H3-Ref2VA-Acc-8Step</th>
|
| 75 |
+
</tr>
|
| 76 |
+
</thead>
|
| 77 |
+
<tr>
|
| 78 |
+
<td>
|
| 79 |
+
<video src="results/minimax_h3_ref2va_baseline_1.mp4" width="100%" controls autoplay loop></video>
|
| 80 |
+
</td>
|
| 81 |
+
<td>
|
| 82 |
+
<video src="results/minimax_h3_ref2va_turbo_4step_v0.1_768p_1.mp4" width="100%" controls autoplay loop></video>
|
| 83 |
+
</td>
|
| 84 |
+
<td>
|
| 85 |
+
<video src="results/minimax_h3_ref2va_acc_8step_v1_1.mp4" width="100%" controls autoplay loop></video>
|
| 86 |
+
</td>
|
| 87 |
+
</tr>
|
| 88 |
+
<tr>
|
| 89 |
+
<td>
|
| 90 |
+
<video src="results/minimax_h3_ref2va_baseline_2.mp4" width="100%" controls autoplay loop></video>
|
| 91 |
+
</td>
|
| 92 |
+
<td>
|
| 93 |
+
<video src="results/minimax_h3_ref2va_turbo_4step_v0.1_2.mp4" width="100%" controls autoplay loop></video>
|
| 94 |
+
</td>
|
| 95 |
+
<td>
|
| 96 |
+
<video src="results/minimax_h3_ref2va_acc_8step_v1_2.mp4" width="100%" controls autoplay loop></video>
|
| 97 |
+
</td>
|
| 98 |
+
</tr>
|
| 99 |
+
<tr>
|
| 100 |
+
<td>
|
| 101 |
+
<video src="results/minimax_h3_ref2va_baseline_3.mp4" width="100%" controls autoplay loop></video>
|
| 102 |
+
</td>
|
| 103 |
+
<td>
|
| 104 |
+
<video src="results/minimax_h3_ref2v_turbo_4step_v0.1_768p_3.mp4" width="100%" controls autoplay loop></video>
|
| 105 |
+
</td>
|
| 106 |
+
<td>
|
| 107 |
+
<video src="asset/minimax_h3_ref2va_acc_8step_v1_3.mp4" width="100%" controls autoplay loop></video>
|
| 108 |
+
</td>
|
| 109 |
+
</tr>
|
| 110 |
+
</table>
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
> [!NOTE]
|
| 114 |
+
> The above test cases are from <a href="https://github.com/ModelTC/Minimax-H3-Turbo/tree/main/examples">Minimax-H3-Turbo</a>.
|
| 115 |
+
> Videos are generated with a LoRA weight of 1.0 at both 4 and 8 NFE.
|
| 116 |
+
|
| 117 |
+
## Quick Start
|
| 118 |
+
Set `model_path` and `pdd_lora_path` to the MiniMax-H3 model and the matching acceleration LoRA checkpoint in [predict_t2v.py](https://github.com/aigc-apps/VideoX-Fun/blob/main/examples/MiniMax-H3-Acc-LoRAs/predict_t2v.py) for FL2VA or [predict_ref2v.py](https://github.com/aigc-apps/VideoX-Fun/blob/main/examples/MiniMax-H3-Acc-LoRAs/predict_ref2v.py) for Ref2VA, then run the corresponding script. Each example uses `apply_pdd_lora` to load the checkpoint and derive the required number of inference steps from its configuration.
|
| 119 |
+
|
| 120 |
+
> [!NOTE]
|
| 121 |
+
> These scripts use Diffusers' MiniMax-H3 `ModularPipeline` and require `diffusers >= 0.40.0`.
|
| 122 |
+
|
| 123 |
+
## Reference
|
| 124 |
+
<ol>
|
| 125 |
+
<li id="ref1">Neta Shaul, et al. "Parallel Decoding Distillation for Fast Image and Video Generation.". arXiv preprint arXiv:2607.26004 (2026).</li>
|
| 126 |
+
</ol>
|
asset/ref2va_test_5_1.jpg
ADDED
|
asset/ref2va_test_5_2.jpg
ADDED
|
Git LFS Details
|
asset/ref2va_test_5_3.jpg
ADDED
|
Git LFS Details
|
asset/ref2va_test_5_4.jpg
ADDED
|
Git LFS Details
|
minimax_h3_pdd.py
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""PDD LoRA adapter for MiniMax-H3 on Diffusers 0.40.
|
| 2 |
+
|
| 3 |
+
Parallel Decoding Distillation (PDD, arXiv 2607.26004) is not a PEFT LoRA: the
|
| 4 |
+
backbone gets low-rank updates, and the two final heads (`proj_out`,
|
| 5 |
+
`audio_proj_out`) are repeated once per interval of a length-`N` grid. Each
|
| 6 |
+
generation step fuses a block of those heads into one Euler velocity, so
|
| 7 |
+
`NFE = N / L` transformer evaluations cover the whole trajectory.
|
| 8 |
+
|
| 9 |
+
This module is inference-only. It mutates a loaded
|
| 10 |
+
`MiniMaxH3Transformer3DModel` in place and arms the fused heads on every
|
| 11 |
+
forward, so the stock Diffusers modular pipeline does not need a step callback.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
|
| 16 |
+
import json
|
| 17 |
+
from pathlib import Path
|
| 18 |
+
from typing import Sequence
|
| 19 |
+
|
| 20 |
+
import torch
|
| 21 |
+
import torch.nn as nn
|
| 22 |
+
import torch.nn.functional as F
|
| 23 |
+
|
| 24 |
+
DEFAULT_PDD_CONFIG = {
|
| 25 |
+
"pdd_num_steps": 32,
|
| 26 |
+
"pdd_block_size": 4,
|
| 27 |
+
"lora_rank": 64,
|
| 28 |
+
"lora_alpha": 64.0,
|
| 29 |
+
"lora_targets": "to_q,to_k,to_v,to_out.0,ff.net.0.proj,ff.net.2,adaln_proj.linear",
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def shifted_sigma(shift: float, sigma: torch.Tensor) -> torch.Tensor:
|
| 34 |
+
return shift * sigma / (1 + (shift - 1) * sigma)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def pdd_time_grid(shift: float, num_steps: int) -> torch.Tensor:
|
| 38 |
+
"""Ascending grid `0 = t_0 < ... < t_N = 1` of one MiniMax-H3 schedule."""
|
| 39 |
+
sigma = torch.linspace(1.0, 0.0, num_steps + 1, dtype=torch.float64)
|
| 40 |
+
return 1.0 - shifted_sigma(shift, sigma)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def pdd_sampling_plan(step_sizes: torch.Tensor, start: int, block_size: int) -> torch.Tensor:
|
| 44 |
+
"""Mean velocity of one block, which an Euler step over the block boundaries consumes."""
|
| 45 |
+
plan = torch.zeros(1, step_sizes.shape[0], dtype=step_sizes.dtype, device=step_sizes.device)
|
| 46 |
+
span = step_sizes[start : start + block_size].sum()
|
| 47 |
+
plan[0, start : start + block_size] = step_sizes[start : start + block_size] / span
|
| 48 |
+
return plan
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class MiniMaxH3ParallelHead(nn.Module):
|
| 52 |
+
"""`N` per-interval output heads in place of one final linear layer."""
|
| 53 |
+
|
| 54 |
+
def __init__(self, source: nn.Linear, num_steps: int):
|
| 55 |
+
super().__init__()
|
| 56 |
+
self.num_steps = num_steps
|
| 57 |
+
self.in_features = source.in_features
|
| 58 |
+
self.out_features = source.out_features
|
| 59 |
+
self.weight = nn.Parameter(source.weight.detach()[None].repeat(num_steps, 1, 1).clone())
|
| 60 |
+
self.bias = (
|
| 61 |
+
None if source.bias is None else nn.Parameter(source.bias.detach()[None].repeat(num_steps, 1).clone())
|
| 62 |
+
)
|
| 63 |
+
self.plan = torch.zeros(1, num_steps)
|
| 64 |
+
self.plan[0, 0] = 1.0
|
| 65 |
+
|
| 66 |
+
def set_plan(self, plan: torch.Tensor) -> None:
|
| 67 |
+
if plan.ndim != 2 or plan.shape[1] != self.num_steps:
|
| 68 |
+
raise ValueError(f"A PDD plan must be `(num_directions, {self.num_steps})`, got {list(plan.shape)}.")
|
| 69 |
+
self.plan = plan
|
| 70 |
+
|
| 71 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 72 |
+
plan = self.plan.to(device=self.weight.device, dtype=self.weight.dtype)
|
| 73 |
+
weight = torch.einsum("pn,noi->poi", plan, self.weight).flatten(0, 1)
|
| 74 |
+
bias = None if self.bias is None else torch.einsum("pn,no->po", plan, self.bias).flatten()
|
| 75 |
+
return F.linear(hidden_states, weight, bias)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class LoRALinear(nn.Module):
|
| 79 |
+
"""Frozen `nn.Linear` plus `y += (alpha / rank) * B A x`."""
|
| 80 |
+
|
| 81 |
+
# Diffusers' AdaLN casts activations with `get_parameter_dtype(linear)`. That
|
| 82 |
+
# helper returns the first floating parameter, so skip the float32 adapters
|
| 83 |
+
# and expose the frozen backbone dtype.
|
| 84 |
+
_keep_in_fp32_modules = ["lora_down", "lora_up"]
|
| 85 |
+
|
| 86 |
+
def __init__(self, base: nn.Linear, rank: int, alpha: float):
|
| 87 |
+
super().__init__()
|
| 88 |
+
self.base = base
|
| 89 |
+
self.base.requires_grad_(False)
|
| 90 |
+
self.scaling = alpha / rank
|
| 91 |
+
self.lora_down = nn.Parameter(torch.empty(rank, base.in_features, dtype=torch.float32))
|
| 92 |
+
self.lora_up = nn.Parameter(torch.zeros(base.out_features, rank, dtype=torch.float32))
|
| 93 |
+
nn.init.kaiming_uniform_(self.lora_down, a=5**0.5)
|
| 94 |
+
|
| 95 |
+
@property
|
| 96 |
+
def weight(self) -> torch.Tensor:
|
| 97 |
+
return self.base.weight
|
| 98 |
+
|
| 99 |
+
@property
|
| 100 |
+
def bias(self):
|
| 101 |
+
return self.base.bias
|
| 102 |
+
|
| 103 |
+
@property
|
| 104 |
+
def in_features(self) -> int:
|
| 105 |
+
return self.base.in_features
|
| 106 |
+
|
| 107 |
+
@property
|
| 108 |
+
def out_features(self) -> int:
|
| 109 |
+
return self.base.out_features
|
| 110 |
+
|
| 111 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 112 |
+
out = self.base(hidden_states)
|
| 113 |
+
update = F.linear(
|
| 114 |
+
F.linear(hidden_states, self.lora_down.to(device=hidden_states.device, dtype=hidden_states.dtype)),
|
| 115 |
+
self.lora_up.to(device=hidden_states.device, dtype=hidden_states.dtype),
|
| 116 |
+
)
|
| 117 |
+
return out + self.scaling * update.to(out.dtype)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def attach_parallel_decoder(transformer: nn.Module, num_steps: int) -> None:
|
| 121 |
+
transformer.proj_out = MiniMaxH3ParallelHead(transformer.proj_out, num_steps)
|
| 122 |
+
transformer.audio_proj_out = MiniMaxH3ParallelHead(transformer.audio_proj_out, num_steps)
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def add_lora(module: nn.Module, target_names: Sequence[str], rank: int, alpha: float) -> int:
|
| 126 |
+
targets = [
|
| 127 |
+
(name, child)
|
| 128 |
+
for name, child in module.named_modules()
|
| 129 |
+
if isinstance(child, nn.Linear) and any(name.endswith(suffix) for suffix in target_names)
|
| 130 |
+
]
|
| 131 |
+
for name, child in targets:
|
| 132 |
+
parent_name, _, attribute = name.rpartition(".")
|
| 133 |
+
parent = module.get_submodule(parent_name) if parent_name else module
|
| 134 |
+
setattr(parent, attribute, LoRALinear(child, rank, alpha))
|
| 135 |
+
return len(targets)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def set_parallel_plan(transformer: nn.Module, video_plan: torch.Tensor, audio_plan: torch.Tensor) -> None:
|
| 139 |
+
transformer.proj_out.set_plan(video_plan)
|
| 140 |
+
transformer.audio_proj_out.set_plan(audio_plan)
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def _load_pdd_config(config_path: Path | None) -> dict:
|
| 144 |
+
config = dict(DEFAULT_PDD_CONFIG)
|
| 145 |
+
if config_path is None or not config_path.is_file():
|
| 146 |
+
return config
|
| 147 |
+
with config_path.open(encoding="utf-8") as handle:
|
| 148 |
+
saved = json.load(handle)
|
| 149 |
+
for key in config:
|
| 150 |
+
if key in saved:
|
| 151 |
+
config[key] = saved[key]
|
| 152 |
+
if not isinstance(config["lora_targets"], str):
|
| 153 |
+
config["lora_targets"] = ",".join(config["lora_targets"])
|
| 154 |
+
return config
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def _load_state_dict(path: Path) -> dict[str, torch.Tensor]:
|
| 158 |
+
if path.suffix.lower() == ".safetensors":
|
| 159 |
+
from safetensors.torch import load_file
|
| 160 |
+
|
| 161 |
+
checkpoint = load_file(path, device="cpu")
|
| 162 |
+
else:
|
| 163 |
+
try:
|
| 164 |
+
checkpoint = torch.load(path, map_location="cpu", weights_only=True, mmap=True)
|
| 165 |
+
except TypeError:
|
| 166 |
+
checkpoint = torch.load(path, map_location="cpu", weights_only=True)
|
| 167 |
+
if isinstance(checkpoint, dict) and isinstance(checkpoint.get("state_dict"), dict):
|
| 168 |
+
checkpoint = checkpoint["state_dict"]
|
| 169 |
+
if not isinstance(checkpoint, dict):
|
| 170 |
+
raise TypeError(f"Expected a state-dict mapping in {path}, got {type(checkpoint).__name__}.")
|
| 171 |
+
return checkpoint
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def resolve_pdd_checkpoint(path: str) -> tuple[Path, Path | None]:
|
| 175 |
+
"""Return `(weights_path, config_path)` from a local file/folder or a Hub repo id."""
|
| 176 |
+
local = Path(path).expanduser()
|
| 177 |
+
if local.is_file():
|
| 178 |
+
return local.resolve(), local.resolve().parent / "pdd_config.json"
|
| 179 |
+
if local.is_dir():
|
| 180 |
+
for name in ("pdd.pt", "pdd.safetensors"):
|
| 181 |
+
candidate = local / name
|
| 182 |
+
if candidate.is_file():
|
| 183 |
+
return candidate.resolve(), (local / "pdd_config.json").resolve()
|
| 184 |
+
raise FileNotFoundError(f"No pdd.pt or pdd.safetensors in {local}")
|
| 185 |
+
if "/" not in path:
|
| 186 |
+
raise FileNotFoundError(
|
| 187 |
+
f"PDD checkpoint not found: {path}. Pass a local pdd.pt / folder, or a Hugging Face repo id."
|
| 188 |
+
)
|
| 189 |
+
|
| 190 |
+
from huggingface_hub import hf_hub_download
|
| 191 |
+
from huggingface_hub.utils import EntryNotFoundError, RepositoryNotFoundError
|
| 192 |
+
|
| 193 |
+
try:
|
| 194 |
+
weights = Path(hf_hub_download(path, "pdd.pt"))
|
| 195 |
+
except (EntryNotFoundError, RepositoryNotFoundError):
|
| 196 |
+
weights = Path(hf_hub_download(path, "pdd.safetensors"))
|
| 197 |
+
try:
|
| 198 |
+
config = Path(hf_hub_download(path, "pdd_config.json"))
|
| 199 |
+
except EntryNotFoundError:
|
| 200 |
+
config = None
|
| 201 |
+
return weights, config
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
class _PDDStepArm:
|
| 205 |
+
"""Arm the fused heads before each transformer forward, then advance one block."""
|
| 206 |
+
|
| 207 |
+
def __init__(
|
| 208 |
+
self,
|
| 209 |
+
transformer: nn.Module,
|
| 210 |
+
video_steps: torch.Tensor,
|
| 211 |
+
audio_steps: torch.Tensor,
|
| 212 |
+
block_size: int,
|
| 213 |
+
nfe: int,
|
| 214 |
+
):
|
| 215 |
+
self.transformer = transformer
|
| 216 |
+
self.video_steps = video_steps
|
| 217 |
+
self.audio_steps = audio_steps
|
| 218 |
+
self.block_size = block_size
|
| 219 |
+
self.nfe = nfe
|
| 220 |
+
self.index = 0
|
| 221 |
+
self.arm(0)
|
| 222 |
+
|
| 223 |
+
def arm(self, step_index: int) -> None:
|
| 224 |
+
start = step_index * self.block_size
|
| 225 |
+
set_parallel_plan(
|
| 226 |
+
self.transformer,
|
| 227 |
+
pdd_sampling_plan(self.video_steps, start, self.block_size).float(),
|
| 228 |
+
pdd_sampling_plan(self.audio_steps, start, self.block_size).float(),
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
def __call__(self, _module, _args, output):
|
| 232 |
+
self.index += 1
|
| 233 |
+
if self.index < self.nfe:
|
| 234 |
+
self.arm(self.index)
|
| 235 |
+
else:
|
| 236 |
+
self.index = 0
|
| 237 |
+
self.arm(0)
|
| 238 |
+
return output
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
def apply_pdd_lora(transformer: nn.Module, checkpoint: str, video_shift: float, audio_shift: float) -> int:
|
| 242 |
+
"""Inject PDD LoRA + parallel heads, load weights, and arm them on every forward.
|
| 243 |
+
|
| 244 |
+
Args:
|
| 245 |
+
transformer: MiniMax-H3 `transformer` or `transformer_ref`.
|
| 246 |
+
checkpoint: Local `pdd.pt` / folder / Hugging Face repo id.
|
| 247 |
+
video_shift, audio_shift: Scheduler shifts (12.0 / 3.0 as released).
|
| 248 |
+
|
| 249 |
+
Returns:
|
| 250 |
+
`nfe`, the number of transformer evaluations. Diffusers' MiniMax-H3
|
| 251 |
+
scheduler counts the terminal sigma, so call the pipeline with
|
| 252 |
+
`num_inference_steps=nfe + 1`.
|
| 253 |
+
"""
|
| 254 |
+
weights_path, config_path = resolve_pdd_checkpoint(checkpoint)
|
| 255 |
+
config = _load_pdd_config(config_path)
|
| 256 |
+
num_steps = int(config["pdd_num_steps"])
|
| 257 |
+
block_size = int(config["pdd_block_size"])
|
| 258 |
+
if block_size < 1 or num_steps % block_size != 0:
|
| 259 |
+
raise ValueError(f"pdd_num_steps={num_steps} must be divisible by pdd_block_size={block_size}.")
|
| 260 |
+
nfe = num_steps // block_size
|
| 261 |
+
|
| 262 |
+
add_lora(
|
| 263 |
+
transformer,
|
| 264 |
+
config["lora_targets"].split(","),
|
| 265 |
+
int(config["lora_rank"]),
|
| 266 |
+
float(config["lora_alpha"]),
|
| 267 |
+
)
|
| 268 |
+
attach_parallel_decoder(transformer, num_steps)
|
| 269 |
+
|
| 270 |
+
state_dict = _load_state_dict(weights_path)
|
| 271 |
+
incompatible = transformer.load_state_dict(state_dict, strict=False)
|
| 272 |
+
if incompatible.unexpected_keys:
|
| 273 |
+
preview = ", ".join(incompatible.unexpected_keys[:3])
|
| 274 |
+
raise RuntimeError(
|
| 275 |
+
f"{weights_path} holds keys the parallel decoder does not have, e.g. {preview}."
|
| 276 |
+
)
|
| 277 |
+
print(
|
| 278 |
+
f"Loaded PDD LoRA: {weights_path} ({len(state_dict)} tensors, "
|
| 279 |
+
f"grid={num_steps}, block={block_size}, nfe={nfe})",
|
| 280 |
+
flush=True,
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
video_steps = pdd_time_grid(float(video_shift), num_steps).diff()
|
| 284 |
+
audio_steps = pdd_time_grid(float(audio_shift), num_steps).diff()
|
| 285 |
+
controller = _PDDStepArm(transformer, video_steps, audio_steps, block_size, nfe)
|
| 286 |
+
transformer.register_forward_hook(controller)
|
| 287 |
+
transformer._pdd_step_arm = controller
|
| 288 |
+
transformer.requires_grad_(False)
|
| 289 |
+
transformer.eval()
|
| 290 |
+
return nfe
|
predict_ref2v.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
from diffusers import ComponentsManager, ModularPipeline
|
| 6 |
+
from diffusers.modular_pipelines.minimax_h3 import MiniMaxH3ImageReference
|
| 7 |
+
from diffusers.utils.export_utils import encode_video
|
| 8 |
+
|
| 9 |
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
| 10 |
+
from minimax_h3_pdd import apply_pdd_lora
|
| 11 |
+
|
| 12 |
+
here = Path(__file__).resolve().parent
|
| 13 |
+
model_path = "MiniMaxAI/MiniMax-H3"
|
| 14 |
+
pdd_lora_path = str(here / "MiniMax-H3-Ref2VA-Acc-8Step.safetensors")
|
| 15 |
+
reference_images = [here / "asset" / f"ref2va_test_5_{i}.jpg" for i in range(1, 5)]
|
| 16 |
+
prompt = "subject_definitions:\n<Subject 1> is the East Asian woman sword fighter shown in <Picture 1>, preserving her facial identity, black hair tied in a high bun, focused expression, cream martial-arts jacket, and black-handled katana.\n<Subject 2> is the pale-skinned woman fighter shown in <Picture 2>, preserving her facial identity, short layered black hair with bangs, dark eye makeup, black lipstick, black leather jacket, and black clothing. The motorcycle is not present in the target video.\n<Subject 3> is the outdoor competition swimming pool shown in <Picture 3>, preserving the bright turquoise water, blue-and-white lane dividers, pale pool deck, and strong daylight.\n<Subject 4> is the underwater pool environment shown in <Picture 4>, preserving the cyan water, tiled pool floor, dark lane marking, moving caustic light, suspended bubbles, and refracted surface light.\n\nsummary:\n[reference generation] In one continuous 10-second action shot, <Subject 1> and <Subject 2> sprint into a violent exchange beside <Subject 3>. A rolling dodge leads into a left-fist versus right-hand-sword collision, a jumping counter, and a connected two-body tumble across the wet deck. Their momentum throws both women into the pool, where the confrontation finishes underwater inside <Subject 4>.\n\nretention_analysis:\n<Subject 1> (appears throughout): fully_preserved - retain the same face, high bun, cream jacket, body proportions, and single black-handled katana before and after entering the water.\n<Subject 2> (appears throughout): fully_preserved - retain the same face, short black hair, dark makeup, black lipstick, black leather jacket, black clothing, and body proportions before and after entering the water.\n<Subject 3> (appears before the underwater transition): fully_preserved - retain the competition-pool geometry, turquoise water, blue-and-white lane ropes, sunlit deck, and bright daylight.\n<Subject 4> (appears after the underwater transition): fully_preserved - retain the tiled floor, dark lane marking, cyan water, caustic illumination, bubbles, and refracted sunlight.\n\ndetailed_description:\nPhotorealistic live-action action cinema, 16:9 composition, hard bright daylight above water and luminous cyan light below water. Use one continuous aggressively tracking shot with no cuts: low lateral sprint tracking, one fast camera roll during the deck tumble, then a seamless plunge through the waterline. Keep both adult women clearly distinguishable by face, hairstyle, clothing color, and silhouette. <Subject 1> holds exactly one katana in her right hand throughout. <Subject 2> remains unarmed. Any fist-to-sword contact strikes the flat near the guard, never the sharpened edge. No blood, wounds, severed contact, duplicate people, duplicate weapons, motorcycle, readable text, logos, or costume changes.\n\n[Shot 1, 00:00.000-00:02.000] A low handheld camera races parallel to the wet edge of <Subject 3>. <Subject 1> in the cream jacket and <Subject 2> in black leather sprint toward each other from opposite ends of the pool deck. <Subject 1> carries the single katana low in her right hand, blade trailing safely behind. She plants one foot on a low starting block, leaps forward, and brings the sword into a fast diagonal arc. <Subject 2> drops beneath it into a forward shoulder roll, slides across the wet tiles, and rises inside the sword's range as water sprays from both fighters' shoes.\n\n[Shot 1, 00:02.000-00:04.200] <Subject 2> drives upward from the roll with one explosive left punch. <Subject 1> snaps the katana across in her right hand so the punch collides with the flat of the blade immediately beside the guard. The impact produces one sharp metallic crack, a compact spray of sparks, and a ring of displaced droplets. Their faces pass within centimeters. <Subject 2> hooks the sword wrist with her right hand and whips a low leg sweep; <Subject 1> jumps over it, twists in the air, and lands one step from the pool edge without releasing the sword.\n\n[Shot 1, 00:04.200-00:06.800] <Subject 2> immediately tackles <Subject 1> around the waist. <Subject 1> braces her left forearm across <Subject 2>'s shoulder and extends her right sword arm away from both bodies. Locked together, they crash onto the wet deck and complete two fast connected shoulder-over-shoulder rolls. The camera rolls with them at tile level as sky, lane ropes, faces, black leather, and cream fabric whip across frame. <Subject 1> hooks <Subject 2>'s ankle during the second rotation; their combined momentum carries both bodies over the pool lip.\n\n[Shot 1, 00:06.800-00:07.900] Both women rotate once in open air and strike the turquoise surface together in an enormous splash. The camera plunges between them without a cut, crossing the violently folding waterline as daylight becomes refracted cyan and a dense wall of bubbles engulfs the frame.\n\n[Shot 1, 00:07.900-00:10.000] Inside <Subject 4>, their clothes and hair surge in opposite currents while both identities remain clear. <Subject 2> throws one slowed left punch through the bubbles. <Subject 1>, still gripping the single katana in her right hand, blocks with the hilt and her left forearm while keeping the blade pointed safely away. The impact rolls both women apart above the dark lane marking. They kick backward, regain facing guard positions, and end suspended on opposite sides of a rising column of bubbles as caustic light flashes across their faces and the tiled floor.\n\noverall_soundscape:\nRapid wet footfalls, starting-block impact, sword whoosh, rolling bodies, one metallic fist-to-blade crack, sparks, tile scrapes, a heavy synchronized splash, dense bubbles, muffled underwater impact, and filtered pool ambience.\n\nnon_diegetic_music:\nFast taiko-like percussion accelerates through the sprint and deck rolls, cuts completely at the splash, then returns underwater as two muffled bass pulses ending on one suspended metallic note.\n"
|
| 17 |
+
sample_size = [768, 1344]
|
| 18 |
+
video_length = 243
|
| 19 |
+
|
| 20 |
+
manager = ComponentsManager()
|
| 21 |
+
manager.enable_auto_cpu_offload(device="cuda", memory_reserve_margin="12GB")
|
| 22 |
+
pipeline = ModularPipeline.from_pretrained(model_path, workflow="ref2va", components_manager=manager)
|
| 23 |
+
pipeline.load_components(dtype=torch.bfloat16, pretrained_model_name_or_path=model_path)
|
| 24 |
+
|
| 25 |
+
nfe = apply_pdd_lora(
|
| 26 |
+
pipeline.transformer_ref, pdd_lora_path, pipeline.scheduler.shift, pipeline.audio_scheduler.shift
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
generator = torch.Generator().manual_seed(42)
|
| 30 |
+
result = pipeline(
|
| 31 |
+
prompt=prompt,
|
| 32 |
+
references=[MiniMaxH3ImageReference.from_file(str(path)) for path in reference_images],
|
| 33 |
+
height=sample_size[0],
|
| 34 |
+
width=sample_size[1],
|
| 35 |
+
num_frames=video_length,
|
| 36 |
+
num_inference_steps=nfe + 1,
|
| 37 |
+
generator=generator,
|
| 38 |
+
output_type="np",
|
| 39 |
+
output=["videos", "audio", "sampling_rate"],
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
output_path = here / "samples" / "ref2v.mp4"
|
| 43 |
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
| 44 |
+
encode_video(
|
| 45 |
+
result["videos"][0],
|
| 46 |
+
fps=24,
|
| 47 |
+
output_path=str(output_path),
|
| 48 |
+
audio=result["audio"][0],
|
| 49 |
+
audio_sample_rate=int(result["sampling_rate"]),
|
| 50 |
+
)
|
| 51 |
+
print(f"Saved {output_path}", flush=True)
|
predict_t2v.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
from diffusers import ComponentsManager, ModularPipeline
|
| 6 |
+
from diffusers.utils.export_utils import encode_video
|
| 7 |
+
|
| 8 |
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
| 9 |
+
from minimax_h3_pdd import apply_pdd_lora
|
| 10 |
+
|
| 11 |
+
model_path = "MiniMaxAI/MiniMax-H3"
|
| 12 |
+
pdd_lora_path = str(Path(__file__).resolve().parent / "MiniMax-H3-FL2VA-Acc-8Step.safetensors")
|
| 13 |
+
prompt = (
|
| 14 |
+
"[Shot 1] Cinematic wide shot, low angle, camera pushing in fast. A knight in dented steel plate armour "
|
| 15 |
+
"charges across a muddy tournament field toward a shaggy brown warhorse-mounted rival, his tattered blue "
|
| 16 |
+
"surcoat streaming behind him. He raises a notched longsword in both hands and brings it down; the rival "
|
| 17 |
+
"parries with a round wooden shield that splinters, chips of wood spinning into the air. Clods of wet earth "
|
| 18 |
+
"fly up from their boots and hooves. Grey banners snap in a strong crosswind under an overcast sky, and a "
|
| 19 |
+
"crowd of blurred spectators presses against a wooden barricade in the background. The camera swings around "
|
| 20 |
+
"the clash as the knight shoulders into the shield and both men stagger sideways."
|
| 21 |
+
"Ringing steel-on-steel impacts with long metallic decay, the dry crack of splitting wood, heavy plate "
|
| 22 |
+
"rattling with every stride, boots sucking out of deep mud, a horse snorting and stamping, wind buffeting the "
|
| 23 |
+
"microphone and a roaring crowd swelling behind it."
|
| 24 |
+
"Full orchestral battle cue, fast ostinato strings under blaring low brass, timpani accenting each sword "
|
| 25 |
+
"impact."
|
| 26 |
+
)
|
| 27 |
+
sample_size = [704, 1280]
|
| 28 |
+
video_length = 124
|
| 29 |
+
|
| 30 |
+
manager = ComponentsManager()
|
| 31 |
+
manager.enable_auto_cpu_offload(device="cuda", memory_reserve_margin="12GB")
|
| 32 |
+
pipeline = ModularPipeline.from_pretrained(model_path, workflow="t2va", components_manager=manager)
|
| 33 |
+
pipeline.load_components(dtype=torch.bfloat16, pretrained_model_name_or_path=model_path)
|
| 34 |
+
|
| 35 |
+
nfe = apply_pdd_lora(
|
| 36 |
+
pipeline.transformer, pdd_lora_path, pipeline.scheduler.shift, pipeline.audio_scheduler.shift
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
generator = torch.Generator().manual_seed(42)
|
| 40 |
+
result = pipeline(
|
| 41 |
+
prompt=prompt,
|
| 42 |
+
height=sample_size[0],
|
| 43 |
+
width=sample_size[1],
|
| 44 |
+
num_frames=video_length,
|
| 45 |
+
num_inference_steps=nfe + 1,
|
| 46 |
+
generator=generator,
|
| 47 |
+
output_type="np",
|
| 48 |
+
output=["videos", "audio", "sampling_rate"],
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
output_path = Path(__file__).resolve().parent / "samples" / "t2v.mp4"
|
| 52 |
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
| 53 |
+
encode_video(
|
| 54 |
+
result["videos"][0],
|
| 55 |
+
fps=24,
|
| 56 |
+
output_path=str(output_path),
|
| 57 |
+
audio=result["audio"][0],
|
| 58 |
+
audio_sample_rate=int(result["sampling_rate"]),
|
| 59 |
+
)
|
| 60 |
+
print(f"Saved {output_path}", flush=True)
|
results/minimax_h3_fl2va_acc_8step_v1_1.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6fcef5076fec4cfad72b9e1a2104744ace87a6c0c2f804088b45d9817841741e
|
| 3 |
+
size 851760
|
results/minimax_h3_fl2va_acc_8step_v1_2.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e15ed74a9883384f3a3662f959067de0793e8aefff20e6fd55885fb882529d23
|
| 3 |
+
size 8798213
|
results/minimax_h3_fl2va_acc_8step_v1_3.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cf02dd4516a9453bb1bc012fb912f1ef83f854d07151686dfbcd16757cab0219
|
| 3 |
+
size 1961347
|
results/minimax_h3_fl2va_baseline_1.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:afcbd76e7e5b6107df3b5b0a77fbdc09253d2e20beca55403730d45c55032584
|
| 3 |
+
size 835589
|
results/minimax_h3_fl2va_baseline_2.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6673f964eda77ada13384af36cca1fb3fa058a45a788780ff9da154569cdfd44
|
| 3 |
+
size 7719897
|
results/minimax_h3_fl2va_baseline_3.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7cc312bdafbf84b3a9d9da6c0af8a5b5cd75888b3d695d33f6bb5016c811d253
|
| 3 |
+
size 1814474
|
results/minimax_h3_fl2va_turbo_4step_v1.1_768p_1.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b92514db1f7a59517a1ef165748e5d7fab6e317fc51e8212e1b95645e3912f18
|
| 3 |
+
size 893312
|
results/minimax_h3_fl2va_turbo_4step_v1.1_768p_2.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f21d92548605a8e05c2d8bee608df81c47a584ed483d3b7c9aea17f1ddaeaa0c
|
| 3 |
+
size 9806891
|
results/minimax_h3_fl2va_turbo_4step_v1.1_768p_3.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:831a974e4f25cb01c60845f1b6dc5720966c12f88801d5aefe8a077d76f99fda
|
| 3 |
+
size 2326695
|
results/minimax_h3_ref2va_acc_8step_v1_1.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c38d56649aeb5420e6e595808d4f5f92aabdcc059fc52350ce09806fa7f5e1a6
|
| 3 |
+
size 6085204
|
results/minimax_h3_ref2va_acc_8step_v1_2.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:49db1d2d853a189c5f04987439fe2f4ca7b8d99827cddf9e5cf93ce0a0cafa4b
|
| 3 |
+
size 2006767
|
results/minimax_h3_ref2va_acc_8step_v1_3.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:87955def3dbb1ea49726ae9c07fc8709dbb15f9026ecc38a15ee300771d944f6
|
| 3 |
+
size 4151912
|
results/minimax_h3_ref2va_baseline_1.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b06369445bba07ed87581b49e163ec47ea4219c4624f95ff59d0c8870d4fad09
|
| 3 |
+
size 5930077
|
results/minimax_h3_ref2va_baseline_2.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8bd6e9b1b8e992d00d9c7754215ce47314be76a81d461b5d50a1ad250b808afc
|
| 3 |
+
size 1659004
|
results/minimax_h3_ref2va_baseline_3.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:233d92ae3bedad301581c1a7df1c19fef6746967620902c9330950a52c1ae30c
|
| 3 |
+
size 4891750
|
results/minimax_h3_ref2va_turbo_4step_v0.1_768p_1.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fa71f4bc139274893d2ffcf07a1c461bc04afd54f8959c94843a2c6418f7222f
|
| 3 |
+
size 5273075
|
results/minimax_h3_ref2va_turbo_4step_v0.1_768p_2.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:67b13c561a0d34387c0fc46904435edfc618bce23923fcebaab8102806638cc7
|
| 3 |
+
size 1012341
|
results/minimax_h3_ref2va_turbo_4step_v0.1_768p_3.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bfce9c6dd70388c1eef57ae8c2455a492d2d2104a55c6ee31ad5188c0e3dcf9c
|
| 3 |
+
size 3734857
|