使用异步dump调试解决A910静态图模式下的run task error!
本文主要分享个人在开发过程中使用dump调试功能在静态图模式下解决run task error的问题。
1.问题描述
模型在A910设备下训练几个epoch之后突然报错,静态图报错run task error!动态图报错Sync stream error。具体报错信息可参考issueI4EY4G
问题观察
观察报错前的几个epoch输出的loss,有明显下降的趋势,因此代码逻辑应该没有问题。
通过对报错信息的分析,并没有提及具体的逻辑运算错误,因此可以初步判断是数据问题。
2.个人排查
2.1 由于同样逻辑的代码在GPU设备上并不会报错,并且能够完整训练完成,测试结果也较为正常,故可以从昇腾A910设备的特性开始着手。
2.2 我尝试过将反向传播(求梯度更新参数的部分)去除,也就是去掉nn.TrainOneStep或者nn.TrainOneStepWithLossScaleCell去掉,直接输出模型的loss不进行更新。在此设置下可以稳定运行不报错(同时因为参数不更新,loss也不会变小)。由此确定模型前向传播的逻辑和算子暂无问题。
至此,结合报错信息unsorted segment sum算子属于gather算子反向传播时经历的算子,可以确定是反向传播时算子报的数据错误。
3.使用异步dump调试
由于报错的位置不确定,因此需要在静态图模式下使用dump调试功能确定报错位置和数据。
3.1 固定随机性
在进行dump调试之前,应固定模型的随机性,使其训练时报错在一个固定的step里。根据我的经验,需要确定以下几点:
① 数据集构建时应设置shuffle=False;
② 在需要进行随机采样或者设置随机数的文件最前面加上set_seed()和np.random.seed(),固定随机种子;
③ 使用numpy脚本生成npy文件固定模型参数的初始值,如果模型中有dropout等也应去掉。
具体可参考官网固定随机性
经过随机性固定,模型可以稳定在第5个epoch第50个step报错(可能是由于机器的原因,会有个别偶然的情况不是在此步报错)。
3.2 使用异步dump
参考官网给定的异步dump的方法流程异步dump
3.2.1 配置dump.json

PS:
- net_name需要自己指定,dump成功之后会在rank_0文件夹下生成以此命名的数据文件夹;
- iteration在model.train模型下可以叠加计算,比如一个epoch有100个step,报错在第3个epoch的第50个step,那么就可以计算iteration为100×2+50;
- op_debug_mode设置为3可以dump有问题算子的脏数据,如果最初出现脏数据的地方不明显,可以先设置为3,然后在上述net_name文件夹下查看哪个op以确定最初出现脏数据的地方,后续再设置为0从而dump全量数据;
3.2.2 配置环境变量
设置dump环境变量export MINDSPORE_DUMP_CONFIG={dump.json的绝对路径}
完成上述步骤后即可进行dump数据。
3.2.3 配置mindinsight进行离线调试
参考官方教程离线调试器
- 启动mindinsight:
mindinsight start --port {PORT} --summary-base-dir {SUMMARY_BASE_DIR}
注意此处的SUMMARY_BASE_DIR应该是dump.json中path的上一级目录,端口设置尽量设置成不常用的。 - 本地命令行建立连接:
ssh root@{remote ip} -f -N -L localhost:{port}:localhost:{port}
本地执行上述命令无误后即可打开localhost:{port}看到mindinsight界面
3.2.4 离线调试查看数据
- 打开界面后点击离线调试器进入离线调试界面
-
根据报错信息,最开始在op223处报了一个overflow的warning,设置当前轮次为727并搜索op223,可以查看此op的输入输出数据。点击查看后,可以观察到op223的输出有-INF的脏数据。
![op223]()
-
在查看页面的下方可以顺着数据流检查数据的传导。经过检查,最终报错的算子op252处于op223的下游,因此可以确定op252的错误是op223产生的脏数据级联传播造成的。
![op223-info]()
-
经过dump全量数据之后,进一步观察op223的输入,发现其中有几个0数据,通过查看堆栈信息,此处为求范数。
![op223堆栈]()
对应的前向中出现sqrt求根操作,因此对应反向会将输入x置于分母上,当输入数据产生0,就会产生除0操作从而生成脏数据。 -
由于昇腾机器上部分算子为fp16精度,个人猜测是由于fp32的数据强制转换为fp16时进行了截断,导致一些很小的数截断后直接变成了0。
按照严谨解决此类问题的思路,应当是顺着数据流和算子,找到需要降精度的算子,强制让其不转换精度。
我这里的解决方法是直接重写nn.Norm,在求根操作时加上一个极小的偏置值,使其不会出现除0操作产生脏数据:x = self.sqrt(self.reduce_sum(F.square(x), self.axis)+1e-6)
4. 修正后训练测试
在加上偏置值进行修正Norm算子之后,模型可以完整地训练给定的迭代次数:
经过测试,结果虽然与GPU有点差距(可能是随机数抖动等其他原因),但是总体较为合理。
5. 注意事项
- 不同的模型应该就具体情况而论,出现问题的算子也不一定就都是norm算子,最好的解决方案还是找到降低精度的算子进行修正,当前的修正方案尚在评估精度和效率影响;
- dump全量数据可能会占用大量的磁盘空间(我dump全量的数据占用了90G磁盘空间),需要保证有充足的磁盘空间。



浙公网安备 33010602011771号