【onnx模型转kmodel】记录和踩坑——nncase-v1.9使用

news2024/10/11 0:03:04

最近几天一直在找相关资料,坑太多,也可能我菜的成分更多一点吧!记录下来,以观后用;
在这里插入图片描述

背景

  1. 我手里有一个正点原子的K210的开发板;

  2. 刚刚安装了wsl2下的ubuntu22.04

  3. 我手里有正点原子的源码,但是源码中只有输出好的kmodel模型文件,我并不知道kmodel怎么来的,由此疑问;
    在这里插入图片描述

  4. 所以我想尝试用Pyttorch搞个简单的模型跑一下MNIST数据集,然后放进K210里面运行,补充上面的疑问之处;

  5. 了解到共有如下步骤:模型训练——>导出"model.pth"——>“model.onnx”——>“model.kmodel”——>编译产物"xxx.bin"——>烧录——>成功运行;

  6. 然后我成功的在"model.onnx"——>"model.kmodel"卡壳了;

需求

  1. 准备可以转换的onnx模型文件
  2. 准备转换所需的软件和脚本
  3. 修改和使用转换所需的脚步
  4. 执行onnx转kmodel
  5. 编译烧录之后可以成功运行

步骤

  1. 我的设备是k210的,根据nncase官网所说,nncase2.x版本不支持,所以我选择nncase1.9;
  2. 下载和安装时依据我ubuntu的python版本3.10选择如下:
# 下载nncase
root@2b11cc15c7f8:/mnt# wget -P https://github.com/kendryte/nncase/releases/download/v1.9.0/nncase-1.9.0.20230322-cp310-cp310-manylinux_2_24_x86_64.whl
# 安装nncase
root@2b11cc15c7f8:/mnt# pip3 install nncase-1.9.0.20230322-cp310-cp310-manylinux_2_24_x86_64.whl

在这里插入图片描述
3. 准备onnx模型

def pth_to_onnx():
    
    # 加载模型
    model = OptimizedMobileNetV1()
    model.load_state_dict(torch.load('./to_kmodel_test/demo_model.pth'))

    # 模型放到cpu上
    model = model.to(device)                            
    model.eval()

    # 模型输入shape
    input_shape = (1, 1, 28, 28)
    input_data = torch.randn(input_shape).to(device)

    # 模型转onnx,这里注意1.x的nncase不支持动态shape,不能使用dynamic_axes,否则影响转换kmodel
    torch.onnx.export(model, input_data, './to_kmodel_test/demo_model.onnx',
              do_constant_folding=True,)  # 是否执行常量折叠优化
            #   input_names=["input"],  # 输入名
            #   output_names=["output"],  # 输出名
            #   dynamic_axes={"input": {0: "batch_size"},  # 批处理变量
            #                 "output": {0: "batch_size"}})

    # 加载并检查模型
    model_onnx = onnx.load('./to_kmodel_test/demo_model.onnx')
    onnx.checker.check_model(model_onnx)
    print(onnx.helper.printable_graph(model_onnx.graph))

    # 使用onnxsim进行模型精简
    model_simp, check = simplify(model_onnx)
    assert check, "Simplified ONNX model could not be validated"
    onnx.save(model_simp, './to_kmodel_test/demo_model.onnx')
    print("model to onnx done!")
  1. 安排转换脚本
    我是从 这里 找到的,这里我重命名为onnx_to_kmodel_copy.py
    以下代码我只修改了cpl_opt.input_shape = [1, 3, 224, 224]为我的模型输入shapecpl_opt.input_shape = [1, 1, 28, 28]
import argparse
import os
import sys
from pathlib import Path

import cv2
import nncase
import numpy as np
print(os.getpid())


def preproc(img, input_size, transpose=True):
    if len(img.shape) == 3:
        padded_img = np.ones((input_size[0], input_size[1], 3), dtype=np.uint8) * 114
    else:
        padded_img = np.ones(input_size, dtype=np.uint8) * 114

    r = min(input_size[0] / img.shape[0], input_size[1] / img.shape[1])
    resized_img = cv2.resize(
        img,
        (int(img.shape[1] * r), int(img.shape[0] * r)),
        interpolation=cv2.INTER_LINEAR,
    ).astype(np.uint8)
    padded_img[: int(img.shape[0] * r), : int(img.shape[1] * r)] = resized_img
    padded_img = cv2.cvtColor(padded_img, cv2.COLOR_BGR2RGB)
    if transpose:
        padded_img = padded_img.transpose((2, 0, 1))
    padded_img = np.ascontiguousarray(padded_img)
    return padded_img, r


def read_images(imgs_dir: str, test_size: list):
    imgs_dir = Path(imgs_dir)
    imgs = []
    for p in imgs_dir.iterdir():
        img = cv2.imread(str(p))
        img, _ = preproc(img, test_size, True)  # img [h,w,c] rgb,
        imgs.append(img)

    imgs = np.stack(imgs)
    return len(imgs), imgs.tobytes()


def main(onnx: str, kmodel: str, target: str, method: str, imgs_dir: str, test_size: list, legacy: bool, no_preprocess: bool):
    cpl_opt = nncase.CompileOptions()
    cpl_opt.preprocess = not no_preprocess
    # (x - mean) / scale
    if legacy:
        cpl_opt.swapRB = False  # legacy use RGB 
        cpl_opt.input_range = [0, 1]
        cpl_opt.mean = [0.485, 0.456, 0.406]
        cpl_opt.std = [0.229, 0.224, 0.225]
    else:
        cpl_opt.swapRB = True  # new model use BGR 
        cpl_opt.input_range = [0, 255]
        cpl_opt.mean = [0, 0, 0]
        cpl_opt.std = [1, 1, 1]
    cpl_opt.target = target  # cpu , k210, k510!
    cpl_opt.input_type = 'uint8'
    cpl_opt.input_layout = 'NCHW'
    # cpl_opt.input_shape = [1, 3, 224, 224]
    cpl_opt.input_shape = [1, 1, 28, 28]
    cpl_opt.quant_type = 'uint8'  # uint8 or int8

    compiler = nncase.Compiler(cpl_opt)
    with open(onnx, 'rb') as f:
        imp_opt = nncase.ImportOptions()
        compiler.import_onnx(f.read(), imp_opt)
        # ptq
        if imgs_dir is not None:
            ptq_opt = nncase.PTQTensorOptions()
            ptq_opt.calibrate_method = method
            ptq_opt.samples_count, tensor_data = read_images(
                imgs_dir, test_size)
            ptq_opt.set_tensor_data(tensor_data)
            compiler.use_ptq(ptq_opt)
        compiler.compile()
        kmodel_bytes = compiler.gencode_tobytes()
    with open(kmodel, 'wb') as of:
        of.write(kmodel_bytes)
        of.flush()


if __name__ == '__main__':
    parser = argparse.ArgumentParser("YOLOX Compile Demo!")
    parser.add_argument('onnx', default='model/yolox_nano_224_new.onnx', help='model path')
    parser.add_argument('kmodel', default='yolox_nano_224_new.kmodel', help='bin path')
    parser.add_argument('--target', default='cpu',
                        choices=['cpu', 'k210', 'k510'], help='compile target')
    parser.add_argument('--method', default='no_clip',
                        choices=['no_clip', 'l2', 'kld_m0', 'kld_m1', 'kld_m2', 'cdf'],
                        help='calibrate method')
    parser.add_argument('--test_size', default=[224, 224],
                        nargs='+', help='test size')
    parser.add_argument("--imgs_dir", default=None, help="images dir")
    parser.add_argument("--legacy", default=False,
                        action="store_true", help="To be compatible with older versions")
    parser.add_argument("--no_preprocess", default=False,
                        action="store_true", help="disable nncase preprocess for debug")

    args = parser.parse_args()
    main(args.onnx, args.kmodel, args.target, args.method, args.imgs_dir,
         args.test_size, args.legacy, args.no_preprocess)
  1. 执行onnx转换kmodel
root@2b11cc15c7f8: python3 ./to_kmodel_test/onnx_to_kmodel_copy.py ./to_kmodel_test/demo_model.onnx ./to_kmodel_test/demo_model.kmodel --legacy --target=k210
# 以下为执行成功输出
16303
1. Import graph...
1.1 Pre-process...
 |Dequantize:
 |Normalize:
2. Optimize target independent...
3. Optimize target dependent...
5. Optimize target dependent after quantization...
6. Optimize modules...
7.1. Merge module regions...
7.2. Optimize buffer fusion...
7.3. Optimize target dependent after buffer fusion...
8. Generate code...

SUMMARY
INPUTS

成功后如下:
在这里插入图片描述

  1. 编译时需要用v1.9版本的nncaseruntime替换掉原来版本的,保持版本对齐才行,否则编译成功后运行可能出错
# nncaseruntime运行时库
root@2b11cc15c7f8:/mnt# wget https://github.com/kendryte/nncase/releases/download/v1.9.0/nncaseruntime-riscv64-none-k210.zip

替换路径:

kendryte-standalone-sdk/lib/nncase/v1/

删除该路径下的
在这里插入图片描述
将解压nncaseruntime-riscv64-none-k210.zip后的文件放进去就可以编译了

探索过程中遇到的问题

  1. 找不到转换的可用脚本 or 可用命令,一开始看网上大佬2022年的博客用到的nncase都是0.5左右版本的,用到的是ncc命令,更有大佬直接下源码编译,好吧我菜,看官网的使用说明,提供的程序试了有问题,毕竟程序可能是针对2.x版本的,而我的是1.9,最后才在https://github.com/kendryte/nncase/blob/v1.9.0/examples/下找到了yolox的例子,看了 readme ,yolox目录下的model目录有yolox_nano_224.onnx文件,我就尝试了python tools/compile.py model/yolox_nano_224.onnx yolox_nano_224.kmodel --legacy发现可以,这个过程断断续续花了我3天吧,苦逼啊;
  2. 使用脚本进行onnx转kmodel的时候,死活提示缺少xx shape信息,尝试把模型换成全连接的没问题,

在这里插入图片描述后穷尽手段后加了nncase 的QQ群,问了专业人士,好,结论如下:

1.x的nncase不支持动态shape,不能使用dynamic_axes,否则影响转换kmodel
  1. 终于烧录好,运行时却提示失败,G…,要崩溃了呀,不想玩了,我的模型才100多k,编译后的bin也就1M+
[..m/runtime_module.cpp:65 (shape_reg)] id < shape_regs_.size() = false (bool)
error: Result too large

还好有前人踩坑,大佬回答,一句话我就秒懂,发现编译时需要用v1.9版本的nncaseruntime替换掉原来版本的,保持版本对齐才行
https://github.com/kendryte/kendryte-standalone-sdk/issues/133

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

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

相关文章

【Redis入门到精通十一】Redis集群

目录 集群 1.三种分片算法 1.哈希求余算法 2.一致性哈希算法 3.哈希槽分区算法 2.搭建集群环境 3.集群故障处理 4.集群扩容 集群 上篇文章我们了解Redis哨兵的相关操作&#xff0c;使用哨兵只是解决了主节点瘫痪&#xff0c;从节点不能自动变为主节点的问题&#xff0c;…

[ComfyUI]太赞了!阿里妈妈发布升级版 Flux 图像修复模型,更强细节生成,更高融合度以及更大分辨率支持

小伙伴们还记得我们之前介绍的阿里妈妈发布的 Flux 的 ControlNet 图像修复模型不&#xff0c;之前发布的是 Alpha 早期测试版本&#xff0c;说实话和 Flux 原生的重绘其实差距不大&#xff0c;有些方面甚至还是原生的效果更好。 但是现在&#xff0c;Alpha 的升级版本 Beta 版…

基于java的零食销售系统(源码+定制+开发)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

qt小练习

制作简易闹钟 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimer> //定时器类 #include <QDebug> //信息调试类 #include <QMessageBox> //消息对话框类 #include <QTime> //时间类 #include…

【C语言复习】常见概念(零基础)

【C语言复习】常见概念 1、C语言是什么&#xff1f;2、C语言的历史和辉煌3、 编译器的选择VS20223.1编译和链接3.2编译器的对比3.3 VS2022 的优缺点 4、VS项⽬ 和 源⽂件、头⽂件介绍5、第一个C语言程序6、main函数&#xff08;主函数&#xff09;7、printf和库函数8、关键字介…

基于springboot的大学生体质测试管理系统(含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于springboot的大学生体质测试管理系统1拥有三种角色 管理员&#xff1a;学生管理、教师管理、日常运行管理、运动分析管理、成绩管理、论坛管理、轮播图管理等 教师&#xff1a;登录…

如何在RuoYi-Vue项目中去除`/dev-api`前缀

前言 在使用RuoYi-Vue框架进行Web应用开发时&#xff0c;有时会遇到API路径需要特定前缀的问题。例如&#xff0c;在某些情况下&#xff0c;开发者可能希望移除或更改默认的/dev-api前缀。 问题描述 当使用YApi直接请求后台接口时&#xff0c;无需添加/dev-api前缀。在生成和…

C++第十六节课 万字详细手动实现string类!

std::basic_string std::basic_string 是 C 标准库中定义的一个模板类&#xff0c;它用于表示字符串。C 中的 std::string 实际上是 std::basic_string<char> 的一个特化版本。也就是说&#xff0c;std::string 是 std::basic_string 这个模板类的一个具体实现&#xff0…

DAY29|| 93.复原ip地址 |78.子集 |90.子集Ⅱ

93.复原ip地址 题目&#xff1a;93. 复原 IP 地址 - 力扣&#xff08;LeetCode&#xff09; 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.…

国外电商系统开发-运维系统上传脚本

创建脚本的方式有两种&#xff1a;第一种脚本文件&#xff0c;第二种在线写脚本。并且友好的支持中文的显示和脚本的中文名。 第一种是从您的PC电脑上传一个脚本文件&#xff0c;当然了&#xff0c;还是以老用法&#xff0c;直接拖动就行&#xff1a; 第二种上传方式&#xff0…

『网络游戏』服务器日志工具类优化【18】

创建脚本&#xff1a;PECommon.cs 编写脚本&#xff1a;PECommon.cs 修改脚本&#xff1a;LoginSys 替换 替换完成 修改客户端脚本&#xff1a;ResSvc.cs 本章结束

仓储物流行业--仓储服务升级经典案例

在当今内外贸竞争激烈的仓储物流行业&#xff0c;高效的数据处理和管理是企业提升竞争力的关键。杭州某供应链公司作为一家专注为中小卖家提供定制仓储服务方案的第三方云仓&#xff0c;在业务发展过程中面临着数据处理方面的诸多挑战。本文将详细介绍云仓项目如何通过轻易云数…

上海交通大学震撼发布:首个OpenAI O1项目复现报告,揭秘独家经验!

来源 | 机器之心 团队介绍&#xff1a;本项目的核心开发团队主要由上海交通大学 GAIR 研究组的本科三年级、四年级学生以及直博一年级研究生组成。项目得到了来自 NYU 等一线大型语言模型领域顶尖研究科学家的指导。 详细作者介绍见&#xff1a;https://github.com/GAIR-NLP/…

FireFox简单设置设置

文章目录 一 设置不显示标签页1原来的样子2新的样子3操作方法 二 设置竖直标签页栏1 效果图2 设置方法 三 设置firefox不提醒更新 一 设置不显示标签页 1原来的样子 2新的样子 3操作方法 地址栏输入 about:config搜索icon,双击选项列表中browserchrome.site icons的值&#…

HWS赛题 入门 MIPS Pwn-Mplogin(MIPS_shellcode)

解题所涉知识点&#xff1a; 泄露或修改内存数据&#xff1a; 堆地址&#xff1a;栈地址&#xff1a;栈上数据的连带输出(Stack Leak) && Stack溢出覆盖内存libc地址&#xff1a;BSS段地址&#xff1a; 劫持程序执行流程&#xff1a;[[MIPS_ROP]] 获得shell或flag&am…

关于css文字下划线动画实现

直接上代码 html部分 <div><span class"txt">文字下滑动画</span></div>css部分 .txt {background: linear-gradient(270deg, #4f95fd 0%, #1059df 100%) no-repeat left bottom;background-size: 0px 2px;background-position-x: right;tr…

汽车微控制器 (MCU)市场报告:未来几年年复合增长率CAGR为5.8%

汽车微控制器是一种高度集成的电路芯片&#xff0c;集成了中央处理器&#xff08;CPU&#xff09;、存储器&#xff08;ROM、RAM、EEPROM等&#xff09;和各种输入输出接口&#xff08;I/O&#xff09;&#xff0c;能够通过软件编程实现对汽车各种电子设备的控制和管理。在汽车…

字符设备驱动模块 dev和misc

字符设备驱动模块 用户空间和内核空间数据拷贝&#xff1a; copy_from_user(); 用户空间数据传给内核 copy_to_user(); 内核数据传给用户空间 动态和静态分别编译 设置Kconfig属性 编译内核文件&#xff08;静态、动态内核文件&#xff09; 启动动态驱动模块 查看动态驱动…

深度学习每周学习总结J2(ResNet-50v2算法实战与解析 - 鸟类识别)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 目录 0. 总结1. 设置GPU2. 导入数据及处理部分3. 划分数据集4. 模型构建部分5. 设置超参数&#xff1a;定义损失函数&#xff0c;学习率&a…

Web开发:总结常见的批处理脚本(.bat)

一、一键复制多个文件 echo off setlocalset source01.pngcopy "%source%" "a.png" copy "%source%" "b.png" copy "%source%" "c.png"endlocal说明&#xff1a; 将上述代码复制到一个新的文本文件中。将文件保…