环境:
wsl2 Ubuntu 20.04
mindspore 2.0.0
python 3.8
pytorch 2.0.1 cpu
基于已有的mindspore FCOS项目和FCOS官方pytorch权重来做迁移,
迁移其实就是在做权重的键值映射对齐,这其中有一些规律可寻,但不多,更多需要自己的分析比对,建立映射字典。
可参考的经验:
根据上述迁移经验,打印两种框架的权重的名称及shape进行比对,总结名称转换方式如下(pytorch的名称改为mindspore的):
import copy, torch
import mindspore as ms
def fcos_pth2ckpt():
m = ms.load_checkpoint('test.ckpt') # mindspore FCOS保存的随机权重
t = torch.load('./weights/FCOS_imprv_R_50_FPN_1x.pth', map_location=torch.device('cpu')) # pytorch FCOS权重
match_pt_kv = {} # 匹配到的pt权重的name及value的字典
match_pt_kv_mslist = [] # 匹配到的pt权重的name及value的字典, mindspore加载权重需求的格式
not_match_pt_kv = {} # 未匹配到的pt权重的name及value
matched_ms_k = [] # 被匹配到的ms权重名称
'''一般性的转换规则'''
pt2ms = {'module': 'fcos_body', # backbone部分
'stem.': '',
'.body': '',
'.rpn': '',
'downsample': 'down_sample_layer',
'backbone.fpn': 'fpn', # FPN部分
'fpn_inner4': 'prj_5',
'fpn_layer4': 'conv_5',
'fpn_inner3': 'prj_4',
'fpn_layer3': 'conv_4',
'fpn_inner2': 'prj_3',
'fpn_layer2': 'conv_3',
'top_blocks.p': 'conv_out',
'bbox_tower': 'reg_conv', # head部分
'cls_tower': 'cls_conv',
'bbox_pred': 'reg_pred',
'scales': 'scale_exp',
'centerness': 'cnt_logits',
"running_mean": "moving_mean", # BN部分
"running_var": "moving_variance",
}
'''BN层的特殊转换规则'''
pt2ms_bn = {
"weight": "gamma",
"bias": "beta",
}
for i in t['model'].keys():
pt_name = copy.deepcopy(i)
pt_value = copy.deepcopy(t['model'][i])
'''通用的处理'''
for k, v in pt2ms.items():
if k in pt_name:
pt_name = pt_name.replace(k, v)
'''BN层处理'''
if 'bn' in pt_name:
for k, v in pt2ms_bn.items():
if k in pt_name:
pt_name = pt_name.replace(k, v)
'''下采样层特别处理'''
if 'down' in pt_name:
if 'bias' in pt_name:
pt_name = pt_name.replace('bias', 'beta')
if 'down_sample_layer.1.weight' in pt_name:
pt_name = pt_name.replace('weight', 'gamma')
'''head部分的特殊处理'''
if 'cls_conv' in pt_name or 'reg_conv' in pt_name:
if '1' in pt_name or '4' in pt_name or '7' in pt_name or '10' in pt_name:
pt_name = pt_name.replace('weight', 'gamma')
pt_name = pt_name.replace('bias', 'beta')
'''改名成功,匹配到ms中的权重了,记录'''
if pt_name in m.keys():
assert pt_value.shape == m[pt_name].shape
match_pt_kv[pt_name] = pt_value
match_pt_kv_mslist.append({'name': pt_name, 'data': ms.Tensor(pt_value.numpy(), m[pt_name].dtype)})
matched_ms_k.append(pt_name)
else:
not_match_pt_kv[i + ' ' + pt_name] = pt_value
'''打印未匹配的pt权重名称'''
print('\n\n------------------未匹配的pt权重名称--------------------')
for j in not_match_pt_kv.keys():
print(j, np.array(not_match_pt_kv[j].shape))
'''打印未被匹配到的ms权重名称'''
print('\n\n------------------未被匹配到的ms权重名称--------------------')
for j in m.keys():
if j not in matched_ms_k:
print(j, np.array(m[j].shape))
print('end')
return match_pt_kv_mslist
输出:
------------------未匹配的pt权重名称--------------------
------------------未被匹配到的ms权重名称--------------------
fcos_body.backbone.end_point.weight [1001 2048]
fcos_body.backbone.end_point.bias [1001]
这俩权重不参与模型forward,是冗余的。
match_pt_kv_mslist就是转换后的mindspore权重,加载后测试发现输出有很大出入,第一个原因是mindspore1.10的ops.sort算子有bug,已提交[issue]https://gitee.com/mindspore/mindspore/issues/I7EHKI),后续版本修复了,所以我升级到2.0.0版本了,其他原因就是网络实现未对齐,接下来主要讲这部分。
MindSpore FCOS项目链接 输入处理方式就与FCOS官方pytorch实现不一样
其他的裁剪,图像padding对推理结果影响不会很大。
归一化对齐为官方实现后仍发现图片值仍有不同(B通道的最大值不一样),可能Normalize的底层实现有区别?没有深究,后续直接用torch的Normalize结果张量输入到mindspore中以实现模型输入对齐。
输入对齐后的测试:使用coco2017验证集第一张图像(val/000000000139.jpg),resize到(800,1216)大小,两个框架的模型分别输入进去,输出有差别,
进行排查,发现模型第一个卷积的padding没对齐。
same模式下的卷积跟torch的pad模式下肯定不一样,且两种框架的same也不一样:算子区别
结果自然不一样:
原实现:
nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=0, pad_mode='same', weight_init=weight)
改为:
nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, pad_mode='pad', weight_init=weight)
其实发现设置mindsporefocs实现的resnet中的self.res_base=True就会调用正确的7x7卷积。
第一个卷积对了,但后面BN层就不对了,官方的BN层是一种frozenBN,没有使用eps,去除了eps按公式手动计算,但还是有误差,不知为何…
此外,mindspore实现的fcos的卷积pad_mode全选的same,这个肯定与官方的对不齐,pytorch官方的全使用的zeros模式,对应的mindspore应该是pad模式吧
FCOS对齐先放在这儿,后续再处理,已经有了一定的经验,先去做TOOD的迁移。