上一篇咱們在樹莓派上安裝了OpenVINO的環境,並跑了幾個官方demo,做爲關鍵點的模型轉換工做,以各個版本的yolo實現爲例,在這篇作一下實現。
目標檢測是人工智能應用比較成熟的領域,不只要可以識別出圖片的目標,還要定位其位置,在自動駕駛方面會是一個基礎的場景。通常分爲兩大類別,一類是two-stage的,基於R-CNN,Fast R-CNN, Faster R-CNN等等,先生成待選框(Region Proposal),再進行分類獲取類別,迴歸獲取位置;另外一類就是Yolo,SSD這種one-stage算法,直接用CNN網絡獲取目標和位置。html
相對來講,R-CNN系的精度更高,但速度慢,Yolo系的則速度快,準確率低些。在不少CV領域裏,只要保證分類的準確率,檢測速度比定位精度重要的多,而one-stage的模型部署有着自然的優點,極大的減輕算力有限的邊緣設備的計算壓力。python
Yolo則是目標檢測類優化速度的表明,而轉換爲Openvino後,速度還能更進一步。這篇主要介紹兩種主流框架 Tensorflow 和 Pytorch 的模型轉換到 Openvino的方法。linux
首先,Tensorflow 模型的轉化流程是,先將權重文件.weight 轉換成靜態圖 .pb文件,再轉化成 IR 模型的 .bin 和 .xml,最後部署到神經棒運行。咱們先來跑一個yolov4-tiny的應用來體驗一下。git
git clone https://github.com/TNTWEN/OpenVINO-YOLOV4.git cd OpenVINO-YOLOV4
下載 yolov4.weight 和 yolov4-tiny.weight 放入該目錄下github
python convert_weights_pb.py --class_names cfg/coco.names --weights_file yolov4-tiny.weights --data_format NHWC --tiny
目錄下有輸出 frozen_darknet_yolov4_model.pb 就轉化成功了。算法
Tip:其中必須指定數據格式爲NHWC,以匹配 intel NCS2 上對應的格式。shell
Tip:如遇到有 ‘cloud’ 導入錯誤的信息,那是因爲編譯 TF 時開啓了 --nogcp 標誌,致使 tensorflow/contrib/cloud 沒有被加入 pip 的安裝包。這裏只要將 init 裏的兩行代碼註釋掉便可修復這個bug了。其中必須指定數據格式爲NHWC,因此這裏須要 reverse_input_channels 翻轉一下對應的輸入通道。json
/home/pi/my_envs/tensorflow/lib/python3.7/site-packages/tensorflow/contrib/__init__.py
要在Windows 或 linux主機上作轉換,須要安裝 OpenVINO 開發工具套件。ubuntu
"C:\Program Files (x86)\IntelSWTools\openvino\bin\setupvars.bat"
切換到 OpenVINO-YOLOV4 目錄下,將 pb 文件 轉化爲 xml 和 bin 文件。網絡
python "C:\Program Files (x86)\IntelSWTools\openvino_2020.4.287\deployment_tools\model_optimizer\mo.py" --input_model frozen_darknet_yolov4_model.pb --transformations_config yolo_v4_tiny.json --batch 1 --reverse_input_channels
Tip:轉換 IR 模型前必定要注意 op 算子的兼容性,以及對應平臺的數據精度。 如下這個頁面能夠查詢到具體信息,不少模型轉化失敗是因爲尚未獲得支持。
https://docs.openvinotoolkit.org/latest/openvino_docs_IE_DG_supported_plugins_Supported_Devices.html
在樹莓派上運行模型,FPS 穩定在 6-7 幀左右
source ~/my_envs/tensorflow/bin/activate /opt/intel/openvino/bin/setupvars.sh python object_detection_demo_yolov4_async.py -i cam -m frozen_darknet_yolov4_model.xml -d MYRIAD
Yolov5 主要引入了馬賽克加強,自適應錨框,這些新特性,結構上與 Yolov4 的差別不大,不過v5的開源版本是 pytorch 的,相對 darknet 來講更容易轉化到各個平臺上部署些。
工做流:
另外一種主流框架 Pytorch 若要用 openvino 作優化的話,那其轉換流程爲,先將 pytorch 的模型文件 .pt,轉化爲 onnx 格式,而後再轉化成 IR 模型,便可部署到神經棒上了。
接下來咱們用最新版的 Yolov5 來跑一下這個過程。
下載安裝包 2020年4月的 linux 版本
cd ~/Downloads/ wget http://registrationcenter-download.intel.com/akdlm/irc_nas/16803/l_openvino_toolkit_p_2020.4.287.tgz tar -xvzf l_openvino_toolkit_p_2020.4.287.tgz
pip3 install defusedxml pip3 install networkx pip3 install test-generator==0.1.1 # 這裏咱們只須要轉換 onnx cd l_openvino_toolkit_p_2020.4.287 sudo ./install_prerequisites_onnx.sh
Python>=3.8,PyTorch==1.5.1,ONNX>=1.7。
conda activate openvino # 進入ubuntu 的虛擬環境 git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip3 install -r requirements.txt onnx # 降版本 pip install torch==1.5.1 torchvision==0.6.1
先下載 yolov5s.pt(或者訓練自有數據集的yolov5模型),放入目錄下
wget https://github.com/ultralytics/yolov5/releases/download/v2.0/yolov5s.pt mv yolov5s.pt yolov5s_2.0.pt
Tip:
要下載 v2.0 with nn.LeakyReLU(0.1) 的版本,由於 3.0 的 nn.Hardswish 尚未被支持。
因爲onnx和openvino 還不支持 Hardswitch,要將 Hardswish 激活函數改爲 Relu 或者 Leaky Relu。
# yolov5/models/common.py # Line 26 in 5e0b90d # self.act = nn.Hardswish() if act else nn.Identity() self.act = nn.Relu() if act else nn.Identity()
# yolov5/models/yolo.py # Lines 49 to 53 in 5e0b90d # y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i] # xy # y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh # z.append(y.view(bs, -1, self.no)) # # return x if self.training else (torch.cat(z, 1), x)
修改輸出層堆疊,不包含輸入層
c=(y[..., 0:2] * 2. - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i] # xy d=(y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh e=y[..., 4:] f=torch.cat((c,d,e),4) z.append(f.view(bs, -1, self.no)) return x if self.training else torch.cat(z, 1)
# yolov5/models/export.py # Line 31 in 5e0b90d # model.model[-1].export = True # set Detect() layer export=True model.model[-1].export = False
由於版本爲10的 opset 能支持 resize 算子,要修改 opset 版本號。
# yolov5/models/export.py # Lines 51 to 52 in 5e0b90d # torch.onnx.export(model, img, f, verbose=False, opset_version=12, input_names=['images'], torch.onnx.export(model, img, f, verbose=False, opset_version=10, input_names=['images'], output_names=['classes', 'boxes'] if y is None else ['output'])
Tip:
務必確保 torch==1.15.1,torchvision==0.6.1,onnx==1.7,opset=10。激活函數爲 Relu,並修改了網絡推理層。
export PYTHONPATH="$PWD" python models/export.py --weights yolov5s_2.0.pt --img 640 --batch 1
顯示導出爲 onnx 和 torchscript 文件便可。
ONNX export success, saved as ./yolov5s.onnx Export complete. Visualize with https://github.com/lutzroeder/netron.
python3 /opt/intel/openvino_2020.4.287/deployment_tools/model_optimizer/mo.py --input_model yolov5s_2.0.onnx --output_dir ./out --input_shape [1,3,640,640]
順利的話,就能在 out 目錄下生成 yolov5s 的 IR 模型了,接着將文件傳輸到樹莓派上。
Tip:
這裏要匹配 yolov5s 的 input shape 爲 [ 1, 3, 640, 640 ]。
git clone https://github.com/linhaoqi027/yolov5_openvino_sdk.git
修改推理設備和輸入 shape
# device = 'CPU' # input_h, input_w, input_c, input_n = (480, 480, 3, 1) device = 'MYRIAD' input_h, input_w, input_c, input_n = (640, 640, 3, 1)
修改類別信息
# label_id_map = { # 0: "fire", # } names=['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush'] label_id_map = {index: item for index, item in enumerate(names)}
修改多類別輸出
for idx, proposal in enumerate(data): if proposal[4] > 0: print(proposal) confidence = proposal[4] xmin = np.int(iw * (proposal[0] / 640)) ymin = np.int(ih * (proposal[1] / 640)) xmax = np.int(iw * (proposal[2] / 640)) ymax = np.int(ih * (proposal[3] / 640)) idx = int(proposal[5]) #if label not in label_id_map: # log.warning(f'{label} does not in {label_id_map}') # continue detect_objs.append({ 'name': label_id_map[idx], 'xmin': int(xmin), 'ymin': int(ymin), 'xmax': int(xmax), 'ymax': int(ymax), 'confidence': float(confidence) })
if __name__ == '__main__': # Test API img = cv2.imread('../inference/images/bus.jpg') predictor = init() import time t = time.time() n = 10 for i in range(n): result = process_image(predictor, img) print("平均推理時間",(time.time()-t)/n) print("FPS", 1/((time.time()-t)/n)) # log.info(result) for obj in json.loads(result)['objects']: print(obj)
python inference.py
yolov5s 的 FPS 才 2 幀左右,相比 yolov4-tiny 來講速度不算快,input shape 640 比 yolov4 的416大了很多,主要耗時集中在神經棒的推理之中,需耗費 377ms,仍是蠻可觀的。
另外,轉換後的 IR 模型在 CPU 上和 MYRIAD 上推理的置信度差別也很高,看來 yolov5 還有很大的優化空間。
要追求極致的檢測速度,還有幾個方向能夠嘗試:
特別是最近優圖開源了一個yolo-fastest版本,backbone爲EfficientNet-lite 使得訓練的模型權重才1.2M,yolo-fastest-xl 也才3.3M,很是的小巧。
這個 MobileNet SSD 居然在樹莓派4 + 5個 NCS2 上跑到了 FPS 40以上,Amazing!!!
這篇主要介紹了 Tensorflow 和 Pytorch 兩種主流框架的轉換到 OpenVINO模型的方式。
其實還有更多AI框架,大體也是經過 pb,onnx,ir,這類中間模型來相互轉化,好比應用於 TensorRT,Tensorflow Lite 等等。
通常只要瞭解,輸入輸出層的結構,每種模型對應的權重和網絡結構文件,注意op算子的支持,就能用框架提供的 pipeline 來作轉換了。
最後的手段,就是重寫網絡拓撲,用相似 load_weight 的方法來導入權重,再 save 到目標框架中了。
本期相關文件資料,可在公衆號後臺回覆:「rpi09」,獲取下載連接。
咱們將介紹樹莓派上的代理軟件,
部署Openwrt軟路由,
打造一勞永逸的上網環境,
敬請期待...