手写选择题识别-封装tensorflow模型-移植到android程序

来源:互联网 发布:男士内裤淘宝店铺名 编辑:程序博客网 时间:2024/05/17 23:49

第一部分:

现在遇到的问题:还是因为线下python训练好的模型,不能够直接无缝连接使用。

这个看起来是android中直接调用tensorflow模型,那就不用和c++模块进行对接了 。

说起来惭愧,虽然接触了一段时间的tensorflow,但是好多东西没有需要所以也没有接触到,比如一般使用的多的是tensorflow的模型保存与重载,现在又接触到了对tf的封装(还看了部署到线上的情况)。
所以知道需要看这些的资料,就顺便记录下来,万一使用这个方法了呢?

参考的教程:http://blog.csdn.net/masa_fish/article/details/56049710

在以前的模型中,需要注意 的几点
1.需要对输入层x,和输出层y,通过形参name进行命名。

######################   1.对输入层x命名,注意x是以placehoder占位的,而输出层不是这个。x = tf.placeholder(tf.float32,shape=[None,n_input],name="x_input")y = tf.placeholder(tf.float32,shape=[None,n_output])###########################################   2.对输出层命名pred = tf.add(tf.matmul(dense, w2), b2, name="y_output")######################   2.对输出层命名

2.需要将模型保存为.pb格式的文件(就在模型训练完后的位置添加一行代码就可以保存,)
需要导入graph_util包

from tensorflow.python.framework import graph_util##################模型封装        output_graph_def = graph_util.convert_variables_to_constants(sess, sess.graph_def,                                                                         output_node_names=["output"])        # 形参output_node_names用于指定输出的节点名称(不是指的要输出的节点的名称)        pb_file_path = " my_net/save_net.pb"        with tf.gfile.FastGFile(pb_file_path, mode='wb') as f:            f.write(output_graph_def.SerializeToString())        ##################模型封装

第二部分:

#################################下面入了个坑,果然是要首先有好教程才行

应该是需要从源码安装tensorflow才能够移植到android应用,所以教程上 都是说下载源码后,然后再安装 Bazel 。(如果是已经通过pip 安装了tensorflow ,那么再来源码编译 会冲突吗?)

1.从github上下载源码(这个会下载最新的,应该不会因为代码是前面一点的版本,而这个是后面的版本就有问题吧)

sudo apt install gitgit clone https://github.com/tensorflow/tensorflow

这个会在当前文件夹下 克隆tensorflow源码到一个新的tensorflow文件夹中。
这里写图片描述

2.需要下载编译源码的工具

sudo apt-get install python-pip python-dev

这里写图片描述

先安装Bazel的依赖

sudo add-apt-repository ppa:webupd8team/javasudo apt-get install openjdk-8-jdk openjdk-8-sourcesudo apt-get install pkg-config zip g++ zlib1g-dev unzip

下载安装文件,然后再改变可执行权限

https://github.com/bazelbuild/bazel/releaseschmod +x bazel-0.5.3-installer-linux-x86_64.sh

https://github.com/bazelbuild/bazel/releases/download/0.5.3/bazel-0.5.3-installer-linux-x86_64.sh

这里写图片描述

下载速度好慢啊 ,是教育网的原因吗。(在这个地方等了会时间)

只能找网上其他的资源了
http://download.csdn.net/download/l1505624/9886656

从csdn下载的是:
bazel-0.5.2-installer-linux-x86_64.sh

开始:
1.复制到虚拟机中, 终端然后切换到.sh文件存放的路径,文件添加可执行权限:

$ chmod +x bazel-0.5.2-installer-linux-x86_64.sh

2.然后执行该文件:

$ ./bazel-0.5.2-installer-linux-x86_64.sh –user

3、 设置Bazel 环境变量

$ sudo gedit ~/.bashrc

在 ~/.bashrc文件的末尾添加

export PATH=’PATH:/home/fish/bin’

在这个地方需要注意 修改完成后利用source使改变生效。我一开始就是忘记这个步骤,然后导致后面的情况出现(命令路径问题:比如我在这块不知道怎么就覆盖了一些路径,导致各种/usr/bin.找不到,所以需要添加到bashrc文件中进去 /bin,/usr/bin.)

source  ~/.bashrc

4.在解压的tensorflow目录中运行,进行相关配置,尽量什么都不添加,因为还需要配置添加的路径,也就是需要安装那些。(在这个地方坑了很长时间,后面的安装会有报错信息)

./configure

前面准备的tensorflow地址中存在着中文,会报错,所以移动到其他 的目录下
另外看教程学习的时候最好是同时看两篇,这样可以综合起来(毕竟环境不同,个人选择不同)

# pip3 uninstall -y tensorflow #若之前安装过则需要强制卸载,注意pip版本

bazel build -c opt tensorflow/tools/pip_package:build_pip_package、

这个与前面的config配置需要相对应。
执行的很长恨慢,而且报各种错。这个还是显示的是info,估计有error就失败了。
这里写图片描述

最后的
bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg

然后进入/tmp/tensorflow_pkg 利用pip install 安装下载的版本。

但是,在import tensorflow时出错,明明还是挺简单的东西,却用了 这么久的时间,还出错。

第三部分:

。。。。感觉好失败。
本来还想放弃一个系统,来安装的
该放弃吧,得换个办法从tf模型移植到android,刚好又看到另外一个教程,最初开始的时候没有选择它,但是后面经过了上面的挫折后,才意识到他的好处,那就是如果你不想自己编译的话,提供了文件的地址下载。
虽然是要越错越勇,算了 主要的目的得先解决才好。嗯,是这样的越快决定了 ,饿死我了。

这里写图片描述

这里写图片描述

这里写图片描述

这样终于回归到了主线上来。该准备的准备的差不多了。

1.pb模型(没钱,只有一台破主机cpu的,跑了一段时间跑出来了):
这里写图片描述

可以先用一个简单点的模型来做测试:

# -*- coding:utf-8 -*-import tensorflow as tffrom tensorflow.python.framework import graph_utilsession = tf.Session()matrix1 = tf.constant([[3., 3.]], name='input')add2Mat = tf.add(matrix1, matrix1, name='output')session.run(add2Mat)output_graph_def = graph_util.convert_variables_to_constants(session, session.graph_def,output_node_names=['output'])with tf.gfile.FastGFile('model/cxq.pb', mode='wb') as f:    f.write(output_graph_def.SerializeToString())session.close()

这里写图片描述

2.so和jar包:
这里写图片描述

3.创建android 项目:(我这个项目最后需要使用到c++,所以看到c++的就勾选中了,不知道后面的配置会有问题吗,先记录一下,方便以后对照)
这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述
这里写图片描述

这个下面的两个选择项不知道要不要选?

遇到的几个小问题:

直接下载ndk来解决;
这里写图片描述

这里写图片描述

直接点击error下载cmakedeng来解决;
这里写图片描述

4.把刚才的pb文件存放到assets文件夹下(默认没有这个文件夹,需要手动创建并配置)
这里写图片描述

按下面操作可以知道在哪以及为什么在main下面创建这个文件夹:

()

这里写图片描述

可以如下创建这个folder。(如果直接创建的是一个目录需要配置什么呢?)

这里写图片描述

下面就可以直接复制pb文件到这个文件夹下了:

这里写图片描述

这里写图片描述

5.将libandroid_tensorflow_inference_java.jar存放到/app/libs目录下,并且右键“add as Libary”

这里写图片描述

这里写图片描述
这里写图片描述
这里写图片描述

6.在/app/libs下新建armeabi文件夹,并将libtensorflow_inference.so放进去(这个应该是添加so文件吧,应该是放在jinlibs下吧?)

这里写图片描述

其实在android jinlibs和 project下libs两个是一样的:
这里写图片描述

注意:还有个jni文件夹(是放没有编译好的so文件的),而这个jinlibs是放的是已经编译好的文件。

添加两个配置:
这里写图片描述

再添加一个配置:
这里写图片描述

这样tensorflow的环境就配置好了。

7.创建模型调用类。
这里写图片描述

然后就是需要编写代码了,注意抽取框架,才好改自己的代码。

import android.content.res.AssetManager;import android.os.Trace;import org.tensorflow.contrib.android.TensorFlowInferenceInterface;

会自动提示:
这里写图片描述

有个就是android资源的问题:

第一种是res目录下存放的可编译的资源文件:
这种资源文件系统会在R.Java里面自动生成该资源文件的ID,所以访问这种资源文件比较简单,通过R.XXX.ID即可;
第二种是assets目录下存放的原生资源文件:
因为系统在编译的时候不会编译assets下的资源文件,所以我们不能通过R.XXX.ID的方式访问它们。
所以我们无法直接获取到assets的绝对路径,因为它们根本就没有。

遇到的问题:trace: Trace.beginSection(“feed”);
这里写图片描述

这里写图片描述

直接更改 minsdkversion 为18
这里写图片描述

代码:

package com.example.yanguokai.handerwriterreg;/** * Created by YANGUOKAI on 2017/8/1. *////////importimport android.content.res.AssetManager;import android.os.Trace;import org.tensorflow.contrib.android.TensorFlowInferenceInterface;///////importpublic class TFtest {//1.定义模型存放路径,注意的是android相关资源的获取的表示形式。    private static final String MODEL_PATH = "file:///android_asset/cxq.pb";//2.定义数据的维度    private static final int HEIGHT = 1;    private static final int WIDTH = 2;//3.模型中变量的名称    private static final String INPUT_NAME = "input";    private static final String OUTPUT_NAME = "output";//4.用于存储模型的数据    private float[] inputs = new float[HEIGHT*WIDTH];    private float[] outputs = new float[HEIGHT*WIDTH];//5.tensorflow 接口对象    TensorFlowInferenceInterface inferenceInterface;    //6.静态加载库文件    static {        System.loadLibrary("tensorflow_inference");    }    //7.利用assertManager来处理assert中的资源    TFtest(AssetManager assetManager){        //接口定义        inferenceInterface = new TensorFlowInferenceInterface(assetManager,MODEL_PATH);    }    //8.定义获取结果的函数    public float[] getAddResults(int a,int b){        //获取输入数据        inputs[0] = a;        inputs[1] = b;        //将输入数据feed给tensorflow        Trace.beginSection("feed");        inferenceInterface.feed(INPUT_NAME,inputs,WIDTH,HEIGHT);        Trace.endSection();        //运行操作        Trace.beginSection("run");        String[] outputNames = new String[]{OUTPUT_NAME};        inferenceInterface.run(outputNames);        Trace.endSection();        //将输出保存        Trace.beginSection("fetch");        inferenceInterface.fetch(OUTPUT_NAME,outputs);        Trace.endSection();        //返回保存的输出        return outputs;    }}

最后就是在activity中使用 类:
定义一个点击

这里写图片描述

 // 声明button        Button button01 =  (Button) findViewById(R.id.button_01);        button01.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                TFtest tFtest = new TFtest(getAssets());                float[] outputs = tFtest.getAddResults(2,10);                for(int i=0;i<outputs.length;i++){                    Log.i("TAG","button01"+outputs[i]);                }            }        });

运行结果:

这里写图片描述

这里写图片描述

整体框架已经搭好,中间遇到好多别人没有遇到的问题,自己在解决这些问题的时候也是只想着找到现成的解决方法,然后直接套用就好,说时候,也算是没有真正的去解决里面的问题吧,现在将所有的问题都贴出来,一个是方便下次再来做的时候遇到同样问题的可以有个参考,二个事以后是时候再来回看。

后面是时候将自己的模型导入了。