PyTorch 教程-卷积神经网络模型的验证
在训练部分,我们在MNIST数据集上训练了我们的CNN模型(无限数据集),并且似乎达到了合理的损失和准确性。如果模型能够将其所学到的内容推广到新数据,那么这将是其性能的真正证明。这将与我们在先前主题中所做的方式相同。
步骤1:
我们将使用我们在训练部分创建的训练数据集来创建验证集。这一次,我们将设置train=False
:
validation_dataset=datasets.MNIST(root='./data',train=False,download=True,transform=transform1)
步骤2:
现在,类似于我们在训练部分中声明训练加载器的原因,我们将定义一个验证加载器。验证加载器的创建方式与我们创建训练加载器的方式相同,但这次我们传递验证数据集而不是训练数据集,并且我们将shuffle
设置为False
,因为我们不会对验证数据进行训练。不需要对其进行洗牌,因为它仅用于测试目的。
validation_loader=torch.utils.data.DataLoader(dataset=validation_dataset,batch_size=100,shuffle=False)
步骤3:
我们的下一步是在每个epoch处分析验证损失和准确性。为此目的,我们必须创建两个用于验证运行损失和验证运行准确性的列表。
val_loss_history=[]
val_correct_history=[]
步骤4:
在接下来的步骤中,我们将验证模型。该模型将在相同的epoch中验证。在我们完成通过整个训练集进行迭代以训练数据之后,我们现在将通过验证集进行迭代以测试数据。
我们首先测量两件事情。第一件事是我们模型的性能,即在验证集上有多少正确分类。我们将设置验证运行损失和验证运行准确性:
val_loss=0.0
val_correct=0.0
步骤5:
现在,我们可以循环遍历我们的测试数据。因此,在else语句之后,我们将为标签和输入定义一个循环语句:
for val_input,val_labels in validation_loader:
步骤6:
我们正在处理卷积神经网络,将输入首先传递给它们。我们将专注于这些图像的四个维度。因此,无需将它们展平。
由于我们已经将模型分配给我们的设备,因此我们也将输入和标签分配给我们的设备。
input=input.to(device)
labels=input.to(device)
现在,通过这些输入,我们得到输出:
val_outputs=model(val_inputs)
步骤7:
借助输出,我们将计算总体分类交叉熵损失,最终将输出与实际标签进行比较。
val_loss1=criteron(val_outputs,val_labels)
由于我们不再训练神经网络,因此无需调用zero_grad()
、backward()
或任何其他操作。在for
循环之前的范围内调用no_grad()
方法,以节省内存:
with torch.no_grad():
它将临时将所有requires_grad
标志设置为False
。
步骤8:
现在,我们将以与训练损失和训练准确性相同的方式计算验证损失和准确性:
_,val_preds=torch.max(val_outputs,1)
val_loss+=val_loss1.item()
val_correct+=torch.sum(val_preds==val_labels.data)
步骤9:
现在,我们将计算验证epoch损失,其方式与我们计算训练epoch损失的方式相同,其中我们通过数据集的长度除以总体运行损失。因此,它将被写成:
val_epoch_loss=val_loss/len(validation_loader)
val_epoch_acc=val_correct.float()/len(validation_loader)
val_loss_history.append(val_epoch_loss)
val_correct_history.append(val_epoch_acc)
步骤10:
我们将打印验证损失和验证准确性:
print('validation_loss:{:.4f},{:.4f}'.format(val_epoch_loss,val_epoch_acc.item()))
步骤11:
现在,我们将为可视化目的绘制它。我们将它绘制为:
plt.plot(loss_history,label='Training Loss')
plt.plot(val_loss_history,label='Validation Loss')
plt.legend()
plt.show()
plt.plot(correct_history,label='Training accuracy')
plt.plot(val_correct_history,label='Validation accuracy')
plt.legend()
plt.show()
从上面的图表中可以清楚地看出,在CNN中发生了过拟合。为了减少这种过拟合,我们将介绍另一种名为Dropout Layer的快速技术。
步骤12:
在下一步中,我们将转到我们的LeNet类,并添加一种称为Dropout层的特定层类型,该层类型将减少我们数据的过拟合。该层类型称为Dropout层。此层本质上通过在训练过程中随机将输入单元的分数比例设置为0来工作,每次更新。
上图显示了标准神经网络,以及在应用了dropout后相同神经网络的情况。我们可以看到,有些节点已被关闭,并且不再沿着网络与信息通信。
我们将使用不止一个dropout层,这将在给定网络中用于获得所需的性能。我们将这些dropout层放在卷积层之间和全连接层之间。在完全连接的层之间,我们将dropout层放置在这些高参数层之间,因为这些高参数层更有可能过度拟合和记忆训练数据。因此,我们将在完全连接的层之间设置我们的dropout层。
我们将使用nn.Dropout模块初始化我们的dropout层,并将dropout率作为参数传递给我们的初始化器。给定节点被丢弃的概率将设置为0.5,如下所示:
self.dropout1=nn.dropout(0.5)
步骤13:
在下一步中,我们将在forward函数中在完全连接的层之间定义第二个dropout层:
x=self.dropout1(x)
现在,我们将运行我们的程序,它将为我们提供更准确的结果:
完整代码
import torch
import matplotlib.pyplot as plt
import numpy as np
import torch.nn.functional as func
import PIL.ImageOps
from torch import nn
from torchvision import datasets,transforms
device=torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
transform1=transforms.Compose([transforms.Resize((28,28)),transforms.ToTensor(),transforms.Normalize((0.5,),(0.5,))])
training_dataset=datasets.MNIST(root='./data',train=True,download=True,transform=transform1)
validation_dataset=datasets.MNIST(root='./data',train=False,download=True,transform=transform1)
training_loader=torch.utils.data.DataLoader(dataset=training_dataset,batch_size=100,shuffle=True)
validation_loader=torch.utils.data.DataLoader(dataset=validation_dataset,batch_size=100,shuffle=False)
class LeNet(nn.Module):
def __init__(self):
super().__init__()
self.conv1=nn.Conv2d(1,20,5,1)
self.conv2=nn.Conv2d(20,50,5,1)
self.fully1=nn.Linear(4*4*50,500)
self.dropout1=nn.Dropout(0.5)
self.fully2=nn.Linear(500,10)
def forward(self,x):
x=func.relu(self.conv1(x))
x=func.max_pool2d(x,2,2)
x=func.relu(self.conv2(x))
x=func.max_pool2d(x,2,2)
x=x.view(-1,4*4*50) #Reshaping the output into desired shape
x=func.relu(self.fully1(x)) #Applying relu activation function to our first fully connected layer
x=self.dropout1(x)
x=self.fully2(x) #We will not apply activation function here because we are dealing with multiclass dataset
return x
model=LeNet().to(device)
criteron=nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(model.parameters(),lr=0.00001)
epochs=12
loss_history=[]
correct_history=[]
val_loss_history=[]
val_correct_history=[]
for e in range(epochs):
loss=0.0
correct=0.0
val_loss=0.0
val_correct=0.0
for input,labels in training_loader:
input=input.to(device)
labels=labels.to(device)
outputs=model(input)
loss1=criteron(outputs,labels)
optimizer.zero_grad()
loss1.backward()
optimizer.step()
_,preds=torch.max(outputs,1)
loss+=loss1.item()
correct+=torch.sum(preds==labels.data)
else:
with torch.no_grad():
for val_input,val_labels in validation_loader:
val_input=val_input.to(device)
val_labels=val_labels.to(device)
val_outputs=model(val_input)
val_loss1=criteron(val_outputs,val_labels)
_,val_preds=torch.max(val_outputs,1)
val_loss+=val_loss1.item()
val_correct+=torch.sum(val_preds==val_labels.data)
epoch_loss=loss/len(training_loader)
epoch_acc=correct.float()/len(training_loader)
loss_history.append(epoch_loss)
correct_history.append(epoch_acc)
val_epoch_loss=val_loss/len(validation_loader)
val_epoch_acc=val_correct.float()/len(validation_loader)
val_loss_history.append(val_epoch_loss)
val_correct_history.append(val_epoch_acc)
print('training_loss:{:.4f},{:.4f}'.format(epoch_loss,epoch_acc.item()))
print('validation_loss:{:.4f},{:.4f}'.format(val_epoch_loss,val_epoch_acc.item()))
plt.plot(loss_history,label='Training Loss')
plt.plot(val_loss_history,label='Validation Loss')
plt.legend()
plt.show()
plt.plot(correct_history,label='Training accuracy')
plt.plot(val_correct_history,label='Validation accuracy')
plt.legend()
plt.show()