不爱胡萝卜的仓鼠 发表于 2024-10-31 23:47

[嘉楠 CanMV K230]测评 ⑩自己训练一个分类识别模型

<div class='showpostmsg'> 本帖最后由 不爱胡萝卜的仓鼠 于 2024-11-1 09:43 编辑

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;之前我们跑的功能,基本上都是一些基础的图像识别功能,没有太多AI的加持,面对一些复杂的场景,之前的那些内容就显得不足了,需要有AI的加入</p>

<p>&nbsp;</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们今天就来自己训练一个AI模型,并运行在K230开发板上,实现分类识别</p>

<p>&nbsp;</p>

<p><strong><span style="font-size:24px;">一.模型训练</span></strong></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;嘉楠官网就有模型训练的服务,免费的,对于我们这些初学者来说非常友好。地址如下:<a href="https://developer.canaan-creative.com/training/" target="_blank">https://developer.canaan-creative.com/training/</a></p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 打开网页后需要注册账号并登录</p>

<div style="text-align: center;">
<div style="text-align: center;"></div>

<p>&nbsp;</p>
</div>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 接下来我们开始添加数据集</p>

<div style="text-align: center;">
<div style="text-align: center;"></div>

<p style="text-align: left;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;添加成功后如图,点击名称,即可打开该数据集,我们就可以开始添加图片了</p>

<div style="text-align: center;">
<div style="text-align: center;">d</div>

<p>&nbsp;</p>
</div>

<p style="text-align: left;">&nbsp; &nbsp; 点击上传图片,把准备好的图片上传,我这边准备了几十张鼠标的照片,全部上传</p>

<div style="text-align: center;"></div>

<p style="text-align: left;">等待上传完成</p>

<div style="text-align: center;"></div>

<p>&nbsp;</p>
</div>

<p>上传完成后,会弹出如图内容,输入标签名称,我这边是鼠标,那我就叫mouse,然后再点击确定</p>

<div style="text-align: center;"></div>

<p>刚才上传的图片就全部被打上了鼠标的标签</p>

<div style="text-align: center;"></div>

<p>接下来我还准备了笔的照片,流程和鼠标一致,我就不再赘述了(这边的照片数量越多越好,以前官方教程中是推荐100张以上,但是现在这个貌似删掉了。并且官方提供的训练集也是50张,因此50张应该是基本够用)</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>最后点击&ldquo;训练&rdquo;,开始模型训练</p>

<div style="text-align: center;"></div>

<p>训练参数填写如下</p>

<div style="text-align: center;"></div>

<p>其中nnacse的版本要根据实际下载到开发板上的镜像说决定,我现在运行的是1.0版本,根据官网下载固件的名称可以得到对应版本为2.8.3</p>

<div style="text-align: center;"></div>

<p>迭代次数默认700,我为了快速完成训练,就调整为100。这个数字越大训练时长越长,效果我认为也会越好</p>

<p>&nbsp;</p>

<p>接下来就是坐等训练结束</p>

<div style="text-align: center;"></div>

<p>&nbsp;</p>

<p>&nbsp;经过漫长的等待,训练完成后界面如图。</p>

<div style="text-align: center;"></div>

<p>同时你注册账号的邮箱会收到一封邮件,通知你训练完成,并给结果</p>

<div style="text-align: center;"></div>

<p>下载得到的压缩包内容如下</p>

<div style="text-align: center;"></div>

<p>cls_results:里面是一些测试用的图片</p>

<p>cpp_deployment_source.zip:C++版本的部署包</p>

<p>mp_deployment_source.zip:MicroPython版本部署包</p>

<p>README.md:使用指导手册</p>

<p>&nbsp;</p>

<p>我这边使用的micropython的固件,所以我实际需要的是 &ldquo;mp_deployment_source.zip&rdquo;这个文件,其他的都不重要</p>

<div>&nbsp;</div>

<div>(以上截图是我第一次训练时的截图,中途因为一些小问题,导致我又调整参数重新训练了模型。下面的模型是每个分类160张图片,700次迭代,其他参数不变)</div>

<div>&nbsp;</div>

<div>&nbsp;</div>

<p><strong><span style="font-size:24px;">二.使用模型</span></strong></p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 把mp_deployment_source.zip解压,内容如下</p>

<div style="text-align: center;"></div>

<p>将这个文件夹拷贝到开发板的SD卡中</p>

<div style="text-align: center;"></div>

<p><strong><span style="font-size:18px;">2.1&nbsp; &nbsp;视频识别</span></strong>&nbsp;</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;打开IDE,连接开发板后,打开储存在SD卡中的&ldquo;cls_video.py&rdquo;文件</p>

<p>&nbsp; &nbsp;</p>

<div style="text-align: center;"></div>

<div style="text-align: center;">
<div style="text-align: center;"></div>
</div>

<p>&nbsp;&nbsp;&nbsp;&nbsp;运行,即可在日志串口看到识别的结果,效果如下,基本上识别还是准确的,可信度基本上都是0.8多0.9多</p>

<p>8530d9915a5c07353098966c42cb923b<br />
&nbsp;</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cls_video.py的代码如下</p>

<pre>
<code class="language-python">import os
import ujson
from media.sensor import *
from media.display import *
from media.media import *
from time import *
import nncase_runtime as nn
import ulab.numpy as np
import time
import utime
import image
import random
import gc

DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080

OUT_RGB888P_WIDTH = ALIGN_UP(1280, 16)
OUT_RGB888P_HEIGH = 720

root_path="/sdcard/mp_deployment_source/"
config_path=root_path+"deploy_config.json"
deploy_conf={}
debug_mode=1

class ScopedTiming:
    def __init__(self, info="", enable_profile=True):
      self.info = info
      self.enable_profile = enable_profile

    def __enter__(self):
      if self.enable_profile:
            self.start_time = time.time_ns()
      return self

    def __exit__(self, exc_type, exc_value, traceback):
      if self.enable_profile:
            elapsed_time = time.time_ns() - self.start_time
            print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")

def read_deploy_config(config_path):
    # 打开JSON文件以进行读取deploy_config
    with open(config_path, 'r') as json_file:
      try:
            # 从文件中加载JSON数据
            config = ujson.load(json_file)

            # 打印数据(可根据需要执行其他操作)
            #print(config)
      except ValueError as e:
            print("JSON 解析错误:", e)
    return config

# 任务后处理
def softmax(x):
    exp_x = np.exp(x - np.max(x))
    return exp_x / np.sum(exp_x)

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

def classification():
    print("start")

    # 使用json读取内容初始化部署变量
    deploy_conf=read_deploy_config(config_path)
    kmodel_name=deploy_conf["kmodel_path"]
    labels=deploy_conf["categories"]
    confidence_threshold=deploy_conf["confidence_threshold"]
    img_size=deploy_conf["img_size"]
    num_classes=deploy_conf["num_classes"]
    cls_idx=-1
    score=0.0

    # init kpu and load kmodel
    kpu = nn.kpu()
    ai2d = nn.ai2d()
    kpu.load_kmodel(root_path+kmodel_name)
    ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
                                 nn.ai2d_format.NCHW_FMT,
                                 np.uint8, np.uint8)
    ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel )
    ai2d_builder = ai2d.build(, ,img_size])
    # 初始化并配置sensor
    sensor = Sensor()
    sensor.reset()
    # 设置镜像
    sensor.set_hmirror(False)
    # 设置翻转
    sensor.set_vflip(False)
    # 通道0直接给到显示VO,格式为YUV420
    sensor.set_framesize(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
    sensor.set_pixformat(PIXEL_FORMAT_YUV_SEMIPLANAR_420)
    # 通道2给到AI做算法处理,格式为RGB888
    sensor.set_framesize(width = OUT_RGB888P_WIDTH , height = OUT_RGB888P_HEIGH, chn=CAM_CHN_ID_2)
    sensor.set_pixformat(PIXEL_FORMAT_RGB_888_PLANAR, chn=CAM_CHN_ID_2)
    # 绑定通道0的输出到vo
    sensor_bind_info = sensor.bind_info(x = 0, y = 0, chn = CAM_CHN_ID_0)
    Display.bind_layer(**sensor_bind_info, layer = Display.LAYER_VIDEO1)
    # 设置为LT9611显示,默认1920x1080
    Display.init(Display.LT9611, to_ide = True)

    #创建OSD图像
    osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888)

    try:
      # media初始化
      MediaManager.init()
      # 启动sensor
      sensor.run()
      # init
      rgb888p_img = None
      ai2d_input_tensor = None
      data = np.ones((1,3,img_size,img_size),dtype=np.uint8)
      ai2d_output_tensor = nn.from_numpy(data)
      whileTrue:
            with ScopedTiming("total",debug_mode &gt; 0):
                rgb888p_img = sensor.snapshot(chn=CAM_CHN_ID_2)
                if rgb888p_img == -1:
                  print("capture_image failed")
                  camera.release_image(CAM_DEV_ID_0, CAM_CHN_ID_2, rgb888p_img)
                  continue
                # for rgb888planar
                if rgb888p_img.format() == image.RGBP888:
                  ai2d_input = rgb888p_img.to_numpy_ref()
                  ai2d_input_tensor = nn.from_numpy(ai2d_input)
                  ai2d_builder.run(ai2d_input_tensor, ai2d_output_tensor)
                  # set input
                  kpu.set_input_tensor(0, ai2d_output_tensor)
                  # run kmodel
                  kpu.run()
                  # get output
                  results = []
                  for i in range(kpu.outputs_size()):
                        output_data = kpu.get_output_tensor(i)
                        result = output_data.to_numpy()
                        del output_data
                        results.append(result)
                  if num_classes&gt;2:
                        softmax_res=softmax(results)
                        cls_idx=np.argmax(softmax_res)
                        if softmax_res&gt;confidence_threshold:
                            score=softmax_res
                            print("classification result:")
                            print(labels)
                            print("score",score)
                        else:
                            cls_idx=-1
                            score=0.0
                  else:
                        sigmoid_res=sigmoid(results)
                        if sigmoid_res&gt;confidence_threshold:
                            cls_idx=1
                            score=sigmoid_res
                            print("classification result:")
                            print(labels)
                            print("score",score)
                        else:
                            cls_idx=0
                            score=1-sigmoid_res
                            print("classification result:")
                            print(labels)
                            print("score",score)
                osd_img.clear()
                if cls_idx&gt;=0:
                  osd_img.draw_string_advanced(5,5,32,"result:"+labels+" score:"+str(score),color=(0,255,0))
                Display.show_image(osd_img, 0, 0, Display.LAYER_OSD3)
                rgb888p_img = None
                gc.collect() #用于需要调用gc.mem_alloc()的内存
    except Exception as e:
      print(f"An error occurred during buffer used: {e}")
    finally:
      os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
      del ai2d_input_tensor
      del ai2d_output_tensor
      #停止摄像头输出
      sensor.stop()
      #去初始化显示设备
      Display.deinit()
      #释放媒体缓冲区
      MediaManager.deinit()
      gc.collect()
      time.sleep(1)
      nn.shrink_memory_pool()
    print("end")
    return 0


if __name__=="__main__":
    classification()
</code></pre>

<p><strong><span style="font-size:16px;">2.2 图片识别</span></strong></p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 结果中还有一个图片的示例程序</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;需要拷贝一张图片到sd卡中,我们可以从训练结果中的cls_results文件夹下得到一些样本的照片,如果你有其他照片也可以(但是需要.jpg格式)。修改照片的名称为test,拷贝到SD卡中</p>

<div style="text-align: center;"></div>

<p>&nbsp;</p>

<p>然后ide中运行的代码为SD卡中的cls_image.py,具体的操作与上面的一样,我就不赘述了</p>

<p>&nbsp;</p>

<p>如果运行提示以下内容,可能的原因是放到SD卡中的图片太大了,可以看到,我刚才拷贝进去的有1.63M,太大了,缓冲区不够用了。需要用软件压缩一下,我选择的是降低分辨率,我选择320*240.压缩完了就只有32KB了</p>

<div style="text-align: center;"></div>

<p>&nbsp;</p>

<p>运行结果如下,</p>

<div style="text-align: center;"></div>

<div style="text-align: center;">
<div style="text-align: center;"></div>

<p>&nbsp;</p>
</div>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cls_image.py的代码如下</p>

<pre>
<code class="language-python">import os
import ujson
from time import *
import nncase_runtime as nn
import ulab.numpy as np
import time
import image
import gc
import utime

root_path="/sdcard/mp_deployment_source/"      # root_path要以/结尾
config_path=root_path+"deploy_config.json"
image_path=root_path+"test.jpg"
deploy_conf={}
debug_mode=1

class ScopedTiming:
    def __init__(self, info="", enable_profile=True):
      self.info = info
      self.enable_profile = enable_profile

    def __enter__(self):
      if self.enable_profile:
            self.start_time = time.time_ns()
      return self

    def __exit__(self, exc_type, exc_value, traceback):
      if self.enable_profile:
            elapsed_time = time.time_ns() - self.start_time
            print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")

def read_img(img_path):
    img_data = image.Image(img_path)
    img_data_rgb888=img_data.to_rgb888()
    img_hwc=img_data_rgb888.to_numpy_ref()
    shape=img_hwc.shape
    img_tmp = img_hwc.reshape((shape * shape, shape))
    img_tmp_trans = img_tmp.transpose()
    img_res=img_tmp_trans.copy()
    img_return=img_res.reshape((shape,shape,shape))
    return img_return

# 读取deploy_config.json文件
def read_deploy_config(config_path):
    # 打开JSON文件以进行读取deploy_config
    with open(config_path, 'r') as json_file:
      try:
            # 从文件中加载JSON数据
            config = ujson.load(json_file)

            # 打印数据(可根据需要执行其他操作)
            #print(config)
      except ValueError as e:
            print("JSON 解析错误:", e)
    return config

# 任务后处理
def softmax(x):
    exp_x = np.exp(x - np.max(x))
    return exp_x / np.sum(exp_x)

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

def classification():
    print("--------------start-----------------")
    # 使用json读取内容初始化部署变量
    deploy_conf=read_deploy_config(config_path)
    kmodel_name=deploy_conf["kmodel_path"]
    labels=deploy_conf["categories"]
    confidence_threshold=deploy_conf["confidence_threshold"]
    img_size=deploy_conf["img_size"]
    num_classes=deploy_conf["num_classes"]
    cls_idx=-1

    # ai2d输入输出初始化
    ai2d_input = read_img(image_path)
    ai2d_input_tensor = nn.from_numpy(ai2d_input)
    ai2d_input_shape=ai2d_input.shape
    data = np.ones((1,3,img_size,img_size),dtype=np.uint8)
    ai2d_out = nn.from_numpy(data)

    # init kpu and load kmodel
    kpu = nn.kpu()
    ai2d = nn.ai2d()
    kpu.load_kmodel(root_path+kmodel_name)
    ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
                                 nn.ai2d_format.NCHW_FMT,
                                 np.uint8, np.uint8)
    ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel )
    ai2d_builder = ai2d.build(,ai2d_input_shape], ,img_size])
    with ScopedTiming("total",debug_mode &gt; 0):
      ai2d_builder.run(ai2d_input_tensor, ai2d_out)
      kpu.set_input_tensor(0, ai2d_out)
      kpu.run()
      del ai2d_input_tensor
      del ai2d_out
      # 获取分类结果
      results = []
      for i in range(kpu.outputs_size()):
            data = kpu.get_output_tensor(i)
            result = data.to_numpy()
            results.append(result)
      # 后处理
      if num_classes&gt;2:
            softmax_res=softmax(results)
            res_idx=np.argmax(softmax_res)
            if softmax_res&gt;confidence_threshold:
                cls_idx=res_idx
                print("classification result:")
                print(labels)
                print("score",softmax_res)
                image_draw=image.Image(image_path).to_rgb565()
                image_draw.draw_string(10, 10,labels , scale=2,color=(255,0,255,0))
                image_draw.compress_for_ide()
                image_draw.save(root_path+"cls_result.jpg")
            else:
                cls_idx=-1
      else:
            sigmoid_res=sigmoid(results)
            if sigmoid_res&gt;confidence_threshold:
                cls_idx=1
                print("classification result:")
                print(labels)
                print("score",sigmoid_res)
                image_draw=image.Image(image_path).to_rgb565()
                image_draw.draw_string(10, 10,labels , scale=2,color=(255,0,255,0))
                image_draw.compress_for_ide()
                image_draw.save(root_path+"cls_result.jpg")
            else:
                cls_idx=0
                print("classification result:")
                print(labels)
                print("score",1-sigmoid_res)
                image_draw=image.Image(image_path).to_rgb565()
                image_draw.draw_string(10, 10,labels , scale=2,color=(255,0,255,0))
                image_draw.compress_for_ide()
                image_draw.save(root_path+"cls_result.jpg")
      del data

    del ai2d
    del ai2d_builder
    del kpu
    print("---------------end------------------")
    gc.collect()
    nn.shrink_memory_pool()

if __name__=="__main__":
    nn.shrink_memory_pool()
    classification()
</code></pre>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p><strong><span style="font-size:24px;">三.模型训练经验分享</span></strong></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.单个标签的图片不要太少,以前官方教程中是推荐100张以上,但是现在这个貌似删掉了。并且官方提供的训练集也是50张,因此50张应该是基本够用。但是小于50张,训练效果可能会变差</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.图片不要太多,当然不是说多了会对模型训练造成影响,对于训练来说是越多越好的,但是训练平台资源有限,做了1G的限制(应该是所有图片总大小不能超过1G),超过1G不能开始训练。不然一次性上传的太多,回头一张一张删除累死人(建议官方改进一下,可以做一个上传的限制,或者允许批量删除)</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3.不要一次性上传很多图片,我一次性上传150张,上传时会报错,具体报错信息我也没太看明白,后面换成50张传一次,分批传就可以了</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 4.如果遇到问题,自己多次尝试实在解决不了问题的,可以考虑去嘉楠官网提问。我做模型测试时,遇到问题,捣鼓了很久,没解决,在晚上提问后,官方技术人员第二天就回复了,并解决了问题。给官方的技术人员大大的点赞</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>本文最终训练得到的结果见附件,大家可以下载后复现</p>

<div></div>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>

Jacktang 发表于 2024-11-3 08:46

<p>应该是所有图片总大小不能超过1G,AI模型训练的过程了解了,谢谢分享</p>
页: [1]
查看完整版本: [嘉楠 CanMV K230]测评 ⑩自己训练一个分类识别模型