摘要
本文為系列部落格tensorflow模型部署系列的一部分,用于實作通用模型的部署。本文主要實作用tflite接口調用tensorflow模型進行推理。實作了tensorflow在邊緣計算及一些低成本方案、物聯網或工業級應用中使用的輕量級模型部署方案,并提供相關示例源代碼。相關源碼見連結
引言
本文為系列部落格tensorflow模型部署系列的一部分,用于tflite實作通用模型的部署。本文主要使用pb格式的模型檔案,其它格式的模型檔案請先進行格式轉換,參考tensorflow模型部署系列————預訓練模型導出。從模型檔案中擷取輸入輸出op名請參考部落格tensorflow模型部署系列————單機python部署
主題
上一篇博文tensorflow模型部署系列————單機JAVA部署就如何使用JAVA語言加載模型檔案并利用模型檔案進行推理進行講解,博文中也開放了使用
JAVA
進行模型推理的代碼,目前多數智能終端都已經支援JAVA,但在一些低成本方案、物聯網或工業級應用中更多的是使用嵌入式linux甚至是單片機,主要使用的編譯語言是C。博文tensorflow模型部署系列————單機C++部署介紹了使用C/C++進行模型部署的方法,但實際應用時因為tensorflow庫檔案太大(已超過100M),限制了tensorflow在嵌入式上的應用。目前tensorflow提供了專門用于嵌入式的tflite架構(僅700K左右)。tflite的目标是嵌入式部署,嵌入式部署需要針對處理器進行特殊優化。google官方除了android庫以外,并沒有已編譯好的檔案可供下載下傳。本文先對tflite及運作作簡單介紹,然後分别對tflite在python c/c++ java上的部署進行解說。
tflite介紹
應用場景
tflite是google為深度學習在嵌入式物聯網應用而推出的輕量級架構。它提供了python、java和C++接口,同時可以将浮點運算轉換為整數運算,進而在特定的硬體平台上加快推理速度。tflite使用的模型不是pb檔案,而是更小的基于FlatBuffers的模型檔案。
tflite主要有兩個元件:推理元件和模型轉換元件。推理元件用于運作模型;模型轉換元件用于将需要的模型轉換為tflite模型檔案
模型檔案轉換
模型檔案轉換時需要指定輸入輸出張量名,從模型檔案中擷取輸入輸出op名請參考部落格tensorflow模型部署系列————單機python部署
從session轉換
import tensorflow as tf
img = tf.placeholder(name="img", dtype=tf.float32, shape=(1, 64, 64, 3))
var = tf.get_variable("weights", dtype=tf.float32, shape=(1, 64, 64, 3))
val = img + var
out = tf.identity(val, name="out")
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
converter = tf.lite.TFLiteConverter.from_session(sess, [img], [out])
tflite_model = converter.convert()
open("converted_model.tflite", "wb").write(tflite_model)
從pb檔案轉換
import tensorflow as tf
graph_def_file = "/path/to/Downloads/mobilenet_v1_1.0_224/frozen_graph.pb"
input_arrays = ["input"]
output_arrays = ["MobilenetV1/Predictions/Softmax"]
converter = tf.lite.TFLiteConverter.from_frozen_graph(
graph_def_file, input_arrays, output_arrays)
tflite_model = converter.convert()
open("converted_model.tflite", "wb").write(tflite_model)
python部署
python語言目前在嵌入式系統上應用的還不多。這裡把python也拿出來講有兩個原因:
- 可以快速的在上位機上評估tflite模型檔案的準确率等性能
- 後面講解java和c++部署代碼時,參考python代碼,以便于讀者更好地了解tflite的運作原理
安裝
python版不需要額外安裝,安裝完畢tensorflow
pip install tensorflow
後就可以使用了
tf.lite
API講解
tflite的python接口詳見官方文檔。這裡針對常用接口做一個說明。
- 加載模型檔案
interpreter = tf.lite.Interpreter(model_path=tflite_file)
- 建立tensors
interpreter.allocate_tensors()
- 擷取輸入輸出OP
input_details = interpreter.get_input_details() output_details = interpreter.get_output_details()
- 張量填充
interpreter.set_tensor(input_details[0]['index'], d)
- 運作推理
interpreter.invoke()
- 擷取張量值
interpreter.get_tensor(output_details[0]['index'])
部署代碼
模型封裝庫
模型封裝類主要包括兩個方法:初始化和推理。
初始化隻用于處理與
invoke
無關的所有操作,以減少推理時的操作,保證模型推理高效運作。初始化主要進行的操作包括:模型檔案加載、擷取輸入輸出張量。
推理主要進行的操作有:輸入張量填充、
interpreter.invoke
、和輸出張量擷取。
模型封裝類示例代碼
經過模型封裝類封裝以後,示例代碼就很簡單了。隻用準備資料,然後推理就行了。
C++部署
API講解
這裡針對tflite的c++常用接口做一個說明,并跟将C接口跟本文中的python部署代碼進行對應,以便于讀者更好地了解接口
- 加載模型檔案,對應python代碼
interpreter = tf.lite.Interpreter(model_path=tflite_file)
std::unique_ptr<Interpreter> interpreter; std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromFile(model_file); InterpreterBuilder builder(*model, resolver); builder(&interpreter);
- 建立tensors,對應python代碼
interpreter.allocate_tensors()
interpreter->AllocateTensors()
- 擷取輸入輸出OP,對應python代碼
interpreter.get_input_details()
interpreter.get_output_details()
in_index = interpreter->inputs()[0]; out_index = interpreter->outputs()[0];
- 張量填充,對應python代碼
interpreter.set_tensor(input_details[0]['index'], d)
memcpy(interpreter->typed_tensor<float>(in_index), &input_vals[0], INPUT_SIZE*sizeof(input_vals[0]));
- 運作推理,對應python代碼
interpreter.invoke()
interpreter->Invoke()
- 擷取張量值,對應python代碼
interpreter.get_tensor(output_details[0]['index'])
float* output = interpreter->typed_tensor<float>(out_index); memcpy(&output_vals[0], output, OUTPUT_SIZE*sizeof(output_vals[0]));
部署代碼
模型封裝庫
模型封裝類主要包括兩個方法:初始化和推理。
初始化隻用于處理與
invoke
無關的所有操作,以減少推理時的操作,保證模型推理高效運作。初始化主要進行的操作包括:模型檔案加載、擷取輸入輸出張量。
推理主要進行的操作有:輸入張量填充、
interpreter.invoke
、和輸出張量擷取。
模型封裝類示例代碼
經過模型封裝類封裝以後,示例代碼就很簡單了。隻用準備資料,然後推理就行了。
編譯
tflite的編譯需要使用tensorflow源代碼,下面給出簡單的編譯步驟。後續在我的部落格中會放出各平台下已編譯好的tflite庫檔案,請持續關注
- 下載下傳tensorflow源碼
- 拷貝代碼檔案夾
到C++
tensorflow/lite/tools/make/
- 在
檔案中增加如下代碼tensorflow/lite/tools/make/Makefile
HELLOW_TFLIET := hellow_tf HELLOW_TFLIET_BINARY := $(BINDIR)$(HELLOW_TFLIET) HELLOW_TFLIET_SRCS := \ tensorflow/lite/tools/make/C++/model.cc \ tensorflow/lite/tools/make/C++/example.cc INCLUDES += \ -Itensorflow/lite/tools/make/C++/ ALL_SRCS += \ $(HELLOW_TFLIET_SRCS) CORE_CC_EXCLUDE_SRCS += \ $(wildcard tensorflow/lite/tools/make/C++/model.cc) \ $(wildcard tensorflow/lite/tools/make/C++/example.cc) HELLOW_TFLIET_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(HELLOW_TFLIET_SRCS)))) $(HELLOW_TFLIET): $(LIB_PATH) $(HELLOW_TFLIET_OBJS) @mkdir -p $(BINDIR) $(CXX) $(CXXFLAGS) $(INCLUDES) \ -o $(HELLOW_TFLIET_BINARY) $(HELLOW_TFLIET_OBJS) \ $(LIBFLAGS) $(LIB_PATH) $(LDFLAGS) $(LIBS)
- 執行編譯指令
make hellow_tf -j8 -f tensorflow/lite/tools/make/Makefile
JAVA(android)部署
tflite官方代碼提供了直接在android代碼上使用tflite庫的方法,本文針對android中使用庫為例進行說明。
android環境配置
要在android代碼中使用tflite庫,隻需在工程配置中加入
implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly'
即可,另外需要注意的是android打包時會對assets下不認識的檔案壓縮,這會導緻模型檔案讀取失敗,解決方法是在配置檔案添加
android {
...
aaptOptions {
noCompress "tflite"
}
...
}
部署代碼
模型封裝庫
模型封裝類主要包括兩個方法:初始化、關閉和推理。
初始化隻用于處理與
invoke
無關的所有操作,以減少推理時的操作,保證模型推理高效運作。初始化主要進行的操作包括:模型檔案加載。
推理主要進行的操作有:
interpreter.invoke
模型封裝類示例代碼
經過模型封裝類封裝以後,示例代碼就很簡單了。隻用準備資料,然後推理就行了。
示例代碼
- tflite的python模型部署代碼
- tflite的python模型封裝庫代碼
- tflite的python模型封裝庫示例代碼
- tflite的c/c++模型部署代碼
- tflite的c++模型封裝庫代碼
- tflite的c++模型封裝庫示例代碼
- 編譯步驟
- tflite的c/c++模型部署代碼——去除對tensorflow源碼的依賴
- tflite的c++模型封裝庫代碼
- [tflite的c++模型封裝庫示例代碼](https://github.com/gdyshi/model_deployment/blob/master/tflite/lib_app /example.cc)
- tflite的java(android)模型部署代碼
- tflite的java模型封裝庫代碼
- tflite的java模型封裝庫示例代碼
- pb檔案轉換為tflite模型檔案代碼
- 從tensorflow的session轉換為tflite模型檔案代碼
後記
因最近在做一個嵌入式移植的實際項目,前面我放出的源碼需要修改多處Makefile,容易出錯,加上評論區有人咨詢代碼編譯相關問題,故對嵌入式部署博文及代碼進行更新。主要目的是簡化編譯操作,不需要下載下傳整個tensorflow代碼,隻需引用相關頭檔案就可完成編譯。
要達到以上目的,需要去除編譯自已寫的代碼時對tflite相關源碼的依賴,一種方式是将tensorflow代碼先編譯為庫,這樣大家在編譯自己的特殊應用時隻需連結編譯好的庫即可。因為官方的編譯腳本也在不斷更新中,我這裡主要把修改點進行說明。同樣的,我會把相關代碼放到github裡。
編譯tflib
主要通過修改官方編譯示例的編譯腳本來實作。
- 複制一份編譯腳本
cp Makefile Makefile_tflib cp build_rpi_lib.sh build_tflib.sh
- 在
檔案修改如下行Makefile_tflib
$(LIB_PATH): tensorflow/lite/schema/schema_generated.h $(LIB_OBJS) @mkdir -p $(dir [email protected]) $(AR) $(ARFLAGS) $(LIB_PATH) $(LIB_OBJS) ++++$(CC) -shared -o $(LIBDIR)libtensorflow-lite.so $(LIB_OBJS)
- 在
檔案中将build_tflib.sh
替換為Makefile
Makefile_tflib
- 加載交叉編譯環境,然後執行
./build_tflib.sh
進行編譯
編譯完成後,會在
下生成./gen/rpi_armv7l/lib
和libtensorflow-lite.a
檔案。隻要嵌入式環境的編譯器沒變,我們就可以一直用生成的這兩個檔案libtensorflow-lite.so
- 打包庫檔案。
mkdir -p mpapp/tflib/lib cp ./gen/rpi_armv7l/lib/libtensorflow* mpapp/tflib/lib
編譯應用代碼
執行
build.sh
即可
附錄
參考
- tflite官方網址
- tensorflow 官方 java接口文檔