手写数字识别,也就是让机器能够习得图片中的手写数字,并能正确归类。
本文使用 pytorch 搭建一个简单的神经网络,实现手写数字的识别,
从本文,你可了解到:
1、搭建神经网络的流程
2、完成手写数字识别模型
3、pytorch基本库
1.准备数据
''' 1. 导人必要的模块 ''' import numpy as np import torch # 导入 pytorch 内置的 mnist 数据 from torchvision.datasets import mnist #导入预处理模块 import torchvision.transforms as transforms from torch.utils.data import DataLoader #导入nn及优化器 import torch.nn.functional as F import torch.optim as optim from torch import nn
其中,torch.nn 是 pytorch 中重要的神经网络高级封装,其封装了:常见的网络层,如:卷积,以及优化器等。
我们这里使用 mnist 数据集 ,其里面就包括了 手写数字识别的数据集。
transforms 和 DataLoader 主要用来做数据的下载和预处理。
torch.optim 是我们使用的优化器
下面我们定义一些超参数:
''' 2. 定义一些超参数 ''' train_batch_size = 64 # 训练批次 test_batch_size = 128 # 测试批次 learning_rate = 0.01 # 学习率 num_epoches = 20 lr = 0.01 momentum = 0.5
接下来,下载 mnist 数据集,并封装到 DataLoader 中:
''' 3. 下载数据并对数据进行预处理 ''' #定义预处理函数,这些预处理依次放在Compose函数中。 transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize([0.5], [0.5])]) #下载数据,并对数据进行预处理 train_dataset = mnist.MNIST('/Users/zhouzhan/Documents/to_github/NLPLearn/Deep-Learning/data', train=True, transform=transform, download=True) test_dataset = mnist.MNIST('/Users/zhouzhan/Documents/to_github/NLPLearn/Deep-Learning/data', train=False, transform=transform) #dataloader是一个可迭代对象,可以使用迭代器一样使用。 train_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True) test_loader = DataLoader(test_dataset, batch_size=test_batch_size, shuffle=False)
2.可视化数据源
下载成功后,我们看一下数据集长什幺样:
import matplotlib.pyplot as plt %matplotlib inline examples = enumerate(test_loader) batch_idx, (example_data, example_targets) = next(examples) fig = plt.figure() for i in range(6): plt.subplot(2,3,i+1) plt.tight_layout() plt.imshow(example_data[i][0], cmap='gray', interpolation='none') plt.title("Ground Truth: {}".format(example_targets[i])) plt.xticks([]) plt.yticks([])
运行结果:
3.构建模型
构建模型,即:构建神经网络模型
其搭建神经网络所需组件:
- 层:神经网络的层级
- 模型:层构成的网络
- 损失函数:学习过程中的目标函数,即:损失函数最小化
- 优化器:如何使损失函数最小化的方法
首先,是构建网络模型,模型是由层构成的网络,我们的模型有2个隐藏层,且每层都含有一个激活函数 ReLU,最后使用 torch.max(out,1) 找出张量 out 最大值对应索引作为预测值:
代码如下:
''' 1. 构建网络 ''' class Net(nn.Module): """ 使用sequential构建网络,Sequential()函数的功能是将网络的层组合到一起 """ def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim): super(Net, self).__init__() # 第一层网络 self.layer1 = nn.Sequential(nn.Linear(in_dim, n_hidden_1),nn.BatchNorm1d(n_hidden_1)) # 第二层网络 self.layer2 = nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2),nn.BatchNorm1d(n_hidden_2)) # 输出层 self.layer3 = nn.Sequential(nn.Linear(n_hidden_2, out_dim)) def forward(self, x): x = F.relu(self.layer1(x)) x = F.relu(self.layer2(x)) x = self.layer3(x) return x
我们定义了一个 class Net,它继承 nn.Module 类(它是所有网络的基类)
nn.Module 类里面定义了很多模型,如:卷积层、全连接层、池化层等,一般定义网络都需基层该类。
其中,__ init __ 方法,用于定义网络;forward 方法,实现前向传播。
forward函数:任务是把输入层、网络层、输出层链接起来,实现信息的前向传导。
nn.Sequential:一个有序的容器,它可将神经网络模块依次添加到计算图中执行。
实例化网络:
''' 2. 实例化网络 ''' #检测是否有可用的GPU,有则使用,否则使用CPU device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") #实例化网络 model = Net(28 * 28, 300, 100, 10) model.to(device)
最后,我们定义损失函数和优化器,则网络构造完毕:
# 定义损失函数和优化器 criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum
criterion: 交叉熵损失
optimizer:SGD优化器(随机梯度下降算法)
4.训练模型
训练模型的代码有点长,
根据之前设置的 num_epoches 进行多次训练和预测,
其目的是通过训练,学习到适合的参数,动态修改学习率
其步骤如下:
- 动态修改学习率;
- model.train(),将模型设置为训练模式;
- 前向传播;
- 反向传播;
- 计算训练误差;
- model.eval(),将模型设置为预测模式;
- 预测模型;
- 计算预测误差。
根据以上步骤,其代码实现如下:
''' 1. 训练模型 ''' # 开始训练 losses = [] acces = [] eval_losses = [] eval_acces = [] for epoch in range(num_epoches): train_loss = 0 train_acc = 0 model.train() #动态修改参数学习率 if epoch%5==0: optimizer.param_groups[0]['lr']*=0.1 for img, label in train_loader: img=img.to(device) label = label.to(device) img = img.view(img.size(0), -1) # 前向传播 out = model(img) loss = criterion(out, label) # 反向传播 optimizer.zero_grad() loss.backward() optimizer.step() # 记录误差 train_loss += loss.item() # 计算分类的准确率 _, pred = out.max(1) num_correct = (pred == label).sum().item() acc = num_correct / img.shape[0] train_acc += acc losses.append(train_loss / len(train_loader)) acces.append(train_acc / len(train_loader)) # 在测试集上检验效果 eval_loss = 0 eval_acc = 0 # 将模型改为预测模式 model.eval() for img, label in test_loader: img=img.to(device) label = label.to(device) img = img.view(img.size(0), -1) out = model(img) loss = criterion(out, label) # 记录误差 eval_loss += loss.item() # 记录准确率 _, pred = out.max(1) num_correct = (pred == label).sum().item() acc = num_correct / img.shape[0] eval_acc += acc eval_losses.append(eval_loss / len(test_loader)) eval_acces.append(eval_acc / len(test_loader)) print('epoch: {}, Train Loss: {:.4f}, Train Acc: {:.4f}, Test Loss: {:.4f}, Test Acc: {:.4f}' .format(epoch, train_loss / len(train_loader), train_acc / len(train_loader), eval_loss / len(test_loader), eval_acc / len(test_loader)))
下面进行关键代码解释:
前向传播,就是将图片输入到模型中,得出结果,并计算出损失值
# 前向传播 out = model(img) loss = criterion(out, label)
反向传播,
zero_grad():将梯度清零,因为缺省情况下梯度是累加的,所以需要手动清零;
backward():自动生成梯度;
step():执行优化器,把梯度传播回每个网络。
# 反向传播 optimizer.zero_grad() loss.backward() optimizer.step()
最后的结果:
我们看看损失函数:
''' 2. 可视化训练及测试损失值 ''' plt.title('train loss') plt.plot(np.arange(len(losses)), losses) plt.legend(['Train Loss'], loc='upper right')
从这个例子中,我们学到了:
- 是一个不错的入门学习数据集;
- 数据集的下载与预处理,可使用:transforms 和 DataLoader 来完成;
- 使用 matplotlib.pyplot 来做可视化操作;
- 通过继承 nn.Module 来构建神经网络模型;
- 训练模型,最后得出结果
最后,你可以根据本文,自己手写代码完成,学习效果更佳哦~
Be First to Comment