【ATU Book-i.MX8系列 - TFLite 進(jìn)階】 物件偵測應(yīng)用

一.? ?概述

邊緣運(yùn)算的重點(diǎn)技術(shù)之中,模組輕量化網(wǎng)路架構(gòu) 是不可或缺的一環(huán),如何高效的利用硬體資源來達(dá)到最佳目標(biāo),特別是在效能與準(zhǔn)確度的衡量上,是個(gè)非常有趣的議題。此章節(jié)先探討深度學(xué)習(xí)熱門的研究項(xiàng)目之一 物件偵測(Object Detection) ,此項(xiàng)目為最初的研究議題,故有衍伸出多種神經(jīng)網(wǎng)路架構(gòu),比如 VGG-19、ResNet、Inception V4、MobileNet + SSD 以及近年相當(dāng)火紅的 yolo 系列皆可實(shí)現(xiàn)物件識(shí)別之目的,其中差異就是取決於準(zhǔn)確度、參數(shù)量(模組大小)、運(yùn)行速度性能等等。

若新讀者欲理解更多人工智慧、機(jī)器學(xué)習(xí)以及深度學(xué)習(xí)的資訊,可點(diǎn)選查閱下方博文
?大大通精彩博文???【ATU Book-i.MX8系列】博文索引

??

TensorFlow Lite 進(jìn)階系列博文-文章架構(gòu)示意圖

?二.? 算法介紹
由於?邊緣運(yùn)算(edge computing) 需以輕量、快速、準(zhǔn)確為主。故採用神經(jīng)網(wǎng)路架構(gòu)最輕量,且有一定識(shí)別能力、運(yùn)行效率極佳的 MobileNet + SSD 架構(gòu)。

本章節(jié)將介紹由 輕量化網(wǎng)路架構(gòu) MobileNet物件檢測算法(Single Shot MultiBox Detector, SSD) 組成之應(yīng)用。

神經(jīng)網(wǎng)路架構(gòu)探討 :

(1) MobileNet

核心概念是利用拆分的概念,將原本的卷積層拆成 深度卷積(Depthwise Convolution) 與 逐點(diǎn)卷積(Pointwise Convolution) 兩個(gè)部分,稱作 深層可分離卷積(Depthwise Separable Convolution) 。
以此方式進(jìn)行運(yùn)算,能夠大幅度減少參數(shù)量,以達(dá)到加快運(yùn)算速度(用途擷取特徵)

?

MobileNet 輕量化概念示意圖, 參考 LaptrihnX 網(wǎng)站

?

?(2) Single Shot Multi-Box Detector, SSD

核心概念是由 金字塔特徵結(jié)構(gòu)(Pyramidal Feature Hierarchy)先驗(yàn)框(Prior Boxes) 的概念組成。

金字塔特徵結(jié)構(gòu)(Pyramidal Feature Hierarchy) :

採用不同大小的特徵圖檢測物件,比如說大特徵圖檢測小物件、小特徵圖檢測大物件。

先驗(yàn)框(Prior Boxes) :

讓每個(gè)特徵圖上設(shè)置不同尺寸長寬比的先驗(yàn)框,以作為預(yù)測框的基準(zhǔn)。這能夠幫助訓(xùn)練過程時(shí),提供梯度一定程度的範(fàn)圍限制,能夠降低一定程度的訓(xùn)練難度。

如下圖所示,金字塔特徵結(jié)構(gòu)概念就是在每個(gè)不同大小的特徵層之中,進(jìn)行預(yù)測來判斷是否有物件,並總和每個(gè)特徵層的結(jié)果,找出最大可能性的物件。

金字塔特徵結(jié)構(gòu)(Pyramidal Feature Hierarchy) 示意圖, 參考 ResearchGate 網(wǎng)誌?

如下圖所示,為 VGG-19 搭配 SSD 的神經(jīng)網(wǎng)路架構(gòu)(Neural Network)。如同上述金字塔特徵結(jié)構(gòu)概念,更明確的呈現(xiàn) SSD 架構(gòu)的作法。
其實(shí)就是在 VGG 每一層的輸出都裝上 檢測器(Detector) 與分類器 (Classifier) ,並將每層結(jié)果連結(jié)至 Fast NMS 來找最佳的物件檢測結(jié)果 !!

SSD 架構(gòu)概念示意圖, 參考 Medium 網(wǎng)誌

換個(gè)方式呈現(xiàn)上述概念。如下圖所示,說明 SSD 是檢測多個(gè)物件視窗來找到最佳方案。

?

SSD 架構(gòu)概念示意圖 - 2 , 參考 ITREAD 網(wǎng)誌

MobileNet SSD 實(shí)際架構(gòu) ( Netron呈現(xiàn) ) :

如下圖所示,為實(shí)際 MobileNet SSD 模組架構(gòu)。從右側(cè)灰色欄位可看出 Input 與 Output 資訊。
依設(shè)計(jì)所代表輸入端為彩色影像(300x300, RGB)、輸出端依序分別為物件位置、種類、分?jǐn)?shù)、數(shù)量等資訊。亦可從架構(gòu)圖上看到在最後每個(gè)輸出層有延伸出 “Pyramidal Feature Hierarchy 結(jié)構(gòu)” 。

?

三.? 算法實(shí)現(xiàn)

Google 官方有提供效果極佳的 mobilenet_object_detector.tflite 模組,即可直接使用,請(qǐng)點(diǎn)選下載。
這裡本篇文章以 github 的資源,並利用 遷移學(xué)習(xí)方法 與 TF-Slim 來實(shí)現(xiàn) 宮崎駿 《龍貓》 之? TOTORO 物件檢測器(Object Detector)

實(shí)現(xiàn)步驟如下:

?第一步 :??開啟 Colab 設(shè)定環(huán)境

%tensorflow_version 1.x
!python -c 'import matplotlib as tf; print(tf.__version__)' # Check the version of the tensorflow

※ 由於 colab 已暫停支援 Tensorflow 1.x 的方式,請(qǐng)本機(jī)方式實(shí)現(xiàn)。

第二步 :? TensorFlow Model Garden 下載與安裝

%cd root
!git clone https://github.com/tensorflow/models.git
%cd root/models/research/
!protoc object_detection/protos/*.proto --python_out=. # gernate *.proto
!python setup.py build # 建置 TensorFlow Model Garden 檔案

第三步 :? TensorFlow? Slim 下載與安裝

import os
os.environ['PYTHONPATH'] += ':/root/models/research/:/root/models/research/slim/:/root/models/research/object_detection/utils/:/root/models/research/object_detection'
!pip install tf_slim # 安裝 TensorFlow Slim
!python object_detection/builders/model_builder_test.py # TensorFlow Slim 模組建立是否成功測試

第四步 :??下載資料庫

常見的物件識(shí)別的資料庫為 COCO DataSets

%cd /root/models/
!git clone https://github.com/fllay/totoro.git #Download TOTORO

第五步 :??數(shù)據(jù)特徵處理

此步驟須將事先把物件的位置特徵與分類資訊紀(jì)錄於 xml 之中,如下圖所示與參考 GitHub 網(wǎng)站。

import os
import glob
import pandas as pd
import xml.etree.ElementTree as ET

# 將 xml 檔資料轉(zhuǎn)換成 DataFrame 形式
def xml_to_csv(path):
xml_list = []
for xml_file in glob.glob(path + '/*.xml'):
tree = ET.parse(xml_file)
root = tree.getroot()
for member in root.findall('object'):
value = (root.find('filename').text,
int(root.find('size')[0].text),
int(root.find('size')[1].text),
member[0].text,
int(member[4][0].text),
int(member[4][1].text),
int(member[4][2].text),
int(member[4][3].text)
)
xml_list.append(value)
column_name = ['filename', 'width', 'height', 'class', 'xmin', 'ymin', 'xmax', 'ymax']
xml_df = pd.DataFrame(xml_list, columns=column_name)
return xml_df

# 將 xml 資料轉(zhuǎn)換成 train_labels.csv 與 test_labels.csv 兩個(gè)檔案
def main():
image_path = os.path.join(os.getcwd(), 'totoro/images/train')
xml_df = xml_to_csv(image_path)
xml_df.to_csv('totoro/data/train_labels.csv', index=None)
image_path = os.path.join(os.getcwd(), 'totoro/images/test')
xml_df = xml_to_csv(image_path)
xml_df.to_csv('totoro/data/test_labels.csv',index=None)

main()

第六步 :? 製作 TensorFlow Record

%cd /root/models/totoro/tfrecord
!python generate_tfrecord.py --csv_input=/root/models/totoro/data/train_labels.csv \
--output_path=train.record --image_dir=/root/models/totoro/images/train
!python generate_tfrecord.py --csv_input=/root/models/totoro/data/test_labels.csv\
--output_path=test.record --image_dir=/root/models/totoro/images/test

第七步 :??下載訓(xùn)練過的 MobileNet 模組

此步驟利用之前訓(xùn)練過的模組資源重新訓(xùn)練,即 遷移學(xué)習(xí)(Transfer Learning) 的技術(shù)。


%cd ~/models
import shutil
import tarfile
from requests import get
MODEL = 'ssd_mobilenet_v1_coco_2017_11_17'
MODEL_FILE = MODEL + '.tar.gz'
DOWNLOAD_BASE = 'http://download.tensorflow.org/models/object_detection/'
DEST_DIR = 'pretrained_model'
# 下載mobilenet 模組
if not (os.path.exists(MODEL_FILE)):
with open(MODEL_FILE, "wb") as file:
response = get(DOWNLOAD_BASE + MODEL_FILE)
file.write(response.content)

# 解壓縮 mobilenet 模組
tar = tarfile.open(MODEL_FILE)
tar.extractall()
tar.close()
os.remove(MODEL_FILE)
if (os.path.exists(DEST_DIR)):
shutil.rmtree(DEST_DIR)
os.rename(MODEL, DEST_DIR)

# 移動(dòng) mobilenet.config" 資訊
shutil.move( "/root/models/research/object_detection/samples/configs/ssd_mobilenet_v1_coco.config", "/root/models" )

第八步 :??修改 Config 檔案

%cd /root/models/research/
# 編輯Pipeline 資訊
import tensorflow as tf
from google.protobuf import text_format
from object_detection.protos import pipeline_pb2
pipeline = pipeline_pb2.TrainEvalPipelineConfig()
config_path = '/root/models/ssd_mobilenet_v1_coco.config'
with tf.gfile.GFile( config_path, "r") as f:
proto_str = f.read()
text_format.Merge(proto_str, pipeline)
pipeline.train_input_reader.tf_record_input_reader.input_path[:] = ['/root/models/totoro/tfrecord/train.record'] # train data
pipeline.train_input_reader.label_map_path = '/root/models/totoro/data/object-detection.pbtxt'
pipeline.eval_input_reader[0].tf_record_input_reader.input_path[:] = ['/root/models/totoro/tfrecord/test.record'] # test data
pipeline.eval_input_reader[0].label_map_path = '/root/models/totoro/data/object-detection.pbtxt' # network
pipeline.train_config.fine_tune_checkpoint = '/root/models/pretrained_model/model.ckpt' # weight
pipeline.train_config.num_steps = 500 # training step
pipeline.model.ssd.num_classes = 2 # classes num
pipeline.eval_config.num_examples = 5 # test image number
config_text = text_format.MessageToString(pipeline)
with tf.gfile.Open( config_path, "wb") as f:
f.write(config_text)

第九步 :??進(jìn)行訓(xùn)練

!python /root/models/research/object_detection/legacy/train.py \
--logtostderr \
--train_dir=/root/models/trained \
--pipeline_config_path=/root/models/ssd_mobilenet_v1_coco.config

※ 訓(xùn)練完成後,將於 models/trained/ 資料夾內(nèi)產(chǎn)出 model.ckpt-500 檔案

第十步 :??產(chǎn)生 Frozen Graph

此步驟可以調(diào)整模組輸出大小,比如說將原本輸入大小 224x224 改成 96x96 。

!python /root/models/research/object_detection/export_tflite_ssd_graph.py \
--pipeline_config_path=/root/models/ssd_mobilenet_v1_coco.config \
--output_directory=/root/models/fine_tuned_model \
--trained_checkpoint_prefix=/root/models/trained/model.ckpt-500

※ 訓(xùn)練完成後,將於 models/fine_tuned_model / 資料夾內(nèi)產(chǎn)出 tflite_graph.pb檔案

第十一步 :? TensorFlow Lite 轉(zhuǎn)換

# 此處以指令方式進(jìn)行轉(zhuǎn)換,亦可使用上述文章所介紹代碼方式。
! tflite_convert \
--output_file=/root/models/fine_tuned_model/ mobilenetssd_uint8.tflite \
--graph_def_file=/root/models/fine_tuned_model/tflite_graph.pb \
--inference_type=QUANTIZED_UINT8 \
--input_arrays=normalized_input_image_tensor \
--input_shapes=1,300,300,3 \
--output_arrays= 'TFLite_Detection_PostProcess','TFLite_Detection_PostProcess:1','TFLite_Detection_PostProcess:2','TFLite_Detection_PostProcess:3’ \
--default_ranges_min=0 \
--default_ranges_max=6 \
--mean_values=128 \
--std_dev_values=127 \
--allow_custom_ops

※ 訓(xùn)練完成後,將於 models/fine_tuned_model / 資料夾內(nèi)產(chǎn)出 mobilenetssd_uint8.tflite檔案

第十二步 :? Object Detection 範(fàn)例實(shí)現(xiàn) ( i.MX8M Plus 撰寫運(yùn)行)

import cv2
import numpy as np
from tflite_runtime.interpreter import Interpreter
# 解析 tensorflow lite 檔案
interpreter = Interpreter(model_path='mobilenetssd_uint8.tflite') # 記得將模組移動(dòng)至 i.MX8 平臺(tái)
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
width = input_details[0]['shape'][2]
height = input_details[0]['shape'][1]
# 讀取測試資料,並設(shè)置於解譯器中
frame = cv2.imread('/root/models/totoro/images/test/image1.jpg')
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame_resized = cv2.resize(frame_rgb, (width, height))
input_data = np.expand_dims(frame_resized, axis=0)
interpreter.set_tensor(input_details[0]['index'], input_data)
# 進(jìn)行推理
interpreter.invoke()
# 取得輸出資料
detection_boxes = interpreter.get_tensor(output_details[0]['index']) # 輸出位置資訊
detection_classes = interpreter.get_tensor(output_details[1]['index']) # 輸出類別資訊
detection_scores = interpreter.get_tensor(output_details[2]['index']) # 輸出分?jǐn)?shù)資訊
num_boxes = interpreter.get_tensor(output_details[3]['index'])
# 標(biāo)示物件
for i in range(10):
if detection_scores[0, i] > .5: # 預(yù)測值大於 0.5則顯示
x = detection_boxes[0, i, [1, 3]] * frame_rgb.shape[1]
y = detection_boxes[0, i, [0, 2]] * frame_rgb.shape[0]
class_id = detection_classes[0, i]
cv2.rectangle(frame_rgb, (x[0], y[0]), (x[1], y[1]), (0, 255, 0), 2)

cv2.imshow('TOTORO',frame_rgb)
cv2.waitKey(0)
cv2.destroyAllWindows()

實(shí)現(xiàn)結(jié)果 :

如下圖所示,成功檢測出豆豆龍(物件)。
在 i.MX8M Plus 的 NPU 處理器,推理時(shí)間(Inference Time) 約 9 ms。

?

四.? 結(jié)語

物件偵測是目前深度學(xué)習(xí)的一套基礎(chǔ)應(yīng)用,現(xiàn)在主流的算法架構(gòu)多數(shù)為 YOLO 系列為主,並已發(fā)展到第七、八代的模組框架。而 MobileNet-SSD 的架構(gòu)在準(zhǔn)確度略輸於 YOLO 架構(gòu),但仍是輕量化的速度表現(xiàn)上的一個(gè)標(biāo)竿指標(biāo)。
在 i.MX8MP 的 Vivante VIP8000 NPU 運(yùn)行,其推理時(shí)間可達(dá)每秒 8-9 ms 的處理速度,約 125 張 FPS 。此外,搭配本篇做法以及相應(yīng)的資料庫,就能訓(xùn)練出各式各樣的物件偵測的應(yīng)用,像是人臉偵測、手部偵測、水果偵測等等都是以這個(gè)概念!!
下篇將結(jié)合人臉資料庫來實(shí)現(xiàn)所謂的 人臉偵測 (Face Detection),敬請(qǐng)期待 !!

五.??參考文件

[1] SSD: Single Shot MultiBox Detector
[2] SSD-Tensorflow

[3] Single Shot MultiBox Detector (SSD) 論文閱讀
[4]?ssd-mobilenet v1 演算法結(jié)構(gòu)及程式碼介紹

[5] Get models for TensorFlow Lite
[6]?totoro example
[7]
MobileNets: Efficient Convolutional Neural Networks for Mobile Vision

如有任何相關(guān)?TensorFlow Lite?進(jìn)階技術(shù)問題,歡迎至博文底下留言提問?!!
接下來還會(huì)分享更多?TensorFlow Lite?進(jìn)階文章?!!敬請(qǐng)期待?【ATU Book-i.MX8系列 – TFLite 進(jìn)階
?!!

★博文內(nèi)容均由個(gè)人提供,與平臺(tái)無關(guān),如有違法或侵權(quán),請(qǐng)與網(wǎng)站管理員聯(lián)繫。

★文明上網(wǎng),請(qǐng)理性發(fā)言。內(nèi)容一周內(nèi)被舉報(bào)5次,發(fā)文人進(jìn)小黑屋喔~

參考來源

評(píng)論