TINY-DNN导入TensorFlow模型
上一篇中通过mnist例子介绍了tensorflow卷积网络inference过程,根据这部分已经可以写出相应的C/C++代码了,但在github上看到了tiny-dnn这个开源库,就尝试将TensorFlow的mnist模型导入到tiny-dnn中
1.保存tensorflow模型
先在tensorflow中训练好tensorflow,保存checkpoint,这里就不freeze保存pb了,毕竟tiny-dnn也不能读取这种格式,计算图需要在tiny-dnn中重新构建
1.1 保存卷积层参数
如第一个卷积层conv1_w大小为,第二个为,可以通过numpy.savetxt将参数保存为txt,这里要注意参数纬度的保存循序,numpy手册中reshape操作有如下介绍
read / write the elements using C-like index order, with the last axis index changing fastest, back to the first axis index changing slowest.
表示当做numpy.shape(w,(1,-1))操作时时默认是从最后一维最先变换,说具体点就是按w(0,0,0,0)、w(0,0,0,1)…….w(0,0,1,0)、w(0,0,1,1)这样的顺序保存的,但tinydnn中是按filter保存的,顺序为w(0,0,0,0)、w(0,1,0,0)…..w(1,0,0,0)、w(1,1,0,0)……清楚顺序就可以写一个函数如下
def save_conv_variable(conv_w,filename_w,conv_b,filename_b):
width = conv_w.shape[0]
height = conv_w.shape[1]
w = conv_w[:,:,0,0]
for i in range(0,conv_w.shape[3]):
for j in range(0,conv_w.shape[2]):
w = numpy.append(w,conv_w[:,:,j,i])
w = w[width*height:]
numpy.savetxt(filename_w, w, delimiter=',', newline=',\n')
numpy.savetxt(filename_b, conv_b, delimiter=',', newline=',\n')
1.2 保存FC层参数
FC层参数一般是二维的,就直接保存就行了
def save_fc_variable(fc_w,filename_w,fc_b,filename_b):
numpy.savetxt(filename_w,fc_w,delimiter=',',newline=',\n')
numpy.savetxt(filename_b,fc_b,delimiter=',',newline=',\n')
1.3 保存reshape后的参数
上面说到numpy的reshape特性,如果tensorflow的某一层输出经过reshape后到一下个FC层,相应的参数也要按住特定顺序复原,如上一篇mnist例子中pool2输出为1*7*7*64,经reshape变为1*3136,下一层FC的输出为1024,则这个FC层参数为3136*1024,那么保存这个参数时也应该按照7*7*64*1024的顺序保存,这也很简单,写成一个函数就行了
def save_reshape_fc_variable(reshape_dim,fc_w,filename_w,fc_b,filename_b):
features = reshape_dim[2]
row = reshape_dim[0]
col = reshape_dim[1]
a = np.zeros([row*col*features,fc_w.shape[1]],dtype=float)
t = 0
for k in range(features):
for j in range(row):
for i in range(col):
a[t, :] = fc_w[i * features + j * col * features + k, :]
t = t + 1
numpy.savetxt(filename_w,a,delimiter=',',newline=',\n')
numpy.savetxt(filename_b,fc_b,delimiter=',',newline=',\n')
1.4 保存bn参数
这里的模型没有用到bn层,不过还是记录下相关内容
batchNormalization计算过程原论文描述如下,
在训练过程中是根据batch样本计算moving_mean、moving_variance,而训练完成后,利用mean、variance、gamma、beta按照上图中最后一个公式更新待测样本即可。
注意,tiny-dnn的bn层并没有scale与shift,即没有gamma和beta,如果想跟tensorflow一样,需要在bn层后面再添加linear_layer
如果不用缩放和偏置,在tensorflow的bn层设置一下即可
second_conv = tf.layers.batch_normalization(second_conv, training=is_training,
name='bn2',center=False,scale=False)
这样tensorflow跟tiny-dnn的BN层计算方式就相同了,保存参数就简单如下
def save_bn_variable(moving_mean,moving_variance,beta=0,gamma=1,filename='bn'):
numpy.savetxt(filename+'_beta.txt',beta,delimiter=',',newline=',\n')
numpy.savetxt(filename + '_gamma.txt', gamma, delimiter=',', newline=',\n')
numpy.savetxt(filename + '_moving_mean.txt', moving_mean, delimiter=',', newline=',\n')
numpy.savetxt(filename + '_moving_variance.txt', moving_variance, delimiter=',', newline=',\n')
2.tiny-dnn导入
tiny-dnn导入也很简单,直接在给的例子中修改
2.1 重构计算图
因为上面只保存了tensorflow的参数,没有计算图信息,那就在tiny-dnn中重新定义一下
nn << conv(28,28,5,5,1,32,padding::same) /* 32x32 in, 5x5 kernel, 1-6 fmaps conv */
<< relu(28,28,32)
<< max_pool(28, 28, 32, 2) /* 28x28 in, 6 fmaps, 2x2 subsampling */
<< conv(14, 14, 5, 5,32, 64, padding::same)
<< relu()
<< max_pool(14, 14, 64, 2)
<< fc(7 * 7 * 64, 1024)
<< relu()
<< fc(1024, 10);
可以看到定义过程很简单
2.2 导入参数
有了计算图后,下一步就是导入参数了
tiny-dnn可以很方便的看到自己定义的计算图信息
在C/C++程序里直接读取上面保存的txt参数文件就行了
std::vector<vec_t*> weights_input_layer1 = nn[0]->weights();
auto &w_w = *weights_input_layer1[0];
auto &w_b = *weights_input_layer1[1];
Read_File(*weights_input_layer1[0], "weights/3/conv1_w.txt");
Read_File(*weights_input_layer1[1], "weights/3/conv1_b.txt");
std::vector<vec_t*> weights_layer1_layer2 = nn[3]->weights();
Read_File(*weights_layer1_layer2[0], "weights/3/conv2_w.txt");
Read_File(*weights_layer1_layer2[1], "weights/3/conv2_b.txt");
std::vector<vec_t*> weights_layer2_layer3 = nn[6]->weights();
Read_File(*weights_layer2_layer3[0], "weights/3/fc1_w.txt");
Read_File(*weights_layer2_layer3[1], "weights/3/fc1_b.txt");
std::vector<vec_t*> weights_layer3_output = nn[8]->weights();
Read_File(*weights_layer3_output[0], "weights/3/fc2_w.txt");
Read_File(*weights_layer3_output[1], "weights/3/fc2_b.txt");
以上代码就是读取保存的tensorflow参数到tiny-dnn的nn的相应层weights里
这样就完成了tiny-dnn导入tensorflow卷积网络模型的过程了
predict就可以看到最终结果,可以看到c++程序与tensorflow的结果相同
auto result = nn.predict(test_data);
这个例子比较简单,后续再探索导入一些更多的操作
参考链接
https://github.com/keras-team/keras/issues/4914
https://github.com/ethereon/caffe-tensorflow/issues/59
https://github.com/ethereon/caffe-tensorflow/issues/40
https://github.com/tiny-dnn/tiny-dnn/issues/292
上一篇: 代码里的“随机应变”
下一篇: Java编辑版本问题解决