用 WasmEdge 和 YoMo 對實時數據流進行 AI 推理

YoMo 是一個用於輔助開發者方便構建分佈式雲系統(Geo-Distributed Cloud System)的編程框架。YoMo 的通信層構建在 QUIC 協議之上,帶來高速數據傳輸的同時,內置了 Streaming Serverless 的「流函數」,大幅提高了分佈式雲系統的開發體驗。YoMo 構建的分佈式雲系統在近場算力和終端之間提供了超高速通信機制,在 Metaverse、VR/AR、IoT 等領域有普遍的應用場景。git

YoMo 使用 Go 語言編寫,Streaming Serverless 部分使用了 Golang 的插件和共享庫動態加載用戶代碼,但也給開發者帶來了一些侷限性。加之Serverless 架構對隔離的剛性需求,這使得 WebAssembly 成爲運行用戶定義函數的絕佳選擇。github

例如在 AR/VR、智能工廠裏作實時 AI 推理的過程當中,攝像頭能夠經過 YoMo 將實時的非結構化數據發送到近場 MEC (多訪問邊緣計算)設備中的計算節點,並自動執行託管的 AI 推理函數。當 AI 推理完成,YoMo將 AI 計算結果實時發送給端設備。golang

然而,YoMo 面臨的挑戰是在邊緣計算節點中合併和管理由多個外部開發者編寫的處理程序函數。這須要在不犧牲性能的狀況下對這些函數進行 runtime 隔離。傳統的軟件容器解決方案,如 Docker,沒法勝任這項任務,由於過重且速度太慢,沒法處理實時任務。編程

WebAssembly 提供了一個輕量級高性能的軟件容器。它很是適合做爲 YoMo 數據處理 handler 函數的 runtime。session

本文中,咱們將向你展現如何爲基於 Tensorflow 的圖片識別建立 Rust 函數,將其編譯爲 WebAssembly,而後使用 YoMo 將其做爲流數據 handler 運行。咱們使用 WasmEdge 做爲 WebAssembly runtime,由於與其它 WebAssembly runtime 相比,WasmEdge 提供了最佳性能和最高靈活度。WasmEdge 是惟一穩定支持 Tensorflow 的 WebAssembly 虛擬機。 YoMo 經過 WasmEdge 的 Golang API管理 WasmEdge VM 實例和容器內的 WebAssembly 字節碼應用。架構

GitHub 源代碼:https://github.com/yomorun/yomo-wasmedge-tensorflowapp

準備工做

顯然,須要安裝 Golang 。咱們假設你已經安裝。框架

Golang 版本須要比 1.15 新,咱們的示例才能運行。less

同時,須要安裝 YoMo CLI 應用程序。它安排和協調數據流和 handler 函數調用。分佈式

$ go install github.com/yomorun/cli/yomo@latest
$ yomo version
YoMo CLI version: v0.0.5

接下來,請安裝 WasmEdge 及 Tensorflow 共享庫。 WasmEdge 是領先的 WebAssembly runtime,由 CNCF 託管。 咱們將使用它嵌入和運行來自 YoMo 的 WebAssembly 程序。

# Install WasmEdge
$ wget https://github.com/second-state/WasmEdge-go/releases/download/v0.8.1/install_wasmedge.sh
$ chmod +x ./install_wasmedge.sh
$ sudo ./install_wasmedge.sh /usr/local

# Install WasmEdge Tensorflow extension
$ wget https://github.com/second-state/WasmEdge-go/releases/download/v0.8.1/install_wasmedge_tensorflow_deps.sh
$ wget https://github.com/second-state/WasmEdge-go/releases/download/v0.8.1/install_wasmedge_tensorflow.sh
$ chmod +x ./install_wasmedge_tensorflow_deps.sh
$ chmod +x ./install_wasmedge_tensorflow.sh
$ sudo ./install_wasmedge_tensorflow_deps.sh /usr/local
$ sudo ./install_wasmedge_tensorflow.sh /usr/local

# Install WasmEdge Images extension
$ wget https://github.com/second-state/WasmEdge-go/releases/download/v0.8.1/install_wasmedge_image_deps.sh
$ wget https://github.com/second-state/WasmEdge-go/releases/download/v0.8.1/install_wasmedge_image.sh
$ chmod +x ./install_wasmedge_image_deps.sh
$ chmod +x ./install_wasmedge_image.sh
$ sudo ./install_wasmedge_image_deps.sh /usr/local
$ sudo ./install_wasmedge_image.sh /usr/local

最後, 由於咱們的 demo WebAssembly 函數是用 Rust 編寫的,所以你還須要安裝 Rust 編譯器和 rustwasmc工具鏈。

demo 的其它部分能夠 fork 和 clone 源代碼 repo

$ git clone https://github.com/yomorun/yomo-wasmedge-tensorflow.git

圖片分類函數

處理 YoMo 圖片流的圖片識別函數是用 Rust 編寫的。 它用 WasmEdge Tensorflow API 來處理輸入圖片。

#[wasm_bindgen]
pub fn infer(image_data: &[u8]) -> String {
    // Load the TFLite model and its meta data (the text label for each recognized object number)
    let model_data: &[u8] = include_bytes!("lite-model_aiy_vision_classifier_food_V1_1.tflite");
    let labels = include_str!("aiy_food_V1_labelmap.txt");

    // Pre-process the image to a format that can be used by this model
    let flat_img = wasmedge_tensorflow_interface::load_jpg_image_to_rgb8(image_data, 192, 192);
    
    // Run the TFLite model using the WasmEdge Tensorflow API
    let mut session = wasmedge_tensorflow_interface::Session::new(&model_data, wasmedge_tensorflow_interface::ModelType::TensorFlowLite);
    session.add_input("input", &flat_img, &[1, 192, 192, 3])
           .run();
    let res_vec: Vec<u8> = session.get_output("MobilenetV1/Predictions/Softmax");

    // Find the object index in res_vec that has the greatest probability
    // Translate the probability into a confidence level
    // Translate the object index into a label from the model meta data food_name
    
    ret_str = format!(
        "It {} a <a href='https://www.google.com/search?q={}'>{}</a> in the picture",
        confidence, food_name, food_name
    );
    return ret_str;
}

你能夠使用 rustwasmc 工具將該函數編譯爲 WebAssembly 字節碼。

這裏,咱們要求 Rust 編譯器版本爲 1.50 或更早,才能讓 WebAssembly 函數與 WasmEdge 的 Golang API 一塊兒使用。一旦 interface type 規範最終肯定並獲得支持,咱們會遇上最新的Rust 編譯器版本

$ rustup default 1.50.0

$ cd flow/rust_mobilenet_food
$ rustwasmc  build `--enable-ext`
# The output WASM will be pkg/rust_mobilenet_food_lib_bg.wasm.

# Copy the wasm bytecode file to the flow/ directory
$ cp pkg/rust_mobilenet_food_lib_bg.wasm ../

與 YoMo 集成

在 YoMo 這端,咱們使用 WasmEdge Golang API 來爲圖片識別函數啓動和運行 WasmEdge 虛擬機。 app.go 文件在源代碼項目中以下:

package main

... ...

var (
    vm      *wasmedge.VM
    vmConf  *wasmedge.Configure
    counter uint64
)

func main() {
    // Initialize WasmEdge's VM
    initVM()
    defer vm.Delete()
    defer vmConf.Delete()

    // Connect to Zipper service
    cli, err := client.NewServerless("image-recognition").Connect("localhost", 9000)
    if err != nil {
        log.Print("❌ Connect to zipper failure: ", err)
        return
    }

    defer cli.Close()
    cli.Pipe(Handler)
}

// Handler process the data in the stream
func Handler(rxStream rx.RxStream) rx.RxStream {
    stream := rxStream.
        Subscribe(ImageDataKey).
        OnObserve(decode).
        Encode(0x11)
        
    return stream
}

// decode Decode and perform image recognition
var decode = func(v []byte) (interface{}, error) {
    // get image binary
    p, _, _, err := y3.DecodePrimitivePacket(v)
    if err != nil {
        return nil, err
    }
    img := p.ToBytes()

    // recognize the image
    res, err := vm.ExecuteBindgen("infer", wasmedge.Bindgen_return_array, img)
    
    return hash, nil
}

... ...

// initVM initialize WasmEdge's VM
func initVM() {
    wasmedge.SetLogErrorLevel()
    vmConf = wasmedge.NewConfigure(wasmedge.WASI)
    vm = wasmedge.NewVMWithConfig(vmConf)

    var wasi = vm.GetImportObject(wasmedge.WASI)
    wasi.InitWasi(
        os.Args[1:],     /// The args
        os.Environ(),    /// The envs
        []string{".:."}, /// The mapping directories
        []string{},      /// The preopens will be empty
    )

    /// Register WasmEdge-tensorflow and WasmEdge-image
    var tfobj = wasmedge.NewTensorflowImportObject()
    var tfliteobj = wasmedge.NewTensorflowLiteImportObject()
    vm.RegisterImport(tfobj)
    vm.RegisterImport(tfliteobj)
    var imgobj = wasmedge.NewImageImportObject()
    vm.RegisterImport(imgobj)

    /// Instantiate wasm
    vm.LoadWasmFile("rust_mobilenet_food_lib_bg.wasm")
    vm.Validate()
    vm.Instantiate()
}

運行

最後,咱們啓動 YoMo 並查看整個數據處理管道的運行狀況。從項目文件夾啓動 YoMo CLI 應用程序。 yaml文件定義了 YoMo 應聽取的端口以及觸發傳入數據的工做流 handler。請注意,流名稱 image-recognition 與上面提到的數據 handler app.go 相匹配。

$ yomo serve -c ./zipper/workflow.yaml

經過運行上面提到的 app.go 程序啓動 handler 程序。

$ cd flow
$ go run --tags "tensorflow image" app.go

經過給 YoMo 發送數據來啓動模擬數據源。 視頻是一系列圖片幀。 app.go 中的 WasmEdge 函數將針對視頻中的每一個圖片幀進行調用。

# Download a video file
$ wget -P source 'https://github.com/yomorun/yomo-wasmedge-tensorflow/releases/download/v0.1.0/hot-dog.mp4'

# Stream the video to YoMo
$ go run ./source/main.go ./source/hot-dog.mp4

你能夠在控制檯中看到 WasmEdge handler函數的輸出。它將在視頻的每一個圖片幀中檢測到的對象的名稱進行打印。

展望將來

本文討論瞭如何使用 YoMo 框架中的 WasmEdge Tensorflow API 和 Golang SDK 來幾近實時地處理圖片流。

與 YoMo 合做,咱們很快將在智能工廠的實際生產中部署 WasmEdge,用於各類裝配線上的任務。 WasmEdge 是邊緣計算的軟件 runtime!

相關文章
相關標籤/搜索