4. 数据准备:数据集采集与标注,数据增强策略,转换为TFRecord或二进制格式
数据准备这一步,说白了就是给模型喂饭。饭好不好吃,直接决定了模型能不能干活。我在做嵌入式AI项目时,见过太多因为数据没处理好导致模型在MCU上跑偏的案例。今天咱们就把这块掰开揉碎了讲清楚。
4.1 数据集采集:从哪儿搞数据?
嵌入式AI的数据集采集,跟云端那套不太一样。云端可以海量爬取,咱们MCU端讲究的是「精准打击」。我个人习惯把数据来源分成三类:
| 数据来源 | 适用场景 | 采集成本 | 注意事项 |
|---|---|---|---|
| 公开数据集 | 通用任务(手势识别、关键词唤醒) | 低 | 需确认授权,注意与目标场景的分布差异 |
| 传感器现场采集 | 特定工业场景、设备振动分析 | 高 | 需考虑传感器型号、采样率、环境噪声 |
| 仿真/合成数据 | 极端工况、罕见故障模式 | 中 | 需验证仿真与真实数据的迁移效果 |
我记得有一次做电机故障检测,直接用了公开的振动数据集,结果部署到现场后准确率掉了20%。后来发现是公开数据的采样频率是10kHz,而现场传感器只有2kHz。嗯,这里要注意:采集条件必须与最终部署环境一致。
4.2 数据标注:给数据打标签
标注这件事,听起来简单,做起来全是坑。我见过团队花了两周标注了10万张图片,结果发现标签定义不一致——有人把「轻微抖动」标成「正常」,有人标成「异常」。这种数据喂进去,模型不学歪才怪。
我的建议是:
- 制定标注规范文档:明确每个类别的边界条件,附上正例和反例截图
- 多人标注+交叉验证:至少两人独立标注,不一致的地方由专家仲裁
- 标注质量抽检:每批数据抽检5%-10%,错误率超过2%的批次退回重标
对于时序数据(比如麦克风音频、IMU数据),标注更麻烦。你想想看,一段10秒的音频里,只有中间2秒是关键词,前后都是噪声。这时候我习惯用分段标注工具,把长序列切成短片段再标。
4.3 数据增强策略:让数据「变多」
MCU上的数据增强,跟云端不太一样。云端可以搞复杂的随机裁剪、色彩抖动,但MCU资源有限,增强策略必须轻量且有效。说白了,就是花小钱办大事。
我常用的增强策略分三类:
| 增强类型 | 具体方法 | 适用数据 | MCU端可行性 |
|---|---|---|---|
| 几何变换 | 随机裁剪、翻转、小角度旋转 | 图像、雷达点云 | 高(整数运算即可) |
| 噪声注入 | 高斯噪声、椒盐噪声、量化噪声模拟 | 音频、振动、图像 | 高(可离线完成) |
| 时序扰动 | 时间拉伸、随机偏移、幅度缩放 | IMU、ECG、语音 | 中(需注意边界处理) |
为什么会这样?因为模型在训练时没见过量化噪声,部署时突然遇到,表现就会不稳定。提前在数据增强阶段引入,相当于给模型打了一剂「预防针」。
4.4 数据格式转换:TFRecord与二进制格式
数据准备好了,接下来就是格式转换。MCU上跑推理,不可能像PC一样从文件夹里一张张读图片。我们需要把数据打包成连续存储的二进制格式,方便MCU直接读取。
我个人常用两种格式:
4.4.1 TFRecord格式(TensorFlow生态)
如果你用TensorFlow训练模型,TFRecord是首选。它把数据序列化成二进制,读取速度快,还能利用TensorFlow的数据管道。
# 将图像数据写入TFRecord
import tensorflow as tf
def serialize_example(image_bytes, label):
feature = {
'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image_bytes])),
'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
}
example = tf.train.Example(features=tf.train.Features(feature=feature))
return example.SerializeToString()
# 写入文件
with tf.io.TFRecordWriter('dataset.tfrecord') as writer:
for image_path, label in data_pairs:
image_bytes = open(image_path, 'rb').read()
example = serialize_example(image_bytes, label)
writer.write(example)
4.4.2 自定义二进制格式(通用方案)
对于非TensorFlow生态,或者需要极致存储效率的场景,我习惯自己定义二进制格式。结构很简单:
// 二进制文件结构示例
// [文件头] [样本1] [样本2] ... [样本N]
// 文件头(12字节)
typedef struct {
uint32_t magic; // 魔数,用于校验文件类型,例如 0x44415441
uint32_t num_samples; // 样本总数
uint32_t sample_size; // 每个样本的字节数(固定长度)
} FileHeader;
// 每个样本(固定长度)
typedef struct {
uint8_t data[SAMPLE_DATA_SIZE]; // 原始数据
uint8_t label; // 标签
} Sample;
4.5 数据准备全流程
说了这么多,咱们用一张图把整个流程串起来:
4.6 实战建议:小数据集起步
最后说点实在的。很多初学者一上来就想搞几万张图片,觉得数据越多越好。但在嵌入式AI项目里,我建议从小数据集开始。
为什么?因为MCU上的模型通常很小(几十KB到几百KB),参数量有限。你拿1000张图片训练一个MobileNetV2,效果可能跟10000张差不多。但数据准备的工作量却差了10倍。
嗯,数据准备这块就讲到这里。记住一句话:垃圾进,垃圾出。数据质量决定了模型的天花板,后面的剪枝和部署只是在这个天花板下做优化。把数据准备好,后面的路就好走了。