使用异步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

dump.json

PS:

  1. net_name需要自己指定,dump成功之后会在rank_0文件夹下生成以此命名的数据文件夹;
  2. iteration在model.train模型下可以叠加计算,比如一个epoch有100个step,报错在第3个epoch的第50个step,那么就可以计算iteration为100×2+50;
  3. 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进行离线调试

参考官方教程离线调试器

  1. 启动mindinsight:
    mindinsight start --port {PORT} --summary-base-dir {SUMMARY_BASE_DIR}
    注意此处的SUMMARY_BASE_DIR应该是dump.json中path的上一级目录,端口设置尽量设置成不常用的。
  2. 本地命令行建立连接:
    ssh root@{remote ip} -f -N -L localhost:{port}:localhost:{port}
    本地执行上述命令无误后即可打开localhost:{port}看到mindinsight界面

3.2.4 离线调试查看数据

  1. 打开界面后点击离线调试器进入离线调试界面
  2. 根据报错信息,最开始在op223处报了一个overflow的warning,设置当前轮次为727并搜索op223,可以查看此op的输入输出数据。点击查看后,可以观察到op223的输出有-INF的脏数据。
    op223

  3. 在查看页面的下方可以顺着数据流检查数据的传导。经过检查,最终报错的算子op252处于op223的下游,因此可以确定op252的错误是op223产生的脏数据级联传播造成的。
    op223-info

  4. 经过dump全量数据之后,进一步观察op223的输入,发现其中有几个0数据,通过查看堆栈信息,此处为求范数。
    op223堆栈
    对应的前向中出现sqrt求根操作,因此对应反向会将输入x置于分母上,当输入数据产生0,就会产生除0操作从而生成脏数据。

  5. 由于昇腾机器上部分算子为fp16精度,个人猜测是由于fp32的数据强制转换为fp16时进行了截断,导致一些很小的数截断后直接变成了0。
    按照严谨解决此类问题的思路,应当是顺着数据流和算子,找到需要降精度的算子,强制让其不转换精度。
    我这里的解决方法是直接重写nn.Norm,在求根操作时加上一个极小的偏置值,使其不会出现除0操作产生脏数据:
    x = self.sqrt(self.reduce_sum(F.square(x), self.axis)+1e-6)

4. 修正后训练测试

在加上偏置值进行修正Norm算子之后,模型可以完整地训练给定的迭代次数:
train-log
经过测试,结果虽然与GPU有点差距(可能是随机数抖动等其他原因),但是总体较为合理。
result

5. 注意事项

    1. 不同的模型应该就具体情况而论,出现问题的算子也不一定就都是norm算子,最好的解决方案还是找到降低精度的算子进行修正,当前的修正方案尚在评估精度和效率影响;
    2. dump全量数据可能会占用大量的磁盘空间(我dump全量的数据占用了90G磁盘空间),需要保证有充足的磁盘空间。
posted @ 2021-12-25 15:25  MS小白  阅读(113)  评论(0)    收藏  举报