0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

DataParallel里为什么会显存不均匀以及如何解决

深度学习自然语言处理 来源:深度学习自然语言处理 作者:台运鹏 2022-12-14 10:36 次阅读

鉴于网上此类教程有不少模糊不清,对原理不得其法,代码也难跑通,故而花了几天细究了一下相关原理和实现,欢迎批评指正!

关于此部分的代码,可以去https://github.com/sherlcok314159/dl-tools查看

「在开始前,我需要特别致谢一下一位挚友,他送了我双显卡的机器来赞助我做个人研究,否则多卡的相关实验就得付费在云平台上跑了,感谢好朋友一路以来的支持,这份恩情值得一辈子铭记!这篇文章作为礼物赠与挚友。」

Why Parallel

我们在两种情况下进行并行化训练[1]:

「模型一张卡放不下」:我们需要将模型不同的结构放置到不同的GPU上运行,这种情况叫ModelParallel(MP)

「一张卡的batch size(bs)过小」:有些时候数据的最大长度调的比较高(e.g., 512),可用的bs就很小,较小的bs会导致收敛不稳定,因而将数据分发到多个GPU上进行并行训练,这种情况叫DataParallel(DP)。当然,DP肯定还可以加速训练,常见于大模型的训练中

这里只讲一下DP在pytorch中的原理和相关实现,即DataParallel和DistributedParallel

Data Parallel

实现原理

实现就是循环往复一个过程:数据分发,模型复制,各自前向传播,汇聚输出,计算损失,梯度回传,梯度汇聚更新,可以参见下图[2]:

b84b1762-7acd-11ed-8abf-dac502259ad0.png

pytorch中部分关键源码[3]截取如下:

defdata_parallel(
module,
input,
device_ids,
output_device=None
):
ifnotdevice_ids:
returnmodule(input)

ifoutput_deviceisNone:
output_device=device_ids[0]

#复制模型
replicas=nn.parallel.replicate(module,device_ids)
#拆分数据
inputs=nn.parallel.scatter(input,device_ids)
replicas=replicas[:len(inputs)]
#各自前向传播
outputs=nn.parallel.parallel_apply(replicas,inputs)
#汇聚输出
returnnn.parallel.gather(outputs,output_device)

代码使用

因为运行时会将数据平均拆分到GPU上,所以我们准备数据的时候, batch size = per_gpu_batch_size * n_gpus

同时,需要注意主GPU需要进行汇聚等操作,因而需要比单卡运行时多留出一些空间

importtorch.nnasnn
#device_ids默认所有可使用的设备
#output_device默认cuda:0
net=nn.DataParallel(model,device_ids=[0,1,2],
output_device=None,dim=0)
#input_varcanbeonanydevice,includingCPU
output=net(input_var)

接下来看个更详细的例子[4],需要注意的是被DP包裹之后涉及到模型相关的,需要调用DP.module,比如加载模型

classModel(nn.Module):
#Ourmodel
def__init__(self,input_size,output_size):
super(Model,self).__init__()
#forconvenience
self.fc=nn.Linear(input_size,output_size)

defforward(self,input):
output=self.fc(input)
print("	InModel:inputsize",input.size(),
"outputsize",output.size())
returnoutput

bs,input_size,output_size=6,8,10
#defineinputs
inputs=torch.randn((bs,input_size)).cuda()
model=Model(input_size,output_size)
iftorch.cuda.device_count()>1:
print("Let'suse",torch.cuda.device_count(),"GPUs!")
#dim=0[6,xxx]->[2,...],[2,...],[2,...]on3GPUs
model=nn.DataParallel(model)
#先DataParallel,再cuda
model=model.cuda()
outputs=model(inputs)
print("Outside:inputsize",inputs.size(),
"output_size",outputs.size())
#assume2GPUSareavailable
#Let'suse2GPUs!
#InModel:inputsizetorch.Size([3,8])outputsizetorch.Size([3,10])
#InModel:inputsizetorch.Size([3,8])outputsizetorch.Size([3,10])
#Outside:inputsizetorch.Size([6,8])output_sizetorch.Size([6,10])

#savethemodel
torch.save(model.module.state_dict(),PATH)
#loadagain
model.module.load_state_dict(torch.load(PATH))
#doanythingyouwant

如果经常使用huggingface,这里有两个误区需要小心:

#dataparallelobjecthasnosave_pretrained
model=xxx.from_pretrained(PATH)
model=nn.DataParallel(model).cuda()
model.save_pretrained(NEW_PATH)#error
#因为model被DPwrap了,得先取出模型#
model.module.save_pretrained(NEW_PATH)
#HF实现貌似是返回N个loss(N为GPU数量)
#然后对N个loss取mean
outputs=model(**inputs)
loss,logits=outputs.loss,outputs.logits
loss=loss.mean()
loss.backward()

#返回的logits是汇聚后的
#HF实现和我们手动算loss有细微差异
#手动算略好于HF
loss2=loss_fct(logits,labels)
assertloss!=loss2
True

显存不均匀

了解前面的原理后,就会明白为什么会显存不均匀。因为GPU0比其他GPU多了汇聚的工作,得留一些显存,而其他GPU显然是不需要的。那么,解决方案就是让其他GPU的batch size开大点,GPU0维持原状,即不按照默认实现的平分数据

首先我们继承原来的DataParallel(此处参考[5])),这里我们给定第一个GPU的bs就可以,这个是实际的bs而不是乘上梯度后的。假如你想要总的bs为64,梯度累积为2,一共2张GPU,而一张最多只能18,那么保险一点GPU0设置为14,GPU1是18,也就是说你DataLoader每个batch大小是32,gpu0_bsz=14

classBalancedDataParallel(DataParallel):
def__init__(self,gpu0_bsz,*args,**kwargs):
self.gpu0_bsz=gpu0_bsz
super().__init__(*args,**kwargs)

核心代码就在于我们重新分配chunk_sizes,实现思路就是将总的减去第一个GPU的再除以剩下的设备,源码的话有些死板,用的时候不妨参考我的[6]

defscatter(self,inputs,kwargs,device_ids):
#不同于源码,获取batchsize更加灵活
#支持只有kwargs的情况,如model(**inputs)
iflen(inputs)>0:
bsz=inputs[0].size(self.dim)
elifkwargs:
bsz=list(kwargs.values())[0].size(self.dim)
else:
raiseValueError("Youmustpassinputstothemodel!")

num_dev=len(self.device_ids)
gpu0_bsz=self.gpu0_bsz
#除第一块之外每块GPU的bsz
bsz_unit=(bsz-gpu0_bsz)//(num_dev-1)
ifgpu0_bsz< bsz_unit:
        # adapt the chunk sizes
        chunk_sizes = [gpu0_bsz] + [bsz_unit] * (num_dev - 1)
        delta = bsz - sum(chunk_sizes)
        # 补足偏移量
        # 会有显存溢出的风险,因而最好给定的bsz是可以整除的
        # e.g., 总的=52 =>bsz_0=16,bsz_1=bsz_2=18
#总的=53=>bsz_0=16,bsz_1=19,bsz_2=18
foriinrange(delta):
chunk_sizes[i+1]+=1
ifgpu0_bsz==0:
chunk_sizes=chunk_sizes[1:]
else:
returnsuper().scatter(inputs,kwargs,device_ids)

returnscatter_kwargs(inputs,kwargs,device_ids,chunk_sizes,dim=self.dim)

优缺点

优点:便于操作,理解简单

缺点:GPU分配不均匀;每次更新完都得销毁「线程」(运行程序后会有一个进程,一个进程可以有很多个线程)重新复制模型,因而速度慢

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • gpu
    gpu
    +关注

    关注

    27

    文章

    4415

    浏览量

    126663
  • 显卡
    +关注

    关注

    16

    文章

    2356

    浏览量

    65846
  • pytorch
    +关注

    关注

    2

    文章

    760

    浏览量

    12828

原文标题:DataParallel里为什么会显存不均匀以及如何解决

文章出处:【微信号:zenRRan,微信公众号:深度学习自然语言处理】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    不均匀性的定义 PPT学习资料

    不均匀性的定义可以这样说:组成系统是由传输线+功能电路,这中间遇到大量的不均匀性或者说不连续性。对于不均性的研究有两个方面,不均匀性分析方
    发表于 11-02 17:13

    矩阵式LED的显示亮度不均匀

    亮的效果(比如点亮LED2 LED 4 LED6 LED8 LED11 LED12)刷新6次;然后,发现亮度不够,并且显示起来亮度不均匀,然后又尝试了行列式点亮,也是亮度不均匀,其实单独做个LED显示
    发表于 12-17 08:41

    在同步设计中使用占空比不均匀的时钟是否可行

    喜 在同步设计中使用占空比不均匀的时钟是否可行,我使用这些时钟在上升沿和下降沿执行操作。我使用这些脉冲作为总是块的时钟。请建议。谢谢,维沙尔以上来自于谷歌翻译以下为原文hi
    发表于 01-30 08:03

    VHDL小源程序平均频率输出不均匀

    一.积分分频(小数分频)注:只是对平均频率,输出不均匀。library ieee; use ieee.std_logic_1164.all; use
    发表于 02-20 06:35

    十倍频电路仿真出来的波形不均匀

    这是一个十倍频的仿真电路,仿真出来的波形不均匀,请问一下图中的C1、R2、R3、R4怎么确定啊?
    发表于 04-17 04:38

    何解决厚铜PCB电路板铜厚度不均匀的问题呢?

    何解决厚铜PCB电路板铜厚度不均匀的问题呢?
    发表于 04-11 14:31

    基于不均匀密度的自动聚类算法

    针对基于密度的聚类算法不能自动处理密度分布不均匀的数据问题,提出一种基于不均匀密度的自动聚类算法。该算法既保持了一般基于密度算法的优点,也能有效地处理分布不均
    发表于 04-09 09:39 16次下载

    为什么数码管亮度不均匀

    为什么数码管亮度不均匀?  关于亮度一致性的问题是一个行业内的常见问题。  有二个大的因素影响到亮度一致性。  一是使用原材料芯片的选
    发表于 11-06 11:44 5651次阅读

    RTC 计秒不均匀

    STM32F103RCT6的RTC 计秒不均匀
    发表于 12-07 18:13 0次下载

    简单分析光纤激光打标机打标效果不均匀的原因

    光纤激光打标机打标效果不均匀的原因分析。
    的头像 发表于 12-25 13:15 575次阅读

    机器人静电喷涂不均匀的原因有哪些

    因种种原因机器人静电喷涂有时候也有喷涂不均匀的情况,下面分享一下机器人静电喷涂不均匀的原因。
    发表于 12-26 08:27 651次阅读

    厚度不均匀的工件点焊时出现熔核偏移应如何调整

    点焊机以焊接效率高以及点焊品质优良而成为汽摩装配行业以及电子电器加工行业的重要应用设备之一。随着其应用率的不断高涨,市场上涌现了一批又一批专业的点焊机,现在点焊机厂家就厚度不均匀的工件点焊时出现
    发表于 11-02 17:58 1334次阅读

    立磨磨辊磨损不均匀及磨辊与辊皮间隙变大了怎么解决

    立磨磨辊磨损不均匀怎么修补?磨辊与辊皮间隙变大了怎么办?
    发表于 09-19 15:50 0次下载

    浅谈连接器电镀不均匀的原因

    很多连接器都需要电镀,但是有些连接器电镀后会出现电镀不均匀的情况。我们来看看原因。
    的头像 发表于 12-07 14:18 1135次阅读

    锡膏点胶时拉丝不均匀,如何解决?

    时就出现拉丝不均匀,那么如何解决呢?接下来深圳佳金源锡膏厂家为大家讲解一下:在点胶的过程中比较容易出现的问题就是拉丝,可以采取以下几种措施:1、设置开胶延时。由于胶嘴
    的头像 发表于 04-20 16:03 42次阅读
    锡膏点胶时拉丝<b class='flag-5'>不均匀</b>,如<b class='flag-5'>何解</b>决?