深度学习落地实战:手势识别

前言

大家好,我是机长

本专栏将持续收集整理市场上深度学习的相关项目,旨在为准备从事深度学习工作或相关科研活动的伙伴,储备、提升更多的实际开发经验,每个项目实例都可作为实际开发项目写入简历,且都附带完整的代码与数据集。可通过百度云盘进行获取,实现开箱即用

正在跟新中~

项目背景

(基于CNN实现手势识别)

在5G时代,随着网络速度的大幅提升和低延迟特性的普及,手势识别技术迎来了前所未有的发展机遇,特别是在视频直播、智能家居和智能驾驶等领域。这些应用场景都极大地依赖于用户与设备之间的高效、直观交互,而手势识别正是实现这一目标的关键技术之一。

项目环境

  • 平台:windows 10
  • 语言环境:python 3.8
  • 编辑器:PyCharm
  • PyThorch版本:1.8

1.创建并跳转到虚拟环境

python -m venv myenv

myenv\Scripts\activate.bat
  1. 虚拟环境pip命令安装其他工具包

    pip install torch torchvision torchaudio

注:此处只示范安装pytorch,其他工具包安装类似,可通过运行代码查看所确实包提示进行安装

3.pycharm 运行环境配置

进入pytcharm =》点击file =》点击settings=》点击Project:...=》点击 Python Interpreter,进入如下界面

点击add =》点击Existing environment =》 点击 ... =》选择第一步1创建虚拟环境目录myenv\Scripts\下的python.exe文件点击ok完成环境配置

数据集介绍

训练数据样式

数据集是一个包含手势识别信息的综合数据集,具体特点如下:

参与者数量:数据集由14个不同的个体(或"受试者")组成,每个人都在数据集中贡献了他们的手势数据。

手势种类:每个参与者执行了10种不同的手势,这些手势可能代表了特定的命令、符号或动作,具体取决于数据集的设计目的。

数据重复性:为了增加数据集的多样性和鲁棒性,每种手势都被每个参与者重复了10次。这意味着对于每个手势,数据集都包含了来自每个参与者的10个样本。

总数据量:综合以上信息,数据集总共包含了14(人)x 10(手势种类)x 10(重复次数)= 1400个手势样本。

数据来源:

Kinect数据:这些数据通过Microsoft Kinect传感器获取,可能包括深度图像、彩色图像、骨骼跟踪数据等。Kinect的校准参数也被提供,这对于确保数据的准确性和一致性至关重要。

Leap Motion数据:Leap Motion是一种小型的手部追踪设备,能够提供高精度的手部姿势和手指运动数据。Leap SDK(软件开发工具包)提供的所有相关参数都被包含在内,这些参数可能包括手掌位置、手指关节角度、指尖位置等。

训练数据获取:

私信博主获取

LeNet网络介绍

LeNet网络,由Yann LeCun及其团队在1990年代初期设计并优化,是卷积神经网络(CNN)领域的先驱之作。其最为人熟知的版本是LeNet-5,该网络在1998年被正式提出,主要用于手写数字识别,尤其是MINIST数据集上的表现尤为出色。

LeNet-5的结构相对简洁而高效,包括两个卷积层(C1、C3)、两个池化层(S2、S4)、两个全连接层(F6、OUTPUT)以及输入层和输出层。卷积层通过卷积核自动提取图像中的特征,池化层则通过下采样减少数据的空间尺寸,同时保留重要信息。全连接层则将提取的特征映射到最终的分类结果上。

LeNet网络的核心优势在于其自动提取特征的能力,这大大减少了传统图像识别方法中对手动设计特征的依赖。此外,其结构简单、计算量相对较小,使得在当时的硬件条件下也能实现较快的训练和推理速度。

然而,受限于当时的硬件条件和计算资源,LeNet网络的规模相对较小,难以处理更大规模或更复杂的图像识别任务。随着计算机硬件和深度学习技术的飞速发展,更加深层、更加复杂的卷积神经网络被设计出来,如AlexNet、VGG、ResNet等,它们在图像识别、分类、检测等领域取得了更加卓越的性能。

尽管如此,LeNet网络作为卷积神经网络的开山之作,其设计思想和基本结构仍然对后来的研究产生了深远的影响。它证明了卷积神经网络在图像识别领域的巨大潜力,并为后续的研究提供了宝贵的经验和启示。在今天,LeNet网络仍然被广泛应用于教学和科研领域,作为学习深度学习和卷积神经网络的基础模型之一。

定义CNN网络

  • 卷积层:用于图像的高级特征

  • 输出层:将卷积提取出的特征进行分类

    class LeNet5(nn.Module):
    def init(self,num_class=10):
    super(LeNet5,self).init()
    self.conv1 = nn.Conv2d(3, 8, 5)
    self.pool1 = nn.AvgPool2d((2, 2))

          self.conv2 = nn.Conv2d(8, 16, 5)
          self.pool2 = nn.AvgPool2d((2, 2))
          
          self.conv3 = nn.Conv2d(16, 32, 5)
          
          self.relu = nn.ReLU()
    
          self.fc1 = nn.Linear(28800, 1024)
          self.fc2 = nn.Linear(1024, num_class)
          
      def forward(self, x):
          # x: torch.Size([32, 3, 150, 150])
          
          x = self.conv1(x) # torch.Size([32, 8, 146, 146])
          x = self.relu(x)
          x = self.pool1(x) # torch.Size([32, 8, 73, 73])
    
          x = self.conv2(x) # torch.Size([32, 16, 69, 69])
          x = self.relu(x)
          x = self.pool2(x) # torch.Size([32, 16, 34, 34])
    
          x = self.conv3(x) # torch.Size([32, 32, 30, 30])
          x = self.relu(x)
          
          x = x.flatten(start_dim=1) # torch.Size([32, 28800])
          
          x = self.fc1(x) # torch.Size([32, 2024])
          x = self.relu(x)
          x = self.fc2(x) # torch.Size([32, 4])
    
          return x
    

加载数据集

# 1.数据转换
data_transform = {
    # 训练中的数据增强和归一化
    'train': transforms.Compose([
        transforms.RandomResizedCrop(150), # 随机裁剪
        transforms.ToTensor(), # 均值方差归一化
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
}

# 2.形成训练集
train_dataset = datasets.ImageFolder(root=os.path.join(image_path),
                                     transform=data_transform['train'])


# 3.形成迭代器
train_loader = torch.utils.data.DataLoader(train_dataset,
                                           batch_size,
                                           True)

print('using {} images for training.'.format(len(train_dataset)))

测试代码

# 加载索引与标签映射字典
with open('class_dict.pk', 'rb') as f:
    class_dict = pickle.load(f)

# 数据变换
data_transform = transforms.Compose([
     transforms.CenterCrop(150),
     transforms.ToTensor(),
     transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])

# 图片路径
img_path = r'./data/000-one/gesture-one-2021-03-07_23-07-48-1_37388.jpg'

# 打开图像
img = Image.open(img_path)
print(np.array(img).shape)

# 对图像进行变换
img = data_transform(img)

plt.imshow(img.permute(1,2,0))
plt.show()


# 将图像升维,增加batch_size维度
img = torch.unsqueeze(img, dim=0)

# 获取预测结果
pred = class_dict[model(img).argmax(axis=1).item()]
print('【预测结果分类】:%s' % pred)

完整运行代码

import math
import pickle
import os

import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from keras.preprocessing.sequence import pad_sequences
from sklearn.model_selection import train_test_split
from torch.utils.data import TensorDataset
from torchvision import transforms, datasets
from torch import optim
from torchnet import meter
from tqdm import tqdm
from PIL import Image

import matplotlib.pyplot as plt

# 模型输入参数,需要自己根据需要调整
num_class = 14 # 分类数
epochs = 20 # 迭代次数
batch_size = 64 # 每个批次样本大小
lr = 0.003 # 学习率
image_path = './data' # 图像数据路径
save_path = './best_model.pkl' # 模型保存路径
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') # 设备

# 1.数据转换
data_transform = {
    # 训练中的数据增强和归一化
    'train': transforms.Compose([
        transforms.RandomResizedCrop(150), # 随机裁剪
        transforms.ToTensor(), # 均值方差归一化
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
}

# 2.形成训练集
train_dataset = datasets.ImageFolder(root=os.path.join(image_path),
                                     transform=data_transform['train'])


# 3.形成迭代器
train_loader = torch.utils.data.DataLoader(train_dataset,
                                           batch_size,
                                           True)

print('using {} images for training.'.format(len(train_dataset)))

# 4.建立分类标签与索引的关系
cloth_list = train_dataset.class_to_idx
class_dict = {}
for key, val in cloth_list.items():
    class_dict[val] = key
with open('class_dict.pk', 'wb') as f:
    pickle.dump(class_dict, f)

class LeNet5(nn.Module):
    def __init__(self,num_class=10):
        super(LeNet5,self).__init__()
        self.conv1 = nn.Conv2d(3, 8, 5)
        self.pool1 = nn.AvgPool2d((2, 2))
        
        self.conv2 = nn.Conv2d(8, 16, 5)
        self.pool2 = nn.AvgPool2d((2, 2))
        
        self.conv3 = nn.Conv2d(16, 32, 5)
        
        self.relu = nn.ReLU()

        self.fc1 = nn.Linear(28800, 1024)
        self.fc2 = nn.Linear(1024, num_class)
        
    def forward(self, x):
        # x: torch.Size([32, 3, 150, 150])
        
        x = self.conv1(x) # torch.Size([32, 8, 146, 146])
        x = self.relu(x)
        x = self.pool1(x) # torch.Size([32, 8, 73, 73])

        x = self.conv2(x) # torch.Size([32, 16, 69, 69])
        x = self.relu(x)
        x = self.pool2(x) # torch.Size([32, 16, 34, 34])

        x = self.conv3(x) # torch.Size([32, 32, 30, 30])
        x = self.relu(x)
        
        x = x.flatten(start_dim=1) # torch.Size([32, 28800])
        
        x = self.fc1(x) # torch.Size([32, 2024])
        x = self.relu(x)
        x = self.fc2(x) # torch.Size([32, 4])

        return x

# 6.模型训练
model = LeNet5(num_class)
model = model.to('cpu')
criterion = nn.CrossEntropyLoss() # 损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=lr) # 优化器

best_acc = 0 # 最优精确率
best_model = None # 最优模型参数

for epoch in range(epochs):
    model.train()
    running_loss = 0 # 损失
    epoch_acc = 0  # 每个epoch的准确率
    epoch_acc_count = 0  # 每个epoch训练的样本数
    train_count = 0  # 用于计算总的样本数,方便求准确率
    train_bar = tqdm(train_loader)
    for data in train_bar:
        images, labels = data
        optimizer.zero_grad()
        output = model(images.to(device))
        loss = criterion(output, labels.to(device))
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                 epochs,
                                                                 loss)
        # 计算每个epoch正确的个数
        epoch_acc_count += (output.argmax(axis=1) == labels.view(-1)).sum()
        train_count += len(images)

    # 每个epoch对应的准确率
    epoch_acc = epoch_acc_count / train_count

    # 打印信息
    print("【EPOCH: 】%s" % str(epoch + 1))
    print("训练损失为%s" % str(running_loss))
    print("训练精度为%s" % (str(epoch_acc.item() * 100)[:5]) + '%')

    if epoch_acc > best_acc:
        best_acc = epoch_acc
        best_model = model.state_dict()

    # 在训练结束保存最优的模型参数
    if epoch == epochs - 1:
        # 保存模型
        torch.save(best_model, save_path)

print('Finished Training')

# 加载索引与标签映射字典
with open('class_dict.pk', 'rb') as f:
    class_dict = pickle.load(f)

# 数据变换
data_transform = transforms.Compose([
     transforms.CenterCrop(150),
     transforms.ToTensor(),
     transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])

# 图片路径
img_path = r'./data/000-one/gesture-one-2021-03-07_23-07-48-1_37388.jpg'

# 打开图像
img = Image.open(img_path)
print(np.array(img).shape)

# 对图像进行变换
img = data_transform(img)

plt.imshow(img.permute(1,2,0))
plt.show()


# 将图像升维,增加batch_size维度
img = torch.unsqueeze(img, dim=0)

# 获取预测结果
pred = class_dict[model(img).argmax(axis=1).item()]
print('【预测结果分类】:%s' % pred)
相关推荐
余炜yw8 分钟前
【LSTM实战】跨越千年,赋诗成文:用LSTM重现唐诗的韵律与情感
人工智能·rnn·深度学习
莫叫石榴姐24 分钟前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
967726 分钟前
对抗样本存在的原因
深度学习
如若1231 小时前
利用 `OpenCV` 和 `Matplotlib` 库进行图像读取、颜色空间转换、掩膜创建、颜色替换
人工智能·opencv·matplotlib
YRr YRr1 小时前
深度学习:神经网络中的损失函数的使用
人工智能·深度学习·神经网络
ChaseDreamRunner1 小时前
迁移学习理论与应用
人工智能·机器学习·迁移学习
Guofu_Liao1 小时前
大语言模型---梯度的简单介绍;梯度的定义;梯度计算的方法
人工智能·语言模型·矩阵·llama
我爱学Python!1 小时前
大语言模型与图结构的融合: 推荐系统中的新兴范式
人工智能·语言模型·自然语言处理·langchain·llm·大语言模型·推荐系统
果冻人工智能1 小时前
OpenAI 是怎么“压力测试”大型语言模型的?
人工智能·语言模型·压力测试
日出等日落1 小时前
Windows电脑本地部署llamafile并接入Qwen大语言模型远程AI对话实战
人工智能·语言模型·自然语言处理