classification_performance_loss_test.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. """
  2. 针对图像分类模型的测试性能损失脚本,通过比较推理过程中CPU、GPU占用、推理时间来进行计算
  3. 需要安装指定python库实现功能
  4. pip install psutil gputil pynvml
  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. import onnxruntime as ort
  17. from PIL import Image
  18. # 定义监控函数
  19. class UsageMonitor:
  20. def __init__(self, interval=0.5):
  21. self.interval = interval
  22. self.cpu_usage = []
  23. self.gpu_usage = []
  24. self.running = False
  25. def start(self):
  26. self.running = True
  27. self.monitor_thread = Thread(target=self._monitor)
  28. self.monitor_thread.start()
  29. def _monitor(self):
  30. while self.running:
  31. # 记录 CPU 使用率
  32. self.cpu_usage.append(psutil.cpu_percent(interval=None))
  33. # 记录 GPU 使用率
  34. gpus = GPUtil.getGPUs()
  35. if gpus:
  36. self.gpu_usage.append(gpus[0].load * 100) # 获取第一个 GPU 的使用率
  37. else:
  38. self.gpu_usage.append(0) # 若没有 GPU 则记为 0
  39. time.sleep(self.interval)
  40. def stop(self):
  41. self.running = False
  42. self.monitor_thread.join()
  43. def get_average_usage(self):
  44. avg_cpu_usage = np.mean(self.cpu_usage)
  45. avg_gpu_usage = np.mean(self.gpu_usage)
  46. return avg_cpu_usage, avg_gpu_usage
  47. def process_image(image_path, transpose=True):
  48. """
  49. 图片处理
  50. :param image_path: 图片路径
  51. :param transpose: 是否进行维度转换,在使用pytorch框架训练出来的权重需要进行维度转换,tensorflow、keras框架不需要
  52. :return:
  53. """
  54. # 打开图像并转换为RGB
  55. image = Image.open(image_path).convert("RGB")
  56. # 调整图像大小
  57. image = image.resize((224, 224))
  58. # 转换为numpy数组并归一化
  59. image_array = np.array(image) / 255.0 # 将像素值缩放到[0, 1]
  60. # 进行标准化
  61. mean = np.array([0.485, 0.456, 0.406])
  62. std = np.array([0.229, 0.224, 0.225])
  63. image_array = (image_array - mean) / std
  64. if transpose:
  65. image_array = image_array.transpose((2, 0, 1)).copy()
  66. return image_array.astype(np.float32)
  67. def batch_predict_images(session, image_dir, target_class, batch_size=10, pytorch=True):
  68. """
  69. 对指定图片文件夹图片进行批量检测
  70. :param session: onnx runtime session
  71. :param image_dir: 待推理的图像文件夹
  72. :param target_class: 目标分类
  73. :param batch_size: 每批图片数量, 默认为10
  74. :param pytorch: 模型是否使用pytorch框架训练出的权重导出的onnx文件,默认为True
  75. :return: 检测结果
  76. """
  77. image_files = [f for f in os.listdir(image_dir) if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
  78. results = {}
  79. input_name = session.get_inputs()[0].name
  80. correct_predictions = 0
  81. total_predictions = 0
  82. for i in range(0, len(image_files), batch_size):
  83. batch_files = image_files[i:i + batch_size]
  84. batch_images = []
  85. for image_file in batch_files:
  86. image_path = os.path.join(image_dir, image_file)
  87. image = process_image(image_path, pytorch)
  88. batch_images.append(image)
  89. # 将批次图片堆叠成 (batch_size, 3, 224, 224) 维度
  90. batch_images = np.stack(batch_images)
  91. # 执行预测
  92. outputs = session.run(None, {input_name: batch_images})
  93. # 提取预测结果
  94. for j, image_file in enumerate(batch_files):
  95. predicted_class = np.argmax(outputs[0][j]) # 假设输出是每类的概率
  96. results[image_file] = predicted_class
  97. total_predictions += 1
  98. # 比较预测结果与目标分类
  99. if predicted_class == target_class:
  100. correct_predictions += 1
  101. # 计算准确率
  102. accuracy = correct_predictions / total_predictions if total_predictions > 0 else 0
  103. return accuracy
  104. # 模型推理函数
  105. def model_inference(model_filename, val_dataset_dir):
  106. """
  107. 模型推理验证集目录下所有图片
  108. :param model_filename: 模型文件
  109. :param val_dataset_dir: 验证集图片目录
  110. :return: 验证集推理准确率
  111. """
  112. # 以下使用GPU进行推理出现问题,需要较新的CUDA版本,默认使用CPU进行推理
  113. # if ort.get_available_providers():
  114. # session = ort.InferenceSession(model_filename, providers=['CUDAExecutionProvider'])
  115. # else:
  116. # session = ort.InferenceSession(model_filename)
  117. session = ort.InferenceSession(model_filename)
  118. accuracy = 0
  119. class_num = 0
  120. index = 0
  121. for class_dir in os.listdir(val_dataset_dir):
  122. class_path = os.path.join(val_dataset_dir, class_dir)
  123. # 检查是否为目录
  124. if not os.path.isdir(class_path):
  125. continue
  126. class_num += 1
  127. is_pytorch = False if "keras" in model_filename or "tensorflow" in model_filename else True
  128. batch_result = batch_predict_images(session, class_path, index, pytorch=is_pytorch)
  129. accuracy += batch_result
  130. index += 1
  131. print(f"class_num: {class_num}, index: {index}")
  132. return accuracy * 1.0 / class_num
  133. if __name__ == '__main__':
  134. parser = argparse.ArgumentParser(description='模型推理性能验证脚本')
  135. parser.add_argument('--origin_model_file', default=None, type=str, help='待测试原始模型的onnx文件')
  136. parser.add_argument('--watermark_model_file', default=None, type=str, help='待测试水印模型的onnx文件')
  137. parser.add_argument('--val_dataset_dir', default=None, type=str, help='验证集目录')
  138. args, _ = parser.parse_known_args()
  139. if args.origin_model_file is None:
  140. raise Exception("待测试模型的onnx文件不可为空")
  141. if args.val_dataset_dir is None:
  142. raise Exception("验证集目录不可为空")
  143. monitor = UsageMonitor(interval=0.5) # 每隔 0.5 秒采样一次
  144. monitor.start()
  145. # 记录推理开始时间
  146. start_time = time.time()
  147. # 进行模型推理
  148. accuracy = model_inference(args.origin_model_file, args.val_dataset_dir)
  149. # 记录推理结束时间
  150. end_time = time.time()
  151. monitor.stop()
  152. # 输出平均 CPU 和 GPU 使用率
  153. avg_cpu, avg_gpu = monitor.get_average_usage()
  154. print("原始模型推理性能:")
  155. print(f"平均 CPU 使用率:{avg_cpu:.2f}%")
  156. print(f"平均 GPU 使用率:{avg_gpu:.2f}%")
  157. print(f"模型推理时间: {end_time - start_time:.2f} 秒")
  158. print(f"准确率: {accuracy * 100:.2f}%")
  159. if args.watermark_model_file: # 加入存在比对模型,进行再次推理,然后统计性能指标
  160. time.sleep(20)
  161. monitor2 = UsageMonitor(interval=0.5) # 每隔 0.5 秒采样一次
  162. monitor2.start()
  163. # 记录推理开始时间
  164. start_time2 = time.time()
  165. # 进行模型推理
  166. accuracy2 = model_inference(args.watermark_model_file, args.val_dataset_dir)
  167. # 记录推理结束时间
  168. end_time2 = time.time()
  169. monitor2.stop()
  170. # 输出平均 CPU 和 GPU 使用率
  171. avg_cpu2, avg_gpu2 = monitor2.get_average_usage()
  172. print("水印模型推理性能:")
  173. print(f"平均 CPU 使用率:{avg_cpu2:.2f}%")
  174. print(f"平均 GPU 使用率:{avg_gpu2:.2f}%")
  175. print(f"模型推理时间: {end_time2 - start_time2:.2f} 秒")
  176. print(f"准确率: {accuracy2 * 100:.2f}%")
  177. print("------------------性能指标如下-------------------------")
  178. print(f"嵌入后模型推理准确率下降值:{(accuracy - accuracy2) * 100:.2f}%")
  179. print(f"算力资源消耗增加值:{(avg_cpu2 - avg_cpu):.2f}%")
  180. print(f"运行效率降低值: {((end_time2 - start_time2) - (end_time - start_time)) * 100 / (end_time - start_time):.2f} %")