4、模型训练准备:数据集采集与标注、数据增强策略、训练集/验证集/测试集划分,以及使用TensorFlow/Keras或PyTorch训练模型

在STM32CubeAI的模型移植流程中,模型训练准备阶段直接决定了最终部署到MCU上的模型性能上限。很多工程师在完成环境搭建后,急于开始训练网络,却忽略了数据层面的系统性工程。实际上,对于嵌入式视觉任务而言,数据质量与数据分布的管理远比网络结构的选择更为关键。本章将围绕数据集采集与标注、数据增强策略、数据集划分以及基于TensorFlow/Keras与PyTorch的训练实现展开,帮助您建立一套可复现、可评估、可部署的模型训练工作流。

模型训练准备阶段数据工程链路 ① 数据采集 场景覆盖/光照变化 分辨率/视角/遮挡 目标物与背景多样性 ② 数据标注 分类标签/边界框 语义分割掩码 标注一致性校验 ③ 数据增强 几何变换/色彩扰动 随机裁剪/翻转/旋转 MixUp/CutMix策略 ④ 数据集划分 训练集/验证集/测试集 分层采样/类别均衡 分布一致性检查 ⑤ 模型训练与验证 TensorFlow/Keras 或 PyTorch 训练/验证/测试三阶段评估 性能不达标 → 数据补充/增强策略调整/网络结构优化 模型导出 .tflite / .onnx 量化/剪枝准备 关键原则:数据分布决定模型上限,增强策略影响泛化能力,划分方式影响评估可信度

4.1 数据集采集:场景覆盖与样本多样性

核心原理:嵌入式视觉模型部署后所面对的真实环境是复杂多变的。STM32CubeAI生成的模型通常运行在资源受限的MCU上,其泛化能力完全依赖于训练数据的分布覆盖度。如果训练数据仅包含单一光照、单一背景或单一角度的样本,模型在实机运行时会频繁出现误判。数据采集的核心目标是让训练集尽可能逼近目标部署场景的真实分布,包括光照强度、色温、视角偏移、遮挡程度、运动模糊以及目标物自身的形态变化。

操作流程:在采集阶段,建议按照以下步骤执行:

  • 场景清单制定:列出目标应用可能出现的所有场景,例如室内/室外、白天/夜晚、强光/逆光、近距离/远距离等,并为每个场景分配采集比例。
  • 设备参数固定:使用与最终部署硬件相同或相近的摄像头模组(如OV2640、OV5640),固定分辨率、帧率、白平衡模式,避免训练与推理时的域偏移。
  • 样本量规划:对于分类任务,每类至少采集500-1000张原始图片;对于检测任务,每类目标实例不少于2000个标注框。若条件允许,原始数据量建议达到最终训练样本量的1.5倍,为清洗和筛选留出余量。
  • 数据清洗:剔除模糊、过曝、失焦以及标注错误的图片,使用直方图或感知哈希算法辅助去重。
实战经验:在工业缺陷检测项目中,我们曾因采集时使用了高分辨率工业相机,而部署时使用低分辨率OV5640,导致模型精度从98%骤降至82%。后续重新使用目标摄像头采集数据并加入分辨率扰动增强后,精度恢复至96.5%。务必保证采集设备与部署设备的一致性。

4.2 数据标注:格式选择与质量控制

核心原理:标注质量直接决定了模型学习的上限。对于STM32CubeAI支持的视觉任务,常见标注格式包括:图像分类的文件夹标签、目标检测的Pascal VOC XML或YOLO TXT格式、语义分割的PNG掩码。标注的核心矛盾在于人工成本与标注精度之间的平衡。对于嵌入式场景,建议优先选择边界框标注,因为其成本低于像素级分割,且对MCU端的目标检测任务足够有效。

标注流程与工具:

  1. 使用LabelImg或Labelme工具进行标注,导出为VOC格式或COCO格式。
  2. 设置标注规范:明确边界框包含完整目标物,遮挡超过50%的目标不标注,模糊目标不标注。
  3. 执行交叉验证:由第二人随机抽取10%的标注结果进行复核,计算IoU(Intersection over Union)一致性,要求IoU > 0.85。
  4. 对于分类任务,检查类别分布是否均衡,若存在长尾分布,需补充少数类样本或采用类别权重。
标注类型 适用任务 STM32CubeAI支持格式 标注成本 推荐工具
分类标签 图像分类 文件夹结构 低 自写脚本
边界框 目标检测 VOC XML / YOLO TXT 中 LabelImg
实例分割 像素级分割 COCO JSON 高 Labelme
风险提示:标注不一致是模型训练中最隐蔽的错误来源。例如,不同标注者对“目标完整”的定义不同,导致部分边界框只框住目标的一半。这种噪声会严重干扰模型的回归收敛,且难以通过数据增强弥补。务必在标注前制定详细的标注手册,并附示例图。

4.3 数据增强策略:提升泛化能力的关键手段

核心原理:数据增强通过在训练过程中对原始样本施加随机变换,生成语义不变但外观多样的新样本,从而扩大训练数据的有效分布范围。对于MCU部署场景,增强策略需要特别关注光照鲁棒性和几何鲁棒性,因为嵌入式设备往往面临视角变化和光照波动。但需注意,增强强度过大会导致训练分布与真实分布偏离,反而降低精度。

推荐增强组合:以下策略经过实际项目验证,适用于STM32CubeAI的典型视觉任务:

  • 几何变换:随机水平翻转(概率0.5)、随机旋转(±15°)、随机缩放(0.8~1.2倍)、随机平移(±10%)。
  • 色彩扰动:亮度调整(±20%)、对比度调整(±20%)、饱和度调整(±20%)、色相微调(±5°)。
  • 噪声注入:高斯噪声(σ=0.01~0.03)、椒盐噪声(概率0.01),模拟传感器噪声。
  • 高级策略:MixUp(按比例混合两张图及其标签)、CutMix(将一张图的区域粘贴到另一张图上)。
# TensorFlow/Keras 数据增强示例
from tensorflow.keras.preprocessing.image import ImageDataGenerator

datagen = ImageDataGenerator(
    rotation_range=15,
    width_shift_range=0.1,
    height_shift_range=0.1,
    brightness_range=[0.8, 1.2],
    zoom_range=[0.8, 1.2],
    horizontal_flip=True,
    fill_mode='nearest'
)

# PyTorch 数据增强示例
from torchvision import transforms

transform = transforms.Compose([
    transforms.RandomHorizontalFlip(p=0.5),
    transforms.RandomRotation(15),
    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2),
    transforms.RandomResizedCrop(size=(224, 224), scale=(0.8, 1.0)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
案例拆解:在智能家居人体检测项目中,初始模型在实验室测试集上准确率达97%,但部署到实际家庭环境后误报率高达30%。分析发现,训练数据均为白天正常光照,而实际场景包含黄昏逆光、夜间红外补光等条件。解决方案:在增强管线中加入随机伽马校正(γ∈[0.8,1.4])和灰度化概率变换(概率0.2),并补充500张真实夜间样本。重新训练后,实机误报率降至4.2%。此案例说明,增强策略必须基于部署场景的失败模式进行针对性设计。

4.4 训练集/验证集/测试集划分:评估可信度的基石

核心原理:三集划分的核心目的是模拟模型在未知数据上的表现。训练集用于参数更新,验证集用于超参数调优和早停判断,测试集仅用于最终评估。对于嵌入式模型,测试集必须与训练集完全隔离,且分布应代表真实部署场景。常见的错误是使用随机划分,导致同一场景的连续帧同时出现在训练集和测试集中,造成评估结果虚高。

划分策略与操作:

  1. 分层采样:按类别比例进行分层划分,确保每个类别在三集中的比例与原始数据集一致。
  2. 场景隔离:如果数据来自不同时间或不同地点,应确保同一场景的数据只出现在一个集合中。例如,按视频片段划分,而非按帧划分。
  3. 比例建议:对于中小规模数据集(<10万张),建议训练:验证:测试 = 7:2:1;对于大规模数据集,可调整为 8:1:1。
  4. 验证集与测试集区分:验证集用于比较不同超参数或不同网络结构的效果,测试集仅在最终模型确定后使用一次,避免对测试集产生过拟合。
数据集 用途 使用频率 典型比例 注意事项
训练集 参数更新 每个epoch 70%-80% 参与数据增强
验证集 超参数调优、早停 每个epoch后 10%-15% 不参与训练,可重复使用
测试集 最终性能评估 仅一次 10%-15% 完全隔离,模拟真实场景
常见误区:许多工程师在模型精度不理想时,反复使用测试集进行调试,这实际上将测试集变成了验证集,导致最终评估结果过于乐观。正确做法是:当测试集表现不佳时,应回到训练集和验证集进行分析,调整数据或模型后重新训练,测试集只做最终确认。

4.5 使用TensorFlow/Keras与PyTorch训练模型

核心原理:TensorFlow/Keras和PyTorch是当前主流的深度学习框架。对于STM32CubeAI工作流,TensorFlow/Keras可以直接导出TFLite模型,而PyTorch需要先导出ONNX再转换为TFLite。两者在训练流程上本质相同,但API风格和调试体验有所差异。选择框架时,应考虑团队熟悉度以及后续量化工具链的兼容性。

训练流程与关键参数:

  • 输入尺寸:STM32CubeAI对输入尺寸有严格限制,通常为32×32、48×48、96×96或224×224。训练时应使用与部署一致的尺寸,避免resize带来的精度损失。
  • 优化器选择:推荐使用Adam(初始学习率1e-3)或SGD(初始学习率1e-2,动量0.9)。对于小数据集,Adam收敛更快;对于大数据集,SGD配合余弦退火可获得更好泛化。
  • 损失函数:分类任务使用交叉熵,检测任务使用Smooth L1 + 交叉熵组合。
  • 批次大小:受限于MCU内存,训练批次不宜过大,建议16-64之间。若显存不足,可使用梯度累积。
  • 早停策略:监控验证集损失,若连续10个epoch无改善,则停止训练并恢复最佳权重。
# TensorFlow/Keras 训练示例
import tensorflow as tf
from tensorflow.keras import layers, models

model = models.Sequential([
    layers.Conv2D(16, 3, activation='relu', input_shape=(96, 96, 3)),
    layers.MaxPooling2D(2),
    layers.Conv2D(32, 3, activation='relu'),
    layers.MaxPooling2D(2),
    layers.Flatten(),
    layers.Dense(64, activation='relu'),
    layers.Dense(10, activation='softmax')
])

model.compile(optimizer='adam',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

history = model.fit(
    train_generator,
    validation_data=val_generator,
    epochs=50,
    callbacks=[tf.keras.callbacks.EarlyStopping(patience=10, restore_best_weights=True)]
)

# PyTorch 训练示例
import torch
import torch.nn as nn
import torch.optim as optim

class SimpleCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 16, 3, padding=1)
        self.conv2 = nn.Conv2d(16, 32, 3, padding=1)
        self.pool = nn.MaxPool2d(2)
        self.fc1 = nn.Linear(32*24*24, 64)
        self.fc2 = nn.Linear(64, 10)
    
    def forward(self, x):
        x = self.pool(torch.relu(self.conv1(x)))
        x = self.pool(torch.relu(self.conv2(x)))
        x = x.view(x.size(0), -1)
        x = torch.relu(self.fc1(x))
        return self.fc2(x)

model = SimpleCNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)

for epoch in range(50):
    for images, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
案例拆解:在垃圾分类嵌入式项目中,我们使用PyTorch训练了一个ResNet-18的简化版本(通道数缩减至1/4),输入尺寸96×96。训练集包含20,000张图片,10个类别。初始训练使用Adam,学习率1e-3,验证集准确率达到91%。随后切换至SGD(lr=1e-2,momentum=0.9)并采用余弦退火调度,训练50个epoch后验证集准确率提升至94.5%。测试集准确率为93.8%。导出ONNX后,通过ONNX Runtime转换为TFLite,再使用STM32CubeAI进行8位量化,最终在STM32H743上推理耗时约180ms,RAM占用约320KB,满足实时性要求。此案例表明,训练阶段的精度与部署后的精度存在约1-2%的差距,这是量化误差所致,应在训练时预留精度余量。

训练后评估与复盘:训练完成后,应记录以下关键指标:训练集准确率、验证集准确率、测试集准确率、损失曲线收敛情况、混淆矩阵以及每类别的精确率/召回率。若训练集准确率远高于验证集,说明存在过拟合,应增加数据增强强度或引入Dropout;若两者均低,则说明模型容量不足或数据质量有问题。对于STM32CubeAI部署,还需额外关注模型参数量和FLOPs,确保满足MCU的Flash和RAM限制。

经验总结:在模型训练准备阶段,建议建立标准化的数据管理流程:原始数据目录、标注文件目录、增强后数据缓存目录分离;每次训练记录数据版本、增强参数、网络结构、超参数和最终指标,便于后续复现和问题追溯。使用MLflow或简单的CSV日志均可实现此目的。