第一篇的差异四说,多模态数据加工的主力从 SQL 换成了解码和模型推理。这一步相当于数仓里从明细层到汇总层的 ETL:给每一帧补上标签、质量分数和向量,让数据能被检索、筛选、组装成训练集。这一篇讲为什么 Spark 这类引擎在这里别扭,以及 Daft 怎样解决。
加工前后,数据长什么样
切到「对齐明细」是加工前的样子,切到「汇总与标签」是加工后的样子:每一帧多了阶段、夹爪状态、清晰度和一个 512 维的向量;每条 episode 多了一行汇总。
- 原始消息
- 对齐明细
- 汇总与标签
- 训练样本
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。整条流水线的速度,则由最慢的那个阶段决定。
动手:找到流水线的瓶颈
- 读取 Lance
- CPU 解码
- GPU 打标
- 写回 Lance
16 个 CPU 核 · 2 张 GPU · 每批 32 帧
默认 16 个 CPU 核、2 张 GPU:瓶颈在 CPU 解码,GPU 只有一半时间在干活。
把 GPU 加到 4 张:整体速度一点没变,GPU 利用率反而更低——钱花在了不是瓶颈的地方。
把 CPU 核加到 32:解码追上了 GPU,两边都接近满载。
把「每批帧数」改成 1:GPU 的产能骤降,瓶颈换到了 GPU。
对比下面两种执行方式的耗时和内存:分阶段执行要存下一百多 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正在加载评论…