最完整的 PyTorch 数据科学家指南(2)
本篇文章继续为同学们讲解神经网络的卷积层部分。
因此,Conv2d图层需要使用Cin通道将高度为H且宽度为W的图像作为输入 。现在,对于卷积网络中的第一层,的数量in_channels将为3(RGB),并且out_channels用户可以定义数量。kernel_size大多采用3×3是,并且stride通常使用为1。
为了检查一个我不太了解的新层,我通常尝试查看该层的输入和输出,如下所示,在该层我首先初始化该层:
conv_layer = nn.Conv2d(in_channels = 3, out_channels = 64, kernel_size = (3,3), stride = 1, padding=1)
然后通过它传递一些随机输入。此处的批量大小为100。
因此,我们根据需要从卷积运算中获得了输出,并且我掌握了有关如何在我设计的任何神经网络中使用此层的足够信息。
数据集和数据加载器
在训练或测试时,我们如何将数据传递到神经网络?我们绝对可以像上面一样传递张量,但是Pytorch还为我们提供了预先构建的数据集,以使我们更轻松地将数据传递到神经网络。您可以检出torchvision.datasets 和 torchtext.datasets提供的数据集的完整列表 。但是,为了给出数据集的具体示例,假设我们必须使用具有以下结构的文件夹的图像将图像传递到Image Neural网络:
我们可以使用 torchvision.datasets.ImageFolder数据集来获取如下示例图像:
该数据集包含847张图像,我们可以使用索引获取图像及其标签。现在我们可以使用for循环将图像一张一张地传递到任何图像神经网络:
但这不是最佳选择。我们要进行批处理。 实际上,我们可以编写更多代码来批量添加图像和标签,然后将其传递给神经网络。但是Pytorch为我们提供了一个实用程序迭代器 torch.utils.data.DataLoader来精确地做到这一点。现在我们可以简单地将其包装 train_dataset在Dataloader中,并且将获得批处理而不是单个示例。
我们可以使用以下命令简单地迭代批处理:
因此,实际上,使用数据集和数据加载器的整个过程变为:
你可以看一下这个特殊的例子在行动在我以前的博文上使用Deep学习图像分类 在这里。
这很棒,而且Pytorch确实提供了许多现成的功能。但是Pytorch的主要功能来自其巨大的自定义功能。如果PyTorch提供的数据集不适合我们的用例,我们也可以创建自己的自定义数据集。
了解自定义数据集
要编写我们的自定义数据集,我们可以利用torch.utils.data.Dataset Pytorch提供的抽象类 。我们需要继承Dataset类,并需要定义两个方法来创建自定义数据集。
例如,我们可以创建一个简单的自定义数据集,该数据集从文件夹返回图像和标签。看到大多数任务都发生在 __init__一部分,我们 glob.glob用来获取图像名称并进行一些常规预处理。
另外,请注意,我们在__getitem__ 方法中一次而不是在初始化时一次打开图像。之所以没有这样做,__init__是因为我们不想将所有图像加载到内存中,而只需要加载所需的图像。
现在,我们可以Dataloader像以前一样将此数据集与实用程序一起使用 。它的工作方式与PyTorch提供的先前数据集相似,但没有一些实用程序功能。
了解自定义DataLoader
这个特定的部分有些高级,可以跳过这篇文章,因为在很多情况下都不需要它。但是我为了完整起见在这里添加它。
因此,假设您要向处理文本输入的网络提供批次,并且网络可以采用任意序列大小的序列,只要批次中的大小保持不变即可。例如,我们可以拥有一个BiLSTM网络,该网络可以处理任何长度的序列。如果您现在不了解其中使用的层,那就没关系了。只是知道它可以处理可变大小的序列。
该网络期望其输入具有(batch_size,seq_length)的形状, 并且可以与任何seq_length。我们可以通过将两个具有不同序列长度(10和25)的随机批次传递给模型来进行检查。
现在,我们要为该模型提供紧密的批次,以便每个批次都基于批次中的最大序列长度具有相同的序列长度,以最大程度地减少填充。这具有使神经网络运行更快的附加好处。实际上,这是在Kaggle赢得Quora Insincere挑战赛的获奖方法中使用的方法之一,在其中,运行时间至关重要。
那么,我们该怎么做呢?首先让我们编写一个非常简单的自定义数据集类。
另外,让我们生成一些随机数据,将其与此自定义数据集一起使用。
现在,我们可以使用以下自定义数据集:
如果现在尝试对batch_size大于1的该数据集使用数据加载器 ,则会收到错误消息。这是为什么?
发生这种情况是因为序列的长度不同,并且我们的数据加载器期望序列的长度相同。请记住,在前面的图像示例中,我们使用了变换将所有图像的大小调整为224,因此我们没有遇到这个错误。
那么,如何遍历此数据集,以使每个批次具有相同长度的序列,但不同批次可能具有不同的序列长度?
我们可以collate_fn在DataLoader中使用 参数,该参数使我们可以定义如何在特定批次中堆叠序列。要使用此功能,我们需要定义一个函数,该函数将一个批处理作为输入并返回 基于 该批处理的填充序列长度的(x_batch, y_batch)max_sequence_length。我在以下函数中使用的函数是简单的NumPy操作。另外,该函数已正确注释,因此您可以了解发生了什么。
这次将可以正常运行,因为我们提供了一个自定义, collate_fn。并且看到批次现在具有不同的序列长度。因此,我们将能够根据需要使用可变的输入大小来训练BiLSTM。
训练神经网络
我们知道如何使用创建神经网络, nn.Module。但是如何训练它呢?任何需要训练的神经网络都会有一个训练循环,看起来类似于以下内容:
在上面的代码中,我们正在运行五个Epoch,每个Epoch:
我们使用数据加载器遍历数据集。
在每次迭代中,我们使用 model(x_batch)
我们使用 loss_criterion
我们使用loss.backward()通话反向传播该损失 。我们完全不必担心梯度的计算,因为这个简单的调用可以为我们完成所有工作。
采取优化程序步骤,使用更改整个网络中的权重 optimizer.step()。这是使用loss.backward()呼叫中计算出的梯度来修改网络权重的地方 。
我们通过验证数据加载器检查验证得分/指标。在进行验证之前,我们使用来将模型设置为评估模式。 model.eval().请注意,我们不会在评估模式下反向传播损失。
到目前为止,我们已经讨论了如何用于 nn.Module创建网络以及如何在Pytorch中使用自定义数据集和数据加载器。因此,让我们谈谈损失函数和优化器的各种可用选项。
损失函数
Pytorch为我们提供了 最常见任务(例如分类和回归)的各种 损失函数。一些最常用的例子是nn.CrossEntropyLoss,nn.NLLLoss,nn.KLDivLoss并且nn.MSELoss.可以读取每个损失函数的文档,但讲解如何使用这些损失的功能,我将通过的例子nn.NLLLoss
NLLLoss的文档非常简洁。如图所示,此损失函数用于多类分类,并且基于文档:
输入的期望值必须为(batch_sizex Num_Classes)—这是我们创建的神经网络的预测。
我们需要在输入中包含每个类的对数概率—要从神经网络获取对数概率,我们可以添加一个 LogSoftmaxLayer作为网络的最后一层。
目标必须是类的张量,其类编号在(0,C-1)范围内,其中C是类的数量。
因此,我们可以尝试将此Loss函数用于简单的分类网络。请注意 LogSoftmax最后线性层之后的层。如果您不想使用此 LogSoftmax层,则可以使nn.CrossEntropyLoss。
并将其传递给模型以获得预测:
现在,我们可以得出以下损失:
自定义损失功能
定义自定义损失函数仍然是小菜一碟,只要您在损失函数中使用张量运算就可以了。例如,这是 customMseLoss
您可以像以前一样使用此自定义损失。但是请注意,由于这次我们将其定义为函数,因此我们并未使用准则实例化损失。
如果需要,我们也可以使用编写它作为一个类 nn.Module ,然后我们就可以将其用作对象。这是一个NLLLoss自定义示例:
优化器
使用loss.backward()调用获得梯度后 ,我们需要采取优化程序步骤来更改整个网络的权重。Pytorch使用该torch.optim模块提供了各种不同的即用型优化器。例如 torch.optim.Adadelta, torch.optim.Adagrad,torch.optim.RMSprop和最广泛使用 torch.optim.Adam。要使用从PyTorch最常用的Adam优化,我们可以简单地用实例吧:
然后 在训练模型时使用optimizer.zero_grad()和optimizer.step()。
我不是在讨论如何编写自定义优化器,因为这是一个很少见的用例,但是如果您想拥有更多的优化器,请查看 pytorch-optimizer 库,该库提供了研究论文中使用的许多其他优化器。另外,如果您想创建自己的优化器,则可以使用PyTorch 或 pytorch-optimizers中已实现的优化器的源代码来激发灵感 。
使用GPU /多个GPU
到现在为止,我们所做的一切都在CPU上。如果要使用GPU,可以使用将模型放入GPU model.to('cuda')。或者,如果您想使用多个GPU,则可以使用nn.DataParallel。这是一个实用程序功能,用于检查计算机中GPU的数量,并DataParallel根据需要自动设置并行训练 。
我们唯一需要更改的是,如果有GPU,我们将在训练时将数据加载到GPU。这就像在我们的训练循环中添加几行代码一样简单。
结论
Pytorch用最少的代码提供了很多可定制性。刚开始时,可能很难理解整个生态系统是如何用类构造的,最后,它是简单的Python。在本文中,我尝试分解了使用Pytorch时可能需要的大部分部件,希望阅读后对您来说更有意义。
版权声明: 本文为 InfoQ 作者【计算机与AI】的原创文章。
原文链接:【http://xie.infoq.cn/article/da271f6f020e516b7198a0118】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论