基于RK3588平台的机器学习模型部署

硬件平台介绍

这次体验的硬件平台是来自飞凌嵌入式的OK3588-C开发板,该开发板基于Rockchip新一代旗舰 RK3588处理器开发,采用核心板+底板的分体式设计,将FET3588-C核心板的全部功能引脚以最便利的方式引出,并针对不同的功能做了深度优化,方便用户二次开发的同时简化用户设计。

rk3588 NPU及开发工具介绍

rk3588专门针对机器学习模型部署的需求配置了性能强劲的NPU,rk3588配置的NPU不仅提供6TOPS的算力,还支持INT4/INT8/INT16/FP16格式的混合操作。除了非常不错的硬件支持之外,Rockchip公司还提供了便捷的开发工具rknn-toolkit2 和 rknpu2-api,让开发者可以方便的将自己的机器学习模型进行转换和部署。

rknn-toolkit2

根据rockchip提供的《Rockchip_User_Guide_RKNN_Toolkit2》开发文档介绍:

RKNN-Toolkit2 是为用户提供在 PC、Rockchip NPU 平台上进行模型转换、推理和性能评估的
开发套件,用户通过该工具提供的 Python 接口可以便捷地完成以下功能:

  • 模型转换: 支持 Caffe、TensorFlow、TensorFlow Lite、ONNX、DarkNet、PyTorch 等模型
    转为 RKNN 模型,并支持 RKNN 模型导入导出,RKNN 模型能够在 Rockchip NPU 平台
    上加载使用。
  • 量 化 功 能 : 支 持 将 浮 点 模 型 量 化 为 定 点 模 型 , 目 前 支 持 的 量 化 方 法 为 非 对 称 量 化
    ( asymmetric_quantized-8 ) , 并 支 持 混 合 量 化 功 能 。
  • 模型推理:能够在 PC 上模拟 Rockchip NPU 运行 RKNN 模型并获取推理结果;或将 RKNN
    模型分发到指定的 NPU 设备上进行推理并获取推理结果。
  • 性能和内存评估:将 RKNN 模型分发到指定 NPU 设备上运行,以评估模型在实际设备上
    运行时的性能和内存占用情况。
  • 量化精度分析:该功能将给出模型量化前后每一层推理结果与浮点模型推理结果的余弦距
    离,以便于分析量化误差是如何出现的,为提高量化模型的精度提供思路。
安装

可以从github上的rockchip官方仓库中下载最新的适合于本地python环境的rknn_toolkit2安装文件,目前github上最新的版本已经支持python3.10了。下载完python安装文件后,可以用pip直接安装,这里以python3.10版本为例。需要注意的是rknn_toolkit2的安装是以来tensorflow2.8版本tf-estimator-nightly特定版本的。所以在安装rknn_toolkit2之前先利用pip安装tensorflow2.8版本,对于已经安装其他版本tensorflow的,要先进行卸载,再重新安装。

pip3 install tensorflow==2.8
pip3 install tf-estimator-nightly==2.8.0.dev2021122109 -i https://pypi.org/simple

接着用pip安装刚下在的rknn_toolkit2安装包。

pip3 install rknn_toolkit2-1.5.2%2Bb642f30c-cp310-cp310-linux_x86_64.whl

下图是安装完成后的提示信息:
在这里插入图片描述

测试

在安装完成之后,可以直接使用rknn_toolkit2提供的demo程序进行测试。这里我们选择tensorflow构建的机器学习模型进行测试。

官方提供的测试程序在rknn-toolkit2/examples/tensorflow/ssd_mobilenet_v1路径下。

具体测试程序如下:

import numpy as np
import re
import math
import random
import cv2

from rknn.api import RKNN

INPUT_SIZE = 300

NUM_RESULTS = 1917
NUM_CLASSES = 91

Y_SCALE = 10.0
X_SCALE = 10.0
H_SCALE = 5.0
W_SCALE = 5.0

def expit(x):
    return 1. / (1. + math.exp(-x))

def unexpit(y):
    return -1.0 * math.log((1.0 / y) - 1.0)

def CalculateOverlap(xmin0, ymin0, xmax0, ymax0, xmin1, ymin1, xmax1, ymax1):
    w = max(0.0, min(xmax0, xmax1) - max(xmin0, xmin1))
    h = max(0.0, min(ymax0, ymax1) - max(ymin0, ymin1))
    i = w * h
    u = (xmax0 - xmin0) * (ymax0 - ymin0) + (xmax1 - xmin1) * (ymax1 - ymin1) - i

    if u <= 0.0:
        return 0.0

    return i / u

def load_box_priors():
    box_priors_ = []
    fp = open('./box_priors.txt', 'r')
    ls = fp.readlines()
    for s in ls:
        aList = re.findall('([-+]?\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?', s)
        for ss in aList:
            aNum = float((ss[0]+ss[2]))
            box_priors_.append(aNum)
    fp.close()

    box_priors = np.array(box_priors_)
    box_priors = box_priors.reshape(4, NUM_RESULTS)

    return box_priors

if __name__ == '__main__':

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

    # Pre-process config
    print('--> Config model')
    rknn.config(mean_values=[127.5, 127.5, 127.5], std_values=[127.5, 127.5, 127.5], target_platform='rk3566')
    print('done')

    # Load model (from https://github.com/fvmassoli/Deep-Learning-SSD-Object-Detection)
    print('--> Loading model')
    ret = rknn.load_tensorflow(tf_pb='./ssd_mobilenet_v1_coco_2017_11_17.pb',
                               inputs=['Preprocessor/sub'],
                               outputs=['concat', 'concat_1'],
                               input_size_list=[[1, INPUT_SIZE, INPUT_SIZE, 3]])
    if ret != 0:
        print('Load model failed!')
        exit(ret)
    print('done')

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

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

    # Set inputs
    orig_img = cv2.imread('./road.bmp')
    img = cv2.cvtColor(orig_img, cv2.COLOR_BGR2RGB)
    img = cv2.resize(img, (INPUT_SIZE, INPUT_SIZE), interpolation=cv2.INTER_CUBIC)

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

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

    predictions = outputs[0].reshape((1, NUM_RESULTS, 4))
    np.save('./tensorflow_ssd_mobilenet_v1_0.npy', outputs[0])
    outputClasses = outputs[1].reshape((1, NUM_RESULTS, NUM_CLASSES))
    np.save('./tensorflow_ssd_mobilenet_v1_1.npy', outputs[0])
    candidateBox = np.zeros([2, NUM_RESULTS], dtype=int)
    classScore = [-1000.0] * NUM_RESULTS
    vaildCnt = 0

    box_priors = load_box_priors()

    # Post Process
    # got valid candidate box
    for i in range(0, NUM_RESULTS):
        topClassScore = -1000
        topClassScoreIndex = -1

        # Skip the first catch-all class.
        for j in range(1, NUM_CLASSES):
            score = expit(outputClasses[0][i][j])

            if score > topClassScore:
                topClassScoreIndex = j
                topClassScore = score

        if topClassScore > 0.4:
            candidateBox[0][vaildCnt] = i
            candidateBox[1][vaildCnt] = topClassScoreIndex
            classScore[vaildCnt] = topClassScore
            vaildCnt += 1

    # calc position
    for i in range(0, vaildCnt):
        if candidateBox[0][i] == -1:
            continue

        n = candidateBox[0][i]
        ycenter = predictions[0][n][0] / Y_SCALE * box_priors[2][n] + box_priors[0][n]
        xcenter = predictions[0][n][1] / X_SCALE * box_priors[3][n] + box_priors[1][n]
        h = math.exp(predictions[0][n][2] / H_SCALE) * box_priors[2][n]
        w = math.exp(predictions[0][n][3] / W_SCALE) * box_priors[3][n]

        ymin = ycenter - h / 2.
        xmin = xcenter - w / 2.
        ymax = ycenter + h / 2.
        xmax = xcenter + w / 2.

        predictions[0][n][0] = ymin
        predictions[0][n][1] = xmin
        predictions[0][n][2] = ymax
        predictions[0][n][3] = xmax

    # NMS
    for i in range(0, vaildCnt):
        if candidateBox[0][i] == -1:
            continue

        n = candidateBox[0][i]
        xmin0 = predictions[0][n][1]
        ymin0 = predictions[0][n][0]
        xmax0 = predictions[0][n][3]
        ymax0 = predictions[0][n][2]

        for j in range(i+1, vaildCnt):
            m = candidateBox[0][j]

            if m == -1:
                continue

            xmin1 = predictions[0][m][1]
            ymin1 = predictions[0][m][0]
            xmax1 = predictions[0][m][3]
            ymax1 = predictions[0][m][2]

            iou = CalculateOverlap(xmin0, ymin0, xmax0, ymax0, xmin1, ymin1, xmax1, ymax1)

            if iou >= 0.45:
                candidateBox[0][j] = -1

    # Draw result
    for i in range(0, vaildCnt):
        if candidateBox[0][i] == -1:
            continue

        n = candidateBox[0][i]

        xmin = max(0.0, min(1.0, predictions[0][n][1])) * INPUT_SIZE
        ymin = max(0.0, min(1.0, predictions[0][n][0])) * INPUT_SIZE
        xmax = max(0.0, min(1.0, predictions[0][n][3])) * INPUT_SIZE
        ymax = max(0.0, min(1.0, predictions[0][n][2])) * INPUT_SIZE

        print("%d @ (%d, %d) (%d, %d) score=%f" % (candidateBox[1][i], xmin, ymin, xmax, ymax, classScore[i]))
        cv2.rectangle(orig_img, (int(xmin), int(ymin)), (int(xmax), int(ymax)),
                      (random.random()*255, random.random()*255, random.random()*255), 3)

    cv2.imwrite("result.jpg", orig_img)

    rknn.release()

以上程序主要包括以下几个步骤和对应的接口(省略了参数调用):

  1. 创建RKNN对象: rknn=RKNN()
  2. 模型输入的预处理配置: rknn.config()
  3. 加载tensorflow生成的pb模型:rknn.load_tensorflow()
  4. 构建rknn模型:rknn.build()
  5. 导出rknn模型:rknn.export_rknn()
  6. 初始化模型运行环境:rknn.init_runtime()
  7. 设置模型的输入:按照实际模型输入的要求准备好输入数据
  8. 利用rknn模型进行推理: rknn.inference()
  9. 模型推理结果的后处理:将模型输出的推理结果处理成方便理解和使用的形式

以上是针对tensorflow框架下的机器学习模型的转换和运行步骤,rknn-toolkit2还支持以下机器学习模型框架:

机器学习模型框架输入模型文件后缀对应模型加载API
Caffe.prototxtload_caffe
TensorFlow.pbload_tensorflow
TensorFlow Lite.tfliteload_tflite
ONNX.onnxload_onnx
DarkNet.cfgload_darknet
PyTorch.ptload_pytorch
连板运行

rknn模型的推理运行有两种方式,一种是直接在PC上利用模拟环境调用rknn模型进行推理运算。还有一种是利用rk3588上的硬件NPU进行推理运算。第二种方式需要在PC和RK3588开发板之间进行数据传输,主要包括以下步骤:

  1. 用USB转typeC的线,将开发板上的TypeC0口和PC的USB口进行连接
  2. 在RK3588开发板上启动rknn_server。在终端中运行
/usr/bin/rknn_server start

rknn_server启动后会打印相关信息,并且在有PC端连接时也会打印相应信息,如下图所示:

  1. 修改上面的测试程序,在运行环境初始化时指定运行的目标平台:
rknn.init_runtime(target='rk3588')
  1. 运行测试程序得到推理结果
python3 test.py

得到输出结果如下:

res

从上面的打印信息可以看出, PC上的rknn-toolkit2成功与开发板上运行的rknn服务器连接成功,并且通过调用rk3588的硬件NPU进行模型的推理运算,得出运算结果。

下图是通过连板运行模型推理得到的结果,可以看出目标检测模型成功检测出凸显各种的人,自行车,汽车等目标。

在这里插入图片描述

总结

通过rknn-toolkit2开发工具,可以方便的将各种框架下得到的机器学习模型转化为rk3588需要的rknn模型,并且调用rk3588的NPU进行模型的推理运算。考虑到rk3588的NPU具有很强的运算能力(6TOPS),将机器学习模型部署到rk3588上并由专用的NPU提供算力支持,可以大大提高模型的计算速度和能力,为机器学习模型的终端部署提供有效保证。

当然,在实际产品开发中,如果是使用python环境,其实是通过在rk3588中调用rknn-lite工具包调用rknn模型进行推理计算。而更高效的方式则是利用rockchip公司提供的rknpu的C语言API对转换后的rknn模型进行调用和推理,关于这部分内容,有机会博主会在后续的文章中详细总结整理。

rk3588evb是一款基于Rockchip RK3588芯片的开发板,用于人工智能应用领域。模型部署是指将训练好的模型应用到实际场景中,以实现特定的任务。而视频检测则是指通过对视频中的内容进行分析和识别,从而实现人、物体、动作等的检测和识别功能。 将模型部署rk3588evb上进行视频检测,首先需要将训练好的模型导入到rk3588evb的开发环境中。开发环境可以是使用开发板的SDK或者工具包。通过将模型开发环境进行适配,可以确保模型可以在rk3588evb上正常运行。 接下来,在rk3588evb上编写运行代码,使用模型对视频进行检测。视频检测涉及到识别和追踪视频中的人和物体。可以通过处理视频的每一帧图像,并将其输入到模型中进行预测和判别。模型会根据训练时的分类标准,识别出视频中的不同物体和动作。 为了提高视频检测的效果,可以采用一些优化方法和技术。例如,可以使用图像处理算法对视频图像进行预处理,以提高模型的输入质量。同时,可以将模型与物体跟踪算法相结合,以实现对运动物体的跟踪和检测。 最后,将视频检测的结果进行输出和展示。可以将检测到的物体和动作标注在视频中,或者生成报告和统计数据。这样可以方便用户对视频内容进行分析和理解。 通过在rk3588evb上进行模型部署,可以实现视频检测的任务,为用户提供智能化的视频分析。同时,rk3588evb作为高性能的开发板,能够提供足够的计算能力,以满足视频检测的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无知的Talent

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值