windows10+vs2015下编译GPU版本将Tensorflow封装成SDK

本文主要介绍两部分内容,一是在Windows10下编译GPU版本的TensorFlow,包括准备环境、下载源码、配置CMakeLists.txt并编译得到lib和dll文件;二是利用编译好的文件,在VS2015中创建C++工程,编写基于TensorFlow C++ API的推理示例程序,实现图片输入和模型运算结果输出。
部署运行你感兴趣的模型镜像

文章目录

 

 

本篇博客的内容分为两部分:1、在windows10下编译GPU版本的tensorflow,并获得tensorflow.lib和tensorflow.dll文件;2、利用这两个文件,创建一个C++工程,编写inference示例程序

一、windows10下编译GPU版本的tensorflow

1、首先需要准备的环境

  • x墙工具
  • vs2015
  • swig,官网是这里,注意下载windows版本的(含有.exe文件),解压即可
  • python,建议3.6或以上版本
  • CMake,官网下载安装,安装完成后添加到环境变量里(../CMake/bin)
  • Git,官网下载安装,安装完成后添加到环境变量里(../Git/bin)
  • cuda及cudnn,本文编译的是最新的1.6版本tf,所以cuda要求9.0,cudnn要求7,限于篇幅,配置方法自行网上搜索
  • 矩阵运算库,例如BLAS、MKL、eigen这些,这里我选择的是eigen,下载最新的版本,解压后添加到环境变量里(../eigen3.3.4)

2、下载tensorflow源码,配置CMakeLists.txt

打开命令提示符,cd到专门的路径下,输入

git clone --recursive https://github.com/tensorflow/tensorflow.git
  • 1

下载好tensorflow源码后,找到tensorflow/contrib/cmake/CMakeLists.txt,搜索“tensorflow_OPTIMIZE_FOR_NATIVE_ARCH”,找到后做出如下修改

if (tensorflow_OPTIMIZE_FOR_NATIVE_ARCH)
  include(CheckCXXCompilerFlag)
  CHECK_CXX_COMPILER_FLAG("-march=native" COMPILER_OPT_ARCH_NATIVE_SUPPORTED)
  if (COMPILER_OPT_ARCH_NATIVE_SUPPORTED)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
  else()
    CHECK_CXX_COMPILER_FLAG("/arch:AVX" COMPILER_OPT_ARCH_AVX_SUPPORTED)
    if(COMPILER_OPT_ARCH_AVX_SUPPORTED)
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX")
    endif()
  endif()
endif()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3、开始编译lib和dll

首先进入tensorflow/contrib/cmake目录下,新建一个build文件夹。然后打开命令提示符,输入cmake-gui,配置相关的路径,再configure(选择vs2015的那个vc14 64位编译器),即可得到下图 
这里写图片描述 
原本根据需要自行勾选编译选项,再configure+generate,即可打开vs2015完成编译,我最开始就是这么做的,但始终没有成功,报的错误也看不懂(后来我在知乎上看到有一篇文章,作者说tensorflow各工程之间依赖十分复杂,直接用ALL_BUILD要改一些配置)。所以这里我采用的是vs2015自带的编译工具,如下图 
这里写图片描述 
右键-更多-以管理员身份运行,先cd到tensorflow/contrib/cmake/build目录下,再输入以下内容进行configure

cmake .. -A x64 -DCMAKE_BUILD_TYPE=Release -DSWIG_EXECUTABLE=D:/3rd_party/swigwin-3.0.12/swig.exe -DPYTHON_EXECUTABLE=D:/python.exe -DPYTHON_LIBRARIES=D:/libs/python36.lib -Dtensorflow_ENABLE_GPU=ON -Dtensorflow_ENABLE_GRPC_SUPPORT=OFF -Dtensorflow_BUILD_SHARED_LIB=ON
  • 1

说明一下需要自行修改的参数,SWIG_EXECUTABLE是swig.exe所在路径,PYTHON_EXECUTABLE和PYTHON_LIBRARIES分别是python的exe和lib所在路径,这些都需要自己配,而且路径不能含有空格或者中文字符。后面tensorflow_ENABLE_GRPC_SUPPORT涉及到tensorflow线上部署,默认是ON的状态,如果要编译,则需要下载一堆文件,这里我不需要,就关闭了。另外要想指定编译其它内容,可以参考前面cmake-gui的那张图,配置的格式就是”-D+xxx=ON/OFF”。修改完这段话后,即可执行,等待configure done。

configure完成之后,就要开始正式编译动态库了。此时需要打开你的x墙工具,因为编译的过程中,会从网上下载几个文件,虽然都不大,但它们是存储在含有google的网址内的。接下来,在刚才的命令行窗口继续输入

MSBuild /p:Configuration=Release ALL_BUILD.vcxproj
  • 1

注意如果想编debug版本的动态库,就把Release改成Debug。接下来就是漫长的等待了,在我的i7电脑下,编了大概三小时才完成,期间电脑会特别卡(vs默认是多线程编译的)。注意最后不能有错误,否则无法顺利生成tensorflow.lib和tensorflow.dll。最终得到的lib和dll是在tensorflow/contrib/cmake/build/Release目录下,这里提供我编译的一个release版本,还包含整理好的头文件。


二、基于tensorflow C++ api的inference示例程序

前面编译的方法其实很多网址都能搜到,这里写出来是为了做个记录。接下来写的才是本文的重点:如何利用已训练好的tensorflow模型,在windows下编写inference程序,打包生成exe文件,供线下部署。

1、在vs2015中新建项目,配置环境

头文件的配置如下 
这里写图片描述 
说明:第一个eigen是前面所说的矩阵运算库,因为c++没有类似numpy的东西(不过c++14有个xtensor),所以矩阵运算可以利用eigen的api。E:\Git\tensorflow是我的tensorflow源码根目录,此外还需要包含一些build里面的头文件,因为部分.h和.cc文件是编译的时候生成的。

lib文件配置即配置tensorflow.lib所在目录,此外注意配置tensorflow.dll

2、新建main.cpp。

本程序的主要功能是输入一张图片,读取tensorflow模型,给出运算结果。下面一步步介绍其实现

2.1 宏定义和头文件

先上代码

#define COMPILER_MSVC
#define NOMINMAX
#define PLATFORM_WINDOWS   // 指定使用tensorflow/core/platform/windows/cpu_info.h

#include<iostream>
#include<opencv2/opencv.hpp>
#include"tensorflow/core/public/session.h"
#include "tensorflow/core/platform/env.h"

using namespace tensorflow;
using std::cout;
using std::endl;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这里需要将宏定义放在最前面,因为涉及到平台、编译器这些,否则就会报一些未定义的错误。另外这里使用了tensorflow的命名空间,是为了使后面的代码更简洁一些。

2.2 读取图片

有两种方式:一是用tensorflow自带的api,参考这里的讨论;二是利用opencv读取,再转化为tensorflow可识别的数据格式,这种方法的好处就是,可以很方便地利用opencv库做一些预处理。下面介绍第二种方法

// 设置输入图像
cv::Mat img = cv::imread(image_path);
cv::cvtColor(img, img, cv::COLOR_BGR2GRAY);
int height = img.rows;
int width = img.cols;
int depth = img.channels();

// 图像预处理
img = (img - 128) / 128.0;
img.convertTo(img, CV_32F);

// 取图像数据,赋给tensorflow支持的Tensor变量中
const float* source_data = (float*)img.data;
tensorflow::Tensor input_tensor(DT_FLOAT, TensorShape({1, height, width, depth })); //这里只输入一张图片,参考tensorflow的数据格式NHWC
auto input_tensor_mapped = input_tensor.tensor<float, 4>(); // input_tensor_mapped相当于input_tensor的数据接口,“4”表示数据是4维的。后面取出最终结果时也能看到这种用法                                                                                                      

// 把数据复制到input_tensor_mapped中,实际上就是遍历opencv的Mat数据
for (int i = 0; i < height; i++) {
    const float* source_row = source_data + (i * width * depth);
    for (int j = 0; j < width; j++) {
        const float* source_pixel = source_row + (j * depth);
        for (int c = 0; c < depth; c++) {
            const float* source_value = source_pixel + c;
            input_tensor_mapped(0, i, j, c) = *source_value;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

需要说明的都放在了代码注释当中,这里用到了auto,包括后面的代码也用到了不少,主要原因是我也还未弄清部分数据的格式(后来看到了tf官方的示例,他们也建议用auto,这样代码更简洁一些)。

2.3 建立会话,加载模型文件

模型来自我前段时间训练的验证码识别,注意要保存为.pb格式,这样inference的时候就不用重新搭建网络结构了。

// 初始化tensorflow session
Session* session;
Status status = NewSession(SessionOptions(), &session);
if (!status.ok()){
    std::cerr << status.ToString() << endl;
    return -1;
}
else {
    cout << "Session created successfully" << endl;
}   


// 读取二进制的模型文件到graph中
GraphDef graph_def;
status = ReadBinaryProto(Env::Default(), model_path, &graph_def);
if (!status.ok()) {
    std::cerr << status.ToString() << endl;
    return -1;
}
else {
    cout << "Load graph protobuf successfully" << endl;
}


// 将graph加载到session
status = session->Create(graph_def);
if (!status.ok()) {
    std::cerr << status.ToString() << endl;
    return -1;
}
else {
    cout << "Add graph to session successfully" << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

2.4 定义输入输出数据格式,run inference,输出数据可视化

// 输入inputs,“ x_input”是我在模型中定义的输入数据名称,此外模型用到了dropout,所以这里有个“keep_prob”
tensorflow::Tensor keep_prob(DT_FLOAT, TensorShape());
keep_prob.scalar<float>()() = 1.0;
std::vector<std::pair<std::string, tensorflow::Tensor>> inputs = {      
    { "x_input", input_tensor },    
    { "keep_prob", keep_prob },
};

// 输出outputs
std::vector<tensorflow::Tensor> outputs;

// 运行会话,计算输出"x_predict",即我在模型中定义的输出数据名称,最终结果保存在outputs中
status = session->Run(inputs, { "x_predict" }, {}, &outputs);
if (!status.ok()) {
    std::cerr << status.ToString() << endl;
    return -1;
}
else {
    cout << "Run session successfully" << endl;
}

// 下面进行输出结果的可视化
tensorflow::Tensor output = std::move(outputs.at(0)); // 模型只输出一个结果,这里首先把结果移出来(也为了更好的展示)
auto out_shape = output.shape(); // 这里的输出结果为1x4x16
auto out_val = output.tensor<float, 3>(); // 与开头的用法对应,3代表结果的维度
// cout << out_val.argmax(2) << " "; // 预测结果,与python一致,但具体数值有差异,猜测是计算精度不同造成的

// 输出这个1x4x16的矩阵(实际就是4x16)
for (int i = 0; i < out_shape.dim_size(1); i++) {
    for (int j = 0; j < out_shape.dim_size(2); j++) {
        cout << out_val(0, i, j) << " ";
    }
    cout << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

输出结果如下图(懒得打码了,请无视我的显卡。。) 
这里写图片描述
至此,模型输出数据的格式也清楚了,完整流程就是这样,想进一步研究Tensor用法的话可以参考这里,完整的代码参考我的GitHub


PS

本次完成所有工程,在网上参考了大量资料,时间关系,这里不分先后,列举一下:tensorflow编译TensorFlow C++ API案例一TensorFlow C++ API案例二TensorFlow C++ API案例三(gist貌似也要x墙?)

距离上一次更新博客已经半年了,主要原因还是自己比较懒,这次总算是做了一点综合性的工作,希望以后能继续。另外吐槽一点,百度的搜索太差劲了,我先前的博客,即使输入原标题也在百度里面搜不到,但是在谷歌里面第一条就是。

PPS

更新一个tensorflow官方的c++ api inference例子

Windows10下源码编译基于GPU的tensorflow.dll

时间:2017-09-05 10:06  来源:清屏网   作者:那一抹忧伤   点击:1737次

笔者因为想尝试一些机器学习方面的idea,所以于TensorFlow产生了交集, 笔者搞计算机图形学,所以更多地与windows和visual studio打交道, 于是想在windows和visual studio环境下编译出tensorflow的gpu版本。

但是整个互联网对于在windows和vs2015下编译tensorflow的信息少的可怜, 甚至在tensorflow的官方git hub页面, 也宣称没有在windows+vs环境下成功build出tensorflow GPU。

所以, 似乎, 为了搞点儿机器学习的算法,我们就不得不放弃可爱的windows屈从于Linux了? -- 就因为那些不搞游戏编程的人喜欢用Linux?不可能的。

在开始这篇文章前, 我首先要讲一下为什么要编译tf源码。(从此以后tf就是tensorflow的简称)。

为此, 我首先要声明, 如果读者不想使用tensorflow的高级功能的话, 在windows上安装它的python接口就可以了, 很简单, 也很方便, 同样是带了gpu支持的,是tensorflow开发者对于windows python预编好的接口, 对此, 有以下几点需要注意:

  1. 必须先安装好cuda8.0, 其它版本不建议, cuda8.0可从nvidia的官网获得, 使用默认安装路径即可
  2. 之后安装cudnn5.0, 任何其它版本不适用。 因为nvidia提供的cudnn只是一个 .zip文件, 所以解压后把里头的 \bin; \include; \lib; 文件夹合并到cuda的安装目录下, 比如我的为: C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\
  3. 之后安装python, 必须是 python3.5 , 任何其它版本不可。安装python时,请勾选“添加系统Path”。
  4. 接下来, 打开你的cmd, 输入pip install update pip; 这个指令会自动更新python的pip功能,帮助它识别tensorflow软件包。
  5. 接下来, pip install update numpy。
  6. 最后,pip install update tensorflow-gpu。
  7. 此安装简单易懂, 很容易完成, 安装好的tensorflow绝对能跑(在python环境下), 比如此时, 你可以: 
    >python 
    >import tensorflow as tf 
    >tf.Session() 
    这时候tensorflow就会输出你的显卡型号等的信息.

那么, 为什么要编译tensorflow的源代码呢????

-- 因为我想使用tensorflow的高级功能, 其中包括编写tensorflow的插件, 这个工作流程最简化如下所示:

 

 

所以, 如果不能在自己的机器上编译出tensorflow的话, 那么tensorflow的真正功能, 相当于只用了很小的一部分, 因为它提供的interface就算再好再全也是有限的, 而扩展的那部分能做的是, 理论上是无限的. 比如完全有可能利用Unreal Engine的插件把UE的内部数据直接导入到tensorflow里去, 也有可能在unity里直接造出一个基于tensorflow的插件.

到此为止总结一下我们面临的问题: 为了使用tf的高级功能, 想在windows上编译tf-gpu, 但是网上存在的信息实在少之又少, 所以笔者开始了漫长的征程. 终于成功的build出了tensorflow.dll以及tensorflow.lib. 下面是我详细的编译过程:

 

  • 让我们开始一个相对来说还算干净的windows 10. 比如我正好因为一些原因重装过系统.
  • 安装visual studio 2015. 注意, 请安装这个版本.
  • 下载官方的cuda8.0, 默认安装路径.
  • 下载cudnn5.0 for CUDA8.0, exactly. 其它版本皆不可. 讲解压后的文件合并到cuda的安装目录下.
  • 下载并安装git bash.
  • 下载并安装cmake 3.9
  • 下载并安装swig, Windows users should download swigwin-3.0.12 which includes a prebuilt executable.
  • 进入你的git bash, 输入git clone https:// github.com/tensorflow/t ensorflow.git 

 

  • 回车, 此时git会自动下载tensorflow 源代码, 成功的话, 你的users\xxx\ 目录下会多出tensorflow的文件夹.
  • 打开你的cmake gui, 因为有很多选项, 所以我不建议在command line 里头调用. 
    在打开的界面, 给予tensorflow的cmakeList的位置和你想要把VC项目放到哪里去的文件夹, 典型的如下所示, 请勾选grouped 和advanced 两个tag
  • 接下来, 点击cmake的config,选择visual studio2015作为你的build 目标, 点击finish 。

 

  • 此时你一定会看见一些error, 不要慌张, 大多是因为swig没找到或者python没找到或者cuda没找到之类的信息, 因为我们用的是gui版Cmake, 这些问题很好解决!!!

    比如:

 

  • 此时只需要手动输入所相关的路径, 如图(我这种懒鬼会把.exe直接装到桌面上的.....)
  • 此时你再点config, 就不会出错了, 此时你的设置大概是这样:
  • 展开tensorflow 这个tag, 并做如下勾选
  • 此时如果再点config那个按钮, 你的界面应该变成这样, 多出了有关CUDA的tag!!
  • 对于CUDA, 我们并不需要什么特别的设置, 此时点Generate, Cmake就会自动生成相应的tensorflow.sln, 这个可爱的, 我们可以用visual studio 2015打开的好东西!!!!!!!
  • Generate完后点击Open Project.
  • 理论上在ALL BUILD 那里右键BUILD就可以, 但是....................如果这样就能够build成功了, 我还tmd需要写这篇文章吗?????? 因为tensorflow的作者根本就没考虑windows的缘故, 就这样build一般是build不成功的!!!!!

此处有几点必须注意的!!!visual studio一般是默认多线程编译的, 由于tensorflow这个项目文件的dependency过于复杂, 在编译到某些项目的时候会出现: Faltal Error "compiler is out of heap space", 看到这个问题, 不要慌张, 因为你的背后有这篇强大的攻略!!

去你的tools --> options:

 

在弹出的对话框里如下设置:

 

然后再去build, 此时, 笔者很不幸的告诉你, 尽管已经这样设置, 还是有可能build不成功的!!!!!有一个很特殊的项目, 由于dependency过于复杂, 仍然会 Faltal Error "compiler is out of heap space"

 

这个该死的项目就是 "tf_core_kernels" , 请对它特殊照顾!!! 在你的 ALL BUILD编译完不管报了什么错以后, 请独立对这个项目再编译一次, 如图所示:

 

这样做可以把之前很多没有编译出来的 *.obj 代码给编译出来. , 为了保险起见, 请去重新单独编译(project only build)tensorflow_static 这个项目, 这个项目会编译出tensorflow_static.lib. 在编译这个项目的过程中, 如果有遇到缺少任何xxx.obj, 请按照那个名字搜索 http:// xxx.cc 文件, locate到相应的项目, 并project only build 那个项目.

如果你完成了build tensorflow_static 这个项目, 在你的tensorflow 这个项目(目标是dll), 下会成功的多出tensorflow.def这个source, 这是你tensorflow_static的编译所得!!! 如图:

 

此时你可以右键, project only, build tensorflow, 然而, 愚昧的你真的以为这次就终于可以了么?????

错!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

由于写那个cmakeList的人没有考虑周全, 所以此处你会有一个unresolved symbol

 

 

照理说这个symbol应该是属于tf_c 或者tf_core_cpu那两个项目的, 而且我们已经针对项目only重新build过了, 应该有才对啊.........到底发生了什么????? 原来, tensorflow这个最终的boss项目, 在生成的时候, 忘记把编译好的 *.obj给包含进去了!!!!!!!

 

 

 

然后, 再 右键, project only, build tensorflow, 这次你终于可以成功的build了!!!!!!!!!!!!!!!!!!!!!!!!!!!

Creating library C:/Users/zxx_1/tensorflow/tensorflow/contrib/cmake/build/Release/tensorflow.lib and object C:/Users/zxx_1/tensorflow/tensorflow/contrib/cmake/build/Release/tensorflow.exp

tensorflow.vcxproj ->C:\Users\zxx_1\tensorflow\tensorflow\contrib\cmake\build\Release

\tensorflow.dll

看到这两行字, 心情还是无比激动的. 这意味着接下来你终于可以自由得飞翔了.

 

 

 

在笔者尝试编译的过程中, 这篇文章的帮助很大, 虽然他只build了无gpu的版本, 但还是起到了很多参考作用的, 虽然在看了我的build大法后完全不用去看原来那个文章, 但还是在此提及以作感谢. Building a static Tensorflow C++ library on Windows – Joe Antognini

您可能感兴趣的与本文相关的镜像

TensorFlow-v2.15

TensorFlow-v2.15

TensorFlow

TensorFlow 是由Google Brain 团队开发的开源机器学习框架,广泛应用于深度学习研究和生产环境。 它提供了一个灵活的平台,用于构建和训练各种机器学习模型

使用C++代码封装的win32操作类, 与MFC相似,对于学习SDK与C++是巨好的参考 Tutorials Menu of tutorials Tutorial 1: The Simplest Window Tutorial 2: Using Classes and Inheritance Tutorial 3: Using Messages to Create a Scribble Window Tutorial 4: Repainting the Window Tutorial 5: Wrapping a Frame around our Scribble Window Tutorial 6: Customising Window Creation Tutorial 7: Customising the Toolbar Tutorial 8: Loading and Saving Files Tutorial 9: Printing Tutorial 10: Finishing Touches Tutorial 1: The Simplest Window The following code uses Win32++ to create a window. This is all the code you need (in combination with Win32++) to create and display a simple window. Note that in order to add the Win32++ code to our program, we use an #include statement as shown below. #include "../Win32++/Wincore.h" INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPTSTR, int) { //Start Win32++ CWinApp MyApp; //Create a CWnd object CWnd MyWindow; //Create (and display) the window MyWindow.Create(); //Run the application return MyApp.Run(); } This program has four key steps: Start Win32++. We do this here by creating a CWinApp object called MyApp. Create a CWnd object called MyWindow. Create a default window by calling the Create function. Start the message loop, by calling the Run function. If you compile and run this program, you'll find that the application doesn't end when the window is closed. This is behaviour is normal. An illustration of how to use messages to control the windows behaviour (including closing the application) will be left until tutorial 3.
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值