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

15

我成功地构建了一个简单的C++应用程序,运行TF Lite模型,并将我的源代码添加到tensorflow/lite/examples中,与官方的C++ 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相关的未定义引用。 我用nm检查了一下,发现这些符号确实在libframework.pic.a中丢失了,我在Bazel输出中的各种.o文件中找到了其中一些。 手动选择所有这些.o文件似乎是不正确的。

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


1
只是一条评论,我认为你应该能够从 TensorFlow 树中的 bazel-genfiles/external(构建后)收集 flatbuffers 标头而不是从 ~/.cache/... - jdehesa
1
关于你的问题,每个.a只包含其目标的.c代码,而不包含依赖项。此外,我还没有找到任何跟踪头文件的好方法。我的解决方法是在TF树中添加一个代码文件和一个新目标(在新的子目录中),并将其依赖于我所需的内容。使用cc_library,我不确定是否可以通过选项获取包含所有内容的.a,使用cc_binary,您至少可以在bazel-bin下获得一个.params文件,其中包含所有.a依赖项,您可以扫描它,或者您可以制作一个.so(我个人在Windows上制作DLL)。 - jdehesa
1
关于头文件,我曾经痛苦地列出了所有需要复制的必要路径,基本上是通过试错来完成的。现在我做的是为库编写自己的包装器(一个非常简单的接口,我可以在其中加载一个.pb模型文件并为给定的输入运行它),隐藏每个本地TF类型(主要是pimpl)。这需要一些技巧,但现在我只需要我的几个头文件和这个DLL。 - jdehesa
1
这些都是针对常规的TF,而不是TF Lite。现在有了TensorFlow for C,所以这项工作变得有点多余了,但我认为Lite没有相应的等效物,所以我认为相同的方法应该适用。 - jdehesa
1
@jdehesa,非常感谢您的所有建议。实际上,我在bazel-tensorflow/external中找到了外部头文件。对于链接,我真的不想向TF树添加任何代码,而是将TF视为通常的依赖项,因此我最终将所有所需的目标文件添加到了上面的TFLite_LIBS变量中,并且现在它可以工作了。我编写了一个脚本,遍历每个未定义的引用,并在bazel-bin中查找定义它的目标文件,在几次迭代后,我收集了所需的一切。 - Andrii Zymohliad
显示剩余2条评论
1个回答

17

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

我使用了一个简单的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中(当然要带上正确的路径前缀)。之后,我得到了一些新的未定义引用,但经过几次迭代,解决了所有问题。

可能我也可以从我的初始树内构建中的*-params文件中获取完整的依赖项列表,但快速检查显示它有一些冗余项,脚本只收集必要的项。

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

更新:
可以使用普通的make非常类似于官方构建指令来进行RPiiOSARM64的全包括TF Lite静态库的本地构建:
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'。你知道这是怎么回事吗? - Marcel_marcel1991
好的,我发现有一个名为build_lib.sh的构建脚本,它似乎关闭了nnapi。这样我就不再遇到这个错误了。但是我想知道nnapi有什么用处,它是否只支持某些平台? - Marcel_marcel1991
@Marcel_marcel1991,我真的不知道,抱歉。我仍然使用第一种方法:使用bazel构建,然后手动列出所有文件以进行链接。我发现这样更容易在多个平台上维护。 - Andrii Zymohliad
关于Make构建:tensorflow/lite/tools/make/目录下只包含一个README文件,说明“使用Makefile构建TensorFlow Lite在2021年8月已被弃用,请改用CMake或Bazel。” - Danijel
最后一个包含Make文件的标签似乎是v2.6.3:https://github.com/devialet/tensorflow/tree/v2.6.3/tensorflow/lite/tools/make - Danijel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接