如果你做过数仓,对数据的直觉大概是:一行订单几百字节,表按天分区,SQL 扫一遍做聚合,T+1 出报表。机器人团队的数据会把这些直觉逐一打破:一行数据是某个瞬间的三路画面加十几个关节读数,体积以 TB/天计,最重要的读法不是扫描而是随机抽样,加工的主力是解码和模型推理。

这组文章回答一个问题:数仓的经验哪些还能用,哪些必须换,新一代的工具又分别解决了什么。这一篇先把差异讲清楚;接下来两篇分别讲存储侧的 Lance 和计算侧的 Daft;最后用一个机器人数据闭环把它们串起来。全程不写代码,也不是教程。

一行数据长什么样

贯穿全组的例子是机器人的训练数据。机器人领域把「对一个任务的一次完整尝试」叫作一条 episode,相当于数仓里一笔订单的全过程。比如机械臂接到指令「把杯子放进水槽」,从伸手到收回大约 30 秒,这期间同时记录着:

数据源

频率

每条多大

是什么

数仓里像什么

头部相机

每秒 30 帧

约 25 KB(压缩后)

第三人称视角的画面

埋点里的大字段,但大几百倍

左右腕部相机

各每秒 30 帧

约 25 KB

夹爪附近的特写,抓取的细节全靠它

同上

关节状态

每秒 200 条

约 70 字节

双臂 14 个关节角加 2 个夹爪开合,共 16 个数

高频指标流

操作员指令

每秒 50 条

约 70 字节

操作员通过主手给出的目标关节角,就是模型要学的「答案」

标签

任务指令

每条 episode 一次

几十字节

自然语言,比如「把杯子放进水槽」

维度属性

下面这个实验模拟了这样一条 episode。拖动「时间点」,切换「数据层」,可以看到同一时刻在四层里分别是什么样的记录——原始消息、对齐明细、汇总与标签、训练样本。先随便看看,后面每一篇会细讲其中一层。

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

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 向量索引

关联靠时间

没有主键可等值关联

分桶后取最近,先同步时钟

数仓老办法 + 采集端

判断规则只有一条:先问这条数据怎么被读、被谁算。读法和算法变了,存储和引擎才需要换;没变的地方,数仓的经验照样好用。

这组文章

  1. 从数仓到多模态:哪些数据直觉要改:数据本身变了什么,数仓的做法在哪里不够

  2. Lance:为随机读、加列和版本而设计:存储这一侧的问题怎么解决

  3. Daft:让解码和推理流起来:计算这一侧的问题怎么解决

  4. 例子:机器人数据闭环走一圈:把前三篇放回一个真实场景

参考资料


全文评论

0

正在加载评论…