NPU上PyTorch模型调优问题案例
在昇腾AI处理器上训练PyTorch框架模型时,可能由于算子在CPU上的下发速度、动态shape等问题,导致性能降低,那么本期就分享几个关于PyTorch模型调优的典型案例,给出调优思路及具体的调优方法:
1、NPU亲和优化器替换调优案例
2、动态shape(算子二进制)调优案例
01 NPU亲和优化器替换调优案例
问题背景
在PyTorch混合精度模式下,每次迭代执行一次参数更新时,在Loss乘/除以缩放系数的过程中都会包含连续多个小算子(如add、mul、sqrt等)下发,由于小算子在NPU上计算快,导致算子在CPU上的下发成为性能的主要瓶颈。本调优案例以MobileNetV1模型为例,进行梯度融合调优解决此性能问题。
总体思路
使用昇腾AI处理器亲和的融合优化器替换原始优化器,昇腾AI处理器亲和的融合优化器可以在梯度更新过程中将连续多个小算子融合后下发,提升模型计算性能,完整融合优化器替换表如下所示。
用户可在代码中搜索关键词optim以搜索当前模型的优化器函数名,对照下表查看是否有可替换的亲和优化器。本例中使用torch_npu.optim.NpuFusedSGD替换原始优化器torch.optim.SGD。
优化器名称 |
样例原代码 |
修改后代码 |
sgd |
optimizer = torch.optim.SGD(model.parameters(), lr=args.lr) |
optimizer = torch_npu.optim.NpuFusedSGD(model.parameters(), lr=args.lr) |
adadelta |
optimizer = torch.optim.Adadelta(model.parameters(), lr=args.lr) |
optimizer = torch_npu.optim.NpuFusedAdadelta(model.parameters(), lr=args.lr) |
lamb |
optimizer = torch.optim.Lamb(model.parameters(), lr=args.lr) |
optimizer = torch_npu.optim.NpuFusedLamb(model.parameters(), lr=args.lr) |
adam |
optimizer = torch.optim.Adam(model.parameters(), lr=args.lr) |
optimizer = torch_npu.optim.NpuFusedAdam(model.parameters(), lr=args.lr) |
adamw |
optimizer = torch.optim.AdamW(model.parameters(), lr=args.lr) |
optimizer = torch_npu.optim.NpuFusedAdamW(model.parameters(), lr=args.lr) |
adamp |
optimizer = torch.optim.AdamP(model.parameters(), lr=args.lr) |
optimizer = torch_npu.optim.NpuFusedAdamP(model.parameters(), lr=args.lr) |
bert_adam |
optimizer = torch.optim.BertAdam(model.parameters(), lr=args.lr) |
optimizer = torch_npu.optim.NpuFusedBertAdam(model.parameters(), lr=args.lr) |
rmsprop |
optimizer = torch.optim.RMSprop(model.parameters(), lr=args.lr) |
optimizer = torch_npu.optim.NpuFusedRMSprop(model.parameters(), lr=args.lr) |
rmsprop_tf |
optimizer = torch.optim.RMSpropTF(model.parameters(), lr=args.lr) |
optimizer = torch_npu.optim.NpuFusedRMSpropTF(model.parameters(), lr=args.lr) |
环境准备
点击
,获取MobileNetV1模型,并根据readme准备训练环境和数据集。注意:从昇腾ModelZoo上获取的MobileNetV1模型脚本为已经进行了梯度融合优化的版本。若用户想使用该模型脚本体验本案例中的调优流程,可先将模型脚本main.py中的优化器定义代码改为原始优化器torch.optim.SGD,再执行以下流程。
瓶颈识别
1、修改训练脚本还原原生优化器函数,此操作在原生模型脚本中无需进行。
2、执行训练,记录耗时。
耗时数据如下图所示。
性能调优
以下调优步骤基于已完成模型向NPU的迁移。
1、模型脚本开头添加库代码。
2、找到模型脚本main.py中的优化器定义代码,将原始优化器替换为对应的NPU亲和融合优化器。样例代码如下。
原代码:
修改后代码:
或
注意:当前昇腾适配的MobileNetV1模型使用的是apex组件包中的NpuFusedSGD,此处修改为torch_npu.optim.NpuFusedSGD可能会导致精度异常。
3、拉起模型训练,进行profiling,生成json文件。以下命令以单卡训练为例。
4、执行训练,对比未设置亲和优化器时的训练数据,每秒处理的图片数提升,性能有一定程度优化。
耗时数据如下图所示。
02 动态shape(算子二进制)调优案例
问题背景
有些模型在模型计算的过程中会存在动态shape场景,即在模型计算过程中,模型的输入和输出存在多种shape。这种情况下,由于编译时无法已知全部shape信息,每次调用算子进行计算时都需要进行编译,会增加编译开销和内存使用,降低性能。
总体思路
使用PyTorch Analyse工具的动态shape模式对于模型脚本进行分析,判断是否存在动态shape算子。
- 若不存在可进行其他内容调优。
- 若存在,请安装算子二进制包。PyTorch框架可调用算子二进制包中算子编译信息,可设置模型编译时是否优先在线编译,以此解决动态shape问题、优化模型训练性能。
环境准备
- 点击 ,获取Yolov5模型,并根据readme准备训练环境和数据集。
- 参考 中“常用操作 > 安装、升级和卸载二进制算子包”章节,安装算子二进制包。
瓶颈识别
使用PyTorch Analyse工具的动态shape模式对于模型脚本进行分析,生成动态shape的分析报告msft_dynamic_shape_analysis_report.csv。
1、进入分析工具所在路径。
2、执行分析。
命令中的参数说明如下表所示。
参数 |
参数说明 |
取值示例 |
-i --input |
|
/home/xxx/analysis |
-o --output |
|
/home/xxx/analysis_output |
-v --version |
|
|
-m --mode |
|
|
-env --env-path |
|
/home/xxx/transformers/src /home/xxx/transformers/utils 多个文件路径使用空格隔开。 |
-api --api-files |
|
/home/xxx/mmcv_analysis/full_unsupported_results.csv /home/xxx/transformers_analysis/full_unsupported_results.csv 多个文件路径使用空格隔开。 |
-h --help |
显示帮助信息。 |
- |
3、完成分析后,在分析结果输出路径下会生成一个完整的模型脚本文件夹,其中模型脚本代码都做了自动修改。在分析结果输出路径下,修改训练脚本文件中读取训练数据集的for循环,手动开启动态shape检测,请参考下方示例进行修改。
修改前:
修改后:
在分析结果输出路径下,拉起修改过的训练脚本,运行一个epoch即可。完成后会在路径下生成动态shape的分析报告msft_dynamic_shape_analysis_report.csv。其中存储了函数名称、调用栈、所在文件、文件行号、输入和输出的shape范围,样例动态shape分析报告如下:
性能调优
在代码中使能算子二进制即可完成性能调优。
1、打开训练脚本。
2、在主函数中添加使能算子二进制的代码。
3、拉起训练。对比使能算子二进制前后的step训练耗时,发现性能数据获得提升即说明调优成功。如下图所示,未开启算子二进制时一个step训练总耗时(mTime)约为1.4s,开启后一个step训练总耗时(mTime)约为1.2s。
未开启算子二进制:
开启算子二进制:
- 点赞
- 收藏
- 关注作者
评论(0)