如何将 TensorFlow Lite 构建为静态库并从单独的(CMake)项目链接到它?

Posted

技术标签:

【中文标题】如何将 TensorFlow Lite 构建为静态库并从单独的(CMake)项目链接到它?【英文标题】:How to build TensorFlow Lite as a static library and link to it from a separate (CMake) project? 【发布时间】:2019-08-03 04:34:51 【问题描述】:

通过将我的源代码添加到tensorflow/lite/examples,我已经成功构建了一个运行TF Lite 模型的简单C++ 应用程序,类似于the official C++ TF guide 建议的完整TF。现在我想将它构建为一个单独的项目(共享库),静态链接到 TF Lite 并使用 CMake 作为构建系统。

我尝试将自定义目标添加到我的CMakeLists.txt,这将使用 Bazel 构建 TF Lite:

set(TENSORFLOW_DIR $CMAKE_SOURCE_DIR/thirdparty/tensorflow)
add_custom_target(TFLite
    COMMAND bazel build //tensorflow/lite:framework
    COMMAND bazel build //tensorflow/lite/kernels:builtin_ops
    WORKING_DIRECTORY $TENSORFLOW_DIR)

我选择了这些 Bazel 目标,因为来自 tensorflow/lite/examples/minimalBUILD 文件将它们作为依赖项,并且在我构建时它们对我有用 我在 TF repo 中使用 Bazel 的代码。不知道够不够。

然后我手动收集包含目录(带有那个丑陋的临时硬编码路径)和库:

set(TFLite_INCLUDES
    $TENSORFLOW_DIR
    ~/.cache/bazel/_bazel_azymohliad/ec8567b83922796adb8477fcbb00a36a/external/flatbuffers/include)

set(TFLite_LIBS
    $TENSORFLOW_DIR/bazel-bin/tensorflow/lite/libframework.pic.a)
    
target_include_directories(MyLib ... PRIVATE ... $TFLite_INCLUDES)
target_link_libraries(MyLib ... $TFLite_LIBS)

使用此配置,我在链接期间会收到许多对 TFLite 内容的未定义引用。我检查了nmlibframework.pic.a 中确实缺少这些符号,我在 Bazel 输出的各种 .o 文件中发现了其中一些符号。手动选择所有这些 .o 文件似乎是错误的。

那么,是否可以像我尝试的那样从 CMake 很好地链接到 TF Lite?也许有一些神奇的bazel query include_dirs(//tensorflow/lite:framework) 命令可以为我提供所有必要包含目录的路径,以及用于链接库的类似命令,以便我可以将此信息传递给 CMake?

【问题讨论】:

只是评论,我认为您应该能够从 TensorFlow 树中的 bazel-genfiles/external 收集 flatbuffers 标头(构建后),而不是 ~/.cache/... 啊等等,也许是另一个,你检查过bazel-bin/externalbazel-tensorflow/external吗? 不管怎样,关于你的问题,问题是每个.a 只包含其目标的.c 代码,而不是依赖项。另外,我也没有找到任何跟踪标题的好方法。我解决这个问题的方法是向 TF 树(在新的子目录中)添加一个代码文件和一个新目标,并依赖于我需要的内容。使用cc_library,我不确定您是否可以通过选项获得包含所有内容的.a,使用cc_binary,您至少会在bazel-bin 下获得一个.params 文件,其中包含所有.a 依赖项可以扫描,或者你可以制作一个.so(我个人现在在Windows上制作一个DLL) 关于标题,我曾经痛苦地列出了我需要复制的所有必要路径,几乎是通过反复试验。我现在要做的是我有自己的库包装器(一个非常简单的界面,我可以加载.pb 模型文件并针对给定的输入运行它),隐藏每个原生 TF 类型(主要是 pimpl)。它花了很多技巧,但现在我只需要我的几个头文件和这个 DLL。 这都是针对常规 TF 的,不是 TF Lite。现在有了TensorFlow for C,所以这项工作变得有点多余,但我认为 Lite 没有等价物,所以我认为相同的方法应该可以工作。 【参考方案1】:

我最终为 CMake 的 target_link_libraries(在 TFLite_LIBS 中)手动列出了所有必要的 TFLite 对象文件,并且它可以工作。

我使用了一个简单的 shell 脚本来获取必要的目标文件列表。 首先,我将构建日志中的所有未定义引用收集到一个 bash 数组中,如下所示:

SYMBOLS=(\
    'tflite::CombineHashes('\
    'tflite::IsFlexOp('\
    'tflite::ConvertArrayToTfLiteIntArray('\
    'tflite::EqualArrayAndTfLiteIntArray('\
    ...
    'tflite::ConvertVectorToTfLiteIntArray(')

然后对于该数组中的每个符号,我检查了 bazel 构建输出中的每个 *.o 文件:

for SYMBOL in $SYMBOLS[@]; do
    for OBJ in $(find -L /path/to/tensorflow/bazel-bin/ -name '*.o'); do
        nm -C $OBJ | grep "T $SYMBOL" > /dev/null && echo $OBJ
    done
done | sort | uniq

并将输出添加到 CMake 中的TFLite_LIBS(当然,使用正确的路径前缀)。在那之后,我得到了一个新的未定义引用部分,但经过几次迭代,它解决了所有问题。

也许我还可以从我最初的 in-tree 构建中的 *-params 文件中获取完整的依赖项列表,但快速检查显示它有一些冗余项,并且脚本只收集了必要的项。

对于包含位置,我用$TENSORFLOW_DIR/bazel-tensorflow/external/flatbuffers/include/ 替换了 bazel 缓存中平面缓冲区的硬编码路径。感谢jdehesa 的提示。

更新: 全包 TF Lite 静态库的本机构建可以非常类似于使用普通旧 make 的 RPi、ios 或 ARM64 的官方构建说明: 1../tensorflow/lite/tools/make/download_dependencies.sh 2.make -f tensorflow/lite/tools/make/Makefile

输出库将存储为<tensorflow-root>/tensorflow/lite/tools/make/gen/<platform>/lib/libtensorflow-lite.a。并且带有其标头的外部依赖项将进入<tensorflow-root>/tensorflow/tensorflow/lite/tools/make/downloads(例如flatbuffers 标头在<tensorflow-root>/tensorflow/tensorflow/lite/tools/make/downloads/flatbuffers/include 中)。

指南没有提到可以直接调用 make 。有针对不同交叉编译目标的包装脚本,它们只需设置适当的变量并运行 make。但默认情况下,make 只会进行原生构建。这个 make 调用可以作为自定义命令添加到 CMakeLists.txt

【讨论】:

当我尝试使用使用更新方法构建的静态库时,出现未定义的引用错误:./tensorflow/tensorflow/lite/tools/make/gen/linux_x86_64/lib/libtensorflow- lite.a(nnapi_implementation.o): In function (anonymous namespace)::ASharedMemory_create(char const*, unsigned long)': nnapi_implementation.cc:(.text+0x14): undefined reference to shm_open' 你知道这是从哪里来的吗? 好的,我发现有一个名为 build_lib.sh 的构建脚本似乎关闭了 nnapi。有了这个,我不再收到这个错误了。但是我想知道 nnapi 有什么用,是否仅在某些平台上受支持? @Marcel_marcel1991,我真的不知道,抱歉。而且我仍然使用第一种方法:使用 bazel 构建,然后手动列出所有文件进行链接。我发现在多个平台上维护起来更容易。

以上是关于如何将 TensorFlow Lite 构建为静态库并从单独的(CMake)项目链接到它?的主要内容,如果未能解决你的问题,请参考以下文章

使用 JAX 构建强化学习 agent,并借助 TensorFlow Lite 将其部署到 Android 应用中

使用 JAX 构建强化学习 agent,并借助 TensorFlow Lite 将其部署到 Android 应用中

如何将冻结图转换为 TensorFlow lite

如何将 HED 模型转换为 Tensorflow Lite 模型

将 Keras 模型转换为 TensorFlow lite - 如何避免不支持的操作?

如何创建可轻松转换为 TensorFlow Lite 的模型?