手势识别数据集处理和模型训练文档
手势识别数据集处理和模型训练文档
数据预处理实现
图像预处理核心代码
1 |
|
这个预处理函数实现了多个关键功能:
使用cv2.imread读取图片时返回BGR格式,需要转换为灰度图。选择灰度图而不是彩色图有两个原因:首先,手势识别主要依赖形状特征而不是颜色信息;其次,单通道输入可以显著减少模型参数量和计算量。
resize到96x96是在效果和性能间的平衡。过大的尺寸会增加计算量但边缘细节过多可能引入噪声,过小的尺寸会丢失重要特征。经过实验,96x96是一个比较好的平衡点。
归一化使用了float32而不是float64,这是深度学习中的最佳实践:
- float32提供足够的精度
- 相比float64可以节省一半的内存和带宽
- 现代GPU对float32的计算做了优化
数据集构建与加载
1 |
|
这个Dataset类的设计体现了几个重要的技术考量:
继承PyTorch的Dataset类,这样可以利用PyTorch的DataLoader进行高效的数据加载:
- 支持多进程加载,提高数据读取效率
- 自动处理batch划分
- 支持打乱数据顺序
- 支持内存固定(pin_memory),提高GPU传输效率
unsqueeze(1)操作在实现中非常关键:
- 原始图片数据形状为(H,W)
- CNN需要的输入形状是(C,H,W)
- unsqueeze(1)在第1维度(通道维度)增加一个维度
- 这样就从(H,W)变成了(1,H,W),符合CNN的输入要求
transform的设计采用了可选参数模式:
- 训练集需要数据增强,会传入transform
- 验证集和测试集不需要数据增强,transform为None
- 这种设计让一个Dataset类可以同时用于训练和评估
模型架构实现
倒残差块(Inverted Residual Block)
1 |
|
倒残差块是MobileNetV2中提出的创新结构,其设计包含多个精妙之处:
expand_ratio控制特征通道的扩展:
- 常规残差块是先降维再升维
- 倒残差块反其道而行之,先升维再降维
- 升维可以提供更大的特征空间,有利于特征提取
- 主要计算量在中间层,通过分组卷积降低计算复杂度
深度可分离卷积的实现:
- groups=hidden_dim使每个通道独立卷积
- 相比常规3x3卷积可以降低9倍计算量
- 在精度损失很小的情况下大幅提升效率
bias=False的使用:
- 卷积层后接BatchNorm时可以省略偏置项
- BatchNorm会学习偏置的作用
- 减少参数量并提供更好的正则化效果
ReLU6的选择:
- 相比普通ReLU,ReLU6在6处截断
- 适合低精度量化部署
- 有更好的数值稳定性
完整网络结构
1 |
|
网络整体架构经过精心设计,每个部分都有其特定的作用:
第一层卷积设计:
- 使用stride=2直接下采样,减少后续计算量
- 16个输出通道提供基础特征表达
- 3x3卷积核在第一层提取基础纹理特征
- padding=1保持特征图尺寸关系简单
主干网络的设计考量:
- 采用4个倒残差块,深度适中
- 通道数从16->24->32逐步上升,符合特征提取的规律
- stride=2的块实现特征图尺寸降低
- stride=1的块在相同尺度上精炼特征
- expand_ratio=6提供了充足的特征变换空间
分类头的特殊设计:
- 使用AdaptiveAvgPool2d自适应池化到1x1
- 这种设计可以适应不同输入尺寸
- 全局平均池化相比全连接层参数更少
- 最后一层才使用全连接,减少过拟合风险
训练实现
优化器与学习率设置
1 |
|
训练配置的每个选择都经过了仔细考虑:
优化器选择Adam的原因:
- 自适应学习率,减少手动调整
- 动量项帮助跨过局部最优
- 对学习率不太敏感,便于调试
- RMSprop和动量的结合提供了良好的收敛性
余弦退火学习率调度的优势:
- 前期快速下降探索空间
- 中期缓慢下降精细搜索
- 后期小学习率精调模型
- 周期性调整可能跳出局部最优
权重初始化的策略:
- 卷积层用He初始化,适合ReLU激活函数
- BatchNorm层的gamma初始化为1,beta初始化为0
- 全连接层采用Xavier初始化,在该层更适合
- mode=’fan_out’考虑了输出神经元数量
训练循环
1 |
|
训练循环实现了多个重要的训练技巧:
训练和验证的模式切换:
- model.train()启用BatchNorm和Dropout
- model.eval()关闭这些随机行为
- 这对于获得稳定的验证结果很重要
梯度清零和更新:
- optimizer.zero_grad()避免梯度累积
- loss.backward()计算梯度
- optimizer.step()更新参数
- 这个顺序保证了正确的参数更新
验证阶段的优化:
- with torch.no_grad()避免存储计算图
- 减少了显存占用
- 提高了验证速度
指标统计方法:
- loss.item()只获取标量值
- predicted.eq(labels)计算正确预测
- sum().item()统计正确总数
- 这些操作都很轻量,不影响训练速度
模型导出
ONNX导出
1 |
|
ONNX导出过程包含几个关键技术点:
dummy_input的设置:
- 形状必须匹配模型输入要求
- 使用randn生成随机值
- 第一维是batch维度
- 单通道输入所以第二维是1
dynamic_axes的作用:
- 允许改变batch_size大小
- 适应不同的推理场景
- 其他维度保持固定
- 有利于部署时的灵活性
命名规范:
- input_names和output_names要有意义
- 便于在不同框架中识别
- 方便推理时的张量绑定
TFLite转换
1 |
|
TFLite转换涉及一些重要的优化设置:
优化级别选择:
- DEFAULT优化是基础优化集合
- 包括常量折叠、算子融合等
- 在精度和性能间取得平衡
- 适合大多数移动端场景
数据类型设置:
- 使用float32保持精度
- 避免量化带来的精度损失
- 手势识别任务对精度敏感
- 模型足够小,不需要量化压缩
部署考虑:
- TFLite模型可直接在移动端使用
- 支持CPU、GPU和NPU加速
- 兼容Android和iOS平台
- 便于集成到移动应用中
通过这样的导出配置,我们得到了一个既保持了原始精度,又适合移动端部署的模型。整个导出过程充分考虑了实际应用场景的需求,为后续的部署工作打下了良好的基础。