一、实验要求
在计算机上验证和测试Pytorch卷积神经网络的原理和算法实现,测试卷积神经网络的训练效果,同时查阅相关资料。
- 实验目的
1、掌握PyTorch的基本使用;
2、掌握PyTorch的卷积神经网络;
3、掌握PyTorch的图像分类训练流程;
三、实验内容
实验步骤
import os os.environ["CUDA_VISIBLE_DEVICES"] = '0' #默认为显卡0 import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim # torchvision 包收录了若干重要的公开数据集、网络模型和计算机视觉中的常用图像变换 from torchvision import datasets, transforms #做图片处理的库 #from torch.autograd import Variable import time # Training settings BATCH_SIZE = 512 # 一个batch 的大小,大概需要2G的显存 EPOCHS = 10 # 总共训练批次 # 设备配置 DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(DEVICE.type)#输出设备类型 # MNIST Dataset # transform=transforms.ToTensor():将图像转化为Tensor,在加载数据的时候,就可以对图像做预处理 train_dataset = datasets.MNIST(root='./data/', train=True, download=True, transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1037,), (0.3081,)) ])) test_dataset = datasets.MNIST(root='./data/', train=False,download=False, transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1037,), (0.3081,)) ])) # Data Loader (Input Pipeline) # 训练数据集的加载器,自动将数据分割成batch,顺序随机打乱 train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=BATCH_SIZE, #显存有限制 shuffle=True) # 训测试数据集的加载器,自动将数据分割成batch,顺序随机打乱 test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=BATCH_SIZE, shuffle=True) # 定义模型 class ConvNet(nn.Module): def __init__(self): super().__init__() #输入图像1*1*28*28 self.conv1 = nn.Conv2d(1, 10, 5) # 第一层卷积1 input channel, 10 output channels, 5x5 square convolution kernel self.conv2 = nn.Conv2d(10, 20, 3) # 第二层卷积:10input channel, 20 output channels, 3x3 square convolution kernel self.fc1 = nn.Linear(20 * 10 * 10, 500) # 线性连接层的输入尺寸为最后一层立方体的平铺,输出层500个节点 self.fc2 = nn.Linear(500, 10) # 最后一层线性分类单元,输入为500,输出为要做分类的类别数 def forward(self, x): #forward() 函数ConvNet的前向传播 # x尺寸:(batch_size, image_channels, image_width, image_height) #卷积层->池化层->激活函数,两个卷积层 in_size = x.size(0) out= self.conv1(x) # 1* 10 * 24 *24 第一层卷积 out = F.relu(out) # 激活函数用ReLU,防止过拟合 #最大池化层:将图像分成小块,在每一块中寻找最大值,返回给输出, out = F.max_pool2d(out, 2, 2) # 1* 10 * 12 * 12 第二层池化,将图片变小 out = self.conv2(out) # 1* 20 * 10 * 10 # 第三层又是卷积 out = F.relu(out) # 非线性函数 # 将立体的特征图 tensor 压成一个一维的向量 # view 函数可以将一个tensor 按指定的方式重新排布 out = out.view(in_size, -1) # 1 * 2000 # 一个线性连接层,输入尺寸为最后一层立方体的线性平铺,输出层 500个节点 out = self.fc1(out) # 1 * 500 out = F.relu(out) # 第五层为全连接,ReLU激活函数 # 最后一层线性分类单元,输入为 500,输出为要做分类的类别数10个 out = self.fc2(out) # 1 * 10 # out的尺寸:(batch_size, num_classes) # # 输出层为 log_Softmax,即概率对数值 log(p(×))。采用log_softmax可以使后面的交叉熵计算更快 # dim=0 ,即softmax后横向的和为1 out = F.log_softmax(out, dim = 1) return out # 定义训练函数 def train(model, device, train_loader, optimizer, epoch): model.train() # 新建一个卷积神经网络的实例 for batch_idx, (data, target) in enumerate(train_loader): # 针对容器中的每一个批进行循环 # 将数据送入GPU中计算 data 为一批图像,target 为一批标签 data, target = data.to(device), target.to(device) # optimizer.zero_grad() # 清空梯度 output = model(data) ## 神经网络完成一次前馈的计算过程,得到预测输出output loss = F.nll_loss(output, target) # 将output与标签target比较,计算误差 optimizer.zero_grad() # 清空梯度 loss.backward() # 反向传播 optimizer.step() ## 一步随机梯度下降算法 if (batch_idx + 1) % 30 == 0: # 每间隔batch_idx + 1个batch 执行一次打印操作 print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) # 定义测试函数 def test(model, device, test_loader): model.eval() # 给网络楧型做标记,标志着模型在训练集上训练 test_loss =0 #损失率 correct = 0 #准确率 with torch.no_grad(): #不需要计算梯度 for data, target in test_loader: #遍历数据集中的每一个batch data, target = data.to(device), target.to(device) #保存测试的输入和输出,将数据送入GPU output = model(data) #模型输出 test_loss += F.nll_loss(output, target, reduction = 'sum') # 将一批的损失相加 pred = output.max(1, keepdim = True)[1] # 找到概率最大的下标 correct += pred.eq(target.view_as(pred)).sum().item() #准确率 test_loss /= len(test_loader.dataset) #错误率 print("\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%) \n".format( test_loss, correct, len(test_loader.dataset), 100.* correct / len(test_loader.dataset))) #输出准确率,损失率 start = time.perf_counter() #perf_counter()返回当前的计算机系统时间 #生成模型和优化器 model = ConvNet().to(DEVICE) optimizer = optim.Adam(model.parameters()) # 最后开始训练和测试 for epoch in range(1, EPOCHS + 1): # 总共训练批次为EPOCHS+1 train(model, DEVICE, train_loader, optimizer, epoch) #训练数据 test(model, DEVICE, test_loader) #测试数据 end = time.perf_counter() #系统终止时间 print(DEVICE.type,'时间',end) #输出当前时间
四、实验总结
整个卷积神经网络的运作分成了两个阶段:前馈运算阶段和反馈学习阶段。在网络的前馈阶段(从输人图像到输出数字),所有连接的权重值都不改变,系统会根据输人图像计算输出分类,并根据网络的分类与数据中的标签(标准答案)进行比较计算出交叉熵作为损失函数。接下来,在反馈阶段,根据前馈阶段的损失两数调整所有连接上的权重值,从而完成神经网络的学习过程。