亚洲精品久久久久久久久久久,亚洲国产精品一区二区制服,亚洲精品午夜精品,国产成人精品综合在线观看,最近2019中文字幕一页二页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

基于RV1126開發(fā)板的AI算法開發(fā)流程

ljx2016 ? 來源:ljx2016 ? 作者:ljx2016 ? 2025-04-18 10:47 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

1. 概述

AI算法開發(fā)流程由以下流程組成:

wKgZPGgByNSAJK6EAABmkGKw1LA163.png

2. 需求分析

算法的功能常??梢杂靡粋€短詞概括,如人臉識別、司機行為檢測、商場顧客行為分析等系統(tǒng),但是卻需要依靠多個子算法的有序運作才能達成。其原因在于子算法的神經網絡結構各有不同,這些結構的差異化優(yōu)化了各個子算法在其功能上的實現效果。

模型分類名稱 功能
目標檢測模型 檢測圖像中是否存在目標物體,并給出其在圖像中的具體坐標,可同時提供分類功能
關鍵點定位模型 檢測圖像中的特定目標,并標出關鍵點位,常見骨骼點位、面部器官點位等
相似度比對模型 比較兩個不同的個體的相似度,常見人臉、豬臉識別,商品識別
分割模型 檢測圖像中存在的物體,按輪廓或其他標準分割出物體所在的不規(guī)則像素區(qū)域,可同時帶分類功能
OCR模型 識別字體

以下我們列出組成例子:

例子a: 人臉識別算法 = 人臉檢測(檢測模型)+ 矯正人臉姿態(tài)模型(關鍵點定位模型)+ 人臉比對模型(相似度比對模型)

??例子b: 司機行為檢測算法 = 人臉識別算法(具體組成如上例)+ 抽煙玩手機等危險動作識別(檢測模型) + 疲勞駕駛檢測(關鍵點定位模型)+ 車道線偏移檢測(檢測模型)

??例子c: 商場分析 = 人臉識別算法(具體組成如首例)+ 人體跟蹤算法(檢測模型 + 相似度比對模型)

??只有在確定了具體需求所需要的步驟后,我們才能有的放矢的采集數據,優(yōu)化模型,訓練出合乎我們需求的模型。

3. 準備數據

即使準備數據在大多數人看來是繁瑣重復的工作,這期間仍有許多細節(jié)需要注意的。

數據樣本需要良好的多樣性。樣本多樣性是保證算法泛化能力的基礎,例如想要識別農產品的功能中,假如我們只是搜集紅蘋果的數據,那么訓練出來的網絡就很難將綠色的蘋果準確識別出。同時還需要加入充足的負樣本,例如我們只是單純地把農產品的圖片數據喂給神經網絡,那么我們就很難期望訓練出來的神經網絡可以有效區(qū)分真蘋果還有塑料蘋果。為了增強算法的可靠性,我們就需要充分的考慮到實際應用場景中會出現什么特殊情況,并將該種情況的數據添加進我們的訓練數據里面。

??數據樣本是否可被壓縮。單個樣本數據的大小往往決定了網絡模型的運行效率,在保證效果的情況下,應當盡量壓縮圖片的大小來提高運行效率,如112x112的圖片,在相同環(huán)境下的處理速度將比224x224圖片的快4倍左右。但是有些場景卻是需要完整的圖片來保證圖片信息不會丟失,如山火檢測一般需要很高的查全率,過度的壓縮都會導致查全率下降導致算法效果不佳。

??數據需要合適正確的標注與預處理。數據標注在一定程度上決定了訓練效果能達到的高度,過多的錯誤標記將帶來一個無效的訓練結果。而數據的預處理,是指先對數據做出一定的操作,使其更容易被機器讀懂,例如農產品在畫面中的位置,如果是以像素點為單位,如農產品的中心點在左起第200個像素點,這種處理方式雖然直觀準確,但是會因為不同像素點之間的差距過大,導致訓練困難,這個時候就需要將距離歸一化,如中心點在圖中左起40%寬的位置上。而音頻的預處理更為多樣,不同的分詞方式、傅里葉變換都會影響訓練結果。

??數據的準備不一定得在一開始就做到毫無遺漏。模型訓練完成后,如果有一定的效果但還存在部分缺陷,就可以考慮添加或優(yōu)化訓練樣本數據,對已有模型進行復訓練修正。即使是后期的優(yōu)化,增添合適的照片往往是最有效的效果。所以對數據的考量優(yōu)化應該貫穿整個流程,不能在只是在開頭階段才關注數據樣本的問題。

4. 選取模型

通常來講,對于同一個功能,存在著不同的模型,它們在精度、計算速率上各有長處。模型來發(fā)現主要來源于學術研究、公司之間的公開比賽等,所以在研發(fā)過程中,就需從業(yè)人員持續(xù)地關注有關ai新模型的文章;同時對舊模型的積累分析也是十分重要的,這里我們在 下表 中列出目前在各個功能上較優(yōu)的模型結構以供參考。

模型類型 模型名稱 效果 速率
檢測模型 yolov5 精度高 中等
檢測模型 ssd 精度中等,對小物體的識別一般 快速
關鍵點定位模型 mtcnn 精度一般,關鍵點較少
關鍵點的定位模型 openpose 精度高,關鍵點多 中等
相似度比對模型 resnet18 精度高 快速
相似度比對模型 resnet50 精度高,魯棒性強,有比較強的抗干擾能力 中等
分割模型 mask-rcnn 精度中,分割出畫面中的不規(guī)則物體

5. 訓練模型

對于有AI開發(fā)經驗的研發(fā)人員,可以用自己熟悉的常見框架訓練即可,如tensorflow、pytorch、caffe等主流框架,我們的開發(fā)套件可以將其轉為EASY EAI Nano的專用模型。

6. 模型轉換

研發(fā)tensorflow、pytorch、caffe等自主的模型后,需先將模型轉換為rknn模型。同時一般需要對模型進行量化與預編譯,以達到運行效率的提升。

6.1 模型轉換環(huán)境搭建

6.1.1 概述

模型轉換環(huán)境搭建流程如下所示:

wKgZO2gByNSAWaH5AABSPNTuEOQ674.png

6.1.2 下載模型轉換工具

為了保證模型轉換工具順利運行,請下載網盤里“AI算法開發(fā)/RKNN-Toolkit模型轉換工具/rknn-toolkit-v1.7.1/docker/rknn-toolkit-1.7.1-docker.tar.gz”。

網盤下載鏈接:https://pan.baidu.com/s/1LUtU_-on7UB3kvloJlAMkA 提取碼:teuc

6.1.3 把工具移到ubuntu18.04

把下載完成的docker鏡像移到我司的虛擬機ubuntu18.04的rknn-toolkit目錄,如下圖所示:

wKgZPGgByNSAS60YAACKKS_ejJo272.png

6.1.4 運行模型轉換工具環(huán)境

(1)打開終端

在該目錄打開終端:

wKgZO2gByNWATdx4AADmNtserU4570.png

(2)加載docker鏡像

執(zhí)行以下指令加載模型轉換工具docker鏡像:

docker load --input /home/developer/rknn-toolkit/rknn-toolkit-1.7.1-docker.tar.gz

(3)進入鏡像bash環(huán)境

執(zhí)行以下指令進入鏡像bash環(huán)境:

docker run -t -i --privileged -v /dev/bus/usb:/dev/bus/usb rknn-toolkit:1.7.1 /bin/bash

現象如下圖所示:

wKgZPGgByNWATuiXAAB1-Itbk9o994.png

(4)測試環(huán)境

輸入“python”加載python相關庫,嘗試加載rknn庫,如下圖環(huán)境測試成功:

wKgZO2gByNWAAjRnAACqhZg-QVo746.png

至此,模型轉換工具環(huán)境搭建完成。

6.2 模型轉換示例

6.2.1 模型轉換流程介紹

EASY EAI Nano支持.rknn后綴的模型的評估及運行,對于常見的tensorflow、tensroflow lite、caffe、darknet、onnx和Pytorch模型都可以通過我們提供的 toolkit 工具將其轉換至 rknn 模型,而對于其他框架訓練出來的模型,也可以先將其轉至 onnx 模型再轉換為 rknn 模型。

模型轉換操作流程入下圖所示:

wKgZPGgByNWAQ9hfAADxwNhuUHo741.png

6.2.2 模型轉換Demo下載

下載百度網“AI算法開發(fā)/模型轉換Demo/model_convert.tar.bz2”。把model_convert.tar.bz2解壓到虛擬機,如下圖所示:

wKgZO2gByNWAVSleAACZ_VJVDfc658.png

下載鏈接:https://pan.baidu.com/s/1OjDXM8kGXDbn5BqIeErpmw 提取碼:drv0

6.2.3 進入模型轉換工具docker環(huán)境

執(zhí)行以下指令把工作區(qū)域映射進docker鏡像,其中/home/developer/rknn-toolkit/model_convert為工作區(qū)域,/test為映射到docker鏡像,/dev/bus/usb:/dev/bus/usb為映射usb到docker鏡像:

docker run -t -i --privileged -v /dev/bus/usb:/dev/bus/usb -v /home/developer/rknn-toolkit/model_convert:/test rknn-toolkit:1.7.1 /bin/bash

執(zhí)行成功如下圖所示:

wKgZPGgByNaAJNRWAACHBuVMM9s967.png

6.2.4 模型轉換操作說明

(1)模型轉換Demo目錄結構

模型轉換測試Demo由coco_object_detect和quant_dataset組成。coco_object_detect存放軟件腳本,quant_dataset存放量化模型所需的數據。如下圖所示:

wKgZO2gByNaACJxdAACpCaeJenQ984.png

coco_object_detect文件夾存放以下內容,如下圖所示:

wKgZPGgByNaAdbEsAADdjKdwfOk697.png

(2)生成量化圖片列表

在docker環(huán)境切換到模型轉換工作目錄:

cd /test/coco_object_detect/

如下圖所示:

wKgZO2gByNaAfGhlAAB0UqvMKo0126.png

執(zhí)行gen_list.py生成量化圖片列表:

python gen_list.py

命令行現象如下圖所示:

wKgZPGgByNeAbwC5AACRhzy0UYU596.png

生成“量化圖片列表”如下文件夾所示:

wKgZO2gByNeABJRzAADDeVg5sdA444.png

(3)onnx模型轉換為rknn模型

rknn_convert.py腳本默認進行int8量化操作,腳本代碼清單如下所示:

import os
import urllib
import traceback
import time
import sys
import numpy as np
import cv2
from rknn.api import RKNN

ONNX_MODEL = 'yolov5_coco.onnx'
RKNN_MODEL = './yolov5_coco_rv1126.rknn'
DATASET = './pic_path.txt'
QUANTIZE_ON = True

if __name__ == '__main__':

	# Create RKNN object
	rknn = RKNN(verbose=True)

	if not os.path.exists(ONNX_MODEL):
		print('model not exist')
		exit(-1)

	# pre-process config
	print('--> Config model')
	rknn.config(reorder_channel='0 1 2',
			    mean_values=[[0, 0, 0]],
			    std_values=[[255, 255, 255]],
			    optimization_level=3,
			    target_platform = 'rv1126',
			    output_optimize=1,
			    quantize_input_node=QUANTIZE_ON)
	print('done')

	# Load ONNX model
	print('--> Loading model')
	ret = rknn.load_onnx(model=ONNX_MODEL)
	if ret != 0:
		print('Load yolov5 failed!')
		exit(ret)
	print('done')

	# Build model
	print('--> Building model')
	ret = rknn.build(do_quantization=QUANTIZE_ON, dataset=DATASET)
	if ret != 0:
		print('Build yolov5 failed!')
		exit(ret)
	print('done')

	# Export RKNN model
	print('--> Export RKNN model')
	ret = rknn.export_rknn(RKNN_MODEL)
	if ret != 0:
		print('Export yolov5rknn failed!')
		exit(ret)
	print('done')

在執(zhí)行rknn_convert.py腳本進行模型轉換:

python rknn_convert.py

生成模型如下圖所示,此模型可以在rknn環(huán)境和EASY EAI Nano環(huán)境運行:

wKgZPGgByNeAfxmXAAJOjJBfq_Q159.png

(4)運行rknn模型

用yolov5_coco_test.py腳本在PC端的環(huán)境下可以運行rknn的模型,如下圖所示:

wKgZO2gByNeAdSAUAADYj2jhQ8w733.png

yolov5_coco_test.py腳本程序清單如下所示:

import os
import urllib
import traceback
import time
import sys
import numpy as np
import cv2
import random
from rknn.api import RKNN

RKNN_MODEL = 'yolov5_coco_rv1126.rknn'
IMG_PATH = './test.jpg'
DATASET = './dataset.txt'

BOX_THRESH = 0.25
NMS_THRESH = 0.6
IMG_SIZE = 640

CLASSES = ("person", "bicycle", "car","motorbike ","aeroplane ","bus ","train","truck ","boat","traffic light",
           "fire hydrant","stop sign ","parking meter","bench","bird","cat","dog ","horse ","sheep","cow","elephant",
           "bear","zebra ","giraffe","backpack","umbrella","handbag","tie","suitcase","frisbee","skis","snowboard","sports ball","kite",
           "baseball bat","baseball glove","skateboard","surfboard","tennis racket","bottle","wine glass","cup","fork","knife",
           "spoon","bowl","banana","apple","sandwich","orange","broccoli","carrot","hot dog","pizza ","donut","cake","chair","sofa",
           "pottedplant","bed","diningtable","toilet ","tvmonitor","laptop","mouse","remote ","keyboard ","cell phone","microwave ",
           "oven ","toaster","sink","refrigerator ","book","clock","vase","scissors ","teddy bear ","hair drier", "toothbrush")

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def xywh2xyxy(x):
    # Convert [x, y, w, h] to [x1, y1, x2, y2]
    y = np.copy(x)
    y[:, 0] = x[:, 0] - x[:, 2] / 2  # top left x
    y[:, 1] = x[:, 1] - x[:, 3] / 2  # top left y
    y[:, 2] = x[:, 0] + x[:, 2] / 2  # bottom right x
    y[:, 3] = x[:, 1] + x[:, 3] / 2  # bottom right y
    return y

def process(input, mask, anchors):
    anchors = [anchors[i] for i in mask]
    grid_h, grid_w = map(int, input.shape[0:2])

    box_confidence = sigmoid(input[..., 4])
    box_confidence = np.expand_dims(box_confidence, axis=-1)

    box_class_probs = sigmoid(input[..., 5:])

    box_xy = sigmoid(input[..., :2])*2 - 0.5

    col = np.tile(np.arange(0, grid_w), grid_w).reshape(-1, grid_w)
    row = np.tile(np.arange(0, grid_h).reshape(-1, 1), grid_h)
    col = col.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
    row = row.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
    grid = np.concatenate((col, row), axis=-1)
    box_xy += grid
    box_xy *= int(IMG_SIZE/grid_h)

    box_wh = pow(sigmoid(input[..., 2:4])*2, 2)
    box_wh = box_wh * anchors

    box = np.concatenate((box_xy, box_wh), axis=-1)

    return box, box_confidence, box_class_probs

def filter_boxes(boxes, box_confidences, box_class_probs):
	"""Filter boxes with box threshold. It's a bit different with origin yolov5 post process!

	# Arguments
		boxes: ndarray, boxes of objects.
		box_confidences: ndarray, confidences of objects.
		box_class_probs: ndarray, class_probs of objects.

	# Returns
		boxes: ndarray, filtered boxes.
		classes: ndarray, classes for boxes.
		scores: ndarray, scores for boxes.
	"""
	box_scores = box_confidences * box_class_probs
	box_classes = np.argmax(box_class_probs, axis=-1)
	box_class_scores = np.max(box_scores, axis=-1)
	pos = np.where(box_confidences[...,0] >= BOX_THRESH)


	boxes = boxes[pos]
	classes = box_classes[pos]
	scores = box_class_scores[pos]

	return boxes, classes, scores

def nms_boxes(boxes, scores):
    """Suppress non-maximal boxes.

    # Arguments
        boxes: ndarray, boxes of objects.
        scores: ndarray, scores of objects.

    # Returns
        keep: ndarray, index of effective boxes.
    """
    x = boxes[:, 0]
    y = boxes[:, 1]
    w = boxes[:, 2] - boxes[:, 0]
    h = boxes[:, 3] - boxes[:, 1]

    areas = w * h
    order = scores.argsort()[::-1]

    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)

        xx1 = np.maximum(x[i], x[order[1:]])
        yy1 = np.maximum(y[i], y[order[1:]])
        xx2 = np.minimum(x[i] + w[i], x[order[1:]] + w[order[1:]])
        yy2 = np.minimum(y[i] + h[i], y[order[1:]] + h[order[1:]])

        w1 = np.maximum(0.0, xx2 - xx1 + 0.00001)
        h1 = np.maximum(0.0, yy2 - yy1 + 0.00001)
        inter = w1 * h1

        ovr = inter / (areas[i] + areas[order[1:]] - inter)
        inds = np.where(ovr <= NMS_THRESH)[0]
        order = order[inds + 1]
    keep = np.array(keep)
    return keep


def yolov5_post_process(input_data):
    masks = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
    anchors = [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45],
              [59, 119], [116, 90], [156, 198], [373, 326]]

    boxes, classes, scores = [], [], []
    for input,mask in zip(input_data, masks):
        b, c, s = process(input, mask, anchors)
        b, c, s = filter_boxes(b, c, s)
        boxes.append(b)
        classes.append(c)
        scores.append(s)

    boxes = np.concatenate(boxes)
    boxes = xywh2xyxy(boxes)
    classes = np.concatenate(classes)
    scores = np.concatenate(scores)

    nboxes, nclasses, nscores = [], [], []
    for c in set(classes):
        inds = np.where(classes == c)
        b = boxes[inds]
        c = classes[inds]
        s = scores[inds]

        keep = nms_boxes(b, s)

        nboxes.append(b[keep])
        nclasses.append(c[keep])
        nscores.append(s[keep])

    if not nclasses and not nscores:
        return None, None, None

    boxes = np.concatenate(nboxes)
    classes = np.concatenate(nclasses)
    scores = np.concatenate(nscores)

    return boxes, classes, scores

def scale_coords(x1, y1, x2, y2, dst_width, dst_height):
	
	dst_top, dst_left, dst_right, dst_bottom = 0, 0, 0, 0
	gain = 0

	if dst_width > dst_height:
		image_max_len = dst_width
		gain = IMG_SIZE / image_max_len
		resized_height = dst_height * gain
		height_pading = (IMG_SIZE - resized_height)/2
		print("height_pading:", height_pading)
		y1 = (y1 - height_pading)
		y2 = (y2 - height_pading)
	
	print("gain:", gain)
	dst_x1 = int(x1 / gain)
	dst_y1 = int(y1 / gain)
	dst_x2 = int(x2 / gain)
	dst_y2 = int(y2 / gain)

	return dst_x1, dst_y1, dst_x2, dst_y2

def plot_one_box(x, img, color=None, label=None, line_thickness=None):
    tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1  # line/font thickness
    color = color or [random.randint(0, 255) for _ in range(3)]
    c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
    cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
    if label:
        tf = max(tl - 1, 1)  # font thickness
        t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
        c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
        cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA)  # filled
        cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)

def draw(image, boxes, scores, classes):
	"""Draw the boxes on the image.

	# Argument:
		image: original image.
		boxes: ndarray, boxes of objects.
		classes: ndarray, classes of objects.
		scores: ndarray, scores of objects.
		all_classes: all classes name.
	"""
	for box, score, cl in zip(boxes, scores, classes):

		x1, y1, x2, y2 = box
		print('class: {}, score: {}'.format(CLASSES[cl], score))
		print('box coordinate x1,y1,x2,y2: [{}, {}, {}, {}]'.format(x1, y1, x2, y2))
		x1 = int(x1)
		y1 = int(y1)
		x2 = int(x2)
		y2 = int(y2)

		dst_x1, dst_y1, dst_x2, dst_y2 = scale_coords(x1, y1, x2, y2, image.shape[1], image.shape[0])
		#print("img.cols:", image.cols)

		plot_one_box((dst_x1, dst_y1, dst_x2, dst_y2), image, label='{0} {1:.2f}'.format(CLASSES[cl], score))
			

		'''
		cv2.rectangle(image, (dst_x1, dst_y1), (dst_x2, dst_y2), (255, 0, 0), 2)
		cv2.putText(image, '{0} {1:.2f}'.format(CLASSES[cl], score),
					(dst_x1, dst_y1 - 6),
					cv2.FONT_HERSHEY_SIMPLEX,
					0.6, (0, 0, 255), 2)
		'''


def letterbox(im, new_shape=(640, 640), color=(0, 0, 0)):
    # Resize and pad image while meeting stride-multiple constraints
    shape = im.shape[:2]  # current shape [height, width]
    if isinstance(new_shape, int):
        new_shape = (new_shape, new_shape)

    # Scale ratio (new / old)
    r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])

    # Compute padding
    ratio = r, r  # width, height ratios
    new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
    dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # wh padding

    dw /= 2  # divide padding into 2 sides
    dh /= 2

    if shape[::-1] != new_unpad:  # resize
        im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
    top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
    left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
    im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add border
    return im, ratio, (dw, dh)

if __name__ == '__main__':

	# Create RKNN object
	rknn = RKNN(verbose=True)

	print('--> Loading model')
	ret = rknn.load_rknn(RKNN_MODEL)
	if ret != 0:
		print('load rknn model failed')
		exit(ret)
	print('done')

	# init runtime environment
	print('--> Init runtime environment')
	ret = rknn.init_runtime()
	# ret = rknn.init_runtime('rv1126', device_id='1126')
	if ret != 0:
		print('Init runtime environment failed')
		exit(ret)
	print('done')

	# Set inputs
	img = cv2.imread(IMG_PATH)
	letter_img, ratio, (dw, dh) = letterbox(img, new_shape=(IMG_SIZE, IMG_SIZE))
	letter_img = cv2.cvtColor(letter_img, cv2.COLOR_BGR2RGB)

	# Inference
	print('--> Running model')
	outputs = rknn.inference(inputs=[letter_img])

	print('--> inference done')

	# post process
	input0_data = outputs[0]
	input1_data = outputs[1]
	input2_data = outputs[2]

	input0_data = input0_data.reshape([3,-1]+list(input0_data.shape[-2:]))
	input1_data = input1_data.reshape([3,-1]+list(input1_data.shape[-2:]))
	input2_data = input2_data.reshape([3,-1]+list(input2_data.shape[-2:]))

	input_data = list()
	input_data.append(np.transpose(input0_data, (2, 3, 0, 1)))
	input_data.append(np.transpose(input1_data, (2, 3, 0, 1)))
	input_data.append(np.transpose(input2_data, (2, 3, 0, 1)))

	print('--> transpose done')

	boxes, classes, scores = yolov5_post_process(input_data)

	print('--> get result done')

	#img_1 = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
	if boxes is not None:
		draw(img, boxes, scores, classes)

	cv2.imwrite('./result.jpg', img)
	#cv2.imshow("post process result", img_1)
	#cv2.waitKeyEx(0)

	rknn.release()

執(zhí)行yolov5_coco_test.py腳本測試rknn模型:

python yolov5_coco_test.py

執(zhí)行后得到result.jpg如下圖所示:

wKgZO2gByNeAaMUcACQfv4w0v3Y402.png

(5)模型預編譯

由于rknn模型用NPU API在EASY EAI Nano加載的時候啟動速度會好慢,在評估完模型精度沒問題的情況下,建議進行模型預編譯。預編譯的時候需要通過EASY EAI Nano主板的環(huán)境,所以請務必接上adb口與ubuntu保證穩(wěn)定連接。

板子端接線如下圖所示,撥碼開關需要是adb:

wKgZPGgByNiAZo69ABuYkhN4g5g470.png

虛擬機要保證接上adb設備:

wKgZO2gByNmABV_GAAGxSQEPX9E014.png

由于在虛擬機里ubuntu環(huán)境與docker環(huán)境對adb設備資源是競爭關系,所以需要關掉ubuntu環(huán)境下的adb服務,且在docker里面通過apt-get安裝adb軟件包。以下指令在ubuntu環(huán)境與docker環(huán)境里各自執(zhí)行:

wKgZPGgByNmAcWffAADuLq22Nuc637.png

在docker環(huán)境里執(zhí)行adb devices,現象如下圖所示則設備連接成功:

wKgZO2gByNmASM3fAAF3nOnUodo477.png

運行precompile_rknn.py腳本把模型執(zhí)行預編譯:

python precompile_rknn.py

執(zhí)行效果如下圖所示,生成預編譯模型yolov5_coco_int8_rv1126_pre.rknn:

wKgZPGgByNmATbFDAAEt-oq36nc577.png

至此預編譯部署完成,模型轉換步驟已全部完成。生成如下預編譯后的int8量化模型:

wKgZO2gByNqAdiV2AAEGvxyFJKg906.png

6.3 模型轉換API說明

6.3.1 RKNN 初始化及對象釋放

在使用 RKNN-Toolkit 的所有 API 接口時,都需要先調用 RKNN()方法初始化 RKNN 對象,并 不再使用該對象時,調用該對象的 release()方法進行釋放。
初始化 RKNN 對象時,可以設置 verboseverbose_file 參數,以打印詳細的日志信息。其中 verbose 參數指定是否要在屏幕上打印詳細日志信息;如果設置了 verbose_file 參數,且 verbose 參 數值為 True,日志信息還將寫到該參數指定的文件中。如果出現 Error 級別的錯誤,而 verbose_file 又被設為 None,則錯誤日志將自動寫到 log_feedback_to_the_rknn_toolkit_dev_team.log 文件中。
反饋錯誤信息給 Rockchip NPU 團隊時,建議反饋完整的錯誤日志。

舉例如下:

# 將詳細的日志信息輸出到屏幕,并寫到 mobilenet_build.log 文件中 
rknn = RKNN(verbose=True, verbose_file=’./mobilenet_build.log’) 
# 只在屏幕打印詳細的日志信息 
rknn = RKNN(verbose=True) 
… 
rknn.release()

6.3.2 RKNN 模型配置

在構建 RKNN 模型之前,需要先對模型進行通道均值、通道順序、量化類型等的配置,這些 操作可以通過 config 接口進行配置。

API config
描述 設置模型參數
參數 batch_size:批處理大小,默認值為 100。量化時將根據該參數決定每一批次參與運算的數據量,以校正量化結果。如果dataset中的數據量小于batch_size,則該參數值將自動調整為dataset中的數據量。如果量化時出現內存不足的問題,建議將這個值設小一點,例如 8。
mean_values:輸入的均值。該參數與 channel_mean_value參數不能同時設置。參數格式是一個列表,列表中包含一個或多個均值子列表,多輸入模型對應多個子列表,每個子列表的長度與該輸入的通道數一致,例如[[128,128,128]],表示一個輸入的三個通道的值減去128。如果 reorder_channel設置成’2 1 0‘,則優(yōu)先做通道調整,再做減均值。
std_values:輸入的歸一化值。該參數與channel_mean_value參數不能同時設置。參數格式是一個列表,列表中包含一個或多個歸一化值子列表,多輸入模型對應多個子列表,每個子列表的長度與該輸入的通道數一致,例如[[128,128,128]],表示設置一個輸入的三個通道的值減去均值后再除以128。如果 reorder_channel設置成’2 1 0‘,則優(yōu)先做通道調整,再減均值和除以歸一化值。
epochs:量化時的迭代次數,每迭代一次,就選擇 batch_size 指定數量的圖片進行量化校正。默認值為-1,此時 RKNN-Toolkit 會根據dataset中的圖片數量自動計算迭代次數以最大化利用數據集中的數據。
reorder_channel:表示是否需要對圖像通道順序進行調整,只對三通道輸入有效?!? 1 2’表示按照輸入的通道順序來推理,比如圖片輸入時是 RGB,那推理的時候就根據 RGB順序傳給輸入層;’2 1 0’表示會對輸入做通道轉換,比如輸入時通道順序是RGB,推理時會將其轉成 BGR,再傳給輸入層,同樣的,輸入時通道的順序為 BGR 的話,會被轉成 RGB 后再傳給輸入層。如果有多個輸入,每個輸入的參數以“#”進行分 隔,如 ’0 1 2#0 1 2’。該參數的默認值是 None,對于 Caffe 框架的三通道輸入模型,表示需要做通道順序的調整,其他框架的三通道輸入模型,默認不做通道順序調整。
need_horizontal_merge:是否需要進行水平合并,默認值為 False。如果模型是 inception v1/v3/v4,建議開啟該選項,可以提高推理時的性能。
quantized_dtype:量化類型,目前支持的量化類型有 asymmetric_quantized-u8、dynamic_fixed_point-i8、dynamic_fixed_point-i16,默認值為 asymmetric_quantized-u8。
quantized_algorithm: 量化參數優(yōu)化算法。當前版本支持的算法有:normal,mmse和kl_divergence,默認值為normal。其中normal 算法的特點是速度較快。而mmse算 法,因為需要對量化參數進行多次調整,其速度會慢很多,但通常能得到比normal算法更高的精度;kl_divergence所用時間會比 normal多一些,但比 mmse 會少很多,在某些場景下可以得到較好的改善效果。
mmse_epoch:mmse量化算法的迭代次數,默認值為 3。通常情況下,迭代次數越多,精度往往越高。
optimization_level:模型優(yōu)化等級。通過修改模型優(yōu)化等級,可以關掉部分或全部模型轉換過程中使用到的優(yōu)化規(guī)則。該參數的默認值為 3,打開所有優(yōu)化選項。值為2或1時關閉一部分可能會對部分模型精度產生影響的優(yōu)化選項,值為0時關閉所有 優(yōu)化選項。
target_platform:指定RKNN模型目標運行平臺。目前支持RK1806、RK1808、RK3399Pro、RV1109和RV1126。其中基于RK1806、RK1808 或 RK3399Pro生成的RKNN模型可以在這三個平臺上通用,基于 RV1109或RV1126生成的RKNN模型可以在這兩個平臺通用。如果模型要在RK1806、RK1808或RK3399Pro上運行,該參數的值可以是 [“rk1806”], [“rk1808”], [“rk3399pro”]或 [“rk1806”, “rk1808”, “rk3399pro”]等;如果模型要在RV1109 或 RV1126 上運行,該參數的值可以是 [“rv1126”], [“rv1109”]或[“rv1109”, “rv1126”]等。這個參數的值不可以是類似[“rk1808”, “rv1126”]這樣的組合,因為這兩款芯片互不兼容。如果不填該參數,則默認是 [“rk1808”],生成的RKNN 模型可以在 RK1806、RK1808和RK3399Pro 平臺上運行。 該參數的值大小寫不敏感。
quantize_input_node: 開啟后無論模型是否量化,均強制對模型的輸入節(jié)點進行量化。 輸入節(jié)點被量化的模型,在部署時會有性能優(yōu)勢,rknn_input_set接口的耗時更少。當 RKNN-Toolkit 量化沒有按理想情況對輸入節(jié)點進行量化(僅支持輸入為圖片的模型)、或用戶選擇加載深度學習框架已生成的量化模型時可以啟用(這種情況下,第一層的quantize_layer會被合并到輸入節(jié)點)。默認值為 False。
merge_dequant_layer_and_output_node: 將模型輸出節(jié)點與上一層的dequantize_layer,合并成一個被量化的輸出節(jié)點,允許模型在部署時返回 uint8或 float類型的推理結果。此配置僅對加載深度學習框架已生成的量化模型有效。默認為 False。
返回值

舉例如下:

# model config 
rknn.config(mean_values=[[103.94, 116.78, 123.68]], 
             std_values=[[58.82, 58.82, 58.82]], 
             reorder_channel=’0 1 2’, 
             need_horizontal_merge=True, 
             target_platform=[‘rk1808’, ‘rk3399pro’]

6.3.3 模型加載

RKNN-Toolkit 目前支持Caffe, Darknet, Keras, MXNet, ONNX, PyTorch, TensorFlow,和TensorFlow Lite等模型的加載轉換,這些模型在加載時需調用對應的接口,以下為這些接口的詳細說明。

(1)Caffe 模型加載接口

API load_caffe
描述 加載 caffe 模型
參數 model:caffe 模型文件(.prototxt 后綴文件)所在路徑。
proto:caffe模型的格式(可選值為’caffe’或’lstm_caffe’)。為了支持RNN 模型,增加了相關網絡層的支持,此時需要設置 caffe 格式為’lstm_caffe’。
blobs:caffe模型的二進制數據文件(.caffemodel 后綴文件)所在路徑。該參數值可以為 None,RKNN-Toolkit將隨機生成權重等參數。
返回值 0:導入成功
-1:導入失敗

舉例如下:

#從當前路徑加載 mobilenet_v2 模型 
ret = rknn.load_caffe(model=’./mobilenet_v2.prototxt’, 
                                 proto=’caffe’, 
                                 blobs=’./mobilenet_v2.caffemodel’)

(2)Darknet 模型加載接口

API load_darknet
描述 加載 Darknet模型
參數 model:Darknet模型文件(.cfg 后綴)所在路徑。
weight:權重文件(.weights 后綴)所在路徑
返回值 0:導入成功
-1:導入失敗

舉例如下:

# 從當前目錄加載yolov3-tiny模型 
ret= rknn.load_darknet(model=‘./yolov3-tiny.cfg’, 
                                  weight=’./yolov3.weights’)

(3)Keras 模型加載接口

API load_keras
描述 加載 Keras模型
參數 model:Keras模型文件(后綴為.h5)。必填參數。
convert_engine:轉換引擎,可以是’Keras’或’tflite’。默認轉換引擎為 Keras。可選參數。
返回值 0:導入成功
-1:導入失敗

舉例如下:

# 從當前目錄加載 xception 模型 
ret = rknn.load_keras(model=’./xception_v3.h5’)

(4)MXNet 模型加載接口

API load_mxnet
描述 加載 MXNet模型
參數 symbol:MXNet模型的網絡結構文件,后綴是json。必填參數。
params:MXnet模型的參數文件,后綴是 params。必填參數。
input_size_list :每個輸入節(jié)點對應的圖片的尺寸和通道數。例如 [[1,224,224],[ 3,224,224]]表示有兩個輸入,其中一個輸入的 shape是[1,224,224],另外一個輸入的 shape 是[3,224,224]。必填參數。
返回值 0:導入成功
-1:導入失敗

舉例如下:

# 從當前目錄加載 resnext50 模型 
ret = rknn.load_mxnet(symbol=’resnext50_32x4d-symbol.json’, 
                              params=’resnext50_32x4d-4ecf62e2.params’, 
                              input_size_list=[[3,224,224]] )

(5)ONNX 模型加載

API load_onnx
描述 加載ONNX模型
參數 model:ONNX模型文件(.onnx 后綴)所在路徑。
inputs:指定模型的輸入節(jié)點,數據類型為列表。例如示例中的 resnet50v2模型,其輸入節(jié)點是['data']。默認值是 None,此時工具自動從模型中查找輸入節(jié)點。可選參數。
input_size_list:每個輸入節(jié)點對應的數據形狀。例如示例中的 resnet50v2模型,其輸入節(jié)點對應的輸入尺寸是[[3, 224, 224]]??蛇x參數。
注:
1. 填寫輸入數據形狀時不要填 batch 維。如果要批量推理,請使用 build 接口的 rknn_batch_size 參數。
2. 如果指定了 inputs 節(jié)點,則該參數必須填寫。
outputs:指定模型的輸出節(jié)點,數據類型為列表。例如示例中的 resnet50v2模型,其 輸出節(jié)點是['resnetv24_dense0_fwd']。默認值是 None,此時工具將自動從模型中搜索輸出節(jié)點??蛇x參數。
返回值 0:導入成功
-1:導入失敗

舉例如下:

# 從當前目錄加載 resnet50v2 模型 
ret = rknn.load_onnx(model = './resnet50v2.onnx', 
                    inputs = ['data'], 
                    input_size_list = [[3, 224, 224]], 
                    outputs=['resnetv24_dense0_fwd'])

(6)PyTorch 模型加載接口

API load_pytorch
描述 加載 PyTorch模型
參數 model:PyTorch模型文件(.pt后綴)所在路徑,而且需要是 torchscript格式的模型。 必填參數。
input_size_list :每個輸入節(jié)點對應的圖片的尺寸和通道數。例如 [[1,224,224],[ 3,224,224]]表示有兩個輸入,其中一個輸入的 shape 是[1,224,224],另外一個輸入的 shape是[3,224,224]。必填參數。
返回值 0:導入成功
-1:導入失敗

舉例如下:

# 從當前目錄加載 resnet18 模型 
ret = rknn. Load_pytorch(model = ‘./resnet18.pt’, 
                                  input_size_list=[[3,224,224]])

(7)TensorFlow 模型加載接口

API load_tensorflow
描述 加載TensorFlow模型
參數 tf_pb:TensorFlow模型文件(.pb 后綴)所在路徑。
inputs:模型輸入節(jié)點,支持多個輸入節(jié)點。所有輸入節(jié)點名放在一個列表中。
input_size_list:每個輸入節(jié)點對應的數據形狀。如示例中的 mobilenet-v1模型,其輸入節(jié)點對應的輸入尺寸是[[224, 224, 3]]。
outputs:模型的輸出節(jié)點,支持多個輸出節(jié)點。所有輸出節(jié)點名放在一個列表中。
predef_file:為了支持一些控制邏輯,需要提供一個npz格式的預定義文件??梢酝ㄟ^以下方法生成預定義文件:np.savez(‘prd.npz’,placeholder_name=prd_value)。如果 “placeholder_name”中包含’/’,請用’#’替換。
返回值 0:導入成功
-1:導入失敗

舉例如下:

# 從當前目錄加載 ssd_mobilenet_v1_coco_2017_11_17 模型 
ret = rknn.load_tensorflow( 
        tf_pb=’./ssd_mobilenet_v1_coco_2017_11_17.pb’,
        inputs=[‘FeatureExtractor/MobilenetV1/MobilenetV1/Conv2d_0 
               /BatchNorm/batchnorm/mul_1’], 
        outputs=[‘concat’, ‘concat_1’], 
        input_size_list=[[300, 300, 3]])

(8)TensorFlow Lite 模型加載接口

API load_tflite
描述 加載 TensorFlow Lite 模型。
注:因為tflite不同版本的schema之間是互不兼容的,所以構建的tflite模型使用與RKNNToolkit不同版本的schema可能導致加載失敗。目前RKNN-Toolkit使用的tflite schema 是基于官網 master 分支上的提交: 0c4f5dfea4ceb3d7c0b46fc04828420a344f7598。
官網地址如下:
https://github.com/tensorflow/tensorflow/commits/master/tensorflow/lite/schema/schema.f bs
參數 model:TensorFlow Lite 模型文件(.tflite 后綴)所在路徑
返回值 0:導入成功
-1:導入失敗

舉例如下:

# 從當前目錄加載 mobilenet_v1 模型 
ret = rknn.load_tflite(model = ‘./mobilenet_v1.tflite’)

6.3.4 構建 RKNN 模型

API build
描述 依照加載的模型結構及權重數據,構建對應的 RKNN模型。
參數 do_quantization:是否對模型進行量化,值為 True或 False。
ataset:量化校正數據的數據集。目前支持文本文件格式,用戶可以把用于校正的圖片(jpg或 png格式)或 npy文件路徑放到一個.txt 文件中。文本文件里每一行一條路徑信息。如:
a.jpg
b.jpg

a.npy
b.npy
如有多個輸入,則每個輸入對應的文件用空格隔開,如:
a.jpg a2.jpg
b.jpg b2.jpg

a.npy a2.npy
b.npy b2.npy
pre_compile:模型預編譯開關。預編譯 RKNN 模型可以減少模型初始化時間,但是無法通過模擬器進行推理或性能評估。如果 NPU 驅動有更新,預編譯模型通常也需要重新構建。
注:
1. 該選項只在 Linux x86_64 平臺上有效。
2. RKNN-Toolkit-V1.0.0 及以上版本生成的預編譯模型不能在 NPU 驅動版本小于0.9.6 的設備上運行;RKNN-Toolkit-V1.0.0 以前版本生成的預編譯模型不能在NPU 驅 動 版 本 大 于 等 于 0.9.6 的 設 備 上 運 行 。 驅 動 版 本 號 可 以 通 過get_sdk_version 接口查詢。
rknn_batch_size:模型的輸入 Batch 參數調整,默認值為 1。如果大于 1,則可以在一次推理中同時推理多幀輸入圖像或輸入數據,如 MobileNet 模型的原始input 維度為[1, 224, 224, 3],output 維度為[1, 1001],當 rknn_batch_size 設為 4 時,input 的維度變?yōu)閇4, 224, 224, 3],output 維度變?yōu)閇4, 1001]。
注:
1. rknn_batch_size 的調整并不會提高一般模型在 NPU 上的執(zhí)行性能,但卻會顯著增加內存消耗以及增加單幀的延遲。
2. rknn_batch_size 的調整可以降低超小模型在 CPU 上的消耗,提高超小模型的平均幀率。(適用于模型太小,CPU 的開銷大于 NPU 的開銷)。
3. rknn_batch_size 的值建議小于 32,避免內存占用太大而導致推理失敗。
4. rknn_batch_size 修改后,模型的 input/output 維度會被修改,使用 inference 推理模型時需要設置相應的 input 的大小,后處理時,也需要對返回的 outputs 進行處理。
返回值 0:構建成功
-1:構建失敗

注:如果在執(zhí)行腳本前設置RKNN_DRAW_DATA_DISTRIBUTE 環(huán)境變量的值為1,則 RKNN Toolkit會將每一層的weihgt/bias(如果有)和輸出數據的直方圖保存在當前目錄下的 dump_data_distribute文件夾中。使用該功能時推薦只在dataset.txt中存放單獨一個(組)輸入。

舉例如下:

# 構建 RKNN 模型,并且做量化 
ret = rknn.build(do_quantization=True, dataset='./dataset.txt')

6.3.5 導出 RKNN 模型

通過該接口導出 RKNN 模型文件,用于模型部署。

API export_rknn
描述 將 RKNN模型保存到指定文件中(.rknn 后綴)。
參數 export_path:導出模型文件的路徑。
返回值 0:導入成功 ;
-1:導入失敗

舉例如下:

…… 
# 將構建好的 RKNN 模型保存到當前路徑的 mobilenet_v1.rknn 文件中 
ret = rknn.export_rknn(export_path = './mobilenet_v1.rknn')
 …… 

6.3.6 加載 RKNN模型

API load_rknn
描述 加載 RKNN 模型。
參數 path:RKNN模型文件路徑。
load_model_in_npu:是否直接加載npu中的rknn模型。其中path為rknn 模型在npu中的路徑。只有當RKNN-Toolkit運行在RK3399Pro Linux 開發(fā)板或連有 NPU 設備 的 PC 上時才可以設為 True。默認值為 False。
返回值 0:導入成功
-1:導入失敗

舉例如下:

# 從當前路徑加載 mobilenet_v1.rknn 模型 
ret = rknn.load_rknn(path='./mobilenet_v1.rknn')

6.3.7 初始化運行時環(huán)境

在模型推理或性能評估之前,必須先初始化運行時環(huán)境,明確模型在的運行平臺(具體的目標硬件平臺或軟件模擬器)。

API init_runtime
描述 初始化運行時環(huán)境。確定模型運行的設備信息(硬件平臺信息、設備 ID);性能評估時是否啟用debug 模式,以獲取更詳細的性能信息。
參數 target:目標硬件平臺,目前支持“rk3399pro”、“rk1806”、“rk1808”、“rv1109”、 “rv1126”。默認為 None,即在 PC 使用工具時,模型在模擬器上運行,在RK3399Pro Linux 開發(fā)板運行時,模型在RK3399Pro自帶NPU上運行,否則在設定的target上 運行。其中“rk1808”包含了TB-RK1808 AI 計算棒。
device_id:設備編號,如果PC連接多臺設備時,需要指定該參數,設備編號可以通過”list_devices”接口查看。默認值為 None。
注:MAC OS X 系統(tǒng)當前版本還不支持多個設備。
perf_debug:進行性能評估時是否開啟debug 模式。在 debug 模式下,可以獲取到每一層的運行時間,否則只能獲取模型運行的總時間。默認值為 False。
eval_mem: 是否進入內存評估模式。進入內存評估模式后,可以調用 eval_memory 接口獲取模型運行時的內存使用情況。默認值為 False。
async_mode:是否使用異步模式。調用推理接口時,涉及設置輸入圖片、模型推理、獲取推理結果三個階段。如果開啟了異步模式,設置當前幀的輸入將與推理上一幀同時進行,所以除第一幀外,之后的每一幀都可以隱藏設置輸入的時間,從而提升性能。 在異步模式下,每次返回的推理結果都是上一幀的。該參數的默認值為 False。
返回值 0:構建成功
-1:構建失敗

舉例如下:

# 初始化運行時環(huán)境 
ret = rknn.init_runtime(target='rk1808', device_id='012345789AB') 
if ret != 0: 
        print('Init runtime environment failed') 
        exit(ret)

6.3.8 模型推理

在進行模型推理前,必須先構建或加載一個 RKNN 模型。

API inference
描述 對當前模型進行推理,返回推理結果。
如果 RKNN-Toolkit運行在PC上,且初始化運行環(huán)境時設置 target 為Rockchip NPU設備,得到的是模型在硬件平臺上的推理結果。
如果 RKNN-Toolkit 運行在PC上,且初始化運行環(huán)境時沒有設置 target,得到的是模型在模擬器上的推理結果。模擬器可以模擬 RK1808,也可以模擬RV1126,具體模擬哪款芯片,取決于RKNN 模型的 target_platform 參數值。
如果 RKNN-Toolkit運行在 RK3399Pro Linux開發(fā)板上,得到的是模型在實際硬件上的推理結果。
參數 inputs:待推理的輸入,如經過 cv2 處理的圖片。格式是 ndarray list。
data_type:輸入數據的類型,可填以下值:’float32’, ‘float16’, ‘int8’, ‘uint8’, ‘int16’。默認值為’uint8’。
data_format:數據模式,可以填以下值: “nchw”, “nhwc”。默認值為’nhwc’。這兩個的 不同之處在于 channel 放置的位置。
inputs_pass_through:將輸入透傳給NPU 驅動。非透傳模式下,在將輸入傳給NPU驅動之前,工具會對輸入進行減均值、除方差等操作;而透傳模式下,不會做這些操作。這個參數的值是一個數組,比如要透傳input0,不透徹input1,則這個參數的值為[1, 0]。默認值為None,即對所有輸入都不透傳。
返回值 results:推理結果,類型是 ndarray list。

對于分類模型,如mobilenet_v1,模型推理代碼如下(完整代碼參考 example/tflite/mobilent_v1):

# 使用模型對圖片進行推理,輸出 TOP5
 …… 
outputs = rknn.inference(inputs=[img]) 
show_outputs(outputs) 
…… 

輸出的 TOP5 結果如下:

-----TOP 5----- 
[156]: 0.85107421875 
[155]: 0.09173583984375 
[205]: 0.01358795166015625 
[284]: 0.006465911865234375 
[194]: 0.002239227294921875

6.3.9 模型性能評估

API eval_perf
描述 評估模型性能。模型運行在PC上,初始化運行環(huán)境時不指定 target,得到的是模型在模擬器上運行的性能數據,包含逐層的運行時間及模型完整運行一次需要的時間。模擬器可以模擬 RK1808,也可以模擬 RV1126,具體模擬哪款芯片,取決于RKNN模型的target_platform參數值。 模型運行在與PC連接的Rockchip NPU上,且初始化運行環(huán)境時設置perf_debug為False,則獲得的是模型在硬件上運行的總時間;如果設置perf_debug為 True,除了返回總時間外,還將返回每一層的耗時情況。模型運行在RK3399Pro Linux開發(fā)板上時,如果初始化運行環(huán)境時設置perf_debug為False,獲得的也是模型在硬件上運行的總時間;如果設置perf_debug為 True,返回總時間及每一層的耗時情況。
參數 loop_cnt::指定RKNN模型推理次數,用于計算平均推理時間。該參數只在init_runtime中的 target為非模擬器,且perf_debug設成 False時生效。該參數數據類型為整型,默認值為 1。
返回值 perf_result:性能評估結果,詳細說明請參考 5.3 章節(jié)。

舉例如下:

# 對模型性能進行評估 
…… 
rknn.eval_perf(inputs=[image], is_print=True)
……

6.3.10 獲取內存使用情況

API eval_memory
描述 獲取模型在硬件平臺運行時的內存使用情況。 模型必須運行在與 PC 連接的 Rockchip NPU 設備上,或直接運行在 RK3399Pro Linux 開發(fā)板上。

注:
1. 使用該功能時,對應的驅動版本必須要大于等于 0.9.4。驅動版本可以通過 get_sdk_version 接口查詢。
參數 is_print: 是否以規(guī)范格式打印內存使用情況。默認值為 True。
返回值 memory_detail:內存使用情況。詳細說明請參考 6.3 章節(jié)。

舉例如下:

# 對模型內存使用情況進行評估 
…… 
memory_detail = rknn.eval_memory() 
……

如 example/tflite 中的 mobilenet_v1,它在 RK1808 上運行時內存占用情況如下:

============================================== 
Memory Profile Info Dump 
============================================== 
System memory: 
maximum allocation : 22.65 MiB 
total allocation : 72.06 MiB 
NPU memory: 
maximum allocation : 33.26 MiB 
total allocation : 34.57 MiB 
Total memory: 
maximum allocation : 55.92 MiB 
total allocation : 106.63 MiB 
INFO: When evaluating memory usage, we need consider 
the size of model, current model size is: 4.10 MiB 
==============================================

6.3.11 混合量化

(1)hybrid_quantization_step1

使用混合量化功能時,第一階段調用的主要接口是 hybrid_quantization_step1,用于生成模型結構文件( {model_name}.json )、權重文件( {model_name}.data ) 和量化配置文件({model_name}.quantization.cfg)。接口詳情如下:

API hybrid_quantization_step1
描述 根據加載的原始模型,生成對應的模型結構文件、權重文件和量化配置文件。
參數 dataset::量化校正數據的數據集。目前支持文本文件格式,用戶可以把用于校正的圖片(jpg或 png格式)或npy文件路徑放到一個.txt 文件中。文本文件里每一行一條路徑信息。
如:
a.jpg
b.jpg

a.npy
b.npy
返回值 0:成功
-1:失敗

舉例如下:

# Call hybrid_quantization_step1 to generate quantization config 
…… 
ret = rknn.hybrid_quantization_step1(dataset='./dataset.txt') 
……

(2)hybrid_quantization_step2

使用混合量化功能時,生成混合量化 RKNN模型階段調用的主要接口是 hybrid_quantization_step2。接口詳情如下:

API hybrid_quantization_step2
描述 接收模型結構文件、權重文件、量化配置文件、校正數據集作為輸入,生成混合量化后的 RKNN 模型。
參數 model_input:第一步生成的模型結構文件,例如“{model_name}.json”。數據類型為字符串。必填參數。
data_input:第一步生成的權重數據文件,例如“{model_name}.data”。數據類型為字符串。必填參數。
dataset:量化校正數據的數據集。目前支持文本文件格式,用戶可以把用于校正的圖片(jpg 或 png 格式)或 npy 文件路徑放到一個.txt 文件中。文本文件里每一行一條路徑信息。
如:
a.jpg
b.jpg

a.npy
b.npy
pre_compile:模型預編譯開關。預編譯 RKNN模型可以減少模型初始化時間,但是無法通過模擬器進行推理或性能評估。如果 NPU 驅動有更新,預編譯模型通常也需要重新構建。
注:
1. 該選項不能在 RK3399Pro Linux 開發(fā)板/Windows PC/Mac OS X PC 上使用。
2. RKNN-Toolkit-V1.0.0及以上版本生成的預編譯模型不能在 NPU 驅動版本小于0.9.6 的設備上運行;RKNN-Toolkit-V1.0.0 以前版本生成的預編譯模型不能在NPU驅動版本大于等于 0.9.6 的設備上運行。驅動版本號可以通過get_sdk_version 接口查詢。
返回值 0:成功
-1:失敗

舉例如下:

# Call hybrid_quantization_step2 to generate hybrid quantized RKNN model 
…… 
ret = rknn.hybrid_quantization_step2( 
       model_input='./ssd_mobilenet_v2.json', 
       data_input='./ssd_mobilenet_v2.data', 
       model_quantization_cfg='./ssd_mobilenet_v2.quantization.cfg', 
       dataset='./dataset.txt') 
……

6.3.12 量化精度分析

API accuracy_analysis
描述 逐層對比浮點模型和量化模型的輸出結果,輸出余弦距離和歐式距離,用于分析量化模型精度下降原因。
注:
1.該接口在build或hybrid_quantization_step1或 hybrid_quantization_step2之后調用,并且原始模型應該為浮點模型,否則會調用失敗。
2. 該接口使用的量化方式與 config 中指定的一致。
inputs:包含輸入圖像或數據的數據集文本文件(與量化校正數據集 dataset 格式相同, 但只能包含一組輸入)。
output_dir:輸出目錄,所有快照都保存在該目錄。該目錄內容的詳細說明見 4.3.3 章 節(jié)。
calc_qnt_error:是否計算量化誤差(默認為 True)。
target:指定設備類型。如果指定 target,在逐層量化誤差分析時,將連接到 NPU上獲取每一層的真實結果,跟浮點模型結果相比較。這樣可以更準確的反映實際運行時的誤差。
device_id:如果 PC 連接了多個NPU設備,需要指定具體的設備編號。
dump_file_type:精度分析過程中會輸出模型每一層的結果,這個參數指定了輸出文件的類型。有效值為’tensor’和’npy’,默認值是’tensor’。如果指定數據類型為’npy’, 可以減少精度分析的耗時。
返回值 0:成功
-1:失敗

注:如果在執(zhí)行腳本前設置 RKNN_DRAW_DATA_DISTRIBUTE 環(huán)境變量的值為 1,則 RKNNToolkit 會將每一層的 weihgt/bias(如果有)和輸出數據的直方圖保存在當前目錄下的 dump_data_distribute 文件夾中。

舉例如下:

……

print('--> config model')
rknn.config(channel_mean_value='0 0 0 1', reorder_channel='0 1 2')
print('done')

print('--> Loading model')
ret = rknn.load_onnx(model='./mobilenetv2-1.0.onnx')
if ret != 0:
    print('Load model failed! Ret = {}'.format(ret))
    exit(ret)
print('done')

# Build model
print('--> Building model')
ret = rknn.build(do_quantization=True, dataset='./dataset.txt')
if ret != 0:
    print('Build rknn failed!')
    exit(ret)
print('done')

print('--> Accuracy analysis')
rknn.accuracy_analysis(inputs='./dataset.txt', target='rk1808')
print('done')

……

6.3.13 注冊自定義算子

API register_op
描述 注冊自定義算子。
參數 op_path:算子編譯生成的 rknnop 文件的路徑
返回值

參考代碼如下所示。注意,該接口要在模型轉換前調用。自定義算子的使用和開發(fā)請參考 《Rockchip_Developer_Guide_RKNN_Toolkit_Custom_OP_CN》文檔。

rknn.register_op('./resize_area/ResizeArea.rknnop')
rknn.load_tensorflow(…)

6.3.14 導出預編譯模型(在線預編譯)

構建RKNN模型時,可以指定預編譯選項以導出預編譯模型,這被稱為離線編譯。同樣,RKNN Toolkit 也提供在線編譯的接口:export_rknn_precompile_model。使用該接口,可以將普通 RKNN 模型轉成預編譯模型。

API export_rknn_precompile_model
描述 經過在線編譯后導出預編譯模型。
注:
1. 使用該接口前必須先調用 load_rknn 接口加載普通 rknn 模型;
2. 使用該接口前必須調用 init_runtime 接口初始化模型運行環(huán)境,target 必須是RK NPU 設備,不能是模擬器;而且要設置 rknn2precompile 參數為 True。
參數 export_path:導出模型路徑。必填參數。
返回值 0:成功
-1:失敗

舉例如下:

from rknn.api import RKNN
if __name__ == '__main__':
   # Create RKNN object
   rknn = RKNN()
 
   # Load rknn model
   ret = rknn.load_rknn('./test.rknn')
   if ret != 0:
       print('Load RKNN model failed.')
       exit(ret)
   # init runtime
   ret = rknn.init_runtime(target='rk1808', rknn2precompile=True)
   if ret != 0:
       print('Init runtime failed.')
       exit(ret)
   # Note: the rknn2precompile must be set True when call init_runtime
   ret = rknn.export_rknn_precompile_model('./test_pre_compile.rknn')
   if ret != 0:
       print('export pre-compile model failed.')
       exit(ret)
 
rknn.release()

6.3.15 導出分段模型

該接口的功能是將普通的 RKNN 模型轉成分段模型,分段的位置由用戶指定。

API export_rknn_sync_model
描述 在用戶指定的模型層后面插入sync層,用于將模型分段,并導出分段后的模型。
參數 input_model: 待分段的rknn模型路徑。數據類型為字符串。必填參數。
sync_uids: 待插入 sync 節(jié)點層的層uid列表,RKNN-Toolkit 將在這些層后面插入sync層。
注:
1. uid 只能通過 eval_perf 接口獲取,且需要在 init_runtime 時設置 perf_debug 為True,target 不能是模擬器。
2. uid 的值不可以隨意填寫,一定需要是在 eval_perf 獲取性能詳情的 uid 列表中,否則可能出現不可預知的結果。
output_model:導出模型的保存路徑。數據類型:字符串。默認值為 None,如果不填該參數,導出的模型將保存在 input_model 指定的文件中。
返回值 0:成功
-1:失敗

舉例如下:

from rknn.api import RKNN

if __name__ == '__main__':
    rknn = RKNN()
    ret = rknn.export_rknn_sync_model(
input_model='./ssd_inception_v2.rknn',
            sync_uids=[206, 186, 152, 101, 96, 67, 18, 17],
            output_model='./ssd_inception_v2_sync.rknn')
   if ret != 0:
       print('export sync model failed.')
       exit(ret)
   rknn.release()

6.3.16 導出加密模型

該接口的功能是將普通的 RKNN 模型進行加密,得到加密后的模型。

API export_encrypted_rknn_model
描述 根據用戶指定的加密等級對普通的 RKNN 模型進行加密。
參數 input_model:待加密的RKNN模型路徑。數據類型為字符串。必填參數。
output_model:模型加密后的保存路徑。數據類型為字符串。選填參數,如果不填該參數,則使用{original_model_name}.crypt.rknn作為加密后的模型的名字。
crypt_level:加密等級。等級越高,安全性越高,解密越耗時;反之,安全性越低,解密越快。數據類型為整型,默認值為 1,目前安全等級有 3 個等級,1,2 和 3。
返回值 0:成功
-1:失敗

舉例如下:

from rknn.api import RKNN

if __name__ == '__main__':
    rknn = RKNN()
    ret = rknn.export_encrypted_rknn_model('test.rknn')
    if ret != 0:
        print('Encrypt RKNN model failed.')
        exit(ret)
    rknn.release()

6.3.17 查詢 SDK 版本

API get_sdk_version
描述 獲取 SDK API 和驅動的版本號。
注:使用該接口前必須完成模型加載和初始化運行環(huán)境。且該接口只有在target是 Rockchip NPU 或 RKNN-Toolkit 運行在 RK3399Pro Linux 開發(fā)板才可以使用。
參數
返回值 sdk_version:API 和驅動版本信息。類型為字符串。

舉例如下:

# 獲取 SDK 版本信息
……
sdk_version = rknn.get_sdk_version()
……

返回的 SDK 信息如下:

==============================================
RKNN VERSION:
    API: 1.7.1 (566a9b6 build: 2021-10-28 14:53:41)
    DRV: 1.7.1 (566a9b6 build: 2021-11-12 20:24:57)
==============================================

6.3.18 獲取設備列表

API list_devices
描述 列出已連接的RK3399PRO/RK1808/RV1109/RV1126或TB-RK1808 AI 計算棒。
注:目前設備連接模式有兩種:ADB 和 NTB。其中RK3399PRO 目前只支持ADB 模 式,TB-RK1808 AI計算棒只支持NTB模式,RK1808/RV1109 支持 ADB/NTB 模式。多設備連接時請確保他們的模式都是一樣的。
參數
返回值 返回adb_devices列表和ntb_devices 列表,如果設備為空,則返回空列表。例如我們的環(huán)境里插了兩個 TB-RK1808 AI 計算棒,得到的結果如下:
adb_devices = []
ntb_devices = ['TB-RK1808S0', '515e9b401c060c0b']

舉例如下:

from rknn.api import RKNN

if __name__ == '__main__':
    rknn = RKNN()
    rknn.list_devices()
    rknn.release() 

返回的設備列表信息如下(這里有兩個 RK1808 開發(fā)板,它們的連接模式都是 adb):

*************************
all device(s) with adb mode:
['515e9b401c060c0b', 'XGOR2N4EZR']
*************************

注:使用多設備時,需要保證它們的連接模式都是一致的,否則會引起沖突,導致設備通信失敗。

6.3.19 查詢模型可運行平臺

API list_support_target_platform
描述 查詢給定 RKNN 模型可運行的芯片平臺。
參數 rknn_model:RKNN模型路徑。如果不指定模型路徑,則按類別打印 RKNN-Toolkit 當前支持的芯片平臺。
返回值 support_target_platform: Returns runnable chip platforms, or None if the model path is empty.

參考代碼如下所示:

rknn.list_support_target_platform(rknn_model=’mobilenet_v1.rknn’)

參考結果如下:

**************************************************
Target platforms filled in RKNN model: []
Target platforms supported by this RKNN model: ['RK1806', 'RK1808', 'RK3399PRO']
**************************************************

7. 模型部署

模型轉換為rknn模型后,需再參考NPU API說明文檔,編寫應用工程。經過編譯后傳輸至EASY EAI Nano平臺上實現部署。

7.1 模型部署示例

7.1.1 模型部署示例介紹

本小節(jié)展示yolov5模型的在EASY EAI Nano的部署過程,該模型僅經過簡單訓練供示例使用,不保證模型精度。

7.1.2 準備工作

(1)硬件準備

EASY EAI Nano開發(fā)板,microUSB數據線,帶linux操作系統(tǒng)電腦。需保證EASY EAI Nano與linux系統(tǒng)保持adb連接。

(2)開發(fā)環(huán)境準備

如果您初次閱讀此文檔,請閱讀《入門指南/開發(fā)環(huán)境準備/Easy-Eai編譯環(huán)境準備與更新》,并按照其相關的操作,進行編譯環(huán)境的部署。

在PC端Ubuntu系統(tǒng)中執(zhí)行run腳本,進入EASY-EAI編譯環(huán)境,具體如下所示。

cd ~/develop_environment
./run.sh
wKgZPGgByNqAEWKQAACbrHAYX20781.png

7.1.3 源碼下載以及例程編譯

下載yolov5 C Demo示例文件。

百度網盤鏈接:https://pan.baidu.com/s/1xmEgBGQCMrvHm9kfU9uBkg (提取碼:lanz )。

下載解壓后如下圖所示:

wKgZO2gByNqACIATAABcT1xeqDA990.png

在EASY-EAI編譯環(huán)境下,切換到例程目錄執(zhí)行編譯操作:

cd /opt/test/yolov5_detect_C_demo
./build.sh

注:

* 由于依賴庫部署在板卡上,因此交叉編譯過程中必須保持adb連接。

wKgZPGgByNqAIB-0AAGzKTsdWKI066.png

7.1.4 在開發(fā)板執(zhí)行yolov5 demo

在EASY-EAI編譯環(huán)境下,在例程目錄執(zhí)行以下指令把可執(zhí)行程序推送到開發(fā)板端:

cp yolov5_detect_demo_release/ /mnt/userdata/ -rf

通過按鍵Ctrl+Shift+T創(chuàng)建一個新窗口,執(zhí)行adb shell命令,進入板卡運行環(huán)境:

adb shell

wKgZO2gByNuABBYDAABIcK9_G-Q533.png

進入板卡后,定位到例程上傳的位置,如下所示:

 cd /userdata/yolov5_detect_demo_release/

運行例程命令如下所示:

./yolov5_detect_demo

執(zhí)行結果如下圖所示:

wKgZPGgByNuABTuoAAHGQBdCGTs457.png

退出板卡環(huán)境,取回測試圖片:

exit
adb pull /userdata/yolov5_detect_demo_release/result.jpg .

測試結果如下圖所示:

wKgZO2gByNuAAw58AAUHRiqt13k085.jpg

7.2 模型部署API說明

7.2.1 API流程說明

從 RKNN API V1.6.0 版本開始,新增加了一組設置輸入的函數:

rknn_inputs_map

rknn_inputs_sync

rknn_inputs_unmap

以及一組獲取輸出的函數:

rknn_outputs_map

rknn_outputs_sync

rknn_outputs_unmap

在設置輸入時,用戶可以使用 rknn_inputs_set 或者 rknn_inputs_map 系列函數。獲取推理的輸出時,使用 rknn_outputs_get 或者 rknn_outputs_map 系列函數。特定場景下,使用 map 系列接口可以減少內存拷貝的次數,提高完整推理的速度。

rknn_inputs_map 系列接口和 rknn_inputs_set 接口的調用流程不同 rknn_outputs_map 系列接口和rknn_outputs_get 接口的調用流程也不同。兩個系列API調用流程差異如下圖所示,其中圖 1.1(a)為set/get系列接口調用流程,圖 1.1(b)為map系列接口調用流程。

wKgZPGgByNuAJvsqAAEA5qNolRw986.png圖 1.1? 使用 set/get 系列(a)和 map 系列(b)接口流

設置輸入和獲取輸出接口沒有綁定關系,因此可以混合使用 set/get 系列接口和 map 系列接口。如圖 1.2(a),用戶可以使用 rknn_inputs_map 系列接口設置輸入,再通過rknn_outputs_get 接口獲取輸出,或者如圖 1.2(b)通過 rknn_inputs_set 系列接口設置 輸入,再使用 rknn_outputs_map 接口獲取輸出。

wKgZO2gByNyATRUeAAEEwFTHFec806.png圖 1.2? 混合使用 set/get 系列和 map 系列接口的調用流程

(1)API內部處理流程

在推理 RKNN 模型時,原始數據要經過輸入處理、NPU 運行模型、輸出處理三大流程。

在典型的圖片推理場景中,假設輸入數據 data 是 3 通道的圖片且為 NHWC 排布格式,運行時(Runtime)對數據處理的流程如圖 1.3所示。在 API 層面上,rknn_inputs_set 接口(當 pass_through=0 時,詳見 rknn_input 結構體)包含了顏色通道交換、歸一化、量化、NHWC 轉換成 NCHW 的過程,rknn_outputs_get 接口(當want_float=1時,詳見 rknn_output 結構體)包含了反量化的過程。

wKgZPGgByNyAFGLkAADKdEyZ4eY272.png圖 1.3? 完整的圖片數據處理流

(2)量化和反量化

當使用rknn_inputs_set(pass_through=1)和 rknn_inputs_map 時,表明在 NPU 推理之前的流程要用戶處理。rknn_outputs_map獲取輸出后,用戶也要做反量化得到 32位浮點結果。

量化和反量化用到的量化方式、量化數據類型以及量化參數,可以通過 rknn_query 接口查詢。目前,RK1808/RK3399Pro/RV1109/RV1126 (EASY EAI Nano為RV1126)的NPU有非對稱量化和動態(tài)定點 量化兩種量化方式,每種量化方式指定相應的量化數據類型??偣灿幸韵滤姆N數據類型和量化方式組合:

uint8(非對稱量化)

int8(動態(tài)定點)

int16(動態(tài)定點)

float16(無)

通常,歸一化后的數據用 32 位浮點數保存,32 位浮點轉換成 16 位浮點數請參考 IEEE-754標準。假設歸一化后的32位浮點數據是D,下面介紹量化流程:

(2.1)float32 轉 uint8

假設輸入tensor的非對稱量化參數是S(q),ZP ,數據D量化過程表示為下式:

wKgZO2gByNyAcwe7AAAi4ccNEjE613.png

上式中,clamp表示將數值限制在某個范圍。round表示做舍入處理。

(2.2)float32 轉 int8

假設輸入tensor的動態(tài)定點量化參數是fl,數據 D 量化過程表示為下式:

wKgZPGgByNyAMXrPAAAhNh5uIIs990.png

(2.3)float32 轉 int16

假設輸入tensor的動態(tài)定點量化參數是fl,數據D量化過程表示為下式:

wKgZO2gByN2AJWKkAAAkeoL_hX0399.png

反量化流程是量化的逆過程,可以根據上述量化公式反推出反量化公式,這里不做贅述。

(3)零拷貝

在特定的條件下,可以把輸入數據拷貝次數減少到零,即零拷貝。比如,當RKNN模型是非對稱量化,量化數據類型是uint8,3通道的均值是相同的整數同時縮放因子相同的情況下,歸一化和量化可以省略。證明如下:

假設輸入圖像數據是D(f),量化參數是 S(q),ZP 。M(i)表示第 i 通道的均值, S(i)表示第 i 通道的歸一化因子。則第i通道歸一化后的數據 如下式子:

wKgZPGgByN2AKBHiAAATgNsofh4709.png

數據 D(i)量化過程表示為下式:

wKgZO2gByN2ACudXAAAeSXRCupE096.png

上述兩個式子合并后,可以得出

wKgZPGgByN2Ac99JAAAhss0NUhU763.png

假設量化圖片矯正集數據范圍包含0到 255的整數值,當 M1=M2 =M3 ,S1 =S2 =S3 時, 歸一化數值范圍表示如下:

wKgZO2gByN2AK_goAAAs-iPHJok532.png

因此,量化參數計算如下:

wKgZPGgByN2AAK-oAAAaRuo1wmY395.png

把式(3-9)和式(3-10)代入式(3-6),可以得出

wKgZPGgBwZWAXhzRAAAFk3y-meY838.png

,即符合零拷貝的條件下:3 通道的均值是相同的整數同時歸一化的縮放因子相同,輸入uint8 數據等于量化后的uint8數據。

輸入零拷貝能降低 CPU 負載,提高整體的推理速度。針對 RGB 或 BGR 輸入數據,實現 輸入零拷貝的步驟如下:

1)三個通道的均值是相同的整數同時歸一化的縮放因子相同。

2)在 rknn-toolkit 的 config 函數中,設置 force_builtin_perm=True,導出 NHWC輸入的 RKNN 模型。

3)使用 rknn_inputs_map 接口,獲取輸入 tensor 內存地址信息。

4)往內存地址填充輸入數據,比如調用 RGA 縮放函數,目標地址使用 rknn_inputs_map獲取的物理地址。

5)調用 rknn_inputs_sync 接口。

6)調用 rknn_run 接口。

7)調用獲取輸出接口。

7.2.2 API詳細說明

(1)rknn_init

rknn_init初始化函數將創(chuàng)建 rknn_context 對象、加載 RKNN 模型以及根據 flag執(zhí)行 特定的初始化行為。

API rknn_init
功能 初始化 rknn
參數 rknn_context *context:rknn_context指針。函數調用之后,context 將會被賦值。
void *model:RKNN 模型的二進制數據。
uint32_t size:模型大小。
uint32_t flag:特定的初始化標志。目前 RK1808 平臺僅支持以下標志: RKNN_FLAG_COLLECT_PERF_MASK:打開性能收集調試開關,打開之后能夠通過rknn_query 接口查詢網絡每層運行時間。需要注意,該標志被設置后rknn_run的運行時間將會變長。
返回值 int錯誤碼(見rknn 返回值錯誤碼)。

示例代碼如下:

rknn_context ctx;
int ret = rknn_init(&ctx, model_data, model_data_size, 0);

(2)rknn_destroy

rknn_destroy 函數將釋放傳入的 rknn_context及其相關資源。

API rknn_destroy
功能 銷毀 rknn_context 對象及其相關資源。
參數 rknn_context context:要銷毀的 rknn_context 對象。
返回值 int 錯誤碼(見 rknn 返回值錯誤碼)。

示例代碼如下:

int ret = rknn_destroy (ctx);

(3)rknn_query

rknn_query 函數能夠查詢獲取到模型輸入輸出、運行時間以及 SDK 版本等信息。

API rknn_query
功能 查詢模型與 SDK 的相關信息。
參數 rknn_context context:rknn_context 對象。
rknn_query_cmd cmd:查詢命令。
void* info:存放返回結果的結構體變量。
uint32_t size:info 對應的結構體變量的大小。
返回值 int 錯誤碼(見 rknn 返回值錯誤碼)

當前 SDK 支持的查詢命令如下表所示:

查詢命令 返回結果結構體 功能
RKNN_QUERY_IN_OUT_N UM rknn_input_output_num 查詢輸入輸出 Tensor 個數
RKNN_QUERY_INPUT_ATT R rknn_tensor_attr 查詢輸入 Tensor 屬性
RKNN_QUERY_OUTPUT_A TTR rknn_tensor_attr 查詢輸出 Tensor 屬性
RKNN_QUERY_PERF_DET AIL rknn_perf_detail 查詢網絡各層運行時間
RKNN_QUERY_SDK_VERSI ON rknn_sdk_version 查詢 SDK 版

接下來的將依次詳解各個查詢命令如何使用。

(3.1)查詢輸入輸出 Tensor 個數

傳入 RKNN_QUERY_IN_OUT_NUM 命令可以查詢模型輸入輸出 Tensor 的個數。其中 需要先創(chuàng)建rknn_input_output_num 結構體對象。

示例代碼如下:

rknn_input_output_num io_num;
ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, 
                          sizeof(io_num));
printf("model input num: %d, output num: %dn", 
                         io_num.n_input, io_num.n_output);

(3.2)查詢輸入 Tensor 屬性

傳入 RKNN_QUERY_INPUT_ATTR 命令可以查詢模型輸入 Tensor 的屬性。其中需要先 創(chuàng)建 rknn_tensor_attr 結構體對象。

示例代碼如下:

rknn_tensor_attr input_attrs[io_num.n_input];
memset(input_attrs, 0, sizeof(input_attrs));
for (int i = 0; i < io_num.n_input; i++) {
    input_attrs[i].index = i;
    ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), 
                          sizeof(rknn_tensor_attr));
}

(3.3)查詢輸出 Tensor 屬性

傳入RKNN_QUERY_OUTPUT_ATTR命令可以查詢模型輸出Tensor的屬性。其中需要先創(chuàng)建rknn_tensor_attr 結構體對象。

示例代碼如下:

rknn_tensor_attr output_attrs[io_num.n_output];
memset(output_attrs, 0, sizeof(output_attrs));
for (int i = 0; i < io_num.n_output; i++) {
     output_attrs[i].index = i;
    ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), 
                           sizeof(rknn_tensor_attr));
}

(3.4)查詢網絡各層運行時間

如果在rknn_init函數調用時有設置RKNN_FLAG_COLLECT_PERF_MASK標志,那么 在執(zhí)行rknn_run完成之后,可以傳入RKNN_QUERY_PERF_DETAIL命令來查詢網絡每層 運行時間。其中需要先創(chuàng)建rknn_perf_detail結構體對象。

示例代碼如下:

rknn_perf_detail perf_detail;
ret = rknn_query(ctx, RKNN_QUERY_PERF_DETAIL, &perf_detail, 
                   sizeof(rknn_perf_detail));
printf("%s", perf_detail.perf_data);

注意,用戶不需要釋放rknn_perf_detail中的perf_data,SDK會自動管理該Buffer內存。

(3.5)查詢 SDK 版本

傳入RKNN_QUERY_SDK_VERSION命令可以查詢RKNN SDK的版本信息。其中需要先創(chuàng)建rknn_sdk_version結構體對象。

示例代碼如下:

rknn_sdk_version version;
ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, 
                  sizeof(rknn_sdk_version));
printf("sdk api version: %sn", version.api_version);
printf("driver version: %sn", version.drv_version);

(4)rknn_inputs_set

通過 rknn_inputs_set 函數可以設置模型的輸入數據。該函數能夠支持多個輸入,其中 每個輸入是 rknn_input 結構體對象,在傳入之前用戶需要設置該對象。

API rknn_inputs_set
功能 設置模型輸入數據。
參數 rknn_context context:rknn_contex 對象。
uint32_t n_inputs:輸入數據個數。
rknn_input inputs[]:輸入數據數組,數組每個元素是 rknn_input 結構體對象。
返回值 int 錯誤碼(見 rknn 返回值錯誤碼)。

示例代碼如下:

rknn_input inputs[1];
memset(inputs, 0, sizeof(inputs));
inputs[0].index = 0;
inputs[0].type = RKNN_TENSOR_UINT8;
inputs[0].size = img_width*img_height*img_channels;
inputs[0].fmt = RKNN_TENSOR_NHWC;
inputs[0].buf = in_data;

ret = rknn_inputs_set(ctx, 1, inputs);

(5)rknn_inputs_map

rknn_inputs_map 函數用于獲取模型輸入tensor初始化后的存儲狀態(tài),存儲狀態(tài)包括虛擬地址,物理地址fd,存儲空間大小。它需要和rknn_inputs_sync接口(見rknn_inputs_sync 函數)配合使用,在模型初始化后,用戶通過返回的的內存位置設置輸入數據,并且在推理前調用rknn_inputs_sync函數。存儲狀態(tài)使用rknn_tensor_mem結構體表示。輸入參數mem 是rknn_tensor_mem結構體數組。

目前,在 RK1808/RV1109/RV1126 芯片上,返回的fd是-1。當返回的物理地址值是 0xffffffffffffffff(2的64次冪-1),表示無法獲取正確的物理地址,而虛擬地址仍然有效。如果有多個模型輸入tensor的存儲空間較大,用戶可以在掛載驅動時,適當增加模型輸入和 輸出存儲空間或者擴增固件中的CMA內存空間。以RV1109_RV1126為例,配置驅動存儲 空間,可以參考如下修改:

把/etc/init.d/S60NPU_init文件這一行:

insmod /lib/modules/galcore.ko contiguousSize=0x400000 gpuProfiler=1

改成

insmod /lib/modules/galcore.ko contiguousSize=0x600000 gpuProfiler=1

然后重啟生效。此配置應該大于用戶模型輸入和輸出總大小,但不超過固件中可用的CMA空間大小。

API rknn_inputs_map
功能 讀取輸入存儲狀態(tài)信息。
參數 rknn_context context:rknn_contex 對象。
uint32_t n_inputs:輸入數據個數。
rknn_input inputs[]:輸入數據數組,數組每個元素是 rknn_input 結構體對象。
返回值 int 錯誤碼(見rknn 返回值錯誤碼)。

示例代碼如下:

rknn_tensor_mem mem[1];
ret = rknn_inputs_map(ctx, 1, mem);

(6)rknn_inputs_sync

rknn_inputs_sync 函數將 CPU 緩存寫回內存,讓設備能獲取正確的數據。

API rknn_inputs_sync
功能 同步輸入數據。
參數 rknn_context context:rknn_contex 對象。
uint32_t n_inputs:輸入數據個數。
rknn_tensor_mem mem[]:存儲狀態(tài)信息數組,數組每個元素是 rknn_tensor_mem結構體對象。
返回值 int 錯誤碼(見 rknn 返回值錯誤碼)

示例代碼如下:

rknn_tensor_mem mem[1];
ret = rknn_inputs_map(ctx, 1, mem);

ret = rknn_inputs_sync(ctx, 1, mem);

(7)rknn_inputs_unmap

rknn_inputs_unmap 函數將清除 rknn_inputs_map函數獲取的輸入tensor的存儲位 置信息和標志。

API rknn_inputs_unmap
功能 清除rknn_inputs_map函數獲取的輸入tensor的存儲位置信息和標志。
參數 rknn_context context:rknn_contex對象。
uint32_t n_inputs:輸入數據個數。
rknn_tensor_mem mem[] : 存 儲 狀 態(tài) 信 息 數 組 , 數 組 每 個 元 素 是 rknn_tensor_mem 結構體對象。
返回值 int 錯誤碼(見 rknn 返回值錯誤碼)

示例代碼如下:

rknn_tensor_mem mem[1];
ret = rknn_inputs_map(ctx, 1, mem);
ret = rknn_inputs_sync(ctx, 1, mem);
ret = rknn_run(ctx, NULL);

ret = rknn_inputs_unmap(ctx, 1, mem);

(8)rknn_run

rknn_run函數將執(zhí)行一次模型推理,調用之前需要先通過rknn_inputs_set函數設置輸入數據。

API rknn_run
功能 執(zhí)行一次模型推理。
參數 rknn_context context:rknn_context 對象。
rknn_run_extend* extend:保留擴展,當前沒有使用,傳入 NULL 即可。
返回值 int 錯誤碼(見 rknn 返回值錯誤碼)

示例代碼如下:

ret = rknn_run(ctx, NULL);

(9)rknn_outputs_get

rknn_outputs_get函數可以獲取模型推理的輸出數據。該函數能夠一次獲取多個輸出數據。其中每個輸出是rknn_output結構體對象,在函數調用之前需要依次創(chuàng)建并設置每個 rknn_output對象。

對于輸出數據的buffer存放可以采用兩種方式:一種是用戶自行申請和釋放,此時 rknn_output對象的is_prealloc需要設置為1,并且將buf指針指向用戶申請的 buffer;另一種是由 rknn來進行分配,此時rknn_output對象的is_prealloc設置為0即可,函數執(zhí)行之后 buf將指向輸出數據。

API rknn_outputs_get
功能 獲取模型推理輸出。
參數 rknn_context context:rknn_context 對象。
uint32_t n_outputs:輸出數據個數。
rknn_output outputs[]:輸出數據的數組,其中數組每個元素為 rknn_output 結構體對象,代表模型的一個輸出。
rknn_output_extend* extend:保留擴展,當前沒有使用,傳入 NULL 即可
返回值 int 錯誤碼(見 rknn 返回值錯誤碼)

示例代碼如下:

rknn_output outputs[io_num.n_output];
memset(outputs, 0, sizeof(outputs));
for (int i = 0; i < io_num.n_output; i++) {
       outputs[i].want_float = 1;
}
ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL)

(10)rknn_outputs_release

rknn_outputs_release函數將釋放rknn_outputs_get函數得到的輸出的相關資源。

API rknn_outputs_release
功能 釋放rknn_output對象。
參數 rknn_context context:rknn_context對象。
uint32_t n_outputs:輸出數據個數。
rknn_output outputs[]:要銷毀的 rknn_output 數組。
返回值 int 錯誤碼(見rknn返回值錯誤碼)

示例代碼如下

ret = rknn_outputs_release(ctx, io_num.n_output, outputs);

(11)rknn_outputs_map

rknn_outputs_map函數獲取模型初始化后輸出tensor 的存儲狀態(tài)。需要和rknn_outputs_sync函數(見 rknn_outputs_sync 函數)配合使用,在模型初始化后調用 rknn_outputs_map 接口,接著每次推理完調用 rknn_outputs_sync 接口。如果用戶需要 32 位浮點類型的數據,需要根據量化方式和量化的數據類型做反量化。

API rknn_outputs_map
功能 讀取輸出存儲狀態(tài)信息。
參數 rknn_context context:rknn_context 對象。
uint32_t n_outputs:輸出數據個數。
rknn_tensor_mem mem[]:存儲狀態(tài)信息數組,數組每個元 素 是 rknn_tensor_mem結構體對象。
返回值 int 錯誤碼(見 rknn 返回值錯誤碼)

示例代碼如下:

rknn_tensor_mem mem[1];
ret = rknn_outputs_map(ctx, 1, mem);

(12)rknn_outputs_sync

當使用 rknn_outputs_map 接口映射完模型運行時模型輸出 tensor 存儲狀態(tài)信息后, 為確保緩存一致性,使用 rknn_outputs_sync 函數讓 CPU 獲取推理完最新的數據。

API rknn_outputs_sync
功能 推理完,同步最新的輸出數據。
參數 rknn_context context:rknn_context 對象。
uint32_t n_outputs:輸出數據個數。
rknn_tensor_mem mem[]:存儲狀態(tài)信息數組,數組每個元素是 rknn_tensor_mem 結構體對象。
返回值 int 錯誤碼(見 rknn 返回值錯誤碼)

示例代碼如下:

rknn_tensor_mem mem[1];

ret = rknn_run(ctx, NULL);
ret = rknn_outputs_sync(ctx, io_num.n_output, mem)

(13)rknn_outputs_unmap

rknn_outputs_unmap函數將清除rknn_outputs_map函數獲取的輸出tensor的存儲狀態(tài)。

API rknn_outputs_unmap
功能 清除 rknn_outputs_map 函數獲取的輸出 tensor 的存儲狀態(tài)。
參數 rknn_context context:rknn_context 對象。
uint32_t n_outputs:輸出數據個數。
rknn_tensor_mem mem[]:存儲狀態(tài)信息數組,數組每個元素是 rknn_tensor_mem結構體對象。
返回值 int 錯誤碼(見 rknn 返回值錯誤碼)

示例代碼如下:

rknn_tensor_mem mem[1];
ret = rknn_outputs_unmap(ctx, io_num.n_output,mem);

7.2.3 RKNN 數據結構定義

(1)rknn_input_output_num

結構體rknn_input_output_num表示輸入輸出Tensor個數,其結構體成員變量如下表所示:

成員變量 數據類型 含義
n_input uint32_t 輸入 Tensor個數
n_output uint32_t 輸出 Tensor個數

(2)rknn_tensor_attr

結構體 rknn_tensor_attr 表示模型的 Tensor 的屬性,結構體的定義如下表所示:

成員變量 數據類型 含義
index uint32_t 表示輸入輸出 Tensor 的索引位置。
n_dims uint32_t Tensor 維度個數。
dims uint32_t[] Tensor 各維度值。
name char[] Tensor 名稱。
n_elems uint32_t Tensor 數據元素個數。
size uint32_t Tensor 數據所占內存大小。
fmt rknn_tensor_form at Tensor 維度的格式,有以下格式:
RKNN_TENSOR_NCHW RKNN_TENSOR_NHWC
type rknn_tensor_type Tensor 數據類型,有以下數據類型:
RKNN_TENSOR_FLOAT32 RKNN_TENSOR_FLOAT16 RKNN_TENSOR_INT8 RKNN_TENSOR_UINT8 RKNN_TENSOR_INT16
qnt_type rknn_tensor_qnt_type Tensor量化類型,有以下的量化類型:
RKNN_TENSOR_QNT_NONE:未量化; RKNN_TENSOR_QNT_DFP:動態(tài)定點量 化; RKNN_TENSOR_QNT_AFFINE_ASYMM ETRIC:非對稱量化。
fl int8_t RKNN_TENSOR_QNT_DFP 量化類型的參數。
zp uint32_t RKNN_TENSOR_QNT_AFFINE_ASYMMETRI C 量化類型的參數
scale float RKNN_TENSOR_QNT_AFFINE_ASYMMETRI C 量化類型的參數。

(3)rknn_input

結構體rknn_input 表示模型的一個數據輸入,用來作為參數傳入給rknn_inputs_set函數。結構體的定義如下表所示:

成員變量 數據類型 含義
index uint32_t 該輸入的索引位置。
buf void* 輸入數據 Buffer 的指針。
size uint32_t 輸入數據 Buffer 所占內存大小。
pass_through uint8_t 設置為 1 時會將 buf存放的輸入數據直接設置給 模型的輸入節(jié)點,不做任何預處理。
type rknn_tensor_type 輸入數據的類型。
fmt rknn_tensor_form at 輸入數據的格式。

(4)rknn_tensor_mem

結構體 rknn_tensor_mem表示 tensor 初始化后的存儲狀態(tài)信息,用來作為參數傳入給 rknn_inputs_map系列和rknn_outputs_map系列函數。結構體的定義如下表所示:

成員變量 數據類型 含義
logical_addr void* 該輸入的虛擬地址。
physical_addr uint64_t 該輸入的物理地址。
fd int32_t 該輸入的 fd。
size uint32_t 該輸入 tensor 占用的內存大小。
handle uint32_t 該輸入的 handle。
priv_data void* 保留的數據。
reserved_flag uint64_t 保留的標志位。

(5)rknn_output

結構體rknn_output表示模型的一個數據輸出,用來作為參數傳入給rknn_outputs_get 函數,在函數執(zhí)行后,結構體對象將會被賦值。結構體的定義如下表所示:

成員變量 數據類型 含義
want_float uint8_t 標識是否需要將輸出數據轉為 float 類型輸出。
is_prealloc uint8_t 標識存放輸出數據的 Buffer 是否是預分配。
index uint32_t 該輸出的索引位置。
buf void* 輸出數據 Buffer 的指針。
size uint32_t 輸出數據 Buffer 所占內存大小。

(6)rknn_perf_detail

結構體 rknn_perf_detail 表示模型的性能詳情,結構體的定義如下表所示:

成員變量 數據類型 含義
perf_data char* 性能詳情包含網絡每層運行時間,能夠直接打印 出來查看。
data_len uint64_t 存放性能詳情的字符串數組的長度。

(7)rknn_sdk_version

結構體 rknn_sdk_version 用來表示 RKNN SDK 的版本信息,結構體的定義如下:

成員變量 數據類型 含義
api_version char[] char[] SDK 的版本信息。
drv_version char[] SDK所基于的驅動版本信息。

7.2.4 RKNN返回值錯誤碼

RKNN API 函數的返回值錯誤碼定義如下表所示

錯誤碼 錯誤詳情
RKNN_SUCC(0) 執(zhí)行成功
RKNN_ERR_FAIL(-1) 執(zhí)行出錯
RKNN_ERR_TIMEOUT(-2) 執(zhí)行超時
RKNN_ERR_DEVICE_UNAVAILABLE (-3) NPU 設備不可用
RKNN_ERR_MALLOC_FAIL(-4) 內存分配失敗
RKNN_ERR_PARAM_INVALID(-5) 傳入參數錯誤
RKNN_ERR_MODEL_INVALID(-6) 傳入的 RKNN 模型無效
RKNN_ERR_CTX_INVALID(-7) 傳入的 rknn_context 無效
RKNN_ERR_INPUT_INVALID(-8) 傳入的 rknn_input 對象無
RKNN_ERR_OUTPUT_INVALID(-9) 傳入的 rknn_output 對象無效
RKNN_ERR_DEVICE_UNMATCH(-10) 版本不匹配
RKNN_ERR_INCOMPATILE_PRE_COM PILE_MODEL(-11) RKNN模型使用pre_compile 模式,但是和當前驅動不兼容
RKNN_ERR_INCOMPATILE_OPTIMIZAT ION_LEVEL_VERSION(-12) RKNN模型設置了優(yōu)化等級的選項,但是和當前驅動不兼容
RKNN_ERR_TARGET_PLATFORM_UN MATCH(-13) RKNN模型和當前平臺不兼容,一般是將 RK1808的平臺的RKNN模型放到了 RV1109/RV1126上。
RKNN_ERR_NON_PRE_COMPILED_M ODEL_ON_MINI_DRIVER(-14) RKNN模型不是pre_compile模式,在mini-driver上無法執(zhí)行。



審核編輯 黃宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 開發(fā)板
    +關注

    關注

    25

    文章

    6037

    瀏覽量

    110906
  • AI算法
    +關注

    關注

    0

    文章

    268

    瀏覽量

    13006
  • rv1126
    +關注

    關注

    0

    文章

    114

    瀏覽量

    3990
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    基于RV1126開發(fā)板實現人臉檢測方案

    RV1126開發(fā)板上實現人臉檢測:在圖像中找出人臉,以及每張人臉的landmarks位置。 方案設計邏輯流程圖,方案代碼分為分為兩個業(yè)務流程,主體代碼負責抓取、合成圖像,
    的頭像 發(fā)表于 04-21 17:59 ?759次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>實現人臉檢測方案

    基于RV1126開發(fā)板實現二維碼識別方案

    RV1126開發(fā)板上實現方案設計邏輯流程圖,方案代碼分為兩個業(yè)務流程,主體代碼負責抓取、合成圖像,算法代碼負責二維碼識別功能。
    的頭像 發(fā)表于 04-21 14:25 ?54次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>實現二維碼識別方案

    基于RV1126開發(fā)板實現人臉檢測方案

    RV1126開發(fā)板上實現人臉檢測:在圖像中找出人臉,以及每張人臉的landmarks位置。 方案設計邏輯流程圖,方案代碼分為分為兩個業(yè)務流程,主體代碼負責抓取、合成圖像,
    的頭像 發(fā)表于 04-21 14:00 ?13次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>實現人臉檢測方案

    基于RV1126開發(fā)板實現自學習圖像分類方案

    RV1126開發(fā)板上實現自學習:在識別前對物體圖片進行模型學習,訓練完成后通過算法分類得出圖像的模型ID。 方案設計邏輯流程圖,方案代碼分為分為兩個業(yè)務
    的頭像 發(fā)表于 04-21 13:37 ?11次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>實現自學習圖像分類方案

    基于RV1126開發(fā)板實現人臉識別方案

    RV1126開發(fā)板上實現人臉識別:在圖像中找出人臉,并與數據庫進行比對,得出該人臉對應的身份信息。 方案設計邏輯流程圖,方案代碼分為分為三個業(yè)務流程,主體代碼負責抓取、
    的頭像 發(fā)表于 04-21 10:24 ?107次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>實現人臉識別方案

    基于RV1126開發(fā)板實現人臉檢測方案

    RV1126開發(fā)板上實現人臉檢測:在圖像中找出人臉,以及每張人臉的landmarks位置。 方案設計邏輯流程圖,方案代碼分為分為兩個業(yè)務流程,主體代碼負責抓取、合成圖像,
    的頭像 發(fā)表于 04-21 10:21 ?99次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>實現人臉檢測方案

    基于RV1126開發(fā)板實現安全帽檢測方案

    RV1126開發(fā)板上實現安全帽檢測:在圖像中找出人頭/安全帽。 方案設計邏輯流程圖,方案代碼分為分為兩個業(yè)務流程,主體代碼負責抓取、合成圖像,
    的頭像 發(fā)表于 04-21 09:20 ?640次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>實現安全帽檢測方案

    基于RV1126開發(fā)板實現駕駛員行為檢測方案

    RV1126開發(fā)板上實現駕駛員行為檢測:通過圖像識別出這幾種行為:打電話、抽煙、疲勞駕駛。
    的頭像 發(fā)表于 04-18 17:47 ?354次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>實現駕駛員行為檢測方案

    基于RV1126開發(fā)板實現人員檢測方案

    RV1126開發(fā)板實現人員檢測:在圖像中找出人。 方案設計邏輯流程圖,方案代碼分為分為兩個業(yè)務流程,主體代碼負責抓取、合成圖像,算法
    的頭像 發(fā)表于 04-18 17:14 ?220次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>實現人員檢測方案

    基于RV1126開發(fā)板實現人臉識別方案

    RV1126開發(fā)板實現人臉識別:在圖像中找出人臉,并與數據庫進行比對,得出該人臉對應的身份信息。 方案設計邏輯流程圖,方案代碼分為分為三個業(yè)務流程,主體代碼負責抓取、合
    的頭像 發(fā)表于 04-18 16:55 ?83次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>實現人臉識別方案

    基于RV1126開發(fā)板AI算法開發(fā)流程

    AI算法開發(fā)流程由需求分析到準備數據,然后到選取模型,訓練模型,接著模型轉換后進行模型部署
    的頭像 發(fā)表于 04-18 14:03 ?1911次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>的<b class='flag-5'>AI</b><b class='flag-5'>算法</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>流程</b>

    基于RV1126開發(fā)板的按鍵測試方法與例程

    RV1126開發(fā)板的按鍵測試方法與例程詳細描述
    的頭像 發(fā)表于 04-15 17:03 ?668次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>的按鍵測試方法與例程

    基于RV1126開發(fā)板的人員檢測算法開發(fā)

    RV1126開發(fā)人員檢測AI算法組件
    的頭像 發(fā)表于 04-14 13:56 ?551次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>的人員檢測<b class='flag-5'>算法</b><b class='flag-5'>開發(fā)</b>

    基于RV1126開發(fā)板的人臉檢測算法開發(fā)

    RV1126開發(fā)人臉檢測算法組件
    的頭像 發(fā)表于 04-14 10:19 ?669次閱讀
    基于<b class='flag-5'>RV1126</b><b class='flag-5'>開發(fā)板</b>的人臉檢測<b class='flag-5'>算法</b><b class='flag-5'>開發(fā)</b>

    RV1126 實現簡單的UI開發(fā)示例

    RV1126上實現簡單的UI開發(fā)實例
    的頭像 發(fā)表于 04-09 16:08 ?791次閱讀
    <b class='flag-5'>RV1126</b> 實現簡單的UI<b class='flag-5'>開發(fā)</b>示例