linux上的tensorflow 2.4.1-gpu c++接口编译并用其运行.pb模型

System information

OS Platform and Distribution : ( Ubuntu 18.04)
TensorFlow installed from : (source)
TensorFlow version: Tags v2.4.1
Python version: Python 3.7.6
Installed using virtualenv? pip? conda?: conda
Bazel version (if compiling from source): have tried bazel 3.4.0 、 bazel 3.1.0
GCC/Compiler version (if compiling from source): gcc (GCC) 7.4.0
CUDA/cuDNN version: cuda_11.1 , cudnn 8.0.5
GPU model and memory: rtx3060
nvidia-driver version: 460.91
eigen version : eigen-3.3.90

建议搭配:tf2.4.0 、cuda 11.0、cudnn 8.0.5




构建tensorflow的c++接口

安装bazel

bazel:用来自动化构建大型工程的,和make、maven属于同一类工具。
bazel版本需要和tensorflow版本对应

tf2.4 适配的bazel版本为3.1.0

使用sh文件安装bazel,执行bazel version时报错
/usr/local/lib/bazel/bin/bazel-real: cannot execute binary file: Exec format error
改为下载zip后解压安装

wget https://github.com/bazelbuild/bazel/releases/download/3.1.0/bazel-3.1.0-dist.zip
mkdir bazel

unzip -d bazel bazel-3.1.0-dist.zip
cd bazel
bash ./compile.sh
sudo cp output/bazel /usr/local/bin

#验证
bazel version 


安装gcc

镜像源
(国内外各镜像源都没有7.3.1版本,可以用7.4.0或7.5.0)

gcc安装步骤



安装protobuf

在选择编译的tensorflow版本的github官方源码里的workspace.bzl中查看该版本适配的protobuf版本和下载url,
比如tf2.4.0 适配的protobuf版本为3.9.2 :
https://github.com/tensorflow/tensorflow/blob/r2.4/tensorflow/workspace.bzl

    tf_http_archive(
        name = "com_google_protobuf",
        patch_file = clean_dep("//third_party/protobuf:protobuf.patch"),
        sha256 = "cfcba2df10feec52a84208693937c17a4b5df7775e1635c1e3baffc487b24c9b",
        strip_prefix = "protobuf-3.9.2",
        system_build_file = clean_dep("//third_party/systemlibs:protobuf.BUILD"),
        system_link_files = {
            "//third_party/systemlibs:protobuf.bzl": "protobuf.bzl",
        },
        urls = [
            "https://storage.googleapis.com/mirror.tensorflow.org/github.com/protocolbuffers/protobuf/archive/v3.9.2.zip",
            "https://github.com/protocolbuffers/protobuf/archive/v3.9.2.zip",
        ],
    )

安装步骤

wget https://storage.googleapis.com/mirror.tensorflow.org/github.com/protocolbuffers/protobuf/archive/v3.9.2.zip
# 或 wget https://github.com/protocolbuffers/protobuf/archive/v3.9.2.zip
unzip v3.9.2.zip
cd protobuf-3.9.2
./autogen.sh
./configure
make
sudo make install
# sudo make uninstall 安装错版本后卸载指令
protoc --version  # 查看protobuf版本


安装eigen

eigen下载url eigen3.3.9。下载tar.gz后解压

tar -zxvf eigen-3.3.90.tar.gz
cd eigen-3.3.90/
mkdir build
cd build
cmake ..
sudo make
sudo make install


编译tf2.4.0

参考

mkdir tensorflow
cd tensorflow
git clone --depth 1 --branch v2.4.1 https://github.com/tensorflow/tensorflow.git 

cd tensorflow
./configure  #配置编译选项
compute capability计算力点进提示给的nvidia官网url中查看

#使用Bazel编译C++ API的库
#CPU版本
bazel build --config=opt //tensorflow:libtensorflow_cc.so

#GPU版本
bazel build --config=opt --config=cuda //tensorflow:libtensorflow_cc.so
  • 编译过程报错
    1)...include/cudnn_backend.h No such file or directory
    解决方法:缺失cudnn_backend.h、cudnn_adv_infer.h等文件,下载cudnn压缩包将解压后的文件夹中所有报错中提示缺失的文件重新拷贝到cuda文件夹下。
    2)llvm下载报错 Error downloading [https://storage.googleapis.com/mirror.tensorflow.org/github.com/llvm/llvm-project/archive/f402e682d0ef5598eeffc9a21a691b03e602ff58.tar.gz, https://github.com/llvm/llvm-project/archive/f402e682d0ef5598eeffc9a21a691b03e602ff58.tar.gz]
    去报错提示中的github地址(我这里是https://github.com/llvm/llvm-project/archive/f402e682d0ef5598eeffc9a21a691b03e602ff58.tar.gz)手动下载llvm-project,
    放到任意本地路径下 (我的路径 /home/app/llvm-project-f402e682d0ef5598eeffc9a21a691b03e602ff58.tar.gz;
    然后修改 tensorflow/tensorflow/workspace.bzl 文件中的llvm下载路径,改为所存的本地路径
LLVM_URLS = [
        "file:///home/app/llvm-project-f402e682d0ef5598eeffc9a21a691b03e602ff58.tar.gz",
        "https://github.com/llvm/llvm-project/archive/{commit}.tar.gz".format(commit = LLVM_COMMIT),
    ]
  • 编译成功后的测试用例
    如果cmake报版本相关错误,卸载并重装cmake最新版本。

下载依赖文件

cd tensorflow/tensorflow/lite/tools/make
./download_dependencies.sh

若网络原因下载失败,可分别手动下载相应文件并解压,下载的文件url见 download_dependencies.sh文件;
将以上下载的文件解压后并按照以上文件名进行命名,统一存放在 tensorflow/tensorflow/lite/tools/make/downloads 文件夹中。




使用tensorflow c++接口运行.pb模型

大概的步骤:

$ cd ~/demo/build
$ cmake ..
$ make
$ ./demo

移动运行所依赖的各个文件夹到运行的项目目录下

注: 我的tensorflow 2.4.1编译完成后没有contrib文件夹、没有bazel-genfiles文件夹。
这里采取的是将依赖的文件夹移动到运行项目demo/ 目录下,为的是方便后期该项目迁移到其他主机运行时能带着 依赖一起迁移。
如果你只在本机运行该项目,那么移动的目的文件夹就是 /usr/local/include/tf/

我移动后的 demo/ 文件夹层级结构:

demo/
	CMakeLists.txt
	build/
		model.pb
	include/
		UseTensorFlowDLL.h
	src/
		main.cpp
	opencv3.4.0/
		bin/
		include/
		lib/
		share/
	tf2/
		bazel-bin/
			external/
			_solib_local/
			tensorflow/
			third_party/
		eigen/
			..太多这里省略
		lib/
			libtensorflow_cc.so.2
			libtensorflow_framework.so.2			
		tensorflow/
			..省略
		third_party/
			..省略
	

其中tf2目录为自己手动创建,tf2/ 下的各个文件夹手动从编译完成的目录下拷贝过来,各文件夹来源:
bazel-bin/ tensorflow/bazel-bin/ (tensorflow/为编译完成的最终文件夹)
eigen/ : tensorflow/lite/tools/make/downloads/eigen
lib/: 两个文件(拷贝完可能需要重命名)分别来自
tensorflow/bazel-bin/tensorflow/libtensorflow_cc.so.2.4.1
tensorflow/bazel-bin/tensorflow/libtensorflow_framework.so.2.4.1
tensorflow/ : tensorflow/
third_party/ : tensorflow/third_party


其中CMakeLists.txt 内容:

# cmake needs this line
cmake_minimum_required(VERSION 3.10)

# Define project name
project(shufflenetPbLinuxDemo)

find_package(OpenCV REQUIRED)

message(STATUS "OpenCV library status:")
message(STATUS "    config: ${OpenCV_DIR}")
message(STATUS "    version: ${OpenCV_VERSION}")
message(STATUS "    libraries: ${OpenCV_LIBS}")
message(STATUS "    include path: ${OpenCV_INCLUDE_DIRS}")

set(TENSORFLOW_LIBS   
        ${CMAKE_CURRENT_SOURCE_DIR}/tf2/lib/libtensorflow_cc.so.2
        ${CMAKE_CURRENT_SOURCE_DIR}/tf2/lib/libtensorflow_framework.so.2
)
# 头文件的搜索目录
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/include
        ${CMAKE_CURRENT_SOURCE_DIR}/tf2
        ${CMAKE_CURRENT_SOURCE_DIR}/tf2/bazel-bin
        ${CMAKE_CURRENT_SOURCE_DIR}/tf2/tensorflow
        ${CMAKE_CURRENT_SOURCE_DIR}/tf2/third-party
        ${CMAKE_CURRENT_SOURCE_DIR}/tf2/eigen
)

# Declare the executable target built from your sources
add_executable(shufflenetPbLinuxDemo ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp)

# Link your application with OpenCV libraries
target_link_libraries(shufflenetPbLinuxDemo PRIVATE ${OpenCV_LIBS}  ${TENSORFLOW_LIBS})




报错及解决办法

cmake .. 报错合集
1、cmake报错tensorflow/core/framework/device_attributes.pb.h:没有那个文件或目录 或者报错没有其他的pb.h文件

正常情况下,pb.h是在编译过程中由protobuf编译生成的,应该存在于bazel-genfiles/tensorflow/core/framework 文件夹下。
.pb.h is a generated file when you build with bazel build. The
.pb.h files should appear under bazel-genfiles folder thereafter.
而我之前用tensorflow2.4.0版本虽然也没生成bazel-genfiles/ ,但没有这个报错,不知道为什么换成tf2.4.1 分支后就报错了。

解决方法:手动用命令挨个将报错中的 proto文件转为pb.h
sudo protoc <protofile> --cpp_out=./
在tensorflow github的相关提问
若执行protoc命令报错tensorflow/core/framework/xx.proto File not found 即import的文件未找到。可能是执行protoc命令时所处的路径层级不对,在import后跟的路径的层级下重新尝试。

2、OpenCV 报错Could not find a package configuration file provided by “Opencv” with any of the following names:OpencvConfig.cmake opencv-config.cmake

方法:设置环境变量:
sudo gedit /etc/profile
添加: export OpenCV_DIR=/home/workspace/demo/opencv3.4.0
保存退出,source /etc/profile




make 报错合集
1、运行make报错cv::glob(cv::String,std) 未定义的引用

原因:CMakeLists.txt中的opencv路径没加或者不对。(”未定义的引用“这类报错都是因为缺少链接库)
对‘cv::imread(cv::String const&, int)’未定义的引用


2、运行make报错 Session* session "Session was not declared in this scope"

文件头加入tensorflow定义域:

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

3、报错ReadBinaryProto的load_status failed

如果你的 model.pb为 meta_graph,ReadBinaryProto可能只适用于frozen graph,
因此需要将加载模型方式改为 tensorflow::LoadSavedModel(session_options, run_options, model_path,{tensorflow::kSavedModelTagServe}, &bundle))
LoadSavedModel示例


4、make报错.../eigen3/unsupported/Eigen/CXX11/Tensor:1:42: error: #include nested too deeply

方法

find . -name "eigen*" -type d
修改CMakeLists.txt 的include_directories 里eigen的路径,换成上面find出来的结果(如果find出来多个路径则可以挨个尝试)。




./your_demo 过程报错合集
1、段错误(核心已转储)

原因:程序发生了越界访问
1)内存访问越界(数组越界)
2)多线程 线程不安全
3)堆栈溢出(使用大的局部变量容易造成栈溢出,因为局部变量都分配在栈上)
扩展:core报错具体原因可以 配置操作系统使其生成core文件,用gdb查看core文件 结合程序崩溃后的core文件分析bug

最终原因以及解决办法
升级到tf2.4.1+cuda11.1后报这个错是因为main.cpp最后输出outputs置信度这里的outputs数组越界,

	//前向运行,网络进行实际推理inference
	tensorflow::Status status_run = session->Run(inputs, { out_put_nodes }, {}, &outputs);

	//获取置信度
	auto confidence_vector = outputs[0].tensor<float, 2>();
	for (int ProposalNum = 0; ProposalNum < maxbatchSize; ProposalNum++) {

		float confidence_float1 = confidence_vector(ProposalNum, 1);
		cout << "the confidence is:" << confidence_float1 << endl;
	}
	return 0;

outputs[0] 越界 是因为session 根本就没有run成功,
(通过在session->Run 之后加上

tensorflow::Status status_run = session->Run(inputs, {out_put_nodes}, {}, &output)
if (!status_run.ok()) {
   std::cout << "ERROR: RUN failed in session run..."  << std::endl;
   std::cout << status_run.ToString() << "\n";
   

判断出session没有run成功,所以outputs自然是空数组,outputs[0]也就越界访问了)
而session没有run成功的原因在 下一个问题中描述。


2、报错Invalid argument:Tensor ShuffleOutputPb:0,specified in either feed_devices or fetch_devices was not found in the Graph

原因: main.cpp中的 Tensor变量ShuffleOutputPb:0 和python中生成pb模型的设置的tensor名不一样,修改main.cpp中的变量名。
然后再重新跑,终于status_run.ok()为True了,但是又报下面的错:


3、报错 No algorithm worked

① main.cpp 中的 bundle.session->Run(inputs,{out_put_nodes},{},&outputs);这里直接写inputs,而不是{{input_name, input}}
因为本项目中已经提前把input_name和resized_tensor都push_back进inputs张量中了。
OP_REQUIRES failed at conv_ops.cc:Not found: No algorithm worked!

原因:tensorflow官方github的issue中查询这个报错,判断可能是OOM内存超出,可以在运行时同时watch -n 0.5 nvidia-smi 看到当memory使用快到达最大容量时,就报错。

python的解决方案

c++的解决方法
在session定义前如果没有设置限制GPU的内存使用,要加上内存限制的相关语句:

	tensorflow::SessionOptions session_options;
	tensorflow::ConfigProto* session_options_config = &session_options.config;

    // 内存限制
	session_options_config->set_allow_soft_placement(true);
	session_options_config->mutable_gpu_options()->set_per_process_gpu_memory_fraction(0.33);
	session_options_config->mutable_gpu_options()->set_allow_growth(true);

	//创建新的会话 并进行参数配置
	status = NewSession(session_options, &session);//创建新会话Session
	status_load = tensorflow::ReadBinaryProto(tensorflow::Env::Default(), "model.pb", &graphdef); //从pb文件中读取图模型;
	status_create = session->Create(graphdef); //将模型导入会话Session中;

我的问题是把 session_options_config-> ... 这3行写在了NewSession定义之后了,把这3行设置移到 NewSession定义语句前面就可以了。



经验总结

问题搜索的网站:
1、tensorflow官方github的 Issues 中搜索报错或问题关键词。
2、和cuda相关的问题搜索 nvidia官网论坛
3、stackoverflow


总结解决问题中卡壳的原因以及获取的经验:
① 如果报错没有展开详细描述,寻找其是否存在错误log日志,根据错误日志分析原因。
② 明确报错的最初有可能产生位置,而非眉毛胡子一把抓。
③ 同样的代码在别人主机上能运行成功,不代表这份代码或流程就是正确的(可能其中几行逻辑错误,但他人主机上这几行等于注释、不起作用)
重点是理清排查思路,再下手。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值