如果你做过数仓,对数据的直觉大概是:一行订单几百字节,表按天分区,SQL 扫一遍做聚合,T+1 出报表。机器人团队的数据会把这些直觉逐一打破:一行数据是某个瞬间的三路画面加十几个关节读数,体积以 TB/天计,最重要的读法不是扫描而是随机抽样,加工的主力是解码和模型推理。
这组文章回答一个问题:数仓的经验哪些还能用,哪些必须换,新一代的工具又分别解决了什么。这一篇先把差异讲清楚;接下来两篇分别讲存储侧的 Lance 和计算侧的 Daft;最后用一个机器人数据闭环把它们串起来。全程不写代码,也不是教程。
一行数据长什么样
贯穿全组的例子是机器人的训练数据。机器人领域把「对一个任务的一次完整尝试」叫作一条 episode,相当于数仓里一笔订单的全过程。比如机械臂接到指令「把杯子放进水槽」,从伸手到收回大约 30 秒,这期间同时记录着:
数据源 | 频率 | 每条多大 | 是什么 | 数仓里像什么 |
|---|---|---|---|---|
头部相机 | 每秒 30 帧 | 约 25 KB(压缩后) | 第三人称视角的画面 | 埋点里的大字段,但大几百倍 |
左右腕部相机 | 各每秒 30 帧 | 约 25 KB | 夹爪附近的特写,抓取的细节全靠它 | 同上 |
关节状态 | 每秒 200 条 | 约 70 字节 | 双臂 14 个关节角加 2 个夹爪开合,共 16 个数 | 高频指标流 |
操作员指令 | 每秒 50 条 | 约 70 字节 | 操作员通过主手给出的目标关节角,就是模型要学的「答案」 | 标签 |
任务指令 | 每条 episode 一次 | 几十字节 | 自然语言,比如「把杯子放进水槽」 | 维度属性 |
下面这个实验模拟了这样一条 episode。拖动「时间点」,切换「数据层」,可以看到同一时刻在四层里分别是什么样的记录——原始消息、对齐明细、汇总与标签、训练样本。先随便看看,后面每一篇会细讲其中一层。
- 原始消息
- 对齐明细
- 汇总与标签
- 训练样本
episode「把杯子放进水槽」· 从第 7.5 秒的「原始消息」看起
差异一:一行变大了,99% 是图像
订单表一行几百字节;这里一行是某个瞬间的三路画面加十几个关节读数。拖动下面的「机器人数量」和「每路视频码率」,看一天的数据在各层是什么样:
20 台机器人 × 每天 6 小时 · 一天 978 GB,视频占 99.4%
视频占 99% 以上。关节状态每秒 200 条看起来很密,但每条只有几十字节;一路 6 Mbps 的视频每秒就是 750 KB。存储、带宽和加工的成本几乎都由图像决定。
明细层每行几十 KB,是订单表一行的几百倍。扫一遍整张表做统计的代价和数仓完全不同——能只读需要的列,就绝不碰图像列。
训练集几乎不占空间。它只是一张「从哪些 episode 的哪些帧取样」的索引表,引用精选层的数据,而不是再复制一份视频。
数仓的做法为什么不够:数仓里「扫一遍全表做统计」很便宜,这里一扫就是 TB 级的视频。存储、带宽和加工成本几乎都由图像决定,所有设计都要围绕一件事:不需要图像的时候,绝不碰图像列。
差异二:读法从扫描变成随机抽样
训练机器人策略时,数据加载器每一步都把全部样本打乱,随机抽出一批(比如 256 个),每个样本再取最近几帧画面、当前关节角和接下来一小段动作。一个样本要读 9 张图(三路相机 × 三个时刻)、1 行关节角、16 行动作,而且这些行在整个数据集里随机分布;一个 epoch 要把几百万帧都这样读一遍。
数仓的做法为什么不够:Parquet 为扫描设计,压缩和编码以页为单位,默认一页约 1 MB,为了读一张 60 KB 的图要解开整页,放大十几到几十倍。视频更麻烦:H.264 只完整保存关键帧,其余帧只记和前一帧的差,想看第 369 帧,要从第 360 帧(关键帧)一路解过来。
谁来解决:Lance 的文件格式把「最小读取单位」做小,大值直接定位,见 Lance。
差异三:表结构天天变,训练集要能复现
数仓里加一列是件大事,常常要回刷历史。这里加列是日常:标签和向量都是模型算出来的,模型一升级,全表的标签和向量都要重算一遍。同时,每一版训练集都必须能精确复现:几个月后有人问「模型为什么在玻璃杯上变好了」,要能说清当时用的是哪些数据。
数仓的做法为什么不够:Hive 加列并回填要重写数据;Iceberg 加列只改元数据,但给历史数据填上值仍然要重写文件,而那些文件里躺着几百 GB 的视频。
谁来解决:Lance 表加列只为新列写新文件,每次提交一份清单,可以给版本打标签,见 Lance。
差异四:加工的主力从 SQL 换成了解码和推理
数据入湖以后,还要给每一帧补上标签和向量,才能被检索、筛选、组装成训练集。这一步相当于数仓里从明细层到汇总层的 ETL,但干活的不再是 SQL:
步骤 | 用什么资源 | 产出 | 例子 |
|---|---|---|---|
解码 | CPU | 像素 | 把视频第 360 到 369 帧解出来,缩放到 224×224 |
质量过滤 | CPU | 清晰度、曝光、遮挡分数 | 清晰度低于 100 的帧标为模糊,不进训练集 |
规则标签 | CPU | 阶段、夹爪是否握着物体 | 夹爪闭合且末端在移动 → 搬运阶段 |
模型打标 | GPU | 检测框、物体类别、文字描述 | 画面里有一个透明玻璃杯 |
算向量 | GPU | 每帧一个 512 维向量 | [0.12, -0.03, 0.27, …] |
汇总 | CPU | episode 级统计 | 抓取时刻、成功与否、episode 向量(关键帧向量取平均) |
解码后的图像比压缩前大十几倍:一百万帧 224×224 的图解出来是 150 GB。分阶段执行要把它们全部存下来;流式执行时,内存里只有正在处理的几批,几十 MB。整条流水线的速度,则由最慢的那个阶段决定。
数仓的做法为什么不够:Spark 这类引擎的算子是无状态的 SQL 表达式,阶段之间的中间结果落盘再交给下一阶段。而这里有两种资源(CPU 解码、GPU 推理)要配比,模型要常驻显存,解码后的中间结果大得存不下。
谁来解决:Daft 把图像当成原生类型,算子能声明要几张 GPU,各阶段流式执行,见 Daft。
差异五:「像不像」成了一种查询
数仓里描述一个用户,用的是人定义的标签:年龄段、城市、消费等级。描述一帧画面也可以这样做(场景 = 厨房,物体 = 杯子),但人能想到的标签有限,「透明」「反光」「杯子被遮住一半」这类细节很难穷举。
向量换了一种思路:把画面交给一个视觉模型,模型输出几百个数,比如 512 个。单看每个数没有含义,但模型保证了一件事:内容相似的画面,向量也相近。两个向量之间的距离(常用余弦相似度),就成了「这两帧有多像」的度量。有了它,就能做三件规则做不到的事:
去重:距离非常近的两条 episode,大概率是重复的示范。
检索:拿一个失败案例的向量去找最近的邻居,就能找到「和失败很像」的数据。
聚类与覆盖分析:看哪些场景的数据扎堆、哪些场景几乎没有。
数仓的做法为什么不够:数仓的查询是等值、范围和聚合,「找和这个失败案例最像的 100 条」写不成 WHERE。传统做法是另起一个向量数据库,于是同一份数据存两处,还要保持同步。
谁来解决:Lance 在表里直接建向量索引,检索时还能带上过滤条件,见 Lance。
差异六:关联靠时间,不靠主键
数仓里多张表按订单号等值关联。机器人的多路数据没有业务主键:相机每秒 30 帧,关节每秒 200 条,各自打着自己的时间戳,要拼成「一个时刻一行」,只能为每一帧找时间上最近的一条状态,太远就丢掉。各设备的时钟还要先同步,否则每一帧都会配到错误的时刻,而流程里看不出来。
这一处数仓的老办法照样管用:先按时间分桶变成等值关联,再在桶里取最近的一条,和数仓里的拉链表、as-of 关联是一个思路。真正的新问题在采集端的时钟同步,不在工具。细节见 例子:机器人数据闭环走一圈。
哪些经验可以照搬
变的是数据,不是方法。数仓的分层思想原样保留,只是每一层存的东西变了:
数仓分层 | 这里存什么 |
|---|---|
ODS 贴源层 | 机器人上传的录包文件,原样保存 |
DWD 明细层 | 帧表(一个相机时刻一行)和 episode 表(视频以 Blob 存放) |
DWS 汇总层 | 质检通过、补上标签和向量的帧与 episode |
ADS 应用层 | 训练样本索引,每一版打上标签 |
同样可以照搬的还有:质量规则(换成对齐率、丢帧、物理约束)、任务血缘(换成训练集的版本记录)、分区裁剪的思路(向量索引先分桶再细找,是同一个想法)。
总结:六处差异,各由谁接手
差异 | 数仓的做法哪里不够 | 新做法 | 谁来做 |
|---|---|---|---|
一行 99% 是图像 | 扫全表代价是 TB 级 | 能不读图像列就不读 | 列式存储,Lance 的 Blob 列 |
训练随机抽样 | Parquet 以页为单位解码,视频要从关键帧解起 | 把最小读取单位做小,大值直接定位 | Lance 文件格式 |
加列是日常,训练集要复现 | 回填要重写含视频的文件 | 只为新列写新文件,版本打标签 | Lance 表格式 |
解码和推理当主力 | 无状态算子、中间结果落盘 | 图像是原生类型,声明 GPU,流式执行 | Daft |
「像不像」成了查询 | WHERE 写不出来,另起向量库 | 表里建向量索引,带过滤检索 | Lance 向量索引 |
关联靠时间 | 没有主键可等值关联 | 分桶后取最近,先同步时钟 | 数仓老办法 + 采集端 |
判断规则只有一条:先问这条数据怎么被读、被谁算。读法和算法变了,存储和引擎才需要换;没变的地方,数仓的经验照样好用。
这组文章
从数仓到多模态:哪些数据直觉要改:数据本身变了什么,数仓的做法在哪里不够
Lance:为随机读、加列和版本而设计:存储这一侧的问题怎么解决
Daft:让解码和推理流起来:计算这一侧的问题怎么解决
例子:机器人数据闭环走一圈:把前三篇放回一个真实场景
参考资料
Lance 文件格式规范:编码,mini-block 与 full-zip 两种布局。
Daft 文档:架构,Swordfish 流式执行引擎。
全文评论
0正在加载评论…