在博客开始前,首先郑重感谢博主jiongnima的博客对我的帮助,我按照博主的方法顺利的把bcnn模型的tensorflow版转为caffe版,关于bcnn模型,它是个细粒度分类模型,我将在后续的博客中详细介绍。
首先隆重呈上博主的链接:
https://blog.csdn.net/jiongnima/article/details/78382972
转模型系列博文的操作主要基于博主的实验探索,中间也穿插着我自己的一些想法以及重新编排,在强烈膜拜原创的基础上,将转模型过程进行了亲自的实践并成功复现,因此暂时(不要脸)的将该系列博文归为原创。
下面进入正题部分。
上一篇博客:https://blog.csdn.net/zl3090/article/details/89042717 已经介绍了tensorflow中参数的提取,本篇主要讲述如何将它转为caffe需要的shape.
tensorflow的卷积层权重shape是[kernel_height, kernel_width, input_channels, output_channels],其中input暂时可以理解为nchw中的c(channel),也就是输入的通道数,output可以暂时看做n,也就是卷积核的个数,也是输出的通道数,当然这里的in和out与nc在意义上有区别,下文会具体介绍二者的不同,此处这么理解是为了和我们熟悉的nchw格式有个对应. 补充介绍tf.nn.conv2d函数的参数:
这里说明filter中的hwio与data_formate的区别: hwio是指filter的形式,也就是卷积之前设置卷积核以及其输入输出的形式,它影响的是参数weight的数据形式,也就是说weight是按照hwio的形式存储的,也是我们将weight写入caffe中deploy需要依据的数据存储形式; data_formate中的nhwc是指卷积计算后输出数据的形式,也就是[batch_size, height, width, number_of_filters],也即是输出数据和weight和bias计算后的结果存储形式,也就是feature map中的形式,在这里写入deploy不会用到.
在caffe中,数据按照blob形式存储,对于blob中的数据来说,也就是计算后的数据存储形式是nchw, 也就是[number channel, height,width]; 对于卷积权重来说,也应该按照io来描述,也就是[output_channels, input_channels, kernel_height, kernel_width].
根据上述理解,我们可以进行shape的转换,代码如下:
#!/usr/bin/python
import tensorflow as tf
import numpy as np
with tf.Session() as sess:
new_saver = tf.train.import_meta_graph('.model.meta')
for var in tf.trainable_variables():
print var.name
new_saver.restore(sess, tf.train.latest_checkpoint('./checkpoint_dir/'))
all_vars = tf.trainable_variables()
for v in all_vars:
name = v.name
fname = name + '.prototxt'
fname = fname.replace('/','_')
print fname
v_4d = np.array(sess.run(v))
if v_4d.ndim == 4: # conv_w
#v_4d.shape [ H, W, I, O ]
v_4d = np.swapaxes(v_4d, 0, 2) # swap H, I
v_4d = np.swapaxes(v_4d, 1, 3) # swap W, O
v_4d = np.swapaxes(v_4d, 0, 1) # swap I, O
#v_4d.shape [ O, I, H, W ]
f = open(fname, 'w')
vshape = v_4d.shape[:]
v_1d = v_4d.reshape(v_4d.shape[0]*v_4d.shape[1]*v_4d.shape[2]*v_4d.shape[3])
f.write(' blobs {\n')
for vv in v_1d:
f.write(' data: %8f' % vv)
f.write('\n')
f.write(' shape {\n')
for s in vshape:
f.write(' dim: ' + str(s))#print dims
f.write('\n')
f.write(' }\n')
f.write(' }\n')
elif v_4d.ndim == 2: # fc_w
v_4d = np.swapaxes(v_4d,0,1) # swap IO
f = open(fname, 'w')
vshape = v_4d[:]
v_1d = v_4d.reshape(v_4d.shape[0]*v_4d.shape[1])
f.write(' blobs {\n')
for vv in v_1d:
f.write(' data: %8f' % vv)
f.write('\n')
f.write(' shape {\n')
for s in vshape:
f.write(' dim: ' + str(s))#print dims
f.write('\n')
f.write(' }\n')
f.write(' }\n')
elif v_4d.ndim == 1 :#do not swap #all_bias
f = open(fname, 'w')
f.write(' blobs {\n')
for vv in v_4d:
f.write(' data: %.8f' % vv)
f.write('\n')
f.write(' shape {\n')
f.write(' dim: ' + str(v_4d.shape[0]))#print dims
f.write('\n')
f.write(' }\n')
f.write(' }\n')
f.close()
上述代码将卷积层与全连接层的w和b分别保存为独立的prototxt,到此我们完成了转换。
下一篇博客会介绍如何放如deploy.prototxt里和生成caffemodel。