第一篇的差异四说,多模态数据加工的主力从 SQL 换成了解码和模型推理。这一步相当于数仓里从明细层到汇总层的 ETL:给每一帧补上标签、质量分数和向量,让数据能被检索、筛选、组装成训练集。这一篇讲为什么 Spark 这类引擎在这里别扭,以及 Daft 怎样解决。

加工前后,数据长什么样

切到「对齐明细」是加工前的样子,切到「汇总与标签」是加工后的样子:每一帧多了阶段、夹爪状态、清晰度和一个 512 维的向量;每条 episode 多了一行汇总。

一条 episode 的数据拖动时间点、切换数据层,看同一时刻在原始消息、明细、汇总和训练样本里各是什么样。
  1. 原始消息
  2. 对齐明细
  3. 汇总与标签
  4. 训练样本

episode「把杯子放进水槽」· 从第 12.0 秒的「汇总与标签」看起

加工通常包括这些步骤:

步骤

用什么资源

产出

例子

解码

CPU

像素

把视频第 360 到 369 帧解出来,缩放到 224×224

质量过滤

CPU

清晰度、曝光、遮挡分数

清晰度低于 100 的帧标为模糊,不进训练集

规则标签

CPU

阶段、夹爪是否握着物体

夹爪闭合且末端在移动 → 搬运阶段

模型打标

GPU

检测框、物体类别、文字描述

画面里有一个透明玻璃杯

算向量

GPU

每帧一个 512 维向量

[0.12, -0.03, 0.27, …]

汇总

CPU

episode 级统计

抓取时刻、成功与否、episode 向量(关键帧向量取平均)

其中向量是后面去重和检索的基础,它是什么、为什么相似的画面向量相近,见第一篇的差异五。

核心原理:CPU 和 GPU 怎样配合

加工流水线里有两种完全不同的活:

  • 解码和预处理在 CPU 上:解视频、缩放、算清晰度,每帧都要做,没法批量加速太多,靠多核并行。

  • 模型推理在 GPU 上:一次喂一批帧(比如 32 帧)效率最高;一批太小,GPU 大部分时间在等数据。

  • 模型要常驻:加载一个模型要好几秒、占几 GB 显存,不能每处理一批就重新加载一次,要像常驻进程一样一直开着。

Daft 把这几点做成了引擎层面的能力:图像、向量是它的原生数据类型;一个加工步骤可以声明「需要 1 张 GPU、最多开 4 个实例」,模型在实例里只加载一次;更关键的是流式执行——读取、解码、推理、写回同时开工,数据一批批流过去,而不是像传统批处理那样,等上一个阶段把全部数据处理完、存成中间结果,再交给下一个阶段。

落到 Daft 的接口上:一个加工步骤写成一个类,用 @daft.cls(gpus=1, max_concurrency=4) 声明每个实例要 1 张 GPU、最多开 4 个实例,模型在实例初始化时加载一次,之后一批批处理。执行它的是 Daft 用 Rust 写的流式引擎 Swordfish;接上 Ray 以后,每台机器上跑一个 Swordfish 实例,单机和集群用的是同一套执行方式。

解码后的图像比压缩前大十几倍:一百万帧 224×224 的图解出来是 150 GB。分阶段执行要把它们全部存下来;流式执行时,内存里只有正在处理的几批,几十 MB。整条流水线的速度,则由最慢的那个阶段决定。

动手:找到流水线的瓶颈

Daft 流水线的瓶颈CPU 解码和 GPU 打标串成一条流水线,看看谁在拖后腿。
  1. 读取 Lance
  2. CPU 解码
  3. GPU 打标
  4. 写回 Lance

16 个 CPU 核 · 2 张 GPU · 每批 32 帧

  1. 默认 16 个 CPU 核、2 张 GPU:瓶颈在 CPU 解码,GPU 只有一半时间在干活。

  2. 把 GPU 加到 4 张:整体速度一点没变,GPU 利用率反而更低——钱花在了不是瓶颈的地方。

  3. 把 CPU 核加到 32:解码追上了 GPU,两边都接近满载。

  4. 把「每批帧数」改成 1:GPU 的产能骤降,瓶颈换到了 GPU。

  5. 对比下面两种执行方式的耗时和内存:分阶段执行要存下一百多 GB 的中间结果,流式执行只要几十 MB。

和数仓 ETL 的区别


数仓 ETL(Spark / Hive)

多模态加工(Daft)

处理的单位

一行,几百字节

一帧或一段视频,几十 KB,解码后再大十几倍

计算

SQL 表达式,CPU

解码靠 CPU,模型靠 GPU

算子

无状态的函数

常驻显存的模型实例,有状态

执行方式

分阶段,中间结果落盘再交给下一阶段

流式,各阶段同时开工,中间结果不落地

产出

数值、字符串、标签

标签 + 向量;向量要随模型升级整体重算

回填历史

重刷分区

只为新列写新文件

下一篇:例子:机器人数据闭环走一圈,把存储和计算放回一个真实场景。

参考资料

  • Daft 文档:架构:原生执行器 Swordfish 是用 Rust 写的流式引擎;在 Ray 上每个工作节点运行一个 Swordfish 实例。

  • Daft 源码:daft.cls:gpus 是每个实例需要的 GPU 数,max_concurrency 对同步方法控制实例进程的个数。


全文评论

0

正在加载评论…