pytorch手势识别分类网络的学习记录

编程入门 行业动态 更新时间:2024-10-21 04:00:27

pytorch<a href=https://www.elefans.com/category/jswz/34/1767590.html style=手势识别分类网络的学习记录"/>

pytorch手势识别分类网络的学习记录

代码的步骤和流程

  • 文件的组织
  • 收集数据
    • 调用相机
    • 界面的搭建
    • 绑定功能
  • 训练网络模型
    • 训练神经网络模型
    • 导入包
    • 将数据集拆分为训练集和测试集
    • 创建数据加载器以批量加载数据
    • 定义神经网络
    • 训练神经网络
  • 加载训练模型
    • 预处理功能

本文的运行环境在jupuyter下的pytorch使用。
本文参考了亚博智能的代码,该代码应该改编自jetbot的避免碰撞的收集和训练,测试代码。
在jetbot的github主页的notebook可以看到相应的代码。

文件的组织

收集数据

首先调用相机,此处使用的是继承traitlets类的相机调用,并使用虚拟控件,进行图片的显示

调用相机

import traitlets
import ipywidgetsimport ipywidgets.widgets as widgets
from IPython.display import display
from camera import Camera
from image import bgr8_to_jpegcamera = Camera.instance(width=224, height=224)image = widgets.Image(format='jpeg', width=224, height=224)  # this width and height doesn't necessarily have to match the cameracamera_link = traitlets.dlink((camera, 'value'), (image, 'value'), transform=bgr8_to_jpeg)display(image)

运行完上面的代码块后,就可以实时的看到摄像头拍摄到的画面。

接下来让我们创建一些目录存储数据。我们将会建立一个叫dataset的文件夹,里面有3个文件夹。

import os
CLASS = ['one', 'two', 'three']
one = 'dataset/one'
two = 'dataset/two'
three = 'dataset/three'# we have this "try/except" statement because these next functions can throw an error if the directories exist already
try:os.makedirs(one)os.makedirs(two)os.makedirs(three)except FileExistsError:print('Directories not created becasue they already exist')

如果刷新左边的Jupyter文件浏览器,现在应该可以看到这些目录出现了。


# 计算文件夹的文件数量。
# 对应的,接受的是插件传递的参数值
import os
import re
#re正则表达式无效,提取出是图片.jpg结尾的部分
# files=  [f for f in os.listdir('dataset/'+str(value)) if re.match(r'*.jpg',f)]
# 该函数会以列表的形式列出文件夹下的文件名。
files = [f for f in os.listdir('dataset/one/') if f.endswith('jpg')]
# for f in os.listdir('dataset/one/'):
#     print(f)/
len(files)

界面的搭建

# 下拉框,选项,描述
CLASS = ['one', 'two', 'three']
#创建一个下拉框控件,有class的几个类
dataset widget = ipywidgets.Dropdown(options=CLASS, description='class')
# 布局,宽和高度
button_layout = widgets.Layout(width='128px', height='64px')
#保存按钮
save_button = widgets.Button(description='Save', button_style='success', layout=button_layout)# blocked_button = widgets.Button(description='add blocked', button_style='danger', layout=button_layout)
#保存计数,是一个int型的文本框,值使用前面的计数函数,来进行更新
save_count = widgets.IntText(layout=button_layout, value=len([f for f in os.listdir('dataset/'+str(dataset_widget.value))if f.endswith('jpg')]))
# blocked_count = widgets.IntText(layout=button_layout, value=len(os.listdir(blocked_dir)))
#使用水平布局的方式,传入一个列表。
display(widgets.HBox([save_button, save_count,dataset_widget]))# display(widgets.HBox([blocked_count, blocked_button]))

界面如下

向按钮添加事件绑定函数。
对收集的图片进行编号。
使用uuid的编码

# -*- coding:utf-8 -*-
import uuidprint(uuid.uuid1())
#bf1dfacf-67d8-11e8-9a23-408d5c985711
print(uuid.uuid3(uuid.NAMESPACE_DNS, 'yuanlin'))
#ddb366f5-d4bc-3a20-ac68-e13c0560058f
print(uuid.uuid4())
#144d622b-e83a-40ea-8ca1-66af8a86261c
print(uuid.uuid5(uuid.NAMESPACE_DNS, 'yuanlin'))
#4a47c18d-037a-5df6-9e12-20b643c334d3

如何去除中间的横杠

uuid.uuid1().hex

建立文件命名

绑定功能

from uuid import uuid1
# 传入的是分类的路径,加上生成的编号和后缀
def save_snapshot(directory):image_path = os.path.join(directory, str(uuid1().hex) + '.jpg')
#     写字节流,创建一个文件,写入图片的值with open(image_path, 'wb') as f:f.write(image.value)def save_(x):global save_countsave_snapshot('dataset/'+str(dataset_widget.value))
#     save_count.value = len(os.listdir('dataset/'+str(dataset_widget.value)))save_count.value =len([f for f in os.listdir('dataset/'+str(dataset_widget.value))if f.endswith('jpg')])
# attach the callbacks, we use a 'lambda' function to ignore the
# parameter that the on_click event would provide to our function
# because we don't need it.# 和opencv的事件一样,会传递一个回调事件,可以通过这个事件的类型来判断触发的事件类型。
# save_button.on_click(lambda x: save_())
#传入的是函数名,即C中的函数指针,python中是函数对象
save_button.on_click(save_)
#blocked_button.on_click(lambda x: save_blocked())

现在上面的按钮和选项栏已经可以将图像保存到对应的目录中。您可以使用Jupyter左边目录文件浏览器来查看这些文件! 现在继续收集一些数据,选择不同的class后让着摄像头对着相应的场景,用save对图片进行保存

1.尝试不同的方向
2.尝试不同的照明 运行下面单元格代码后就会显示图像和按钮,你就可以开始采集数据了

#后续处理,解除绑定
camera_link.unlink() 
image.close()
#按照opencv中的函数,释放相机对象
camera.cap.release()
#或者
camera.stop()

Camera.py的链接

训练网络模型

训练神经网络模型

我们将训练我们的图像分类器来检测几个类,对于这个模型,事实上是一个通用的分类模型,我们可以训练一个碰撞物和安全的分类,也可以构建一个手势分类,做一些简单的迁移训练,就可以达到效果。最好在主机端进行训练,jetson nano 2G的可能会卡死。

导入包

import torch
import torch.optim as optim
import torch.nn.functional as F
import torchvision
import torchvision.datasets as datasets
import torchvision.models as models
import torchvision.transforms as transforms

ColorJitter图像增强,修改图像的属性:亮度(brightness)、对比度(contrast)、饱和度(saturation)和色调(hue)

现在我们使用torchvision.datasets 库中的ImageFolder数据集类创建数据集实例。里面有个附加torchvision.transforms库用于转换数据,为训练模型做好准备。,如果打印出来多出来文件夹名,将多出来的文件夹删掉,重新运行下列代码,例如我们dataset只有“one”,“two”,"three"三个文件夹,打印出来应该为{‘one’: 0, ‘three’: 1, ‘two’: 2}

#图像变换的集成
dataset = datasets.ImageFolder('dataset',transforms.Compose([transforms.ColorJitter(0.1, 0.1, 0.1, 0.1),transforms.Resize((224, 224)),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
)
print(dataset.class_to_idx )

将数据集拆分为训练集和测试集

接下来,我们将数据集拆分为 训练集 和 测试集。测试集将用于验证我们训练完的模型准确性。
对于数据集划分可以参考李沐的交叉验证。

train_dataset, test_dataset = torch.utils.data.random_split(dataset, [len(dataset) - 80, 80])

创建数据加载器以批量加载数据

我们将创建两个DataLoader实例,它们为洗牌数据提供实用程序,生成批次图像,并与多个任务并行加载样本。
笔记端运行应选择0并行工作,因为无法指定并行工作数,会报错,0是指默认使用全部的。

# 创建训练的并行加载
train_loader = torch.utils.data.DataLoader(train_dataset,batch_size=16,shuffle=True,num_workers=1
)test_loader = torch.utils.data.DataLoader(test_dataset,batch_size=16,shuffle=True,num_workers=1
)

定义神经网络

torchvision 库提供了一系列我们可以使用的预训练模型。

在一个称为迁移学习的过程中,我们可以重新利用预先训练的模型(在数百万图像上进行训练),以获得可能少的数据,准确完成尽量多的任务。

在预训练模型的原始训练中学到的重要特征可重复用于新任务。 我们将使用alexnet模型。

model = models.alexnet(pretrained=True)


.html
运行该命令会下载,如果网络不好,可以将预训练权重直接放在该路径下。

此处指定预训练后的结果,即导入在源数据集上训练过后的模型。
对于网络,可以输出它的一些结构信息来了解它的原理。

print(model)


修改模型的分类层,将输出从原来的1000变成我们要输出的个数

model.classifier[6] = torch.nn.Linear(model.classifier[6].in_features, 3)

将模型放进cuda中

device = torch.device('cuda')
model = model.to(device)

训练神经网络

#设置轮数
NUM_EPOCHS = 10
#最好的模型的路径
BEST_MODEL_PATH = 'gesture_model.pth'
best_accuracy = 0.0
#定义优化器,使用SGD,学习率为0.001,动量算法为0.9,即是累积值的十倍
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
#对于每轮的代码
for epoch in range(NUM_EPOCHS):#图片和标签在训练数据加载的迭代器中,放入cudafor images, labels in iter(train_loader):images = images.to(device)labels = labels.to(device)#将梯度清零optimizer.zero_grad()#输出图片放入模型的推理结果。outputs = model(images)#将输出和标签进行交叉熵损失函数,计算损失值,即优化的方向loss = F.cross_entropy(outputs, labels)#使用损失的方向传递优化参数loss.backward()#通过逐步的优化运行optimizer.step()#训练过程结束#进行测试集的验证,导入测试集test_error_count = 0.0for images, labels in iter(test_loader):images = images.to(device)labels = labels.to(device)outputs = model(images)#计算分类错误的数量#argmax是输出的参数最大的位置的量,1这个参数应该是按维度,列来进行计算	output的结果是test_error_count += float(torch.sum(torch.abs(labels - outputs.argmax(1))))#精确度是1.0减去错误率test_accuracy = 1.0 - float(test_error_count) / float(len(test_dataset))print('%d: %f' % (epoch, test_accuracy))#要设置最后的出口,即最后的精确度。,如果达到了最优的准确度,就保存模型。if test_accuracy > best_accuracy:torch.save(model.state_dict(), BEST_MODEL_PATH)best_accuracy = test_accuracy

解释
iter会产生一个迭代器,对于迭代器,会根据batchsize来确定每次返回的数据,对于一个迭代器,一次运行会返回该次的所有值,是在创建迭代器的数据源的时候规定的。output类型是一个根据批次生成的,分类的概率值,如推理数为3,放入的批次为25,则output的形状为[25,3],在通过argmax的处理后,即传入维度,便成了25。且数组对应的是标签的index,也就是预测值。将不一致的预测值的绝对值进行加和,就得到了最后的错误率。

加载训练模型

执行以下代码,初始化PyTorch模型。

import torch
import torchvisionmodel = torchvision.models.alexnet(pretrained=False)
model.classifier[6] = torch.nn.Linear(model.classifier[6].in_features, 3)
model.load_state_dict(torch.load('gesture_model.pth'))

转移到cuda上

device = torch.device('cuda')
model = model.to(device)

预处理功能

现在我们加载了模型,但有一个小问题,就是我们的摄像头的图像格式要与训练模型时的图像格式完全相同。要做到这一点,我们需要做一些预处理。分如下几个步骤:

  1. 从BGR转换为RGB模式
  2. 从HWC布局转换为CHW布局
  3. 使用与训练期间相同的参数进行标准化(我们的摄像机提供[0,255]范围内的值,并在[0,1]范围内训练加载的图像,因此我们需要缩放255.0
  4. 将数据从CPU内存传输到GPU内存
  5. 批量添加维度
import cv2
import numpy as np
#图像的基础设置,均值和标准差,这个是原数据集图像的一些数据,应该是RGB的归一化后的均值
mean = 255.0 * np.array([0.485, 0.456, 0.406])
stdev = 255.0 * np.array([0.229, 0.224, 0.225])#根据图像的基础属性初始化一个参数
normalize = torchvision.transforms.Normalize(mean, stdev)#处理相机的值,声明全局变量device和标准值,这个是初始值。
def preprocess(camera_value):global device, normalize#对于相机值,将其转化为RGB模式x = camera_valuex = cv2.cvtColor(x, cv2.COLOR_BGR2RGB)#修改其行列通道顺序,对于0,1,2,转变为2,1,0,从通道最后,转变为通道在最前x = x.transpose((2, 0, 1))#将值从numpy转变为torch的tensorx = torch.from_numpy(x).float()#标准化x = normalize(x)x = x.to(device)#添加一个维度x = x[None, ...]return x

通过jetcam来获得相机的数据

#from jetcam.usb_camera import USBCamera
from jetcam.csi_camera import CSICamera
from jetcam.utils import bgr8_to_jpeg
import traitlets
from IPython.display import display
import ipywidgets
import ipywidgets.widgets as widgets#camera = USBCamera(width=WIDTH, height=HEIGHT, capture_fps=30)
camera = CSICamera(width=224, height=224, capture_fps=30)
#如果使用笔记本的相机,可以修改初始化的内容后,声明
#相机运行状态
camera.running = Trueimage = widgets.Image(format='jpeg', width=224, height=224)
display(widgets.HBox([image]))

接下来,我们创建一个函数,只要相机的值发生变化,就会调用该函数。 此功能将执行以下步骤

  1. 预处理相机图像
  2. 执行神经网络
import torch.nn.functional as F
import time
import sysa=0
one_blocked=0.0
two_blocked=0.0
three_blocked=0.0
#自定义的更新函数
def update(change):global one_blocked, two_blocked, three_blocked,ax = change['new'] #原相机的值已经被修改,将中间的宽度进行取反image.value = bgr8_to_jpeg(x[:, ::-1, :])#预处理x = preprocess(x)#根据模型输出预测值y = model(x)# we apply the `softmax` function to normalize the output vector so it sums to 1 (which makes it a probability distribution)
#    y = F.softmax(y, dim=1).detach().cpu().numpy().flatten()#按照维度将y的最终预测值取出y = F.softmax(y, dim=1)one_blocked = float(y.flatten()[0])two_blocked = float(y.flatten()[1])three_blocked = float(y.flatten()[2])#做比较,判断输出的值是什么if(one_blocked > two_blocked and one_blocked > three_blocked):a = 0elif(two_blocked > one_blocked and two_blocked > three_blocked):a = 1elif(three_blocked > one_blocked and three_blocked > one_blocked):a = 2'''index = y.argmax()if y[index]>0.70:prediction_widget.value = hand[index]else:prediction_widget.value = hand[0]'''time.sleep(0.001)#update在此处调用,传入的监测值是相机的值。
update({'new': camera.value})  # we call the function once to intialize

我们已经创建了神经网络执行功能,但现在我们需要将它附加到相机进行处理。

我们用observe 函数完成这个处理。

camera.observe(update, names='value')  # this attaches the 'update' function to the 'value' traitlet of our camera
### import colors
from colorama import Fore, Back, Style
import random
display(widgets.HBox([image]))
def name_of_value(val):if val == 0:return "Rock    ";if val == 1:return "Paper   ";if val == 2:return "Scissor ";# main process
game_count = won_count = 0
TIME_DELTA = 0.7
try:while True: # forever loop# wait for signalsys.stdout.write("\n\rAre you ready?") time.sleep(2.0)#GPIO.wait_for_edge(BUTTON_PIN, GPIO.RISING)# reset light and rotationgame_count = game_count+1sys.stdout.flush()sys.stdout.write("\rGame %2s: Rock-" % game_count)time.sleep( TIME_DELTA )    # Rock-sys.stdout.flush()        sys.stdout.write("\rGame %2s: Paper-" % game_count)time.sleep( TIME_DELTA )    # Paper-sys.stdout.flush()sys.stdout.write("\rGame %2s: Scissors-" % game_count)time.sleep( TIME_DELTA )# Scissors (GO!)sys.stdout.flush()sys.stdout.write("\rGame %2s: GO!" % game_count )rint = random.randint(0,2)# Wait a little and detect hand gesturetime.sleep( TIME_DELTA )sint = a; sys.stdout.write("\rGame %2s  " % game_count)sys.stdout.write("Opponent: %s You: %s " % (name_of_value(rint), name_of_value(sint)))  sys.stdout.write("\tR:%f, P:%f, S:%f\t" % (one_blocked, two_blocked, three_blocked) )# win or losediff = 0diff = (rint - sint) % 3;if diff == 0:sys.stdout.write(Fore.YELLOW+"Draw")#tie_game();elif diff == 1:sys.stdout.write(Fore.RED+"You lose")#won_game();elif diff == 2:sys.stdout.write(Fore.GREEN + "You win!!")won_count = won_count + 1#lost_game();sys.stdout.write(Style.RESET_ALL)except KeyboardInterrupt:sys.stdout.write(Style.RESET_ALL + "\n")# statistics at the endif game_count != 0:print("Toatal score: %s/%s\n" % (won_count, game_count) )sys.exit(0)

更多推荐

pytorch手势识别分类网络的学习记录

本文发布于:2024-03-14 18:46:11,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1737109.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:手势   网络   pytorch

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!