从零开始在Windows上构建Android版的Tensorflow Lite
文章目錄
- 第一步:獲取源代碼
- 1. 工具:Git
- 2. 下載代碼
- 第二步:了解代碼
- 第三步:工具準備
- 1. Git
- 2. NDK
- 3. CMake
- 4. Python 3
- 5. Patch
- 第四步:環境準備
- 第五步:補寫一個CMakeLists.txt
- 第六步:CMake項目配置
- 配置常見錯誤1
- 配置常見錯誤2
- 第七步:構建
- 構建常見錯誤1
- 構建常見錯誤2
- 第八步:符號剝離
- 構建其他版本
- 總結
一般情況下,構建Android版本的Tensorflow Lite可以參考谷歌的標準文檔:
https://tensorflow.google.cn/lite/android/lite_build?hl=cs
其思路是使用Docker構建系統。也可以到網上查詢,一般的解決方案是使用Bazel構建系統。這兩種構建都需要在Linux系統上進行,可以在一臺Linux機器上構建也可以在Windows上安裝一個Linux子系統來進行。
本文提供了另一種方案,即在Windows上通過NDK和CMake進行構建。如果熟悉CMake構建系統,整個過程還是比較容易的。
第一步:獲取源代碼
1. 工具:Git
獲取源代碼的工具是git,在Windows上,我們從下面網址可以下載Windows git
https://gitforwindows.org/
2. 下載代碼
我們在自己計算機的某個工作目錄下(<work_dir>) 創建目錄: tfbuild,在tfbuild中再創建一層目錄tensorflow_src。在tfbuild/tensorflow_src目錄下,用以下命令獲取代碼:
git clone https://github.com/tensorflow/tensorflow.git
如果覺得用這個方法下載比較慢,也可以考慮到gitee上下載:
https://gitee.com/erban-myproject/tensorflow
我覺得后面這個下載速度比較快。直接在網站上查看Release.md,版本是 2.12.0。通過git或者直接下載zip都可以。zip下載的,解壓后,把tensorflow所在的哪一級內容全部拷貝到tfbuild/tensorflow_src下。目錄結構如下:
第二步:了解代碼
我們大致瀏覽一下代碼,發現lite里相關位置有一個說明:
tfbuild/tensorflow_src/tensorflow/lite/tools/cmake/README.md
打開一看,指向了另一個位置,我們打開看一下:
tfbuild/tensorflow_src/tensorflow/lite/g3doc/guide/build_cmake.md
很有意思,就是一個如何用CMake編譯Tensorflow Lite庫的說明。一上來就說了CMake版本。往下看有六步,最后一步構建benchmark可以不做。第四步定義提到了很多參數,非常重要。另外里面還有一個Specifics of Android cross-compilation的部分也非常重要。在全文最后有一個Build TensorFlow Lite C library,可以讓你用CMake在PC上構建tensorflow lite的動態庫,也很不錯。有興趣的可以在完成本文構建后自行嘗試。
以上內容,基本上也是本文構建Tensorflow Lite的基本思路。
第三步:工具準備
1. Git
第一步已經準備了。因為從github上下載的tensorflow是不包括其依賴庫的,在編譯過程中依賴庫需要被下載,獲取的工具就是git。
2. NDK
既然是為Android構建的,我假設看本文的開發者是Android開發者。一般都會安裝有Android Studio,通過Android Studio安裝NDK即可。我們通過Android Studio安裝最新的NDK:25.1.8937393。
3. CMake
按照第二步中的說明,需要3.16或以上版本。我機子上裝的是3.19.1。可以直接安裝到tfbuild\cmake下。
4. Python 3
從這里下載最新的:
https://www.python.org/getit/
5. Patch
有部分模塊在編譯前可能會用到。到網上找一個具有和linux版相同功能的Windows版patch:
https://gnuwin32.sourceforge.net/packages/patch.htm
把這個patch放在tfbuild\bin下面
(注意:實測構建Tensorflow 2.12.0 的lite不需要這個工具。)
第四步:環境準備
在tfbuild目錄下創建一個批處理文件:env.bat
這個批處理文件內容如下:
(注意:上面尖括號中的內容根據讀者自己計算機上的路徑來填寫)
上面三行是配置環境,最后一行將啟動一個命令窗口。那么在這個打開的命令窗口中運行的命令都會依賴于前面設置的環境。這就很像在Linux上打開一個Console然后source一些環境。在Windows上我們可以用這種批處理文件來達到類似的目的。
我們可以點擊這個批處理文件,用where命令測試一下工具的存在性:
where cmake where python第五步:補寫一個CMakeLists.txt
為什么還要補寫一個CMake編譯腳本?我們來分析一下。你可以用上面提到的步驟編tensorflow lite,但是它得到的并不是Android可以用.so,而是一個tensorflow-lite.a。
如果我們在Android中來使用,那一定是以JNI方式來使用的。也就是說有一批Java類,通過這些類調用底層JNI接口。如果只在tensorflow/lite里編譯,那么那些jni接口在哪里?
我們重新再找一下代碼,看到在tensorflow/lite/java/src/main下有java和native目錄。java目錄是提供Java類的,native是提供底層接口的,也就是說這個native中的代碼也要編譯進去。
另外nnapi也是需要的。tensorflow/lite/delegates/nnapi/java/src/main里面也是一個java和一個native目錄。那說明和上面處理是相同的。
從tensorflow/lite/java/src/main/java/org/tensorflow/lite/TensorFlowLite.java來看這個.so名字叫libtensorflowlite_jni.so。我們在tensorflow_src/tensorflow下創建一個CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)project(tensorflowlite_jni)add_library(tensorflowlite_jniSHAREDlite/java/src/main/native/interpreter_factory_impl_jni.cclite/java/src/main/native/jni_utils.cclite/java/src/main/native/nativeinterpreterwrapper_jni.cclite/java/src/main/native/nativesignaturerunner_jni.cclite/java/src/main/native/op_resolver_lazy_delegate_proxy.cclite/java/src/main/native/tensor_jni.cclite/java/src/main/native/tensorflow_lite_jni.cclite/delegates/nnapi/java/src/main/native/nnapi_delegate_impl_jni.cclite/core/shims/jni/jni_utils.cc)include_directories(${PROJECT_SOURCE_DIR})add_subdirectory(lite)find_library(log-liblog )target_link_libraries(tensorflowlite_jnitensorflow-lite${log-lib} )第六步:CMake項目配置
我們以構建Release版本的arm64-v8a為例子。在tfbuild下創建一個目錄:tflite_build。雙擊env.bat,鍵入如下命令:
cd tflite_build cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=%NDK_DIR%/build/cmake/android.toolchain.cmake -DANDROID_TOOLCHAIN=clang -DANDROID_ABI=arm64-v8a -DTFLITE_ENABLE_GPU=ON ../tensorflow_src/tensorflow可以看到CMake的工具鏈配置來自于NDK。
這一步將消耗比較多的時間,因為系統會根據依賴性將所有依賴庫的源代碼全部下載下來,這時候就看你的網速了。這一步主要是等待和反復執行命令。你會看到原本tflite_build目錄是空的,隨著時間的推移會有一個個目錄被創建出來,一個個測試模塊被編譯和運行。命令行窗口上會顯示各種測試結果。
配置常見錯誤1
Cloning into 'farmhash'... fatal: unable to access 'https://github.com/google/farmhash/': OpenSSL SSL_read: Connection was reset, errno 10054 Cloning into 'farmhash'... fatal: unable to access 'https://github.com/google/farmhash/': Failed to connect to github.com port 443: Timed out遇到這種錯誤是網絡連接的問題,再次執行上面開始構建的命令即可。一般當你看到出錯庫的對應目錄創建出來了,就說明已經連上了,正在下載了。
配置常見錯誤2
CMake Error at <work_dir>/tfbuild/tflite_build/cpuinfo/CMakeLists.txt:262 (ADD_SUBDIRECTORY): ADD_SUBDIRECTORY not given a binary directory but the given sourcedirectory "<work_dir>/tfbuild/tflite_build/clog-source" isnot a subdirectory of"<work_dir>/tfbuild/tflite_build/cpuinfo". When specifyingan out-of-tree source a binary directory must be explicitly specified.這個錯誤非常奇怪,通過觀察,發現clog和cpuinfo的代碼是完全一樣的,甚至clog的CMakeLists.txt里項目名寫的是cpuinfo。另外在cpuinfo/deps下發現一個clog模塊,看樣子外面下載的那個clog是搞錯了,應該使用里面這個clog模塊。
將<work_dir>/tfbuild/tflite_build/cpuinfo/CMakeLists.txt中有關clog的部分進行修改, 即注釋掉256和258行:
#IF(NOT DEFINED CLOG_SOURCE_DIR)SET(CLOG_SOURCE_DIR "${PROJECT_SOURCE_DIR}/deps/clog")#ENDIF()也就是無條件設置CLOG_SOURCE_DIR為cpuinfo下的clog
(注意:在修改完CMakeLists.txt后,最好刪除tfbuild\tflite_build\CMakeCache.txt,然后重新運行本步驟配置CMake項目。)
第七步:構建
終于進入構建步驟了。其命令是:
cmake --build . -j構建過程也是比較長的,取決于你計算機的編譯能力。有4500多個文件要進行編譯。在編譯過程中也會遇到一些問題。
構建常見錯誤1
找不到flatc
Traceback (most recent call last): File "<work_dir>\tflite_build\flatbuffers\scripts\generate_code.py", line 148, in <module>flatc( File "<work_dir>\tflite_build\flatbuffers\scripts\generate_code.py", line 82, in flatcresult = subprocess.run(cmd, cwd=str(cwd), check=True)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\Python3\Lib\subprocess.py", line 548, in runwith Popen(*popenargs, **kwargs) as process:^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\Python3\Lib\subprocess.py", line 1024, in __init__self._execute_child(args, executable, preexec_fn, close_fds, File "D:\Python3\Lib\subprocess.py", line 1493, in _execute_childhp, ht, pid, tid = _winapi.CreateProcess(executable, args,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OSError: [WinError 193] %1 不是有效的 Win32 應用程序。這個錯誤是由于flatc不是Windows可執行文件導致的。仔細查找generate_code.py在flatbuffers的CMakeLists.txt里的使用。
我們看到它在函數compile_flatbuffers_schema_to_binary里,再查找compile_flatbuffers_schema_to_binary的使用點:
從這里往上看,有條件FLATBUFFERS_BUILD_TESTS,可以知道如果開啟了構建flatbuffers測試用例,那么最終就會調用flatc。
既然我們現在的目標是編譯tensorflow lite,我們可以跳過這些測試用例的構建。
我們查找FLATBUFFERS_BUILD_TESTS,可以看到:
option(FLATBUFFERS_BUILD_TESTS "Enable the build of tests and samples." ON)option(FLATBUFFERS_INSTALL "Enable the installation of targets." ON)option(FLATBUFFERS_BUILD_FLATC "Enable the build of the flatbuffers compiler"ON)option(FLATBUFFERS_STATIC_FLATC "Build flatbuffers compiler with -static flag"ON)我們可以將這些項的ON改為OFF
(注意,在修改完CMakeLists.txt后,刪除tfbuild\tflite_build\CMakeCache.txt,然后重新運行第六步配置CMake項目。完成后,再啟動本步驟的構建。)
這里,我們也可以考慮把flatc編出來的方案,那就要使用Visual Studio。網絡已經有文章可以參考:
https://blog.csdn.net/huangjiazhi_/article/details/103262814
問題是編出flatc.exe如何在本構建中來使用。經測試,本文提供一個方法:
# append .exe on Windows 那一段是需要加的內容。就是運行測試前,在要用的可執行文件名后面加上.exe
感覺關閉開關的方案較方便
構建常見錯誤2
JNI源文件缺失導致鏈接錯誤。
error: undefined symbol: TfLiteCheckInitializedOrThrow >>> referenced by jni_utils.cc:50 (E:/2023/CSDN/BuildTFLite/tfbuild/tensorflow_src/tensorflow/lite/java/src/main/native/jni_utils.cc:50) >>> CMakeFiles/tensorflowlite_jni.dir/lite/java/src/main/native/jni_utils.cc.o:(tflite::jni::CheckJniInitializedOrThrow(_JNIEnv*)) clang++: error: linker command failed with exit code 1 (use -v to see invocation)(注意,這是筆者曾經遇到過的錯誤。如果你按前面的tensorflow_src/tensorflow/CMakeLists.txt來構建,是不會遇到這個錯誤的。這里只是舉一個例子。)
這個錯誤是沒有TfLiteCheckInitializedOrThrow函數導致的,我們在tfbuild/tensorflow_src/tensorflow/lite里查找所有的文件,在tfbuild\tensorflow_src\tensorflow\lite\core\shims\jni\jni_utils.cc中找到了這個函數,把它加到tensorflow_src/tensorflow/CMakeLists.txt里即可。
第八步:符號剝離
如果上述問題都解決你將看到正確的編譯和鏈接:
我們在tfbuild\tflite_build下得到了Release版本的libtensorflowlite_jni.so
祝賀你按照本文獲得了你要的.so文件。但是這.so文件好像有點大,達到70多M。
這是由于.so文件中含有大量的符號信息,如果要最終在應用中使用應該把這些符號信息去掉。
運行如下命令:
(注意,不同版本的NDK,strip的位置略有不同。)
再看libtensorflowlite_jni.so,就變成了5.5M,竟然有60多M的符號信息!
構建其他版本
如果打算編譯如armv7-a, x86, x86_64, 那在第六步中的參數-DANDROID_ABI=arm64-v8a,可以改為:
-DANDROID_ABI=armv7-a, -DANDROID_ABI=x86, -DANDROID_ABI=x86_64即可。
編譯Debug版本使用-DCMAKE_BUILD_TYPE=Debug
(注意,如果改變參數,最好將tfbuild\tflite_build\CMakeCache.txt刪除后,再按第六步命令來配置。)
總結
本文整理了在Windows上編譯Android版本的Tensorflow Lite的步驟。整個過程會遇到一些坑,需要開發人員具有一定的CMake構建項目的經驗。開發者需要通過仔細觀察CMakeLists.txt文件的內容,錯誤輸出內容來分析和解決問題。
總結
以上是生活随笔為你收集整理的从零开始在Windows上构建Android版的Tensorflow Lite的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android复习系列②之《Java进阶
- 下一篇: 股票买入方法,股池和和公式