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:用来自动化构建大型工程的,和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
    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

unzip v3.9.2.zip
cd protobuf-3.9.2
./autogen.sh
./configure
make
sudo make install

protoc --version
tar -zxvf eigen-3.3.90.tar.gz
cd eigen-3.3.90/
mkdir build
cd build
cmake ..

sudo make
sudo make install
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 build --config=opt //tensorflow:libtensorflow_cc.so

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),
    ]
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})

报错及解决办法

正常情况下,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=./ </protofile>
在tensorflow github的相关提问
若执行protoc命令报错 tensorflow/core/framework/xx.proto File not found 即import的文件未找到。可能是执行protoc命令时所处的路径层级不对,在import后跟的路径的层级下重新尝试。

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

文件头加入tensorflow定义域:

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

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

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

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

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


    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] &#x8D8A;&#x754C; 是因为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成功的原因在 下一个问题中描述。

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

① 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使用快到达最大容量时,就报错。

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);
    status_load = tensorflow::ReadBinaryProto(tensorflow::Env::Default(), "model.pb", &graphdef);
    status_create = session->Create(graphdef);

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

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

总结解决问题过程中出现问题的原因和经验:

[En]

Summarize the causes of jams in solving problems and the experience gained:

① 如果报错没有展开详细描述,寻找其是否存在错误log日志,根据错误日志分析原因。
② 明确报错的最初有可能产生位置,而非眉毛胡子一把抓。
③ 同样的代码在别人主机上能运行成功,不代表这份代码或流程就是正确的(可能其中几行逻辑错误,但他人主机上这几行等于注释、不起作用)
关键是先理清思路,然后再开始做。

[En]

The key point is to sort out the train of thought before starting to do it.

Original: https://blog.csdn.net/qq_33936417/article/details/121955645
Author: nolabel
Title: linux上的tensorflow 2.4.1-gpu c++接口编译并用其运行.pb模型

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/511418/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球