Pytorch之Dataparallel源码解析
之前对Pytorch 1.0 的Dataparallel的使用方法一直似懂非懂,总是会碰到各种莫名其妙的问题,今天就好好从源头梳理一下,更好地理解它的原理或者说说下步骤。
源码地址: https://github.com/pytorch/pytorch/blob/master/torch/nn/parallel/data_parallel.py
初始化
首先我们一行一行地来看一下Dataparallel是如何初始化的。
super
就是继承torch.nn.Module父类,这里不做解释- 第一个if判断语句:检查是否有可用GPU
- 第二个if判断语句:如果没有指定GPU,则默认使用所有可用的GPU
- 第三个if判断语句:
output_device
表示输出到哪一个GPU上,默认是第一个GPU,注意这个第一个是device_ids列表上的第一个,所以如果你有三个GPU,而你在将model复制到cuda上时写的代码是model.cuda(1)
或者model.cuda(2)
,则会报错,因为device_ids
是[0,1,2].其第一个元素是0。这一点可以在后面的forward
函数中看到。 - emm,后面每行代码的作用很清楚,就不再一一解释了。
def __init__(self, module, device_ids=None, output_device=None, dim=0):
super(DataParallel, self).__init__()
if not torch.cuda.is_available():
self.module = module
self.device_ids = []
return
if device_ids is None:
device_ids = list(range(torch.cuda.device_count()))
if output_device is None:
output_device = device_ids[0]
self.dim = dim
self.module = module
self.device_ids = list(map(lambda x: _get_device_index(x, True), device_ids))
self.output_device = _get_device_index(output_device, True)
self.src_device_obj = torch.device("cuda:{}".format(self.device_ids[0]))
_check_balance(self.device_ids)
if len(self.device_ids) == 1:
self.module.cuda(device_ids[0])
前向传播
下面进入到重头戏:Dataparallel的forward函数。
def forward(self, *inputs, **kwargs):
if not self.device_ids:
return self.module(*inputs, **kwargs)
for t in chain(self.module.parameters(), self.module.buffers()):
if t.device != self.src_device_obj:
raise RuntimeError("module must have its parameters and buffers "
"on device {} (device_ids[0]) but found one of "
"them on device: {}".format(self.src_device_obj, t.device))
inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids)
if len(self.device_ids) == 1:
return self.module(*inputs[0], **kwargs[0])
replicas = self.replicate(self.module, self.device_ids[:len(inputs)])
outputs = self.parallel_apply(replicas, inputs, kwargs)
return self.gather(outputs, self.output_device)
- 第一个if判断语句:如果没有可用的GPU设备,则使用原来的module进行计算。
- for循环就是对应了前面提到的问题,用于检查model和input是不是放在第一个GPU上
- 之后下一步就是将将input平均划分到每个GPU上,用到的是下面的
scatter
函数
def scatter(inputs, target_gpus, dim=0):
r"""
Slices tensors into approximately equal chunks and
distributes them across given GPUs. Duplicates
references to objects that are not tensors.
"""
def scatter_map(obj):
if isinstance(obj, torch.Tensor):
return Scatter.apply(target_gpus, None, dim, obj)
if isinstance(obj, tuple) and len(obj) > 0:
return list(zip(*map(scatter_map, obj)))
if isinstance(obj, list) and len(obj) > 0:
return list(map(list, zip(*map(scatter_map, obj))))
if isinstance(obj, dict) and len(obj) > 0:
return list(map(type(obj), zip(*map(scatter_map, obj.items()))))
return [obj for targets in target_gpus]
# After scatter_map is called, a scatter_map cell will exist. This cell
# has a reference to the actual function scatter_map, which has references
# to a closure that has a reference to the scatter_map cell (because the
# fn is recursive). To avoid this reference cycle, we set the function to
# None, clearing the cell
try:
res = scatter_map(inputs)
finally:
scatter_map = None
return res
- 数据划分之后呢,再判断一下有几个可用的GPU(前面是判断有没有,这里是判断有几个),如果只有一个GPU,那就不用进入到下一步了。
- 如果有多个GPU,那么就需要用到
replica
函数,这个函数比较复杂,就不解释了,感兴趣的可以阅读一下源码:https://github.com/pytorch/pytorch/blob/master/torch/nn/parallel/replicate.py 。不过它的主要作用就是将模型复制到多个GPU上。 - 下一步中的
parallel_apply
作用就是并行地在多个GPU上计算模型,每个模型是一样的,只不过输入数据是不一样的,因为前面将数据平均划分了。例如你有两个GPU,一个batch大小是64,那么两个GPU分别处理batch大小为32的数据。 - 最后就是将输出值
gather
到一起,传送到output_device
,即第一个GPU设备上。
Pytorch之Dataparallel源码解析的更多相关文章
- [源码解析] PyTorch 分布式(2) ----- DataParallel(上)
[源码解析] PyTorch 分布式(2) ----- DataParallel(上) 目录 [源码解析] PyTorch 分布式(2) ----- DataParallel(上) 0x00 摘要 0 ...
- [源码解析] PyTorch 分布式(3) ----- DataParallel(下)
[源码解析] PyTorch 分布式(3) ----- DataParallel(下) 目录 [源码解析] PyTorch 分布式(3) ----- DataParallel(下) 0x00 摘要 0 ...
- [源码解析] PyTorch 分布式(1) --- 数据加载之DistributedSampler
[源码解析] PyTorch 分布式(1) --- 数据加载之DistributedSampler 目录 [源码解析] PyTorch 分布式(1) --- 数据加载之DistributedSampl ...
- [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader
[源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader 目录 [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader 0x00 摘要 0x01 ...
- [源码解析] PyTorch 流水线并行实现 (1)--基础知识
[源码解析] PyTorch 流水线并行实现 (1)--基础知识 目录 [源码解析] PyTorch 流水线并行实现 (1)--基础知识 0x00 摘要 0x01 历史 1.1 GPipe 1.2 t ...
- [源码解析] PyTorch 流水线并行实现 (5)--计算依赖
[源码解析] PyTorch 流水线并行实现 (5)--计算依赖 目录 [源码解析] PyTorch 流水线并行实现 (5)--计算依赖 0x00 摘要 0x01 前文回顾 0x02 计算依赖 0x0 ...
- [源码解析] PyTorch 分布式(1)------历史和概述
[源码解析] PyTorch 分布式(1)------历史和概述 目录 [源码解析] PyTorch 分布式(1)------历史和概述 0x00 摘要 0x01 PyTorch分布式的历史 1.1 ...
- [源码解析] PyTorch 如何使用GPU
[源码解析] PyTorch 如何使用GPU 目录 [源码解析] PyTorch 如何使用GPU 0x00 摘要 0x01 问题 0x02 移动模型到GPU 2.1 cuda 操作 2.2 Modul ...
- [源码解析] PyTorch 分布式(4)------分布式应用基础概念
[源码解析] PyTorch 分布式(4)------分布式应用基础概念 目录 [源码解析] PyTorch 分布式(4)------分布式应用基础概念 0x00 摘要 0x01 基本概念 0x02 ...
随机推荐
- Android Studio 之 Navigation【1.页面之间的切换】
1.创建 2个 Fragment ,下面两个include 不要勾 2.创建好 Fragment 后,打开layout中的 fragment.xml 文件,将里面默认的 textView 控件删除掉 ...
- 20165230田坤烨网络对抗免考报告_Windows系统提权
目录 KERNEL EXPLOITATION 服务攻击: DLL劫持 攻击 不安全的服务权限 探测 unquoted path未被引号标记的路径 探测 攻击 服务注册表键 探测 攻击 Named Pi ...
- centos删除指定名称的进程
vim kill_process.sh ----------------------------------- if [ "$1" = "" ]; then e ...
- vue-router学习笔记(一)
学习vue-router首先要认识的两个属性 $router 和 $route. $router指的是router实例,$route则是当前激活的路由信息对象,是只读属性,不可更改,但是可以watch ...
- Sitecore 8.2 数据库权限设置
在我的一个项目中,客户决定改变基础设施.在这个过程中,我得到了一些新的东西需要学习.在本文中,我将分享有关Sitecore数据库权限的经验. 在将数据库从一个服务器移动到另一个服务器时,您需要检查提供 ...
- C++完全二叉树的权值
#include<stdio.h> #include<stdlib.h> #include<math.h> #include<string.h> int ...
- 【转】Mac入门(一)基本用法
我前五年一直外包到微软,每天使用的都是Windows系统和.NET. 2012年加入VMware, 公司的工作机是台Mac 笔记本(MacBook Pro), 所以有机会接触Mac系统 Mac和Wi ...
- 什么是k8s
•Kubernetes介绍 1.背景介绍 云计算飞速发展 - IaaS - PaaS - SaaS Docker技术突飞猛进 - 一次构建,到处运行 - 容器的快速轻量 - 完整的生态环境 2.什么是 ...
- windows10结束进程
.net winfrom 程序关于结束进程触发事件 在任务管理器中有进程.详细信息栏 在进程栏对应用程序结束任务,会触发应用程序窗体的FormClosed事件 在详细信息栏对应用程序结束任务,不会触发 ...
- 防止用iframe调用网页dom元素
<system.webServer> <httpProtocol> <customHeaders> <add name="X-Frame-Optio ...