视频生成

三个视频家族:LTX 2.5 Distilled 联合生成视频与 48 kHz 立体声,MiniMax-H3 联合生成视频与 32 kHz 立体声,Wan 2.1 / 2.2 生成视频。下面说明当前实现、文件、配方与实际验证范围。

💡

这些命令遵循与文本模型示例相同的约定:Hugging Face CLI、从仓库根目录源码构建,并把 --backend 换成与你硬件匹配的值。参见按家族下载与运行。

LTX 2.5 Distilled(视频与 48 kHz 立体声)

当前源码实现完整两阶段蒸馏管线:专用 48 层 Gemma 4 Unified 编码器、音视频连接器、联合去噪器、视频 VAE 与音频 VAE/声码器。先以输出尺寸的一半运行 8 个发布方指定的 ancestral Euler 步,随后用必需的 2 倍潜变量放大器放大,再以完整尺寸精修 3 步;CFG 固定为 1。steps: 8 指第一阶段的固定调度。显式 --sampler euler 仅用于确定性诊断。

config/ltx-2.5-distilled.json 固定五个文件的提交与 SHA-256,共 44,764,412,884 字节。官方伴随文件需要先获得 Hugging Face 访问许可,并设置 HF_TOKEN、HUGGING_FACE_HUB_TOKEN 或 HF CLI 令牌缓存。专用编码器内嵌分词器;普通 Gemma 4 检查点不能替代。默认使用卷积视频 VAE。

TENSORSHARP_MODELS=/path/to/models MLX_ENABLE_TF32=0 \
dotnet run --project TensorSharp.Cli -- \
  --config config/ltx-2.5-distilled.json --backend mlx \
  --prompt "A red toy car drives along a quiet road in warm afternoon light." \
  --diffusion-seed 42 --output ltx25.mp4

默认输出 1024×1024、17 帧、24 fps;宽高必须是 64 的倍数,帧数遵循 8k+1。TensorAgent 的 LTX 2.5 Distilled — Q4_K_M + BF16 实验条目使用保守的 48 GB 档位,Download 下载五个文件,Use 激活模型;这不是已证明的最低内存,也不保证任意尺寸和时长。

cuda / ggml_cuda 使用 TensorSharp 的直接 CUDA 执行器,mlx / ggml_metal 使用 MLX,cpu / ggml_cpu 使用直接 CPU 实现;不支持 Vulkan 与多 GPU。Apple 进程启动时设置 MLX_ENABLE_TF32=0 可保持 Float32 质量路径;Mac 应用在启动时设置这个默认值。桌面 MP4 音轨需要 FFmpeg 或 TS_FFMPEG,同时保留原始 48 kHz 立体声 WAV;Mac 应用使用 AVFoundation。

时间引导与身份参考

支持首帧、尾帧、首尾帧与位于指定输出帧位置的多张图像。普通 referenceImages 列表会被当作均匀分布的时间关键帧;一张图像固定第零帧。videoMode: "ref" 则需要可选的 Ingredients IC-LoRA,把人物、道具或场景作为身份参考,并保留一次完整尺寸生成;其训练预览为 768×448×121。参考视频/音频、retake、专门控制管线、HDR/EXR 与多 GPU 尚不可用。

--video-upscaler / TS_VIDEO_UPSCALER 指定放大器;可选 --video-diffusion-vae 选择颗粒/闪烁更少但较柔和的扩散解码器,仅支持 MLX/CPU,CUDA 会拒绝。实际 CUDA、MLX 与 Mac Catalyst 短片验证、数值参考与局限详见模型卡与复现指南。组件测试、旧版单阶段耗时与成功导出均不能证明当前两阶段管线的通用质量或性能对等。

MiniMax-H3(提示词 → 视频带音频)

H3 把视频与 32 kHz 立体声作为一条打包潜变量联合去噪。FL2VA 支持文本、图生视频与首尾帧,Ref2VA 支持身份参考。检查点经过 CFG 蒸馏,要求 --cfg 1.0;默认 20 步。减少基础步数可以更快得到较粗糙的预览,而 Turbo 插件让 FL2VA 只按蒸馏时的 8 或 4 步采样。当前性能与精度以 2026-10-15 验证为准;旧的 Metal 与 16 GB 笔记本速度倍数已被替代。需要以下四个网络,以及分词器文件:

# 去噪器 + 文本编码器
hf download unsloth/MiniMax-H3-GGUF minimax_h3_fl2va_pruned-Q4_K.gguf --local-dir models
hf download unsloth/MiniMax-H3-GGUF qwen3vl_32b_minimax_h3-Q4_K_M.gguf --local-dir models
# 两个 VAE
hf download Comfy-Org/MiniMax-H3 vae/minimax_h3_video_vae_fp16.safetensors --local-dir models
hf download Comfy-Org/MiniMax-H3 vae/minimax_h3_audio_vae_fp32.safetensors --local-dir models
# 文本编码器不带分词器:从上游仓库取三个文件,放在编码器旁边
curl -L -o models/vocab.json https://huggingface.co/MiniMaxAI/MiniMax-H3/resolve/42ed227ee7df40d41602854ae760620d6eb651fe/processor/vocab.json
curl -L -o models/merges.txt https://huggingface.co/MiniMaxAI/MiniMax-H3/resolve/42ed227ee7df40d41602854ae760620d6eb651fe/processor/merges.txt
curl -L -o models/tokenizer_config.json https://huggingface.co/MiniMaxAI/MiniMax-H3/resolve/42ed227ee7df40d41602854ae760620d6eb651fe/processor/tokenizer_config.json

# 文生视频(带音频)。输出 fox.mp4 和 fox.wav
dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --model models/minimax_h3_fl2va_pruned-Q4_K.gguf \
    --backend ggml_metal --prompt "a red fox trotting through falling snow, cinematic" \
    --width 640 --height 384 --video-frames 22 --diffusion-steps 8 --cfg 1.0 --output fox.mp4

文本编码器 GGUF 不带分词器。随仓库提供的配置会将已固定版本的 vocab.json、merges.txt 与 tokenizer_config.json 下载到编码器旁边。tokenizer_config.json 定义 <|vision_start|>、<|image_pad|> 与 <|vision_end|>;手动安装时可用 TS_VIDEO_TOKENIZER 指定目录。

单个配置 config/minimax-h3-fl2va.json 会下载两种模式各自的去噪器、共享的文本编码器与 VAE,以及分词器文件(总计约 46 GB)。同一个加载中的服务可在首尾帧与参考模式间切换,无需重启:

dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --config config/minimax-h3-fl2va.json \
    --prompt "a red fox trotting through falling snow, cinematic" --output fox.mp4
dotnet TensorSharp.Server.Host/bin/TensorSharp.Server.Host.dll --config config/minimax-h3-fl2va.json

一张图代表什么

一张图对 H3 有两种完全不同的含义,而且用不同的检查点。不指定时会自动推断,--video-mode 用于显式声明。

你想要什么--video-mode检查点
"让这张照片动起来"——它成为第一帧i2vminimax_h3_fl2va_pruned-*
"从照片 A 变到照片 B"fl2vminimax_h3_fl2va_pruned-*
"用这个人物,生成全新场景"refminimax_h3_ref2va_pruned-*
"参考这个产品/人物,但换机位、背景和构图"refminimax_h3_ref2va_pruned-*
只有文本t2v都可以

两个去噪器是不同的文件而不是一个开关,而且各自会指名拒绝对方的输入:在 FL2VA 上要求 ref,报错会告诉你去加载 minimax_h3_ref2va_pruned-*.gguf;在 Ref2VA 上把 --image/--end-image 当关键帧传,报错会告诉你去加载 FL2VA 那个文件,或者改用 --ref-image 传这张图。关键帧与显式参考同时出现时会被直接拒绝,而不是悄悄丢掉其中一个——一段看起来"照做了"的视频才是更糟的失败。用的是哪个分区由文件名决定:名字里含 ref2va(不区分大小写)即选择参考条件路径,重命名或重新量化时请把它保留下来。

# 让照片动起来:它成为第一帧
dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --model models/minimax_h3_fl2va_pruned-Q4_K.gguf \
    --backend ggml_metal --image portrait.jpg \
    --prompt "the person turns toward the camera and smiles, subtle handheld motion" \
    --video-frames 22 --diffusion-steps 8 --cfg 1.0 --output animated.mp4

# 首尾帧:两端钉住,模型补出中间运动
dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --model models/minimax_h3_fl2va_pruned-Q4_K.gguf \
    --backend ggml_metal --image start.png --end-image end.png \
    --prompt "a slow cinematic push-in" \
    --video-frames 22 --diffusion-steps 8 --cfg 1.0 --output morph.mp4

# 参考:保留主体,机位、背景和构图全部重来
dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --model models/minimax_h3_ref2va_pruned-Q4_K.gguf \
    --backend ggml_metal --ref-image person.jpg --ref-image bottle.png \
    --prompt "she holds the bottle up to the light on a rooftop at golden hour, slow orbit" \
    --width 640 --height 384 --video-frames 22 --diffusion-steps 20 --cfg 1.0 --output rooftop.mp4

两个关键帧示例有意不给尺寸:i2v 与 fl2v 保留关键帧的构图,画布按 MiniMax 发布的 768p 配方由第一个关键帧决定——3:2 照片为 1152×768,2:3 竖图为 768×1152(见尺寸)。参考图只会被缩小并保持自身宽高比,输出画布仍由 --width/--height 决定,两者都不给时为 640×384。在 Ref2VA 检查点上,普通的 --image 会被当作单张参考,并按该图宽高比保持 640×384 的面积,因此只会"上传一张图"的客户端(包括 Web UI)无需改动即可使用。

参考:静图、片段与音轨

最多 九个参考,可以任意混搭。--ref-image 是静图,提示词里按位置称作 <Picture 1>、<Picture 2>……;--ref-video 是片段——视频文件或帧目录——会被重采样到 H3 自己的 24 fps 与自己的画布,称作 <Video 1>、<Video 2>……;--ref-audio 是音轨,重采样到音频 VAE 的 32 kHz 立体声并截断到所生成片段的时长。片段自带的声音要用 --ref-video-audio 单独给出,按位置与 --ref-video 配对,因为容器里的音轨无法通过帧解码器读取。

# 一个参考片段,外加它自己的音轨
dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --model models/minimax_h3_ref2va_pruned-Q4_K.gguf \
    --backend ggml_metal --ref-video walk.mp4 --ref-video-audio walk.wav \
    --prompt "the same woman walks along a beach at sunset, wide shot, waves behind her" \
    --width 640 --height 384 --video-frames 22 --diffusion-steps 20 --cfg 1.0 --output beach.mp4

# 一张静图,加一段独立音轨
dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --model models/minimax_h3_ref2va_pruned-Q4_K.gguf \
    --backend ggml_metal --ref-image singer.jpg --ref-audio song.wav \
    --prompt "she performs on a small club stage under a single spotlight" \
    --width 640 --height 384 --video-frames 22 --diffusion-steps 20 --cfg 1.0 --output stage.mp4

每个参考都是同一条打包序列里的额外 token,因此去噪器这边的开销是线性的、也容易预估:640×384 的参考是 240 个 token,576×448 的是 252 个;在 RTX 3080 Laptop 上对 640×384、22 帧的片段实测,单个去噪步从没有参考时的 4.37 s 变成八个参考时的 9.38 s——大约每个参考每步 626 ms,从一个到八个基本恒定。超过大约四个参考之后,主导的就变成 Qwen3-VL 那一趟,而不是去噪器:每个参考会给提示词加上约 250 个视觉占位 token,而这条提示词要过完整 50 层——两个参考是 548 token 的提示词,八个就是 2086,文本条件从约 65 s 涨到约 447 s,而整个 8 步去噪只要约 75 s。与其减少步数,不如先减少参考数量、提高参考质量。

参考片段是 H3 最昂贵的输入:22 帧 448×320 的参考会在输出本身所需的 1680 个 token 之外再加 980 个条件 token,而且 VAE 还得先把这 22 帧全部编码——在 M5 Pro 上实测,编码 14 s,之后每个去噪步约 9 s,而不带参考时只要 5 s。语言模型以 2 fps、每次两帧的方式看这段片段;去噪器则以潜变量的形式看到全部内容。

长度、尺寸,以及长片段的上限

不指定尺寸时,文生视频与参考为 640×384;图生视频与首尾帧按 MiniMax 发布的 768p 配方由第一个关键帧(只有末帧时用末帧)决定画布:保持其宽高比、短边 768 像素、不超过 768×1344 像素,每边取最接近的 32 的倍数——3:2 照片为 1152×768,2:3 竖图为 768×1152,4:3 为 1024×768,16:9 为 1344×768,方图为 768×768,与 vllm-omni 和 sglang 一致;宽高比夹紧在 1:4 至 4:1 之间,多余部分居中裁掉。这两种模式保留关键帧的构图,而视频 VAE 在每个方向下采样 16 倍:全身照放进 640×384 后人脸只有约 60 像素高,仅 VAE 编码再解码就已经糊掉,与去噪器无关;在配方画布上人脸得以保留。代价是每帧视频 token 约为 640×384 的 2.4 倍(方图)到 4.2 倍(16:9 及更宽),4:3 为 3.2 倍、3:2 为 3.6 倍,而注意力开销增长得更快,因此一次运行要多花数倍时间。同时给出 --width 与 --height 会对所有模式(包括关键帧)固定同一尺寸;只给一个时,另一边按图像宽高比推出。TensorAgent 应用在其 32 GB 档位测得配方尺寸的峰值内存之前,仍按照片宽高比使用 640×384 面积的紧凑画布(3:2 为 608×416)。

显式给出的宽高向上取整到 32 的倍数。帧数向上对齐到 17k+5 网格——5、22、39、56、73、90……——这来自视频 VAE 的时间分块,而不是随意选定的;fps 无论请求写什么都固定为 24。VAE 实现了跨该网格的分块解码:VAE 每次跑 5 个潜变量帧、带 2 帧前瞻,并对接缝做交叉淡化。反过来,一次性解码长片段会让细节被逐渐冲淡——对着条件照片测量,第 0 帧的相关度从 22 帧时的 0.97 掉到 90 帧时的 0.86——所以分块是正确性要求,而不是优化。只想要画面时用 --no-audio 跳过音频解码,省下音频 VAE 的时间与内存。

⚠️

长片段过去会整片全黑。H3 在一条不加掩码的打包序列上做双向注意力,因此它的 key 数量就是整段片段——22 帧是 2364 个 token,107 帧是 8646——而所有上游 ggml flash attention 内核都把 softmax 分子保持在 FP16,只留三个比特的余量。107 帧的片段在第一个去噪步就溢出成 NaN,回来时每个像素全黑、每个音频采样被削平,而同一条请求在 73 帧下却完全正常。现在 h3_attend 会按 key 数量取一个 2 的幂预先缩放 V,并在输出上还原——由于注意力对 V 是线性的,这一步是精确的;采样器也不再保存已经发散的片段,而是让请求失败并指出是哪一步,不再写出一个全黑文件。设 TS_H3_TRACE=1 可打印每一步的潜变量与速度幅值。

💡

音频导出保留独立 .wav。MP4 是否含音轨取决于导出提供器:FFmpeg 或 Apple AVFoundation 路径可以混入音轨;其他路径可能返回视频与单独 WAV。可检查路由的 audioMuxed,或用 ffmpeg -i fox.mp4 -i fox.wav -c:v copy -c:a aac fox_with_audio.mp4 合并。分块解码与有限输出不能证明长片段感知质量。详见模型卡。

Turbo 插件:8 步或 4 步

LightX2V 的 Turbo LoRA 把 FL2VA 检查点蒸馏为 8 或 4 次 Transformer 前向,取代基础的 20 步。两份插件配置 config/lora/minimax-h3-fl2v-turbo-8step.json 与 -4step.json 在 CLI 与服务端上应用它们。每份在首次使用时从 lightx2v/Minimax-h3-Turbo 下载权重,固定在修订版 a29e57d 并校验 SHA-256(各 1,383,677,808 字节,Apache-2.0),保存到模型根目录下的 minimax-h3/loras/。

# CLI:插件的配方取代 --diffusion-steps / --flow-shift / --sampler
dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --config config/minimax-h3-fl2va.json \
    --lora config/lora/minimax-h3-fl2v-turbo-8step.json \
    --prompt "a red fox trotting through falling snow, cinematic" --output fox-turbo.mp4

# 服务端:每段片段都按插件的配方采样;请求不要带 "steps"
dotnet TensorSharp.Server.Host/bin/TensorSharp.Server.Host.dll --config config/minimax-h3-fl2va.json \
    --lora config/lora/minimax-h3-fl2v-turbo-4step.json
插件步数配方
LightX2V Turbo 8-step(FL2V v1.0,768p),-8step.json8端点包含的 linspace 网格上的 Euler,视频 shift 6、音频 shift 3、CFG 1,与发布方给出的一致
LightX2V Turbo 4-step(FL2V v1.2,768p),-4step.json4相同的网格与 shift。推断所得:发布方没有给出 v1.2 的配方,文件也未记录 shift,因此沿用其 4 步 v1.0 一行,直到经过验证

2026-10-15:当前性能与精度

以下是同一精度下的版本前后对照,并非对 stable-diffusion.cpp 的速度倍数。FL2VA Q4_K,640×384×22、24 fps、20 Euler 步、种子 42、CFG 1、flow shift 12;两轮交替运行,包含加载、生成与编码。M5 Pro 为 48 GiB 统一内存,A40 为 46,068 MiB 显存/CUDA 12.8;OS 缓存未受控,不是热模型服务基准。两边均使用未修改的上游 ggml 3612c0af93f2cfc8f048295300a61187723b12ae。

后端/主机基线中位数候选中位数结论
M5 Pro 原生 Metal151.246 s143.622 s延迟降低 5.0%
M5 Pro 直接 MLX143.264 s125.426 s延迟降低 12.5%
A40 原生 CUDA68.029 s67.174 s1.3% 差异不足以证明有意义的增益
A40 直接 CUDA,同精度220.456 s178.667 s延迟降低 19.0%

所有完整比较的解码 RGB 与音频完全一致。优化减少重复 RoPE/切片/调制工作,缓存原生 CUDA 图绑定,并让 MLX VAE 直接持有 F32 权重。固定潜变量检查中,采样的热进程 RSS 中位数 23.048 → 14.024 GiB;这不是峰值统一/GPU 内存的降低测量。

直接 CUDA 的 BF16 权重投影现在默认采用 pedantic F32,TS_H3_CUDA_BF16_PEDANTIC=0 才会恢复旧近似。独立 PyTorch F32 全 50 层合成参考中,音频/视频相对 L2 误差为 0.000125891 / 0.000084754,均通过 0.001 门限;这不是自然媒体的多步感知质量验证。实验性批量注意力未通过完整生成质量门限,模型开关已移除。

最终构建另有 CPU/MLX 与远端 CUDA 聚焦测试、四条 GPU 通道各九个组件参考检查及完整生成保真对照;这些保真对照不增加速度样本。TS_H3_PHASE=1 打印阶段耗时,TS_H3_TRACE=1 打印潜变量诊断。完整条件、结果和被拒绝的实验见复现指南。

Metal 上的长序列注意力。FL2VA 上一段 1280×768×107 的首尾帧片段有 34,991 个打包 token,在这个长度下每一步都以注意力为主。ggml-metal 只有在 key 数为 64 的倍数时才会把长 flash attention 放到 Metal 4 tensor-API 内核上,因此 TensorSharp 用带掩码的零行把 key 补齐(结果不变),并把 query 分块运行、共用一个很小的掩码。在 M5 Pro 上用 ggml_metal、未修改的 ggml a05c9922613c6a2230dbb1d0de679519ecafb4f0、20 步、种子 42,整次运行从 11,553.8 秒降到 5,438.5 秒(2.12×),每步约从 570 秒降到 263 秒。这是单机上的单次运行,按 CLI 报告的整段片段计时;TS_METAL_FLASH_KV_PAD=0 可关闭补齐以做 A/B 对比。

后端与验证范围

六个后端有独立执行路径:ggml_metal、ggml_cuda、ggml_cpu 使用 TensorSharp 原生 ggml 集成,mlx、cuda、cpu 使用直接实现。纯 C# CPU 支持文本、首尾帧与参考图像/视频/音频,无需 ggml;完整检查点吞吐仍受限于主机资源。合成组件、技术冒烟和成功写出文件不构成所有模式的感知质量验证;图像与音频参考的具体门限见模型卡。视频噪声种子使用 --diffusion-seed。

部署到服务端

在服务端,尺寸是启动参数,因为浏览器不会自己发尺寸;而对 H3 推荐的做法是不设 --video-width / --video-height:这样文生视频与参考为 640×384,i2v/fl2v 采用第一个关键帧的配方尺寸。两个都设会对所有模式(包括关键帧)固定同一尺寸,并关闭 Studio 的“按首帧(模型推荐配置)”尺寸(--width / --height 是别名;只给其中一个时,H3 会从条件图像的宽高比推出另一个)。--video-steps 20 与默认步数一致。--video-mode 用于给只提供一种模式的部署固定条件模式;不写它,则每条请求各自推断,通常这才是你想要的。--video-frames、--fps、--video-vae、--video-text-encoder 与 --audio-vae 补齐其余部分。服务端完全没有 --cfg,这也是随仓库提供的配置既不设步数也不设引导的原因。

dotnet TensorSharp.Server.Host/bin/TensorSharp.Server.Host.dll --model models/minimax_h3_fl2va_pruned-Q4_K.gguf \
    --backend ggml_metal --video-steps 20 --video-frames 22

curl -s http://localhost:5000/api/video-generate -H 'content-type: application/json' -d '{
  "prompt": "a red fox trotting through falling snow, cinematic",
  "frames": 22, "steps": 8, "cfg": 1.0,
  "imagePath": "card.jpeg", "videoMode": "i2v"
}'

要按 Turbo 配方提供服务,加上 --lora config/lora/minimax-h3-fl2v-turbo-8step.json(或 4 步那份),--video-steps 不设或设为插件的步数;此后请求不要带 steps,与 Web UI 一样。示例中的 "steps": 8 只有 8 步插件接受,冲突的取值会被拒绝而不是被覆盖。

有三条路由接收这个请求体:POST /api/video-generate、POST /api/video-generate/stream(同样的东西,外加 SSE 进度事件)以及 OpenAI 形状的 POST /v1/videos/generations。三者共用同一个解析器,因此 videoMode、generateAudio、endImage、referenceImages、referenceVideos、referenceAudios 与 referenceVideoAudios 在三条路由上都可用,并且各自还接受下划线写法;referenceVideoAudios 按下标与 referenceVideos 配对。响应里带 audioUrl(OpenAI 路由上是 audio_url),模型没有产生音轨时为 null;除 OpenAI 路由外还带 audioMuxed,表示 MP4 里是否也带着这条音轨。所有文件字段都必须指向此前通过 /api/upload 上传的文件,并被限制在上传目录之内。模型层面的拒绝(加载了错误的检查点、关键帧与参考混用)会以 400 加上模型自己的报错信息返回,而不是笼统的 500。

GET /api/models 会返回一个 video 对象——非视频模型一律为 null——其中包含 family(minimax-h3)、supportsAudio、supportsImageConditioning、supportsEndImageConditioning、supportsReferenceConditioning 与 maxReferenceImages。Web UI 正是靠它判断该不该提供首帧、尾帧,或最多九个参考:它根据 supportsReferenceConditioning 启用参考附件控件,并在 maxReferenceImages 处停止添加,而不是去匹配架构字符串。

Wan 2.1 / 2.2(提示词 → 视频、图像 → 视频)

请下载 Turbo 版 DiT,而不是基础版。基础的 Wan2.2-TI2V-5B 走官方的 50 步 × CFG 配方,也就是每段视频 100 次 DiT 前向;同一模型的步数蒸馏 Turbo 版本经过训练可在无引导的 4 步内完成,TensorSharp 会从文件名识别出来,于是同一条请求从数小时变成数分钟。Turbo 仓库只提供 DiT,VAE 与文本编码器仍从基础仓库获取:

# 快车道:DiT 前向 4 次而不是 100 次。注意 “Wan2_2” 用的是下划线。
hf download hum-ma/Wan2.2-TI2V-5B-Turbo-GGUF Wan2_2-TI2V-5B-Turbo-Q8_0.gguf --local-dir models
hf download QuantStack/Wan2.2-TI2V-5B-GGUF VAE/Wan2.2_VAE.safetensors --local-dir models
hf download city96/umt5-xxl-encoder-gguf umt5-xxl-encoder-Q8_0.gguf --local-dir models

# 文本 → 视频
dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --model models/Wan2_2-TI2V-5B-Turbo-Q8_0.gguf \
    --video-text-encoder models/umt5-xxl-encoder-Q8_0.gguf --video-vae models/VAE/Wan2.2_VAE.safetensors \
    --prompt "A red fox trotting through falling snow, cinematic" \
    --video-frames 81 --fps 24 --output out.mp4 --backend ggml_cuda
# 图像 → 视频(上传的图像作为首帧;仅 Wan 2.2 模型支持)
dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --model models/Wan2_2-TI2V-5B-Turbo-Q8_0.gguf \
    --video-text-encoder models/umt5-xxl-encoder-Q8_0.gguf --video-vae models/VAE/Wan2.2_VAE.safetensors \
    --image first_frame.png --prompt "the camera pushes in as the waves rise" \
    --output out.mp4 --backend ggml_cuda
dotnet TensorSharp.Server.Host/bin/TensorSharp.Server.Host.dll --model models/Wan2_2-TI2V-5B-Turbo-Q8_0.gguf \
    --video-text-encoder models/umt5-xxl-encoder-Q8_0.gguf --video-vae models/VAE/Wan2.2_VAE.safetensors \
    --backend ggml_cuda --video-frames 121 --fps 24

加载时控制台会打印 step-distilled checkpoint detected -> 4 steps, guidance off (--diffusion-steps / --cfg override) 确认识别成功。在 M5 Pro 上用 ggml_metal 实测:一条 1088×832、121 帧(5 秒,720p 级)的图生视频请求,基础检查点需要 100 次 DiT 前向、约 3 小时 30 分,Turbo 检查点只需 4 次、17 分 30 秒——参数相同、分辨率相同,只有 --model 路径不同。若改到 480p(736×544、121 帧),Turbo 检查点 6 分 19 秒即可完成。

若要复现基础配方——例如生成参考样本,或使用没有蒸馏版本的 Wan 2.1 检查点——只需替换第一条下载命令与 --model 路径:

hf download QuantStack/Wan2.2-TI2V-5B-GGUF Wan2.2-TI2V-5B-Q8_0.gguf --local-dir models

# 50 步 x 2 次 CFG 前向;--cfg-cache-stride 以少量精度换取 1.30x / 1.43x
dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --model models/Wan2.2-TI2V-5B-Q8_0.gguf \
    --video-text-encoder models/umt5-xxl-encoder-Q8_0.gguf --video-vae models/VAE/Wan2.2_VAE.safetensors \
    --prompt "A red fox trotting through falling snow, cinematic" \
    --video-frames 81 --fps 24 --cfg-cache-stride 2 --output out.mp4 --backend ggml_cuda

Wan 2.2 A14B 图生视频请把两个蒸馏专家下载到同一个 --local-dir——加载器会依据高噪专家的文件名找到对应的低噪专家,因此只需传一个 --model 路径。A14B 使用 Wan 2.1 的 VAE:

hf download jayn7/WAN2.2-I2V_A14B-DISTILL-LIGHTX2V-4STEP-GGUF \
    high_noise/wan2.2_i2v_A14b_high_noise_lightx2v_4step-Q4_K_M.gguf --local-dir models
hf download jayn7/WAN2.2-I2V_A14B-DISTILL-LIGHTX2V-4STEP-GGUF \
    low_noise/wan2.2_i2v_A14b_low_noise_lightx2v_4step-Q4_K_M.gguf --local-dir models
hf download QuantStack/Wan2.2-I2V-A14B-GGUF VAE/Wan2.1_VAE.safetensors --local-dir models

dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll \
    --model models/high_noise/wan2.2_i2v_A14b_high_noise_lightx2v_4step-Q4_K_M.gguf \
    --video-text-encoder models/umt5-xxl-encoder-Q8_0.gguf --video-vae models/VAE/Wan2.1_VAE.safetensors \
    --image first_frame.png --prompt "the camera pushes in as the waves rise" \
    --output out.mp4 --backend ggml_cuda
⚡

请在 Wan 训练过的分辨率上生成。Wan 的训练分辨率是 480p(832×480)与 720p(1280×704),TI2V-5B 原生就是 720p 模型。低于约 0.3 MP 时 DiT 已经超出分布,无论花多少步,画面都会发糊、不稳定并出现偏色——低于该阈值时流水线会给出警告。请先在受支持的分辨率上生成,再缩小,而不是直接生成小尺寸。

Wan 的伴随文件、成本与分辨率

Wan 把提示词——在 Wan 2.2 检查点上还可以加一张上传的首帧图像——变成 H.264 MP4。--model 指定的 GGUF 只是 DiT,TensorSharp 会在其旁边解析两个伴随文件(放在 VAE/、HighNoise/ 或 LowNoise/ 子目录中同样可以):

系列潜空间模式说明
Wan 2.1 T2V(1.3B / 14B)16 通道,8×8×4文本 → 视频单个 DiT
Wan 2.2 TI2V-5B48 通道,16×16×4文本 → 视频、图像 → 视频稠密 5B,24 fps,可出 720p
Wan 2.2 A14B(T2V / I2V)16 通道(I2V 输入 36 通道)文本 → 视频、图像 → 视频两个 14B 专家在时间步边界切换

每个去噪步是一张常驻权重的 ggml 图(CUDA 图捕获、flash attention;TI2V 图生视频还带 per-token 时间步调制),视频 VAE 的编码与解码各是一张图。各阶段按顺序交接显存——先文本编码器,再 DiT,最后 VAE——因此峰值约为 max(TE, DiT + attention, VAE):TI2V-5B 在 16 GB GPU 上 8 分钟内生成 81 帧 480p 图生视频,A14B 的两个专家也能在同一张卡上顺序执行。数值上与 diffusers 对齐(DiT 余弦相似度 > 0.995,VAE 编码器 > 0.999,解码 59.9 dB PSNR),同等负载下 Wan 2.1 端到端比 stable-diffusion.cpp 快 6.0×。

检查点决定端到端耗时

Wan 的 DiT token 数为 latent_frames × (h/2) × (w/2),自注意力开销为 O(tokens²),因此 5 秒 720p 视频确实是个大活:1088×832、121 帧就是 27 404 个 token,而 TI2V-5B 的官方配方要在其上跑 50 步 × 2 次无分类器引导前向 = 100 次 DiT 前向。步数蒸馏检查点经过训练可在无引导的 4 步内完成,因此同一段视频只需 4 次前向——去噪工作量只有 1/25。TensorSharp 会从 DiT 文件名识别(turbo、distill、lightning、lightx2v、fastwan、-dmd,或显式的 …-4steps-…,步数取 1–16),在加载时打印 step-distilled checkpoint detected -> 4 steps, guidance off 并自动套用该配方;--diffusion-steps 与 --cfg 仍可覆盖。

M5 Pro、ggml_metal、TI2V-5B Q8_0、1088×832×121 帧(27 404 token,图 → 视频)基础版,优化前基础版,现在Turbo,现在
DiT 前向次数100(50 步 × CFG)1004(无引导)
单次前向206.2 s120.2 s120.2 s
去噪总耗时20 615 s12 020 s481 s
VAE 解码 121 帧863 s563 s563 s
端到端约 5 小时 58 分约 3 小时 30 分17 分 30 秒

两条加速路径互相独立:单次前向约 1.7× 来自把 DiT flash 注意力的 K/V 保持为 F16(单次 27k token 自注意力实测 2.02×,与 diffusers 参考实现的余弦相似度 0.999964——TS_WAN_DIT_KV_F16=0 可恢复 F32),以及在 Metal 上把 Wan VAE 卷积改走 MPSGraph 而非 ggml 的 im2col+GEMM 降级路径(736×544×81 帧下 VAE 解码 159 s → 80 s,数值不变,93.9 dB PSNR——TS_WAN_VAE_MPS_CONV=0 可回退);另一条是蒸馏检查点带来的前向次数减少 25×。一旦用上蒸馏检查点,瓶颈就变成 VAE 解码(约占整轮的 55%),而不再是 DiT。

帧数与分辨率决定其余部分

输出(M5 Pro,同一 Turbo 检查点与图像)Token去噪VAE 解码总计
736×544 × 81 帧(3.4 秒,480p 级)8 21184 s159 s4 分 09 秒
736×544 × 121 帧(5 秒,480p 级)12 121137 s237 s6 分 19 秒
1088×832 × 121 帧(5 秒,720p 级)27 404481 s563 s17 分 30 秒

480p(约 0.4 MP)本身就是 Wan 训练过的分辨率,因此前两行属于分布内的正常档位,而不是降级模式——想在几分钟内出片就该选它。质量真正开始下滑是在约 0.3 MP 以下:那里 DiT 已超出分布,无论花多少步,画面都会发糊、不稳定并偏色,流水线也会给出警告。请在受支持的分辨率上生成后再缩小(用 --width 480 --height 704 而不是 320×480)。

要让一次大请求更便宜,按效果排序:(1) 换成步数蒸馏检查点——100 次前向变 4 次,压倒其他一切;(2) 减少帧数——121 → 61 大致把注意力工作量降到四分之一,VAE 解码减半;(3) 减小画面面积,但不要低于约 0.3 MP;(4) 减少步数,仅限基础检查点——30 步与 50 步观感接近,成本低 1.7×;(5) --cfg-cache-stride 2 或 3,即每 N 步才跑一次无条件前向、其余步复用缓存的引导方向——50 步下 100 次前向只跑 77 次(1.30×)或 70 次(1.43×)。它是近似方法,需要对齐参考样本时请关掉;在本就无引导的蒸馏检查点上也没有作用。

在 NVIDIA 上 ggml_cuda 是最快的 Wan 后端:RTX 2000 Ada 16 GB 跑官方 480p Wan2.1-1.3B 配方时为 12.0 s/步,ggml_vulkan 为 17.2,直连 cuda 后端为 19.3。Wan 完全不支持 mlx;cpu / ggml_cpu 后端只用于功能性验证。

可通过 CLI(--prompt、可选 --image、--video-frames、--fps、--flow-shift、--sampler、--negative-prompt)、HTTP API(/v1/videos/generations)或 Web UI 聊天上传图像来驱动。在服务端,--video-frames 与 --fps 设定的是默认值而非上限——请求自带 frames 或 fps 时各自覆盖;两者都不给时套用模型自身的配方(Wan2.2-TI2V 为 49 帧 24 fps,其余为 33 帧 16 fps)。帧数会对齐到 VAE 的时间网格(4k+1)。完整说明见仓库中的 docs/models/wan_zh-cn.md 卡片。