├── Pytorch API&Tutorials CN.md
├── Pytorch API&Tutorials CN.pdf
└── README.md
/Pytorch API&Tutorials CN.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Pytorch版本:1.6.0
4 |
5 | 上版更新:nn.SyncBatchNorm
6 |
7 | 当前更新:nn.utils.data
8 |
9 |
10 |
11 | 本文档是对Pytorch官方文档和教材学习及查阅过程中的笔记,不仅对原文档做了便于理解的翻译,还对一些重要的部分进行了解释说明。
12 |
13 |
14 |
15 | 相比官方文档,本文以最简洁的方式呈现了Pytorch一些功能和API的使用方法,对于这些方法实现的细节和原理不做过多的介绍,而是给出了原文链接,需要了解的读者可自行查看;同时,本文保留了文档中的代码示例,对复杂脚本进行了代码分析,希望可以帮助读者更快地理解相关内容,节省读者的时间;最后,本项目尚未包含官方文档所有内容,仍在持续更新中。
16 |
17 |
18 |
19 | ***NOTE***
20 |
21 | - 本人在查看官方文档时,有时看懂一个功能需要很长时间。所以每次都会记录下来,也因此萌生了将其总结到一起,编写一个文档的想法。既是自己的学习历程,也希望能帮到更多的初学者。
22 | - 本文档与官方文档结构类似,对每个函数(API)都有书签直接定位,可以作为学习资料,也可作为速查手册。
23 | - 文中添加了很多链接,以紫色字体显示,便于读者快速转到对应的官方页面,进行进一步的了解。
24 | - 文档仍在持续更新中,由于是作者一个人在编写,又限于本人课题压力,无法做到定时更新。
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | 最后,如果你想对本项目做出贡献,为学习者提供便利,欢迎联系我!
35 |
36 | 415091lz@gmail.com
37 |
38 | 仓库地址:https://github.com/liuzhaoo/Pytorch-API-and-Tutorials-CN
39 |
40 |
41 |
42 | # PART 1 API DOCS
43 |
44 | ## TORCH
45 |
46 | ### Tensors
47 |
48 | #### is_tensor
49 |
50 | 判断对象是否为tensor,相当于```is_instance(obj,Tensor)```,返回bool。用法:`is_tensor(obj)`
51 |
52 | ### Creation Ops
53 |
54 | > Random sampling(随机采样)操作在 Random sampling 下,包括 `torch.rand()` `torch.rand_like()` `torch.randn()` `torch.randn_like()` `torch.randint()` `torch.randint_like()` `torch.randperm()`
55 |
56 | #### tensor
57 |
58 | > `torch.tensor(data, dtype=None, device=None, requires_grad=False, pin_memory=False) → Tensor`
59 |
60 | 使用数据来构造一个tensor,此方法会复制数据,如果输入数据是NumPy的`ndarray`,而想避免复制,可使用`torch.as_tensor`
61 |
62 | 如果数据x是tensor,则`torch.tensor(x)` 等价于`x.clone().detach()`,`torch.tensor(x,requires_grad=True)` 等价于`x.clone().detach().requires_grad_(True)` 此时推荐使用后面的方法。
63 |
64 | **Parameters**
65 |
66 | *data* 为要转换的数据 ,可以是列表等数据形式
67 |
68 | *dtype* 为数据类型,*device* 用来指定设备,*requires_grad* 表示是否需要计算梯度
69 |
70 | #### as_tensor
71 |
72 | > `torch.as_tensor(data, dtype=None, device=None) → Tensor`
73 |
74 | 将数据转换为`torch.Tensor` ,如果数据是tensor,而且与此方法指定的 *dtype* 和 *device* 相同,就不会复制tensor(此时应该只 是添加了一个新的指向)
75 |
76 | #### from_numpy
77 |
78 | > `torch.from_numpy(ndarray) → Tensor`
79 |
80 | 将`ndarray`类型的数据转换为tensor,返回的tensor使用原来数据的内存 。对 tensor 的修改会对原来数据产生影响,反之亦然。注意不可对返回的 tensor 做改变形状的操作
81 |
82 | #### zeros/ones
83 |
84 | > `torch.zeros`(**size*, *out=None*, *dtype=None*, *layout=torch.strided*, *device=None*, *requires_grad=False*) → Tensor
85 |
86 | 产生指定size的用0或1填充的tensor,一般只用第一个参数,其他参数默认
87 |
88 | #### zeros_like/ones_like
89 |
90 | > `torch.zeros_like`(*input*, *dtype=None*, *layout=None*, *device=None*, *requires_grad=False*, *memory_format=torch.preserve_format*) → Tensor
91 |
92 | 生成与`input`相同大小的0或1tensor(后面的各种参数也相等),等价于`torch.zeros(input.size(), dtype=input.dtype, layout=input.layout, device=input.device)` 输入是Tensor
93 |
94 | #### arange
95 |
96 | > `torch.arange`(*start=0*, *end*, *step=1*, *out=None*, *dtype=None*, *layout=torch.strided*, *device=None*, *requires_grad=False*) → Tensor
97 |
98 | 返回一个一维的tensor(类似于一维列表),以 *start* 和 *end-1* 为端点,*step* 为步长,若不指定 *start* 则使用默认值0
99 |
100 | **Example:**
101 |
102 | ```python
103 | >>> torch.arange(5)
104 | tensor([ 0, 1, 2, 3, 4])
105 | >>> torch.arange(1, 4)
106 | tensor([ 1, 2, 3])
107 | >>> torch.arange(1, 2.5, 0.5)
108 | tensor([ 1.0000, 1.5000, 2.0000])
109 | ```
110 |
111 | #### range
112 |
113 | 与`arrange` 类似,但是是以*start* 和 *end* 为端点
114 |
115 | #### linspace
116 |
117 | > `torch.linspace`(*start*, *end*, *steps=100*, *out=None*, *dtype=None*, *layout=torch.strided*, *device=None*, *requires_grad=False*) → Tensor
118 |
119 | 生成一维的tensor,其中 *steps* 是返回tensor的元素总数,默认为100,步长计算为$(end-start)\div(steps-1)$
120 |
121 | **Example**
122 |
123 | ```python
124 | >>> torch.linspace(3, 10, steps=5)
125 | tensor([ 3.0000, 4.7500, 6.5000, 8.2500, 10.0000])
126 | >>> torch.linspace(-10, 10, steps=5)
127 | tensor([-10., -5., 0., 5., 10.])
128 | >>> torch.linspace(start=-10, end=10, steps=5)
129 | tensor([-10., -5., 0., 5., 10.])
130 | >>> torch.linspace(start=-10, end=10, steps=1)
131 | tensor([-10.])
132 | ```
133 |
134 | #### logspace
135 |
136 | > `torch.logspace`(*start*, *end*, *steps=100*, *base=10.0*, *out=None*, *dtype=None*, *layout=torch.strided*, *device=None*, *requires_grad=False*) → Tensor
137 |
138 | 生成`steps`size的一维tensor,起点是 $base^{start}$,终点是$base^{end}$ , 步长计算为$base^{(end-start)\div(steps-1)}$
139 |
140 | #### eye
141 |
142 | > `torch.eye`(*n*, *m=None*, *out=None*, *dtype=None*, *layout=torch.strided*, *device=None*, *requires_grad=False*) → Tensor
143 |
144 | 生成二维对角矩阵,n是行数,m为可选参数,用于指定列数,默认等于n‘
145 |
146 | #### empty / empty_like
147 |
148 | 返回未初始化的tensor。`torch.empty((n,m))` `torch.empty_like(input)` input是tensor
149 |
150 | #### quantize_per_tensor
151 |
152 | > `torch.quantize_per_tensor`(*input*, *scale*, *zero_point*, *dtype*) → Tensor
153 |
154 | 将浮点型张量转换为给定scale和零点的量化张量。量化是指以低于浮点精度的位宽存储张量的技术,即可以减小模型尺寸,降低内存带宽要求,通常用于推理过程,因为不支持后向传播
155 |
156 | $$Q(x,scale,zero\_point) = round(\frac{x}{scale} + zero\_point)$$
157 |
158 | ### 索引,切片,连接,变异操作
159 |
160 | #### cat
161 |
162 | > `torch.cat`(*tensors*, *dim=0*, *out=None*) → Tensor
163 |
164 | 在指定的维度(必须是给出的tensor已有的维度)上对给出的tensor进行连接
165 |
166 | **example**
167 |
168 | ```python
169 | >>> x = torch.randn(2, 3)
170 | >>> x
171 | tensor([[ 0.6580, -1.0969, -0.4614],
172 | [-0.1034, -0.5790, 0.1497]])
173 | >>> torch.cat((x, x, x), 0)
174 | tensor([[ 0.6580, -1.0969, -0.4614],
175 | [-0.1034, -0.5790, 0.1497],
176 | [ 0.6580, -1.0969, -0.4614],
177 | [-0.1034, -0.5790, 0.1497],
178 | [ 0.6580, -1.0969, -0.4614],
179 | [-0.1034, -0.5790, 0.1497]])
180 | >>> torch.cat((x, x, x), 1)
181 | tensor([[ 0.6580, -1.0969, -0.4614, 0.6580, -1.0969, -0.4614, 0.6580,
182 | -1.0969, -0.4614],
183 | [-0.1034, -0.5790, 0.1497, -0.1034, -0.5790, 0.1497, -0.1034,
184 | -0.5790, 0.1497]])
185 | ```
186 |
187 | #### chunk
188 |
189 | > `torch.chunk`(*input*, *chunks*, *dim=0*) → List of Tensors
190 |
191 | cat 的反操作:在指定维度将tensor分为 chunks个tensor,若该维度的长度不能整除chunks,则最后一个取最小值.
192 |
193 |
194 |
195 | ## TORCH.NN
196 |
197 | ### Normalization Layers
198 |
199 | #### nn.SyncBatchNorm
200 |
201 | 在N维的输入(一个具有额外的通道维度的N-2 d的mini-batch)中应用Batch Normalization。在多GPU时使用。
202 |
203 | > CLASS `torch.nn.SyncBatchNorm`(*num_features: int*, *eps: float = 1e-05*, *momentum: float = 0.1*, *affine: bool = True*, *track_running_stats: bool = True*, *process_group: Optional[Any] = None*)
204 |
205 | 计算公式 :
206 | $$
207 | y = \frac{x - E(x)}{\sqrt{Var(x)+\epsilon}}\times\gamma + \beta
208 | $$
209 | 均值和标准差是在同一进程组里所有mini-batch的每个维度上计算得来的,$\gamma$和$\beta$是大小为C的向量。默认情况下,$\gamma$在U(0,1)上取样,$\beta$ 为0。标准差通过有偏估计量计算,等价于`torch.var(input, unbiased=False)`。
210 |
211 | 同样在默认情况下,在训练期间,这一层将继续运行其计算的平均值和方差的估计值,然后在评估期间使用这些估计值进行归一化。运行估计保持默认`momentum`为0.1。
212 |
213 | 如果`track_running_stats`设为False,这一层就不会再保留运行的估计值,而在评估期间会使用批处理统计信息。
214 |
215 | 注意,这里的`momentum`与优化器中和卷积层中的动量不同。
216 |
217 | 由于此处的BN是对C维中的每一个通道进行的,计算N个Batch中的(N,+)切片统计量。通常称之为容量Batch Normalization或时空Batch Normalization。
218 |
219 | 当前 [`SyncBatchNorm`](https://pytorch.org/docs/stable/generated/torch.nn.SyncBatchNorm.html#torch.nn.SyncBatchNorm) 只支持每个进程单个GPU的 `DistributedDataParallel` (DDP) ,在使用DDP包装网络之前使用[torch.nn.SyncBatchNorm.convert_sync_batchnorm()](https://pytorch.org/docs/stable/generated/torch.nn.SyncBatchNorm.html#torch.nn.SyncBatchNorm.convert_sync_batchnorm)来将BN层(1d/2d/3d)转换为`SyncBatchNorm`
220 |
221 | **参数**
222 |
223 | - **num_features** -- C (也就是通道数)
224 | - **eps** -- 为数值稳定性而在分母上增加的值($\epsilon$),默认是1e-5
225 | - **momentum** -- 用于running_mean和running_var计算的值。累积移动平均(即简单平均)可设为None,默认为0.1
226 | - **affine** -- 布尔值,决定该模块是否有可学习的仿射参数。默认为True。
227 | - **track_running_stats** -- 布尔值,为True时,此模块跟踪运行时的平均和方差。为False则不跟踪这些统计量,如果运行的平均值和方差都为None,则在训练和eval模式中使用批处理统计信息。
228 | - **process_group** -- 状态同步在每个进程组中内部进行。
229 |
230 | **shape**
231 |
232 | - 输入:(N,C,+)
233 | - 输出:(N,C,+)
234 |
235 |
236 |
237 | 实例:
238 |
239 | ```python
240 | >>> # With Learnable Parameters
241 | >>> m = nn.SyncBatchNorm(100)
242 | >>> # creating process group (optional)
243 | >>> # process_ids is a list of int identifying rank ids.
244 | >>> process_group = torch.distributed.new_group(process_ids)
245 | >>> # Without Learnable Parameters
246 | >>> m = nn.BatchNorm3d(100, affine=False, process_group=process_group)
247 | >>> input = torch.randn(20, 100, 35, 45, 10)
248 | >>> output = m(input)
249 |
250 | >>> # network is nn.BatchNorm layer
251 | >>> sync_bn_network = nn.SyncBatchNorm.convert_sync_batchnorm(network, process_group)
252 | >>> # only single gpu per process is currently supported
253 | >>> ddp_sync_bn_network = torch.nn.parallel.DistributedDataParallel(
254 | >>> sync_bn_network,
255 | >>> device_ids=[args.local_rank],
256 | >>> output_device=args.local_rank)
257 | ```
258 |
259 | > 对象的方法 `convert_sync_batchnorm`(*module*, *process_group=None*)
260 |
261 | 将模型中的BN层都转换为 [`torch.nn.SyncBatchNorm`](https://pytorch.org/docs/stable/generated/torch.nn.SyncBatchNorm.html#torch.nn.SyncBatchNorm) 的函数
262 |
263 | **参数**
264 |
265 | - **module**(([*nn.Module*](https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module)) -- 包含BN层的模型
266 | - **process_group** -- 进行同步的进程组,默认为整个组
267 |
268 | **返回值**
269 |
270 | 包含转换过的BN层的原始模型。
271 |
272 | 示例:
273 |
274 | ```python
275 | >>> # Network with nn.BatchNorm layer
276 | >>> module = torch.nn.Sequential(
277 | >>> torch.nn.Linear(20, 100),
278 | >>> torch.nn.BatchNorm1d(100),
279 | >>> ).cuda()
280 | >>> # creating process group (optional)
281 | >>> # process_ids is a list of int identifying rank ids.
282 | >>> process_group = torch.distributed.new_group(process_ids)
283 | >>> sync_bn_module = torch.nn.SyncBatchNorm.convert_sync_batchnorm(module, process_group)
284 | ```
285 |
286 | ### DataParallel Layers (multi-GPU, distributed)
287 |
288 | #### DataParallel
289 |
290 | > `CLASS torch.nn.DataParallel`(*module*, *device_ids=None*, *output_device=None*, *dim=0*)
291 |
292 | 在模块级别实现数据并行。
293 |
294 | **参数**
295 |
296 | - **module**([*Module*](https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module)) -- 需要并行的模块(通常是整个模型)
297 | - **device_ids**(*list of python:int* *or* [*torch.device*](https://pytorch.org/docs/stable/tensor_attributes.html#torch.torch.device)) -- CUDA 设备(默认为所有设备)
298 | - **output_device**([*int*](https://docs.python.org/3/library/functions.html#int) *or* [*torch.device*](https://pytorch.org/docs/stable/tensor_attributes.html#torch.torch.device)) -- 外部设备(一般用不到)
299 |
300 | **例子**
301 |
302 | ```python
303 | >>> net = torch.nn.DataParallel(model, device_ids=[0, 1, 2])
304 | >>> output = net(input_var) # input_var can be on any device, including CPU
305 | ```
306 |
307 | 此容器通过在batch维度上将输入分布到指定的设备(gpu)上来实现给定模型的并行(其他对象被复制到每个设备中)。在前向传播过程中,每个设备都有一份完整的model,这些副本分别处理分配到的输入数据。在后向传播时,副本中的的梯度被聚集到原始moudle中。因此batchsize应该大于gpu数量
308 |
309 | > ***警告***
310 | >
311 | > 在多GPU训练时,即使只有一个节点,也建议使用
312 | >
313 | > 参见:: [Use nn.parallel.DistributedDataParallel instead of multiprocessing or nn.DataParallel](https://pytorch.org/docs/stable/notes/cuda.html#cuda-nn-ddp-instead) , [Distributed Data Parallel](https://pytorch.org/docs/stable/notes/ddp.html#ddp).
314 |
315 | 更多信息参见官方文档:[LINK](https://pytorch.org/docs/stable/generated/torch.nn.DataParallel.html#torch.nn.DataParallel)
316 |
317 | #### DistributedDataParallel
318 |
319 | 在模块级别实现基于`torch.distributed`的分布式数据并行结构
320 |
321 | 此容器通过batch维度中分组,将输入分割到指定的设备上,从而并行化给定模块的应用程序。模块被复制到每台机器(多节点时)和每台设备上,每个这样的副本处理输入的一部分。在后向传播期间,每个节点的梯度被平均。
322 |
323 | *另请参阅*:[Basics](#基础),[Use nn.parallel.DistributedDataParallel instead of multiprocessing or nn.DataParallel](https://pytorch.org/docs/stable/notes/cuda.html#cuda-nn-ddp-instead)
324 |
325 | 想要创建此类需要先对[torch.distributed.init_process_group()](#初始化)进行初始化
326 |
327 | **以下是使用方法:**
328 |
329 | 在每个有N个GPU 的主机上,都应该创建N个进程。同时确保每个进程分别在从0到N-1的单独的GPU上工作。因此,应该分别指定工作的GPU:
330 |
331 | ```python
332 | >>> torch.cuda.set_device(i) # i为0 - N-1
333 | ```
334 |
335 | 在每个进程中,参考以下内容来构建模块
336 |
337 | ```python
338 | >>> torch.distributed.init_process_group(backend='nccl', world_size=4, init_method='...')
339 | >>> model = DistributedDataParallel(model, device_ids=[i], output_device=i)
340 | ```
341 |
342 | 为了在每个节点上产生多个进程,您可以使用[torch.distributed.launch](#Launch utility)或[torch.multiprocessing.spawn](https://pytorch.org/docs/stable/multiprocessing.html)
343 |
344 | 请参阅[PyTorch分布式概述](#Distributed Overview),了解与分布式培训相关的所有功能。更多信息查看[官方文档](https://pytorch.org/docs/stable/generated/torch.nn.parallel.DistributedDataParallel.html#torch.nn.parallel.DistributedDataParallel)
345 |
346 | **实例**[link](https://pytorch.org/docs/stable/notes/ddp.html#ddp)
347 |
348 | ```python
349 | import torch
350 | import torch.distributed as dist
351 | import torch.multiprocessing as mp
352 | import torch.nn as nn
353 | import torch.optim as optim
354 | from torch.nn.parallel import DistributedDataParallel as DDP
355 |
356 |
357 | def example(rank, world_size):
358 | # create default process group
359 | dist.init_process_group("gloo", rank=rank, world_size=world_size)
360 | # create local model
361 | model = nn.Linear(10, 10).to(rank)
362 | # construct DDP model
363 | ddp_model = DDP(model, device_ids=[rank])
364 | # define loss function and optimizer
365 | loss_fn = nn.MSELoss()
366 | optimizer = optim.SGD(ddp_model.parameters(), lr=0.001)
367 |
368 | # forward pass
369 | outputs = ddp_model(torch.randn(20, 10).to(rank))
370 | labels = torch.randn(20, 10).to(rank)
371 | # backward pass
372 | loss_fn(outputs, labels).backward()
373 | # update parameters
374 | optimizer.step()
375 |
376 | def main():
377 | world_size = 2
378 | mp.spawn(example,
379 | args=(world_size,),
380 | nprocs=world_size,
381 | join=True)
382 |
383 | if __name__=="__main__":
384 | main()
385 | ```
386 |
387 |
388 |
389 | ## torch.distributed
390 |
391 | ### Backends(后端)
392 |
393 | `torch.distributed `支持三种内置后端,它们分别有不同的功能,下表显示哪些函数可用于CPU/CUDA张量。仅当用于构建PyTorch的实现支持时,MPI才支持CUDA。
394 |
395 |
396 |
397 | **Backend** | **gloo** | **mpi** | **nccl**
398 |
399 | | Device | CPU | GPU | CPU | GPU | CPU | GPU |
400 | | :------------: | :--: | :--: | :--: | :--: | :--: | :--: |
401 | | send | ✔ | ✖ | ✔ | ? | ✖ | ✖ |
402 | | recv | ✔ | ✖ | ✔ | ? | ✖ | ✖ |
403 | | broadcast | ✔ | ✔ | ✔ | ? | ✖ | ✔ |
404 | | all_reduce | ✔ | ✔ | ✔ | ? | ✖ | ✔ |
405 | | reduce | ✔ | ✖ | ✔ | ? | ✖ | ✔ |
406 | | all_gather | ✔ | ✖ | ✔ | ? | ✖ | ✔ |
407 | | gather | ✔ | ✖ | ✔ | ? | ✖ | ✖ |
408 | | scatter | ✔ | ✖ | ✔ | ? | ✖ | ✖ |
409 | | reduce_scatter | ✖ | ✖ | ✖ | ✖ | ✖ | ✔ |
410 | | all_to_all | ✖ | ✖ | ✔ | ? | ✖ | ✖ |
411 | | barrier | ✔ | ✖ | ✔ | ? | ✖ | ✔ |
412 |
413 | PyTorch distributed目前只支持Linux。默认情况下,Gloo和NCCL后端是在PyTorch distributed中构建和包含的(只有在使用CUDA构建时才使用NCCL)。MPI是一个可选的后端,只有在从源代码构建PyTorch时才能包含它。(例如,在安装了MPI的主机上构建PyTorch。)
414 |
415 | 一般来说,使用GPU进行分布式训练时,使用NCCL后端
416 |
417 | ### 常用环境变量
418 |
419 | - 选择要使用的网络接口
420 |
421 | 默认情况下,NCCL和Gloo后端都会尝试查找用于通信的网络接口。如果自动检测到的结构不正确,可以可以使用以下环境变量覆盖它(每个变量适用于其各自的后端):
422 |
423 | - **NCCL_SOCKET_IFNAME**, 比如 `export NCCL_SOCKET_IFNAME=eth0`
424 | - **GLOO_SOCKET_IFNAME**, 比如 `export GLOO_SOCKET_IFNAME=eth0`
425 |
426 | 如果使用Gloo后端,可以指定多个接口,用逗号分隔它们:`export GLOO_SOCKET_IFNAME=eth0,eth1,eth2,eth3`后端将以循环方式跨这些接口分派操作。所有进程必须在此变量中指定相同数量的接口。
427 |
428 | - 其他NCCL环境变量
429 |
430 | - NCCL还提供了许多用于微调目的的环境变量
431 |
432 | 常用的包括以下用于调试目的:
433 |
434 | - `export NCCL_DEBUG=INFO`
435 | - `export NCCL_DEBUG_SUBSYS=ALL`
436 |
437 | 有关NCCL环境变量的完整列表,请参阅[NVIDIA NCCL的官方文档](https://docs.nvidia.com/deeplearning/sdk/nccl-developer-guide/docs/env.html)
438 |
439 | ### 基础
440 |
441 | `torch.distributed`包为在一台或多台机器上运行的多个计算节点上的多进程并行结构提供PyTorch支持和通信原语。 [torch.nn.parallel.DistributedDataParallel()](https://pytorch.apachecn.org/docs/1.2/nn.html#torch.nn.parallel.DistributedDataParallel)类就是基于此功能构建的,作为任何PyTorch模型的包装来提供同步分布式训练。这不同于 [Multiprocessing package - torch.multiprocessing](https://pytorch.org/docs/stable/multiprocessing.html) 和 [torch.nn.DataParallel()](https://pytorch.org/docs/stable/generated/torch.nn.DataParallel.html#torch.nn.DataParallel) 提供的并行结构,因为它支持多台联网的机器而且用户必须显式地为每个进程启动主要训练脚本的副本。
442 |
443 | 在单机情况下, `torch.nn.parallel.DistributedDataParallel()`与其他数据并行方式相比,仍然具有优势:
444 |
445 | - 每个进程都有其对应的优化器(optimizer)并且在每次迭代时都执行完整的优化步骤,虽然这看起来是多余的,但是因为各个进程之间的梯度已经收集到一起并平均,因此对于每个进程都是相同的,这意味着不需要参数广播步骤,减少了节点(GPU或主机)间传输张量的时间。
446 | - 每个进程都包含一个独立的Python解释器,消除了额外的解释器开销和来自单个Python进程驱动多个执行线程,模型副本或GPU的“GIL-thrashing”。这对于大量使用Python运行时的模型尤其重要,包括具有循环层或许多小组件的模型
447 |
448 | ### 初始化
449 |
450 | 在调用其他任何方法之前,需要用`torch.distributed.init_process_group()` 对此包进行初始化,这将阻止所有进程加入。
451 |
452 | > `torch.distributed.is_available`()
453 |
454 | 若返回 True 则证明分布式包可以使用。目前,`torch.distributed`支持Linux和Macos。当从源码构建Pytorch时设置`USE_DISTRIBUTED=1` Linux的默认值为1,Macos的默认值为0
455 |
456 | > `torch.distributed.init_process_group`(*backend*, *init_method=None*, *timeout=datetime.timedelta(0*, *1800)*, *world_size=-1*, *rank=-1*, *store=None*, *group_name=''*)
457 |
458 | 初始化默认的分布式进程组,这也将同时初始化分布式包
459 |
460 | **初始化进程组的方式有两种**
461 |
462 | 1. 明确指定`store`,`rank`,以及`world_size`
463 |
464 | 2. 指定`init_method`(一个URL字符串),它指示在哪里/如何发现对等点,可以选择指定rank和world_size,或者在URL中 编码所有必需的参数并省略它们。
465 |
466 | **如果两者都没有指定,则假设`init_method`为' 'env://''。**
467 |
468 | **参数**
469 |
470 | ***backend***(字符串或后端端口名称),用到的后端。取决于构建时的配置,有效值包括`mpi`,`gloo`,`nccl` ,应该为小写的形 式,也可以通过后端属性访问,比如`Backend.GLOO` 。如果在每台机器上通过`nccl`后端来使用多个进程,每个进程必须独占 访问它使用的每个GPU,因为进程之间共享GPU会导致锁死。
471 |
472 | ***init_method*** (字符串,可选),指定如何初始化流程组的URL。如果没有指定`init_method`或`store`,默认为"env://"。与
473 | `store`相互排斥
474 |
475 | ***world_size*** (整数,可选), 参与工作的进程数,若指定了`store`,则此项是必须的。
476 |
477 | ***rank***(整数,可选),当前进程的排名,若指定了`store`,则此项是必须的。
478 |
479 | ***store*** (存储,可选),所有任务都可访问的键值对,用来交换连接/地址信息。与`init_method`互斥
480 |
481 | ***timout***(时间间隔对象,可选),针对进程组执行的操作超时,默认值等于30分钟,这仅适用于`gloo`后端对于。`nccl`,只 有在环境变量`NCCL_BLOCKING_WAIT`被设置为1时才适用。
482 |
483 | ***group_name*** (字符串,可选,已弃用) 组名称。
484 |
485 | > `CLASS` torch.distributed.Backend
486 |
487 | 可用后端的类似于枚举的类:GLOO,NCCL,MPI,以及其他注册的后端。
488 |
489 | 这个类的值时小写字符串,比如"gloo"。可以将它们看作属性来进行访问:`Backend.NCCL`
490 |
491 | 可以直接使用此类来解析字符串,比如,`Backend(backend_str)` 会检查`backend_str`是否有效,如果是,会返回解析后的小写字符串。也可以接受大写字符串。`Backend("GLOO")`返回"`gloo`"
492 |
493 | > `torch.distributed.get_backend`(*group= < object object>*)
494 |
495 | 返回给定进程组的后端
496 |
497 | > `torch.distributed.get_backend`(*group= < object object>*)
498 |
499 | 返回当前进程组的rank
500 |
501 | Rank是分配给分布式进程组中的每个进程的唯一标识符。它们是从0到world_size的连续整数。
502 |
503 | > `torch.distributed.get_backend`(*group= < object object>*)
504 |
505 | 返回当前进程组中的进程数
506 |
507 | > `torch.distributed.is_initialized`()
508 |
509 | 检查默认进程组是否已初始化
510 |
511 | > `torch.distributed.is_nccl_available`()
512 |
513 | 检查NCCL后端是否可用
514 |
515 | **目前支持三种初始化方式**
516 |
517 | ### TCP 初始化
518 |
519 | 使用TCP进行初始化的方法有两种,都需要一个所有进程都可以访问的网络地址和一个设定好的的`world_size`。
520 |
521 | 第一种方法需要指定一个属于rank0进程的地址。此初始化方法要求所有进程都手动指定rank。
522 |
523 | 注意,在最新的分布式包中不再支持多播地址。也不赞成使用`group_name`。
524 |
525 | 不同进程内,均使用主进程的 `ip` 地址和 `port`,确保每个进程能够通过一个 `master` 进行协作。该 `ip` 一般为主进程所在的主机的 `ip`,端口号应该未被其他应用占用。实际使用时,在每个进程内运行代码,并需要为每一个进程手动指定一个 `rank`,进程可以分布于相同或不同主机上。
526 |
527 | ```python
528 | import torch.distributed as dist
529 |
530 | # Use address of one of the machines
531 | dist.init_process_group(backend, init_method='tcp://10.1.1.20:23456', rank=args.rank, world_size=4)
532 | ```
533 |
534 | ### 共享文件初始化
535 |
536 | 另一种初始化方法使用一个文件系统,该文件系统与所需的`world_size` 都可以被同组组中的所有机器共享并可见。URL应以`file://`开头,并包含共享文件系统上不存在的文件(在现有目录中)的路径。如果文件不存在,文件系统初始化将自动创建该文件,但不会删除该文件。因此,下一次在相同的文件路径中初始化 [`init_process_group()`](https://pytorch.apachecn.org/docs/1.2/distributed.html#torch.distributed.init_process_group) 之前,应该确保已经清理了文件。
537 |
538 | ```python
539 | import torch.distributed as dist
540 |
541 | # rank should always be specified
542 | dist.init_process_group(backend, init_method='file:///mnt/nfs/sharedfile',
543 | world_size=4, rank=args.rank)
544 | ```
545 |
546 | ### 环境变量初始化
547 |
548 | 此方法将从环境变量中读取配置,从而可以完全自定义信息的获取方式。要设置的变量是:
549 |
550 | - `MASTER_PORT` - 需要;,必须是机器上的rank为0的空闲端口,。
551 | - `MASTER_ADDR` - 需要,(0级除外); rank 0节点的地址。
552 | - `WORLD_SIZE` - 需要,可以在这里设置,也可以在调用init函数时设置。
553 | - `RANK` - 需要,可以在这里设置,也可以在调用init函数时设置。
554 |
555 | 等级为0的机器将用于设置所有连接。
556 |
557 | 这是默认方法,意味着不必指定`init_method`(或者可以是`env://`)。
558 |
559 | ### GROUPS 组
560 |
561 | 默认情况下,集合在默认组(也叫world)上运行,并要求所有进程进入分布式函数调用。然而,一些工作负载可以从更具细粒度的通信中受益,这就是分布式组发挥作用的地方。`new_group` 函数可以用来创建一个新的组,,这个组具有所有进程的任意子集。它返回一个不透明的组句柄,可以将其作为`group`的参数提供给所有集合(在某些众所周知的编程模式中,集合是交换信息的分布式函数)
562 |
563 | > `torch.distributed.new_group`(*ranks=None*, *timeout=datetime.timedelta(0*, *1800)*, *backend=None*)
564 |
565 | 创建一个新的分布式组
566 |
567 | 此函数要求主组中的所有进程(即属于分布式作业的所有进程)都进入此函数,即使它们不是该组(新建的组)的成员。此外,应在所有进程中以相同的顺序创建组。
568 |
569 | ### 点对点(P2P)通信
570 |
571 | > `torch.distributed.send`(*tensor*, *dst*, *group=