WIN10+CUDA10.0+CUDNN7.6.5+Tensorflow1.14 编译及C++调用

WIN10+CUDA10.0+CUDNN7.6.5+Tensorflow1.14 编译及C++调用

    • 参考
    • 预备环境
    • Bazel编译
    • VS2019调用
    • 调用代码
    • 测试结果

最近需要在C++中调用Tensorflow训练好的Pb模型进行推理,查了一下现有的编译攻略,windos下写的比较详细的最高也只到了Tensorflow1.13.1(后文会说到具体原因),但由于Pb模型是在1.14版本训练的,因此1.12(我按照攻略编译了这个)版本无法进行正确调用,会显示不支持某些参数,因此只能尝试对1.14进行编译,折腾了好几天终于成功了,遇到了各种问题,这里给大家梳理了一下整体流程及问题的相应解决方式,希望对同样受到编译折磨的人有些帮助。(第一次写CSDN,格式什么的不太会弄,各位凑合看)

参考

1.Ubuntu下 Tensorflow 1.14源码编译:
2.windows+bazel+tensorflow-v1.12.0(GPU)编译生成dll与lib
3.使用bazel在windows 10下构建tensorflow 2.0的动态链接库
4.其他的还有很多,但是我记不住了= =……

预备环境

1.MSYS2安装
按照参考2中MSYS2按照步骤即可,尽量不要修改安装路径,注意使用管理员模式打开安装文件,不然安装过程可能会卡在Updating database不动。

pacman -Syuu patch

如果运行时由于网络问题无法正确安装,则可以使用

pacman -S patch

只对patch包进行安装

2.Bazel安装
Tensorflow1.14对应Bazel版本为0.24.1,直接去

https://github.com/bazelbuild/bazel/releases?after=0.26.0

下载就行,如果下载过慢,可以尝试右键下载文件,复制链接地址,到

https://d.serctl.com/

进行下载
3.Protoc安装
Tensorflow1.14对应Protoc版本为3.7.1,直接去

https://github.com/protocolbuffers/protobuf/releases?after=v3.9.2

下载,下载完成后,解压并将bin目录加入环境变量,在cmd中输入protoc来判断是否正确安装
4.Python安装
如果电脑中有Anaconda创建的虚拟环境,则可跳过此步。否则下载、安装Python即可
5.Tensorflow源码下载
安装git后(方法很简单,略过)(直接下载的zip版本是tensorflow2.0,本人不太懂怎么切换到之前版本,如果可以的话下载1.14的zip也可以),使用

git clone -b v1.14.0 https://github.com/tensorflow/tensorflow.git

进行tensorflow1.14.0的下载。如果下载速度过慢可以尝试在host文件夹中加入相应内容的方法(自行百度 git clone速度慢解决方式),如果有梯子可输入

git config --global http.proxy http://127.0.0.1:1080
git config --global https.proxy https://127.0.0.1:1080

(亲测可用)
速度提升非常明显,从几十K增加到几M

Bazel编译

在我看到的编译tensorflow的教程中,如Tensorlfow1.12 1.13.1,均需要提前下好对应版本的修改文件,
https://github.com/guikarist/tensorflow-windows-build-script
但由于其没发布1.13.1以上的版本文件,因此缺少1.13.1版本以上编译教程。经过本人研究,发现并不需要该文件即可编译。
在下载好的tensorflow源码文件夹中打开cmd,输入

configure

(此处可能需要python,如果打不开可以activate 虚拟环境进行尝试)稍微等待,按照提示输入相应编译参数即可
WIN10+CUDA10.0+CUDNN7.6.5+Tensorflow1.14 编译及C++调用_第1张图片
Python目录输入anaconda的虚拟环境,若没有则输入已经安装的Python路径
library会自动选择python对应的安装包路径(直接回车即可,如果没有对应安装包可能会自动下载)
如果有GPU的话CUDA选择y,会自动查询cuda及cudnn版本及路径
compute capabilities选择和你显卡对应的版本,在提示的那个网站可以查询,我的是GTX 2060super 因此选择7.5。
’–config=opt‘那行回车即可
override eigen那行输入y然后回车
出现一大堆config应该就可以了
完成configure后,输入

bazel build --config=opt --config=cuda  //tensorflow:tensorflow_cc.dll

(没有gpu不输入cuda)
等待编译,过程中需要对依赖包进行下载,如果中断了重新输入上述命令,会接着之前的继续,网不好可能需要很多次,放平心态
下载后会进行编译,一共6000多个编译内容,等待编译完成,出现类似下图的内容即可。

在这里插入图片描述
如果编译过程中出现找不到某些cuda相关的dll错误,按照网上所说,安装一个cuda10.1,把相应dll复制到10.0对应位置,再删除10.1就可以了(记得删除环境变量中与10.1相关的,不然会默认使用10.1)。

VS2019调用

编译完成后,在编译路径会创建tensorflow_cc.dlllibtensorflow_cc.dll.ifso(具体地址看编译后的提示)。
随便在一个地方如D盘下创建一个空文件夹libtensorflow,里面再创建三个文件夹分别为
dll、lib、include
将tensorflow_cc.dll复制到dll文件中
将libtensorflow_cc.dll.ifso改名为tensorflow_cc.lib复制到lib文件夹中
将编译路径中的bazel-out、external、tensorflow、third_party四个文件夹复制到include中(可能会有没用的东西,但是我懒得挑了,全都复制进去,如果找不到在编译路径搜索即可)。去参考2中网盘下载,将其中include/absl include/Eigen include/unsupported 文件夹也复制到include中。

https://pan.baidu.com/s/1OGxmDrKnufeww2Y1gn7rQQ%C2%A0
提取码:rgzf

VS2019创建一个空项目,将tensorflow_cc.dll、tensorflow_cc.lib复制到项目文件夹下。设置项目的相关目录
项目-属性-VC++目录-包含目录将include文件夹添加进去(我的是D:\libtensorflow1.14-1\include)
上面两个是opcv的
第三个路径在git clone的源码里,找到对应路径添加即可(原因后述)
在这里插入图片描述
项目-属性-VC++目录-库将lib文件夹添加进去(我的是D:\libtensorflow1.14-1\lib)
在这里插入图片描述
项目-属性-链接器-输入-附加依赖项将tensorflow_cc.lib文件添加进去
在这里插入图片描述
在cpp文件中加入

#include"tensorflow/core/public/session.h"
#include "tensorflow/core/platform/env.h"

生成解决方案,可能会提示无法打开各种.pb.h文件,大多都在include\tensorflow\core\framework下,进入该文件夹中发现存在对应名称的.proto文件,因此使用之前下载好的protoc软件进行操作。
在include文件夹下进入cmd(一定要在该文件夹下,在framework下不行),输入
protoc -cpp_out=./ ./tensorflow/core/framework/xxx.proto(XXX为缺少的pb.h文件的名字)
WIN10+CUDA10.0+CUDNN7.6.5+Tensorflow1.14 编译及C++调用_第2张图片
进行protoc->pb.h转化后,重新生成解决方案,如果上面包含目录没有加第三条,可能会出现无法打开google/protobuf/port_def.inc,将对应路径加入包含目录即可。
生成解决方案后还可能存在min、max函数问题,按照参考2中的修改方式进行逐一修改,即在std前面加入左括号(,max(min)后面加入右括号),如:

private:
  static constexpr size_type kMaxSize =
      std::numeric_limits<difference_type>::max();
 
改为
 private:
  static constexpr size_type kMaxSize =
      (std::numeric_limits<difference_type>::max)();

完成上述操作后,应该可以正确生成解决方案,但如果此时直接调用pb文件,在使用接口

Status status = NewSession(SessionOptions(), &session);

时可能会出现下图问题(不知道为什么图这么小。。反正就是无法解析的外部符号)
在这里插入图片描述
打开下载的tensorflow1.14源码 在\tensorflow\tools\def_file_filter\def_file_filter.py.tpl文件中,找到 Header for the def file这行,将无法识别的符号加入到后面,添加一行def_fp.write("\t XXXXXXXXXX\n")把XXXXXX换成VS提示的无法解析符号在这里插入图片描述
重新进行上述configure、build(很快,只build有修改的地方)、复制操作(不要复制include,不然还得重新protoc+修改min、max,只复制lib与dll即可)。

调用代码

完成上述操作后,便完成了对于Tensorflow1.14的编译,使用CPP代码调用即可。
以下为我使用的测试CPP代码,其中model_path为我生成的pb模型路径(不会checkpoint转pb的自行百度),image_path为测试图像路径,后面的vgg19_input:0、dense_3/Softmax:0时模型的输入输出,按照你们自己的模型修改即可。

#include 

#include
#include"tensorflow/core/public/session.h"
#include "tensorflow/core/platform/env.h"

using namespace cv;
using namespace tensorflow;
using std::cout;
using std::endl;
int main()
{
	const std::string model_path = "D:/chengxu/VGG16-Model-Transfer-Learning-master/trans_model/my_weights_vgg18_use.pb";
	const std::string image_path = "D:/chengxu/VGG16-Model-Transfer-Learning-master/test_set/normal/10.09.14-10.20.33[R][0@0][0]40500.jpg";


	cv::Mat img = cv::imread(image_path);
	cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
	resize(img, img, cv::Size(224, 224), 0, 0, INTER_NEAREST);
	int height = img.rows;
	int width = img.cols;
	int depth = img.channels();

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

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

	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;
				//printf("%d");
			}
		}
    }



	Session* session;

	Status status = NewSession(SessionOptions(), &session);
	if (!status.ok()) {
		std::cerr << status.ToString() << endl;
		return -1;
	}
	else {
		cout << "Session created successfully" << endl;
	}
	tensorflow::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;
	}
	std::vector<std::pair<std::string, tensorflow::Tensor>> inputs = {
		{ "vgg19_input:0", input_tensor },
	};
	// 输出outputs
	//tensorflow::Tensor outputs(DT_FLOAT, TensorShape({ 1, height, width, depth }));
	std::vector<tensorflow::Tensor> outputs;
	for (int i = 1; i < 100; i++) {
		double start = clock();
		// 运行会话,计算输出"x_predict",即我在模型中定义的输出数据名称,最终结果保存在outputs中
		status = session->Run(inputs, { "dense_3/Softmax:0" }, {}, &outputs);
		double end = clock();
		cout << "time = " << (end - start) << "\n";
		if (!status.ok()) {
			std::cerr << status.ToString() << endl;
			return -1;
		}
		else {
			//cout << "Run session successfully" << endl;
		}
	}
}

测试结果

1.Python端时长
WIN10+CUDA10.0+CUDNN7.6.5+Tensorflow1.14 编译及C++调用_第3张图片
2.C++推理时长
WIN10+CUDA10.0+CUDNN7.6.5+Tensorflow1.14 编译及C++调用_第4张图片
Python比C++大约在快1.5倍左右

你可能感兴趣的:(WIN10+CUDA10.0+CUDNN7.6.5+Tensorflow1.14 编译及C++调用)