本次实验主要是使用pytorch完成手势识别。网络包含两个隐藏层,第一层隐藏层有576个节点,第二层隐藏层有144个节点,输入784个节点(图片大小为28×28),输出10个节点(10种手势)。
目录
1. 数据集处理
本次实验所用数据集为自建数据集,首先预览了解数据,确保数据能够被正常载入。
import pandas
from torch.utils.data import Dataset
import torch
import matplotlib.pyplot as plt
class GestureDataset(Dataset):
def __init__(self, csv_file):
self.dataset = pandas.read_csv(csv_file, header=0)
def __len__(self):
return len(self.dataset)
def __getitem__(self, index):
# 图像标签
label = self.dataset.iloc[index, 0]
target = torch.zeros(10) # 神经网络预期输出
target[label] = 1.0
# 图像数据,取值范围是0~255,标准化为0~1
image_value = torch.FloatTensor(self.dataset.iloc[index, 1:].values) / 255
# 返回标签、图像数据张量以及目标张量
return label, image_value, target
def plot_image(self, index):
arr = self.dataset.iloc[index, 1:].values.reshape(28, 28)
plt.title("label = " + str(self.dataset.iloc[index, 0]))
plt.imshow(arr, interpolation='none', cmap='gray')
plt.show()
# 查看图片
gesture_dataset = GestureDataset('train.csv')
gesture_dataset.plot_image(9)
print(gesture_dataset[100])
print(len(gesture_dataset))
以上代码中各函数含义如下:
__len__() 函数的作用是返回DataFrame的大小。
__getitem__()函数索引获取数据集中的第 n 项,数据集中的第index项中提取一个标签(label)。返回值中的 target 表示神经网络的预期输出。除了与标签相对应的位置是1之外,其他值皆为0。比如手势 2 的 target 应该表示为[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]。
运行以上代码,结果如下:
2. 神经网络的建立
import torch
import torch.nn as nn
import pandas, numpy
import matplotlib.pyplot as plt
class Classifier(nn.Module):
def __init__(self):
# 初始化pytorch父类
super().__init__()
# 定义神经网络
self.model = nn.Sequential(
nn.Linear(784, 576),
nn.LeakyReLU(0.02),
nn.LayerNorm(576),
nn.Linear(576, 144),
nn.LeakyReLU(0.02),
nn.LayerNorm(144),
nn.Linear(144, 10),
nn.Sigmoid()
)
# 创建损失函数
self.Loss_function = nn.BCELoss()
# 优化器
self.optimiser = torch.optim.Adam(self.parameters(),
lr=0.0001)
# 记录训练进展的计数器和列表
self.counter = 0
self.process = []
def forward(self, inputs):
# 直接运行模型
return self.model(inputs)
def cnn_train(self, inputs, targets):
# 计算网络的输出值
outputs = self.forward(inputs)
# 计算损失值
loss = self.Loss_function(outputs, targets)
# 梯度归零,反向传播,并更新权重
self.optimiser.zero_grad() # 梯度全部归零
loss.backward()
self.optimiser.step() # 使用梯度更新可学习参数
# 每隔10个训练样本增加一次计数器的值,并将损失值添加进列表的末尾,共36080张图片
self.counter += 1
if self.counter % 10 == 0:
self.process.append(loss.item())
# 在每10000次训练后打印计数器的值,了解训练进展的快慢
if self.counter % 10000 == 0:
print("counter=", self.counter)
# 绘制训练过程的损失值
def plot_progress(self):
df = pandas.DataFrame(self.process, columns=["loss"])
df.plot(ylim=(0, 1.0), figsize=(16, 8), alpha=0.1, marker='.',
grid=True, yticks=(0, 0.25, 0.5))
plt.show()
3. 神经网络的训练
数据集处理部分代码保存为gesture_dataset.py,神经网络的建立部分代码保存为gesture_cnn.py,在创建了网络后,需要使用数据集训练网络,并保存网络参数以便后续使用。
import torch
from gesture_dataset import GestureDataset
from gesture_cnn import Classifier
# 创建神经网络
C = Classifier()
gesture_dataset = GestureDataset('train.csv')
# 在数据集训练神经网络
epochs = 3
for i in range(epochs):
print('training epoch', i + 1, "of", epochs)
for label, image_data_tensor, target_tensor in gesture_dataset:
C.cnn_train(image_data_tensor, target_tensor)
pass
pass
# 绘制分类器损失值
C.plot_progress()
# 保存网络
torch.save(C.model, 'gesture_cnn_model.pkl')
4. 神经网络的测试
from gesture_dataset import GestureDataset
import torch
import pandas
import numpy as np
import matplotlib.pyplot as plt
# 加载测试集数据
test_dataset = GestureDataset('test.csv')
record = 19
test_dataset.plot_image(record)
image_data = test_dataset[record][1]
# 调用训练后的神经网络
cnn_model = torch.load('gesture_cnn_model.pkl')
output = cnn_model(image_data)
# 绘制输出张量
# pandas.DataFrame(output.detach().numpy()).plot(kind='bar',
# legend=False, ylim=(0, 1))
# plt.show()
predict = output.detach().numpy()
print(np.where(predict == np.max(predict))[0][0])
# 测试正确率
T_test = 0
counter_test = 0
for label, image_data_tensor, target_tensor in test_dataset:
predict = cnn_model(image_data_tensor).detach().numpy()
if np.where(predict == np.max(predict))[0][0] == label:
T_test += 1
pass
counter_test += 1
if counter_test % 100 == 0:
print('counter_test = ', counter_test)
pass
test_accuracy = T_test/len(test_dataset)
print('Test Accuracy = ', test_accuracy)
# 训练集正确率
train_dataset = GestureDataset('train.csv')
T_train = 0
counter_train = 0
for label, image_data_tensor, target_tensor in train_dataset:
predict = cnn_model(image_data_tensor).detach().numpy()
if np.where(predict == np.max(predict))[0][0] == label:
T_train += 1
pass
counter_train += 1
if counter_train % 1000 == 0:
print('counter_train = ', counter_train)
pass
train_accuracy = T_train/len(train_dataset)
print('Train Accuracy = ', train_accuracy)
print(len(train_dataset))
测试结果如下:
网络最终在训练集上的正确率约为99.83%,在测试集上的正确率约为97.50%,测试结果表明网络性能较好,训练结果较好,最终的手势识别效果较好。
代码注释详细,作者能力有限,如有发现问题欢迎评论提出。