玩命加载中 . . .

西瓜书课后习题第五章(5.10)


§1.题目

image-20201103155051695

§2.数据集

image-20201103201334383

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处理。

  • 基本步骤:

    1. 使用optimizer.zero_grad()手动将梯度设置为零——PyTorch在默认情况下会累积梯度。
    2. 通过前向传递生成网络的输出。
    3. 计算输出与真值标签之间的负对数概率损失。
    4. 收集一组新的梯度,反向传播。
    5. 使用optimizer.step()将其传播回每个网络参数。
    6. 格式化输出打印信息。
    • 代码:

      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%,就目前的条件来讲尚未达到过拟合,由图像看曲线渐趋平滑


文章作者: Angus Lan
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Angus Lan !
评论
  目录