转载地址:https://bbs.huaweicloud.com/forum/thread-165718-1-1.html
作者:ttandtt
网络训练跑通了,精度OK了,对很多人来说可能已经万事大吉了,但如果网络需要在生产环境跑,还有一个点不得不去关注,那就是性能。对于大的网络,训练一次可能需要上月的时间,这时候就真正的体会到时间就是金钱了。提高网络的性能,缩短训练的时间,可能会节省上百万的金钱。
下面给大家介绍下之前对ResNet50网络的性能调优案例,希望能帮助到大家。调优过程中用到了MindSpore中的调试调优工具:MindInsight,不仅可以调试性能,还可以调试精度,感兴趣的同学可以到官网了解下:https://www.mindspore.cn/mindinsight
现象
我们将ResNet50网络batch size设置为32(代码可参考MindSpore ModelZoo中的ResNet50网络,当前代码已经是调优后的。本次主要给大家介绍下调优的经历),跑网络训练后发现单step时间约为90ms,性能很差。通常batch size为32时,单step耗时应在20ms以内。
原因分析
分析过程中,我们主要用到了MindInsight中的Profiler模块。如何跑Profiler收集性能数据大家可以到官网上查看教程,此次不再赘述。
将性能数据用UI页面可视化后,我们开始了此次的性能调优过程。
首先我们先看页面里的迭代轨迹部分。迭代轨迹将训练过程分为了3部分:迭代间隙表示前一个迭代结束到后一个迭代开始的时间,该段时间可以认为是后一个迭代等待数据的时间;前反向时间表示的是真正的前反向计算的时间;迭代拖尾表示的是反向完成后进行梯度更新的时间。从下图中我们看到迭代间隙的时间占了整个迭代的很大部分,因此我们需要重点关注数据处理的性能。
第二步,我们到数据准备详情页面,确认数据处理是否是性能瓶颈点。查看数据准备详情页面中的迭代间隙标签页,我们观察到,数据队列在前期有较多的数据,后期数据的个数变为0,分析原因是前期在图编译阶段已经开始了数据集的加载和增强,队列中随即缓存了多条数据;而后期正常训练开始后,队列中的数据被消费的速度要快于被生产的速度,因此数据队列逐渐变为空,说明此时数据变成了瓶颈。观察主机队列也是同样的情况。综合分析,正常训练过程中,数据处理为性能瓶颈点。
第三步,我们到数据准备详情页面中的数据处理标签页来查看具体问题。通过观察数据处理标签页的算子间队列关系,我们发现,Queue_3及其之后的队列使用率较低,即MapOp_3作为生产者生产数据的速度较慢,因此可以判定MapOp_3的性能还有优化空间,需要对该算子进行性能优化。
我们查看了数据处理的代码,发现map算子的num_parallel_workers参数没有设置,而该参数的默认为1,代码如下:
if do_train:
trans = [
C.RandomCropDecodeResize(image_size, scale=(0.08, 1.0), ratio=(0.75, 1.333)),
C.RandomHorizontalFlip(prob=0.5),
C.Normalize(mean=mean, std=std),
C.HWC2CHW()
]
else:
trans = [
C.Decode(),
C.Resize(256),
C.CenterCrop(image_size),
C.Normalize(mean=mean, std=std),
C.HWC2CHW()
]
data_set = data_set.map(operations=trans, input_columns="image")
因此我们分析可以尝试提高该算子的并发数来提高网络的性能。
效果验证
我们将num_parallel_workers参数调整为12后,再次运行训练脚本,优化参考代码如下:data_set = data_set.map(operations=trans, input_columns="image", num_parallel_workers=12)
通过MindInsight性能分析页面观察迭代轨迹,可以看到迭代间隙时长由72.8ms缩短到0.25ms,单step时长由90ms缩短到18.07ms。
至此,我们完成了ResNet50网络的性能调优工作。
总结
MindInsight调优工具对性能调优和精度调优提供了大量的数据,并形成了一套完整的方法论,需要的同学可以参考奥。