Toybrick

yolov10模型精度问题

asdasdasd

新手上路

积分
12
楼主
发表于 2025-5-16 14:57:22    查看: 189|回复: 4 | [复制链接]    打印 | 只看该作者
本帖最后由 asdasdasd 于 2025-5-16 15:14 编辑

想请教一下,yolov10的模型不量化转出的rknn(连扳runtime精度校验没有问题),在rknn_model_zoo/example/yolov10/上也能正常推理,命令如下:
python yolov10.py --model_path /home/zionchu/Desktop/Engineering/RK3588_dev/model_calibration/models/nosigmoid.rknn --target Rk3588 --img_show --device_id 0973f07c156de1b4

但是把这个代码移植到rk3588上运行,输入同一张图片推理会有分类结果不匹配的问题,精度也降低很多,有什么可能影响的原因吗?命令如下:
python yolov10.py --model_path /home/pi/zzh/metal_detection_rknn/weights/nosigmoid.rknn --img_save
代码提供:1.yolov10
  1. import os
  2. import cv2
  3. import sys
  4. import argparse
  5. import torch
  6. # add path
  7. realpath = os.path.abspath(__file__)
  8. _sep = os.path.sep
  9. realpath = realpath.split(_sep)
  10. sys.path.append(os.path.join(realpath[0]+_sep, *realpath[1:realpath.index('official_yolov10_python')+1]))

  11. from py_utils.coco_utils import COCO_test_helper
  12. import numpy as np


  13. OBJ_THRESH = 0.25
  14. NMS_THRESH = 0.45

  15. # The follew two param is for map test
  16. # OBJ_THRESH = 0.001
  17. # NMS_THRESH = 0.65

  18. IMG_SIZE = (640, 640)  # (width, height), such as (1280, 736)

  19. CLASSES = ("1", "2", "3")

  20. coco_id_list = [1, 2, 3]

  21. def filter_boxes(boxes, box_confidences, box_class_probs):
  22.     """Filter boxes with object threshold.
  23.     """
  24.     box_confidences = box_confidences.reshape(-1)
  25.     candidate, class_num = box_class_probs.shape

  26.     class_max_score = np.max(box_class_probs, axis=-1)
  27.     classes = np.argmax(box_class_probs, axis=-1)

  28.     _class_pos = np.where(class_max_score* box_confidences >= OBJ_THRESH)
  29.     scores = (class_max_score* box_confidences)[_class_pos]

  30.     boxes = boxes[_class_pos]
  31.     classes = classes[_class_pos]

  32.     return boxes, classes, scores

  33. def nms_boxes(boxes, scores):
  34.     """Suppress non-maximal boxes.
  35.     # Returns
  36.         keep: ndarray, index of effective boxes.
  37.     """
  38.     x = boxes[:, 0]
  39.     y = boxes[:, 1]
  40.     w = boxes[:, 2] - boxes[:, 0]
  41.     h = boxes[:, 3] - boxes[:, 1]

  42.     areas = w * h
  43.     order = scores.argsort()[::-1]

  44.     keep = []
  45.     while order.size > 0:
  46.         i = order[0]
  47.         keep.append(i)

  48.         xx1 = np.maximum(x[i], x[order[1:]])
  49.         yy1 = np.maximum(y[i], y[order[1:]])
  50.         xx2 = np.minimum(x[i] + w[i], x[order[1:]] + w[order[1:]])
  51.         yy2 = np.minimum(y[i] + h[i], y[order[1:]] + h[order[1:]])

  52.         w1 = np.maximum(0.0, xx2 - xx1 + 0.00001)
  53.         h1 = np.maximum(0.0, yy2 - yy1 + 0.00001)
  54.         inter = w1 * h1

  55.         ovr = inter / (areas[i] + areas[order[1:]] - inter)
  56.         inds = np.where(ovr <= NMS_THRESH)[0]
  57.         order = order[inds + 1]
  58.     keep = np.array(keep)
  59.     return keep

  60. def dfl(position):
  61.     # Distribution Focal Loss (DFL)
  62.     import torch
  63.     x = torch.tensor(position)
  64.     n,c,h,w = x.shape
  65.     p_num = 4
  66.     mc = c//p_num
  67.     y = x.reshape(n,p_num,mc,h,w)
  68.     y = y.softmax(2)
  69.     acc_metrix = torch.tensor(range(mc)).float().reshape(1,1,mc,1,1)
  70.     y = (y*acc_metrix).sum(2)
  71.     return y.numpy()


  72. def box_process(position):
  73.     grid_h, grid_w = position.shape[2:4]
  74.     col, row = np.meshgrid(np.arange(0, grid_w), np.arange(0, grid_h))
  75.     col = col.reshape(1, 1, grid_h, grid_w)
  76.     row = row.reshape(1, 1, grid_h, grid_w)
  77.     grid = np.concatenate((col, row), axis=1)
  78.     stride = np.array([IMG_SIZE[1]//grid_h, IMG_SIZE[0]//grid_w]).reshape(1,2,1,1)

  79.     position = dfl(position)
  80.     box_xy  = grid +0.5 -position[:,0:2,:,:]
  81.     box_xy2 = grid +0.5 +position[:,2:4,:,:]
  82.     xyxy = np.concatenate((box_xy*stride, box_xy2*stride), axis=1)

  83.     return xyxy

  84. def post_process(input_data):
  85.     boxes, scores, classes_conf = [], [], []
  86.     defualt_branch=3
  87.     pair_per_branch = len(input_data)//defualt_branch
  88.     # Python 忽略 score_sum 输出
  89.     for i in range(defualt_branch):
  90.         boxes.append(box_process(input_data[pair_per_branch*i]))
  91.         classes_conf.append(input_data[pair_per_branch*i+1])
  92.         scores.append(np.ones_like(input_data[pair_per_branch*i+1][:,:1,:,:], dtype=np.float32))

  93.     def sp_flatten(_in):
  94.         ch = _in.shape[1]
  95.         _in = _in.transpose(0,2,3,1)
  96.         return _in.reshape(-1, ch)

  97.     boxes = [sp_flatten(_v) for _v in boxes]
  98.     classes_conf = [sp_flatten(_v) for _v in classes_conf]
  99.     scores = [sp_flatten(_v) for _v in scores]

  100.     boxes = np.concatenate(boxes)
  101.     classes_conf = np.concatenate(classes_conf)
  102.     scores = np.concatenate(scores)

  103.     # filter according to threshold
  104.     boxes, classes, scores = filter_boxes(boxes, scores, classes_conf)

  105.     # nms
  106.     nboxes, nclasses, nscores = [], [], []
  107.     for c in set(classes):
  108.         inds = np.where(classes == c)
  109.         b = boxes[inds]
  110.         c = classes[inds]
  111.         s = scores[inds]
  112.         keep = nms_boxes(b, s)

  113.         if len(keep) != 0:
  114.             nboxes.append(b[keep])
  115.             nclasses.append(c[keep])
  116.             nscores.append(s[keep])

  117.     if not nclasses and not nscores:
  118.         return None, None, None

  119.     boxes = np.concatenate(nboxes)
  120.     classes = np.concatenate(nclasses)
  121.     scores = np.concatenate(nscores)

  122.     return boxes, classes, scores


  123. def post_process_yolov10(input_data):
  124.     max_det, nc = 300, len(CLASSES)

  125.     boxes, scores = [], []
  126.     defualt_branch=3
  127.     pair_per_branch = len(input_data)//defualt_branch
  128.     # Python 忽略 score_sum 输出
  129.     for i in range(defualt_branch):
  130.         boxes.append(box_process(input_data[pair_per_branch*i]))
  131.         scores.append(input_data[pair_per_branch*i+1])

  132.     def sp_flatten(_in):
  133.         ch = _in.shape[1]
  134.         _in = _in.transpose(0,2,3,1)
  135.         return _in.reshape(-1, ch)

  136.     boxes = [sp_flatten(_v) for _v in boxes]
  137.     scores = [sp_flatten(_v) for _v in scores]

  138.     boxes = torch.from_numpy(np.expand_dims(np.concatenate(boxes), axis=0))
  139.     scores = torch.from_numpy(np.expand_dims(np.concatenate(scores), axis=0))

  140.     max_scores = scores.amax(dim=-1)
  141.     max_scores, index = torch.topk(max_scores, max_det, axis=-1)
  142.     index = index.unsqueeze(-1)
  143.     boxes = torch.gather(boxes, dim=1, index=index.repeat(1, 1, boxes.shape[-1]))
  144.     scores = torch.gather(scores, dim=1, index=index.repeat(1, 1, scores.shape[-1]))

  145.     scores, index = torch.topk(scores.flatten(1), max_det, axis=-1)
  146.     labels = index % nc
  147.     index = index // nc
  148.     boxes = boxes.gather(dim=1, index=index.unsqueeze(-1).repeat(1, 1, boxes.shape[-1]))

  149.     preds = torch.cat([boxes, scores.unsqueeze(-1), labels.unsqueeze(-1)], dim=-1)

  150.     mask = preds[..., 4] > OBJ_THRESH

  151.     preds = [p[mask[idx]] for idx, p in enumerate(preds)][0]
  152.     boxes = preds[..., :4].numpy()
  153.     scores =  preds[..., 4].numpy()
  154.     classes = preds[..., 5].numpy().astype(np.int64)

  155.     return boxes, classes, scores

  156. def draw(image, boxes, scores, classes):
  157.     for box, score, cl in zip(boxes, scores, classes):
  158.         top, left, right, bottom = [int(_b) for _b in box]
  159.         print("%s @ (%d %d %d %d) %.3f" % (CLASSES[cl], top, left, right, bottom, score))
  160.         cv2.rectangle(image, (top, left), (right, bottom), (255, 0, 0), 2)
  161.         cv2.putText(image, '{0} {1:.2f}'.format(CLASSES[cl], score),
  162.                     (top, left - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)

  163. def setup_model(args):
  164.     model_path = args.model_path
  165.     if model_path.endswith('.pt') or model_path.endswith('.torchscript'):
  166.         platform = 'pytorch'
  167.         from py_utils.pytorch_executor import Torch_model_container
  168.         model = Torch_model_container(args.model_path)
  169.     elif model_path.endswith('.rknn'):
  170.         platform = 'rknn'
  171.         from py_utils.rknn_executor import RKNN_model_container
  172.         model = RKNN_model_container(args.model_path)
  173.     elif model_path.endswith('onnx'):
  174.         platform = 'onnx'
  175.         from py_utils.onnx_executor import ONNX_model_container
  176.         model = ONNX_model_container(args.model_path)
  177.     else:
  178.         assert False, "{} is not rknn/pytorch/onnx model".format(model_path)
  179.     print('Model-{} is {} model, starting val'.format(model_path, platform))
  180.     return model, platform

  181. def img_check(path):
  182.     img_type = ['.jpg', '.jpeg', '.png', '.bmp']
  183.     for _type in img_type:
  184.         if path.endswith(_type) or path.endswith(_type.upper()):
  185.             return True
  186.     return False

  187. if __name__ == '__main__':
  188.     parser = argparse.ArgumentParser(description='Process some integers.')
  189.     # basic params
  190.     parser.add_argument('--model_path', type=str, required= True, help='model path, could be .pt or .rknn file')
  191.     parser.add_argument('--target', type=str, default='rk3566', help='target RKNPU platform')
  192.     parser.add_argument('--device_id', type=str, default=None, help='device id')
  193.    
  194.     parser.add_argument('--img_show', action='store_true', default=False, help='draw the result and show')
  195.     parser.add_argument('--img_save', action='store_true', default=False, help='save the result')

  196.     # data params
  197.     parser.add_argument('--anno_json', type=str, default='../../../datasets/COCO/annotations/instances_val2017.json', help='coco annotation path')
  198.     # coco val folder: '../../../datasets/COCO//val2017'
  199.     parser.add_argument('--img_folder', type=str, default='../model', help='img folder path')
  200.     parser.add_argument('--coco_map_test', action='store_true', help='enable coco map test')

  201.     args = parser.parse_args()

  202.     # init model
  203.     model, platform = setup_model(args)

  204.     file_list = sorted(os.listdir(args.img_folder))
  205.     img_list = []
  206.     for path in file_list:
  207.         if img_check(path):
  208.             img_list.append(path)
  209.     co_helper = COCO_test_helper(enable_letter_box=True)

  210.     # run test
  211.     for i in range(len(img_list)):
  212.         print('infer {}/{}'.format(i+1, len(img_list)), end='\r')

  213.         img_name = img_list[i]
  214.         img_path = os.path.join(args.img_folder, img_name)
  215.         if not os.path.exists(img_path):
  216.             print("{} is not found", img_name)
  217.             continue

  218.         img_src = cv2.imread(img_path)
  219.         if img_src is None:
  220.             continue

  221.         # Due to rga init with (0,0,0), we using pad_color (0,0,0) instead of (114, 114, 114)
  222.         pad_color = (0,0,0)
  223.         img = co_helper.letter_box(im= img_src.copy(), new_shape=(IMG_SIZE[1], IMG_SIZE[0]), pad_color=(0,0,0))
  224.         img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

  225.         input_data = img
  226.         input_data = np.expand_dims(img, axis=0)

  227.         outputs = model.run([input_data])
  228.         boxes, classes, scores = post_process_yolov10(outputs)

  229.         if args.img_show or args.img_save:
  230.             print('\n\nIMG: {}'.format(img_name))
  231.             img_p = img_src.copy()
  232.             if boxes is not None:
  233.                 draw(img_p, co_helper.get_real_box(boxes), scores, classes)

  234.             if args.img_save:
  235.                 if not os.path.exists('./result'):
  236.                     os.mkdir('./result')
  237.                 result_path = os.path.join('./result', img_name)
  238.                 cv2.imwrite(result_path, img_p)
  239.                 print('Detection result save to {}'.format(result_path))
  240.                         
  241.             if args.img_show:
  242.                 cv2.imshow("full post process result", img_p)
  243.                 cv2.waitKeyEx(0)

  244.         # record maps
  245.         if args.coco_map_test is True:
  246.             if boxes is not None:
  247.                 for i in range(boxes.shape[0]):
  248.                     co_helper.add_single_record(image_id = int(img_name.split('.')[0]),
  249.                                                 category_id = coco_id_list[int(classes[i])],
  250.                                                 bbox = boxes[i],
  251.                                                 score = round(scores[i], 5).item()
  252.                                                 )

  253.     # calculate maps
  254.     if args.coco_map_test is True:
  255.         pred_json = args.model_path.split('.')[-2]+ '_{}'.format(platform) +'.json'
  256.         pred_json = pred_json.split('/')[-1]
  257.         pred_json = os.path.join('./', pred_json)
  258.         co_helper.export_to_json(pred_json)

  259.         from py_utils.coco_utils import coco_eval_with_json
  260.         coco_eval_with_json(args.anno_json, pred_json)

  261.     # release
  262.     model.release()
复制代码

2.rknn_excutor
  1. from rknnlite.api import RKNNLite


  2. class RKNN_model_container():
  3.     def __init__(self, model_path):
  4.         rknn = RKNNLite()

  5.         # Direct Load RKNN Model
  6.         rknn.load_rknn(model_path)

  7.         print('--> Init runtime environment')
  8.         rknn.init_runtime()
  9.         print('done')
  10.         
  11.         self.rknn = rknn

  12.     # def __del__(self):
  13.     #     self.release()

  14.     def run(self, inputs):
  15.         if self.rknn is None:
  16.             print("ERROR: rknn has been released")
  17.             return []

  18.         if isinstance(inputs, list) or isinstance(inputs, tuple):
  19.             pass
  20.         else:
  21.             inputs = [inputs]

  22.         result = self.rknn.inference(inputs=inputs)
  23.    
  24.         return result

  25.     def release(self):
  26.         self.rknn.release()
  27.         self.rknn = None
复制代码









回复

使用道具 举报

jefferyzhang

版主

积分
14767
沙发
发表于 5 天前 | 只看该作者
请参看文档进行逐层输出调试,以及量化和不量化对比
回复

使用道具 举报

asdasdasd

新手上路

积分
12
板凳
 楼主| 发表于 5 天前 | 只看该作者
jefferyzhang 发表于 2025-5-19 08:52
请参看文档进行逐层输出调试,以及量化和不量化对比

您好,非量化情况下逐层输出没有问题
C:\Users\zhuzi\Desktop\1.png
回复

使用道具 举报

asdasdasd

新手上路

积分
12
地板
 楼主| 发表于 5 天前 | 只看该作者
jefferyzhang 发表于 2025-5-19 08:52
请参看文档进行逐层输出调试,以及量化和不量化对比

您好,不量化逐层调试精度都是0.99,想请问一下官方提供的rknn python推理框架是在板端npu推理的还是pc模拟的
回复

使用道具 举报

jefferyzhang

版主

积分
14767
5#
发表于 4 天前 | 只看该作者
asdasdasd 发表于 2025-5-19 10:30
您好,不量化逐层调试精度都是0.99,想请问一下官方提供的rknn python推理框架是在板端npu推理的还是pc模 ...

都可以,可以pc仿真,也可以pc调用板端推理,也可以在板端推理。
量化精度不准问题一样要逐层调试,如果他模型有两个不一样值域范围的结果给concat在一起的,需要用分组量化或者手动拆开。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

产品中心 购买渠道 开源社区 Wiki教程 资料下载 关于Toybrick


快速回复 返回顶部 返回列表