ai cast 向けサンプルコードの実行

ai cast 向けサンプルコードの入手 #

ai cast 向けの サンプルコードはこちらにあります。

$ git clone https://github.com/Idein/actcast-app-examples.git

ai cast 向けサンプルコードの実行 #

Actcast Documents : アプリケーション開発チュートリアル に記載の方法で開発環境をセットアップします。

サンプルコードの imagenet-classification-for-aicast ディレクトリへ移動し以下を実行します。

$ make

Actcast Documents : ローカルデバイス上でのアプリケーションテスト 記載の内容でセットアップした ai cast を利用しサンプルコードの動作を確認します。

サンプルコード補足事項 #

共有ライブラリを Python から呼び出しています。 各ファイルの概要について以下に記載します。

app/resnet_v1_18.hef #

resnet_v1_18.onnx から hef ファイル (Hailo にロードされるバイナリファイル) に変換するまでの例を記載します。

他のモデルの変換方法やより詳細な情報は Hailo Developer Zone で配布されているドキュメント ( Hailo DataflowCompiler User Guide ) をご参照ください。ダウンロードのためには https://hailo.ai/ でアカウントを作成し Developer Zone Access を申請する必要があります。

変換時のエラー等は Hailo へ Contact Customer Support からお問い合わせください。

Hailo ドキュメント ( Hailo DataflowCompiler User Guide ) の記載内容の通り、Dataflow Compiler をインストールします。

onnx から har への変換 #

Netron で INPUTS , OUTPUTS を確認し start-node-names input.1 , end-node-names 191 をそれぞれ指定しています。

$ hailo parser onnx resnet_v1_18.onnx --start-node-names input.1 --end-node-names 191 --hw-arch hailo8

newtron_resnet_v1_18.png

Allocator Script (ALLS) ファイルの用意 #

Allocator Script(もしくは Model Script)とは Hailo チップのリソースをどのように割り当てる(Allocate する)かの指示を記述するファイルです。 Hailo チップで行える処理には、Neural Network の実行以外に例えば入力に対するリサイズ、正規化、RGB->YUV 変換などの前処理を行うことが可能です。こういった処理を Hailo 上で実行する為には ALLS ファイルにその指示を記述する必要があります。

normalization1 = normalization([123.675, 116.28, 103.53], [58.395, 57.12, 57.375])
model_optimization_config(calibration, batch_size=1, calibset_size=10000)

Calibration Dataset の用意 #

32-bit float f から 8-bit(もしくは 4-bit) int i への量子化は以下の式によって行われます。

f = scale * (i - bias)

scale と bias を決めるためにサンプルとなる入力データ ( Calibration Dataset ) を渡す必要があります。 imagenette を用い .npy として Calibration Dataset (calib_set.npy) を作成します。ResNet-V1-18 の場合は 224x224 の RGB 画像を入力に取るため(N, 224, 224, 3)の配列を作成します。

import numpy as np
import glob
from PIL import Image

images = []
for f in glob.glob('imagenette2-320/**/*.JPEG', recursive=True):
    image = Image.open(f).convert('RGB').resize((224, 224))
    images.append(np.asarray(image))
np.save('calib_set.npy', np.stack(images, axis=0))

モデルの量子化を実行 #

以下のように Allocator Script ( resnet_v1_18.alls ) , Calibration Dataset ( calib_set.npy ) を指定してモデルの量子化を実行します。量子化後のモデルは resnet_v1_18_quantized.har として保存されます。

$ hailo optimize resnet_v1_18.har --calib-set-path calib_set.npy --model-script resnet_v1_18.alls --hw-arch hailo8

hef へのコンパイル #

以下を実行して Hailo にロードされるバイナリファイル ( .hef ) を生成します。

$ hailo compiler resnet_v1_18_quantized.har --hw-arch hailo8

Dockerfile #

Hailo 用にモデルをコンパイルするための Docker イメージを作ります。

FROM idein/cross-rpi:armv6-slim

ENV SYSROOT /home/idein/x-tools/armv6-rpi-linux-gnueabihf/armv6-rpi-linux-gnueabihf/sysroot

ADD root.tar $SYSROOT
RUN sudo rm $SYSROOT/usr/lib/libhailort.so
RUN sudo ln -s $SYSROOT/usr/lib/libhailort.so.4.10.0 $SYSROOT/usr/lib/libhailort.so

src/resnet_v1_18.c #

#include <assert.h>
#include "hailo/hailort.h"

#define MAX_EDGE_LAYERS (16)
#define HEF_FILE ("resnet_v1_18.hef")

static hailo_vdevice vdevice = NULL;
static hailo_hef hef = NULL;
static hailo_configure_params_t config_params = {0};
static hailo_configured_network_group network_group = NULL;
static size_t network_group_size = 1;
static hailo_input_vstream_params_by_name_t input_vstream_params[MAX_EDGE_LAYERS] = {0};
static hailo_output_vstream_params_by_name_t output_vstream_params[MAX_EDGE_LAYERS] = {0};
static hailo_activated_network_group activated_network_group = NULL;
static size_t vstreams_infos_size = MAX_EDGE_LAYERS;
static hailo_vstream_info_t vstreams_infos[MAX_EDGE_LAYERS] = {0};
static hailo_input_vstream input_vstreams[MAX_EDGE_LAYERS] = {NULL};
static hailo_output_vstream output_vstreams[MAX_EDGE_LAYERS] = {NULL};
static size_t input_vstreams_size = MAX_EDGE_LAYERS;
static size_t output_vstreams_size = MAX_EDGE_LAYERS;

int infer(unsigned char *input0, float *out0)
{
    unsigned char q_out0[1000];
    hailo_status status = HAILO_UNINITIALIZED;
    /* Feed Data */
    status = hailo_vstream_write_raw_buffer(input_vstreams[0], input0, 224 * 224 * 3);
    assert(status == HAILO_SUCCESS);
    status = hailo_flush_input_vstream(input_vstreams[0]);
    assert(status == HAILO_SUCCESS);

    status = hailo_vstream_read_raw_buffer(output_vstreams[0], q_out0, 1000);
    assert(status == HAILO_SUCCESS);
    /* dequantize */

    float scale = vstreams_infos[1].quant_info.qp_scale;
    float zp = vstreams_infos[1].quant_info.qp_zp;
    for (int i = 0; i < 1000; i++)
        out0[i] = scale * (q_out0[i] - zp);

    return status;
}

int init()
{
    hailo_status status = HAILO_UNINITIALIZED;

    status = hailo_create_vdevice(NULL, &vdevice);
    assert(status == HAILO_SUCCESS);

    status = hailo_create_hef_file(&hef, HEF_FILE);
    assert(status == HAILO_SUCCESS);

    status = hailo_init_configure_params(hef, HAILO_STREAM_INTERFACE_PCIE, &config_params);
    assert(status == HAILO_SUCCESS);

    status = hailo_configure_vdevice(vdevice, hef, &config_params, &network_group, &network_group_size);
    assert(status == HAILO_SUCCESS);

    status = hailo_make_input_vstream_params(network_group, true, HAILO_FORMAT_TYPE_AUTO,
        input_vstream_params, &input_vstreams_size);
    assert(status == HAILO_SUCCESS);

    status = hailo_make_output_vstream_params(network_group, true, HAILO_FORMAT_TYPE_AUTO,
        output_vstream_params, &output_vstreams_size);
    assert(status == HAILO_SUCCESS);

    status = hailo_hef_get_all_vstream_infos(hef, NULL, vstreams_infos, &vstreams_infos_size);
    assert(status == HAILO_SUCCESS);

    status = hailo_create_input_vstreams(network_group, input_vstream_params, input_vstreams_size, input_vstreams);
    assert(status == HAILO_SUCCESS);

    status = hailo_create_output_vstreams(network_group, output_vstream_params, output_vstreams_size, output_vstreams);
    assert(status == HAILO_SUCCESS);

    status = hailo_activate_network_group(network_group, NULL, &activated_network_group);
    assert(status == HAILO_SUCCESS);

    return status;
}

void destroy() {
    (void) hailo_deactivate_network_group(activated_network_group);
    (void) hailo_release_output_vstreams(output_vstreams, output_vstreams_size);
    (void) hailo_release_input_vstreams(input_vstreams, input_vstreams_size);
    (void) hailo_release_hef(hef);
    (void) hailo_release_vdevice(vdevice);
}

Makefile #

SOURCE=resnet_v1_18.c
TARGET=libresnet_v1_18.so

all: app/$(TARGET)

app/$(TARGET): src/$(SOURCE)
	docker build -t cross-rpi .
	docker run -it --rm -d --name cross cross-rpi /bin/bash
	docker cp src cross:/home/idein/src
	docker exec -it cross armv6-rpi-linux-gnueabihf-gcc -W -Wall -Wextra -Werror -O2 -pipe -fPIC -std=c99 -I src src/$(SOURCE) -mcpu=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard -shared -o $(TARGET) -lhailort -lpthread
	docker cp cross:/home/idein/$(TARGET) app/$(TARGET)
	docker stop cross

root.tar #

Actcast アプリケーションから Hailo にアクセスするためのファイル群です。

アプリのルートディレクトリに root.tar という名前のファイルがある場合にファイル内をアプリの docker image に含める actdk の機能を利用します。

app/model.py #

from ctypes import cdll
import numpy as np

class Model:
    def __init__(self):
        self.lib = cdll.LoadLibrary('./libresnet_v1_18.so')
        self.lib.init()

    def __del__(self):
        self.lib.destroy()

    def infer(self, image):
        out = np.zeros(1000, dtype=np.float32)
        self.lib.infer(
            image.ctypes.data,
            out.ctypes.data
            )
        return out,

app/main (一部) #

#!/usr/bin/python3
import argparse
import subprocess
import time
import os
from PIL import Image, ImageDraw, ImageFont
import actfw_core
from actfw_core.task import Pipe, Consumer
from actfw_core.capture import V4LCameraCapture
import actfw_raspberrypi
from actfw_raspberrypi.vc4 import Display
import numpy as np
from model import Model
from configuration import *

(CAPTURE_WIDTH, CAPTURE_HEIGHT) = (224, 224)  # capture image size
(DISPLAY_WIDTH, DISPLAY_HEIGHT) = (640, 480)  # display area size

class Classifier(Pipe):

    def __init__(self, settings, capture_size):
        super(Classifier, self).__init__()
        self.model = Model()
        self.settings = settings
        self.capture_size = capture_size

    def proc(self, frame):
        captured_image = Image.frombuffer('RGB', self.capture_size, frame.getvalue(), 'raw', 'RGB')
        if self.settings['resize_method'] == 'crop':
            if self.capture_size != (CAPTURE_WIDTH, CAPTURE_HEIGHT):
                w, h = self.capture_size
                captured_image = captured_image.crop((w // 2 - CAPTURE_WIDTH // 2, h // 2 - CAPTURE_HEIGHT // 2, w // 2 + CAPTURE_WIDTH // 2, h // 2 + CAPTURE_HEIGHT // 2))
        else:
            w, h = self.capture_size
            l = min(w, h)
            captured_image = captured_image.crop((w // 2 - l // 2, h // 2 - l // 2, w // 2 + l // 2, h // 2 + l // 2))
            captured_image = captured_image.resize((CAPTURE_WIDTH, CAPTURE_HEIGHT))
        input_image = np.asarray(captured_image)
        probs, = self.model.infer(input_image)
        return (captured_image, probs)

次のステップ: ai cast を Actcast に登録する

前のステップ: ai cast での Actsim セットアップ


ai cast チュートリアル に戻る