提交 91ae0187 编写于 作者: W wizardforcel

2021-01-22 10:57:20

上级 e71cf9ac
...@@ -520,7 +520,7 @@ ...@@ -520,7 +520,7 @@
3. 考虑到该模型存在较高的偏差,重点应放在增加周期的数量上,或通过在每层中增加额外的层或单位来增加网络的规模。目标应该是将验证集的准确度近似到 80%。 3. 考虑到该模型存在较高的偏差,重点应放在增加周期的数量上,或通过在每层中增加额外的层或单位来增加网络的规模。目标应该是将验证集的准确度近似到 80%。
之后,将显示性能最佳的模型,该模型是在几次微调尝试之后实现的。 首先,定义模型架构和正向传,如以下代码片段所示: 之后,将显示性能最佳的模型,该模型是在几次微调尝试之后实现的。 首先,定义模型架构和正向传,如以下代码片段所示:
```py ```py
class Classifier(nn.Module): class Classifier(nn.Module):
...@@ -910,7 +910,7 @@ ...@@ -910,7 +910,7 @@
        return x         return x
``` ```
前面的代码段包含一个定义了网络架构的类(`__init__`方法),以及在信息正向传过程中所遵循的步骤(`forward`方法)。 前面的代码段包含一个定义了网络架构的类(`__init__`方法),以及在信息正向传过程中所遵循的步骤(`forward`方法)。
7. 定义训练模型所需的所有参数。设置周期数为`50` 7. 定义训练模型所需的所有参数。设置周期数为`50`
......
...@@ -250,9 +250,9 @@ self.dropout = nn.Dropout(p=0.2) ...@@ -250,9 +250,9 @@ self.dropout = nn.Dropout(p=0.2)
丢弃是一种规范化我们的神经网络以防止过拟合的方法。 在每个训练周期上,对于已应用丢包的层中的每个节点,都有可能(此处定义为`p`= 20%)该层内的每个节点将不用于训练/反向传播 。 这意味着,在训练时,我们的网络会针对过拟合变得健壮,因为在训练过程的每次迭代中都不会使用每个节点。 这可以防止我们的网络过于依赖网络中特定节点的预测。 丢弃是一种规范化我们的神经网络以防止过拟合的方法。 在每个训练周期上,对于已应用丢包的层中的每个节点,都有可能(此处定义为`p`= 20%)该层内的每个节点将不用于训练/反向传播 。 这意味着,在训练时,我们的网络会针对过拟合变得健壮,因为在训练过程的每次迭代中都不会使用每个节点。 这可以防止我们的网络过于依赖网络中特定节点的预测。
## 定义正向传 ## 定义正向传
接下来,我们在分类器中定义正向传 接下来,我们在分类器中定义正向传
```py ```py
def forward(self, x): def forward(self, x):
...@@ -311,7 +311,7 @@ Loss(y) = -log(y) ...@@ -311,7 +311,7 @@ Loss(y) = -log(y)
opt.zero_grad() opt.zero_grad()
``` ```
3. 接下来,我们使用模型的当前状态对我们的数据集进行预测。这实际上是我们的正向传,因为我们然后使用这些预测来计算我们的损失。 3. 接下来,我们使用模型的当前状态对我们的数据集进行预测。这实际上是我们的正向传,因为我们然后使用这些预测来计算我们的损失。
```py ```py
outputs = model(images) outputs = model(images)
...@@ -355,7 +355,7 @@ y_test = torch.Tensor(test_labels).long() ...@@ -355,7 +355,7 @@ y_test = torch.Tensor(test_labels).long()
preds = model(X_test) preds = model(X_test)
``` ```
与我们在模型中训练数据的正向传上计算输出的方式相同,现在我们将测试数据传递通过模型并获得预测。 我们可以像这样查看其中一张图像的预测: 与我们在模型中训练数据的正向传上计算输出的方式相同,现在我们将测试数据传递通过模型并获得预测。 我们可以像这样查看其中一张图像的预测:
```py ```py
print(preds[0]) print(preds[0])
......
...@@ -318,7 +318,7 @@ self.embedding = nn.Embedding(n_vocab, n_embed) ...@@ -318,7 +318,7 @@ self.embedding = nn.Embedding(n_vocab, n_embed)
        self.sigmoid = nn.Sigmoid()         self.sigmoid = nn.Sigmoid()
``` ```
接下来,我们需要在模型类中定义正向传。 在此正向传播中,我们只是将一层的输出链接在一起,成为下一层的输入。 在这里,我们可以看到我们的嵌入层将`input_words`作为输入并输出了嵌入的单词。 然后,我们的 LSTM 层将嵌入的单词作为输入并输出`lstm_out`。 唯一的区别是,我们使用`view()`将 LSTM 输出中的张量整形为正确的大小,以输入到全连接层中。 重塑隐藏层的输出以匹配输出节点的输出也是如此。 请注意,我们的输出将返回`class = 0``class = 1`的预测,因此我们将输出切为仅返回`class = 1`的预测— 也就是说,我们的句子为正的概率为: 接下来,我们需要在模型类中定义正向传。 在此正向传播中,我们只是将一层的输出链接在一起,成为下一层的输入。 在这里,我们可以看到我们的嵌入层将`input_words`作为输入并输出了嵌入的单词。 然后,我们的 LSTM 层将嵌入的单词作为输入并输出`lstm_out`。 唯一的区别是,我们使用`view()`将 LSTM 输出中的张量整形为正确的大小,以输入到全连接层中。 重塑隐藏层的输出以匹配输出节点的输出也是如此。 请注意,我们的输出将返回`class = 0``class = 1`的预测,因此我们将输出切为仅返回`class = 1`的预测— 也就是说,我们的句子为正的概率为:
```py ```py
def forward (self, input_words): def forward (self, input_words):
...@@ -431,7 +431,7 @@ for epoch in range(n_epochs): ...@@ -431,7 +431,7 @@ for epoch in range(n_epochs):
        optimizer.step()         optimizer.step()
``` ```
在这里,我们只训练了多个时期的模型,对于每个时期,我们首先使用批量大小参数初始化隐藏层。 在这种情况下,我们设置`batch_size = 1`,因为我们一次只训练我们的模型一个句子。 对于训练装载机中的每批输入语句和标签,我们首先将梯度归零(以防止它们累积),并使用模型的当前状态使用数据的正向传来计算模型输出。 然后使用此输出,使用模型的预测输出和正确的标签来计算损失。 然后,我们通过网络对该损失进行反向传递,以计算每个阶段的梯度。 接下来,我们使用`grad_clip_norm()`函数裁剪梯度,因为这将阻止梯度爆炸,如本章前面所述。 我们定义了`clip = 5`,这意味着任何给定节点的最大梯度为`5`。 最后,我们通过调用`optimizer.step()`,使用在反向传播中计算出的梯度来更新权重。 在这里,我们只训练了多个时期的模型,对于每个时期,我们首先使用批量大小参数初始化隐藏层。 在这种情况下,我们设置`batch_size = 1`,因为我们一次只训练我们的模型一个句子。 对于训练装载机中的每批输入语句和标签,我们首先将梯度归零(以防止它们累积),并使用模型的当前状态使用数据的正向传来计算模型输出。 然后使用此输出,使用模型的预测输出和正确的标签来计算损失。 然后,我们通过网络对该损失进行反向传递,以计算每个阶段的梯度。 接下来,我们使用`grad_clip_norm()`函数裁剪梯度,因为这将阻止梯度爆炸,如本章前面所述。 我们定义了`clip = 5`,这意味着任何给定节点的最大梯度为`5`。 最后,我们通过调用`optimizer.step()`,使用在反向传播中计算出的梯度来更新权重。
如果我们自己运行此循环,我们将训练我们的模型。 但是,我们想在每个时期之后评估模型的性能,以便根据验证数据集确定模型的性能。 我们这样做如下: 如果我们自己运行此循环,我们将训练我们的模型。 但是,我们想在每个时期之后评估模型的性能,以便根据验证数据集确定模型的性能。 我们这样做如下:
......
...@@ -494,7 +494,7 @@ for line in lines[:3]: ...@@ -494,7 +494,7 @@ for line in lines[:3]:
我们在输入句子中保持两个隐藏状态以及每一步的输出。 我们在输入句子中保持两个隐藏状态以及每一步的输出。
3. 接下来,我们需要为我们的编码器创建一个正向传。我们首先将输入句子嵌入,然后使用`pack_padded_sequence`函数对我们的嵌入进行处理。这个函数对我们的填充序列进行 "打包",使我们所有的输入都具有相同的长度。然后,我们将打包后的序列通过 GRU 传递出去,进行正向传播。 3. 接下来,我们需要为我们的编码器创建一个正向传。我们首先将输入句子嵌入,然后使用`pack_padded_sequence`函数对我们的嵌入进行处理。这个函数对我们的填充序列进行 "打包",使我们所有的输入都具有相同的长度。然后,我们将打包后的序列通过 GRU 传递出去,进行正向传播。
```py ```py
def forward(self, input_seq, input_lengths, hidden=None): def forward(self, input_seq, input_lengths, hidden=None):
...@@ -645,7 +645,7 @@ for line in lines[:3]: ...@@ -645,7 +645,7 @@ for line in lines[:3]:
    n_totals = 0     n_totals = 0
``` ```
3. 然后,通过编码器执行输入和序列长度的正向传,得到输出和隐藏状态。 3. 然后,通过编码器执行输入和序列长度的正向传,得到输出和隐藏状态。
```py ```py
encoder_outputs, encoder_hidden = encoder(input_variable, lengths) encoder_outputs, encoder_hidden = encoder(input_variable, lengths)
...@@ -810,7 +810,7 @@ for line in lines[:3]: ...@@ -810,7 +810,7 @@ for line in lines[:3]:
        self.decoder = decoder         self.decoder = decoder
``` ```
2. 接下来,为我们的解码器定义一个正向传。我们将输入通过编码器得到我们编码器的输出和隐藏状态。我们把编码器的最后一个隐藏层作为解码器的第一个隐藏输入。 2. 接下来,为我们的解码器定义一个正向传。我们将输入通过编码器得到我们编码器的输出和隐藏状态。我们把编码器的最后一个隐藏层作为解码器的第一个隐藏输入。
```py ```py
def forward(self, input_seq, input_length, max_length): def forward(self, input_seq, input_length, max_length):
...@@ -827,7 +827,7 @@ for line in lines[:3]: ...@@ -827,7 +827,7 @@ for line in lines[:3]:
all_scores = torch.zeros([0], device=device) all_scores = torch.zeros([0], device=device)
``` ```
4. 之后,对序列进行迭代,每次解码一个词。我们对编码器进行正向传,并添加一个`max`函数,以获得得分最高的预测词及其得分,然后将其追加到`all_tokens``all_scores`变量中。最后,我们将这个预测的标记作为我们解码器的下一个输入。在整个序列被迭代过后,我们返回完整的预测句。 4. 之后,对序列进行迭代,每次解码一个词。我们对编码器进行正向传,并添加一个`max`函数,以获得得分最高的预测词及其得分,然后将其追加到`all_tokens``all_scores`变量中。最后,我们将这个预测的标记作为我们解码器的下一个输入。在整个序列被迭代过后,我们返回完整的预测句。
```py ```py
for _ in range(max_length): for _ in range(max_length):
......
...@@ -477,7 +477,7 @@ Training loss: 0.2596 ...@@ -477,7 +477,7 @@ Training loss: 0.2596
在本秘籍中,我们将研究实现丢弃。 在训练神经网络模型或一般任何机器学习模型时,我们可能会遇到的一种较常见的现象是过拟合。 当模型学习提供给训练的数据而不是在求解空间上进行泛化时,就会发生过拟合,也就是说,模型学习的是训练数据的细微细节和噪声,而不是掌握全局,因此在效果上表现不佳。 新数据。 正则化是防止模型过拟合的过程。 在本秘籍中,我们将研究实现丢弃。 在训练神经网络模型或一般任何机器学习模型时,我们可能会遇到的一种较常见的现象是过拟合。 当模型学习提供给训练的数据而不是在求解空间上进行泛化时,就会发生过拟合,也就是说,模型学习的是训练数据的细微细节和噪声,而不是掌握全局,因此在效果上表现不佳。 新数据。 正则化是防止模型过拟合的过程。
使用丢弃是神经网络中最流行的正则化技术之一,在这种技术中,训练时会关闭随机选择的神经元,也就是说,神经元的作用会暂时从正向传中移除,而反向传播不会影响权重 ,因此没有一个神经元或神经元子集能获得模型的所有决定力; 相反,所有神经元都被迫为预测做出积极贡献。 使用丢弃是神经网络中最流行的正则化技术之一,在这种技术中,训练时会关闭随机选择的神经元,也就是说,神经元的作用会暂时从正向传中移除,而反向传播不会影响权重 ,因此没有一个神经元或神经元子集能获得模型的所有决定力; 相反,所有神经元都被迫为预测做出积极贡献。
丢弃可以直观地理解为创建大量集成模型,学习在一个模型的大定义下捕获各种功能。 丢弃可以直观地理解为创建大量集成模型,学习在一个模型的大定义下捕获各种功能。
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
1. 登录或登录[这里](http://www.packt.com) 1. 登录或登录[这里](http://www.packt.com)
2. 选择**支持**标签。 2. 选择**支持**标签。
3. 单击**代码下载&勘误表** 3. 单击**代码下载 & 勘误表**
4.**搜索**框中输入书籍的名称,然后按照屏幕上的说明进行操作。 4.**搜索**框中输入书籍的名称,然后按照屏幕上的说明进行操作。
下载文件后,请确保使用以下最新版本解压缩或解压缩文件夹: 下载文件后,请确保使用以下最新版本解压缩或解压缩文件夹:
......
...@@ -269,7 +269,7 @@ train, val, test = data.TabularDataset.splits( ...@@ -269,7 +269,7 @@ train, val, test = data.TabularDataset.splits(
`Field``build_vocab`要求我们传递上一步中`DataSet.split`方法返回的`data`对象。 `Field`就是这样知道数据集中存在的单词,总词汇量的长度等等。 `build_vocab`方法还可以为您下载预训练的词汇向量(如果您还没有的话)。 通过`torchtext`可用的词嵌入为: `Field``build_vocab`要求我们传递上一步中`DataSet.split`方法返回的`data`对象。 `Field`就是这样知道数据集中存在的单词,总词汇量的长度等等。 `build_vocab`方法还可以为您下载预训练的词汇向量(如果您还没有的话)。 通过`torchtext`可用的词嵌入为:
* 字符 N 元组 * 字符 N 元组
* 快速文字 * Fasttext
* GloVe 向量 * GloVe 向量
```py ```py
......
...@@ -44,7 +44,7 @@ CNN 通过两种机制实现位置不变:跨步和合并。 步幅值决定了 ...@@ -44,7 +44,7 @@ CNN 通过两种机制实现位置不变:跨步和合并。 步幅值决定了
* 卷积层 * 卷积层
* 非线性层 * 非线性层
* 汇聚 * 池化
* 全连接层 * 全连接层
## 使用 PyTorch 的计算机视觉 ## 使用 PyTorch 的计算机视觉
...@@ -269,16 +269,16 @@ trainloader, testloader = get_data() ...@@ -269,16 +269,16 @@ trainloader, testloader = get_data()
现在我们已经准备好训练我们的神经网络。 至此,我们可以使用模板代码进行训练了: 现在我们已经准备好训练我们的神经网络。 至此,我们可以使用模板代码进行训练了:
1. 遍历时代 1. 遍历周期
2. 循环遍历每个期的数据。 2. 循环遍历每个期的数据。
3. 通过调用以下命令使现有的梯度为零: 3. 通过调用以下命令使现有的梯度为零:
* `optimizer.zero_grad()` * `optimizer.zero_grad()`
* `net.zero_grad()` * `net.zero_grad()`
4. 运行网络的正向传 4. 运行网络的正向传
5. 通过通过网络输出调用损失函数来获取损失。 5. 通过使用网络输出调用损失函数来获取损失。
6. 运行反向传播。 6. 运行反向传播。
7. 使用优化程序进行梯度更新。 7. 使用优化程序进行梯度更新。
8. 如果需要,可以节省运行损失。 8. 如果需要,可以保存运行损失。
在保存运行损失时要小心,因为 PyTorch 会在变量进行反向传播之前保存整个图形。 增量保存图形只是图形中的另一种操作,其中每次迭代中的图形都使用求和运算将先前的图形附加到图形上,最终导致内存不足。 始终从图形中取出值并将其保存为没有图形历史记录的普通张量。 在保存运行损失时要小心,因为 PyTorch 会在变量进行反向传播之前保存整个图形。 增量保存图形只是图形中的另一种操作,其中每次迭代中的图形都使用求和运算将先前的图形附加到图形上,最终导致内存不足。 始终从图形中取出值并将其保存为没有图形历史记录的普通张量。
...@@ -380,7 +380,7 @@ class ConvBlock(nn.Module): ...@@ -380,7 +380,7 @@ class ConvBlock(nn.Module):
return self.conv_block(x) return self.conv_block(x)
``` ```
LinkNet 中的所有卷积都紧随其后的是批量规范化和 ReLU 层,但是有一些例外,没有 ReLU 层。 这就是`ConvBlock`的目标。 如前所述,`ConvBlock``torch.nn.Module`的子类,可以根据正向传中发生的任何事情进行反向传播。 `__init__`接受输入和输出尺寸,内核大小,步幅值,填充宽度,表示是否需要偏置的布尔值和表示是否需要激活(ReLU)的布尔值。 LinkNet 中的所有卷积都紧随其后的是批量规范化和 ReLU 层,但是有一些例外,没有 ReLU 层。 这就是`ConvBlock`的目标。 如前所述,`ConvBlock``torch.nn.Module`的子类,可以根据正向传中发生的任何事情进行反向传播。 `__init__`接受输入和输出尺寸,内核大小,步幅值,填充宽度,表示是否需要偏置的布尔值和表示是否需要激活(ReLU)的布尔值。
我们使用`torch.nn.Conv2d``torch.nn.BatchNorm2d``torch.nn.ReLu`来配置`ConvBlock`。 PyTorch 的`Conv2D`接受`ConvBlock``__init__`的所有参数,但表示类似激活要求的布尔值除外。 除此之外,`Conv2D`还接受另外两个用于`dilation``group`的可选参数。 `torch.nn`的 ReLU 函数仅接受一个称为`inplace`的可选参数,默认为`False`。 如果`inplace``True`,则 ReLU 将应用于原地数据,而不是创建另一个存储位置。 在许多情况下,这可能会稍微节省内存,但会导致问题,因为我们正在破坏输入。 经验法则是:除非您迫切需要内存优化,否则请远离它。 我们使用`torch.nn.Conv2d``torch.nn.BatchNorm2d``torch.nn.ReLu`来配置`ConvBlock`。 PyTorch 的`Conv2D`接受`ConvBlock``__init__`的所有参数,但表示类似激活要求的布尔值除外。 除此之外,`Conv2D`还接受另外两个用于`dilation``group`的可选参数。 `torch.nn`的 ReLU 函数仅接受一个称为`inplace`的可选参数,默认为`False`。 如果`inplace``True`,则 ReLU 将应用于原地数据,而不是创建另一个存储位置。 在许多情况下,这可能会稍微节省内存,但会导致问题,因为我们正在破坏输入。 经验法则是:除非您迫切需要内存优化,否则请远离它。
......
...@@ -25,14 +25,14 @@ ...@@ -25,14 +25,14 @@
在本章中,我将首先解决要解决的问题,然后说明概念,同时解决我们遇到的问题。 问题是用三种不同的方法来找到两个英语句子之间的相似性。 为了使比较公平,我们将在所有实现中使用单词嵌入。 不用担心,我们还将进行单词嵌入。 手头的问题通常称为**包含问题**,其中我们每次都有两个句子,我们的工作是预测这些句子之间的相似性。 我们可以将句子分为三类: 在本章中,我将首先解决要解决的问题,然后说明概念,同时解决我们遇到的问题。 问题是用三种不同的方法来找到两个英语句子之间的相似性。 为了使比较公平,我们将在所有实现中使用单词嵌入。 不用担心,我们还将进行单词嵌入。 手头的问题通常称为**包含问题**,其中我们每次都有两个句子,我们的工作是预测这些句子之间的相似性。 我们可以将句子分为三类:
* 蕴含:这两个句子是同一意思: * 蕴含:这两个句子是同一意思:
* 与多个男性一起玩的足球比赛。 * `A soccer game with multiple males playing.`
* 有些男人在玩运动。 * `Some men are playing a sport.`
* 中性:两个句子有一个共同点: * 中性:两个句子有一个共同点:
* 一年轻和年轻人微笑。 * `An older and younger man smiling.`
* 两名男子在笑和嘲笑在地板上玩的猫。 * `Two men are smiling and laughing at the cats playing on the floor.`
* 矛盾:两个句子都传达两种不同的含义: * 矛盾:两个句子都传达两种不同的含义:
* 一辆黑色赛车在人群面前启动。 * `A black race car starts up in front of a crowd of people.`
* 一个人在孤独的道路上行驶。 * `A man is driving down a lonely road.`
![The problem](img/B09475_05_01.jpg) ![The problem](img/B09475_05_01.jpg)
......
...@@ -29,7 +29,7 @@ GAN 的创建者 Ian Goodfellow 描述了几类生成网络: ...@@ -29,7 +29,7 @@ GAN 的创建者 Ian Goodfellow 描述了几类生成网络:
我们将讨论这两个主要类别,它们在过去已经讨论过很多并且仍然是活跃的研究领域: 我们将讨论这两个主要类别,它们在过去已经讨论过很多并且仍然是活跃的研究领域:
* 自回归模型 * 自回归模型
* * GAN
自回归模型是从先前的值推断当前值的模型,正如我们在第 5 章,“序列数据处理”中使用 RNN 所讨论的那样。 **变分自编码器****VAE**)是自编码器的一种变体,由编码器和解码器组成,其中编码器将输入编码为低维潜在空间向量, 解码器解码潜向量以生成类似于输入的输出。 自回归模型是从先前的值推断当前值的模型,正如我们在第 5 章,“序列数据处理”中使用 RNN 所讨论的那样。 **变分自编码器****VAE**)是自编码器的一种变体,由编码器和解码器组成,其中编码器将输入编码为低维潜在空间向量, 解码器解码潜向量以生成类似于输入的输出。
......
...@@ -150,7 +150,7 @@ tensor([True, True]) ...@@ -150,7 +150,7 @@ tensor([True, True])
* 将它们累积在各自的张量的`.grad`属性中,然后 * 将它们累积在各自的张量的`.grad`属性中,然后
* 使用链规则,一直传播到叶张量。 * 使用链规则,一直传播到叶张量。
下面是我们示例中 DAG 的直观表示。 在图中,箭头指向前进的方向。 节点代表正向传中每个操作的反向功能。 蓝色的叶节点代表我们的叶张量`a``b` 下面是我们示例中 DAG 的直观表示。 在图中,箭头指向前进的方向。 节点代表正向传中每个操作的反向功能。 蓝色的叶节点代表我们的叶张量`a``b`
![../../_img/dag_autograd.png](img/1270bde38f2cfccd4900a5df8ac70a7d.png) ![../../_img/dag_autograd.png](img/1270bde38f2cfccd4900a5df8ac70a7d.png)
......
...@@ -514,7 +514,7 @@ print(f'Result: {model.string()}') ...@@ -514,7 +514,7 @@ print(f'Result: {model.string()}')
作为动态图和权重共享的示例,我们实现了一个非常奇怪的模型:一个三阶多项式,在每个正向传播中选择 3 到 5 之间的一个随机数,并使用该阶数,多次重复使用相同的权重进行计算 第四和第五阶。 作为动态图和权重共享的示例,我们实现了一个非常奇怪的模型:一个三阶多项式,在每个正向传播中选择 3 到 5 之间的一个随机数,并使用该阶数,多次重复使用相同的权重进行计算 第四和第五阶。
对于此模型,我们可以使用常规的 Python 流控制来实现循环,并且可以通过在定义正向传时简单地多次重复使用相同的参数来实现权重共享。 对于此模型,我们可以使用常规的 Python 流控制来实现循环,并且可以通过在定义正向传时简单地多次重复使用相同的参数来实现权重共享。
我们可以轻松地将此模型实现为`Module`子类: 我们可以轻松地将此模型实现为`Module`子类:
......
...@@ -137,7 +137,7 @@ torch::Tensor d_sigmoid(torch::Tensor z) { ...@@ -137,7 +137,7 @@ torch::Tensor d_sigmoid(torch::Tensor z) {
#### 正向传播 #### 正向传播
接下来,我们可以将整个正向传到 C++: 接下来,我们可以将整个正向传到 C++:
```py ```py
#include <vector> #include <vector>
......
...@@ -36,7 +36,7 @@ PyTorch 为数据并行训练提供了几种选择。 对于从简单到复杂 ...@@ -36,7 +36,7 @@ PyTorch 为数据并行训练提供了几种选择。 对于从简单到复杂
### `torch.nn.parallel.DistributedDataParallel` ### `torch.nn.parallel.DistributedDataParallel`
[`DataParallel`](https://pytorch.org/docs/master/generated/torch.nn.DataParallel.html)相比,[`DistributedDataParallel`](https://pytorch.org/docs/master/generated/torch.nn.parallel.DistributedDataParallel.html)还需要设置一个步骤,即调用[`init_process_group`](https://pytorch.org/docs/stable/distributed.html#torch.distributed.init_process_group)。 DDP 使用多进程并行性,因此在模型副本之间没有 GIL 争用。 此外,该模型是在 DDP 构建时而不是在每个正向传时广播的,这也有助于加快训练速度。 DDP 附带了几种性能优化技术。 有关更深入的说明,请参阅此 [DDP 论文](https://arxiv.org/abs/2006.15704)(VLDB'20)。 [`DataParallel`](https://pytorch.org/docs/master/generated/torch.nn.DataParallel.html)相比,[`DistributedDataParallel`](https://pytorch.org/docs/master/generated/torch.nn.parallel.DistributedDataParallel.html)还需要设置一个步骤,即调用[`init_process_group`](https://pytorch.org/docs/stable/distributed.html#torch.distributed.init_process_group)。 DDP 使用多进程并行性,因此在模型副本之间没有 GIL 争用。 此外,该模型是在 DDP 构建时而不是在每个正向传时广播的,这也有助于加快训练速度。 DDP 附带了几种性能优化技术。 有关更深入的说明,请参阅此 [DDP 论文](https://arxiv.org/abs/2006.15704)(VLDB'20)。
DDP 材料如下: DDP 材料如下:
......
...@@ -115,7 +115,7 @@ def run_demo(demo_fn, world_size): ...@@ -115,7 +115,7 @@ def run_demo(demo_fn, world_size):
## 带偏差的处理速度 ## 带偏差的处理速度
在 DDP 中,构造器,正向传和反向传递都是分布式同步点。 预期不同的进程将启动相同数量的同步,并以相同的顺序到达这些同步点,并在大致相同的时间进入每个同步点。 否则,快速流程可能会提早到达,并在等待流浪者时超时。 因此,用户负责平衡流程之间的工作负载分配。 有时,由于例如网络延迟,资源争夺,不可预测的工作量峰值,不可避免地会出现处理速度偏差。 为了避免在这种情况下超时,请在调用[`init_process_group`](https://pytorch.org/docs/stable/distributed.html#torch.distributed.init_process_group)时传递足够大的`timeout`值。 在 DDP 中,构造器,正向传和反向传递都是分布式同步点。 预期不同的进程将启动相同数量的同步,并以相同的顺序到达这些同步点,并在大致相同的时间进入每个同步点。 否则,快速流程可能会提早到达,并在等待流浪者时超时。 因此,用户负责平衡流程之间的工作负载分配。 有时,由于例如网络延迟,资源争夺,不可预测的工作量峰值,不可避免地会出现处理速度偏差。 为了避免在这种情况下超时,请在调用[`init_process_group`](https://pytorch.org/docs/stable/distributed.html#torch.distributed.init_process_group)时传递足够大的`timeout`值。
## 保存和加载检查点 ## 保存和加载检查点
......
...@@ -138,7 +138,7 @@ class ResNetShard2(ResNetBase): ...@@ -138,7 +138,7 @@ class ResNetShard2(ResNetBase):
## 第 2 步:将 ResNet50 模型片段拼接到一个模块中 ## 第 2 步:将 ResNet50 模型片段拼接到一个模块中
然后,我们创建一个`DistResNet50`模块来组装两个分片并实现流水线并行逻辑。 在构造器中,我们使用两个`rpc.remote`调用分别将两个分片放在两个不同的 RPC 工作器上,并保持`RRef`到两个模型部分,以便可以在正向传中引用它们。 `forward`函数将输入批量分为多个微批量,并将这些微批量以流水线方式馈送到两个模型部件。 它首先使用`rpc.remote`调用将第一个分片应用于微批量,然后将返回的中间输出`RRef`转发到第二个模型分片。 之后,它将收集所有微输出的`Future`,并在循环后等待所有它们。 请注意,`remote()``rpc_async()`都立即返回并异步运行。 因此,整个循环是非阻塞的,并将同时启动多个 RPC。 中间输出`y_rref`保留了两个模型零件上一个微批量的执行顺序。 微批量的执行顺序无关紧要。 最后,正向函数将所有微批量的输出连接到一个单一的输出张量中并返回。 `parameter_rrefs`函数是简化分布式优化器构造的助手,将在以后使用。 然后,我们创建一个`DistResNet50`模块来组装两个分片并实现流水线并行逻辑。 在构造器中,我们使用两个`rpc.remote`调用分别将两个分片放在两个不同的 RPC 工作器上,并保持`RRef`到两个模型部分,以便可以在正向传中引用它们。 `forward`函数将输入批量分为多个微批量,并将这些微批量以流水线方式馈送到两个模型部件。 它首先使用`rpc.remote`调用将第一个分片应用于微批量,然后将返回的中间输出`RRef`转发到第二个模型分片。 之后,它将收集所有微输出的`Future`,并在循环后等待所有它们。 请注意,`remote()``rpc_async()`都立即返回并异步运行。 因此,整个循环是非阻塞的,并将同时启动多个 RPC。 中间输出`y_rref`保留了两个模型零件上一个微批量的执行顺序。 微批量的执行顺序无关紧要。 最后,正向函数将所有微批量的输出连接到一个单一的输出张量中并返回。 `parameter_rrefs`函数是简化分布式优化器构造的助手,将在以后使用。
```py ```py
class DistResNet50(nn.Module): class DistResNet50(nn.Module):
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
1. 主服务器在参数服务器上创建一个嵌入表,并为其保留一个 [RRef](https://pytorch.org/docs/master/rpc.html#rref) 1. 主服务器在参数服务器上创建一个嵌入表,并为其保留一个 [RRef](https://pytorch.org/docs/master/rpc.html#rref)
2. 然后,主持人开始在训练器上进行训练循环,并将嵌入表 RRef 传递给训练器。 2. 然后,主持人开始在训练器上进行训练循环,并将嵌入表 RRef 传递给训练器。
3. 训练器创建一个`HybridModel`,该`HybridModel`首先使用主机提供的嵌入表 RRef 执行嵌入查找,然后执行包装在 DDP 中的 FC 层。 3. 训练器创建一个`HybridModel`,该`HybridModel`首先使用主机提供的嵌入表 RRef 执行嵌入查找,然后执行包装在 DDP 中的 FC 层。
4. 训练者执行模型的正向传,并使用[分布式 Autograd](https://pytorch.org/docs/master/rpc.html#distributed-autograd-framework) 使用损失执行反向传递。 4. 训练者执行模型的正向传,并使用[分布式 Autograd](https://pytorch.org/docs/master/rpc.html#distributed-autograd-framework) 使用损失执行反向传递。
5. 作为向后遍历的一部分,将首先计算 FC 层的梯度,并通过 DDP 中的`allreduce`将其同步到所有训练器。 5. 作为向后遍历的一部分,将首先计算 FC 层的梯度,并通过 DDP 中的`allreduce`将其同步到所有训练器。
6. 接下来,分布式 Autograd 将梯度传播到参数服务器,在该服务器中更新嵌入表的梯度。 6. 接下来,分布式 Autograd 将梯度传播到参数服务器,在该服务器中更新嵌入表的梯度。
7. 最后,[分布式优化器](https://pytorch.org/docs/master/rpc.html#module-torch.distributed.optim)用于更新所有参数。 7. 最后,[分布式优化器](https://pytorch.org/docs/master/rpc.html#module-torch.distributed.optim)用于更新所有参数。
...@@ -187,7 +187,7 @@ def _run_trainer(emb_rref, rank): ...@@ -187,7 +187,7 @@ def _run_trainer(emb_rref, rank):
现在,我们准备介绍在每个训练器上运行的主要训练循环。 `get_next_batch`只是一个辅助函数,用于生成随机输入和训练目标。 我们针对多个时期和每个批量运行训练循环: 现在,我们准备介绍在每个训练器上运行的主要训练循环。 `get_next_batch`只是一个辅助函数,用于生成随机输入和训练目标。 我们针对多个时期和每个批量运行训练循环:
1. 为分布式 Autograd 设置[分布式 Autograd 上下文](https://pytorch.org/docs/master/rpc.html#torch.distributed.autograd.context) 1. 为分布式 Autograd 设置[分布式 Autograd 上下文](https://pytorch.org/docs/master/rpc.html#torch.distributed.autograd.context)
2. 运行模型的正向传并检索其输出。 2. 运行模型的正向传并检索其输出。
3. 使用损失函数,根据我们的输出和目标计算损失。 3. 使用损失函数,根据我们的输出和目标计算损失。
4. 使用分布式 Autograd 使用损失执行分布式反向传递。 4. 使用分布式 Autograd 使用损失执行分布式反向传递。
5. 最后,运行“分布式优化器”步骤以优化所有参数。 5. 最后,运行“分布式优化器”步骤以优化所有参数。
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册