前言
上一章内容我们初步了解了卷积、卷积神经网络、卷积神经网络的搭建过程以及经典的LeNet网络结构,本篇内容将基于LeNet网络结构,实现手势识别。
手势识别
数据集介绍
在开展手势识别之前,我们需要先下载并初步了解数据集的情况。
数据下载地址
下载地址:手势识别数据集

项目流程
在《【课程总结】Day8(上):深度学习基本流程》中,我们已了解到深度学习的基本流程为:
- 数据预处理 1.1 数据读取 1.2 数据切分 1.3 数据规范化
- 批量化打包数据
- 模型搭建
- 筹备训练
- 训练模型 5.1 定义监控指标和方法 5.2 实现训练过程 5.3 开始训练
因此,本次项目也采用如上的基本流程。
数据预处理
由上述目录结构可知,我们需要在训练前使用DataLoader将数据集打包成适合训练的格式,因此需要解决2个问题:
问题1:如何记录标签数据和图片数据
解决方法:
- 获取标签:上述目录中的G0、G1、G2...G9文件夹名称即为手势标签,因此我们可以通过os.listdir()函数获取文件夹名称。
- 保存标签:将上述遍历的G0、G1、G2...G9文件夹名称保存到列表
label_train
中,方便后续使用。 - 获取图片路径:通过os.listdir()函数获取文件夹中的图片名称,从而获取图片路径。
- 保存图片路径:将上述遍历的图片路径保存到列表
img_train
中,方便后续使用。
问题2:如何将图片和标签数据打包成适合训练的格式
解决方法:
- 构建自定义数据集类GesturesDataset
- 重写__getitem__(),len(),init()方法
- 在__getitem__()方法中:
- 使用cv2.imread()读取图片
- 使用cv2.resize()调整图片大小
- 将图像转为numpy数组
- 对矩阵数组中的数据进行归一化处理,规范化为[-1, 1]
- 使用torch将数据转为张量
- 将数据从图片数据的[H(高度), W(宽度), C(通道数)]转维度为[N(批量个数), H(高度), W(宽度), C(通道数)]
- 将标签转为数字,例如:G0 -> 0, G1 -> 1, G2 -> 2, ..., G9 -> 9
- 将标签转为张量
模型搭建
本次模型使用LeNet网络结构,相关结构已在《【课程总结】Day10:卷积网络的基本组件》阐述,本次过程不再赘述。
- 将以上代码单独封装为model.py文件,方便后续直接import。
- 在主程序中使用以下方式直接调用即可:
from models import LeNet
model = LeNet()
筹备训练
由于计算的数据量较大,所以我们需要借助torch以及GPU来提升训练速度。
模型评估
为了观察训练过程情况,定义模型评估函数:
实现训练过程
开始训练
图形化监控数据
运行结果:


通过以上执行过程可以看到,经过80轮训练后,LeNet模型在训练集上的准确率达到99%,在测试集上的准确率达到94%。
模型预测
接下来,我们使用streamlit实现一个前端页面,用户在页面上输入图片,模型会自动识别图片中的手势。
整体实现流程:
- 创建一个streamlit应用,并导入相关依赖。
- 显示当前设备是GPU设备还是CPU
- 加载模型
- 使用streamlit.file_uploader显示上传图片控件
- 使用streamlit.image显示上传的图片
- 使用加载的模型进行预测 6.1 读取图像 6.2 图像预处理 6.3 图形转为张量 6.4 转换图形的维度为[C, H, W] 6.5 新建一个批量维度[N, C, H, W] 6.6 数据搬家 6.7 模型设为评估模式 6.8 模型预测 6.9 预测结果转为标签 0 → G0, 1 → G1, 2 → G2, 3 → G3, 4 → G4, 5 → G5 6.10 返回标签结果
import streamlit
import torch
import os
import numpy as np
from PIL importImage
from models importLeNet
# 生成idx2label字典,用于显示预测结果
idx2label ={
0:'G0',
1:'G1',
2:'G2',
3:'G3',
4:'G4',
5:'G5',
6:'G6',
7:'G7',
8:'G8',
9:'G9'
}
definfer(img_path, model, device, idx2label):
"""
输入:图像地址
输出:预测类别
"""
# 1,读取图像
ifnot os.path.exists(img_path):
raiseFileNotFoundError("文件没找到")
# 2, 判断当前局部变量中是否有model
# if "m1" not in globals() or not isinstance(globals()["m1"], LeNet):
# raise ValueError("m1模型不存在")
# 3,读取图像
img =Image.open(fp=img_path)
# 4,预处理
img = img.resize((32,32))
img = np.array(img)
img = img /255
img =(img -0.5)/0.5
# 5, 转张量
img = torch.tensor(data=img, dtype=torch.float32)
# 6, 转换维度
img = img.permute(dims=(2,0,1))
# 7, 新增一个批量维度
img = img.unsqueeze(dim=0)
# 8,数据搬家
img = img.to(device=device)
# 9,模型设为评估模式
model.eval()
# 10,无梯度环境
with torch.no_grad():
# 11,正向传播
y_pred = m1(img)
# 12, 解析结果
y_pred = y_pred.argmax(dim=-1).item()
# 13,标签转换
label = idx2label.get(y_pred)
# 14, 返回结果
return label
if __name__ =="__main__":
# 1, 显示当前设备是GPU设备还是CPU
# 检测设备
device ="cuda"if torch.cuda.is_available()else"cpu"
streamlit.write(f"当前设备是{device}设备")
# 2, 加载模型
m1 =LeNet()
m1.to(device=device)
# 加载权重
m1.load_state_dict(state_dict=torch.load(f="lenet_best.pt", map_locatinotallow=device),
strict=False)
ifnotisinstance(m1,LeNet):
raiseValueError("模型加载失败")
# 3, 上传一张图片
img_path = streamlit.file_uploader(label="上传一张图片",type=["png","jpg","jpeg"])
# 3.1, 将上传的图像文件保存到临时文件
if img_path:
withopen(file="temp_img.jpg", mode="wb")as f:
f.write(img_path.getvalue())
img_path ="temp_img.jpg"
# 4, 显示上传的图片
if img_path:
img =Image.open(fp=img_path)
streamlit.image(image=img, captinotallow="上传的图片", use_column_width=True)
# 5, 加载本地的lenet_best.pt模型
if img_path:
label = infer(img_path=img_path, model=m1, device=device, idx2label=idx2label)
streamlit.write(f"预测结果是{label}")
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
运行结果:
图片
内容小结
- 回顾深度学习的整体流程,仍然是:数据预处理→批量化打包数据→模型搭建→训练模型→模型评估→模型预测
- 图片数据预处理时,批量化打包数据需要构造为[N, C, H, W]的格式
- 预处理的过程大致为:读取图片→调整图片大小→转为numpy数组→归一化→转为张量→调整维度→标签转为数字→转为张量,该过程需要在自定义数据集的__getitem__函数中完成
- 模型构建使用的是LeNet模型,该模型定义可以单独在models.py中实现,训练代码中直接import引用即可
- 训练过程以及训练时的监控过程,与前两章学习的深度学习训练过程是一样的
本文转载自公众号一起AI技术 作者:热情的Dongming
原文链接:https://mp.weixin.qq.com/s/uLcszdSP99AepL5d47e6ZQ