news 2026/9/12 23:44:50

diffusers 中 Wan-Animate-2 角色动画 ModularPipeline 完整实战指南:分段参考提取与蒸馏采样

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
diffusers 中 Wan-Animate-2 角色动画 ModularPipeline 完整实战指南:分段参考提取与蒸馏采样

diffusers 中 Wan-Animate-2 角色动画 ModularPipeline 完整实战指南:分段参考提取与蒸馏采样

【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers

本篇技术指南围绕 Hugging Face diffusers 仓库中对阿里 Wan 团队 Wan-Animate-2 模型的官方支持展开,讲解如何用一张参考角色图像 + 一段驱动视频(driving video)生成角色跟随视频,并深入剖析其"定长分段 + 逐段参考 K/V 提取 + 循环内解码"的模块化流水线实现,以及基础版与蒸馏版两套预设的差异。读完本文,你将掌握从模型加载、显存优化到参数调优的完整实战方案,并能从源码层面理解每个分段内部究竟发生了什么。

Wan-Animate-2 在 diffusers 中的定位

Wan-Animate-2(由阿里 Wan 团队提出,仓库即 Wan-Video/Wan2.2)是一个角色动画(character animation)模型:它把一张参考角色图像的外观与一段驱动视频的运动"嫁接"在一起,输出角色按照驱动动作表演的新视频。在 diffusers 中,它被实现为基于ModularPipeline的模块化流水线,核心源码位于 src/diffusers/modular_pipelines/wan_animate_2/,测试位于 tests/modular_pipelines/wan_animate_2/test_modular_pipeline_wan_animate_2.py。

驱动视频不是一次整段处理的,而是被切分为定长分段(fixed-length segments),每个分段依次经历三个阶段:

  1. 参考提取(reference-extraction pass):把该分段送入 Transformer,在每一层提取并缓存该分段的 K/V(键值对);
  2. 去噪(denoise):在去噪的每一步,生成 token 通过注意力机制共同关注本分段缓存的参考 K/V,从而让生成结果对齐驱动运动;
  3. 循环内解码(decode inside the loop):解码必须在分段循环内部完成,因为下一个分段要以当前分段解码后的尾部帧作为条件,运动连续性是在像素空间而非隐空间上跨分段传递的。

官方文档(即本仓库中的 docs/source/en/api/pipelines/wan_animate_2.md)提供了完整的可直接运行示例,本文将在其基础上逐层深入。

快速上手:加载流水线并生成角色动画

以下示例完整继承自官方文档,可直接复制运行。它使用ModularPipeline通用加载入口,配合load_componentsbfloat16精度加载组件:

import torch from diffusers import ModularPipeline from diffusers.utils import export_to_video, load_image, load_video pipe = ModularPipeline.from_pretrained("Wan-AI/Wan2.2-Animate-2-14B-Diffusers") pipe.load_components(dtype=torch.bfloat16) # The transformer weights and the per-segment reference KV cache do not co-reside on one 80 GB # card at the default resolution, so stream the transformer's blocks. Compiling the blocks is # required as the in-context attention runs on the flex backend pipe.transformer.enable_group_offload( onload_device=torch.device("cuda"), offload_device=torch.device("cpu"), offload_type="block_level", use_stream=True, ) pipe.text_encoder.to("cuda") # or "mps", "xpu", "cpu" pipe.image_encoder.to("cuda") pipe.vae.to("cuda") pipe.transformer.compile_repeated_blocks(fullgraph=False) # The first demo from the official repository: https://github.com/Wan-Video/Wan-Animate-2 demo = "https://raw.githubusercontent.com/Wan-Video/Wan-Animate-2/main/examples/demo1" image = load_image(f"{demo}/reference.png") driving_video, driving_video_fps = load_video(f"{demo}/template.mp4", return_fps=True) prompt = "人物外观描述:一只银灰色虎斑纹的小猫,拥有圆润的脸庞、竖立的耳朵和巨大的圆形眼睛。它身穿一套深蓝色的制服套装,包括一件带有金色纽扣的西装外套和一条百褶裙。外套里面搭配着白色衬衫,领口处系着一个红色的蝴蝶结,袖口露出白色的衬衫边缘。背景描述:背景为纯白色,光线均匀明亮,无其他杂物或装饰。" videos = pipe( image=image, driving_video=driving_video, driving_video_fps=driving_video_fps, prompt=prompt, output="videos", ) export_to_video(videos[0], "output.mp4", fps=24)

代码中的关键点说明:

  • load_video(..., return_fps=True)返回驱动视频的原始帧率,流水线会据此将驱动视频重采样到模型生成帧率fps(默认 24);
  • output="videos"是模块化流水线风格的输出命名参数,最终返回视频列表videos
  • export_to_video(videos[0], "output.mp4", fps=24)把首段视频以 24 fps 写盘;
  • pipe.transformer.enable_group_offload(...)compile_repeated_blocks(fullgraph=False)属于显存优化与性能必需项,详见下文"显存与性能"一节。

两套预设:基础版与蒸馏版

diffusers 为 Wan-Animate-2 提供了两套检查点预设,二者唯一的加载差异就是模型仓库 ID,其余调用方式完全一致:

预设模型仓库 ID采样步数引导方式
基础版Wan-AI/Wan2.2-Animate-2-14B-Diffusers40 步(默认)分类器自由引导(CFG),guidance_scale=3.0
蒸馏版Wan-AI/Wan2.2-Animate-2-14B-Distilled-Diffusers10 步(默认)无 CFG,guider 被固定在guidance_scale=1.0

关键差异可以从测试用例 tests/modular_pipelines/wan_animate_2/test_modular_pipeline_wan_animate_2.py 中直接印证:基础版配置{"num_inference_steps": 40}且 guider 配置{"guidance_scale": 3.0},蒸馏版则是{"num_inference_steps": 10}{"guidance_scale": 1.0}

需要特别注意的是,整个流水线不存在guidance_scale参数:引导完全由流水线的 guider 组件(ClassifierFreeGuidance)接管。基础版的 guider 需要同时跑条件分支与无条件分支(is_uncondtion分别传False/True),蒸馏版则只跑条件分支。这一定义位于 src/diffusers/modular_pipelines/wan_animate_2/denoise.py:

  • WanAnimate2SegmentDenoiseInnerexpected_components中以FrozenDict({"guidance_scale": 3.0})创建 guider;
  • WanAnimate2DistilledSegmentDenoiseInner继承前者,但 guider 配置改为FrozenDict({"guidance_scale": 1.0}),注释明确指出"蒸馏模型专为少步采样训练、不使用 CFG,因此只运行条件分支"。

两个预设分别对应WanAnimate2ModularPipelineWanAnimate2DistilledModularPipeline(见 src/diffusers/modular_pipelines/wan_animate_2/modular_pipeline.py),后者的default_blocks_name指向WanAnimate2DistilledBlocks

分段循环:源码级的执行流程拆解

从源码结构看,整个分段循环被组织为一个WanAnimate2SegmentLoopWrapper(继承LoopSequentialPipelineBlocks),基础版与蒸馏版分别组合出WanAnimate2DenoiseStepWanAnimate2DistilledDenoiseStep。以基础版为例,每个分段内部依次执行 7 个子步骤(见 denoise.py):

vae_encoder -> prev_frames -> prepare -> scheduler_reset -> ref_extract -> denoise_inner -> decode

各子步骤的职责如下:

子步骤核心行为
vae_encoderWanAnimate2SegmentVaeEncoderStep对本分段的驱动视频切片做 VAE 编码,并叠加 i2v 条件掩码,得到驱动分段隐变量driving_video_latents与条件张量driving_video_condition
prev_framesWanAnimate2SegmentPrevFramesStep把上一分段解码出的尾部帧(首分段为全零)VAE 编码、掩码后与参考图像隐变量堆叠,构成生成侧条件张量reference_latents;这是运动连续性跨分段的载体
prepareWanAnimate2SegmentPrepareStep为本分段抽取初始噪声,并新建一个全新的参考 K/V 缓存WanAnimate2KVCache
scheduler_resetWanAnimate2SegmentSchedulerResetStep重置调度器——每个分段都是独立去噪轨迹,求解器状态与时间步需逐段重新准备
ref_extractWanAnimate2RefExtractStepkv_cache_mode="extract"运行 Transformer,一次性编码驱动分段并把每一层的参考 K/V 存入缓存
denoise_innerWanAnimate2SegmentDenoiseInner内层时间步循环,以kv_cache_mode="cached"逐时间去噪,去噪前向关注缓存的参考 K/V
decodeWanAnimate2SegmentDecodeStepVAE 解码本分段,完成帧移到 CPU,释放本分段的 K/V 缓存与隐变量

值得强调的两个设计动机(源码注释明确写出):

  • 为什么解码必须在循环内:下一个分段要以上一分段的解码后像素为条件,且"运动连续性在像素空间而非隐空间跨分段传递"——这正是WanAnimate2SegmentPrevFramesStep在像素域取上一段尾部帧并重新 VAE 编码的原因(见 denoise.py);
  • 为什么解码后要立即释放资源:完成帧被搬到 CPU,并调用block_state.kv_cache.clear()torch.cuda.empty_cache()——"跨分段保留它们会把分配器碎片化到足以在高分辨率下 OOM"(见 denoise.py)。

另外,WanAnimate2SegmentVaeEncoderStep的说明还揭示了一个微妙的技术点:Wan VAE 在时间维度上是因果的,因此"整段视频一次性编码再切片"与"每个分段在其自身切片上重新开始时间卷积"并不等价——这正是每个分段都必须单独 VAE 编码的原因。

参考提取与 K/V 缓存的底层原理

参考提取的核心实现在 Transformer 模型 src/diffusers/models/transformers/transformer_wan_animate_2.py 中,WanAnimate2KVCache承载每一层的参考 K/V。kv_cache_mode只有两种取值:

  • "extract"(参考提取):对参考 token 做密集自注意力,并把每一层的 K/V 写入缓存;该模式下时间步被固定为 1(源码中timestep_input = timestep * 0 + 1),因为这一步不是去噪而是"记忆";
  • "cached"(生成):生成 token 与缓存的参考 token联合参与注意力,通过 flex attention 后端完成 in-context 注意力计算。

正是"in-context attention 运行在 flex 后端"这一点,决定了官方示例必须调用transformer.compile_repeated_blocks(fullgraph=False)做编译:源码注释明确写道,未经编译时 PyTorch 的 flex attention 会回退到非编译实现,而编译是保证性能的前提。此外提取与生成两种模式还使用了不同的 RoPE 步长(rope_stride=self.refer_stride if kv_cache_mode == "extract" else 1)。

在流水线层面,参考提取前需要先算好与分段无关的几何量,这由WanAnimate2PrepareSegmentsStep(src/diffusers/modular_pipelines/wan_animate_2/before_denoise.py)完成:由于 zigzag 填充保证每个分段恰好segment_frame_length帧,因此隐变量网格、打包序列长度(max_seq_lenmax_seq_len_ref)与噪声形状对所有分段一致,只需计算一次。它还校验驱动视频与参考图像的 letterbox 尺寸必须一致,否则抛出ValueError

关键输入参数详解

结合 modular_blocks_wan_animate_2.py 中的自动文档字符串与测试用例中的默认值(test_modular_pipeline_wan_animate_2.py),完整参数清单如下:

参数默认值说明
prompt必填角色/背景外观描述提示词,必须是str类型(源码check_inputs强制校验)
negative_promptNone负向提示词;仅在 guider 需要无条件嵌入或显式传入时才会编码
prompt_ref"人物动作的参考视频"驱动视频上下文的固定参考提示词,用于引导参考提取前向
max_sequence_length512提示词编码的最大序列长度,超出部分截断、不足部分补零
image必填承载待动画角色的参考图像
driving_video必填提供运动的驱动视频(VideoProcessor.preprocess_video接受的任意格式)
driving_video_fpsNone驱动视频原始帧率;设置时按最近邻重采样到fps,为None时按原样使用
fps24模型生成帧率
height/width800/640生成视频的目标面积(不是硬性分辨率),详见下文
segment_frame_length81每个推理分段的帧数
prev_segment_conditioning_frames1从上一分段携带的条件帧数(分段间重叠量)
num_inference_steps40(基础版)/10(蒸馏版)去噪步数
generatorNone用于确定性生成的 torch 生成器
output_type"np"解码视频的输出类型(np/pt/ PIL)

height/width 的"面积"语义

heightwidth(默认 800 × 640)设定的是生成视频的目标面积height * width),而非最终分辨率。实际帧尺寸会保持参考图像的宽高比,计算方式见 encoders.py 中的WanAnimate2ProcessImagesInputStep

aspect_ratio = image_height / image_width max_area = block_state.height * block_state.width block_state.height = int(math.sqrt(max_area * aspect_ratio)) // mod_value * mod_value block_state.width = int(math.sqrt(max_area / aspect_ratio)) // mod_value * mod_value

其中mod_value = vae_scale_factor_spatial * 2(即 16),保证分辨率满足 VAE 与 2×2 空间 patch 的可整除要求。驱动视频的每一帧随后被letterbox(保持宽高比缩放 + 黑色填充)到该分辨率;填充的"黑边"区域在最终输出时依据预处理阶段记录的crop_region裁剪掉(见 decoders.py 的WanAnimate2DecodeStep)。

一个实用提示(官方文档明确提到):输入已经处于目标 letterbox 尺寸时,预处理会原样通过,因此预处理也可以完全放到流水线外部完成,再直接传入已处理好的张量。

驱动视频的 fps 重采样与 zigzag 填充

WanAnimate2ProcessVideosInputStep(encoders.py)中:

  1. 若传入了driving_video_fps,先按最近邻重采样到fpsget_frame_indices实现);
  2. 逐帧 letterbox 到目标帧尺寸;
  3. 计算分段数:由于分段间重叠prev_segment_conditioning_frames帧,每段实际前进effective_segment = segment_frame_length - prev_segment_conditioning_frames帧;
  4. 若尾部剩余帧不足以凑满一个完整分段,则用zigzag(镜像)填充补齐,例如[0 1 2 3 4]补 3 帧变成[0 1 2 3 4 | 4 3 2]。填充帧对模型而言是"真实内容",但为填充帧生成的多余帧会在最终拼接时按real_frame_len裁掉。

WanAnimate2SegmentVaeEncoderStep的另一个细节:每个分段的驱动切片都要单独 VAE 编码,因为 Wan VAE 时间上因果,"整段编码后切片"并不等价。

letterbox 处理器的一像素差异

驱动视频与参考图像都使用自定义的 WanAnimate2VideoProcessor:resize_mode="fill"保持宽高比并把剩余区域填充为fill_color(默认黑色)。其文档字符串特别强调,内容的粘贴位置是((height - src_h) // 2, (width - src_w) // 2)——这是参考实现的放置约定,与VaeImageProcessor(height // 2 - src_h // 2, ...)在"帧尺寸为偶数、内容尺寸为奇数"时相差一行或一列,而这一像素的位移偏差是模型真实看到的内容差异,因此不能简单复用通用处理器。

显存与性能:分组卸载与编译

官方示例的前几行不是可选项,而是让默认分辨率(约 800 × 640)的推理跑得起来的必要条件:

  • 分组卸载(group offload):Transformer 权重与逐段参考 K/V 缓存无法在默认分辨率下共驻一块 80 GB 显存(源码与文档均如此说明),因此把 Transformer 的 block 以"block_level"粒度在 CUDA 与 CPU 之间流式传输(use_stream=True启用流式重叠);
  • 重复块编译compile_repeated_blocks(fullgraph=False)编译 Transformer 的重复块,因为 in-context 注意力运行在 flex 后端,未经编译会显著拖慢。

其余组件(text_encoderimage_encodervae)显式搬到目标设备,支持"cuda"/"mps"/"xpu"/"cpu"。整体加载使用load_components(dtype=torch.bfloat16)指定精度,14B 参数量级的模型配合低精度与卸载策略才能落在消费级到单卡 A100/H100 的可行区间内。

模块化 API 一览

本文档对应的公开 API(见 src/diffusers/modular_pipelines/wan_animate_2/init.py):

  • WanAnimate2ModularPipeline:基础版角色动画流水线,default_blocks_name = "WanAnimate2Blocks";通过vae_scale_factor_spatial(8)、vae_scale_factor_temporal(4)、num_channels_latents(16)等属性为预处理与噪声张量提供尺度信息,requires_unconditional_embeds依据 guider 是否启用且条件数 >1 来决定是否需要无条件嵌入;
  • WanAnimate2DistilledModularPipeline:蒸馏版,继承基础版,default_blocks_name = "WanAnimate2DistilledBlocks"
  • WanAnimate2Blocks:基础版流水线块,按text_encoder -> image_encoder -> video_encoder -> vae_encoder -> denoise -> decode组合,各块声明了自己的ComponentSpec(组件依赖)与InputParam/OutputParam(输入输出契约);
  • WanAnimate2DistilledBlocks:蒸馏版流水线块,仅将num_inference_steps默认值覆写为 10、guider 固定为 1.0,其余块复用。

测试方面,test_modular_pipeline_wan_animate_2.py 覆盖了快速推理、加载、工作流、显存与 guider 行为;其中明确标注 Wan-Animate-2不支持批处理(单次调用只接受一张角色图与一段驱动视频,num_videos_per_prompt亦不存在),相关批量测试被@pytest.mark.skip跳过。

小结

Wan-Animate-2 的 diffusers 实现把"长视频动画"拆解为可管理的定长分段流水线:每个分段依次完成驱动切片 VAE 编码、参考 K/V 提取、缓存引导的去噪与循环内解码,通过上一段解码尾帧完成像素级运动接力;基础版与蒸馏版共享同一套模块化骨架,仅在采样步数与 guider 配置上分道扬镳。理解这条vae_encoder -> prev_frames -> prepare -> scheduler_reset -> ref_extract -> denoise_inner -> decode的段内链路,以及height/width的"面积"语义与 zigzag 填充规则,就能在官方示例之上自由调整分辨率、分段长度与采样参数,跑出稳定且连贯的角色动画结果。

【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 23:44:47

无限画布百万节点性能压测:从卡顿到流畅的选型指南

前一阵帮一个做工业流程可视化的团队做技术选型,他们原本在某款开源无限画布上搭原型,节点数刚过八千就开始明显掉帧,拖拽时连线像橡皮筋一样拉丝,客户来验收那天直接在框选操作时卡了十几秒。后来换了策略,我先帮他们…

作者头像 李华
网站建设 2026/9/12 23:44:45

U-Net眼底血管分割实战:数据、训练与推理全流程解析

简介:一套基于U-Net的眼底血管分割项目包,面向医学图像处理初学者与算法开发人员,解决眼底血管二分割任务从数据准备到训练推理的完整流程。压缩包共216个文件,以182张切片PNG图像为主,另含8个Python脚本、5个XML配置、…

作者头像 李华
网站建设 2026/9/12 23:43:49

Windows 的 A 卡/I 卡用户如何为 RVC 安装并启用 DirectML 依赖?

Windows 的 A 卡/I 卡用户如何为 RVC 安装并启用 DirectML 依赖&#xff1f; 【免费下载链接】Retrieval-based-Voice-Conversion-WebUI Easily train a good VC model with voice data < 10 mins! 项目地址: https://gitcode.com/GitHub_Trending/re/Retrieval-based-Voi…

作者头像 李华
网站建设 2026/9/12 23:42:39

打造高质量技术博文:从规范输入素材开始

我需要先拿到你的输入内容&#xff0c;才能按规范输出博文。请按以下格式提供原始素材&#xff1a;项目标题: [标题] 项目正文: [比较零散、不完整的原始描述] 关键词: [关键词1, 关键词2, ...] 摘要描述: [一句话简介]你这次只贴了要求&#xff0c;没有给我具体的标题、正文和…

作者头像 李华
网站建设 2026/9/12 23:40:21

告别Postman!15款接口测试工具分类盘点与选型指南

最近好几个做测试和开发的朋友问我&#xff1a;接口测试到底该用什么工具&#xff1f;我第一反应是&#xff0c;你八成在用 Postman 吧&#xff1f;对方点头。然后下一句就是&#xff0c;那除了 Postman 还有别的吗&#xff1f;这问题问得特别好。不是 Postman 不好&#xff0c…

作者头像 李华