CanMV K230使用经验分享

CanMV K230使用经验-初稿

CanMV K230开发板简介

K230宣传了很久,K230的开发板目前还处于缺货的状态,价格在250左右,CanMV K230 在官网已经更新了资料,包括镜像和教程。
官网地址:https://www.canaan-creative.com/
在官网的开发者社区页面可以看到镜像、开发工具、在线文档等资料。

在这里插入图片描述

开发板图

其余信息可以查阅官网信息

CanMV K230开发板使用案例

CanMV-IDE可以通过python变成开发,简化了开发过程,但还是涉及kmodel转换
主要流程为开发模型,模型转换及测试,模型部署.

CanMV K230开发板Micropython开发

模型转换

import tensorflow as tf 
import numpy as np
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
#拟合一个一次函数 Y=2X-1
l0 = Dense(units=1, input_shape=[1])
model = Sequential([l0])
model.compile(optimizer='sgd', loss='mean_squared_error')
xs = np.array([-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
ys = np.array([-3.0, -1.0, 1.0, 3.0, 5.0, 7.0], dtype=float)
model.fit(xs, ys, epochs=500)

print(model.predict([10.0])) #测试结果
print("Here is what I learned: {}".format(l0.get_weights()))

在训练完模型后需要转换为tflite或onnx格式文件,推荐转换为onnx文件,tflite文件在输入维度少时可能会存在动态形状难以转换为kmodel模型。

import tensorflow as tf
import os
import onnx
#需要先使用model.save方法保存模型
model.save('model')
#调用tf2onnx将上一步保存的模型导出为ONNX
os.system("python3 -m tf2onnx.convert --saved-model model --output test1.onnx --opset 11")

检测是否转换成功

import onnx

onnx_model = onnx.load("./test1.onnx")
check = onnx.checker.check_model(onnx_model)
print('Check: ', check) # 返回 Check:  None 为成功

还需要调整输入维度,因为转换为onnx模型后存在输入输出维度异常
在netron上打开onnx模型,如下图所示

输入输出tensor异常
通过下面代码修正

import onnx
onnx_model = onnx.load("./test1.onnx")
onnx_model.graph.input[0].type.tensor_type.shape.dim[0].dim_value = 1
onnx_model.graph.output[0].type.tensor_type.shape.dim[0].dim_value = 1
onnx.save(onnx_model, './test1_dim.onnx')

修正结果如图
输入输出tensor修正准换onnx为kmodel模型,便于部署在k230开发板上,转换代码如下:
配置环境变量

import os
import sys
import subprocess

result = subprocess.run(["pip", "show", "nncase"], capture_output=True)

split_flag = "\n"
if sys.platform == "win32":
    split_flag = "\r\n"
    
location_s = [i for i in result.stdout.decode().split(split_flag) if i.startswith("Location:")]
location = location_s[0].split(": ")[1]

if "PATH" in os.environ:
    os.environ["PATH"] += os.pathsep + location
else:
    os.environ["PATH"] = location

定义转换参数

import nncase
import numpy as np
from nncase_base_func import *

def compile_kmodel(model_path, dump_path, calib_data):
    """
    Set compile options and ptq options.
    Compile kmodel.
    Dump the compile-time result to 'compile_options.dump_dir'
    """
    print("\n----------   compile    ----------")
    print("Simplify...")
    model_file = model_simplify(model_path)

    print("Set options...")
    # import_options
    import_options = nncase.ImportOptions()

    ############################################
    # The code below, you need to modify to fit your model.
    # You can find more details about these options in docs/USAGE_v2.md.
    ############################################
    # compile_options
    compile_options = nncase.CompileOptions()
    compile_options.target = "k230" #"cpu"
    compile_options.dump_ir = False  # if False, will not dump the compile-time result.
    compile_options.dump_asm = True
    compile_options.dump_dir = dump_path
    compile_options.input_file = ""
    
    # preprocess args   不采用预处理过程,应用与原始数据与训练模型输入之间的差异
    compile_options.preprocess = False#True
    if compile_options.preprocess:
        compile_options.input_type = "float32" # "uint8" "float32"
        compile_options.input_shape = [1,1]
        compile_options.input_range = [-1,4]
        compile_options.input_layout = "" # "NHWC"
        compile_options.swapRB = False
        compile_options.mean = [1.5]
        compile_options.std = [1.7]
        compile_options.letterbox_value = 0
        compile_options.output_layout = "" # "NHWC"

    # quantize options
    ptq_options = nncase.PTQTensorOptions()
    ptq_options.quant_type = "uint8"    # datatype : "float32", "int8", "int16"
    ptq_options.w_quant_type = "uint8"  # datatype : "float32", "int8", "int16"
    ptq_options.calibrate_method = "NoClip" # "Kld"
    ptq_options.finetune_weights_method = "NoFineTuneWeights"
    ptq_options.dump_quant_error = True   # False 输出模型推断的误差结果,评估转换情况
    ptq_options.dump_quant_error_symmetric_for_signed = True  #False

    # mix quantize options
    # more details in docs/MixQuant.md
    ptq_options.quant_scheme = ""
    ptq_options.quant_scheme_strict_mode = False
    ptq_options.export_quant_scheme = False
    ptq_options.export_weight_range_by_channel = False
    ############################################
    
    ptq_options.samples_count = len(calib_data[0])
    ptq_options.set_tensor_data(calib_data)
    
    print("Compiling...")
    compiler = nncase.Compiler(compile_options)
    # import
    model_content = read_model_file(model_file)
    if model_path.split(".")[-1] == "onnx":
        compiler.import_onnx(model_content, import_options)
    elif model_path.split(".")[-1] == "tflite":
        compiler.import_tflite(model_content, import_options)
    
    compiler.use_ptq(ptq_options)
    
    # compile
    compiler.compile()
    kmodel = compiler.gencode_tobytes()
    
    kmodel_path = os.path.join(dump_path, "test1.kmodel")
    with open(kmodel_path, 'wb') as f:
        f.write(kmodel)
    print("----------------end-----------------")
    return kmodel_path

模型转换

# compile kmodel single input  动态过程需要预先设置,
model_path = "./test1_dim.onnx"
dump_path = "./test1_onnx"
# sample_count is 2
calib_data = [[100*np.random.rand(1, 1).astype(np.float32), 100*np.random.rand(1, 1).astype(np.float32)]]  # 代表数据集
kmodel_path = compile_kmodel(model_path, dump_path, calib_data)

模型测试

# run kmodel(simulate)
import os

kmodel_path = "./test1_onnx/test1.kmodel"

input_data=10*np.random.rand(1, 1).astype(np.float32)
print(input_data)
result = run_kmodel(kmodel_path, input_data)
# 输出被限制在代表数据集内 代表数据集的限制
print(result)

CanMV K230开发板python(完善)

将转换后的test1.kmodel拷贝到sd卡中,可以采用读卡器读取,将文件放置在/sdcard/app/tests/k230_test/test1.kmodel位置。

链接canmv ide
新建文件并编写代码

import nncase_runtime as nn
import ulab.numpy as np

a=np.array([22])

kpu = nn.kpu()
kpu.load_kmodel("/sdcard/app/tests/k230_test/test1.kmodel")

print("inputs info:")
for i in range(kpu.inputs_size()):
    print(kpu.inputs_desc(i))

print("outputs info:")
for i in range(kpu.outputs_size()):
    print(kpu.outputs_desc(i))

input_data = np.frombuffer(a,dtype=np.float)
print(input_data.shape)
input_data = input_data.reshape((1,1))
print(input_data.shape)

kpu.set_input_tensor(0, nn.from_numpy(input_data))

# run kmodel
kpu.run()

print('input  is : ',a[0])
# get output
for i in range(kpu.outputs_size()):
    result = kpu.get_output_tensor(i)
    result = result.to_numpy()
    print('output is : ',result[0][0])

del kpu
del input_data
del result

测试结果如下图所示
k230输出结果
图中显示在结果与函数接近30×2-1=59,但300、100时其结果为192.625,当数据降为90时正常,可能的原因是在转换模型时校正数据集时,限制了模型的范围,因此校正数据集的设置很重要,虽然这是一个很小的模型,但转换后的量化导致推断出现问题,可以尝试扩大数据集的参数范围。

以上为简单的一个模型尝试过程,欢迎讨论,后续会补充一些细节及扩展,例如传感器输入数据进行推断,如adc采集或温度传感器等采集数据进行推断等。

CanMV K230开发板使用感受

python开发较为简单,便于模型的快速验证,但实现的功能有限,感觉C++的学习还是有必要的。同时工具链的使用还不熟练,官方的文档等也有待加强,嘻嘻

  • 37
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值