Google Coral USB Accelerator開箱

Centralized and Edged AI

一提到AI,我們腦海中總會浮現出火星文般的複雜演算法、深不可測的神經網路、數量龐大到以TB計數的dataset、以及高速運算耗電量驚人的GPU…等等,它們可能存在於雲端甚至就在自家機房內部。但是,就算它們的演算法再精確、GPU運算更迅速、網路傳輸再加快,還是無法滿足一般使用者最基本的需求:即時回饋,因此更貼近使用者無延遲的Edge AI近年來大量興起,前述龐大笨重的AI系統則穩身幕後稱為Centralized AI。

image001圖片來源:https://www.linkedin.com/pulse/ai-5g-wireless-network-edge-berge-ayvazian/

如上圖左側,Edge AI佈署於使用者終端,專用於推論及決策等動作,可滿足低延遲快速回應的AI需求,例如Robots、Drones、Portable or Mobile Devices、Outdoor Devices…等等,Edge AI與Centralized AI兩者組成Distributed AI的架構。

Google Coral USB Accelerator

目前市面可滿足Edge AI的硬體裝置的選擇相當多,大致可分為可單獨運行的單版AI board以及USB接口無法獨立運行的AI加速器,今天要開箱的是一片可以透過USB與樹莓派搭配的AI加速器:Google Coral USB Accelerator,它的外形與功能,很容易讓我們聯想到Intel的Neural Computing Stick, NCS。這兩種都屬於針對深度學習運算所特別設計的ASIC IC(Application Specific Integrated Circuit),它們非常適合平行的處理大量加乘運算,也由於是專門使用於特定用途,因此它們只支援有限的深度學習框架,並且必須先將模型轉換為特定的中介格式後才能運作。

NCS使用的中介檔稱為IR(Intermediate Representation),支援TensorFlow、Caffe、MXNet、ONNX、Darknet…等模型轉換為IR(Intermediate Representation)的中介檔格式,且還支援OpenCV的DNN模組。而Google Edge TPU目前則僅支援自家的Tensorflow Lite格式,且是Edge TPU專用的tflite-tpu,因此使用前還需將TF Lite模型轉檔後才能使用。

image002

開箱

Google Coral USB Accelerator尺寸與重量比想像中的更小更輕,相較於部置在雲端能夠提供訓練與推論的Cloud TPU,這塊Coral USB Accelerator則屬於Edge AI的一環,稱為Edge TPU。

推論時間比較

這片小巧的加速器能提昇多少深度模型的推論速度呢?下面統計資料來自官方網站,由左至右:藍色為使用一般PC(CPU)、橘色為PC加上Edge TPU、灰色為樹莓派3、黑色為樹莓派加Edge TPU,數值代表模型的推論時間,因此愈小愈好,由圖表中可看出樹莓派加上Edge TPU之後可說是如虎添翼,比PC的CPU快上數十倍,甚至與CPU+Edge TPU並駕齊驅。可惜樹莓派沒有支援USB3.0,否則速度應該會更令人滿意。

image005

image006

Install the Edge TPU runtime and Python library

接下來,讓我們把Google Coral USB Accelerator 接上樹莓派。

image007

A)  下載Edge TPU API

cd ~/

wget https://dl.google.com/coral/edgetpu_api/edgetpu_api_latest.tar.gz -O edgetpu_api.tar.gz –trust-server-names

tar -xvf edgetpu_api.tar.gz

B)  安裝Edge TPU API

cd edgetpu_api

bash ./install.sh

下方為安裝的畫面,未來如果想要更改設定值,可以重複執行此安裝的步驟。

Would you like to enable the maximum operating frequency?

如果輸入Y,那麼Edge TPU在推論時會全力運作加快速度,但需注意在這種情況下可能會產生高熱。

image008

安裝的過程相當簡單且順利,目前的版本為edgetpu-1.9.2。

image009

C)  重新插入

安裝好API之後,如果Accelerator已經接在USB上,那麼建議再重新插拔一次,讓udev rule生效。接下來,便可以開始實作下面的demo測試了。

D)  Demo範例實作

D.1)  Image classification

首先試試最基本的影像分類模型,請執行下方scripts來下載預訓練好的模型及示範圖片。

cd ~/Downloads/
wget https://dl.google.com/coral/canned_models/mobilenet_v2_1.0_224_inat_bird_quant_edgetpu.tflite \
https://dl.google.com/coral/canned_models/inat_bird_labels.txt \
https://dcoral.withgoogle.com/static/docs/images/parrot.jpg

下載完成後,進入demo目錄執行classify_image.py程式。

cd /usr/local/lib/python3.5/dist-packages/edgetpu/demo
python3 classify_image.py \
--model ~/Downloads/mobilenet_v2_1.0_224_inat_bird_quant_edgetpu.tflite \
--label ~/Downloads/inat_bird_labels.txt \
--image ~/Downloads/parrot.jpg

但是卻出現下發的error:

Traceback (most recent call last):
  File "classify_image.py", line 19, in <module>
    from edgetpu.classification.engine import ClassificationEngine
ImportError: No module named 'edgetpu'

原因是,edgetpu API預設是安裝到/usr/local/lib/python3.5/dist-packages,但如果你的Python環境是使用virtualenv,那麼需要多執行下列的安裝步驟:

cd /home/pi/envAI/lib/python3.5/site-packages/
ln -s /usr/local/lib/python3.5/dist-packages/edgetpu
ln -s /usr/local/lib/python3.5/dist-packages/edgetpu-1.9.2.dist-info

如此一來,這個範例便可順利完成了。此為待分類的示範圖片鸚鵡,範例程式預測其為Ara macao,機率為0.76。

D.2)  Object detection範例

這個範例是使用SSD MobileNet V2所訓練的臉孔偵測模型。一樣先下載預訓練模型及示範圖片。

cd ~/Downloads
curl -O https://dl.google.com/coral/canned_models/mobilenet_ssd_v2_face_quant_postprocess_edgetpu.tflite \
-O https://coral.withgoogle.com/static/docs/images/face.jpg

接著執行demo目錄中的object_detection.py程式。

cd /usr/local/lib/python3.5/dist-packages/edgetpu/demo
python3 object_detection.py \
--model ~/Downloads/mobilenet_ssd_v2_face_quant_postprocess_edgetpu.tflite \
--input ~/Downloads/face.jpg \
--output ~/detection_results.jpg

結果又出現下方的error:

Traceback (most recent call last):
  File "object_detection.py", line 110, in <module>
    main()
  File "object_detection.py", line 103, in main
    subprocess.Popen(['feh', output_name])
  File "/usr/lib/python3.5/subprocess.py", line 676, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.5/subprocess.py", line 1282, in _execute_child
    raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'feh'

原來是範例是使用feh這個相片模組來顯示圖片,因此馬上手動來安裝feh:

sudo apt-get install feh

(ps. feh這支image viewer程式還挺好用的,不但可透過command line執行,也支援Python,此外還有各種花俏的顯示功能,如多圖顯示以及桌布設定等等。)

執行結果:相片中的四張人臉都抓到了,而且我們可以得到各個臉部的bbox及score。

image012

D.3) Image classification with PICamera

這是影像分類結合樹莓派PI Camera的範例,使用MobileNet V2訓練,可分類超過1000種物品,一樣先下載模型。

cd ~/Downloads
curl -O https://dl.google.com/coral/canned_models/mobilenet_v2_1.0_224_quant_edgetpu.tflite \
-O https://dl.google.com/coral/canned_models/imagenet_labels.txt

接著執行demo目錄的classify_capture.py

cd /usr/local/lib/python3.5/dist-packages/edgetpu/demo
python3 classify_capture.py \
--model ~/Downloads/mobilenet_v2_1.0_224_quant_edgetpu.tflite \
--label ~/Downloads/imagenet_labels.txt

D.4) Face detection with webcam

接下來,我們修改一下前面的臉孔偵測範例,改為使用webcam來輸入影像進行即時的偵測,並觀察其FPS數值。

  1. 下載預訓練模型(與前面的臉孔偵測模型相同)
cd ~/Downloads
curl -O https://dl.google.com/coral/canned_models/mobilenet_ssd_v2_face_quant_postprocess_edgetpu.tflite \
-O https://coral.withgoogle.com/static/docs/images/face.jpg

2. 即時的臉孔偵測程式(修改自範本object_detection.py)

import argparse
from edgetpu.detection.engine import DetectionEngine
from PIL import Image
import cv2
import time, sys

def fps_count(total_frames):
  global last_time, last_frames, fps
  timenow = time.time()

  if(timenow - last_time)>10:
    fps  = (total_frames - last_frames) / (timenow - last_time)
    last_time  = timenow
    last_frames = total_frames

  return fps


def main():

  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--model', help='Path of the detection model.', required=True)
  args = parser.parse_args()

  engine = DetectionEngine(args.model)
  frameid = 0
  fps_rate = 0

  while(camera.isOpened()):
    (grabbed, frame) = camera.read()
    frame = cv2.flip(frame, 1)
    frame= cv2.resize(frame,(800,480))
    img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))

    # Run inference.
    ans = engine.DetectWithImage(img, threshold=0.4, keep_aspect_ratio=True,

                                 relative_coord=False, top_k=10)

    # Display result.
    if ans:
      for obj in ans:
        box = obj.bounding_box.flatten().tolist()
        x1,y1,x2,y2 = int(box[0]), int(box[1]), int(box[2]), int(box[3])
        lblTXT = "face({}%)".format(int(float(obj.score)*100))
        cv2.rectangle( frame,(x1,y1),(x2,y2),(0,255,0),2)
        cv2.putText( frame, lblTXT,(x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8,(0, 0, 255), 2)
        fps_rate = fps_count(frameid)

    else:
      print ('No object detected!')

    cv2.putText( frame, "FPS:{}".format(round(fps_rate,1)),(10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1.0,(255, 0, 0), 2)
    frameid += 1

    cv2.imshow("FRAME", frame)
    key = cv2.waitKey(1)

    if(key==113):
      sys.exit(0)

if __name__ == '__main__':
  fps = 0
  fps_rate = 0
  start = time.time()
  last_time = time.time()
  last_frames = 0

  camera = cv2.VideoCapture(0)
  cv2.namedWindow("FRAME", cv2.WND_PROP_FULLSCREEN)
  cv2.setWindowProperty("FRAME", cv2.WND_PROP_FULLSCREEN,cv2.WINDOW_FULLSCREEN)

  main()

3. 執行:

python3 face_detection_webcam.py --model mobilenet_ssd_v2_face_quant_postprocess_edgetpu.tflite

D.5) Object detection with webcam

接著一樣修改前面的物件偵測範例,改為使用webcam來輸入影像進行即時的偵測,並觀察其FPS數值。

  1. 下載預訓練模型
curl -O http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_quantized_300x300_coco_2019_01_03.tar.gz
  1. 解壓
tar –xvf object_detection/ssd_mobilenet_v2_quantized_300x300_coco_2019_01_03.tar.gz
  1. 即時的物件偵測程式 (修改自範本py)
import argparse
from edgetpu.detection.engine import DetectionEngine
from PIL import Image
import cv2
import time, sys

def ReadLabelFile(file_path):
  with open(file_path, 'r', encoding="utf-8") as f:
    lines = f.readlines()

  ret = {}
  for line in lines:
    pair = line.strip().split(maxsplit=1)
    ret[int(pair[0])] = pair[1].strip()

  return ret

def fps_count(total_frames):
  global last_time, last_frames, fps
  timenow = time.time()

  if(timenow - last_time)>10:
    fps  = (total_frames - last_frames) / (timenow - last_time)
    last_time  = timenow
    last_frames = total_frames

  return fps

def main():
  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--model', help='Path of the detection model.', required=True)
  parser.add_argument(
      '--label', help='Path of the labels file.')
  args = parser.parse_args()

  engine = DetectionEngine(args.model)
  labels = ReadLabelFile(args.label) if args.label else None
  frameid = 0

  while(camera.isOpened()):
    (grabbed, frame) = camera.read()
    frame = cv2.flip(frame, 1)
    frame= cv2.resize(frame,(800,480))
    img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))

    ans = engine.DetectWithImage(img, threshold=0.4, keep_aspect_ratio=True,
                                 relative_coord=False, top_k=10)

    if ans:
      for obj in ans:
        box = obj.bounding_box.flatten().tolist()
        x1,y1,x2,y2 = int(box[0]),int(box[1]),int(box[2]),int(box[3])
        lblTXT = "{}({}%)".format(labels[obj.label_id], int(float(obj.score)*100))
        cv2.rectangle( frame,(x1,y1),(x2,y2),(0,255,0),2)
        cv2.putText( frame, lblTXT,(x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8,(0, 0, 255), 2)
        fps_rate = fps_count(frameid)

    else:
      print ('No object detected!')

    cv2.putText( frame, "FPS:{}".format(round(fps_rate,1)),(10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1.0,(255, 0, 0), 2)
    frameid += 1
    cv2.imshow("FRAME", frame)

    key = cv2.waitKey(1)
    if(key==113):
      sys.exit(0)

if __name__ == '__main__':
  fps = 0
  fps_rate = 0
  start = time.time()
  last_time = time.time()
  last_frames = 0

  camera = cv2.VideoCapture(0)
  cv2.namedWindow("FRAME", cv2.WND_PROP_FULLSCREEN)
  cv2.setWindowProperty("FRAME", cv2.WND_PROP_FULLSCREEN,cv2.WINDOW_FULLSCREEN)

  main()

3. 執行:

python3 object_detection_webcam.py --model mobilenet_ssd_v2_coco_quant_postprocess_edgetpu.tflite  --label coco_labels.txt

使用webcam,FPS約在5~6左右。

使用720P, 30fps的影片測試,FPS約3.8~4.1左右。

與Intel Neural Computing Stick比較

我們分別將Google Coral USB Accelerator以及Intel Neural Computing Stick 2接樹莓派上,撥放800×400的影片並同樣使用COCO dataset(可分辨80種物件)訓練的模型進行辨識,在不考慮其速度的情況下,by frame比較其辨識的差異。

事實上,在此範例中,Coral USB Accelerator+樹莓派的速度,與NCS2+樹莓派(透過OpenCV DNN)相當接近,都在2.5左右(註)。兩者最大的差異在於兩者模型的精度不同,分別為INT8與FP16。雖然前者的模型精度較低,但透過重新訓練,也可取得與後者不相軒輊的accuracy。您可以下載此影片,以格放方式來比較其差異。

註:理論上,INT8的MobileNet模型在Google Coral USB Accelerator上執行應該非常迅速,上面的測試表現,Google Coral USB Accelerator與NCS2是差不多的,原因可能在於:1.影片的尺寸較大(720P) 2. Coral Accelerator需要USB3.0才能完全發揮效力 3. NCS2透過OpenCV DNN執行有加速的效果

        持平來說,雖然Google Coral USB Accelerator在外形體積和耗電量上佔盡優勢,且使用精度更低的INT8,使得Coral USB Accelerator在SSD Mobilenet V2模型的推論速度比起起其它Edge AI chip更快(請參考https://blog.hackster.io/benchmarking-tensorflow-and-tensorflow-lite-on-the-raspberry-pi-43f51b796796?fbclid=IwAR0kQpCMoJmXMfXp0w9pac_gIHaAELAIIgTiFD2dyrxKHVJ_cV75Tksec_g),但這是種種不方便使用之下妥協的結果,例如Edge TPU目前僅支援Tensorflow lite一種格式,且是更為簡化的Tensorflow lite版本,所支援的神經網路layer種類更少,因此,您在上手一支Google Coral USB Accelerator之後,除了執行官方所提供的範例之外,可能還要煩惱,如何將自己先前所訓練、FP32精度的模型透過一道道繁瑣的程序轉換為最終INT8的Tensorflow lite for Edge TPU模型(此步驟稱為post-training quantization),並且思量能否接受INT8模型所帶來較低accuracy的表現,以及後續是否需要採取re-train方式來提昇INT8精度模型的準確率。