卷积神经网络初体验——使用pytorch搭建CNN

〇、基本流程

加载数据->搭建模型->训练->测试

一、加载数据

通过使用torch.utils.data.DataLoader和torchvision.datasets两个模块可以很方便地去获取常用数据集(手写数字MNIST、分类CIFAR),以及将其加载进来。

1.加载内置数据集

 import torch
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision import transforms
 1 train_loader = torch.utils.data.DataLoader(
 2     torchvision.datasets.MNIST('mnist_data', train=True, download=True,
 3                                transform=torchvision.transforms.Compose([
 4                                    torchvision.transforms.ToTensor(),
 5                                    torchvision.transforms.Normalize(
 6                                        (0.1307,), (0.3081,))
 7                                ])),
 8     batch_size=batch_size, shuffle=True)
 9 # train 是否为训练集,         download 数据集不存在时是否下载数据集
10 # ToTensor() 转换成tensor格式,Normalize() 归一化,将数据作(data-mean)/std
11 # batch_size 加载一批数量,    shuffle 是否打散数据

2.加载自定义数据集

用torchvision.datasets.ImageFolder加载图片数据集

二、搭建模型

一个模型可以表示为python的一个类,这个类要继承torch.nn.modules.Module,并且实现forward( )方法

 1 class Lenet5(nn.Module):
 2     """
 3     for CIFAR10
 4     """
 5     def __init__(self):
 6         super(Lenet5, self).__init__()
 7 
 8         # 两层卷积
 9         self.conv_unit = nn.Sequential(
10             
11             # 3表示input,可以理解为图片的通道数量,即我的卷积核一次要到几个tensor去作卷积
12             # 6表示有多少个卷积核
13             # stride表示卷积核移动步长,padding表示边缘扩充
14             nn.Conv2d(3, 6, kernel_size=5, stride=1, padding=0),    # 卷积
15             nn.AvgPool2d(kernel_size=2, stride=2, padding=0),      # 池化
16 
17             nn.Conv2d(6, 16, kernel_size=5, stride=1, padding=0),
18             nn.AvgPool2d(kernel_size=2, stride=2, padding=0)
19         )
20 
21         # 3层全连接层
22         self.fc_unit = nn.Sequential(
23             nn.Linear(16*5*5, 120),
24             nn.ReLU(),
25             nn.Linear(120, 84),
26             nn.ReLU(),
27             nn.Linear(84, 10)
28         )
31 
32 
33     def forward(self, x):                # 数据从此进来,经过定义好的各层网络,最终输出
34         batchsz = x.size(0)
35         x = self.conv_unit(x)
36         x = x.view(batchsz, 16*5*5)          # 经过卷积层后,对数据维度作处理,以适应全连接层
37         logits = self.fc_unit(x)
38         return logits

三、训练

训练过程可以认为是对参数优化的过程,通过输入数据,得到输出,计算损失(误差),再经过误差反向传播得到梯度信息,以更新参数。

 1     # 实例模型、配置损失函数、优化器
 2     device = torch.device('cuda')                       # 转为GPU上执行
 3     model = Lenet5().to(device)                         # 实例化模型
 4     criteon = nn.CrossEntropyLoss().to(device)          # 损失函数
 5     optimizer = optim.Adam(model.parameters(), lr=1e-3) # 优化器
 6     print(model)
 7 
 8     # 训练
 9     for epoch in range(1000):                   # 迭代1000次
10         model.train()                           # 模型切换为训练模式
11         for batchidx, (x, label) in enumerate(cifar_train):
12             x, label = x.to(device), label.to(device)
13             logist = model(x)                   # 得到模型的输出
14             loss = criteon(logist, label)       # 计算损失
15             optimizer.zero_grad()               # 旧梯度清零
16             loss.backward()                     # 误差反向传播
17             optimizer.step()                    # 梯度更新
18 
19         print(epoch, loss.item())

四、测试

当模型训练完毕后,进行数据测试。

 1      model.eval()                 # 切换为验证模式
 2         with torch.no_grad():            # 不进行梯度更新
 3             total_correct = 0                   # 记录正确的数据量
 4             total_num = 0                       # 记录总数据量
 5             for x, label in cifar_test:
 6                 x, label = x.to(device), label.to(device)
 7                 logist = model(x)               # 获得模型输出
 8                 pred = logist.argmax(dim=1)     # 取值最大的下标,在这里恰好对应图片标签
 9                 
10                 # eq(pred, label)表示比较预测值和实际标签
11                 total_correct += torch.eq(pred, label).float().sum().item()
12                 total_num += x.size(0)
13             acc = total_correct / total_num     # 计算正确率

五、其他

1、保存与加载模型

即当模型训练好之后,将模型保存,下一次可以直接使用。

1 torch.save(model.state_dict(), 'best.mdl')      # 保存模型
2 
3 model.load_state_dict(torch.load('best.mdl'))   # 加载模型