2019.09

数据集

大名鼎鼎的MNIST数据集,包含60000张手写数字的图片,其中50000张被划分为训练集,剩下10000张被划分为测试集。数据集中图片尺寸为28281的黑白图片。
torchvision中集成了MNIST数据集,用起来很方便。

  1. import torch
  2. import torchvision
  3. import torch.nn as nn
  4. import torch.nn.functional as F
  5. import torch.optim as optim
  6. from torchvision import datasets, transforms
  7. train_dataset = datasets.MNIST(root='./data/', train=True, transform=transforms.ToTensor(),
  8. download=True)
  9. test_dataset = datasets.MNIST(root='./data/', train=False, transform=transforms.ToTensor(),
  10. download = True)

随便搭一个网络

需要注意的是,BN紧跟于卷积或全连接层之后,且BN的通道数需要与权重层的输出通道相等。另外全连接层之间也需要加非线性激活

  1. def __init__(self):
  2. super(Net, self).__init__()
  3. self.stage1 = nn.Sequential(
  4. nn.Conv2d(1, 16, kernel_size=3, padding=1),
  5. nn.BatchNorm2d(16),
  6. nn.ReLU(),
  7. nn.Conv2d(16, 16, kernel_size=3, padding=1),
  8. nn.BatchNorm2d(16),
  9. nn.ReLU(),
  10. nn.MaxPool2d(2),
  11. )
  12. self.stage2 = nn.Sequential(
  13. nn.Conv2d(16, 32, kernel_size=3, padding=1),
  14. nn.BatchNorm2d(32),
  15. nn.ReLU(),
  16. nn.Conv2d(32, 32, kernel_size=3, padding=1),
  17. nn.BatchNorm2d(32),
  18. nn.ReLU(),
  19. nn.MaxPool2d(2),
  20. )
  21. self.classify = nn.Sequential(
  22. nn.Linear(1568, 256),
  23. nn.BatchNorm1d(256),
  24. nn.ReLU(),
  25. nn.Linear(256, 10),
  26. )

训练/测试过程

第 1 行:根据上面搭出的网络进行实例化,用GPU跑时model、data、target必须加“.cuda()”
第 2 行:定义一个优化器,这里使用带动量的SGD,选择优化对象为model.parameters(),学习率0.1
第 5 行:dataloader可以根据要求每次给出一个batch的(数据,标签)元组
第 6 行:前向推理
第 7 行:清空当前优化器梯度以便第9行的梯度反传
第 8 行:计算交叉熵loss
第 9 行:根据loss进行梯度反传
第10行:根据反传的梯度更新模型参数
第17行:with torch.no_grad()下的所有代码段对parameter的操作都将不进行自动求导,test下可加速
第18行:前向推理
第19行:计算交叉熵,reduction有none、mean、sum三种模式,分别表示不降维、取batch的平均loss、取 batch中loss的总和
第20行:取出模型前向推理结果中预测概率最大的分类作为预测分类
第21行:比对预测分类与target(lable),记录预测正确的样例数
第23行:将loss总和除以样例数(len(test_dataset)为10000,len(test_loader)为10000/batch_size);
如果再第19行中取reduction=’mean’,并将loss总和除以len(teset_loader)是错的,因为每个epoch
的最后一个batch可能样例数不足batch_size,导致算出来的平均loss略微偏小。
第15、22行:model.eval()model.train()两个函数用于控制flag,决定dropout是否启用,以及BN使用批 统计还是running_mean、running_var。所以需要在test时候调用model.eval(),并在test结束的 时候调用model.train()。

  1. model = Net().cuda()
  2. optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
  3. def train(epoch):
  4. for batch_idx, (data, target) in enumerate(train_loader):
  5. output = model(data.cuda())
  6. optimizer.zero_grad()
  7. loss = F.cross_entropy(output, target.cuda())
  8. loss.backward()
  9. optimizer.step()
  10. def test():
  11. test_loss = 0
  12. correct = 0
  13. model.eval()
  14. for data, target in test_loader:
  15. with torch.no_grad():
  16. output = model(data.cuda())
  17. test_loss += F.cross_entropy(output, target.cuda(), reduction='sum').item()
  18. pred = output.data.cpu().max(1, keepdim=True)[1]
  19. correct += pred.eq(target.data.view_as(pred)).cpu().sum()
  20. model.train()
  21. test_loss /= len(test_loader.dataset)