Paddle CPU&GPU混合训练时,结果不符合预期

llew8vvj  于 2021-12-07  发布在  Java
关注(0)|答案(16)|浏览(599)

假设一个执行流程为op1->op2->op3->op4,且整个过程在op1,op2,op4在GPU上跑,op3由于没有GPU的实现,在CPU上跑。假设op2的输出为a,op3的输入和输出也均为a(比如scale op)。在framework/operator.cc的OperatorWithKernel::RunImpl函数,先调用PrepareData将a从gpu上转化为cpu上的数据,且换到runtime_ctx的input中,且由于op3的输入输出均为a,transferd_inplace_vars中包含了a的变量。在op3的执行过程中,从Input中获取了转化后的a的变量,从output中获取输出的a的变量(与输入不是同一个变量),output中的a会重新分配内存,op3的计算结果放到了output的3中。
op3执行结束后,调用TransferInplaceVarsBack想把op3中output拷贝回原来的scope中,但是在拷贝回去的时候,获得的是op3中input的a,导致op3的结果丢失。
碰到的错误如下图所示,图表示的是sum op后面接着一个momentum,momentum利用sum的结果更新权重。第一个红色框中3个数表示sum op需要的输入a, b和结果c。第2个红框表示momentum使用的sum的结果。

这边还有一个问题是:PrepareData和TransferInplaceVarsBack这套机制是使用在什么场景中?什么样情况下能工作正确?

c90pui9n

c90pui9n1#

您好!目前官方版本的 Paddle 还不支持使用 XPU 训练。请用标准 Paddle Fluid 版本复现问题后,将问题的复现环境和脚本提供给我们,以便跟进调试。谢谢!

sigwle7e

sigwle7e2#

补充一下,上面晓伟说的XPU,这个issue问题虽然是CPU&GPU混合训练时,但是看log是在做cpu和xpu之间的切换?因为我们还不支持XPU训练,如果需要某种支持(如将GPU改XPU等),请进一步提供更具体的复现条件或者需求给paddle组?

r7s23pms

r7s23pms3#

我觉得不用纠结于是不是支持XPU,用户现在只是想问PrepareData和TransferInplaceVarsBack机制是怎样的,希望能有熟悉paddle fluid框架的大佬来回复下。

ars1skjm

ars1skjm4#

PrepareData就是从scope里拿出op run所需的变量

TransferInplaceVarsBack,是为了在做DataTransform后(data transform 是做比如CPU -> GPU, CPU->XPU 一类的,DataTransform会new一个新的scope),把output和input同名的变量assign回父scope里去

a11xaf1n

a11xaf1n5#

补充一下,在PrepareData中可能会进行data transform:当输入数据的dtype、所在的设备、layout与期望执行的Kernel不一致时,就会对输入数据做transform。比如输入数据在CPU上,设置执行的place是GPU,那么将会执行Op的GPU实现(如果有),这种场景下需要将数据从CPU拷贝到GPU。从上面的log来看,输入数据在CPU,而要执行的place是XPU。如果要进行data transform,就会new一个新的scope,称之为transfer_scope,在transfer_scope中存放了需要transform的变量。PrepareData会返回这个transfer_scope。

对于支持inplace的Op而言。如果进行了data transform,Op运行时实际使用的输入就在transfer_scope中,地址发生了变化,runtime_ctx的input与transfer_scope是关联的,所以从runtime_ctx获取的输入就是transform后的。Op计算完,既然是将结果放在输入所在地址,那么也就相当于放在transfer_scope中。但原来的scope中输出没有修改到,所以就需要TransferInplaceVarsBack,将inplace_vars放回原来的scope中。

以上是PrepareData和TransferInplaceVarsBack的使用场景。

ao218c7q

ao218c7q6#

@zhangting2020 谢谢回复。但是问题出现在Op计算完,既然是将结果放在输入所在地址,那么也就相当于放在transfer_scope中。实际上Op计算完,并没有把结果放在输入所在的地址,而是放在输出所在新分配的地址。所以TransferInplaceVarsBack时,把输入的数据放回到原来的scope中了。
比如看SumKernel的实现,out_var是从context.OutputVar中查找,保存计算结果,与transfer_scope中的input没有关系。

yqyhoc1h

yqyhoc1h7#

@dashulu 需要纠正下前面关于的TransferInplaceVarsBack说法,或许描述有些误差。
在PrepareData中如果Op的输入输出变量同名,同时输入需要transform,就会记录在transferd_inplace_vars中。TransferInplaceVarsBack将transferd_inplace_vars放回原来的scope中。

我尝试按照你描述的场景,将sum的输入放在CPU上,设置GPU执行,sum op的输入输出变量并不是同名的,也不会发生TransferInplaceVarsBack。

import paddle.fluid as fluid
import numpy as np

data1 = fluid.layers.fill_constant(shape=[1], value=0, dtype='int64', force_cpu=True)
data2 = fluid.layers.fill_constant(shape=[1], value=5, dtype='int64', force_cpu=True)
output = fluid.layers.sum([data1, data2])
exe = fluid.Executor(fluid.CUDAPlace(0))
exe.run(fluid.default_startup_program())
res = exe.run(fluid.default_main_program(), fetch_list=[output])
print(res)

从你的log来看,sum op的输入输出变量是同名的,所以才在计算完后发生了TransferInplaceVarsBack,这与预期并不一致。

所以建议提供一下可以复现问题的脚本和你所使用的Paddle版本。

tjjdgumg

tjjdgumg8#

可以设置data1和output的名字相同吗?
使用paddle v151, paddle model中的resnet50模型,只需要把batchNorm的前向和反向的GPU实现注解了,应该就复现问题

vxbzzdmp

vxbzzdmp9#

@dashulu ,这个同名应该是因为我们在1.5版本里,一些inplace显存优化是通过变量重命名做的。。如果是基于1.6版本改,由于策略不是重命名,应该不会有这个变量被设置相同名的问题。。。

iyr7buue

iyr7buue10#

@zhhsplendid 所以这个是1.5的bug?只要同名就会出现问题?如果1.6的策略是变量不重命名,那这个transfer的机制有什么用?

rsl1atfo

rsl1atfo11#

@dashulu TransferInplaceVarsBack的场景是:输入和输出是同名的,也就是说op的输入输出是同一个Variable。我在前面的回复中有写过:如果对输入做了data transform,Op运行时的真正的输入就会在transfer_scope中,Op计算完,它的输出放在输入的Variable中,就需要将它transfer到原来的scope。

sum op在1.6版本中,输入和输出是不同名的,也就是说它们是不同的Variable,只是复用了更加底层的数据域。如果sum op的输入发生了data transform,但Op的输出放在原来的数据域,也就是放在了原始的scope中。sum op输入和输出变量不同名,计算完不会发生TransferInplaceVarsBack,因此就不会导致输出数据丢失。

关于是否是1.5版本的bug,我们需要进一步确认。同时也期望你能使用1.6版本进行实验,给我们反馈一下结果。

jaxagkaj

jaxagkaj12#

@zhangting2020 我之前也回复过,我看到的是输入输出是同名,但是它们不是同一个Variable,对输入做了data transform后,Op运行时的真正输入确实是在transfer_scope中,但是结果放在了输出的Variable(尽管与输入同名,但确实是不同的variable),这时候由于输入输出同名,触发了TransferInplaceVarsBack, 用输入的variable覆盖了实际的输出。

az31mfrm

az31mfrm13#

@dashulu 理解你的意思,从log上看的确是发生了TransferInplaceVarsBack。能确认的是1.6版本不会有这个问题。另外看log是做了CPU->XPU的data transform,我这边只能构造CPU->GPU的data transform场景。所以你可以先用1.6版本试试吗

nszi6y05

nszi6y0514#

1.6的版本改动起来工作量太大了,短期内比较难构造出来。GPU的话跑resnet50,把batchNorm的gpu实现注解了,应该就能复现问题。
另外,从之前的讨论中,1.6是保证了sum op的输入输出name不一样,其他op也有同样的保证吗?如果能保证输入输出的name不同,是不是inplace根本不会触发,那inplace的机制是否还有留着的必要?

zte4gxcn

zte4gxcn15#

@dashulu sum这一类Op中,你看到的inplace与TransferInplaceVarsBack是没有关系的。会有一些Op输入输出是同名的,例如sgd。

另外我尝试用我上面贴出的代码测试了1.5版本的sum op,也没有发生TransferInplaceVarsBack。目前我这边得到的结论:应该不是Paddle源码实现的Bug。

相关问题