欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

TINY-DNN导入TensorFlow模型

程序员文章站 2022-07-16 17:11:12
...

上一篇中通过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大小为[5,5,1,32],第二个为[5,5,32,64],可以通过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计算过程原论文描述如下,


TINY-DNN导入TensorFlow模型

在训练过程中是根据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可以很方便的看到自己定义的计算图信息
TINY-DNN导入TensorFlow模型

在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

代码地址https://github.com/wangwei2009/tinydnn-tensorflow