3、数据准备与预处理:多模态数据集格式与对齐策略
数据准备这件事,说实话,是VLA模型训练里最容易被低估的环节。我见过太多团队花几个月调模型,最后发现是数据格式出了问题——图像和文本对不上,动作序列标注有偏差,训练出来的模型像个“睁眼瞎”。
今天我们就来聊聊多模态数据到底该怎么准备。我会把我在多个项目中踩过的坑、总结的经验,一次性讲清楚。
3.1 多模态数据集格式:三种主流方案
多模态数据存储,说白了就是要把图像、文本、动作序列这些不同类型的数据,塞进一个统一的容器里。目前主流的有三种格式:JSON、HDF5、TFRecord。我个人的习惯是:小规模实验用JSON,大规模训练用HDF5或TFRecord。
3.1.1 JSON格式:灵活但别滥用
JSON的好处是直观,人类可读。但坏处也很明显——文件大了以后,加载速度慢得让人抓狂。
{
"dataset_name": "vla_robot_arm",
"samples": [
{
"image_path": "data/images/001.jpg",
"text": "将红色方块放到蓝色盒子里",
"actions": [
{"joint_angles": [0.1, 0.2, 0.3, 0.4, 0.5, 0.6], "gripper": 0.0},
{"joint_angles": [0.2, 0.3, 0.4, 0.5, 0.6, 0.7], "gripper": 0.5}
],
"metadata": {
"task_id": "pick_place_001",
"timestamp": 1234567890
}
}
]
}
3.1.2 HDF5格式:大规模数据的首选
HDF5是我个人最推荐的格式。它支持分层存储,可以高效地读取部分数据,不用一次性加载全部。
import h5py
import numpy as np
# 创建HDF5文件
with h5py.File('vla_dataset.h5', 'w') as f:
# 创建组
grp = f.create_group('sample_001')
# 存储图像(RGB格式)
grp.create_dataset('image', data=np.random.rand(224, 224, 3))
# 存储文本(需要编码)
grp.create_dataset('text', data="将红色方块放到蓝色盒子里".encode('utf-8'))
# 存储动作序列
grp.create_dataset('actions', data=np.random.rand(10, 7)) # 10个时间步,7维动作
# 存储元数据
grp.attrs['task_id'] = 'pick_place_001'
grp.attrs['timestamp'] = 1234567890
3.1.3 TFRecord格式:TensorFlow生态的标配
如果你用TensorFlow,TFRecord是绕不开的。它的优势在于和TF的数据管道无缝集成,支持高效的并行读取。
import tensorflow as tf
def serialize_example(image, text, actions):
feature = {
'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image.tobytes()])),
'text': tf.train.Feature(bytes_list=tf.train.BytesList(value=[text.encode('utf-8')])),
'actions': tf.train.Feature(float_list=tf.train.FloatList(value=actions.flatten())),
'action_shape': tf.train.Feature(int64_list=tf.train.Int64List(value=actions.shape))
}
example = tf.train.Example(features=tf.train.Features(feature=feature))
return example.SerializeToString()
# 写入TFRecord
with tf.io.TFRecordWriter('vla_dataset.tfrecord') as writer:
for i in range(1000):
example = serialize_example(
image=np.random.rand(224, 224, 3).astype(np.float32),
text="将红色方块放到蓝色盒子里",
actions=np.random.rand(10, 7).astype(np.float32)
)
writer.write(example)
3.2 图像与文本对齐:别让模型“看错话”
图像和文本的对齐,说白了就是确保模型看到的图像和它读到的文本描述的是同一件事。这个问题看似简单,但实际项目中经常出问题。
我记得有一次,数据标注员把图像文件名和文本描述搞混了,结果模型训练了三天,loss死活降不下去。后来一查,发现30%的数据对不齐。
3.2.1 对齐策略
- 时间戳对齐: 如果数据是实时采集的,用时间戳做对齐是最可靠的。我一般用微秒级的时间戳,避免精度问题。
- 帧号对齐: 对于视频数据,用帧号对齐更直观。但要注意帧率变化,我曾经遇到过相机掉帧导致对齐错位的情况。
- 语义对齐: 用CLIP等模型做语义匹配,检查图像和文本是否描述同一场景。这个方法适合做数据清洗。
3.3 动作序列标注:机器人动作的“语言”
动作序列标注,是VLA模型训练中最费时费力的环节。你需要把机器人的每个动作都标注清楚,包括关节角度、末端执行器位置、夹爪状态等。
3.3.1 标注格式
| 字段 | 类型 | 说明 |
|---|---|---|
| timestamp | float | 时间戳(秒) |
| joint_angles | float[6] | 6个关节的角度(弧度) |
| gripper | float | 夹爪开合度(0.0~1.0) |
| ee_pose | float[7] | 末端执行器位姿(位置+四元数) |
| action_type | int | 动作类型(0:移动, 1:抓取, 2:放置) |
# 动作序列标注示例
action_sequence = [
{
"timestamp": 0.0,
"joint_angles": [0.1, 0.2, 0.3, 0.4, 0.5, 0.6],
"gripper": 0.0,
"ee_pose": [0.5, 0.3, 0.2, 0.0, 0.0, 0.0, 1.0],
"action_type": 0 # 移动
},
{
"timestamp": 0.5,
"joint_angles": [0.2, 0.3, 0.4, 0.5, 0.6, 0.7],
"gripper": 0.5,
"ee_pose": [0.6, 0.4, 0.3, 0.0, 0.0, 0.0, 1.0],
"action_type": 1 # 抓取
}
]
3.4 数据增强策略:让模型更“皮实”
数据增强,说白了就是给模型制造“困难”。你想想看,如果训练数据全是完美光照、标准角度,模型到了真实环境肯定抓瞎。
3.4.1 图像增强
- 颜色抖动: 调整亮度、对比度、饱和度。我一般用0.2的幅度,太大反而会破坏语义信息。
- 随机裁剪: 从原图中随机裁剪224x224的区域。注意不要裁掉关键物体。
- 高斯噪声: 模拟传感器噪声。我习惯用均值为0、标准差为0.01的噪声。
- 随机擦除: 随机遮挡图像的一部分,强迫模型关注全局特征。
3.4.2 文本增强
- 同义词替换: 把“放置”换成“摆放”、“放好”。注意不要改变动作语义。
- 语序调整: “将红色方块放到蓝色盒子里” -> “把红色方块放进蓝色盒子”。
- 随机删除: 随机删除10%的词语,模拟语音识别错误。
3.4.3 动作序列增强
- 时间缩放: 把动作序列的时间轴拉伸或压缩。我一般用0.8~1.2的缩放因子。
- 动作抖动: 在关节角度上添加小幅度噪声(标准差0.01弧度)。
- 轨迹平滑: 用Savitzky-Golay滤波器平滑动作轨迹,去除高频噪声。
3.5 知识体系总览
下面这张图,是我对本章知识体系的总结。你可以把它当作一个检查清单,看看自己有没有遗漏什么。
嗯,到这里,数据准备与预处理的核心内容就讲完了。说实话,这部分工作很琐碎,但真的值得花时间做好。我见过太多项目因为数据问题翻车,最后不得不返工。
记住一句话:数据质量决定模型上限,预处理决定训练效率。把这两件事做好了,后面的模型训练会顺畅很多。