§1.题目
§2.数据集
MNIST包含70000张手写数字图像: 60000张用于训练,10000张用于测试。图像是灰度的,28x28像素的,并且居中的,以减少预处理时间并加快运行。
§3.实现步骤分析
§3.1.部署环境
- 语言:Python(3.7 Anoconda)
- 框架:pytorch
- 运行环境:Windows10 CPU
§3.2 定义初始化各参数
-
#### 定义初始化循环整个训练数据集的次数 n_epochs = 3
-
#### 定义初始化训练集合测试集的处理batch大小 batch_size_train = 64 batch_size_test = 1000
-
### 超参数 #### 定义初始化学习率 learning_rate = 0.01
-
### 超参数 #### 定义初始化随机梯度下降(Stochastic gradient descent)算法里面的 momentum(动量)值 momentum = 0.5
-
#### 可重复的实验,需要为任何使用随机数产生的东西设置随机种子——random random_seed = 1 torch.manual_seed(random_seed)
-
#### 定义初始化打印训练状态间隔多少个batch 设置为10,即10*64,一个batch-size是64 ,即每640张图片打印一次训练结果。 log_interval = 10
§3.3 准备数据集及预处理
-
工具包:torchvision
-
torchvision 是独立于 pytorch 的关于图像操作的一些方便工具库。
-
使用batch_size=64对MNIST数据集进行训练。
-
使用size=1000对MNIST数据集进行测试。
-
使用vision.transforms包(常用的图像操作,例如:随机切割,旋转,数据类型转换,图像到tensor ,numpy 数组到tensor , tensor 到 图像等)中的Normalize()标准化函数
-
转换使用的(预定)值:
-
0.1307(MNIST数据集的全局平均值)
-
0.3081(MNIST数据集的标准偏差)
-
-
-
代码:
# 3.准备数据集 #### train_loader——60000个训练用例,包含有图片和图片的标签。 train_loader = torch.utils.data.DataLoader( torchvision.datasets.MNIST('./data/', train=True, download=True, transform=torchvision.transforms.Compose([ torchvision.transforms.ToTensor(), torchvision.transforms.Normalize( (0.1307,), (0.3081,)) ])), batch_size=batch_size_train, shuffle=True) #### test_loader——10000个测试用例,包含有图片和图片的标签。 test_loader = torch.utils.data.DataLoader( torchvision.datasets.MNIST('./data/',train=False,download=True, transform=torchvision.transforms.Compose([ torchvision.transforms.ToTensor(), torchvision.transforms.Normalize( (0.1307,), (0.3081,)) ])), batch_size=batch_size_test, shuffle=True)
§3.4 构建网络
-
组成:
-
2D卷积层:2个
-
全连接层:2个
-
-
激活函数:整流线性单元(简称ReLUs)(softmax())
-
正则化手段:2个dropout层
-
构建方法:创建新类封装
-
代码:
# 4.构建网络模型 class Net(nn.Module): # 初始化函数 def __init__(self): super(Net, self).__init__() # 2个2D卷积层 self.conv1 = nn.Conv2d(1, 10, kernel_size=5) self.conv2 = nn.Conv2d(10, 20, kernel_size=5) self.conv2_drop = nn.Dropout2d() # 2个全连接层 self.fc1 = nn.Linear(320, 50) self.fc2 = nn.Linear(50, 10) # forward():定义使用给定的层和函数计算输出的方式 # 将数据送到模型,模型如何处理的过程 def forward(self, x): x = F.relu(F.max_pool2d(self.conv1(x), 2)) x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2)) x = x.view(-1, 320) x = F.relu(self.fc1(x)) x = F.dropout(x, training=self.training) x = self.fc2(x) return F.log_softmax(x)
§3.5 初始化网络和优化器
-
代码
# 5.初始化网络和优化器 network = Net() #### 将网络参数传递给优化器 #### 优化方法为SGD(随机梯度下降), 第一个参数是模型的参数,第二个是学习率,第三个是动量。 optimizer = optim.SGD(network.parameters(), lr=learning_rate, momentum=momentum)
§3.6 封装训练模型函数
-
基本方式:每个epoch对所有训练数据进行一次迭代。加载单独批次由DataLoader处理。
-
基本步骤:
- 使用optimizer.zero_grad()手动将梯度设置为零——PyTorch在默认情况下会累积梯度。
- 通过前向传递生成网络的输出。
- 计算输出与真值标签之间的负对数概率损失。
- 收集一组新的梯度,反向传播。
- 使用optimizer.step()将其传播回每个网络参数。
- 格式化输出打印信息。
-
代码:
def train(epoch): network.train() #### 一次64张图片以及对应的类别(0-9) # data是图片shape为[64, 1, 28, 28],64张图片,灰度图片,像素大小为28*28 # target是64个图片的类别,每个用0-9的图片表示 for batch_idx, (data, target) in enumerate(train_loader): # 使用optimizer.zero_grad()手动将梯度设置为零——PyTorch在默认情况下会累积梯度。 optimizer.zero_grad() # 通过前向传递生成网络的输出。 output = network(data) # 计算输出与真值标签之间的负对数概率损失。 loss = F.nll_loss(output, target) # 收集一组新的梯度,反向传播。 loss.backward() # 使用optimizer.step()将其传播回每个网络参数。 optimizer.step() # 格式化输出打印信息——每10*64张图片打印训练状态 if batch_idx % log_interval == 0: print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) train_losses.append(loss.item()) train_counter.append( (batch_idx * 64) + ((epoch - 1) * len(train_loader.dataset))) # 配置为要保持模型,保存模型到model.pth和optimizer.pth文件里面,如果没有会新建。 torch.save(network.state_dict(), './model.pth') torch.save(optimizer.state_dict(), './optimizer.pth')
-
辅助工作:
-
设置打印输出跟踪进度:
-
代码:
#### 保存训练和测试过程的loss train_losses = [] train_counter = [] test_losses = [] test_counter = [i*len(train_loader.dataset) for i in range(n_epochs + 1)]
-
-
正式训练前,测试仅使用随机初始化的网络参数可以获得多大的精度/损失
-
§3.7 封装测试函数
-
代码:
# 7.测试 def test(): # 模型评估 network.eval() # 初始化计数变量 test_loss = 0 correct = 0 # with torch.no_grad()或者@torch.no_grad()中的数据不需要计算梯度,也不会进行反向传播 with torch.no_grad(): # 加载测试图片 for data, target in test_loader: # 将data由模型处理,然后计算叠加损失loss output = network(data) test_loss += F.nll_loss(output, target, size_average=False).item() # pred就是找出softmax之后数组里面最大值的索引 # 最大值为预测最有可能的概率,索引则为预测的数字。 pred = output.data.max(1, keepdim=True)[1] # 叠加准确的个数 correct += pred.eq(target.data.view_as(pred)).sum() # loss和准确个数除以测试图片个数就是平均的loss和准确率 test_loss /= len(test_loader.dataset) test_losses.append(test_loss) # 格式化输出打印信息。 print('\nTest set: Avg. loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format( test_loss, correct, len(test_loader.dataset), 100. * correct / len(test_loader.dataset)))
§3.8 正式执行
-
代码:
# 8.正式执行 #### 一共训练3次,每次训练完都测试一次。 for epoch in range(1, n_epochs + 1): print("*******************第{}轮**********************\n".format(epoch)) train(epoch) test()
§3.9 绘图,可视化
-
代码:
fig = plt.figure() plt.plot(train_counter, train_losses, color='green') plt.scatter(test_counter, test_losses, color='orange') plt.legend(['Train Loss', 'Test Loss'], loc='best') plt.xlabel(u'使用的训练样例数',fontproperties=font_set) plt.ylabel(u'负对数概率损失',fontproperties=font_set) plt.show()
§4.结果分析总结
§4.1 20轮训练测试结果:
第一轮: 第二轮:
第十五轮: 第二十轮:
综合结果对比:
- 训练3轮:
- 训练20轮:
§4.2 总结
用pytorch框架,设置学习率为0.01,训练的batch-size为64,等条件下处理MNIST数据集,训练3轮时最终的训练精度达到了99%,测试精度达到了98%,训练20轮时最终的训练精度和测试精度则均达到了99%,就目前的条件来讲尚未达到过拟合,由图像看曲线渐趋平滑。