【Movidius神經運算棒】什麼!用電池就能驅動的深度學習推論引擎?

 

作者/攝影   曾吉弘
時間   3小時
難度

★★★★★

材料表
  • Intel Movidius Neural Compute Stick 神經運算棒
  • Raspberry Pi (RPi) 3 Model B 單板電腦 (OS需為最新的 Stretch)
  • Raspberry Pi 攝影機模組 (picam)或任何USB webcam
  • Raspberry Pi 觸碰螢幕
  • Raspberry Pi 觸碰螢幕外殼 [選配],也可用這款外殼:Pimoroni® case on Adafruit

(Original post from Intel Movidius NCS blog: “Battery-Powered Deep Learning Inference Engine”:https://movidius.github.io/blog/battery-powered-dl-engine/ )

 

加強邊緣裝置的視覺感知功能

鋰離子聚合物電池(LiPo、lithium polymer batteries)與嵌入式處理器兩者大大助長了物聯網(IoT)市場的發展。它們讓IoT裝置製造商得以在攜帶式(行動)邊緣裝置上加入更多功能,只要充電一次就可以執行相當長的時間。感測科技的長足進步,尤其是視覺相關的感測器、以及可處理來自這類感測器的超大量資料的軟體演算法,對於在更好的運算速度但又不能妥協於電池壽命與即時運算效能等方面上,這類行動邊緣裝置的需求受到了阻礙。

Intel® Movidius™ 視覺處理元件(Intel® Movidius™ VPU) 專門為可由電池供電的商用/企業級邊緣裝置提供即時視覺運算功能,這類裝置像是Google ClipsDJI® Spark droneMotorola® 360 cameraHuaRay® industrial smart cameras 等等。本文目標並非重製它們,而是要做一台小型手持裝置,運用深度神經網路(DNN)來即時辨識物體。

專案實體照片

 

邊做邊學!

您會製作:

一台可由電池(或行動電源)供電的DIY手持裝置,裝有攝影機與觸碰螢幕,可以辨識(分類)位於攝影機前方的物體。

您會學到:

  • 如何使用Raspberry Pi與Intel® Movidius™ NCS神經運算棒製作一台即時影像分類器。

您會需要:

  • Intel Movidius Neural Compute Stick 神經運算棒
  • Raspberry Pi (RPi) 3 Model B 單板電腦 (OS需為最新的 Stretch)
  • Raspberry Pi 攝影機模組 (picam)或任何USB webcam
  • Raspberry Pi 觸碰螢幕
  • Raspberry Pi 觸碰螢幕外殼 [選配],也可用這款外殼:Pimoroni® case on Adafruit

如果還沒完成的話,請在您的Raspberry Pi上安裝Intel Movidius NCSDK,完整SDK或API-only模式都可以。請參考Intel Movidius NCS Quick Start Guide來安裝完整SDK,或參考Run NCS Apps on RPi 來安裝API-only模式。

 

先看結果:

如果想先看看程式輸出結果的話,請用以下指令來取得範例程式並執行

$ mkdir -p ~/workspace

$ cd ~/workspace

$ git clone https://github.com/movidius/ncappzoo

$ cd ncappzoo/apps/live-image-classifier

$ make run

上述指另需在裝有完整SDK的系統中才可執行,單單安裝API framework 是不型的。另外也請確認已經在系統上接了一台UVC攝影機( 筆電內建攝影機也可運作).

這樣應該會在方形外框中看到即時串流影像。在攝影機前方放置一個物體並對齊方形。執行畫面請看本文最後的照片。

 

硬體建置

下圖是我硬體的實際照片:

 

Step1:設定螢幕

觸碰螢幕設定:請參考下方影片

畫面轉向:根據您所用的螢幕外殼或支架,您的畫面可能是倒過來的。如果是的話,請根據以下操作來將畫面反轉180度。

 

[pastacode lang=”python” manual=”%24%20sudo%20nano%20%2Fboot%2Fconfig.txt%0A%0A%0A%0A%23%20%E5%9C%A8%2Fboot%2Fconfig.txt%20%E6%9C%80%E5%BE%8C%E4%B8%80%E8%A1%8C%E8%BC%B8%E5%85%A5%E4%BB%A5%E4%B8%8B%E5%85%A7%E5%AE%B9%EF%BC%8C%E6%8C%89%E4%B8%8BCtrl-x%E5%AD%98%E6%AA%94%E5%BE%8C%E9%9B%A2%E9%96%8B%0A%0Alcd_rotate%3D2%0A%0A%0A%0A%24%20sudo%20reboot” message=”” highlight=”” provider=”manual”/]

如果您是使用USB攝影機的話請跳過STEP2

Step 2:設定攝影機

啟動CSI攝影機模組:請參考Raspberry Pi官方說明文件.

啟動v4l2 driver:基於不明原因,Raspbian預設不會去載入CSI攝影機模組用的V4L2 driver。本專題範例採用OpenCV-Python,作法是逐次使用V4L2來存取攝影機 (路徑:/dev/video0),所以我們得先載入V4L2 driver.

[pastacode lang=”python” manual=”%24%20sudo%20nano%20%2Fetc%2Fmodules%0A%0A%0A%0A%23%20%E5%9C%A8%2Fetc%2Fmodules%E6%9C%80%E5%BE%8C%E4%B8%80%E8%A1%8C%E8%BC%B8%E5%85%A5%E4%BB%A5%E4%B8%8B%E5%85%A7%E5%AE%B9%EF%BC%8C%E6%8C%89%E4%B8%8BCtrl-x%E5%AD%98%E6%AA%94%E5%BE%8C%E9%9B%A2%E9%96%8B%0A%0Abcm2835-v4l2%0A%0A%0A%0A%24%20sudo%20reboot%0A%0A” message=”” highlight=”” provider=”manual”/]

來寫程式吧

我強力支持程式再運用,所以本專題的Python程式大部分是來自於[五步驟打造影像分類器]。兩者主要的差別在於我把每一個步驟整合在同一個函式中了,讀者可以比較一下。

本程式的架構不需要大幅修改就可以執行任何分類器神經網路。以下是一些使用者定義的參數:

  1. GRAPH_PATH:要進行推論的graph檔路徑
    • 預設值為 ~/workspace/ncappzoo/caffe/GoogLeNet/graph
  2. CATEGORIES_PATH:列出各分類輸出標籤的文字檔路徑
    • 預設值為 ~/workspace/ncappzoo/tensorflow/mobilenets/categories.txt
  3. IMAGE_DIM:所採用神經網路之影像尺寸規格
    • 例如 GoogLeNet 採用 224×224像素,AlexNet則是227×227像素
  4. IMAGE_STDDEV:您選用之神經網路所定義的標準差(scaling value)
    • 例如:GoogLeNet不採用任何比例因子,但InceptionV3的比例因子為128 (stddev = 1/128)
  5. IMAGE_MEAN:均值減法(Mean subtraction)是深度學習領域中常用的資料整理技巧。
    • 對ILSVRC資料集來說,B、G、R的平均值分別為102、117與123

在操作NCSDK API之前,需要先由 mvnc 函式庫匯入mvncapi 模組:

import mvnc.mvncapi as mvnc

如果您已經完成本連結的影像分類器的話,請跳過Step1 2 5

 

Step 1:開啟裝置

如同其他USB裝置,當您將NCS接上應用程式處理器 (執行Ubuntu的桌上型/筆記型電腦) 的USB埠,前者會被後者枚舉為一個USB裝置。我們可以呼叫API來檢視與開啟各個NCS裝置。

# —- Step 1: 開啟裝置並準備處理 ————-

def open_ncs_device():

   # 尋找 NCS裝置,如果沒有就離開程式

   devices = mvnc.EnumerateDevices()

   if len( devices ) == 0:

       print( ‘No devices found’ )

       quit()

 

   # 取得第一個枚舉裝置並開啟

   device = mvnc.Device( devices[0] )

   device.OpenDevice()

 

   return device

Step 2:將graph檔載入NCS

為了讓本專案簡單化,我們採用來自預訓練好的GoogLeNet模型的預編譯graph,這當您在ncappzoo資料夾中執行 make 指令時已經一併下載與編譯好了。先來看看如何將 graph 載入至NCS。

# —- Step 2: 將graph檔載入NCS ————————-

 

[pastacode lang=”python” manual=”def%20load_graph(%20device%20)%3A%0A%0A%C2%A0%C2%A0%20%23%20%E8%AE%80%E5%8F%96graph%E6%AA%94%E8%87%B3%E7%B7%A9%E8%A1%9D%0A%0A%C2%A0%C2%A0%20with%20open(%20GRAPH_PATH%2C%20mode%3D’rb’%20)%20as%20f%3A%0A%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20blob%20%3D%20f.read()%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20%23%20%E8%BC%89%E5%85%A5%E7%B7%A9%E8%A1%9D%E8%87%B3NCS.%0A%0A%C2%A0%C2%A0%20graph%20%3D%20device.AllocateGraph(%20blob%20)%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20return%20graph” message=”” highlight=”” provider=”manual”/]

 

Step 3:預處理來自攝影機的畫面

如影像分類器一文所述,分類器所用的神經網路會假設在影像中只有一個物體。但這件事在即時影像中很難掌控,除非您把桌面清理非常乾淨並採用單色背景。為了處理這件事,我們來偷雞一下。藉由OpenCV API在畫面上畫一個虛擬的盒子,並要求使用者把物件以手動方式對齊到盒子中;接著就切出這個盒子的區域並把該區域影像送到NCS進行分類。

# —- Step 3: 預處理影像 —————————————-

 

[pastacode lang=”python” manual=”def%20pre_process_image()%3A%0A%0A%C2%A0%C2%A0%20%23%20%E5%8F%96%E5%BE%97%E4%BE%86%E8%87%AA%E6%94%9D%E5%BD%B1%E6%A9%9F%E7%9A%84%E4%B8%80%E5%80%8B%E7%95%AB%E9%9D%A2%0A%0A%C2%A0%C2%A0%20ret%2C%20frame%20%3D%20cam.read()%0A%0A%C2%A0%C2%A0%20height%2C%20width%2C%20channels%20%3D%20frame.shape%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20%23%20%E5%88%87%E5%89%B2%E7%95%AB%E9%9D%A2%E4%B8%A6%E8%AA%BF%E6%95%B4%E5%A4%A7%E5%B0%8F%0A%0A%C2%A0%C2%A0%20×1%20%3D%20int(%20width%20%2F%203%20)%0A%0A%C2%A0%C2%A0%20y1%20%3D%20int(%20height%20%2F%204%20)%0A%0A%C2%A0%C2%A0%20×2%20%3D%20int(%20width%20*%202%20%2F%203%20)%0A%0A%C2%A0%C2%A0%20y2%20%3D%20int(%20height%20*%203%20%2F%204%20)%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20cv2.rectangle(%20frame%2C%20(%20×1%2C%20y1%20)%20%2C%20(%20×2%2C%20y2%20)%2C%20(%200%2C%20255%2C%200%20)%2C%202%20)%0A%0A%C2%A0%C2%A0%20cv2.imshow(%20’NCS%20real-time%20inference’%2C%20frame%20)%0A%0A%C2%A0%C2%A0%20cropped_frame%20%3D%20frame%5B%20y1%20%3A%20y2%2C%20×1%20%3A%20×2%20%5D%0A%0A%C2%A0%C2%A0%20cv2.imshow(%20’Cropped%20frame’%2C%20cropped_frame%20)%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20%23%20%E8%AA%BF%E6%95%B4%E5%BD%B1%E5%83%8F%E5%A4%A7%E5%B0%8F%20%5B%20%E6%A0%B9%E6%93%9A%E6%89%80%E9%81%B8%E7%94%A8%E7%9A%84%E7%B6%B2%E8%B7%AF%E8%80%8C%E7%95%B0%5D.%0A%0A%C2%A0%C2%A0%20cropped_frame%20%3D%20cv2.resize(%20cropped_frame%2C%20IMAGE_DIM%20)%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20%23%20%E5%9D%87%E5%80%BC%E6%B8%9B%E6%B3%95%E6%88%96%E7%B8%AE%E6%94%BE%20%5B%E5%B8%B8%E8%A6%8B%E7%9A%84%E8%B3%87%E6%96%99%E7%BD%AE%E4%B8%AD%E6%96%B9%E6%B3%95%5D.%0A%0A%C2%A0%C2%A0%20cropped_frame%20%3D%20cropped_frame.astype(%20numpy.float16%20)%0A%0A%C2%A0%C2%A0%20cropped_frame%20%3D%20(%20cropped_frame%20-%20IMAGE_MEAN%20)%20*%20IMAGE_STDDEV%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20return%20cropped_frame” message=”” highlight=”” provider=”manual”/]

 

Step 4:卸載影像/畫面至NCS來執行推論

感謝Intel Movidius VPU提供的高效能與低功耗,有了NCS之後,Raspberry Pi 唯一要做的事情就是預處理攝影機的畫面(step 3) 並丟給NCS。推論結果會以陣列來呈現,包含了各分類的機率值。透過 argmax() 可以取得 top prediction的索引值以以及相關於該索引的標籤內容。

# —- Step 4: 卸載影像,讀取與顯示推論結果 —————-

 

[pastacode lang=”python” manual=”def%20infer_image(%20graph%2C%20img%20)%3A%0A%0A%C2%A0%C2%A0%20%23%20%E5%B0%87%E6%89%80%E6%9C%89%E5%88%86%E9%A1%9E%E7%9B%AE%E9%8C%84%E8%AE%80%E5%8F%96%E8%87%B3%E6%B8%85%E5%96%AE%E4%B8%AD%0A%0A%C2%A0%C2%A0%20categories%20%3D%20%5Bline.rstrip(‘%5Cn’)%20for%20line%20in%0A%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20open(%20CATEGORIES_PATH%20)%20if%20line%20!%3D%20’classes%5Cn’%5D%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20%23%20%E4%BB%A5%E5%8D%8A%E7%B2%BE%E5%BA%A6%E6%B5%AE%E9%BB%9E%E6%95%B8%E9%99%A3%E5%88%97%E8%BC%89%E5%85%A5%E5%BD%B1%E5%83%8F.%0A%0A%C2%A0%C2%A0%20graph.LoadTensor(%20img%20%2C%20’user%20object’%20)%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20%23%20%E5%8F%96%E5%BE%97NCS%E6%8E%A8%E8%AB%96%E7%B5%90%E6%9E%9C.%0A%0A%C2%A0%C2%A0%20output%2C%20userobj%20%3D%20graph.GetResult()%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20%23%20%E5%8F%96%E5%BE%97%E6%9C%80%E9%AB%98%E4%BF%A1%E5%BF%83%E9%A0%85%E7%9B%AE%E4%B9%8B%E7%B4%A2%E5%BC%95%E5%80%BC.%0A%0A%C2%A0%C2%A0%20top_prediction%20%3D%20output.argmax()%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20%23%20%E9%A1%AF%E7%A4%BA%E7%B5%90%E6%9E%9C.%0A%0Aprint(%20%22Prediction%3A%20%22%20%2B%20str(top_prediction)%0A%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%2B%20%22%20%22%20%2B%20categories%5Btop_prediction%5D%0A%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%2B%20%22%20with%20%253.1f%25%25%20confidence%22%20%25%20(100.0%20*%20output%5Btop_prediction%5D%20)%20)%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20return%0A%0A%E5%A6%82%E6%9E%9C%E6%82%A8%E6%83%B3%E7%9C%8B%E7%9C%8BNCS%E7%9A%84%E5%AF%A6%E9%9A%9B%E8%BC%B8%E5%87%BA%E7%9A%84%E5%8C%96%EF%BC%8C%E8%AB%8B%E6%89%BE%E5%88%B0ncappzoo%2Fapps%2Fimage-classifier.py%E4%B8%A6%E5%A6%82%E4%BB%A5%E4%B8%8B%E4%BF%AE%E6%94%B9%EF%BC%9A%0A%0A%23%20—-%20Step%204%3A%20%E8%AE%80%E5%8F%96%E8%88%87%E9%A1%AF%E7%A4%BANCS%E7%9A%84%E6%8E%A8%E8%AB%96%E7%B5%90%E6%9E%9C%20——————-%0A%0A%C2%A0%0A%0A%23%20%E5%8F%96%E5%BE%97NCS%E6%8E%A8%E8%AB%96%E7%B5%90%E6%9E%9C.%0A%0Aoutput%2C%20userobj%20%3D%20graph.GetResult()%0A%0A%C2%A0%0A%0A%23%20%E9%A1%AF%E7%A4%BA%E8%BC%B8%E5%87%BA%E7%B5%90%E6%9E%9C.%0A%0Aprint(%20output%20)%0A%0A…%0A%0A%C2%A0%0A%0A%23%20%E9%A1%AF%E7%A4%BA%20top%20prediction.%0A%0Afor%20i%20in%20range(%200%2C%204%20)%3A%0A%0A%C2%A0%C2%A0%20print(%20%22Prediction%20%22%20%2B%20str(%20i%20)%20%2B%20%22%3A%20%22%20%2B%20str(%20order%5Bi%5D%20)%0A%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%2B%20%22%20with%20%253.1f%25%25%20confidence%22%20%25%20(100.0%20*%20output%5Border%5Bi%5D%5D%20)%20)%0A%0A…” message=”” highlight=”” provider=”manual”/]

執行上述修改後的程式之後,它把整個會輸出陣列都秀出來。以下是使用37個分類的網路來進行推論的結果畫面,請注意陣列大小為37,且top prediction (73.8%)出現在陣列的30號位置(7.37792969e-01),如下紅字:

[ 0.00000000e+00   2.51293182e-04   0.00000000e+00   2.78234482e-04

0.00000000e+00   2.36272812e-04   1.89781189e-04   5.07831573e-04

6.40749931e-05   4.22477722e-04   0.00000000e+00   1.77288055e-03

2.31170654e-03   0.00000000e+00   8.55255127e-03   6.45518303e-05

2.56919861e-03   7.23266602e-03   0.00000000e+00 1.37573242e-01

7.32898712e-04   1.12414360e-04   1.29342079e-04   0.00000000e+00

0.00000000e+00   0.00000000e+00   6.94580078e-02   1.38878822e-04

7.23266602e-03   0.00000000e+00   7.37792969e-01   0.00000000e+00

7.14659691e-05   0.00000000e+00   2.22778320e-02   9.25064087e-05

0.00000000e+00]

Prediction 0: 30 with 73.8% confidence

Prediction 1: 19 with 13.8% confidence

Prediction 2: 26 with 6.9% confidence

Prediction 3: 34 with 2.2% confidence

 

Step 5:卸載graph檔並關閉裝置

為了避免記憶體洩漏與/或區段錯誤(segmentation fault),請記得關閉任何開啟中的檔案、資源以及 deallocate 使用中的記憶體。

# —- Step 5: 卸載graph檔並關閉裝置 ————————-

 

[pastacode lang=”python” manual=”def%20close_ncs_device(%20device%2C%20graph%20)%3A%0A%0A%C2%A0%C2%A0%20cam.release()%0A%0A%C2%A0%C2%A0%20cv2.destroyAllWindows()%0A%0A%C2%A0%C2%A0%20graph.DeallocateGraph()%0A%0A%C2%A0%C2%A0%20device.CloseDevice()%0A%0A%C2%A0%0A%0A%C2%A0%C2%A0%20return” message=”” highlight=”” provider=”manual”/]

恭喜!DNN即時影像分析器完成囉!

 

專案實體照片

Rpi 上接了NCS神經運算棒與無線鍵盤發射器

RPi 與 Pi camera 的配置

分類”碗”的畫面

分類”滑鼠”的畫面

還能做什麼

  • 將本專案移植到執行Raspbian Lite 作業系統的 Raspberry Pi Zero上。
  • 本範例採用MobileNets來分類影像。試著加入分類年紀與性別的功能吧。
    • 提示:使用來自ncappzoo/caffe/AgeNet 與 ncappzoo/caffe/GenderNet 的graph檔。
  • 使用ncappzoo/SSD_MobileNet 或 Tiny YOLO,將本範例改為物件偵測(一張圖中有多個物體)

延伸閱讀

 

相關文章:

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *