HunyuanVideo 文生視頻模型實踐
flyfish
運行 HunyuanVideo 模型使用文本生成視頻的推薦配置(batch size = 1):
模型 | 分辨率 (height/width/frame) | 峰值顯存 |
---|---|---|
HunyuanVideo | 720px1280px129f | 60G |
HunyuanVideo | 544px960px129f | 45G |
- 本項目適用于使用 NVIDIA GPU 和支持 CUDA 的設備
- 模型在單張 80G GPU 上測試
- 運行 720px1280px129f 的最小顯存要求是 60GB,544px960px129f 的最小顯存要求是 45GB。
- 測試操作系統:Linux
HunyuanVideo: A Systematic Framework For Large Video Generation Model
HunyuanVideo/ckpts/文件夾下的模型
HunyuanVideo├──ckpts│ ├──README.md│ ├──hunyuan-video-t2v-720p│ │ ├──transformers│ │ │ ├──mp_rank_00_model_states.pt│ │ │ ├──mp_rank_00_model_states_fp8.pt│ │ │ ├──mp_rank_00_model_states_fp8_map.pt├ │ ├──vae│ ├──text_encoder│ ├──text_encoder_2├──...關鍵配置項:| 參數 | 默認值 | 描述 |
|:----------------------:|:---------:|:-----------------------------------------:|
| `--prompt` | None | 用于生成視頻的 prompt |
| `--video-size` | 720 1280 | 生成視頻的高度和寬度 |
| `--video-length` | 129 | 生成視頻的幀數 |
| `--infer-steps` | 50 | 生成時采樣的步數 |
| `--embedded-cfg-scale` | 6.0 | 文本的控制強度 |
| `--flow-shift` | 7.0 | 推理時 timestep 的 shift 系數,值越大,高噪區域采樣步數越多 |
| `--flow-reverse` | False | If reverse, learning/sampling from t=1 -> t=0 |
| `--neg-prompt` | None | 負向詞 |
| `--seed` | 0 | 隨機種子 |
| `--use-cpu-offload` | False | 啟用 CPU offload,可以節省顯存 |
| `--save-path` | ./results | 保存路徑 |## 結果
```csharp
(HunyuanVideo) sss@sss-Super-Server:~/source/HunyuanVideo$ python3 sample_video.py \--video-size 544 960 \--video-length 129 \--infer-steps 50 \--prompt "A cat walks on the grass, realistic style." \--flow-reverse \--use-cpu-offload \--save-path ./results
Namespace(model='HYVideo-T/2-cfgdistill', latent_channels=16, precision='bf16', rope_theta=256, vae='884-16c-hy', vae_precision='fp16', vae_tiling=True, text_encoder='llm', text_encoder_precision='fp16', text_states_dim=4096, text_len=256, tokenizer='llm', prompt_template='dit-llm-encode', prompt_template_video='dit-llm-encode-video', hidden_state_skip_layer=2, apply_final_norm=False, text_encoder_2='clipL', text_encoder_precision_2='fp16', text_states_dim_2=768, tokenizer_2='clipL', text_len_2=77, denoise_type='flow', flow_shift=7.0, flow_reverse=True, flow_solver='euler', use_linear_quadratic_schedule=False, linear_schedule_end=25, model_base='ckpts', dit_weight='ckpts/hunyuan-video-t2v-720p/transformers/mp_rank_00_model_states.pt', model_resolution='540p', load_key='module', use_cpu_offload=True, batch_size=1, infer_steps=2, disable_autocast=False, save_path='./results', save_path_suffix='', name_suffix='', num_videos=1, video_size=[544, 960], video_length=129, prompt='A cat walks on the grass, realistic style.', seed_type='auto', seed=None, neg_prompt=None, cfg_scale=1.0, embedded_cfg_scale=6.0, use_fp8=False, reproduce=False, ulysses_degree=1, ring_degree=1)
2024-12-21 21:50:51.616 | INFO | hyvideo.inference:from_pretrained:154 - Got text-to-video model root path: ckpts
2024-12-21 21:50:51.616 | INFO | hyvideo.inference:from_pretrained:189 - Building model...
2024-12-21 21:50:52.098 | INFO | hyvideo.inference:load_state_dict:340 - Loading torch model ckpts/hunyuan-video-t2v-720p/transformers/mp_rank_00_model_states.pt...
/home/sss/tool/HunyuanVideo/hyvideo/inference.py:341: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.state_dict = torch.load(model_path, map_location=lambda storage, loc: storage)
2024-12-21 21:51:05.739 | INFO | hyvideo.vae:load_vae:29 - Loading 3D VAE model (884-16c-hy) from: ./ckpts/hunyuan-video-t2v-720p/vae
/home/sss/tool/HunyuanVideo/hyvideo/vae/__init__.py:39: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.ckpt = torch.load(vae_ckpt, map_location=vae.device)
2024-12-21 21:51:07.546 | INFO | hyvideo.vae:load_vae:55 - VAE to dtype: torch.float16
2024-12-21 21:51:07.577 | INFO | hyvideo.text_encoder:load_text_encoder:28 - Loading text encoder model (llm) from: ./ckpts/text_encoder
Loading checkpoint shards: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:01<00:00, 2.42it/s]
2024-12-21 21:51:10.888 | INFO | hyvideo.text_encoder:load_text_encoder:50 - Text encoder to dtype: torch.float16
2024-12-21 21:51:10.890 | INFO | hyvideo.text_encoder:load_tokenizer:64 - Loading tokenizer (llm) from: ./ckpts/text_encoder
2024-12-21 21:51:11.263 | INFO | hyvideo.text_encoder:load_text_encoder:28 - Loading text encoder model (clipL) from: ./ckpts/text_encoder_2
2024-12-21 21:51:11.331 | INFO | hyvideo.text_encoder:load_text_encoder:50 - Text encoder to dtype: torch.float16
2024-12-21 21:51:11.332 | INFO | hyvideo.text_encoder:load_tokenizer:64 - Loading tokenizer (clipL) from: ./ckpts/text_encoder_2
2024-12-21 21:51:11.454 | INFO | hyvideo.inference:predict:580 - Input (height, width, video_length) = (544, 960, 129)
2024-12-21 21:51:11.469 | DEBUG | hyvideo.inference:predict:640 - height: 544width: 960video_length: 129prompt: ['A cat walks on the grass, realistic style.']neg_prompt: ['Aerial view, aerial view, overexposed, low quality, deformation, a poor composition, bad hands, bad teeth, bad eyes, bad limbs, distortion']seed: Noneinfer_steps: 50num_videos_per_prompt: 1guidance_scale: 1.0n_tokens: 67320flow_shift: 7.0embedded_guidance_scale: 6.0
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 50/50 [01:25<00:00, 42.71s/it]
2024-12-21 21:54:32.410 | INFO | hyvideo.inference:predict:669 - Success, time: 200.9416298866272
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:- Avoid using `tokenizers` before the fork if possible- Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)
2024-12-21 21:54:34.807 | INFO | __main__:main:55 - Sample save to: ./results/seed452372_A cat walks on the grass, realistic style.mp4
代碼
import os
import time
from pathlib import Path
from loguru import logger
from datetime import datetimefrom hyvideo.utils.file_utils import save_videos_grid
from hyvideo.config import parse_args
from hyvideo.inference import HunyuanVideoSamplerdef main():# 解析命令行參數args = parse_args()# 打印解析得到的參數,方便調試和查看輸入信息print(args)# 將模型的根路徑轉換為 Path 對象,方便后續路徑操作models_root_path = Path(args.model_base)# 檢查模型根路徑是否存在,如果不存在則拋出異常if not models_root_path.exists():raise ValueError(f"`models_root` 不存在: {models_root_path}")# 創建保存樣本的文件夾# 如果 save_path_suffix 為空,則使用 save_path 作為保存路徑,否則將后綴添加到 save_path 后save_path = args.save_path if args.save_path_suffix == "" else f'{args.save_path}_{args.save_path_suffix}'# 若保存路徑不存在,則創建該目錄,exist_ok=True 表示如果目錄已存在不會引發異常if not os.path.exists(args.save_path):os.makedirs(save_path, exist_ok=True)# 從預訓練模型的路徑加載 HunyuanVideoSampler 模型,并傳入解析得到的命令行參數hunyuan_video_sampler = HunyuanVideoSampler.from_pretrained(models_root_path, args=args)# 獲取更新后的參數,可能是因為加載模型時對參數進行了某些調整args = hunyuan_video_sampler.args# 開始采樣# TODO: 批量推理檢查,這里可能需要后續完善批量推理的檢查邏輯outputs = hunyuan_video_sampler.predict(# 輸入的提示信息,用于引導視頻生成prompt=args.prompt, # 視頻的高度height=args.video_size[0],# 視頻的寬度width=args.video_size[1],# 視頻的長度video_length=args.video_length,# 隨機種子,用于保證結果的可重復性seed=args.seed,# 負向提示信息,可能用于引導模型避免生成某些內容negative_prompt=args.neg_prompt,# 推理的步數,可能影響生成視頻的質量和細節infer_steps=args.infer_steps,# 引導規模,可能影響生成結果與提示的符合程度guidance_scale=args.cfg_scale,# 每個提示對應的視頻數量num_videos_per_prompt=args.num_videos,# 可能與視頻流的偏移有關的參數flow_shift=args.flow_shift,# 推理的批量大小batch_size=args.batch_size,# 可能是嵌入的引導規模參數embedded_guidance_scale=args.embedded_cfg_scale)# 從輸出中獲取采樣得到的樣本,可能是生成的視頻數據samples = outputs['samples']# 保存樣本# 檢查是否處于分布式環境或當前進程是否是主進程,可能用于分布式訓練時的保存操作if 'LOCAL_RANK' not in os.environ or int(os.environ['LOCAL_RANK']) == 0:for i, sample in enumerate(samples):# 為當前樣本添加一個維度,可能是為了滿足后續保存操作的格式要求sample = samples[i].unsqueeze(0)# 獲取當前時間并格式化為字符串,作為時間戳time_flag = datetime.fromtimestamp(time.time()).strftime("%Y-%m-%d-%H:%M:%S")# 構建保存樣本的路徑,包含時間戳、種子信息和提示信息save_path = f"{save_path}/{time_flag}_seed{outputs['seeds'][i]}_{outputs['prompts'][i][:100].replace('/','')}.mp4"# 使用 save_videos_grid 函數保存視頻,幀率為 24save_videos_grid(sample, save_path, fps=24)# 記錄樣本保存的路徑信息,方便查看保存位置logger.info(f'樣本保存到: {save_path}')if __name__ == "__main__":main()
VAE 理解
VAE即變分自編碼器(Variational Autoencoder),是一種生成模型,以下是對其的詳細介紹:
基本架構
? 編碼器:將輸入數據編碼成潛在空間中的概率分布參數,通常是輸出一個均值向量和一個方差向量,這兩個向量共同描述了潛在變量的正態分布。
? 解碼器:從潛在空間的概率分布中采樣得到潛在變量,然后將其解碼還原為與輸入數據相似的輸出。
工作原理
? 編碼過程:輸入數據通過編碼器網絡,編碼器學習到輸入數據的潛在特征,并將這些特征表示為潛在空間中的概率分布參數,即均值和方差。
? 重參數化技巧:由于概率分布無法直接進行梯度下降優化,VAE采用重參數化技巧,將潛在變量的采樣過程轉化為可微分的操作。具體來說,通過引入一個隨機噪聲變量,將其與均值和方差相結合,從而得到潛在變量的樣本,這樣就可以在反向傳播過程中計算梯度并更新網絡參數。
? 解碼過程:采樣得到的潛在變量輸入到解碼器網絡,解碼器根據這些潛在特征嘗試重建原始輸入數據。
? 損失函數:VAE的損失函數由兩部分組成,一部分是重建誤差,衡量重建數據與原始數據之間的差異,通常使用均方誤差等指標;另一部分是KL散度,衡量編碼器輸出的概率分布與先驗分布(一般為標準正態分布)之間的差異,通過最小化KL散度,使潛在空間的分布更加平滑和連續,有助于生成更高質量的新樣本。
特點
? 生成能力:能夠學習數據的潛在分布,從而生成與訓練數據相似但又不完全相同的全新樣本,可用于圖像生成、文本生成等任務。
? 連續且有結構的隱空間:在潛在空間中學習到的表示是連續且有結構的,這使得樣本插值和生成更加自然,也便于進行各種基于潛在空間的操作,如插值、算術運算等,以探索數據的不同特征和屬性。
? 概率建模:通過最大化似然函數,能夠有效地捕捉數據的復雜分布,為數據建模提供了一種概率視角,有助于更好地理解和解釋數據的生成過程。
應用場景
? 圖像生成:可以生成各種類型的圖像,如手寫數字、人臉圖像、自然景觀等,通過調整潛在變量的值,可以控制生成圖像的不同特征,如人臉的表情、年齡、性別等。
? 數據增強:在訓練數據有限的情況下,利用VAE生成與原始數據相似的新樣本,擴充數據集,提高模型在分類、回歸等任務上的性能和泛化能力。
? 異常檢測:先學習正常數據的分布,然后對測試數據進行重建,如果重建誤差較大,則認為該數據是異常數據,可用于工業設備故障檢測、網絡安全入侵檢測等領域。
? 特征學習與降維:通過編碼器將高維數據壓縮成低維的潛在表示,這些潛在特征可以用于后續的機器學習任務,如分類、聚類等,同時也可以實現數據的可視化降維。
variational 理解
在變分自編碼器(Variational Autoencoder,VAE)中,“variational”一詞主要來源于其采用的變分推斷(Variational Inference)方法,以下是對其具體理解:
“variational”體現了VAE在建模過程中對數據不確定性的一種變分處理方式,通過引入變分分布并利用變分推斷方法來近似難以直接計算的真實后驗分布,從而能夠在潛在空間中學習到數據的概率分布,實現有效的數據生成和特征學習。這種變分的思想使得VAE在生成模型領域具有獨特的優勢,能夠生成具有多樣性且符合數據分布的新樣本。
變分推斷的引入背景
? 在傳統的自編碼器中,編碼器將輸入數據編碼為一個確定性的低維表示,即一個固定向量。然而,這種表示方式無法捕捉數據的不確定性信息,也不利于生成新的樣本。而VAE的目標是學習數據的概率分布,以便能夠生成與訓練數據相似的新樣本,這就需要引入概率模型和推斷方法來處理數據的不確定性,變分推斷便應運而生。
變分推斷的基本思想
? 概率圖模型框架:VAE可以看作是一個概率圖模型,其中輸入數據x與潛在變量z之間存在某種概率關系。理想情況下,我們希望直接計算后驗分布p(z|x),即給定輸入數據x時潛在變量z的分布,從而了解數據的內在結構和不確定性。然而,這個后驗分布往往難以直接計算,因為它涉及到復雜的積分運算。
? 變分分布的引入:變分推斷通過引入一個變分分布q(z|x),來近似真實的后驗分布p(z|x)。這個變分分布q(z|x)是參數化的,其參數可以通過優化過程來學習,使其盡可能地接近真實的后驗分布。在VAE中,編碼器的作用就是學習這個變分分布q(z|x)的參數,通常是輸出潛在變量z的均值和方差,從而定義了一個以這些參數為特征的正態分布作為變分分布。
優化過程與KL散度
? 證據下界(ELBO):為了衡量變分分布q(z|x)與真實后驗分布p(z|x)之間的相似程度,變分推斷定義了一個證據下界(Evidence Lower BOund,ELBO),它是模型對數似然函數的一個下界。ELBO由兩部分組成,一部分是重建誤差,衡量解碼器根據潛在變量z重建輸入數據x的質量;另一部分是KL散度,衡量變分分布q(z|x)與先驗分布p(z)之間的差異。
? 優化目標:VAE的訓練目標就是最大化ELBO,這等價于最小化變分分布q(z|x)與真實后驗分布p(z|x)之間的KL散度,同時最大化重建誤差。通過這種方式,編碼器學習到的變分分布能夠更好地近似真實的后驗分布,使得潛在變量z能夠有效地捕捉輸入數據x的不確定性信息,為生成新樣本提供有力支持。