用樹莓派4b構建深度學習應用(九)Yolo篇

前言

上一篇咱們在樹莓派上安裝了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

Yolov4 應用

1. 下載源碼

git clone https://github.com/TNTWEN/OpenVINO-YOLOV4.git
cd OpenVINO-YOLOV4

2. 將 weight --> pb

下載 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

3. 初始化openvino環境

要在Windows 或 linux主機上作轉換,須要安裝 OpenVINO 開發工具套件。ubuntu

ubuntu01

"C:\Program Files (x86)\IntelSWTools\openvino\bin\setupvars.bat"

4. 將 pb --> ir

切換到 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

5. 運行模型

在樹莓派上運行模型,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 應用

Yolov5 主要引入了馬賽克加強,自適應錨框,這些新特性,結構上與 Yolov4 的差別不大,不過v5的開源版本是 pytorch 的,相對 darknet 來講更容易轉化到各個平臺上部署些。

工做流:

另外一種主流框架 Pytorch 若要用 openvino 作優化的話,那其轉換流程爲,先將 pytorch 的模型文件 .pt,轉化爲 onnx 格式,而後再轉化成 IR 模型,便可部署到神經棒上了。

接下來咱們用最新版的 Yolov5 來跑一下這個過程。

1. 在 Ubuntu 上安裝 OpenVINO 開發工具

下載安裝包 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

2. 安裝依賴

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

3. 創建虛擬環境

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

4. 導出在 pytorch 上訓練好的 yolov5 模型

先下載 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 尚未被支持。

5. 修改激活函數

因爲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()

6. 修改 yolo.py

# 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)

7. 修改 export.py

# 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,並修改了網絡推理層。

8. 將 pt --> onnx

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.

9. 將 onnx --> ir

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 ]。

10. 修改參數匹配訓練模型

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)
        })

11. 推理輸出

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 還有很大的優化空間。

要追求極致的檢測速度,還有幾個方向能夠嘗試:

  • 多個神經棒分佈式推理;
  • 使用多線程推理;
  • 採用異步方式刷新屏幕;
  • 選用更小的模型;
  • 推理代碼由 Python 改成 C++

特別是最近優圖開源了一個yolo-fastest版本,backbone爲EfficientNet-lite 使得訓練的模型權重才1.2M,yolo-fastest-xl 也才3.3M,很是的小巧。

這個 MobileNet SSD 居然在樹莓派4 + 5個 NCS2 上跑到了 FPS 40以上,Amazing!!!

這篇主要介紹了 Tensorflow 和 Pytorch 兩種主流框架的轉換到 OpenVINO模型的方式。

  • .weight --> .pb --> .xml .bin
  • .pt --> onnx --> .xml .bin

其實還有更多AI框架,大體也是經過 pb,onnx,ir,這類中間模型來相互轉化,好比應用於 TensorRT,Tensorflow Lite 等等。

通常只要瞭解,輸入輸出層的結構,每種模型對應的權重和網絡結構文件,注意op算子的支持,就能用框架提供的 pipeline 來作轉換了。

最後的手段,就是重寫網絡拓撲,用相似 load_weight 的方法來導入權重,再 save 到目標框架中了。

代碼下載

本期相關文件資料,可在公衆號後臺回覆:「rpi09」,獲取下載連接。


下一篇預告

咱們將介紹樹莓派上的代理軟件,
部署Openwrt軟路由,
打造一勞永逸的上網環境,
敬請期待...

歡迎掃碼關注,更多分享

相關文章
相關標籤/搜索