微軟 LOBE ai – 離線訓練影像辨識模型!

撰寫/攝影曾吉弘
時間1.5 – 2 hrs
成本具備webcam的筆記型電腦或桌上型電腦
難度

★★★☆☆

Lobe ai

  Lobe ai 是微軟推出的離線版神經網路分類工具,相較於 Google Teachable Machine,離線就能訓練神經網路也許更適合教室使用。這股浪潮確實是資料驅動(data-driven)的時代,誰能掌握高品質的資料誰就能更快擁有合乎需求的神經網路。CAVEDU 2019 年所出版[實戰AI資料導向式學習]也有談到這個概念,使用 Raspberry Pi 搭配 Keras 訓練的視覺分類神經網路模型來實作路牌辨識機器人(讓我打一下書~嘛)

好,回歸正題,快開啟主頁面(https://lobe.ai)來看看!

根據主頁,目前可用的功能為影像分類,後續會推出物件偵測與資料分類等,非常令人期待。

下載與安裝

 

   請由主頁右上角的 Download 選項來下載,安裝包大約 380MB。依照預設設定安裝完畢,開啟主畫面如下,相當清爽。

   點選 New Project 來開啟新專案,目前只有影像分類可用囉

建立專案

 

    空白專案畫面如下,我今天想要辨識兩種目標,都是玩具:薩克頭(zaku) 與 德姆頭 (DOM),因此總共需要三個類別,第三個類別是用來處理以上皆非的狀況,否則後續在辨識時就算看到別的東西,您的神經網路也只能薩克 / 德姆二選一。

 

    Lobe 標榜每個類別只要5張照片就可以訓練,這真的是很厲害的數字啊!

點選右上角 Import,可以看到有三種建立資料集的方式,

Images: 從電腦端上傳照片

Camera: 使用 webcam 拍照

Dataset: 上傳已經整理好的資料集

 

在此使用 Camera 方便我們快速驗證。

建立資料集

 

在此總共建立三個類別:zaku、dom 與 other。

在上一步的 Import → Camera 之後,會出現即時的 webcam 畫面,點選中間的圓圈就會拍照,一直按著就會連拍。

 

本專案針對薩克與德姆各準備兩種不同顏色與略有不同的外型,希望能看看辨識的效果如何。可以看到左側 Unlabeled (未標註) 的影片有 47張,後續再標註就好。

 

下圖左為沙漠型德姆,圖右為一般型德姆(在專業什麼..)

把想要被標誌為 dom 的照片全部選起來,點選任何一張照片左下角標籤名稱,就可以一次全部修改好。如果發現哪張照片糊了或是不滿意,都可以馬上刪除。

再來是薩克,一樣有藍色與紅色的薩克兩種。類別的照片數量請盡量保持差不多的數量。

最後是 other,這裡我放了人臉、水壺以及背景。但是請注意,可能會出現但不希望被辨識錯誤的項目才要放在 other 類別中,否則就不需要放進去喔。

訓練

發現了嗎?只要建立了第二個類別並標註之後,Lobe 就會自動開始訓練了。訓練速度和您的電腦規格有關,以我的筆記型電腦 (i7, 10GB ram),192 張照片大約10分鐘,相較於 Google Teachable Machine 的一分鐘之內來說這實在有點久,但離線執行確實有其方便之處,請您多多評估囉。

測試

   測試分為 Images Camera 兩個選項,前者讓您從電腦端上傳照片,可以看到以下這張照片被分類為 dom,分類錯誤!但您可以點選右下角的紅色禁止符號來告訴 Lobe 他做錯了並給予正確的標籤(zaku),之後再次訓練時就會更好喔!

 

接著是 Camera 即時預覽畫面,正確辨識為 zaku!您可以點選右下角的綠色勾勾將本張照片加入 zaku 類別,這時 lobe 也會馬上開始訓練。

多多調整,盡量讓每個類別的辨識結果都愈高愈好。

匯出

   如果您滿意模型表現的話,就可以匯出(export)了,這也是這類工具最棒的地方。

請點選左上角的[]圖示,開啟選單: Preferences → Project Settings,有兩個選項,分別是針對準確度最佳化以及針對速度最佳化。在此選擇後者,您可以兩個都比較看看。點選之後 Lobe 會需要一些時間完成最佳化。

接著就可以匯出了,請點選左上角的[]圖示,開啟選單:Export,會看到 lobe 提供以下三個匯出選項:TensorFlow、TensorFlow Lite 與 Local API,請點選 TensorFlow Lite 就會開始匯出。

 

下載之後會出現一個與您專案同名並加上 TFLite 的資料夾(例如 DOM_ZAKU TFLite),會有 .example 資料夾,裡面有一個 python 程式可以跑跑看。另外就是 saved_model.tflite 與 signature.json 這兩個模型檔。

在此用 https://netron.app/ 這個神經網路視覺化工具來看看 .tflite 模型,層數約在80層左右,大量使用了 Conv2D 來進行卷積運算。

 

如果選擇 local API 會出現對應的語法與 parse result 說明

範例

馬上寫一個 python 來玩玩看。請先根據本篇文章在您的電腦上安裝好 Anaconda 與所需的套件。

 

執行語法:

python LOBE_WEBCAM_tflite.py --model "DOM_ZAKU TFLite"

 

注意 LOBE_WEBCAM_tflite.py 要與 DOM_ZAKU TFLite 資料夾同一層。

 

LOBE_WEBCAM_tflite.py 內容:

#
#  -------------------------------------------------------------
#   Copyright (c) CAVEDU.  All rights reserved.
#  -------------------------------------------------------------
"""
Skeleton code showing how to load and run the TensorFlow Lite export package from Lobe.
"""

import argparse
import json
import os

import numpy as np
from PIL import Image

import cv2

import tflite_runtime.interpreter as tflite

def get_prediction(image, interpreter, signature):
    """
    Predict with the TFLite interpreter!
    """
    # Combine the information about the inputs and outputs from the signature.json file with the Interpreter runtime
    signature_inputs = signature.get("inputs")
    input_details = {detail.get("name"): detail for detail in interpreter.get_input_details()}
    model_inputs = {key: {**sig, **input_details.get(sig.get("name"))} for key, sig in signature_inputs.items()}
    signature_outputs = signature.get("outputs")
    output_details = {detail.get("name"): detail for detail in interpreter.get_output_details()}
    model_outputs = {key: {**sig, **output_details.get(sig.get("name"))} for key, sig in signature_outputs.items()}

    # process image to be compatible with the model
    input_data = process_image(image, model_inputs.get("Image").get("shape"))

    # set the input to run
    interpreter.set_tensor(model_inputs.get("Image").get("index"), input_data)
    interpreter.invoke()

    # grab our desired outputs from the interpreter!
    # un-batch since we ran an image with batch size of 1, and convert to normal python types with tolist()
    outputs = {key: interpreter.get_tensor(value.get("index")).tolist()[0] for key, value in model_outputs.items()}
    # postprocessing! convert any byte strings to normal strings with .decode()
    for key, val in outputs.items():
        if isinstance(val, bytes):
            outputs[key] = val.decode()

    return outputs


def process_image(image, input_shape):
    """
    Given a PIL Image, center square crop and resize to fit the expected model input, and convert from [0,255] to [0,1] values.
    """
    width, height = image.size

    # ensure image type is compatible with model and convert if not
    if image.mode != "RGB":
        image = image.convert("RGB")

    # center crop image (you can substitute any other method to make a square image, such as just resizing or padding edges with 0)
    if width != height:
        square_size = min(width, height)
        left = (width - square_size) / 2
        top = (height - square_size) / 2
        right = (width + square_size) / 2
        bottom = (height + square_size) / 2
        # Crop the center of the image
        image = image.crop((left, top, right, bottom))

    # now the image is square, resize it to be the right shape for the model input
    input_width, input_height = input_shape[1:3]
    if image.width != input_width or image.height != input_height:
        image = image.resize((input_width, input_height))


    # make 0-1 float instead of 0-255 int (that PIL Image loads by default)
    image = np.asarray(image) / 255.0

    # format input as model expects
    return image.reshape(input_shape).astype(np.float32)


def main():
    """
    Load the model and signature files, start the TF Lite interpreter, and run prediction on the image.

    Output prediction will be a dictionary with the same keys as the outputs in the signature.json file.
    """
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument(
        '--model', help='Model path of .tflite file and .json file', required=True)
    args = parser.parse_args()
    
    with open( args.model + "/signature.json", "r") as f:
        signature = json.load(f)

    model_file = signature.get("filename")

    interpreter = tflite.Interpreter(args.model + '/' + model_file)
    interpreter.allocate_tensors()

    cap = cv2.VideoCapture(0)
    #擷取畫面 寬度 設定為640
    cap.set(cv2.CAP_PROP_FRAME_WIDTH,640)
    #擷取畫面 高度 設定為480
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

    key_detect = 0
    times=1
    while (key_detect==0):
        ret,frame =cap.read()

        image = Image.fromarray(cv2.cvtColor(frame,cv2.COLOR_BGR2RGB))

        if (times==1):
            prediction = get_prediction(image, interpreter, signature)

        print('Result = '+ prediction['Prediction'])
        print('Confidences = ' + str(max(prediction['Confidences'])) )
 
        cv2.putText(frame, prediction['Prediction'] + " " +
                    str(round(max(prediction['Confidences']),3)),
                    (5,30), cv2.FONT_HERSHEY_SIMPLEX, 1,
                    (0,0,255), 1, cv2.LINE_AA)
        
        cv2.imshow('Detecting....',frame)

        times=times+1
        if (times >= 5):
            times=1

        read_key = cv2.waitKey(1)
        if ((read_key & 0xFF == ord('q')) or (read_key == 27) ):
            key_detect = 1

    cap.release()
    cv2.destroyAllWindows()


if __name__ == "__main__":
    main()

執行效果

    執行效果如下,會開啟一個預覽畫面,並把辨識結果(label)與信心指數顯示出來。

與 Google Teachable Machine 比較與展望

最後免不了要與Google Teachable Machine (簡稱TM)比較一下,暑期的 AIGO 高中職生扎根營隊我們大量使用 TM來匯出視覺分類模型,也可以放在 Raspberry Pi、Jetson Nano 上執行,或者連動 7697 等這類MCU板來做到各種有趣的 AIoT 應用。

 

但Lobe標榜離線運作的話,教室的網路壓力就不會這麼高了,很期待各位後許的意見分享喔!

 

Lobe.aiTeachable Machine
網路需求下載安裝包才需要,之後都可離線使用連線到網站使用,全程都須使用網路
訓練速度於電腦端進行運算

根據該電腦等級而有明顯差異

200張約3-5分鐘(i7 / 16GB ram)

於電腦端進行運算

幾乎都可在1分鐘內完成

支援之神經網路視覺分類

物件辨識(尚未)

資料分類(尚未)

視覺分類

聲音分類

姿勢分類

匯出模型格式TensorFlow

TensorFlow Lite

TensorFlow

TensorFlow Lite

TensorFlow js

TFLite 神經網路層數7974

 

發佈留言

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