This commit is contained in:
zhengzangw 2024-06-27 14:02:04 +00:00
commit 7e325e4e7b
5 changed files with 163 additions and 40 deletions

View file

@ -20,21 +20,27 @@
## 📰 资讯
* **[2024.06.17]** 🔥
* 我们发布了**Open-Sora 1.2**,其中包括**3D-VAE****整流流**和**得分条件**。视频质量大大提高。[[checkpoints]](#open-sora-10-model-weights) [[report]](/docs/report_03.md)
* **[2024.06.22]** 🔥我们在[潞晨云](https://cloud.luchentech.com/)上发布了Open-Sora1.2镜像并在B站上传了详细的[使用教程](https://www.bilibili.com/video/BV1ow4m1e7PX/)
* **[2024.06.17]** 🔥我们发布了**Open-Sora 1.2**,其中包括**3D-VAE****整流流**和**得分条件**。视频质量大大提高。[[模型权重]](#模型权重) [[技术报告]](report_v3.md) [[公众号文章]](https://mp.weixin.qq.com/s/QHq2eItZS9e00BVZnivdjg)
* **[2024.04.25]** 🤗 我们在 Hugging Face Spaces 上发布了 [Open-Sora的Gradio演示](https://huggingface.co/spaces/hpcai-tech/open-sora)。
* **[2024.04.25]** 我们发布了**Open-Sora 1.1**,支持**2s~15s、144p 到 720p、任意比例的文本转图片、文本转视频、图片转视频、视频转视频、无限时间生成**。此外,还发布了完整的视频处理管道。 [[checkpoints]]() [[report]](/docs/report_02.md)
* **[2024.04.25]** 我们发布了**Open-Sora 1.1**,支持**2s~15s、144p 到 720p、任意比例的文本转图片、文本转视频、图片转视频、视频转视频、无限时间生成**。此外,还发布了完整的视频处理管道。 [[模型权重]](#模型权重) [[技术报告]](report_v2.md)[[公众号文章]](https://mp.weixin.qq.com/s/nkPSTep2se__tzp5OfiRQQ)
* **[2024.03.18]** 我们发布了 **Open-Sora 1.0**, 一个完全开源的视频生成项目。Open-Sora 1.0 支持完整的视频数据预处理流程、加速训练
<a href="https://github.com/hpcaitech/ColossalAI"><img src="assets/readme/colossal_ai.png" width="8%" ></a>
、推理等。我们的模型只需 3 天的训练就可以生成 2 秒的 512x512 视频。 [[checkpoints]](#open-sora-10-model-weights)
[[blog]](https://hpc-ai.com/blog/open-sora-v1.0) [[report]](/docs/report_01.md)
<a href="https://github.com/hpcaitech/ColossalAI"><img src="/assets/readme/colossal_ai.png" width="8%" ></a>
、推理等。我们的模型只需 3 天的训练就可以生成 2 秒的 512x512 视频。 [[模型权重]](#模型权重)
[[公众号文章]](https://mp.weixin.qq.com/s/H52GW8i4z1Dco3Sg--tCGw) [[技术报告]](report_v1.md)
* **[2024.03.04]** Open-Sora 提供培训,成本降低 46%。
[[blog]](https://hpc-ai.com/blog/open-sora)
[[公众号文章]](https://mp.weixin.qq.com/s/OjRUdrM55SufDHjwCCAvXg)
## 🎥 Latest Demo
🔥 您可以在HuggingFace上的 [🤗 Gradio应用程序](https://huggingface.co/spaces/hpcai-tech/open-sora)上体验Open-Sora. 我们的[画廊](https://hpcaitech.github.io/Open-Sora/)中提供了更多示例.
| **4s 720×1280** | **4s 720×1280** | **4s 720×1280** |
| ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| [<img src="/assets/demo/v1.2/sample_0013.gif" width="">](https://github.com/hpcaitech/Open-Sora/assets/99191637/7895aab6-ed23-488c-8486-091480c26327) | [<img src="/assets/demo/v1.2/sample_1718.gif" width="">](https://github.com/hpcaitech/Open-Sora/assets/99191637/20f07c7b-182b-4562-bbee-f1df74c86c9a) | [<img src="/assets/demo/v1.2/sample_0087.gif" width="">](https://github.com/hpcaitech/Open-Sora/assets/99191637/3d897e0d-dc21-453a-b911-b3bda838acc2) |
| [<img src="/assets/demo/v1.2/sample_0052.gif" width="">](https://github.com/hpcaitech/Open-Sora/assets/99191637/644bf938-96ce-44aa-b797-b3c0b513d64c) | [<img src="/assets/demo/v1.2/sample_1719.gif" width="">](https://github.com/hpcaitech/Open-Sora/assets/99191637/272d88ac-4b4a-484d-a665-8d07431671d0) | [<img src="/assets/demo/v1.2/sample_0002.gif" width="">](https://github.com/hpcaitech/Open-Sora/assets/99191637/ebbac621-c34e-4bb4-9543-1c34f8989764) |
| [<img src="/assets/demo/v1.2/sample_0011.gif" width="">](https://github.com/hpcaitech/Open-Sora/assets/99191637/a1e3a1a3-4abd-45f5-8df2-6cced69da4ca) | [<img src="/assets/demo/v1.2/sample_0004.gif" width="">](https://github.com/hpcaitech/Open-Sora/assets/99191637/d6ce9c13-28e1-4dff-9644-cc01f5f11926) | [<img src="/assets/demo/v1.2/sample_0061.gif" width="">](https://github.com/hpcaitech/Open-Sora/assets/99191637/561978f8-f1b0-4f4d-ae7b-45bec9001b4a) |
<details>
<summary>OpenSora 1.1 演示</summary>
@ -189,7 +195,8 @@ docker run -ti --gpus all -v .:/workspace/Open-Sora opensora
### Open-Sora 1.2 模型权重
| 分辨率 | 模型大小 | 数据 | 迭代次数 | 批次大小 | 网址 |
| ---------- | ---------- | ---- | ----------- | ---------- | --- |
| 待定 |
| Diffusion | 1.1B | 30M | 70k | 动态大小 | [:link:](https://huggingface.co/hpcai-tech/OpenSora-STDiT-v3) |
| VAE | 384M | 3M | 1M | 8 | [:link:](https://huggingface.co/hpcai-tech/OpenSora-VAE-v1.2) |
请参阅我们的**[report 1.2](docs/report_v3.md)**以了解更多信息。
@ -280,6 +287,49 @@ export OPENAI_API_KEY=YOUR_API_KEY
### Open-Sora 1.2 命令行推理
基础的命令行推理:
```bash
# text to video
python scripts/inference.py configs/opensora-v1-2/inference/sample.py \
--num-frames 4s --resolution 720p --aspect-ratio 9:16 \
--prompt "a beautiful waterfall"
```
您可以向命令行添加更多选项来定制生成。
```bash
python scripts/inference.py configs/opensora-v1-2/inference/sample.py \
--num-frames 4s --resolution 720p --aspect-ratio 9:16 \
--num-sampling-steps 30 --flow 5 --aes 6.5 \
--prompt "a beautiful waterfall"
```
对于图像到视频生成和其他功能API 与 Open-Sora 1.1 兼容。请参阅[此处]](commands.md)了解更多说明。
如果您的安装不包含 `apex``flash-attn`, 则需要在配置文件中或通过以下命令禁用它们。
```bash
python scripts/inference.py configs/opensora-v1-2/inference/sample.py \
--num-frames 4s --resolution 720p \
--layernorm-kernel False --flash-attn False \
--prompt "a beautiful waterfall"
```
### 序列并行推理
要启用序列并行,您需要使用 `torchrun` 来运行推理脚本。以下命令将使用 2 个 GPU 运行推理。
```bash
# text to video
CUDA_VISIBLE_DEVICES=0,1 torchrun --nproc_per_node 2 scripts/inference.py configs/opensora-v1-2/inference/sample.py \
--num-frames 4s --resolution 720p --aspect-ratio 9:16 \
--prompt "a beautiful waterfall"
```
:warning: **注意**: gradio 部署不支持序列并行。目前,只有当维度可以除以 GPU 数量时才支持序列并行。因此,在某些情况下可能会失败。我们测试了 4 个 GPU 用于 720p 和 2 个 GPU 用于 480p。
### GPT-4o 快速细化
我们发现 GPT-4o 可以细化提示并提高生成视频的质量。利用此功能,您还可以使用其他语言(例如中文)作为提示。要启用此功能,您需要在环境中准备您的 openai api 密钥:
@ -347,6 +397,17 @@ torchrun --standalone --nproc_per_node 2 scripts/inference.py configs/opensora/i
### Open-Sora 1.2 训练
训练过程与Open-Sora 1.1相同。
```bash
# one node
torchrun --standalone --nproc_per_node 8 scripts/train.py \
configs/opensora-v1-2/train/stage1.py --data-path YOUR_CSV_PATH --ckpt-path YOUR_PRETRAINED_CKPT
# multiple nodes
colossalai run --nproc_per_node 8 --hostfile hostfile scripts/train.py \
configs/opensora-v1-2/train/stage1.py --data-path YOUR_CSV_PATH --ckpt-path YOUR_PRETRAINED_CKPT
```
### Open-Sora 1.1 训练
<details>
@ -390,11 +451,48 @@ colossalai run --nproc_per_node 8 --hostfile hostfile scripts/train.py configs/o
## 评估
### VBench
我们支持基于以下方面的评估:
### VBench-i2v
- 验证损失
- [VBench](https://github.com/Vchitect/VBench/tree/master)h分数
- VBench-i2v 分数
- 批量生成以供人工评估
所有评估代码均发布在 `eval`文件夹中。查看[README](/eval/README.md)了解更多详细信息。我们的 [技术报告](report_v3.md#评估)还提供了有关训练期间评估的更多信息。下表显示 Open-Sora 1.2 大大改进了 Open-Sora 1.0。
| 模型 | 总得分 | 质量得分 | 语义得分 |
| -------------- | ----------- | ------------- | -------------- |
| Open-Sora V1.0 | 75.91% | 78.81% | 64.28% |
| Open-Sora V1.2 | 79.23% | 80.71% | 73.30% |
## VAE 训练与评估
我们训练一个由空间 VAE 和时间 VAE 组成的 VAE 管道。有关更多详细信息,请参阅[VAE 文档](vae.md)。在运行以下命令之前,请按照我们的[安装文档](installation.md)安装 VAE 和评估所需的依赖项。
如果您想训练自己的 VAE我们需要按照[数据处理](#data-processing)流程在 csv 中准备数据,然后运行以下命令。请注意,您需要根据自己的 csv 数据大小相应地调整配置文件中的训练`epochs`数量。
```bash
# stage 1 training, 380k steps, 8 GPUs
torchrun --nnodes=1 --nproc_per_node=8 scripts/train_vae.py configs/vae/train/stage1.py --data-path YOUR_CSV_PATH
# stage 2 training, 260k steps, 8 GPUs
torchrun --nnodes=1 --nproc_per_node=8 scripts/train_vae.py configs/vae/train/stage2.py --data-path YOUR_CSV_PATH
# stage 3 training, 540k steps, 24 GPUs
torchrun --nnodes=3 --nproc_per_node=8 scripts/train_vae.py configs/vae/train/stage3.py --data-path YOUR_CSV_PATH
```
为了评估 VAE 的性能,您需要首先运行 VAE 推理来生成视频,然后计算生成的视频的分数:
```bash
# video generation
torchrun --standalone --nnodes=1 --nproc_per_node=1 scripts/inference_vae.py configs/vae/inference/video.py --ckpt-path YOUR_VAE_CKPT_PATH --data-path YOUR_CSV_PATH --save-dir YOUR_VIDEO_DIR
# the original videos will be saved to `YOUR_VIDEO_DIR_ori`
# the reconstructed videos through the pipeline will be saved to `YOUR_VIDEO_DIR_rec`
# the reconstructed videos through the spatial VAE only will be saved to `YOUR_VIDEO_DIR_spatial`
# score calculation
python eval/vae/eval_common_metric.py --batch_size 2 --real_video_dir YOUR_VIDEO_DIR_ori --generated_video_dir YOUR_VIDEO_DIR_rec --device cuda --sample_fps 24 --crop_size 256 --resolution 256 --num_frames 17 --sample_rate 1 --metric ssim psnr lpips flolpips
```
请参阅 [c此处](/eval/README.md)了解更多说明。
## 贡献

View file

@ -79,6 +79,7 @@ def parse_args(training=False):
parser.add_argument("--load", default=None, type=str, help="path to continue training")
parser.add_argument("--start-from-scratch", action="store_true", help="start training from scratch")
parser.add_argument("--warmup-steps", default=None, type=int, help="warmup steps")
parser.add_argument("--record-time", default=False, action="store_true", help="record time of each part")
return parser.parse_args()

View file

@ -6,11 +6,12 @@ import time
from collections import OrderedDict
from collections.abc import Sequence
from itertools import repeat
from typing import Tuple
from typing import Optional, Tuple
import numpy as np
import torch
import torch.distributed as dist
from colossalai.cluster.dist_coordinator import DistCoordinator
# ======================================================
# Logging
@ -358,11 +359,12 @@ def all_exists(paths):
class Timer:
def __init__(self, name, log=False):
def __init__(self, name, log=False, coordinator: Optional[DistCoordinator] = None):
self.name = name
self.start_time = None
self.end_time = None
self.log = log
self.coordinator = coordinator
@property
def elapsed_time(self):
@ -374,6 +376,8 @@ class Timer:
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self.coordinator is not None:
self.coordinator.block_all()
torch.cuda.synchronize()
self.end_time = time.time()
if self.log:

View file

@ -12,7 +12,7 @@ from opensora.acceleration.plugin import ZeroSeqParallelPlugin
from .misc import get_logger
def create_colossalai_plugin(plugin, dtype, grad_clip, sp_size):
def create_colossalai_plugin(plugin, dtype, grad_clip, sp_size, reduce_bucket_size_in_m: int = 20):
if plugin == "zero2":
assert sp_size == 1, "Zero2 plugin does not support sequence parallelism"
plugin = LowLevelZeroPlugin(
@ -20,6 +20,7 @@ def create_colossalai_plugin(plugin, dtype, grad_clip, sp_size):
precision=dtype,
initial_scale=2**16,
max_norm=grad_clip,
reduce_bucket_size_in_m=reduce_bucket_size_in_m,
)
set_data_parallel_group(dist.group.WORLD)
elif plugin == "zero2-seq":
@ -30,6 +31,7 @@ def create_colossalai_plugin(plugin, dtype, grad_clip, sp_size):
precision=dtype,
initial_scale=2**16,
max_norm=grad_clip,
reduce_bucket_size_in_m=reduce_bucket_size_in_m,
)
set_sequence_parallel_group(plugin.sp_group)
set_data_parallel_group(plugin.dp_group)

View file

@ -1,4 +1,5 @@
import os
from contextlib import nullcontext
from copy import deepcopy
from datetime import timedelta
from pprint import pformat
@ -38,6 +39,7 @@ def main():
# ======================================================
# == parse configs ==
cfg = parse_configs(training=True)
record_time = cfg.get("record_time", False)
# == device and dtype ==
assert torch.cuda.is_available(), "Training currently requires at least one GPU."
@ -76,6 +78,7 @@ def main():
dtype=cfg_dtype,
grad_clip=cfg.get("grad_clip", 0),
sp_size=cfg.get("sp_size", 1),
reduce_bucket_size_in_m=cfg.get("reduce_bucket_size_in_m", 20),
)
booster = Booster(plugin=plugin)
torch.set_num_threads(1)
@ -229,6 +232,21 @@ def main():
# 5. training loop
# =======================================================
dist.barrier()
timers = {}
timer_keys = [
"move_data",
"encode",
"mask",
"diffusion",
"backward",
"update_ema",
"reduce_loss",
]
for key in timer_keys:
if record_time:
timers[key] = Timer(key, coordinator=coordinator)
else:
timers[key] = nullcontext()
for epoch in range(start_epoch, cfg_epochs):
# == set dataloader to new epoch ==
sampler.set_epoch(epoch)
@ -245,13 +263,14 @@ def main():
) as pbar:
for step, batch in pbar:
timer_list = []
with Timer("move data") as move_data_t:
with timers["move_data"] as move_data_t:
x = batch.pop("video").to(device, dtype) # [B, C, T, H, W]
y = batch.pop("text")
timer_list.append(move_data_t)
if record_time:
timer_list.append(move_data_t)
# == visual and text encoding ==
with Timer("encode") as encode_t:
with timers["encode"] as encode_t:
with torch.no_grad():
# Prepare visual inputs
if cfg.get("load_video_features", False):
@ -267,17 +286,17 @@ def main():
model_args["mask"] = mask
else:
model_args = text_encoder.encode(y)
coordinator.block_all()
timer_list.append(encode_t)
if record_time:
timer_list.append(encode_t)
# == mask ==
with Timer("mask") as mask_t:
with timers["mask"] as mask_t:
mask = None
if cfg.get("mask_ratios", None) is not None:
mask = mask_generator.get_masks(x)
model_args["x_mask"] = mask
coordinator.block_all()
timer_list.append(mask_t)
if record_time:
timer_list.append(mask_t)
# == video meta info ==
for k, v in batch.items():
@ -285,13 +304,13 @@ def main():
model_args[k] = v.to(device, dtype)
# == diffusion loss computation ==
with Timer("diffusion") as loss_t:
with timers["diffusion"] as loss_t:
loss_dict = scheduler.training_losses(model, x, model_args, mask=mask)
coordinator.block_all()
timer_list.append(loss_t)
if record_time:
timer_list.append(loss_t)
# == backward & update ==
with Timer("backward") as backward_t:
with timers["backward"] as backward_t:
loss = loss_dict["loss"].mean()
booster.backward(loss=loss, optimizer=optimizer)
optimizer.step()
@ -300,24 +319,24 @@ def main():
# update learning rate
if lr_scheduler is not None:
lr_scheduler.step()
coordinator.block_all()
timer_list.append(backward_t)
if record_time:
timer_list.append(backward_t)
# == update EMA ==
with Timer("update_ema") as ema_t:
with timers["update_ema"] as ema_t:
update_ema(ema, model.module, optimizer=optimizer, decay=cfg.get("ema_decay", 0.9999))
coordinator.block_all()
timer_list.append(ema_t)
if record_time:
timer_list.append(ema_t)
# == update log info ==
with Timer("reduce_loss") as reduce_loss_t:
with timers["reduce_loss"] as reduce_loss_t:
all_reduce_mean(loss)
running_loss += loss.item()
global_step = epoch * num_steps_per_epoch + step
log_step += 1
acc_step += 1
coordinator.block_all()
timer_list.append(reduce_loss_t)
if record_time:
timer_list.append(reduce_loss_t)
# == logging ==
if coordinator.is_master() and (global_step + 1) % cfg.get("log_every", 1) == 0:
@ -376,12 +395,11 @@ def main():
global_step + 1,
save_dir,
)
log_str = f"Rank {dist.get_rank()} | Epoch {epoch} | Step {step} | "
for timer in timer_list:
log_str += f"{timer.name}: {timer.elapsed_time:.3f}s | "
print(log_str)
coordinator.block_all()
if record_time:
log_str = f"Rank {dist.get_rank()} | Epoch {epoch} | Step {step} | "
for timer in timer_list:
log_str += f"{timer.name}: {timer.elapsed_time:.3f}s | "
print(log_str)
sampler.reset()
start_step = 0