From bf006681bc97dc04245b54ac6fa34170c0b220c1 Mon Sep 17 00:00:00 2001 From: wizardforcel <562826179@qq.com> Date: Thu, 14 Oct 2021 22:31:23 +0800 Subject: [PATCH] 2021-10-14 22:31:23 --- docs/0.4/0.md | 1 + docs/0.4/1.md | 73 + docs/0.4/10.md | 3267 +++++++++++++++++++++++++ docs/0.4/11.md | 1283 ++++++++++ docs/0.4/12.md | 120 + docs/0.4/13.md | 102 + docs/0.4/14.md | 267 ++ docs/0.4/15.md | 53 + docs/0.4/16.md | 1636 +++++++++++++ docs/0.4/17.md | 1299 ++++++++++ docs/0.4/18.md | 194 ++ docs/0.4/19.md | 507 ++++ docs/0.4/2.md | 113 + docs/0.4/20.md | 236 ++ docs/0.4/21.md | 7 + docs/0.4/22.md | 71 + docs/0.4/23.md | 260 ++ docs/0.4/24.md | 35 + docs/0.4/25.md | 57 + docs/0.4/26.md | 131 + docs/0.4/27.md | 126 + docs/0.4/28.md | 24 + docs/0.4/29.md | 29 + docs/0.4/3.md | 180 ++ docs/0.4/30.md | 142 ++ docs/0.4/31.md | 11 + docs/0.4/32.md | 1 + docs/0.4/33.md | 23 + docs/0.4/34.md | 203 ++ docs/0.4/35.md | 161 ++ docs/0.4/36.md | 133 + docs/0.4/37.md | 35 + docs/0.4/4.md | 155 ++ docs/0.4/5.md | 90 + docs/0.4/6.md | 81 + docs/0.4/7.md | 32 + docs/0.4/8.md | 226 ++ docs/0.4/9.md | 1 + docs/0.4/README.md | 13 + docs/0.4/SUMMARY.md | 39 + docs/0.4/img/2017121722000236815.jpg | Bin 0 -> 26949 bytes docs/0.4/img/2018033000352689884.jpeg | Bin 0 -> 16557 bytes docs/0.4/img/65ca6a0b.jpg | Bin 0 -> 89205 bytes docs/0.4/img/73dd6bbb.png | Bin 0 -> 32165 bytes docs/0.4/img/cover_image.jpg | Bin 0 -> 81598 bytes 45 files changed, 11417 insertions(+) create mode 100644 docs/0.4/0.md create mode 100644 docs/0.4/1.md create mode 100644 docs/0.4/10.md create mode 100644 docs/0.4/11.md create mode 100644 docs/0.4/12.md create mode 100644 docs/0.4/13.md create mode 100644 docs/0.4/14.md create mode 100644 docs/0.4/15.md create mode 100644 docs/0.4/16.md create mode 100644 docs/0.4/17.md create mode 100644 docs/0.4/18.md create mode 100644 docs/0.4/19.md create mode 100644 docs/0.4/2.md create mode 100644 docs/0.4/20.md create mode 100644 docs/0.4/21.md create mode 100644 docs/0.4/22.md create mode 100644 docs/0.4/23.md create mode 100644 docs/0.4/24.md create mode 100644 docs/0.4/25.md create mode 100644 docs/0.4/26.md create mode 100644 docs/0.4/27.md create mode 100644 docs/0.4/28.md create mode 100644 docs/0.4/29.md create mode 100644 docs/0.4/3.md create mode 100644 docs/0.4/30.md create mode 100644 docs/0.4/31.md create mode 100644 docs/0.4/32.md create mode 100644 docs/0.4/33.md create mode 100644 docs/0.4/34.md create mode 100644 docs/0.4/35.md create mode 100644 docs/0.4/36.md create mode 100644 docs/0.4/37.md create mode 100644 docs/0.4/4.md create mode 100644 docs/0.4/5.md create mode 100644 docs/0.4/6.md create mode 100644 docs/0.4/7.md create mode 100644 docs/0.4/8.md create mode 100644 docs/0.4/9.md create mode 100644 docs/0.4/README.md create mode 100644 docs/0.4/SUMMARY.md create mode 100644 docs/0.4/img/2017121722000236815.jpg create mode 100644 docs/0.4/img/2018033000352689884.jpeg create mode 100644 docs/0.4/img/65ca6a0b.jpg create mode 100644 docs/0.4/img/73dd6bbb.png create mode 100644 docs/0.4/img/cover_image.jpg diff --git a/docs/0.4/0.md b/docs/0.4/0.md new file mode 100644 index 00000000..c1dd0888 --- /dev/null +++ b/docs/0.4/0.md @@ -0,0 +1 @@ +# 笔记 \ No newline at end of file diff --git a/docs/0.4/1.md b/docs/0.4/1.md new file mode 100644 index 00000000..197aeea6 --- /dev/null +++ b/docs/0.4/1.md @@ -0,0 +1,73 @@ +# 自动求导机制 + +1. [从后向中排除子图](#excluding-subgraphs-from-backward) + * [requires_grad](#requires_grad) + * [volatile](#volatile) +2. [自动求导如何编码历史信息](#how-autograd-encodes-the-history) +3. [Variable 上的 In-place 操作](#in-place-operations-on-variables) +4. [In-place 正确性检查](#in-place-correctness-checks) + +* * * + +本说明将概述 Autograd 如何工作并记录操作。没有必要全部了解,但建议您熟悉它,他可以将帮助你编写程序更高效,更清洁;同时还可以帮助您进行调试。 + +### 向后排除子视图: + +每个变量都有一个标记:`requires_grad`允许从梯度计算中细分排除子图,并可以提高效率。 + +#### requires_grad + +如果一个输入变量定义`requires_grad`,那么他的输出也可以使用`requires_grad`;相反,只有当所有的输入变量都不定义`requires_grad`梯度,才不会输出梯度。如果其中所有的变量都不需要计算梯度,在子图中从不执行向后计算。 + +``` +>>> x = Variable(torch.randn(5, 5)) +>>> y = Variable(torch.randn(5, 5)) +>>> z = Variable(torch.randn(5, 5), requires_grad=True) +>>> a = x + y +>>> a.requires_grad +False +>>> b = a + z +>>> b.requires_grad +True +``` + +当您想要冻结部分模型时,这个标志特别有用;除非您事先知道不会使用到某些参数的梯度。 + +例如,如果您想调整预训练的`CNN`,只要切换冻结模型中的`requires_grad`标志即可,直到计算到最后一层才会保存中间缓冲区,仿射变换和网络输出都需要使用梯度的权值。 + +``` +model = torchvision.models.resnet18(pretrained=True) +for param in model.parameters(): + param.requires_grad = False +# Replace the last fully-connected layer +# Parameters of newly constructed modules have requires_grad=True by default +model.fc = nn.Linear(512, 100) + +# Optimize only the classifier +optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9) +``` + +### autograd 如何编码历史信息: + +每个变量都有一个`.creator`属性,它指向把它作为输出的函数。这是一个由`Function`对象作为节点组成的有向无环图(DAG)的入口点,它们之间的引用就是图的边。每次执行一个操作时,一个表示它的新`Function`就被实例化,它的`forward()`方法被调用,并且它输出的`Variable`的创建者被设置为这个`Function`。然后,通过跟踪从任何变量到叶节点的路径,可以重建创建数据的操作序列,并自动计算梯度。 + +需要注意的一点是,整个图在每次迭代时都是从头开始重新创建的,这就允许使用任意的 Python 控制流语句,这样可以在每次迭代时改变图的整体形状和大小。在启动训练之前不必对所有可能的路径进行编码—— what you run is what you differentiate. + +### Variable 上的 In-place 操作: + +支持自动归档中的就地操作是一件很困难的事情,我们在大多数情况下都不鼓励使用它们。Autograd 的积极缓冲区释放和重用使其非常高效,并且在现场操作实际上会降低内存使用量的情况下,极少数场合很少。除非您在内存压力很大的情况下运行,否则您可能永远不需要使用它们。 + +限制现场操作适用性的两个主要原因: + +1. 覆盖计算梯度所需的值。这就是为什么变量不支持`log_`。其梯度公式需要原始输入,而通过计算逆运算可以重新创建它,它在数值上是不稳定的,并且需要额外的工作,这往往会失败使用这些功能的目的。 +2. 每个`in-place`操作实际上需要实现重写计算图。不合适的版本只需分配新对象,并保留对旧图的引用,而`in-place`操作则需要将所有输入的`creator`更改为`Function`表示此操作。这就比较棘手,特别是如果有许多变量引用相同的存储(例如通过索引或转置创建的),并且如果被修改输入的存储被其他`Variable`引用,则`in-place`函数实际上会抛出错误。 + +### In-place 正确性检测: + +每个变量都保留一个版本计数器`version counter`,当在任何操作中被使用时,它都会递增。当函数保存任何用于后向的`tensor`时,还会保存其包含变量的版本计数器`version counter`。一旦访问,`self.saved_tensors`它被会被检查,如果它大于保存的值,则会引起错误。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/10.md b/docs/0.4/10.md new file mode 100644 index 00000000..be7e4eac --- /dev/null +++ b/docs/0.4/10.md @@ -0,0 +1,3267 @@ +# Torch + +`torch`包含了多维张量的数据结构以及基于其上的多种数学运算。此外,它也提供了多种实用工具,其中一些可以更有效地对张量和任意类型进行序列化的工具。 + +它具有 CUDA 的对应实现,可以在`NVIDIA GPU`上进行张量运算(计算能力>=3.0)。 + +## 张量 Tensors + +``` +torch.is_tensor(obj) +``` + +判断是否为张量,如果是 pytorch 张量,则返回 True + +* 参数: obj (Object) – 判断对象 + +``` +torch.is_storage(obj) +``` + +判断是否为 pytorch Storage,如何是,则返回 True + +* 参数: input (Object) – 判断对象 + +``` +torch.set_default_tensor_type(t) +``` + +``` +torch.numel(input)->int +``` + +返回`input` 张量中的元素个数 + +* 参数: input ([Tensor](http://pytorch.org/docs/tensors.html#torch.Tensor)) – 输入张量 + +例子: + +``` +>>> a = torch.randn(1,2,3,4,5) +>>> torch.numel(a) +120 +>>> a = torch.zeros(4,4) +>>> torch.numel(a) +16 +``` + +``` +torch.set_printoptions(precision=None, threshold=None, edgeitems=None, linewidth=None, profile=None) +``` + +设置打印选项。 完全参考自 [Numpy](https://docs.scipy.org/doc/numpy/reference/generated/numpy.set_printoptions.html)。 + +参数: + +* `precision` – 浮点数输出的精度位数 (默认为 8 ) +* `threshold` – 阈值,触发汇总显示而不是完全显示(repr)的数组元素的总数 (默认为 1000) +* `edgeitems` – 每个维度的开头和结尾的摘要中的数组项目数(默认为 3)。 +* `linewidth` – 用于插入行间隔的每行字符数(默认为 80)。阈值矩阵将忽略此参数。 +* `profile` – pretty 打印的完全默认值。 可以覆盖上述所有选项 (默认为 short, full) + +## 创建操作 Creation Ops + +``` +torch.eye(n, m=None, out=None) +``` + +返回一个 2 维张量,对角线数字为 1,其它位置为 0 + +参数: + +* `n` (int) – 行数 +* `m` (int, 可选) – 列数.如果为 None,则默认为*n* +* `out` (Tensor,可选) - 输出张量 + +返回值: 2 维张量,对角线为 1,其它为 0 + +返回类型: [Tensor](http://pytorch.org/docs/tensors.html#torch.Tensor) + +例子: + +``` +>>> torch.eye(3) + 1 0 0 + 0 1 0 + 0 0 1 +[torch.FloatTensor of size 3x3] +``` + +``` +torch.from_numpy(ndarray) → Tensor +``` + +将`numpy.ndarray`转换为`Tensor`。 返回的张量 tensor 和 numpy 的 ndarray 共享同一内存空间。修改一个会导致另外一个也被修改。返回的张量不能调整大小。 + +例子: + +``` +>>> a = numpy.array([1, 2, 3]) +>>> t = torch.from_numpy(a) +>>> t +torch.LongTensor([1, 2, 3]) +>>> t[0] = -1 +>>> a +array([-1, 2, 3]) +``` + +``` +torch.linspace(start, end, steps=100, out=None) → Tensor +``` + +返回 start 和 end 之间长度为`steps`的一维张量 参数: + +* `start (float)` – 点集的起始值 +* `end (float)` – 点集的最终值 +* `steps (int)` – 在`start` 和 `end`间的采样数,即返回多少个数 +* `out (Tensor, 可选的)` – 结果张量 + +例子: + +``` +>>> torch.linspace(1.0,10.0,steps=5,out=None) + + 1.0000 + 3.2500 + 5.5000 + 7.7500 + 10.0000 +[torch.FloatTensor of size 5] + +>>> torch.linspace(-10, 10, steps=5) + +-10 + -5 + 0 + 5 + 10 +[torch.FloatTensor of size 5] + +>>> torch.linspace(start=-10, end=10, steps=5) + +-10 + -5 + 0 + 5 + 10 +[torch.FloatTensor of size 5] +``` + +``` +torch.logspace(start, end, steps=100, out=None) → Tensor +``` + +返回一个 1 维张量,包含在区间 10^start 和 10^end 上以对数刻度均匀间隔的`steps`个点。 输出 1 维张量的长度为`steps`。 + +参数: + +* `start (float)` – 该点集的起始点 +* `end (float)` – 该点集的最终值 +* `steps (int)` – 在`start` 和 `end`间生成的样本数 +* `out (Tensor, 可选)` – 结果张量 + +例子: + +``` +>>> torch.logspace(start=-10, end=10, steps=5) + + 1.0000e-10 + 1.0000e-05 + 1.0000e+00 + 1.0000e+05 + 1.0000e+10 +[torch.FloatTensor of size 5] + +>>> torch.logspace(start=0.1, end=1.0, steps=5) + + 1.2589 + 2.1135 + 3.5481 + 5.9566 + 10.0000 +[torch.FloatTensor of size 5] +``` + +``` +torch.ones(*sizes, out=None) → Tensor +``` + +返回一个全为 1 的张量,形状由可变参数`sizes`定义。 + +参数: + +* `sizes (int...)` – 整数序列,定义了输出形状,如:(5,5),(2) +* `out (Tensor, 可选)` – 结果张量 + +例子: + +``` +>>> torch.ones(2, 3) + + 1 1 1 + 1 1 1 +[torch.FloatTensor of size 2x3] + +>>> torch.ones(5) + + 1 + 1 + 1 + 1 + 1 +[torch.FloatTensor of size 5] +``` + +``` +torch.rand(*sizes, out=None) → Tensor +``` + +返回一个张量,填充在[0,1]区间的一组均匀分布随机数。 Tensor 的形状由变量`sizes`定义。 + +参数: + +* `sizes (int...)` – 整数序列,定义了输出形状 +* `out` (Tensor, 可选) - 结果张量 + +例子: + +``` +>>> torch.rand(4) + + 0.9193 + 0.3347 + 0.3232 + 0.7715 +[torch.FloatTensor of size 4] + +>>> torch.rand(2, 3,out=None) + + 0.5010 0.5140 0.0719 + 0.1435 0.5636 0.0538 +[torch.FloatTensor of size 2x3] +``` + +``` +torch.randn(*sizes, out=None) → Tensor +``` + +返回一个张量,包含了从正态分布(均值为 0,方差为 1,即高斯白噪声)中抽取一组随机数。 Tensor 的形状由变量`sizes`定义。 + +参数: + +* `sizes (int...)` – 整数序列,定义了输出形状 +* `out (Tensor, 可选) - 结果张量 + +例子:: + +``` +>>> torch.randn(4) + +-0.1145 + 0.0094 +-1.1717 + 0.9846 +[torch.FloatTensor of size 4] + +>>> torch.randn(2, 3) + + 1.4339 0.3351 -1.0999 + 1.5458 -0.9643 -0.3558 +[torch.FloatTensor of size 2x3] +``` + +``` +torch.randperm(n, out=None) → LongTensor +``` + +输入参数`n`,返回一个从`0` 到`n -1`的随机整数排列。 + +参数: + +* `n(int)` – 上限(独占),即最大值 + +例子: + +``` +>>> torch.randperm(4) + + 2 + 1 + 3 + 0 +[torch.LongTensor of size 4] +``` + +``` +torch.arange(start, end, step=1, out=None) → Tensor +``` + +返回一个 1 维张量,长度为 floor((end−start)/step),floor 代表向下取整。包含从`start`到`end`,以`step`为步长的一组序列值(默认步长为 1)。 + +参数: + +* `start (float)` – 该点集的起始点 +* `end (float)` – 该点集的终止点 +* `step (float)` – 相邻点的间隔大小 +* `out (Tensor, 可选的)` – 结果张量 + +例子: + +``` +>>> torch.arange(1, 4) + + 1 + 2 + 3 +[torch.FloatTensor of size 3] + +>>> torch.arange(1, 2.5, 0.5) + + 1.0000 + 1.5000 + 2.0000 +[torch.FloatTensor of size 3] +``` + +``` +torch.range(start, end, step=1, out=None) → Tensor +``` + +返回一个 1 维张量,长度为 floor((end−start)/step)+1,其中 floor 代表向下取整数。从`start`开始,`end`为结尾,以`step`为步长的一组值。 `step` 是两个值之间的间隔,即 Xi+1=Xi+step + +参数: + +* start (float) – 该点集的起始点 +* end (float) – 该点集的最终值 +* step (int) – 相邻点之间的间隔大小 +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> torch.range(1, 4) + + 1 + 2 + 3 + 4 +[torch.FloatTensor of size 4] + +>>> torch.range(1, 4, 0.5) + + 1.0000 + 1.5000 + 2.0000 + 2.5000 + 3.0000 + 3.5000 + 4.0000 +[torch.FloatTensor of size 7] +``` + +``` +torch.zeros(*sizes, out=None) → Tensor +``` + +返回一个全 0 的张量,形状由可变参数`sizes`定义。 + +参数: + +* sizes (int...) – 整数序列,定义了输出形状 +* out (Tensor, 可选) – 结果张量 + +例子: + +``` +>>> torch.zeros(2, 3) + + 0 0 0 + 0 0 0 +[torch.FloatTensor of size 2x3] + +>>> torch.zeros(5) + + 0 + 0 + 0 + 0 + 0 +[torch.FloatTensor of size 5] +``` + +## 索引,切片,连接,变异操作 + +``` +torch.cat(seq, dim=0, out=None) → Tensor +``` + +在给定维度上对输入的张量序列`seq` 进行连接操作。 + +`torch.cat()`可以看做 `torch.split()` 和 `torch.chunk()`的逆运算。 + +`cat()`函数可以通过下面例子很好的的理解。 + +参数: + +* seq(Tensors 的序列) - 可以是相同类型的 Tensor 的任何 python 序列。 +* dim(int,可选) - 张量连接的尺寸 +* out(Tensor,可选) - 输出参数 + + 例子: + + ``` + >>> x = torch.randn(2, 3) + >>> x + + 0.5983 -0.0341 2.4918 + 1.5981 -0.5265 -0.8735 + [torch.FloatTensor of size 2x3] + ``` + +> > > torch.cat((x, x, x), 0) + +0.5983 -0.0341 2.4918 1.5981 -0.5265 -0.8735 0.5983 -0.0341 2.4918 1.5981 -0.5265 -0.8735 0.5983 -0.0341 2.4918 1.5981 -0.5265 -0.8735 [torch.FloatTensor of size 6x3] + +> > > torch.cat((x, x, x), 1) + +0.5983 -0.0341 2.4918 0.5983 -0.0341 2.4918 0.5983 -0.0341 2.4918 1.5981 -0.5265 -0.8735 1.5981 -0.5265 -0.8735 1.5981 -0.5265 -0.8735 [torch.FloatTensor of size 2x9] + +``` + ```python +torch.chunk(tensor, chunks, dim=0) +```py + +将张量沿着给定维度分解成的多个块。 + +参数: + +* tensor (Tensor) – 待分块的输入张量 +* chunks (int) – 分块的个数 +* dim (int) – 沿着此维度进行分块 +``` + +torch.gather(input, dim, index, out=None) → Tensor + +``` + 沿给定轴`dim`,将输入索引张量`index`指定位置的值进行聚合。 + +对一个 3 维张量,输出可以定义为: +``` + +out[i][j][k] = tensor[index[i][j][k]][j][k] # dim=0 out[i][j][k] = tensor[i][index[i][j][k]][k] # dim=1 out[i][j][k] = tensor[i][j][index[i][j][k]] # dim=3 + +``` + 如果输入是一个 n 维张量((x0,x1...,xi−1,xi,xi+1,...,xn−1)和暗=我,然后指数必须是一个 n 维张量的大小(x0,x1,...,xi−1,y,xi+1,...,xn−1)其中 y > = 1 和有同样大小的指标。 + +参数: + +* input(Tensor) - 源张量 +* dim(int) - 要索引的轴 +* index(LongTensor) - 要收集的元素的索引 +* out(Tensor,可选) - 目的张量 + +例子: +``` + +> > > t = torch.Tensor([[1,2],[3,4]]) torch.gather(t, 1, torch.LongTensor([[0,0],[1,0]])) 1 1 4 3 [torch.FloatTensor of size 2x2] ```py + +``` +torch.index_select(input, dim, index, out=None) → Tensor +```py + +返回一个新的张量,其索引 input 张量沿尺寸 dim 使用的条目中 index 这是一个 LongTensor。 + +返回的 Tensor 具有与原始 Tensor 相同数量的尺寸。 + +注意: 返回的张量不与原始张量共享内存空间。 + +参数: + +* input (Tensor) – 输入张量 +* dim (int) – 索引的轴 +* index (LongTensor) – 包含索引下标的一维张量 +* out (Tensor, 可选的) – 目标张量 + +例子: +``` + +> > > x = torch.randn(3, 4) x + +1.2045 2.4084 0.4001 1.1372 0.5596 1.5677 0.6219 -0.7954 1.3635 -1.2313 -0.5414 -1.8478 [torch.FloatTensor of size 3x4] + +> > > indices = torch.LongTensor([0, 2]) torch.index_select(x, 0, indices) + +1.2045 2.4084 0.4001 1.1372 1.3635 -1.2313 -0.5414 -1.8478 [torch.FloatTensor of size 2x4] + +> > > torch.index_select(x, 1, indices) + +1.2045 0.4001 0.5596 0.6219 1.3635 -0.5414 [torch.FloatTensor of size 3x2] + +``` + * * * +``` + +torch.masked_select(input, mask, out=None) → Tensor + +``` + 根据掩码张量`mask`中的二元值,取输入张量中的指定项( `mask`为一个 _ByteTensor_),将取值返回到一个新的 1D 张量, + +张量 `mask`须跟`input`张量有相同数量的元素数目,但形状或维度不需要相同。 注意: 返回的张量不与原始张量共享内存空间。 + +参数: + +* input (Tensor) – 输入张量 +* mask (ByteTensor) – 掩码张量,包含了二元索引值 +* out (Tensor, 可选的) – 目标张量 + +例子: +``` + +> > > x = torch.randn(3, 4) x + +1.2045 2.4084 0.4001 1.1372 0.5596 1.5677 0.6219 -0.7954 1.3635 -1.2313 -0.5414 -1.8478 [torch.FloatTensor of size 3x4] + +> > > indices = torch.LongTensor([0, 2]) torch.index_select(x, 0, indices) + +1.2045 2.4084 0.4001 1.1372 1.3635 -1.2313 -0.5414 -1.8478 [torch.FloatTensor of size 2x4] + +> > > torch.index_select(x, 1, indices) + +1.2045 0.4001 0.5596 0.6219 1.3635 -0.5414 [torch.FloatTensor of size 3x2] + +``` + ### torch.nonzero +``` + +torch.nonzero(input, out=None) → LongTensor + +``` + 返回一个包含输入`input`中非零元素索引的张量。输出张量中的每行包含输入中非零元素的索引。 + +如果输入`input`有`n`维,则输出的索引张量`output`的形状为 z x n, 这里 z 是输入张量`input`中所有非零元素的个数。 + +参数: + +* input (Tensor) – 源张量 +* out (LongTensor, 可选的) – 包含索引值的结果张量 + +例子: +``` + +> > > torch.nonzero(torch.Tensor([1, 1, 1, 0, 1])) + +0 1 2 4 [torch.LongTensor of size 4x1] + +> > > torch.nonzero(torch.Tensor([[0.6, 0.0, 0.0, 0.0], ... [0.0, 0.4, 0.0, 0.0], ... [0.0, 0.0, 1.2, 0.0], ... [0.0, 0.0, 0.0,-0.4]])) + +0 0 1 1 2 2 3 3 [torch.LongTensor of size 4x2] + +``` + ### torch.split +``` + +torch.split(tensor, split_size, dim=0) + +``` + 将输入张量分割成相等形状的 chunks(如果可分)。 如果沿指定维的张量形状大小不能被`split_size` 整分, 则最后一个分块会小于其它分块。 + +参数: + +* tensor (Tensor) – 待分割张量 +* split_size (int) – 单个分块的形状大小 +* dim (int) – 沿着此维进行分割 + +### torch.squeeze +``` + +torch.squeeze(input, dim=None, out=None) + +``` + 将输入张量形状中的`1` 去除并返回。 如果输入是形如\((A \times 1\times B \times 1 \times C \times 1 \times D) \),那么输出形状就为: \((A \times B \times C \times D) \) + +当给定`dim`时,那么挤压操作只在给定维度上。例如,输入形状为: \((A \times 1 \times B) \), `squeeze(input, 0)` 将会保持张量不变,只有用 `squeeze(input, 1)`,形状会变成 \( (A \times B )\)。 + +注意: 返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个。 + +参数: + +* input (Tensor) – 输入张量 +* dim (int, 可选的) – 如果给定,则`input`只会在给定维度挤压 +* out (Tensor, 可选的) – 输出张量 + +例子: +``` + +> > > x = torch.zeros(2,1,2,1,2) x.size() (2L, 1L, 2L, 1L, 2L) y = torch.squeeze(x) y.size() (2L, 2L, 2L) y = torch.squeeze(x, 0) y.size() (2L, 1L, 2L, 1L, 2L) y = torch.squeeze(x, 1) y.size() (2L, 2L, 1L, 2L) ```py + +### torch.stack[[source]](http://pytorch.org/docs/_modules/torch/functional.html#stack) + +``` +torch.stack(sequence, dim=0) +```py + +沿着一个新维度对输入张量序列进行连接。 序列中所有的张量都应该为相同形状。 + +参数: + +* sqequence (Sequence) – 待连接的张量序列 +* dim (int) – 插入的维度。必须介于 0 与 待连接的张量序列数之间。 + +### torch.t +``` + +torch.t(input, out=None) → Tensor + +``` + 输入一个矩阵(2 维张量),并转置 0, 1 维。 可以被视为函数`transpose(input, 0, 1)`的简写函数。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 结果张量 +``` + +``` + >>> x = torch.randn(2, 3) +>>> x + +0.4834 0.6907 1.3417 +-0.1300 0.5295 0.2321 +[torch.FloatTensor of size 2x3] +```py +``` + +> > > torch.t(x) + +0.4834 -0.1300 0.6907 0.5295 1.3417 0.2321 [torch.FloatTensor of size 3x2] + +``` + ### torch.transpose +```pypython +torch.transpose(input, dim0, dim1, out=None) → Tensor +``` + +返回输入矩阵`input`的转置。交换维度`dim0`和`dim1`。 输出张量与输入张量共享内存,所以改变其中一个会导致另外一个也被修改。 + +参数: + +* input (Tensor) – 输入张量 +* dim0 (int) – 转置的第一维 +* dim1 (int) – 转置的第二维 + +``` +>>> x = torch.randn(2, 3) +>>> x + + 0.5983 -0.0341 2.4918 + 1.5981 -0.5265 -0.8735 +[torch.FloatTensor of size 2x3] + +>>> torch.transpose(x, 0, 1) + + 0.5983 1.5981 +-0.0341 -0.5265 + 2.4918 -0.8735 +[torch.FloatTensor of size 3x2] +``` + +### torch.unbind + +``` +torch.unbind(tensor, dim=0)[source] +``` + +移除指定维后,返回一个元组,包含了沿着指定维切片后的各个切片 + +参数: + +* tensor (Tensor) – 输入张量 +* dim (int) – 删除的维度 + +### torch.unsqueeze + +``` +torch.unsqueeze(input, dim, out=None) +``` + +返回一个新的张量,对输入的制定位置插入维度 1 + +注意: 返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个。 + +如果`dim`为负,则将会被转化( dim+input.dim()+1 ) + +参数: + +* tensor (Tensor) – 输入张量 +* dim (int) – 插入维度的索引 +* out (Tensor, 可选的) – 结果张量 + +``` +>>> x = torch.Tensor([1, 2, 3, 4]) +>>> torch.unsqueeze(x, 0) + 1 2 3 4 +[torch.FloatTensor of size 1x4] +>>> torch.unsqueeze(x, 1) + 1 + 2 + 3 + 4 +[torch.FloatTensor of size 4x1] +``` + +### torch.manual_seed + +``` +torch.manual_seed(seed) +``` + +设定生成随机数的种子,并返回一个 *torch._C.Generator* 对象. + +参数: seed (int or long) – 种子. + +### torch.initial_seed + +``` +torch.initial_seed() +``` + +返回生成随机数的原始种子值(python long)。 + +### torch.get_rng_state + +``` +torch.get_rng_state()[source] +``` + +返回随机生成器状态(*ByteTensor*) + +### torch.set_rng_state + +``` +torch.set_rng_state(new_state)[source] +``` + +设定随机生成器状态 参数: new_state (torch.ByteTensor) – 期望的状态 + +### torch.default_generator + +``` +torch.default_generator = +``` + +### torch.bernoulli + +``` +torch.bernoulli(input, out=None) → Tensor +``` + +从伯努利分布中抽取二元随机数(0 或者 1)。 + +输入张量须包含用于抽取上述二元随机值的概率。 因此,输入中的所有值都必须在[0,1]区间,即 ( 0<=input_i<=1 ) + +输出张量的第*`i`*个元素值, 将会以输入张量的第*`i`*个概率值等于`1`。 + +返回值将会是与输入相同大小的张量,每个值为 0 或者 1 参数: + +* input (Tensor) – 输入为伯努利分布的概率值 +* out (Tensor, 可选的) – 输出张量(可选) + +例子: + +``` +>>> a = torch.Tensor(3, 3).uniform_(0, 1) # generate a uniform random matrix with range [0, 1] +>>> a + + 0.7544 0.8140 0.9842 + 0.5282 0.0595 0.6445 + 0.1925 0.9553 0.9732 +[torch.FloatTensor of size 3x3] + +>>> torch.bernoulli(a) + + 1 1 1 + 0 0 1 + 0 1 1 +[torch.FloatTensor of size 3x3] + +>>> a = torch.ones(3, 3) # probability of drawing "1" is 1 +>>> torch.bernoulli(a) + + 1 1 1 + 1 1 1 + 1 1 1 +[torch.FloatTensor of size 3x3] + +>>> a = torch.zeros(3, 3) # probability of drawing "1" is 0 +>>> torch.bernoulli(a) + + 0 0 0 + 0 0 0 + 0 0 0 +[torch.FloatTensor of size 3x3] +``` + +### torch.multinomial + +``` +torch.multinomial(input, num_samples,replacement=False, out=None) → LongTensor +``` + +返回一个张量,每行包含从`input`相应行中定义的多项分布中抽取的`num_samples`个样本。 + +当抽取样本时,依次从左到右排列(第一个样本对应第一列)。 + +如果输入`input`是一个向量,输出`out`也是一个相同长度`num_samples`的向量。如果输入`input`是有 (m )行的矩阵,输出`out`是形如( m \times n )的矩阵。 + +如果参数`replacement` 为 *True*, 则样本抽取可以重复。否则,一个样本在每行不能被重复抽取。 + +参数`num_samples`必须小于`input`长度(即,`input`的列数,如果是`input`是一个矩阵)。 + +参数: + +* input (Tensor) – 包含概率值的张量 +* num_samples (int) – 抽取的样本数 +* replacement (bool, 可选的) – 布尔值,决定是否能重复抽取 +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> weights = torch.Tensor([0, 10, 3, 0]) # create a Tensor of weights +>>> torch.multinomial(weights, 4) + + 1 + 2 + 0 + 0 +[torch.LongTensor of size 4] + +>>> torch.multinomial(weights, 4, replacement=True) + + 1 + 2 + 1 + 2 +[torch.LongTensor of size 4] +``` + +### torch.normal() + +``` +torch.normal(means, std, out=None) +``` + +返回一个张量,包含从给定参数`means`,`std`的离散正态分布中抽取随机数。 均值`means`是一个张量,包含每个输出元素相关的正态分布的均值。 `std`是一个张量,包含每个输出元素相关的正态分布的标准差。 均值和标准差的形状不须匹配,但每个张量的元素个数须相同。 + +参数: + +* means (Tensor) – 均值 +* std (Tensor) – 标准差 +* out (Tensor) – 可选的输出张量 + +``` +torch.normal(means=torch.arange(1, 11), std=torch.arange(1, 0, -0.1)) + + 1.5104 + 1.6955 + 2.4895 + 4.9185 + 4.9895 + 6.9155 + 7.3683 + 8.1836 + 8.7164 + 9.8916 +[torch.FloatTensor of size 10] +``` + +``` +torch.normal(mean=0.0, std, out=None) +``` + +与上面函数类似,所有抽取的样本共享均值。 + +参数: + +* means (Tensor,可选的) – 所有分布均值 +* std (Tensor) – 每个元素的标准差 +* out (Tensor) – 可选的输出张量 + +例子: + +``` +>>> torch.normal(mean=0.5, std=torch.arange(1, 6)) + + 0.5723 + 0.0871 + -0.3783 + -2.5689 + 10.7893 +[torch.FloatTensor of size 5] +``` + +``` +torch.normal(means, std=1.0, out=None) +``` + +与上面函数类似,所有抽取的样本共享标准差。 + +参数: + +* means (Tensor) – 每个元素的均值 +* std (float, 可选的) – 所有分布的标准差 +* out (Tensor) – 可选的输出张量 + +例子: + +``` +>>> torch.normal(means=torch.arange(1, 6)) + + 1.1681 + 2.8884 + 3.7718 + 2.5616 + 4.2500 +[torch.FloatTensor of size 5] +``` + +### torch.saves[[source]](http://pytorch.org/docs/_modules/torch/serialization.html#save) + +``` +torch.save(obj, f, pickle_module=, pickle_protocol=2) +``` + +保存一个对象到一个硬盘文件上 参考: [Recommended approach for saving a model](http://pytorch.org/docs/notes/serialization.html#recommend-saving-models) 参数: + +* obj – 保存对象 +* f - 类文件对象 (返回文件描述符)或一个保存文件名的字符串 +* pickle_module – 用于 pickling 元数据和对象的模块 +* pickle_protocol – 指定 pickle protocal 可以覆盖默认参数 + +### torch.load[[source]](http://pytorch.org/docs/_modules/torch/serialization.html#load) + +``` +torch.load(f, map_location=None, pickle_module=) +``` + +从磁盘文件中读取一个通过`torch.save()`保存的对象。 `torch.load()` 可通过参数`map_location` 动态地进行内存重映射,使其能从不动设备中读取文件。一般调用时,需两个参数: storage 和 location tag. 返回不同地址中的 storage,或着返回 None (此时地址可以通过默认方法进行解析). 如果这个参数是字典的话,意味着其是从文件的地址标记到当前系统的地址标记的映射。 默认情况下, location tags 中 "cpu"对应 host tensors,‘cuda:device_id’ (e.g. ‘cuda:2’) 对应 cuda tensors。 用户可以通过 register_package 进行扩展,使用自己定义的标记和反序列化方法。 + +参数: + +* f – 类文件对象 (返回文件描述符)或一个保存文件名的字符串 +* map_location – 一个函数或字典规定如何 remap 存储位置 +* pickle_module – 用于 unpickling 元数据和对象的模块 (必须匹配序列化文件时的 pickle_module ) + +例子: + +``` +>>> torch.load('tensors.pt') +# Load all tensors onto the CPU +>>> torch.load('tensors.pt', map_location=lambda storage, loc: storage) +# Map tensors from GPU 1 to GPU 0 +>>> torch.load('tensors.pt', map_location={'cuda:1':'cuda:0'}) +``` + +### torch.get_num_threads + +``` +torch.get_num_threads() → int +``` + +获得用于并行化 CPU 操作的 OpenMP 线程数 + +### torch.set_num_threads + +``` +torch.set_num_threads(int) +``` + +设定用于并行化 CPU 操作的 OpenMP 线程数 + +## Pointwise Ops + +### torch.abs + +``` +torch.abs(input, out=None) → Tensor +``` + +计算输入张量的每个元素绝对值 + +例子: + +``` +>>> torch.abs(torch.FloatTensor([-1, -2, 3])) +FloatTensor([1, 2, 3]) +``` + +### torch.acos(input, out=None) → Tensor + +``` +torch.acos(input, out=None) → Tensor +``` + +返回一个新张量,包含输入张量每个元素的反余弦。 参数: + +* input (Tensor) – 输入张量 +* out ([Tensor](http://pytorch.org/docs/tensors.html#torch.Tensor), 可选的) – 结果张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + +-0.6366 + 0.2718 + 0.4469 + 1.3122 +[torch.FloatTensor of size 4] + +>>> torch.acos(a) + 2.2608 + 1.2956 + 1.1075 + nan +[torch.FloatTensor of size 4] +``` + +### torch.add() + +``` +torch.add(input, value, out=None) +``` + +对输入张量`input`逐元素加上标量值`value`,并返回结果到一个新的张量`out`,即 ( out = tensor + value )。 + +如果输入`input`是 FloatTensor or DoubleTensor 类型,则`value` 必须为实数,否则须为整数。 + +* input (Tensor) – 输入张量 +* value (Number) – 添加到输入每个元素的数 +* out (Tensor, 可选的) – 结果张量 + +``` +>>> a = torch.randn(4) +>>> a + + 0.4050 +-1.2227 + 1.8688 +-0.4185 +[torch.FloatTensor of size 4] + +>>> torch.add(a, 20) + + 20.4050 + 18.7773 + 21.8688 + 19.5815 +[torch.FloatTensor of size 4] +``` + +``` +torch.add(input, value=1, other, out=None) +``` + +`other` 张量的每个元素乘以一个标量值`value`,并加到`iput` 张量上。返回结果到输出张量`out`。即,( out=input+(other∗value ) ) + +两个张量 `input` and `other`的尺寸不需要匹配,但元素总数必须一样。 + +如果`other`是 FloatTensor or DoubleTensor 类型,则`value` 必须为实数,否则须为整数。 + +参数: + +* input (Tensor) – 第一个输入张量 +* value (Number) – 用于第二个张量的尺寸因子 +* other (Tensor) – 第二个输入张量 +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> import torch +>>> a = torch.randn(4) +>>> a + +-0.9310 + 2.0330 + 0.0852 +-0.2941 +[torch.FloatTensor of size 4] + +>>> b = torch.randn(2, 2) +>>> b + + 1.0663 0.2544 +-0.1513 0.0749 +[torch.FloatTensor of size 2x2] + +>>> torch.add(a, 10, b) + 9.7322 + 4.5770 +-1.4279 + 0.4552 +[torch.FloatTensor of size 4] +``` + +### torch.addcdiv + +``` +torch.addcdiv(tensor, value=1, tensor1, tensor2, out=None) → Tensor +``` + +对`tensor2`对`tensor1`逐元素相除,然后乘以标量值`value` 并加到`tensor`。 + +张量的形状不需要匹配,但元素数量必须一致。 + +如果输入是 FloatTensor or DoubleTensor 类型,则`value` 必须为实数,否则须为整数。 + +参数: + +* tensor (Tensor) – 张量,对 tensor1 ./ tensor 进行相加 +* value (Number, 可选的) – 标量,对 tensor1 ./ tensor2 进行相乘 +* tensor1 (Tensor) – 张量,作为被除数(分子) +* tensor2 (Tensor) –张量,作为除数(分母) +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> t = torch.randn(2, 3) +>>> t1 = torch.randn(1, 6) +>>> t2 = torch.randn(6, 1) +>>> torch.addcdiv(t, 0.1, t1, t2) + + 0.0122 -0.0188 -0.2354 + 0.7396 -1.5721 1.2878 +[torch.FloatTensor of size 2x3] +``` + +### torch.addcmul + +``` +torch.addcmul(tensor, value=1, tensor1, tensor2, out=None) → Tensor +``` + +用`tensor2`对`tensor1`逐元素相乘,并对结果乘以标量值`value`然后加到`tensor`。 张量的形状不需要匹配,但元素数量必须一致。 如果输入是 FloatTensor or DoubleTensor 类型,则`value` 必须为实数,否则须为整数。 + +参数: + +* tensor (Tensor) – 张量,对 tensor1 ./ tensor 进行相加 +* value (Number, 可选的) – 标量,对 tensor1 . tensor2 进行相乘 +* tensor1 (Tensor) – 张量,作为乘子 1 +* tensor2 (Tensor) –张量,作为乘子 2 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> t = torch.randn(2, 3) +>>> t1 = torch.randn(1, 6) +>>> t2 = torch.randn(6, 1) +>>> torch.addcmul(t, 0.1, t1, t2) + + 0.0122 -0.0188 -0.2354 + 0.7396 -1.5721 1.2878 +[torch.FloatTensor of size 2x3] +``` + +### torch.asin + +``` +torch.asin(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的反正弦函数 + +参数: + +* tensor (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a +-0.6366 + 0.2718 + 0.4469 + 1.3122 +[torch.FloatTensor of size 4] + +>>> torch.asin(a) +-0.6900 + 0.2752 + 0.4633 + nan +[torch.FloatTensor of size 4] +``` + +### torch.atan + +``` +torch.atan(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的反正切函数 + +参数: + +* tensor (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a +-0.6366 + 0.2718 + 0.4469 + 1.3122 +[torch.FloatTensor of size 4] + +>>> torch.atan(a) +-0.5669 + 0.2653 + 0.4203 + 0.9196 +[torch.FloatTensor of size 4] +``` + +### torch.atan2 + +``` +torch.atan2(input1, input2, out=None) → Tensor +``` + +返回一个新张量,包含两个输入张量`input1`和`input2`的反正切函数 + +参数: + +* input1 (Tensor) – 第一个输入张量 +* input2 (Tensor) – 第二个输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a +-0.6366 + 0.2718 + 0.4469 + 1.3122 +[torch.FloatTensor of size 4] + +>>> torch.atan2(a, torch.randn(4)) +-2.4167 + 2.9755 + 0.9363 + 1.6613 +[torch.FloatTensor of size 4] +``` + +### torch.ceil + +``` +torch.ceil(input, out=None) → Tensor +``` + +天井函数,对输入`input`张量每个元素向上取整, 即取不小于每个元素的最小整数,并返回结果到输出。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + + 1.3869 + 0.3912 +-0.8634 +-0.5468 +[torch.FloatTensor of size 4] + +>>> torch.ceil(a) + + 2 + 1 +-0 +-0 +[torch.FloatTensor of size 4] +``` + +### torch.clamp + +``` +torch.clamp(input, min, max, out=None) → Tensor +``` + +将输入`input`张量每个元素的夹紧到区间 ([min, max] ),并返回结果到一个新张量。 + +操作定义如下: + +``` + | min, if x_i < min +y_i = | x_i, if min <= x_i <= max + | max, if x_i > max +``` + +如果输入是 FloatTensor or DoubleTensor 类型,则参数`min` `max` 必须为实数,否则须为整数。 + +参数: + +* input (Tensor) – 输入张量 +* min (Number) – 限制范围下限 +* max (Number) – 限制范围上限 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + + 1.3869 + 0.3912 +-0.8634 +-0.5468 +[torch.FloatTensor of size 4] + +>>> torch.clamp(a, min=-0.5, max=0.5) + + 0.5000 + 0.3912 +-0.5000 +-0.5000 +[torch.FloatTensor of size 4] +``` + +``` +torch.clamp(input, *, min, out=None) → Tensor +``` + +将输入`input`张量每个元素的限制到不小于`min` ,并返回结果到一个新张量。 + +如果输入是 FloatTensor or DoubleTensor 类型,则参数 `value` 必须为实数,否则须为整数。 + +参数: + +* input (Tensor) – 输入张量 +* value (Number) – 限制范围下限 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + + 1.3869 + 0.3912 +-0.8634 +-0.5468 +[torch.FloatTensor of size 4] + +>>> torch.clamp(a, min=0.5) + + 1.3869 + 0.5000 + 0.5000 + 0.5000 +[torch.FloatTensor of size 4] +``` + +``` +torch.clamp(input, *, max, out=None) → Tensor +``` + +将输入`input`张量每个元素的限制到不大于`max` ,并返回结果到一个新张量。 + +如果输入是 FloatTensor or DoubleTensor 类型,则参数 `value` 必须为实数,否则须为整数。 + +参数: + +* input (Tensor) – 输入张量 +* value (Number) – 限制范围上限 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + + 1.3869 + 0.3912 +-0.8634 +-0.5468 +[torch.FloatTensor of size 4] + +>>> torch.clamp(a, max=0.5) + + 0.5000 + 0.3912 +-0.8634 +-0.5468 +[torch.FloatTensor of size 4] +``` + +### torch.cos + +``` +torch.cos(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的余弦。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a +-0.6366 + 0.2718 + 0.4469 + 1.3122 +[torch.FloatTensor of size 4] + +>>> torch.cos(a) + 0.8041 + 0.9633 + 0.9018 + 0.2557 +[torch.FloatTensor of size 4] +``` + +### torch.cosh + +``` +torch.cosh(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的双曲余弦。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a +-0.6366 + 0.2718 + 0.4469 + 1.3122 +[torch.FloatTensor of size 4] + +>>> torch.cosh(a) + 1.2095 + 1.0372 + 1.1015 + 1.9917 +[torch.FloatTensor of size 4] +``` + +### torch.div() + +``` +torch.div(input, value, out=None) +``` + +将`input`逐元素除以标量值`value`,并返回结果到输出张量`out`。 即 ( out=tensor/value ) + +如果输入是 FloatTensor or DoubleTensor 类型,则参数 `value` 必须为实数,否则须为整数。 + +参数: + +* input (Tensor) – 输入张量 +* value (Number) – 除数 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(5) +>>> a + +-0.6147 +-1.1237 +-0.1604 +-0.6853 + 0.1063 +[torch.FloatTensor of size 5] + +>>> torch.div(a, 0.5) + +-1.2294 +-2.2474 +-0.3208 +-1.3706 + 0.2126 +[torch.FloatTensor of size 5] +``` + +``` +torch.div(input, other, out=None) +``` + +两张量`input`和`other`逐元素相除,并将结果返回到输出。即, ( out_i= input_i / other_i ) + +两张量形状不须匹配,但元素数须一致。 + +注意:当形状不匹配时,`input`的形状作为输出张量的形状。 + +参数: + +* input (Tensor) – 张量(分子) +* other (Tensor) – 张量(分母) +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4,4) +>>> a + +-0.1810 0.4017 0.2863 -0.1013 + 0.6183 2.0696 0.9012 -1.5933 + 0.5679 0.4743 -0.0117 -0.1266 +-0.1213 0.9629 0.2682 1.5968 +[torch.FloatTensor of size 4x4] + +>>> b = torch.randn(8, 2) +>>> b + + 0.8774 0.7650 + 0.8866 1.4805 +-0.6490 1.1172 + 1.4259 -0.8146 + 1.4633 -0.1228 + 0.4643 -0.6029 + 0.3492 1.5270 + 1.6103 -0.6291 +[torch.FloatTensor of size 8x2] + +>>> torch.div(a, b) + +-0.2062 0.5251 0.3229 -0.0684 +-0.9528 1.8525 0.6320 1.9559 + 0.3881 -3.8625 -0.0253 0.2099 +-0.3473 0.6306 0.1666 -2.5381 +[torch.FloatTensor of size 4x4] +``` + +### torch.exp + +``` +torch.exp(tensor, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的指数。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + + ``` + >>> torch.exp(torch.Tensor([0, math.log(2)])) + torch.FloatTensor([1, 2]) + ``` + + ### torch.floor + +``` +torch.floor(input, out=None) → Tensor +``` + +床函数: 返回一个新张量,包含输入`input`张量每个元素的 floor,即不小于元素的最大整数。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + + 1.3869 + 0.3912 +-0.8634 +-0.5468 +[torch.FloatTensor of size 4] + +>>> torch.floor(a) + + 1 + 0 +-1 +-1 +[torch.FloatTensor of size 4] +``` + +### torch.fmod + +``` +torch.fmod(input, divisor, out=None) → Tensor +``` + +计算除法余数。 除数与被除数可能同时含有整数和浮点数。此时,余数的正负与被除数相同。 + +参数: + +* input (Tensor) – 被除数 +* divisor (Tensor or float) – 除数,一个数或与被除数相同类型的张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> torch.fmod(torch.Tensor([-3, -2, -1, 1, 2, 3]), 2) +torch.FloatTensor([-1, -0, -1, 1, 0, 1]) +>>> torch.fmod(torch.Tensor([1, 2, 3, 4, 5]), 1.5) +torch.FloatTensor([1.0, 0.5, 0.0, 1.0, 0.5]) +``` + +参考: [`torch.remainder()`](http://pytorch.org/docs/torch.html#torch.remainder), 计算逐元素余数, 相当于 python 中的 % 操作符。 + +### torch.frac + +``` +torch.frac(tensor, out=None) → Tensor +``` + +返回每个元素的分数部分。 + +例子: + +``` +>>> torch.frac(torch.Tensor([1, 2.5, -3.2]) +torch.FloatTensor([0, 0.5, -0.2]) +``` + +### torch.lerp + +``` +torch.lerp(start, end, weight, out=None) +``` + +对两个张量以`start`,`end`做线性插值, 将结果返回到输出张量。 + +即,( out_i=start_i+weight∗(end_i−start_i) ) + +参数: + +* start (Tensor) – 起始点张量 +* end (Tensor) – 终止点张量 +* weight (float) – 插值公式的 weight +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> start = torch.arange(1, 5) +>>> end = torch.Tensor(4).fill_(10) +>>> start + + 1 + 2 + 3 + 4 +[torch.FloatTensor of size 4] + +>>> end + + 10 + 10 + 10 + 10 +[torch.FloatTensor of size 4] + +>>> torch.lerp(start, end, 0.5) + + 5.5000 + 6.0000 + 6.5000 + 7.0000 +[torch.FloatTensor of size 4] +``` + +### torch.log + +``` +torch.log(input, out=None) → Tensor +``` + +计算`input` 的自然对数 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(5) +>>> a + +-0.4183 + 0.3722 +-0.3091 + 0.4149 + 0.5857 +[torch.FloatTensor of size 5] + +>>> torch.log(a) + + nan +-0.9883 + nan +-0.8797 +-0.5349 +[torch.FloatTensor of size 5] +``` + +### torch.log1p + +``` +torch.log1p(input, out=None) → Tensor +``` + +计算 ( input +1 )的自然对数 ( y_i=log(x_i+1) ) + +注意:对值比较小的输入,此函数比`torch.log()`更准确。 + +如果输入是 FloatTensor or DoubleTensor 类型,则`value` 必须为实数,否则须为整数。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(5) +>>> a + +-0.4183 + 0.3722 +-0.3091 + 0.4149 + 0.5857 +[torch.FloatTensor of size 5] + +>>> torch.log1p(a) + +-0.5418 + 0.3164 +-0.3697 + 0.3471 + 0.4611 +[torch.FloatTensor of size 5] +``` + +### torch.mul + +``` +torch.mul(input, value, out=None) +``` + +用标量值`value`乘以输入`input`的每个元素,并返回一个新的结果张量。 ( out=tensor ∗ value ) + +如果输入是 FloatTensor or DoubleTensor 类型,则`value` 必须为实数,否则须为整数。 + +参数: + +* input (Tensor) – 输入张量 +* value (Number) – 乘到每个元素的数 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(3) +>>> a + +-0.9374 +-0.5254 +-0.6069 +[torch.FloatTensor of size 3] + +>>> torch.mul(a, 100) + +-93.7411 +-52.5374 +-60.6908 +[torch.FloatTensor of size 3] +``` + +``` +torch.mul(input, other, out=None) +``` + +两个张量`input`,`other`按元素进行相乘,并返回到输出张量。即计算( out_i=input_i ∗ other_i ) + +两计算张量形状不须匹配,但总元素数须一致。 + +参数: + +* input (Tensor) – 第一个相乘张量 +* other (Tensor) – 第二个相乘张量 +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> a = torch.randn(4,4) +>>> a + +-0.7280 0.0598 -1.4327 -0.5825 +-0.1427 -0.0690 0.0821 -0.3270 +-0.9241 0.5110 0.4070 -1.1188 +-0.8308 0.7426 -0.6240 -1.1582 +[torch.FloatTensor of size 4x4] + +>>> b = torch.randn(2, 8) +>>> b + + 0.0430 -1.0775 0.6015 1.1647 -0.6549 0.0308 -0.1670 1.0742 +-1.2593 0.0292 -0.0849 0.4530 1.2404 -0.4659 -0.1840 0.5974 +[torch.FloatTensor of size 2x8] + +>>> torch.mul(a, b) + +-0.0313 -0.0645 -0.8618 -0.6784 + 0.0934 -0.0021 -0.0137 -0.3513 + 1.1638 0.0149 -0.0346 -0.5068 +-1.0304 -0.3460 0.1148 -0.6919 +[torch.FloatTensor of size 4x4] +``` + +### torch.neg + +``` +torch.neg(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input` 张量按元素取负。 即, ( out=−1∗input ) + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(5) +>>> a + +-0.4430 + 1.1690 +-0.8836 +-0.4565 + 0.2968 +[torch.FloatTensor of size 5] + +>>> torch.neg(a) + + 0.4430 +-1.1690 + 0.8836 + 0.4565 +-0.2968 +[torch.FloatTensor of size 5] +``` + +### torch.pow + +``` +torch.pow(input, exponent, out=None) +``` + +对输入`input`的按元素求`exponent`次幂值,并返回结果张量。 幂值`exponent` 可以为单一 `float` 数或者与`input`相同元素数的张量。 + +当幂值为标量时,执行操作: $$ out_i=x^{exponent} $$ + +当幂值为张量时,执行操作: $$ out_i=x^{exponent_i} $$ + +参数: + +* input (Tensor) – 输入张量 +* exponent (float or Tensor) – 幂值 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + +-0.5274 +-0.8232 +-2.1128 + 1.7558 +[torch.FloatTensor of size 4] + +>>> torch.pow(a, 2) + + 0.2781 + 0.6776 + 4.4640 + 3.0829 +[torch.FloatTensor of size 4] + +>>> exp = torch.arange(1, 5) +>>> a = torch.arange(1, 5) +>>> a + + 1 + 2 + 3 + 4 +[torch.FloatTensor of size 4] + +>>> exp + + 1 + 2 + 3 + 4 +[torch.FloatTensor of size 4] + +>>> torch.pow(a, exp) + + 1 + 4 + 27 + 256 +[torch.FloatTensor of size 4] +``` + +``` +torch.pow(base, input, out=None) +``` + +`base` 为标量浮点值,`input`为张量, 返回的输出张量 `out` 与输入张量相同形状。 + +执行操作为: $$ out_i=base^{input_i} $$ + +参数: + +* base (float) – 标量值,指数的底 +* input ( Tensor) – 幂值 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> exp = torch.arange(1, 5) +>>> base = 2 +>>> torch.pow(base, exp) + + 2 + 4 + 8 + 16 +[torch.FloatTensor of size 4] +``` + +### torch.reciprocal + +``` +torch.reciprocal(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的倒数,即 1.0/x。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + + 1.3869 + 0.3912 +-0.8634 +-0.5468 +[torch.FloatTensor of size 4] + +>>> torch.reciprocal(a) + + 0.7210 + 2.5565 +-1.1583 +-1.8289 +[torch.FloatTensor of size 4] +``` + +### torch.remainder + +``` +torch.remainder(input, divisor, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的除法余数。 除数与被除数可能同时包含整数或浮点数。余数与除数有相同的符号。 + +参数: + +* input (Tensor) – 被除数 +* divisor (Tensor or float) – 除数,一个数或者与除数相同大小的张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> torch.remainder(torch.Tensor([-3, -2, -1, 1, 2, 3]), 2) +torch.FloatTensor([1, 0, 1, 1, 0, 1]) +>>> torch.remainder(torch.Tensor([1, 2, 3, 4, 5]), 1.5) +torch.FloatTensor([1.0, 0.5, 0.0, 1.0, 0.5]) +``` + +### torch.round + +``` +torch.round(input, out=None) → Tensor +``` + +返回一个新张量,将输入`input`张量每个元素舍入到最近的整数。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + + 1.2290 + 1.3409 +-0.5662 +-0.0899 +[torch.FloatTensor of size 4] + +>>> torch.round(a) + + 1 + 1 +-1 +-0 +[torch.FloatTensor of size 4] +``` + +### torch.rsqrt + +``` +torch.rsqrt(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的平方根倒数。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + + 1.2290 + 1.3409 +-0.5662 +-0.0899 +[torch.FloatTensor of size 4] + +>>> torch.rsqrt(a) + + 0.9020 + 0.8636 + nan + nan +[torch.FloatTensor of size 4] +``` + +### torch.sigmoid + +``` +torch.sigmoid(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的 sigmoid 值。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + +-0.4972 + 1.3512 + 0.1056 +-0.2650 +[torch.FloatTensor of size 4] + +>>> torch.sigmoid(a) + + 0.3782 + 0.7943 + 0.5264 + 0.4341 +[torch.FloatTensor of size 4] +``` + +### torch.sign + +``` +torch.sign(input, out=None) → Tensor +``` + +符号函数:返回一个新张量,包含输入`input`张量每个元素的正负。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a +-0.6366 + 0.2718 + 0.4469 + 1.3122 +[torch.FloatTensor of size 4] + +>>> torch.sign(a) + +-1 + 1 + 1 + 1 +[torch.FloatTensor of size 4] +``` + +### torch.sin + +``` +torch.sin(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的正弦。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a +-0.6366 + 0.2718 + 0.4469 + 1.3122 +[torch.FloatTensor of size 4] + +>>> torch.sin(a) +-0.5944 + 0.2684 + 0.4322 + 0.9667 +[torch.FloatTensor of size 4] +``` + +### torch.sinh + +``` +torch.sinh(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的双曲正弦。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a +-0.6366 + 0.2718 + 0.4469 + 1.3122 +[torch.FloatTensor of size 4] + +>>> torch.sinh(a) +-0.6804 + 0.2751 + 0.4619 + 1.7225 +[torch.FloatTensor of size 4] +``` + +### torch.sqrt + +``` +torch.sqrt(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的平方根。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + + 1.2290 + 1.3409 +-0.5662 +-0.0899 +[torch.FloatTensor of size 4] + +>>> torch.sqrt(a) + + 1.1086 + 1.1580 + nan + nan +[torch.FloatTensor of size 4] +``` + +### torch.tan + +``` +torch.tan(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的正切。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a +-0.6366 + 0.2718 + 0.4469 + 1.3122 +[torch.FloatTensor of size 4] + +>>> torch.tan(a) +-0.7392 + 0.2786 + 0.4792 + 3.7801 +[torch.FloatTensor of size 4] +``` + +### torch.tanh + +``` +torch.tanh(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的双曲正切。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a +-0.6366 + 0.2718 + 0.4469 + 1.3122 +[torch.FloatTensor of size 4] + +>>> torch.tanh(a) +-0.5625 + 0.2653 + 0.4193 + 0.8648 +[torch.FloatTensor of size 4] +``` + +### torch.trunc + +``` +torch.trunc(input, out=None) → Tensor +``` + +返回一个新张量,包含输入`input`张量每个元素的截断值(标量 x 的截断值是最接近其的整数,其比 x 更接近零。简而言之,有符号数的小数部分被舍弃)。 + +参数: + +* input (Tensor) – 输入张量 +* out (Tensor, 可选的) – 输出张量 + +例子: + +``` +>>> a = torch.randn(4) +>>> a + +-0.4972 + 1.3512 + 0.1056 +-0.2650 +[torch.FloatTensor of size 4] + +>>> torch.trunc(a) + +-0 + 1 + 0 +-0 +[torch.FloatTensor of size 4] +``` + +### torch.cumprod + +``` +torch.cumprod(input, dim, out=None) → Tensor +``` + +返回输入沿指定维度的累积积。例如,如果输入是一个 N 元向量,则结果也是一个 N 元向量,第`i` 个输出元素值为( yi=x1∗x2∗x3∗...∗xi ) + +参数: + +* input (Tensor) – 输入张量 +* dim (int) – 累积积操作的维度 +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> a = torch.randn(10) +>>> a + + 1.1148 + 1.8423 + 1.4143 +-0.4403 + 1.2859 +-1.2514 +-0.4748 + 1.1735 +-1.6332 +-0.4272 +[torch.FloatTensor of size 10] + +>>> torch.cumprod(a, dim=0) + + 1.1148 + 2.0537 + 2.9045 +-1.2788 +-1.6444 + 2.0578 +-0.9770 +-1.1466 + 1.8726 +-0.8000 +[torch.FloatTensor of size 10] + +>>> a[5] = 0.0 +>>> torch.cumprod(a, dim=0) + + 1.1148 + 2.0537 + 2.9045 +-1.2788 +-1.6444 +-0.0000 + 0.0000 + 0.0000 +-0.0000 + 0.0000 +[torch.FloatTensor of size 10] +``` + +### torch.cumsum + +``` +torch.cumsum(input, dim, out=None) → Tensor +``` + +返回输入沿指定维度的累积和。例如,如果输入是一个 N 元向量,则结果也是一个 N 元向量,第`i` 个输出元素值为 ( yi=x1+x2+x3+...+xi) + +参数: + +* input (Tensor) – 输入张量 +* dim (int) – 累积和操作的维度 +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> a = torch.randn(10) +>>> a + +-0.6039 +-0.2214 +-0.3705 +-0.0169 + 1.3415 +-0.1230 + 0.9719 + 0.6081 +-0.1286 + 1.0947 +[torch.FloatTensor of size 10] + +>>> torch.cumsum(a, dim=0) + +-0.6039 +-0.8253 +-1.1958 +-1.2127 + 0.1288 + 0.0058 + 0.9777 + 1.5858 + 1.4572 + 2.5519 +[torch.FloatTensor of size 10] +``` + +### torch.dist + +``` +torch.dist(input, other, p=2, out=None) → Tensor +``` + +返回 (`input` - `other`) 的 `p`范数 。 + +参数: + +* input (Tensor) – 输入张量 +* other (Tensor) – 右侧输入张量 +* p (float, 可选的) – 所计算的范数 +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> x = torch.randn(4) +>>> x + + 0.2505 +-0.4571 +-0.3733 + 0.7807 +[torch.FloatTensor of size 4] + +>>> y = torch.randn(4) +>>> y + + 0.7782 +-0.5185 + 1.4106 +-2.4063 +[torch.FloatTensor of size 4] + +>>> torch.dist(x, y, 3.5) +3.302832063224223 +>>> torch.dist(x, y, 3) +3.3677282206393286 +>>> torch.dist(x, y, 0) +inf +>>> torch.dist(x, y, 1) +5.560028076171875 +``` + +### torch.mean + +``` +torch.mean(input) → float +``` + +返回输入张量所有元素的均值。 + +参数: input (Tensor) – 输入张量 + +例子: + +``` +>>> a = torch.randn(1, 3) +>>> a + +-0.2946 -0.9143 2.1809 +[torch.FloatTensor of size 1x3] + +>>> torch.mean(a) +0.32398951053619385 +``` + +``` +torch.mean(input, dim, out=None) → Tensor +``` + +返回输入张量给定维度`dim`上每行的均值。 + +输出形状与输入相同,除了给定维度上为 1. + +参数: + +* input (Tensor) – 输入张量 +* dim (int) – the dimension to reduce +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> a = torch.randn(4, 4) +>>> a + +-1.2738 -0.3058 0.1230 -1.9615 + 0.8771 -0.5430 -0.9233 0.9879 + 1.4107 0.0317 -0.6823 0.2255 +-1.3854 0.4953 -0.2160 0.2435 +[torch.FloatTensor of size 4x4] + +>>> torch.mean(a, 1) + +-0.8545 + 0.0997 + 0.2464 +-0.2157 +[torch.FloatTensor of size 4x1] +``` + +### torch.median + +``` +torch.median(input, dim=-1, values=None, indices=None) -> (Tensor, LongTensor) +``` + +返回输入张量给定维度每行的中位数,同时返回一个包含中位数的索引的`LongTensor`。 + +`dim`值默认为输入张量的最后一维。 输出形状与输入相同,除了给定维度上为 1. + +*注意*: 这个函数还没有在`torch.cuda.Tensor`中定义 + +参数: + +* input (Tensor) – 输入张量 +* dim (int) – 缩减的维度 +* values (Tensor, 可选的) – 结果张量 +* indices (Tensor, 可选的) – 返回的索引结果张量 + +``` +>>> a + + -0.6891 -0.6662 + 0.2697 0.7412 + 0.5254 -0.7402 + 0.5528 -0.2399 +[torch.FloatTensor of size 4x2] + +>>> a = torch.randn(4, 5) +>>> a + + 0.4056 -0.3372 1.0973 -2.4884 0.4334 + 2.1336 0.3841 0.1404 -0.1821 -0.7646 +-0.2403 1.3975 -2.0068 0.1298 0.0212 +-1.5371 -0.7257 -0.4871 -0.2359 -1.1724 +[torch.FloatTensor of size 4x5] + +>>> torch.median(a, 1) +( + 0.4056 + 0.1404 + 0.0212 +-0.7257 +[torch.FloatTensor of size 4x1] +, + 0 + 2 + 4 + 1 +[torch.LongTensor of size 4x1] +) +``` + +### torch.mode + +``` +torch.mode(input, dim=-1, values=None, indices=None) -> (Tensor, LongTensor) +``` + +返回给定维`dim`上,每行的众数值。 同时返回一个`LongTensor`,包含众数职的索引。`dim`值默认为输入张量的最后一维。 + +输出形状与输入相同,除了给定维度上为 1. + +*注意*: 这个函数还没有在`torch.cuda.Tensor`中定义 + +参数: + +* input (Tensor) – 输入张量 +* dim (int) – 缩减的维度 +* values (Tensor, 可选的) – 结果张量 +* indices (Tensor, 可选的) – 返回的索引张量 + +例子: + +``` +>>> a + + -0.6891 -0.6662 + 0.2697 0.7412 + 0.5254 -0.7402 + 0.5528 -0.2399 +[torch.FloatTensor of size 4x2] + +>>> a = torch.randn(4, 5) +>>> a + + 0.4056 -0.3372 1.0973 -2.4884 0.4334 + 2.1336 0.3841 0.1404 -0.1821 -0.7646 +-0.2403 1.3975 -2.0068 0.1298 0.0212 +-1.5371 -0.7257 -0.4871 -0.2359 -1.1724 +[torch.FloatTensor of size 4x5] + +>>> torch.mode(a, 1) +( +-2.4884 +-0.7646 +-2.0068 +-1.5371 +[torch.FloatTensor of size 4x1] +, + 3 + 4 + 2 + 0 +[torch.LongTensor of size 4x1] +) +``` + +### torch.norm + +``` +torch.norm(input, p=2) → float +``` + +返回输入张量`input` 的 p 范数。 + +参数: + +* input (Tensor) – 输入张量 +* p (float,可选的) – 范数计算中的幂指数值 + +例子: + +``` +>>> a = torch.randn(1, 3) +>>> a + +-0.4376 -0.5328 0.9547 +[torch.FloatTensor of size 1x3] + +>>> torch.norm(a, 3) +1.0338925067372466 +``` + +``` +torch.norm(input, p, dim, out=None) → Tensor +``` + +返回输入张量给定维`dim` 上每行的 p 范数。 输出形状与输入相同,除了给定维度上为 1. + +参数: + +* input (Tensor) – 输入张量 +* p (float) – 范数计算中的幂指数值 +* dim (int) – 缩减的维度 +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> a = torch.randn(4, 2) +>>> a + +-0.6891 -0.6662 + 0.2697 0.7412 + 0.5254 -0.7402 + 0.5528 -0.2399 +[torch.FloatTensor of size 4x2] + +>>> torch.norm(a, 2, 1) + + 0.9585 + 0.7888 + 0.9077 + 0.6026 +[torch.FloatTensor of size 4x1] + +>>> torch.norm(a, 0, 1) + + 2 + 2 + 2 + 2 +[torch.FloatTensor of size 4x1] +``` + +### torch.prod + +``` +torch.prod(input) → float +``` + +返回输入张量`input` 所有元素的积。 + +参数:input (Tensor) – 输入张量 + +例子: + +``` +>>> a = torch.randn(1, 3) +>>> a + + 0.6170 0.3546 0.0253 +[torch.FloatTensor of size 1x3] + +>>> torch.prod(a) +0.005537458061418483 +``` + +``` +torch.prod(input, dim, out=None) → Tensor +``` + +返回输入张量给定维度上每行的积。 输出形状与输入相同,除了给定维度上为 1. + +参数: + +* input (Tensor) – 输入张量 +* dim (int) – 缩减的维度 +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> a = torch.randn(4, 2) +>>> a + + 0.1598 -0.6884 +-0.1831 -0.4412 +-0.9925 -0.6244 +-0.2416 -0.8080 +[torch.FloatTensor of size 4x2] + +>>> torch.prod(a, 1) + +-0.1100 + 0.0808 + 0.6197 + 0.1952 +[torch.FloatTensor of size 4x1] +``` + +### torch.std + +``` +torch.std(input) → float +``` + +返回输入张量`input` 所有元素的标准差。 + +参数:- input (Tensor) – 输入张量 + +例子: + +``` +>>> a = torch.randn(1, 3) +>>> a + +-1.3063 1.4182 -0.3061 +[torch.FloatTensor of size 1x3] + +>>> torch.std(a) +1.3782334731508061 +``` + +``` +torch.std(input, dim, out=None) → Tensor +``` + +返回输入张量给定维度上每行的标准差。 输出形状与输入相同,除了给定维度上为 1. + +参数: + +* input (Tensor) – 输入张量 +* dim (int) – 缩减的维度 +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> a = torch.randn(4, 4) +>>> a + + 0.1889 -2.4856 0.0043 1.8169 +-0.7701 -0.4682 -2.2410 0.4098 + 0.1919 -1.1856 -1.0361 0.9085 + 0.0173 1.0662 0.2143 -0.5576 +[torch.FloatTensor of size 4x4] + +>>> torch.std(a, dim=1) + + 1.7756 + 1.1025 + 1.0045 + 0.6725 +[torch.FloatTensor of size 4x1] +``` + +### torch.sum + +``` +torch.sum(input) → float +``` + +返回输入张量`input` 所有元素的和。 + +输出形状与输入相同,除了给定维度上为 1. + +参数: + +* input (Tensor) – 输入张量 + +例子: + +``` +>>> a = torch.randn(1, 3) +>>> a + + 0.6170 0.3546 0.0253 +[torch.FloatTensor of size 1x3] + +>>> torch.sum(a) +0.9969287421554327 +``` + +``` +torch.sum(input, dim, out=None) → Tensor +``` + +返回输入张量给定维度上每行的和。 输出形状与输入相同,除了给定维度上为 1. + +参数: + +* input (Tensor) – 输入张量 +* dim (int) – 缩减的维度 +* out (Tensor, 可选的) – 结果张量 + +例子: + +``` +>>> a = torch.randn(4, 4) +>>> a + +-0.4640 0.0609 0.1122 0.4784 +-1.3063 1.6443 0.4714 -0.7396 +-1.3561 -0.1959 1.0609 -1.9855 + 2.6833 0.5746 -0.5709 -0.4430 +[torch.FloatTensor of size 4x4] + +>>> torch.sum(a, 1) + + 0.1874 + 0.0698 +-2.4767 + 2.2440 +[torch.FloatTensor of size 4x1] +``` + +### torch.var + +``` +torch.var(input) → float +``` + +返回输入张量所有元素的方差 + +输出形状与输入相同,除了给定维度上为 1. + +参数: + +* input (Tensor) – 输入张量 + +例子: + +``` +>>> a = torch.randn(1, 3) +>>> a + +-1.3063 1.4182 -0.3061 +[torch.FloatTensor of size 1x3] + +>>> torch.var(a) +1.899527506513334 +``` + +``` +torch.var(input, dim, out=None) → Tensor +``` + +返回输入张量给定维度上每行的方差。 输出形状与输入相同,除了给定维度上为 1. + +参数: + +* input (Tensor) – 输入张量 +* dim (int) – the dimension to reduce +* out (Tensor, 可选的) – 结果张量 例子: + + ``` + >>> a = torch.randn(4, 4) + >>> a + ``` + +-1.2738 -0.3058 0.1230 -1.9615 0.8771 -0.5430 -0.9233 0.9879 1.4107 0.0317 -0.6823 0.2255 -1.3854 0.4953 -0.2160 0.2435 [torch.FloatTensor of size 4x4] + +> > > torch.var(a, 1) + +0.8859 0.9509 0.7548 0.6949 [torch.FloatTensor of size 4x1] + +``` + ## 比较操作 Comparison Ops + +### torch.eq +```python +torch.eq(input, other, out=None) → Tensor +```py + +比较元素相等性。第二个参数可为一个数或与第一个参数同类型形状的张量。 + +参数: + +* input (Tensor) – 待比较张量 +* other (Tensor or float) – 比较张量或数 +* out (Tensor, 可选的) – 输出张量,须为 ByteTensor 类型 or 与`input`同类型 + +返回值: 一个 `torch.ByteTensor` 张量,包含了每个位置的比较结果(相等为 1,不等为 0 ) + +返回类型: Tensor + +例子: +``` + +> > > torch.eq(torch.Tensor([[1, 2], [3, 4]]), torch.Tensor([[1, 1], [4, 4]])) 1 0 0 1 [torch.ByteTensor of size 2x2] ```py + +### torch.equal + +``` +torch.equal(tensor1, tensor2) → bool +```py + +如果两个张量有相同的形状和元素值,则返回`True` ,否则 `False`。 + +例子: +``` + +> > > torch.equal(torch.Tensor([1, 2]), torch.Tensor([1, 2])) True ```py + +### torch.ge + +``` +torch.ge(input, other, out=None) → Tensor +```py + +逐元素比较`input`和`other`,即是否 \( input >= other \)。 + +如果两个张量有相同的形状和元素值,则返回`True` ,否则 `False`。 第二个参数可以为一个数或与第一个参数相同形状和类型的张量 + +参数: + +* input (Tensor) – 待对比的张量 +* other (Tensor or float) – 对比的张量或`float`值 +* out (Tensor, 可选的) – 输出张量。必须为`ByteTensor`或者与第一个参数`tensor`相同类型。 + +返回值: 一个 `torch.ByteTensor` 张量,包含了每个位置的比较结果(是否 input >= other )。 返回类型: Tensor + +例子: +``` + +> > > torch.ge(torch.Tensor([[1, 2], [3, 4]]), torch.Tensor([[1, 1], [4, 4]])) 1 1 0 1 [torch.ByteTensor of size 2x2] ```py + +### torch.gt + +``` +torch.gt(input, other, out=None) → Tensor +```py + +逐元素比较`input`和`other` , 即是否\( input > other \) 如果两个张量有相同的形状和元素值,则返回`True` ,否则 `False`。 第二个参数可以为一个数或与第一个参数相同形状和类型的张量 + +参数: + +* input (Tensor) – 要对比的张量 +* other (Tensor or float) – 要对比的张量或`float`值 +* out (Tensor, 可选的) – 输出张量。必须为`ByteTensor`或者与第一个参数`tensor`相同类型。 + +返回值: 一个 `torch.ByteTensor` 张量,包含了每个位置的比较结果(是否 input >= other )。 返回类型: Tensor + +例子: +``` + +> > > torch.gt(torch.Tensor([[1, 2], [3, 4]]), torch.Tensor([[1, 1], [4, 4]])) 0 1 0 0 [torch.ByteTensor of size 2x2] ```py + +### torch.kthvalue + +``` +torch.kthvalue(input, k, dim=None, out=None) -> (Tensor, LongTensor) +```py + +取输入张量`input`指定维上第 k 个最小值。如果不指定`dim`,则默认为`input`的最后一维。 + +返回一个元组 _(values,indices)_,其中`indices`是原始输入张量`input`中沿`dim`维的第 `k` 个最小值下标。 + +参数: + +* input (Tensor) – 要对比的张量 +* k (int) – 第 `k` 个最小值 +* dim (int, 可选的) – 沿着此维进行排序 +* out (tuple, 可选的) – 输出元组 (Tensor, LongTensor) 可选地给定作为 输出 buffers + +例子: +``` + +> > > x = torch.arange(1, 6) x + +1 2 3 4 5 [torch.FloatTensor of size 5] + +> > > torch.kthvalue(x, 4) ( 4 [torch.FloatTensor of size 1] , 3 [torch.LongTensor of size 1] ) ```py + +### torch.le + +``` +torch.le(input, other, out=None) → Tensor +```py + +逐元素比较`input`和`other` , 即是否\( input <= other \) 第二个参数可以为一个数或与第一个参数相同形状和类型的张量 + +参数: + +* input (Tensor) – 要对比的张量 +* other (Tensor or float ) – 对比的张量或`float`值 +* out (Tensor, 可选的) – 输出张量。必须为`ByteTensor`或者与第一个参数`tensor`相同类型。 + +返回值: 一个 `torch.ByteTensor` 张量,包含了每个位置的比较结果(是否 input >= other )。 返回类型: Tensor + +例子: +``` + +> > > torch.le(torch.Tensor([[1, 2], [3, 4]]), torch.Tensor([[1, 1], [4, 4]])) 1 0 1 1 [torch.ByteTensor of size 2x2] ```py + +### torch.lt + +``` +torch.lt(input, other, out=None) → Tensor +```py + +逐元素比较`input`和`other` , 即是否 \( input < other \) + +第二个参数可以为一个数或与第一个参数相同形状和类型的张量 + +参数: + +* input (Tensor) – 要对比的张量 +* other (Tensor or float ) – 对比的张量或`float`值 +* out (Tensor, 可选的) – 输出张量。必须为`ByteTensor`或者与第一个参数`tensor`相同类型。 + +input: 一个 `torch.ByteTensor` 张量,包含了每个位置的比较结果(是否 tensor >= other )。 返回类型: Tensor + +例子: +``` + +> > > torch.lt(torch.Tensor([[1, 2], [3, 4]]), torch.Tensor([[1, 1], [4, 4]])) 0 0 1 0 [torch.ByteTensor of size 2x2] ```py + +### torch.max + +``` +torch.max() +```py + +返回输入张量所有元素的最大值。 + +参数: + +* input (Tensor) – 输入张量 + +例子: +``` + +> > > a = torch.randn(1, 3) a + +0.4729 -0.2266 -0.2085 [torch.FloatTensor of size 1x3] + +> > > torch.max(a) 0.4729 ```py + +``` +torch.max(input, dim, max=None, max_indices=None) -> (Tensor, LongTensor) +```py + +返回输入张量给定维度上每行的最大值,并同时返回每个最大值的位置索引。 + +输出形状中,将`dim`维设定为 1,其它与输入形状保持一致。 + +参数: + +* input (Tensor) – 输入张量 +* dim (int) – 指定的维度 +* max (Tensor, 可选的) – 结果张量,包含给定维度上的最大值 +* max_indices (LongTensor, 可选的) – 结果张量,包含给定维度上每个最大值的位置索引 + +例子: +``` + +> > a = torch.randn(4, 4) a + +0.0692 0.3142 1.2513 -0.5428 0.9288 0.8552 -0.2073 0.6409 1.0695 -0.0101 -2.4507 -1.2230 0.7426 -0.7666 0.4862 -0.6628 torch.FloatTensor of size 4x4] + +> > > torch.max(a, 1) ( 1.2513 0.9288 1.0695 0.7426 [torch.FloatTensor of size 4x1] , 2 0 0 0 [torch.LongTensor of size 4x1] ) ```py + +``` +torch.max(input, other, out=None) → Tensor +```py + +返回输入张量给定维度上每行的最大值,并同时返回每个最大值的位置索引。 即,\( out_i=max(input_i,other_i) \) + +输出形状中,将`dim`维设定为 1,其它与输入形状保持一致。 + +参数: + +* input (Tensor) – 输入张量 +* other (Tensor) – 输出张量 +* out (Tensor, 可选的) – 结果张量 + +例子: +``` + +> > > a = torch.randn(4) a + +1.3869 0.3912 -0.8634 -0.5468 [torch.FloatTensor of size 4] + +> > > b = torch.randn(4) b + +1.0067 -0.8010 0.6258 0.3627 [torch.FloatTensor of size 4] + +> > > torch.max(a, b) + +1.3869 0.3912 0.6258 0.3627 [torch.FloatTensor of size 4] + +``` + ### torch.min +``` + +torch.min(input) → float + +``` + 返回输入张量所有元素的最小值。 + +参数: input (Tensor) – 输入张量 + +例子: +``` + +> > > a = torch.randn(1, 3) a + +0.4729 -0.2266 -0.2085 [torch.FloatTensor of size 1x3] + +> > > torch.min(a) -0.22663167119026184 ```py + +``` +torch.min(input, dim, min=None, min_indices=None) -> (Tensor, LongTensor) +```py + +返回输入张量给定维度上每行的最小值,并同时返回每个最小值的位置索引。 + +输出形状中,将`dim`维设定为 1,其它与输入形状保持一致。 + +参数: + +* input (Tensor) – 输入张量 +* dim (int) – 指定的维度 +* min (Tensor, 可选的) – 结果张量,包含给定维度上的最小值 +* min_indices (LongTensor, 可选的) – 结果张量,包含给定维度上每个最小值的位置索引 + +例子: +``` + +> > a = torch.randn(4, 4) a + +0.0692 0.3142 1.2513 -0.5428 0.9288 0.8552 -0.2073 0.6409 1.0695 -0.0101 -2.4507 -1.2230 0.7426 -0.7666 0.4862 -0.6628 torch.FloatTensor of size 4x4] + +> > torch.min(a, 1) + +0.54 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | + +``` \ No newline at end of file diff --git a/docs/0.4/11.md b/docs/0.4/11.md new file mode 100644 index 00000000..ce2af305 --- /dev/null +++ b/docs/0.4/11.md @@ -0,0 +1,1283 @@ +# torch.Tensor + +`torch.Tensor`是一种包含单一数据类型元素的多维矩阵。 + +Torch 定义了七种 CPU 张量类型和八种 GPU 张量类型: + +| Data tyoe | CPU tensor | GPU tensor | +| --- | --- | --- | +| 32-bit floating point | `torch.FloatTensor` | `torch.cuda.FloatTensor` | +| 64-bit floating point | `torch.DoubleTensor` | `torch.cuda.DoubleTensor` | +| 16-bit floating point | N/A | `torch.cuda.HalfTensor` | +| 8-bit integer (unsigned) | `torch.ByteTensor` | `torch.cuda.ByteTensor` | +| 8-bit integer (signed) | `torch.CharTensor` | `torch.cuda.CharTensor` | +| 16-bit integer (signed) | `torch.ShortTensor` | `torch.cuda.ShortTensor` | +| 32-bit integer (signed) | `torch.IntTensor` | `torch.cuda.IntTensor` | +| 64-bit integer (signed) | `torch.LongTensor` | `torch.cuda.LongTensor` | + +`torch.Tensor`是默认的 tensor 类型(`torch.FlaotTensor`)的简称。 + +张量可以从 Python 的`list`或序列构成: + +``` +>>> torch.FloatTensor([[1, 2, 3], [4, 5, 6]]) +1 2 3 +4 5 6 +[torch.FloatTensor of size 2x3] +``` + +可以通过指定它的大小来构建一个空的张量: + +``` +>>> torch.IntTensor(2, 4).zero_() +0 0 0 0 +0 0 0 0 +[torch.IntTensor of size 2x4] +``` + +可以使用 Python 的索引和切片符号来访问和修改张量的内容: + +``` +>>> x = torch.FloatTensor([[1, 2, 3], [4, 5, 6]]) +>>> print(x[1][2]) +6.0 +>>> x[0][1] = 8 +>>> print(x) + 1 8 3 + 4 5 6 +[torch.FloatTensor of size 2x3] +``` + +每一个张量 tensor 都有一个相应的`torch.Storage`保存其数据。张量类提供了一个多维的、横向视图的存储,并定义了数字操作。 + +> 注意: 改变张量的方法可以用一个下划线后缀来标示。比如,`torch.FloatTensor.abs_()`会在原地计算绝对值并返回修改的张量,而`tensor.FloatTensor.abs()`将会在新张量中计算结果。 + +``` +class torch.Tensor +class torch.Tensor(*sizes) +class torch.Tensor(size) +class torch.Tensor(sequence) +class torch.Tensor(ndarray) +class torch.Tensor(tensor) +class torch.Tensor(storage) +``` + +从可选的大小或数据创建新的张量。 如果没有给出参数,则返回空的零维张量。如果提供了`numpy.ndarray`,`torch.Tensor`或`torch.Storage`,将会返回一个相同数据的新张量.如果提供了 python 序列,则从序列的副本创建一个新的张量。 + +#### abs() + +参考`torch.abs()`,矩阵数组绝对值 + +#### abs_() + +`abs()`的直接运算形式 + +#### acos() + +参考`torch.acos()` + +#### acos_() + +`acos()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### add(value) + +参考`torch.add()` + +#### add_(value) + +`add()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### addbmm(beta=1, mat, alpha=1, batch1, batch2) + +参考`torch.addbmm()` + +#### addbmm_(beta=1, mat, alpha=1, batch1, batch2) + +`addbmm()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### addcdiv(value=1, tensor1, tensor2) + +参考`torch.addcdiv()` + +#### addcdiv_(value=1, tensor1, tensor2) + +`addcdiv()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### addcmul(value=1, tensor1, tensor2) + +参考`torch.addcmul()` + +#### addcmul_(value=1, tensor1, tensor2) + +`addcmul()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### addmm(beta=1, mat, alpha=1, mat1, mat2) + +参考`torch.addmm()` + +#### addmm_(beta=1, mat, alpha=1, mat1, mat2) + +`addmm()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### addmv(beta=1, tensor, alpha=1, mat, vec) + +参考`torch.addmv()` + +#### addmv_(beta=1, tensor, alpha=1, mat, vec) + +`addmv()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### addr(beta=1, alpha=1, vec1, vec2) + +参考`torch.addr()` + +#### addr_(beta=1, alpha=1, vec1, vec2) + +`addr()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### apply_(callable) + +将函数`callable`作用于 tensor 中每一个元素,并替换每个元素用`callable`函数返回的值。 + +> 注意: 该函数只能在 CPU tensor 中使用,并且不应该用在有较高性能要求的代码块。 + +#### asin() + +参考`torch.asin()` + +#### asin_() + +`asin()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### atan() + +参考`torch.atan()` + +#### atan2() + +参考`torch.atan2()` + +#### atan2_() + +`atan2()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### atan_() + +`atan()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### baddbmm(beta=1, alpha=1, batch1, batch2) + +参考`torch.baddbmm()` + +#### baddbmm_(beta=1, alpha=1, batch1, batch2) + +`baddbmm()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### bernoulli() + +参考`torch.bernoulli()` + +#### bernoulli_() + +`bernoulli()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### bmm(batch2) + +参考`torch.bmm()` + +#### byte() + +将 tensor 改为 byte 类型 + +#### bmm(median=0, sigma=1, *, generator=None) + +将 tensor 中元素用柯西分布得到的数值填充: P(x)=1/π * σ/(x−median)2+σ2 + +#### ceil() + +参考`torch.ceil()` + +#### ceil_() + +`ceil()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### char() + +将 tensor 元素改为 char 类型 + +#### chunk(n_chunks, dim=0) + +将 tensor 分割为 tensor 元组. 参考`torch.chunk()` + +#### clamp(min, max) + +参考`torch.clamp()` + +#### clamp_(min, max) + +`clamp()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### clone() + +返回与原 tensor 有相同大小和数据类型的 tensor + +#### contiguous() + +返回一个内存连续的有相同数据的 tensor,如果原 tensor 内存连续则返回原 tensor + +#### copy_(src, async=False) + +将`src`中的元素复制到 tensor 中并返回这个 tensor。 如果 broadcast 是 True,则源张量必须可以使用该张量广播。否则两个 tensor 应该有相同数目的元素,可以是不同的数据类型或存储在不同的设备上。 + +参数: + +* src(Tensor) - 要复制的源张量 +* async(bool) - 如果为 True,并且此副本位于 CPU 和 GPU 之间,则副本可能会相对于主机异步发生。对于其他副本,此参数无效。 +* broadcast(bool) - 如果为 True,src 将广播到底层张量的形状。 + +#### cos() + +参考`torch.cos()` + +#### cos_() + +`cos()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### cosh() + +参考`torch.cosh()` + +#### cosh_() + +`cosh()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### cpu() + +如果在 CPU 上没有该 tensor,则会返回一个 CPU 的副本 + +#### cross(other, dim=-1) + +参考`torch.cross()` + +#### cuda(device=None, async=False) + +返回此对象在 CPU 内存中的一个副本 如果该对象已经在 CUDA 内存中,并且在正确的设备上,则不会执行任何副本,并返回原始对象。 + +参数: + +* device(int) :目标 GPU ID。默认为当前设备。 +* async(bool) :如果为 True 并且源处于固定内存中,则该副本将相对于主机是异步的。否则,该参数没有意义。 + +#### cumprod(dim) + +参考`torch.cumprod()` + +#### cumsum(dim) + +参考`torch.cumsum()` + +#### data_ptr() → int + +返回 tensor 第一个元素的地址 + +#### diag(diagonal=0) + +参考`torch.diag()` + +#### dim() → int + +返回 tensor 的维数 + +#### dist(other, p=2) + +参考`torch.dist()` + +#### div(value) + +参考`torch.div()` + +#### div_(value) + +`div()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### dot(tensor2) → float + +参考`torch.dot()` + +#### double() + +将该 tensor 投射为 double 类型 + +#### eig(eigenvectors=False) -> (Tensor, Tensor) + +参考`torch.eig()` + +#### element_size() → int + +返回单个元素的字节大小。 例: + +``` +>>> torch.FloatTensor().element_size() +4 +>>> torch.ByteTensor().element_size() +1 +``` + +#### eq(other) + +参考`torch.eq()` + +#### eq_(other) + +`eq()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### equal(other) → bool + +参考`torch.equal()` + +#### exp() + +参考`torch.exp()` + +#### exp_() + +`exp()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### expand(*sizes) + +返回 tensor 的一个新视图,单个维度扩大为更大的尺寸。 tensor 也可以扩大为更高维,新增加的维度将附在前面。 扩大 tensor 不需要分配新内存,只是仅仅新建一个 tensor 的视图,其中通过将`stride`设为 0,一维将会扩展位更高维。任何一个一维的在不分配新内存情况下可扩展为任意的数值。 + +参数: + +* sizes(torch.Size or int...)-需要扩展的大小 + +例: + +``` +>>> x = torch.Tensor([[1], [2], [3]]) +>>> x.size() +torch.Size([3, 1]) +>>> x.expand(3, 4) + 1 1 + 1 1 + 2 2 2 2 + 3 3 3 3 + [torch.FloatTensor of size 3x4] +``` + +#### expand_as(tensor) + +将 tensor 扩展为参数 tensor 的大小。 该操作等效与: + +``` +self.expand(tensor.size()) +``` + +#### exponential_(lambd=1, *, generator=None) $to$ Tensor + +将该 tensor 用指数分布得到的元素填充: $$ P(x)= \lambda e^{- \lambda x} $$ + +#### fill_(value) + +将该 tensor 用指定的数值填充 + +#### float() + +将 tensor 投射为 float 类型 + +#### floor() + +参考`torch.floor()` + +#### floor_() + +`floor()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### fmod(divisor) + +参考`torch.fmod()` + +#### fmod_(divisor) + +`fmod()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### frac() + +参考`torch.frac()` + +#### frac_() + +`frac()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### gather(dim, index) + +参考`torch.gather()` + +#### ge(other) + +参考`torch.ge()` + +#### ge_(other) + +`ge()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### gels(A) + +参考`torch.gels()` + +#### geometric_(p, *, generator=None) + +将该 tensor 用几何分布得到的元素填充: $$ P(X=k)= (1-p)^{k-1}p $$ + +#### geqrf() -> (Tensor, Tensor) + +参考`torch.geqrf()` + +#### ger(vec2) + +参考`torch.ger()` + +#### gesv(A), Tensor + +参考`torch.gesv()` + +#### gt(other) + +参考`torch.gt()` + +#### gt_(other) + +`gt()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### half() + +将 tensor 投射为半精度浮点类型 + +#### histc(bins=100, min=0, max=0) + +参考`torch.histc()` + +#### index(m) + +用一个二进制的掩码或沿着一个给定的维度从 tensor 中选取元素。`tensor.index(m)`与`tensor[m]`完全相同。 + +参数: + +* m(int or Byte Tensor or slice)-用来选取元素的维度或掩码 + + #### index*add*(dim, index, tensor) + + 按参数 index 中的索引数确定的顺序,将参数 tensor 中的元素加到原来的 tensor 中。参数 tensor 的尺寸必须严格地与原 tensor 匹配,否则会发生错误。 + +参数: + +* dim(int)-索引 index 所指向的维度 +* index(LongTensor)-需要从 tensor 中选取的指数 +* tensor(Tensor)-含有相加元素的 tensor + +例: + +``` +>>> x = torch.Tensor([[1, 1, 1], [1, 1, 1], [1, 1, 1]]) +>>> t = torch.Tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) +>>> index = torch.LongTensor([0, 2, 1]) +>>> x.index_add_(0, index, t) +>>> x + 2 3 4 + 8 9 10 + 5 6 7 +[torch.FloatTensor of size 3x3] +``` + +#### index*copy*(dim, index, tensor) + +按参数 index 中的索引数确定的顺序,将参数 tensor 中的元素复制到原来的 tensor 中。参数 tensor 的尺寸必须严格地与原 tensor 匹配,否则会发生错误。 + +参数: + +* dim (int)-索引 index 所指向的维度 +* index (LongTensor)-需要从 tensor 中选取的指数 +* tensor (Tensor)-含有被复制元素的 tensor + +例: + +``` +>>> x = torch.Tensor(3, 3) +>>> t = torch.Tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) +>>> index = torch.LongTensor([0, 2, 1]) +>>> x.index_copy_(0, index, t) +>>> x + 1 2 3 + 7 8 9 + 4 5 6 +[torch.FloatTensor of size 3x3] +``` + +#### index*fill*(dim, index, val) + +按参数 index 中的索引数确定的顺序,将原 tensor 用参数`val`值填充。 + +参数: + +* dim (int)-索引 index 所指向的维度 +* index (LongTensor)-索引 +* val (Tensor)-填充的值 + +例: + +``` +>>> x = torch.Tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) +>>> index = torch.LongTensor([0, 2]) +>>> x.index_fill_(0, index, -1) +>>> x + -1 2 -1 + -1 5 -1 + -1 8 -1 +[torch.FloatTensor of size 3x3] +``` + +#### index_select(dim, index) + +参考`torch.index_select()` + +#### int() + +将该 tensor 投射为 int 类型 + +#### inverse() + +参考`torch.inverse()` + +#### is_contiguous() → bool + +如果该 tensor 在内存中是连续的则返回 True。 + +#### is_pinned() + +如果该 tensor 在固定内内存中则返回 True + +#### is_set_to(tensor) → bool + +如果此对象引用与 Torch C API 相同的`THTensor`对象作为给定的张量,则返回 True。 + +#### kthvalue(k, dim=None) -> (Tensor, LongTensor) + +参考`torch.kthvalue()` + +#### le(other) + +参考`torch.le()` + +#### le_(other) + +`le()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### lerp(start, end, weight) + +参考`torch.lerp()` + +#### lerp*(_start, end, weight*) + +`lerp()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### log() + +参考`torch.log()` + +#### loglp() + +参考`torch.loglp()` + +#### loglp_() + +`loglp()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### log_()→ Tensor + +`log()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### log*normal*(*mwan=1, std=2,* , gegnerator=None*) + +将该 tensor 用均值为$\mu$,标准差为$\sigma$的对数正态分布得到的元素填充。要注意`mean`和`stdv`是基本正态分布的均值和标准差,不是返回的分布: $$ P(X)= \frac {1} {x \sigma \sqrt {2 \pi}}e^{- \frac {(lnx- \mu)^2} {2 \sigma^2}} $$ + +#### long() + +将 tensor 投射为 long 类型 + +#### lt(*other*) + +参考`torch.lt()` + +#### lt*(_other*) + +`lt()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### map*(_tensor, callable*) + +将`callable`作用于本 tensor 和参数 tensor 中的每一个元素,并将结果存放在本 tensor 中。`callable`应该有下列标志: + +``` +def callable(a, b) -> number +``` + +#### masked*copy*(*mask, source*) + +将`mask`中值为 1 元素对应的`source`中位置的元素复制到本 tensor 中。`mask`应该有和本 tensor 相同数目的元素。`source`中元素的个数最少为`mask`中值为 1 的元素的个数。 + +参数: + +* mask (*ByteTensor*)-二进制掩码 +* source (*Tensor*)-复制的源 tensor + +> 注意: `mask`作用于`self`自身的 tensor,而不是参数中的`source`。 +> +> #### masked*fill*(*mask, value*) +> +> 在`mask`值为 1 的位置处用`value`填充。`mask`的元素个数需和本 tensor 相同,但尺寸可以不同。形状 mask 必须 与下面的张量的形状一起广播。 + +参数: + +* mask (ByteTensor)-二进制掩码 +* value (Tensor)-用来填充的值 + +#### masked*select(_mask*) + +参考`torch.masked_select()` + +#### max(*dim=None*) -> *float or(Tensor, Tensor)* + +参考`torch.max()` + +#### mean(*dim=None*) -> *float or(Tensor, Tensor)* + +参考`torch.mean()` + +#### median(*dim=-1, value=None, indices=None*) -> *(Tensor, LongTensor)* + +参考`torch.median()` + +#### min(*dim=None*) -> *float or(Tensor, Tensor)* + +参考`torch.min()` + +#### mm(*mat2*) + +参考`torch.mm()` + +#### mode(*dim=-1, value=None, indices=None*) -> *(Tensor, LongTensor)* + +参考`torch.mode()` + +#### mul(*value*) + +参考`torch.mul()` + +#### mul*(_value*) + +`mul()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### multinomial(*num_samples, replacement=False,* , generator=None*) + +参考`torch.multinomial()` + +#### mv(*vec*) + +参考`torch.mv()` + +#### narrow(*dimension, start, length*) → Te + +返回这个张量的缩小版本的新张量。维度`dim`缩小范围是`start`到`start+length`。返回的张量和该张量共享相同的底层存储。 + +参数: + +* dimension (*int*)-需要缩小的维度 +* start (*int*)-起始维度 +* length (*int*)-长度 + +例: + +``` +>>> x = torch.Tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) +>>> x.narrow(0, 0, 2) + 1 2 3 + 4 5 6 +[torch.FloatTensor of size 2x3] +>>> x.narrow(1, 1, 2) + 2 3 + 5 6 + 8 9 +[torch.FloatTensor of size 3x2] +``` + +#### ndimension() → int + +`dim()`的另一种表示。 + +#### ne(*other*) + +参考`torch.ne()` + +#### ne*(_other*) + +`ne()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### neg() + +参考`torch.neg()` + +#### neg_() + +`neg()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### nelement() → int + +`numel()`的另一种表示 + +#### new(*args, **kwargs) + +构建一个有相同数据类型的 tensor + +#### nonezero() → LongTensor + +参考`torch.nonezero() + +#### norm(*p=2*) → float + +参考`torch.norm() + +#### normal*(_mean=0, std=1,* , gengerator=None*) + +将 tensor 用均值为`mean`和标准差为`std`的正态分布填充。 + +#### numel() → int + +参考`numel()` + +#### numpy() → ndarray + +将该 tensor 以 NumPy 的形式返回`ndarray`,两者共享相同的底层内存。原 tensor 改变后会相应的在`ndarray`有反映,反之亦然。 + +#### orgqr(*input2*) + +参考`torch.orgqr()` + +#### ormqr(*input2, input3, left=True, transpose=False*) + +参考`torch.ormqr()` + +#### permute(*dims*) + +将 tensor 的维度换位。 + +参数: + +* _dims (_int..*)-换位顺序 + +例: + +``` +>>> x = torch.randn(2, 3, 5) +>>> x.size() +torch.Size([2, 3, 5]) +>>> x.permute(2, 0, 1).size() +torch.Size([5, 2, 3]) +``` + +#### pin_memory() + +将张量复制到固定内存(如果尚未固定)。 + +#### potrf(*upper=True*) + +参考`torch.potrf()` + +#### potri(*upper=True*) + +参考`torch.potri()` + +#### potrs(*input2, upper=True*) + +参考`torch.potrs()` + +#### pow(*exponent*) + +参考`torch.pow()` + +#### pow_() + +`pow()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### prod()) → float + +参考`torch.prod()` + +#### pstrf(*upper=True, tol=-1*) -> (*Tensor, IntTensor*) + +参考`torch.pstrf()` + +#### qr()-> (*Tensor, IntTensor*) + +参考`torch.qr()` + +#### random_(from=0, to=None, *, generator=None) + +将 tensor 用从在[from, to-1]上的正态分布或离散正态分布取样值进行填充。如果未指定,则该值仅由该张量数据类型限定。 + +#### reciprocal() + +参考`torch.reciprocal()` + +#### reciprocal_() + +`reciprocal()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### remainder(*divisor*) + +参考`torch.remainder()` + +#### remainder*(_divisor*) + +`remainder()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### renorm(*p, dim, maxnorm*) + +参考`torch.renorm()` + +#### renorm*(_p, dim, maxnorm*) + +`renorm()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### repeat(*sizes) + +沿指定维度重复此张量。 与`expand()`功能不同,此功能可复制张量中的数据。 + +参数: + +* *sizes (torch.Size ot int...)-沿着每个维重复这个张量的次数 + +例: + +``` +>>> x = torch.Tensor([1, 2, 3]) +>>> x.repeat(4, 2) + 1 2 3 1 2 3 + 1 2 3 1 2 3 + 1 2 3 1 2 3 + 1 2 3 1 2 3 +[torch.FloatTensor of size 4x6] +>>> x.repeat(4, 2, 1).size() +torch.Size([4, 2, 3]) +``` + +#### resize_(*sizes) + +将 tensor 的大小调整为指定的大小。如果元素个数比当前的内存大小大,就将底层存储大小调整为与新元素数目一致的大小。如果元素个数比当前内存小,则底层存储不会被改变。原来 tensor 中被保存下来的元素将保持不变,但新内存将不会被初始化。 + +参数: + +* sizes (torch.Size or int...)-需要调整的大小 + +例: + +``` +>>> x = torch.Tensor([[1, 2], [3, 4], [5, 6]]) +>>> x.resize_(2, 2) +>>> x + 1 2 + 3 4 +[torch.FloatTensor of size 2x2] +``` + +#### resize*as*(tensor) + +将当前张量调整为与指定张量相同的大小。这相当于: + +``` +self.resize_(tensor.size()) +``` + +#### round() + +参考`torch.round()` + +#### round_() + +`round()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### rsqrt() + +参考`torch.rsqrt()` + +#### rsqrt_() + +`rsqrt()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### scatter_(dim, index, src) + +将`src`中的所有值按照`index`确定的索引写入本 tensor 中。其中索引是根据给定的 dimension,dim 按照`gather()`描述的规则来确定。 + +请注意,对于 collect,index 的值必须在 0 和(self.size(dim)-1)之间,包括所有值,并且指定维中的一行中的所有值必须是唯一的。 + +参数: + +* input (Tensor)-源 tensor +* dim (int)-索引的轴向 +* index (LongTensor)-散射元素的索引指数 +* src (Tensor or float)-散射的源元素 + +例子: + +``` +>>> x = torch.rand(2, 5) +>>> x + + 0.4319 0.6500 0.4080 0.8760 0.2355 + 0.2609 0.4711 0.8486 0.8573 0.1029 +[torch.FloatTensor of size 2x5] + +>>> torch.zeros(3, 5).scatter_(0, torch.LongTensor([[0, 1, 2, 0, 0], [2, 0, 0, 1, 2]]), x) + + 0.4319 0.4711 0.8486 0.8760 0.2355 + 0.0000 0.6500 0.0000 0.8573 0.0000 + 0.2609 0.0000 0.4080 0.0000 0.1029 +[torch.FloatTensor of size 3x5] + +>>> z = torch.zeros(2, 4).scatter_(1, torch.LongTensor([[2], [3]]), 1.23) +>>> z + + 0.0000 0.0000 1.2300 0.0000 + 0.0000 0.0000 0.0000 1.2300 +[torch.FloatTensor of size 2x4] +``` + +#### select(dim, index) or number + +按照 index 中选定的维度将 tensor 切片。如果 tensor 是一维的,则返回一个数字。否则,返回给定维度已经被移除的 tensor。 + +参数: + +* dim (int)-切片的维度 +* index (int)-用来选取的索引 + +> 注意: `select()`等效于切片。例如:`tensor.select(0, index)`等效于`tensor[index]`,`tensor.select(2, index)`等效于`tensor[:,:,index]` + +#### set(source=None, storage_offset=0, size=None, stride=None) + +设置底层内存,大小和步长。如果 source 是张量,则该张量将共享相同的存储空间,并且具有与给定张量相同的大小和步长。另一个则反映在一个张量内的元素变化。 + +参数: + +* source (Tensor or Storage)-用到的 tensor 或内存 +* storage_offset (int)-内存的偏移量 +* size (torch.Size)-需要的大小,默认为源 tensor 的大小。 +* stride(tuple)-需要的步长,默认为 C 连续的步长。 + +#### share*memory*() + +将底层存储器移动到共享内存。 如果底层存储已经在共享内存和 CUDA 张量中,不进行任何操作。共享内存中的 Tensors 无法调整大小。 + +#### short() + +将 tensor 投射为 short 类型。 + +#### sigmoid() + +参考`torch.sigmoid()` + +#### sigmoid_() + +`sidmoid()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### sign() + +参考`torch.sign()` + +#### sign_() + +`sign()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### sin() + +参考`torch.sin()` + +#### sin_() + +`sin()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### sinh() + +参考`torch.sinh()` + +#### sinh_() + +`sinh()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### size() → torch.Size + +返回 tensor 的大小。返回的值是`tuple`的子类。 + +例: + +``` +>>> torch.Tensor(3, 4, 5).size() +torch.Size([3, 4, 5]) +``` + +#### sort(dim=None, descending=False) -> (Tensor, LongTensor) + +参考`torhc.sort()` + +#### split(split_size, dim=0) + +将 tensor 分割成 tensor 数组。 参考`torhc.split()` + +#### sqrt() + +参考`torch.sqrt()` + +#### sqrt_() + +`sqrt()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### squeeze(dim=None) + +参考`torch.squeeze()` + +#### squeeze_(dim=None) + +`squeeze()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### std() → float + +参考`torch.std()` + +#### storage() → torch.Storage + +返回底层内存。 + +#### storage_offset() → int + +以储存元素的个数的形式返回 tensor 在地城内存中的偏移量。 例: + +``` +>>> x = torch.Tensor([1, 2, 3, 4, 5]) +>>> x.storage_offset() +0 +>>> x[3:].storage_offset() +3 +``` + +#### stride() + +返回 tesnor 的步长。 + +#### sub(value, other) + +从该张量中排除一个标量或张量。如果`value`和`other`都是给定的,则在使用之前`other`的每一个元素都会被`value`缩放。 当`other`是一个张量,`other`的形状必须可以与下面的张量的形状一起[广播](http://pytorch.org/docs/master/notes/broadcasting.html#broadcasting-semantics)。 + +#### sub_(x) + +`sub()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### sum(dim=None) + +参考`torch.sum()` + +#### svd(some=True) -> (Tensor, Tensor, Tensor) + +参考`torch.svd()` + +#### symeig(eigenvectors=False, upper=True) -> (Tensor, Tensor) + +参考`torch.symeig()` + +#### t() + +参考`torch.t()` + +#### t() + +`t()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### tan() + +参考`torch.tan()` + +#### tan_() + +`tan()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### tanh() + +参考`torch.tanh()` + +#### tanh_() + +`tanh()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### tolist() + +返回一个 tensor 的嵌套列表表示。 + +#### topk(k, dim=None, largest=True, sorted=True) -> (Tensor, LongTensor) + +参考`torch.topk()` + +#### trace() → float + +参考`torch.trace()` + +#### transpose(dim0, dim1) + +参考`torch.transpose()` + +#### transpose(dim0, dim1) + +`transpose()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### tril(k=0) + +参考`torch.tril()` + +#### tril_(k=0) + +`tril()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### triu(k=0) + +参考`torch.triu()` + +#### triu(k=0) + +`triu()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### trtrs(A, upper=True, transpose=False, unitriangular=False) -> (Tensor, Tensor) + +参考`torch.trtrs()` + +#### trunc() + +参考`torch.trunc()` + +#### trunc() + +`trunc()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### type(new_type=None, async=False) + +如果未提供 new_type,则返回类型,否则将此对象转换为指定的类型。 如果已经是正确的类型,则不会执行且返回原对象。 + +参数: + +* new_type (type 或 string)-需要的类型 +* async (bool)-如果为 True,并且源处于固定内存中,目标位于 GPU 上,反之亦然,则相对于主机异步执行该副本。否则,该参数不发挥作用。 + +#### type_as(tesnor) + +将此张量转换为给定类型的张量。 如果张量已经是正确的类型,则不会执行操作。等效于: + +``` +self.type(tensor.type()) +``` + +参数: + +* tensor (Tensor):有所需要类型的 tensor + +#### unfold(dim, size, step) + +返回一个张量,其中包含尺寸 size 中所有尺寸的维度。 如果*sizedim*是 dim 维度的原始大小,则返回张量中的维度是(sizedim-size)/step+1 + +维度大小的附加维度将附加在返回的张量中。 + +参数: + +* dim (int)- 展开的维度 +* size (int)- 展开的每个切片的大小 +* step (int)-相邻切片之间的步长 + +例子: + +``` +>>> x = torch.arange(1, 8) +>>> x + + 1 + 2 + 3 + 4 + 5 + 6 + 7 +[torch.FloatTensor of size 7] + +>>> x.unfold(0, 2, 1) + + 1 2 + 2 3 + 3 4 + 4 5 + 5 6 + 6 7 +[torch.FloatTensor of size 6x2] + +>>> x.unfold(0, 2, 2) + + 1 2 + 3 4 + 5 6 +[torch.FloatTensor of size 3x2] +``` + +#### uniform_(from=0, to=1) + +用均匀分布采样的数字填充该张量: + +#### unsqueeze(dim) + +参考`torch.unsqueeze()` + +#### unsqueeze_(dim) + +`unsqueeze()`的直接运算形式,即直接执行并且返回修改后的张量 + +#### var() + +参考`torch.var()` + +#### view(*args) + +返回具有相同数据但大小不同的新张量。 返回的张量必须有与原张量相同的数据和相同数量的元素,但可以有不同的大小。一个张量必须是连续`contiguous()`的才能被查看。 + +例子: + +``` +>>> x = torch.randn(4, 4) +>>> x.size() +torch.Size([4, 4]) +>>> y = x.view(16) +>>> y.size() +torch.Size([16]) +>>> z = x.view(-1, 8) # the size -1 is inferred from other dimensions +>>> z.size() +torch.Size([2, 8]) +``` + +#### view_as(tensor) + +返回被视作与给定的 tensor 相同大小的原 tensor。 等效于: + +``` +self.view(tensor.size()) +``` + +#### zero_() + +用 0 填充这个张量。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/12.md b/docs/0.4/12.md new file mode 100644 index 00000000..e289e0d4 --- /dev/null +++ b/docs/0.4/12.md @@ -0,0 +1,120 @@ +# Tensor Attributes + +* [torch.dtype](#torch-dtype) +* [torch.device](#torch-device) +* [torch.layout](#torch-layout) + +每个`torch.Tensor`都有`torch.dtype`, `torch.device`,和`torch.layout`。 + +### torch.dtype + +`torch.dtype`是表示`torch.Tensor`的数据类型的对象。`PyTorch`有八种不同的数据类型: + +| | Data type | dtype | Tensor types | +| --- | --- | --- | --- | +| 32-bit floating point | torch.float32 or torch.float | torch.*.FloatTensor | +| 64-bit floating point | torch.float64 or torch.double | torch.*.DoubleTensor | +| 16-bit floating point | torch.float16 or torch.half | torch.*.HalfTensor | +| 8-bit integer (unsigned) | torch.uint8 | torch.*.ByteTensor | +| 8-bit integer (signed) | torch.int8 | torch.*.CharTensor | +| 16-bit integer (signed) | torch.int16 or torch.short | torch.*.ShortTensor | +| 32-bit integer (signed) | torch.int32 or torch.int | torch.*.IntTensor | +| 64-bit integer (signed) | torch.int64 or torch.long | torch.*.LongTensor | + +使用方法: + +``` +>>> x = torch.Tensor([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) +>>> print x.type() +torch.FloatTensor +``` + +### torch.device + +`torch.device`代表将`torch.Tensor`分配到的设备的对象。 + +`torch.device`包含一个设备类型(`'cpu'`或`'cuda'`设备类型)和可选的设备的序号。如果设备序号不存在,则为当前设备; 例如,`torch.Tensor`用设备构建`'cuda'`的结果等同于`'cuda:X'`,其中`X`是`torch.cuda.current_device()`的结果。 + +`torch.Tensor`的设备可以通过`Tensor.device`访问属性。 + +构造`torch.device`可以通过字符串/字符串和设备编号。 + +通过一个字符串: + +``` +>>> torch.device('cuda:0') +device(type='cuda', index=0) + +>>> torch.device('cpu') +device(type='cpu') + +>>> torch.device('cuda') # current cuda device +device(type='cuda') +``` + +通过字符串和设备序号: + +``` +>>> torch.device('cuda', 0) +device(type='cuda', index=0) + +>>> torch.device('cpu', 0) +device(type='cpu', index=0) +``` + +> **注意** `torch.device`函数中的参数通常可以用一个字符串替代。这允许使用代码快速构建原型。 +> +> ``` +> >> # Example of a function that takes in a torch.device +> >> cuda1 = torch.device('cuda:1') +> >> torch.randn((2,3), device=cuda1) +> ``` +> +> ``` +> >> # You can substitute the torch.device with a string +> >> torch.randn((2,3), 'cuda:1') +> ``` + +* * * + +> **注意** 出于传统原因,可以通过单个设备序号构建设备,将其视为`cuda`设备。这匹配`Tensor.get_device()`,它为`cuda`张量返回一个序数,并且不支持`cpu`张量。 +> +> ``` +> >> torch.device(1) +> device(type='cuda', index=1) +> ``` + +* * * + +> **注意** 指定设备的方法可以使用(properly formatted)字符串或(legacy)整数型设备序数,即以下示例均等效: +> +> ``` +> >> torch.randn((2,3), device=torch.device('cuda:1')) +> >> torch.randn((2,3), device='cuda:1') +> >> torch.randn((2,3), device=1) # legacy +> ``` + +### torch.layout + +`torch.layout`表示`torch.Tensor`内存布局的对象。目前,我们支持`torch.strided(dense Tensors)`并为`torch.sparse_coo(sparse COO Tensors)`提供实验支持。 + +`torch.strided`代表密集张量,是最常用的内存布局。每个`strided`张量都会关联 一个`torch.Storage`,它保存着它的数据。这些张力提供了多维度, 存储的`strided`视图。`Strides`是一个整数型列表:`k-th stride`表示在张量的第 k 维从一个元素跳转到下一个元素所需的内存。这个概念使得可以有效地执行多张量。 + +例: + +``` +>>> x = torch.Tensor([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) +>>> x.stride() +(5, 1) + +>>> x.t().stride() +(1, 5) +``` + +关于`torch.sparse_coo`张量的更多信息,请参阅[torch.sparse](https://ptorch.com/docs/8/torch-sparse)。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/13.md b/docs/0.4/13.md new file mode 100644 index 00000000..f34e63a9 --- /dev/null +++ b/docs/0.4/13.md @@ -0,0 +1,102 @@ +# torch.sparse + +> 警告 该 API 目前是实验性的,可能在不久的将来会发生变化。 + +torch 支持 COO(rdinate)格式的稀疏张量,可以有效地存储和处理大多数元素为零的张量。 + +稀疏张量被表示为一对致密张量:值的张量和 2D 张量的索引。可以通过提供这两个张量来构造稀疏张量,以及稀疏张量的大小(不能从这些张量推断出!)假设我们要在位置(0,2)处定义具有条目 3 的稀疏张量, ,位置(1,0)的条目 4,位置(1,2)的条目 5。我们会写: + +``` +>>> i = torch.LongTensor([[0, 1, 1], + [2, 0, 2]]) +>>> v = torch.FloatTensor([3, 4, 5]) +>>> torch.sparse.FloatTensor(i, v, torch.Size([2,3])).to_dense() + 0 0 3 + 4 0 5 +[torch.FloatTensor of size 2x3] +``` + +请注意,LongTensor 的输入不是索引元组的列表。如果要以这种方式编写索引,则在将它们传递给稀疏构造函数之前,应该进行转置: + +``` +>>> i = torch.LongTensor([[0, 2], [1, 0], [1, 2]]) +>>> v = torch.FloatTensor([3, 4, 5 ]) +>>> torch.sparse.FloatTensor(i.t(), v, torch.Size([2,3])).to_dense() + 0 0 3 + 4 0 5 +[torch.FloatTensor of size 2x3] +``` + +您还可以构建混合稀疏张量,其中只有第一个 n 维是稀疏的,其余的维度是密集的。 + +``` +>>> i = torch.LongTensor([[2, 4]]) +>>> v = torch.FloatTensor([[1, 3], [5, 7]]) +>>> torch.sparse.FloatTensor(i, v).to_dense() + 0 0 + 0 0 + 1 3 + 0 0 + 5 7 +[torch.FloatTensor of size 5x2] +``` + +可以通过指定一个空的稀疏张量来构建一个空的稀疏张量: + +``` +print torch.sparse.FloatTensor(2, 3) +# FloatTensor of size 2x3 with indices: +# [torch.LongTensor with no dimension] +# and values: +# [torch.FloatTensor with no dimension] +``` + +> 注意: +> +> 我们的稀疏张量格式允许未被缩小的稀疏张量,其中索引中可能有重复的坐标; 在这种情况下,解释是该索引处的值是所有重复值条目的总和。非协调张量允许我们更有效地实施某些运营商。 +> +> 在大多数情况下,您不必关心稀疏张量是否合并,因为大多数操作将在合并或未被缩小的稀疏张量的情况下工作相同。但是,您可能需要关心两种情况。 +> +> 首先,如果您反复执行可以产生重复条目(例如 torch.sparse.FloatTensor.add())的操作,则应偶尔将您的稀疏张量合并,以防止它们变得太大。 +> +> 其次,一些运营商将取决于它们是否被合并或不产生不同的值(例如, torch.sparse.FloatTensor._values()和 torch.sparse.FloatTensor._indices(),以及 torch.Tensor._sparse_mask())。这些运算符前面加上一个下划线,表示它们显示内部实现细节,应谨慎使用,因为与聚结的稀疏张量一起工作的代码可能无法与未被缩放的稀疏张量一起使用; 一般来说,在与这些运营商合作之前明确地合并是最安全的。 +> +> 例如,假设我们想通过直接操作来实现一个操作符 torch.sparse.FloatTensor._values()。随着乘法分布的增加,标量的乘法可以以明显的方式实现; 然而,平方根不能直接实现,因为(如果你被赋予了未被缩放的张量,那将是什么)。sqrt(a + b) != sqrt(a) + sqrt(b) + +### class torch.sparse.FloatTensor + +* add() +* add_() +* clone() +* dim() +* div() +* div_() +* get_device() +* hspmm() +* mm() +* mul() +* mul_() +* resizeAs_() +* size() +* spadd() +* spmm() +* sspaddmm() +* sspmm() +* sub() +* sub_() +* t_() +* toDense() +* transpose() +* transpose_() +* zero_() +* coalesce() +* is_coalesced() +* _indices() +* _values() +* _nnz() + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/14.md b/docs/0.4/14.md new file mode 100644 index 00000000..859cdc94 --- /dev/null +++ b/docs/0.4/14.md @@ -0,0 +1,267 @@ +# torch.cuda + +* [交流集](https://ptorch.com/docs/1/torch-cuda#communication-collectives) +* [流和事件](https://ptorch.com/docs/1/torch-cuda#streams-and-events) +* [NVIDIA 工具扩展(NVTX)](https://ptorch.com/docs/1/torch-cuda#nvidia-tools-extension-nvtx) + +* * * + +该包增加了对 CUDA 张量类型的支持,实现了与 CPU 张量相同的功能,但使用 GPU 进行计算。 + +它是延迟的初始化,所以你可以随时导入它,并使用`is_available()`来确定系统是否支持 CUDA。 + +[CUDA 语义](http://pytorch.org/docs/master/notes/cuda.html#cuda-semantics)有关于使用 CUDA 的更多细节。 + +``` +torch.cuda.current_blas_handle() +``` + +返回指向当前 cuBLAS 句柄的 cublasHandle_t 指针 + +``` +torch.cuda.current_device() +``` + +返回当前所选设备的索引。 + +``` +torch.cuda.current_stream() +``` + +返回当前选定的`Stream` + +``` +class torch.cuda.device(idx) +``` + +更改所选设备的上下文管理器。 + +参数: + +* idx(int) – 设备索引选择。如果这个参数是负的,则是无效操作。 + +``` +torch.cuda.device_count() +``` + +返回可用的 GPU 数量。 + +``` +class torch.cuda.device_of(obj) +``` + +将当前设备更改为给定对象的上下文管理器。 + +可以使用张量和存储作为参数。如果给定的对象不是在 GPU 上分配的,则是无效操作。 + +参数: + +* obj (Tensor 或者 Storage) – 在选定设备上分配的对象。 + +``` +torch.cuda.is_available() +``` + +返回 bool 值,指示当前 CUDA 是否可用。 + +``` +torch.cuda.set_device(device) +``` + +设置当前设备。 + +不鼓励使用此功能函数。在大多数情况下,最好使用`CUDA_VISIBLE_DEVICES`环境变量。 + +参数: + +* device(int) - 选择的设备。如果此参数为负,则此函数是无操作的。 + +``` +torch.cuda.stream(stream) +``` + +选择给定流的上下文管理器。 + +在其上下文中排队的所有 CUDA 核心将在所选流上排列。 + +参数: + +* stream(Stream) – 选择的流。如果为`None`,则这个管理器是无效的。 + +``` +torch.cuda.synchronize() +``` + +等待当前设备上所有流中的所有内核完成。 + +### 交流集 + +``` +torch.cuda.comm.broadcast(tensor, devices) +``` + +向一些 GPU 广播张量。 + +参数: + +* tensor (Tensor) – 广播的张量 +* devices (Iterable) – 可以广播的设备的迭代。注意,它的设备形式为(src,dst1,dst2,...),其第一个元素是广播来源的设备。 + +返回: 包含张量副本的元组,放置在对应于索引的设备上。 + +``` +torch.cuda.comm.reduce_add(inputs, destination=None) +``` + +将来自多个 GPU 的张量相加。 + +所有输入应具有匹配的形状。 + +参数: + +* inputs (Iterable[Tensor]) – 要相加张量的迭代 +* destination (int, 可选) – 将放置输出的设备(默认值:当前设备)。 + +返回: 包含放置在`destination`设备上的所有输入的元素总和的张量。 + +``` +torch.cuda.comm.scatter(tensor, devices, chunk_sizes=None, dim=0, streams=None) +``` + +跨多个 GPU 分散张量。 + +参数: + +* tensor (Tensor) – 要分散的张量 +* devices (Iterable[int]) – 可迭代的 int,指定张量应分散在哪些设备中。 +* chunk_sizes (Iterable[int], 可选) – 要放置在每个设备上的块大小。它应该匹配`devices`的长度且总和为`tensor.size(dim)`。 如果没有指定,张量将被分成相等的块。 +* dim (int, 可选) – 将张量块化的一个维度。 + +返回: 包含`tensor`块的元组,传播给`devices`。 + +``` +torch.cuda.comm.gather(tensors, dim=0, destination=None) +``` + +从多个 GPU 收集张量。 + +所有张量的测量尺寸与 dim 不一致。 + +参数: + +* tensors (Iterable[Tensor]) – 要收集的张量的迭代。 +* dim (int) – 张量沿着此维度来连接。 +* destination (int, 可选) – 输出设备(-1 表示 CPU,默认值:当前设备)。 + +返回: 一个张量位于`destination`设备上,这是沿着`dim`连接`tensors`的结果。 + +## 流和事件 + +``` +class torch.cuda.Stream +``` + +CUDA 流的包装。 + +参数: + +* device (int, 可选) – 分配流的设备。 +* priority (int, 可选) – 流的优先级。数字越小优先级越高。 + + > query() + + 检查所有提交的工作是否已经完成。 + + 返回: 一个布尔值,表示此流中的所有核心是否完成。 + + > record_event(event=None) + + 记录事件。 + + 参数: + + * event (Event, 可选) – 要记录的事件。如果未定义,将分配一个新的。 返回: 记录的事件。 + + > synchronize() + + 等待此流中的所有核心完成。 + + > wait_event(event) + + 将所有未来的工作提交到流等待事件。 + + 参数: event (Event) – 等待的事件 + + > wait_stream(stream) + + 与另一个流同步。 + + 提交到此流的所有未来工作将等待直到所有核心在调用完成时提交给给定的流。 + +``` +class torch.cuda.Event(enable_timing=False, blocking=False, interprocess=False, _handle=None) +``` + +CUDA 事件的包装。 + +参数: + +* enable_timing (bool) – 指示事件是否应该测量时间(默认值:False) +* blocking (bool) – 如果为 true,`wait()`将被阻塞(默认值:False) +* interprocess (bool) – 如果为 true,事件可以在进程之间共享(默认值:False) + + > elapsed_time(end_event) + + 返回事件记录之前经过的时间。 + + > ipc_handle() + + 返回此事件的 IPC 句柄。 + + > query() + + 检查事件是否已被记录。 + + 返回:指示事件是否已被记录的布尔值。 + + > record(stream=None) + + 在给定的流中记录事件。 + + > synchronize() + + 与事件同步。 + + > wait(stream=None) + + 使给定的流等待事件。 + + ## NVIDIA 工具扩展(NVTX) + + ``` + torch.cuda.nvtx.mark(msg) + ``` + + 描述在某个时刻发生的瞬时事件。 参数: + + * msg(string) - 与事件关联的 ASCII 消息。 + + ``` + torch.cuda.nvtx.range_push(msg) + ``` + + 将范围推送到嵌套范围跨度的堆栈。返回起始范围的基于 zero-based 的深度。 参数: + + * msg(string) - 与范围关联的 ASCII 消息 + + ``` + torch.cuda.nvtx.range_pop() + ``` + + 从一堆嵌套的范围范围内弹出一个范围。返回结束的范围的基于 zero-based 的深度。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/15.md b/docs/0.4/15.md new file mode 100644 index 00000000..94e9c518 --- /dev/null +++ b/docs/0.4/15.md @@ -0,0 +1,53 @@ +# torch.Storage + +* * * + +`torch.Storage`是单个数据类型的连续的`一维数组`,每个`torch.Tensor`都具有相同数据类型的相应存储。 + +``` +class torch.FloatStorage +``` + +* `byte()`:将 Storage 转为 byte 类型 +* `char()`:将 Storage 转为 char 类型 +* `clone()`:返回 Storage 的副本 +* `copy_()` +* `cpu()`:如果尚未在 CPU 上,则返回 Storage 的 CPU 副本 +* `cuda(device=None, async=False)`:在 CUDA 内存中返回此对象的副本。如果该对象已经在 CUDA 内存中,并且在正确的设备上,则不会执行任何操作,并返回原始对象。 参数说明: + + 1. device (int) - 目标 GPU 的 id。默认值是当前设备。 + 2. async (bool) -如果值为 True,且源在锁定内存中,则副本相对于宿主是异步的。否则此参数不起效果。 +* `data_ptr()`: 返回一个时间戳 +* `double()`:将 Storage 转为 double 类型 +* `element_size()`:返回参数的 size +* `fill_()` +* `float()`:将 Storage 转为 float 类型 +* `from_buffer()` +* `half()`:将 Storage 转为 half 类型 +* `int()`:将 Storage 转为 int 类型 +* `is_cuda = False` +* `is_pinned()` +* `is_shared()` +* `is_sparse = False` +* `long()`:将 Storage 转为 long 类型 +* `new()` +* `pin_memory()`:将存储复制到固定内存(如果尚未固定)。 +* `resize_()` +* `share_memory_()`:这对于已经在共享内存和 CUDA 存储器中的存储器是无效的,不需要为了跨进程共享而移动。无法调整共享内存中的存储空间。返回:self +* `short()`:将 Storage 转为 short 类型 +* `size()`:返回 Storage 转的大小 +* `tolist()`:返回一个包含 Storage 中元素的列表 +* `type(new_type=None, async=False)`:将此对象转为指定类型。如果已经是正确类型,不会执行复制操作,直接返回原对象。 + +参数说明: + +1. `new_type` (type 或 string) -需要转成的类型 +2. `async (bool)` -如果值为 True,并且源处于固定内存中,目标位于 GPU 上,反之亦然,则相对于主机异步执行该副本。否则,参数没有效果。 + +具体使用教程请参考:[使用 torch.Storage 共享多个张量的相同存储以及将 torch.Tensor 转化为 torch.Storage](https://ptorch.com/news/52.html) + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/16.md b/docs/0.4/16.md new file mode 100644 index 00000000..f6477ad0 --- /dev/null +++ b/docs/0.4/16.md @@ -0,0 +1,1636 @@ +# torch.nn + +* [Parameters](#parameters) +* [Containers](#containers) + +### class torch.nn.Parameter() + +一种`Variable`,被视为一个模块参数。 + +`Parameters` 是 `Variable` 的子类。当与`Module`一起使用时,它们具有非常特殊的属性,当它们被分配为模块属性时,它们被自动添加到其参数列表中,并将出现在例如`parameters()`迭代器中。分配变量没有这样的效果。这是因为人们可能希望在模型中缓存一些临时状态,如`RNN`的最后一个隐藏状态。如果没有这样的班级`Parameter`,这些临时人员也会注册。 + +另一个区别是,`parameters`不能是`volatile`,他们默认要求梯度。 + +参数说明: + +* data (Tensor) – parameter tensor. + +* requires_grad (bool, optional) – 如果需要计算剃度,可以参考[从向后排除子图](http://pytorch.org/docs/master/notes/autograd.html#excluding-subgraphs) + +### class torch.nn.Module + +所有神经网络模块的基类。 + +你的模型也应该继承这个类。 + +`Modules`还可以包含其他模块,允许将它们嵌套在树结构中。您可以将子模块分配为常规属性: + +``` +import torch.nn as nn +import torch.nn.functional as F + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + self.conv1 = nn.Conv2d(1, 20, 5)# submodule: Conv2d + self.conv2 = nn.Conv2d(20, 20, 5) + + def forward(self, x): + x = F.relu(self.conv1(x)) + return F.relu(self.conv2(x)) +``` + +以这种方式分配的子模块将被注册,并且在调用`.cuda()`等时也会转换参数。 + +#### add_module(name, module) + +将一个子模块添加到当前模块。 该模块可以使用给定的名称作为属性访问。 例: + +``` +import torch.nn as nn +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + self.add_module("conv", nn.Conv2d(10, 20, 4)) + #self.conv = nn.Conv2d(10, 20, 4) 和上面这个增加 module 的方式等价 +model = Model() +print(model.conv) +``` + +输出: + +``` +Conv2d(10, 20, kernel_size=(4, 4), stride=(1, 1)) +``` + +#### apply(fn) + +适用`fn`递归到每个子模块(如返回`.children()`),以及自我。典型用途包括初始化模型的参数(另见`torch-nn-init`)。 例如: + +``` +>>> def init_weights(m): +>>> print(m) +>>> if type(m) == nn.Linear: +>>> m.weight.data.fill_(1.0) +>>> print(m.weight) +>>> +>>> net = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2)) +>>> net.apply(init_weights) +Linear (2 -> 2) +Parameter containing: + 1 1 + 1 1 +[torch.FloatTensor of size 2x2] +Linear (2 -> 2) +Parameter containing: + 1 1 + 1 1 +[torch.FloatTensor of size 2x2] +Sequential ( + (0): Linear (2 -> 2) + (1): Linear (2 -> 2) +) +``` + +#### children() + +返回直接的子模块的迭代器。 + +#### cpu(device_id=None) + +将所有模型参数和缓冲区移动到 CPU + +#### cuda(device_id=None) + +将所有模型参数和缓冲区移动到 GPU。 + +参数说明: + +* device_id (int, 可选) – 如果指定,所有参数将被复制到该设备 + +#### double() + +将所有参数和缓冲区转换为双数据类型。 + +#### eval() + +将模型设置成`evaluation`模式 + +仅仅当模型中有`Dropout`和`BatchNorm`是才会有影响。 + +#### float() + +将所有参数和缓冲区转换为 float 数据类型。 + +#### forward(* input) + +定义计算在每一个调用执行。 应该被所有子类重写。 + +#### half() + +将所有参数和缓冲区转换为`half`类型。 + +#### load_state_dict(state_dict) + +将参数和缓冲区复制`state_dict`到此模块及其后代。键`state_dict`必须与此模块`state_dict()`功能返回的键完全相符。 + +参数说明: + +* state_dict (dict) – 保存`parameters`和`persistent buffers`的`dict`。 + +#### modules() + +返回网络中所有模块的迭代器。 + +> NOTE: 重复的模块只返回一次。在以下示例中,`l`将仅返回一次。 + +``` +>>> l = nn.Linear(2, 2) +>>> net = nn.Sequential(l, l) +>>> for idx, m in enumerate(net.modules()): +>>> print(idx, '->', m) +0 -> Sequential ( + (0): Linear (2 -> 2) + (1): Linear (2 -> 2) +) +1 -> Linear (2 -> 2) +``` + +#### named_children() + +返回包含子模块的迭代器,同时产生模块的名称以及模块本身。 + +例子: + +``` +>>> for name, module in model.named_children(): +>>> if name in ['conv4', 'conv5']: +>>> print(module) +``` + +#### named_modules(memo=None, prefix='') + +返回网络中所有模块的迭代器,同时产生模块的名称以及模块本身。 + +> 注意: 重复的模块只返回一次。在以下示例中,`l`将仅返回一次。 +> +> ``` +> >> l = nn.Linear(2, 2) +> >> net = nn.Sequential(l, l) +> >> for idx, m in enumerate(net.named_modules()): +> >> print(idx, '->', m) +> 0 -> ('', Sequential ( +> (0): Linear (2 -> 2) +> (1): Linear (2 -> 2) +> )) +> 1 -> ('0', Linear (2 -> 2)) +> ``` +> +> #### named_parameters(memo=None, prefix='') +> +> 返回模块参数的迭代器,同时产生参数的名称以及参数本身 例如: +> +> ``` +> >> for name, param in self.named_parameters(): +> >> if name in ['bias']: +> >> print(param.size()) +> ``` + +#### parameters() + +返回模块参数的迭代器。 这通常被传递给优化器。 + +例子: + +``` +for param in model.parameters(): + print(type(param.data), param.size()) + + (20L,) + (20L, 1L, 5L, 5L) +``` + +#### register_backward_hook(hook) + +在模块上注册一个向后的钩子。 + +每当计算相对于模块输入的梯度时,将调用该钩。挂钩应具有以下签名: + +``` +hook(module, grad_input, grad_output) -> Variable or None +``` + +如果`module`有多个输入输出的话,那么`grad_input` `grad_output`将会是个`tuple`。 `hook`不应该修改它的`arguments`,但是它可以选择性的返回关于输入的梯度,这个返回的梯度在后续的计算中会替代`grad_input`。 + +这个函数返回一个句柄(`handle`)。它有一个方法 `handle.remove()`,可以用这个方法将`hook`从`module`移除。 + +#### register_buffer(name, tensor) + +给`module`添加一个持久缓冲区。 + +这通常用于注册不应被视为模型参数的缓冲区。例如,BatchNorm running_mean 不是参数,而是持久状态的一部分。 + +缓冲区可以使用给定的名称作为属性访问。 + +例子: + +``` +self.register_buffer('running_mean', torch.zeros(num_features)) +``` + +#### register_forward_hook(hook) + +在模块上注册一个`forward hook`。 每次调用`forward()`计算输出的时候,这个`hook`就会被调用。它应该拥有以下签名: + +``` +hook(module, input, output) -> None +``` + +`hook`不应该修改 `input`和`output`的值。 这个函数返回一个有`handle.remove()`方法的句柄(`handle`)。可以用这个方法将`hook`从`module`移除。 + +#### register_parameter(name, param) + +向`module`添加 `parameter` + +该参数可以使用给定的名称作为属性访问。 + +#### state_dict(destination=None, prefix='') + +返回包含模块整体状态的字典。 + +包括参数和持久缓冲区(例如运行平均值)。键是相应的参数和缓冲区名称。 + +例子: + +``` +module.state_dict().keys() +# ['bias', 'weight'] +``` + +#### train(mode=True) + +将模块设置为训练模式。 + +仅仅当模型中有`Dropout`和`BatchNorm`是才会有影响。 + +#### zero_grad() + +将所有模型参数的梯度设置为零。 + +### class torch.nn.Sequential(* args) + +一个时序容器。`Modules` 会以他们传入的顺序被添加到容器中。当然,也可以传入一个`OrderedDict`。 + +为了更容易理解,给出的是一个小例子: + +``` +# Example of using Sequential + +model = nn.Sequential( + nn.Conv2d(1,20,5), + nn.ReLU(), + nn.Conv2d(20,64,5), + nn.ReLU() + ) +# Example of using Sequential with OrderedDict +model = nn.Sequential(OrderedDict([ + ('conv1', nn.Conv2d(1,20,5)), + ('relu1', nn.ReLU()), + ('conv2', nn.Conv2d(20,64,5)), + ('relu2', nn.ReLU()) + ])) +``` + +### class torch.nn.ModuleList(modules=None) + +将`submodules`保存在一个`list`中。 + +`ModuleList`可以像一般的`Python list`一样被`索引`。而且`ModuleList`中包含的`modules`已经被正确的注册,对所有的`module method`可见。 + +参数说明: + +* modules (list, optional) – 要添加的模块列表 + +例子: + +``` +class MyModule(nn.Module): + def __init__(self): + super(MyModule, self).__init__() + self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)]) + + def forward(self, x): + # ModuleList can act as an iterable, or be indexed using ints + for i, l in enumerate(self.linears): + x = self.linears[i // 2](x) + l(x) + return x +``` + +#### append(module) + +在列表末尾附加一个给定的模块。 + +参数说明: + +* module (nn.Module) – 要追加的模块 + +#### extend(modules) + +最后从 Python 列表中追加模块。 + +参数说明: + +* modules(list) – 要附加的模块列表 + +### class torch.nn.ParameterList(parameters=None) + +在列表中保存参数。 + +ParameterList 可以像普通 Python 列表一样进行索引,但是它包含的参数已经被正确注册,并且将被所有的 Module 方法都可见。 + +参数说明: + +* modules (list, 可选) – nn.Parameter 要添加的列表 + +例子: + +``` +class MyModule(nn.Module): + def __init__(self): + super(MyModule, self).__init__() + self.params = nn.ParameterList([nn.Parameter(torch.randn(10, 10)) for i in range(10)]) + + def forward(self, x): + # ModuleList can act as an iterable, or be indexed using ints + for i, p in enumerate(self.params): + x = self.params[i // 2].mm(x) + p.mm(x) + return x +``` + +#### append(parameter) + +在列表末尾添加一个给定的参数。 + +参数说明: + +* parameter (nn.Parameter) – 要追加的参数 + +#### extend(parameters) + +在 Python 列表中附加参数。 + +参数说明: + +* parameters (list) – 要追加的参数列表 + +### class torch.nn.Conv1d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True) + +一维卷积层,输入的尺度是(N, C_in,L),输出尺度( N,C_out,L_out)的计算方式: + +$$ out(N*i, C*{out*j})=bias(C* {out*j})+\sum^{C*{in}-1}*{k=0}weight(C*{out_j},k)\bigotimes input(N_i,k) $$ + +**说明** + +`bigotimes`: 表示相关系数计算 `stride`: 控制相关系数的计算步长 `dilation`: 用于控制内核点之间的距离,详细描述在[这里](https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md) `groups`: 控制输入和输出之间的连接, `group=1`,输出是所有的输入的卷积;`group=2`,此时相当于有并排的两个卷积层,每个卷积层计算输入通道的一半,并且产生的输出是输出通道的一半,随后将这两个输出连接起来。 + +**Parameters:** + +* in_channels(`int`) – 输入信号的通道 +* out_channels(`int`) – 卷积产生的通道 +* kerner_size(`int` or `tuple`) - 卷积核的尺寸 +* stride(`int` or `tuple`, `optional`) - 卷积步长 +* padding (`int` or `tuple`, `optional`)- 输入的每一条边补充 0 的层数 +* dilation(`int` or `tuple`, `optional``) – 卷积核元素之间的间距 +* groups(`int`, `optional`) – 从输入通道到输出通道的阻塞连接数 +* bias(`bool`, `optional`) - 如果`bias=True`,添加偏置 + +**shape:** 输入: (N,C_in,L_in) 输出: (N,C_out,L_out) 输入输出的计算方式: + +$$L*{out}=floor((L*{in}+2*padding-dilation*(kernerl_size-1)-1)/stride+1)$$ + +**变量:** + +* weight(`tensor`) - 卷积的权重,大小是(`out_channels`, `in_channels`, `kernel_size`) +* bias(`tensor`) - 卷积的偏置系数,大小是(`out_channel`) + +**example:** + +``` +>>> m = nn.Conv1d(16, 33, 3, stride=2) +>>> input = autograd.Variable(torch.randn(20, 16, 50)) +>>> output = m(input) +``` + +### class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True) + +二维卷积层, 输入的尺度是(N, C_in,H,W),输出尺度(N,C_out,H_out,W_out)的计算方式: + +$$out(N*i, C*{out*j})=bias(C*{out*j})+\sum^{C*{in}-1}*{k=0}weight(C*{out_j},k)\bigotimes input(N_i,k)$$ + +**说明** `bigotimes`: 表示二维的相关系数计算 `stride`: 控制相关系数的计算步长 `dilation`: 用于控制内核点之间的距离,详细描述在[这里](https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md) `groups`: 控制输入和输出之间的连接: `group=1`,输出是所有的输入的卷积;`group=2`,此时相当于有并排的两个卷积层,每个卷积层计算输入通道的一半,并且产生的输出是输出通道的一半,随后将这两个输出连接起来。 + +参数`kernel_size`,`stride,padding`,`dilation`也可以是一个`int`的数据,此时卷积 height 和 width 值相同;也可以是一个`tuple`数组,`tuple`的第一维度表示 height 的数值,tuple 的第二维度表示 width 的数值 + +**Parameters:** + +* in_channels(`int`) – 输入信号的通道 +* out_channels(`int`) – 卷积产生的通道 +* kerner_size(`int` or `tuple`) - 卷积核的尺寸 +* stride(`int` or `tuple`, `optional`) - 卷积步长 +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 +* dilation(`int` or `tuple`, `optional`) – 卷积核元素之间的间距 +* groups(`int`, `optional`) – 从输入通道到输出通道的阻塞连接数 +* bias(`bool`, `optional`) - 如果`bias=True`,添加偏置 + +**shape:** input: (N,C_in,H_in,W_in) output: (N,C_out,H_out,W_out) + +$$H*{out}=floor((H*{in}+2*padding[0]-dilation[0]*(kernerl_size[0]-1)-1)/stride[0]+1)$$ + +$$W*{out}=floor((W*{in}+2*padding[1]-dilation[1]*(kernerl_size[1]-1)-1)/stride[1]+1)$$ + +**变量:** weight(`tensor`) - 卷积的权重,大小是(`out_channels`, `in_channels`,`kernel_size`) bias(`tensor`) - 卷积的偏置系数,大小是(`out_channel`) + +Examples: + +``` +>>> # With square kernels and equal stride +>>> m = nn.Conv2d(16, 33, 3, stride=2) +>>> # non-square kernels and unequal stride and with padding +>>> m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2)) +>>> # non-square kernels and unequal stride and with padding and dilation +>>> m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1)) +>>> input = autograd.Variable(torch.randn(20, 16, 50, 100)) +>>> output = m(input) +``` + +### class torch.nn.Conv3d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True) + +三维卷积层, 输入的尺度是(N, C_in,D,H,W),输出尺度(N,C_out,D_out,H_out,W_out)的计算方式: + +$$out(N*i, C*{out*j})=bias(C*{out*j})+\sum^{C*{in}-1}*{k=0}weight(C*{out_j},k)\bigotimes input(N_i,k)$$ + +**说明** `bigotimes`: 表示二维的相关系数计算 `stride`: 控制相关系数的计算步长 `dilation`: 用于控制内核点之间的距离,详细描述在[这里](https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md) `groups`: 控制输入和输出之间的连接: `group=1`,输出是所有的输入的卷积;`group=2`,此时相当于有并排的两个卷积层,每个卷积层计算输入通道的一半,并且产生的输出是输出通道的一半,随后将这两个输出连接起来。 参数`kernel_size`,`stride`,`padding`,`dilation`可以是一个`int`的数据 - 卷积 height 和 width 值相同,也可以是一个有三个`int`数据的`tuple`数组,`tuple`的第一维度表示 depth 的数值,`tuple`的第二维度表示 height 的数值,`tuple`的第三维度表示 width 的数值 + +**Parameters:** + +* in_channels(`int`) – 输入信号的通道 +* out_channels(`int`) – 卷积产生的通道 +* kernel_size(`int` or `tuple`) - 卷积核的尺寸 +* stride(`int` or `tuple`, `optional`) - 卷积步长 +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 +* dilation(`int` or `tuple`, `optional`) – 卷积核元素之间的间距 +* groups(`int`, `optional`) – 从输入通道到输出通道的阻塞连接数 +* bias(`bool`, `optional`) - 如果`bias=True`,添加偏置 + +**shape:** `input`: ((N, C*{in}, D*{in}, H*{in}, W*{in})) `output`: ((N, C*{out}, D*{out}, H*{out}, W*{out})) where (D*{out} = floor((D*{in} + 2 *padding[0] - dilation[0]* (kernel*size[0] - 1) - 1) / stride[0] + 1)) (H*{out} = floor((H*{in} + 2 _padding[1] - dilation[1]* (kernel*size[1] - 1) - 1) / stride[1] + 1)) (W*{out} = floor((W*{in} + 2 _padding[2] - dilation[2]* (kernel_size[2] - 1) - 1) / stride[2] + 1)) + +**变量:** + +* weight(`tensor`) - 卷积的权重,shape 是(`out_channels`, `in_channels`,`kernel_size`)` +* bias(`tensor`) - 卷积的偏置系数,shape 是(`out_channel`) + +Examples: + +``` +>>> # With square kernels and equal stride +>>> m = nn.Conv3d(16, 33, 3, stride=2) +>>> # non-square kernels and unequal stride and with padding +>>> m = nn.Conv3d(16, 33, (3, 5, 2), stride=(2, 1, 1), padding=(4, 2, 0)) +>>> input = autograd.Variable(torch.randn(20, 16, 10, 50, 100)) +>>> output = m(input) +``` + +### class torch.nn.ConvTranspose1d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1) + +1 维的解卷积操作(`transposed convolution operator`,注意改视作操作可视作解卷积操作,但并不是真正的解卷积操作) 该模块可以看作是`Conv1d`相对于其输入的梯度,有时(但不正确地)被称为解卷积操作。 + +**注意** 由于内核的大小,输入的最后的一些列的数据可能会丢失。因为输入和输出是不是完全的互相关。因此,用户可以进行适当的填充(padding 操作)。 + +**参数** + +* in_channels(`int`) – 输入信号的通道数 +* out_channels(`int`) – 卷积产生的通道 +* kernel_size(`int` or `tuple`) - 卷积核的大小 +* stride(`int` or `tuple`, `optional`) - 卷积步长 +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 +* output_padding(`int` or `tuple`, `optional`) - 输出的每一条边补充 0 的层数 +* dilation(`int` or `tuple`, `optional`) – 卷积核元素之间的间距 +* groups(`int`, `optional`) – 从输入通道到输出通道的阻塞连接数 +* bias(`bool`, `optional`) - 如果`bias=True`,添加偏置 + +**shape:** 输入: ((N, C*{in}, L*{in})) 输出: ((N, C*{out}, L*{out})) where (L*{out} = (L*{in} - 1) *stride - 2* padding + kernel_size + output_padding) + +**变量:** + +* weight(`tensor`) - 卷积的权重,大小是(`in_channels`, `in_channels`,`kernel_size`) +* bias(`tensor`) - 卷积的偏置系数,大小是(`out_channel`) + +### class torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1) + +2 维的转置卷积操作(`transposed convolution operator`,注意改视作操作可视作解卷积操作,但并不是真正的解卷积操作) 该模块可以看作是`Conv2d`相对于其输入的梯度,有时(但不正确地)被称为解卷积操作。 + +**说明** + +`stride`: 控制相关系数的计算步长 `dilation`: 用于控制内核点之间的距离,详细描述在[这里](https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md) `groups`: 控制输入和输出之间的连接: `group=1`,输出是所有的输入的卷积;`group=2`,此时相当于有并排的两个卷积层,每个卷积层计算输入通道的一半,并且产生的输出是输出通道的一半,随后将这两个输出连接起来。 + +参数`kernel_size`,`stride`,`padding`,`dilation`数据类型: 可以是一个`int`类型的数据,此时卷积 height 和 width 值相同; 也可以是一个`tuple`数组(包含来两个`int`类型的数据),第一个`int`数据表示`height`的数值,第二个`int`类型的数据表示 width 的数值 + +> 注意 由于内核的大小,输入的最后的一些列的数据可能会丢失。因为输入和输出是不是完全的互相关。因此,用户可以进行适当的填充(`padding`操作)。 + +**参数:** + +* in_channels(`int`) – 输入信号的通道数 +* out_channels(`int`) – 卷积产生的通道数 +* kerner_size(`int` or `tuple`) - 卷积核的大小 +* stride(`int` or `tuple`,`optional`) - 卷积步长 +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 +* output_padding(`int` or `tuple`, `optional`) - 输出的每一条边补充 0 的层数 +* dilation(`int` or `tuple`, `optional`) – 卷积核元素之间的间距 +* groups(`int`, `optional`) – 从输入通道到输出通道的阻塞连接数 +* bias(`bool`, `optional`) - 如果`bias=True`,添加偏置 + +**shape:** 输入: ((N, C*{in}, H*{in}, W*{in})) 输出: ((N, C*{out}, H*{out}, W*{out})) where (H*{out} = (H*{in} - 1) *stride[0] - 2* padding[0] + kernel*size[0] + output_padding[0]) (W*{out} = (W*{in} - 1) _stride[1] - 2* padding[1] + kernel_size[1] + output_padding[1]) + +**变量:** + +* weight(`tensor`) - 卷积的权重,大小是(`in_channels`, `in_channels`,`kernel_size`) +* bias(`tensor`) - 卷积的偏置系数,大小是(`out_channel`) + +**Example** + +``` +>>> # With square kernels and equal stride +>>> m = nn.ConvTranspose2d(16, 33, 3, stride=2) +>>> # non-square kernels and unequal stride and with padding +>>> m = nn.ConvTranspose2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2)) +>>> input = autograd.Variable(torch.randn(20, 16, 50, 100)) +>>> output = m(input) +>>> # exact output size can be also specified as an argument +>>> input = autograd.Variable(torch.randn(1, 16, 12, 12)) +>>> downsample = nn.Conv2d(16, 16, 3, stride=2, padding=1) +>>> upsample = nn.ConvTranspose2d(16, 16, 3, stride=2, padding=1) +>>> h = downsample(input) +>>> h.size() +torch.Size([1, 16, 6, 6]) +>>> output = upsample(h, output_size=input.size()) +>>> output.size() +torch.Size([1, 16, 12, 12]) +``` + +### class torch.nn.ConvTranspose3d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1) + +3 维的转置卷积操作(`transposed convolution operator`,注意改视作操作可视作解卷积操作,但并不是真正的解卷积操作) 转置卷积操作将每个输入值和一个可学习权重的卷积核相乘,输出所有输入通道的求和 + +该模块可以看作是`Conv3d`相对于其输入的梯度,有时(但不正确地)被称为解卷积操作。 + +**说明** + +`stride`: 控制相关系数的计算步长 `dilation`: 用于控制内核点之间的距离,详细描述在[这里](https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md) `groups`: 控制输入和输出之间的连接: `group=1`,输出是所有的输入的卷积;`group=2`,此时相当于有并排的两个卷积层,每个卷积层计算输入通道的一半,并且产生的输出是输出通道的一半,随后将这两个输出连接起来。 + +参数`kernel\_size`,`stride`, `padding`,`dilation`数据类型: 一个`int`类型的数据,此时卷积 height 和 width 值相同; 也可以是一个`tuple`数组(包含来两个`int`类型的数据),第一个`int`数据表示 height 的数值,tuple 的第二个 int 类型的数据表示 width 的数值 + +**注意** 由于内核的大小,输入的最后的一些列的数据可能会丢失。因为输入和输出是不是完全的互相关。因此,用户可以进行适当的填充(padding 操作)。 + +**参数:** + +* in_channels(`int`) – 输入信号的通道数 +* out_channels(`int`) – 卷积产生的通道数 +* kernel_size(`int` or `tuple`) - 卷积核的大小 +* stride(`int` or `tuple`, `optional`) - 卷积步长 +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 +* output_padding(`int` or `tuple`, `optional`) - 输出的每一条边补充 0 的层数 +* dilation(`int` or `tuple`, `optional`) – 卷积核元素之间的间距 +* groups(`int`, `optional`) – 从输入通道到输出通道的阻塞连接数 +* bias(`bool`, `optional`) - 如果`bias=True`,添加偏置 + +**shape:** 输入: ((N, C*{in}, D*{in}, H*{in}, W*{in})) 输出: ((N, C*{out}, D*{out}, H*{out}, W*{out})) where (D*{out} = (D*{in} - 1) *stride[0] - 2* padding[0] + kernel*size[0] + output_padding[0]) (H*{out} = (H*{in} - 1) _stride[1] - 2* padding[1] + kernel*size[1] + output_padding[1]) (W*{out} = (W*{in} - 1) _stride[2] - 2* padding[2] + kernel_size[2] + output_padding[2]) + +**变量:** + +* weight(`tensor`) - 卷积的权重,大小是(`in_channels`, `in_channels`,`kernel_size`) +* bias(`tensor`) - 卷积的偏置系数,大小是(`out_channel`) + +**Example** + +``` +>>> # With square kernels and equal stride +>>> m = nn.ConvTranspose3d(16, 33, 3, stride=2) +>>> # non-square kernels and unequal stride and with padding +>>> m = nn.Conv3d(16, 33, (3, 5, 2), stride=(2, 1, 1), padding=(0, 4, 2)) +>>> input = autograd.Variable(torch.randn(20, 16, 10, 50, 100)) +>>> output = m(input) +``` + +### class torch.nn.MaxPool1d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False) + +对于输入信号的输入通道,提供 1 维最大池化(`max pooling`)操作 + +如果输入的大小是(N,C,L),那么输出的大小是(N,C,L_out)的计算方式是: + +$$out(N*i, C_j,k)=max^{kernel_size-1}*{m=0}input(N_{i},C_j,stride*k+m)$$ + +如果`padding`不是 0,会在输入的每一边添加相应数目 0 `dilation`用于控制内核点之间的距离,详细描述在[这里](https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md) + +**参数:** + +* kernel_size(`int` or `tuple`) - max pooling 的窗口大小 +* stride(`int` or `tuple`, `optional`) - max pooling 的窗口移动的步长。默认值是`kernel_size` +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 +* dilation(`int` or `tuple`, `optional`) – 一个控制窗口中元素步幅的参数 +* return_indices - 如果等于`True`,会返回输出最大值的序号,对于上采样操作会有帮助 +* ceil_mode - 如果等于`True`,计算输出信号大小的时候,会使用向上取整,代替默认的向下取整的操作 + +**shape:** 输入: (N,C_in,L_in) 输出: (N,C_out,L_out) + +$$L*{out}=floor((L*{in} + 2*padding - dilation*(kernel_size - 1) - 1)/stride + 1$$ + +**example:** + +``` +>>> # pool of size=3, stride=2 +>>> m = nn.MaxPool1d(3, stride=2) +>>> input = autograd.Variable(torch.randn(20, 16, 50)) +>>> output = m(input) +``` + +### class torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False) + +对于输入信号的输入通道,提供 2 维最大池化(`max pooling`)操作 + +如果输入的大小是(N,C,H,W),那么输出的大小是(N,C,H_out,W_out)和池化窗口大小(kH,kW)的关系是: + +$$out(N*i, C_j,k)=max^{kH-1}*{m=0}max^{kW-1}*{m=0}input(N*{i},C_j,stride[0]_h+m,stride[1]_w+n)$$ + +如果`padding`不是 0,会在输入的每一边添加相应数目 0 `dilation`用于控制内核点之间的距离,详细描述在[这里](https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md) + +参数`kernel_size`,`stride`, `padding`,`dilation`数据类型: 可以是一个`int`类型的数据,此时卷积 height 和 width 值相同; 也可以是一个`tuple`数组(包含来两个 int 类型的数据),第一个`int`数据表示 height 的数值,`tuple`的第二个 int 类型的数据表示 width 的数值 + +**参数:** + +* kernel_size(`int` or `tuple`) - max pooling 的窗口大小 +* stride(`int` or `tuple`, `optional`) - max pooling 的窗口移动的步长。默认值是`kernel_size` +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 +* dilation(`int` or `tuple`, `optional`) – 一个控制窗口中元素步幅的参数 +* return_indices - 如果等于`True`,会返回输出最大值的序号,对于上采样操作会有帮助 +* ceil_mode - 如果等于`True`,计算输出信号大小的时候,会使用向上取整,代替默认的向下取整的操作 + +**shape:** 输入: (N,C,H_{in},W_in) 输出: (N,C,H_out,W_out) + +$$H*{out}=floor((H*{in} + 2*padding[0] - dilation[0]*(kernel_size[0] - 1) - 1)/stride[0] + 1$$ + +$$W*{out}=floor((W*{in} + 2*padding[1] - dilation[1]*(kernel_size[1] - 1) - 1)/stride[1] + 1$$ + +**example:** + +``` +>>> # pool of square window of size=3, stride=2 +>>> m = nn.MaxPool2d(3, stride=2) +>>> # pool of non-square window +>>> m = nn.MaxPool2d((3, 2), stride=(2, 1)) +>>> input = autograd.Variable(torch.randn(20, 16, 50, 32)) +>>> output = m(input) +``` + +### class torch.nn.MaxPool3d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False) + +对于输入信号的输入通道,提供 3 维最大池化(max pooling)操作 + +如果输入的大小是(N,C,D,H,W),那么输出的大小是(N,C,D,H_out,W_out)和池化窗口大小(kD,kH,kW)的关系是: + +$$out(N*i,C_j,d,h,w)=max^{kD-1}*{m=0}max^{kH-1}*{m=0}max^{kW-1}*{m=0}$$ + +$$input(N_{i},C_j,stride[0]_k+d,stride[1]_h+m,stride[2]*w+n)$$ + +如果`padding`不是 0,会在输入的每一边添加相应数目 0 `dilation`用于控制内核点之间的距离,详细描述在[这里](https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md) + +参数`kernel_size`,`stride`, `padding`,`dilation`数据类型: 可以是`int`类型的数据,此时卷积 height 和 width 值相同; 也可以是一个`tuple`数组(包含来两个`int`类型的数据),第一个`int`数据表示 height 的数值,`tuple`的第二个`int`类型的数据表示 width 的数值 + +**参数:** + +* kernel_size(`int` or `tuple`) - max pooling 的窗口大小 +* stride(`int` or `tuple`, `optional`) - max pooling 的窗口移动的步长。默认值是 kernel_size +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 +* dilation(`int` or `tuple`, `optional`) – 一个控制窗口中元素步幅的参数 +* return_indices - 如果等于`True`,会返回输出最大值的序号,对于上采样操作会有帮助 +* ceil_mode - 如果等于`True`,计算输出信号大小的时候,会使用向上取整,代替默认的向下取整的操作 + +**shape:** 输入: (N,C,H_in,W_in) 输出: (N,C,H_out,W_out) + +$$D*{out}=floor((D*{in} + 2*padding[0] - dilation[0]*(kernel_size[0] - 1) - 1)/stride[0] + 1)$$ + +$$H*{out}=floor((H*{in} + 2*padding[1] - dilation[1]*(kernel_size[0] - 1) - 1)/stride[1] + 1)$$ + +$$W*{out}=floor((W*{in} + 2*padding[2] - dilation[2]*(kernel_size[2] - 1) - 1)/stride[2] + 1)$$ + +**example:** + +``` +>>> # pool of square window of size=3, stride=2 +>>>m = nn.MaxPool3d(3, stride=2) +>>> # pool of non-square window +>>> m = nn.MaxPool3d((3, 2, 2), stride=(2, 1, 2)) +>>> input = autograd.Variable(torch.randn(20, 16, 50,44, 31)) +>>> output = m(input) +``` + +#### class torch.nn.MaxUnpool1d(kernel_size, stride=None, padding=0) + +`Maxpool1d`的逆过程,不过并不是完全的逆过程,因为在`maxpool1d`的过程中,一些最大值的已经丢失。 `MaxUnpool1d`输入`MaxPool1d`的输出,包括最大值的索引,并计算所有`maxpool1d`过程中非最大值被设置为零的部分的反向。 + +**注意:** `MaxPool1d`可以将多个输入大小映射到相同的输出大小。因此,反演过程可能会变得模棱两可。 为了适应这一点,可以在调用中将输出大小(`output_size`)作为额外的参数传入。 具体用法,请参阅下面的输入和示例 + +**参数:** + +* kernel_size(`int` or `tuple`) - max pooling 的窗口大小 +* stride(`int` or `tuple`, `optional`) - max pooling 的窗口移动的步长。默认值是`kernel_size` +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 + +**输入:** `input`:需要转换的`tensor` `indices`:Maxpool1d 的索引号 `output_size`:一个指定输出大小的`torch.Size` + +**shape:** `input`: (N,C,H_in) `output`:(N,C,H_out) + +$$H*{out}=(H*{in}-1)_stride[0]-2_padding[0]+kernel_size[0]$$ 也可以使用`output_size`指定输出的大小 + +**Example:** + +``` +>>> pool = nn.MaxPool1d(2, stride=2, return_indices=True) +>>> unpool = nn.MaxUnpool1d(2, stride=2) +>>> input = Variable(torch.Tensor([[[1, 2, 3, 4, 5, 6, 7, 8]]])) +>>> output, indices = pool(input) +>>> unpool(output, indices) + Variable containing: + (0 ,.,.) = + 0 2 0 4 0 6 0 8 + [torch.FloatTensor of size 1x1x8] + +>>> # Example showcasing the use of output_size +>>> input = Variable(torch.Tensor([[[1, 2, 3, 4, 5, 6, 7, 8, 9]]])) +>>> output, indices = pool(input) +>>> unpool(output, indices, output_size=input.size()) + Variable containing: + (0 ,.,.) = + 0 2 0 4 0 6 0 8 0 + [torch.FloatTensor of size 1x1x9] +>>> unpool(output, indices) + Variable containing: + (0 ,.,.) = + 0 2 0 4 0 6 0 8 + [torch.FloatTensor of size 1x1x8] +``` + +#### class torch.nn.MaxUnpool2d(kernel_size, stride=None, padding=0) + +`Maxpool2d`的逆过程,不过并不是完全的逆过程,因为在 maxpool2d 的过程中,一些最大值的已经丢失。 `MaxUnpool2d`的输入是`MaxPool2d`的输出,包括最大值的索引,并计算所有`maxpool2d`过程中非最大值被设置为零的部分的反向。 + +**注意:** `MaxPool2d`可以将多个输入大小映射到相同的输出大小。因此,反演过程可能会变得模棱两可。 为了适应这一点,可以在调用中将输出大小(`output_size`)作为额外的参数传入。具体用法,请参阅下面示例 + +**参数:** + +* kernel_size(`int` or `tuple`) - max pooling 的窗口大小 +* stride(`int` or `tuple`, `optional`) - max pooling 的窗口移动的步长。默认值是`kernel_size` +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 + +**输入:** `input`:需要转换的`tensor` `indices`:Maxpool1d 的索引号 `output_size`:一个指定输出大小的`torch.Size` + +**大小:** `input`: (N,C,H_in,W_in) `output`:(N,C,H_out,W_out) + +$$H*{out}=(H*{in}-1)_stride[0]-2_padding[0]+kernel_size[0]$$ + +$$W*{out}=(W*{in}-1)_stride[1]-2_padding[1]+kernel_size[1]$$ + +也可以使用`output_size`指定输出的大小 + +**Example:** + +``` +>>> pool = nn.MaxPool2d(2, stride=2, return_indices=True) +>>> unpool = nn.MaxUnpool2d(2, stride=2) +>>> input = Variable(torch.Tensor([[[[ 1, 2, 3, 4], + ... [ 5, 6, 7, 8], + ... [ 9, 10, 11, 12], + ... [13, 14, 15, 16]]]])) +>>> output, indices = pool(input) +>>> unpool(output, indices) + Variable containing: + (0 ,0 ,.,.) = + 0 0 0 0 + 0 6 0 8 + 0 0 0 0 + 0 14 0 16 + [torch.FloatTensor of size 1x1x4x4] + +>>> # specify a different output size than input size +>>> unpool(output, indices, output_size=torch.Size([1, 1, 5, 5])) + Variable containing: + (0 ,0 ,.,.) = + 0 0 0 0 0 + 6 0 8 0 0 + 0 0 0 14 0 + 16 0 0 0 0 + 0 0 0 0 0 + [torch.FloatTensor of size 1x1x5x5] +``` + +#### class torch.nn.MaxUnpool3d(kernel_size, stride=None, padding=0) + +`Maxpool3d`的逆过程,不过并不是完全的逆过程,因为在`maxpool3d`的过程中,一些最大值的已经丢失。 `MaxUnpool3d`的输入就是`MaxPool3d`的输出,包括最大值的索引,并计算所有`maxpool3d`过程中非最大值被设置为零的部分的反向。 + +**注意:** `MaxPool3d`可以将多个输入大小映射到相同的输出大小。因此,反演过程可能会变得模棱两可。为了适应这一点,可以在调用中将输出大小(`output_size`)作为额外的参数传入。具体用法,请参阅下面的输入和示例 + +**参数:** + +* kernel_size(`int` or `tuple`) - Maxpooling 窗口大小 +* stride(`int` or `tuple`, `optional`) - max pooling 的窗口移动的步长。默认值是`kernel_size` +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 + +**输入:** `input`:需要转换的`tensor` `indices`:`Maxpool1d`的索引序数 `output_size`:一个指定输出大小的`torch.Size` + +**大小:** `input`: (N,C,D_in,H_in,W_in) `outpu`t:(N,C,D_out,H_out,W_out) + +$$ \begin{aligned} D*{out}=(D*{in}-1)*stride[0]-2_padding[0]+kernel_size[0]\ H*{out}=(H*{in}-1)_stride[1]-2_padding[0]+kernel_size[1]\ W*{out}=(W_{in}-1)_stride[2]-2_padding[2]+kernel_size[2] \end{aligned} $$ + +也可以使用`output_size`指定输出的大小 + +**Example:** + +``` +>>> # pool of square window of size=3, stride=2 +>>> pool = nn.MaxPool3d(3, stride=2, return_indices=True) +>>> unpool = nn.MaxUnpool3d(3, stride=2) +>>> output, indices = pool(Variable(torch.randn(20, 16, 51, 33, 15))) +>>> unpooled_output = unpool(output, indices) +>>> unpooled_output.size() +torch.Size([20, 16, 51, 33, 15]) +``` + +### class torch.nn.AvgPool1d(kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True) + +对信号的输入通道,提供 1 维平均池化(average pooling ) 输入信号的大小(N,C,L),输出大小(N,C,L_out)和池化窗口大小 k 的关系是: + +$$out(N*i,C_j,l)=1/k*\sum^{k}*{m=0}input(N*{i},C*{j},stride*l+m)$$ 如果`padding`不是 0,会在输入的每一边添加相应数目 0 + +**参数:** + +* kernel_size(`int` or `tuple`) - 池化窗口大小 +* stride(`int` or `tuple`, `optional`) - max pooling 的窗口移动的步长。默认值是`kernel_size` +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 +* dilation(`int` or `tuple`, `optional`) – 一个控制窗口中元素步幅的参数 +* return_indices - 如果等于`True`,会返回输出最大值的序号,对于上采样操作会有帮助 +* ceil_mode - 如果等于`True`,计算输出信号大小的时候,会使用向上取整,代替默认的向下取整的操作 + +**大小:** `input`:(N,C,L_in) `output`:(N,C,L_out) + +$$L*{out}=floor((L*{in}+2*padding-kernel_size)/stride+1)$$ + +**Example:** + +``` +>>> # pool with window of size=3, stride=2 +>>> m = nn.AvgPool1d(3, stride=2) +>>> m(Variable(torch.Tensor([[[1,2,3,4,5,6,7]]]))) +Variable containing: + (0 ,.,.) = + 2 4 6 + [torch.FloatTensor of size 1x1x3] +``` + +### class torch.nn.AvgPool2d(kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True) + +对信号的输入通道,提供 2 维的平均池化(average pooling ) 输入信号的大小(N,C,H,W),输出大小(N,C,H_out,W_out)和池化窗口大小(kH,kW)的关系是: + +$$ out(N*i,C_j,h,w)=1/(kH_kW)*\sum^{kH-1}*{m=0}\sum^{kW-1}*{n=0}input(N*{i},C*{j},stride[0]_h+m,stride[1]_w+n)$$ + +如果`padding`不是 0,会在输入的每一边添加相应数目 0 + +**参数:** + +* kernel_size(`int` or `tuple`) - 池化窗口大小 +* stride(`int` or `tuple`, `optional`) - max pooling 的窗口移动的步长。默认值是`kernel_size` +* padding(`int` or `tuple`, `optional`) - 输入的每一条边补充 0 的层数 +* dilation(`int` or `tuple`, `optional`) – 一个控制窗口中元素步幅的参数 +* ceil_mode - 如果等于`True`,计算输出信号大小的时候,会使用向上取整,代替默认的向下取整的操作 +* count_include_pad - 如果等于`True`,计算平均池化时,将包括`padding`填充的 0 + +**shape:** `input`: (N,C,H_in,W_in) `output`: (N,C,H_out,W_out) + +$$\begin{aligned} H*{out}=floor((H*{in}+2*padding[0]-kernel*size[0])/stride[0]+1)\ W*{out}=floor((W_{in}+2*padding[1]-kernel_size[1])/stride[1]+1) \end{aligned} $$ + +**Example:** + +``` +>>> # pool of square window of size=3, stride=2 +>>> m = nn.AvgPool2d(3, stride=2) +>>> # pool of non-square window +>>> m = nn.AvgPool2d((3, 2), stride=(2, 1)) +>>> input = autograd.Variable(torch.randn(20, 16, 50, 32)) +>>> output = m(input) +``` + +### class torch.nn.AvgPool3d(kernel_size, stride=None) + +对信号的输入通道,提供 3 维的平均池化(`average pooling`) 输入信号的大小(N,C,D,H,W),输出大小(N,C,D_out,H_out,W_out)和池化窗口大小(kD,kH,kW)的关系是: + +$$ \begin{aligned} out(N*i,C_j,d,h,w)=1/(kD_kH_kW)*\sum^{kD-1}*{k=0}\sum^{kH-1}*{m=0}\sum^{kW-1}*{n=0}input(N*{i},C*{j},stride[0]_d+k,stride[1]_h+m,stride[2]*w+n) \end{aligned} $$ 如果`padding`不是 0,会在输入的每一边添加相应数目 0 + +**参数:** + +* kernel_size(`int` or `tuple`) - 池化窗口大小 +* stride(`int` or `tuple`, `optional`) - max `pooling`的窗口移动的步长。默认值是`kernel_size` + +**shape:** 输入大小:(N,C,D*in,H_in,W_in) 输出大小:(N,C,D_out,H_out,W_out) $$\begin{aligned} D*{out}=floor((D*{in}+2*padding[0]-kernel_size[0])/stride[0]+1)\ H*{out}=floor((H*{in}+2*padding[1]-kernel_size[1])/stride[1]+1)\ W*{out}=floor((W_{in}+2*padding[2]-kernel_size[2])/stride[2]+1) \end{aligned} $$ + +**Example:** + +``` +>>> # pool of square window of size=3, stride=2 +>>> m = nn.AvgPool3d(3, stride=2) +>>> # pool of non-square window +>>> m = nn.AvgPool3d((3, 2, 2), stride=(2, 1, 2)) +>>> input = autograd.Variable(torch.randn(20, 16, 50,44, 31)) +>>> output = m(input) +``` + +### class torch.nn.FractionalMaxPool2d(kernel_size, output_size=None, output_ratio=None, return_indices=False, _random_samples=None) + +对输入的信号,提供 2 维的分数最大化池化操作 分数最大化池化的细节请阅读[论文](https://arxiv.org/abs/1412.6071) 由目标输出大小确定的随机步长,在$kH*kW$区域进行最大池化操作。输出特征和输入特征的数量相同。 + +**参数:** + +* kernel_size(`int` or `tuple`) - 最大池化操作时的窗口大小。可以是一个数字(表示`K*K`的窗口),也可以是一个元组(`kh*kw`) +* output_size - 输出图像的尺寸。可以使用一个`tuple`指定(oH,oW),也可以使用一个数字 oH 指定一个 oH*oH 的输出。 +* output_ratio – 将输入图像的大小的百分比指定为输出图片的大小,使用一个范围在(0,1)之间的数字指定 +* return_indices - 默认值`False`,如果设置为`True`,会返回输出的索引,索引对 `nn.MaxUnpool2d`有用。 + +**Example:** + +``` +>>> # pool of square window of size=3, and target output size 13x12 +>>> m = nn.FractionalMaxPool2d(3, output_size=(13, 12)) +>>> # pool of square window and target output size being half of input image size +>>> m = nn.FractionalMaxPool2d(3, output_ratio=(0.5, 0.5)) +>>> input = autograd.Variable(torch.randn(20, 16, 50, 32)) +>>> output = m(input) +``` + +### class torch.nn.LPPool2d(norm_type, kernel_size, stride=None, ceil_mode=False) + +对输入信号提供 2 维的幂平均池化操作。 输出的计算方式: + +$$f(x)=pow(sum(X,p),1/p)$$ + +* 当 p 为无穷大的时候时,等价于最大池化操作 +* 当`p=1`时,等价于平均池化操作 + +参数`kernel_size`, `stride`的数据类型: + +* `int`,池化窗口的宽和高相等 +* `tuple`数组(两个数字的),一个元素是池化窗口的高,另一个是宽 + +**参数** + +* kernel_size: 池化窗口的大小 +* stride:池化窗口移动的步长。`kernel_size`是默认值 +* ceil_mode: `ceil_mode=True`时,将使用向下取整代替向上取整 + +**shape** + +* 输入:(N,C,H_in,W_in) +* 输出:(N,C,H*out,W_out) $$\begin{aligned} H*{out} = floor((H*{in}+2_padding[0]-dilation[0]*(kernel*size[0]-1)-1)/stride[0]+1)\ W*{out} = floor((W*{in}+2_padding[1]-dilation[1]*(kernel_size[1]-1)-1)/stride[1]+1) \end{aligned} $$ + +**Example:** + +``` +>>> # power-2 pool of square window of size=3, stride=2 +>>> m = nn.LPPool2d(2, 3, stride=2) +>>> # pool of non-square window of power 1.2 +>>> m = nn.LPPool2d(1.2, (3, 2), stride=(2, 1)) +>>> input = autograd.Variable(torch.randn(20, 16, 50, 32)) +>>> output = m(input) +``` + +### class torch.nn.AdaptiveMaxPool1d(output_size, return_indices=False) + +对输入信号,提供 1 维的自适应最大池化操作 对于任何输入大小的输入,可以将输出尺寸指定为 H,但是输入和输出特征的数目不会变化。 + +**参数:** + +* output_size: 输出信号的尺寸 +* return_indices: 如果设置为`True`,会返回输出的索引。对 `nn.MaxUnpool1d`有用,默认值是`False` + +**Example:** + +``` +>>> # target output size of 5 +>>> m = nn.AdaptiveMaxPool1d(5) +>>> input = autograd.Variable(torch.randn(1, 64, 8)) +>>> output = m(input) +``` + +### class torch.nn.AdaptiveMaxPool2d(output_size, return_indices=False) + +对输入信号,提供 2 维的自适应最大池化操作 对于任何输入大小的输入,可以将输出尺寸指定为 H*W,但是输入和输出特征的数目不会变化。 + +**参数:** + +* output_size: 输出信号的尺寸,可以用(H,W)表示`H*W`的输出,也可以使用数字`H`表示`H*H`大小的输出 +* return_indices: 如果设置为`True`,会返回输出的索引。对 `nn.MaxUnpool2d`有用,默认值是`False` + +**Example:** + +``` +>>> # target output size of 5x7 +>>> m = nn.AdaptiveMaxPool2d((5,7)) +>>> input = autograd.Variable(torch.randn(1, 64, 8, 9)) +>>> # target output size of 7x7 (square) +>>> m = nn.AdaptiveMaxPool2d(7) +>>> input = autograd.Variable(torch.randn(1, 64, 10, 9)) +>>> output = m(input) +``` + +### class torch.nn.AdaptiveAvgPool1d(output_size) + +对输入信号,提供 1 维的自适应平均池化操作 对于任何输入大小的输入,可以将输出尺寸指定为 H*W,但是输入和输出特征的数目不会变化。 + +**参数:** + +* output_size: 输出信号的尺寸 + +**Example:** + +``` +>>> # target output size of 5 +>>> m = nn.AdaptiveAvgPool1d(5) +>>> input = autograd.Variable(torch.randn(1, 64, 8)) +>>> output = m(input) +``` + +### class torch.nn.AdaptiveAvgPool2d(output_size) + +对输入信号,提供 2 维的自适应平均池化操作 对于任何输入大小的输入,可以将输出尺寸指定为`H*W`,但是输入和输出特征的数目不会变化。 + +**参数:** + +* output_size: 输出信号的尺寸,可以用(H,W)表示`H*W`的输出,也可以使用耽搁数字 H 表示 H*H 大小的输出 + +**Example:** + +``` +>>> # target output size of 5x7 +>>> m = nn.AdaptiveAvgPool2d((5,7)) +>>> input = autograd.Variable(torch.randn(1, 64, 8, 9)) +>>> # target output size of 7x7 (square) +>>> m = nn.AdaptiveAvgPool2d(7) +>>> input = autograd.Variable(torch.randn(1, 64, 10, 9)) +>>> output = m(input) +``` + +## Non-Linear Activations + +> class torch.nn.ReLU(inplace=False) + +对输入运用修正线性单元函数${ReLU}(x)= max(0, x)$, + +参数: inplace-选择是否进行覆盖运算 + +shape: + +* 输入:$(N, *)$,*代表任意数目附加维度 +* 输出:$(N, *)$,与输入拥有同样的 shape 属性 + +例子: + +``` +>>> m = nn.ReLU() +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.ReLU6(inplace=False) + +对输入的每一个元素运用函数${ReLU6}(x) = min(max(0,x), 6)$, + +参数: inplace-选择是否进行覆盖运算 + +shape: + +* 输入:$(N, *)$,*代表任意数目附加维度 +* 输出:$(N, *)$,与输入拥有同样的 shape 属性 + +例子: + +``` +>>> m = nn.ReLU6() +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.ELU(alpha=1.0, inplace=False) + +对输入的每一个元素运用函数$f(x) = max(0,x) + min(0, alpha * (e^x - 1))$, + +shape: + +* 输入:$(N, *)$,星号代表任意数目附加维度 +* 输出:$(N, *)$与输入拥有同样的 shape 属性 + +例子: + +``` +>>> m = nn.ELU() +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.PReLU(num_parameters=1, init=0.25) + +对输入的每一个元素运用函数$PReLU(x) = max(0,x) + a * min(0,x)$,`a`是一个可学习参数。当没有声明时,`nn.PReLU()`在所有的输入中只有一个参数`a`;如果是`nn.PReLU(nChannels)`,`a`将应用到每个输入。 + +注意:当为了表现更佳的模型而学习参数`a`时不要使用权重衰减(weight decay) + +参数: + +* num_parameters:需要学习的`a`的个数,默认等于 1 +* init:`a`的初始值,默认等于 0.25 + +shape: + +* 输入:$(N, *)$,*代表任意数目附加维度 +* 输出:$(N, *)$,与输入拥有同样的 shape 属性 + +例子: + +``` +>>> m = nn.PReLU() +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.LeakyReLU(negative_slope=0.01, inplace=False) + +对输入的每一个元素运用$f(x) = max(0, x) + {negative_slope} * min(0, x)$ + +参数: + +* negative_slope:控制负斜率的角度,默认等于 0.01 +* inplace-选择是否进行覆盖运算 + +shape: + +* 输入:$(N, *)$,*代表任意数目附加维度 +* 输出:$(N, *)$,与输入拥有同样的 shape 属性 + +例子: + +``` +>>> m = nn.LeakyReLU(0.1) +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.Threshold(threshold, value, inplace=False) + +Threshold 定义: + +$$ y = x ,if\ x >= threshold\ y = value,if\ x < threshold $$ + +参数: + +* threshold:阈值 +* value:输入值小于阈值则会被 value 代替 +* inplace:选择是否进行覆盖运算 + +shape: + +* 输入:$(N, *)$,*代表任意数目附加维度 +* 输出:$(N, *)$,与输入拥有同样的 shape 属性 + +例子: + +``` +>>> m = nn.Threshold(0.1, 20) +>>> input = Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.Hardtanh(min_value=-1, max_value=1, inplace=False) + +对每个元素, + +$$ f(x) = +1, if\ x > 1;\ f(x) = -1, if\ x < -1;\ f(x) = x, otherwise $$ + +线性区域的范围[-1,1]可以被调整 + +参数: + +* min_value:线性区域范围最小值 +* max_value:线性区域范围最大值 +* inplace:选择是否进行覆盖运算 + +shape: + +* 输入:(N, *),*表示任意维度组合 +* 输出:(N, *),与输入有相同的 shape 属性 + +例子: + +``` +>>> m = nn.Hardtanh() +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.Sigmoid + +对每个元素运用 Sigmoid 函数,Sigmoid 定义如下: + +$$f(x) = 1 / ( 1 + e^{-x})$$ + +shape: + +* 输入:(N, *),*表示任意维度组合 +* 输出:(N, *),与输入有相同的 shape 属性 + +例子: + +``` +>>> m = nn.Sigmoid() +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.Tanh + +对输入的每个元素, + +$$f(x) = \frac{e^{x} - e^{-x}} {e^{x} + e^{x}}$$ + +shape: + +* 输入:(N, *),*表示任意维度组合 +* 输出:(N, *),与输入有相同的 shape 属性 + +例子: + +``` +>>> m = nn.Tanh() +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.LogSigmoid + +对输入的每个元素,$LogSigmoid(x) = log( 1 / ( 1 + e^{-x}))$ + +shape: + +* 输入:(N, *),*表示任意维度组合 +* 输出:(N, *),与输入有相同的 shape 属性 + +例子: + +``` +>>> m = nn.LogSigmoid() +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.Softplus(beta=1, threshold=20) + +对每个元素运用 Softplus 函数,Softplus 定义如下: + +$$f(x) = \frac{1}{beta} *log(1 + e^{(beta* x_i)})$$ + +Softplus 函数是 ReLU 函数的平滑逼近,Softplus 函数可以使得输出值限定为正数。 + +为了保证数值稳定性,线性函数的转换可以使输出大于某个值。 + +参数: + +* beta:Softplus 函数的 beta 值 +* threshold:阈值 + +shape: + +* 输入:(N, *),*表示任意维度组合 +* 输出:(N, *),与输入有相同的 shape 属性 + +例子: + +``` +>>> m = nn.Softplus() +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.Softshrink(lambd=0.5) + +对每个元素运用 Softshrink 函数,Softshrink 函数定义如下: + +$$ f(x) = x-lambda, if\ x > lambda\ f(x) = x+lambda, if\ x < -lambda\ f(x) = 0, otherwise $$ + +参数: + +lambd:Softshrink 函数的 lambda 值,默认为 0.5 + +shape: + +* 输入:(N, *),*表示任意维度组合 +* 输出:(N, *),与输入有相同的 shape 属性 + +例子: + +``` +>>> m = nn.Softshrink() +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.Softsign + +$f(x) = x / (1 + |x|)$ + +shape: + +* 输入:(N, *),*表示任意维度组合 +* 输出:(N, *),与输入有相同的 shape 属性 + +例子: + +``` +>>> m = nn.Softsign() +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.Softshrink(lambd=0.5) + +对每个元素运用 Tanhshrink 函数,Tanhshrink 函数定义如下: + +$$ Tanhshrink(x) = x - Tanh(x) $$ + +shape: + +* 输入:(N, *),*表示任意维度组合 +* 输出:(N, *),与输入有相同的 shape 属性 + +例子: + +``` +>>> m = nn.Tanhshrink() +>>> input = autograd.Variable(torch.randn(2)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.Softmin + +对 n 维输入张量运用 Softmin 函数,将张量的每个元素缩放到(0,1)区间且和为 1。Softmin 函数定义如下: + +$$f_i(x) = \frac{e^{(-x_i - shift)}} { \sum^j e^{(-x_j - shift)}},shift = max (x_i)$$ + +shape: + +* 输入:(N, L) +* 输出:(N, L) + +例子: + +``` +>>> m = nn.Softmin() +>>> input = autograd.Variable(torch.randn(2, 3)) +>>> print(input) +>>> print(m(input)) +``` + +* * * + +> class torch.nn.Softmax + +对 n 维输入张量运用 Softmax 函数,将张量的每个元素缩放到(0,1)区间且和为 1。Softmax 函数定义如下: + +$$f_i(x) = \frac{e^{(x_i - shift)}} { \sum^j e^{(x_j - shift)}},shift = max (x_i)$$ + +shape: + +* 输入:(N, L) +* 输出:(N, L) + +返回结果是一个与输入维度相同的张量,每个元素的取值范围在(0,1)区间。 + +例子: + +``` +>>> m = nn.Softmax() +>>> input = autograd.Variable(torch.randn(2, 3)) +>>> print(input) +>>> print(m(input)) +``` + +> class torch.nn.LogSoftmax + +对 n 维输入张量运用 LogSoftmax 函数,LogSoftmax 函数定义如下: + +$$f_i(x) = log \frac{e^{(x_i)}} {a}, a = \sum^j e^{(x_j)}$$ + +shape: + +* 输入:(N, L) +* 输出:(N, L) + +例子: + +``` +>>> m = nn.LogSoftmax() +>>> input = autograd.Variable(torch.randn(2, 3)) +>>> print(input) +>>> print(m(input)) +``` + +### class torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True) + +对小批量(mini-batch)的 2d 或 3d 输入进行批标准化(Batch Normalization)操作 + +$$ y = \frac{x - mean[x]}{ \sqrt{Var[x]} + \epsilon} * gamma + beta $$ + +在每一个小批量(mini-batch)数据中,计算输入各个维度的均值和标准差。gamma 与 beta 是可学习的大小为 C 的参数向量(C 为输入大小) + +在训练时,该层计算每次输入的均值与方差,并进行移动平均。移动平均默认的动量值为 0.1。 + +在验证时,训练求得的均值/方差将用于标准化验证数据。 + +**参数:** + +* **num_features:** 来自期望输入的特征数,该期望输入的大小为'batch_size x num_features [x width]' +* **eps:** 为保证数值稳定性(分母不能趋近或取 0),给分母加上的值。默认为 1e-5。 +* **momentum:** 动态均值和动态方差所使用的动量。默认为 0.1。 +* **affine:** 一个布尔值,当设为 true,给该层添加可学习的仿射变换参数。 + +**Shape:** + +* 输入:(N, C)或者(N, C, L) +* 输出:(N, C)或者(N,C,L)(输入输出相同) + +**例子** + +``` +>>> # With Learnable Parameters +>>> m = nn.BatchNorm1d(100) +>>> # Without Learnable Parameters +>>> m = nn.BatchNorm1d(100, affine=False) +>>> input = autograd.Variable(torch.randn(20, 100)) +>>> output = m(input) +``` + +* * * + +### class torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True) + +对小批量(mini-batch)3d 数据组成的 4d 输入进行批标准化(Batch Normalization)操作 + +$$ y = \frac{x - mean[x]}{ \sqrt{Var[x]} + \epsilon} * gamma + beta $$ + +在每一个小批量(mini-batch)数据中,计算输入各个维度的均值和标准差。gamma 与 beta 是可学习的大小为 C 的参数向量(C 为输入大小) + +在训练时,该层计算每次输入的均值与方差,并进行移动平均。移动平均默认的动量值为 0.1。 + +在验证时,训练求得的均值/方差将用于标准化验证数据。 + +**参数:** + +* **num_features:** 来自期望输入的特征数,该期望输入的大小为'batch_size x num_features x height x width' +* **eps:** 为保证数值稳定性(分母不能趋近或取 0),给分母加上的值。默认为 1e-5。 +* **momentum:** 动态均值和动态方差所使用的动量。默认为 0.1。 +* **affine:** 一个布尔值,当设为 true,给该层添加可学习的仿射变换参数。 + +**Shape:** + +* 输入:(N, C,H, W) +* 输出:(N, C, H, W)(输入输出相同) + +**例子** + +``` +>>> # With Learnable Parameters +>>> m = nn.BatchNorm2d(100) +>>> # Without Learnable Parameters +>>> m = nn.BatchNorm2d(100, affine=False) +>>> input = autograd.Variable(torch.randn(20, 100, 35, 45)) +>>> output = m(input) +``` + +* * * + +### class torch.nn.BatchNorm3d(num_features, eps=1e-05, momentum=0.1, affine=True) + +对小批量(mini-batch)4d 数据组成的 5d 输入进行批标准化(Batch Normalization)操作 + +$$ y = \frac{x - mean[x]}{ \sqrt{Var[x]} + \epsilon} * gamma + beta $$ + +在每一个小批量(mini-batch)数据中,计算输入各个维度的均值和标准差。gamma 与 beta 是可学习的大小为 C 的参数向量(C 为输入大小) + +在训练时,该层计算每次输入的均值与方差,并进行移动平均。移动平均默认的动量值为 0.1。 + +在验证时,训练求得的均值/方差将用于标准化验证数据。 + +**参数:** + +* **num_features:** 来自期望输入的特征数,该期望输入的大小为'batch_size x num_features depth x height x width' +* **eps:** 为保证数值稳定性(分母不能趋近或取 0),给分母加上的值。默认为 1e-5。 +* **momentum:** 动态均值和动态方差所使用的动量。默认为 0.1。 +* **affine:** 一个布尔值,当设为 true,给该层添加可学习的仿射变换参数。 + +**Shape:** + +* 输入:(N, C,D, H, W) +* 输出:(N, C, D, H, W)(输入输出相同) + +**例子** + +``` +>>> # With Learnable Parameters +>>> m = nn.BatchNorm3d(100) +>>> # Without Learnable Parameters +>>> m = nn.BatchNorm3d(100, affine=False) +>>> input = autograd.Variable(torch.randn(20, 100, 35, 45, 10)) +>>> output = m(input) +``` + +* * * + +### class torch.nn.RNN( *args, ** kwargs) + +将一个多层的 `Elman RNN`,激活函数为`tanh`或者`ReLU`,用于输入序列。 + +对输入序列中每个元素,`RNN`每层的计算公式为 $$ h*t=tanh(w*{ih} *x_t+b*{ih}+w*{hh}* h*{t-1}+b*{hh}) $$ $h_t$是时刻$t$的隐状态。 $x_t$是上一层时刻$t$的隐状态,或者是第一层在时刻$t$的输入。如果`nonlinearity='relu'`,那么将使用`relu`代替`tanh`作为激活函数。 + +参数说明: + +* input_size – 输入`x`的特征数量。 + +* hidden_size – 隐层的特征数量。 + +* num_layers – RNN 的层数。 + +* nonlinearity – 指定非线性函数使用`tanh`还是`relu`。默认是`tanh`。 + +* bias – 如果是`False`,那么 RNN 层就不会使用偏置权重 $b_ih$和$b_hh$,默认是`True` + +* batch_first – 如果`True`的话,那么输入`Tensor`的 shape 应该是[batch_size, time_step, feature],输出也是这样。 + +* dropout – 如果值非零,那么除了最后一层外,其它层的输出都会套上一个`dropout`层。 + +* bidirectional – 如果`True`,将会变成一个双向`RNN`,默认为`False`。 + +`RNN`的输入: **(input, h_0)** + +* input (seq_len, batch, input_size): 保存输入序列特征的`tensor`。`input`可以是被填充的变长的序列。细节请看`torch.nn.utils.rnn.pack_padded_sequence()` + +* h_0 (num_layers * num_directions, batch, hidden_size): 保存着初始隐状态的`tensor` + +`RNN`的输出: **(output, h_n)** + +* output (seq_len, batch, hidden_size * num_directions): 保存着`RNN`最后一层的输出特征。如果输入是被填充过的序列,那么输出也是被填充的序列。 +* h_n (num_layers * num_directions, batch, hidden_size): 保存着最后一个时刻隐状态。 + +`RNN`模型参数: + +* weight_ih_l[k] – 第`k`层的 `input-hidden` 权重, 可学习,形状是`(input_size x hidden_size)`。 + +* weight_hh_l[k] – 第`k`层的 `hidden-hidden` 权重, 可学习,形状是`(hidden_size x hidden_size)` + +* bias_ih_l[k] – 第`k`层的 `input-hidden` 偏置, 可学习,形状是`(hidden_size)` + +* bias_hh_l[k] – 第`k`层的 `hidden-hidden` 偏置, 可学习,形状是`(hidden_size)` + +示例: + +``` +rnn = nn.RNN(10, 20, 2) +input = Variable(torch.randn(5, 3, 10)) +h0 = Variable(torch.randn(2, 3, 20)) +output, hn = rnn(input, h0) +``` + +### class torch.nn.LSTM( *args, ** kwargs) + +将一个多层的 `(LSTM)` 应用到输入序列。 + +对输入序列的每个元素,`LSTM`的每层都会执行以下计算: [\begin{split}\begin{array}{ll} i*t = \mathrm{sigmoid}(W*{ii} x*t + b*{ii} + W*{hi} h*{(t-1)} + b*{hi}) \ f_t = \mathrm{sigmoid}(W*{if} x*t + b*{if} + W*{hf} h*{(t-1)} + b*{hf}) \ g_t = \tanh(W*{ig} x*t + b*{ig} + W*{hc} h*{(t-1)} + b*{hg}) \ o_t = \mathrm{sigmoid}(W*{io} x*t + b*{io} + W*{ho} h*{(t-1)} + b*{ho}) \ c_t = f_t * c*{(t-1)} + i*t _g_t \ h_t = o_t* \tanh(c_t) \end{array}\end{split}]是时刻$t$的隐状态,$c_t$是时刻$t$的细胞状态,$x_t$是上一层的在时刻$t$的隐状态或者是第一层在时刻$t$的输入。$i_t, f_t, g_t, o_t$ 分别代表 输入门,遗忘门,细胞 + +*class*`torch.nn.``GRU`(**args*, ***kwargs*)[[source]](http://pytorch.org/docs/master/_modules/torch/nn/modules/rnn.html#GRU) + +Applies a multi-layer gated recurrent unit (GRU) RNN to an input sequence. For each element in the input sequence, each layer computes the following function:[\begin{split}\begin{array}{ll} r*t = \mathrm{sigmoid}(W*{ir} x*t + b*{ir} + W*{hr} h*{(t-1)} + b*{hr}) \ z_t = \mathrm{sigmoid}(W*{iz} x*t + b*{iz} + W*{hz} h*{(t-1)} + b*{hz}) \ n_t = \tanh(W*{in} x*t + b*{in} + r*t * (W*{hn} h*{(t-1)}+ b*{hn})) \ h*t = (1 - z_t) *n_t + z_t* h*{(t-1)} \ \end{array}\end{split}]where (h_t) is the hidden state at time t, (x_t) is the hidden state of the previous layer at time t or (input_t) for the first layer, and (r_t), (z_t), (n_t) are the reset, input, and new gates, respectively. | Parameters: | ***input_size** – The number of expected features in the input x* **hidden_size** – The number of features in the hidden state h ***num_layers** – Number of recurrent layers.* **bias** – If False, then the layer does not use bias weights b_ih and b_hh. Default: True ***batch_first** – If True, then the input and output tensors are provided as (batch, seq, feature)* **dropout** – If non-zero, introduc \ No newline at end of file diff --git a/docs/0.4/17.md b/docs/0.4/17.md new file mode 100644 index 00000000..e428e2b3 --- /dev/null +++ b/docs/0.4/17.md @@ -0,0 +1,1299 @@ +# torch.nn.functional + +## 卷积函数 + +``` +torch.nn.functional.conv1d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/modules/conv.html#Conv1d) + +对由几个平面组成的输入进行卷积操作 对于细节和输出形状,详细可见[Conv1d](http://pytorch.org/docs/master/nn.html#torch.nn.Conv1d) + +### 参数: + +``` +input:输入的张量形状(minibatch x in_channels x iW) +weight – 过滤器的形状 (out_channels, in_channels, kW) +bias – 可选偏置的形状(out_channels)默认值:None +stride – 卷积内核的步长,默认为 1 +padding – 输入上的隐含零填充。可以是单个数字或元组。默认值:0 +dilation – 内核元素之间的间距。默认值:1 +groups – 将输入分成组,in_channels 应该被组数整除。默认值:1 +``` + +举例: + +``` +>>> filters = autograd.Variable(torch.randn(33, 16, 3) +>>> inputs = autograd.Variable(torch.randn(20, 16, 50)) +>>> F.conv1d(inputs, filters) +``` + +* * * + +``` +torch.nn.functional.conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/modules/conv.html#Conv2d) + +在由几个输入平面组成的输入图像上应用 2D 卷积。 对于细节和输出形状详细可见[Conv2d](http://pytorch.org/docs/master/nn.html#torch.nn.Conv2d) + +### 参数: + +``` +input – 输入的张量 (minibatch x in_channels x iH x iW) +weight – 过滤器 (out_channels, in_channels/groups, kH, kW) +bias – 可选偏置张量(out_channels)。默认值:None +stride – 卷积核的步长,可以是单个数字或元组(sh x sw)。默认值:1 +padding – 输入中默认 0 填充。可以是单个数字或元组。默认值:0 +dilation – 核元素之间的间距。默认值:1 +groups – 将输入分成组,in_channels 应该被组数整除。默认值:1 +``` + +举例: + +``` +>>> # With square kernels and equal stride +>>> filters = torch.randn(8,4,3,3) +>>> inputs = torch.randn(1,4,5,5) +>>> F.conv2d(inputs, filters, padding=1) +``` + +* * * + +``` +torch.nn.functional.conv3d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/modules/conv.html#Conv3d) + +在由几个输入平面组成的输入图像上应用 3D 卷积。 对于细节和输出形状,查看[Conv3d](http://pytorch.org/docs/master/nn.html#torch.nn.Conv3d) + +### 参数: + +``` +input – 输入张量的形状 (minibatch x in_channels x iT x iH x iW) +weight – 过滤器的形状 (out_channels x in_channels/groups x kT x kH x kW) +bias – 可选的偏差项的大小(out_channels).默认值是:None +stride – 卷积核的步长,可以是单个数字或元组(st x sh x sw).默认值:1 +padding – 在输入中默认的 0 填充。可以是单个数字或元组(padT, padH, padW).默认值:0 +dilation – 核元素之间的间距(dT, dH, dW)。默认值:1 +groups – 将输入分成组,in_channels 应该被组数整除。默认值:1 +``` + +### 举例: + +``` +>>> filters = torch.randn(33, 16, 3, 3, 3) +>>> inputs = torch.randn(20, 16, 50, 10, 20) +>>> F.conv3d(inputs, filters) +``` + +* * * + +``` +torch.nn.functional.conv_transpose1d(input, weight, bias=None, stride=1, padding=0, output_padding=0, groups=1, dilation=1) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/modules/conv.html#ConvTranspose1d) + +在由几个输入平面组成的输入图像上应用 1D 转置卷积,有时也被称为去卷积。 有关详细信息和输出形状,参考[ConvTranspose1d](http://pytorch.org/docs/master/nn.html#torch.nn.ConvTranspose1d)。 + +### 参数: + +``` +input:输入的张量形状(minibatch x in_channels x iW) +weight – 过滤器的形状 (in_channels x out_channels/groups x kW) +bias – 可选偏置的形状(out_channels)默认值:None +stride – 卷积内核的步长,也可以是一个数字或者元组(sW),默认为 1 +padding – 输入上的隐含零填充(0≤padding>> inputs = torch.randn(20, 16, 50) +>>> weights = torch.randn(16, 33, 5) +>>> F.conv_transpose1d(inputs, weights) +> +``` + +* * * + +``` +torch.nn.functional.conv_transpose2d(input, weight, bias=None, stride=1, padding=0, output_padding=0, groups=1, dilation=1) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/modules/conv.html#ConvTranspose2d) + +在由几个输入平面组成的输入图像上应用 2D 转置卷积,有时也被称为去卷积。 有关详细信息和输出形状,参考[ConvTranspose2d](http://pytorch.org/docs/master/nn.html#torch.nn.ConvTranspose2d)。 + +### 参数: + +``` +input:输入的张量形状(minibatch x in_channels x iH x iW) +weight – 过滤器的形状 (in_channels x out_channels/groups x kH x kW) +bias – 可选偏置的形状(out_channels)默认值:None +stride – 卷积内核的步长,也可以是一个数字或者元组(sH,sW),默认为 1 +padding – 输入上的隐含零填充(0≤padding>> # With square kernels and equal stride +>>> inputs = torch.randn(1, 4, 5, 5) +>>> weights = torch.randn(4, 8, 3, 3) +>>> F.conv_transpose2d(inputs, weights, padding=1) +``` + +* * * + +``` +torch.nn.functional.conv_transpose3d(input, weight, bias=None, stride=1, padding=0, output_padding=0, groups=1, dilation=1) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/modules/conv.html#ConvTranspose3d) 在由几个输入平面组成的输入图像上应用 3D 转置卷积,有时也被称为去卷积。 有关详细信息和输出形状,参考[ConvTranspose3d](http://pytorch.org/docs/master/nn.html#torch.nn.ConvTranspose3d)。 + +### 参数: + +``` +input:输入的张量形状(minibatch x in_channels x iT x iH x iW) +weight – 过滤器的形状 (in_channels x out_channels/groups x kT x kH x kW) +bias – 可选偏置的形状(out_channels)默认值:None +stride – 卷积内核的步长,也可以是一个数字或者元组(sT,sH,sW),默认为 1 +padding – 在输入的两端上的隐含零填充。可以是单个数字或者元组(padT,padH,padW)。默认值:0 +output_padding-在两端的进行 0 填充(0≤padding>> inputs = torch.randn(20, 16, 50, 10, 20) +>>> weights = torch.randn(16, 33, 3, 3, 3) +>>> F.conv_transpose3d(inputs, weights) +``` + +## 池化函数 + +``` +torch.nn.functional.avg_pool1d(input, kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#avg_pool1d) + +对由几个输入平面组成的输入进行 1D 平均池化。 有关详细信息和输出形状,参考[AvgPool1d](http://pytorch.org/docs/master/nn.html#torch.nn.AvgPool1d) + +### 参数: + +``` +input – 输入的张量 (minibatch x in_channels x iW) +kernel_size – 池化区域的大小,可以是单个数字或者元组 (kw) +stride – 池化操作的步长,可以是单个数字或者元组 (ssw)。默认值等于内核大小 +padding – 在输入上隐式的零填充,可以是单个数字或者一个元组 ( padw),默认: 0 +ceil_mode – 当为 True 时,公式中将使用 ceil 而不是 floor 来计算输出形状。默认值:False +count_include_pad – 当为 True 时,将包括平均计算中的零填充。默认值:True +``` + +### 举例: + +``` +>>> # pool of square window of size=3, stride=2 +>>> input = torch.tensor([[[1,2,3,4,5,6,7]]]) +>>> F.avg_pool1d(input, kernel_size=3, stride=2) +tensor([[[ 2., 4., 6.]]]) +``` + +* * * + +``` +torch.nn.functional.avg_pool2d(input, kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True) → Tensor +``` + +[source](http://pytorch.org/docs/master/nn.html#torch.nn.functional.avg_pool2d) + +通过步长 dh x dw 步骤在 kh x kw 区域中应用二维平均池操作。输出特征的数量等于输入平面的数量。 + +有关详细信息和输出形状,参考[AvgPool2d](http://pytorch.org/docs/master/nn.html#torch.nn.AvgPool2d) + +### 参数: + +``` +input – 输入的张量 (minibatch x in_channels x iH x iW) +kernel_size – 池化区域的大小,可以是单个数字或者元组 (kh x kw) +stride – 池化操作的步长,可以是单个数字或者元组 (sh x sw)。默认值等于内核大小 +padding – 在输入上隐式的零填充,可以是单个数字或者一个元组 (padh x padw),默认: 0 +ceil_mode – 当为 True 时,公式中将使用 ceil 而不是 floor 来计算输出形状。默认值:False +count_include_pad – 当为 True 时,将包括平均计算中的零填充。默认值:True +``` + +* * * + +``` +torch.nn.functional.avg_pool3d(input, kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True) → Tensor +``` + +[source](http://pytorch.org/docs/master/nn.html#torch.nn.functional.avg_pool3d) + +通过步长 dt x dh x dw 步骤在 kt x kh x kw 区域中应用 3D 平均池操作。输出功能的数量等于输入平面数/ dt。 有关详细信息和输出形状,参考[AvgPool3d](http://pytorch.org/docs/master/nn.html#torch.nn.AvgPool3d) + +### 参数: + +``` +input – 输入的张量 (minibatch x in_channels x iT x iH x iW) +kernel_size – 池化区域的大小,可以是单个数字或者元组 (kT x kh x kw) +stride – 池化操作的步长,可以是单个数字或者元组 (sT x sh x sw)。默认值等于内核大小 +padding – 在输入上隐式的零填充,可以是单个数字或者一个元组 (padT x padh x padw),默认: 0 +ceil_mode – 当为 True 时,公式中将使用 ceil 而不是 floor 来计算输出形状。默认值:False +count_include_pad – 当为 True 时,将包括平均计算中的零填充。默认值:True +``` + +* * * + +``` +torch.nn.functional.max_pool1d(input, kernel_size, stride=None, padding=0, dilation=1, ceil_mode=False, return_indices=False) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#max_pool1d) + +对由几个输入平面组成的输入进行 1D 最大池化。 有关详细信息和输出形状,参考[MaxPool1d](http://pytorch.org/docs/master/nn.html#torch.nn.MaxPool1d) + +* * * + +``` +torch.nn.functional.max_pool2d(input, kernel_size, stride=None, padding=0, dilation=1, ceil_mode=False, return_indices=False) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#max_pool2d) + +对由几个输入平面组成的输入进行 2D 最大池化。 有关详细信息和输出形状,参考[MaxPool2d](http://pytorch.org/docs/master/nn.html#torch.nn.MaxPool2d) + +* * * + +``` +torch.nn.functional.max_pool3d(input, kernel_size, stride=None, padding=0, dilation=1, ceil_mode=False, return_indices=False) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#max_pool3d) + +对由几个输入平面组成的输入进行 3D 最大池化。 有关详细信息和输出形状,参考[MaxPool3d](http://pytorch.org/docs/master/nn.html#torch.nn.MaxPool3d) + +* * * + +``` +torch.nn.functional.max_unpool1d(input, indices, kernel_size, stride=None, padding=0, output_size=None) +``` + +计算 MaxPool1d 的部分逆。 + +有关详细信息和输出形状,参考[MaxUnPool1d](http://pytorch.org/docs/master/nn.html#torch.nn.MaxUnpool1d) + +* * * + +``` +torch.nn.functional.max_unpool2d(input, indices, kernel_size, stride=None, padding=0, output_size=None) +``` + +计算 MaxPool2d 的部分逆。 + +有关详细信息和输出形状,参考[MaxUnPool2d](http://pytorch.org/docs/master/nn.html#torch.nn.MaxUnpool2d) + +* * * + +``` +torch.nn.functional.max_unpool3d(input, indices, kernel_size, stride=None, padding=0, output_size=None) +``` + +计算 MaxPool3d 的部分逆。 + +有关详细信息和输出形状,参考[MaxUnPool3d](http://pytorch.org/docs/master/nn.html#torch.nn.MaxUnpool3d) + +* * * + +``` +torch.nn.functional.lp_pool1d(input, norm_type, kernel_size, stride=None, ceil_mode=False) +``` + +适用在几个输入平面组成的输入信号的 1D power-平均池。 + +有关详细信息[LPPool1d](http://pytorch.org/docs/master/nn.html#torch.nn.LPPool1d) + +* * * + +``` +torch.nn.functional.lp_pool2d(input, norm_type, kernel_size, stride=None, ceil_mode=False) +``` + +适用在几个输入平面组成的输入信号的 2D power-平均池。 + +有关详细信息[LPPool2d](http://pytorch.org/docs/master/nn.html#torch.nn.LPPool2d) + +* * * + +``` +torch.nn.functional.adaptive_max_pool1d(input, output_size, return_indices=False) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#adaptive_max_pool1d) + +对由多个输入平面组成的输入进行 1D 自适应最大池化。 + +有关详细信息可见[AdaptiveMaxPool1d](http://pytorch.org/docs/master/nn.html#torch.nn.AdaptiveMaxPool1d) + +* * * + +``` +torch.nn.functional.adaptive_max_pool2d(input, output_size, return_indices=False) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#adaptive_max_pool2d) + +对由多个输入平面组成的输入进行 2D 自适应最大池化。 + +有关详细信息可见[AdaptiveMaxPool2d](http://pytorch.org/docs/master/nn.html#torch.nn.AdaptiveMaxPool2d) + +* * * + +``` +torch.nn.functional.adaptive_max_pool3d(input, output_size, return_indices=False) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#adaptive_max_pool3d) + +对由多个输入平面组成的输入进行 3D 自适应最大池化。 + +有关详细信息可见[AdaptiveMaxPool3d](http://pytorch.org/docs/master/nn.html#torch.nn.AdaptiveMaxPool3d) + +* * * + +``` +torch.nn.functional.adaptive_avg_pool1d(input, output_size) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#adaptive_avg_pool1d) + +对由多个输入平面组成的输入进行 1D 自适应平均池化。 + +有关详细信息可见[AdaptiveAvgPool1d](http://pytorch.org/docs/master/nn.html#torch.nn.AdaptiveAvgPool1d) + +* * * + +``` +torch.nn.functional.adaptive_avg_pool2d(input, output_size) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#adaptive_avg_pool2d) + +对由多个输入平面组成的输入进行 2D 自适应平均池化。 + +有关详细信息可见[AdaptiveAvgPool2d](http://pytorch.org/docs/master/nn.html#torch.nn.AdaptiveAvgPool2d) + +* * * + +``` +torch.nn.functional.adaptive_avg_pool3d(input, output_size) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#adaptive_avg_pool3d) + +对由多个输入平面组成的输入进行 3D 自适应平均池化。 + +有关详细信息可见[AdaptiveAvgPool3d](http://pytorch.org/docs/master/nn.html#torch.nn.AdaptiveAvgPool3d) + +## 非线性激活函数 + +``` +torch.nn.functional.threshold(input, threshold, value, inplace=False) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#threshold) + +对于输入的张量进行筛选 + +详细请看[Threshold](http://pytorch.org/docs/master/nn.html#torch.nn.Threshold) + +* * * + +``` +torch.nn.functional.threshold_(input, threshold, value) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#threshold) + +和 threshold 函数一样 + +详细请看[Threshold](http://pytorch.org/docs/master/nn.html#torch.nn.functional.threshold) + +* * * + +``` +torch.nn.functional.relu(input, inplace=False) → Tensor +torch.nn.functional.relu_(input) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#relu) + +对输入元素应用 relu 函数 + +详细请看[Relu](http://pytorch.org/docs/master/nn.html#torch.nn.ReLU) + +* * * + +``` +torch.nn.functional.hardtanh(input, min_val=-1., max_val=1., inplace=False) → Tensor +torch.nn.functional.hardtanh_(input, min_val=-1., max_val=1.) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#hardtanh) + +对输入元素应用 HardTanh 函数 + +详细请看[Hardtanh](http://pytorch.org/docs/master/nn.html#torch.nn.Hardtanh) + +* * * + +``` +torch.nn.functional.relu6(input, inplace=False) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#relu6) + +对输入元素应用 ReLU6 函数 ReLU6(x)=min(max(0,x),6) + +详细请看[ReLU6](http://pytorch.org/docs/master/nn.html#torch.nn.ReLU6) + +* * * + +``` +torch.nn.functional.elu(input, alpha=1.0, inplace=False) +torch.nn.functional.elu_(input, alpha=1.) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#elu) + +对输入元素应用 ELU 函数 ELU(x)=max(0,x)+min(0,α∗(exp(x)−1)) + +详细请看[ELU](http://pytorch.org/docs/master/nn.html#torch.nn.ELU) + +* * * + +``` +torch.nn.functional.selu(input, inplace=False) → Tensor[source] +``` + +详细可见[selu](http://pytorch.org/docs/master/nn.html#torch.nn.SELU) + +* * * + +``` +torch.nn.functional.leaky_relu(input, negative_slope=0.01, inplace=False) → Tensor +torch.nn.functional.leaky_relu_(input, negative_slope=0.01) → Tensor +``` + +详细可见[LeakyReLu](http://pytorch.org/docs/master/nn.html#torch.nn.LeakyReLU) + +* * * + +``` +torch.nn.functional.prelu(input, weight) → Tensor +``` + +详细可见[PRelu](http://pytorch.org/docs/master/nn.html#torch.nn.PRelu) + +* * * + +``` +torch.nn.functional.rrelu(input, lower=1./8, upper=1./3, training=False, inplace=False) → Tensor +torch.nn.functional.rrelu_(input, lower=1./8, upper=1./3, training=False) → Tensor +``` + +详细可见[RRelu](http://pytorch.org/docs/master/nn.html#torch.nn.RRelu) + +* * * + +``` +torch.nn.functional.glu(input, dim=-1) → Tensor +``` + +门控制线性单元 H=A×σ(B),输入张量将被按照特定维度分成一半是 A,一半是 B + +可以参看论文[Language Modeling with Gated Convolutional Networks](https://arxiv.org/abs/1612.08083) + +### 参数: + +``` +input:输入的张量 +dim:需要被分割的输入张量的维度 +``` + +* * * + +``` +torch.nn.functional.logsigmoid(input) → Tensor +``` + +具体细节可以看[LogSigmoid](http://pytorch.org/docs/master/nn.html#torch.nn.LogSigmoid) + +* * * + +``` +torch.nn.functional.hardshrink(input, lambd=0.5) → Tensor +``` + +具体细节可以看[Hardshrink](http://pytorch.org/docs/master/nn.html#torch.nn.Hardshrink) + +* * * + +``` +torch.nn.functional.tanhshrink(input, lambd=0.5) → Tensor +``` + +具体细节可以看[Tanhshrink](http://pytorch.org/docs/master/nn.html#torch.nn.Tanhshrink) + +* * * + +``` +torch.nn.functional.softsign(input, lambd=0.5) → Tensor +``` + +具体细节可以看[Softsign](http://pytorch.org/docs/master/nn.html#torch.nn.Softsign) + +* * * + +``` +torch.nn.functional.softplus(input, beta=1, threshold=20) → Tensor +``` + +* * * + +``` +torch.nn.functional.softmin(input, dim=None, _stacklevel=3) +``` + +应用 softmin 函数 请注意 Softmin(x)= Softmax(-x)。 请参阅数学公式的 softmax 定义。 + +具体细节可以看[Softmin](http://pytorch.org/docs/master/nn.html#torch.nn.Softmin) + +### 参数: + +``` +input:输入张量 +dim:softmin 将被计算的维度(因此每个沿着 dim 的切分将总计为 1)。 +``` + +* * * + +``` +torch.nn.functional.softmax(input, dim=None, _stacklevel=3 +``` + +Softmax(x_i)=exp(x_i)/∑_jexp(x_j) + +它被应用于沿着对应维度的所有切分,并且将对它们进行重新缩放,以使得这些元素位于范围(0,1)中并且总计为 1。 + +具体细节可以看[Softmax](http://pytorch.org/docs/master/nn.html#torch.nn.Softmax) + +### 参数: + +``` +input:输入张量 +dim:softmax 被计算的维度。 +``` + +* * * + +``` +torch.nn.functional.softshrink(input, lambd=0.5) → Tensor +``` + +具体细节可以看[Softshrink](http://pytorch.org/docs/master/nn.html#torch.nn.Softshrink) + +* * * + +``` +torch.nn.functional.log_softmax(input, dim=None, _stacklevel=3) +``` + +尽管在数学上等同于 log(softmax(x)),但单独执行这两个操作会更慢,并且数值不稳定。 该函数使用替代公式来正确计算输出和梯度。 + +具体细节可以看[LogSoftmax](http://pytorch.org/docs/master/nn.html#torch.nn.LogSoftmax) + +* * * + +``` +torch.nn.functional.tanh(input) → Tensor +``` + +具体细节可以看[Tanh](http://pytorch.org/docs/master/nn.html#torch.nn.Tanh) + +* * * + +``` +torch.nn.functional.sigmoid(input) → Tensor +``` + +sigmoid(x) = 1/1+exp(-x) + +具体细节可以看[Sigmoid](http://pytorch.org/docs/master/nn.html#torch.nn.Sigmoid) + +## 归一化函数 + +* * * + +``` +torch.nn.functional.batch_norm(input, running_mean, running_var, weight=None, bias=None, training=False, momentum=0.1, eps=1e-05) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#batch_norm) + +具体细节可以看[BatchNorm1d](http://pytorch.org/docs/master/nn.html#torch.nn.BatchNorm1d) [BatchNorm2d](http://pytorch.org/docs/master/nn.html#torch.nn.BatchNorm2d) [BatchNorm3d](http://pytorch.org/docs/master/nn.html#torch.nn.BatchNorm3d) + +* * * + +``` +torch.nn.functional.instance_norm(input, running_mean=None, running_var=None, weight=None, bias=None, use_input_stats=True, momentum=0.1, eps=1e-05) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#instance_norm) + +具体细节可以看[InstanceNorm1d](http://pytorch.org/docs/master/nn.html#torch.nn.InstanceNorm1d) [InstanceNorm2d](http://pytorch.org/docs/master/nn.html#torch.nn.InstanceNorm2d) [InstanceNorm3d](http://pytorch.org/docs/master/nn.html#torch.nn.InstanceNorm3d) + +* * * + +``` +torch.nn.functional.layer_norm(input, normalized_shape, weight=None, bias=None, eps=1e-05) +``` + +具体细节可以看[LayerNorm](http://pytorch.org/docs/master/nn.html#torch.nn.LayerNorm) + +* * * + +``` +torch.nn.functional.local_response_norm(input, size, alpha=0.0001, beta=0.75, k=1) +``` + +对由多个输入平面组成的输入应用本地响应规范化,其中通道占据第二维。 通道应用归一化。 + +具体细节可以看[LocalResponseNorm](http://pytorch.org/docs/master/nn.html#torch.nn.LocalResponseNorm) + +* * * + +``` +torch.nn.functional.normalize(input, p=2, dim=1, eps=1e-12) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#linear) + +对指定维度的输入执行$$L_p$$标准化。 + +$$v = \frac{v}{\max(\lVert v \rVert_p, \epsilon)}$$ + +对于输入的维度 dim 的每个子扩展 v。 每个子扩张被平化成一个向量,即‖v‖p 不是一个矩阵范数。 + +使用默认参数在第二维上用欧几里得范数进行归一化。 + +### 参数: + +``` +input - 输入张量的形状 +p(float) - 规范公式中的指数值。默认值:2 +dim(int) - 要缩小的维度。默认值:1 +eps(float) - 小值以避免除以零。默认值:1e-12 +``` + +* * * + +## 线性函数 + +``` +torch.nn.functional.linear(input, weight, bias=None) +``` + +对于输入数据进行线性变化:$$y = xA^T+b$$. + +### 形状: + +``` +Input: (N,∗,in_features)(N,∗,in_features)这里的*表示为任意数量的附加维度 +Weight: (out_features,in_features)(out_features,in_features) +Bias: (out_features)(out_features) +Output: (N,∗,out_features) +``` + +* * * + +## Dropout 函数 + +``` +torch.nn.functional.dropout(input, p=0.5, training=False, inplace=False) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#dropout) + +* * * + +``` +torch.nn.functional.alpha_dropout(input, p=0.5, training=False) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#alpha_dropout) + +详细可见[alpha_dropout](http://pytorch.org/docs/master/nn.html#torch.nn.AlphaDropout) + +### 参数: + +``` +p(float,optional)-丢弃的可能性,默认是 0.5 +training(bool,optinal)-在训练模型和验证模型之间的切换,默认是 false +``` + +* * * + +``` +torch.nn.functional.dropout2d(input, p=0.5, training=False, inplace=False) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#dropout2d) + +* * * + +``` +torch.nn.functional.dropout3d(input, p=0.5, training=False, inplace=False) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#dropout3d) + +* * * + +## 距离函数 + +``` +torch.nn.functional.pairwise_distance(x1, x2, p=2, eps=1e-06, keepdim=False) +``` + +详细可见 [PairwiseDistance](http://pytorch.org/docs/master/nn.html#torch.nn.PairwiseDistance) + +* * * + +``` +torch.nn.functional.cosine_similarity(x1, x2, dim=1, eps=1e-08) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#cosine_similarity) + +计算向量 v1、v2 之间的距离 $$similarity = \frac{x_1 x x_2}{\max(\lVert v_1 \rVert_2 x \max(\lVert v_2 \rVert_2,\epsilon)}$$ + +### 参数: + +``` +x1 (Variable) – 首先输入参数. +x2 (Variable) – 第二个输入参数 (of size matching x1). +dim (int, optional) – 向量维数. 默认为: 1 +eps (float, optional) – 小值避免被零分割。默认为: 1e-8 模型: +``` + +### 形状: + +``` +input: (∗1,D,∗2)(∗1,D,∗2) D 是位置维度 +output: (∗1,∗2)(∗1,∗2) 1 是位置维度 +``` + +### 举例: + +``` +>>> input1 = autograd.Variable(torch.randn(100, 128)) +>>> input2 = autograd.Variable(torch.randn(100, 128)) +>>> output = F.cosine_similarity(input1, input2) +>>> print(output) +``` + +## 损失函数 + +``` +torch.nn.functional.binary_cross_entropy(input, target, weight=None, size_average=True, reduce=True) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#binary_cross_entropy) + +测量目标和输出之间的二进制交叉熵的函数。 + +详细可见[BCELoss](http://pytorch.org/docs/master/nn.html#torch.nn.BCELoss) + +### 参数: + +``` +input:任意维度 +target:与输入维度相同 +weight(张量,可选):如果提供的权重矩阵能匹配输入张量形状,则手动调整重量 +size_average(布尔值,可选):默认情况下,对每个小批次的损失进行平均观察。 但是,如果 field size_average 设置为 False,则每个小批次的损失将相加。 默认值:True +reduce(布尔值,可选):默认情况下,根据 size_average 的不同,对每个小批次的损失进行平均或累计。 当 reduce 为 False 时,将返回每个输入/目标元素的损失,而忽略 size_average。 默认值:True +``` + +### 举例: + +``` +>>> input = torch.randn((3, 2), requires_grad=True) +>>> target = torch.rand((3, 2), requires_grad=False) +>>> loss = F.binary_cross_entropy(F.sigmoid(input), target) +>>> loss.backward() +``` + +* * * + +``` +torch.nn.functional.poisson_nll_loss(input, target, log_input=True, full=False, size_average=True, eps=1e-08, reduce=True) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#poisson_nll_loss) + +泊松负对数似然损失 详细可见[PoissonNLLLoss](http://pytorch.org/docs/master/nn.html#torch.nn.PoissonNLLLoss) + +### 参数: + +``` +input:按照泊松分布的期望 +target:随机样例 target~Poisson(input) +log_input:如果为真,则损失计算为 exp(inout)-target * input,如果为 False,则 input-target *log⁡(input + eps)。 默认值:True +full:是否计算完全损失。即添加斯特林近似项。 默认值:False target * log(target)-target + 0.5 * log(2 *π* target) +size_average:默认情况下,对每个小批次的损失进行平均观察。 但是,如果 field size_average 设置为 False,则每个小批次的损失将相加。 默认值:True +eps(float,可选) - 当 log_input`=“False”时避免评估 log(0)log⁡(0)的较小值。 默认:1e-8 +reduce(布尔值,可选):默认情况下,根据每个小批次的观测结果对损失进行平均,或根据 size_average 进行汇总。 如果 reduce 为 False,则返回每批损失,并忽略 size_average。 默认值:True +``` + +* * * + +``` +torch.nn.functional.cosine_embedding_loss(input1, input2, target, margin=0, size_average=True, reduce=True) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#cosine_embedding_loss) + +详细可见[CosineEmbeddingLoss](http://pytorch.org/docs/master/nn.html#torch.nn.CosineEmbeddingLoss) + +* * * + +``` +torch.nn.functional.cross_entropy(input, target, weight=None, size_average=True, ignore_index=-100, reduce=True) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#cross_entropy) + +该标准将 log_softmax 和 nll_loss 结合在一个函数中。 + +[CrossEntropyLoss](http://pytorch.org/docs/master/nn.html#torch.nn.CrossEntropyLoss) + +### 参数: + +``` +input(张量)- (N,C) 其中,C 是类别的个数 +target(张量)- (N) 其大小是 0 <= targets[i] <= C-1 1\. weight (Variable, optional) – (N) 其大小是 0 <= targets[i] <= C-1 +weight (张量, optional) – 为每个类别提供的手动权重。如果给出,必须是大小为 C 的张量 +size_average (bool, optional) – 默认情况下,是 mini-batchloss 的平均值;如果 size_average=False,则是 mini-batchloss 的总和。 +ignore_index(int,可选) - 指定被忽略且不对输入渐变有贡献的目标值。当 size_average 为 True 时,对非忽略目标的损失是平均的。默认值:-100 +reduce(布尔值,可选)-默认情况下,根据每个小批次的观测结果对损失进行平均,或根据 size_average 进行汇总。 如果 reduce 为 False,则返回每批损失,并忽略 size_average。 默认值:True +``` + +### 举例: + +``` +>>> input = torch.randn(3, 5, requires_grad=True) +>>> target = torch.randint(5, (3,), dtype=torch.int64) +>>> loss = F.cross_entropy(input, target) +>>> loss.backward() +``` + +* * * + +``` +torch.nn.functional.hinge_embedding_loss(input, target, margin=1.0, size_average=True, reduce=True) → Tensor +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#hinge_embedding_loss) + +详细可见[HingeEmbeddingLoss](http://pytorch.org/docs/master/nn.html#torch.nn.HingeEmbeddingLoss) + +* * * + +``` +torch.nn.functional.kl_div(input, target, size_average=True) → Tensor +``` + +[source](http://pytorch.org/docs/master/nn.html#torch.nn.functional.kl_div) + +The [Kullback-Leibler divergence](https://en.wikipedia.org/wiki/Kullback-Leibler_divergence) Loss + +详细可见[KLDivLoss](http://pytorch.org/docs/master/nn.html#torch.nn.KLDivLoss) + +### 参数: + +``` +input – 变量的任意形状 +target - 与输入相同形状的变量 +size_average – 如果是真的,输出就除以输入张量中的元素个数 +reduce(布尔值,可选)-默认情况下,根据每个小批次的观测结果对损失进行平均,或根据 size_average 进行汇总。 如果 reduce 为 False,则返回每批损失,并忽略 size_average。 默认值:True +``` + +* * * + +``` +torch.nn.functional.l1_loss(input, target, size_average=True, reduce=True) → Tensor +``` + +详细可见[L1Loss](http://pytorch.org/docs/master/nn.html#torch.nn.L1Loss) + +* * * + +``` +torch.nn.functional.mse_loss(input, target, size_average=True, reduce=True) → Tensor +``` + +详细可见[MSELoss](http://pytorch.org/docs/master/nn.html#torch.nn.MSELoss) + +* * * + +``` +torch.nn.functional.margin_ranking_loss(input, target, size_average=True, reduce=True) → Tensor +``` + +详细可见[MarginRankingLoss](http://pytorch.org/docs/master/nn.html#torch.nn.MarginRankingLoss) + +* * * + +``` +torch.nn.functional.multilabel_soft_margin_loss(input, target, size_average=True, reduce=True) → Tensor +``` + +详细可见[MultiLabelSoftMarginLoss](http://pytorch.org/docs/master/nn.html#torch.nn.MultiLabelSoftMarginLoss) + +* * * + +``` +torch.nn.functional.multi_margin_loss(input, target, p=1, margin=1, weight=None, size_average=True, reduce=True) → Tensor +``` + +详细可见[MultiMarginLoss](http://pytorch.org/docs/master/nn.html#torch.nn.MultiMarginLoss) + +* * * + +``` +torch.nn.functional.nll_loss(input, target, weight=None, size_average=True, ignore_index=-100, reduce=True) +``` + +详细可见[NLLLoss](http://pytorch.org/docs/master/nn.html#torch.nn.NLLLoss) + +### 参数: + +``` +input - \((N,C)\)其中 C =类的数量或( 2D,Loss )的情况下的(N,C,H,W) target - \((N)\),其中每个值为 0 <= targets [i] <= C-1 +weight(可变,可选) - 给每个类别的手动重新调整重量。如果给定,必须变量大小是 C +size_average(bool,可选) - 默认情况下,损失是对每个小型服务器的观察值进行平均。如果 size_average 为 False,则对于每个 minibatch 都会将损失相加。默认值:True +ignore_index(int,可选) - 指定被忽略且不对输入渐变有贡献的目标值。当 size_average 为 True 时,对非忽略目标的损失是平均的。默认值:-100 +``` + +### 举例: + +``` +>>> # input is of size N x C = 3 x 5 +>>> input = torch.randn(3, 5, requires_grad=True) +>>> # each element in target has to have 0 <= value < C +>>> target = torch.tensor([1, 0, 4]) +>>> output = F.nll_loss(F.log_softmax(input), target) +>>> output.backward() +``` + +* * * + +``` +torch.nn.functional.binary_cross_entropy_with_logits(input, target, weight=None, size_average=True, reduce=True) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#binary_cross_entropy_with_logits) + +测量目标和输出逻辑之间二进制十进制熵的函数 + +详细可见[BCEWithLogitsLoss](http://pytorch.org/docs/master/nn.html#torch.nn.BCEWithLogitsLoss) + +### 参数: + +``` +input - 任意形状的变量 +target - 与输入形状相同的变量 +weight(可变,可选) - 手动重量重量,如果提供重量以匹配输入张量形状 +size_average(bool,可选) - 默认情况下,损失是对每个小型服务器的观察值进行平均。然而,如果字段 sizeAverage 设置为 False,则相应的损失代替每个 minibatch 的求和。默认值:True +reduce(布尔值,可选)-默认情况下,根据每个小批次的观测结果对损失进行平均,或根据 size_average 进行汇总。 如果 reduce 为 False,则返回每批损失,并忽略 size_average。 默认值:True +``` + +### 举例: + +``` +>>> input = torch.randn(3, requires_grad=True) +>>> target = torch.empty(3).random_(2) +>>> loss = F.binary_cross_entropy_with_logits(input, target) +>>> loss.backward() +``` + +* * * + +``` +torch.nn.functional.smooth_l1_loss(input, target, size_average=True, reduce=True) → Tensor +``` + +详细可见[SmoothL1Loss](http://pytorch.org/docs/master/nn.html#torch.nn.SmoothL1Loss) + +* * * + +``` +torch.nn.functional.soft_margin_loss(input, target, size_average=True, reduce=True) → Tensor +``` + +详细可见[Soft_margin_loss](http://pytorch.org/docs/master/nn.html#torch.nn.Soft_margin_loss) + +* * * + +``` +torch.nn.functional.triplet_margin_loss(anchor, positive, negative, margin=1.0, p=2, eps=1e-06, swap=False, size_average=True, reduce=True) +``` + +详细可见[TripletMarginLoss](http://pytorch.org/docs/master/nn.html#torch.nn.TripletMarginLoss) + +## 视觉函数 + +``` +torch.nn.functional.pixel_shuffle(input, upscale_factor) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#pixel_shuffle) + +将形状为[_, C_r^2, H, W]的张量重新排列成形状为[C, H_r, W_r]的张量. + +详细请看[PixelShuffle](http://pytorch.org/docs/master/nn.html#torch.nn.PixelShuffle) + +### 参数: + +``` +input (Variable) – 输入 +upscale_factor (int) – 增加空间分辨率的因子. +``` + +### 举例: + +``` +>>> ps = nn.PixelShuffle(3) +>>> input = autograd.Variable(torch.Tensor(1, 9, 4, 4)) +>>> output = ps(input) +>>> print(output.size()) +torch.Size([1, 1, 12, 12]) +``` + +* * * + +``` +torch.nn.functional.pad(input, pad, mode='constant', value=0) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#pad) + +填充张量. + +可以参考[torch.nn.ConstantPad2d](http://pytorch.org/docs/master/nn.html#torch.nn.ConstantPad2d), [torch.nn.ReflectionPad2d](http://pytorch.org/docs/master/nn.html#torch.nn.ReflectionPad2d), 和 [torch.nn.ReplicationPad2d](http://pytorch.org/docs/master/nn.html#torch.nn.ReplicationPad2d)对于每个填充模式如何工作的具体例子。 + +### 参数: + +``` +input (Variable) – 4D 或 5D tensor +pad (tuple) – 4 元素 或 6-元素 tuple +mode – ‘constant’, ‘reflect’ or ‘replicate’ +value – 用于 constant padding 的值. +``` + +### 举例: + +``` +>>> t4d = torch.empty(3, 3, 4, 2) +>>> p1d = (1, 1) # pad last dim by 1 on each side +>>> out = F.pad(t4d, p1d, "constant", 0) # effectively zero padding +>>> print(out.data.size()) +torch.Size([3, 3, 4, 4]) +>>> p2d = (1, 1, 2, 2) # pad last dim by (1, 1) and 2nd to last by (2, 2) +>>> out = F.pad(t4d, p2d, "constant", 0) +>>> print(out.data.size()) +torch.Size([3, 3, 8, 4]) +>>> t4d = torch.empty(3, 3, 4, 2) +>>> p3d = (0, 1, 2, 1, 3, 3) # pad by (0, 1), (2, 1), and (3, 3) +>>> out = F.pad(t4d, p3d, "constant", 0) +>>> print(out.data.size()) +torch.Size([3, 9, 7, 3]) +``` + +* * * + +``` +torch.nn.functional.upsample(input, size=None, scale_factor=None, mode='nearest', align_corners=None) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#upsample) + +Upsamples 输入内容要么就是给定的 size 或者 scale_factor 用于采样的算法是由模型决定的 目前支持的是空间和容量的采样,即期望输入的形状是 4-d 或 5-d。 输入尺寸被解释为:迷你批 x 通道 x 深度 x 高度 x 宽度 用于 upsampling 的模型是:线性的(仅 3D),双线性的(仅 4D),三线性(仅 5D) + +### 参数: + +``` +input (Variable) – 输入内容 +size (int or Tuple[int, int] or Tuple[int, int, int]) – 输出空间的大小。 +scale_factor (int) – 乘数的空间大小。必须是一个整数。 +mode (string) – 用于向上采样的算法: ‘nearest’ | ‘bilinear’ | ‘trilinear’ +align_corners(bool,可选) - 如果为 True,则输入和输出张量的角点像素对齐,从而保留这些像素的值。 这只在模式为线性,双线性或三线性时才有效。 默认值:False +``` + +### 警告: + +``` +使用 align_corners = True 时,线性插值模式(线性,双线性和三线性)不会按比例对齐输出和输入像素,因此输出值可能取决于输入大小。 +这是这些模式到版本 0.3.1 的默认行为。 此后,默认行为是 align_corners = False。 有关这将如何影响输出的具体示例,请参见 Upsample。 +``` + +* * * + +``` +torch.nn.functional.upsample_nearest(input, size=None, scale_factor=None) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#upsample_nearest) 使用最接近的邻居的像素值来对输入进行采样。 + +### 警告: + +``` +此功能已弃用,以支持 torch.nn.functional.upsample()。 这相当于 nn.functional.upsample(...,mode ='nearest')。 +``` + +目前支持空间和体积上采样(即预期的输入是 4 或 5 维)。 + +### 参数: + +``` +input (Variable) – 输入内容 +size (int or Tuple[int, int]) – 输出空间的大小。 +scale_factor (int or Tuple[int, int]) – 乘数的空间大小 +``` + +* * * + +``` +torch.nn.functional.upsample_bilinear(input, size=None, scale_factor=None) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#upsample_bilinear) 使用双线性向上采样来扩展输入 + +### 警告: + +``` +这个函数是被弃用的。使用 nn.functional。upsample 相反 预期的输入是空间的(4 维)。 +使用 upsampletri 线性来进行体积(5 维)输入。 +``` + +### 参数: + +``` +input (Variable) – 输入内容 +size (int or Tuple[int, int]) – 输出空间的大小。 +scale_factor (int or Tuple[int, int]) – 乘数的空间大小 +``` + +* * * + +``` +torch.nn.functional.grid_sample(input, grid, mode='bilinear', padding_mode='zeros') +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#grid_sample) + +给定输入和流场网格,使用网格中的输入像素位置计算输出。 + +使用双线性插值来对输入像素进行采样。 目前,仅支持空间(4 维)和体积(5 维)输入。 + +对于每个输出位置,网格具有用于计算输出的 x,y 输入像素位置。 在 5D 输入的情况下,网格具有 x,y,z 像素位置。 + +grid 的值在[-1,1]的范围内。 这是因为像素位置是由输入高度和宽度标准化的。 + +例如,值:x:-1,y:-1 是输入的左上像素,值:x:1,y:1 是输入的右下像素。 + +如果 grid 的值超出[-1,1]的范围,则按 padding_mode 的定义处理这些位置。 选项为零或边框,定义这些位置以使用 0 或图像边界值作为对双线性插值的贡献。 + +### 参数: + +``` +input(Tensor) - 输入批次(N x C x IH x IW)或(N x C x ID x IH x IW) +grid(Tensor) - 尺寸(N x OH x OW x 2)或(N x OD x OH x OW x 3)的流场, +padding_mode(str) - 用于外部网格值“零”|的填充模式 '边境'。 默认值:'零' +``` + +### 返回: + +``` +output(tensor) +``` + +* * * + +``` +torch.nn.functional.affine_grid(theta, size) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/functional.html#affine_grid) + +生成一个 2d 流场,给定一批仿射矩阵 theta 通常与 grid_sample()一起使用来实现 Spatial Transformer Networks。 + +### 参数: + +``` +theta(Tensor) - 输入一批仿射矩阵(N×2×3N×2×3) +size(torch.Size) - 目标输出图像尺寸(N×C×H×WN×C×H×W)例如:torch.Size((32,3,24,24)) +``` + +### 返回: + +``` +output(tensor):输出张量大小 (N×H×W×2N×H×W×2) +``` + +## 并行函数(多 GPU,分布式) + +``` +torch.nn.parallel.data_parallel(module, inputs, device_ids=None, output_device=None, dim=0, module_kwargs=None) +``` + +[source](http://pytorch.org/docs/master/_modules/torch/nn/parallel/data_parallel.html#data_parallel) + +在 device_ids 中给出的 GPU 上并行评估模块(输入)。 这是 DataParallel 模块的功能版本。 + +### 参数: + +``` +module - 并行评估的模块 +input - 输入到模块 +device_ids - 要在其上复制模块的 GPU ID +output_device - 输出的 GPU 位置使用-1 指示 CPU。 (默认:device_ids [0]) +``` + +### 返回: + +``` +包含位于 output_device 上的模块(输入)结果的张量 +``` + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| travel | ![](img/65ca6a0b.jpg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/18.md b/docs/0.4/18.md new file mode 100644 index 00000000..494556f7 --- /dev/null +++ b/docs/0.4/18.md @@ -0,0 +1,194 @@ +# 自动差异化包 - torch.autograd + +* [Variable](https://ptorch.com/docs/1/autograd#variable) +* [API 兼容性](https://ptorch.com/docs/1/autograd#api-compatibility) +* [变量的直接操作](https://ptorch.com/docs/1/autograd#in-place-operations-on-variables) +* [In-place 正确性检查](https://ptorch.com/docs/1/autograd#in-place-correctness-checks) +* [Function](https://ptorch.com/docs/1/autograd#function) + +* * * + +`torch.autograd`提供了类和函数用来对任意标量函数进行求导。要想使用自动求导,只需要对已有的代码进行微小的改变。只需要将所有的`tensor`包含进`Variable`对象中即可。 + +### torch.autograd.backward(variables, grad_variables, retain_variables=False) + +计算给定变量 wrt 图叶的梯度的总和。 该图使用链规则进行区分。如果任何 variables 非标量(即它们的数据具有多个元素)并且需要梯度,则该函数另外需要指定 grad_variables。它应该是一个匹配长度的序列,其包含差分函数 wrt 对应变量的渐变(None 对于不需要梯度张量的所有变量,它是可接受的值)。 + +此函数在树叶中累加梯度 - 在调用它之前可能需要将其清零。 + +参数: + +* variables (variable 列表) – 将计算导数的变量 。 + +* grad_variables (序列(`Tensor`, `Variable`或者 `None`)) – 渐变写入相应变量的每个元素。任何张量将被自动转换为 volatile,除非 create_graph 为 True。可以为标量变量或不需要 grad 的值指定无值。如果所有 grad_variables 都可以接受 None 值,则该参数是可选的。 + +* retain_graph(bool,可选) - 如果为 False,则用于计算 grad 的图形将被释放。请注意,在几乎所有情况下,将此选项设置为 True 不是必需的,通常可以以更有效的方式解决。默认值为 create_graph。 + +* create_graph(bool,可选) - 如果为 true,则构造导数的图形,允许计算更高阶的衍生产品。默认为 False,除非 grad_variables 包含至少一个非易失性变量。 + +### torch.autograd.grad(outputs, inputs, grad_outputs=None, retain_graph=None, create_graph=None, only_inputs=True) + +计算并返回输入的输出梯度的总和。 + +grad*outputs 应该是 output 包含每个输出的预先计算的梯度的长度匹配序列。如果输出不需要* grad,则渐变可以是 None)。当不需要派生图的图形时,梯度可以作为 Tensors 给出,或者作为 Variables,在这种情况下将创建图形。 + +如果 only_inputs 为 True,该函数将仅返回指定输入的渐变列表。如果它是 False,则仍然计算所有剩余叶子的渐变度,并将累积到其.grad 属性中。 + +参数: + +* outputs(可变序列) - 差分函数的输出。 +* inputs(可变序列) - 输入将返回梯度的积分(并不积累.grad)。 +* grad_outputs(Tensor 或 Variable 的序列) - 渐变 wrd 每个输出。任何张量将被自动转换为 volatile,除非 create_graph 为 True。可以为标量变量或不需要 grad 的值指定无值。如果所有 grad_variables 都可以接受 None 值,则该参数是可选的。 +* retain_graph(bool,可选) - 如果为 False,则用于计算 grad 的图形将被释放。请注意,在几乎所有情况下,将此选项设置为 True 不是必需的,通常可以以更有效的方式解决。默认值为 create_graph。 +* create_graph(bool,可选) - 如果为 True,则构造导数的图形,允许计算高阶衍生产品。默认为 False,除非 grad_variables 包含至少一个非易失性变量。 +* only_inputs(bool,可选) - 如果为 True,则渐变 wrt 离开是图形的一部分,但不显示 inputs 不会被计算和累积。默认为 True。 + +### API 兼容性 + +可变 API 与常规 Tensor API(除了几个就地方法,覆盖渐变计算所需的输入之外)几乎相同。在大多数情况下,传感器可以安全地替换为变量,代码将保持工作正常。因此,我们不会记录变量的所有操作,torch.Tensor 为此也应该参考文档。 + +### 变量的直接操作 + +支持自动归档中的就地操作是一件很困难的事情,我们在大多数情况下都不鼓励使用它们。Autograd 的积极缓冲区释放和重用使其非常高效,并且在现场操作实际上会降低内存使用量的情况下,极少数场合很少。除非您在内存压力很大的情况下运行,否则您可能永远不需要使用它们。 + +### In-place 正确性检查 + +所有 Variable 的跟踪应用于它们的就地操作,并且如果实现检测到在一个功能中保存了向后的变量,但是之后被修改就位,则一旦向后通过开始就会产生错误。这可以确保如果您使用就地功能并且没有看到任何错误,则可以确定计算出的渐变是正确的。 + +### class torch.autograd.Variable + +包裹张量并记录应用的操作。 + +变量是 Tensor 对象周围的薄包装,它也保存了渐变 wrt,并引用了创建它的函数。此引用允许回溯创建数据的整个操作链。如果变量是由用户创建的,那么它的 grad_fn 将被 None 我们称为这样的对象叶变量。 + +由于自动调整仅支持标量值函数微分,因此 grad 大小始终与数据大小匹配。此外,grad 通常仅分配给叶变量,否则将始终为零。 + +变量: + +* data – 包含的`Tensor` + +* grad - 变量保持类型和位置的坡度匹配.data。此属性是懒惰分配的,无法重新分配。 + +* requires_grad - 指示变量是否由包含任何需要的变量的子图创建的布尔值。有关详细信息,请参阅从向后排除子图。只能在叶变量上更改。 +* volatile - Boolean 表示在推断模式下应该使用 Variable,即不保存历史记录。有关详细信息,请参阅 从向后排除子图。只能在叶变量上更改。 +* is_leaf - Boolean 指示变量是否为图形叶(即如果由用户创建)。 +* grad_fn - 梯度函数图跟踪。 + +属性: + +* data (any tensor class) – 被包含的`Tensor` + +* requires_grad (bool) – `requires_grad`标记. 只能通过`keyword`传入. + +* volatile (bool) – `volatile`标记. 只能通过`keyword`传入. + +#### backward(gradient=None, retain_variables=False) + +当前`Variable`对`leaf variable`求偏导。 + +计算图可以通过链式法则求导。如果`Variable`是 非标量(`non-scalar`)的,且`requires_grad=True`。那么此函数需要指定`gradient`,它的形状应该和`Variable`的长度匹配,里面保存了`Variable`的梯度。 + +此函数累积`leaf variable`的梯度。你可能需要在调用此函数之前将`Variable`的梯度置零。 + +参数: + +* grad_variables(Tensor,Variable 或 None) - 渐变 wrt 变量。如果它是张量,它将被自动转换为 volatile,除非 create_graph 是 True。可以为标量变量或不需要 grad 的值指定无值。如果无值可接受,则此参数是可选的。 +* retain_graph(bool,可选) - 如果为 False,用于计算 grads 的图形将被释放。请注意,在几乎所有情况下,将此选项设置为 True 不是必需的,通常可以以更有效的方式解决。默认值为 create_graph。 +* create_graph(bool,可选) - 如果为 true,则构造导数的图形,允许计算更高阶的衍生产品。默认为 False,除非 gradient 是 volatile 变量。 + +#### detach() + +返回一个新变量,与当前图形分离。 + +结果将永远不需要渐变。如果输入是易失的,输出也将变得不稳定。 + +> 注意: 返回变量使用与原始变量相同的数据张量,并且可以看到其中任何一个的就地修改,并且可能会触发正确性检查中的错误。 + +#### detach_() + +从创建它的图形中分离变量,使其成为叶。 + +#### register_hook(hook) + +注册一个`backward`钩子。 + +每次`gradients`被计算的时候,这个`hook`都被调用。`hook`应该拥有以下签名: + +``` +hook(grad) -> Variable or None +``` + +钩子不应该修改它的参数,但是它可以选择返回一个新的渐变,用来代替它 grad。 + +此函数返回一个带有 handle.remove() 从模块中删除钩子的方法的句柄。 + +例: + +``` +>>> v = Variable(torch.Tensor([0, 0, 0]), requires_grad=True) +>>> h = v.register_hook(lambda grad: grad * 2) # double the gradient +>>> v.backward(torch.Tensor([1, 1, 1])) +>>> v.grad.data + 2 + 2 + 2 +[torch.FloatTensor of size 3] +>>> h.remove() # removes the hook +``` + +#### reinforce(reward) + +注册一个奖励,这个奖励是由一个随机过程得到的。 + +微分一个随机节点需要提供一个奖励值。如果你的计算图中包含随机 `operations`,你需要在他们的输出上调用这个函数。否则的话,会报错。 + +参数: + +* reward (Tensor) – 每个元素的 reward 张量。必须和`Varaible`形状相同,并在同一个设备上。 + +#### retain_grad() + +启用非叶变量的.grad 属性。 + +### class torch.autograd.Function + +记录操作历史并定义用于区分操作的公式。 + +在 Variables 上执行的每个操作创建一个新的函数对象,执行计算,并记录它发生的情况。历史以 DAG 的形式保留,边缘表示数据依赖()。然后,当调用向后时,通过调用每个对象的方法,并将返回的梯度传递给下一个 s 来处理图形的拓扑排序 。input <- outputbackward()FunctionFunction + +通常,用户与功能交互的唯一方式是创建子类并定义新的操作。这是延长 torch.autograd 的推荐方法。 + +由于函数逻辑是大多数脚本的热点,因此几乎所有脚本都被移动到 C 后端,以确保框架开销最小化。 + +每个功能仅用于一次(在正向通行证中)。 + +变量: + +* saved_tensors - 保存在呼叫中的 Tensor 元组 forward()。 +* saved_variables - 对应于调用中保存的张量的变量元组 forward()。 +* needs_input_grad - 长度的布尔元组 num_inputs,指示给定输入是否需要渐变。这可以用于优化缓冲区保存为向后,忽略梯度计算 backward()。 +* num_inputs - 给出的输入数量 forward()。 +* num_outputs - 返回的张量数 forward()。 +* requires_grad - 布尔值,表示是否 backward()需要调用。 + +#### backward(* grad_output) + +定义用于区分操作的公式。 + +此功能将被所有子类覆盖。 + +所有参数都是张量。它必须接受完全一样多的论据,因为许多输出都 forward()返回,它应该返回尽可能多的张量,因为有输入 forward()。每个参数是给定输出的渐变 wrt,每个返回的值应该是对应输入的渐变。 + +#### static forward(*args, **kwargs) + +执行操作。 + +此功能将被所有子类覆盖。 + +它可以采取并返回任意数量的张量。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/19.md b/docs/0.4/19.md new file mode 100644 index 00000000..a9a99e89 --- /dev/null +++ b/docs/0.4/19.md @@ -0,0 +1,507 @@ +# torch.optim + +1. [如何使用 optimizer](#how-to-use-an-optimizer) + * [构建](#constructing-it) + * [为每个参数单独设置选项](#per-parameter-options) + * [进行单次优化](#taking-an-optimization-step) + * [optimizer.step()](#optimizer.step()) + * [optimizer.step(closure)](#optimizer.step(closure)) +2. [算法](#algorithms) +3. [如何调整学习率](#how-to-adjust-learning-rate) + +* * * + +`torch.optim`是实现各种优化算法的包。最常用的方法都已经支持,接口很常规,所以以后也可以很容易地集成更复杂的方法。 + +#### 如何使用 optimizer + +要使用`torch.optim`,您必须构造一个`optimizer`对象。这个对象能保存当前的参数状态并且基于计算梯度更新参数 + +#### 构建 + +要构造一个`Optimizer`,你必须给它一个包含参数(必须都是`Variable`对象)进行优化。然后,您可以指定`optimizer`的参 数选项,比如学习率,权重衰减等。 + +例子: + +``` +optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9) +optimizer = optim.Adam([var1, var2], lr = 0.0001) +``` + +#### 为每个参数单独设置选项 + +`Optimizer`也支持为每个参数单独设置选项。若想这么做,不要直接传入`Variable`的`iterable`,而是传入`dict 的 iterable`。每一个`dict`都分别定 义了一组参数,并且包含一个`param`键,这个键对应参数的列表。其他的键应该`optimizer`所接受的其他参数的关键字相匹配,并且会被用于对这组参数的 优化。 + +> 注意: +> +> 您仍然可以将选项作为关键字参数传递。它们将被用作默认值,在不覆盖它们的组中。当您只想改变一个选项,同时保持参数组之间的所有其他选项一致时,这很有用。 + +例如,当我们想指定每一层的学习率时,这是非常有用的: + +``` +optim.SGD([ + {'params': model.base.parameters()}, + {'params': model.classifier.parameters(), 'lr': 1e-3} + ], lr=1e-2, momentum=0.9) +``` + +这意味着`model.base`参数将使用默认的学习速率`1e-2`,`model.classifier`参数将使用学习速率`1e-3`,并且`0.9`的`momentum`将会被用于所有的参数。 + +#### 进行单次优化 + +所有的`optimizer`都会实现`step()`更新参数的方法。它能按两种方式来使用: + +#### optimizer.step() + +这是大多数`optimizer`所支持的简化版本。一旦梯度被如`backward()`之类的函数计算好后,我们就可以调用该函数。 + +例子 + +``` +for input, target in dataset: + optimizer.zero_grad() + output = model(input) + loss = loss_fn(output, target) + loss.backward() + optimizer.step() +``` + +#### optimizer.step(closure) + +一些优化算法例如`Conjugate Gradient`和`LBFGS`需要重复多次计算函数,因此你需要传入一个闭包去允许它们重新计算你的模型。这个闭包会清空梯度, 计算损失,然后返回。 + +例子: + +``` +for input, target in dataset: + def closure(): + optimizer.zero_grad() + output = model(input) + loss = loss_fn(output, target) + loss.backward() + return loss + optimizer.step(closure) +``` + +#### 算法 + +``` +class torch.optim.Optimizer(params, defaults) +``` + +所有优化的基类. + +参数: + +1. params (iterable) —— 可迭代的`Variable` 或者 `dict`。指定应优化哪些变量。 +2. defaults-(dict):包含优化选项的默认值的 dict(一个参数组没有指定的参数选项将会使用默认值)。 + +``` +load_state_dict(state_dict) +``` + +加载`optimizer`状态 + +参数: + +1. state_dict (dict) —— `optimizer`的状态。应该是`state_dict()`调用返回的对象。 + +``` +state_dict() +``` + +将优化器的状态返回为一个`dict`。 + +它包含两个内容: + +1. state - 持有当前`optimization`状态的`dict`。它包含了 优化器类之间的不同。 +2. param_groups - 一个包含了所有参数组的`dict`。 + +``` +step(closure) +``` + +执行单个优化步骤(参数更新)。 + +参数: + +1. closure (callable,可选) – 重新评估模型并返回损失的闭包。hon zero_grad() + +清除所有优化过的`Variable`的梯度。 + +``` +class torch.optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-06, weight_decay=0) +``` + +实现`Adadelta`算法。 + +[ADADELTA](https://arxiv.org/abs/1212.5701)中提出了一种[自适应学习速率法](https://arxiv.org/abs/1212.5701)。 + +参数: + +1. params (iterable) – 用于优化的可以迭代参数或定义参数组 +2. rho (float, 可选) – 用于计算平方梯度的运行平均值的系数(默认值:0.9) +3. eps (float, 可选) – 增加到分母中以提高数值稳定性的术语(默认值:1e-6) +4. lr (float, 可选) – 将 delta 应用于参数之前缩放的系数(默认值:1.0) +5. weight_decay (float, 可选) – 权重衰减 (L2 范数)(默认值: 0) + +``` +step(closure) +``` + +执行单个优化步骤。 + +参数: + +1. closure (callable,可选) – 重新评估模型并返回损失的闭包。 + +``` +class torch.optim.Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0) +``` + +实现 Adagrad 算法。 + +在[在线学习和随机优化的自适应子梯度方法](http://jmlr.org/papers/v12/duchi11a.html)中被提出。 + +参数: + +1. params (iterable) – 用于优化的可以迭代参数或定义参数组 +2. lr (float, 可选) – 学习率(默认: 1e-2) +3. lr_decay (float, 可选) – 学习率衰减(默认: 0) +4. weight_decay (float, 可选) – 权重衰减(L2 范数)(默认: 0) + +``` +step(closure) +``` + +执行单个优化步骤。 + +参数: + +1. closure (callable,可选) – 重新评估模型并返回损失的闭包。 + +``` +class torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)[source] +``` + +实现 Adam 算法。 + +它在[Adam: A Method for Stochastic Optimization](https://arxiv.org/abs/1412.6980)中被提出。 + +参数: + +1. params (iterable) – 用于优化的可以迭代参数或定义参数组 +2. lr (float, 可选) – 学习率(默认:1e-3) +3. betas (Tuple[float, float], 可选) – 用于计算梯度运行平均值及其平方的系数(默认:0.9,0.999) +4. eps (float, 可选) – 增加分母的数值以提高数值稳定性(默认:1e-8) +5. weight_decay (float, 可选) – 权重衰减(L2 范数)(默认: 0) + +``` +step(closure) +``` + +执行单个优化步骤。 + +参数: + +1. closure (callable,可选) – 重新评估模型并返回损失的闭包。 + +``` +class torch.optim.Adamax(params, lr=0.002, betas=(0.9, 0.999), eps=1e-08, weight_decay=0) +``` + +实现`Adamax`算法(Adam 的一种基于无穷范数的变种)。 + +它在[Adam: A Method for Stochastic Optimization](https://arxiv.org/abs/1412.6980)中被提出。 + +参数: + +1. params (iterable) – 用于优化的可以迭代参数或定义参数组 +2. lr (float, 可选) – 学习率(默认:2e-3) +3. betas (Tuple[float, float], 可选) – 用于计算梯度以及梯度平方的运行平均值的系数 +4. eps (float, 可选) – 增加分母的数值以提高数值稳定性(默认:1e-8) +5. weight_decay (float, 可选) – 权重衰减(L2 范数)(默认: 0) + +``` +step(closure=None) +``` + +执行单个优化步骤。 + +参数: + +1. closure (callable,可选) – 重新评估模型并返回损失的闭包。 + +``` +class torch.optim.ASGD(params, lr=0.01, lambd=0.0001, alpha=0.75, t0=1000000.0, weight_decay=0) +``` + +实现平均随机梯度下降。 + +它在[Acceleration of stochastic approximation by averaging](http://dl.acm.org/citation.cfm?id=131098)中被提出。 + +参数: + +1. params (iterable) – 用于优化的可以迭代参数或定义参数组 +2. lr (float, 可选) – 学习率(默认:1e-2) +3. lambd (float, 可选) – 衰减期(默认:1e-4) +4. alpha (float, 可选) – eta 更新的指数(默认:0.75) +5. t0 (float, 可选) – 指明在哪一次开始平均化(默认:1e6) +6. weight_decay (float, 可选) – 权重衰减(L2 范数)(默认: 0) + +``` +step(closure) +``` + +执行单个优化步骤。 + +参数: + +1. closure (callable,可选) – 重新评估模型并返回损失的闭包。 + +``` +class torch.optim.LBFGS(params, lr=1, max_iter=20, max_eval=None, tolerance_grad=1e-05, tolerance_change=1e-09, history_size=100, line_search_fn=None) +``` + +实现 L-BFGS 算法。 + +> 警告: 这个 optimizer 不支持为每个参数单独设置选项以及不支持参数组(只能有一个) 现在所有参数必须在单个设备上。将来会有所改善。 +> +> 注意: 这是一个内存高度密集的 optimizer(它要求额外的`param_bytes * (history_size + 1)` 个字节)。如果它不适应内存,尝试减小 history size,或者使用不同的算法。 + +参数: + +1. lr (float) – 学习率(默认:1) +2. max_iter (int) – 每个优化步骤的最大迭代次数(默认:20)) +3. max_eval (int) – 每个优化步骤的最大函数评估次数(默认:max * 1.25) +4. tolerance_grad (float) – 一阶最优的终止容忍度(默认:1e-5) +5. tolerance_change (float) – 功能值/参数更改的终止公差(默认:1e-9) +6. history_size (int) – 更新历史记录大小(默认:100) + +``` +step(closure) +``` + +执行单个优化步骤。 + +参数: + +1. closure (callable,可选) – 重新评估模型并返回损失的闭包。 + +``` +class torch.optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)[source] +``` + +实现`RMSprop`算法。 + +由`G. Hinton`在他的[课程中提出](http://www.cs.toronto.edu/~tijmen/csc321/slides/lecture_slides_lec6.pdf). + +中心版本首次出现在[Generating Sequences With Recurrent Neural Networks](https://arxiv.org/pdf/1308.0850v5.pdf). + +参数: + +1. params (iterable) – 用于优化的可以迭代参数或定义参数组 +2. lr (float, 可选) – 学习率(默认:1e-2) +3. momentum (float, 可选) – 动量因子(默认:0) +4. alpha (float, 可选) – 平滑常数(默认:0.99) +5. eps (float, 可选) – 增加分母的数值以提高数值稳定性(默认:1e-8) +6. centered (bool, 可选) – 如果为 True,计算中心化的 RMSProp,通过其方差的估计来对梯度进行归一化 +7. weight_decay (float, 可选) – 权重衰减(L2 范数)(默认: 0) + +``` +step(closure) +``` + +执行单个优化步骤。 + +参数: + +1. closure (callable,可选) – 重新评估模型并返回损失的闭包。 + +``` +class torch.optim.Rprop(params, lr=0.01, etas=(0.5, 1.2), step_sizes=(1e-06, 50)) +``` + +实现弹性反向传播算法。 + +参数: + +1. params (iterable) – 用于优化的可以迭代参数或定义参数组 +2. lr (float, 可选) – 学习率(默认:1e-2) +3. etas (Tuple[float, float], 可选) – 一对(etaminus,etaplis), 它们是乘数增加和减少因子(默认:0.5,1.2) +4. step_sizes (Tuple[float, float], 可选) – 允许的一对最小和最大的步长(默认:1e-6,50) + +``` +step(closure) +``` + +执行单个优化步骤。 + +参数: + +1. closure (callable,可选) – 重新评估模型并返回损失的闭包。 + +``` +class torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False) +``` + +实现随机梯度下降算法(momentum 可选)。 + +Nesterov 动量基于[On the importance of initialization and momentum in deep learning](http://www.cs.toronto.edu/~hinton/absps/momentum.pdf)中的公式. + +参数: + +1. params (iterable) – 用于优化的可以迭代参数或定义参数组 +2. lr (float) – 学习率 +3. momentum (float, 可选) – 动量因子(默认:0) +4. weight_decay (float, 可选) – 权重衰减(L2 范数)(默认:0) +5. dampening (float, 可选) – 动量的抑制因子(默认:0) +6. nesterov (bool, 可选) – 使用 Nesterov 动量(默认:False) + +例子: + +``` +>>> optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9) +>>> optimizer.zero_grad() +>>> loss_fn(model(input), target).backward() +>>> optimizer.step() +``` + +提示: + +> 带有动量/Nesterov 的 SGD 的实现稍微不同于 Sutskever 等人以及其他框架中的实现。 考虑到 Momentum 的具体情况,更新可以写成 v=ρ∗v+g p=p−lr∗v 其中,p、g、v 和ρ分别是参数、梯度、速度和动量。 这是在对比 Sutskever et. al。和其他框架采用该形式的更新 v=ρ∗v+lr∗g p=p−v Nesterov 版本被类似地修改。 + +``` +step(closure) +``` + +执行单个优化步骤。 + +参数: + +1. closure (callable,可选) – 重新评估模型并返回损失的闭包。 + +#### 如何调整学习率 + +`torch.optim.lr_scheduler` 提供了几种方法来根据 epoches 的数量调整学习率。 `torch.optim.lr_scheduler.ReduceLROnPlateau`允许基于一些验证测量来降低动态学习速率。 + +``` +class torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1) +``` + +将每个参数组的学习速率设置为初始的 lr 乘以一个给定的函数。当 last_epoch=-1 时,将初始 lr 设置为 lr。 + +参数: + +1. optimizer (Optimizer) – 包装的优化器。 +2. lr_lambda (function or list) – 一个函数来计算一个乘法因子给定一个整数参数的`epoch`,或列表等功能,为每个组`optimizer.param_groups`。 +3. last_epoch (int) – 最后一个时期的索引。默认: -1. + +例子: + +``` +>>> # Assuming optimizer has two groups. +>>> lambda1 = lambda epoch: epoch // 30 +>>> lambda2 = lambda epoch: 0.95 ** epoch +>>> scheduler = LambdaLR(optimizer, lr_lambda=[lambda1, lambda2]) +>>> for epoch in range(100): +>>> scheduler.step() +>>> train(...) +>>> validate(...) +``` + +``` +class torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1) +``` + +将每个参数组的学习速率设置为每个 step_size 时间段由 gamma 衰减的初始 lr。当 last_epoch = -1 时,将初始 lr 设置为 lr。 + +1. optimizer (Optimizer) – 包装的优化器。 +2. step_size (int) – 学习率衰减期。 +3. gamma (float) – 学习率衰减的乘积因子。默认值:-0.1。 +4. last_epoch (int) – 最后一个时代的指数。默认值:1。 + +例子: + +``` +>>> # Assuming optimizer uses lr = 0.5 for all groups +>>> # lr = 0.05 if epoch < 30 +>>> # lr = 0.005 if 30 <= epoch < 60 +>>> # lr = 0.0005 if 60 <= epoch < 90 +>>> # ... +>>> scheduler = StepLR(optimizer, step_size=30, gamma=0.1) +>>> for epoch in range(100): +>>> scheduler.step() +>>> train(...) +>>> validate(...) +``` + +``` +class torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1) +``` + +一旦时间的数量达到一个里程碑,则将每个参数组的学习率设置为伽玛衰减的初始值。当 last_epoch=-1 时,将初始 lr 设置为 lr。 + +参数: + +1. optimizer (Optimizer) – 包装的优化器。 +2. milestones (list) – 时期指标的列表。必须增加。 +3. gamma (float) – 学习率衰减的乘积因子。 默认: -0.1. +4. last_epoch (int) – 最后一个时代的指数。 默认: -1. + +例子: + +``` +>>> # Assuming optimizer uses lr = 0.5 for all groups +>>> # lr = 0.05 if epoch < 30 +>>> # lr = 0.005 if 30 <= epoch < 80 +>>> # lr = 0.0005 if epoch >= 80 +>>> scheduler = MultiStepLR(optimizer, milestones=[30,80], gamma=0.1) +>>> for epoch in range(100): +>>> scheduler.step() +>>> train(...) +>>> validate(...) +``` + +``` +class torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch=-1) +``` + +将每个参数组的学习速率设置为每一个时代的初始 lr 衰减。当 last_epoch=-1 时,将初始 lr 设置为 lr。 + +1. optimizer (Optimizer) – 包装的优化器。 +2. gamma (float) – 学习率衰减的乘积因子。 +3. last_epoch (int) – 最后一个指数。默认: -1. + +``` +class torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=10, verbose=False, threshold=0.0001, threshold_mode='rel', cooldown=0, min_lr=0, eps=1e-08) +``` + +当指标停止改善时,降低学习率。当学习停滞不前时,模型往往会使学习速度降低 2-10 倍。这个调度程序读取一个指标量,如果没有提高 epochs 的数量,学习率就会降低。 + +1. optimizer (Optimizer) – 包装的优化器。 +2. mode (str) – min, max 中的一个. 在最小模式下,当监测量停止下降时,lr 将减少; 在最大模式下,当监控量停止增加时,会减少。默认值:'min'。 +3. factor (float) – 使学习率降低的因素。 new_lr = lr * factor. 默认: 0.1. +4. patience (int) –epochs 没有改善后,学习率将降低。 默认: 10. +5. verbose (bool) – 如果为 True,则会向每个更新的 stdout 打印一条消息。 默认: False. +6. threshold (float) – 测量新的最优值的阈值,只关注显着变化。 默认: 1e-4. +7. threshold*mode (str) – rel, abs 中的一个. 在 rel 模型, dynamic_threshold = best* ( 1 + threshold ) in ‘max’ mode or best_ ( 1 - threshold ) 在最小模型. 在绝对值模型中, dynamic_threshold = best + threshold 在最大模式或最佳阈值最小模式. 默认: ‘rel’. +8. cooldown (int) – 在 lr 减少后恢复正常运行之前等待的时期数。默认的: 0. +9. min_lr (float or list) – 标量或标量的列表。对所有的组群或每组的学习速率的一个较低的限制。 默认: 0. +10. eps (float) – 适用于 lr 的最小衰减。如果新旧 lr 之间的差异小于 eps,则更新将被忽略。默认: 1e-8. + +``` +>>> optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9) +>>> scheduler = torch.optim.ReduceLROnPlateau(optimizer, 'min') +>>> for epoch in range(10): +>>> train(...) +>>> val_loss = validate(...) +>>> # Note that step should be called after validate() +>>> scheduler.step(val_loss) +``` + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/2.md b/docs/0.4/2.md new file mode 100644 index 00000000..83255fa3 --- /dev/null +++ b/docs/0.4/2.md @@ -0,0 +1,113 @@ +# 广播语义 + +* [一般语义](#general-semantics) +* [直接语义](#in-place-semantics) +* [向后兼容性](#backwards-compatibility) + +许多`pytorch`操作都支持[NumPy 广播语义](https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#module-numpy.doc.broadcasting) + +简而言之,如果`Pytorch`操作支持广播,则其张量参数可以自动扩展为相同大小(不需要复制数据)。 + +### 一般语义 + +如果`pytorch`张量满足以下条件,那么就可以广播: + +* 每个张量至少有一个维度。 +* 在遍历维度大小时, 从尾部维度开始遍历, 并且二者维度必须相等, 它们其中一个要么是`1`要么不存在. + +例如: + +``` +>>> x=torch.FloatTensor(5,7,3) +>>> y=torch.FloatTensor(5,7,3) +# 相同形状的质量可以被广播(上述规则总是成立的) + +>>> x=torch.FloatTensor() +>>> y=torch.FloatTensor(2,2) +# can line up trailing dimensions +>>> x=torch.FloatTensor(5,3,4,1) +>>> y=torch.FloatTensor( 3,1,1) +# x 和 y 可以被广播 +# 1st trailing dimension: both have size 1 +# 2nd trailing dimension: y has size 1 +# 3rd trailing dimension: x size == y size +# 但是: +>>> x=torch.FloatTensor(5,2,4,1) +>>> y=torch.FloatTensor( 3,1,1) +# x 和 y 不能被广播,因为在`3rd`中 +# x and y are not broadcastable, because in the 3rd trailing dimension 2 != 3 +``` + +如果`x`和`y`可以被广播,得到的张量大小的计算方法如下: + +* 如果维数`x`和`y`不相等,在维度较少的张量的维度前加上`1`使它们相等的长度。 +* 然后,对于每个维度的大小,生成维度的大小是`x`和`y`的最大值。 + +例如: + +``` +# 可以排列尾部维度,使阅读更容易 +>>> x=torch.FloatTensor(5,1,4,1) +>>> y=torch.FloatTensor( 3,1,1) +>>> (x+y).size() +torch.Size([5, 3, 4, 1]) + +# 但不是必要的: +>>> x=torch.FloatTensor(1) +>>> y=torch.FloatTensor(3,1,7) +>>> (x+y).size() +torch.Size([3, 1, 7]) + +>>> x=torch.FloatTensor(5,2,4,1) +>>> y=torch.FloatTensor(3,1,1) +>>> (x+y).size() +RuntimeError: The size of tensor a (2) must match the size of tensor b (3) at non-singleton dimension 1 +``` + +### 直接语义(In-place 语义) + +一个复杂的问题是`in-place`操作不允许`in-place`张量像广播那样改变形状。 + +例如: + +``` +>>> x=torch.FloatTensor(5,3,4,1) +>>> y=torch.FloatTensor(3,1,1) +>>> (x.add_(y)).size() +torch.Size([5, 3, 4, 1]) + +# but: +>>> x=torch.FloatTensor(1,3,1) +>>> y=torch.FloatTensor(3,1,7) +>>> (x.add_(y)).size() +RuntimeError: The expanded size of the tensor (1) must match the existing size (7) at non-singleton dimension 2. +``` + +### 向后兼容性 + +以前版本的`PyTorch`只要张量中的元素数量是相等的, 便允许某些点状`pointwise`函数在不同的形状的张量上执行, 其中点状操作是通过将每个张量视为`1`维来执行。`PyTorch`现在支持广播语义并且不推荐使用点状函数操作向量,并且张量不支持广播但具有相同数量的元素将生成一个`Python`警告。 + +注意,广播的引入可能会导致向后不兼容,因为两个张量的形状不同,但是可以被广播且具有相同数量的元素。 + +例如: + +``` +>>> torch.add(torch.ones(4,1), torch.randn(4)) +``` + +事先生成一个`torch.Size([4,1])`的张量,然后再提供一个`torch.Size([4,4])`的张量。为了帮助识别你代码中可能存在的由引入广播语义的向后不兼容情况, 您可以设置`torch.utils.backcompat.broadcast_warning.enabled`为`True`,这种情况下会生成一个`python`警告。 + +例如: + +``` +>>> torch.utils.backcompat.broadcast_warning.enabled=True +>>> torch.add(torch.ones(4,1), torch.ones(4)) +__main__:1: UserWarning: self and other do not have the same shape, but are broadcastable, and have the same number of elements. +Changing behavior in a backwards incompatible manner to broadcasting rather than viewing as 1-dimensional. +``` + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/20.md b/docs/0.4/20.md new file mode 100644 index 00000000..7f6be4b9 --- /dev/null +++ b/docs/0.4/20.md @@ -0,0 +1,236 @@ +# torch.nn.init + +``` +torch.nn.init.calculate_gain(nonlinearity,param=None) +``` + +返回给定非线性函数的推荐增益值。值如下: + +| 非线性 | 获得 | +| --- | --- | +| linear | 1 | +| conv{1,2,3}d | 1 | +| sigmoid | 1 | +| tanh | 5/3 | +| relu | sqrt(2) | +| leaky_relu | sqrt(2/(1+negative_slope^2)) | + +参数: + +1. nonlinearity - 非线性函数(nn.functional 名称) +2. param - 非线性函数的可选参数 + +例子: + +``` +gain = nn.init.gain('leaky_relu') +``` + +``` +torch.nn.init.uniform(tensor, a=0, b=1)[source] +``` + +从均匀分布 U(a, b)中生成值,填充输入的张量或变量 + +参数: + +1. tensor - n 维的 torch.Tensor 或者 autograd.Variable +2. a - 均匀分布的下限 +3. b - 均匀分布的上限 + +例子: + +``` +w = torch.Tensor(3, 5) +print nn.init.uniform(w) +# 输出: +# 0.0470 0.9742 0.9736 0.7976 0.1219 +# 0.9390 0.7575 0.9370 0.4786 0.8396 +# 0.1849 0.5384 0.0625 0.3719 0.1739 +# [torch.FloatTensor of size 3x5] +``` + +``` +torch.nn.init.normal(tensor, mean=0, std=1) +``` + +从给定均值和标准差的正态分布 N(mean, std)中生成值,填充输入的张量或变量 + +参数: + +1. tensor – n 维的 torch.Tensor 或者 autograd.Variable +2. mean – 正态分布的平均值 +3. std – 正态分布的标准偏差 + +例子: + +``` +w = torch.Tensor(3, 5) +print torch.nn.init.normal(w) +``` + +``` +torch.nn.init.constant(tensor, val) +``` + +使用值 val 填充输入 Tensor 或 Variable 。 + +参数: + +1. tensor – n 维的 torch.Tensor 或 autograd.Variable +2. val – 填充张量的值 + +例子: + +``` +w = torch.Tensor(3, 5) +print torch.nn.init.constant(w) +``` + +``` +torch.nn.init.eye(tensor) +``` + +用单位矩阵来填充 2 维输入张量或变量。在线性层尽可能多的保存输入特性。 + +参数: + +1. tensor – 2 维的 torch.Tensor 或 autograd.Variable + +例子: + +``` +w = torch.Tensor(3, 5) +print torch.nn.init.eye(w) +``` + +``` +torch.nn.init.dirac(tensor) +``` + +用`Dirac delta`函数来填充{3, 4, 5}维输入张量或变量。在卷积层尽可能多的保存输入通道特性。 + +参数: + +1. tensor – {3, 4, 5}维的 torch.Tensor 或 autograd.Variable + +例子: + +``` +w = torch.Tensor(3, 16, 5, 5) +print torch.nn.init.dirac(w) +``` + +``` +torch.nn.init.xavier_uniform(tensor, gain=1) +``` + +根据 Glorot, X.和 Bengio, Y.在"理解难度训练深前馈神经网络"中描述的方法,使用均匀分布,填充张量或变量。结果张量中的值采样自 U(-a, a),其中 a= gain _sqrt( 2/(fan_in + fan_out))_sqrt(3). 该方法也被称为`glorot`的初始化。 + +参数: + +1. tensor – n 维的 torch.Tensor 或 autograd.Variable +2. gain - 可选的缩放因子 + +例子: + +``` +w = torch.Tensor(3, 5) +print torch.nn.init.xavier_uniform(w, gain=nn.init.calculate_gain('relu')) +``` + +``` +torch.nn.init.xavier_normal(tensor, gain=1) +``` + +根据 Glorot, X.和 Bengio, Y. 于 2010 年在"理解难度训练深前馈神经网络"中描述的方法,采用正态分布,填充张量或变量。结果张量中的值采样自均值为 0,标准差为 gain * sqrt(2/(fan_in + fan_out))的正态分布。该方法也被称为`glorot`的初始化。 + +参数: + +1. tensor – n 维的 torch.Tensor 或 autograd.Variable +2. gain - 可选的缩放因子 + +例子: + +``` +>>> w = torch.Tensor(3, 5) +>>> nn.init.xavier_normal(w) +``` + +``` +torch.nn.init.kaiming_uniform(tensor, a=0, mode='fan_in') +``` + +根据 He, K 等人于 2015 年在"深入研究了超越人类水平的性能:整流器在 ImageNet 分类"中描述的方法,采用正态分布,填充张量或变量。结果张量中的值采样自 U(-bound, bound),其中 bound = sqrt(2/((1 + a^2) *fan_in))* sqrt(3)。该方法也被称为`He`的初始化。 + +参数: + +1. tensor – n 维的 torch.Tensor 或 autograd.Variable +2. a -此层后使用的整流器的负斜率(默认为 ReLU 为 0) +3. mode - "fan_in"(默认)或"fan_out"。"fan_in"保留正向传播时权值方差的量级,"fan_out"保留反向传播时的量级。 + +例子: + +``` +w = torch.Tensor(3, 5) +torch.nn.init.kaiming_uniform(w, mode='fan_in') +``` + +``` +torch.nn.init.kaiming_normal(tensor, a=0, mode='fan_in') +``` + +根据 He, K 等人 2015 年在"深入研究了超越人类水平的性能:整流器在 ImageNet 分类"中描述的方法,采用正态分布,填充张量或变量。结果张量中的值采样自均值为 0,标准差为 sqrt(2/((1 + a^2) * fan_in))的正态分布。该方法也被称为`He`的初始化。 + +参数: + +1. tensor – n 维的 torch.Tensor 或 autograd.Variable +2. a -此层后使用的整流器的负斜率(默认为 ReLU 为 0) +3. mode - "fan_in"(默认)或"fan_out"。"fan_in"保留正向传播时权值方差的量级,"fan_out"保留反向传播时的量级。 + +``` +w = torch.Tensor(3, 5) +print torch.nn.init.kaiming_normal(w, mode='fan_out') +``` + +``` +torch.nn.init.orthogonal(tensor, gain=1) +``` + +使用(半)正交矩阵填充输入张量或变量,参考 Saxe,A.等人 2013 年"深深度线性神经网络学习的非线性动力学的精确解"。输入张量必须至少是 2 维的,对于更高维度的张量,超出的维度会被展平。 + +参数: + +1. tensor – n 维的 torch.Tensor 或 autograd.Variable,其中 n>=2 +2. gain - 可选缩放因子 + +例子: + +``` +w = torch.Tensor(3, 5) +print torch.nn.init.orthogonal(w) +``` + +``` +torch.nn.init.sparse(tensor, sparsity, std=0.01) +``` + +将二维输入张量或变为稀疏矩阵的非零元素,其中非零元素根据一个均值为 0,标准差为 std 的正态分布生成。如"深度学习通过 Hessian 免费优化"- Martens,J.(2010)。 + +参数: + +1. tensor – n 维的 torch.Tensor 或 autograd.Variable +2. sparsity - 每列中需要被设置成零的元素比例 +3. std - 用于生成的正态分布的标准差 +4. non-zero values (the) – 例子: + +``` +w = torch.Tensor(3, 5) +print torch.nn.init.sparse(w, sparsity=0.1) +``` + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/21.md b/docs/0.4/21.md new file mode 100644 index 00000000..995feb36 --- /dev/null +++ b/docs/0.4/21.md @@ -0,0 +1,7 @@ +# torch.distributions + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/22.md b/docs/0.4/22.md new file mode 100644 index 00000000..87a8bedd --- /dev/null +++ b/docs/0.4/22.md @@ -0,0 +1,71 @@ +# Multiprocessing 包 - torch.multiprocessing + +* [战略管理](https://ptorch.com/docs/1/torch-multiprocessing#strategy-management) +* [共享 CUDA 张量](https://ptorch.com/docs/1/torch-multiprocessing#sharing-cuda-tensors) +* [共享策略](https://ptorch.com/docs/1/torch-multiprocessing#sharing-strategies) + 1. [文件描述符 - file_descriptor](https://ptorch.com/docs/1/torch-multiprocessing#file-descriptor-file-descriptor) + 2. [文件系统 - file_system](https://ptorch.com/docs/1/torch-multiprocessing#file-system-file-system) + +* * * + +`torch.multiprocessing`是本机`multiprocessing`模块的封装。封装了`multiprocessing`模块。它注册自定义的`reducer`,它使用共享内存为不同进程中的相同数据提供视图共享。一旦张量/存储被移动到`shared_memory`(参见[share*memory*()](http://pytorch.org/docs/master/tensors.html#torch.Tensor.share_memory_)),就可以将其发送到其他进程而不进行其它任何操作。 + +这个 API 与原始模块 100%兼容,将`import multiprocessing`改为`import torch.multiprocessing`就可以将所有张量通过**队列发送**或**通过其他机制共享转移**到共享内存中 + +由于 API 的相似性,我们没有记录这个软件包的大部分内容,我们建议您参考`Python multiprocessing`原始模块的文档。 + +> **警告:** 如果主进程突然退出(例如因为传入的信号),Python `multiprocessing`有时无法清理其子进程。这是一个已知的警告,所以如果您在中断解释器后看到任何资源泄漏,这可能意味着这刚刚发生在您身上。 + +## 战略管理 + +``` +torch.multiprocessing.get_all_sharing_strategies() +``` + +返回一组当前系统支持的共享策略。 + +``` +torch.multiprocessing.get_sharing_strategy() +``` + +返回共享 CPU 张量的当前策略 + +``` +torch.multiprocessing.set_sharing_strategy(new_strategy) +``` + +设置共享 CPU 张量的策略 + +参数: new_strategy(str)- 所选策略的名称。应当是上面`get_all_sharing_strategies()`中系统支持的共享策略之一。 + +## 共享 CUDA 张量 + +只支持在 Python 3 中使用`spawn`或`forkserver`启动方法才支持在进程之间共享 CUDA 张量。 + +Python2 中的`multiprocessing`只能使用`fork`创建子进程,并且不支持 CUDA 运行时。 + +> **警告:** CUDA API 要求导出到其他进程的分配一直保持有效,只要它们被使用。您应该小心,确保您共享的 CUDA 张力不要超出范围,只要有必要。这不应该是共享模型参数的问题,但传递其他类型的数据应该小心。请注意,此限制不适用于共享 CPU 内存。 + +## 共享策略 + +本节简要概述了不同的共享策略如何工作。请注意,它只适用于 CPU 张量 - CUDA 张量将始终使用 CUDA API,因为它们是唯一的共享方式。 + +### 文件描述符 -`file_descripor` + +> **注意:** 这是默认策略(除了不支持的 MacOS 和 OS X)。 + +此策略将使用文件描述符作为共享内存句柄。当存储器被移动到共享存储器时,每当存储器被移动到共享内存中时,从`shm_open`对象获取的文件描述符被缓存,并且当它被发送到其他进程时,文件描述符也将被传送(例如通过 UNIX 套接字)。接收器还将缓存文件描述符并且`mmap`它,以获得对存储数据的共享视图。 + +请注意,如果要共享很多张量,则此策略将保留大量文件描述符需要很多时间才能打开。如果您的系统对打开的文件描述符数量有限制,并且无法提高,你应该使用`file_system`策略。 + +### 文件系统 -file_system + +该策略将使用给定的文件名`shm_open`来标识共享内存区域。这具有不需要实现缓存从其获得的文件描述符的优点,但是同时容易发生共享内存泄漏。该文件创建后不能被删除,因为其他进程需要访问它以打开其视图。如果进程死机或死机,并且不调用存储析构函数,则文件将保留在系统中。这是非常严重的,因为它们在系统重新启动之前不断使用内存,或者手动释放它们。 + +为了解决共享内存文件泄漏的问题,`torch.multiprocessing`将产生一个守护程序`torch_shm_manager`,它将自己与当前进程组隔离,并且将跟踪所有共享内存分配。一旦连接到它的所有进程退出,它将等待一会儿,以确保不会有新的连接,并且将遍历该组分配的所有共享内存文件。如果发现它们中的任何一个仍然存在,就释放掉它。我们已经测试了这种方法,并且证明对于各种故障是稳健的。不过,如果您的系统具有足够的限制,并且支持`file_descriptor`策略,我们不建议切换到该策略。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/23.md b/docs/0.4/23.md new file mode 100644 index 00000000..d0a6049a --- /dev/null +++ b/docs/0.4/23.md @@ -0,0 +1,260 @@ +# 分布式通讯包 - torch.distributed + +* [基本](#basics) +* [初始化](#initialization) +* [TCP 初始化](#tcp-initialization) +* [共享文件系统初始化](#shared-file-system-initialization) +* [环境变量初始化](#environment-variable-initialization) +* [组](#groups) +* [点对点通信](#point-to-point-communication) +* [集体功能](#collective-functions) + +* * * + +`torch.distributed`提供了一种类似 MPI 的接口,用于跨多机器网络交换张量数据。它支持几种不同的后端和初始化方法。 + +目前,`torch.distributed`支持三个后端,每个后端具有不同的功能。下表显示哪些功能可用于 CPU / CUDA 张量。只有当用于构建 PyTorch 的实现支持它时,MPI 才支持 cuda。 + +| 后端 | `tcp` | `gloo` | `mpi` | +| --- | --- | --- | --- | +| 设备 | 中央处理器 | GPU | 中央处理器 | GPU | 中央处理器 | GPU | +| --- | --- | --- | --- | --- | --- | --- | +| 发送 | ✓ | ✘ | ✘ | ✘ | ✓ | ? | +| 的 recv | ✓ | ✘ | ✘ | ✘ | ✓ | ? | +| 广播 | ✓ | ✘ | ✓ | ✓ | ✓ | ? | +| all_reduce | ✓ | ✘ | ✓ | ✓ | ✓ | ? | +| 减少 | ✓ | ✘ | ✘ | ✘ | ✓ | ? | +| all_gather | ✓ | ✘ | ✘ | ✘ | ✓ | ? | +| 收集 | ✓ | ✘ | ✘ | ✘ | ✓ | ? | +| 分散 | ✓ | ✘ | ✘ | ✘ | ✓ | ? | +| 屏障 | ✓ | ✘ | ✓ | ✓ | ✓ | ? | + +## 基本 + +所述`torch.distributed`包提供跨在一个或多个计算机上运行的几个计算节点对多进程并行 PyTorch 支持与通信原语。该类[torch.nn.parallel.DistributedDataParallel()](http://pytorch.org/docs/master/nn.html#torch.nn.parallel.DistributedDataParallel)基于此功能,提供同步分布式培训作为围绕任何 PyTorch 模型的包装器。这不同于所提供的类型的并行的 [:模块:`torch.multiprocessing`](http://pytorch.org/docs/master/distributed.html#id1)和[torch.nn.DataParallel()](http://pytorch.org/docs/master/nn.html#torch.nn.DataParallel)在它支持多个网络连接的机器和在用户必须明确地启动主训练脚本的单独副本为每个进程。 + +在单机同步情况下,torch.distributed 或 [torch.nn.parallel.DistributedDataParallel()](http://pytorch.org/docs/master/nn.html#torch.nn.parallel.DistributedDataParallel)wrapper 可能仍然具有优于数据并行性的其他方法的优点,包括[torch.nn.DataParallel()](http://pytorch.org/docs/master/nn.html#torch.nn.DataParallel): + +* 每个进程维护自己的优化器,并且每次迭代执行完整的优化步骤。虽然这可能是冗余的,因为梯度已经被聚集在一起,并且在进程之间进行平均,因此对于每个进程是相同的,这意味着不需要参数广播步骤,从而减少在节点之间传送张量所花费的时间。 +* 每个进程都包含一个独立的 Python 解释器,消除了来自单个 Python 进程中驱动多个执行线程,模型副本或 GPU 的额外的解释器开销和“GIL-thrashing”。这对于大量使用 Python 运行时的模型尤其重要,包括具有循环层或多个小组件的模型。 + +## 初始化 + +[torch.distributed.init_process_group()](http://pytorch.org/docs/master/distributed.html#torch.distributed.init_process_group) 在调用任何其他方法之前,需要使用该函数初始化该包。这将阻止所有进程加入。 + +### torch.distributed.init_process_group(backend, init_method='env://', kwargs) + +初始化分布式包。 + +参数: + +* backend (str) - 要使用的后端的名称。根据构建时配置有效值包括:`tcp`,`mpi`和`gloo`。 +* init*method([_str*]([https://docs.python.org/2/library/functions.html#str)_,__ 可选 _)](https://docs.python.org/2/library/functions.html#str)_,__ 可选 _)) - 指定如何初始化包的 URL。 +* world*size([_int*]([https://docs.python.org/2/library/functions.html#int)_,__ 可选 _)](https://docs.python.org/2/library/functions.html#int)_,__ 可选 _)) - 参与作业的进程数。 +* rank([*int*](https://docs.python.org/2/library/functions.html#int)*,__ 可选*) - 当前进程的等级。 +* group*name([_str*]([https://docs.python.org/2/library/functions.html#str)_,__ 可选 _)](https://docs.python.org/2/library/functions.html#str)_,__ 可选 _)) - 组名称。请参阅 init 方法的说明。 + +### torch.distributed.get_rank() + +返回当前进程的排名。 + +Rank 是分配给分布式组中每个进程的唯一标识符。它们总是连续的整数,范围从 0 到`world_size`。 + +### torch.distributed.get_world_size() + +返回分布式组中的进程数。 + +* * * + +目前支持三种初始化方式: + +### TCP 初始化 + +有两种方法来初始化使用 TCP,这两种方法都需要可以从所有进程访问的网络地址和所需的 world_size。第一种方法需要指定属于等级 0 进程的地址。第一种初始化方式要求所有进程都具有手动指定的等级。 + +或者,地址必须是有效的 IP 多播地址,在这种情况下可以自动分配等级。组播初始化还支持一个 group_name 参数,只要使用不同的组名,就可以为多个作业使用相同的地址。 + +``` +import torch.distributed as dist + +# Use address of one of the machines +dist.init_process_group(init_method='tcp://10.1.1.20:23456', rank=args.rank, world_size=4) + +# or a multicast address - rank will be assigned automatically if unspecified +dist.init_process_group(init_method='tcp://[ff15:1e18:5d4c:4cf0:d02d:b659:53ba:b0a7]:23456', + world_size=4) +``` + +### 共享文件系统初始化 + +另一个初始化方法利用一个文件系统,该文件系统可以从组中的所有计算机共享和可见,以及所需的 world_size。该 URL 应 file://以共享文件系统上的不存在的文件(在现有目录中)开头并包含一个路径。此初始化方法也支持一个 group_name 参数,只要使用不同的组名,就可以使用相同的共享文件路径进行多个作业。 + +警告 + +该方法假设文件系统支持使用 fcntl 大多数本地系统进行锁定,NFS 支持它。 + +``` +import torch.distributed as dist + +# Rank will be assigned automatically if unspecified +dist.init_process_group(init_method='file:///mnt/nfs/sharedfile', world_size=4, + group_name=args.group) +``` + +### 环境变量初始化 + +该方法将从环境变量中读取配置,从而可以完全自定义获取信息的方式。要设置的变量是: + +* MASTER_PORT - 必需 必须是排名 0 的机器上的免费端口 +* MASTER_ADDR - 必需(等级 0 除外); 地址 0 级节点 +* WORLD_SIZE - 必需 可以在这里设置,也可以调用 init 函数 +* RANK - 必需 可以在这里设置,也可以调用 init 函数 + +等级为 0 的机器将用于设置所有连接。 + +这是默认方法,这意味着 init_method 不必指定(或可以 env://)。 + +## Groups 组 + +默认集合在默认组(也称为世界)上运行,并要求所有进程进入分布式函数调用。然而,一些工作负载可以从更细粒度的通信中受益。这就是分布式组织发挥作用的地方。[new_group()](http://pytorch.org/docs/master/distributed.html#torch.distributed.new_group)函数可用于创建新组,具有所有进程的任意子集。它返回一个不透明的组句柄,可以作为 group 参数给予所有集合(集合是分布式函数以在某些众所周知的编程模式中交换信息)。 + +### torch.distributed.new_group(_rank = None) + +创建一个新的分布式组。 + +此功能要求主组中的所有进程(即作为分布式作业的一部分的所有进程)都将输入此功能,即使它们不会是组的成员。此外,应在所有进程中以相同的顺序创建组。 + +参数: + +* ranks (list[int]) - 团体成员名单。 返回: 分配组的句柄可以被赋予集体调用。 + +### torch.distributed.send(tensor, dst) + +同步发送张量。 + +参数: + +* 张量([*张量*](http://pytorch.org/docs/master/tensors.html#torch.Tensor)) - 张量发送。 +* dst([*int*](https://docs.python.org/2/library/functions.html#int)) - 目的地排名。 + +### torch.distributed.recv(tensor, src=None) + +同步接收张量。 + +参数: + +* 张量([*Tensor*](http://pytorch.org/docs/master/tensors.html#torch.Tensor)) - 张量填充收到的数据。 +* src([*int*](https://docs.python.org/2/library/functions.html#int)) - 源排名。 + +[isend()](http://pytorch.org/docs/master/distributed.html#torch.distributed.isend)并[irecv()](http://pytorch.org/docs/master/distributed.html#torch.distributed.irecv) 在使用时返回分布式请求对象。一般来说,此对象的类型是未指定的,因为它们不应该手动创建,而是保证支持两种方法: + +* is_completed() - 如果操作完成,返回 True +* wait() - 将阻止进程,直到操作完成。 is_completed()保证在返回 True 后返回 True。 + +### torch.distributed.isend(tensor, dst) + +以异步方式发送张量。 + +参数: + +* 张量([*张量*](http://pytorch.org/docs/master/tensors.html#torch.Tensor)) - 张量发送。 +* dst([*int*](https://docs.python.org/2/library/functions.html#int)) - 目的地排名。 + +返回: + +分布式请求对象。 + +### torch.distributed.irecv(tensor, src) + +以异步方式接收张量。 + +参数: + +* 张量([*Tensor*](http://pytorch.org/docs/master/tensors.html#torch.Tensor)) - 张量填充收到的数据。 +* src([*int*](https://docs.python.org/2/library/functions.html#int)) - 源排名。 + +返回: 分布式请求对象。 + +### torch.distributed.broadcast(tensor, src, group=) + +向整个组播放张量。 + +`tensor` 在参与集体的所有过程中必须具有相同数量的元素。 + +参数: + +* 张量([*Tensor*](http://pytorch.org/docs/master/tensors.html#torch.Tensor)) - 如果`src`是当前进程的等级,则要发送的数据,否则用于保存接收数据的张量。 +* src([*int*](https://docs.python.org/2/library/functions.html#int)) - 源排名。 +* 集团(*可选*) - 集体集团。 + +### torch.distributed.all_reduce(tensor, op=, group=) + +减少所有机器上的张量数据,使得所有机器都得到最终结果。 + +`tensor`在所有进程中,呼叫将是相同的。 + +参数: + +* 张量([*张量*](http://pytorch.org/docs/master/tensors.html#torch.Tensor)) - 集体的输入和输出。该功能就地运行。 +* op(*可选*) - `torch.distributed.reduce_op` 枚举中的一个值。指定用于元素方式减少的操作。 +* 集团(*可选*) - 集体集团。 + +### torch.distributed.reduce(tensor, dst, op=, group=) + +减少所有机器的张量数据。 + +只有排名过程`dst`才会得到最终结果。 + +参数: + +* 张量([*张量*](http://pytorch.org/docs/master/tensors.html#torch.Tensor)) - 集体的输入和输出。该功能就地运行。 +* op(*可选*) - `torch.distributed.reduce_op` 枚举中的一个值。指定用于元素方式减少的操作。 +* 集团(*可选*) - 集体集团。 + +### torch.distributed.all_gather(tensor_list, tensor, group=) + +从列表中收集整个组的张量。 + +参数: + +* tensor*list(*list _*[* [*Tensor*](http://pytorch.org/docs/master/tensors.html#torch.Tensor) *]*) - 输出列表。它应该包含用于集体产出的正确大小的张量。 +* 张量([*Tensor*](http://pytorch.org/docs/master/tensors.html#torch.Tensor)) - 从当前进程广播的张量。 +* 集团(*可选*) - 集体集团。 + +### torch.distributed.gather(tensor, kwargs) + +在单个过程中收集张量列表。 + +参数: + +* 张量([*Tensor*](http://pytorch.org/docs/master/tensors.html#torch.Tensor)) - 输入张量。 +* dst([*int*](https://docs.python.org/2/library/functions.html#int)) - 目的地排名。除了接收数据之外的所有进程都需要。 +* gather*list(*list _*[* [*Tensor*](http://pytorch.org/docs/master/tensors.html#torch.Tensor) *]*) - 用于接收数据的适当大小的张量的列表。仅在接收过程中需要。 +* 集团(*可选*) - 集体集团。 + +### torch.distributed.scatter(tensor, kwargs) + +向组中的所有进程散布张量列表。 + +每个进程将只收到一个张量,并将其数据存储在 `tensor`参数中。 + +参数: + +* 张量([*张量*](http://pytorch.org/docs/master/tensors.html#torch.Tensor)) - 输出张量。 +* src([*int*](https://docs.python.org/2/library/functions.html#int)) - 源排名。除了发送数据之外的所有进程都需要。 +* scatter*list(*list _*[* [*Tensor*](http://pytorch.org/docs/master/tensors.html#torch.Tensor) *]*) - 要分散的张量列表。只有在发送数据的过程中才需要。 +* 集团(*可选*) - 集体集团。 + +### torch.distributed.barrier(group=) + +同步所有进程。 + +这个集体阻止进程,直到整个组进入这个功能。 + +参数: group (可选) - Group of the collective. + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/24.md b/docs/0.4/24.md new file mode 100644 index 00000000..7f1d98a6 --- /dev/null +++ b/docs/0.4/24.md @@ -0,0 +1,35 @@ +# torch.utils.bottleneck + +`torch.utils.bottleneck`是一个可以用作调试程序瓶颈的初始步骤的工具。它汇总了`Python`分析器和`PyTorch`的`autograd`分析器脚本的运行情况。 + +用命令行运行它 + +``` +python -m torch.utils.bottleneck /path/to/source/script.py [args] +``` + +`[args]`是`script.py`中的任意参数,也可以运行如下代码获取更多使用说明。 + +``` +python -m torch.utils.bottleneck -h +``` + +> 警告 由于您的脚本将被分析,请确保它在有限的时间内退出。 +> +> 警告 由于 CUDA 内核的异步特性,在针对 CUDA 代码运行时,cProfile 输出和 CPU 模式 autograd 分析器可能无法显示以正确的顺序显示:报告的 CPU 时间报告启动内核所用的时间量,但不包括时间内核花在 GPU 上执行,除非操作进行同步。在常规 CPU 模式分析器下,进行同步的 Ops 似乎非常昂贵。在这些情况下,时序不正确,CUDA 模式 autograd 分析器可能会有所帮助。 + +* * * + +> 注意 要决定要查看哪个(纯 CPU 模式或 CUDA 模式)autograd 分析器输出,应首先检查脚本是否受 CPU 限制(“CPU 总时间远远超过 CUDA 总时间”)。如果它受到 CPU 限制,查看 CPU 模式 autograd 分析器的结果将有所帮助。另一方面,如果脚本大部分时间都在 GPU 上执行,那么开始在 CUDA 模式 autograd 分析器的输出中查找负责任的 CUDA 操作符是有意义的。 当然,实际情况要复杂得多,根据你正在评估的模型部分,你的脚本可能不会处于这两个极端之一。如果探查器输出没有帮助,你可以尝试寻找的结果 torch.autograd.profiler.emit_nvtx()用 nvprof。但是,请注意 NVTX 的开销非常高,并且经常会产生严重偏斜的时间表。 + +* * * + +> 警告 如果您正在分析 CUDA 代码,那么 bottleneck 运行的第一个分析器(cProfile)将在其时间报告中包含 CUDA 启动时间(CUDA 缓冲区分配成本)。如果您的瓶颈导致代码比 CUDA 启动时间慢得多,这应该没有关系。 + +有关分析器的更复杂用途(如多`GPU`情况下),请参阅[https://docs.python.org/3/library/profile.html](https://docs.python.org/3/library/profile.html) 或使用`torch.autograd.profiler.profile()`获取更多信息。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/25.md b/docs/0.4/25.md new file mode 100644 index 00000000..58722ed2 --- /dev/null +++ b/docs/0.4/25.md @@ -0,0 +1,57 @@ +# torch.utils.checkpoint + +### torch.utils.checkpoint.checkpoint(function, *args) + +`checkpoint`模型或模型的一部分 + +`checkpoint`通过交换计算内存来工作。而不是存储整个计算图的所有中间激活用于向后计算,`checkpoint`不会不保存中间激活部分,而是在反向传递中重新计算它们。它可以应用于模型的任何部分。 + +具体来说,在正向传递中,`function`将以`torch.no_grad()`方式运行 ,即不存储中间激活。相反,正向传递保存输入元组和 `function`参数。在向后计算中,保存的输入变量以及 `function`会被回收,并且正向计算被`function`再次计算 ,现在跟踪中间激活,然后使用这些激活值来计算梯度。 + +> **警告** `checkpoint`在`torch.autograd.grad()`中不起作用,但仅适用于`torch.autograd.backward()`。 +> +> **警告** 如果`function`在向后执行和前向执行都不同,例如由于某个全局变量,`checkpoint`版本将不等同,并且不幸的是无法检测到。 + +参数: + +* function - 描述在模型的正向传递或模型的一部分中运行的内容。它也应该知道如何处理作为元组传递的输入。例如,在 LSTM 中,如果用户通过 ,应正确使用第一个输入作为第二个输入(activation, hidden)functionactivationhidden +* args - 包含输入的元组 function + +返回: attr`function`开`*args` + +返回类型: 运行输出 + +### torch.utils.checkpoint.checkpoint_sequential(functions, segments, *inputs) + +用于`checkpoint` `sequential`模型的辅助函数。 + +`sequential 模型按顺序执行一系列模块/函数(按顺序)。因此,我们可以将这种模型分为不同的部分和`checkpoint`。除最后一个段以外的所有段都将以某种`torch.no_grad()`方式运行 ,即不存储中间活动。将保存每个`checkpoint`段的输入,以便在向后传递中重新运行段。 + +关于`checkpoint`如何工作可以参考[checkpoint()](https://pytorch.org/docs/master/checkpoint.html#torch.utils.checkpoint.checkpoint)。 + +> **警告** `checkpoint`在`torch.autograd.grad()`中不起作用,但仅适用于`torch.autograd.backward()`。 + +参数: + +* functions - A torch.nn.Sequential 或按顺序运行的模块或函数(包括模型)的列表。 +* segments - 要在模型中创建的块数 +* segments - 输入的张量元组 functions + +返回: + +`functions`按顺序运行的输出`*inputs` + +例: + +``` +>>> model = nn.Sequential(...) +>>> input_var = checkpoint_sequential(model, chunks, input_var) +``` + +部分地方存在翻译错误,即将修复 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/26.md b/docs/0.4/26.md new file mode 100644 index 00000000..f3cc161b --- /dev/null +++ b/docs/0.4/26.md @@ -0,0 +1,131 @@ +# torch.utils.cpp_extension + +### torch.utils.cpp_extension.CppExtension(name, sources, *args, **kwargs) + +创建一个`C++`的`setuptools.Extension`。 + +便捷地创建一个`setuptools.Extension`具有最小(但通常是足够)的参数来构建`C++`扩展的方法。 + +所有参数都被转发给`setuptools.Extension`构造函数。 + +例 + +``` +>>> from setuptools import setup +>>> from torch.utils.cpp_extension import BuildExtension, CppExtension +>>> setup( + name='extension', + ext_modules=[ + CppExtension( + name='extension', + sources=['extension.cpp'], + extra_compile_args=['-g'])), + ], + cmdclass={ + 'build_ext': BuildExtension + }) +``` + +### torch.utils.cpp_extension.CUDAExtension(name, sources, *args, **kwargs) + +为`CUDA/C++`创建一个`setuptools.Extension`。 创建一个`setuptools.Extension`用于构建`CUDA/C ++`扩展的最少参数(但通常是足够的)的便捷方法。这里包括`CUDA`路径,库路径和运行库。 所有参数都被转发给`setuptools.Extension`构造函数。 + +例 + +``` +>>> from setuptools import setup +>>> from torch.utils.cpp_extension import BuildExtension, CppExtension +>>> setup( + name='cuda_extension', + ext_modules=[ + CUDAExtension( + name='cuda_extension', + sources=['extension.cpp', 'extension_kernel.cu'], + extra_compile_args={'cxx': ['-g'], + 'nvcc': ['-O2']}) + ], + cmdclass={ + 'build_ext': BuildExtension + }) +``` + +### torch.utils.cpp_extension.BuildExtension(dist,** kw )[source] + +自定义`setuptools`构建扩展。 + +`setuptools.build_ext`子类负责传递所需的最小编译器参数(例如`-std=c++11`)以及混合的`C ++/CUDA`编译(以及一般对`CUDA`文件的支持)。 + +当使用`BuildExtension`时,它将提供一个用于`extra_compile_args`(不是普通列表)的词典,通过语言(`cxx`或`cuda`)映射到参数列表提供给编译器。这样可以在混合编译期间为`C ++`和`CUDA`编译器提供不同的参数。 + +### torch.utils.cpp_extension.load(name, sources, extra_cflags=None, extra_cuda_cflags=None, extra_ldflags=None, extra_include_paths=None, build_directory=None, verbose=False) + +即时加载(JIT)`PyTorch C ++`扩展。 + +为了加载扩展,会创建一个`Ninja`构建文件,该文件用于将指定的源编译为动态库。随后将该库作为模块加载到当前`Python`进程中,并从该函数返回,以备使用。 + +默认情况下,构建文件创建的目录以及编译结果库是`<tmp>/torch_extensions/<name>`,其中`<tmp>`是当前平台上的临时文件夹以及`<name>`为扩展名。这个位置可以通过两种方式被覆盖。首先,如果`TORCH_EXTENSIONS_DIR`设置了环境变量,它将替换`<tmp>/torch_extensions`并将所有扩展编译到此目录的子文件夹中。其次,如果`build_directory`函数设置了参数,它也将覆盖整个路径,即,库将直接编译到该文件夹中。 + +要编译源文件,使用默认的系统编译器(c++),可以通过设置`CXX`环境变量来覆盖它。将其他参数传递给编译过程,`extra_cflags`或者`extra_ldflags`可以提供。例如,要通过优化来编译您的扩展,你可以传递`extra_cflags=['-O3']`,也可以使用 `extra_cflags`传递进一步包含目录。 + +提供了混合编译的`CUDA`支持。只需将`CUDA`源文件(`.cu`或`.cuh`)与其他源一起传递即可。这些文件将被检测,并且使用`nvcc`而不是`C ++`编译器进行编译。包括将`CUDA lib64`目录作为库目录传递并进行`cudart`链接。您可以将其他参数传递给`nvcc extra_cuda_cflags`,就像使用`C ++`的`extra_cflags`一样。使用了各种原始方法来查找`CUDA`安装目录,通常情况下可以正常运行。如果不可以,最好设置`CUDA_HOME`环境变量。 + +* 参数: + * name - 要构建的扩展名。这个必须和`pybind11`模块的名字一样! + * sources - `C++`源文件的相对或绝对路径列表。 + * extra_cflags - 编译器参数的可选列表,用于转发到构建。 + * extra_cuda_cflags - 编译器标记的可选列表,在构建`CUDA`源时转发给`nvcc`。 + * extra_ldflags - 链接器参数的可选列表,用于转发到构建。 + * extra_include_paths - 转发到构建的包含目录的可选列表。 + * build_directory - 可选路径作为构建区域。 + * verbose - 如果为`True`,打开加载步骤的详细记录。 +* 返回: + * 加载`PyTorch`扩展作为`Python`模块。 + +例 + +``` +>>> from torch.utils.cpp_extension import load +>>> module = load( + name='extension', + sources=['extension.cpp', 'extension_kernel.cu'], + extra_cflags=['-O2'], + verbose=True) +``` + +### torch.utils.cpp_extension.include_paths(cuda=False) + +获取构建`C++`或`CUDA`扩展所需的路径。 + +* 参数: `cuda` - 如果为 True,则包含`CUDA`特定的包含路径。 +* 返回: 包含路径字符串的列表。 + +例如: + +``` +from setuptools import setup +from torch.utils.cpp_extension import BuildExtension, CppExtension + +torch.utils.cpp_extension.include_paths(cuda=False) +# ['/usr/local/lib/python3.6/site-packages/torch/lib/include', '/usr/local/lib/python3.6/site-packages/torch/lib/include/TH', '/usr/local/lib/python3.6/site-packages/torch/lib/include/THC'] +``` + +### torch.utils.cpp_extension.check_compiler_abi_compatibility(compiler) + +验证给定的编译器是否与`PyTorch` ABI 兼容。 + +* 参数:compiler(str) - 要检查可执行的编译器文件名(例如 g++),必须在`shell`进程中可执行。 +* 返回:如果编译器(可能)与`PyTorch`ABI 不兼容,则为`False`,否则返回`True`。 + +### torch.utils.cpp_extension.verify_ninja_availability() + +如果可以在[ninja](https://ninja-build.org/)上运行则返回`True`。 + +文档地址:[torch.utils.cpp_extension + +]([https://ptorch.com/docs/8/torch-utils-cpp_extension](https://ptorch.com/docs/8/torch-utils-cpp_extension)) + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/27.md b/docs/0.4/27.md new file mode 100644 index 00000000..cab1b86e --- /dev/null +++ b/docs/0.4/27.md @@ -0,0 +1,126 @@ +## torch.utils.data + +``` +class torch.utils.data.Dataset +``` + +表示数据集的抽象类。 + +所有其他数据集都应该进行子类化。所有子类应该覆盖`__len__`和`__getitem__`,`__len__`提供了数据集的大小,`__getitem__`支持整数索引,范围从 0 到 len(self)。 + +``` +class torch.utils.data.TensorDataset(data_tensor, target_tensor) +``` + +包装数据和目标张量的数据集。 + +通过沿着第一个维度索引两个张量来恢复每个样本。 + +参数: + +1. data_tensor (Tensor) - 包含样本数据 +2. target_tensor (Tensor) - 包含样本目标(标签) + +例子: + +``` +x = torch.linspace(1, 10, 10) # x data (torch tensor) +y = torch.linspace(10, 1, 10) # y data (torch tensor) + +# 先转换成 torch 能识别的 Dataset +torch_dataset = torch.utils.data.TensorDataset(data_tensor=x, target_tensor=y) +``` + +``` +class torch.utils.data.ConcatDataset(datasets) +``` + +连接多个数据集。 + +目的:组合不同的现有数据集,可能是大规模数据集,因为连续操作是随意连接的。 + +* datasets 的参数:要连接的数据集列表 +* datasets 样式:iterable + +``` +class torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, num_workers=0, collate_fn=, pin_memory=False, drop_last=False) +``` + +数据加载器。组合数据集和采样器,并在数据集上提供单进程或多进程迭代器。 + +参数: + +1. dataset (Dataset) – 从中加载数据的数据集。 +2. batch_size (int, optional) – 批训练的数据个数(默认: 1)。 +3. shuffle (bool, optional) – 设置为 True 在每个 epoch 重新排列数据(默认值:False,一般打乱比较好)。 +4. sampler (Sampler, optional) – 定义从数据集中提取样本的策略。如果指定,则忽略 shuffle 参数。 +5. batch_sampler(sampler,可选) - 和 sampler 一样,但一次返回一批索引。与 batch_size,shuffle,sampler 和 drop_last 相互排斥。 +6. num_workers (int, 可选) – 用于数据加载的子进程数。0 表示数据将在主进程中加载(默认值:0) +7. collate_fn (callable, optional) – 合并样本列表以形成小批量。 +8. pin_memory (bool, optional) – 如果为 True,数据加载器在返回前将张量复制到 CUDA 固定内存中。 +9. drop_last (bool, optional) – 如果数据集大小不能被 batch_size 整除,设置为 True 可删除最后一个不完整的批处理。如果设为 False 并且数据集的大小不能被 batch_size 整除,则最后一个 batch 将更小。(默认: False) + +``` +class torch.utils.data.sampler.Sampler(data_source) +``` + +所有采样器的基础类。 + +每个采样器子类必须提供一个`__iter__`方法,提供一种迭代数据集元素的索引的方法,以及返回迭代器长度的`__len__`方法。 + +``` +class torch.utils.data.sampler.SequentialSampler(data_source) +``` + +始终以相同的顺序将样本元素按顺序排列。 + +参数: + +* `data_source (Dataset)` – 采样的数据集。 + +``` +class torch.utils.data.sampler.RandomSampler(data_source) +``` + +样本元素随机排列,并没有替换。 + +参数: - `data_source (Dataset)` – 采样的数据集。 + +``` +class torch.utils.data.sampler.SubsetRandomSampler(indices) +``` + +样本元素从指定的索引列表中随机抽取,并没有替换。 + +参数: - `indices (list)` – 索引的列表 + +``` +class torch.utils.data.sampler.WeightedRandomSampler(weights, num_samples, replacement=True) +``` + +样本元素来自[0,..,len(weights)-1],给定概率(权重)。 + +参数: + +* `weights (list)` – 权重列表。不需要加起来为 1 +* `num_samples (int)` – 要绘制的样本数 + +``` +class torch.utils.data.distributed.DistributedSampler(dataset, num_replicas=None, rank=None) +``` + +将数据加载限制到数据集的子集的采样器。 在`torch.nn.parallel.DistributedDataParallel`中是特别有用的。在这种情况下,每个进程都可以作为 DataLoader 采样器传递一个 DistributedSampler 实例,并加载独占的原始数据集的子集。 + +> 注意 假设数据集的大小不变。 + +参数: + +* dataset - 采样的数据集。 +* num_replicas(可选) - 参与分布式培训的进程数。 +* rank(可选) - num_replicas 中当前进程的等级。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/28.md b/docs/0.4/28.md new file mode 100644 index 00000000..ed87b043 --- /dev/null +++ b/docs/0.4/28.md @@ -0,0 +1,24 @@ +# torch.utils.ffi + +``` +torch.utils.ffi.create_extension(name, headers, sources, verbose=True, with_cuda=False, package=False, relative_to='.', **kwargs) +``` + +创建并配置一个构建 PyTorch 扩展的 cffi.FFI 对象。 + +参数: + +1. **name (str)** – 包名。可以是嵌套的模块如 `.ext.my_lib`。 +2. **headers (str or List[str])** – 仅包含导出函数的头文件列表 +3. **sources (List[str])** – 要编译的源列表。 +4. **verbose (bool, optional)** – 如果设置为`False`,则不打印任何输出(默认值:`True`)。 +5. **with_cuda (bool, optional)** – 设置为`True`表示使用 CUDA 头进行编译(默认值:`False`)。 +6. **package (bool, optional)** – 设置为`True`表示以程序包的模式构建(对于要以 pip 包安装的模块)(默认值:`False`)。 +7. **relative_to (str, optional)** –构建文件的路径。 `package`为`True`时需要。最好使用`__file__`作为参数。 +8. **kwargs** – 传递给 ffi 以声明扩展名的其他参数。详情请参阅[扩展 API 参考](https://docs.python.org/3/distutils/apiref.html#distutils.core.Extension)。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/29.md b/docs/0.4/29.md new file mode 100644 index 00000000..b26a4375 --- /dev/null +++ b/docs/0.4/29.md @@ -0,0 +1,29 @@ +# torch.utils.model_zoo + +``` +torch.utils.model_zoo.load_url(url, model_dir=None) +``` + +在给定 URL 上加载 Torch 序列化对象。 + +如果对象已经存在于`model_dir`中,则将被反序列化并返回。URL 的文件名部分应遵循命名约定`filename-<sha256>.ext`,其中`<sha256>`是文件内容的哈希(SHA256)的前八位或更多位数字。哈希用于确保名称唯一性的并验证文件的内容。 + +`model_dir`的默认值为`$TORCH_HOME/models`,其中`$TORCH_HOME`默认为`~/.torch`。可以使用`$TORCH_MODEL_ZOO`环境变量来覆盖默认目录。 + +参数: + +* url (string) - 要下载对象的 URL +* model_dir (string, 可选) - 保存对象的目录 +* map_location(可选) - 指定如何重映射存储位置的函数或 dict(参见 torch.load) + +例如: + +``` +>>> state_dict = torch.utils.model_zoo.load_url('https://s3.amazonaws.com/pytorch/models/resnet18-5c106cde.pth') +``` + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/3.md b/docs/0.4/3.md new file mode 100644 index 00000000..f8492af5 --- /dev/null +++ b/docs/0.4/3.md @@ -0,0 +1,180 @@ +# CUDA 语义 + +* [异步执行](#asynchronous-execution) + * [CUDA 流](#cuda-streams) +* [内存管理](#memory-management) +* [最佳实践](#best-practices) + * [设备无关代码](#device-agnostic-code) + * [使用固定的内存缓冲区](#use-pinned-memory-buffers) + * [使用 nn.DataParallel 替代 multiprocessing](#use-nn-dataparallel-instead-of-multiprocessing) + +### 3.1 设备分配 + +`torch.cuda` 用于设置和运行 `CUDA` 操作。它会跟踪当前选定的 GPU,并且您分配的所有 CUDA 张量将默认在该设备上创建。所选设备可以使用 `torch.cuda.device` 环境管理器进行更改。 + +一旦分配了张量,您就可以对其执行操作而必在意所选的设备如何,并且结果将总是与张量一起放置在相同的设备上。 + +默认的情况下不允许进行交叉 GPU 操作,除了 `copy_()` 和其他具有类似复制功能的方法(如 `to()` 和 `cuda()` )之外。除非启用端到端的存储器访问,否则任何尝试将张量分配到不同设备上的操作都会引发错误。 + +下面可以用一个小例子来展示: + +``` +cuda = torch.device("cuda") # 默认为 CUDA 设备 +cuda0 = torch.device("cuda:0") +cuda2 = torch.device("cuda:2") # GPU 2 + +# x, y 的设备是 device(type='cuda', index=0) +x = torch.tensor([1., 2.], device=cuda0) +y = torch.tensor([1., 2.], cuda()) + +# 在 GPU 1 上分配张量 +with torch.cuda.device(1): + # GPU 1 + a = torch.tensor([1., 2.], device=cuda) + + # 从 CPU 传递到 GPU 1 + # a、b 所在的设备都是 device(type='cuda', index=1) + b = torch.tensor([1., 2.]).cuda() + + # 也可以用 Tensor.to 来传递张量 + # b2 的设备与 a 同 + b2 = torch.tensor([1., 2.]).to(device=cuda) + + # 即使是在环境中,你也可以指定设备 + d = torch.randn(2, device=cuda2) + e = torch.randn(2).to(cuda2) +``` + +### 3.2 异步执行 + +默认情况下,GPU 操作是异步的。当你调用一个使用 GPU 的函数时,这些操作会在特定的设备上排队,但不一定会在稍后执行。这允许我们并行更多的计算,包括 CPU 或其他 GPU 上的操作。 + +一般情况下,异步计算的效果对调用者是不可见的,因为(1)每个设备按照它们排队的顺序执行操作,(2)在 CPU 和 GPU 之间或两个 GPU 之间复制数据时,PyTorch 自动执行必要的同步。因此,计算将按每个操作同步执行的方式进行。 + +您可以通过设置环境变量 `CUDA_LAUNCH_BLOCKING = 1` 来强制进行同步计算。当 GPU 发生错误时,这可能非常方便。 (使用异步执行,只有在实际执行操作之后才会报告此类错误,因此堆栈跟踪不会显示请求的位置。) + +作为一个例外,`copy_()` 等几个函数允许一个显式的异步参数 `async`,它允许调用者在不必要时绕过同步。另一个例外是 CUDA 流,解释如下: + +#### 3.2.1 CUDA 流 + +CUDA 流是属于特定设备的线性执行序列。您通常不需要明确创建一个,默认情况下,每个设备都使用其自己的“默认”流。 + +除非显式的使用同步函数(例如 `synchronize()` 或 `wait_stream()` ),否则每个流内的操作都按照它们创建的顺序进行序列化,但是来自不同流的操作可以以任意相对顺序并发执行。例如,下面的代码是不正确的: + +``` +cuda = torch.device("cuda") +s = torch.cuda.stream() # 在当前流中创建一个新的流 +A = torch.empty((100,100), device = cuda).normal_(0.0, 1.0) + +with torch.cuda.stream(s): + # sum()可能在 normal_()执行完成前就开始执行 + B = torch.sum(A) +``` + +当“当前流”是默认流时,如上所述,PyTorch 在数据移动时自动执行必要的同步。但是,使用非默认流时,用户有责任确保正确的同步。 + +### 3.3 内存管理 + +`PyTorch`使用缓存内存分配器来加速内存分配。这允许在没有设备同步的情况下快速释放内存。但是,由分配器管理的未使用的内存仍将显示为在 `nvidia-smi` 中使用。您可以使用 `memory_allocated()` 和 `max_memory_allocated()` 来监视张量占用的内存,并使用 `memory_cached()` 和 `max_memory_cached()` 来监视由缓存分配器管理的内存。调用 `empty_cache()` 可以从 `PyTorch` 中释放所有未使用的缓存内存,以便其他 GPU 应用程序可以使用这些内存。但是,被张量占用的 GPU 内存不会被释放,因此它不能为 `PyTorch` 增加可用的 GPU 内存量。 + +#### 3.4.1 设备诊断 + +由于 PyTorch 的结构,您可能需要明确编写与设备无关的(CPU 或 GPU)代码;比如创建一个新的张量作为循环神经网络的初始隐藏状态。 + +第一步是确定是否应该使用 GPU。一种常见的模式是使用 Python 的 `argparse` 模块来读入用户参数,并且有一个标志可用于禁用 CUDA,并结合 `is_available()` 使用。在下面的内容中,`args.device` 会生成一个 `torch.device` 对象,该对象可用于将张量移动到 CPU 或 CUDA。 + +``` +import argparse +import torch + +parser = argparse.ArgumentParser(description='PyTorch Example') +parser.add_argument('--disable-cuda', action='stroe_true', help='Disable CUDA') +args = parser.parse_args() +args.device = None +if not args.disable_cuda and torch.cuda.is_available(): + args.device = torch.device('cuda') +else: + args.device = torch.device('cpu') +``` + +现在我们有了 `args.device`,我们可以使用它在所需的设备上创建一个张量。 + +``` +x = torch.empty((8, 42), device = args.device) +net = Network().to(device = args.device) +``` + +这可以在许多情况下用于生成设备不可知代码。以下是使用 `dataloader` 的例子: + +``` +cuda0 = torch.device('cuda:0') # CUDA GPU 0 +for i, x in enumerate(train_loader): + x = x.to(cuda0) +``` + +在系统上使用多个 GPU 时,您可以使用 `CUDA_VISIBLE_DEVICES` 环境标志来管理 PyTorch 可用的 GPU。如上所述,要手动控制在哪个 GPU 上创建张量,最佳做法是使用 `torch.cuda.device` 上下文管理器。 + +``` +print("外部的设备是 0") # 在设备 0 上 +with torch.cuda.device(1): + print("内部的设备是 1") # 设备 1 +print("外部的设备仍是 0") # 设备 0 +``` + +如果您有一个张量,并且想要在同一个设备上创建一个相同类型的张量,那么您可以使用 `torch.Tensor.new_*` 方法(请参阅 `torch.Tensor` )。`torch.Tensor.new_*` 方法保留了设备和张量的其他属性,而前面提到的 `torch.*` 工厂函数(Creation Ops)创建的张量则取决于当前的 GPU 上下文和所传递的属性参数。 + +这是建立模块时推荐的做法,在前向传递期间需要在内部创建新的张量 + +``` +cuda = torch.device("cuda") +x_cpu = torch.empty(2) +y_gpu = torch.empty(2, device = cuda) +x_cpu_long = torch.empty(2, dtype=torch.int64) + +y_cpu = x_cpu.new_full([3,2], fill_value=0.3) +print(y_cpu) + tensor([[ 0.3000, 0.3000], + [ 0.3000, 0.3000], + [ 0.3000, 0.3000]]) +y_gpu = x_gpu.new_full([3,2], fill_value=-5) +print(y_gpu) + tensor([[-5.0000, -5.0000], + [-5.0000, -5.0000], + [-5.0000, -5.0000]], device='cuda:0') + +y_cpu_long = x_cpu_long.new_tensor([[1,2,3]]) +print(y_cpu_long) + tensor([[ 1, 2, 3]]) +``` + +如果要创建与另一个张量相同类型和大小的张量,并将其填充为 1 或 0,则可以使用 `ones_like()` 或 `zeros_like()` 作为便捷的辅助函数(也可以保留 `torch.device` 和 `torch.dtype` 的张量)。 + +``` +x_cpu = torch.empty(2,3) +x_gpu = torch.empty(2,3) + +y_cpu = torch.ones_like(x_cpu) +y_gpu = torch.zeros_like(x_gpu) +``` + +### 3.4 使用固定的内存缓冲区 + +当数据源自固定(页面锁定)内存时,主机在 GPU 上的数据副本运行速度会更快。 CPU Tensors 和存储器暴露了一个 `pin_memory()` 方法,该方法返回对象的一个副本,并将数据放入固定区域。 + +一旦您固定(pin)一个张量或存储器,您就可以使用异步 GPU 副本。只需将一个额外的 `non_blocking = True` 参数传递给 `cuda()` 调用即可。这可以用于计算与数据传输的并行。 + +通过将 `pin_memory = True`传递给其构造函数,可以将 `DataLoader` 返回的批量数据置于固定内存(pin memory)中。 + +### 3.5 使用 nn.DataParallel 代替多线程处理 + +大多数涉及批量输入和多个 GPU 的使用案例应默认使用 `DataParallel`来利用多个 GPU。即使使用 GIL,单个 Python 进程也可以使多个 GPU 饱和。 + +从版本 0.1.9 开始,大量的 GPU(8+)可能未被充分利用。但是,这是一个正在积极开发中的已知问题。像往常一样,测试你的用例。 + +使用 CUDA 模型进行多线程处理(multiprocessing)存在重要的注意事项;除非准确的满足了数据处理的要求,否则很可能您的程序将具有不正确或未定义的行为。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| 风中劲草 | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/30.md b/docs/0.4/30.md new file mode 100644 index 00000000..1fc35b64 --- /dev/null +++ b/docs/0.4/30.md @@ -0,0 +1,142 @@ +# torch.onnx + +`torch.onnx`模块包含将模型导出为`ONNX IR`格式的功能。这些模型可以加载`ONNX`库,然后转换为在其他深度学习框架上运行的模型。 ![ONNX](2017121722000236815.jpg) + +### 示例:从 PyTorch 到 Caffe2 的端到端的 AlexNet + +这是一个简单的脚本,将`torchvision`中定义的预训练的`AlexNet`导出到`ONNX`中。它运行一轮推理,然后将结果跟踪模型保存到`alexnet.proto`: + +``` +from torch.autograd import Variable +import torch.onnx +import torchvision + +dummy_input = Variable(torch.randn(10, 3, 224, 224)).cuda() +model = torchvision.models.alexnet(pretrained=True).cuda() +torch.onnx.export(model, dummy_input, "alexnet.proto", verbose=True) +``` + +保存文件`alexnet.proto`是一个二进制`protobuf`文件,其中包含您导出的模型(在本例中为`AlexNet`)的网络结构和参数。关键字参数`verbose=True`导致导出器打印出一个人类可读的网络表示: + +``` +# All parameters are encoded explicitly as inputs. By convention, +# learned parameters (ala nn.Module.state_dict) are first, and the +# actual inputs are last. +graph(%1 : Float(64, 3, 11, 11) + %2 : Float(64) + # The definition sites of all variables are annotated with type + # information, specifying the type and size of tensors. + # For example, %3 is a 192 x 64 x 5 x 5 tensor of floats. + %3 : Float(192, 64, 5, 5) + %4 : Float(192) + # ---- omitted for brevity ---- + %15 : Float(1000, 4096) + %16 : Float(1000) + %17 : Float(10, 3, 224, 224)) { # the actual input! + # Every statement consists of some output tensors (and their types), + # the operator to be run (with its attributes, e.g., kernels, strides, + # etc.), its input tensors (%17, %1) + %19 : UNKNOWN_TYPE = Conv[kernels=[11, 11], strides=[4, 4], pads=[2, 2, 2, 2], dilations=[1, 1], group=1](%17, %1), uses = [[%20.i0]]; + # UNKNOWN_TYPE: sometimes type information is not known. We hope to eliminate + # all such cases in a later release. + %20 : Float(10, 64, 55, 55) = Add[broadcast=1, axis=1](%19, %2), uses = [%21.i0]; + %21 : Float(10, 64, 55, 55) = Relu(%20), uses = [%22.i0]; + %22 : Float(10, 64, 27, 27) = MaxPool[kernels=[3, 3], pads=[0, 0, 0, 0], dilations=[1, 1], strides=[2, 2]](%21), uses = [%23.i0]; + # ... + # Finally, a network returns some tensors + return (%58); +} +``` + +您也可以使用[onnx](https://github.com/onnx/onnx/)库来验证`protobuf`。你可以`onnx`用`conda`安装: + +``` +conda install -c conda-forge onnx +``` + +然后,你可以运行: + +``` +import onnx + +# Load the ONNX model +model = onnx.load("alexnet.proto") + +# Check that the IR is well formed +onnx.checker.check_model(model) + +# Print a human readable representation of the graph +onnx.helper.printable_graph(model.graph) +``` + +要用`caffe2`运行导出的脚本,您将需要三件事情: + +* 1、您需要安装`Caffe2`。如果您还没有,请 按照安装说明进行操作。 + +* 2、你需要`onnx-caffe2`,一个纯`Python`库,为`ONNX`提供一个`Caffe2`后端。`onnx-caffe2`你可以用`pip`来安装: + +``` +pip install onnx-caffe2 +``` + +安装完成后,您可以使用`Caffe2`的后端: + +``` +# ...continuing from above +import onnx_caffe2.backend as backend +import numpy as np + +rep = backend.prepare(model, device="CUDA:0") # or "CPU" +# For the Caffe2 backend: +# rep.predict_net is the Caffe2 protobuf for the network +# rep.workspace is the Caffe2 workspace for the network +# (see the class onnx_caffe2.backend.Workspace) +outputs = rep.run(np.random.randn(10, 3, 224, 224).astype(np.float32)) +# To run networks with more than one input, pass a tuple +# rather than a single numpy ndarray. +print(outputs[0]) +``` + +未来,其他框架也会有后端。 + +### 限制与不足 + +* `ONNX`出口是一个基于跟踪的出口,这意味着它通过执行一次模型来运行,并导出在此运行期间实际运行的运营商。这意味着如果您的模型是动态的,例如,根据输入数据改变行为,则导出将不准确。类似地,跟踪可能仅对特定输入大小有效(这是我们为什么需要明确输入跟踪的一个原因)。我们建议检查模型跟踪并确保跟踪的运算符看起来合理。 +* `PyTorch`和`Caffe2`通常具有一些数字差异的操作符的实现。根据模型结构的不同,这些差异可能是微不足道的,但是它们也会造成行为上的重大分歧(特别是在未经训练的模型上)。在未来的版本中,我们计划让`Caffe2`直接调用操作员的`Torch`实现,以帮助您平滑在精度很重要的时候处理这些差异,并记录这些差异。 + +### 支持的运算函数 + +支持以下运算符: `add (nonzero alpha not supported)`,`sub (nonzero alpha not supported)`,`mul`,`div`,`cat`,`mm`,`addmm`,`neg`,`tanh`,`sigmoid`,`mean`,`t`,`expand (only when used before a broadcasting ONNX operator; e.g. add)`,`transpose`,`view`,`split`,`squeeze`,`prelu (single weight shared among input channels not supported)`,`threshold (non-zero threshold/non-zero value not supported)`,`leaky_relu`,`glu`,`softmax`,`avg_pool2d (ceil_mode not supported)`,`log_softmax`,`unfold (experimental support with ATen-Caffe2 integration)`,`elu`,`Conv`,`BatchNorm`,`MaxPool1d (ceil_mode not supported)`,`MaxPool2d (ceil_mode not supported)`,`MaxPool3d (ceil_mode not supported)`,`Embedding (no optional arguments supported)`,`RNN`,`ConstantPadNd`,`Dropout`,`FeatureDropout (training mode not supported)`,`Index (constant integer and tuple indices supported)`,`Negate` + +以上设置的操作足以导出以下型号: `AlexNet`,`DCGAN`,`DenseNet`,`Inception (warning: this model is highly sensitive to changes in operator implementation)`,`ResNet`,`SuperResolution`,`VGG`,`word_language_model` + +用于指定运算符定义的界面是高度实验性的,并且没有记录; 有冒险精神的用户应该注意,这些`API`可能会在未来的界面中发生变化。 + +### torch.onnx 功能 + +``` +torch.onnx.export(model, args, f, export_params=True, verbose=False, training=False) +``` + +将模型导出为`ONNX`格式。这个导出器运行你的模型一次,以获得其导出的执行轨迹; 目前,它不支持动态模型(例如,`RNN`)。 + +另见:`onnx-export` + +#### 参数: + +* 模型(`torch.nn.Module`) - 要导出的模型。 +* args(参数元组) - 模型的输入,例如,这-model(*args)是模型的有效调用。任何非变量参数将被硬编码到导出的模型中; 任何变量参数都将成为输出模型的输入,按照它们在参数中出现的顺序。如果 args 是一个变量,这相当于用该变量的一个元组来调用它。(注意:将关键字参数传递给模型目前还不支持,如果需要,给我们留言。) + +* f - 类文件对象(必须实现返回文件描述符的 fileno)或包含文件名的字符串。一个二进制 Protobuf 将被写入这个文件。 + +* export_params(布尔,默认为 True) - 如果指定,所有参数将被导出。如果要导出未经训练的模型,请将其设置为 False。在这种情况下,导出的模型将首先将其所有参数作为参数,按照指定的顺序 model.state_dict().values() + +* verbose(布尔,默认为 False) - 如果指定,我们将打印出一个调试描述的导出轨迹。 + +* training(布尔,默认为 False) - 在训练模式下导出模型。目前,ONNX 只是为了推导出口模型,所以你通常不需要将其设置为 True。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/31.md b/docs/0.4/31.md new file mode 100644 index 00000000..3685bc05 --- /dev/null +++ b/docs/0.4/31.md @@ -0,0 +1,11 @@ +# 遗留包 - torch.legacy + +此包中包含从 Lua Torch 移植来的代码。 + +为了可以使用现有的模型并且方便当前 Lua Torch 使用者过渡,我们创建了这个包。 可以在`torch.legacy.nn`中找到`nn`代码,并在`torch.legacy.optim`中找到`optim`代码。 API 应该完全匹配 Lua Torch。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/32.md b/docs/0.4/32.md new file mode 100644 index 00000000..5fbb83f8 --- /dev/null +++ b/docs/0.4/32.md @@ -0,0 +1 @@ +# torchvision 参考 \ No newline at end of file diff --git a/docs/0.4/33.md b/docs/0.4/33.md new file mode 100644 index 00000000..0cf4f360 --- /dev/null +++ b/docs/0.4/33.md @@ -0,0 +1,23 @@ +# torchvision + +`torchvision`包朗阔了目前流行的数据集,模型结构和常用的图片转换工具。 + +如下代码用于获取加载图像的包的名称。 + +``` +torchvision.get_image_backend() +``` + +指定用于加载图像的包。 + +``` +torchvision.set_image_backend(backend) +``` + +参数: backend(string)-backend 代表图片名称。属于{'pil ','accimage'}之一。accimage 包使用 Intel IPP 库。速度比 PIL 快,但不支持多操作。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/34.md b/docs/0.4/34.md new file mode 100644 index 00000000..6ebbf3d6 --- /dev/null +++ b/docs/0.4/34.md @@ -0,0 +1,203 @@ +# torchvision.datasets + +`torchvision.datasets`中包含了以下数据集 + +1. [MNIST](#mnist) +2. [COCO(用于图像标注和目标检测)(Captioning and Detection)](#coco) +3. [LSUN Classification](#lsun) +4. [ImageFolder](#imagefolder) +5. [Imagenet-12](#imagenet-12) +6. [CIFAR10 and CIFAR100](#cifar) +7. [STL10](#stl10) +8. [SVHN](#SVHN) +9. [PhotoTour](#PhotoTour) + +* * * + +所有数据集都是`torch.utils.data.Dataset`的子类, 即它们具有**getitem**和**len**实现方法。因此,它们都可以传递给`torch.utils.data.DataLoader` 可以使用`torch.multiprocessing`工作人员并行加载多个样本的数据。例如: + +``` +imagenet_data = torchvision.datasets.ImageFolder('path/to/imagenet_root/') +data_loader = torch.utils.data.DataLoader(imagenet_data, + batch_size=4, + shuffle=True, + num_workers=args.nThreads) +``` + +所有的数据集都有几乎相似的 API。他们都有两个共同的参数: transform 和 target_transform 分别转换输入和目标。 + +#### MNIST + +``` +dset.MNIST(root, train=True, transform=None, target_transform=None, download=False) +``` + +参数说明: + +* `root` : 数据集,存在于根目录 processed/training.pt 和 processed/test.pt 中。 +* `train` : `True` = 训练集, `False` = 测试集 +* `download` : 如果为 true,请从 Internet 下载数据集并将其放在根目录中。如果数据集已经下载,则不会再次下载。 +* `transform` :接收 PIL 映像并返回转换版本的函数/变换。例如:`transforms.RandomCrop` +* `target_transform`:一个接收目标并转换它的函数/变换。 + +#### COCO + +需要安装[COCO API](https://github.com/pdollar/coco/tree/master/PythonAPI) + +``` +dset.CocoCaptions(root="dir where images are", annFile="json annotation file", [transform, target_transform]) +``` + +参数说明: + +* `root(string)` - 图像下载到的根目录。 +* `annFile(string)` - json 注释文件的路径。 +* `transform(可调用,可选)` - 接收 PIL 映像并返回转换版本的函数/变换。例如:transforms.ToTensor +* `target_transform(可调用,可选)` - 一个接收目标并转换它的函数/变换。 + +例子: + +``` +import torchvision.datasets as dset +import torchvision.transforms as transforms +cap = dset.CocoCaptions(root = 'dir where images are', + annFile = 'json annotation file', + transform=transforms.ToTensor()) + +print('Number of samples: ', len(cap)) +img, target = cap[3] # load 4th sample + +print("Image Size: ", img.size()) +print(target) +``` + +输出: + +``` +Number of samples: 82783 +Image Size: (3L, 427L, 640L) +[u'A plane emitting smoke stream flying over a mountain.', +u'A plane darts across a bright blue sky behind a mountain covered in snow', +u'A plane leaves a contrail above the snowy mountain top.', +u'A mountain that has a plane flying overheard in the distance.', +u'A mountain view with a plume of smoke in the background'] +``` + +**getitem**(index) 参数: index(int) - 索引 返回: 元组(图像,目标)。目标是图片的标题列表。 返回类型: 元组 + +检测: + +``` +dset.CocoDetection(root="dir where images are", annFile="json annotation file", [transform, target_transform]) +``` + +参数: + +* `root(string)` - 图像下载到的根目录。 +* `annFile(string)` - json 注释文件的路径。 +* `transform`(可调用,可选) - 接收 PIL 映像并返回转换版本的函数/变换。例如,transforms.ToTensor +* `target_transform`(可调用,可选) - 一个接收目标并转换它的函数/变换。 + +**getitem**(index) 参数: index(int) - 索引 返回: 元组(图像,目标)。目标是图片的标题列表。 返回类型: 元组 + +#### LSUN + +``` +dset.LSUN(db_path, classes='train', [transform, target_transform]) +``` + +参数说明: + +* `db_path` = 数据集文件的根目录 +* `classe` = `train` (所有类别, 训练集), `val` (所有类别, 验证集), `test` (所有类别, 测试集) [`bedroom_train`, `church_train`, …] : 要加载的类别列表 +* `transform`(可调用,可选) - 接收 PIL 映像并返回转换版本的函数/变换。例如,transforms.RandomCrop +* `target_transform`(可调用,可选) - 一个接收目标并转换它的函数/变换。 + +#### ImageFolder + +一个通用的数据加载器,数据集中的数据以以下方式组织 + +``` +root/dog/xxx.png +root/dog/xxy.png +root/dog/xxz.png + +root/cat/123.png +root/cat/nsdf3.png +root/cat/asd932_.png +dset.ImageFolder(root="root folder path", [transform, target_transform]) +``` + +参数说明: + +* `root(string)` - 根目录路径。 +* `transform`(可调用,可选) - 接收 PIL 映像并返回转换版本的函数/变换。例如,transforms.RandomCrop +* `target_transform`(可调用,可选) - 一个接收目标并转换它的函数/变换。 +* `loader` - 加载给定其路径的图像的函数。 + +#### Imagenet-12 + +这应该简单地用`ImageFolder`数据集实现。数据按照这里所述进行预处理 [这里有一个例子](https://github.com/pytorch/examples/blob/27e2a46c1d1505324032b1d94fc6ce24d5b67e97/imagenet/main.py#L48-L62) + +#### CIFAR + +``` +dset.CIFAR10(root, train=True, transform=None, target_transform=None, download=False) + +dset.CIFAR100(root, train=True, transform=None, target_transform=None, download=False) +``` + +参数说明: + +* root : cifar-10-batches-py 的根目录 +* train : True = 训练集, False = 测试集 +* download : True = 从互联上下载数据,并将其放在 root 目录下。如果数据集已经下载,什么都不干。 +* transform(可调用,可选) - 接收 PIL 映像并返回转换版本的函数/变换。例如,transforms.RandomCrop +* target_transform(可调用,可选) - 一个接收目标并转换它的函数/变换。 + +#### STL10 + +``` +dset.STL10(root, split='train', transform=None, target_transform=None, download=False) +``` + +参数说明: + +* root : stl10_binary 的根目录 +* split : 'train' = 训练集, 'test' = 测试集, 'unlabeled' = 无标签数据集, 'train+unlabeled' = 训练 + 无标签数据集 (没有标签的标记为-1) +* download : True = 从互联上下载数据,并将其放在 root 目录下。如果数据集已经下载,什么都不干。 +* transform(可调用,可选) - 接收 PIL 映像并返回转换版本的函数/变换。例如,transforms.RandomCrop +* target_transform(可调用,可选) - 一个接收目标并转换它的函数/变换。 + +#### SVHN + +``` +class torchvision.datasets.SVHN(root, split='train', transform=None, target_transform=None, download=False) +``` + +参数: + +* root(string) - 目录 SVHN 存在的数据集的根目录 。 +* split(string) - {'train','test','extra'}之一。因此选择数据集。“额外”是额外的训练集。 +* transform(可调用,可选) - 接收 PIL 映像并返回转换版本的函数/变换。例如,transforms.RandomCrop +* target_transform(可调用,可选) - 一个接收目标并转换它的函数/变换。 +* download (bool,可选) - 如果为 true,请从 Internet 下载数据集并将其放在根目录中。如果数据集已经下载,则不会再次下载。 + +#### PhotoTour + +``` +class torchvision.datasets.PhotoTour(root, name, train=True, transform=None, download=False) +``` + +参数: + +* root(string) - 图像所在的根目录。 +* name(string) - 要加载的数据集的名称。 +* transform(可调用,可选) - 接收 PIL 映像并返回转换版本的函数/变换。 +* download (bool,可选) - 如果为 true,请从 Internet 下载数据集并将其放在根目录中。如果数据集已经下载,则不会再次下载。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/35.md b/docs/0.4/35.md new file mode 100644 index 00000000..501e2d6a --- /dev/null +++ b/docs/0.4/35.md @@ -0,0 +1,161 @@ +# torchvision.models + +`torchvision.models`模块的 子模块中包含以下模型结构。 + +* [AlexNet](https://arxiv.org/abs/1404.5997) +* [VGG](https://arxiv.org/abs/1409.1556) +* [ResNet](https://arxiv.org/abs/1512.03385) +* [SqueezeNet](https://arxiv.org/abs/1602.07360) +* [DenseNet](https://arxiv.org/abs/1608.06993) + +可以通过调用构造函数来构造具有随机权重的模型: + +``` +import torchvision.models as models +resnet18 = models.resnet18() +alexnet = models.alexnet() +squeezenet = models.squeezenet1_0() +densenet = models.densenet_161() +``` + +我们提供的 Pathway 变体和 alexnet 预训练的模型,利用 pytorch 的`torch.utils.model_zoo`。这些可以通过构建`pretrained=True`: + +``` +import torchvision.models as models +resnet18 = models.resnet18(pretrained=True) +alexnet = models.alexnet(pretrained=True) +``` + +所有预训练的模型的期望输入图像相同的归一化,即小批量形状通道的 RGB 图像(3 x H x W),其中 H 和 W 预计将至少 224。这些图像必须被加载到[ 0, 1 ]的范围内,然后使用平均= [ 0.485,0.456,0.406 ]和 STD=[ 0.229,0.224,0.225 ]进行归一化。您可以使用以下转换来正常化: + +``` +normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225]) +``` + +这种规范化的一个例子可以在这里找到 ImageNet 的例子 ImageNet 1-crop 错误率(224x224) + +| Network | Top-1 error | Top-5 error | +| --- | --- | --- | +| ResNet-18 | 30.24 | 10.92 | +| ResNet-34 | 26.70 | 8.58 | +| ResNet-50 | 23.85 | 7.13 | +| ResNet-101 | 22.63 | 6.44 | +| ResNet-152 | 21.69 | 5.94 | +| Inception v3 | 22.55 | 6.44 | +| AlexNet | 43.45 | 20.91 | +| VGG-11 | 30.98 | 11.37 | +| VGG-13 | 30.07 | 10.75 | +| VGG-16 | 28.41 | 9.62 | +| VGG-19 | 27.62 | 9.12 | +| SqueezeNet 1.0 | 41.90 | 19.58 | +| SqueezeNet 1.1 | 41.81 | 19.38 | +| Densenet-121 | 25.35 | 7.83 | +| Densenet-169 | 24.00 | 7.00 | +| Densenet-201 | 22.80 | 6.43 | +| Densenet-161 | 22.35 | 6.20 | + +``` +torchvision.models.alexnet(pretrained=False, ** kwargs) +``` + +AlexNet 模型结构 paper 地址 + +pretrained (bool) – True, 返回在 ImageNet 上训练好的模型。 + +``` +torchvision.models.resnet18(pretrained=False, ** kwargs) +``` + +构建一个 resnet18 模型 pretrained (bool) – True, 返回在 ImageNet 上训练好的模型。 + +``` +torchvision.models.resnet34(pretrained=False, ** kwargs) +``` + +构建一个 ResNet-34 模型. Parameters: pretrained (bool) – True, 返回在 ImageNet 上训练好的模型。 + +``` +torchvision.models.resnet50(pretrained=False, ** kwargs) +``` + +构建一个 ResNet-50 模型 + +pretrained (bool) – True, 返回在 ImageNet 上训练好的模型。 + +``` +torchvision.models.resnet101(pretrained=False, ** kwargs) +``` + +构建一个 ResNet-101 模型. + +pretrained (bool) – True, 返回在 ImageNet 上训练好的模型。 + +``` +torchvision.models.resnet152(pretrained=False, ** kwargs) +``` + +构建一个 ResNet-152 模型. + +pretrained (bool) – True, 返回在 ImageNet 上训练好的模型。 + +``` +torchvision.models.vgg11(pretrained=False, ** kwargs) +``` + +VGG 11-layer model (configuration “A”) - + +pretrained (bool) – True, 返回在 ImageNet 上训练好的模型。 + +``` +torchvision.models.vgg11_bn(** kwargs) +``` + +VGG 11-layer model (configuration “A”) with batch normalization + +``` +torchvision.models.vgg13(pretrained=False, ** kwargs) +``` + +VGG 13-layer model (configuration “B”) + +pretrained (bool) – True, 返回在 ImageNet 上训练好的模型。 + +``` +torchvision.models.vgg13_bn(** kwargs) +``` + +VGG 13-layer model (configuration “B”) with batch normalization + +``` +torchvision.models.vgg16(pretrained=False, ** kwargs) +``` + +VGG 16-layer model (configuration “D”) + +Parameters: pretrained (bool) – If True, returns a model pre-trained on ImageNet + +``` +torchvision.models.vgg16_bn(** kwargs) +``` + +VGG 16-layer model (configuration “D”) with batch normalization + +``` +torchvision.models.vgg19(pretrained=False, ** kwargs) +``` + +VGG 19-layer model (configuration “E”) + +pretrained (bool) – True, 返回在 ImageNet 上训练好的模型。 + +``` +torchvision.models.vgg19_bn(** kwargs) +``` + +VGG 19-layer model (configuration ‘E’) with batch normalization + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/36.md b/docs/0.4/36.md new file mode 100644 index 00000000..01418cc2 --- /dev/null +++ b/docs/0.4/36.md @@ -0,0 +1,133 @@ +# torchvision.transform + +1. [对 PIL.Image 进行变换](#transforms-on-pil-image) +2. [对 Tensor 进行变换](#transforms-on-torch-tensor) +3. [Conversion Transforms](#conversion-transforms) +4. [通用变换](#generic-transforms) + +* * * + +变换是常用的图像变换。它们可以用`Compose`连接在一起。 + +``` +class torchvision.transforms.Compose(transforms) +``` + +将多个 transform 组合起来使用。 + +transforms: 由 transform 构成的列表. 例子: + +``` +transforms.Compose([ + transforms.CenterCrop(10), + transforms.ToTensor(), +]) +``` + +#### 对 PIL.Image 进行变换 + +* * * + +``` +class torchvision.transforms.Scale(size, interpolation=2) +``` + +按照规定的尺寸重新调节`PIL.Image`。 + +**参数说明**: + +1. size (sequence or int) - 期望输出尺寸。如果 size 是一个像(w, h)的序列,输出大小将按照 w,h 匹配到。如果大小是 int,则图像将匹配到这个数字。例如,如果原图的`height>width`,那么改变大小后的图片大小是`(size*height/width, size)`。 +2. interpolation (int, optional) -需要添加值。默认的是`PIL.Image.BILINEAR` + +``` +class torchvision.transforms.CenterCrop(size) +``` + +将给定的 PIL.Image 进行中心切割,得到给定的 size,size 可以是 tuple,(target_height, target_width)。size 也可以是一个 Integer,在这种情况下,切出来的图片的形状是正方形。 + +``` +class torchvision.transforms.RandomCrop(size, padding=0) +``` + +切割中心点的位置随机选取。size 可以是 tuple 也可以是 Integer。 + +``` +class torchvision.transforms.RandomHorizontalFlip +``` + +随机水平翻转给定的 PIL.Image,概率为 0.5。即:一半的概率翻转,一半的概率不翻转。 + +``` +class torchvision.transforms.RandomSizedCrop(size, interpolation=2) +``` + +先将给定的 PIL.Image 随机切,然后再 resize 成给定的 size 大小。 + +``` +class torchvision.transforms.Pad(padding, fill=0) +``` + +将给定的 PIL.Image 的所有边用给定的 pad value 填充。 padding:要填充多少像素 fill:用什么值填充 + +#### 对 Tensor 进行变换 + +* * * + +``` +class torchvision.transforms.Normalize(mean, std) +``` + +给定均值:(R,G,B) 方差:(R,G,B),将会把 Tensor 正则化。即:Normalized_image=(image-mean)/std。 + +**参数说明:** + +1. mean (sequence) – 序列 R, G, B 的均值。 +2. std (sequence) – 序列 R, G, B 的平均标准偏差. + +****call**(tensor)** 参数: tensor (Tensor) – 规范化的大小(c,h,w)的张量图像. 返回结果: 规范化的图片. 返回样式: Tensor 张量 + +#### 对 Conversion 进行变换 + +* * * + +``` +class torchvision.transforms.ToTensor +``` + +把一个取值范围是[0,255]的 PIL.Image 或者 shape 为(H,W,C)的 numpy.ndarray,转换成形状为[C,H,W],取值范围是[0,1.0]的 torch.FloadTensor + +****call**(pic)** + +1. 参数: pic (PIL.Image or numpy.ndarray) – 图片转换为张量. +2. 返回结果: 转换后的图像。 +3. 返回样式: Tensor 张量 + +``` +class torchvision.transforms.ToPILImage +``` + +将 shape 为(C,H,W)的 Tensor 或 shape 为(H,W,C)的 numpy.ndarray 转换成 PIL.Image,值不变。 + +****call**(pic)** + +1. 参数: pic (Tensor or numpy.ndarray) – 图像转换为 pil.image。 +2. 返回结果: 图像转换为 PIL.Image. +3. 返回样式: PIL.Image + +#### 通用变换 + +* * * + +``` +class torchvision.transforms.Lambda(lambd) +``` + +使用 lambd 作为转换器。 + +参数说明: lambd (function) – Lambda/function 用于转换. + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/37.md b/docs/0.4/37.md new file mode 100644 index 00000000..4b10a913 --- /dev/null +++ b/docs/0.4/37.md @@ -0,0 +1,35 @@ +# torchvision.utils + +``` +torchvision.utils.make_grid(tensor, nrow=8, padding=2, normalize=False, range=None, scale_each=False, pad_value=0) +``` + +制作图像网格。 + +参数说明: + +1. **tensor (Tensor or list)** – 4D 小批量形状 Tensor 张量(B x C x H x W)或所有大小相同的图像列表。 +2. **nrows (int, optional)** – 网格中的行数。最终的网格大小(B / nrow,nrow)。默认值是 8。 +3. **normalize (bool, optional)** – 如果 TRUE,将图像移到范围(0, 1)中,减去最小值并除以最大像素值。 +4. **range (tuple, optional)** – 元组(min,max),其中 min 和 max 是数字,然后使用这些数字对图像进行标准化。默认情况下,min 和 max 是从张量计算的。 +5. **scale_each (bool, optional)** – 如果 TRUE,则在批处理图像中分别缩放每个图像,而不是在所有图像上(最小、最大)缩放图像。 +6. **pad_value (float, optional)** – 填充像素的值。 + +* * * + +查看下面的例子: + +``` +torchvision.utils.save_image(tensor, filename, nrow=8, padding=2, normalize=False, range=None, scale_each=False, pad_value=0) +``` + +将给定的 Tensor 张量保存到图像文件中。 参数说明: + +1. **Tensor (张量或者列表)** – 要保存的图像。如果小批量的 Tensor 张量,调用`make_grid`把 Tensor 张量存储为网格图像。 +2. **kwargs** – make_grid 的其他参数 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/4.md b/docs/0.4/4.md new file mode 100644 index 00000000..6f41b4d5 --- /dev/null +++ b/docs/0.4/4.md @@ -0,0 +1,155 @@ +# 扩展 PyTorch + +* [扩展 torch.autograd](#torchautograd) +* [扩展 torch.nn](#torchnn) + * [增加 Module](#module) +* [编写自定义 C 扩展](#c) + +本篇文章中包含如何扩展`torch.nn`,`torch.autograd`和使用`C`库来编写自定义的`C`扩展工具。 + +### 扩展 torch.autograd + +添加操作`autograd`需要`Function`为每个操作实现一个新的子类。回想一下,`Function`使用`autograd`来计算结果和梯度,并对操作历史进行编码。每个新功能都需要您实现两种方法: + +* `forward()` - 执行操作的代码。如果您指定了默认值,则可以根据需求使用任意参数,其中一些参数可选。这里支持各种`Python`对象。`Variable`参数在调用之前会被转换`Tensor`,并且它们的使用情况将在`graph`中注册。请注意,此逻辑不会遍历`lists`/`dicts`/和其他任何数据的结构,并且只考虑被直接调用的`Variables`参数。如果有多个输出你可以返回单个`Tensor`或`Tensor`格式的元组。另外,请参阅`Function`文档查找只能被`forward()`调用的有用方法的说明。 + +* `backward()` - 计算梯度的公式. 它将被赋予与输出一样多的`Variable`参数, 其中的每一个表示对应梯度的输出. 它应该返回与输入一样多的`Variable`, 其中的每一个表示都包含其相应输入的梯度. 如果输入不需要计算梯度 (请参阅`needs_input_grad`属性),`或者是非`Variable`对象,则可返回`None`类.此外,如果你在`forward()`方法中有可选的参数,`则可以返回比输入更多的梯度,只要它们都是`None`类型即可. + +你可以从下面的代码看到`torch.nn`模块的`Linear`函数, 以及注解 + +``` +# Inherit from Function +class Linear(Function): + + # bias is an optional argument + def forward(self, input, weight, bias=None): + self.save_for_backward(input, weight, bias) + output = input.mm(weight.t()) + if bias is not None: + output += bias.unsqueeze(0).expand_as(output) + return output + + # This function has only a single output, so it gets only one gradient + def backward(self, grad_output): + # This is a pattern that is very convenient - at the top of backward + # unpack saved_tensors and initialize all gradients w.r.t. inputs to + # None. Thanks to the fact that additional trailing Nones are + # ignored, the return statement is simple even when the function has + # optional inputs. + input, weight, bias = self.saved_tensors + grad_input = grad_weight = grad_bias = None + + # These needs_input_grad checks are optional and there only to + # improve efficiency. If you want to make your code simpler, you can + # skip them. Returning gradients for inputs that don't require it is + # not an error. + if self.needs_input_grad[0]: + grad_input = grad_output.mm(weight) + if self.needs_input_grad[1]: + grad_weight = grad_output.t().mm(input) + if bias is not None and self.needs_input_grad[2]: + grad_bias = grad_output.sum(0).squeeze(0) + + return grad_input, grad_weight, grad_bias +``` + +现在,为了更方便使用这些自定义操作,推荐使用`apply`方法: + +``` +linear = LinearFunction.apply +``` + +我们下面给出一个由非变量参数进行参数化的函数的例子: + +``` +class MulConstant(Function): + @staticmethod + def forward(ctx, tensor, constant): + # ctx is a context object that can be used to stash information + # for backward computation + ctx.constant = constant + return tensor * constant + + @staticmethod + def backward(ctx, grad_output): + # We return as many input gradients as there were arguments. + # Gradients of non-Tensor arguments to forward must be None. + return grad_output * ctx.constant, None +``` + +你可能想检测你刚刚实现的`backward`方法是否正确的计算了梯度。你可以使用小的有限差分法(`Finite Difference`)进行数值估计。 + +``` +from torch.autograd import gradcheck + +# gradcheck takes a tuple of tensors as input, check if your gradient +# evaluated with these tensors are close enough to numerical +# approximations and returns True if they all verify this condition. +input = (Variable(torch.randn(20,20).double(), requires_grad=True), Variable(torch.randn(30,20).double(), requires_grad=True),) +test = gradcheck(Linear.apply, input, eps=1e-6, atol=1e-4) +print(test) +``` + +### 扩展 torch.nn + +`nn`模块包含两种接口 - `modules`和他们的功能版本。你可以用两种方法扩展它,但是我们建议,在扩展`layer`的时候使用`modules`, 因为`modules`保存着参数和`buffer`。如果使用无参数操作的话,那么建议使用激活函数,池化等函数。 + +添加操作的功能版本已经在上面的章节中已经介绍了。 + +#### 增加一个`Module`。 + +由于`nn`大量使用`autograd`。所以, 添加一个新的[Module](http://pytorch.org/docs/master/nn.html#torch.nn.Module)类需要实现一个`Function`类, 它会执行对应的操作并且计算梯度。我们只需要很少的代码就可以实现上面`Linear`模块的功能。现在,我们需要实现两个函数: + +* `__init__ (optional)` - 接收`kernel sizes`内核大小,特征数量等参数,并初始化`parameters`参数和`buffers`缓冲区。 +* `forward()` - 实例化`Function`并使用它来执行操作。它与上面显示的`functional wrapper`非常相似。 + +下面是实现`Linear`模块的方式: + +``` +class Linear(nn.Module): + def __init__(self, input_features, output_features, bias=True): + super(Linear, self).__init__() + self.input_features = input_features + self.output_features = output_features + + # nn.Parameter is a special kind of Variable, that will get + # automatically registered as Module's parameter once it's assigned + # as an attribute. Parameters and buffers need to be registered, or + # they won't appear in .parameters() (doesn't apply to buffers), and + # won't be converted when e.g. .cuda() is called. You can use + # .register_buffer() to register buffers. + # nn.Parameters require gradients by default. + self.weight = nn.Parameter(torch.Tensor(output_features, input_features)) + if bias: + self.bias = nn.Parameter(torch.Tensor(output_features)) + else: + # You should always register all possible parameters, but the + # optional ones can be None if you want. + self.register_parameter('bias', None) + + # Not a very smart way to initialize weights + self.weight.data.uniform_(-0.1, 0.1) + if bias is not None: + self.bias.data.uniform_(-0.1, 0.1) + + def forward(self, input): + # See the autograd section for explanation of what happens here. + return LinearFunction.apply(input, self.weight, self.bias) + + def extra_repr(self): + # (Optional)Set the extra information about this module. You can test + # it by printing an object of this class. + return 'in_features={}, out_features={}, bias={}'.format( + self.in_features, self.out_features, self.bias is not None + ) +``` + +### 编写自定义的 C 扩展 + +即将发布。不过现在你可以在[GitHub](https://github.com/pytorch/extension-ffi)上找到一些例子 。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/5.md b/docs/0.4/5.md new file mode 100644 index 00000000..47216066 --- /dev/null +++ b/docs/0.4/5.md @@ -0,0 +1,90 @@ +# 常见问题 + +### 我的模型报告 "cuda runtime error(2): out of memory" + +如错误消息所示,您的`GPU`上的内存不足。由于我们经常在`PyTorch`中处理大量数据,因此小错误可能会迅速导致程序耗尽所有 GPU;幸运的是,这些情况下的修复通常很简单。这里有几个常见的事情要检查: + +**不要在训练循环中累积历史记录**。默认情况下,当计算涉及到有需要梯度的变量时,此计算过程将保留运算的历史记录。这意味着您应该避免在计算中使用这些变量,这些变量的生存期将超出您的训练循环(例如在跟踪统计数据时)。您应该分离该变量或访问其底层数据。 + +有时,当可微分变量可能发生时,它可能并不明显。考虑以下训练循环(从[源代码](https://discuss.pytorch.org/t/high-memory-usage-while-training/162)节选): + +``` +total_loss = 0 +for i in range(10000): + optimizer.zero_grad() + output = model(input) + loss = criterion(output) + loss.backward() + optimizer.step() + total_loss += loss +``` + +在本例中,由于 `loss` 是具有 `autograd` 历史记录的可微分变量,所以 `total_loss` 将在整个训练循环中累积历史记录。你可以替换成 `total_loss + = float(loss)` 来解决这个问题。 + +这个问题的另一个例子:[1](https://discuss.pytorch.org/t/resolved-gpu-out-of-memory-error-with-batch-size-1/3719) + +**删除你不需要的张量和变量**。如果将一个张量或变量分配到一个局部栈,在局部栈超出作用域之前,Python 都不会将其释放。您可以使用 `del x` 释放该引用。同样,如果将一个张量或变量赋值给对象的成员变量,直到该对象超出作用域之前,它将不会释放。如果你及时删除你不需要的临时变量,你将获得最佳的内存使用率。 + +作用域的范围可能比你想象的要大。例如: + +``` +for i in range(5): + intermdeiate = f(input[i]) + result += g(intermediate) +output = h(result) +return output +``` + +在本段代码中,即使当 `h` 在执行时,`intermediate` 仍然存在,因为它的作用域延伸出了循环的末尾。为了尽早释放它,当你不需要它时,你应该用 `del intermediate` 删除这个中间值。 + +**不要在太大的序列上运行 RNN。** 因为 RNN 反向传播所需的内存量与 RNN 的长度成线性关系;因此,如果尝试向 RNN 提供一个太长的序列时,会耗尽内存。 + +这一现象的技术术语是时间反向传播[Backpropagation through time](https://en.wikipedia.org/wiki/Backpropagation_through_time),关于如何实现截断的 BPTT 有很多参考资料,包括在单词语言模型 [word language model](https://github.com/pytorch/examples/tree/master/word_language_model) 中;截断由[这个论坛帖子](https://discuss.pytorch.org/t/help-clarifying-repackage-hidden-in-word-language-model/226)中描述的 `repackage` 函数处理。 + +**不要使用太大的线性图层。** 线性层 `nn.Linear(m,n)` 使用 `O(nm)` 存储器:也就是说,权值的存储器需求随着要素的数量按比例缩放。这种方式会轻易的占用你的内存(并且记住,你将至少需要两倍存储权值的内存量,因为你还需要存储梯度。) + +### 我的 GPU 内存没有正确释放 + +PyTorch 使用缓存内存分配器来加速内存分配。因此,`nvidia-smi` 中显示的值通常不会反映真实的内存使用情况。有关 GPU 内存管理的更多细节,请参阅 [内存管理](http://pytorch.org/docs/stable/notes/cuda.html#cuda-memory-management) 。 + +如果您的 GPU 内存在 Python 退出后仍未释放,那么很可能某些 Python 子进程仍然存在。你可以通过 `ps -elf | grep python` 找到它们,并用 `kill -9 [pid]` 手动杀死它们。 + +### 我的多个数据加载器返回相同的随机数 + +您可能正使用其他库生成数据集中的随机数。例如,当通过 `fork` 启动工作子进程时,NumPy 的 RNG 会被复制。请参阅 [`torch.utils.data.DataLoader`](http://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader) 的文档,了解如何使用其 `worker_init_fn` 选项正确设置工作进程中的随机种子。 + +### 我的回归网络不能使用数据并行 + +在具有 `DataParallel` 或 `data_parallel()` 的模块中使用 `pack sequence -> recurrent network -> unpack sequence` 模式时有一个非常微妙的地方。每个设备上的 `forward()` 的输入只会是整个输入的一部分。由于默认情况下,解包操作 `torch.nn.utils.rnn.pad_packed_sequence()` 仅填充到其所见的最长输入,即该特定设备上的最长输入,所以在将结果收集在一起时会发生尺寸的不匹配。因此,您可以利用 `pad_packed_sequence()` 的 `total_length` 参数来确保 `forward()` 调用返回相同长度 + +的序列。例如,你可以写: + +``` +from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_squence + +class MyModule(nn.Module): + # ... __init__, 以及其他访求 + + # padding_input 的形状是[B x T x *](batch_first 模式),包含按长度排序的序列 + # B 是批量大小 + # T 是最大序列长度 + def forward(self, padded_input, input_lengths): + total_length = padded_input.size(1) # 获取最大序列长度 + packed_input = pack_padded_sequence(padded_input, input_lengths, + batch_first=True) + packed_output, _ = self.my_lstm(packed_input) + output, _ = pad_packed_sequence(packed_output, batch_first=True, + total_length=total_length) + return output + +m = MyModule().cuda() +dp_m = nn.DataParallel(m) +``` + +此外,在批量的维度为 dim `1` (第 1 轴)(即 `batch_first = False` )时需要额外注意数据的并行性。在这种情况下,`pack_padded_sequence` 函数的的第一个参数 `padding_input` 维度将是 `[T x B x *]` ,并且应该沿 dim `1` (第 1 轴)分散,但第二个参数 `input_lengths` 的维度为 `[B]`,应该沿 dim `0` (第 0 轴)分散。需要额外的代码来操纵张量的维度。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| 风中劲草 | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/6.md b/docs/0.4/6.md new file mode 100644 index 00000000..ed6b7192 --- /dev/null +++ b/docs/0.4/6.md @@ -0,0 +1,81 @@ +# 多进程最佳实践 + +* [CUDA 张量的共享](#sharing-cuda-tensors) +* [最佳实践和技巧](#best-practices-and-tips) + * [避免和防止死锁](#avoiding-and-fighting-deadlocks) + * [重用通过队列发送的缓冲区](#reuse-buffers-passed-through-a-queue) + * [异步多进程训练](#asynchronous-multiprocess-training-e-g-hogwild) + * [hogwild](#hogwild) + +`torch.multiprocessing` 是 Python 的 [`multiprocessing`](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing) 多进程模块的替代品。它支持完全相同的操作,但对其进行了扩展,以便所有通过多进程队列 [`multiprocessing.Queue`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Queue) 发送的张量都能将其数据移入共享内存,而且仅将其句柄发送到另一个进程。 + +> 注意: +> +> 当张量 [`Tensor`](http://pytorch.org/docs/stable/tensors.html#torch.Tensor) 被发送到另一个进程时,张量的数据和梯度 `torch.Tensor.grad` 都将被共享。 + +这一特性允许实现各种训练方法,如 Hogwild,A3C 或任何其他需要异步操作的训练方法。 + +### 一、CUDA 张量的共享 + +仅 Python 3 支持进程之间共享 CUDA 张量,我们可以使用 `spawn` 或`forkserver` 启动此类方法。 Python 2 中的 [`multiprocessing`](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing) 多进程处理只能使用 `fork` 创建子进程,并且 CUDA 运行时不支持多进程处理。 + +> 警告: +> +> CUDA API 规定输出到其他进程的共享张量,只要它们被这些进程使用时,都将持续保持有效。您应该小心并确保您共享的 CUDA 张量不会超出它应该的作用范围(不会出现作用范围延伸的问题)。这对于共享模型的参数应该不是问题,但应该小心地传递其他类型的数据。请注意,此限制不适用于共享的 CPU 内存。 + +也可以参阅:, [使用 nn.DataParallel 替代多进程处理](http://pytorch.org/docs/stable/notes/cuda.html#cuda-nn-dataparallel-instead) + +#### 1、避免和防止死锁 + +产生新进程时会出现很多错误,导致死锁最常见的原因是后台线程。如果有任何持有锁或导入模块的线程,并且 `fork` 被调用,则子进程很可能处于崩溃状态,并且会以不同方式死锁或失败。请注意,即使您没有这样做,Python 中内置的库也可能会,更不必说 [`多进程处理`](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing) 了。[`multiprocessing.Queue`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Queue) 多进程队列实际上是一个非常复杂的类,它产生了多个用于序列化、发送和接收对象的线程,并且它们也可能导致上述问题。如果您发现自己处于这种情况,请尝试使用`multiprocessing.queues.SimpleQueue` ,它不使用任何其他额外的线程。 + +我们正在尽可能的为您提供便利,并确保这些死锁不会发生,但有些事情不受我们控制。如果您有任何问题暂时无法应对,请尝试到论坛求助,我们会查看是否可以解决问题。 + +#### 2、重用通过队列发送的缓冲区 + +请记住,每次将张量放入多进程队列 [`multiprocessing.Queue`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Queue) 时,它必须被移动到共享内存中。如果它已经被共享,将会是一个空操作,否则会产生一个额外的内存拷贝,这会减慢整个过程。即使您有一组进程将数据发送到单个进程,也可以让它将缓冲区发送回去,这几乎是不占资源的,并且可以在发送下一批时避免产生拷贝动作。 + +#### 3、异步多进程训练(如: Hogwild) + +使用多进程处理 `torch.multiprocessing`,可以异步地训练一个模型,参数既可以一直共享,也可以周期性同步。在第一种情况下,我们建议发送整个模型对象,而在后者中,我们建议只发送状态字典 `state_dict()` 。 + +我们建议使用多进程处理队列 [`multiprocessing.Queue`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Queue) 在进程之间传递各种 PyTorch 对象。使用 `fork` 启动一个方法时,它也可能会继承共享内存中的张量和存储空间,但这种方式也非常容易出错,应谨慎使用,最好只能让高阶用户使用。而队列,尽管它们有时候不太优雅,却能在任何情况下正常工作。 + +> 警告: +> +> 你应该留意没有用 `if __name__ =='__main__'` 来保护的全局语句。如果使用了不同于 `fork` 启动方法,它们将在所有子进程中执行。 + +#### 4、Hogwild + +具体的 Hogwild 实现可以在 [示例库](https://github.com/pytorch/examples/tree/master/mnist_hogwild) 中找到,但为了展示代码的整体结构,下面还有一个最简单的示例: + +``` +import torch.multiprocessing as mp +from model import MyModel + +def train(model): + # 构建 data_loader,优化器等 + for data, labels in data_loader: + optimizer.zero_grad() + loss_fn(model(data), labels).backward() + optimizer.step() # 更新共享的参数 + +if __name__ == '__main__': + num_processes = 4 + model = MyModel() + # 注意:这是 "fork" 方法工作所必需的 + model.share_memory() + processes = [] + for rank in range(num_processes): + p = mp.Process(target=train, args=(model,)) + p.start() + processes.append(p) + for p in processes: + p.join() +``` + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| 风中劲草 | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/7.md b/docs/0.4/7.md new file mode 100644 index 00000000..7b4c01c3 --- /dev/null +++ b/docs/0.4/7.md @@ -0,0 +1,32 @@ +# 序列化语义 + +#### 保存模型的推荐方法 + +序列化和恢复模型有两种主要方法。 + +第一个(推荐)只保存和加载模型参数: + +``` +torch.save(the_model.state_dict(), PATH) +``` + +然后: + +``` +the_model = TheModelClass(*args, **kwargs) +the_mdel.load_state_dict(torch.load(PATH)) +``` + +第二个方法是保存并加载整个模型: + +``` +torch.save(the_model, PATH) +``` + +然后: + +``` +the_model = torch.load(PATH) +``` + +然而,在这种情况下,序列化的数据会与特定的类结构和准确的目录结构相绑定,所以在其他项目中使用或经大量重构之后,这些结构可能会以各种方式被破坏。 \ No newline at end of file diff --git a/docs/0.4/8.md b/docs/0.4/8.md new file mode 100644 index 00000000..c46c3606 --- /dev/null +++ b/docs/0.4/8.md @@ -0,0 +1,226 @@ +# Windows 常见问题 + +* [源码构建](#building-from-source) + * [包含可选组件](#include-optional-components) + * [加速 Windows 的 CUDA 构建](#speeding-cuda-build-for-windows) + * [一个关键的安装脚本](#one-key-install-script) +* [扩展](#extension) + * [CFFI 扩展](#cffi-extension) + * [Cpp 扩展](#cpp-extension) +* [安装](#installation) + * [在 win-32 中找不到包](#package-not-found-in-win-32-channel) + * [为什么没有 Windows 的 Python 2 包?](#why-are-there-no-python-2-packages-for-windows) + * [导入错误](#import-error) +* [用法(多进程处理)](#usage-multiprocessing) + * [无 if 语句保护的多进程处理错误](#multiprocessing-error-without-if-clause-protection) + * [多进程处理错误 “损坏的管道”](#multiprocessing-error-broken-pipe) + * [多进程处理错误 “驱动程序关闭”](#multiprocessing-error-driver-shut-down) + * [CUDA IPC 业务](#cuda-ipc-operations) + +### 包含可选组件 + +Windows PyTorch 支持两种组件:MKL 和 MAGMA。以下是与他们一起构建的步骤。 + +``` +# REM 确保您安装了 7z 和卷曲 +# REM 下载 MKL 文件 +curl https://s3.amazonaws.com/ossci-windows/mkl_2018.2.185.7z -k -O +7z x -aoa mkl_2018.2.185.7z -omkl + +# REM 下载 MAGMA 文件 +# cuda90 / cuda91 也可在以下行中找到 +set CUDA_PREFIX=cuda80 +curl -k https://s3.amazonaws.com/ossci-windows/magma_%CUDA_PREFIX%_release_mkl_2018.2.185.7z -o magma.7z +7z x -aoa magma.7z -omagma + +# REM 设置基本的环境变量 +set "CMAKE_INCLUDE_PATH=%cd%\\mkl\\include" +set "LIB=%cd%\\mkl\\lib;%LIB%" +set "MAGMA_HOME=%cd%\\magma" +``` + +### 加速 Windows 的 CUDA 构建 + +Visual Studio 目前不支持并行自定义任务。作为替代,我们可以使用 `Ninja` 来并行化 CUDA 构建任务。它只能通过输入几行代码来使用。 + +``` +# REM 先安装 ninja +pip install ninja + +# REM 将其设置为 cmake 生成器 +set CMAKE_GENERATOR=Ninja +``` + +### 一个关键的安装脚本 + +你可以在这里查阅 [脚本](https://github.com/peterjc123/pytorch-scripts) ,它会给你指引。 + +### CFFI 扩展 + +对 CFFI 扩展的支持是非常实验性的。在 Windows 下启用它通常有两个步骤。 + +首先,在扩展对象中指定其他库以更在 Windows 上构建。 + +``` +ffi = create_extension( + '_ext.my_lib', + headers=headers, + sources=sources, + define_macros=defines, + relative_to=__file__, + with_cuda=with_cuda, + extra_compile_args=["-std=c99"], + libraries=['ATen', '_C'] # Append cuda libaries when necessary, like cudart +) +``` + +其次,对于“unresolved external symbol state caused by `extern THCState *state;`” 由`extern THCState *state` 引起的未解析的外部符号状态,将源代码从 C 更改为 C ++。 + +下面列出了一个例子: + +``` +#include +#include + +THCState *state = at::globalContext().thc_state; + +extern "C" int my_lib_add_forward_cuda(THCudaTensor *input1, THCudaTensor *input2, + THCudaTensor *output) +{ + if (!THCudaTensor_isSameSizeAs(state, input1, input2)) + return 0; + THCudaTensor_resizeAs(state, output, input1); + THCudaTensor_cadd(state, output, input1, 1.0, input2); + return 1; +} + +extern "C" int my_lib_add_backward_cuda(THCudaTensor *grad_output, THCudaTensor *grad_input) +{ + THCudaTensor_resizeAs(state, grad_input, grad_output); + THCudaTensor_fill(state, grad_input, 1); + return 1; +} +``` + +### Cpp 扩展 + +与前一种相比,这种类型的扩展有更好的支持。但是,它仍然需要一些手动配置。首先,您应该打开**VS 2017 的 x86_x64 交叉工具链命令提示符**。然后,您可以打开其中的 Git-Bash。它通常位于 `C:\ Program Files \ Git \ git-bash.exe` 中。最后,您可以开始编译过程。 + +### 在 win-32 中找不到包 + +``` +Solving environment: failed + +PackagesNotFoundError: The following packages are not available from current channels: + +- pytorch + +Current channels: +- https://conda.anaconda.org/pytorch/win-32 +- https://conda.anaconda.org/pytorch/noarch +- https://repo.continuum.io/pkgs/main/win-32 +- https://repo.continuum.io/pkgs/main/noarch +- https://repo.continuum.io/pkgs/free/win-32 +- https://repo.continuum.io/pkgs/free/noarch +- https://repo.continuum.io/pkgs/r/win-32 +- https://repo.continuum.io/pkgs/r/noarch +- https://repo.continuum.io/pkgs/pro/win-32 +- https://repo.continuum.io/pkgs/pro/noarch +- https://repo.continuum.io/pkgs/msys2/win-32 +- https://repo.continuum.io/pkgs/msys2/noarch +``` + +PyTorch 不适用于 32 位系统。请使用 Windows 和 Python 64 位版本。 + +### 为什么没有 Windows 的 Python 2 包? + +因为它不够稳定。在我们正式发布之前需要解决一些问题。你可以自己构建它。 + +### 导入错误 + +``` +from torch._C import * + +ImportError: DLL load failed: The specified module could not be found. +``` + +这个问题是由于必要文件丢失造成的。实际上,我们几乎包含了除 VC2017 可再发行组件之外 PyTorch 所需的所有必要文件。您可以通过键入以下命令来解决此问题。 + +``` +conda install -c peterjc123 vc vs2017_runtime +``` + +另一个可能的原因可能是您使用的是没有 NVIDIA 图形卡的 GPU 版本。请将您的 GPU 软件包替换为 CPU 软件包。 + +### 无 if 语句保护的多进程处理错误 + +``` +RuntimeError: + An attempt has been made to start a new process before the + current process has finished its bootstrapping phase. + 在当前进程完成引导阶段之前,已尝试开始一个新进程 + + This probably means that you are not using fork to start your + child processes and you have forgotten to use the proper idiom + in the main module: + 这可能意味着你没有使用 fork 来启动你的子进程,并且你忘记了在主模块中使用 + 正确的用法: + + if __name__ == '__main__': + freeze_support() + ... + + The "freeze_support()" line can be omitted if the program + is not going to be frozen to produce an executable. + 如果程序不会被冻结以生成可执行文件,则可以省略 “freeze_support()” 行。 +``` + +Windows 上 `multiprocessing` 多进程处理的实现不同,它使用 `spawn` 而不是 `fork`。所以我们必须用 if 条件语句来保护代码不被执行多次。将您的代码重构为以下结构: + +``` +import torch + +def main() + for i, data in enumerate(dataloader): + # do something here + +if __name__ == '__main__': + main() +``` + +### 多进程处理错误 “损坏的管道” + +``` +ForkingPickler(file, protocol).dump(obj) + +BrokenPipeError: [Errno 32] Broken pipe +``` + +在父进程发送数据完成之前,子进程先结束就会发生此错误。这说明你的代码可能有问题。你可以通过将 `DataLoader` 的 `num_worker` 减为零来调试代码,看看问题是否仍然存在。 + +### 多进程处理错误 “驱动程序关闭” + +``` +Couldn’t open shared file mapping: , error code: <1455> at torch\lib\TH\THAllocator.c:154 + +[windows] driver shut down +``` + +请更新您的图形驱动程序。如果这种情况持续存在,这可能是因为您的显卡太旧或计算压力对于您的显卡来说太重了。请根据这篇 [文章](https://www.pugetsystems.com/labs/hpc/Working-around-TDR-in-Windows-for-a-better-GPU-computing-experience-777/) 更新 TDR 设置。 + +### CUDA IPC 业务 + +``` +THCudaCheck FAIL file=torch\csrc\generic\StorageSharing.cpp line=252 error=63 : OS call failed or operation not supported on this OS +``` + +Windows 不支持此类业务。就像在 CUDA 张量上进行多进程处理一样无法成功,有两种方法可供选择: + +1. 不要使用多进程处理。将 `DataLoader` 的 `num_worker` 设置为零。 +2. 改为 CPU 共享张量。确保您的自定义 `DataSet` 数据集返回 CPU 张量。 + +### 译者署名 + +| 用户名 | 头像 | 职能 | 签名 | +| --- | --- | --- | --- | +| 风中劲草 | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 | \ No newline at end of file diff --git a/docs/0.4/9.md b/docs/0.4/9.md new file mode 100644 index 00000000..f0130d3d --- /dev/null +++ b/docs/0.4/9.md @@ -0,0 +1 @@ +# 包参考 \ No newline at end of file diff --git a/docs/0.4/README.md b/docs/0.4/README.md new file mode 100644 index 00000000..50fe2316 --- /dev/null +++ b/docs/0.4/README.md @@ -0,0 +1,13 @@ +# PyTorch 0.4 中文文档 + +![](img/73dd6bbb.png) + +> 维护组织: [PyTorch 中文网](https://ptorch.com/docs/8/) + +PyTorch 是一个针对深度学习, 并且使用 GPU 和 CPU 来优化的 tensor library (张量库) . + +* [在线阅读](https://pytorch.apachecn.org/docs/0.4/) +* [PDF 格式](https://www.gitbook.com/download/pdf/book/wizardforcel/pytorch-04-doc) +* [EPUB 格式](https://www.gitbook.com/download/epub/book/wizardforcel/pytorch-04-doc) +* [MOBI 格式](https://www.gitbook.com/download/mobi/book/wizardforcel/pytorch-04-doc) +* [代码仓库](https://github.com/apachecn/pytorch-doc-zh) \ No newline at end of file diff --git a/docs/0.4/SUMMARY.md b/docs/0.4/SUMMARY.md new file mode 100644 index 00000000..1b8a905d --- /dev/null +++ b/docs/0.4/SUMMARY.md @@ -0,0 +1,39 @@ ++ [PyTorch 0.4 中文文档](README.md) ++ [笔记](0.md) ++ [自动求导机制](1.md) ++ [Torch](10.md) ++ [torch.Tensor](11.md) ++ [Tensor Attributes](12.md) ++ [torch.sparse](13.md) ++ [torch.cuda](14.md) ++ [torch.Storage](15.md) ++ [torch.nn](16.md) ++ [torch.nn.functional](17.md) ++ [自动差异化包 - torch.autograd](18.md) ++ [torch.optim](19.md) ++ [广播语义](2.md) ++ [torch.nn.init](20.md) ++ [torch.distributions](21.md) ++ [Multiprocessing 包 - torch.multiprocessing](22.md) ++ [分布式通讯包 - torch.distributed](23.md) ++ [torch.utils.bottleneck](24.md) ++ [torch.utils.checkpoint](25.md) ++ [torch.utils.cpp_extension](26.md) ++ [torch.utils.data](27.md) ++ [torch.utils.ffi](28.md) ++ [torch.utils.model_zoo](29.md) ++ [CUDA 语义](3.md) ++ [torch.onnx](30.md) ++ [遗留包 - torch.legacy](31.md) ++ [torchvision 参考](32.md) ++ [torchvision](33.md) ++ [torchvision.datasets](34.md) ++ [torchvision.models](35.md) ++ [torchvision.transform](36.md) ++ [torchvision.utils](37.md) ++ [扩展 PyTorch](4.md) ++ [常见问题](5.md) ++ [多进程最佳实践](6.md) ++ [序列化语义](7.md) ++ [Windows 常见问题](8.md) ++ [包参考](9.md) diff --git a/docs/0.4/img/2017121722000236815.jpg b/docs/0.4/img/2017121722000236815.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ad75eabf509d6538cff21d8898276c3e2f54451b GIT binary patch literal 26949 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

P70KCZofO@zs4bV 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 literal 0 HcmV?d00001 diff --git a/docs/0.4/img/2018033000352689884.jpeg b/docs/0.4/img/2018033000352689884.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..bf3b4c7103e6f162ca2989cfb17926105a13610f GIT binary patch literal 16557 zcmeHuc|6o>)cALhrR-F+ScW!PX2vpxtV5D4*(o$;29q(vEY^#5Eusj~qLL*`vKNIL z)s0jrlH?*}D`XO3-tTuT)xGz9Klgp#-|vs#=jY-3Ea#l(Jm);ye81;B5Bm+fOU&6c znB)ZjmX?4L008`eAPfMw!5oZR4vb(w<+(U{2zM36I}ZoAKpOFdJXjI7dLa)|!PWsQ zAU@!86SjUSp9|aiQ(Yv)0f2)~F$~4YOM>~9C=$Q}rcm8sFi`~Mg8qavK)@E!#ngyE z^(4yK_)r;Cx)0S~4vUpTA&?q!Tb)Q00yT)f4FHQ69RyMbg^)ubbu>^q>Ijenz;g}& z_@ESlLLsj$=*VURz%{O=c7v~R|Hwm?z`zURUXY#lXZ>KbANs*Uf7Xu+w#Wl^glmbn z$n$x2z%DR_bmXM$H|!BWkfW0a))L@ie*%O#C4g`UqGPuM#{fZoetrRdK>-0lu~jQq ziLDk96ckx4B_Xj|LPAPRkP{Y?`NCggn9!-DuYsp02s(M&Z%TCh_(J(}!sct+h=K-Nf;tV>Qjpo-aGMT)26>IHC5{hl%u( zN3VNXj$VN$5;IC4xAc8Qn>l$0MI~jHJ!$Qq6a%=pKxuh6GV$@Q(Bz09j}+$tB?y2^ ztUw(;F9}KTyKUpBx}s+n?MNKfZZ)2+F}8^p5`b^B=EEjXh_tl?QwJ>uv3p5`|6GDC zKSW@^1q8V{ZHfT~z-L?YO*LryICI5x#W?eA=8AEStI=)m zgn8z^BqSW_U;E(2j=OOY#DL%fik1_U@;CQbA9R})nhO=t=L|=c>S}9KOB~9s^c?pd z5^=0ZW7NEIF?*|&QTNz_aOCa&k;IPu;pjrU^kenP0X2PB&-0ZFT(h`&`G}y5qpaJ| z?6xbqYU`p)+b>0^q0u|Q{cwZEbs z$XN^Tm$}?A7%QKyw)qemwUJP^yM)Xky!lI&gXdd8&6O6Bp*A; zBO@I#Ok@MsZmwVRbcf9BxBZiQraKOOy5cglwn!w~u^->tZNxk>_`H3iyQXn{xUOEQ zy6iwG;M@9#Q1dXWfkJmdBWR4Oy?^dHLC~X(K@t=qjqq zk9T+wrZsoQ?yQ^6i4!c}_KBCz#HeIoE zZ_B&M_N1_i^7`EH6Oq{s32Xp&a7KZ@;i?NQ^uc)X~1fE6T%JSa!@wHq1u9n6)D&;U(=$ zfFVZE*TrhYu&}jACi8B@2(>J9{QU#gQSQMxdbwZlYSxBx>xO6USA{1&s0r!7r%c{^ z0rvcw7kxvX1Nc?ECZn|^za7bfyP`e4x4Cpx%I=B4*G`56=S>$6ju*JQu|oSb%X;GP z;>sVccXVHWg$?+~;#1yT_G{nD(r=8NHrRVIxuuWL@6@P6Z@_4&x7O7k`drlWBBQmn z;?3~0OX$`!-!G~B!3KB>BCq{rFqCPTP z=c4-uzV^A`QduU0eAJGvsjD@-`8gW}`h_nQ6pZH{C6%^>XSC{e3=&*?D;t@Kkt)9T zq6^YvVGZyN%~^7DquXo0RF+LpuB1{epA6W4=(7FdHtEcLyZGpuw;sM8g9d@0nYU-* z6SBA3yrPF=&f0Z&iL_-uuYPB)*-BzM_E17pCtSaVUi@B_!qU^cuFMb+7w>9vXgncp zeop*wl;~X-ne6c?7nbTN55w>d>J1xdBiO86rX%2&P#8q)M$fGAQr|E+O`ct2XJ9rn z-k4qY=~j~cpVN819{bvBQo0_*QpfilBCprlW>7F{&VZOt#moV!v~mDP z8ShrC_e4Q@~o@MtHoubxlZ@oqUDc&*dYzGltow}Y~$*}y}E zq@L2gMj!nCuc1uD`O?R;+XL|tzT4-rUw#NFs%twrDQrZGd54U$-z_E=;L&Vmk;53K z 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 Su@I#ciX`m~x#eiUZu<{ZyN#{@ literal 0 HcmV?d00001 diff --git a/docs/0.4/img/65ca6a0b.jpg b/docs/0.4/img/65ca6a0b.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a674a03491565cea1078a226f350826615282621 GIT binary patch literal 89205 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 Q3{i?G?LY+-Q9vL6*&d>`tN;K2 literal 0 HcmV?d00001 diff --git a/docs/0.4/img/73dd6bbb.png b/docs/0.4/img/73dd6bbb.png new file mode 100644 index 0000000000000000000000000000000000000000..5f359c470a5a107ae8d7e0104e0f7b5663ad49c8 GIT binary patch literal 32165 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%0APMB6TYs7c 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