mobilefacenet mxnet2caffe 对比中间层输出差异

1 mxnet转caffe

参考这篇文章:https://blog.csdn.net/u012101561/article/details/89329130
github工程建议使用:https://github.com/Laulian/MxNet2Caffe-mobilefacenet
此工程有包含上面文章提到的一些修改
如果你使用的是python3,工程里面一些python2语法需要修改,如
1.1
mxnet2caffe过程,报错如下
TypeError: unsupported operand type(s) for +: 'dict_keys' and 'dict_keys'
这是由于python3不支持dict_keys,需要改为list类型
all_keys = list(arg_params.keys()) + list(aux_params.keys())
1.2
报错
AttributeError: 'dict' object has no attribute 'has_key'
修改
比如:if dict.has_key(word): 改为:if word in dict:'
1.3
json2prototxt过程,由于跳过了对_minus_scalar和_mul_scalar这两个op的处理,所以最后生成出来的.prototxt文件的conv_1_conv2d层,需要把bottom由_mulscalar0改为data。不然后面mxnet2caffe.py运行会报错
在这里插入图片描述

2 caffe中间层结果输出

caffe中间层结果输出比较简单,如下

out = mobilefacenet.forward(end='fc1_scale')

end=后面加模型的节点

3 mxnet中间层结果输出

mxnet的比较麻烦,参考文章:https://blog.csdn.net/disen10/article/details/79376631
我的代码:

sym, arg_params, aux_params = mx.model.load_checkpoint('./model/model',0)#载入模型
model = mx.mod.Module(symbol=sym, context=mx.cpu())
########################################################################
args = sym.get_internals().list_outputs() #获得所有中间输出
print('args=',args)
internals = model.symbol.get_internals()
lay22 = internals['_mulscalar0_output']
group = mx.symbol.Group([lay22, sym])  #把需要输出的结果按group方式组合起来,这样就可以得到中间层的输出
#########################################################################
mod = mx.mod.Module(symbol=group,context=mx.cpu()) #创建Module
mod.bind(for_training=False,data_shapes=[('data',(1,3,112,112))]) #绑定,此代码为预测代码,所以training参数设为False
mod.set_params(arg_params,aux_params)

img1_path = "./aligned_face.jpg"
array1 = single_input(img1_path)
Batch = namedtuple("batch", ['data'])
mod.forward(Batch([array1]))

其中mx.symbol.Group([lay22, sym]),其中lay22就是我们要读的中间节点,sym是原来整个模型的输出,在我这个模型也就是fc1_output,这就把两个节点输出结果都输出,foward之后
在这里插入图片描述
通过如下读取结果:

for i in range(len(mod.output_names)):
    print('output name=',mod.output_names[i])
    print(np.squeeze((mod.get_outputs()[i].asnumpy())))

4 两个模型结果差异分析

通过上述读取中间结果输出,我发现mxnet和caffe的数据输入层就有差异
1.归一化动作
要注意第一点,mxnet的归一化动作是放在模型里面_minusscalar0,_mulscalar0两层做的,而caffe没有这两层,所以caffe的数据输出data时候代码里面就需要做归一化动作
在这里插入图片描述
2.就算我caffe 图片输入前做了归一化动作,可是caffe data层输出对比mxnet的_mulscalar0_output层输出还是不一样。
mxnet读取图片用的方法:

    img = cv2.imread(path)
    # mxnet三通道输入是严格的RGB格式,而cv2.imread的默认是BGR格式,因此需要做一个转换
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img = cv2.resize(img, (112, 112))
    # 重塑数组的形态,从(图片高度, 图片宽度, 3)重塑为(3, 图片高度, 图片宽度)
    img = np.swapaxes(img, 0, 2)
    img = np.swapaxes(img, 1, 2)
    # 添加一个第四维度并构建NDArray
    img = img[np.newaxis, :]
    array = mx.nd.array(img)
    
	Batch = namedtuple("batch", ['data'])
	mod.forward(Batch([array]))

caffe读取图片的方式

	image_file="./aligned_face.jpg"
    transformer = caffe.io.Transformer({'data': mobilefacenet.blobs['data'].data.shape})
    transformer.set_transpose('data', (2, 0, 1))        #改变维度的顺序,由原始图片(112,112,3)变为(3,112,112)
    #transformer.set_raw_scale('data', 255)              #caffe.io.load_image将图片存储为[0, 1],设置缩放到[0, 255]
    transformer.set_channel_swap('data', (2, 1, 0))     #RGB设置为BGR
    #transformer.set_mean('data',127.5)
    im = caffe.io.load_image(image_file)
    mobilefacenet.blobs['data'].reshape(1, 3, 112, 112)
    mobilefacenet.blobs['data'].data[...] = transformer.preprocess('data', (im * 255 - 127.5) * 0.0078125)

一个用的是cv.imread,一个用的是caffe.io.load_image,网上说后者只是前者缩放到0~1,我做了如下实验,可以看出c不全是0,说明a*255不完全等于b,为了模型输入一致性,所以两边改为都采用cv.imread的方式
在这里插入图片描述
最后模型某些层输出对比如下,还是存在不小的差异,这些差异需要继续分析
在这里插入图片描述

5 分析第一层batchnorm差异原因

由于第一层batchnorm输出结果就存在小数点后第4位的差异,所以需要分析这一层差异的原因。
首先要了解batchnorm层的作用:https://blog.csdn.net/zhuiqiuk/article/details/88089500
在这里插入图片描述
Caffe中BatchNorm层的计算
在这里插入图片描述在这里插入图片描述
所以要分析batchnorm层的差异,就要分析它的参数是否一致。

5.1 mxnet读取中间层参数

mod.forward(Batch([array1]))
#查看中间层参数
keys1 = mod.get_params()[0].keys()  # 列出所有权重名称
keys2 = mod.get_params()[1].keys()  # 列出所有权重名称
key='conv_1_batchnorm_beta'
if key in keys1:
    conv_w = mod.get_params()[0][key]  # 获取想要查看的权重信息,如conv_weight
    print(key,' _arg_params=',conv_w.asnumpy())  # 查看具体数值
if key in keys2:
    conv_w = mod.get_params()[1][key]  # 获取想要查看的权重信息,如conv_weight
    print(key,' _aux_params=',conv_w.asnumpy())  # 查看具体数值
  1. 读取参数需要执行forward()之后
  2. mxnet把能读的blob分了两组,arg/aux,所以需要读的key需要在两组里面找
    下图左边是mxnet读到的参数。
    在这里插入图片描述

5.2 caffe读取中间层参数

    mobilefacenet = caffe.Net(pretrained,model_file,caffe.TEST)

    #中间层参数
    for param_name in mobilefacenet.params.keys():
        if param_name=='conv_1_batchnorm':
            for i in range(len(mobilefacenet.params[param_name])):
                if i==0:
                    print(param_name,'weight=',mobilefacenet.params[param_name][i].data.shape,)
                if i==1:
                    print(param_name,'bias=',mobilefacenet.params[param_name][i].data.shape,)
                if i==2:
                    print(param_name,'other=',mobilefacenet.params[param_name][i].data.shape,)
                print(mobilefacenet.params[param_name][i].data)
  1. caffe的参数不同层数量会不一样,有些有3个参数,所以代码做了一个循环打印
  2. 能获取参数的blob都在mobilefacenet.params.keys()
    下图右边是caffe读到的参数。
    在这里插入图片描述
    综上图,batchnorm的下面几个参数都是一样的
    在这里插入图片描述
    就剩下ε,也就是eps了

6 差异原因

mxnet的batchnorm层

    {
      "op": "BatchNorm", 
      "name": "conv_1_batchnorm", 
      "attrs": {
        "fix_gamma": "False", 
        "momentum": "0.9"
      }, 
      "inputs": [[4, 0, 0], [5, 0, 0], [6, 0, 0], [7, 0, 1], [8, 0, 1]]
    }, 

caffe的batchnorm层

layer {
  bottom: "conv_1_conv2d"
  top: "conv_1_batchnorm"
  name: "conv_1_batchnorm"
  type: "BatchNorm"
  batch_norm_param {
    use_global_stats: true
    moving_average_fraction: 0.9
    eps: 2e-05
  }
}

可以看到,caffe的eps=2e-05,mxnet没有定义,查看mxnet文档:https://mxnet.apache.org/api/python/docs/api/symbol/contrib/index.html
在这里插入图片描述
我们把mxnet转caffe的代码改一下eps默认值,改成0.0010000000474974513
在这里插入图片描述
最后模型之间的误差减小到0.0000006161
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值