PyTorch是一个非常强大的构建深度学习的框架。与其他深度学习框架相比,这个框架的学习并不复杂,因为它的模型构建方式很直接。在本文中,我们将讨论如何构建一个端到端的深度学习模型,这对机器学习新手来说是有帮助的。通过本教程,我们将通过详细解释每一个步骤,以非常简单的方式展示如何定义和使用卷积神经网络(CNN)。本文要涉及的主要内容如下:
内容表
- 卷积神经网络(CNN)
- 使用Pytorch实现CNN
- 准备数据集
- 建立模型
- 构建模型时应遵循的准则
- 编译模型
- 训练、测试和评估程序
让我们首先讨论CNN,并尝试了解它应该如何实现。
卷积神经网络(CNN)
CNN是深度神经网络模型,最初被设计用来分析二维图像输入,但现在也可以分析一维和三维数据。卷积神经网络的核心可以由两个或更多的卷积层组成,每个卷积层都执行 "卷积",即用一系列的n x n对角线矩阵乘以神经网络的输入。
与传统的神经网络不同,这种乘法是通过使用一个穿越图像的 "窗口 "来完成的,这个窗口被称为过滤器或内核。每次过滤器经过图像时,权重都会与一系列的输入值相乘。每个过滤器位置的卷积操作的输出值(每个过滤器位置一个值)形成一个二维的输出值矩阵,代表从基础图像中提取的特征。"特征图 "是对这个输出矩阵的称呼。
一旦特征图完成,功能图中的任何数值都可以非线性地传送到下一个卷积层(例如,通过ReLU激活)。完全连接层接收卷积层序列的输出,并产生最终预测,这通常是一个描述图像的标签。
总而言之,完整的CNN模型包括两层,主要是卷积层,第二层是Pooling层。卷积层通过使用上面解释的数学运算来创建一个特征图,Pooling层用于进一步减少特征图的大小。 最常见的Pooling层是maxed和average pooling,它分别从过滤器的大小中取最大值和平均值(即2×2,3×3,等等)。
现在接下来我们看看如何用PyTorch实现这样一个CNN模型。
使用Pytorch实现CNN
PyTorch是最知名和最广泛使用的深度学习库之一,特别是在学术研究中。它是一个开源的机器学习框架,缩短了从研究原型到生产部署的时间。这个实现将分为主要步骤,如数据加载、模型构建、模型训练和模型测试。
在这一节中,我们将引导你一步一步地完成这个过程
现在将快速导入所有需要的依赖性:
import torch
import torch.nn as nn
import torchvision
import torch.nn.functional as F
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
准备数据集
正如所讨论的那样,首先我们要加载数据集。我们在这里使用的数据集是EMNIST,它是MNIST数据集的扩展版本,包含手写数字以及小写和大写的手写字母。这个数据集的训练子集包含近1,20000张28 x 28像素的图像,这些图像是灰度的。我们可以通过在API中指定来加载这个数据集作为训练和测试。
下面我们首先定义了图像的预处理功能,以及下载时应用于训练和测试数据集的功能。后来我们使用了数据加载器,特别是在我们有数千张图片的情况下,加载所有这些图片将使我们的系统处于巨大的负担之下。数据加载器使这个数据集可迭代,可有效调用:
# apply transformation
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))
])
# download the data
training_data = torchvision.datasets.EMNIST(root='contents/',download=True, transform=transform, train=True,split='balanced')
test_data = torchvision.datasets.EMNIST(root='contents/',download=True, transform=transform, train=False,split='balanced')
# build the data loader
train_loader = torch.utils.data.DataLoader(dataset = training_data,
batch_size = 128,
shuffle = True)
test_loader = torch.utils.data.DataLoader(dataset = test_data,
batch_size = 128,
shuffle = True)
下面是我们数据集的一些样本:
构建模型
下一步是定义模型。在这里,我们将像在Python中定义一些复杂的类一样来定义我们的模型。我们可以先创建一个新的类,利用PyTorch的 nn.Module类。这在构建神经网络时是必不可少的,因为它为我们提供了大量有用的方法。
然后必须定义我们的神经网络的层。这是在类的 __init__方法中完成。我们可以简单地命名我们的层,并将它们分配给适当的层,例如在这个案例中,卷积层、池化层、全连接层,等等。
最后,在我们的类中,我们必须定义一个 前进方法。这个方法的目的是指定输入数据被各层处理的顺序。综合上述所有内容,我们可以将其编码为:
class CNNModel(nn.Module):
def __init__(self):
super(CNNModel, self).__init__()
self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size= 5,stride = 1)
self.conv2 = nn.Conv2d(32, 64, 5, 1)
self.fc = nn.Linear(64*20*20, 47)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 1)
x = torch.flatten(x, 1)
x = self.fc(x)
output = F.log_softmax(x, dim=1)
return output
# initiating the model
model = CNNModel()
构建模型时应遵循的准则
当你看到层的分布和层下单元的尺寸时,你可能会认为我们可以任意地传递好,但事实并非如此。即使我也试图以这种任意的方式在最后得到一个错误,即尺寸不匹配的错误。
这个错误主要是在我们的卷积层和线性层的连接之间产生的。在PyTorch中,每个层都需要一系列强制性的参数,在我们的例子中,卷积层的参数是:input_channels、output_channels、kernels, 以及可选的 stride。
我们不能任意设置输入通道,但根据我们的图像类型,无论是灰度/还是黑白图像,它都应该是1,而对于RGB图像,它应该是3。我们可以将输出大小设置为任何数字,并且我们应该使其与下一个连续层的输入相等。内核只不过是用来为给定图像创建特征图的过滤器,而步长基本上告诉了内核在图像上的移动,这里的1表示它每次走一步。
因此,在上述线性层中,有两个基本维度,即输入和输出形状。当我们把卷积层连接到线性层时,它基本上接受了我们图像的大小。而最后一个卷积层,我们图像的尺寸已经改变为现在不再是28 x 28像素。
为了得到线性层的输入通道的适当值,可以进行如下计算:
- 对于第一层,我们提供了一个28 x 28的图像,在应用5 x 5的卷积后,每边的像素大小减少了4个,在第一层的输出为24 x 24。
- 此外,在下一层,我们有同样的5×5的过滤器,这将使它进一步成为20×20,这是在第二层输出的单一图像的批量大小。
现在,正如我们所讨论的,线性层的输入通道应该是64*20*20。如果你想增加层数,请按照内核操作,得到适当的全连接层的批量大小。
现在让我们来看看模型的定义,在前向方法中,我们已经将激活函数应用于每一层,之后我们应用了池化操作,其中我们使用了最大池化,之后我们对层进行了扁平化。最后,在前向方法中,我们应用了softmax函数,这将成为一个分类器。
编译模型
接下来,我们将编译我们的模型,这里我们主要定义损失函数和优化函数。这里交叉熵被用作损失函数,随机梯度下降优化器被用来减少训练过程中的损失。
# loss function
criterion = nn.CrossEntropyLoss()
# Optimizer
optimizer = torch.optim.SGD(model.parameters(), lr=0.001)
训练、测试和评估程序
接下来,我们已经建立了所有的程序和计算方法。现在是训练上述模型的时候了。在PyTorch中的训练在这里是有点棘手的。我们必须手动访问每个元素,并且必须安排在一个循环中,以使其连续训练。程序如下。
我们首先在训练数据中迭代历时数,然后是批次。我们根据我们使用的设备转换图像和标签,无论是GPU还是CPU。在前向传递中,我们使用我们的模型进行预测,然后根据这些预测和我们的实际标签来计算损失。
在这之后,我们执行一个后向传递,在这个传递中,我们更新我们的权重,以改善我们的模型。在每次更新之前,梯度被设置为零,使用 optimizer.zero grad()函数将梯度设置为零。然后使用函数计算新的梯度。 loss.backward()函数计算新梯度。
最后,我们使用 optimizer.step()函数来更新权重:
# fetch model to the working device
model.to(device)
# training loss
train_loss = []
test_losses =[]
def train(e):
#Load in the data
for i, (images, labels) in enumerate(train_loader):
# load data on to device
images = images.to(device)
labels = labels.to(device)
# Forward pass
outputs = model(images)
loss = criterion(outputs, labels)
# Backward and optimize
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_loss.append(loss.item())
print('Epoch [{}/{}], Train Loss: {:.4f}'.format(e+1, 10, loss.item()))
同样地,测试技术与训练程序没有什么不同,除了计算梯度之外,因为我们没有更新任何权重。我们在代码中加入了 torch.no grad()因为不需要计算梯度。接下来我们用我们的模型来预测每一批,并计算其中有多少是正确的:
def test():
test_loss = 0
with torch.no_grad():
correct = 0
total = 0
for images, labels in test_loader:
images = images.to(device)
labels = labels.to(device)
outputs = model(images)
test_loss += F.nll_loss(outputs, labels, size_average=False).item()
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
test_loss /= len(test_loader.dataset)
test_losses.append(test_loss)
print('Test Accuracy: {:4f} %, Test loss: {:4f}'.format((100 * correct / total),test_loss))
我们可以通过使用上面定义的两个函数同时开始训练和测试:
for i in range(10):
train(i)
test()
这样做的结果是,测试准确率约为79%。更多细节请参考笔记本。
最后的话
通过这篇文章,我们已经讨论了卷积神经网络,并使用PyTorch实际实现了它。如果我们将其与其他框架相比较,PyTorch的构建更以开发者为中心。要使用PyTorch,人们应该对应用程序有一个清晰的了解。在这种情况下,我们开发了CNN,所以我们应该能够通过各层追踪卷积操作。最后,我想说作为一个初学者,我们应该使用PyTorch。它将有助于在建立模型本身的同时磨砺你的概念。