提交 28aab16e 编写于 作者: W wizardforcel

2021-01-16 18:21:25

上级 06ce0bf1
......@@ -6,14 +6,14 @@
<https://www.youtube.com/embed/u7x8RXwLKcA>
## 什么是 PyTorch?](docs / modern-java-zh /
## 什么是 PyTorch?
PyTorch 是基于 Python 的科学计算软件包,可实现两个广泛的目的:
* 替代 NumPy,以使用 GPU 和其他加速器的功能。
* 一个自动微分库,对实现神经网络很有用。
## 本教程的目标:](docs / modern-java-zh /
## 本教程的目标:
* 全面了解 PyTorch 的 Tensor 库和神经网络。
* 训练一个小型神经网络对图像进行分类
......
......@@ -12,7 +12,7 @@ import numpy as np
```
## 张量初始化](docs / modern-java-zh /
## 张量初始化
张量可以通过多种方式初始化。 看下面的例子:
......@@ -121,7 +121,7 @@ Device tensor is stored on: cpu
* * *
## 张量运算](docs / modern-java-zh /
## 张量运算
[在此处](https://pytorch.org/docs/stable/torch.html)全面描述了超过 100 个张量运算,包括转置,索引,切片,数学运算,线性代数,随机采样等。
......@@ -256,11 +256,11 @@ tensor([[6., 5., 6., 6.],
* * *
## 与 NumPy 桥接](docs / modern-java-zh /
## 与 NumPy 桥接
CPU 和 NumPy 阵列上的张量可以共享其基础内存位置,更改一个将更改另一个。
### 张量到 NumPy 数组](docs / modern-java-zh /
### 张量到 NumPy 数组
```py
t = torch.ones(5)
......@@ -295,7 +295,7 @@ n: [2\. 2\. 2\. 2\. 2.]
```
### 将 NumPy 数组转换为 Tensor](docs / modern-java-zh /
### 将 NumPy 数组转换为 Tensor
```py
n = np.ones(5)
......
......@@ -14,7 +14,7 @@
**向后传播**:在反向传播中,NN 根据其猜测中的错误调整其参数。 它通过从输出向后遍历,收集有关函数参数(*梯度*)的误差导数并使用梯度下降来优化参数来实现。 有关 backprop 的更详细的演练,请查看 3Blue1Brown 的[视频。](https://www.youtube.com/watch?v=tIeHLnjs5U8)
## 在 PyTorch 中的用法](docs / modern-java-zh /
## 在 PyTorch 中的用法
让我们来看一个训练步骤。 对于此示例,我们从`torchvision`加载了经过预训练的 resnet18 模型。 我们创建一个随机数据张量来表示具有 3 个通道的单个图像,高度&宽度为 64,其对应的`label`初始化为一些随机值。
......@@ -59,7 +59,7 @@ optim.step() #gradient descent
* * *
## Autograd 的差异](docs / modern-java-zh /
## Autograd 的差异
让我们来看看`autograd`如何收集渐变。 我们用`requires_grad=True`创建两个张量`a``b`。 这向`autograd`发出信号,应跟踪对它们的所有操作。
......@@ -133,7 +133,7 @@ tensor([True, True])
上面的示例中使用的是 vector-Jacobian 乘积的这一特征。 `external_grad`表示\(\ vec {v} \)
## 计算图](docs / modern-java-zh /
## 计算图
从概念上讲,autograd 在由[函数](https://pytorch.org/docs/stable/autograd.html#torch.autograd.Function)对象组成的有向无环图(DAG)中记录数据(张量)和所有已执行的操作(以及由此产生的新张量)。 在此 DAG 中,叶子是输入张量,根是输出张量。 通过从根到叶跟踪此图,可以使用链规则自动计算梯度。
......@@ -220,7 +220,7 @@ optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9)
* * *
## 进一步阅读:](docs / modern-java-zh /
## 进一步阅读:
* [就地操作&多线程 Autograd](https://pytorch.org/docs/stable/notes/autograd.html)
* [反向模式 autodiff](https://colab.research.google.com/drive/1VpeE6UvEPRz9HmsHh1KS0XxXjYu533EC) 的示例实现
......
......@@ -152,7 +152,7 @@ out.backward(torch.randn(1, 10))
* 计算损失
* 更新网络的权重
## 损失函数](docs / modern-java-zh /
## 损失函数
损失函数采用一对(输出,目标)输入,并计算一个值,该值估计输出与目标之间的距离。
......@@ -247,7 +247,7 @@ tensor([ 0.0111, -0.0064, 0.0053, -0.0047, 0.0026, -0.0153])
> * 更新网络的权重
## 更新权重](docs / modern-java-zh /
## 更新权重
实践中使用的最简单的更新规则是随机梯度下降(SGD):
......
......@@ -6,7 +6,7 @@
现在您可能在想,
## 数据呢?](docs / modern-java-zh /
## 数据呢?
通常,当您必须处理图像,文本,音频或视频数据时,可以使用将数据加载到 numpy 数组中的标准 python 包。 然后,您可以将该数组转换为`torch.*Tensor`
......@@ -34,7 +34,7 @@ cifar10
4. 根据训练数据训练网络
5. 在测试数据上测试网络
### 1.加载并标准化 CIFAR10](docs / modern-java-zh /
### 1.加载并标准化 CIFAR10
使用`torchvision`,加载 CIFAR10 非常容易。
......@@ -116,7 +116,7 @@ dog truck frog horse
```
### 2.定义卷积神经网络](docs / modern-java-zh /
### 2.定义卷积神经网络
之前从“神经网络”部分复制神经网络,然后对其进行修改以获取 3 通道图像(而不是定义的 1 通道图像)。
......@@ -147,7 +147,7 @@ net = Net()
```
### 3.定义损失函数和优化器](docs / modern-java-zh /
### 3.定义损失函数和优化器
让我们使用分类交叉熵损失和带有动量的 SGD。
......@@ -159,7 +159,7 @@ optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
```
### 4.训练网络](docs / modern-java-zh /
### 4.训练网络
这是事情开始变得有趣的时候。 我们只需要遍历数据迭代器,然后将输入馈送到网络并进行优化即可。
......@@ -350,7 +350,7 @@ Accuracy of truck : 52 %
我们如何在 GPU 上运行这些神经网络?
## 在 GPU 上进行训练](docs / modern-java-zh /
## 在 GPU 上进行训练
就像将 Tensor 转移到 GPU 上一样,您也将神经网络转移到 GPU 上。
......@@ -401,7 +401,7 @@ inputs, labels = data[0].to(device), data[1].to(device)
如果您想使用所有 GPU 来获得更大的大规模加速,请查看[可选:数据并行](data_parallel_tutorial.html)
## 我下一步要去哪里?](docs / modern-java-zh /
## 我下一步要去哪里?
* [训练神经网络玩视频游戏](../../intermediate/reinforcement_q_learning.html)
* [在 imagenet](https://github.com/pytorch/examples/tree/master/imagenet) 上训练最先进的 ResNet 网络
......
......@@ -35,9 +35,9 @@ PyTorch 的核心是提供两个主要功能:
* [自动分级](#id2)
* [<cite>nn</cite> 模块](#id3)
## [张量](#id12)](docs / modern-java-zh /
## [张量](#id12)
### [预热:numpy](#id13) ](docs / modern-java-zh /
### [预热:numpy](#id13)
在介绍 PyTorch 之前,我们将首先使用 numpy 实现网络。
......@@ -86,7 +86,7 @@ print(f'Result: y = {a} + {b} x + {c} x^2 + {d} x^3')
```
### [PyTorch:张量](#id14)](docs / modern-java-zh /
### [PyTorch:张量](#id14)
Numpy 是一个很棒的框架,但是它不能利用 GPU 来加速其数值计算。 对于现代深度神经网络,GPU 通常会提供 [50 倍或更高](https://github.com/jcjohnson/cnn-benchmarks)的加速,因此遗憾的是,numpy 不足以实现现代深度学习。
......@@ -143,9 +143,9 @@ print(f'Result: y = {a.item()} + {b.item()} x + {c.item()} x^2 + {d.item()} x^3'
```
## [Autograd](#id15) ](docs / modern-java-zh /
## [Autograd](#id15)
### [PyTorch:张量和 autograd](#id16) ](docs / modern-java-zh /
### [PyTorch:张量和 autograd](#id16)
在上述示例中,我们必须手动实现神经网络的前向和后向传递。 对于小型的两层网络,手动实施反向传递并不是什么大问题,但是对于大型的复杂网络来说,可以很快变得非常麻烦。
......@@ -216,7 +216,7 @@ print(f'Result: y = {a.item()} + {b.item()} x + {c.item()} x^2 + {d.item()} x^3'
```
### [PyTorch:定义新的 autograd 函数](#id17)](docs / modern-java-zh /
### [PyTorch:定义新的 autograd 函数](#id17)
在幕后,每个原始的 autograd 运算符实际上都是在 Tensor 上运行的两个函数。 **正向**函数从输入张量计算输出张量。 **向后**函数接收相对于某个标量值的输出张量的梯度,并计算相对于相同标量值的输入张量的梯度。
......@@ -311,9 +311,9 @@ print(f'Result: y = {a.item()} + {b.item()} * P3({c.item()} + {d.item()} x)')
```
## [<cite>nn</cite> 模块](#id18)](docs / modern-java-zh /
## [<cite>nn</cite> 模块](#id18)
### [PyTorch:nn](#id19) ](docs / modern-java-zh /
### [PyTorch:nn](#id19)
计算图和 autograd 是定义复杂运算符并自动采用导数的非常强大的范例。 但是对于大型神经网络,原始的 autograd 可能会太低级。
......@@ -398,7 +398,7 @@ print(f'Result: y = {linear_layer.bias.item()} + {linear_layer.weight[:, 0].item
```
### [PyTorch:优化](#id20)](docs / modern-java-zh /
### [PyTorch:优化](#id20)
到目前为止,我们已经通过使用`torch.no_grad()`手动更改持有可学习参数的张量来更新模型的权重。 对于像随机梯度下降这样的简单优化算法来说,这并不是一个巨大的负担,但是在实践中,我们经常使用更复杂的优化器(例如 AdaGrad,RMSProp,Adam 等)来训练神经网络。
......@@ -461,7 +461,7 @@ print(f'Result: y = {linear_layer.bias.item()} + {linear_layer.weight[:, 0].item
```
### [PyTorch:自定义 nn 模块](#id21)](docs / modern-java-zh /
### [PyTorch:自定义 nn 模块](#id21)
有时,您将需要指定比一系列现有模块更复杂的模型。 对于这些情况,您可以通过子类化`nn.Module`并定义一个`forward`来定义自己的模块,该模块使用其他模块或在 Tensors 上的其他自动转换操作来接收输入 Tensors 并生成输出 Tensors。
......@@ -528,7 +528,7 @@ print(f'Result: {model.string()}')
```
### [PyTorch:控制流+权重共享](#id22)](docs / modern-java-zh /
### [PyTorch:控制流+权重共享](#id22)
作为动态图和权重共享的示例,我们实现了一个非常奇怪的模型:一个三阶多项式,在每个前向传递中选择 3 到 5 之间的一个随机数,并使用该阶数,多次重复使用相同的权重进行计算 第四和第五阶。
......@@ -606,11 +606,11 @@ print(f'Result: {model.string()}')
```
## [示例](#id23)](docs / modern-java-zh /
## [示例](#id23)
您可以在此处浏览以上示例。
### [张量](#id24)](docs / modern-java-zh /
### [张量](#id24)
![../_img/sphx_glr_polynomial_numpy_thumb.png](img/ea0bddb69dfbd67215b823007544ab8f.png)
......@@ -620,7 +620,7 @@ print(f'Result: {model.string()}')
[PyTorch:张量](examples_tensor/polynomial_tensor.html#sphx-glr-beginner-examples-tensor-polynomial-tensor-py)
### [Autograd](#id25) ](docs / modern-java-zh /
### [Autograd](#id25)
![../_img/sphx_glr_polynomial_autograd_thumb.png](img/ffad28c33f8a48d06521421f1aa441ed.png)
......@@ -630,7 +630,7 @@ print(f'Result: {model.string()}')
[PyTorch:定义新的 autograd 函数](examples_autograd/polynomial_custom_function.html#sphx-glr-beginner-examples-autograd-polynomial-custom-function-py)
### [<cite>nn</cite> 模块](#id26)](docs / modern-java-zh /
### [<cite>nn</cite> 模块](#id26)
![../_img/sphx_glr_polynomial_nn_thumb.png](img/335fb81e535f98bfda7cbdb3e50d8832.png)
......
......@@ -96,7 +96,7 @@ tensor(0) tensor(9)
```
## 从零开始的神经网络(no torch.nn)](docs / modern-java-zh /
## 从零开始的神经网络(no torch.nn)
首先,我们仅使用 PyTorch 张量操作创建模型。 我们假设您已经熟悉神经网络的基础知识。 (如果不是,则可以在 [course.fast.ai](https://course.fast.ai) 中学习它们)。
......@@ -254,7 +254,7 @@ tensor(0.0811, grad_fn=<NegBackward>) tensor(1.)
```
## 使用 torch.nn.functional](docs / modern-java-zh /
## 使用 torch.nn.functional
现在,我们将重构代码,使其执行与以前相同的操作,只是我们将开始利用 PyTorch 的`nn`类使其更加简洁和灵活。 从这里开始的每一步,我们都应该使代码中的一个或多个:更短,更易理解和/或更灵活。
......@@ -286,7 +286,7 @@ tensor(0.0811, grad_fn=<NllLossBackward>) tensor(1.)
```
## 使用 nn.Module 重构](docs / modern-java-zh /
## 使用 nn.Module 重构
接下来,我们将使用`nn.Module``nn.Parameter`进行更清晰,更简洁的训练循环。 我们将`nn.Module`子类化(它本身是一个类并且能够跟踪状态)。 在这种情况下,我们要创建一个类,该类包含前进步骤的权重,偏见和方法。 `nn.Module`具有许多我们将要使用的属性和方法(例如`.parameters()``.zero_grad()`)。
......@@ -386,7 +386,7 @@ tensor(0.0808, grad_fn=<NllLossBackward>)
```
## 使用 nn.Linear 重构](docs / modern-java-zh /
## 使用 nn.Linear 重构
我们继续重构我们的代码。 代替手动定义和初始化`self.weights``self.bias`并计算`xb  @ self.weights + self.bias`,我们将对线性层使用 Pytorch 类 [nn.Linear](https://pytorch.org/docs/stable/nn.html#linear-layers) ,这将为我们完成所有工作。 Pytorch 具有许多类型的预定义层,可以大大简化我们的代码,并且通常也可以使其速度更快。
......@@ -495,7 +495,7 @@ tensor(0.0823, grad_fn=<NllLossBackward>)
```
## 使用数据集重构](docs / modern-java-zh /
## 使用数据集重构
PyTorch 有一个抽象的 Dataset 类。 数据集可以是具有`__len__`函数(由 Python 的标准`len`函数调用)和具有`__getitem__`函数作为对其进行索引的一种方法。 [本教程](https://pytorch.org/tutorials/beginner/data_loading_tutorial.html)演示了一个不错的示例,该示例创建一个自定义`FacialLandmarkDataset`类作为`Dataset`的子类。
......@@ -552,7 +552,7 @@ tensor(0.0819, grad_fn=<NllLossBackward>)
```
## 使用 DataLoader 重构](docs / modern-java-zh /
## 使用 DataLoader 重构
Pytorch 的`DataLoader`负责批量管理。 您可以从任何`Dataset`创建一个`DataLoader``DataLoader`使迭代迭代变得更加容易。 不必使用`train_ds[i*bs : i*bs+bs]`,DataLoader 会自动为我们提供每个小批量。
......@@ -656,7 +656,7 @@ for epoch in range(epochs):
```
## 创建 fit()和 get_data()](docs / modern-java-zh /
## 创建 fit()和 get_data()
现在,我们将自己进行一些重构。 由于我们经历了两次相似的过程来计算训练集和验证集的损失,因此我们将其设为自己的函数`loss_batch`,该函数可计算一批损失。
......@@ -727,7 +727,7 @@ fit(epochs, model, loss_func, opt, train_dl, valid_dl)
您可以使用这些基本的 3 行代码来训练各种各样的模型。 让我们看看是否可以使用它们来训练卷积神经网络(CNN)!
## 切换到 CNN](docs / modern-java-zh /
## 切换到 CNN
现在,我们将构建具有三个卷积层的神经网络。 由于上一节中的任何功能都不假设任何有关模型形式的信息,因此我们将能够使用它们来训练 CNN,而无需进行任何修改。
......@@ -771,7 +771,7 @@ fit(epochs, model, loss_func, opt, train_dl, valid_dl)
```
## nn.Sequential](docs / modern-java-zh /
## nn.Sequential
`torch.nn`还有另一个方便的类,可以用来简化我们的代码: [Sequential](https://pytorch.org/docs/stable/nn.html#torch.nn.Sequential)`Sequential`对象以顺序方式运行其中包含的每个模块。 这是编写神经网络的一种简单方法。
......@@ -820,7 +820,7 @@ fit(epochs, model, loss_func, opt, train_dl, valid_dl)
```
## 包装 DataLoader](docs / modern-java-zh /
## 包装 DataLoader
Our CNN is fairly concise, but it only works with MNIST, because:
......@@ -887,7 +887,7 @@ fit(epochs, model, loss_func, opt, train_dl, valid_dl)
```
## 使用您的 GPU](docs / modern-java-zh /
## 使用您的 GPU
如果您足够幸运地能够使用具有 CUDA 功能的 GPU(可以从大多数云提供商处以每小时$ 0.50 的价格租用一个),则可以使用它来加速代码。 首先检查您的 GPU 是否在 Pytorch 中正常工作:
......@@ -946,7 +946,7 @@ fit(epochs, model, loss_func, opt, train_dl, valid_dl)
```
## 结束语](docs / modern-java-zh /
## 结束语
现在,我们有了一个通用的数据管道和训练循环,您可以将其用于使用 Pytorch 训练许多类型的模型。 要了解现在可以轻松进行模型训练,请查看 <cite>mnist_sample</cite> 示例笔记本。
......
......@@ -107,7 +107,7 @@ optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
```
## 1\. TensorBoard 设置](docs / modern-java-zh /
## 1\. TensorBoard 设置
现在,我们将设置 TensorBoard,从`torch.utils`导入`tensorboard`并定义`SummaryWriter`,这是将信息写入 TensorBoard 的关键对象。
......@@ -121,7 +121,7 @@ writer = SummaryWriter('runs/fashion_mnist_experiment_1')
请注意,仅此行会创建一个`runs/fashion_mnist_experiment_1`文件夹。
## 2.写入 TensorBoard](docs / modern-java-zh /
## 2.写入 TensorBoard
现在,使用 [make_grid](https://pytorch.org/docs/stable/torchvision/utils.html#torchvision.utils.make_grid) 将图像写入到 TensorBoard 中,具体来说就是网格。
......@@ -154,7 +154,7 @@ tensorboard --logdir=runs
现在您知道如何使用 TensorBoard 了! 但是,此示例可以在 Jupyter 笔记本中完成-TensorBoard 真正擅长的地方是创建交互式可视化。 接下来,我们将介绍其中之一,并在本教程结束时介绍更多内容。
## 3.使用 TensorBoard 检查模型](docs / modern-java-zh /
## 3.使用 TensorBoard 检查模型
TensorBoard 的优势之一是其可视化复杂模型结构的能力。 让我们可视化我们构建的模型。
......@@ -172,7 +172,7 @@ writer.close()
TensorBoard 具有非常方便的功能,可在低维空间中可视化高维数据,例如图像数据。 接下来我们将介绍这一点。
## 4.在 TensorBoard 中添加“投影仪”](docs / modern-java-zh /
## 4.在 TensorBoard 中添加“投影仪”
我们可以通过 [add_embedding](https://pytorch.org/docs/stable/tensorboard.html#torch.utils.tensorboard.writer.SummaryWriter.add_embedding) 方法可视化高维数据的低维表示
......@@ -208,7 +208,7 @@ writer.close()
现在我们已经彻底检查了我们的数据,让我们展示了 TensorBoard 如何从训练开始就可以使跟踪模型的训练和评估更加清晰。
## 5.使用 TensorBoard 跟踪模型训练](docs / modern-java-zh /
## 5.使用 TensorBoard 跟踪模型训练
在前面的示例中,我们仅*每 2000 次迭代打印*该模型的运行损失。 现在,我们将运行损失记录到 TensorBoard 中,并通过`plot_classes_preds`函数查看模型所做的预测。
......@@ -299,7 +299,7 @@ print('Finished Training')
在之前的教程中,我们研究了模型训练后的每类准确率; 在这里,我们将使用 TensorBoard 绘制每个类别的精确调用曲线(在中为好解释[)。](https://www.scikit-yb.org/en/latest/api/classifier/prcurve.html)
## 6.使用 TensorBoard 评估经过训练的模型](docs / modern-java-zh /
## 6.使用 TensorBoard 评估经过训练的模型
```py
# 1\. gets the probability predictions in a test_size x num_classes Tensor
......
......@@ -8,7 +8,7 @@
在本教程中,我们将在 [Penn-Fudan 数据库中对行人检测和分割](https://www.cis.upenn.edu/~jshi/ped_html/)进行预训练的 [Mask R-CNN](https://arxiv.org/abs/1703.06870) 模型进行微调。 它包含 170 个图像和 345 个行人实例,我们将用它来说明如何在 torchvision 中使用新功能,以便在自定义数据集上训练实例细分模型。
## 定义数据集](docs / modern-java-zh /
## 定义数据集
用于训练对象检测,实例细分和人员关键点检测的参考脚本可轻松支持添加新的自定义数据集。 数据集应继承自标准`torch.utils.data.Dataset`类,并实现`__len__``__getitem__`
......@@ -36,7 +36,7 @@
此外,如果要在训练过程中使用宽高比分组(以便每个批量仅包含具有相似长宽比的图像),则建议您还实施`get_height_and_width`方法,该方法返回图像的高度和宽度。 如果未提供此方法,我们将通过`__getitem__`查询数据集的所有元素,这会将图像加载到内存中,并且比提供自定义方法慢。
### 为 PennFudan 编写自定义数据集](docs / modern-java-zh /
### 为 PennFudan 编写自定义数据集
让我们为 PennFudan 数据集编写一个数据集。 在[下载并解压缩 zip 文件](https://www.cis.upenn.edu/~jshi/ped_html/PennFudanPed.zip)之后,我们具有以下文件夹结构:
......@@ -216,7 +216,7 @@ model = FasterRCNN(backbone,
```
### PennFudan 数据集的实例细分模型](docs / modern-java-zh /
### PennFudan 数据集的实例细分模型
在我们的案例中,由于我们的数据集非常小,我们希望从预训练模型中进行微调,因此我们将遵循方法 1。
......@@ -250,7 +250,7 @@ def get_model_instance_segmentation(num_classes):
就是这样,这将使`model`随时可以在您的自定义数据集上进行训练和评估。
## 将所有内容放在一起](docs / modern-java-zh /
## 将所有内容放在一起
`references/detection/`中,我们提供了许多帮助程序功能来简化训练和评估检测模型。 在这里,我们将使用`references/detection/engine.py``references/detection/utils.py``references/detection/transforms.py`。 只需将它们复制到您的文件夹中,然后在此处使用它们即可。
......@@ -268,7 +268,7 @@ def get_transform(train):
```
## 测试`forward()`方法(可选)](docs / modern-java-zh /
## 测试`forward()`方法(可选)
在遍历数据集之前,最好先查看模型在训练过程中的期望值以及对样本数据的推断时间。
......@@ -444,7 +444,7 @@ IoU metric: segm
结果看起来还不错!
## 总结](docs / modern-java-zh /
## 总结
在本教程中,您学习了如何在自定义数据集上为实例细分模型创建自己的训练管道。 为此,您编写了一个`torch.utils.data.Dataset`类,该类返回图像以及地面真相框和分段蒙版。 您还利用了在 COCO train2017 上预先训练的 Mask R-CNN 模型,以便对该新数据集执行迁移学习。
......
......@@ -37,7 +37,7 @@ plt.ion() # interactive mode
```
## 加载数据](docs / modern-java-zh /
## 加载数据
我们将使用 torchvision 和 torch.utils.data 包来加载数据。
......@@ -110,7 +110,7 @@ imshow(out, title=[class_names[x] for x in classes])
![../_img/sphx_glr_transfer_learning_tutorial_001.png](img/be538c850b645a41a7a77ff388954e14.png)
## 训练模型](docs / modern-java-zh /
## 训练模型
现在,让我们编写一个通用功能来训练模型。 在这里,我们将说明:
......@@ -190,7 +190,7 @@ def train_model(model, criterion, optimizer, scheduler, num_epochs=25):
```
### 可视化模型预测](docs / modern-java-zh /
### 可视化模型预测
通用功能,显示一些图像的预测
......@@ -223,7 +223,7 @@ def visualize_model(model, num_images=6):
```
## 微调 convnet](docs / modern-java-zh /
## 微调 convnet
加载预训练的模型并重置最终的全连接层。
......@@ -246,7 +246,7 @@ exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)
```
### 训练和评估](docs / modern-java-zh /
### 训练和评估
在 CPU 上大约需要 15-25 分钟。 但是在 GPU 上,此过程不到一分钟。
......@@ -396,7 +396,7 @@ visualize_model(model_ft)
![../_img/sphx_glr_transfer_learning_tutorial_002.png](img/ebec7787362bc53fe2289e5740da5756.png)
## ConvNet 作为固定特征提取器](docs / modern-java-zh /
## ConvNet 作为固定特征提取器
在这里,我们需要冻结除最后一层之外的所有网络。 我们需要设置`requires_grad == False`冻结参数,以便不在`backward()`中计算梯度。
......@@ -424,7 +424,7 @@ exp_lr_scheduler = lr_scheduler.StepLR(optimizer_conv, step_size=7, gamma=0.1)
```
### 训练和评估](docs / modern-java-zh /
### 训练和评估
与以前的方案相比,在 CPU 上将花费大约一半的时间。 这是可以预期的,因为不需要为大多数网络计算梯度。 但是,确实需要计算正向。
......
......@@ -8,13 +8,13 @@
本教程将提高您对 ML 模型的安全漏洞的认识,并深入了解对抗性机器学习的热门话题。 您可能会惊讶地发现,在图像*上添加无法察觉的扰动会导致*导致完全不同的模型性能。 鉴于这是一个教程,我们将通过图像分类器上的示例来探讨该主题。 具体而言,我们将使用最流行的一种攻击方法,即快速梯度符号攻击(FGSM)来欺骗 MNIST 分类器。
## 威胁模型](docs / modern-java-zh /
## 威胁模型
就上下文而言,有多种类型的对抗性攻击,每种攻击者的目标和假设都不同。 但是,总的来说,总体目标是向输入数据添加最少的扰动,以引起所需的错误分类。 攻击者的知识有几种假设,其中两种是:**白盒****黑盒***白盒*攻击假定攻击者具有完全的知识并可以访问模型,包括架构,输入,输出和权重。 *黑匣子*攻击假定攻击者只能访问模型的输入和输出,并且对底层架构或权重一无所知。 目标也有几种类型,包括**错误分类****源/目标错误分类***错误分类的目标是*,这意味着对手只希望输出分类错误,而不在乎新分类是什么。 *源/目标错误分类*意味着对手想要更改最初属于特定源类别的图像,以便将其分类为特定目标类别。
在这种情况下,FGSM 攻击是*白盒*攻击,目标是*错误分类*。 有了这些背景信息,我们现在可以详细讨论攻击了。
## 快速梯度符号攻击](docs / modern-java-zh /
## 快速梯度符号攻击
迄今为止,最早的也是最流行的对抗性攻击之一被称为*快速梯度符号攻击(FGSM)*,由 Goodfellow 等描述。 等 [中的解释和利用对抗性示例](https://arxiv.org/abs/1412.6572)。 攻击非常强大,而且直观。 它旨在利用神经网络的学习方式*梯度*来攻击神经网络。 这个想法很简单,不是通过基于反向传播的梯度来调整权重来使损失最小化,攻击*会基于相同的反向传播的梯度来调整输入数据以使损失*最大化。 换句话说,攻击使用损失损失的斜率而不输入数据,然后调整输入数据以使损失最大化。
......@@ -38,7 +38,7 @@ import matplotlib.pyplot as plt
```
## 实现](docs / modern-java-zh /
## 实现
在本节中,我们将讨论本教程的输入参数,定义受到攻击的模型,然后编写攻击代码并运行一些测试。
......@@ -57,7 +57,7 @@ use_cuda=True
```
### 受到攻击的模型](docs / modern-java-zh /
### 受到攻击的模型
如前所述,受到攻击的模型与 [pytorch / examples / mnist](https://github.com/pytorch/examples/tree/master/mnist) 中的 MNIST 模型相同。 您可以训练并保存自己的 MNIST 模型,也可以下载并使用提供的模型。 这里的*网络*定义和测试数据加载器已从 MNIST 示例中复制而来。 本部分的目的是定义模型和数据加载器,然后初始化模型并加载预训练的权重。
......@@ -120,7 +120,7 @@ CUDA Available: True
```
### FGSM 攻击](docs / modern-java-zh /
### FGSM 攻击
现在,我们可以通过干扰原始输入来定义创建对抗示例的函数。 `fgsm_attack`函数接受三个输入,*图像*是原始的干净图像(\(x \)), *epsilon* 是像素级扰动量(\(\ epsilon \) ), *data_grad* 是输入图像损失的梯度(\(\ nabla_ {x} J(\ mathbf {\ theta},\ mathbf {x},y)\))。 该函数然后创建扰动图像为
......@@ -142,7 +142,7 @@ def fgsm_attack(image, epsilon, data_grad):
```
### 测试功能](docs / modern-java-zh /
### 测试功能
最后,本教程的主要结果来自`test`函数。 每次调用此测试功能都会在 MNIST 测试集上执行完整的测试步骤,并报告最终精度。 但是,请注意,此功能还需要 *epsilon* 输入。 这是因为`test`函数报告实力为\(\ epsilon \)的来自对手的攻击模型的准确率。 更具体地说,对于测试集中的每个样本,函数都会计算输入数据(\(data \ _grad \))的损耗梯度,并使用`fgsm_attack`创建一个扰动图像(\(perturbed \ _data \)) ,然后检查受干扰的示例是否具有对抗性。 除了测试模型的准确率外,该功能还保存并返回了一些成功的对抗示例,以供以后可视化。
......@@ -211,7 +211,7 @@ def test( model, device, test_loader, epsilon ):
```
### 运行攻击](docs / modern-java-zh /
### 运行攻击
实现的最后一部分是实际运行攻击。 在这里,我们为 *epsilons* 输入中的每个 epsilon 值运行完整的测试步骤。 对于每个 epsilon,我们还保存最终精度,并在接下来的部分中绘制一些成功的对抗示例。 请注意,随着ε值的增加,打印的精度如何降低。 另外,请注意\(\ epsilon = 0 \)表示原始测试准确率,没有受到攻击。
......@@ -240,9 +240,9 @@ Epsilon: 0.3 Test Accuracy = 869 / 10000 = 0.0869
```
## 结果](docs / modern-java-zh /
## 结果
### 准确率与 Epsilon](docs / modern-java-zh /
### 准确率与 Epsilon
第一个结果是精度与ε曲线的关系。 如前所述,随着ε的增加,我们预计测试精度会降低。 这是因为更大的ε意味着我们朝着将损失最大化的方向迈出了更大的一步。 请注意,即使 epsilon 值是线性间隔的,曲线中的趋势也不是线性的。 例如,\(\ epsilon = 0.05 \)处的精度仅比\(\ epsilon = 0 \)低约 4%,但\(\ epsilon = 0.2 \)处的精度比\(\ epsilon = 0.15 \)。 另外,请注意,模型的准确率在\(\ epsilon = 0.25 \)和\(\ epsilon = 0.3 \)之间达到 10 类分类器的随机准确率。
......@@ -260,7 +260,7 @@ plt.show()
![../_img/sphx_glr_fgsm_tutorial_001.png](img/7633144b009ac008488a6bd051f404c9.png)
### 对抗示例样本](docs / modern-java-zh /
### 对抗示例样本
还记得没有免费午餐的想法吗? 在这种情况下,随着ε的增加,测试精度降低**,但**的扰动变得更容易察觉。 实际上,在攻击者必须考虑的准确率下降和可感知性之间要进行权衡。 在这里,我们展示了每个 epsilon 值下成功对抗示例的一些示例。 绘图的每一行显示不同的ε值。 第一行是\(\ epsilon = 0 \)示例,这些示例表示没有干扰的原始“干净”图像。 每张图片的标题均显示“原始分类->对抗分类”。 注意,扰动在\(\ epsilon = 0.15 \)处开始变得明显,而在\(\ epsilon = 0.3 \)处则非常明显。 但是,在所有情况下,尽管增加了噪音,人类仍然能够识别正确的类别。
......@@ -286,7 +286,7 @@ plt.show()
![../_img/sphx_glr_fgsm_tutorial_002.png](img/049e79b05a41598709a2aeef166e4a2a.png)
## 接下来要去哪里?](docs / modern-java-zh /
## 接下来要去哪里?
希望本教程对对抗性机器学习主题有所了解。 从这里可以找到许多潜在的方向。 这种攻击代表了对抗性攻击研究的最开始,并且由于随后有许多关于如何攻击和防御来自对手的 ML 模型的想法。 实际上,在 NIPS 2017 上有一个对抗性的攻击和防御竞赛,并且本文描述了该竞赛中使用的许多方法:[对抗性的攻击与防御竞赛](https://arxiv.org/pdf/1804.00097.pdf)。 国防方面的工作还引发了使机器学习模型总体上更健壮*健壮*的想法,以适应自然扰动和对抗性输入。
......
......@@ -4,13 +4,13 @@
**作者**[Nathan Inkawhich](https://github.com/inkawhich)
## 简介](docs / modern-java-zh /
## 简介
本教程将通过一个示例对 DCGAN 进行介绍。 在向其展示许多真实名人的照片后,我们将训练一个生成对抗网络(GAN)来产生新名人。 此处的大多数代码来自 [pytorch / examples](https://github.com/pytorch/examples) 中的 dcgan 实现,并且本文档将对该实现进行详尽的解释,并阐明此模型的工作方式和原因。 但请放心,不需要 GAN 的先验知识,但这可能需要新手花一些时间来推理幕后实际发生的事情。 同样,为了节省时间,拥有一两个 GPU 也将有所帮助。 让我们从头开始。
## 生成对抗网络](docs / modern-java-zh /
## 生成对抗网络
### 什么是 GAN?](docs / modern-java-zh /
### 什么是 GAN?
GAN 是用于教授 DL 模型以捕获训练数据分布的框架,因此我们可以从同一分布中生成新数据。 GAN 由 Ian Goodfellow 于 2014 年发明,并在论文[生成对抗网络](https://papers.nips.cc/paper/5423-generative-adversarial-nets.pdf)中首次进行了描述。 它们由两个不同的模型组成:*生成器**鉴别器*。 生成器的工作是生成看起来像训练图像的“假”图像。 鉴别器的工作是查看图像并从生成器输出它是真实的训练图像还是伪图像。 在训练过程中,生成器不断尝试通过生成越来越好的伪造品而使鉴别器的性能超过智者,而鉴别器正在努力成为更好的侦探并正确地对真实和伪造图像进行分类。 博弈的平衡点是当生成器生成的伪造品看起来像直接来自训练数据时,而鉴别器则总是猜测生成器输出是真实还是伪造品的 50% 置信度。
......@@ -24,7 +24,7 @@ GAN 是用于教授 DL 模型以捕获训练数据分布的框架,因此我们
从理论上讲,此极小极大游戏的解决方案是\(p_g = p_ {data} \),判别器会随机猜测输入是真实的还是假的。 但是,GAN 的收敛理论仍在积极研究中,实际上,模型并不总是能达到这一目的。
### 什么是 DCGAN?](docs / modern-java-zh /
### 什么是 DCGAN?
DCGAN 是上述 GAN 的直接扩展,不同之处在于,DCGAN 分别在鉴别器和生成器中分别使用卷积和卷积转置层。 它最早由 Radford 等人描述。 等 在论文[中使用深度卷积生成对抗网络](https://arxiv.org/pdf/1511.06434.pdf)进行无监督表示学习。 鉴别器由分层的[卷积](https://pytorch.org/docs/stable/nn.html#torch.nn.Conv2d)层,[批量规范](https://pytorch.org/docs/stable/nn.html#torch.nn.BatchNorm2d)层和 [LeakyReLU](https://pytorch.org/docs/stable/nn.html#torch.nn.LeakyReLU) 激活组成。 输入是 3x64x64 的输入图像,输出是输入来自真实数据分布的标量概率。 生成器由[卷积转置](https://pytorch.org/docs/stable/nn.html#torch.nn.ConvTranspose2d)层,批量规范层和 [ReLU](https://pytorch.org/docs/stable/nn.html#relu) 激活组成。 输入是从标准正态分布中提取的潜向量\(z \),输出是 3x64x64 RGB 图像。 跨步的转置层使潜向量可以转换为具有与图像相同形状的体积。 在本文中,作者还提供了一些有关如何设置优化器,如何计算损失函数以及如何初始化模型权重的提示,所有这些都将在接下来的部分中进行解释。
......@@ -121,7 +121,7 @@ ngpu = 1
```
## 数据](docs / modern-java-zh /
## 数据
在本教程中,我们将使用 [Celeb-A Faces 数据集](http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html),该数据集可在链接的站点或 [Google 云端硬盘](https://drive.google.com/drive/folders/0B7EVK8r0v71pTUZsaXdaSnZBZzg)中下载。 数据集将下载为名为 *img_align_celeba.zip* 的文件。 下载完成后,创建一个名为 *celeba* 的目录,并将 zip 文件解压缩到该目录中。 然后,将此笔记本的*数据根*输入设置为刚创建的 *celeba* 目录。 结果目录结构应为:
......@@ -166,11 +166,11 @@ plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding=
![../_img/sphx_glr_dcgan_faces_tutorial_001.png](img/04fb3a8ed8e63cf7cffb5f29224decca.png)
## 实现](docs / modern-java-zh /
## 实现
设置好输入参数并准备好数据集后,我们现在可以进入实现了。 我们将从 weigth 初始化策略开始,然后详细讨论生成器,鉴别器,损失函数和训练循环。
### 权重初始化](docs / modern-java-zh /
### 权重初始化
在 DCGAN 论文中,作者指定所有模型权重均应从均值= 0,stdev = 0.02 的正态分布中随机初始化。 `weights_init`函数采用已初始化的模型作为输入,并重新初始化所有卷积,卷积转置和批量归一化层以满足此标准。 初始化后立即将此功能应用于模型。
......@@ -186,7 +186,7 @@ def weights_init(m):
```
### 生成器](docs / modern-java-zh /
### 生成器
生成器\(G \)用于将潜在空间向量(\(z \))映射到数据空间。 由于我们的数据是图像,因此将\(z \)转换为数据空间意味着最终创建与训练图像大小相同的 RGB 图像(即 3x64x64)。 在实践中,这是通过一系列跨步的二维卷积转置层来完成的,每个层都与 2d 批量规范层和 relu 激活配对。 生成器的输出通过 tanh 函数馈送,以使其返回到输入数据范围\([-1,1] \)。 值得注意的是,在卷积转置层之后存在批量规范函数,因为这是 DCGAN 论文的关键贡献。 这些层有助于训练过程中的梯度流动。 DCGAN 纸生成的图像如下所示。
......@@ -272,7 +272,7 @@ Generator(
```
### 判别器](docs / modern-java-zh /
### 判别器
如前所述,鉴别符\(D \)是一个二进制分类网络,将图像作为输入并输出标量概率,即输入图像是真实的(与假的相对)。 在这里,\(D \)拍摄 3x64x64 的输入图像,通过一系列的 Conv2d,BatchNorm2d 和 LeakyReLU 层对其进行处理,然后通过 Sigmoid 激活函数输出最终概率。 如果需要解决此问题,可以用更多层扩展此架构,但是使用跨步卷积,BatchNorm 和 LeakyReLU 仍然很重要。 DCGAN 论文提到,使用跨步卷积而不是通过池化来进行下采样是一个好习惯,因为它可以让网络学习自己的池化功能。 批量规范和泄漏 relu 函数还可以促进健康的梯度流,这对于\(G \)\(D \)的学习过程都是至关重要的。
......@@ -351,7 +351,7 @@ Discriminator(
```
### 损失函数和优化器](docs / modern-java-zh /
### 损失函数和优化器
使用\(D \)\(G \)设置,我们可以指定它们如何通过损失函数和优化器学习。 我们将使用在 PyTorch 中定义的二进制交叉熵损失( [BCELoss](https://pytorch.org/docs/stable/nn.html#torch.nn.BCELoss) )函数:
......@@ -652,7 +652,7 @@ Starting Training Loop...
```
## 结果](docs / modern-java-zh /
## 结果
最后,让我们看看我们是如何做到的。 在这里,我们将看三个不同的结果。 首先,我们将了解 D 和 G 的损失在训练过程中如何变化。 其次,我们将在每个时期将 G 的输出显示为 fixed_noise 批量。 第三,我们将查看一批真实数据以及来自 G 的一批伪数据。
......
......@@ -18,7 +18,7 @@ import matplotlib.pyplot as plt
```
## 打开文件](docs / modern-java-zh /
## 打开文件
`torchaudio`还支持以 wav 和 mp3 格式加载声音文件。 我们将波形称为原始音频信号。
......@@ -54,7 +54,7 @@ Sample rate of waveform: 44100
`torchaudio`还使 JIT 编译对于功能是可选的,并在可能的情况下使用`nn.Module`
## 转换](docs / modern-java-zh /
## 转换
`torchaudio`支持不断增长的[转换](https://pytorch.org/audio/stable/transforms.html)列表。
......@@ -357,7 +357,7 @@ Mean of highpass_waveform: 1.8138147234170177e-11
```
## 从 Kaldi 迁移到 torchaudio](docs / modern-java-zh /
## 从 Kaldi 迁移到 torchaudio
用户可能熟悉 [Kaldi](http://github.com/kaldi-asr/kaldi) (一种用于语音识别的工具包)。 `torchaudio`提供与`torchaudio.kaldi_io`中的兼容性。 实际上,它可以通过以下方式从 kaldi scp 或 ark 文件或流中读取:
......@@ -445,7 +445,7 @@ Shape of mfcc: torch.Size([1383, 13])
```
## 可用数据集](docs / modern-java-zh /
## 可用数据集
如果您不想创建自己的数据集来训练模型,则`torchaudio`提供了统一的数据集界面。 该接口支持将文件延迟加载到内存,下载和提取函数以及数据集以构建模型。
......@@ -486,7 +486,7 @@ Labels: [0, 0, 1, 0, 0, 0, 1, 0]
现在,每当您从数据集中请求声音文件时,仅当您请求声音文件时,它才会加载到内存中。 这意味着,数据集仅加载所需的项目并将其保留在内存中,并保存在内存中。
## 结论](docs / modern-java-zh /
## 结论
我们使用示例原始音频信号或波形来说明如何使用`torchaudio`打开音频文件,以及如何对该波形进行预处理,变换和应用功能。 我们还演示了如何使用熟悉的 Kaldi 函数以及如何利用内置数据集构建模型。 鉴于`torchaudio`是基于 PyTorch 构建的,因此这些技术可以在利用 GPU 的同时,用作语音识别等更高级音频应用的构建块。
......
......@@ -40,7 +40,7 @@ print(device)
```
## 导入数据集](docs / modern-java-zh /
## 导入数据集
我们使用 torchaudio 下载并表示数据集。 在这里,我们使用 [SpeechCommands](https://arxiv.org/abs/1804.03209) ,它是由不同人员说出的 35 个命令的数据集。 数据集`SPEECHCOMMANDS`是数据集的`torch.utils.data.Dataset`版本。 在此数据集中,所有音频文件的长度约为 1 秒(因此约为 16000 个时间帧)。
......@@ -115,7 +115,7 @@ ipd.Audio(waveform_last.numpy(), rate=sample_rate)
```
## 格式化数据](docs / modern-java-zh /
## 格式化数据
这是将转换应用于数据的好地方。 对于波形,我们对音频进行下采样以进行更快的处理,而不会损失太多的分类能力。
......@@ -208,7 +208,7 @@ test_loader = torch.utils.data.DataLoader(
```
## 定义网络](docs / modern-java-zh /
## 定义网络
在本教程中,我们将使用卷积神经网络来处理原始音频数据。 通常,更高级的转换将应用于音频数据,但是 CNN 可以用于准确处理原始数据。 具体架构是根据本文[中描述的 M5 网络架构建模的。 模型处理原始音频数据的一个重要方面是其第一层过滤器的接收范围。 我们模型的第一个滤波器长度为 80,因此在处理以 8kHz 采样的音频时,接收场约为 10ms(而在 4kHz 时约为 20ms)。 此大小类似于语音处理应用程序,该应用程序通常使用 20ms 到 40ms 的接收域。](https://arxiv.org/pdf/1610.00087.pdf)
......@@ -268,7 +268,7 @@ scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.1) # red
```
## 训练和测试网络](docs / modern-java-zh /
## 训练和测试网络
现在,我们定义一个训练函数,它将训练数据输入模型中,并执行向后传递和优化步骤。 对于训练,我们将使用的损失是负对数可能性。 然后,在每个时期之后将对网络进行测试,以查看训练期间准确率如何变化。
......@@ -443,7 +443,7 @@ ipd.Audio(waveform.numpy(), rate=sample_rate)
```
## 结论](docs / modern-java-zh /
## 结论
在本教程中,我们使用了 torchaudio 来加载数据集并对信号进行重新采样。 然后,我们定义了经过训练的神经网络,以识别给定命令。 还有其他数据预处理方法,例如找到梅尔频率倒谱系数(MFCC),可以减小数据集的大小。 此变换也可以在[torchaudio]中作为`torchaudio.transforms.MFCC`使用。
......
......@@ -76,7 +76,7 @@ class PositionalEncoding(nn.Module):
```
## 加载和批量数据](docs / modern-java-zh /
## 加载和批量数据
本教程使用`torchtext`生成 Wikitext-2 数据集。 vocab 对象是基于训练数据集构建的,用于将标记数字化为张量。 从顺序数据开始,`batchify()`函数将数据集排列为列,以修剪掉数据分成大小为`batch_size`的批量后剩余的所有令牌。 例如,以字母为序列(总长度为 26)并且批大小为 4,我们将字母分为 4 个长度为 6 的序列:
......@@ -126,7 +126,7 @@ test_data = batchify(test_data, eval_batch_size)
```
### 生成输入序列和目标序列的函数](docs / modern-java-zh /
### 生成输入序列和目标序列的函数
`get_batch()`功能为转换器模型生成输入和目标序列。 它将源数据细分为长度为`bptt`的块。 对于语言建模任务,模型需要以下单词作为`Target`。 例如,如果`bptt`值为 2,则`i` = 0 时,我们将获得以下两个变量:
......@@ -144,7 +144,7 @@ def get_batch(source, i):
```
## 启动实例](docs / modern-java-zh /
## 启动实例
使用下面的超参数建立模型。 vocab 的大小等于 vocab 对象的长度。
......@@ -159,7 +159,7 @@ model = TransformerModel(ntokens, emsize, nhead, nhid, nlayers, dropout).to(devi
```
## 运行模型](docs / modern-java-zh /
## 运行模型
[CrossEntropyLoss](https://pytorch.org/docs/master/nn.html?highlight=crossentropyloss#torch.nn.CrossEntropyLoss) 用于跟踪损失, [SGD](https://pytorch.org/docs/master/optim.html?highlight=sgd#torch.optim.SGD) 实现随机梯度下降方法作为优化器。 初始学习率设置为 5.0。 [StepLR](https://pytorch.org/docs/master/optim.html?highlight=steplr#torch.optim.lr_scheduler.StepLR) 用于通过历时调整学习速率。 在训练期间,我们使用 [nn.utils.clip_grad_norm_](https://pytorch.org/docs/master/nn.html?highlight=nn%20utils%20clip_grad_norm#torch.nn.utils.clip_grad_norm_) 函数将所有梯度缩放在一起,以防止爆炸。
......
......@@ -37,7 +37,7 @@ $ python predict.py Schmidhuber
* [循环神经网络的不合理效果](https://karpathy.github.io/2015/05/21/rnn-effectiveness/)显示了许多现实生活中的例子
* [了解 LSTM 网络](https://colah.github.io/posts/2015-08-Understanding-LSTMs/)特别是关于 LSTM 的,但一般来说也有关 RNN 的
## 准备数据](docs / modern-java-zh /
## 准备数据
注意
......@@ -114,7 +114,7 @@ print(category_lines['Italian'][:5])
```
### 将名称转换为张量](docs / modern-java-zh /
### 将名称转换为张量
现在我们已经组织了所有名称,我们需要将它们转换为张量以使用它们。
......@@ -162,7 +162,7 @@ torch.Size([5, 1, 57])
```
## 创建网络](docs / modern-java-zh /
## 创建网络
在进行自动分级之前,在 Torch 中创建一个循环神经网络涉及在多个时间步长上克隆层的参数。 层保留了隐藏状态和渐变,这些层现在完全由图形本身处理。 这意味着您可以非常“纯”的方式将 RNN 用作常规前馈层。
......@@ -232,7 +232,7 @@ tensor([[-2.8934, -2.7991, -2.8549, -2.8915, -2.9122, -2.9010, -2.8979, -2.8875,
## 训练] [docs / modern-java-zh /
### 准备训练](docs / modern-java-zh /
### 准备训练
在接受训练之前,我们应该做一些辅助功能。 首先是解释网络的输出,我们知道这是每个类别的可能性。 我们可以使用`Tensor.topk`获得最大值的索引:
......@@ -290,7 +290,7 @@ category = German / line = Adam
```
### 训练网络](docs / modern-java-zh /
### 训练网络
现在,训练该网络所需要做的就是向它展示大量示例,进行猜测,并告诉它是否错误。
......@@ -415,7 +415,7 @@ plt.plot(all_losses)
![../_img/sphx_glr_char_rnn_classification_tutorial_001.png](img/cc57a36a43d450df4bfc1d1d1b1ce274.png)
## 评估结果](docs / modern-java-zh /
## 评估结果
为了查看网络在不同类别上的表现如何,我们将创建一个混淆矩阵,为每种实际语言(行)指示网络猜测(列)哪种语言。 为了计算混淆矩阵,使用`evaluate()`通过网络运行一堆样本,该样本等于`train()`减去反向传播器。
......@@ -534,7 +534,7 @@ $ python predict.py Hazaki
运行`server.py`并访问`http://localhost:5533/Yourname`以获取预测的 JSON 输出。
## 练习](docs / modern-java-zh /
## 练习
* 尝试使用 line-> category 的其他数据集,例如:
* 任何单词->语言
......
......@@ -47,7 +47,7 @@ Iun
我还建议上一教程[从头开始进行 NLP:使用字符级 RNN 对名称进行分类](char_rnn_classification_tutorial.html)
## 准备数据](docs / modern-java-zh /
## 准备数据
注意
......@@ -110,7 +110,7 @@ O'Neal
```
## 创建网络](docs / modern-java-zh /
## 创建网络
该网络使用最后一个教程的 RNN 扩展了[,并为类别张量附加了一个参数,该参数与其他张量串联在一起。 类别张量就像字母输入一样是一个单向向量。](#Creating-the-Network)
......@@ -152,7 +152,7 @@ class RNN(nn.Module):
## 训练] [docs / modern-java-zh /
### 准备训练](docs / modern-java-zh /
### 准备训练
首先,helper 函数获取随机对(类别,行):
......@@ -216,7 +216,7 @@ def randomTrainingExample():
```
### 训练网络](docs / modern-java-zh /
### 训练网络
与仅使用最后一个输出的分类相反,我们在每个步骤进行预测,因此在每个步骤都计算损失。
......@@ -316,7 +316,7 @@ for iter in range(1, n_iters + 1):
```
### 绘制损失图](docs / modern-java-zh /
### 绘制损失图
绘制 all_loss 的历史损失可显示网络学习情况:
......@@ -331,7 +331,7 @@ plt.plot(all_losses)
![../_img/sphx_glr_char_rnn_generation_tutorial_001.png](img/5ad82e2b23a82287af2caa2fe4b316b3.png)
## 网络抽样](docs / modern-java-zh /
## 网络抽样
为了示例,我们给网络一个字母,询问下一个字母是什么,将其作为下一个字母输入,并重复直到 EOS 令牌。
......@@ -406,7 +406,7 @@ Iun
```
## 练习](docs / modern-java-zh /
## 练习
* 尝试使用类别->行的其他数据集,例如:
* 虚构系列->角色名称
......
......@@ -81,7 +81,7 @@ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
```
## 加载数据文件](docs / modern-java-zh /
## 加载数据文件
该项目的数据是成千上万的英语到法语翻译对的集合。
......@@ -242,7 +242,7 @@ eng 2803
```
## Seq2Seq 模型](docs / modern-java-zh /
## Seq2Seq 模型
循环神经网络(RNN)是在序列上运行并将其自身的输出用作后续步骤的输入的网络。
......@@ -256,7 +256,7 @@ eng 2803
使用 seq2seq 模型,编码器创建单个向量,在理想情况下,该向量将输入序列的“含义”编码为单个向量—在句子的 N 维空间中的单个点。
### 编码器](docs / modern-java-zh /
### 编码器
seq2seq 网络的编码器是 RNN,它为输入句子中的每个单词输出一些值。 对于每个输入字,编码器输出一个向量和一个隐藏状态,并将隐藏状态用于下一个输入字。
......@@ -282,11 +282,11 @@ class EncoderRNN(nn.Module):
```
### 解码器](docs / modern-java-zh /
### 解码器
解码器是另一个 RNN,它采用编码器输出向量并输出单词序列来创建翻译。
#### 简单解码器](docs / modern-java-zh /
#### 简单解码器
在最简单的 seq2seq 解码器中,我们仅使用编码器的最后一个输出。 该最后的输出有时称为*上下文向量*,因为它从整个序列中编码上下文。 该上下文向量用作解码器的初始隐藏状态。
......@@ -319,7 +319,7 @@ class DecoderRNN(nn.Module):
我鼓励您训练并观察该模型的结果,但是为了节省空间,我们将直接努力并引入注意力机制。
#### 注意解码器](docs / modern-java-zh /
#### 注意解码器
如果仅上下文向量在编码器和解码器之间传递,则该单个向量承担对整个句子进行编码的负担。
......@@ -376,7 +376,7 @@ class AttnDecoderRNN(nn.Module):
## 训练] [docs / modern-java-zh /
### 准备训练数据](docs / modern-java-zh /
### 准备训练数据
为了训练,对于每一对,我们将需要一个输入张量(输入句子中单词的索引)和目标张量(目标句子中单词的索引)。 创建这些向量时,我们会将 EOS 令牌附加到两个序列上。
......@@ -396,7 +396,7 @@ def tensorsFromPair(pair):
```
### 训练模型](docs / modern-java-zh /
### 训练模型
为了训练,我们通过编码器运行输入语句,并跟踪每个输出和最新的隐藏状态。 然后,为解码器提供`<SOS>`令牌作为其第一个输入,为编码器提供最后的隐藏状态作为其第一个隐藏状态。
......@@ -529,7 +529,7 @@ def trainIters(encoder, decoder, n_iters, print_every=1000, plot_every=100, lear
```
### 绘图结果](docs / modern-java-zh /
### 绘图结果
使用训练时保存的损失值数组`plot_losses`,使用 matplotlib 进行绘制。
......@@ -549,7 +549,7 @@ def showPlot(points):
```
## 评估](docs / modern-java-zh /
## 评估
评估与训练基本相同,但是没有目标,因此我们只需将解码器的预测反馈给每一步。 每当它预测一个单词时,我们都会将其添加到输出字符串中,如果它预测到 EOS 令牌,我们将在此处停止。 我们还将存储解码器的注意输出,以供以后显示。
......@@ -606,7 +606,7 @@ def evaluateRandomly(encoder, decoder, n=10):
```
## 训练和评估](docs / modern-java-zh /
## 训练和评估
有了所有这些辅助功能(看起来像是额外的工作,但它使运行多个实验更加容易),我们实际上可以初始化网络并开始训练。
......@@ -771,7 +771,7 @@ output = he s a talented young writer . <EOS>
```
## 练习](docs / modern-java-zh /
## 练习
* 尝试使用其他数据集
* 另一对语言
......
......@@ -18,7 +18,7 @@
此示例显示了如何使用这些`TextClassification`数据集之一训练用于分类的监督学习算法。
## 使用 ngrams 加载数据](docs / modern-java-zh /
## 使用 ngrams 加载数据
一袋 ngrams 功能用于捕获有关本地单词顺序的一些部分信息。 在实践中,应用二元语法或三元语法作为单词组比仅一个单词提供更多的好处。 一个例子:
......@@ -76,7 +76,7 @@ class TextSentiment(nn.Module):
```
## 启动实例](docs / modern-java-zh /
## 启动实例
AG_NEWS 数据集具有四个标签,因此类别数是四个。
......@@ -98,7 +98,7 @@ model = TextSentiment(VOCAB_SIZE, EMBED_DIM, NUN_CLASS).to(device)
```
## 用于生成批量的函数](docs / modern-java-zh /
## 用于生成批量的函数
由于文本条目的长度不同,因此使用自定义函数 generate_batch()生成数据批和偏移量。 该功能被传递到`torch.utils.data.DataLoader`中的`collate_fn``collate_fn`的输入是张量列表,其大小为 batch_size,`collate_fn`函数将它们打包成一个小批量。 请注意此处,并确保将`collate_fn`声明为顶级 def。 这样可以确保该功能在每个工作程序中均可用。
......@@ -119,7 +119,7 @@ def generate_batch(batch):
```
## 定义函数以训练模型并评估结果。](docs / modern-java-zh /
## 定义函数以训练模型并评估结果。
建议 PyTorch 用户使用 [torch.utils.data.DataLoader](https://pytorch.org/docs/stable/data.html?highlight=dataloader#torch.utils.data.DataLoader) ,它可以轻松地并行加载数据(教程为[,此处为](https://pytorch.org/tutorials/beginner/data_loading_tutorial.html))。 我们在此处使用`DataLoader`加载 AG_NEWS 数据集,并将其发送到模型以进行训练/验证。
......
......@@ -8,7 +8,7 @@
在本教程结束时,您将可以将句子预处理为张量以用于 NLP 建模,并可以使用 [torch.utils.data.DataLoader](https://pytorch.org/docs/stable/data.html?highlight=dataloader#torch.utils.data.DataLoader) 来训练和验证模型。
## 数据处理](docs / modern-java-zh /
## 数据处理
`torchtext`具有实用程序,可用于创建可以轻松迭代的数据集,以创建语言翻译模型。 在此示例中,我们展示了如何对原始文本句子进行标记,构建词汇表以及将标记数字化为张量。
......@@ -71,7 +71,7 @@ test_data = data_process(test_filepaths)
```
## `DataLoader`](docs / modern-java-zh /
## `DataLoader`
我们将使用的最后`torch`个特定功能是`DataLoader`,它易于使用,因为它将数据作为第一个参数。 具体来说,正如文档所说:`DataLoader`结合了一个数据集和一个采样器,并在给定的数据集上提供了可迭代的。 `DataLoader`支持地图样式和可迭代样式的数据集,具有单进程或多进程加载,自定义加载顺序以及可选的自动批量(归类)和内存固定。
......@@ -108,7 +108,7 @@ test_iter = DataLoader(test_data, batch_size=BATCH_SIZE,
```
## 定义我们的`nn.Module`和`Optimizer`](docs / modern-java-zh /
## 定义我们的`nn.Module`和`Optimizer`
这大部分是从`torchtext`角度出发的:构建了数据集并定义了迭代器,本教程的其余部分仅将模型定义为`nn.Module`以及`Optimizer`,然后对其进行训练。
......@@ -487,7 +487,7 @@ Epoch: 10 | Time: 0m 59s
```
## 后续步骤](docs / modern-java-zh /
## 后续步骤
* 在上查看使用`torchtext` [的 Ben Trevett 其余教程。](https://github.com/bentrevett/)
* 敬请关注使用其他`torchtext`功能以及`nn.Transformer`通过下一个单词预测进行语言建模的教程!
......
......@@ -60,7 +60,7 @@ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
```
## 重播内存](docs / modern-java-zh /
## 重播内存
我们将使用经验重播记忆来训练我们的 DQN。 它存储代理观察到的转换,使我们以后可以重用此数据。 通过从中随机抽样,可以构建批量的转换相关。 已经表明,这极大地稳定和改善了 DQN 训练程序。
......@@ -121,7 +121,7 @@ Q 学习的主要思想是,如果我们有一个函数\(Q ^ *:State \ time
\[\mathcal{L} = \frac{1}{|B|}\sum_{(s, a, s', r) \ \in \ B} \mathcal{L}(\delta)\] \[\begin{split}\text{where} \quad \mathcal{L}(\delta) = \begin{cases} \frac{1}{2}{\delta^2} & \text{for } |\delta| \le 1, \\ |\delta| - \frac{1}{2} & \text{otherwise.} \end{cases}\end{split}\]
### Q-network](docs / modern-java-zh /
### Q-network
我们的模型将是一个卷积神经网络,该卷积神经网络将吸收当前屏幕补丁和先前屏幕补丁之间的差异。 它有两个输出,分别代表\(Q(s,\ mathrm {left})\)\(Q(s,\ mathrm {right})\)(其中\(s \)是网络的输入)。 实际上,网络正在尝试预测在给定当前输入的情况下执行每个操作的*预期收益*
......@@ -156,7 +156,7 @@ class DQN(nn.Module):
```
### 输入提取](docs / modern-java-zh /
### 输入提取
以下代码是用于从环境中提取和处理渲染图像的实用程序。 它使用`torchvision`程序包,可轻松组成图像变换。 一旦运行单元,它将显示它提取的示例补丁。
......@@ -206,7 +206,7 @@ plt.show()
## 训练] [docs / modern-java-zh /
### 超参数和实用程序](docs / modern-java-zh /
### 超参数和实用程序
该单元实例化我们的模型及其优化器,并定义一些实用程序:
......@@ -278,7 +278,7 @@ def plot_durations():
```
### 训练循环](docs / modern-java-zh /
### 训练循环
最后,是训练模型的代码。
......
......@@ -35,7 +35,7 @@ import gym_super_mario_bros
```
## RL 定义](docs / modern-java-zh /
## RL 定义
**环境**代理与之交互并学习的世界。
......@@ -47,9 +47,9 @@ import gym_super_mario_bros
**最佳操作值函数** \(Q ^ *(s,a)\):如果您以状态\(s \)开始,执行任意操作\(a \)并给出期望的回报, 然后针对每个未来时间步长采取使收益最大化的行动。 可以说\(Q \)代表状态中动作的“质量”。 我们尝试近似该函数。
## 环境](docs / modern-java-zh /
## 环境
### 初始化环境](docs / modern-java-zh /
### 初始化环境
在马里奥,环境由试管,蘑菇和其他成分组成。
......@@ -80,7 +80,7 @@ print(f"{next_state.shape},\n {reward},\n {done},\n {info}")
```
### 预处理环境](docs / modern-java-zh /
### 预处理环境
环境数据在`next_state`中返回给代理。 正如您在上面看到的,每个状态都由`[3, 240, 256]`大小数组表示。 通常,这比我们的代理商需要的信息更多; 例如,马里奥(Mario)的举动并不取决于管道或天空的颜色!
......@@ -194,7 +194,7 @@ class Mario:
在以下各节中,我们将填充 Mario 的参数并定义其功能。
### 法案](docs / modern-java-zh /
### 法案
对于任何给定状态,代理都可以选择执行最佳操作(**利用**)或执行随机操作(**探索**)。
......@@ -255,7 +255,7 @@ class Mario:
```
### 缓存和调用](docs / modern-java-zh /
### 缓存和调用
这两个功能是 Mario 的“记忆”过程。
......@@ -309,13 +309,13 @@ class Mario(Mario): # subclassing for continuity
```
### 学习](docs / modern-java-zh /
### 学习
Mario 在后台使用 [DDQN 算法](https://arxiv.org/pdf/1509.06461)。 DDQN 使用两个 ConvNet-\(Q_ {online} \)\(Q_ {target} \)-独立地逼近最佳作用值函数。
在我们的实现中,我们在\(Q_ {online} \)\(Q_ {target} \)之间共享特征生成器`features`,但是为每个特征维护单独的 FC 分类器。 \(\ theta_ {target} \)\(Q_ {target} \)的参数)被冻结,以防止反向传播进行更新。 而是定期与\(\ theta_ {online} \)同步(稍后会对此进行详细介绍)。
#### 神经网络](docs / modern-java-zh /
#### 神经网络
```py
class MarioNet(nn.Module):
......@@ -359,7 +359,7 @@ class MarioNet(nn.Module):
```
#### TD 估算和 TD 目标](docs / modern-java-zh /
#### TD 估算和 TD 目标
学习涉及两个值:
......@@ -398,7 +398,7 @@ class Mario(Mario):
```
#### 更新模型](docs / modern-java-zh /
#### 更新模型
当 Mario 从其重播缓冲区中采样输入时,我们计算\(TD_t \)\(TD_e \)并向后传播该损耗\(Q_ {online} \)以更新其参数\(\ theta_ {online} \)\ \ alpha \)是传递给`optimizer`的学习率`lr`
......@@ -427,7 +427,7 @@ class Mario(Mario):
```
#### 保存检查点](docs / modern-java-zh /
#### 保存检查点
```py
class Mario(Mario):
......@@ -482,7 +482,7 @@ class Mario(Mario):
```
### 日志记录](docs / modern-java-zh /
### 日志记录
```py
import numpy as np
......@@ -592,7 +592,7 @@ class MetricLogger:
```
## 开始吧!](docs / modern-java-zh /
## 开始吧!
在此示例中,我们运行了 10 个情节的训练循环,但是对于 Mario 要真正了解他的世界的方式,我们建议运行至少 40,000 个情节的循环!
......@@ -656,7 +656,7 @@ Episode 0 - Step 40 - Epsilon 0.9999900000487484 - Mean Reward 231.0 - Mean Leng
```
## 结论](docs / modern-java-zh /
## 结论
在本教程中,我们看到了如何使用 PyTorch 来训练玩游戏的 AI。 您可以使用相同的方法训练 AI 在 [OpenAI 体育馆](https://gym.openai.com/)上玩任何游戏。 希望您喜欢本教程,请随时通过[和我们的 github](https://github.com/yuansongFeng/MadMario/) 与我们联系!
......
......@@ -15,7 +15,7 @@
> * 如果您已经熟悉 TorchScript,则可以直接进入我们的 [C ++加载 TorchScript 模型](https://pytorch.org/tutorials/advanced/cpp_export.html) 教程。
> * 如果您首先需要在 TorchScript 上进行复习,请查看我们的 [TorchScript 入门](https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html) 教程。
## API 定义](docs / modern-java-zh /
## API 定义
我们将首先定义 API 端点,请求和响应类型。 我们的 API 端点将位于`/predict`,它通过包含图片的`file`参数接受 HTTP POST 请求。 响应将是包含预测的 JSON 响应:
......@@ -33,7 +33,7 @@ $ pip install Flask==1.0.3 torchvision-0.3.0
```
## 简单的 Web 服务器](docs / modern-java-zh /
## 简单的 Web 服务器
以下是一个简单的网络服务器,摘自 Flask 的文档
......@@ -81,7 +81,7 @@ def predict():
在下一部分中,我们将重点介绍编写推理代码。 这将涉及两部分,第一部分是准备图像,以便可以将其馈送到 DenseNet;第二部分,我们将编写代码以从模型中获取实际的预测。
### 准备图像](docs / modern-java-zh /
### 准备图像
DenseNet 模型要求图像为尺寸为 224 x 224 的 3 通道 RGB 图像。我们还将使用所需的均值和标准差值对图像张量进行归一化。 您可以在上阅读有关它的更多信息。
......@@ -144,7 +144,7 @@ tensor([[[[ 0.4508, 0.4166, 0.3994, ..., -1.3473, -1.3302, -1.3473],
```
### 预测](docs / modern-java-zh /
### 预测
现在将使用预训练的 DenseNet 121 模型来预测图像类别。 我们将使用`torchvision`库中的一个,加载模型并进行推断。 在此示例中,我们将使用预训练模型,但您可以对自己的模型使用相同的方法。 在此[教程](../beginner/saving_loading_models.html)中查看有关加载模型的更多信息。
......@@ -209,7 +209,7 @@ with open("../_static/img/sample_file.jpeg", 'rb') as f:
您是否注意到`model`变量不属于`get_prediction`方法? 还是为什么模型是全局变量? 就内存和计算而言,加载模型可能是一项昂贵的操作。 如果我们以`get_prediction`方法加载模型,则每次调用该方法时都会不必要地加载该模型。 由于我们正在构建一个 Web 服务器,因此每秒可能有成千上万的请求,因此我们不应该浪费时间为每个推断重复加载模型。 因此,我们仅将模型加载到内存中一次。 在生产系统中,必须高效使用计算以能够大规模处理请求,因此通常应在处理请求之前加载模型。
## 将模型集成到我们的 API 服务器中](docs / modern-java-zh /
## 将模型集成到我们的 API 服务器中
在最后一部分中,我们将模型添加到 Flask API 服务器中。 由于我们的 API 服务器应该获取图像文件,因此我们将更新`predict`方法以从请求中读取文件:
......@@ -298,7 +298,7 @@ resp = requests.post("http://localhost:5000/predict",
```
## 后续步骤](docs / modern-java-zh /
## 后续步骤
我们编写的服务器非常琐碎,可能无法完成生产应用程序所需的一切。 因此,您可以采取一些措施来改善它:
......
......@@ -36,7 +36,7 @@ print(torch.__version__)
```
## PyTorch 模型创作基础](docs / modern-java-zh /
## PyTorch 模型创作基础
首先定义一个简单的`Module``Module`是 PyTorch 中组成的基本单位。 它包含:
......@@ -178,7 +178,7 @@ MyCell(
简而言之,即使 PyTorch 具有灵活和动态的特性,TorchScript 也提供了捕获模型定义的工具。 让我们开始研究所谓的**跟踪**
### 跟踪`Modules`](docs / modern-java-zh /
### 跟踪`Modules`
```py
class MyCell(torch.nn.Module):
......@@ -430,7 +430,7 @@ def forward(self,
这样,当情况需要它们时,可以使用脚本和跟踪并将它们一起使用。
## 保存和加载模型](docs / modern-java-zh /
## 保存和加载模型
我们提供 API,以存档格式将 TorchScript 模块保存到磁盘或从磁盘加载 TorchScript 模块。 这种格式包括代码,参数,属性和调试信息,这意味着归档文件是模型的独立表示形式,可以在完全独立的过程中加载。 让我们保存并加载包装好的 RNN 模块:
......@@ -467,7 +467,7 @@ def forward(self,
如您所见,序列化保留了模块层次结构和我们一直在研究的代码。 也可以将模型加载到[中,例如](https://pytorch.org/tutorials/advanced/cpp_export.html)[到 C ++](https://pytorch.org/tutorials/advanced/cpp_export.html) 中,以实现不依赖 Python 的执行。
### 进一步阅读](docs / modern-java-zh /
### 进一步阅读
我们已经完成了教程! 有关更多涉及的演示,[请查看 NeurIPS 演示以使用 TorchScript 转换机器翻译模型](https://colab.research.google.com/drive/1HiICg6jRkBnr5hvK2-VnMi88Vi9pUzEJ)
......
......@@ -4,7 +4,7 @@
顾名思义,PyTorch 的主要接口是 Python 编程语言。 尽管 Python 是许多需要动态性和易于迭代的场景的合适且首选的语言,但是在同样许多情况下,Python 的这些属性恰恰是不利的。 后者经常应用的一种环境是*生产* –低延迟和严格部署要求的土地。 对于生产场景,即使仅将 C ++绑定到 Java,Rust 或 Go 之类的另一种语言中,它也是经常选择的语言。 以下各段将概述 PyTorch 提供的从现有 Python 模型到序列化表示形式的路径,该序列化表示形式可以*加载**完全由 C ++执行,*不依赖于 Python。
## 第 1 步:将 PyTorch 模型转换为 Torch 脚本](docs / modern-java-zh /
## 第 1 步:将 PyTorch 模型转换为 Torch 脚本
PyTorch 模型从 Python 到 C ++的旅程由 [Torch 脚本](https://pytorch.org/docs/master/jit.html)启用,它是 PyTorch 模型的一种表示形式,可以由 Torch 脚本编译器理解,编译和序列化。 如果您从使用香草“渴望” API 编写的现有 PyTorch 模型开始,则必须首先将模型转换为 Torch 脚本。 在最常见的情况下(如下所述),只需很少的努力。 如果您已经有了 Torch 脚本模块,则可以跳到本教程的下一部分。
......@@ -14,7 +14,7 @@ PyTorch 模型从 Python 到 C ++的旅程由 [Torch 脚本](https://pytorch.org
您可以在官方[火炬脚本参考](https://pytorch.org/docs/master/jit.html)中找到这两种方法的完整文档以及使用方法的进一步指导。
### 通过跟踪转换为 Torch 脚本](docs / modern-java-zh /
### 通过跟踪转换为 Torch 脚本
要将 PyTorch 模型通过跟踪转换为 Torch 脚本,必须将模型的实例以及示例输入传递给`torch.jit.trace`函数。 这将产生一个`torch.jit.ScriptModule`对象,并将模型评估的轨迹嵌入到模块的`forward`方法中:
......@@ -42,7 +42,7 @@ Out[2]: tensor([-0.2698, -0.0381, 0.4023, -0.3010, -0.0448], grad_fn=<SliceBack
```
### 通过注释转换为 Torch 脚本](docs / modern-java-zh /
### 通过注释转换为 Torch 脚本
在某些情况下,例如,如果模型采用特定形式的控制流,则可能需要直接在 Torch 脚本中编写模型并相应地注释模型。 例如,假设您具有以下香草 Pytorch 模型:
......@@ -87,7 +87,7 @@ sm = torch.jit.script(my_module)
`my_module`是已准备好进行序列化的`ScriptModule`的实例。
## 第 2 步:将脚本模块序列化为文件](docs / modern-java-zh /
## 第 2 步:将脚本模块序列化为文件
跟踪或注释 PyTorch 模型后,一旦有了`ScriptModule`,就可以将其序列化为文件了。 稍后,您将能够使用 C ++从此文件加载模块并执行它,而无需依赖 Python。 假设我们要序列化先前在跟踪示例中显示的`ResNet18`模型。 要执行此序列化,只需在模块上调用[保存](https://pytorch.org/docs/master/jit.html#torch.jit.ScriptModule.save)并为其传递文件名:
......@@ -98,11 +98,11 @@ traced_script_module.save("traced_resnet_model.pt")
这将在您的工作目录中生成一个`traced_resnet_model.pt`文件。 如果您还想序列化`my_module`,请致电`my_module.save("my_module_model.pt")`。我们现在已经正式离开 Python 领域,并准备跨入 C ++领域。
## 第 3 步:在 C ++中加载脚本模块](docs / modern-java-zh /
## 第 3 步:在 C ++中加载脚本模块
要在 C ++中加载序列化的 PyTorch 模型,您的应用程序必须依赖于 PyTorch C ++ API –也称为 *LibTorch* 。 LibTorch 发行版包含共享库,头文件和 CMake 构建配置文件的集合。 虽然 CMake 不是依赖 LibTorch 的要求,但它是推荐的方法,将来会得到很好的支持。 对于本教程,我们将使用 CMake 和 LibTorch 构建一个最小的 C ++应用程序,该应用程序简单地加载并执行序列化的 PyTorch 模型。
### 最小的 C ++应用程序](docs / modern-java-zh /
### 最小的 C ++应用程序
让我们从讨论加载模块的代码开始。 以下将已经做:
......@@ -135,7 +135,7 @@ int main(int argc, const char* argv[]) {
`<torch/script.h>`标头包含了运行示例所需的 LibTorch 库中的所有相关包含。 我们的应用程序接受序列化的 PyTorch `ScriptModule`的文件路径作为其唯一的命令行参数,然后继续使用`torch::jit::load()`函数对该模块进行反序列化,该函数将该文件路径作为输入。 作为回报,我们收到一个`torch::jit::script::Module`对象。 我们将稍后讨论如何执行它。
### 取决于 LibTorch 和构建应用程序](docs / modern-java-zh /
### 取决于 LibTorch 和构建应用程序
假设我们将以上代码存储到名为`example-app.cpp`的文件中。 最小的`CMakeLists.txt`构建起来看起来很简单:
......@@ -237,7 +237,7 @@ ok
```
## 步骤 4:在 C ++中执行脚本模块](docs / modern-java-zh /
## 步骤 4:在 C ++中执行脚本模块
在用 C ++成功加载序列化的`ResNet18`之后,我们现在离执行它仅几行代码了! 让我们将这些行添加到 C ++应用程序的`main()`函数中:
......@@ -285,7 +285,7 @@ tensor([-0.2698, -0.0381, 0.4023, -0.3010, -0.0448], grad_fn=<SliceBackward>)
要将模型移至 GPU 内存,可以编写`model.to(at::kCUDA);`。 通过调用`tensor.to(at::kCUDA)`来确保模型的输入也位于 CUDA 内存中,这将在 CUDA 内存中返回新的张量。
## 第 5 步:获得帮助并探索 API](docs / modern-java-zh /
## 第 5 步:获得帮助并探索 API
本教程有望使您对 PyTorch 模型从 Python 到 C ++的路径有一个大致的了解。 利用本教程中介绍的概念,您应该能够从原始的“急切的” PyTorch 模型,到 Python 中的已编译`ScriptModule`,再到磁盘上的序列化文件,以及–结束循环–到可执行文件`script::Module`在 C ++中。
......
......@@ -143,7 +143,7 @@ print("Exported model has been tested with ONNXRuntime, and the result looks goo
我们应该看到 PyTorch 和 ONNX Runtime 的输出在数值上与给定的精度匹配(rtol = 1e-03 和 atol = 1e-05)。 附带说明一下,如果它们不匹配,则说明 ONNX 导出器中存在问题,因此请与我们联系。
## 使用 ONNX Runtime 在图像上运行模型](docs / modern-java-zh /
## 使用 ONNX Runtime 在图像上运行模型
到目前为止,我们已经从 PyTorch 导出了一个模型,并演示了如何使用虚拟张量作为输入在 ONNX Runtime 中加载和运行该模型。
......
......@@ -19,7 +19,7 @@
PyTorch 中的命名张量受 [Sasha Rush](https://tech.cornell.edu/people/alexander-rush/) 的启发并与之合作。 Sasha 在他的 [2019 年 1 月博客文章](http://nlp.seas.harvard.edu/NamedTensor)中提出了最初的想法和概念证明。
## 基础知识:命名维度](docs / modern-java-zh /
## 基础知识:命名维度
PyTorch 现在允许张量具有命名尺寸; 工厂函数采用新的<cite>名称</cite>参数,该参数将名称与每个维度相关联。 这适用于大多数工厂功能,例如
......@@ -173,7 +173,7 @@ print(named_imgs.abs().names)
```
### 访问器和约简](docs / modern-java-zh /
### 访问器和约简
可以使用尺寸名称来引用尺寸而不是位置尺寸。 这些操作还传播名称。 索引(基本索引和高级索引)尚未实施,但仍在规划中。 使用上面的`named_imgs`张量,我们可以执行以下操作:
......@@ -262,7 +262,7 @@ Error when attempting to broadcast dims ['N', 'C', 'H', 'W'] and dims ['N']: dim
如果没有`names`,则`per_batch_scale`张量与`imgs`的最后一个尺寸对齐,这不是我们想要的。 我们确实想通过将`per_batch_scale``imgs`的批量尺寸对齐来执行操作。 有关如何按名称对齐张量的信息,请参见新的“按名称显式广播”功能,如下所述。
#### 矩阵乘法](docs / modern-java-zh /
#### 矩阵乘法
`torch.mm(A, B)``A`的第二个暗角和`B`的第一个暗角之间执行点积,返回具有`A`的第一个暗角和`B`的第二个暗角的张量。 (其他 matmul 函数,例如`torch.matmul``torch.mv``torch.dot`的行为类似)。
......@@ -313,7 +313,7 @@ assert torch.allclose(named_result.rename(None), correct_result)
```
### 新行为:按名称展平和展平尺寸](docs / modern-java-zh /
### 新行为:按名称展平和展平尺寸
一种常见的操作是展平和展平尺寸。 现在,用户可以使用`view``reshape``flatten`来执行此操作; 用例包括将批量尺寸展平以将张量发送到必须采用一定数量尺寸的输入的运算符(即 conv2d 采用 4D 输入)。
......@@ -338,7 +338,7 @@ print(imgs.names)
```
### Autograd 支持](docs / modern-java-zh /
### Autograd 支持
Autograd 当前会忽略所有张量上的名称,只是将它们视为常规张量。 梯度计算是正确的,但是我们失去了名称赋予我们的安全性。 在路线图上引入名称以自动分级的处理。
......@@ -557,7 +557,7 @@ def ignore():
在这里,与(II)一样,`align_to``flatten`在语义上比`view``transpose`更有意义(尽管更冗长)。
### 运行示例](docs / modern-java-zh /
### 运行示例
```py
n, t, d, h = 7, 5, 2 * 3, 3
......@@ -594,7 +594,7 @@ print(output.names)
```
### 结论](docs / modern-java-zh /
### 结论
感谢您的阅读! 命名张量仍在发展中。 如果您有反馈和/或改进建议,请通过创建[问题](https://github.com/pytorch/pytorch/issues)来通知我们。
......
......@@ -4,7 +4,7 @@
**作者**[Vitaly Fedyunin](https://github.com/VitalyFedyunin)
## 什么是最后渠道](docs / modern-java-zh /
## 什么是最后渠道
Channels Last 内存格式是在保留内存尺寸的顺序中对 NCHW 张量进行排序的另一种方法。 通道最后一个张量的排序方式使通道成为最密集的维度(又称为每像素存储图像)。
......@@ -26,7 +26,7 @@ N, C, H, W = 10, 3, 32, 32
```
## 内存格式 API](docs / modern-java-zh /
## 内存格式 API
这是在连续和通道最后存储格式之间转换张量的方法。
......@@ -290,7 +290,7 @@ True
以下型号列表完全支持 Channels Last,并在 Volta 设备上显示了 8% -35% 的 perf 增益:`alexnet``mnasnet0_5``mnasnet0_75``mnasnet1_0``mnasnet1_3``mobilenet_v2``resnet101``resnet152``resnet18``resnet34``resnet50``resnext50_32x4d``shufflenet_v2_x0_5``shufflenet_v2_x1_0``shufflenet_v2_x1_5``shufflenet_v2_x2_0``squeezenet1_0``squeezenet1_1``vgg11``vgg11_bn``vgg13``vgg13_bn``vgg16``vgg16_bn``vgg19``vgg19_bn``wide_resnet101_2``wide_resnet50_2`
## 转换现有模型](docs / modern-java-zh /
## 转换现有模型
Channels Last 支持不受现有模型的限制,因为只要输入格式正确,任何模型都可以转换为 Channels Last,并通过图形传播格式。
......@@ -397,7 +397,7 @@ for (m, attrs) in old_attrs.items():
```
## 工作要做](docs / modern-java-zh /
## 工作要做
仍有许多事情要做,例如:
......
......@@ -18,7 +18,7 @@ PyTorch C ++前端是 PyTorch 机器学习框架的纯 C ++接口。 虽然 PyTo
有关 PyTorch C ++生态系统的文档,请访问[这个页面](https://pytorch.org/cppdocs)。 您可以在此处找到高级描述以及 API 级文档。
## 动机](docs / modern-java-zh /
## 动机
在我们开始 GAN 和 MNIST 数字的激动人心的旅程之前,让我们退后一步,讨论为什么要使用 C ++前端而不是 Python。 我们(PyTorch 团队)创建了 C ++前端,以便能够在无法使用 Python 或根本不适合该工具的环境中进行研究。 此类环境的示例包括:
......@@ -32,7 +32,7 @@ C ++前端无意与 Python 前端竞争。 它是对它的补充。 我们知道
C ++前端试图提供一个与 Python 前端尽可能接近的 API。 如果您对 Python 前端有丰富的经验,并且问过自己“我如何使用 C ++前端 X?”,请像在 Python 中那样编写代码,而且大多数情况下,相同的函数和方法也可以在 C ++中使用 就像在 Python 中一样(只记得用双冒号替换点)。
## 编写基本应用程序](docs / modern-java-zh /
## 编写基本应用程序
首先,编写一个最小的 C ++应用程序,以验证我们是否在同一页面上了解我们的设置和构建环境。 首先,您需要获取 *LibTorch* 发行版的副本-我们现成的 zip 归档文件,其中打包了使用 C ++前端所需的所有相关标头,库和 CMake 构建文件。 LibTorch 发行版可从 [PyTorch 网站](https://pytorch.org/get-started/locally/)下载,适用于 Linux,MacOS 和 Windows。 本教程的其余部分将假定基本的 Ubuntu Linux 环境,但是您也可以在 MacOS 或 Windows 上随意进行操作。
......@@ -147,7 +147,7 @@ root@fa350df05ecf:/home/build# ./dcgan
在我看来就像一个身份矩阵!
## 定义神经网络模型](docs / modern-java-zh /
## 定义神经网络模型
现在我们已经配置了基本环境,我们可以深入研究本教程中更有趣的部分。 首先,我们将讨论如何在 C ++前端中定义模块并与之交互。 我们将从基本的小规模示例模块开始,然后使用 C ++前端提供的广泛的内置模块库来实现全面的 GAN。
......@@ -159,7 +159,7 @@ root@fa350df05ecf:/home/build# ./dcgan
参数,缓冲区和子模块必须显式注册。 注册后,可以使用`parameters()``buffers()`之类的方法来检索整个(嵌套)模块层次结构中所有参数的容器。 类似地,使用`to(...)`之类的方法,例如 `to(torch::kCUDA)`将所有参数和缓冲区从 CPU 移到 CUDA 内存,在整个模块层次结构上工作。
#### 定义模块和注册参数](docs / modern-java-zh /
#### 定义模块和注册参数
为了将这些词写成代码,让我们考虑一下用 Python 界面编写的简单模块:
......@@ -197,7 +197,7 @@ struct Net : torch::nn::Module {
就像在 Python 中一样,我们定义了一个名为`Net`的类(为简单起见,这里是`struct`而不是`class`),然后从模块基类派生它。 在构造函数内部,我们使用`torch::randn`创建张量,就像在 Python 中使用`torch.randn`一样。 一个有趣的区别是我们如何注册参数。 在 Python 中,我们用`torch.nn.Parameter`类包装了张量,而在 C ++中,我们不得不通过`register_parameter`方法传递张量。 这样做的原因是 Python API 可以检测到属性为`torch.nn.Parameter`类型并自动注册此类张量。 在 C ++中,反射非常受限制,因此提供了一种更传统(且不太神奇)的方法。
#### 注册子模块并遍历模块层次结构](docs / modern-java-zh /
#### 注册子模块并遍历模块层次结构
以相同的方式我们可以注册参数,我们也可以注册子模块。 在 Python 中,将子模块分配为模块的属性时,会自动检测并注册这些子模块:
......@@ -354,7 +354,7 @@ root@fa350df05ecf:/home/build# ./dcgan
```
#### 模块所有权](docs / modern-java-zh /
#### 模块所有权
至此,我们知道了如何使用 C ++定义模块,注册参数,注册子模块,通过`parameters()`之类的方法遍历模块层次结构并最终运行模块的`forward()`方法。 尽管在 C ++ API 中还有很多方法,类和主题需要使用,但我将为您提供完整菜单的[文档](https://pytorch.org/cppdocs/api/namespace_torch__nn.html)。 我们将在稍后实现 DCGAN 模型和端到端训练管道的过程中,涉及更多概念。 在我们这样做之前,让我简要介绍一下 C ++前端为`torch::nn::Module`的子类提供的*所有权模型*
......@@ -465,7 +465,7 @@ struct Net : torch::nn::Module {
结论:您应该使用哪种所有权模型–哪种语义? C ++前端的 API 最能支持模块所有者提供的所有权模型。 这种机制的唯一缺点是在模块声明下方多了一行样板。 也就是说,最简单的模型仍然是 C ++模块简介中显示的值语义模型。 对于小的,简单的脚本,您也可以摆脱它。 但是,由于技术原因,您迟早会发现它并不总是受支持。 例如,序列化 API(`torch::save``torch::load`)仅支持模块支架(或普通`shared_ptr`)。 因此,建议使用模块持有人 API 和 C ++前端定义模块,此后我们将在本教程中使用此 API。
### 定义 DCGAN 模块](docs / modern-java-zh /
### 定义 DCGAN 模块
现在,我们有必要的背景和简介来定义我们要在本文中解决的机器学习任务的模块。 回顾一下:我们的任务是从 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)生成数字图像。 我们想使用[生成对抗网络(GAN)](https://papers.nips.cc/paper/5423-generative-adversarial-nets.pdf)解决此任务。 特别是,我们将使用 [DCGAN 架构](https://arxiv.org/abs/1511.06434),这是同类中最早,最简单的架构之一,但完全可以完成此任务。
......@@ -473,11 +473,11 @@ struct Net : torch::nn::Module {
您可以在存储库中找到本教程[中提供的完整源代码。](https://github.com/pytorch/examples/tree/master/cpp/dcgan)
#### 什么是 GAN aGAN?](docs / modern-java-zh /
#### 什么是 GAN aGAN?
GAN 由两个不同的神经网络模型组成:*生成器**鉴别器*。 生成器从噪声分布中接收样本,其目的是将每个噪声样本转换为类似于目标分布的图像(在我们的情况下为 MNIST 数据集)。 鉴别器又从 MNIST 数据集接收*实际*图像,或从生成器接收*假*图像。 要求发出一个概率来判断特定图像的真实程度(接近`1`)或伪造(接近`0`)。 来自鉴别器的关于由发生器产生的图像有多真实的反馈被用来训练发生器。 鉴别器对真实性有多好的反馈将用于优化鉴别器。 从理论上讲,生成器和鉴别器之间的微妙平衡使它们串联起来得到改善,从而导致生成器生成与目标分布无法区分的图像,从而使鉴别器(那时)的敏锐眼睛冒出了散发`0.5`的真实和真实可能性。 假图片。 对我们来说,最终结果是一台接收噪声作为输入并生成逼真的数字图像作为其输出的机器。
#### 生成器模块](docs / modern-java-zh /
#### 生成器模块
我们首先定义生成器模块,该模块由一系列转置的 2D 卷积,批量归一化和 ReLU 激活单元组成。 我们在定义自己的模块的`forward()`方法中显式地(在功能上)在模块之间传递输入:
......@@ -660,7 +660,7 @@ Batch size: 64 | Labels: 7 6 5 7 7 5 2 2 4 9 9 4 8 7 4 8 9 4 5 7 1 2 6 9 8 5 1 2
这意味着我们能够成功地从 MNIST 数据集中加载数据。
## 编写训练循环](docs / modern-java-zh /
## 编写训练循环
现在,让我们完成示例的算法部分,并实现生成器和鉴别器之间的精妙舞蹈。 首先,我们将创建两个优化器,一个用于生成器,一个用于区分器。 我们使用的优化程序实现了 [Adam](https://arxiv.org/pdf/1412.6980.pdf) 算法:
......@@ -833,7 +833,7 @@ torch::Device device(torch::cuda::is_available() ? torch::kCUDA : torch::kCPU);
```
## 检查点和恢复训练状态](docs / modern-java-zh /
## 检查点和恢复训练状态
我们应该对训练脚本进行的最后扩充是定期保存模型参数的状态,优化器的状态以及一些生成的图像样本。 如果我们的计算机在训练过程中崩溃,则前两个将使我们能够恢复训练状态。 对于长期的训练课程,这是绝对必要的。 幸运的是,C ++前端提供了一个 API,用于对模型和优化器状态以及单个张量进行序列化和反序列化。
......@@ -879,7 +879,7 @@ for (int64_t epoch = 1; epoch <= kNumberOfEpochs; ++epoch) {
```
## 检查生成的图像](docs / modern-java-zh /
## 检查生成的图像
我们的训练脚本现已完成。 我们准备在 CPU 或 GPU 上训练 GAN。 为了检查我们训练过程的中间输出,为此我们添加了将代码样本定期保存到`"dcgan-sample-xxx.pt"`文件的代码,我们可以编写一个小的 Python 脚本来加载张量并使用 matplotlib 显示它们:
......@@ -954,7 +954,7 @@ Saved out.png
数字! 万岁! 现在,事情就在您的球场上了:您可以改进模型以使数字看起来更好吗?
## 结论](docs / modern-java-zh /
## 结论
希望本教程为您提供了 PyTorch C ++前端的可摘要。 像 PyTorch 这样的机器学习库必然具有非常广泛的 API。 因此,有许多概念我们没有时间或空间来讨论。 但是,我建议您尝试一下 API,并在遇到问题时查阅[我们的文档](https://pytorch.org/cppdocs/),尤其是[库 API](https://pytorch.org/cppdocs/api/library_root.html) 部分。 另外,请记住,只要我们能够做到,就可以期望 C ++前端遵循 Python 前端的设计和语义,因此您可以利用这一事实来提高学习率。
......
......@@ -10,7 +10,7 @@ PyTorch 提供了与神经网络,任意张量代数,数据整理和其他目
C ++扩展是我们开发的一种机制,允许用户(您)创建源外定义的 PyTorch 运算符,即*,即与 PyTorch 后端分开。 该方法与**不同于本机 PyTorch 操作的实现方式。 C ++扩展旨在为您节省大量与将操作与 PyTorch 后端集成在一起相关的样板,同时为基于 PyTorch 的项目提供高度的灵活性。 但是,一旦将操作定义为 C ++扩展,将其转换为本地 PyTorch 函数在很大程度上取决于代码组织,如果您决定在上游进行操作,则可以解决此问题。*
## 动机和示例](docs / modern-java-zh /
## 动机和示例
本说明的其余部分将逐步介绍编写和使用 C ++(和 CUDA)扩展的实际示例。 如果您被追捕,或者在一天结束前仍未完成该操作,就会有人开除您,则可以跳过本节,直接进入下一部分的实施细节。
......@@ -135,7 +135,7 @@ torch::Tensor d_sigmoid(torch::Tensor z) {
`d_sigmoid()`的实现显示了如何使用 ATen API。 PyTorch 的张量和变量接口是从 ATen 库自动生成的,因此我们可以或多或少地将 Python 实现 1:1 转换为 C ++。 我们用于所有计算的主要数据类型将为`torch::Tensor`。 可以在此处检查其完整的 API。 还要注意,我们可以包括`<iostream>`*任何其他 C 或 C ++头文件* –我们拥有 C ++ 11 的全部功能。
#### 前向传递](docs / modern-java-zh /
#### 前向传递
接下来,我们可以将整个正向传递到 C ++:
......@@ -171,7 +171,7 @@ std::vector<at::Tensor> lltm_forward(
```
#### 向后传递](docs / modern-java-zh /
#### 向后传递
C ++扩展 API 当前不提供为我们自动生成向后函数的方法。 因此,我们还必须实现 LLTM 的后向传递,它计算相对于前向传递的每个输入的损耗导数。 最终,我们将前进和后退功能放入`torch.autograd.Function`中,以创建一个不错的 Python 绑定。 向后函数的作用稍大一些,因此我们将不深入研究代码(如果您有兴趣,请阅读 [Alex Graves 的论文](https://www.cs.toronto.edu/~graves/phd.pdf),以获取有关此方面的更多信息):
......@@ -227,7 +227,7 @@ std::vector<torch::Tensor> lltm_backward(
```
### 绑定到 Python](docs / modern-java-zh /
### 绑定到 Python
一旦用 C ++和 ATen 编写了操作,就可以使用 pybind11 以非常简单的方式将 C ++函数或类绑定到 Python 中。 您对 PyTorch C ++扩展部分的疑问或问题将在 [pybind11 文档](https://pybind11.readthedocs.io/en/master/)中得到解决。
......@@ -243,7 +243,7 @@ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
这里要注意的一点是宏`TORCH_EXTENSION_NAME`。 火炬扩展程序构建会将其定义为您在`setup.py`脚本中为扩展程序指定的名称。 在这种情况下,`TORCH_EXTENSION_NAME`的值为“ lltm”。 这是为了避免在两个位置(构建脚本和 C ++代码)都保留扩展名,因为两者之间的不匹配会导致令人讨厌且难以跟踪的问题。
### 使用扩展程序](docs / modern-java-zh /
### 使用扩展程序
现在,我们准备将扩展名导入 PyTorch 中。 此时,您的目录结构可能如下所示:
......@@ -374,7 +374,7 @@ class LLTM(torch.nn.Module):
```
#### 性能比较](docs / modern-java-zh /
#### 性能比较
现在我们已经可以使用并从 PyTorch 调用 C ++代码了,我们可以运行一个小型基准测试,以查看通过用 C ++重写 op 所获得的性能。 我们将向前和向后运行 LLTM 几次,并测量持续时间:
......@@ -508,7 +508,7 @@ Loading extension module lltm_cpp...
生成的 Python 模块将与 setuptools 生成的模块完全相同,但是消除了必须维护单独的`setup.py`构建文件的要求。 如果您的设置更为复杂,并且确实需要`setuptools`的全部功能,则*可以*编写自己的`setup.py` –但是在许多情况下,这种 JIT 技术就可以了。 第一次运行此行时,将需要一些时间,因为扩展程序是在后台编译的。 由于我们使用 Ninja 构建系统来构建您的源代码,因此重新编译是增量的,因此在您第二次运行 Python 模块时重新加载扩展程序非常快捷,而且如果您不更改扩展程序的源文件,开销也很低。
## 编写混合的 C ++ / CUDA 扩展](docs / modern-java-zh /
## 编写混合的 C ++ / CUDA 扩展
为了将实现真正提升到一个新的水平,我们可以使用自定义 CUDA 内核来手写前进和后退通道的一部分。 对于 LLTM,这具有特别有效的前景,因为按顺序有大量的逐点运算,这些运算都可以在单个 CUDA 内核中融合和并行化。 让我们看看如何编写这种 CUDA 内核,并使用此扩展机制将其与 PyTorch 集成。
......@@ -736,7 +736,7 @@ __global__ void lltm_cuda_forward_kernel(
这里最有趣的是,我们能够为门矩阵中的每个单独的组件完全并行地计算所有这些逐点运算。 如果您想象必须用一个串行的百万个元素的`for`大型循环来执行此操作,那么您会明白为什么这样做会更快。
### 使用访问器](docs / modern-java-zh /
### 使用访问器
您可以在 CUDA 内核中看到,我们直接处理正确类型的指针。 实际上,直接在 cuda 内核内部使用高级类型不可知张量会非常低效。
......@@ -973,7 +973,7 @@ lltm = load(name='lltm', sources=['lltm_cuda.cpp', 'lltm_cuda_kernel.cu'])
```
#### 性能比较](docs / modern-java-zh /
#### 性能比较
我们的希望是,将我们的代码的逐点操作与 CUDA 并行化和融合,将改善 LLTM 的性能。 让我们看看这是否成立。 我们可以运行前面列出的代码来运行基准测试。 我们之前最快的版本是基于 CUDA 的 C ++代码:
......@@ -991,6 +991,6 @@ Forward: 129.431 us | Backward 304.641 us
更多性能提升!
## 结论](docs / modern-java-zh /
## 结论
现在,您应该对 PyTorch 的 C ++扩展机制有了一个很好的了解,并有使用它们的动机。 您可以在此处找到本说明[中显示的代码示例。 如有疑问,请使用](https://github.com/pytorch/extension-cpp)[论坛](https://discuss.pytorch.org)。 另外,请务必查看我们的[常见问题解答](https://pytorch.org/cppdocs/notes/faq.html),以防遇到任何问题。
\ No newline at end of file
......@@ -8,7 +8,7 @@ TorchScript 支持`torch`包提供的大量操作子集,使您可以纯粹表
以下段落提供了一个编写 TorchScript 自定义操作以调用 [OpenCV](https://www.opencv.org) (使用 C ++编写的计算机视觉库)的示例。 我们将讨论如何在 C ++中使用张量,如何有效地将它们转换为第三方张量格式(在这种情况下为 OpenCV `Mat`),如何在 TorchScript 运行时中注册您的运算符,以及最后如何编译该运算符并使用 它在 Python 和 C ++中。
## 在 C ++中实现自定义运算符](docs / modern-java-zh /
## 在 C ++中实现自定义运算符
在本教程中,我们将公开 [warpPerspective](https://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html#warpperspective) 函数,该函数将透视转换应用于图像,从 OpenCV 到 TorchScript 作为自定义运算符。 第一步是用 C ++编写自定义运算符的实现。 让我们将此实现的文件称为`op.cpp`,并使其如下所示:
......@@ -91,7 +91,7 @@ TorchScript 编译器了解固定数量的类型。 只有这些类型可以用
从我们的运算符实现返回此张量之前,我们必须在张量上调用`.clone()`以执行基础数据的存储副本。 这样做的原因是`torch::from_blob`返回了一个不拥有其数据的张量。 那时,数据仍归 OpenCV 矩阵所有。 但是,此 OpenCV 矩阵将超出范围,并在函数末尾重新分配。 如果我们按原样返回`output`张量,那么当我们在函数外部使用它时,它将指向无效的内存。 调用`.clone()`会返回一个新张量,其中包含新张量自己拥有的原始数据的副本。 因此,返回外部世界是安全的。
## 使用 TorchScript 注册自定义运算符](docs / modern-java-zh /
## 使用 TorchScript 注册自定义运算符
现在,已经在 C ++中实现了自定义运算符,我们需要在 T​​orchScript 运行时和编译器中将*注册为*。 这将使 TorchScript 编译器可以在 TorchScript 代码中解析对我们自定义运算符的引用。 如果您曾经使用过 pybind11 库,则我们的注册语法非常类似于 pybind11 语法。 要注册一个功能,我们编写:
......@@ -106,7 +106,7 @@ TORCH_LIBRARY(my_ops, m) {
在后台,`def`函数实际上正在做大量工作:它正在使用模板元编程来检查函数的类型签名,并将其转换为可在 TorchScript 的类型系统中指定操作符类型的操作符架构。
## 构建自定义运算符](docs / modern-java-zh /
## 构建自定义运算符
现在,我们已经用 C ++实现了自定义运算符并编写了其注册代码,是时候将该运算符构建到一个(共享的)库中了,可以将其加载到 Python 中进行研究和实验,或者加载到 C ++中以在非 Python 中进行推理。 环境。 有多种方法可以使用纯 CMake 或`setuptools`之类的 Python 替代方法来构建我们的运算符。 为简洁起见,以下段落仅讨论 CMake 方法。 本教程的附录将深入探讨其他替代方法。
......@@ -120,7 +120,7 @@ conda install opencv
```
### 使用 CMake 进行构建](docs / modern-java-zh /
### 使用 CMake 进行构建
为了使用 [CMake](https://cmake.org) 构建系统将自定义运算符构建到共享库中,我们需要编写一个简短的`CMakeLists.txt`文件并将其与我们先前的`op.cpp`文件放置在一起。 为此,让我们就一个看起来像这样的目录结构达成一致:
......@@ -212,7 +212,7 @@ print(torch.ops.my_ops.warp_perspective)
这是我们稍后将用来调用自定义运算符的 Python 函数。
## 在 Python 中使用 TorchScript 自定义运算符](docs / modern-java-zh /
## 在 Python 中使用 TorchScript 自定义运算符
将我们的自定义运算符构建到共享库后,我们就可以在 Python 的 TorchScript 模型中使用此运算符了。 这有两个部分:首先将运算符加载到 Python 中,其次在 TorchScript 代码中使用运算符。
......@@ -436,7 +436,7 @@ TorchScript 图形表示仍可能更改。 不要依靠它看起来像这样。
在 Python 中使用自定义运算符时,确实如此。 简而言之,您可以使用`torch.ops.load_library`导入包含您的运算符的库,并像其他任何`torch`运算符一样,从跟踪或编写脚本的 TorchScript 代码中调用自定义操作。
## 在 C ++中使用 TorchScript 自定义运算符](docs / modern-java-zh /
## 在 C ++中使用 TorchScript 自定义运算符
TorchScript 的一项有用功能是能够将模型序列化到磁盘文件中。 该文件可以通过有线方式发送,存储在文件系统中,或者更重要的是,可以动态反序列化和执行,而无需保留原始源代码。 这在 Python 中是可能的,但在 C ++中也是可能的。 为此,PyTorch 为[提供了纯 C ++ API](https://pytorch.org/cppdocs/) ,用于反序列化以及执行 TorchScript 模型。 如果您还没有的话,请阅读[有关在 C ++](https://pytorch.org/tutorials/advanced/cpp_export.html) 中加载和运行序列化 TorchScript 模型的教程,接下来的几段将基于该教程构建。
......@@ -621,19 +621,19 @@ $ ./example_app example.pt
成功! 您现在可以推断了。
## 结论](docs / modern-java-zh /
## 结论
本教程向您介绍了如何在 C ++中实现自定义 TorchScript 运算符,如何将其构建到共享库中,如何在 Python 中使用它来定义 TorchScript 模型以及如何将其加载到 C ++应用程序中以进行推理工作负载。 现在,您可以使用与第三方 C ++库进行接口的 C ++运算符扩展 TorchScript 模型,编写自定义的高性能 CUDA 内核,或实现任何其他需要 Python,TorchScript 和 C ++之间的界线才能平稳融合的用例。
与往常一样,如果您遇到任何问题或疑问,可以使用我们的[论坛](https://discuss.pytorch.org/)[GitHub 问题](https://github.com/pytorch/pytorch/issues)进行联系。 另外,我们的[常见问题解答(FAQ)页面](https://pytorch.org/cppdocs/notes/faq.html)可能包含有用的信息。
## 附录 A:建立自定义运算符的更多方法](docs / modern-java-zh /
## 附录 A:建立自定义运算符的更多方法
“构建自定义运算符”一节介绍了如何使用 CMake 将自定义运算符构建到共享库中。 本附录概述了两种进一步的编译方法。 他们俩都使用 Python 作为编译过程的“驱动程序”或“接口”。 此外,两者都重用了[现有基础结构](https://pytorch.org/docs/stable/cpp_extension.html) PyTorch 提供了 [* C ++扩展*](https://pytorch.org/tutorials/advanced/cpp_extension.html) ,它们是依赖于 [pybind11 [](https://github.com/pybind/pybind11) 用于将功能从 C ++“显式”绑定到 Python。
第一种方法是使用 C ++扩展程序的[方便的即时(JIT)编译界面](https://pytorch.org/docs/stable/cpp_extension.html#torch.utils.cpp_extension.load)在您首次运行 PyTorch 脚本时在后台编译代码。 第二种方法依赖于古老的`setuptools`包,并涉及编写单独的`setup.py`文件。 这样可以进行更高级的配置,并与其他基于`setuptools`的项目集成。 我们将在下面详细探讨这两种方法。
### 使用 JIT 编译进行构建](docs / modern-java-zh /
### 使用 JIT 编译进行构建
PyTorch C ++扩展工具包提供的 JIT 编译功能可将自定义运算符的编译直接嵌入到您的 Python 代码中,例如 在训练脚本的顶部。
......
......@@ -4,7 +4,7 @@
本教程是[自定义运算符](torch_script_custom_ops.html)教程的后续教程,并介绍了我们为将 C ++类同时绑定到 TorchScript 和 Python 而构建的 API。 该 API 与 [pybind11](https://github.com/pybind/pybind11) 非常相似,如果您熟悉该系统,则大多数概念都将转移过来。
## 用 C ++实现和绑定类](docs / modern-java-zh /
## 用 C ++实现和绑定类
在本教程中,我们将定义一个简单的 C ++类,该类在成员变量中保持持久状态。
......@@ -100,7 +100,7 @@ TORCH_LIBRARY(my_classes, m) {
```
## 使用 CMake 将示例构建为 C ++项目](docs / modern-java-zh /
## 使用 CMake 将示例构建为 C ++项目
现在,我们将使用 [CMake](https://cmake.org) 构建系统来构建上述 C ++代码。 首先,将到目前为止介绍的所有 C ++代码放入`class.cpp`文件中。 然后,编写一个简单的`CMakeLists.txt`文件并将其放在同一目录中。 `CMakeLists.txt`应该是这样的:
......@@ -179,7 +179,7 @@ custom_class_project/
```
## 从 Python 和 TorchScript 使用 C ++类](docs / modern-java-zh /
## 从 Python 和 TorchScript 使用 C ++类
现在我们已经将我们的类及其注册编译为`.so`文件,我们可以将 <cite>.so</cite> 加载到 Python 中并进行尝试。 这是一个演示脚本的脚本:
......@@ -233,7 +233,7 @@ for expected in ["wow", "mom", "hi"]:
```
## 使用自定义类保存,加载和运行 TorchScript 代码](docs / modern-java-zh /
## 使用自定义类保存,加载和运行 TorchScript 代码
我们还可以在使用 libtorch 的 C ++进程中使用自定义注册的 C ++类。 举例来说,让我们定义一个简单的`nn.Module`,它实例化并调用 MyStackClass 类上的方法:
......@@ -379,7 +379,7 @@ $ ./infer
难以置信!
## 将自定义类移入或移出 IValues](docs / modern-java-zh /
## 将自定义类移入或移出 IValues
也可能需要将自定义类从自定义 C ++类实例移入或移出`IValue``s, such as when you take or return ``IValue``s from TorchScript methods or you want to instantiate a custom class attribute in C++. For creating an ``IValue`
......@@ -390,7 +390,7 @@ $ ./infer
* `IValue::toCustomClass<T>()`将返回一个`intrusive_ptr<T>`,指向`IValue`包含的自定义类。 在内部,此函数正在检查`T`是否已注册为自定义类,并且`IValue`实际上确实包含一个自定义类。 您可以通过调用`isCustomClass()`来手动检查`IValue`是否包含自定义类。
## 为自定义 C ++类定义序列化/反序列化方法](docs / modern-java-zh /
## 为自定义 C ++类定义序列化/反序列化方法
如果您尝试将具有自定义绑定 C ++类的`ScriptModule`保存为属性,则会出现以下错误:
......@@ -481,7 +481,7 @@ testing
```
## 定义接受或返回绑定 C ++类的自定义运算符](docs / modern-java-zh /
## 定义接受或返回绑定 C ++类的自定义运算符
定义自定义 C ++类后,您还可以将该类用作自变量或从自定义运算符返回(即自由函数)。 假设您具有以下免费功能:
......@@ -522,7 +522,7 @@ class TryCustomOp(torch.nn.Module):
注册使用 C ++类作为参数的运算符时,要求已注册自定义类。 您可以通过确保自定义类注册和您的自由函数定义在同一`TORCH_LIBRARY`块中,并确保自定义类注册位于第一位来强制实施此操作。 将来,我们可能会放宽此要求,以便可以按任何顺序进行注册。
## 结论](docs / modern-java-zh /
## 结论
本教程向您介绍了如何向 TorchScript(以及扩展为 Python)公开 C ++类,如何注册其方法,如何从 Python 和 TorchScript 使用该类以及如何使用该类保存和加载代码以及运行该代码。 在独立的 C ++过程中。 现在,您可以使用与第三方 C ++库连接的 C ++类扩展 TorchScript 模型,或实现需要 Python,TorchScript 和 C ++之间的界线平滑融合的任何其他用例。
......
......@@ -7,7 +7,7 @@
* 动态-创建的并行任务的数量及其工作量可能取决于程序的控制流。
* 互操作-并行性与并行运行 TorchScript 程序片段有关。 这与*运算内部并行性*不同,后者涉及拆分单个运算符并并行运行运算符工作的子集。
## 基本语法](docs / modern-java-zh /
## 基本语法
动态并行的两个重要 API 是:
......@@ -77,7 +77,7 @@ print(example(torch.ones([])))
本示例使用`fork()`启动函数`foo`的 100 个实例,等待 100 个任务完成,然后对结果求和,返回`-100.0`
## 应用示例:双向 LSTM 的集成](docs / modern-java-zh /
## 应用示例:双向 LSTM 的集成
让我们尝试将并行性应用于一个更现实的示例,看看我们可以从中获得什么样的性能。 首先,让我们定义基准模型:双向 LSTM 层的集合。
......@@ -148,7 +148,7 @@ print('Inference took', time.time() - s, ' seconds')
在我的机器上,该网络运行时间为`2.05`秒。 我们可以做得更好!
## 并行化前向和后向层](docs / modern-java-zh /
## 并行化前向和后向层
我们可以做的一个非常简单的事情是在`BidirectionalRecurrentLSTM`中并行化前进和后退层。 为此,计算结构是静态的,因此我们实际上甚至不需要任何循环。 像这样重写`BidirectionalRecurrentLSTM``forward`方法:
......@@ -188,7 +188,7 @@ def forward(self, x : torch.Tensor) -> torch.Tensor:
时间轴的横轴表示时间,纵轴表示执行线程。 如我们所见,我们一次运行两个`lstm`实例。 这是我们辛勤工作使双向层平行的结果!
## 集成中的并行化模型](docs / modern-java-zh /
## 集成中的并行化模型
您可能已经注意到,我们的代码中还有更多的并行化机会:我们还可以并行运行`LSTMEnsemble`中包含的模型。 做到这一点的方法很简单,这就是我们应该更改`LSTMEnsemble``forward`方法的方式:
......@@ -228,6 +228,6 @@ def forward(self, x : torch.Tensor) -> torch.Tensor:
现在我们可以看到所有`LSTM`实例都在完全并行运行。
## 结论](docs / modern-java-zh /
## 结论
在本教程中,我们学习了`fork()``wait()`,这是在 TorchScript 中执行动态,互操作并行的基本 API。 我们看到了一些典型的使用模式,这些模式使用这些函数并行执行 TorchScript 代码中的函数,方法或`Modules`的执行。 最后,我们通过一个使用该技术优化模型的示例进行了研究,并探索了 PyTorch 中可用的性能测量和可视化工具。
\ No newline at end of file
......@@ -222,7 +222,7 @@ true
有关 C ++张量自动梯度 API 的更多信息,例如`grad` / `requires_grad` / `is_leaf` / `backward` / `detach` / `detach_` / `register_hook` / `retain_grad`,请参见[相应的 C ++ API 文档](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html)
## 用 C ++计算高阶梯度](docs / modern-java-zh /
## 用 C ++计算高阶梯度
高阶梯度的应用之一是计算梯度罚分。 我们来看看使用`torch::autograd::grad`的示例:
......@@ -386,7 +386,7 @@ std::cout << x.grad() << std::endl;
有关`torch::autograd::Function`的更多信息,请参见[其文档](https://pytorch.org/cppdocs/api/structtorch_1_1autograd_1_1_function.html)
## 将 autograd 代码从 Python 转换为 C ++](docs / modern-java-zh /
## 将 autograd 代码从 Python 转换为 C ++
在较高的层次上,在 C ++中使用 autograd 的最简单方法是先在 Python 中拥有可用的 autograd 代码,然后使用下表将您的 autograd 代码从 Python 转换为 C ++:
......@@ -410,6 +410,6 @@ std::cout << x.grad() << std::endl;
翻译后,您的大多数 Python autograd 代码都应仅在 C ++中工作。 如果不是这种情况,请在 [GitHub 问题](https://github.com/pytorch/pytorch/issues)中提交错误报告,我们将尽快对其进行修复。
## 结论](docs / modern-java-zh /
## 结论
现在,您应该对 PyTorch 的 C ++ autograd API 有了一个很好的了解。 您可以在此处找到本说明[中显示的代码示例。 与往常一样,如果您遇到任何问题或疑问,可以使用我们的](https://github.com/pytorch/examples/tree/master/cpp/autograd)[论坛](https://discuss.pytorch.org/)或 [GitHub 问题](https://github.com/pytorch/pytorch/issues)进行联系。
\ No newline at end of file
......@@ -138,7 +138,7 @@ TORCH_LIBRARY_IMPL(myops, Autograd, m) {
```
## 超越 autograd](docs / modern-java-zh /
## 超越 autograd
从某种意义上说,调度员并没有做太多事情:它所做的只是实现一种美化的 if 语句,其方法如下:
......@@ -169,7 +169,7 @@ public:
这是一些特定的调度键,您可能需要为其定义一个运算符。
### 自动播送](docs / modern-java-zh /
### 自动播送
Autocast 调度键实现对[自动混合精度(AMP)](https://pytorch.org/docs/stable/amp.html)的支持。 自动广播包装器内核通常会在运行 op 之前将传入的`float16``float32` CUDA 张量转换为某些首选精度。 例如,浮点 CUDA 张量上的积和卷积通常运行得更快,并且在`float16`中使用较少的内存,而不会影响收敛。 自动广播包装器仅在[启用自动广播的上下文](https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.autocast)中有效。
......@@ -239,6 +239,6 @@ TORCH_LIBRARY_IMPL(myops, Autocast, m) {
批量张量允许您按示例方式编写代码,然后在`vmap`调用下运行时自动对其进行批量。 当前正在开发用于编写批量规则的 API,但是一旦稳定该 API,就可以通过在 Batched 调度键处注册内核来为操作员添加对`vmap`的支持。
### 追踪器](docs / modern-java-zh /
### 追踪器
Tracer 调度键实现了对在运行`torch.jit.trace`时将操作员调用记录到跟踪中的支持。 我们打算提供一个盒装后备,它将实现对任意操作的跟踪,请参阅[问题#41478](https://github.com/pytorch/pytorch/issues/41478) 以跟踪进度。
\ No newline at end of file
......@@ -52,7 +52,7 @@ class MyModule(nn.Module):
```
## 剖析前进通道](docs / modern-java-zh /
## 剖析前进通道
我们初始化随机输入和蒙版张量以及模型。
......@@ -193,7 +193,7 @@ Self CPU time total: 5.347s
此操作的 CPU 内存占用量减少了一半。
## 提高时间表现](docs / modern-java-zh /
## 提高时间表现
虽然所消耗的时间也有所减少,但仍然太高。 原来,将矩阵从 CUDA 复制到 CPU 非常昂贵! `forward (12)`中的`aten::copy_`运算符将`mask`复制到 CPU,以便可以使用 NumPy `argwhere`功能。 `forward(13)`处的`aten::copy_`将数组作为张量复制回 CUDA。 如果我们在这里使用`torch`函数`nonzero()`,则可以消除这两个方面。
......@@ -268,7 +268,7 @@ Self CPU time total: 225.801ms
```
## 进一步阅读](docs / modern-java-zh /
## 进一步阅读
我们已经看到了 Profiler 如何用于调查 PyTorch 模型中的时间和内存瓶颈。 在此处阅读有关 Profiler 的更多信息:
......
......@@ -20,7 +20,7 @@
* `ray[tune]`:分布式超参数调整库
* `torchvision`:用于数据转换器
## 设置/导入](docs / modern-java-zh /
## 设置/导入
让我们从导入开始:
......@@ -43,7 +43,7 @@ from ray.tune.schedulers import ASHAScheduler
建立 PyTorch 模型需要大多数进口产品。 Ray Tune 仅最后三个进口。
## 数据加载器](docs / modern-java-zh /
## 数据加载器
我们将数据加载器包装在它们自己的函数中,并传递一个全局数据目录。 这样,我们可以在不同的试验之间共享数据目录。
......@@ -116,7 +116,7 @@ optimizer = optim.SGD(net.parameters(), lr=config["lr"], momentum=0.9)
我们还将训练数据分为训练和验证子集。 因此,我们训练了 80% 的数据,并计算了其余 20% 的验证损失。 我们遍历训练和测试集的批量大小也是可配置的。
### 通过 DataParallel 添加(多)GPU 支持](docs / modern-java-zh /
### 通过 DataParallel 添加(多)GPU 支持
图像分类主要受益于 GPU。 幸运的是,我们可以继续在 Ray Tune 中使用 PyTorch 的抽象。 因此,我们可以将模型包装在`nn.DataParallel`中,以支持在多个 GPU 上进行数据并行训练:
......@@ -141,7 +141,7 @@ for i, data in enumerate(trainloader, 0):
该代码现在支持在 CPU,单个 GPU 和多个 GPU 上进行训练。 值得注意的是,Ray 还支持[分数 GPU](https://docs.ray.io/en/master/using-ray-with-gpus.html#fractional-gpus) ,因此我们可以在试验之间共享 GPU,只要模型仍适合 GPU 内存即可。 我们稍后再讲。
### 与 Ray Tune 交流](docs / modern-java-zh /
### 与 Ray Tune 交流
最有趣的部分是与 Ray Tune 的交流:
......@@ -254,7 +254,7 @@ def train_cifar(config, checkpoint_dir=None, data_dir=None):
如您所见,大多数代码直接来自原始示例。
## 测试集准确率](docs / modern-java-zh /
## 测试集准确率
通常,机器学习模型的性能是在保持测试集上使用尚未用于训练模型的数据进行测试的。 我们还将其包装在一个函数中:
......@@ -282,7 +282,7 @@ def test_accuracy(net, device="cpu"):
该函数还需要一个`device`参数,因此我们可以在 GPU 上进行测试集验证。
## 配置搜索空间](docs / modern-java-zh /
## 配置搜索空间
最后,我们需要定义 Ray Tune 的搜索空间。 这是一个例子:
......
......@@ -8,7 +8,7 @@
在本教程中,您将学习如何使用`torch.nn.utils.prune`稀疏神经网络,以及如何扩展它以实现自己的自定义修剪技术。
## 要求](docs / modern-java-zh /
## 要求
`"torch>=1.4.0a0+8e8a5e0"`
......@@ -50,7 +50,7 @@ model = LeNet().to(device=device)
```
## 检查模块](docs / modern-java-zh /
## 检查模块
让我们检查一下 LeNet 模型中的(未修剪)`conv1`层。 现在它将包含两个参数`weight``bias`,并且没有缓冲区。
......@@ -104,7 +104,7 @@ print(list(module.named_buffers()))
```
## 修剪模块](docs / modern-java-zh /
## 修剪模块
要修剪模块(在此示例中,为 LeNet 架构的`conv1`层),请首先从`torch.nn.utils.prune`中可用的那些技术中选择一种修剪技术(或[通过子类化`BasePruningMethod`实现您自己的](#extending-torch-nn-utils-pruning-with-custom-pruning-functions))。 然后,指定模块和该模块中要修剪的参数的名称。 最后,使用所选修剪技术所需的适当关键字参数,指定修剪参数。
......@@ -346,7 +346,7 @@ OrderedDict([(0, <torch.nn.utils.prune.RandomUnstructured object at 0x7fda78275e
```
## 迭代修剪](docs / modern-java-zh /
## 迭代修剪
一个模块中的同一参数可以被多次修剪,各种修剪调用的效果等于串联应用的各种蒙版的组合。 `PruningContainer``compute_mask`方法可处理新遮罩与旧遮罩的组合。
......@@ -410,7 +410,7 @@ print(list(hook)) # pruning history in the container
```
## 序列化修剪的模型](docs / modern-java-zh /
## 序列化修剪的模型
所有相关的张量,包括掩码缓冲区和用于计算修剪的张量的原始参数,都存储在模型的`state_dict`中,因此可以根据需要轻松地序列化和保存。
......@@ -615,7 +615,7 @@ dict_keys(['conv1.weight_mask', 'conv2.weight_mask', 'fc1.weight_mask', 'fc2.wei
```
## 全局修剪](docs / modern-java-zh /
## 全局修剪
到目前为止,我们仅查看了通常称为“局部”修剪的情况,即通过比较每个条目的统计信息(权重,激活度,梯度等)来逐个修剪模型中的张量的做法。 到该张量中的其他条目。 但是,一种通用且可能更强大的技术是通过删除(例如)删除整个模型中最低的 20% 的连接,而不是删除每一层中最低的 20% 的连接来一次修剪模型。 这很可能导致每个层的修剪百分比不同。 让我们看看如何使用`torch.nn.utils.prune`中的`global_unstructured`进行操作。
......@@ -704,7 +704,7 @@ Global sparsity: 20.00%
```
## 使用自定义修剪功能扩展`torch.nn.utils.prune`](docs / modern-java-zh /
## 使用自定义修剪功能扩展`torch.nn.utils.prune`
要实现自己的修剪功能,可以通过继承`BasePruningMethod`基类的子类来扩展`nn.utils.prune`模块,这与所有其他修剪方法一样。 基类为您实现以下方法:`__call__``apply_mask``apply``prune``remove`。 除了一些特殊情况外,您无需为新的修剪技术重新实现这些方法。 但是,您将必须实现`__init__`(构造函数)和`compute_mask`(有关如何根据修剪技术的逻辑为给定张量计算掩码的说明)。 另外,您将必须指定此技术实现的修剪类型(支持的选项为`global``structured``unstructured`)。 需要确定在迭代应用修剪的情况下如何组合蒙版。 换句话说,当修剪预修剪的参数时,当前的修剪技术应作用于参数的未修剪部分。 指定`PRUNING_TYPE`将使`PruningContainer`(处理修剪掩码的迭代应用)正确识别要修剪的参数。
......
......@@ -6,7 +6,7 @@
**由**编辑:[赛斯·魏德曼](https://github.com/SethHWeidman/)
## 简介](docs / modern-java-zh /
## 简介
量化涉及将模型的权重和激活从 float 转换为 int,这可以导致模型尺寸更小,推断速度更快,而对准确率的影响很小。
......@@ -24,7 +24,7 @@ import torch.nn.functional as F
```
## 1.定义模型](docs / modern-java-zh /
## 1.定义模型
在这里,我们根据词语言模型示例中的[模型](https://github.com/pytorch/examples/blob/master/word_language_model/model.py)定义 LSTM 模型架构。
......@@ -64,7 +64,7 @@ class LSTMModel(nn.Module):
```
## 2.加载文本数据](docs / modern-java-zh /
## 2.加载文本数据
接下来,我们再次根据单词模型示例对[预处理](https://github.com/pytorch/examples/blob/master/word_language_model/data.py),将 [Wikitext-2 数据集](https://www.google.com/search?q=wikitext+2+data)加载到<cite>语料库</cite>中。
......@@ -119,7 +119,7 @@ corpus = Corpus(model_data_filepath + 'wikitext-2')
```
## 3.加载预先训练的模型](docs / modern-java-zh /
## 3.加载预先训练的模型
这是一本有关动态量化的教程,这是在训练模型后应用的一种量化技术。 因此,我们将简单地将一些预训练的权重加载到此模型架构中; 这些权重是通过使用单词语言模型示例中的默认设置训练五个纪元而获得的。
......@@ -303,7 +303,7 @@ def evaluate(model_, data_source):
```
## 4.测试动态量化](docs / modern-java-zh /
## 4.测试动态量化
最后,我们可以在模型上调用`torch.quantization.quantize_dynamic`! 特别,
......@@ -383,7 +383,7 @@ elapsed time (seconds): 166.3
在没有量化的情况下在 MacBook Pro 上本地运行此操作,推理大约需要 200 秒,而量化则只需大约 100 秒。
## 结论](docs / modern-java-zh /
## 结论
动态量化可能是减小模型大小的简单方法,而对精度的影响有限。
......
......@@ -12,7 +12,7 @@
**由**编辑:[林 ess 琳](https://github.com/jlin27)
## 简介](docs / modern-java-zh /
## 简介
在本教程中,我们将动态量化应用在 BERT 模型上,紧跟 [HuggingFace Transformers 示例](https://github.com/huggingface/transformers)中的 BERT 模型。 通过这一循序渐进的旅程,我们将演示如何将著名的 BERT 等最新模型转换为动态量化模型。
......@@ -22,9 +22,9 @@
![../_img/bert.png](img/b43b70d8a6eef9ea4f75867b5e83b483.png)
## 1.设置](docs / modern-java-zh /
## 1.设置
### 1.1 安装 PyTorch 和 HuggingFace 转换器](docs / modern-java-zh /
### 1.1 安装 PyTorch 和 HuggingFace 转换器
要开始本教程,首先请遵循 PyTorch [(此处](https://github.com/pytorch/pytorch/#installation))和 HuggingFace Github Repo [(此处](https://github.com/huggingface/transformers#installation))中的安装说明。 此外,我们还将安装 [scikit-learn](https://github.com/scikit-learn/scikit-learn) 软件包,因为我们将重复使用其内置的 F1 分数计算帮助器功能。
......@@ -105,7 +105,7 @@ print(torch.__config__.parallel_info())
\[F1 = 2 * (\text{precision} * \text{recall}) / (\text{precision} + \text{recall})\]
### 1.4 下载数据集](docs / modern-java-zh /
### 1.4 下载数据集
在运行 MRPC 任务之前,我们通过运行[此脚本](https://gist.github.com/W4ngatang/60c2bdb54d156a41194446737ce03e2e)并下载 [GLUE 数据](https://gluebenchmark.com/tasks)并将其解压缩到目录`glue_data`中。
......@@ -114,7 +114,7 @@ python download_glue_data.py --data_dir='glue_data' --tasks='MRPC'
```
## 2.微调 BERT 模型](docs / modern-java-zh /
## 2.微调 BERT 模型
BERT 的精神是预训练语言表示形式,然后以最小的任务相关参数微调各种任务上的深层双向表示形式,并获得最新的结果。 在本教程中,我们将专注于对预训练的 BERT 模型进行微调,以对 MRPC 任务上的语义等效句子对进行分类。
......@@ -340,7 +340,7 @@ def load_and_cache_examples(args, task, tokenizer, evaluate=False):
```
## 3.应用动态量化](docs / modern-java-zh /
## 3.应用动态量化
我们在模型上调用`torch.quantization.quantize_dynamic`,将动态量化应用于 HuggingFace BERT 模型。 特别,
......@@ -372,7 +372,7 @@ print_size_of_model(quantized_model)
本教程中使用的 BERT 模型(`bert-base-uncased`)的词汇量 V 为 30522。在嵌入量为 768 的情况下,单词嵌入表的总大小为〜4(Bytes / FP32)* 30522 * 768 = 90 MB 。 因此,借助量化,非嵌入表部分的模型大小从 350 MB(FP32 模型)减少到 90 MB(INT8 模型)。
### 3.2 评估推理准确率和时间](docs / modern-java-zh /
### 3.2 评估推理准确率和时间
接下来,我们比较一下动态量化后原始 FP32 模型和 INT8 模型之间的推断时间以及评估精度。
......@@ -427,13 +427,13 @@ loaded_quantized_model = torch.jit.load("bert_traced_eager_quant.pt")
```
## 结论](docs / modern-java-zh /
## 结论
在本教程中,我们演示了如何演示如何将 BERT 等著名的最新 NLP 模型转换为动态量化模型。 动态量化可以减小模型的大小,而对准确率的影响有限。
谢谢阅读! 与往常一样,我们欢迎您提供反馈,因此,如果有任何问题,请在中创建一个问题[](https://github.com/pytorch/pytorch/issues)
## 参考文献](docs / modern-java-zh /
## 参考文献
[1] J.Devlin,M。Chang,K。Lee 和 K. Toutanova, [BERT:用于语言理解的深度双向转换器的预训练(2018)](https://arxiv.org/pdf/1810.04805.pdf)
......
......@@ -44,7 +44,7 @@ torch.manual_seed(191009)
```
## 1.模型架构](docs / modern-java-zh /
## 1.模型架构
我们首先定义 MobileNetV2 模型架构,并进行了一些值得注意的修改以实现量化:
......@@ -209,7 +209,7 @@ class MobileNetV2(nn.Module):
```
## 2.辅助函数](docs / modern-java-zh /
## 2.辅助函数
接下来,我们定义一些帮助程序功能以帮助模型评估。 这些主要来自[这里](https://github.com/pytorch/examples/blob/master/imagenet/main.py)
......@@ -286,11 +286,11 @@ def print_size_of_model(model):
```
## 3.定义数据集和数据加载器](docs / modern-java-zh /
## 3.定义数据集和数据加载器
作为最后的主要设置步骤,我们为训练和测试集定义了数据加载器。
### ImageNet 数据](docs / modern-java-zh /
### ImageNet 数据
我们为本教程创建的特定数据集仅包含来自 ImageNet 数据的 1000 张图像,每个类别都有一张(此数据集的大小刚好超过 250 MB,可以相对轻松地下载)。 此自定义数据集的 URL 为:
......@@ -470,7 +470,7 @@ Size (MB): 13.999657
这将是我们比较的基准。 接下来,让我们尝试不同的量化方法
## 4.训练后静态量化](docs / modern-java-zh /
## 4.训练后静态量化
训练后的静态量化不仅涉及像动态量化中那样将权重从 float 转换为 int,而且还执行额外的步骤,即首先通过网络馈送一批数据并计算不同激活的结果分布(具体来说,这是 通过在记录此数据的不同点插入<cite>观察者</cite>模块来完成)。 然后使用这些分布来确定在推理时如何具体量化不同的激活(一种简单的技术是将整个激活范围简单地划分为 256 个级别,但我们也支持更复杂的方法)。 重要的是,此附加步骤使我们能够在操作之间传递量化值,而不是在每次操作之间将这些值转换为浮点数,然后再转换为整数,从而显着提高了速度。
......@@ -591,7 +591,7 @@ QConfig(activation=functools.partial(<class 'torch.quantization.observer.Histogr
仅更改这种量化配置方法,就可以将准确率提高到 76% 以上! 尽管如此,这仍比上述 78% 的基准差 1-2%。 因此,让我们尝试量化意识的训练。
## 5.量化意识训练](docs / modern-java-zh /
## 5.量化意识训练
量化意识训练(QAT)是通常导致最高准确率的量化方法。 使用 QAT,在训练的正向和反向过程中,所有权重和激活都被“伪量化”:即,浮点值四舍五入以模拟 int8 值,但所有计算仍使用浮点数完成。 因此,在训练过程中进行所有权重调整,同时“意识到”该模型将最终被量化的事实。 因此,在量化之后,此方法通常会比动态量化或训练后静态量化产生更高的精度。
......@@ -758,7 +758,7 @@ Training: * Acc@1 63.000 Acc@5 81.333
* 由于我们使用伪量化来对实际量化算术的数值建模,因此我们还可以在浮点中模拟量化模型的准确率。
* 我们也可以轻松地模拟训练后量化。
### 量化加速](docs / modern-java-zh /
### 量化加速
最后,让我们确认一下我们上面提到的内容:量化模型实际上执行推理的速度更快吗? 让我们测试一下:
......@@ -798,7 +798,7 @@ Elapsed time: 4 ms
在 MacBook Pro 上本地运行此程序,常规模型的运行时间为 61 毫秒,而量化模型的运行时间仅为 20 毫秒,这说明了量化模型与浮点模型相比,典型的 2-4 倍加速。
## 结论](docs / modern-java-zh /
## 结论
在本教程中,我们展示了两种量化方法-训练后静态量化和量化感知训练-描述它们在“幕后”进行的操作以及如何在 PyTorch 中使用它们。
......
......@@ -23,7 +23,7 @@
在本部分中,您将使用第一种方法-使用量化模型提取特征。
## 第 0 部分。先决条件](docs / modern-java-zh /
## 第 0 部分。先决条件
在深入学习迁移学习之前,让我们回顾一下“先决条件”,例如安装和数据加载/可视化。
......@@ -50,7 +50,7 @@ pip install --pre torch torchvision -f https://download.pytorch.org/whl/nightly/
```
### 加载数据](docs / modern-java-zh /
### 加载数据
注意
......@@ -135,7 +135,7 @@ imshow(out, title=[class_names[x] for x in classes], ax=ax)
```
### 模型训练的支持功能](docs / modern-java-zh /
### 模型训练的支持功能
以下是模型训练的通用功能。 此功能也
......@@ -224,7 +224,7 @@ def train_model(model, criterion, optimizer, scheduler, num_epochs=25, device='c
```
### 可视化模型预测的支持功能](docs / modern-java-zh /
### 可视化模型预测的支持功能
通用功能,显示一些图像的预测
......@@ -259,7 +259,7 @@ def visualize_model(model, rows=3, cols=3):
```
## 第 1 部分。训练基于量化特征提取器的自定义分类器](docs / modern-java-zh /
## 第 1 部分。训练基于量化特征提取器的自定义分类器
在本节中,您将使用“冻结”量化特征提取器,并在其顶部训练自定义分类器头。 与浮点模型不同,您无需为量化模型设置 require_grad = False,因为它没有可训练的参数。 请参阅[文档](https://pytorch.org/docs/stable/quantization.html)了解更多详细信息。
......@@ -340,7 +340,7 @@ exp_lr_scheduler = optim.lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.
```
### 训练和评估](docs / modern-java-zh /
### 训练和评估
此步骤在 CPU 上大约需要 15-25 分钟。 由于量化模型只能在 CPU 上运行,因此您不能在 GPU 上运行训练。
......@@ -353,7 +353,7 @@ plt.tight_layout()
```
## 第 2 部分。微调可量化模型](docs / modern-java-zh /
## 第 2 部分。微调可量化模型
在这一部分中,我们将微调用于迁移学习的特征提取器,并对特征提取器进行量化。 请注意,在第 1 部分和第 2 部分中,特征提取器都是量化的。 不同之处在于,在第 1 部分中,我们使用了预训练的量化模型。 在这一部分中,我们将在对感兴趣的数据集进行微调之后创建一个量化的特征提取器,因此这是一种在具有量化优势的同时通过迁移学习获得更好的准确率的方法。 请注意,在我们的特定示例中,训练集非常小(120 张图像),因此微调整个模型的好处并不明显。 但是,此处显示的过程将提高使用较大数据集进行传递学习的准确率。
......@@ -381,7 +381,7 @@ plt.tight_layout()
#步骤 1 model.train()model.fuse_model()#步骤 2 model_ft = create_combined_model(model)model_ft [0] .qconfig = torch.quantization.default_qat_qconfig#使用默认 QAT 配置#步骤 3 model_ft = torch.quantization.prepare_qat( model_ft,inplace = True)
### 微调模型](docs / modern-java-zh /
### 微调模型
在当前教程中,整个模型都经过了微调。 通常,这将导致更高的精度。 但是,由于此处使用的训练集很小,最终导致我们过度适应了训练集。
......
......@@ -6,7 +6,7 @@
这是`torch.distributed`软件包的概述页面。 由于在不同位置添加了越来越多的文档,示例和教程,因此不清楚要针对特定​​问题咨询哪个文档或教程,或者阅读这些内容的最佳顺序是什么。 该页面的目的是通过将文档分类为不同的主题并简要描述每个主题来解决此问题。 如果这是您第一次使用 PyTorch 构建分布式训练应用程序,建议使用本文档导航至最适合您的用例的技术。
## 简介](docs / modern-java-zh /
## 简介
从 PyTorch v1.6.0 开始,`torch.distributed`中的功能可以分为三个主要组件:
......@@ -16,7 +16,7 @@
现有的大多数文档都是为 DDP 或 RPC 编写的,本页面的其余部分将详细介绍这两个组件的材料。
## 数据并行训练](docs / modern-java-zh /
## 数据并行训练
PyTorch 为数据并行训练提供了几种选择。 对于从简单到复杂以及从原型到生产逐渐增长的应用程序,共同的发展轨迹将是:
......@@ -30,11 +30,11 @@ PyTorch 为数据并行训练提供了几种选择。 对于从简单到复杂
数据并行训练还可以与[自动混合精度(AMP)](https://pytorch.org/docs/master/notes/amp_examples.html#working-with-multiple-gpus)一起使用。
### `torch.nn.DataParallel`](docs / modern-java-zh /
### `torch.nn.DataParallel`
[DataParallel](https://pytorch.org/docs/master/generated/torch.nn.DataParallel.html) 软件包以最低的编码障碍实现了单机多 GPU 并行处理。 它只需要一行更改应用程序代码。 教程[可选:数据并行](https://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html)显示了一个示例。 需要注意的是,尽管`DataParallel`非常易于使用,但通常无法提供最佳性能。 这是因为`DataParallel`的实现会在每个前向传递中复制该模型,并且其单进程多线程并行性自然会遭受 GIL 争用。 为了获得更好的性能,请考虑使用 [DistributedDataParallel](https://pytorch.org/docs/master/generated/torch.nn.parallel.DistributedDataParallel.html)
### `torch.nn.parallel.DistributedDataParallel`](docs / modern-java-zh /
### `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)。
......@@ -45,11 +45,11 @@ DDP 材料如下:
3. [启动和配置分布式数据并行应用程序](https://github.com/pytorch/examples/blob/master/distributed/ddp/README.md)文档显示了如何使用 DDP 启动脚本。
4. [带有 Amazon AWS 的 PyTorch 分布式训练师](aws_distributed_training_tutorial.html)演示了如何在 AWS 上使用 DDP。
### TorchElastic](docs / modern-java-zh /
### TorchElastic
随着应用程序复杂性和规模的增长,故障恢复成为当务之急。 有时,使用 DDP 时不可避免地会遇到 OOM 之类的错误,但是 DDP 本身无法从这些错误中恢复,基本的`try-except`块也无法工作。 这是因为 DDP 要求所有进程以紧密同步的方式运行,并且在不同进程中启动的所有`AllReduce`通信都必须匹配。 如果组中的某个进程抛出 OOM 异常,则很可能导致不同步(`AllReduce`操作不匹配),从而导致崩溃或挂起。 如果您期望在训练过程中发生故障,或者资源可能会动态离开并加入,请使用 [Torrlastic](https://pytorch.org/elastic) 启动分布式数据并行训练。
## 通用分布式训练](docs / modern-java-zh /
## 通用分布式训练
许多训练范式不适合数据并行性,例如参数服务器范式,分布式管道并行性,具有多个观察者或代理的强化学习应用程序等。 [Torch.distributed.rpc](https://pytorch.org/docs/master/rpc.html) 旨在支持一般的分布式训练方案 。
......
......@@ -12,7 +12,7 @@
对于模型跨越多个服务器的分布式模型并行训练,请参考[分布式 RPC 框架入门](rpc_tutorial.html),以获取示例和详细信息。
## 基本用法](docs / modern-java-zh /
## 基本用法
让我们从包含两个线性层的玩具模型开始。 要在两个 GPU 上运行该模型,只需将每个线性层放置在不同的 GPU 上,然后移动输入和中间输出以匹配层设备。
......@@ -49,7 +49,7 @@ optimizer.step()
```
## 将模型并行应用于现有模块](docs / modern-java-zh /
## 将模型并行应用于现有模块
只需进行几行更改,就可以在多个 GPU 上运行现有的单 GPU 模块。 以下代码显示了如何将`torchvision.models.resnet50()`分解为两个 GPU。 这个想法是继承现有的`ResNet`模块,并在构建过程中将层拆分为两个 GPU。 然后,通过相应地移动中间输出,覆盖`forward`方法来缝合两个子网。
......
......@@ -20,7 +20,7 @@
本教程中的代码在 8-GPU 服务器上运行,但可以轻松地推广到其他环境。
## `DataParallel`和`DistributedDataParallel`之间的比较](docs / modern-java-zh /
## `DataParallel`和`DistributedDataParallel`之间的比较
在深入探讨之前,让我们澄清一下为什么尽管增加了复杂性,但还是考虑使用`DistributedDataParallel`而不是`DataParallel`
......@@ -28,7 +28,7 @@
* 回顾[先前的教程](https://pytorch.org/tutorials/intermediate/model_parallel_tutorial.html),如果模型太大而无法容纳在单个 GPU 上,则必须使用**模型并行**将其拆分到多个 GPU 中。 `DistributedDataParallel`**模型并行**一起使用; `DataParallel`目前没有。 当 DDP 与模型并行组合时,每个 DDP 进程将并行使用模型,而所有进程共同将并行使用数据。
* 如果您的模型需要跨越多台机器,或者您的用例不适合数据并行性范式,请参阅 [RPC API](https://pytorch.org/docs/stable/rpc.html) ,以获得更多通用的分布式训练支持。
## 基本用例](docs / modern-java-zh /
## 基本用例
要创建 DDP 模块,请首先正确设置过程组。 更多细节可以在[用 PyTorch 编写分布式应用程序](https://pytorch.org/tutorials/intermediate/dist_tuto.html)中找到。
......@@ -113,11 +113,11 @@ def run_demo(demo_fn, world_size):
如您所见,DDP 包装了较低级别的分布式通信详细信息,并提供了干净的 API,就好像它是本地模型一样。 梯度同步通信发生在反向传递过程中,并且与反向计算重叠。 当`backward()`返回时,`param.grad`已经包含同步梯度张量。 对于基本用例,DDP 仅需要几个 LoC 即可设置流程组。 在将 DDP 应用到更高级的用例时,需要注意一些警告。
## 歪斜的处理速度](docs / modern-java-zh /
## 歪斜的处理速度
在 DDP 中,构造函数,正向传递和反向传递都是分布式同步点。 预期不同的进程将启动相同数量的同步,并以相同的顺序到达这些同步点,并在大致相同的时间进入每个同步点。 否则,快速流程可能会提早到达,并在等待流浪者时超时。 因此,用户负责平衡流程之间的工作负载分配。 有时,由于例如网络延迟,资源争夺,不可预测的工作量峰值,不可避免地会出现处理速度偏差。 为了避免在这种情况下超时,请在调用 [init_process_group](https://pytorch.org/docs/stable/distributed.html#torch.distributed.init_process_group) 时传递足够大的`timeout`值。
## 保存和加载检查点](docs / modern-java-zh /
## 保存和加载检查点
在训练过程中通常使用`torch.save``torch.load`来检查点模块并从检查点中恢复。 有关更多详细信息,请参见[保存和加载模型](https://pytorch.org/tutorials/beginner/saving_loading_models.html)。 使用 DDP 时,一种优化方法是仅在一个进程中保存模型,然后将其加载到所有进程中,从而减少写开销。 这是正确的,因为所有过程都从相同的参数开始,并且梯度在向后传递中同步,因此优化程序应将参数设置为相同的值。 如果使用此优化,请确保在保存完成之前不要启动所有进程。 此外,在加载模块时,您需要提供适当的`map_location`参数,以防止进程进入其他设备。 如果缺少`map_location`,则`torch.load`将首先将模块加载到 CPU,然后将每个参数复制到保存位置,这将导致同一台机器上的所有进程使用相同的设备集。 有关更高级的故障恢复和弹性支持,请参考[这里](https://pytorch.org/elastic)
......@@ -165,7 +165,7 @@ def demo_checkpoint(rank, world_size):
```
## 将 DDP 与模型并行性结合起来](docs / modern-java-zh /
## 将 DDP 与模型并行性结合起来
DDP 还可以与多 GPU 模型一起使用。 当训练具有大量数据的大型模型时,DDP 包装多 GPU 模型特别有用。
......
......@@ -52,7 +52,7 @@ if __name__ == "__main__":
让我们看一下`init_process`功能。 它确保每个进程都可以使用相同的 IP 地址和端口通过主机进行协调。 请注意,我们使用了`gloo`后端,但其他后端也可用。 (请参阅[第 5.1 节](#communication-backends)),我们将在本教程的结尾部分介绍`dist.init_process_group`中发生的魔术,但实际上,它允许进程通过共享位置相互进行通信。
## 点对点通信](docs / modern-java-zh /
## 点对点通信
[![Send and Recv](img/f29264b289639882a61fb5c3447b1ecc.png)](../_img/send_recv.png)
......@@ -146,7 +146,7 @@ def run(rank, size):
* `dist.all_gather(tensor_list, tensor, group)`:将所有进程中的`tensor`从所有进程复制到`tensor_list`
* `dist.barrier(group)`:阻止<cite></cite>中的所有进程,直到每个进程都进入此功能。
## 分布式训练](docs / modern-java-zh /
## 分布式训练
**注意:**您可以在此 GitHub 存储库的[中找到本节的示例脚本。](https://github.com/seba-1511/dist_tuto.pth/)
......@@ -257,7 +257,7 @@ def average_gradients(model):
**注意:**虽然从技术上来说最后一句话是是正确的,但要实现同步 SGD 的生产级实现,还需要[更多技巧。 同样,请使用经过测试和优化的](https://seba-1511.github.io/dist_blog)[](https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel)
### 我们自己的 Ring-Allreduce](docs / modern-java-zh /
### 我们自己的 Ring-Allreduce
另一个挑战是,假设我们想实现 DeepSpeech 的高效环网减少。 使用点对点集合很容易实现。
......@@ -291,14 +291,14 @@ def allreduce(send, recv):
在上面的脚本中,`allreduce(send, recv)`函数的签名与 PyTorch 中的签名略有不同。 它需要一个`recv`张量,并将所有`send`张量的总和存储在其中。 作为练习留给读者,我们的版本与 DeepSpeech 中的版本之间仍然有一个区别:它们的实现将梯度张量划分为*个块*,以便最佳地利用通信带宽。 (提示: [torch.chunk](https://pytorch.org/docs/stable/torch.html#torch.chunk)
## 高级主题](docs / modern-java-zh /
## 高级主题
现在,我们准备发现`torch.distributed`的一些更高级的功能。 由于涉及的内容很多,本节分为两个小节:
1. 通讯后端:我们在这里学习如何使用 MPI 和 Gloo 进行 GPU-GPU 通讯。
2. 初始化方法:我们了解如何在`dist.init_process_group()`中最佳设置初始协调阶段。
### 通讯后端](docs / modern-java-zh /
### 通讯后端
`torch.distributed`最优雅的方面之一是它具有抽象能力,并且可以在不同的后端之上构建。 如前所述,目前在 PyTorch 中实现了三个后端:Glo,NCCL 和 MPI。 它们各自具有不同的规格和权衡,具体取决于所需的用例。 可以在此处找到支持功能的比较表。
......@@ -335,7 +335,7 @@ def allreduce(send, recv):
[NCCL 后端](https://github.com/nvidia/nccl)提供了针对 CUDA 张量的集体运算的优化实现。 如果仅对集体操作使用 CUDA 张量,请考虑使用此后端以获得最佳性能。 NCCL 后端包含在具有 CUDA 支持的预构建二进制文件中。
### 初始化方法](docs / modern-java-zh /
### 初始化方法
为了完成本教程,我们来谈谈我们称为的第一个功能:`dist.init_process_group(backend, init_method)`。 特别是,我们将介绍负责每个过程之间初始协调步骤的不同初始化方法。 这些方法使您可以定义协调方式。 根据您的硬件设置,这些方法之一自然应该比其他方法更合适。 除了以下各节之外,您还应该查看[官方文档](https://pytorch.org/docs/stable/distributed.html#initialization)
......
......@@ -18,7 +18,7 @@
[torch.distributed.rpc](https://pytorch.org/docs/master/rpc.html) 程序包可以帮助解决上述情况。 在情况 1 中, [RPC](https://pytorch.org/docs/master/rpc.html#rpc)[RRef](https://pytorch.org/docs/master/rpc.html#rref) 允许将数据从一个工作程序发送到另一个工作程序,同时轻松引用远程数据对象。 在情况 2 中,[分布式 autograd](https://pytorch.org/docs/master/rpc.html#distributed-autograd-framework)[分布式优化器](https://pytorch.org/docs/master/rpc.html#module-torch.distributed.optim)使执行反向传递和优化器步骤就像本地训练一样。 在接下来的两节中,我们将使用强化学习示例和语言模型示例来演示 [torch.distributed.rpc](https://pytorch.org/docs/master/rpc.html) 的 API。 请注意,本教程并非旨在构建最准确或最有效的模型来解决给定的问题,相反,此处的主要目标是演示如何使用 [torch.distributed.rpc](https://pytorch.org/docs/master/rpc.html) 包来构建分布式训练 应用程序。
## 使用 RPC 和 RRef 进行分布式强化学习](docs / modern-java-zh /
## 使用 RPC 和 RRef 进行分布式强化学习
本节介绍了使用 RPC 建立玩具分布式强化学习模型以解决 [OpenAI Gym](https://gym.openai.com) 中的 CartPole-v1 的步骤。 策略代码主要是从现有的单线程[示例](https://github.com/pytorch/examples/blob/master/reinforcement_learning)中借用的,如下所示。 我们将跳过`Policy`设计的详细信息,并将重点介绍 RPC 的用法。
......@@ -303,7 +303,7 @@ Solved! Running reward is now 475.3163778435275!
接下来,我们将展示如何将 RPC 和 RRef 与分布式 autograd 和分布式优化器结合起来执行分布式模型并行训练。
## 使用 Distributed Autograd 和 Distributed Optimizer 的分布式 RNN](docs / modern-java-zh /
## 使用 Distributed Autograd 和 Distributed Optimizer 的分布式 RNN
在本节中,我们将使用 RNN 模型来展示如何使用 RPC API 构建分布式模型并行训练。 示例 RNN 模型非常小,可以轻松地放入单个 GPU 中,但是我们仍将其层划分为两个不同的工人来演示这一想法。 开发人员可以应用类似的技术在多个设备和机器上分布更大的模型。
......
......@@ -21,13 +21,13 @@
本教程的完整源代码可以在 [pytorch / examples](https://github.com/pytorch/examples/tree/master/distributed/rpc/pipeline) 中找到。
## 基础知识](docs / modern-java-zh /
## 基础知识
上一教程[分布式 RPC 框架入门](rpc_tutorial.html)显示了如何使用 [torch.distributed.rpc](https://pytorch.org/docs/master/rpc.html) 为 RNN 模型实现分布式模型并行性。 该教程使用一个 GPU 来托管`EmbeddingTable`,并且提供的代码可以正常工作。 但是,如果模型驻留在多个 GPU 上,则将需要一些额外的步骤来增加所有 GPU 的摊销利用率。 管道并行性是在这种情况下可以提供帮助的一种范例。
在本教程中,我们使用`ResNet50`作为示例模型,[单机模型并行最佳实践](model_parallel_tutorial.html)教程也使用了该模型。 类似地,`ResNet50`模型被分为两个碎片,输入批量被划分为多个拆分,并以流水线方式馈入两个模型碎片。 区别在于,本教程将调用异步 RPC,而不是使用 CUDA 流来并行执行。 因此,本教程中介绍的解决方案也可以跨计算机边界使用。 本教程的其余部分分四个步骤介绍了实现。
## 第 1 步:对 ResNet50 模型进行分区](docs / modern-java-zh /
## 第 1 步:对 ResNet50 模型进行分区
这是在两个模型分片中实现`ResNet50`的准备步骤。 以下代码是从 Torchvision 中的 [ResNet 实现中借用的。 `ResNetBase`模块包含两个 ResNet 碎片的通用构件和属性。](https://github.com/pytorch/vision/blob/7c077f6a986f05383bcb86b535aedb5a63dd5c4b/torchvision/models/resnet.py#L124)
......@@ -136,7 +136,7 @@ class ResNetShard2(ResNetBase):
```
## 第 2 步:将 ResNet50 模型碎片拼接到一个模块中](docs / modern-java-zh /
## 第 2 步:将 ResNet50 模型碎片拼接到一个模块中
然后,我们创建一个`DistResNet50`模块来组装两个分片并实现流水线并行逻辑。 在构造函数中,我们使用两个`rpc.remote`调用分别将两个分片放在两个不同的 RPC 工作器上,并保持`RRef`到两个模型部分,以便可以在正向传递中引用它们。 `forward`函数将输入批量分为多个微批量,并将这些微批量以流水线方式馈送到两个模型部件。 它首先使用`rpc.remote`调用将第一个分片应用于微批量,然后将返回的中间输出`RRef`转发到第二个模型分片。 之后,它将收集所有微输出的`Future`,并在循环后等待所有它们。 请注意,`remote()``rpc_async()`都立即返回并异步运行。 因此,整个循环是非阻塞的,并将同时启动多个 RPC。 中间输出`y_rref`保留了两个模型零件上一个微批量的执行顺序。 微批量的执行顺序无关紧要。 最后,正向函数将所有微批量的输出连接到一个单一的输出张量中并返回。 `parameter_rrefs`功能是简化分布式优化器构造的助手,将在以后使用。
......@@ -181,7 +181,7 @@ class DistResNet50(nn.Module):
```
## 步骤 3:定义训练循环](docs / modern-java-zh /
## 步骤 3:定义训练循环
定义模型后,让我们实施训练循环。 我们使用专门的“主”工作人员来准备随机输入和标签,并控制分布式反向传递和分布式优化器步骤。 它首先创建`DistResNet50`模块的实例。 它指定每个批量的微批数量,并提供两个 RPC 工作程序的名称(即“ worker1”和“ worker2”)。 然后,它定义损失函数,并使用`parameter_rrefs()`帮助器创建`DistributedOptimizer`以获取参数`RRefs`的列表。 然后,主训练循环与常规本地训练非常相似,除了它使用`dist_autograd`向后启动并为反向和优化器`step()`提供`context_id`之外。
......@@ -223,7 +223,7 @@ def run_master(num_split):
```
## 第 4 步:启动 RPC 进程](docs / modern-java-zh /
## 第 4 步:启动 RPC 进程
最后,下面的代码显示了所有进程的目标函数。 主要逻辑在`run_master`中定义。 工作人员被动地等待主服务器发出的命令,因此只需运行`init_rpc``shutdown`即可,其中默认情况下`shutdown`会阻塞,直到所有 RPC 参与者都完成。
......
......@@ -17,7 +17,7 @@
本教程需要 PyTorch v1.6.0 或更高版本。
## 基础知识](docs / modern-java-zh /
## 基础知识
先前的教程显示了使用 [torch.distributed.rpc](https://pytorch.org/docs/stable/rpc.html) 构建分布式训练应用程序的步骤,但并未详细说明在处理 RPC 请求时被调用方发生的情况。 从 PyTorch v1.5 开始,每个 RPC 请求都会在被调用方上阻塞一个线程,以在该请求中执行该函数,直到该函数返回为止。 这适用于许多用例,但有一个警告。 如果用户功能例如通过嵌套 RPC 调用在 IO 上阻塞,或者例如在等待其他 RPC 请求解除阻塞的信号时阻塞,则被调用方上的 RPC 线程将必须空闲,直到 IO 完成或发生信令事件为止。 结果,RPC 被调用者可能使用了不必要的更多线程。 造成此问题的原因是 RPC 将用户功能视为黑匣子,并且几乎不了解该功能会发生什么。 为了允许用户函数产生和释放 RPC 线程,需要向 RPC 系统提供更多提示。
......@@ -30,7 +30,7 @@
除了减少被调用方上的空闲线程数之外,这些工具还有助于使批量 RPC 处理更容易,更快捷。 本教程的以下两节演示了如何使用 [@ rpc.functions.async_execution](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution) 装饰器来构建分布式批更新参数服务器和批量强化学习应用程序。
## 批量更新参数服务器](docs / modern-java-zh /
## 批量更新参数服务器
考虑具有一个参数服务器(PS)和多个训练师的同步参数服务器训练应用程序。 在此应用程序中,PS 保留参数并等待所有训练师报告坡度。 在每次迭代中,它都会等到收到所有训练者的梯度后,再一次更新所有参数。 下面的代码显示 PS 类的实现。 `update_and_fetch_model`方法是用`@rpc.functions.async_execution`装饰的,将由训练师调用。 每次调用都会返回一个`Future`对象,该对象将填充有更新的模型。 大多数训练师发起的调用仅将梯度累积到`.grad`字段,立即返回,并在 PS 上产生 RPC 线程。 最后到达的训练师将触发优化器步骤,并消耗所有先前报告的梯度。 然后,它使用更新的模型设置`future_model`,该模型又通过`Future`对象通知其他训练师的所有先前请求,并将更新后的模型发送给所有训练师。
......@@ -128,7 +128,7 @@ class Trainer(object):
本节使用一个简单的参数服务器训练示例来说明如何使用 [@ rpc.functions.async_execution](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution) 装饰器实现批量 RPC 应用程序。 在下一节中,我们将使用批量重新实现上一[分布式 RPC 框架](https://pytorch.org/tutorials/intermediate/rpc_tutorial.html)入门指南中的强化学习示例,并演示其对训练速度的影响。
## 批量 CartPole 解算器](docs / modern-java-zh /
## 批量 CartPole 解算器
本节以 [OpenAI Gym](https://gym.openai.com/) 中的 CartPole-v1 为例,说明批量 RPC 的性能影响。 请注意,我们的目标是演示 [@ rpc.functions.async_execution](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution) 的用法,而不是构建最佳的 CartPole 求解器或解决大多数不同的 RL 问题,我们使用非常简单的策略和奖励计算策略,并将重点放在 多观察者单代理批量 RPC 实现。 我们使用与前面的教程类似的`Policy`模型,如下所示。 与上一教程相比,不同之处在于其构造函数使用了一个附加的`batch`参数来控制`F.softmax``dim`参数,因为进行批量时,`forward`函数中的`x`参数包含来自多个观察者的状态 因此尺寸需要适当更改。 其他所有内容保持不变。
......@@ -385,7 +385,7 @@ if __name__ == '__main__':
![](img/f5504c7ed93640f2bed4d2a606c015ba.png)
## 了解更多](docs / modern-java-zh /
## 了解更多
* [批量更新参数服务器源代码](https://github.com/pytorch/examples/blob/master/distributed/rpc/batch/parameter_server.py)
* [批量 CartPole 解算器](https://github.com/pytorch/examples/blob/master/distributed/rpc/batch/reinforce.py)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册