将训练好的模型进行封装然后在不同平台使用(分框架,分平台)

一. tensorflow框架下的模型封装,固化

一般都是使用python在服务器上训练好模型之后,将模型的参数和计算图结构进行固化为pb文件,然后使用C++,JAVA,go,调用,当然也可以在mobile上使用。接下来将分别对应不同的语言和不同的平台给出具体解决方法。

1.1 tensorflow下的模型定义以及训练(基于python语言)

参考

固化模型可以自己写代码,也可以使用tensorflow官方提供的freeze_graph.py小工具进行固化.
先使用一个简单的模型来进行讲解:

#-*- coding:utf-8 -*-
import tensorflow as tf
import numpy as np


with tf.variable_scope('Placeholder'):
    inputs_placeholder = tf.placeholder(tf.float32, name='inputs_placeholder', shape=[None, 10])
    labels_placeholder = tf.placeholder(tf.float32, name='labels_placeholder', shape=[None, 1])

with tf.variable_scope('NN'):
    W1 = tf.get_variable('W1', shape=[10, 1], initializer=tf.random_normal_initializer(stddev=1e-1))
    b1 = tf.get_variable('b1', shape=[1], initializer=tf.constant_initializer(0.1))
    W2 = tf.get_variable('W2', shape=[10, 1], initializer=tf.random_normal_initializer(stddev=1e-1))
    b2 = tf.get_variable('b2', shape=[1], initializer=tf.constant_initializer(0.1))

    a = tf.nn.relu(tf.matmul(inputs_placeholder, W1) + b1)
    a2 = tf.nn.relu(tf.matmul(inputs_placeholder, W2) + b2)

    y = tf.div(tf.add(a, a2), 2)

with tf.variable_scope('Loss'):
    loss = tf.reduce_sum(tf.square(y - labels_placeholder) / 2)

with tf.variable_scope('Accuracy'):
    predictions = tf.greater(y, 0.5, name="predictions")
    correct_predictions = tf.equal(predictions, tf.cast(labels_placeholder, tf.bool), name="correct_predictions")
    accuracy = tf.reduce_mean(tf.cast(correct_predictions, tf.float32))


adam = tf.train.AdamOptimizer(learning_rate=1e-3)
train_op = adam.minimize(loss)

# generate_data
inputs = np.random.choice(10, size=[10000, 10])
labels = (np.sum(inputs, axis=1) > 45).reshape(-1, 1).astype(np.float32)
print('inputs.shape:', inputs.shape)
print('labels.shape:', labels.shape)


test_inputs = np.random.choice(10, size=[100, 10])
test_labels = (np.sum(test_inputs, axis=1) > 45).reshape(-1, 1).astype(np.float32)
print('test_inputs.shape:', test_inputs.shape)
print('test_labels.shape:', test_labels.shape)

batch_size = 32
epochs = 10

batches = []
print("%d items in batch of %d gives us %d full batches and %d batches of %d items" % (
    len(inputs),
    batch_size,
    len(inputs) // batch_size,
    batch_size - len(inputs) // batch_size,
    len(inputs) - (len(inputs) // batch_size) * 32)
)
for i in range(len(inputs) // batch_size):
    batch = [ inputs[batch_size*i:batch_size*i+batch_size], labels[batch_size*i:batch_size*i+batch_size] ]
    batches.append(list(batch))
if (i + 1) * batch_size < len(inputs):
    batch = [ inputs[batch_size*(i + 1):],labels[batch_size*(i + 1):] ]
    batches.append(list(batch))
print("Number of batches: %d" % len(batches))
print("Size of full batch: %d" % len(batches[0]))
print("Size if final batch: %d" % len(batches[-1]))

global_count = 0

with tf.Session() as sess:
    sess.run(tf.initialize_all_variables())
    for i in range(epochs):
        for batch in batches:
            # print(batch[0].shape, batch[1].shape)
            train_loss , _= sess.run([loss, train_op], feed_dict={
                inputs_placeholder: batch[0],
                labels_placeholder: batch[1]
            })
            # print('train_loss: %d' % train_loss)

            if global_count % 100 == 0:
                acc = sess.run(accuracy, feed_dict={
                    inputs_placeholder: test_inputs,
                    labels_placeholder: test_labels
                })
                print('accuracy: %f' % acc)
            global_count += 1

    acc = sess.run(accuracy, feed_dict={
        inputs_placeholder: test_inputs,
        labels_placeholder: test_labels
    })
    print("final accuracy: %f" % acc)
    #在session当中就要将模型进行保存
    saver = tf.train.Saver()
    last_chkp = saver.save(sess, 'results/graph.chkp')

for op in tf.get_default_graph().get_operations():
    print(op.name)

跑出来之后使用tf.train.Saver()之后的结果入下:
result
上面有四个文件:
- .chpt.meta或者data文件,保存tensorflow模型的计算图结构。可以理解为神经网络的网络结构 .
- .chpt.index文件,保存计算图下所有变量的取值。
- checkpoint文件,保存目录下所有模型文件列表。
我们需要的是将index和meta进行固化。也就是图和参数。

1.2 模型的固化

参考:
- 博客
固化步骤如下:
- 恢复我们保存的图
- 开启一个Session,然后载入该图要求的权重
- 删除对预测无关的metadata
- 将处理好的模型序列化之后保存

#-*- coding:utf-8 -*-
import os, argparse
import tensorflow as tf
from tensorflow.python.framework import graph_util

dir = os.path.dirname(os.path.realpath(__file__))

def freeze_graph(model_folder):
    # We retrieve our checkpoint fullpath
    checkpoint = tf.train.get_checkpoint_state(model_folder)
    print(checkpoint)
    input_checkpoint = checkpoint.model_checkpoint_path

    # We precise the file fullname of our freezed graph
    absolute_model_folder = "\\".join(input_checkpoint.split('\\')[:-1])
    output_graph = absolute_model_folder + "\\frozen_model.pb"

    # Before exporting our graph, we need to precise what is our output node
    # this variables is plural, because you can have multiple output nodes
    #freeze之前必须明确哪个是输出结点,也就是我们要得到推论结果的结点
    #输出结点可以看我们模型的定义
    #只有定义了输出结点,freeze才会把得到输出结点所必要的结点都保存下来,或者哪些结点可以丢弃
    #所以,output_node_names必须根据不同的网络进行修改
    output_node_names = "Accuracy\predictions"

    # We clear the devices, to allow TensorFlow to control on the loading where it wants operations to be calculated
    clear_devices = True

    # We import the meta graph and retrive a Saver
    saver = tf.train.import_meta_graph(input_checkpoint + '.meta', clear_devices=clear_devices)

    # We retrieve the protobuf graph definition
    graph = tf.get_default_graph()
    input_graph_def = graph.as_graph_def()

    #We start a session and restore the graph weights
    #这边已经将训练好的参数加载进来,也即最后保存的模型是有图,并且图里面已经有参数了,所以才叫做是frozen
    #相当于将参数已经固化在了图当中 
    with tf.Session() as sess:
        saver.restore(sess, input_checkpoint)

        # We use a built-in TF helper to export variables to constant
        output_graph_def = graph_util.convert_variables_to_constants(
            sess, 
            input_graph_def, 
            output_node_names.split(",") # We split on comma for convenience
        ) 

        # Finally we serialize and dump the output graph to the filesystem
        with tf.gfile.GFile(output_graph, "wb") as f:
            f.write(output_graph_def.SerializeToString())
        print("%d ops in the final graph." % len(output_graph_def.node))


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("--model_folder", type=str, help="Model folder to export")
    args = parser.parse_args()
    print(args.model_folder)
    freeze_graph(args.model_folder)

代码运行python freeze.py --model_folder="F:\graduateStudy\dengtaAI\testSaver\results" ,注意修改文件的路径名,这里使用的是绝对路径。
之后就可以在results文件夹下看到一个frozen_model.pb的文件了。这个就是将模型和参数都进行固化的文件。
这里时自己写的freeze.py脚本,当然也可以调用官方的tools工具,但是有些许的不同,具体可以参考博客
pd文件生成之后,可以在C++,Java,Go语言中调用,也可以用在安卓,苹果手机开发的APP中。具体调用方法接下来说C++和Java的。

1.2 C++调用固化模型

参考:

为了能在你的C++项目中调用tensorflow的固化模型,并且进行模型额使用,你必须先将tensorflow源码编译成.so文件,然后在自己的C++代码环境中依赖这个文件完成编译。C的API依赖libtensorflow.so,C++的API依赖libtensorflow_cc.so。关于C的,在官网已经有了,可以直接下载下来使用,关于C++的编译,可以参考官网. 还有stackoverflow上的解答也可以参考,在你编译完tensorflow的.so文件之后,在自己的项目中调用的代码如下:
新建Session, 从model_path 加载*.pb模型文件,并在Session中创建图。预测的核心代码如下:

#include "tensorflow/core/public/session.h"  
#include "tensorflow/core/platform/env.h"
// @brief: 从model_path 加载模型,在Session中创建图
// ReadBinaryProto() 函数将model_path的protobuf文件读入一个tensorflow::GraphDef的对象
// session->Create(graphdef) 函数在一个Session下创建了对应的图;

int ANNModelLoader::load(tensorflow::Session* session, const std::string model_path) {
    //Read the pb file into the grapgdef member
    tensorflow::Status status_load = ReadBinaryProto(Env::Default(), model_path, &graphdef);
    if (!status_load.ok()) {
        std::cout << "ERROR: Loading model failed..." << model_path << std::endl;
        std::cout << status_load.ToString() << "\n";
        return -1;
    }

    // Add the graph to the session
    tensorflow::Status status_create = session->Create(graphdef);
    if (!status_create.ok()) {
        std::cout << "ERROR: Creating graph in session failed..." << status_create.ToString() << std::endl;
        return -1;
    }
    return 0;
}

在这个例子中我们把C++的API方法都封装在基类里面了。 FeatureAdapterBase 用来处理输入的特征,以及ModelLoaderBase提供统一的模型接口load()和predict()方法。然后可以根据自己的模型可以继承基类实现这两个方法,如本demo中的ann_model_loader.cpp。
编译的时候需要链接到tensorflow.so的那个库才能完成编译,可以写一个cmakelist.txt,然后再将操作命令写入makefile文件中。细节不多说。

1.3 JAVA调用固化模型

参考:

  • 官网
  • 博客1
  • 博客2
  • 官网安卓实例
    要在JAVA项目中使用tensorflow的模型,也是需要先有tensorflow的jar包才能用的,所以可以先参考官网进行java的jar包的生成,然后再在自己的项目中引入即可。

1.4移动端使用固话模型

参考:

二. caffe框架下的模型封装,固化

当训练好一个caffe模型之后,会输出一个.caffemodel的文件,里面存储的就是训练模型的各种参数,接下来我们就可以利用这个model做预测了。在这之前,我们还需要一个文件:deploy.prototxt。那么,就让我们从deploy.prototxt开始说起。deploy.prototxt文件和train.prototxt相似,区别在于第一层的输入数据层被删除,然后添加一个数据维度的描述。同时,移除了最后的”loss”和”accurary”层,加入”prob”层,也就是一个Softmax概率层。当然根据不同的模型可能最后是有些许的不同的。

2.1 python下使用已经训练好的模型

参考:
- 官网webdemo
- 官网
- 博客

准备好训练好的模型以及deploy.prototxt文件之后,python调用方法如下:(必须已经安装caffe)

# -*- coding: UTF-8 -*-
import caffe                                                     
import numpy as np

def test(my_project_root, deploy_proto):
    caffe_model = my_project_root + 'mnist_iter_9380.caffemodel'        #caffe_model文件的位置
    img = my_project_root + 'mnist/test/6/09269.png'                    #随机找的一张待测图片
    labels_filename = my_project_root + 'mnist/test/labels.txt'            #类别名称文件,将数字标签转换回类别名称

    net = caffe.Net(deploy_proto, caffe_model, caffe.TEST)                #加载model和deploy

    #图片预处理设置
    transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})  #设定图片的shape格式(1,3,28,28)
    transformer.set_transpose('data', (2,0,1))                            #改变维度的顺序,由原始图片(28,28,3)变为(3,28,28)
    transformer.set_raw_scale('data', 255)                                # 缩放到【0,255】之间
    transformer.set_channel_swap('data', (2,1,0))                       #交换通道,将图片由RGB变为BGR

    im = caffe.io.load_image(img)                                       #加载图片
    net.blobs['data'].data[...] = transformer.preprocess('data',im)     #执行上面设置的图片预处理操作,并将图片载入到blob中

    out = net.forward()                                                    #执行测试

    labels = np.loadtxt(labels_filename, str, delimiter='\t')           #读取类别名称文件
    prob = net.blobs['prob'].data[0].flatten()                             #取出最后一层(Softmax)属于某个类别的概率值
    order = prob.argsort()[-1]                                          #将概率值排序,取出最大值所在的序号
    print '图片数字为:',labels[order]                                   #将该序号转换成对应的类别名称,并打印

if __name__ == '__main__':
    my_project_root = "/home/Jack-Cui/caffe-master/my-caffe-project/"    #my-caffe-project目录
    deploy_proto = my_project_root + "mnist/deploy.prototxt"            #保存deploy.prototxt文件的位置
    test(my_project_root, deploy_proto)

官网自己写了一个classify.py这个文件进行分类,不过官网都比较全,各种情况都考虑了,所以代码看起来比较多。基本可以参考上面实现就好。

2.2 C++下使用已经训练好的模型

参考:
- 官网
- 博客
- 博客选择看后半部分
具体可以参考官网的来看,官网的C++部分是最详细的。如果想要实现多张图片的处理,也就是循环处理,那么参考最后博客中的后半部分即可。

2.3 java下使用已经训练好的模型

参考:
- jcaffe
- 论坛

2.4 安卓端使用caffe

参考:
- 博客1
- 博客2
- github

三、总结

按照框架分主要有两种tensorflow、caffe等其它,但是这里主要使用这两种。
按照语言分主要是python,C++,Java三种。
按照部署方式主要分为移动端和服务器端的。这里可以参见知乎回答中的阿昌的回答。
个人觉得还是tensorflow框架较为合适,理由只有一个,它来自于google。站在巨人的肩上看世界,眼界比较广。
如果将这个智能系统部署到服务器上的话,可以直接在服务器上跑,如果将这个应用写成APP的话,就会涉及到移动端的部署。无论如何,官网都是靠谱的~

  • 15
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值