本文针对静态图模式,介绍如何运用Dump工具对网络数据进行分析。 分为异步dump和同步dump两种方式。
注:推荐用异步dump。MindSpore默认开启内存复用,而同步dump会关闭内存复用,可能会影响训练状态。
大型网络(如Bert Large)使用同步Dump时会导致内存溢出,MindSpore通过异步Dump提供了大型网络的调试能力。
json文件名称和位置可自定义设置。
- {
- "common_dump_settings": {
- "dump_mode": 0,
- "path": "/absolute_path",
- "net_name": "ResNet50",
- "iteration": "0",
- "input_output": 0,
- "kernels": ["Default/Conv-op12"],
- "support_device": [0,1,2,3,4,5,6,7],
- "op_debug_mode": 0
- }
- }
参数说明:
export MINDSPORE_DUMP_CONFIG=/home/ma-user/xxx/data_dump.json
此处路径应为json配置文件的绝对路径。
启动命令:
python MindSpore_1P.py
异步Dump保存的数据目录结构如下所示:
- {path}/
- |-- {device_id}/
- |-- {new_name}_graph_{graph_id}/
- |-- {graph_id}/
- |-- {iteration}/
- |-- {op_type}.{op_name}.{task_id}.{timestamp}
- …
- |-- graphs/
- ms_output_trace_code_graph_{graph_id}.pb
- ms_output_trace_code_graph_{graph_id}.ir
- |-- execution_order/
- ms_execution_order_graph_{graph_id}.csv
- |-- .metadata/
- data_dump.json
以Resnet50脚本为例:
- def _conv2d(in_channel, out_channel, kernel_size, stride=1, padding=0):
- scale = math.sqrt(1/(in_channel*kernel_size*kernel_size))
- if padding == 0:
- return nn.Conv2d(in_channel, out_channel, kernel_size=kernel_size,
- stride=stride, padding=padding, pad_mode='same',
- weight_init=mindspore.common.initializer.Uniform(scale=scale))
- else:
- return nn.Conv2d(in_channel, out_channel, kernel_size=kernel_size,
- stride=stride, padding=padding, pad_mode='pad',
- weight_init=mindspore.common.initializer.Uniform(scale=scale))
-
- ...class ResNet(nn.Cell):
- def __init__(self, num_blocks, num_classes=10):
- super(ResNet, self).__init__()
- self.in_planes = 64
- self.conv1 = _conv2d(3, 64, kernel_size=3, stride=1, padding=1)
- self.bn1 = nn.BatchNorm2d(64)
- self.relu = nn.ReLU()
- self.layer1 = self._make_layer(64, num_blocks[0], stride=1)
- self.layer2 = self._make_layer(128, num_blocks[1], stride=2)
- self.layer3 = self._make_layer(256, num_blocks[2], stride=2)
- self.layer4 = self._make_layer(512, num_blocks[3], stride=2)
- self.avgpool2d = nn.AvgPool2d(kernel_size=4, stride=4)
- self.reshape = mindspore.ops.Reshape()
- self.linear = _dense(2048, num_classes)
- self.print = P.Print()
- self.print_grad = P.InsertGradientOf(self.save_gradient)
-
- def save_gradient(self, dout):
- return dout
-
- def _make_layer(self, planes, num_blocks, stride):
- strides = [stride] + [1]*(num_blocks-1)
- layers = []
- for stride in strides:
- layers.append(ResidualBlock(self.in_planes, planes, stride))
- self.in_planes = EXPANSION*planes
- return nn.SequentialCell(*layers)
-
- def construct(self, x):
- x = self.conv1(x)
- out = self.relu(self.bn1(x))
- out = self.layer1(out)
- out = self.layer2(out)
- out = self.layer3(out)
- out = self.layer4(out)
- out = self.avgpool2d(out)
- out = self.reshape(out, (out.shape[0], 2048))
- out = self.linear(out)
- return out
若用户想查看脚本中第一个卷积算子的权重 :
x = self.conv1(x)
(1) 查找算子对应的数据文件
执行完训练网络后,可以从最终执行图(ms_output_trace_code_graph_{graph_id}.ir文件)中查找到该行代码所对应的多个算子信息,文件内容如下所示:
- ...
- %4(equivoutput) = Conv2D(%1, %3) {instance name: conv2d} primitive_attrs: {visited: true, pri_format: NC1HWC0, IsFeatureMapInputList: (0), out_channel: 64, kernel_size: (3, 3), IsFeatureMapOutput: true, pad_mode: pad, stride: (1, 1, 1, 1), mode: 1, pad: (1, 1, 1, 1), pad_list: (1, 1, 1, 1), group: 1, format: NCHW, dilation: (1, 1, 1, 1), input_names: [x, w], output_names: [output], groups: 1}
- : (, ) -> ()
- : (, ) -> ()
- : (Default/network/_backbone/conv1/Conv2D-op515)# In file /home/ma-user/miniconda3/envs/MindSpore-python3.7-aarch64/lib/python3.7/site-packages/mindspore/nn/layer/conv.py(258)/ output = self.conv2d(x, self.weight)/# In file /home/ma-user/work/xxx/Resnet50_cifar10/youhuahou/src/resnet.py(88)/ x = self.conv1(x)/
- ...
以上所示文件包括:
- : (, ) -> ()
- : (, ) -> ()
Default/network/_backbone/conv1/Conv2D-op515
# In file /home/ma-user/work/xxx/Resnet50_cifar10/src/resnet.py(88)/ x = self.conv1(x)/
根据算子名称中的op515,在Dump生成的数据文件目录({iteration})中,查找对应的Tensor数据文件。搜索到相应的文件名:Conv2D.Default_network__backbone_conv1_Conv2D-op515.58.16.1624605159829577
(2)使用海思Run包中提供的msaccucmp.py ,解析Dump出来的文件 。
注:不同的环境上msaccucmp.py文件所在的路径可能不同,可以通过find命令进行查找:
find ${run_path} -name "msaccucmp.py"
run包的安装路径。
(3)找到msaccucmp.py后,到/absolute_path目录下,运行如下命令解析Dump数据:
python ${The absolute path of msaccucmp.py} convert -d {file path of dump} -out {file path of output}
数据在Device侧的格式可能和Host侧计算图中的定义不同,异步Dump的数据格式为Device侧格式,如果想要转为Host侧格式,可以参考如何进行dump数据文件Format转换。
由于转换中存在FRACTAL_Z to NCHW格式的转换,工具当前未支持,因此需要编写自定义转换脚本,可使用示例代码包中的convert_FRACTAL_Z_to_NCHW.py脚本进行转换。将convert_FRACTAL_Z_to_NCHW.py文件放在新建的format_convert文件夹中。-c后指定为format_convert目录的上一层目录。
执行: (示例中format_convert文件夹新建在../目录下,也可以创建在其他目录下)
python /usr/local/Ascend/toolkit/tools/operator_cmp/compare/msaccucmp.py convert -d ./Conv2D.Default_network__backbone_conv1_Conv2D-op515.58.16.1624605159829577 -out ../output/ -f NCHW -c ../
在./output下生成该算子的所有输入输出数据。每个数据以.npy后缀的文件保存,生成结果如下:
- Conv2D.Default_network__backbone_conv1_Conv2D-op515.58.16.1624605159829577.input.0.128x3x32x32.npy
- Conv2D.Default_network__backbone_conv1_Conv2D-op515.58.16.1624605159829577.input.1.64x3x3x3.npy
- Conv2D.Default_network__backbone_conv1_Conv2D-op515.58.16.1624605159829577.output.0.128x64x32x32.npy
在文件名的末尾可以看到该文件是算子的第几个输入或输出,再结合维度信息可以判断数据的含义:例如,上面三个文件依次表示该卷积算子的输入、权重和输出。
(4)通过numpy.load接口读取要查看的数据
在终端依次输入:
python->import numpy->numpy.load("Conv2D.Default_network__backbone_conv1_Conv2D-op515.58.16.1624605159829577.input.1.64x3x3x3.npy")
输出数据:
- array([[[[ 0.01878 , 0.0828 , 0.03955 ],
- [ 0.01727 , -0.02939 , 0.05615 ],
- [-0.02402 , 0.1508 , 0.1785 ]],
- ...
- [[ 0.1145 , 0.1186 , 0.1643 ],
- [-0.148 , -0.1088 , 0.0935 ],
- [-0.117 , -0.0822 , -0.1283 ]]]], dtype=float16)
json文件名称和位置可自定义设置。
- {
- "common_dump_settings": {
- "dump_mode": 0,
- "path": "/absolute_path",
- "net_name": "ResNet50",
- "iteration": "0",
- "input_output": 0,
- "kernels": ["Default/Conv-op12"],
- "support_device": [0,1,2,3,4,5,6,7]
- },
- "e2e_dump_settings": {
- "enable": true,
- "trans_flag": true
- }
- }
参数说明:
export MINDSPORE_DUMP_CONFIG=/home/ma-user/xxx/data_dump.json
此处路径应为json配置文件的绝对路径。
启动命令
python MindSpore_1P.py
同步Dump保存的数据目录结构如下所示:
- {path}/
- |-- {net_name}/
- |-- {device_id}/
- |-- iteration_{iteration}/
- -- {op_name}_{input_output_index}_{shape}_{data_type}_{format}.bin
- …
- |-- graphs/
- ms_output_trace_code_graph_{graph_id}.pb
- ms_output_trace_code_graph_{graph_id}.ir
- |-- execution_order/
- ms_execution_order_graph_{graph_id}.csv
-
- |-- .metadata/
- data_dump.json
对于Ascend场景,在通过Dump功能将脚本对应的图保存到磁盘上后,会产生最终执行图文件ms_output_trace_code_graph_{graph_id}.ir。该文件中保存了对应的图中每个算子的堆栈信息。
若用户想查看Resnet50脚本(同异步Dump数据分析样例)中第一个卷积算子的权重:
x = self.conv1(x)
(1) 查找算子对应的数据文件
与异步Dump数据分析样例中查找方式相同。搜索到相应的文件名:Default--network-TrainOneStepCell--network-WithLossCell--_backbone-ResNet--conv1-Conv2d--Conv2D-op516_input_1_shape_64_3_3_3_Float32_DefaultFormat.bin
文件名中可以得到如下信息:
通过numpy.fromfile接口,还原数据:
(2)在终端中依次执行:
python->import numpy->array = numpy.fromfile("Default--network-TrainOneStepCell--network-WithLossCell--_backbone-ResNet--conv1-Conv2d--Conv2D- op516_input_1_shape_64_3_3_3_Float32_DefaultFormat.bin", numpy.float32)->numpy.reshape(array, (64,3,3,3))
还原原始shape数据:
- array([[[[-0.01689148, 0.05319214, 0.00757217],
- [ 0.02868652, 0.00693512, 0.08831787],
- [ 0.01548767, 0.20080566, 0.22070312]],
- ...
- [[ 0.09954834, 0.07037354, 0.15905762],
- [-0.14477539, -0.11743164, 0.11804199],
- [-0.16235352, -0.13134766, -0.13427734]]]], dtype=float32)