Timeline Studio 的数字人链路使用 JoyVASA 从音频生成动作,再由 LivePortrait 把动作施加到源肖像。模型本身可以导出为 ONNX,真正阻塞浏览器的是生成器中的 rank-5 运算和两个 5D GridSample。
01. 先把“数字人”拆成可验证的流水线
这条链路不是单模型调用。它至少包含外观特征提取、源图动作提取、音频驱动、嘴型重定向、关键点拼接、3D 特征形变与 SPADE 图像生成。JoyVASA 的 HuBERT 与 diffusion denoiser 负责把语音转成动作序列,LivePortrait 则负责神经渲染。
JoyVASA
Keypoints
LivePortrait
RGB Frame
先拆管线的价值是可定位:音频驱动的误差、关键点组合的误差、warping 的误差和最终生成器的误差可以分别测量。否则最终人脸“不对”,你几乎无法知道问题来自哪一段。
02. 真正的阻塞点:浏览器没有 5D GridSample
基线 combined generator 使用 opset 20,共 277 个节点,其中有 13 个 rank-5 Conv 与两个 rank-5 GridSample。原仓库附带的 Linux .so 插件无法在浏览器加载,而 ONNX Runtime Web 的 WebGPU 执行提供程序也不能直接执行这两个 5D 采样节点。
输入特征可写成 [N, C, D, H, W],采样网格则在三个空间轴上给出目标坐标。Web 端现成的 GridSample 只处理二维平面。直接删除深度维、把它当批次维或者只取最近切片,都会改变模型含义。
工程原则:当目标运行时缺少算子时,图改写必须保持数学语义。视觉上“差不多”不能替代误差统计。
03. 等价改写:4D 采样 + 深度线性插值
三线性采样可以拆成两步:先在深度轴上找到目标坐标两侧的切片 d0 与 d1;分别对这两个 2D 平面执行双线性 GridSample;最后按深度小数部分 α 做线性插值。
d0 = floor(z)
d1 = min(d0 + 1, D - 1)
alpha = z - d0
v0 = GridSample4D(feature[d0], gridXY)
v1 = GridSample4D(feature[d1], gridXY)
output = (1 - alpha) * v0 + alpha * v1实现时需要把深度切片组织成批次,使 WebGPU 能并行执行 4D GridSample,再还原为原来的空间布局。稀疏动作网格还存在经过验证的共享 XY 结构,可以去掉重复的深度采样。改写后的图从 277 个节点增加到 316 个节点,但全部落在 Web 端已支持的算子集合里。
04. 数值验证:证明改写前后是同一个函数
验证使用固定输入,将原始 512×512 generator 与 WebGPU 改写图的完整输出逐元素比较。除了最大误差,还同时查看均值和高分位误差,以免少量异常点被平均值掩盖。
JoyVASA 的 HuBERT 与 diffusion denoiser ONNX 导出也分别对齐 PyTorch,最大绝对误差为 7.75e-6 和 6.44e-6。只有每一段都建立基线,端到端结果才具有可解释性。
05. 图能跑以后,运行时问题才刚开始
ONNX Runtime Web 的 WebGPU Worker 必须加载匹配的 Asyncify glue 与 WASM 文件。使用普通或 JSEP glue 会触发 webgpuInit is not a function。多个大型执行提供程序会话还需要串行创建,否则 ORT 可能拒绝并发初始化。
模型包约 906 MB,不适合进入 Git 仓库。项目把它拆成 50 MB 分片,发布到 revision-pinned 的 Hugging Face 模型仓库,并由 Service Worker 缓存。版本固定同时保护了两个假设:图结构不变,运行时代码期待的输入输出也不变。
await createJoyVasaSession();
releaseTransientGpuResources();
await createLivePortraitSession();
appearanceFeature = await extractOnce(sourcePortrait);
for (const motion of sparseMotions)
frames.push(await render(appearanceFeature, motion));
06. “跑通”不等于“实时”
项目提供 mixed-FP16 256px 快速预览与 mixed-FP16 512px 质量档。外观特征在帧间保持 GPU 驻留,神经关键帧以 1–2 fps 稀疏渲染,再插值到编码帧率,降低总推理次数。
但必须诚实:在验证设备上,512×512 神经帧仍需要约 25–60 秒。界面展示真实阶段、帧数与耗时,不把稀疏关键帧插值包装成“实时神经渲染”。生成帧会检查非有限值与时间异常,问题帧重试一次,仍失败则沿用上一有效帧。
07. 端到端验收必须走完媒体闭环
单个 ONNX session 返回 tensor 只代表算子层通过。真正的验收还包括:模型分片下载、WebGPU 初始化、JoyVASA 50 步动作生成、LivePortrait 渲染、WebM 编码、浏览器播放、媒体库插入和视觉轨替换。
最终验收产物是一段可播放的 3.56 秒 WebM。抽帧能看到张嘴、闭嘴以及头眼运动,完成后浏览器控制台错误为 0。这个闭环很重要,因为编码、Blob 生命周期或轨道替换中的任意错误,都能让“模型成功”变成“用户什么也得不到”。
结论:算子移植的核心是可证明
将桌面端模型带进浏览器,最有价值的能力不是写一段兼容代码,而是建立完整证据链:审计图、定位不支持算子、构造等价变换、逐层对齐、统计误差、验证真实运行时,最后走完整个媒体产品闭环。
WebGPU 正在快速扩大浏览器的计算边界。但在这个边界上,最可靠的优化不是更响亮的性能口号,而是每一步都有数字、失败路径和可复现产物。
