win10 + bazel-0.20.0 + tensorflow-1.13.1 编译tensorflow GPU版本的C++库

参考资料:

---------------------- 超长文多图预警!!! ----------------------

安装所需软件/库

Step1. 安装vs2015,CUDA 10.0和cuDNN 7.6

vs2015的安装这里就不多说了,Anaconda3、CUDA和cuDNN的安装可参考cmake编译opencv: Win10+cmake3.14.4+cuda10.0+cudnn7.6+opencv-4.0.0+contrib+vs2015

Step2. 安装msys2

进入msys2官网,选择msys2-x86_64-20190524.exe,或直接点击此链接: msys2-x86_64-20190524,进行下载。

在这里插入图片描述
下载好后开始安装,点击Next

在这里插入图片描述
接下来选择安装路径,我个人为了避免安装在系统盘,将盘符改成了D盘,如下图。然而在之后的编译过程中发现,msys64会在C盘再安装一遍,所以这里推荐使用默认,即安装在 C:\msys64 下。接着点击Next,进入下一步。

在这里插入图片描述
这里直接Next

在这里插入图片描述
软件开始安装,稍微等一会儿就好了。

在这里插入图片描述
安装完成,点击Finish

在这里插入图片描述
弹出一个类似cmd的命令窗口,输入一下命令:

pacman -Syu

  
  
  • 1

询问是否进行安装,输入y,回车:

在这里插入图片描述
第一个命令执行完毕:

在这里插入图片描述
接着输入以下命令:

pacman -S git

  
  
  • 1

同样地,询问是否安装输入 y,然后回车:

在这里插入图片描述
第二个命令执行完毕:

在这里插入图片描述
接着输入:

pacman -S patch unzip grep

  
  
  • 1

是否安装输入 y,回车:

在这里插入图片描述
第三个命令执行完毕:

在这里插入图片描述

软件装好以后,需要配置环境变量。
将以下路径添加到系统变量 Path 中:

D:\msys64
D:\msys64\usr\bin

注意,如果之前装在C盘,则需要进行相应修改。
如下图所示:

在这里插入图片描述
msys2安装配置完毕!

Step3. 安装bazel

进入github,选择合适的bazel版本进行下载。
如果CUDA和Tensorflow的版本与本文一致,也可点此链接下载:bazel-0.20.0-windows-x86_64.exe
github上选择下载文件如下图所示:

在这里插入图片描述
将下载好的 .exe 文件复制到 D:\msys64 下(路径根据个人安装情况而定),更名为 bazel.exe

在这里插入图片描述
复制完成后,配置bazel环境变量。
新建三个系统变量:BAZEL_SHBAZEL_VCBAZEL_VS。相应的路径如以下表格所示:

变量
BAZEL_SHD:\msys64\usr\bin\bash.exe
BAZEL_VCD:\Program Files (x86)\Microsoft Visual Studio 14.0\VC
BAZEL_VSD:\Program Files (x86)\Microsoft Visual Studio 14.0

环境变量配置完成:

在这里插入图片描述

bazel安装配置完毕!

Step4. 下载编译所需文件(tensorflow源码及其他文件)

进入tensorflow-v1.13.1源码的github的页面 ,点击页面右侧绿色按钮Clone or download,然后点击Download ZIP进行下载,如下图所示。
或者也可直接点此链接:tensorflow-1.13.1 进行下载。
下载时间也许会很长,请耐心等待。
在这里插入图片描述
进入tensorflow-windows-build-script-master的github页面,下载tensorflow-windows-build-script-master.zip(此链接可直接下载)。

在这里插入图片描述

以上两个 .zip 文件下载完成后,在D盘新建一个文件夹,命名为 tensorflow-1.13.1 (也可根据个人喜好决定)。

  • 将下载好的 tensorflow-1.13.1.zip 解压到刚刚新建的文件夹下,重新命名为 source
  • tensorflow-windows-build-script-master.zip 解压到任意位置,然后把其中的 patchesbuild.ps1 文件,复制到新建的 D:\tensorflow-1.13.1目录下,如下图所示:

在这里插入图片描述

  • patches 下的 eigen_half.patch 复制到 tensorflow-1.13.1\source\third_party 下:

在这里插入图片描述

  • patches 下的 tf_exported_symbols_msvc.lds 复制到 tensorflow-1.13.1\source\tensorflow 下:

在这里插入图片描述

  • 用文本编辑器打开 build.ps1 文件,将以下语句注释掉:

Copy-Item …\patches\tf_exported_symbols_msvc.lds tensorflow\

防止编译时出现Copy-Item命令的问题。
位置在 build.ps1 的180行,如下图所示:

在这里插入图片描述
编译所需文件准备完毕!

Step5. 使用powershell进行配置与编译

C:\Windows\SysWOW64\WindowsPowerShell\v1.0 目录下,右键以管理员身份运行 powershell.exe

在这里插入图片描述
powershell 窗口中输入以下命令,转到 tensorflow-1.13.1 目录下:

cd D:\tensorflow-1.13.1

 
 
  • 1

输入bazel编译的选项:

$parameterString = "--config=opt --config=cuda --define=no_tensorflow_py_deps=true --copt=-nvcc_options=disable-warnings //tensorflow:libtensorflow_cc.so --verbose_failures"

 
 
  • 1

然后输入以下命令,执行 build.ps1 脚本文件:

.\build.ps1 -BazelBuildParameters $parameterString -BuildCppAPI -ReserveSource

 
 
  • 1

执行命令时出现 UnauthorizedAccess 错误,说明可能是 powershell 的执行策略受限,输入以下命令查看当前执行策略:

Get-ExecutionPolicy

 
 
  • 1

我这里显示的是Restricted(受限的),所以需要输入以下语句来取消限制:

Set-ExecutionPolicy Unrestricted

 
 
  • 1

询问是否改变执行策略,输入 y,回车。
修改好后,可以再次输入:

Get-ExecutionPolicy

 
 
  • 1

查看当前执行策略是否已经取消限制。

在这里插入图片描述
执行策略的问题解决以后,重新执行 build.ps1 脚本文件:

.\build.ps1 -BazelBuildParameters $parameterString -BuildCppAPI -ReserveSource

 
 
  • 1

如果一切正常,我们将开始编译前的配置。可参照以下两图进行配置。有些提问可以直接按回车来选择默认配置,括号中出现 default 字眼的,都可以这么做。

在这里插入图片描述
注意 :当问到GPU的计算能力(compute capability),即出现 [Default is: 3.5, 7.0]: 时,先不要急着按回车。我们先找到跟自己显卡对应的计算能力,再进行填写,可参考后面的方法来查看显卡的计算能力。

在这里插入图片描述
打开设备管理器,找到自己的显卡型号:

在这里插入图片描述
然后进入NVDIA官网的CUDA GPUs页面,点击 CUDA-Enabled GeForce and TITAN Products,找到自己的显卡型号对应的数值,将其填入 [Default is: 3.5, 7.0]: 后面,回车继续配置。

在这里插入图片描述
都配置完成后,编译正式开始。注意保持网络通畅,因为编译之前需要下载各种依赖库,网络异常会导致下载失败停止编译。编译时间很长,不要光盯着屏幕看,该吃吃该喝喝该玩玩该睡睡。

编译可能出现无法解析的外部符号的问题,以下面的错误为例:

无法解析的外部符号 “public: virtual __cdecl tensorflow::internal::LogMessage::~LogMessage(void)” (??1LogMessage@internal@tensorflow@@UEAA@XZ),该符号在函数 “public: void __cdecl tensorflow::internal::LogMessage::`vbase destructor’(void)” (??_DLogMessage@internal@tensorflow@@QEAAXXZ) 中被引用

解决方法:
用文本编辑器打开 D:\tensorflow-1.13.1\source\tensorflow\tf_exported_symbols_msvc.lds ,找到前面一个括号中带问号的内容:

??1LogMessage@internal@tensorflow@@UEAA@XZ

将其删除。如果有多个无法解析外部符号的问题,用同样的方法逐一删除即可。
确认全部清除后,重新执行以下命令配置和编译:

.\build.ps1 -BazelBuildParameters $parameterString -BuildCppAPI -ReserveSource

 
 
  • 1

如果一切正常,在漫长的等待之后,将会出现以下结果:
注意:第一个红框部分为编译生成的动态库 libtensorflow_cc.so 所在位置,请记录下来,后面需要用到。

在这里插入图片描述
编译任务圆满完成!!!
可以稍作休息小小庆祝一下!

Step6. 整理生成的文件——dll,lib,include

新建一个文件夹,在文件夹中创建以下三个目录:dlllibinclude

在这里插入图片描述
根据编译结束时显示的动态库所在位置,找到生成的库 libtensorflow_cc.soliblibtensorflow_cc.so.ifso

  • libtensorflow_cc.so 更名为 tensorflow_cc.dll,放到刚才创建的 dll 目录下;
  • liblibtensorflow_cc.so.ifso 更名为 tensorflow_cc.lib,放到刚才创建的 lib 目录下。

在这里插入图片描述
接下来要做的是填满 include 目录,该步相对繁琐,最终将包含以下文件夹:

在这里插入图片描述

1)在include目录中新建名为 _bin 的文件夹。参考以下路径,打开 _embedded_binaries 目录,将下图红框中的文件复制到 _bin 文件夹中:

C:\Users\xxx_bazel_xxx\install\d5b1be53d8db6a1e2d160364df2e7ef6_embedded_binaries

在这里插入图片描述
2-1) 参考以下路径,将下图红框中的 bazel-out 文件夹复制到 include 目录下:

C:\Users\xxx_bazel_xxx\y46qiod6\execroot\org_tensorflow

在这里插入图片描述
2-2) 参考以下路径,将 protobuf_archive 文件夹复制到 include 下的 bazel-out\x64_windows-opt\genfiles\external 目录中:

C:\Users\xxx_bazel_xxx\y46qiod6\external

在这里插入图片描述

3) 将 D:\tensorflow-1.13.1\source 下的 tensorflowthird_party 复制到 include 目录下:

在这里插入图片描述
4-1) 参照以下路径,将下图红框中的 external 文件夹复制到 include 目录下:

C:\Users\xxx_bazel_xxx\y46qiod6

在这里插入图片描述
4-2) 参照以下路径,将 embedded_tools 文件夹下的所有文件复制到 include 下的 external\bazel_tools 目录中(如果 external 里面没有该文件夹,需新建并命名为 bazel_tools):

C:\Users\xxx_bazel_xxx\install\d5b1be53d8db6a1e2d160364df2e7ef6_embedded_binaries\embedded_tools

在这里插入图片描述

4-3) 将 D:\tensorflow-1.13.1\source 下的 tensorflow 复制到 include 下的 external\org_tensorflow 目录中(如果 external 里面没有该文件夹,需新建并命名为 rg_tensorflow):

在这里插入图片描述
至此,调用tensorflow所需的库及包含的目录准备完毕!

Step7. 调用模型测试程序

新建测试工程,在 .cpp 文件中写入以下代码:

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

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

using namespace tensorflow;
using namespace cv;
using std::cout;
using std::endl;

int main() {
const std::string model_path = “frozen_inference_graph.pb”;// tensorflow模型文件,注意不能含有中文
const std::string image_path = “image1.jpg”; // 待inference的图片grace_hopper.jpg

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

// 取图像数据,赋给tensorflow支持的Tensor变量中
tensorflow::Tensor input_tensor(DT_UINT8, TensorShape({ 1, height, width, depth }));
const uint8* source_data = img.data;
auto input_tensor_mapped = input_tensor.tensor&lt;uint8, 4&gt;();

for (int i = 0; i &lt; height; i++) {
	const uint8* source_row = source_data + (i * width * depth);
	for (int j = 0; j &lt; width; j++) {
		const uint8* source_pixel = source_row + (j * depth);
		for (int c = 0; c &lt; depth; c++) {
			const uint8* source_value = source_pixel + c;
			input_tensor_mapped(0, i, j, c) = *source_value;
		}
	}
}

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

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

// 将graph加载到session
status = session-&gt;Create(graph_def);
if (!status.ok()) {
	std::cerr &lt;&lt; status.ToString() &lt;&lt; endl;
	return -1;
}
else {
	cout &lt;&lt; "Add graph to session successfully" &lt;&lt; endl;
}
// 输入inputs,“ x_input”是我在模型中定义的输入数据名称
std::vector&lt;std::pair&lt;std::string, tensorflow::Tensor&gt;&gt; inputs = {
	{ "image_tensor:0", input_tensor },
};

// 输出outputs
std::vector&lt;tensorflow::Tensor&gt; outputs;

//批处理识别
double start = clock();
std::vector&lt;std::string&gt; output_nodes;
output_nodes.push_back("num_detections");
output_nodes.push_back("detection_boxes");
output_nodes.push_back("detection_scores");
output_nodes.push_back("detection_classes");
// 运行会话,最终结果保存在outputs中
status = session-&gt;Run(inputs, { output_nodes }, {}, &amp;outputs);
if (!status.ok()) {
	std::cerr &lt;&lt; status.ToString() &lt;&lt; endl;
	return -1;
}
else {
	cout &lt;&lt; "Run session successfully" &lt;&lt; endl;
}

double	finish = clock();
double duration = (double)(finish - start) / CLOCKS_PER_SEC;
cout &lt;&lt; "spend time:" &lt;&lt; duration &lt;&lt; endl;
cv::imshow("image", img);
cv::waitKey();
return 0;

}

  • 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
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107

先别急着编译工程,首先我们需要把环境配置好:

  • 配置opencv (Release版本);
  • 配置tensorflow C++库。

opencv库配置就不多赘述,这里主要说明tensorflow库的配置。
1)包含目录:

D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76;D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\include;D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\include\external\org_tensorflow;D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\include\external\protobuf_archive\src;D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\include\external\com_google_absl;D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\include\external\eigen_archive;D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\include\bazel-out\x64_windows-opt\genfiles;

 
 
  • 1

在这里插入图片描述

2)库目录:

D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\lib;

 
 
  • 1

3)链接器输入附加依赖项:

tensorflow_cc.lib;

 
 
  • 1

环境配置好后,将之前生成的动态库放入应用程序目录,即复制 tensorflow_cc.dll 到与工程文件 .sln 同级的 x64\Release 文件夹下,如下图所示:

在这里插入图片描述
接下来,从网盘下载模型文件 frozen_inference_graph.pb 和测试图片 image1.jpg(点此链接)

  • 模型文件在 tensorflow\ssd_mobilenet_v1_coco_2017_11_17 目录下;
  • 测试图片在 tensorflow\test_images 目录下。

把它们放到与 .cpp 文件同级的目录下,如下图所示:

在这里插入图片描述
编译前的准备工作完成,现在我们可以编译生成项目。生成之前,注意选择 Releasex64,如下图红框所示:

在这里插入图片描述
编译可能出现无法解析的外部符号的问题,以下面的错误为例:

无法解析的外部符号 “public: virtual __cdecl tensorflow::internal::LogMessage::~LogMessage(void)” (??1LogMessage@internal@tensorflow@@UEAA@XZ),该符号在函数 “public: void __cdecl tensorflow::internal::LogMessage::`vbase destructor’(void)” (??_DLogMessage@internal@tensorflow@@QEAAXXZ) 中被引用

解决方法:
用文本编辑器打开 D:\tensorflow-1.13.1\source\tensorflow\tf_exported_symbols_msvc.lds ,将前面一个括号中带问号的内容:

??1LogMessage@internal@tensorflow@@UEAA@XZ

复制到该文件的末尾,如下图所示。
如果有多个无法解析外部符号的问题,用同样的方法逐一添加。全部添加完成后保存关闭文件,然后重新编译tensorflow,也就是说从运行脚本文件开始,后面的流程要重新走一遍,想想有点心累,不过都已经走到这里了,半途而废有点说不过去呀,咬咬牙重来一遍吧。

在这里插入图片描述

上面的问题已经成功解决,没有意外,还会出现max问题:

在这里插入图片描述
解决办法如下:
双击错误提示,跳转到有max问题的文件: logging.htensor_shape.h,分别进行以下修改:

1)logging.h:

将第250行的

if (TF_PREDICT_FALSE(v2 >= std::numeric_limits<int>::max())) {      \

 
 
  • 1

改为

if (TF_PREDICT_FALSE(v2 >= (std::numeric_limits<int>::max)())) {      \

 
 
  • 1

修改后如下图所示:

在这里插入图片描述

2)tensor_shape.h:
将108-111行的

static const int64 kMaxRep16 = std::numeric_limits<uint16>::max() - 1;
static const int64 kMaxRep32 = std::numeric_limits<uint32>::max() - 1;
static const uint16 kUnknownRep16 = std::numeric_limits<uint16>::max();
static const uint32 kUnknownRep32 = std::numeric_limits<uint32>::max();

 
 
  • 1
  • 2
  • 3
  • 4

改为:

static const int64 kMaxRep16 = (std::numeric_limits<uint16>::max)() - 1;
static const int64 kMaxRep32 = (std::numeric_limits<uint32>::max)() - 1;
static const uint16 kUnknownRep16 = (std::numeric_limits<uint16>::max)();
static const uint32 kUnknownRep32 = (std::numeric_limits<uint32>::max)();

 
 
  • 1
  • 2
  • 3
  • 4

修改后如下图所示:

在这里插入图片描述
修改好后我们重新生成项目,如果上面的步骤都没有问题,将会出现下面的结果:

在这里插入图片描述
没错!编译成功!小腿抖起来小曲儿唱起来!

到这里还没有结束,将程序运行起来,你将看到以下结果:

在这里插入图片描述
红框部分说明GPU和模型调用没毛病,妥妥地成功!小腿再次抖起来小曲儿再次唱起来!

tensorflow C++库的生成和调用全部结束!

支线任务

注意,这是作业,不是彩蛋。
测试程序结尾出现了以下神秘图片:

在这里插入图片描述
蓝狗?!
还别说,狗狗蓝色的美瞳还真是洋气,蓝色的皮鞋更显骚气。
灵魂三问:小狗为什么是蓝色?定位框在哪?识别的结果在哪?
这就需要骚年们好好的研究和琢磨了。

Happy Ending:

在这里插入图片描述

                                </div>
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值