detection_performance_loss_test.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. """
  2. 针对目标检测模型的测试性能损失脚本,通过比较推理过程中CPU、GPU占用、推理时间来进行计算
  3. 需要安装指定python库实现功能
  4. pip install psutil gputil pynvml pycocotools
  5. """
  6. import argparse
  7. import os
  8. import sys
  9. rootpath = str(os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))
  10. sys.path.append(rootpath)
  11. import psutil
  12. import GPUtil
  13. import numpy as np
  14. import time
  15. from threading import Thread
  16. from pycocotools.coco import COCO
  17. import xml.etree.ElementTree as ET
  18. from watermark_verify.inference.rcnn_inference import FasterRCNNInference
  19. from watermark_verify.inference.ssd_inference import SSDInference
  20. from watermark_verify.inference.yolox_inference import YOLOXInference
  21. from watermark_verify.tools.evaluate_tool import calculate_iou
  22. # 定义监控函数
  23. class UsageMonitor:
  24. def __init__(self, interval=0.5):
  25. self.interval = interval
  26. self.cpu_usage = []
  27. self.gpu_usage = []
  28. self.running = False
  29. def start(self):
  30. self.running = True
  31. self.monitor_thread = Thread(target=self._monitor)
  32. self.monitor_thread.start()
  33. def _monitor(self):
  34. while self.running:
  35. # 记录 CPU 使用率
  36. self.cpu_usage.append(psutil.cpu_percent(interval=None))
  37. # 记录 GPU 使用率
  38. gpus = GPUtil.getGPUs()
  39. if gpus:
  40. self.gpu_usage.append(gpus[0].load * 100) # 获取第一个 GPU 的使用率
  41. else:
  42. self.gpu_usage.append(0) # 若没有 GPU 则记为 0
  43. time.sleep(self.interval)
  44. def stop(self):
  45. self.running = False
  46. self.monitor_thread.join()
  47. def get_average_usage(self):
  48. avg_cpu_usage = np.mean(self.cpu_usage)
  49. avg_gpu_usage = np.mean(self.gpu_usage)
  50. return avg_cpu_usage, avg_gpu_usage
  51. def parse_voc_annotation(annotation_path):
  52. """
  53. 解析单个VOC XML文件,并提取边界框和类别信息
  54. :param annotation_path: XML标注文件路径
  55. :return: List[[x1, y1, x2, y2, category_id]]
  56. """
  57. voc_label_map = { # 类别名称到ID的映射字典
  58. 'aeroplane': 0,
  59. 'bicycle': 1,
  60. 'bird': 2,
  61. 'boat': 3,
  62. 'bottle': 4,
  63. 'bus': 5,
  64. 'car': 6,
  65. 'cat': 7,
  66. 'chair': 8,
  67. 'cow': 9,
  68. 'diningtable': 10,
  69. 'dog': 11,
  70. 'horse': 12,
  71. 'motorbike': 13,
  72. 'person': 14,
  73. 'pottedplant': 15,
  74. 'sheep': 16,
  75. 'sofa': 17,
  76. 'train': 18,
  77. 'tvmonitor': 19
  78. }
  79. tree = ET.parse(annotation_path)
  80. root = tree.getroot()
  81. annotations = []
  82. for obj in root.findall("object"):
  83. # 类别名称
  84. class_name = obj.find("name").text
  85. category_id = voc_label_map.get(class_name, -1) # 找不到时返回 -1
  86. # 边界框信息
  87. bndbox = obj.find("bndbox")
  88. x1 = int(bndbox.find("xmin").text)
  89. y1 = int(bndbox.find("ymin").text)
  90. x2 = int(bndbox.find("xmax").text)
  91. y2 = int(bndbox.find("ymax").text)
  92. # 添加到结果
  93. annotations.append([x1, y1, x2, y2, category_id])
  94. return annotations
  95. def get_dataset_images_annotations(args, num):
  96. """
  97. 从指定数据集的验证集中获取指定数量的图片及其标注信息
  98. :param args: 参数
  99. :param num: 获取数量
  100. :return: (image_path, annotations)
  101. """
  102. result = []
  103. dataset_type = args.dataset_type # dataset_type: 数据集类型,可选参数:voc,coco
  104. if dataset_type == 'voc':
  105. voc_val_path = args.val_dataset_dir
  106. voc_annotations_path = os.path.join(voc_val_path, 'Annotations')
  107. voc_images_path = os.path.join(voc_val_path, 'JPEGImages')
  108. annotation_files = os.listdir(voc_annotations_path)
  109. selected_files = annotation_files[:num] # 前5张图片
  110. for file in selected_files:
  111. annotation_path = os.path.join(voc_annotations_path, file)
  112. image_path = os.path.join(voc_images_path, file.replace('.xml', '.jpg'))
  113. annotations = parse_voc_annotation(annotation_path)
  114. result.append((image_path, annotations))
  115. return result
  116. if dataset_type == 'coco':
  117. # 加载COCO验证集
  118. coco = COCO(args.val_annotation)
  119. image_ids = coco.getImgIds()[:num] # 前5张图片
  120. images = coco.loadImgs(image_ids)
  121. for image_info in images:
  122. img_path = f"{args.val_dataset_dir}/{image_info['file_name']}"
  123. # 获取标签信息
  124. ann_ids = coco.getAnnIds(imgIds=image_info['id'])
  125. anns = coco.loadAnns(ann_ids)
  126. annotations = []
  127. for anno in anns:
  128. gt_box = anno['bbox'] # [x, y, width, height]
  129. gt_box = [gt_box[0], gt_box[1], gt_box[0] + gt_box[2], gt_box[1] + gt_box[3]]
  130. gt_class = anno['category_id'] - 1
  131. annotations.append([gt_box[0], gt_box[1], gt_box[2], gt_box[3], gt_class])
  132. result.append((img_path, annotations))
  133. return result
  134. def output_process(model_type, pred):
  135. """
  136. 将目标检测模型输出结果转换为统一结果输出
  137. :param model_type: 目标检测模型类型,可选值:yolox,ssd,faster-rcnn
  138. :param pred: 模型预测结果
  139. :return: bbox,score,cls
  140. """
  141. pred_box = None
  142. score = None
  143. pred_class = None
  144. if model_type == 'yolox':
  145. pred_box = pred[:4] # 前4个值为bbox:[xmin,ymin,xmax,ymax]
  146. score = pred[4] # 第5位为置信度
  147. pred_class = int(pred[5]) # 第6位为类别
  148. elif model_type == 'ssd':
  149. pred_box = np.array([pred[1], pred[0], pred[3], pred[2]]) # 前4个值为bbox:[ymin,xmin,ymax,xmax]
  150. pred_class = int(pred[4]) # 第5位为类别
  151. score = pred[5] # 第6位为置信度
  152. elif model_type == 'faster-rcnn':
  153. pred_box = np.array([pred[1], pred[0], pred[3], pred[2]]) # 前4个值为bbox:[ymin,xmin,ymax,xmax]
  154. score = pred[4] # 第5位为置信度
  155. pred_class = int(pred[5]) # 第6位为类别
  156. return pred_box, score, pred_class
  157. # 模型推理函数
  158. def model_inference(args, model_filename, conf=0.3):
  159. """
  160. 模型推理验证集目录下所有图片
  161. :param args: 运行参数
  162. :param model_filename: 模型文件
  163. :param conf: 置信度阈值
  164. :return: 验证集推理准确率
  165. """
  166. model_type = args.model_type # 目标检测模型类型,可选值:yolox,ssd,faster-rcnn
  167. # 以下使用GPU进行推理出现问题,需要较新的CUDA版本,默认使用CPU进行推理
  168. # if ort.get_available_providers():
  169. # session = ort.InferenceSession(model_filename, providers=['CUDAExecutionProvider'])
  170. # else:
  171. # session = ort.InferenceSession(model_filename)
  172. # 初始化计数
  173. correct_count = 0
  174. total_count = 0
  175. iou_threshold = 0.5
  176. part_dataset = get_dataset_images_annotations(args, 5)
  177. for image_path, annotations in part_dataset:
  178. # 使用模型推理流程定义进行模型推理
  179. if model_type.lower() == 'yolox':
  180. preds = YOLOXInference(model_filename).predict(image_path)
  181. elif model_type.lower() == 'ssd':
  182. preds = SSDInference(model_filename).predict(image_path)
  183. preds = preds[0] # 只获取模型第一个输出
  184. elif model_type.lower() == 'faster-rcnn':
  185. preds = FasterRCNNInference(model_filename).predict(image_path)
  186. preds = preds[0]
  187. else:
  188. raise Exception("目标检测模型类型参数不合法")
  189. for anno in annotations:
  190. gt_box = [anno[0], anno[1], anno[2], anno[3]]
  191. gt_class = anno[4]
  192. total_count += 1
  193. # 比对推理结果
  194. for pred in preds:
  195. if len(pred) == 0:
  196. continue
  197. pred_box, score, pred_class = output_process(model_type, pred)
  198. if score < conf:
  199. continue
  200. iou = calculate_iou(gt_box, pred_box)
  201. if iou > iou_threshold and pred_class == gt_class:
  202. correct_count += 1
  203. break
  204. # 计算准确率
  205. accuracy = correct_count / total_count
  206. return accuracy
  207. if __name__ == '__main__':
  208. parser = argparse.ArgumentParser(description='模型推理性能验证脚本')
  209. parser.add_argument('--model_type', default='faster-rcnn', type=str, help='目标检测模型类型:yolox、ssd、faster-rcnn')
  210. parser.add_argument('--dataset_type', default='voc', type=str, help='验证集的数据集格式,支持的参数:coco,voc')
  211. parser.add_argument('--val_dataset_dir', default=None, type=str, help='验证集目录')
  212. parser.add_argument('--origin_model_file', default=None, type=str, help='待测试原始模型的onnx文件')
  213. parser.add_argument('--watermark_model_file', default=None, type=str, help='待测试水印模型的onnx文件')
  214. parser.add_argument('--val_annotation', default=None, type=str,
  215. help='验证集标注文件,仅有coco数据集需要这个参数')
  216. # parser.add_argument('--val_dataset_dir', default="VOC2007", type=str, help='验证集目录')
  217. # parser.add_argument('--origin_model_file', default="models/origin/faster-rcnn/model.onnx", type=str,
  218. # help='待测试原始模型的onnx文件')
  219. args, _ = parser.parse_known_args()
  220. if args.origin_model_file is None:
  221. raise Exception("待测试模型的onnx文件不可为空")
  222. if args.val_dataset_dir is None:
  223. raise Exception("验证集目录不可为空")
  224. if args.model_type is None:
  225. raise Exception("目标检测模型类型不可为空")
  226. monitor = UsageMonitor(interval=0.5) # 每隔 0.5 秒采样一次
  227. monitor.start()
  228. # 记录推理开始时间
  229. start_time = time.time()
  230. # 进行模型推理
  231. accuracy = model_inference(args, args.origin_model_file)
  232. # 记录推理结束时间
  233. end_time = time.time()
  234. monitor.stop()
  235. # 输出平均 CPU 和 GPU 使用率
  236. avg_cpu, avg_gpu = monitor.get_average_usage()
  237. print("原始模型推理性能:")
  238. print(f"平均 CPU 使用率:{avg_cpu:.2f}%")
  239. print(f"平均 GPU 使用率:{avg_gpu:.2f}%")
  240. print(f"模型推理时间: {end_time - start_time:.2f} 秒")
  241. print(f"准确率: {accuracy * 100:.2f}%")
  242. if args.watermark_model_file: # 加入存在比对模型,进行再次推理,然后统计性能指标
  243. time.sleep(20)
  244. monitor2 = UsageMonitor(interval=0.5) # 每隔 0.5 秒采样一次
  245. monitor2.start()
  246. # 记录推理开始时间
  247. start_time2 = time.time()
  248. # 进行模型推理
  249. accuracy2 = model_inference(args, args.watermark_model_file)
  250. # 记录推理结束时间
  251. end_time2 = time.time()
  252. monitor2.stop()
  253. # 输出平均 CPU 和 GPU 使用率
  254. avg_cpu2, avg_gpu2 = monitor2.get_average_usage()
  255. print("水印模型推理性能:")
  256. print(f"平均 CPU 使用率:{avg_cpu2:.2f}%")
  257. print(f"平均 GPU 使用率:{avg_gpu2:.2f}%")
  258. print(f"模型推理时间: {end_time2 - start_time2:.2f} 秒")
  259. print(f"准确率: {accuracy2 * 100:.2f}%")
  260. print("------------------性能指标如下-------------------------")
  261. print(f"嵌入后模型推理准确率下降值:{(accuracy - accuracy2) * 100:.2f}%")
  262. print(f"算力资源消耗增加值:{(avg_cpu2 - avg_cpu):.2f}%")
  263. print(
  264. f"运行效率降低值: {((end_time2 - start_time2) - (end_time - start_time)) * 100 / (end_time - start_time):.2f} %")