手写数字识别
概述
本节我们利用之前学到的PyTorch相关知识,建立一个全连接神经网络模型,用于识别手写字符. 经过本章的学习, 大家将明白如何利用PyTorch完成数据集的预处理, 数据加载器的生成, 优化器的定义, 损失的定义, 全连接神经网络的搭建,训练,测试等过程
知识点:
- 数据加载器的定义
- 优化器的定义
- 损失的定义
- 全连接网络的建立
- 模型的训练与测试
手写字符识别
在开始之前,我们先确定我们的环境是否支持GPU运算
import torch
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
若type为cuda则表示支持GPU,否则就只支持CPU. 无论说CPU环境还是GPU环境,我们的代码都是可以自由切换的. 我们只需要使用.to(device)就可以将数据在CPU和GPU之间进行切换. 若不支持.to(device)就可以使用变量转成可以放入CPU中的类型.
数据的预处理
在前面章节中,我们已经学习了数据加载器. 本节我们直接加载PyTorch自带的数据集合,该数据集合存在于torchvision.datasets
中, 可以直接利用torchvision.datasets.MNIST
获得
import torch
import torchvision
# 加载训练数据
train_dataset = torchvision.datasets.MNIST(root="./data",
train=True,
transform=torchvision.transforms.ToTensor(),
download=False)
# 加载测试数据
test_dataset = torchvision.datasets.MNIST(root="./data",
train=False,
transform=torchvision.transforms.ToTensor(),
download=False)
len(train_dataset),len(test_dataset)
我们可以看到我们加载了60000条训练的数据,10000条测试的数据
# 将数据放入数据加载器中
batch_size = 100
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
shuffle=False)
train_loader,test_loader
然后,我们再加载几张图片观察一下
import numpy as np
import matplotlib.pyplot as plt
examples = iter(test_loader)
example_data,example_targets = example.next()
for i in range(6):
plt.subplot(2,3,i+1)
plt.imshow(example_data[i][0],cmap="gray")
模型的建立
因为模型包含输入和输出,所以我们可以先定义这些内容
# 输入节点数就为图片的大小: 28x28x1
input_size = 784
# 由于数字为0-9,因此这是一个10分类的问题,输出节点数为10
num_classes = 10
下面就是我们构建的完成网络模型
# 构建手写数字识别的模型
class NerualNet(torch.nn.Module):
def __init__(self,input_size,hidden_size,num_classes):
super(NerualNet,self).__init__()
self.input_size = input_size
self.l1 = torch.nn.Linear(input_size,hidden_size)
self.relu = torch.nn.ReLU()
self.l2 = torch.nn.Linear(hidden_size,num_classes)
def forward(self,x):
out = self.l1(x)
out = self.relu(out)
out = self.l2(out)
return out
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 建立一个中间层为500的三层神经网络,且将模型转为当前环境支持的类型(cpu或GPU)
model = NerualNet(input_size,500,num_classes).to(device)
print(model)
从上面的数据结构可以知道,该神经网络模型一共有三层.
- 第一层为输入层,节点数量和图像大小相同
- 第二层为隐藏层,节点数为500
- 第三层为输出层, 节点大小为10, 节点数大小和类别相同
损失和优化器的定义
定义完模型后, 接下来, 我们需要定义模型训练时所血药的损失函数和优化器
这里我们就使用之前讲过的交叉熵损失函数
criterion = nn.CrossEntropyLoss()
由于传统的梯度下降算法存在一定的缺陷, 比如学习速率一直不变. 因此,我们利用pyTorch中定义的梯度下降算法的优化算法,Adam算法来进行模型的训练.
learning_rate = 0.01
# 定义优化算法
optimizer = torch.optim.Adam(model.parameters(),lr=learning_rate)
模型的训练
模型训练步骤和之前案例中的步骤一致,步骤是固定的:
- 通过模型的正向传播,输出预测结果
- 通过预测结果和真实标签计算损失
- 通过后向传播,获得梯度
- 通过梯度更新模型的权重
- 进行梯度的清空
- 循环上面的操作, 直到损失较小为止
接下来,我们用代码完成上面的步骤:
num_epochs = 2
# 数据总长度
total_steps = len(train_loader)
for epoch in range(num_epochs):
for i,(images,labels) in enumerate(train_loader):
# 100*784
images = images.reshape(-1,28*28).to(device)
labels = labels.to(device)
# 正向传播
outputs = model(images)
l = loss(outputs,labels)
# 反向传播
optimizer.zero_grad()
l.backward()
optimizer.step()
if i%100 == 0:
print(f"Loss:{l.item():.4f}")
print("模型训练完成")
从结果我们可以看出损失已经收敛了.下面我们从测试集中选几张图片来验证一下
examples = iter(test_loader)
example_data,example_targets = examples.next()
# 图片的展示
for i in range(3):
plt.subplot(1,3,i+1)
plt.imshow(example_data[i][0],cmap="gray")
plt.show()
调用我们之前训练的模型,对数据进行预测
# 结果预测
images = example_data
images = images.reshape(-1,28*28).to(device)
labels = labels.to(device)
# 正向传播
outputs = model(images)
# 输出真实结果
print("预测结果:",example_targets[0:3].detach().numpy())
print("预测结果:",np.argmax(outputs[0:3].cpu().detach().numpy(),axis=1))
注意,这里model得到的预测结果为每一个数对应的概率值,我们要利用np.argmax求取该数组中最大数的下标, 下标就是对应的第几类
模型的测试
在训练完模型后,我们将导入测试数据集, 对模型进行测试, 对比模型的预测结果和实际结果, 进而得到模型的识别准确率.
模型的测试代码很简单, 其实就是将数据传入模型之中, 并进行一次正向传播即可.
简单的说,就是复制上面的模型训练代码,然后更换数据集, 删除后面的梯度下降相关代码即可.
# 用测试集的数据,校验模型的准确率
with torch.no_grad():
n_correct = 0
n_samples = 0
for images,labels in test_loader:
# 和训练代码一致
images = images.reshape(-1,28*28).to(device)
labels = labels.to(device)
outputs = model(images)
# 返1 最大值 返2 索引 0每列最大值 1每行最大值
_, predicted = torch.max(outputs.data,1)
n_samples += labels.size(0)
n_correct += (predicted == labels).sum().item()
acc = 100.0 * n_correct/n_samples
print(f"准确率:{acc}%")
从结果可以看出, 我们利用Pytorch建立的三层全连接网络对手写字符图片也有较高的识别准确率,这侧面说明了神经网络的强大
小结
通过本节内容, 大家应该已经掌握了如何利用PyTorch完成数据集的预处理,数据加载器的生成,优化器的定义,损失的定义,全连接神经网络的搭建与训练以及模型的测试等过程.