一. 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()之后的结果入下:
上面有四个文件:
- .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下使用已经训练好的模型
准备好训练好的模型以及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下使用已经训练好的模型
2.4 安卓端使用caffe
三、总结
按照框架分主要有两种tensorflow、caffe等其它,但是这里主要使用这两种。
按照语言分主要是python,C++,Java三种。
按照部署方式主要分为移动端和服务器端的。这里可以参见知乎回答中的阿昌的回答。
个人觉得还是tensorflow框架较为合适,理由只有一个,它来自于google。站在巨人的肩上看世界,眼界比较广。
如果将这个智能系统部署到服务器上的话,可以直接在服务器上跑,如果将这个应用写成APP的话,就会涉及到移动端的部署。无论如何,官网都是靠谱的~