參考文章
tensorflow+ssd_mobilenet實現目標檢測的訓練
TensorFlow基於ssd_mobilenet模型實現目標檢測
使用TransferLearning實現環視圖像的角點檢測——Tensorflow+MobileNetv2_SSD
MobileNet SSD V2模型的壓縮與tflite格式的轉換
使用TensorFlow Lite將ssd_mobilenet移植至安卓客戶端
整個項目代碼(包括models和android,不包括編譯的tensorflow):代碼地址
項目中數據只有一類手勢,android里面有量化和非量化兩種模型
環境准備
- 克隆models
新建mobile_ssd_tf文件夾,git clone https://github.com/tensorflow/models
放到mobile_ssd_tf下面 - 編譯tensorflow
git clone https://github.com/tensorflow/tensorflow
,安裝protobuf,bazel等
tensorflow編譯參考
tensorflow 1.13.1,bazel 0.19.1(注意tf和bazel版本對應,不然出錯)
cd tensorflow/
bazel build tensorflow/python/tools:freeze_graph
bazel build tensorflow/contrib/lite/toco:toco
數據制作
進入models/research/object_detection目錄,下面工作基本都在這個目錄下進行
mkdir ssd_data
- labelImg工具標注數據集,jpg格式圖片放到VOCdevkit/VOC2007/JPEGImages目錄下,xml文件放到VOCdevkit/VOC2007/Annotations文件夾下面
- train_test_split.py把xml分為train,test,val三部分,
- xml2csv.py把xml轉csv
- gen_tfrecords.py生成tfrecord
- 在ssd_data下面新建label_map.pbtxt
item {
id: 1 #id從1開始編號
name: 'name of class1'
}
item {
id: 2
name: 'name of class2'
}
最終目錄如下
訓練
- 進入models/research/object_detection目錄
下面在這個目錄下進行工作
mkdir ssd_model
從samples/config下面拷貝一個配置文件(就是要訓練的模型),放到ssd_model下面(我選的是ssd_mobilenet_v2_coco.config,還有其他各種版本的,看需要選擇),修改這個配置文件
num_classes:前景類別數目+1(背景)
input_path:上面生成的tfrecord路徑
label_map_path:label_map.pbtxt的路徑
fine_tune_checkpoint:預訓練模型的路徑(可以注釋掉從頭開始訓練)
其他學習率,batch_size,num_steps,height,width等等看情況調節
從model zoo(model zoo給出了coco數據集上一些檢測模型的時間和mAP),下載預訓練模型放到ssd_model下面(也可以不下載,從頭開始訓練,我下的是這個ssd_mobilenet_v2_coco_2018_03_29.tar.gz)
- 訓練
python train.py --logtostderr --pipeline_config_pathssd_model/ssd_mobilenet_v2_coco.config --train_dir=ssd_data
注:新版train.py在legacy目錄下,先把它copy到research下。
訓練生成的模型文件和日志都在ssd_data目錄下面。
tensorboard --logdir=ssd_data/
查看訓練日志
報錯:from nets your-net-name ModuleNotFoundError: No module named 'nets'
research/slim下運行:
python setup.py build
python setup.py install
新版也可以用下面方法訓練
python model_main.py --alsologtostderr --pipeline_config_path=... --model_dir=...
- 測試
(新版eval.py在legacy目錄下,所以先拷貝出來)
這里將train產生的pipeline.config重命名為train_pipeline.config(因為eval也會產生pipeline.config,會覆蓋掉)
python eval.py --logtostderr --checkpoint_dir=ssd_data/ --eval_dir=ssd_data/ --pipeline_config_path=ssd_data/train_pipeline.config
ValueError: Image with id b'one_heart11.jpg' already added.
ssd_mobilenet_v2_coco.config和train_pipeline.config里面eval_config下num_examples值改為自己的,默認8000可能太大了
模型導出與轉換
- 導出pb格式:
python export_tflite_ssd_graph.py --pipeline_config_path=ssd_data/pipeline.config --trained_checkpoint_prefix=ssd_data/model.ckpt-200000 --output_directory=ssd_data/ --add_postprocessing_op=true
產生tflite_graph.pb和tflite_graph.pbtxt文件
- 導出tflite格式:(可以從上一步產生的tflite_graph.pbtxt中查看輸入節點,輸出節點名稱和shape)
切換到第一步編譯的tensorflow目錄下面
bazel run tensorflow/contrib/lite/toco:toco
--input_file=tflite_graph.pb
--output_file=model.tflite
--input_shapes=1,300,300,3
--input_arrays=normalized_input_image_tensor
--output_arrays='TFLite_Detection_PostProcess','TFLite_Detection_PostProcess:1','TFLite_Detection_PostProcess:2','TFLite_Detection_PostProcess:3'
--inference_type=FLOAT
--allow_custom_ops
或者執行
cd tensorflow/lite/python
python tflite_convert.py --graph_def_file=tflite_graph.pb --output_format=TFLITE --output_file=model.tflite --inference_type=FLOAT --input_arrays='normalized_input_image_tensor' --output_arrays='TFLite_Detection_PostProcess','TFLite_Detection_PostProcess:1','TFLite_Detection_PostProcess:2','TFLite_Detection_PostProcess:3' --input_shapes=1,300,300,3 --allow_custom_ops
加參數 --mean_values=128 --std_dev_values=128 --inference_type=QUANTIZED_UINT8可以量化,但是預訓練模型要選帶quantized的config文件,后面會在介紹訓練量化版本的模型。
將生成的model.tflite放到ssd_data目錄里,像下面一樣,然后測試(我已經把checkpoint等文件刪了,所以這個目錄少了很多模型文件,只有pb格式的和tflite格式的)
pc上測試tflite:
python test_tflite.py
移植到android項目
- 下載android項目
tensorflow官方demo地址[https://github.com/tensorflow/examples/tree/master/lite/examples/object_detection/android]
svn https://github.com/tensorflow/examples/trunk/lite/examples/object_detection/android
拉取android子文件夾
然后android studio加載項目
- 做些修改
assets/文件夾下面放tflite模型文件
labelmap2.txt放你的類別名稱
修改名字detect2.tflite,labelmap2.txt(不改的話會覆蓋掉你的模型)
我只有一類前景,所以改成下面的(???表示背景)
???
oneheart
改DetectorActivity.java:
TF_OD_API_IS_QUANTIZED = false;//如果之前沒量化,這里要改成false
TF_OD_API_MODEL_FILE = "detect2.tflite";
TF_OD_API_LABELS_FILE = "file:///android_asset/labelmap2.txt";
DetectorTest.java也有上面表示兩個文件路徑的變量,我也改了,可以全局查找,全部改為自己的。
我還改了TFLiteObjectDetectionAPIModel.java里面的labelOffset為0,因為我發現不改下標會有溢出。
- 結果
模型大約18M,運行時間約300ms
效果:
訓練量化版本和時間優化
TensorFlow Mobilenet SSD模型壓縮並移植安卓上以達到實時檢測效果
參考上面這篇文章,從量化,修改圖片尺寸和修改depth_multiplier等角度優化時間。(depth multiplier作為一個因子與網絡中各層的channel數相乘,depth_multiplier越小,網絡中feature map的channel數越少)
這次將samples/config/ssd_mobilenet_v2_quantized_300x300_coco.config拷貝出來放到ssd_model下面,從model zoo里面下ssd_mobilenet_v2_quantized_coco.tar預訓練模型放到ssd_model下面。
如上面修改配置文件,這里我還把圖片尺寸fixed_shape_resizer改為180x180。
我暫時沒有改depth_multiplier參數,可以試驗下0.75看看效果。
訓練前刪除ssd_data下之前生成的訓練的模型和日志,不然可能會有影響。
- 開始訓練
python3 train.py --logtostderr --pipeline_config_path=ssd_model/ssd_mobilenet_v2_quantized_300x300_coco.config --train_dir=ssd_data
- 導出pb模型
python export_tflite_ssd_graph.py --pipeline_config_path=ssd_data/pipeline.config --trained_checkpoint_prefix=ssd_data/model.ckpt-9571 --output_directory=ssd_data/ --add_postprocessing_op=true
- 導出tflite格式模型
python tflite_convert.py --graph_def_file=tflite_graph.pb --output_format=TFLITE --output_file=model_quantized.tflite --inference_type=FLOAT --input_arrays='normalized_input_image_tensor' --output_arrays='TFLite_Detection_PostProcess','TFLite_Detection_PostProcess:1','TFLite_Detection_PostProcess:2','TFLite_Detection_PostProcess:3' --input_shapes=1,180,180,3 --allow_custom_ops --mean_values=128 --std_dev_values=128 --inference_type=QUANTIZED_UINT8
- 移植到android項目里面
訓練好的模型文件放到assets/目錄下面
修改DetectorActivity.java里面的幾個變量
private static final int TF_OD_API_INPUT_SIZE = 180;
private static final boolean TF_OD_API_IS_QUANTIZED = true;//這里改為true
private static final String TF_OD_API_MODEL_FILE = "model_quantized.tflite";
private static final String TF_OD_API_LABELS_FILE = "file:///android_asset/labelmap2.txt";
- 效果
模型大約4M,運行時間約20-30ms
運行效果: