基于RV1126平台分类模型全流程部署(附工程)

news2024/11/25 10:41:43

基于RV1126平台分类模型全流程部署

  • 环境安装
  • 模型训练
  • ONNX模型转换
  • RKNN模型转换
  • 可执行文件上板推理

环境安装

首先要在虚拟机上安装瑞芯微Rv1126的SDK,重要的是要具有rknn_toolchain
一般在以下路径:

sdk/external/rknn-toolkit

按照doc里面的步骤安装即可
项目工程代码在:https://github.com/liuyuan000/rv1126_mnist/tree/main/RKNN_Cpp/rknn_mnist_demo

模型训练

首先以MNIST手写数字体的分类为例说明分类模型的全流程部署,首先要使用torch对模型进行训练,运行以下代码即可:

import torch
import numpy as np
from matplotlib import pyplot as plt
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision import datasets
import torch.nn.functional as F

"""
卷积运算 使用mnist数据集,和10-4,11类似的,只是这里:1.输出训练轮的acc 2.模型上使用torch.nn.Sequential
"""
# Super parameter ------------------------------------------------------------------------------------
batch_size = 64
learning_rate = 0.01
momentum = 0.5
EPOCH = 10

# Prepare dataset ------------------------------------------------------------------------------------
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
# softmax归一化指数函数(https://blog.csdn.net/lz_peter/article/details/84574716),其中0.1307是mean均值和0.3081是std标准差

train_dataset = datasets.MNIST(root='./data/mnist', train=True, transform=transform,download=True)  # 本地没有就加上download=True
test_dataset = datasets.MNIST(root='./data/mnist', train=False, transform=transform)  # train=True训练集,=False测试集
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

fig = plt.figure()
for i in range(12):
    plt.subplot(3, 4, i+1)
    plt.tight_layout()
    plt.imshow(train_dataset.train_data[i], cmap='gray', interpolation='none')
    plt.title("Labels: {}".format(train_dataset.train_labels[i]))
    plt.xticks([])
    plt.yticks([])
plt.show()


# 训练集乱序,测试集有序
# Design model using class ------------------------------------------------------------------------------
class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = torch.nn.Sequential(
            torch.nn.Conv2d(1, 10, kernel_size=5),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2),
        )
        self.conv2 = torch.nn.Sequential(
            torch.nn.Conv2d(10, 20, kernel_size=5),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2),
        )
        self.fc = torch.nn.Sequential(
            torch.nn.Linear(320, 50),
            torch.nn.Linear(50, 10),
        )

    def forward(self, x):
        batch_size = x.size(0)
        x = self.conv1(x)  # 一层卷积层,一层池化层,一层激活层(图是先卷积后激活再池化,差别不大)
        x = self.conv2(x)  # 再来一次
        x = x.view(batch_size, -1)  # flatten 变成全连接网络需要的输入 (batch, 20,4,4) ==> (batch,320), -1 此处自动算出的是320
        x = self.fc(x)
        x = torch.sigmoid(x)
        return x  # 最后输出的是维度为10的,也就是(对应数学符号的0~9)


model = Net()


# Construct loss and optimizer ------------------------------------------------------------------------------
criterion = torch.nn.CrossEntropyLoss()  # 交叉熵损失
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum)  # lr学习率,momentum冲量


# Train and Test CLASS --------------------------------------------------------------------------------------
# 把单独的一轮一环封装在函数类里
def train(epoch):
    running_loss = 0.0  # 这整个epoch的loss清零
    running_total = 0
    running_correct = 0
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data
        optimizer.zero_grad()

        # forward + backward + update
        outputs = model(inputs)
        loss = criterion(outputs, target)

        loss.backward()
        optimizer.step()

        # 把运行中的loss累加起来,为了下面300次一除
        running_loss += loss.item()
        # 把运行中的准确率acc算出来
        _, predicted = torch.max(outputs.data, dim=1)
        running_total += inputs.shape[0]
        running_correct += (predicted == target).sum().item()

        if batch_idx % 300 == 299:  # 不想要每一次都出loss,浪费时间,选择每300次出一个平均损失,和准确率
            print('[%d, %5d]: loss: %.3f , acc: %.2f %%'
                  % (epoch + 1, batch_idx + 1, running_loss / 300, 100 * running_correct / running_total))
            running_loss = 0.0  # 这小批300的loss清零
            running_total = 0
            running_correct = 0  # 这小批300的acc清零

        torch.save(model.state_dict(), './model_Mnist.pth')
        # torch.save(optimizer.state_dict(), './optimizer_Mnist.pth')


def test():
    correct = 0
    total = 0
    with torch.no_grad():  # 测试集不用算梯度
        for data in test_loader:
            images, labels = data
            outputs = model(images)
            _, predicted = torch.max(outputs.data, dim=1)  # dim = 1 列是第0个维度,行是第1个维度,沿着行(第1个维度)去找1.最大值和2.最大值的下标
            total += labels.size(0)  # 张量之间的比较运算
            correct += (predicted == labels).sum().item()
    acc = correct / total
    print('[%d / %d]: Accuracy on test set: %.1f %% ' % (epoch+1, EPOCH, 100 * acc))  # 求测试的准确率,正确数/总数
    return acc


# Start train and Test --------------------------------------------------------------------------------------
if __name__ == '__main__':
    acc_list_test = []
    for epoch in range(EPOCH):
        train(epoch)
        # if epoch % 10 == 9:  #每训练10轮 测试1次
        acc_test = test()
        acc_list_test.append(acc_test)

    plt.plot(acc_list_test)
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy On TestSet')
    plt.show()

程序会自动下载数据集,并进行训练
运行结束后我们得到:model_Mnist.pth模型

ONNX模型转换

仍然使用torch加载训练后的模型,将其转换成onnx模型格式,转换代码如下:

import torch
import numpy as np

class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = torch.nn.Sequential(
            torch.nn.Conv2d(1, 10, kernel_size=5),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2),
        )
        self.conv2 = torch.nn.Sequential(
            torch.nn.Conv2d(10, 20, kernel_size=5),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2),
        )
        self.fc = torch.nn.Sequential(
            torch.nn.Linear(320, 50),
            torch.nn.Linear(50, 10),
        )

    def forward(self, x):
        batch_size = x.size(0)
        x = self.conv1(x)  # 一层卷积层,一层池化层,一层激活层(图是先卷积后激活再池化,差别不大)
        x = self.conv2(x)  # 再来一次
        x = x.view(batch_size, -1)  # flatten 变成全连接网络需要的输入 (batch, 20,4,4) ==> (batch,320), -1 此处自动算出的是320
        x = self.fc(x)
        x = torch.sigmoid(x)
        return x  # 最后输出的是维度为10的,也就是(对应数学符号的0~9)


model = Net()

model.load_state_dict(torch.load('./model_Mnist.pth'))
model.eval()

x=torch.randn((1,1,28,28))

torch.onnx.export(model, # 搭建的网络
    x, # 输入张量
    'mnist.onnx', # 输出模型名称
    input_names=["input"], # 输入命名
    output_names=["output"], # 输出命名
    opset_version=10,
    dynamic_axes={'input':{0:'batch'}, 'output':{0:'batch'}}  # 动态轴
)

其中需要注意的是opset_version,算子的版本不要选的太高,不然在后续模型转换的时候可能会出问题。
这样我们就能得到mnist.onnx文件,使用Netron可以打开:
在这里插入图片描述
可以看出整个网络的结构还是很简单的。

RKNN模型转换

得到onnx模型之后,我们就可以使用rknn.api中的RKNN将其转换为RKNN格式,方便上板推理。
此时应当使用虚拟机连接开发板,转换后会自动使用开发板进行模型推理等操作。
在进行转换之前,还需要准备几张数据集图片,把图片路径放到dataset.txt文件中,方便量化时微调网络参数。
从MNIST数据集中提取图片可以使用以下程序:

import os
import shutil
from tqdm import tqdm
from torchvision import datasets
from concurrent.futures import ThreadPoolExecutor


def mnist_export(root: str = './data/minst'):
    """Export MNIST data to a local folder using multi-threading.

    Args:
        root (str, optional): Path to local folder. Defaults to './data/minst'.
    """
    for i in range(10):
        os.makedirs(os.path.join(root, f'./{i}'), exist_ok=True)
    split_list = ['train', 'test']
    data = {
        split: datasets.MNIST(
            root='./tmp',
            train=split == 'train',
            download=True
        ) for split in split_list
    }
    total = sum([len(data[split]) for split in split_list])
    with tqdm(total=total) as pbar:
        with ThreadPoolExecutor() as tp:
            for split in split_list:
                for index, (image, label) in enumerate(data[split]):
                    tmp = os.path.join(root, f'{label}/{split}_{index}.png')
                    tp.submit(image.save, tmp).add_done_callback(
                        lambda func: pbar.update()
                    )
    shutil.rmtree('./tmp')


if __name__ == '__main__':
    mnist_export('./data/minst')

dataset.txt存放图片路径即可,示例如下:

/home/alientek/atk/mnist/data/minst/0/test_3.png
/home/alientek/atk/mnist/data/minst/0/test_10.png
/home/alientek/atk/mnist/data/minst/0/test_13.png
/home/alientek/atk/mnist/data/minst/0/test_25.png
/home/alientek/atk/mnist/data/minst/0/test_28.png
/home/alientek/atk/mnist/data/minst/0/test_55.png
/home/alientek/atk/mnist/data/minst/0/test_69.png
/home/alientek/atk/mnist/data/minst/0/test_7.png
/home/alientek/atk/mnist/data/minst/0/test_101.png
/home/alientek/atk/mnist/data/minst/0/test_126.png

此时使用python执行量化操作,指定平台为RV1126:

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

ONNX_MODEL = 'mnist.onnx'
RKNN_MODEL = 'mnist.rknn'


def show_outputs(outputs):
    output = outputs[0][0]
    output_sorted = sorted(output, reverse=True)
    top5_str = 'resnet50v2\n-----TOP 5-----\n'
    for i in range(5):
        value = output_sorted[i]
        index = np.where(output == value)
        for j in range(len(index)):
            if (i + j) >= 5:
                break
            if value > 0:
                topi = '{}: {}\n'.format(index[j], value)
            else:
                topi = '-1: 0.0\n'
            top5_str += topi
    print(top5_str)


def readable_speed(speed):
    speed_bytes = float(speed)
    speed_kbytes = speed_bytes / 1024
    if speed_kbytes > 1024:
        speed_mbytes = speed_kbytes / 1024
        if speed_mbytes > 1024:
            speed_gbytes = speed_mbytes / 1024
            return "{:.2f} GB/s".format(speed_gbytes)
        else:
            return "{:.2f} MB/s".format(speed_mbytes)
    else:
        return "{:.2f} KB/s".format(speed_kbytes)


def show_progress(blocknum, blocksize, totalsize):
    speed = (blocknum * blocksize) / (time.time() - start_time)
    speed_str = " Speed: {}".format(readable_speed(speed))
    recv_size = blocknum * blocksize

    f = sys.stdout
    progress = (recv_size / totalsize)
    progress_str = "{:.2f}%".format(progress * 100)
    n = round(progress * 50)
    s = ('#' * n).ljust(50, '-')
    f.write(progress_str.ljust(8, ' ') + '[' + s + ']' + speed_str)
    f.flush()
    f.write('\r\n')


if __name__ == '__main__':

    # Create RKNN object
    rknn = RKNN()

    # If resnet50v2 does not exist, download it.
    # Download address:
    # https://s3.amazonaws.com/onnx-model-zoo/resnet/resnet50v2/resnet50v2.onnx

    
    # pre-process config
    print('--> config model')
    rknn.config(mean_values=[[0]], std_values=[[1]], reorder_channel='0 1 2',target_platform='rv1126')# 灰度图,均值方差单通道
    print('done')

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

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

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

    # Set inputs
    #img = cv2.imread('/home/alientek/atk/mnist/data/minst/0/test_3.png',0)
    img = cv2.imread('/home/alientek/atk/mnist/data/minst/1/test_2.png',0)# 推理图片路径,灰度图
    #img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    
    # init runtime environment
    print('--> Init runtime environment')
    ret = rknn.init_runtime(target='rv1126')
    #ret = rknn.init_runtime(target='rv1126',perf_debug=True,eval_mem=True)
    #ret = rknn.eval_perf(inputs=[img], is_print=True)
    #memory_detail = rknn.eval_memory()
    if ret != 0:
        print('Init runtime environment failed')
        exit(ret)
    print('done')

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

    rknn.release()

运行以上程序即可得到:

--> Export RKNN model
done
--> Init runtime environment
I NPUTransfer: Starting NPU Transfer Client, Transfer version 2.1.0 (b5861e7@2020-11-23T11:50:36)
D RKNNAPI: ==============================================
D RKNNAPI: RKNN VERSION:
D RKNNAPI:   API: 1.7.0 (f75fb8e build: 2021-07-20 16:23:11)
D RKNNAPI:   DRV: 1.7.0 (7880361 build: 2021-08-16 14:05:08)
D RKNNAPI: ==============================================
done
--> Running model
resnet50v2
-----TOP 5-----
[1]: 1.0
-1: 0.0
-1: 0.0
-1: 0.0
-1: 0.0

done

最终输出的结果是1,并且网络具有100%把握。

可执行文件上板推理

最终为了效率等考量,一般在开发板上运行cpp编写成的可执行文件。
这里我把整个项目上传到github
网址如下:https://github.com/liuyuan000/rv1126_mnist/
下载后打开到:RKNN_Cpp/rknn_mnist_demo路径下
修改交叉编译工具链,修改成你的路径:

RV1109_TOOL_CHAIN=/opt/atk-dlrv1126-toolchain/usr
GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf

然后直接执行以下命令即可

./build.sh

执行后如下所示:
请添加图片描述
我们得到 build 和 install文件夹
请添加图片描述
将install下的mnist/RKNN_Cpp/rknn_mnist_demo/install/rknn_mnist_demo文件夹拷贝到开发板:

adb push ./rknn_mnist_demo/ /demo/MY

并且拷贝一张mnist数据集里的图片到开发板上
然后执行:

adb shell

进入开发板命令行:
请添加图片描述
执行以下命令加载模型和测试图片:

./rknn_mnist_demo ./model/mnist.rknn ./model/test_871.png 

程序输出:
请添加图片描述
最终871图片分类为0,查看一下:
请添加图片描述
分类成功结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/519770.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多模态:MiniGPT-4

多模态:MiniGPT-4 IntroductionMethodlimitation参考 Introduction GPT-4具有很好的多模态能力,但是不开源。大模型最近发展的也十分迅速,大模型的涌现能力可以很好的迁移到各类任务,于是作者猜想这种能力可不可以应用到多模态模…

物联网常见协议之 Amqp 协议及使用场景解析

引言 本文围绕 AMQP 协议,为大家详细解析 AMQP 协议、核心技术亮点、多协议之间的对比以及使用实践,并介绍华为云 IoT 通过 Amqp 协议如何为开发者和企业提供了更加灵活和高效的通信方式,使得物联网应用得以在各个领域得到更广泛的推广和应用…

项目文档(request页面代码逻辑)

项目文档 1. 封装请求基地址 2. 添加请求拦截器并设置请求头 作用 在向服务器发送请求时,很多请求都需要在请求头中携带token,每一次去请求都写请求头很麻烦,所以我们写一个请求拦截器,统一拦截并添加一个请求头 代码部分 // 请求拦截器 req…

[架构之路-194]-《软考-系统分析师》- 软件复用技术之软件产品线

目录 1. 思想来源:产品线 1.1 硬件产品线 1.2. 产品组合 2. 软件产品线 2.1 思想来源: 2.2 为什么使用软件产品线? 2.3 软件产品线是一种软件架构 2.4 软件产品线详解 2.5 软件产品线的双生命周期的模型 2.6 软件产品线建立方式 …

Kali E:Unable to locate package错误解决

默认的新装的kali 可能都会遇到这个安装报错E: Unable to locate package httrack问题,今天我记录下彻底解决过程和效果。 Command httrack not found, but can be installed with: apt install httrack Do you want to install it? (N/y)y apt install httrack Re…

介绍动作识别数据集:“NTU RGB+D”数据集和“NTU RGB+D 120”数据集

动作识别数据集:“NTU RGBD”数据集和“NTU RGBD 120”数据集 (还包括AUTH UAV手势数据集:NTU 4级) 本页介绍两个数据集:“NTU RGBD”和“NTU RGBD 120”。 “NTU RGBD”包含60个动作类和56,880个视频样本…

傅里叶分析的历史背景

目录 1. Fourier级数(三角级数)的历史背景 2. 圆和复平面 3. Fourier的大胆猜想 1. Fourier级数(三角级数)的历史背景 自古以来,圆形一直是(现在仍然是)最简单的抽象理解形状。您只需要一个中心点和一个半径就可以了。圆上的所有点与圆心的距离都是固定…

无监督域适应 (UDA)(1)

一、定义 1、无监督域自适应 Unsupervised domain adaptation 经典机器学习假设训练集和测试集来自相同的分布。 然而,这个假设在现实世界的应用程序中可能并不总是成立,例如,数据来源不同。 这种情况下,域分布之间会存在差异…

进阶自定义类型——结构体,枚举,联合

本章重点: 1.结构体 1.1 结构体类型的声明 1.2 结构的自引用 1.3 结构体变量的定义和初始化 1.4 结构体内存对齐 1.5 结构体传参 1.6 结构体实现位段(位段的填充&可移植性) 2.枚举 2.1 枚举类型的定义 2.2 枚举的优点 2.3 枚举的使用 3.联合 3.1 联合类…

年月日计算器——操作符重载的应用(含完整代码,简洁)

前言&#xff1a;大家好&#xff0c;这里是YY&#xff1b;此篇博客主要是操作符重载的应用&#xff1b;包含【流插入&#xff0c;流提取】【>,<,>,<,】【&#xff0c;-&#xff0c;&#xff0c;-】【前置&#xff0c;后置&#xff0c;前置--&#xff0c;后置--】 P…

Goby 漏洞更新 | Weblogic Commons Collections 序列化代码执行漏洞(CVE-2015-4852)

漏洞名称&#xff1a;Weblogic Commons Collections 序列化代码执行漏洞&#xff08;CVE-2015-4852&#xff09; English Name&#xff1a;Weblogic Commons Collections serialization code execution vulnerability (CVE-2015-4852) CVSS core: 7.5 影响资产数&#xff1a…

Docker ELK 监控日志(附yml)

目录 一 安装docker-commpose 二 编写yml文件 2.1 docker配置文件 2.2 filebeat配置文件 2.3 kibana配置文件 三 运行启动 四 打开kibana 一 安装docker-commpose 可以看我之前的docker文章 二 编写yml文件 2.1 docker配置文件 使用的7.17.9版本 &#xff0c;请保…

linux 下 ps、sort、top 命令详解

1、 ps命令 作用&#xff1a;查看系统进程&#xff0c;比如正在运行的进程有哪些&#xff0c;什么时候开始运行的&#xff0c;哪个用户运行的&#xff0c;占用了多少资源。 参数&#xff1a; -e 显示所有进程 -f 显示所有字段&#xff08;UID&#xff0c;PPIP&#xff0c;C…

Redis学习——单机版安装

目录 1.解压 2.安装gcc 3.执行make命令 4.复制redis的配置文件到默认安装目录下 5.修改redis.conf文件 6.启动redis服务与客户端 7.查看redis进行是否启动 8.关闭redis服务 9.redis性能测试 注意&#xff1a;安装redis前要安装jdk。 1.解压 [rootlxm148 install]# t…

ubuntu卷积神经网络——图片数据集的制作以及制作好的数据集的使用

首先我事先准备好五分类的图片放在对应的文件夹&#xff0c;图片资源在我的gitee文件夹中链接如下&#xff1a;文件管理: 用于存各种数据https://gitee.com/xiaoxiaotai/file-management.git 里面有imgs目录和npy目录&#xff0c;imgs就是存放5分类的图片的目录&#xff0c;里面…

Lesson14 高级IO

前言 IO 等待 数据拷贝,比如read/recv,write/send只要在单位事件里,让等的比重减低,IO的效率就越高 五种IO模型 钓鱼小案例 阻塞式 阻塞式: 张三拿着一根鱼竿,一直在岸边钓鱼,期间一直盯着鱼竿,等待鱼上钩 非阻塞式轮询式 非阻塞式轮询式: 李四拿着一根鱼竿,在岸边钓鱼,期…

Weblogic RCE合集

文章目录 CVE-2023-21839(T3/IIOP JNDI注入)前言漏洞简单分析漏洞复现防护措施 CVE-2020-2551(RMI-IIOP RCE)漏洞简单分析漏洞复现防护措施 CVE-2017-3506(wls-wsat组件XMLDecoder反序列化漏洞)漏洞简单分析漏洞复现防护措施 CVE-2020-14882&CVE-2020-14883漏洞简单分析 CV…

2023.05.11 c高级 day3

编写一个名为myfirstshell.sh的脚本&#xff0c;它包括以下内容。 包含一段注释&#xff0c;列出您的姓名、脚本的名称和编写这个脚本的目的和当前用户说“hello 用户名”显示您的机器名 hostname显示上一级目录中的所有文件的列表显示变量PATH和HOME的值显示磁盘使用情况用id命…

算法修炼之练气篇——练气十五层

博主&#xff1a;命运之光 专栏&#xff1a;算法修炼之练气篇 前言&#xff1a;每天练习五道题&#xff0c;炼气篇大概会练习200道题左右&#xff0c;题目有C语言网上的题&#xff0c;也有洛谷上面的题&#xff0c;题目简单适合新手入门。&#xff08;代码都是命运之光自己写的…

来领略一下带头双向循环链表的风采吧

&#x1f349; 博客主页&#xff1a;阿博历练记 &#x1f4d6;文章专栏&#xff1a;数据结构与算法 &#x1f68d;代码仓库&#xff1a;阿博编程日记 &#x1f339;欢迎关注&#xff1a;欢迎友友们点赞收藏关注哦 文章目录 &#x1f344;前言&#x1f37c;双向循环链表&#x1…