Skip to content

Commit

Permalink
2021-01-04 16:47:14
Browse files Browse the repository at this point in the history
  • Loading branch information
wizardforcel committed Jan 4, 2021
1 parent 3530215 commit fa527a7
Showing 1 changed file with 22 additions and 22 deletions.
44 changes: 22 additions & 22 deletions docs/4.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ mlp 的力量来自于添加第二个线性层和允许模型学习一个线性

在上一节中,我们概述了 MLP 的核心思想。在本节中,我们将介绍 PyTorch 中的一个实现。如前所述,MLP 除了第 3 章中简单的感知器之外,还有一个额外的计算层。在我们在例 4-1 中给出的实现中,我们用 PyTorch 的两个线性模块实例化了这个想法。线性对象被命名为`fc1`和`fc2`,它们遵循一个通用约定,即将线性模块称为“完全连接层”,简称为“fc 层”。除了这两个线性层外,还有一个修正的线性单元(ReLU)非线性(在第 3 章“激活函数”一节中介绍),它在被输入到第二个线性层之前应用于第一个线性层的输出。由于层的顺序性,您必须确保层中的输出数量等于下一层的输入数量。使用两个线性层之间的非线性是必要的,因为没有它,两个线性层在数学上等价于一个线性层 4,因此不能建模复杂的模式。MLP 的实现只实现反向传播的前向传递。这是因为 PyTorch 根据模型的定义和向前传递的实现,自动计算出如何进行向后传递和梯度更新。

Example 4-1\. Multilayer Perceptron
示例 4-1:多层感知机

```py
import torch.nn as nn
Expand Down Expand Up @@ -90,7 +90,7 @@ class MultilayerPerceptron(nn.Module):

在例 4-2 中,我们实例化了 MLP。由于 MLP 实现的通用性,我们可以为任何大小的输入建模。为了演示,我们使用大小为 3 的输入维度、大小为 4 的输出维度和大小为 100 的隐藏维度。请注意,在`print`语句的输出中,每个层中的单元数很好地排列在一起,以便为维度 3 的输入生成维度 4 的输出。

Example 4-2\. An example instantiation of an MLP
示例 4-2:NLP 的示例实例化

```py
Input[0]
Expand All @@ -113,7 +113,7 @@ MultilayerPerceptron(

我们可以通过传递一些随机输入来快速测试模型的“连接”,如示例 4-3 所示。因为模型还没有经过训练,所以输出是随机的。在花费时间训练模型之前,这样做是一个有用的完整性检查。请注意 PyTorch 的交互性是如何让我们在开发过程中实时完成所有这些工作的,这与使用 NumPy 或 panda 没有太大区别:

Example 4-3\. Testing the MLP with random inputs
示例 4-3:使用随机输入测试 MLP

```py
Input[0]
Expand Down Expand Up @@ -146,7 +146,7 @@ tensor([[-0.2456, 0.0723, 0.1589, -0.3294],

但是,如果您想将预测向量转换为概率,则需要额外的步骤。具体来说,您需要 softmax 函数,它用于将一个值向量转换为概率。softmax 有许多根。在物理学中,它被称为玻尔兹曼或吉布斯分布;在统计学中,它是多项式逻辑回归;在自然语言处理(NLP)社区,它是最大熵(MaxEnt)分类器。不管叫什么名字,这个函数背后的直觉是,大的正值会导致更高的概率,小的负值会导致更小的概率。在示例 4-3 中,`apply_softmax`参数应用了这个额外的步骤。在例 4-4 中,您可以看到相同的输出,但是这次将`apply_softmax`标志设置为`True`:

Example 4-4\. MLP with apply_softmax=True
示例 4-4:带有`apply_softmax=True`的 MLP

```py
Input[0]
Expand Down Expand Up @@ -181,7 +181,7 @@ tensor([[ 0.2087, 0.2868, 0.3127, 0.1919],

`SurnameDataset`的实现与“示例:餐馆评论的情感分类”中的`ReviewDataset`几乎相同,只是在`__getitem__`方法的实现方式上略有不同。回想一下,本书中呈现的数据集类继承自 PyTorch 的数据集类,因此,我们需要实现两个函数:`__getitem__`方法,它在给定索引时返回一个数据点;以及`__len__`方法,该方法返回数据集的长度。“示例:餐厅评论的情绪分类”中的示例与本示例的区别在`__getitem__`中,如示例 4-5 所示。它不像“示例:将餐馆评论的情绪分类”那样返回一个向量化的评论,而是返回一个向量化的姓氏和与其国籍相对应的索引:

Example 4-5\. Implementing SurnameDataset.__getitem__()
示例 4-5:实现`SurnameDataset.__getitem__()`

```py
class SurnameDataset(Dataset):
Expand Down Expand Up @@ -213,7 +213,7 @@ class SurnameDataset(Dataset):

您应该注意,虽然我们在这个示例中使用了收缩的单热,但是在后面的章节中,您将了解其他向量化方法,它们是单热编码的替代方法,有时甚至更好。具体来说,在“示例:使用 CNN 对姓氏进行分类”中,您将看到一个热门矩阵,其中每个字符都是矩阵中的一个位置,并具有自己的热门向量。然后,在第 5 章中,您将学习嵌入层,返回整数向量的向量化,以及如何使用它们创建密集向量矩阵。但是现在,让我们看一下示例 4-6 中`SurnameVectorizer`的代码。

Example 4-6\. Implementing SurnameVectorizer
示例 4-6:实现`SurnameVectorizer`

```py
class SurnameVectorizer(object):
Expand Down Expand Up @@ -263,7 +263,7 @@ class SurnameVectorizer(object):

在最后一步中,可选地应用 softmax 操作,以确保输出和为 1;这就是所谓的“概率”。它是可选的原因与我们使用的损失函数的数学公式有关——交叉熵损失。我们研究了“损失函数”中的交叉熵损失。回想一下,交叉熵损失对于多类分类是最理想的,但是在训练过程中软最大值的计算不仅浪费而且在很多情况下并不稳定。

Example 4-7\. The SurnameClassifier as an MLP
示例 4-7:作为 MLP 的`SurnameClassifier`

```py
import torch.nn as nn
Expand Down Expand Up @@ -307,7 +307,7 @@ class SurnameClassifier(nn.Module):

虽然我们使用了不同的模型、数据集和损失函数,但是训练例程是相同的。因此,在例 4-8 中,我们只展示了`args`以及本例中的训练例程与“示例:餐厅评论情绪分类”中的示例之间的主要区别。

Example 4-8\. The args for classifying surnames with an MLP
示例 4-8:用于使用 MLP 分类姓氏的参数

```py
args = Namespace(
Expand All @@ -333,7 +333,7 @@ args = Namespace(

在例 4-9 中,我们展示了数据集、模型、损失函数和优化器的实例化。这些实例应该看起来与“示例:将餐馆评论的情绪分类”中的实例几乎相同。事实上,在本书后面的章节中,这种模式将对每个示例进行重复。

Example 4-9\. Instantiating the dataset, model, loss, and optimizer
示例 4-9:实例化数据集,模型,损失和优化器

```py
dataset = SurnameDataset.load_dataset_and_make_vectorizer(args.surname_csv)
Expand All @@ -354,7 +354,7 @@ optimizer = optim.Adam(classifier.parameters(), lr=args.learning_rate)

与“示例:餐馆评论的情感分类”中的训练循环相比,本例的训练循环除了变量名以外几乎是相同的。具体来说,示例 4-10 显示了使用不同的键从`batch_dict`中获取数据。除了外观上的差异,训练循环的功能保持不变。利用训练数据,计算模型输出、损失和梯度。然后,使用梯度来更新模型。

Example 4-10\. A snippet of the training loop
示例 4-10:训练循环的代码段

```py
# the training routine is these 5 steps:
Expand Down Expand Up @@ -394,7 +394,7 @@ CLASSIFYING A NEW SURNAME

示例 4-11 显示了分类新姓氏的代码。给定一个姓氏作为字符串,该函数将首先应用向量化过程,然后获得模型预测。注意,我们包含了`apply_softmax`标志,所以结果包含概率。模型预测,在多项式的情况下,是类概率的列表。我们使用 PyTorch 张量最大函数来得到由最高预测概率表示的最优类。

Example 4-11\. A function for performing nationality prediction
示例 4-11:执行国籍预测的函数

```py
def predict_nationality(name, classifier, vectorizer):
Expand All @@ -419,7 +419,7 @@ RETRIEVING THE TOP-K PREDICTIONS FOR A NEW SURNAME

不仅要看最好的预测,还要看更多的预测。例如,NLP 中的标准实践是采用`k`个最佳预测并使用另一个模型对它们重新排序。PyTorch 提供了一个`torch.topk`函数,它提供了一种方便的方法来获得这些预测,如示例 4-12 所示。

Example 4-12\. Predicting the top-k nationalities
示例 4-12:预测前`k`个国籍

```py
def predict_topk_nationality(name, classifier, vectorizer, k=5):
Expand Down Expand Up @@ -450,7 +450,7 @@ DROPOUT

简单地说,在训练过程中,丢弃有一定概率使属于两个相邻层的单元之间的连接减弱。这有什么用呢?我们从斯蒂芬•梅里蒂(Stephen Merity)的一段直观(且幽默)的解释开始: “丢弃,简单地说,是指如果你能在喝醉的时候反复学习如何做一件事,那么你应该能够在清醒的时候做得更好。这一见解产生了许多最先进的结果和一个新兴的领域。” 神经网络——尤其是具有大量分层的深层网络——可以在单元之间创建有趣的相互适应。“共同适应”(Coadaptation)是神经科学中的一个术语,但在这里它只是指一种情况,即两个单元之间的联系变得过于紧密,而牺牲了其他单元之间的联系。这通常会导致模型与数据过拟合。通过概率地丢弃单元之间的连接,我们可以确保没有一个单元总是依赖于另一个单元,从而产生健壮的模型。丢弃不会向模型中添加额外的参数,但是需要一个超参数——“丢弃概率”。丢弃概率,正如你可能已经猜到的,是单位之间的连接被丢弃的概率。通常将下降概率设置为 0.5。例 4-13 给出了一个带丢弃的 MLP 的重新实现。

Example 4-13\. MLP with dropout
示例 4-13:带有丢弃的 MLP

```py
import torch.nn as nn
Expand Down Expand Up @@ -544,7 +544,7 @@ CNNs 的名称和基本功能源于经典的数学运算卷积。卷积已经应

在例 4-14 中,构造特征向量的第一步是将 PyTorch 的`Conv1d`类的一个实例应用到三维数据张量。通过检查输出的大小,你可以知道张量减少了多少。我们建议您参考图 4-9 来直观地解释为什么输出张量在收缩。

Example 4-14\. Artificial data and using a Conv1d class
示例 4-14:人造数据和使用`Conv1d`

```py
Input[0]
Expand All @@ -565,7 +565,7 @@ torch.Size([2, 16, 5])

进一步减小输出张量的主要方法有三种。第一种方法是创建额外的卷积并按顺序应用它们。最终,对应的`sequence_width``dim=2`)维度的大小将为 1。我们在例 4-15 中展示了应用两个额外卷积的结果。一般来说,对输出张量的约简应用卷积的过程是迭代的,需要一些猜测工作。我们的示例是这样构造的:经过三次卷积之后,最终的输出在最终维度上的大小为 1。

Example 4-15\. The iterative application of convolutions to data
示例 4-15:卷积在数据上的迭代应用

```py
Input[0]
Expand All @@ -592,7 +592,7 @@ torch.Size([2, 64])

另外还有两种方法可以将张量简化为每个数据点的一个特征向量:将剩余的值压平为特征向量,并在额外维度上求平均值。这两种方法如示例 4-16 所示。使用第一种方法,只需使用 PyTorch 的`view()`方法将所有向量平展成单个向量。第二种方法使用一些数学运算来总结向量中的信息。最常见的操作是算术平均值,但沿特征映射维数求和和使用最大值也是常见的。每种方法都有其优点和缺点。扁平化保留了所有的信息,但会导致比预期(或计算上可行)更大的特征向量。平均变得与额外维度的大小无关,但可能会丢失信息。

Example 4-16\. Two additional methods for reducing to feature vectors
示例 4-16:用于归约到特征向量的两个相加方法

```py
Input[0]
Expand Down Expand Up @@ -621,7 +621,7 @@ torch.Size([2, 16])

我们使用数据集中最长的姓氏来控制单热矩阵的大小有两个原因。首先,将每一小批姓氏矩阵组合成一个三维张量,要求它们的大小相同。其次,使用数据集中最长的姓氏意味着可以以相同的方式处理每个小批量。

Example 4-17\. SurnameDataset modified for passing the maximum surname length
示例 4-17:为传递最大姓氏长度而修改的`SurnameDataset`

```py
class SurnameDataset(Dataset):
Expand All @@ -647,7 +647,7 @@ class SurnameDataset(Dataset):

除了更改为使用单热矩阵之外,我们还修改了矢量化器,以便计算姓氏的最大长度并将其保存为`max_surname_length`

Example 4-18\. Implementing the Surname Vectorizer for CNNs
示例 4-18:为 CNN 实现姓氏向量化器

```py
class SurnameVectorizer(object):
Expand Down Expand Up @@ -700,7 +700,7 @@ class SurnameVectorizer(object):

在本例中,我们将每个卷积的通道数与`num_channels`超参数绑定。我们可以选择不同数量的通道分别进行卷积运算。这样做需要优化更多的超参数。我们发现 256 足够大,可以使模型达到合理的性能。

Example 4-19\. The CNN-based SurnameClassifier
示例 4-19:基于 CNN`SurnameClassifier`

```py
import torch.nn as nn
Expand Down Expand Up @@ -758,7 +758,7 @@ class SurnameClassifier(nn.Module):

训练程序包括以下似曾相识的的操作序列:实例化数据集,实例化模型,实例化损失函数,实例化优化器,遍历数据集的训练分区和更新模型参数,遍历数据集的验证分区和测量性能,然后重复数据集迭代一定次数。此时,这是本书到目前为止的第三个训练例程实现,应该将这个操作序列内部化。对于这个例子,我们将不再详细描述具体的训练例程,因为它与“示例:带有多层感知器的姓氏分类”中的例程完全相同。但是,输入参数是不同的,您可以在示例 4-20 中看到。

Example 4-20\. Input arguments to the CNN surname classifier
示例 4-20CNN 姓氏分类器的输入参数

```py
args = Namespace(
Expand Down Expand Up @@ -794,7 +794,7 @@ args = Namespace(

在本例中,`predict_nationality()`函数的一部分发生了更改,如示例 4-21 所示:我们没有使用视图方法重塑新创建的数据张量以添加批量维度,而是使用 PyTorch 的`unsqueeze()`函数在批量应该在的位置添加大小为 1 的维度。相同的更改反映在`predict_topk_nationality()`函数中。

Example 4-21\. Using the trained model to make predictions
示例 4-21:使用训练过的模型做出预测

```py
def predict_nationality(surname, classifier, vectorizer):
Expand Down Expand Up @@ -833,7 +833,7 @@ def predict_nationality(surname, classifier, vectorizer):

批量标准化是设计网络时经常使用的一种工具。BatchNorm 对 CNN 的输出进行转换,方法是将激活量缩放为零均值和单位方差。它用于 Z 转换的平均值和方差值每批更新一次,这样任何单个批中的波动都不会太大地移动或影响它。BatchNorm 允许模型对参数的初始化不那么敏感,并且简化了学习速率的调整(Ioffe and Szegedy, 2015)。在 PyTorch 中,批量规范是在`nn`模块中定义的。例 4-22 展示了如何用卷积和线性层实例化和使用批量规范。

Example 4-22\. Using s Conv1D layer with batch normalization.
示例 4-22:使用`Conv1D`层和批量规范化

```py
# ...
Expand Down

0 comments on commit fa527a7

Please sign in to comment.