主要参考
主要参考 ssrnet2caffe,Keras2caffe,使用Keras2caffe中的代码,参考ssrnet2caffe中的转换代码,对不支持的层进行重新写op。caffe环境采用源码编译,我采用的keras和TensorFlow环境是2.1.5和1.10.0,可以采用pip install keras==2.1.5来安装
这篇文章主要想对ssrnet2caffe中提到的问题进行解决,和具体解决的办法。
问题的出现
Traceback (most recent call last):
File "ssrnet2caffe.py", line 30, in <module>
convert.convert(model, 'ssrnet.prototxt', 'ssrnet.caffemodel')
File "/home/zhr/ssr-net/keras2caffe/convert.py", line 460, in convert
caffe_model.params[layer][n].data[...] = net_params[layer][n]
can't boardcast (3,160) to (3,250)
在进行转换的时候dense1层的参数caffe计算出来是3x250,而根据SSRNET_MODEL.py进行训练的模型读取到的参数是3x160,这两者无法匹配,从而转换失败。
在ssrnet2caffe中,作者也提到这是由于caffe中pooling层的padding模式是向上取整的;而TensorFlow中的pooling是有两个模式:VALID和SAME。SAME是会进行自动填充,而VALID则是舍弃多于区域(向下取整的),所以导致两者的差异
问题的解决
方法一:
在SSR_MODEL.py中,对每一层的pooling的padding方式不用VALID,而是用SAME进行重新训练,这样子得到的模型文件在dense1层的参数也是3x250,与转换之后caffe需要的模型参数是一致的。
但是这个方法费时又费力,而且github上大牛们训练好的模型中pooling的方式也都是VALID,虽然换成SAME之后参数是对了,也不知道对最后的准确率有没有影响。
方法二:(重点介绍)
更改caffe源码中pooling层padding的方式,由向上取整改成,向下取整。所以首先要安装caffe源码,这部分内人在我的另一篇博客中有提到,大家感兴趣的可以去查阅。
具体如何修改caffe呢? caffe源码中pooling层的padding方式其实是有两种方式的,一种是ceil(向上取整),一种是floor(向下取整),代码是在~/caffe/src/caffe/layers/pooling_layer.cpp可以看见,如下:
switch (round_mode_) {
case PoolingParameter_RoundMode_CEIL:
pooled_height_ = static_cast<int>(ceil(static_cast<float>(
height_ + 2 * pad_h_ - kernel_h_) / stride_h_)) + 1;
pooled_width_ = static_cast<int>(ceil(static_cast<float>(
width_ + 2 * pad_w_ - kernel_w_) / stride_w_)) + 1;
break;
case PoolingParameter_RoundMode_FLOOR:
pooled_height_ = static_cast<int>(floor(static_cast<float>(
height_ + 2 * pad_h_ - kernel_h_) / stride_h_)) + 1;
pooled_width_ = static_cast<int>(floor(static_cast<float>(
width_ + 2 * pad_w_ - kernel_w_) / stride_w_)) + 1;
break;
default:
LOG(FATAL) << "Unknown rounding mode.";
}
对~/caffe/src/caffe/proto/caffe.proto进行修改,然后重新编译(make all -j16;make pycaffe -j16)把新生成的库和包对原来的进行覆盖就好了,具体修改如下(951行附近):
//把原先默认的CEIL改成FLOOR即可
//optional RoundMode round_mode = 13 [default = CEIL];
optional RoundMode round_mode = 13 [default = FLOOR];
改完之后重新编译,并把库重新覆盖
make all -j16
make pycaffe -j16
cd python
cp -rf caffe/ ~/miniconda3/envs/keras2caffe/lib/python3.6/
cp -rf ../.build_release/lib/ ~/miniconda3/envs/keras2caffe/lib/
这样就大功告成了,caffe对于dense1中参数的计算也就变成了(3,160),然后就成功转换了。
结果测试
以下是caffe的测试代码:(最后处理的部分对着keras的代码写的)
import cv2
import caffe
import numpy as np
caffe.set_mode_cpu()
model_def = 'age_ssrnet.prototxt'
model_weights = 'age_ssrnet.caffemodel'
#model_def = 'gender_ssrnet.prototxt'
#model_weights = 'gender_ssrnet.caffemodel'
net = caffe.Net(model_def, # defines the structure of the model
model_weights, # contains the trained weights
caffe.TEST) # use test mode (e.g