Tensorflow C++ 从训练到部署(3):使用 Keras 训练和部署 CNN

在上一篇文章中我们并没有去训练一个真正的网络和解决一个实际问题,我们所做的是构建了一个 c = a * b 的计算图,并用 python 进行了保存和 c++ 进行了读取,这一保存和读取中也仅包含图的结构并没有相关参数。本篇文章中我们进一步以 Tensorflow 官方的 Fashion MNIST 为例,完成一个简单的分类问题。本文前面 Keras 训练模型以及转化到 Tensorflow 格式部分与之前一篇博客(Keras 转换成 Tensorflow 模型格式并使用)基本一致。本文主要包含:
1)Python:Fashion MNIST 数据集
2)Python:使用 Keras 定义 CNN 模型、训练并保存
3)Python:转换 Keras 模型到 Tensorflow 格式并保存
4)Python:使用 Tensorflow Python API 加载模型并预测
5)C++:使用 Tensorflow C++ API 加载模型并预测

0、系统环境
Ubuntu 16.04
Tensorflow 1.12.0 (安装详见官网,本文环境使用 pip 方式安装)

1、Fashion MNIST 数据集
1)数据简介
Fashion-MNIST [1] 是一个替代MNIST手写数字集的图像数据集。 它是由Zalando(一家德国的时尚科技公司)旗下的研究部门提供。其涵盖了来自10种类别的共7万个不同商品的正面图片。Fashion-MNIST的大小、格式和训练集/测试集划分与原始的MNIST完全一致。60000/10000的训练测试数据划分,28x28的灰度图片。你可以直接用它来测试你的机器学习和深度学习算法性能,且不需要改动任何的代码 [11]。

典型的 Fashion-MNITST 数据是这样的,其中每三行表示一个类别:
fashion-mnist-sprite

Fashion-MNIST 与 MNIST 同样有 10 个类别,不过并不是 0-9 的 10 个数字,它的类别如下:

screenshot-from-2018-09-25-11-50-54

我们使用过以前的 MNIST 数据集都知道,随便弄个很简单的网络,就可以轻轻松松刷出 99% 以上的分数了,即使传统方法也很容易达到高分。所以 MNIST 手写数字识别由于过于简单,作为一个基本的实验数据已经没有什么意义了。Tensorflow 和很多深度学习框架现在的入门数据也都推荐 Fashion MNIST。

2)数据读取
其实 Keras 为我们提供了简单的接口可以一键下载 Fashion-MNIST 数据并且读取:

不过由于天朝网络的原因,我并不推荐这种方式,建议直接下载到本地读取。我这里将数据直接存到 data/fashion 目录下。
百度网盘下载:
https://pan.baidu.com/s/19zZqU5tSwZyY780z8Y8_VA

读取数据代码如下(保存为:utils/mnist_reader.py):

2、使用 Keras 定义 CNN 模型、训练并保存
下面的代码中我们定义了一个非常简单的 CNN 网络,结构图如下:
model

我们使用这一网络进行训练并且保存为 Keras 标准的 h5 格式。这一部分代码比较基础,就不做过多解释了。

Keras 模型定义和训练代码如下(保存为:train.py):

如果你的运行没有问题则会看到类似如下输出:

同时在 models/ 文件夹下保存了 fashion_mnist.h5 文件,这一文件包含了模型的结构和参数。

3、转换 Keras 模型到 Tensorflow 格式并保存
这一环节我们使用 keras_to_tensorflow [2] 转换工具进行模型转换,其实这个工具原理很简单,首先用 Keras 读取 .h5 模型文件,然后用 tensorflow 的 convert_variables_to_constants 函数将所有变量转换成常量,最后再 write_graph 就是一个包含了网络以及参数值的 .pb 文件了。

具体代码参见(原始代码中可以传入输出 node 数量和名字并使用 identity 生成新的 tensor,我这里稍作修改,直接读取 Keras 的 outputs 的操作名,最后会输出原始 inputs 和 outputs 的名字供后面使用):

Tensorflow 模型转换代码(保存为:utils/keras_to_tensorflow.py)

我们执行如下命令转换 Keras 模型到 Tensorflow 的 pb 格式:

如果你的运行无误的话则会显示如下信息并生成 models/fashion_mnist.h5.pb 这个就是转换过来的 Tensorflow 格式:

这里面也告知了你模型输入和输出的 Tensor 名字,这两个信息很重要我们后面会用到。

4、使用 Tensorflow Python API 加载模型并预测
我们使用标准的 Tensorflow Low-Level API 加载和预测代码如下(保存为:load_predict.py)

这段代码中前面同样是读取 Fashion MNIST 数据集,与训练代码一样。部分代码说明如下:

读取 pb 模型文件:

获取当前的计算图:

获取输出的 Tensor:

可以看到除了之前我们给出的输出 Tensor 名称 output_class/Softmax 外,我们还需要加上一个索引 :0。关于这一问题的解释可以参见 [12]。我们这里简单来理解,"output_class/Softmax" 是指定了一个 Operation 的名字,对应最后 Softmax 层,大部分层的输出都是一个 Tensor,不过也有可能一个层产生多个输出 Tensor,因此我们这里需要指定是哪个输出。通常对于一个输出的时候就是用 :0 指定,对于 Input 也是同理。

执行计算图并打印输出结果,其中 feed_dict={'input_image_input:0': test_images} 将 test_images 作为输入传入网络:

执行整个代码:

如果运行没有问题则可以看到如下结果:

与之前我们使用 Keras 的 predict 接口结果对比,是一样的,说明我们转换后的模型无误。

关于 Keras 转换成 Tensorflow 模型和预测的步骤就到这里。完整示例可以参见:
https://github.com/skylook/tensorflow_cpp

5、使用 Tensorflow C++ API 加载模型并预测
1)使用 C++ 转换 OpenCV 的 Mat 到 Tensor
Tensor 要求输入的是归一化的 float32 格式图片,实际我们使用如下代码来完成 OpenCV Mat 到 Tensor 的转换(保存为:utils/mat2tensor.h)

这段代码比较简单,就是声明一个 {1, img.size().height, img.size().width, img.channels()} 的单个 Tensor,将地址直接赋给 fake_mat,然后使用 OpenCV 把图片转成 float32 格式,声明的 Tensor 自然也就转成了 float32 格式。最后是根据输入 normal 因子进行归一化。这一归一化方法和之前训练时一致。

2)使用 C++ 调用 pb 模型并预测
与前面的文章类似,我们参考 Python 调用 pb 模型及预测接口使用 C++ API 调用之前转换的模型并预测代码如下(保存为:load_predict.cpp):

如果编译运行没有问题的话,会输出如下结果:

输入和调用模型与之前的博客基本一致,这里解释下输出部分:

这里 flat_inner_dims 的 API 官方说明如下:
screenshot-from-2018-10-15-18-57-02

表示将按照指定类型 T 和指定维度 NDIMS 的 Eigen::TensorMap 输出,需要说明的是这一 T 和 NDMS 维度必须与模型中实际输出类型一致,否则运行时会报错。TensorMap 相当于 Tensor 的一个引用,其内存地址并不像 Tensor 一样是自己创建与释放的,因此没有 resize 等操作,其他使用方法与 Tensor 类似。关于 Eigen::Tensor 使用说明参见 [6] 和 [7]。

其中 tensorflow::TTypes::Tensor 与 Eigen::TensorMap, Eigen::Aligned> 其实是等价的,因此上面那一行也可以替换成:

到这里我们就完整介绍了一个 CNN 网络从 Keras 训练、转成 Tensorflow 格式到 C++ 部署的基本流程。

本文完整代码参见 Github:
https://github.com/skylook/tensorflow_cpp

参考文献
[1] https://www.tensorflow.org/tutorials/keras/basic_classification
[2] https://medium.com/tensorflow/hello-deep-learning-fashion-mnist-with-keras-50fcff8cd74a
[3] https://github.com/zalandoresearch/fashion-mnist
[4] https://zhuanlan.zhihu.com/p/30985013
[5] https://github.com/ADozois/proc_deep_detector
[6] http://eigen.tuxfamily.org/index.php?title=Tensor_support
[7] https://github.com/PX4/eigen/blob/master/unsupported/Eigen/CXX11/src/Tensor/README.md
[8] https://www.tensorflow.org/api_docs/cc/class/tensorflow/tensor

Tensorflow C++ 从训练到部署系列文章目录

Tensorflow C++ 从训练到部署(3):使用 Keras 训练和部署 CNN
Tensorflow C++ 从训练到部署(2):简单图的保存、读取与 CMake 编译
Tensorflow C++ 从训练到部署(1):环境搭建

Add a Comment

您的电子邮箱地址不会被公开。 必填项已用 * 标注