由于昇腾上部分算子不支持fp32(如 Conv,Pooling),或部分算子虽然支持fp32但性能极差,不得不切换成fp16(如MatMul),导致其必须走fp16,这增大了训练过程中算子溢出的风险,影响网络最终收敛的精度,这里给出算子溢出分析方法。
- 步骤1:开启溢出检测
- 对于溢出检测,需要注意以下配置:
dump_mode
:设置为0,检测到所有算子
path
:推荐设置到/home/ma-user/modelarts/outputs/output_url_0/
目录下,DI训练时该目录会自动回传数据到obs,方便后续进行溢出检测分析
iteration
:设置为all,检测所有迭代
op_debug_mode
:设置为3,开启全部溢出检测
- 一个典型的溢出检测
data_dump.json
配置为:
{ "common_dump_settings": { "dump_mode": 0, "path": "/home/ma-user/modelarts/outputs/output_url_0/bev/dump/", "net_name": "bevnet", "iteration": "all", "saved_data": "tensor", "input_output": 0, "kernels": [], "support_device": [0,1,2,3,4,5,6,7], "op_debug_mode": 3, "file_format": "npy" }
- 步骤 2:查看溢出算子
- 在日志中搜索关键字
overflow infos
,如果有算子溢出,则日志中会提示算子所在的代码行:
5290 [WARNING] DEVICEC517, ffff1c1b9160,python):2022-12-27-23:50:13.843.123 [mindspore/ccsrc/plugin/device/ascend/hal/device/ascend_kernel_runtime. cc:683]TaskFailCallback] Node run task overflow, node name: Gradients/Default/network-MultiTaskTrainonestepCelL/network-Modulewrapper/detection3d _head-DetHead/det_head-CenterPointHead/head-CenterHead/task_heads-CellList/0-SeparateHead/reg-SequentialCel1/1-_OutputTo32/_op-Conv2d/gradconv2D/Cast-op41878Task overflow infos task_id: 306, streamid: 17, tid:517, device_id: 1, retcode:207003 (aicore over flow 5291 The function call stack: 5292 Corresponding code candidate: 5293 In file /home/ma-user/mindspore/build/package/mindspore/ops/_grad/gradnn_ops.py:106/ dx = input_grad(dout, w, x_shape)/ 5294 In file /home/ma-user/mindspore/build/package/mi ndspore/ops/_grad/grad _nn_ops.py: 107/ dw = filter_grad(dout, x, w_shape)/ 5295 Corresponding forward node candidate: 5296 - In file /hone/na-user/mindspore/build/package/mi ndspore/nn/layer/ conv.py: 309/ output = self.conv2d(x, self.weight)/ 5297 In file /home/na-user/mindspore/bui ld/package/mindspore/train/arp.py:671 return F.cast(self._opCx), mstype. float32)/ 5298 In file /hone/na-user/mindspore/build/package/mi ndspore/nn/layer/container.py:278/ for cell in self.cell_list:/ 5299 In file /home/ma-user/rodelarts/user-job-dir/code/tasks/bev_task/uvp_rodule/models/det_ head/centerpoint/centerpoint_head.py:145/ return {'reg':self.reg(x), "height':self.height(x), 'dim':self.dim(x),'rot':self.rot(x), 'vel':self.vel(x),"heatrap':self.heatmap(x)}/ 5300 In file /home/ra-user/nodelarts/user-job-dir/code/tasks/bev-task/uvp_module/nodels/det_head/centerpoint/centerpoint_head.py:239/ for task in self.task_heads:/ 5301 In file /home/ma-user/nodelarts/user-job-dir/code/tasks/bev_task/uvp_module/models/det_head/centerpoint/centerpoint_ head.py:255/ return multi_apply(self.forward_single,feats)/ 5302 In file /hone/ma-user/nodelarts/user- job-dir/code/tasks/bev_task/uvp_module/models/det_head/centerpoint/centerpoint_head.py: 255/ return multi_apply(self.forward_single,feats)/ 5303 In file /home/ma-user/nodelarts/user-job-dir/code/tasks/bev_task/uvp_module/models/det_head/centerpoint/centerpoint_head.py:413/ pred_dict = self.head(bev_feat_map)/ 5304 In file /home/ma-user/modlelarts/user-job-dir/code/tasks/bev_task/uvp_module/models/det_head/det_head.py:71/ det_output,ret_dict = self.det_headCdata_dict)/ 5305 In file /hone/na-user/nodelarts/user-job-dir/code/uvpsandbox-api/uvpsandbox/uvpsandbox/nodule_wrapper. py: 40/ for net, node, inners, node-tasks in zip(self.nets, self.nodes, self.inner_nodes, self.nodes tasks):/ 5306 In file /hone/ma-user/modelarts/user- job-dir/ code/uvpsandbox-api /uvpsandbox/uvpsandbox/frarework. py: 583/ net_outputs = self.network(img_data,f"labels":[labels], "img_info":img_info})/ 5307 In File /hone/ma-user/mindspore/bui ld/package/mindspore/nn/wrap/loss_scale.py:336/ loss = self.network(*inputs)/
- 溢出的算子为
Cast-op41878
该算子为Cast
算子,scope 名为Gradients/Default/network-MultiTaskTrainOneStepCell/network-ModuleWrapper/detection3d_head-DetHead/det_head-CenterPointHead/head-CenterHead/task_heads-CellList/0-SeparateHead/reg-SequentialCell/1-_OutputTo32/_op-Conv2d/gradConv2D/Cast-op41878
(如果scope的名字以Gradients
开头,说明该算子是反向产生的算子)。根据The function call stack:
提示,找到该算子对应的代码(反向算子需要先找到对应的正向算子)。 - 步骤3:分析溢出产生的原因
- 在步骤2中,我们可以找到具体溢出的算子和所在代码,这一步我们需要分析溢出产生的原因。首先需要找出溢出算子所在的rank,下载该rank 下的dump数据,一个标准的dump数据结构如下所示:
rank_xx |-- bevnet # 根目录,根据 data_dump.json 配置生成 | |-- 0 # 每个step产生的dump文件,序号代表step数 | |-- 1 | |-- 17 | |-- 2 | `-- constants # 网络中的常量,数据不随step数改变 |-- execution_order # 网络中算子的执行顺序(如果有多个图,则产生多个文件) | |-- ms_execution_order_graph_0.csv | |-- ms_execution_order_graph_1.csv | |-- ms_execution_order_graph_17.csv | |-- ms_execution_order_graph_18.csv | |-- ms_execution_order_graph_19.csv | |-- ms_execution_order_graph_2.csv | `-- ms_global_execution_order_graph_17.csv `-- graphs # 网络中算子的图结构(如果有多个图,则产生多个文件) |-- ms_output_trace_code_graph_0.ir |-- ms_output_trace_code_graph_0.pb |-- ms_output_trace_code_graph_1.ir |-- ms_output_trace_code_graph_1.pb |-- ms_output_trace_code_graph_17.ir |-- ms_output_trace_code_graph_17.pb |-- ms_output_trace_code_graph_18.ir |-- ms_output_trace_code_graph_18.pb |-- ms_output_trace_code_graph_19.ir |-- ms_output_trace_code_graph_19.pb |-- ms_output_trace_code_graph_2.ir `-- ms_output_trace_code_graph_2.pb 8 directories, 21 files
- 一般我们只关注
rank_xx/bevnet/x/
下的数据,假设该目录下数据如下所示:
-rw-r--r-- 1 root root 630912 Dec 28 10:00 Cast. Cast-op41877.309.17.1672156213076552. input.0.NCHW. npy -rw-r--r-- 1 root root 315520 Dec 28 10:00 Cast. Cast-op41877.309.17.1672156213076552.output.0.NCHW.npy -rw-r--r-- 1 root root 630912 Dec 28 10:00 Cast.Cast-op41878.306.17.1672156213045891. input.0.NCHW.npy -rw-r--r-- 1 root root 315520 Dec 28 10:00 Cast.Cast-op41878.306.17.1672156213045891.output.0.NCHW. npy -rw-r--r-- 1 root root 1577088 Dec 28 10:00 Cast. Cast-op41879.63.18.1672156213413591. input.0.NCHW.npy -rw-r--r-- 1 root root 788608 Dec 28 10:00 Cast.Cast-op41879.63.18.1672156213413591.output.0.NCHW.npy -rw-r--r-- 1 root root 2432 Dec 28 10:00 Conv2DBackpropInput. Conv2DBackpropInput-op26836.331.17.1672156213185043.input.0.NCHW.npy -rw-r--r-- 1 root root 315520 Dec 28 10:00 Conv2DBackpropInput. Conv2DBackpropInput-op26836.331.17.1672156213185043.input.1.NCHW.npy -rw-r--r-- 1 root root 10092672 Dec 28 10:00 Conv2DBackpropInput.Conv20BackoropInput-op26836.331.17.1672156213185043.output-0.NCHW.nDy
- 可以看到,
Cast-op41878
的溢出数据被dump了出来,读取其中的数据,可以发现该算子的输入104856.29
在cast之后变成了65500.0
,算子发生了溢出。
Python 3.8.6 (default,0ct 6 2020, 03:22:36) [GCC 7.5.0] on linux Tyре "help", "сopyright" , "credits" or "1icense" for more information. >>> inport nunpy as np >>> np.1oad("cast.Cast-op41878.306.17.1672156213045891. input.0.NCHW.npy").maxO 104856.29 >>> np.load("cast.Cast-op41878.306.17.1672156213045891.output.0.NCHW. npy").maxO) 65500.0
- 由于初始时
loss scale
的值比较大,导致初始梯度比较大,这里的溢出也是正常的,可以尝试调小初始loss scale
解决。
溢出常见解决方法:
- 对于发生在训练开始,在后期迭代不再出现的溢出,可以忽略或者调小初始
loss scale
。这是因为前期梯度较大,训练不稳定,如果配置了LossScaleManager
,这些溢出的step会被自动忽略,一般对最终的收敛影响不大。 - 如果溢出的算子输入数据正常,正常计算时不应该引发溢出,此时请保留算子输入输出数据,需要与具体的算子开发负责人确认,该算子可能存在溢出误报的问题。
- 对于
MatMul
、BatchMatMul
、ReduceSum
、ReduceMean
等容易引发溢出的算子,可以尝试在算子输入之前对数据进行缩放(缩小x倍),在算子计算后的某个合适位置进行复原(放大回x倍),这样在保证整个计算数学等价的情况下,避免溢出。