在tensorflow源碼環境里,編譯出獨立的jni.so,避免依賴libtensorflowlite.so,從而實現apk體積最小化

需要在APP里使用tensorflow lite來運行PC端訓練的model.tlite,又想apk的體積最小,嘗試了如下方法:

1. 在gradle里配置

implementation("org.tensorflow:tensorflow-lite:2.16.1")

這樣會引入tensorflow.jar,最終apk的size增加大約2.2M

2. 根據tensorflow官方的優化編譯教程

https://www.tensorflow.org/lite/android/lite_build?spm=5176.28103460.0.0.73711db8niy7UE&hl=zh-cn

針對我們的模型,構建出針對性的TensorFlow Lite AAR,最后集成到apk里,體積增加約1.5M

分析TensorFlow Lite AAR的實現,發現其本質還是通過JNI調用了libtensorflowlite.so,

而這個libtensorflowlite.so,包含了tensorflow lite幾乎所有核心framework代碼,因此肯定很大。

3. 因為我們僅需要用到tensorflow lite里model 初始化,interpreter推理等基礎功能,并不需要tensorflow lite里的其他功能,因此,想要最小,直接在我們的JNI文件里,集成tensorflow lite相關類的源碼進行編譯,應該就能使得體積增加最小化了。

把我們JNI文件依賴的類,比如

#include "tensorflow/lite/interpreter.h"
#include "tensorflow/lite/model.h"

等.h和.cc引入我們的JNI里,一起編譯就行了。

一開始是在android studio里,導入tensorflow lite的源碼, 修改CmakeLists.txt,嘗試編譯出可以獨立運行的JNI so, 但是總是失敗。

最后,把JNI文件,放到tensorflow lite的源碼目錄里,利用tensorflow的編譯工具bazel,編譯成功。然后把生成的milc_jni.so放到app的jniLibs里,成功:

a. 在tensorflow/lite/下創建milc_jni/這個目錄,目錄下創建BUILD,milc_jni.cc,?custom_op_resolver.h和custom_op_resolver.cc

b. 根據我們的模型文件model.tflite里用到的算子,比如,我只用了FULLY_CONNECTED,RELU, LOGISTIC這3個算子,定制精簡算子的Resolver類

custom_op_resolver.h

#ifndef TENSORFLOW_LITE_CUSTOM_OP_RESOLVER_H_
#define TENSORFLOW_LITE_CUSTOM_OP_RESOLVER_H_#include "tensorflow/lite/mutable_op_resolver.h"namespace tflite {class MinimalOpResolver : public MutableOpResolver {public:MinimalOpResolver();
};}  // namespace tflite#endif  // TENSORFLOW_LITE_CUSTOM_OP_RESOLVER_H_

custom_op_resolver.cc

#include "tensorflow/lite/milc_jni/custom_op_resolver.h"
#include "tensorflow/lite/kernels/builtin_op_kernels.h"namespace tflite {
MinimalOpResolver::MinimalOpResolver() {// 使用 kernels::builtin:: 命名空間下的注冊函數AddBuiltin(BuiltinOperator_FULLY_CONNECTED, tflite::ops::builtin::Register_FULLY_CONNECTED());AddBuiltin(BuiltinOperator_RELU, tflite::ops::builtin::Register_RELU());AddBuiltin(BuiltinOperator_LOGISTIC, tflite::ops::builtin::Register_LOGISTIC());
}
}  // namespace tflite

c. 創建JNI文件milc_jni.cc

#include <jni.h>
#include "tensorflow/lite/interpreter.h"
#include "tensorflow/lite/model.h"
#include "tensorflow/lite/milc_jni/custom_op_resolver.h"
#include <android/log.h>#define LOG_TAG "TensorFlowLiteJNI"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)// 移除所有日志輸出
//#define LOGI(...)
//#define LOGE(...)extern "C" JNIEXPORT jfloat JNICALL
Java_com_xm_j_milc_predictJNI(JNIEnv* env, jobject /* this */, jstring modelPath, jfloatArray inputArray) {const char* modelPathStr = env->GetStringUTFChars(modelPath, nullptr);// 獲取輸入數組jfloat* inputElements = env->GetFloatArrayElements(inputArray, nullptr);jsize inputLength = env->GetArrayLength(inputArray);if (inputLength != 31) {LOGE("Input array length must be 31");env->ReleaseStringUTFChars(modelPath, modelPathStr);env->ReleaseFloatArrayElements(inputArray, inputElements, JNI_ABORT);return -1.0;}// 加載 TensorFlow Lite 模型std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromFile(modelPathStr);if (!model) {LOGE("Failed to load model from %s", modelPathStr);env->ReleaseStringUTFChars(modelPath, modelPathStr);env->ReleaseFloatArrayElements(inputArray, inputElements, JNI_ABORT);return -1.0;}// 創建解釋器//tflite::ops::builtin::BuiltinOpResolver resolver;tflite::MinimalOpResolver resolver;std::unique_ptr<tflite::Interpreter> interpreter;tflite::InterpreterBuilder(*model, resolver)(&interpreter);if (!interpreter) {LOGE("Failed to create interpreter");env->ReleaseStringUTFChars(modelPath, modelPathStr);env->ReleaseFloatArrayElements(inputArray, inputElements, JNI_ABORT);return -1.0;}// 分配張量if (interpreter->AllocateTensors() != kTfLiteOk) {LOGE("Failed to allocate tensors");env->ReleaseStringUTFChars(modelPath, modelPathStr);env->ReleaseFloatArrayElements(inputArray, inputElements, JNI_ABORT);return -1.0;}// 設置輸入float* input = interpreter->typed_input_tensor<float>(0);for (int i = 0; i < inputLength; ++i) {input[i] = inputElements[i];}// 運行推理if (interpreter->Invoke() != kTfLiteOk) {LOGE("Failed to invoke interpreter");env->ReleaseStringUTFChars(modelPath, modelPathStr);env->ReleaseFloatArrayElements(inputArray, inputElements, JNI_ABORT);return -1.0;}// 獲取輸出// 5. 獲取輸出結果float* outputTensor = interpreter->typed_output_tensor<float>(0);// 釋放資源env->ReleaseStringUTFChars(modelPath, modelPathStr);env->ReleaseFloatArrayElements(inputArray, inputElements, JNI_ABORT);return outputTensor[0];  // 直接返回標量值
}

d. 創建BUILD文件

# 自定義操作解析器(僅包含必要算子)
cc_library(name = "custom_op_resolver",srcs = ["custom_op_resolver.cc"],hdrs = ["custom_op_resolver.h"],deps = ["//tensorflow/lite/kernels:builtin_ops",],
)cc_binary(name = "milc_jni.so",srcs = ["milc_jni.cc"],linkshared = True,linkstatic = True,  # 靜態鏈接所有依賴deps = [":custom_op_resolver","//tensorflow/lite:framework","//tensorflow/lite/kernels:builtin_ops","@flatbuffers//:flatbuffers",],copts = ["-Oz","-flto=thin","-ffunction-sections","-fdata-sections","-fvisibility=hidden","-fvisibility-inlines-hidden","-DFLATBUFFERS_RELEASE","-DTF_LITE_STRIP_ERROR_STRINGS=1","-DNDEBUG","-DFORCE_MINIMAL_LOGGING","-fno-exceptions","-fno-rtti","-fno-unwind-tables","-fno-asynchronous-unwind-tables","-ffreestanding",],linkopts = ["-flto=thin","-Wl,--gc-sections","-Wl,--exclude-libs,ALL","-s", "-Wl,--as-needed","-Wl,-z,norelro","-Wl,--build-id=none",  # 移除構建ID"-Wl,--strip-all",  # 徹底去除符號"-nostdlib","-lc","-Wl,--hash-style=gnu",  # 更小的哈希表"-Wl,--compress-debug-sections=zlib",  # 壓縮調試節],features = ["-layering_check",],
)

e. 在tensorflow的源碼目錄里,初始化好環境,AndroidNDK之類的,然后執行編譯

bazel build -c opt   --config=android_arm64   --copt="-DFORCE_DISABLE_ALL_OPS"   --linkopt="-Wl,--gc-sections"   --linkopt="-Wl,--exclude-libs,ALL"   --linkopt="-s" --define=tflite_with_xnnpack=false  --copt="-Os" --copt="-fomit-frame-pointer"   --copt="-ffunction-sections"   --copt="-fdata-sections"   --copt="-fvisibility=hidden"   --copt="-g0" --copt="-DFLATBUFFERS_RELEASE"  //tensorflow/lite/milc_jni:milc_jni.so

然后,就會生成一個milc_jni.so,大約500K,它是可以獨立運行的,不用依賴libtensorflowlite.so,因此,APK的size,也就只會增加約500K。

f.針對生成的milc_jni.so,進一步壓縮優化

sudo apt-get install upx
upx --android-shlib --best --lzma milc_jni.so -o milc_jni_upx.so

最終的milc_jni_upx.so大約200K,因此,APK的size,也就只會增加約200K。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/81873.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/81873.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/81873.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

neo4j框架:java安裝教程

安裝使用neo4j需要事先安裝好java&#xff0c;java版本的選擇是一個犯難的問題。本文總結了在安裝java和使用Java過程中遇到的問題以及相應的解決方法。 Java的安裝包可以在java官方網站Java Downloads | Oracle 中國進行下載 以java 8為例&#xff0c;選擇最后一行的x64 compr…

[服務器備份教程] Rclone實戰:自動備份數據到阿里云OSS/騰訊云COS等對象存儲

更多服務器知識&#xff0c;盡在hostol.com 各位服務器的守護者們&#xff0c;咱們都知道&#xff0c;數據是數字時代的“黃金”&#xff0c;而服務器上的數據更是我們業務的命脈。可天有不測風云&#xff0c;硬盤可能會突然“壽終正寢”&#xff0c;手滑執行了“毀滅性”命令…

Nextjs App Router 開發指南

Next.js是一個用于構建全棧web應用的React框架。App Router 是 nextjs 的基于文件系統的路由器&#xff0c;它使用了React的最新特性&#xff0c;比如 Server Components, Suspense, 和 Server Functions。 術語 樹(Tree): 一種用于可視化的層次結構。例如&#xff0c;包含父…

山東大學計算機圖形學期末復習15——CG15

CG15 OpenGL緩沖區、讀寫操作以及混合&#xff08;Blending&#xff09; 一、OpenGL緩沖區概述 OpenGL中的緩沖區是用于存儲像素數據的內存區域&#xff0c;主要包括以下類型&#xff1a; 顏色緩沖區&#xff08;Color Buffer&#xff09;&#xff1a;存儲每個像素的顏色值…

html+css+js趣味小游戲~記憶卡片配對(附源碼)

下面是一個簡單的記憶卡片配對游戲的完整代碼&#xff0c;使用HTML、CSS和JavaScript實現&#xff1a; html <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"wid…

?個并發訪問量?較?的key在某個時間過期,在redis中這個時間過期什么意思

在 Redis 中&#xff0c;當提到一個鍵&#xff08;key&#xff09;“在這個時間過期”&#xff0c;指的是為該鍵設置了生存時間&#xff08;TTL, Time To Live&#xff09;或過期時間&#xff08;expiration time&#xff09;。一旦到達設定的過期時間&#xff0c;Redis 會自動…

【設計模式】- 行為型模式1

模板方法模式 定義了一個操作中的算法骨架&#xff0c;將算法的一些步驟推遲到子類&#xff0c;使得子類可以不改變該算法結構的情況下重定義該算法的某些步驟 【主要角色】&#xff1a; 抽象類&#xff1a;給出一個算法的輪廓和骨架&#xff08;包括一個模板方法 和 若干基…

ubuntu22.04 卸載ESP-IDF

要在Ubuntu 22.04上完全卸載ESP-IDF&#xff0c;請按照以下步驟操作&#xff1a; 卸載ESP-IDF的步驟 刪除ESP-IDF目錄&#xff1a; # 假設ESP-IDF安裝在~/esp/esp-idf目錄 rm -rf ~/esp/esp-idf刪除ESP-IDF工具鏈和下載的工具&#xff1a; rm -rf ~/.espressif從PATH中移除ESP…

SQLMesh 內置宏詳解:@PIVOT等常用宏的核心用法與示例

本文系統解析 SQLMesh 的四個核心內置宏&#xff0c;涵蓋行列轉換的 PIVOT、精準去重的 DEDUPLICATE、靈活生成日期范圍的 DATE_SPINE&#xff0c;以及動態表路徑解析的 RESOLVE_TEMPLATE。通過真實案例演示參數配置與 SQL 渲染邏輯&#xff0c;并對比宏調用與傳統 SQL 的差異&…

基于Springboot + vue3實現的工商局商家管理系統

項目描述 本系統包含管理員、商家兩個角色。 管理員角色&#xff1a; 用戶管理&#xff1a;管理系統中所有用戶的信息&#xff0c;包括添加、刪除和修改用戶。 許可證申請管理&#xff1a;管理商家的許可證申請&#xff0c;包括搜索、修改或刪除許可證申請。 許可證審批管理…

第五部分:第五節 - Express 路由與中間件進階:廚房的分工與異常處理

隨著你的 Express 應用變得越來越大&#xff0c;所有的路由和中間件都寫在一個文件里會變得難以管理。這時候就需要將代碼進行拆分和組織。此外&#xff0c;一個健壯的后端應用必須能夠優雅地處理錯誤和一些常見的 Web 開發問題&#xff0c;比如跨域。 路由模塊化 (express.Ro…

萌新聯賽第(三)場

C題 這道題用暴力去寫想都不要想&#xff0c;一定超時&#xff0c;于是我們需要優化&#xff0c;下面是思路過程&#xff1a; 如圖&#xff0c;本題只需找到x的因數個數和(n-x)的因數個數&#xff0c;這兩個相乘&#xff0c;得到的就是對于這個x來說組合的個數&#xff0c;且x…

【Android構建系統】如何在Camera Hal的Android.bp中選擇性引用某個模塊

背景描述 本篇文章是一個Android.bp中選擇性引用某個模塊的實例。 如果是Android.mk編譯時期&#xff0c;在編譯階段通過某個條件判斷是不是引用某個模塊A, 是比較好實現的。Android15使用Android.bp構建后&#xff0c;要想在Android.bp中通過自定義的一個變量或者條件實現選…

【OneNET】_01_使用微信小程序通過新版OneNET平臺獲取STM32設備信息并進行控制

【OneNET】_01_使用微信小程序通過新版OneNET平臺獲取STM32設備信息并進行控制 一、 前言1.1 OntNET硬件方面: STM32F103C8T6 ESP01S教程 1.2 微信小程序方面 二、STM32代碼部分修改三、微信小程序修改的部分四、小筆記&#xff08;個人雜記&#xff09;4.1 OneNETOneNET物聯網…

用 python 編寫的一個圖片自動分類小程序(三)

圖片自動分類識別小程序記錄 2025/5/18 0:38修改程序界面&#xff0c;增加一些功能 用 python 編寫的一個圖片自動識別分類小程序。 操作系統平臺&#xff1a;Microsoft Windows 11 編程語言和 IDE&#xff1a;python 3.10 Visual studio code 一&#xff1a;圖片自動分…

嵌入式硬件篇---SGP30 氣體傳感器

文章目錄 前言一、SGP30 氣體傳感器詳解(一)基本概述(二)工作原理傳感器結構檢測機制自校準功能(三)主要特性(四)應用場景智能家居空氣質量檢測儀汽車行業商業建筑二、TVOC 與 eCO2 的含義(一)TVOC(總揮發性有機化合物)定義危害健康標準(二)eCO2(等效二氧化碳)…

【原創】ubuntu22.04下載編譯AOSP 15

安裝依賴的庫&#xff0c;順便把vim 也安裝一下 sudo apt-get install vim sudo apt-get install git gnupg flex bison build-essential zip curl zlib1g-dev libc6-dev-i386 x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip font…

防止勒索病毒的兜底方案——備份

勒索病毒入侵會對您的業務數據進行加密勒索&#xff0c;導致業務中斷、數據泄露、數據丟失等&#xff0c;從而帶來嚴重的業務風險。 防止勒索病毒有三個方向&#xff1a; 1&#xff09;實時防御已知勒索病毒 各個云廠商的云安全中心實現了對大量已知勒索病毒的實時防御。在服務…

es在已有歷史數據的文檔新增加字段操作

新增字段設置默認值 場景 在已經有大量數據的索引文檔上&#xff0c;增加新字段 技術實現 一.更新索引映射 通過PUT請求顯式定義新字段類型&#xff0c;確保后續寫入的文檔能被正確解析 PUT /文檔名/_mapping {"properties": {"字段名1": {"type…

留給王小川的時間不多了

王小川&#xff0c;這位頭頂“天才少年”光環的清華學霸、搜狗輸入法創始人、中國互聯網初代技術偶像&#xff0c;正迎來人生中最難啃的硬骨頭。 他在2023年創立的百川智能&#xff0c;被稱為“大模型六小虎”之一。今年4月&#xff0c;王小川在全員信中罕見地反思過去兩年工作…