- 本人在今年的1~3月份参与完成了华为昇腾MindSpore发布的D-LinkNet训练任务,也是第一次接触MindSpore这一框架,主要任务实际上是根据现有的基于pytorch框架的D-LinkNet项目,将其转写到MindSpore框架下进行训练并完成和原论文精度的对标
- 本帖子将分为两部分,第一部分为对于本人完成整个项目过程的简介,第二部分为自己在任务完成过程中遇到的各种问题以及解决方法,希望对看到这一帖子的人可以有所帮助
一、项目过程的简介
1.1 理解原论文并且跑通原有框架下的脚本
- 首先当然是对自己将要转写的模型进行了解学习,如果啃原论文有难度的话通常可以到知乎或csdn等其他平台搜索介绍自己负责模型的博文,一般都会有很大的帮助
- 其次要在原框架下复现跑通标杆的项目脚本,在复现时要注意尽量使用项目中写明的对应版本的框架脚本,否则可能因为版本原因带来额外不必要的麻烦
- 在复现原框架下复现跑通标杆的项目脚本时通常跑通即可,不必要一定自己训练达到论文中给出的精度
1.2 获取数据集并转写数据预处理部分脚本
- 获取数据集这一步实际上在跑通原有框架下的脚本前就应该已经完成了
- 本人在完成此次任务时首先转写了数据预处理部分的脚本,因为这部分要转写的内容其实较少,尤其对于图像处理类型的问题一般都采用opencv库,仅在dataloader的部分需要使用mindspore对应的接口
- mindspore 快速入门数据加载和处理
- 上面这个教程可以帮助你快速入门这一部分
1.3 转写网络架构
- 在开始转写网络架构前,需要首先了解自己使用的模型是否使用了backbone模型,使用了哪些backbone模型,然后到mindspore已有的modelzoo中去搜寻是否已经有其对应的可用的实现,以及对应的预训练权重,如果确实没有的话,则需要自己实现和训练对应的backbone模型(这种情况应该很少)
- 比如我本次任务负责的D-LinkNet模型使用了resnet34模型,我就在modelzoo中找到了官方实现的resnet34模型以及对应的预训练权重
- 在正式开始转写网络架构时,要注意原框架的各个算子在原框架和mindspore框架下的差异,而不要盲目的直接进行复制和简单的改写,比如,PyTorch与MindSpore对应算子的差异可以在这里找到
- 如果遇到原框架包含而mindspore当前没有支持的算子,则需要根据具体情况和华为方面进行沟通,决定是自己进行实现还是等待华为的技术人员进行实现
- 在这一步要进行以跑通为目的的调试,建议此步骤先使用PYNATIVE_MODE模式,再使用GRAPH模式。
- 另外,要注意使你编写的模型可以在定义时选择是否加载预训练权重,这样可以避免在执行eval.py或export.py这类无需加载预训练权重脚本时浪费时间。
1.4 转写训练脚本,并进行训练
- 在转写训练脚本时,要注意对应的损失函数、优化器与原脚本要尽量相同
- 如果原脚本的损失函数是自己定义的,可以参考这一链接进行自定义
- 对于pytorch这类脚本的转写,其原训练脚本可能较为自由,而简单地只使用mindspore的
model.train()
方法可能无法重写,这时候就可以利用mindspore框架提供的自定义callback类的方法进行重写,可以参考这个链接 - 另外,在训练脚本中要做好是否是进行分布式训练的区分,如果进行分布式训练则会额外进行一些步骤,可以参考这个链接
1.5 编写评估(eval)脚本与精度调优
- 在编写评估(eval)脚本时主要注意两点,一是尽量参考与自己模型类似的已有的modelzoo的评估脚本,二是注意突出自己要与原论文主要对标的精度标准
- 对于精度调优,如果遇到精度与目标差距较大的问题,应该首先参考这一文档进行排查
- 额外的,调整定义Model时的
amp_level
参数,以及尝试使用LossScale,也可能对你的精度有较好地提升效果
1.6 编写export.py脚本
- 这一步骤较为简单,参考modelzoo中与自己模型相似的export.py脚本即可实现
1.7 交付前的准备工作
- 确保自己编写的脚本能够顺利执行
- 编写中英文的readme文档
- 填写交付件文档
二、本人遇到的一些问题
2.1 mindspore的Conv2dTranspose算子不支持output_padding参数
- 本人在进行转写网络架构的过程中,遇到了原torch脚本的这一行代码
self.deconv2 = nn.ConvTranspose2d(in_channels // 4, in_channels // 4, 3,
stride=2, padding=1, output_padding=1)
- 而mindspore框架下对应这一算子的
Conv2dTranspose
并没有output_padding
这一参数,见此链接 - 经过我查阅这一链接后,我理解了torch中
Conv2dTranspose
算子的output_padding
这一参数的意义和作用 - 我首先尝试了直接在转写时忽略
output_padding
这一参数,但在调试网络时发生了报错,经检测发现就是由于Conv2dTranspose
这一算子的输出shape不正确导致的 - 这时我又查阅了mindspore框架下对应这一算子的
Conv3dTranspose
,发现这一算子具有output_padding
这一参数,见此链接 - 因此我最后采用了
Conv3dTranspose
算子以替代Conv2dTranspose
算子,同时在Conv3dTranspose
算子前后进行维度的拉伸和压缩,以保证模型调试通过,代码如下(省略代码中其他部分):
class DecoderBlock(nn.Cell):
def __init__(self, in_channels, n_filters):
super(DecoderBlock, self).__init__()
self.expand_dims = P.ExpandDims()
self.deconv3 = nn.Conv3dTranspose(in_channels // 4, in_channels // 4,
kernel_size=(1, 3, 3),
stride=(1, 2, 2),
padding=(0, 0, 1, 1, 1, 1),
output_padding=(0, 1, 1),
pad_mode='pad',
has_bias=True,
weight_init=Tensor(conv_weight_init((
in_channels // 4, in_channels // 4, 1, 3, 3))))
def construct(self, x):
x = self.expand_dims(x, 2)
x = self.deconv3(x)
x = x.squeeze(2)
return x
2.2 训练时train_loss值始终不下降,而是左右摆动
- 这一问题在使用Loss Scale后即解决,具体使用了
DynamicLossScaleManager
这一类 - 具体可见这一链接以及此链接
- 可以发现使用Loss Scale往往对于精度至关重要
2.3 此次任务使用竞赛数据集,难以与原论文进行直接的精度对标
- 由于本次的D-LinkNet任务来源于2018年的一项比赛,其数据集为比赛数据集并不公开,网上的数据集只有训练集拥有对应的标签,验证集和测试集均无对应标签数据,所以在910评估和310推理阶段都无法直接和原论文对标精度
- 我在和负责310推理的同学商量后,同华为方面老师沟通,给出了我们觉得可行的精度对标方案,并获得了同意,解决了这一问题
- 如果有同学遇到与我们这次类似的问题,也应该及时和华为方面老师沟通,给出自己的替代解决方案,尽早解决这一问题
2.4 即使使用线上机器,也要保证线下脚本能够顺利运行
- 由于本人前期均使用线上机器,只少数几次在线下机器执行了脚本,导致最后在线下机器进行验收时执行各个脚本出现了较多状况,进行了多次整改后才解决这些问题
- 建议各位负责训练的同学,即使主要使用线上机器,也要在最后提交前保证线下脚本能够顺利运行
- 以上就是本人对MindSpore训练过程的一点儿经验分享,希望能帮助到大家,谢谢~