diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\345\212\237\350\203\275\346\211\223\351\200\232/\345\212\250\346\200\201Shape\346\216\250\347\220\206\346\214\207\345\257\274(ResNet50).md" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\345\212\237\350\203\275\346\211\223\351\200\232/\345\212\250\346\200\201Shape\346\216\250\347\220\206\346\214\207\345\257\274(ResNet50).md" index b38240d26344eb0d6ccda5b6af326a0c13e860ce..a72efe580ada7306e5d9135873a98ded93df22b3 100644 --- "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\345\212\237\350\203\275\346\211\223\351\200\232/\345\212\250\346\200\201Shape\346\216\250\347\220\206\346\214\207\345\257\274(ResNet50).md" +++ "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\345\212\237\350\203\275\346\211\223\351\200\232/\345\212\250\346\200\201Shape\346\216\250\347\220\206\346\214\207\345\257\274(ResNet50).md" @@ -10,7 +10,7 @@ 现有离线推理工具:benchmark/msame暂不支持动态shape的推理,需要基于pyACL库实现模型的动态shape推理,具体可参照:4. 模型推理 -代码可参考:[Resnet50_dynamic]() +代码可参考:[Resnet50_dynamic](https://gitee.com/zheng-wengang1/personal_model_zoos/tree/master/dynamic_shape_ResNet50) ## 2. 原理说明 diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/image-20211012114743708.png" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/image-20211012114743708.png" new file mode 100644 index 0000000000000000000000000000000000000000..fea7aebfcdaf16014089c7fd55a3c44e33d9fe53 Binary files /dev/null and "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/image-20211012114743708.png" differ diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/image-20211012115044301.png" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/image-20211012115044301.png" new file mode 100644 index 0000000000000000000000000000000000000000..30a59466023ec53d30285c761de61990e1e2b4ce Binary files /dev/null and "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/image-20211012115044301.png" differ diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/image-20211012142658787.png" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/image-20211012142658787.png" new file mode 100644 index 0000000000000000000000000000000000000000..6578ce937a2464a182af0f124befc0069ea39c61 Binary files /dev/null and "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/image-20211012142658787.png" differ diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/screenshot_20211012143829370.png" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/screenshot_20211012143829370.png" new file mode 100644 index 0000000000000000000000000000000000000000..da93eeee4e4643dc54071a516f9d692b869966b3 Binary files /dev/null and "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/screenshot_20211012143829370.png" differ diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/screenshot_20211012144413617.png" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/screenshot_20211012144413617.png" new file mode 100644 index 0000000000000000000000000000000000000000..77d6ada43015ad80f769248afbc1d6e00390dee3 Binary files /dev/null and "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/img/screenshot_20211012144413617.png" differ diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/\346\241\210\344\276\213-Conv1D\347\256\227\345\255\220\344\274\230\345\214\226.md" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/\346\241\210\344\276\213-Conv1D\347\256\227\345\255\220\344\274\230\345\214\226.md" new file mode 100644 index 0000000000000000000000000000000000000000..092e4fab4d0cbddeac653f058e30f53def6c6afe --- /dev/null +++ "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\346\200\247\350\203\275\350\260\203\344\274\230/\346\241\210\344\276\213-Conv1D\347\256\227\345\255\220\344\274\230\345\214\226.md" @@ -0,0 +1,165 @@ +# Conv1D场景优化 + +## 背景分析 + +Conv1D算子指的是输入数据为1d输入场景,于常见CV场景下的2D输入(`[batch_size,channels, height, width]`)不同,其输入形式通常为(以点云输入为例):`[batch_size, coord, npoints]`,这里`coord`为坐标(x,y,z),`npoints`表示点数目。 + +这里以PointNet的网络为例,其输入为`[1,3,2500]`: + +![image-20211012114743708](img/image-20211012114743708.png) + +该模型转化为om模型时,发现性能很差,其基本结构如下: + +![image-20211012115044301](img/image-20211012115044301.png) + +可以发现转化为om模型后,存在以下问题: + ++ 卷积前后插入多余的格式转换算子:`TransData` ++ `Conv2D+BN+Relu`:本身om模型转换的时候时支持算子融合的,可以减少数据搬运的时间,从而提升模型性能 + +## 原因分析 + +首先了解下om模型的算子融合逻辑:基于`Pattern`:本质上是一系列按照算子名称/格式/输入输出参数配置的模板。模型转换过程会按照这些模板进行匹配,如果成功匹配则会进行算子融合。 + +那么,`Conv2D+BN+Relu`算子没有融合的原因是没有匹配到`Pattern`,其原因是因为在`Conv2D`算子后插入了`TransData`算子。 + +而插入`TransData`算子的原因则与`Conv1D`算子的实现有关:目前`Conv1D`算子是基于`Conv2D`算子实现的,可以发现om模型和onnx模型的卷积算子在参数上是不一致的,om模型的卷积参数多了一个维度:也就是真正`Conv1D`算子的实现是先将1d输入转换为2d输入,然后再通过`Conv2D`算子处理,最后再转换为1d输出(所以插入了多余的`TransData`)。查看模型转化的中间图可以发现,中间插入的`Squeeze`算子即为最终模型中的`Transdata`算子: + +![image-20211012142658787](img/image-20211012142658787.png) + +## 解决方案 + +解决方案存在两种: + ++ 图优化方案:本身模型转化的过程中就存在图优化的过程:包括多余算子的消除等。理论上上文中的`Squeeze`算子是需要被消除的。 + ++ onnx改图:改图的思路是所有用到`Conv1d`算子的子图上进行格式转换:人为转为1d输入为2d输入,`Conv1d`转化为`Conv2d`算子,然后只在子图前后插入格式转化算子: + + screenshot_20211012143829370 + + 最终得到转化结果:左图为改图前的网络结构,右图为转换后的改图结构,前后等效。 + + screenshot_20211012144413617 + + ## 代码解析 + + ```python + import sys + import numpy as np + from onnx_tools.OXInterface.OXInterface import OXGraph + + + INPUT_NODE = 'image' + FIX_NODE = 'Relu_35' + + + def conv1d2conv2d(oxgraph, node_conv): + """ + transfer conv1d parameters to conv2d + :param oxgraph: input onnx graph + :param node_conv: conv1d node to be transfered + """ + if node_conv.get_op_type() != 'Conv': + return + node_conv.set_attribute(attr_name='dilations', attr_value=[1, 1]) + node_conv.set_attribute(attr_name='kernel_shape', attr_value=[1, 1]) + node_conv.set_attribute(attr_name='pads', attr_value=[0, 0, 0, 0]) + node_conv.set_attribute(attr_name='strides', attr_value=[1, 1]) + init_conv_w = oxgraph.get_oxinitializer_by_name(node_conv.input[1]) + init_conv_w.set_data(np.expand_dims(init_conv_w.get_data(), axis=2)) + + + def adhoc_fix_multi_output(oxgraph, oxnode): + """ + adhoc func for multi output for 'Relu35' + insert sqeeze node before the second output + return the first output + :param oxgraph: input onnx graph + :param oxnode: input onnx node(Relu35) + """ + next_beg_nodes = oxgraph.get_next_oxnode(oxnode.get_name()) + first_out = next_beg_nodes[0] + second_out = next_beg_nodes[1] + if first_out.get_op_type() == 'Transpose': + first_out, second_out = second_out, first_out + squeeze_node_name = 'Squeeze_after_{}'.format(second_out.get_name()) + oxgraph.insert_node(bef_node_info_list=[oxnode.get_name()], + aft_node_info_list=[second_out.get_name()], + op_type='Squeeze', + op_name=squeeze_node_name) + node_squeeze = oxgraph.get_oxnode_by_name(squeeze_node_name) + node_squeeze.set_attribute(attr_name='axes', attr_value=[2]) + return [first_out] + + + def transfer_structure(oxgraph, beg_node, end_node): + """ + transfer process: + 1. insert unsqueeze node before beg node + 2. insert squeeze node after end node + 3. transfer conv1d paramters for conv2d + :param oxgraph: input onnx graph + :param beg_node: beg node name for searched structure + :param end_node: end node name for searched structure + """ + previous_beg_node = oxgraph.get_previous_oxnode(oxnode_name=beg_node) + if not previous_beg_node: + previous_beg_node = INPUT_NODE + else: + previous_beg_node = previous_beg_node[0].get_name() + next_end_node = oxgraph.get_next_oxnode(oxnode_name=end_node) + unsqueeze_node_name = 'Unsqueeze_before_{}'.format(beg_node) + squeeze_node_name = 'Squeeze_after_{}'.format(end_node) + next_end_node = next_end_node[0].get_name() + + oxgraph.insert_node(bef_node_info_list=[previous_beg_node], + aft_node_info_list=[beg_node], + op_type='Unsqueeze', + op_name=unsqueeze_node_name) + oxgraph.insert_node(bef_node_info_list=[end_node], + aft_node_info_list=[next_end_node], + op_type='Squeeze', + op_name=squeeze_node_name) + node_unsqueeze = oxgraph.get_oxnode_by_name(unsqueeze_node_name) + node_unsqueeze.set_attribute(attr_name='axes', attr_value=[2]) + node_squeeze = oxgraph.get_oxnode_by_name(squeeze_node_name) + node_squeeze.set_attribute(attr_name='axes', attr_value=[2]) + + next_beg_node = oxgraph.get_oxnode_by_name(oxnode_name=beg_node) + while next_beg_node.get_name() != end_node: + conv1d2conv2d(oxgraph, next_beg_node) + if next_beg_node.get_name() == FIX_NODE: + next_beg_node = adhoc_fix_multi_output(oxgraph, next_beg_node) + else: + next_beg_node = oxgraph.get_next_oxnode(oxnode_name=next_beg_node.get_name()) + next_beg_node = next_beg_node[0] + conv1d2conv2d(oxgraph, next_beg_node) + + + def fix_conv1d(model_path, out_path, beg_list, end_list): + """ + main process for fixing conv1d + :param model_path: input onnx model path + :param out_path: out fixed onnx model path + :param beg_list: beg node names for searched structure + :param end_list: end node names for searched structure + """ + oxgraph = OXGraph(model_path) + for idx, beg_node in enumerate(beg_list): + end_node = end_list[idx] + transfer_structure(oxgraph, beg_node, end_node) + oxgraph.save_new_model(out_path) + + + if __name__ == '__main__': + input_path = sys.argv[1] + save_path = sys.argv[2] + beg_nodes = ['Conv_3', 'Conv_33', 'Conv_69'] + end_nodes = ['Relu_11', 'Relu_47', 'Conv_72'] + fix_conv1d(input_path, save_path, beg_nodes, end_nodes) + ``` + + + + + diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/img/image-20211011194902240.png" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/img/image-20211011194902240.png" new file mode 100644 index 0000000000000000000000000000000000000000..7018b1fee7af0aad2a58452535b9a59d7753fbb5 Binary files /dev/null and "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/img/image-20211011194902240.png" differ diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/img/image-20211012103044340.png" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/img/image-20211012103044340.png" new file mode 100644 index 0000000000000000000000000000000000000000..1c016a8a3e4aca9bfdad0e7505035197f8e9b270 Binary files /dev/null and "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/img/image-20211012103044340.png" differ diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/img/image-20211012104559740.png" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/img/image-20211012104559740.png" new file mode 100644 index 0000000000000000000000000000000000000000..f0435190783f0ea09bff89c6471e2054898e16cd Binary files /dev/null and "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/img/image-20211012104559740.png" differ diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/img/image-20211012105532905.png" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/img/image-20211012105532905.png" new file mode 100644 index 0000000000000000000000000000000000000000..42d0d91822c38eab548961eab05316e1156dd68e Binary files /dev/null and "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/img/image-20211012105532905.png" differ diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/\346\241\210\344\276\213-SSD(mmdetection)\346\250\241\345\236\213\347\262\276\345\272\246\350\260\203\350\257\225.md" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/\346\241\210\344\276\213-SSD(mmdetection)\346\250\241\345\236\213\347\262\276\345\272\246\350\260\203\350\257\225.md" index ba0994d7108cce2ddabf1d2609e3a1aff7e28a99..c336615190b98a81766db72851f1df66e1b7ceb6 100644 --- "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/\346\241\210\344\276\213-SSD(mmdetection)\346\250\241\345\236\213\347\262\276\345\272\246\350\260\203\350\257\225.md" +++ "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/\346\241\210\344\276\213-SSD(mmdetection)\346\250\241\345\236\213\347\262\276\345\272\246\350\260\203\350\257\225.md" @@ -376,7 +376,7 @@ def getCosineSim(d1, d2): # 获取异常值 def getUnnormalValue(d1, d2, thre=0.01): data_sub = abs(d1 - d2) - unnormal_mask = (data_sub > 0.01 * d1) + unnormal_mask = (data_sub > abs(thre * (d1 + d2) / 2)) return d1[unnormal_mask], d2[unnormal_mask] # 加载数据 diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/\346\241\210\344\276\213-VitBase\346\250\241\345\236\213\347\262\276\345\272\246\350\260\203\350\257\225.md" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/\346\241\210\344\276\213-VitBase\346\250\241\345\236\213\347\262\276\345\272\246\350\260\203\350\257\225.md" new file mode 100644 index 0000000000000000000000000000000000000000..4b58504a54db60bd11acbd08531db658514abcec --- /dev/null +++ "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\347\262\276\345\272\246\350\260\203\350\257\225/\346\241\210\344\276\213-VitBase\346\250\241\345\236\213\347\262\276\345\272\246\350\260\203\350\257\225.md" @@ -0,0 +1,154 @@ +# Vit-Base精度分析 + +[TOC] + +## 模型背景 + +本文旨在说明如何基于复杂模型分析精度误差来源以及如何进行精度回归。 + +模型介绍:Vit-Base,将**Transformer**结构应用于CV领域,并取得了比较好的结果。其核心思路是:讲图像拆分为块(patch),并讲这些图像块的线性嵌入序列作为**Transformer**的输入。图像块的处理方式于NLP应用中的标记(tokens)相同。整体网络结构如下: + +![image-20211011194902240](img/image-20211011194902240.png) + +可以看到,该网络结构的特点是存在多个“重复”,“堆叠”的网络结构。 + +## 问题现象 + ++ 问题现象: + + onnx转om时(默认fp16精度下),存在精度问题,精度差距20%+:~70%->~50% + + 选用混合精度模式没有任何精度上的收益: + + ```shell + --precision_mode + # 设置网络模型精度:must_keep_origin_dtype>allow_fp32_to_fp16>allow_mix_precision>force_fp16 + # force_fp16: 默认采用fp16精度,精度最低,性能最好 + # allow_mix_precision: 混合精度模式 + # allow_fp32_to_fp16: 算子支持fp32则采用fp32精度模式,算子支持fp16则采用fp16精度模式;但是强制转换可能存在问题 + # must_keep_origin_dtype:保持原图精度,但是通常无法生效:部分算子只支持fp16精度,如Conv2D + ``` + ++ 问题细节: + + 对模型精度进行profiling的结果:最先出现精度误差的算子为**Softmax**算子 + + ![image-20211012103044340](img/image-20211012103044340.png) + +## 调试思路 + +算子精度调试的核心目的:**找到存在精度问题的单算子,且能够稳定复现**;算子精度存在问题的常见场景:算子精度溢出/本身算子实现问题(概率较小) + ++ 首先分析当前**Softmax**算子的精度问题,单独dump出**softmax**前后的数据: + + ```sh + # $dump_path: npu的节点dump文件 + # $output_dir: 输出npy文件目录 + # $format: 输出格式,如NCHW/ND + python3.7 /usr/local/Ascend/ascend-toolkit/latest/tools/operator_cmp/compare/msaccucmp.py convert -d $dump_path -out $output_dir -f $format + ``` + + 输出数据后发现:差距较大的值都是数值很小的值:`e-03~e-05`,同时验证单算子精度是正常的,这里的精度损失来源于输入的差距。 + + 因为**Softmax算子本身会放大精度损失**,根本原因还是前置的算子精度问题。 + ++ 向上回溯算子精度 + + 因为前置算子的预先相似度都是大于0.99,所以这里分析输入输出预先相似度差距较大的算子,发现**LayerNorm**算子前后精度存在较大损失: + + ![image-20211012104559740](img/image-20211012104559740.png) + + 但是注意到**LayerNorm**算子在NPU算子库属于融合大算子,无法分析内部算子的具体输出,所以可以通过关闭对应算子的`Fusion-Pass`进行拆分: + + ```sh + # atc转换模型时添加参数 + --fusion_switch_file=switch_fusion.cfg + # fusion_switch_file内容 + LayerNormONNXFusionPass:off + ``` + + 发现其核心算子为`Pow-ReduceMeanD-Add`组合(注意类似Pow的指数操作可能存在溢出的问题) + + ![image-20211012105532905](img/image-20211012105532905.png) + ++ 确认精度问题 + + 进一步分析单算子的精度问题,在`ReduceMeanD`算子发现明显精度问题:余弦相似度~96%,进一步定位为1/50个数据存在较大精度问题(差距两倍左右),向上回溯发现是前置算子`Pow`算子存在一个数据溢出: + + 对于FP16精度算子其最大值为65504,该场景下`Pow`算子在[0,21,:]维度存在一个数据溢出,对应FP32精度下的值为~13000(两倍左右),然后通过`ReduceMeanD`算子进一步放大了精度误差 + +## 解决方案 + +前置确认了精度问题在于`Pow/LayerNorm`算子的精度溢出,通过算子信息库可以查看算子精度支持范围: + +```python +# /usr/local/Ascend/ascend-toolkit/5.0.2/x86_64-linux/opp/op_impl/built-in/ai_core/tbe/config/ascend310/aic-ascend310-ops-info.json +# 如果上述json文件未定义格式,则需要在算子实现上确认格式: +# /usr/local/Ascend/ascend-toolkit/5.0.2/x86_64-linux/opp/op_impl/built-in/ai_core/tbe/impl + "Pow":{ + "dynamicShapeSupport":{ + "flag":"true" + }, + "input0":{ + "dtype":"float16,float,int32,int8,uint8", + "name":"x1", + "paramType":"required" + }, + "input1":{ + "dtype":"float16,float,int32,int8,uint8", + "name":"x2", + "paramType":"required" + }, + ... +``` + +确认发现:`Pow/LayerNorm`算子都支持FP32格式,那么前置采用`allow_mix_precision`为什么没有生效呢?查看om模型图结构会发现算子仍然只支持FP16格式,后续发现`allow_mix_precision`在选择算子精度的时候存在如下逻辑: + +```latex +# 于算子信息库中算子的precision_reduce值有关 +- 该参数为true,则算子采用FP16精度 +- 该参数为false,则算子采用FP32精度 +- 未配置(LayerNorm属于这种情况),则表示跟随前置算子精度 +``` + +`LayerNorm`算子前置算子为卷积算子,只支持FP16,所以该算子在混合精度模式下,仍然为FP16精度,可以通过添加黑名单的方式进行转换: + +```sh +# 仍然采用混合精度模式,但是添加黑名单,防止LayerNorm算子和前置算子精度一致 +--precision_mode=allow_mix_precision +--modify_mixlist=ops_info.json +# ops_info.json内容 +{ + "black-list":{ + "to-add":[ + "LayerNorm" + ] + } +} + +``` + +另外补充一种额外的指定算子精度方式: + +```sh +# 采用FP16精度模式,对需要FP32精度的算子进行指定 +--precision_mode=force_fp16 +--keep_dtype=execeptionlist.cfg +# execeptionlist.cfg内容: 这里指定Add_652/LayerNorm算子 +Add_652 +``` + +通过前面两种方式都可以回归精度 + +## 关于精度问题的总结 + +精度问题的核心在于:**找到并且复现单算子精度问题** + +经验总结: + ++ 某个算子的余弦相似度低,可能的原因:该算子精度问题或者输入数据的精度问题,典型如:Softmax,本身会放大精度问题,这时候需要向上回溯前置算子的精度问题 ++ 算子精度定位时,如果模型存在算子融合的场景,可以关闭`Fusion-Pass`,方便定位内部算子精度 ++ 对`Pow/Square/Norm`等算子保持敏感,可能会存在算子精度溢出 ++ 对精度结果的`Nan`值也需要关注,可能存在数据异常或者溢出情况 ++ 混合精度模式不一定会转化所有可支持FP32精度模式的算子精度 +