当前位置: 首页 > news >正文

新手学习yolov8目标检测小记2--对比实验中经典模型库MMDetection使用方法(使用自己的数据集训练,并转换为yolo格式评价指标)

一、按照步骤环境配置

pip install timm==1.0.7 thop efficientnet_pytorch==0.7.1 einops grad-cam==1.4.8 dill==0.3.6 albumentations==1.4.11 pytorch_wavelets==1.3.0 tidecv PyWavelets -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install -U openmim -i https://pypi.tuna.tsinghua.edu.cn/simple
mim install mmengine -i https://pypi.tuna.tsinghua.edu.cn/simple
mim install "mmcv==2.1.0" -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install YOLO
pip install ultralytics
pip install -v -e.

二、自定义数据集放置

        我这里已经将数据集按照训练集、验证集、测试集=8:1:1划分好,具体的存放目录结构如下图所示。其中test2017、train2017、val2017存放图片,testlabels、trainlabels、vallabels存放标注文件txt,数据集格式转换后,在annotations文件中。

        YOLO格式转coco格式代码如下

import os
import cv2
import json
from tqdm import tqdm
from sklearn.model_selection import train_test_split
import argparse# visdrone2019
classes = ['beibie1','beibie2','beibie3']parser = argparse.ArgumentParser()
parser.add_argument('--image_path', default=r'E:\mmde\mmdetection-3.0.0\mmdetection-3.0.0\data\coco\val2017', type=str, help="path of images")
parser.add_argument('--label_path', default=r'E:\mmde\mmdetection-3.0.0\mmdetection-3.0.0\data\coco\vallabels', type=str, help="path of labels .txt")
parser.add_argument('--save_path', default='val.json', type=str,help="if not split the dataset, give a path to a json file")
arg = parser.parse_args()def yolo2coco(arg):print("Loading data from ", arg.image_path, arg.label_path)assert os.path.exists(arg.image_path)assert os.path.exists(arg.label_path)originImagesDir = arg.image_pathoriginLabelsDir = arg.label_path# images dir nameindexes = os.listdir(originImagesDir)dataset = {'categories': [], 'annotations': [], 'images': []}for i, cls in enumerate(classes, 0):dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'mark'})# 标注的idann_id_cnt = 0for k, index in enumerate(tqdm(indexes)):# 支持 png jpg 格式的图片.txtFile = f'{index[:index.rfind(".")]}.txt'stem = index[:index.rfind(".")]# 读取图像的宽和高try:im = cv2.imread(os.path.join(originImagesDir, index))height, width, _ = im.shapeexcept Exception as e:print(f'{os.path.join(originImagesDir, index)} read error.\nerror:{e}')# 添加图像的信息if not os.path.exists(os.path.join(originLabelsDir, txtFile)):# 如没标签,跳过,只保留图片信息.continuedataset['images'].append({'file_name': index,'id': stem,'width': width,'height': height})with open(os.path.join(originLabelsDir, txtFile), 'r') as fr:labelList = fr.readlines()for label in labelList:label = label.strip().split()x = float(label[1])y = float(label[2])w = float(label[3])h = float(label[4])# convert x,y,w,h to x1,y1,x2,y2H, W, _ = im.shapex1 = (x - w / 2) * Wy1 = (y - h / 2) * Hx2 = (x + w / 2) * Wy2 = (y + h / 2) * H# 标签序号从0开始计算, coco2017数据集标号混乱,不管它了。cls_id = int(label[0])width = max(0, x2 - x1)height = max(0, y2 - y1)dataset['annotations'].append({'area': width * height,'bbox': [x1, y1, width, height],'category_id': cls_id,'id': ann_id_cnt,'image_id': stem,'iscrowd': 0,# mask, 矩形是从左上角点按顺时针的四个顶点'segmentation': [[x1, y1, x2, y1, x2, y2, x1, y2]]})ann_id_cnt += 1# 保存结果with open(arg.save_path, 'w') as f:json.dump(dataset, f)print('Save annotation to {}'.format(arg.save_path))if __name__ == "__main__":yolo2coco(arg)

三、参数修改

        以faster-rcnn为例,查看文件configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py内容如下:

_base_ = ['../_base_/models/faster-rcnn_r50_fpn.py','../_base_/datasets/coco_detection.py','../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'
]

        根据显示内容,修改具体配置。(‘../_base_/default_runtime.py’无需修改)

        (1)到‘../_base_/models/faster-rcnn_r50_fpn.py’修改

num_classes为自己的实际数据集类别数。

        (2)到‘../_base_/datasets/coco_detection.py’,修改

dataset_type = 'CocoDataset'
data_root = 'data/coco/'

        由于我使用的是coco数据集格式,只需要改data_root为自己数据集的位置即可。并修改

scale为自己的图像尺寸大小,我的是scale=(640, 640)。接下来根据数据集修改ann_file和data_prefix,根据如上数据集的位置放置,我的修改后该文件完整内容如下:

# dataset settings
dataset_type = 'CocoDataset'
data_root = 'data/coco/'# Example to use different file client
# Method 1: simply set the data root and let the file I/O module
# automatically infer from prefix (not support LMDB and Memcache yet)# data_root = 's3://openmmlab/datasets/detection/coco/'# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6
# backend_args = dict(
#     backend='petrel',
#     path_mapping=dict({
#         './data/': 's3://openmmlab/datasets/detection/',
#         'data/': 's3://openmmlab/datasets/detection/'
#     }))
backend_args = Nonetrain_pipeline = [dict(type='LoadImageFromFile', backend_args=backend_args),dict(type='LoadAnnotations', with_bbox=True),dict(type='Resize', scale=(640, 640), keep_ratio=True),dict(type='RandomFlip', prob=0.5),dict(type='PackDetInputs')
]
test_pipeline = [dict(type='LoadImageFromFile', backend_args=backend_args),dict(type='Resize', scale=(640, 640), keep_ratio=True),# If you don't have a gt annotation, delete the pipelinedict(type='LoadAnnotations', with_bbox=True),dict(type='PackDetInputs',meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape','scale_factor'))
]
train_dataloader = dict(batch_size=2,num_workers=2,persistent_workers=True,sampler=dict(type='DefaultSampler', shuffle=True),batch_sampler=dict(type='AspectRatioBatchSampler'),dataset=dict(type=dataset_type,data_root=data_root,ann_file='annotations/instances_train2017.json',data_prefix=dict(img='train2017/'),filter_cfg=dict(filter_empty_gt=True, min_size=32),pipeline=train_pipeline,backend_args=backend_args))
val_dataloader = dict(batch_size=1,num_workers=2,persistent_workers=True,drop_last=False,sampler=dict(type='DefaultSampler', shuffle=False),dataset=dict(type=dataset_type,data_root=data_root,ann_file='annotations/instances_val2017.json',data_prefix=dict(img='val2017/'),test_mode=True,pipeline=test_pipeline,backend_args=backend_args))
# test_dataloader = val_dataloaderval_evaluator = dict(type='CocoMetric',ann_file=data_root + 'annotations/instances_val2017.json',metric='bbox',format_only=False,backend_args=backend_args)
# test_evaluator = val_evaluator# inference on test dataset and
# format the output results for submission.
test_dataloader = dict(batch_size=2,num_workers=2,persistent_workers=True,drop_last=False,sampler=dict(type='DefaultSampler', shuffle=False),dataset=dict(type=dataset_type,data_root=data_root,ann_file=data_root + 'annotations/instances_test2017.json',data_prefix=dict(img='test2017/'),test_mode=True,pipeline=test_pipeline))
test_evaluator = dict(type='CocoMetric',metric='bbox',format_only=True,ann_file=data_root + 'annotations/instances_test2017.json',outfile_prefix='./work_dirs/coco_detection/test')

        (3)到‘../_base_/schedules/schedule_1x.py’修改max_epochs为自己设置的最大训练轮次,其他的val_interval、lr、momentum、weight_decay,如果没有特别的要求可不修改。哦,根据自己的电脑情况,别忘了改base_batch_size。该文件整体内容如下:

# training schedule for 1x
train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=100, val_interval=10)
val_cfg = dict(type='ValLoop')
test_cfg = dict(type='TestLoop')# learning rate
param_scheduler = [dict(type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),dict(type='MultiStepLR',begin=0,end=100,by_epoch=True,milestones=[67, 92],gamma=0.1)
]# optimizer
optim_wrapper = dict(type='OptimWrapper',optimizer=dict(type='SGD', lr=0.01, momentum=0.937, weight_decay=0.0001))# Default setting for scaling LR automatically
#   - `enable` means enable scaling LR automatically
#       or not by default.
#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).
auto_scale_lr = dict(enable=False, base_batch_size=16)

四、命令训练

        使用如下命令训练:

python  tools/train.py configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py

       如果训练中断,使用resume继续(注意‘--work-dir work_dirs/faster-rcnn_r50_fpn_1x_coco’是训练结果输出的位置,epoch_21.pth是上次训练中断后,输出的最后一个pth,根据自己的实际情况修改):

python tools/train.py configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py  --work-dir work_dirs/faster-rcnn_r50_fpn_1x_coco --resume work_dirs/faster-rcnn_r50_fpn_1x_coco/epoch_21.pth

五、转为YOLO格式的评价指标

        (1)找出最佳epoch

import os
import subprocess
import pickle
import numpy as np
import json
from prettytable import PrettyTable
from tqdm import tqdm# 设置工作目录和模型文件路径
work_dir = "work_dirs/faster-rcnn_r50_fpn_1x_coco"
config_file = "configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py"  # 你的config文件路径# 存放模型权重文件(epoch_1.pth 到 epoch_100.pth)
checkpoint_dir = os.path.join(work_dir, "")  # 假设检查点文件在 'checkpoints' 子文件夹下# 遍历模型权重文件(epoch_1.pth, epoch_2.pth, ..., epoch_100.pth)
checkpoint_files = [f for f in os.listdir(checkpoint_dir) if f.endswith('.pth')]
checkpoint_files.sort(key=lambda x: int(x.split('_')[1].split('.')[0]))  # 按照 epoch 数字排序# 用于存储评估结果
results = []# 循环遍历每个模型文件
for checkpoint_file in tqdm(checkpoint_files, desc="Evaluating"):checkpoint_path = os.path.join(checkpoint_dir, checkpoint_file)# 设置输出 pkl 文件的路径output_pkl = f"res_{checkpoint_file.split('.')[0]}.pkl"# 运行 test.py 脚本进行模型评估command = ["python", "tools/test.py", config_file, checkpoint_path,"--out", output_pkl  # 输出 pkl 文件]# 使用 subprocess 运行命令并捕获输出result = subprocess.run(command, capture_output=True, text=True)# 调试输出print(f"Evaluating {checkpoint_file}...")print(result.stdout)# 假设输出的评估结果包含了 mAP# 解析 mAP,假设它包含在 stdout 中,例如 "bbox_mAP: 0.45"for line in result.stdout.splitlines():if "bbox_mAP" in line:try:# 提取 mAP 分数bbox_mAP = float(line.split(":")[-1].strip())epoch = int(checkpoint_file.split("_")[1].split(".")[0])  # 获取 epoch 数字results.append((epoch, bbox_mAP, output_pkl))  # 存储结果 (epoch, mAP, pkl文件)breakexcept ValueError:print(f"Error parsing bbox_mAP for {checkpoint_file}: {line}")continue# 计算并输出最佳 epoch 和 mAP
if results:# 根据 mAP 找到最佳的 epochbest_epoch, best_mAP, best_pkl = max(results, key=lambda x: x[1])print(f"Best Epoch: {best_epoch}, Best bbox_mAP: {best_mAP:.4f}, Best Output pkl: {best_pkl}")
else:print("No valid results found. Please check the log outputs.")# 计算所有 epoch 的 mAP 值
table = PrettyTable()
table.title = f"Evaluation Metrics"
table.field_names = ["Epoch", "bbox_mAP", "Output pkl"]# 添加每个 epoch 的评估结果
for epoch, mAP, pkl_file in results:table.add_row([epoch, f"{mAP:.4f}", pkl_file])print(table)

        (2)根据最佳epoch生成pkl文件 

python tools/test.py work_dirs/faster-rcnn_r50_fpn_1x_coco/faster-rcnn_r50_fpn_1x_coco.py work_dirs/faster-rcnn_r50_fpn_1x_coco/best_coco_bbox_mAP_epoch_90.pth --out res90.pkl

        (3)根据pkl文件输出对比参数,修改内容在‘def parse_opt():’,将内容改为实际的地址名称。

        完整代码如下:

import os, torch, cv2, math, tqdm, time, shutil, argparse, json, pickle
import numpy as np
from prettytable import PrettyTabledef clip_boxes(boxes, shape):# Clip boxes (xyxy) to image shape (height, width)if isinstance(boxes, torch.Tensor):  # faster individuallyboxes[..., 0].clamp_(0, shape[1])  # x1boxes[..., 1].clamp_(0, shape[0])  # y1boxes[..., 2].clamp_(0, shape[1])  # x2boxes[..., 3].clamp_(0, shape[0])  # y2else:  # np.array (faster grouped)boxes[..., [0, 2]] = boxes[..., [0, 2]].clip(0, shape[1])  # x1, x2boxes[..., [1, 3]] = boxes[..., [1, 3]].clip(0, shape[0])  # y1, y2def scale_boxes(img1_shape, boxes, img0_shape, ratio_pad=None):# Rescale boxes (xyxy) from img1_shape to img0_shapeif ratio_pad is None:  # calculate from img0_shapegain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1])  # gain  = old / newpad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2  # wh paddingelse:gain = ratio_pad[0][0]pad = ratio_pad[1]boxes[..., [0, 2]] -= pad[0]  # x paddingboxes[..., [1, 3]] -= pad[1]  # y paddingboxes[..., :4] /= gainclip_boxes(boxes, img0_shape)return boxesdef box_iou(box1, box2, eps=1e-7):"""Calculate intersection-over-union (IoU) of boxes. Both sets of boxes are expected to be in (x1, y1, x2, y2) format.Based on https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.pyArgs:box1 (torch.Tensor): A tensor of shape (N, 4) representing N bounding boxes.box2 (torch.Tensor): A tensor of shape (M, 4) representing M bounding boxes.eps (float, optional): A small value to avoid division by zero. Defaults to 1e-7.Returns:(torch.Tensor): An NxM tensor containing the pairwise IoU values for every element in box1 and box2."""# NOTE: Need .float() to get accurate iou values# inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2)(a1, a2), (b1, b2) = box1.float().unsqueeze(1).chunk(2, 2), box2.float().unsqueeze(0).chunk(2, 2)inter = (torch.min(a2, b2) - torch.max(a1, b1)).clamp_(0).prod(2)# IoU = inter / (area1 + area2 - inter)return inter / ((a2 - a1).prod(2) + (b2 - b1).prod(2) - inter + eps)def process_batch(detections, labels, iouv):"""Return correct prediction matrixArguments:detections (array[N, 6]), x1, y1, x2, y2, conf, classlabels (array[M, 5]), class, x1, y1, x2, y2Returns:correct (array[N, 10]), for 10 IoU levels"""correct = np.zeros((detections.shape[0], iouv.shape[0])).astype(bool)iou = box_iou(labels[:, 1:], detections[:, :4])correct_class = labels[:, 0:1] == detections[:, 5]for i in range(len(iouv)):x = torch.where((iou >= iouv[i]) & correct_class)  # IoU > threshold and classes matchif x[0].shape[0]:matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy()  # [label, detect, iou]if x[0].shape[0] > 1:matches = matches[matches[:, 2].argsort()[::-1]]matches = matches[np.unique(matches[:, 1], return_index=True)[1]]# matches = matches[matches[:, 2].argsort()[::-1]]matches = matches[np.unique(matches[:, 0], return_index=True)[1]]correct[matches[:, 1].astype(int), i] = Truereturn torch.tensor(correct, dtype=torch.bool, device=iouv.device)def smooth(y, f=0.05):# Box filter of fraction fnf = round(len(y) * f * 2) // 2 + 1  # number of filter elements (must be odd)p = np.ones(nf // 2)  # ones paddingyp = np.concatenate((p * y[0], y, p * y[-1]), 0)  # y paddedreturn np.convolve(yp, np.ones(nf) / nf, mode='valid')  # y-smootheddef ap_per_class(tp, conf, pred_cls, target_cls, plot=False, save_dir='.', names=(), eps=1e-16, prefix=''):""" Compute the average precision, given the recall and precision curves.Source: https://github.com/rafaelpadilla/Object-Detection-Metrics.# Argumentstp:  True positives (nparray, nx1 or nx10).conf:  Objectness value from 0-1 (nparray).pred_cls:  Predicted object classes (nparray).target_cls:  True object classes (nparray).plot:  Plot precision-recall curve at mAP@0.5save_dir:  Plot save directory# ReturnsThe average precision as computed in py-faster-rcnn."""# Sort by objectnessi = np.argsort(-conf)tp, conf, pred_cls = tp[i], conf[i], pred_cls[i]# Find unique classesunique_classes, nt = np.unique(target_cls, return_counts=True)nc = unique_classes.shape[0]  # number of classes, number of detections# Create Precision-Recall curve and compute AP for each classpx, py = np.linspace(0, 1, 1000), []  # for plottingap, p, r = np.zeros((nc, tp.shape[1])), np.zeros((nc, 1000)), np.zeros((nc, 1000))for ci, c in enumerate(unique_classes):i = pred_cls == cn_l = nt[ci]  # number of labelsn_p = i.sum()  # number of predictionsif n_p == 0 or n_l == 0:continue# Accumulate FPs and TPsfpc = (1 - tp[i]).cumsum(0)tpc = tp[i].cumsum(0)# Recallrecall = tpc / (n_l + eps)  # recall curver[ci] = np.interp(-px, -conf[i], recall[:, 0], left=0)  # negative x, xp because xp decreases# Precisionprecision = tpc / (tpc + fpc)  # precision curvep[ci] = np.interp(-px, -conf[i], precision[:, 0], left=1)  # p at pr_score# AP from recall-precision curvefor j in range(tp.shape[1]):ap[ci, j], mpre, mrec = compute_ap(recall[:, j], precision[:, j])if plot and j == 0:py.append(np.interp(px, mrec, mpre))  # precision at mAP@0.5# Compute F1 (harmonic mean of precision and recall)f1 = 2 * p * r / (p + r + eps)i = smooth(f1.mean(0), 0.1).argmax()  # max F1 indexp, r, f1 = p[:, i], r[:, i], f1[:, i]tp = (r * nt).round()  # true positivesfp = (tp / (p + eps) - tp).round()  # false positivesreturn tp, fp, p, r, f1, ap, unique_classes.astype(int)def compute_ap(recall, precision):""" Compute the average precision, given the recall and precision curves# Argumentsrecall:    The recall curve (list)precision: The precision curve (list)# ReturnsAverage precision, precision curve, recall curve"""# Append sentinel values to beginning and endmrec = np.concatenate(([0.0], recall, [1.0]))mpre = np.concatenate(([1.0], precision, [0.0]))# Compute the precision envelopempre = np.flip(np.maximum.accumulate(np.flip(mpre)))# Integrate area under curvemethod = 'interp'  # methods: 'continuous', 'interp'if method == 'interp':x = np.linspace(0, 1, 101)  # 101-point interp (COCO)ap = np.trapz(np.interp(x, mrec, mpre), x)  # integrateelse:  # 'continuous'i = np.where(mrec[1:] != mrec[:-1])[0]  # points where x axis (recall) changesap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])  # area under curvereturn ap, mpre, mrecdef parse_opt():parser = argparse.ArgumentParser()parser.add_argument('--label_coco', type=str, default='E:/mmde/mmdetection-3.0.0/mmdetection-3.0.0/test.json',help='label coco path')# parser.add_argument('--pred_coco', type=str, default='runs/val/exp/predictions.json', help='pred coco path')parser.add_argument('--pred_coco', type=str, default='E:/mmde/mmdetection-3.0.0/mmdetection-3.0.0/res90.pkl', help='pred coco path')parser.add_argument('--iou', type=float, default=0.7, help='iou threshold')parser.add_argument('--conf', type=float, default=0.001, help='conf threshold')opt = parser.parse_known_args()[0]return optif __name__ == '__main__':opt = parse_opt()iouv = torch.linspace(0.5, 0.95, 10)  # iou vector for mAP@0.5:0.95niou = iouv.numel()stats = []label_coco_json_path, pred_coco_json_path = opt.label_coco, opt.pred_cocowith open(label_coco_json_path) as f:label = json.load(f)classes = []for data in label['categories']:classes.append(data['name'])image_id_hw_dict = {}for data in label['images']:image_id_hw_dict[data['id']] = [data['height'], data['width']]label_id_dict = {}for data in tqdm.tqdm(label['annotations'], desc='Process label...'):if data['image_id'] not in label_id_dict:label_id_dict[data['image_id']] = []category_id = data['category_id']x_min, y_min, w, h = data['bbox'][0], data['bbox'][1], data['bbox'][2], data['bbox'][3]x_max, y_max = x_min + w, y_min + hlabel_id_dict[data['image_id']].append(np.array([int(category_id), x_min, y_min, x_max, y_max]))if pred_coco_json_path.endswith('json'):with open(pred_coco_json_path) as f:pred = json.load(f)pred_id_dict = {}for data in tqdm.tqdm(pred, desc='Process pred...'):if data['image_id'] not in pred_id_dict:pred_id_dict[data['image_id']] = []score = data['score']category_id = data['category_id']x_min, y_min, w, h = data['bbox'][0], data['bbox'][1], data['bbox'][2], data['bbox'][3]x_max, y_max = x_min + w, y_min + hpred_id_dict[data['image_id']].append(np.array([x_min, y_min, x_max, y_max, float(score), int(category_id)]))else:with open(pred_coco_json_path, 'rb') as f:pred = pickle.load(f)pred_id_dict = {}for data in tqdm.tqdm(pred, desc='Process pred...'):image_id = os.path.splitext(os.path.basename(data['img_path']))[0]if image_id not in pred_id_dict:pred_id_dict[image_id] = []for i in range(data['pred_instances']['labels'].size(0)):score = data['pred_instances']['scores'][i]category_id = data['pred_instances']['labels'][i]bboxes = data['pred_instances']['bboxes'][i]x_min, y_min, x_max, y_max = bboxes.cpu().detach().numpy()# x_min, x_max = x_min / data['scale_factor'][0], x_max / data['scale_factor'][0]# y_min, y_max = y_min / data['scale_factor'][1], y_max / data['scale_factor'][1]pred_id_dict[image_id].append(np.array([x_min, y_min, x_max, y_max, float(score), int(category_id)]))for idx, image_id in enumerate(tqdm.tqdm(list(image_id_hw_dict.keys()), desc="Cal mAP...")):label = np.array(label_id_dict[image_id])if image_id not in pred_id_dict:pred = np.empty((0, 6))else:pred = torch.from_numpy(np.array(pred_id_dict[image_id]))nl, npr = label.shape[0], pred.shape[0]correct = torch.zeros(npr, niou, dtype=torch.bool)if npr == 0:if nl:stats.append((correct, *torch.zeros((2, 0)), torch.from_numpy(label[:, 0])))continueif nl:correct = process_batch(pred, torch.from_numpy(label), iouv)stats.append((correct, pred[:, 4], pred[:, 5], torch.from_numpy(label[:, 0])))stats = [torch.cat(x, 0).cpu().numpy() for x in zip(*stats)]tp, fp, p, r, f1, ap, ap_class = ap_per_class(*stats)print(f'precision:{p}')print(f'recall:{r}')print(f'mAP@0.5:{ap[:, 0]}')table = PrettyTable()table.title = f"Metrice"table.field_names = ["Classes", 'Precision', 'Recall', 'mAP50', 'mAP50-95']table.add_row(['all', f'{np.mean(p):.3f}', f'{np.mean(r):.3f}', f'{np.mean(ap[:, 0]):.3f}', f'{np.mean(ap):.3f}'])for cls_idx, classes in enumerate(classes):table.add_row([classes, f'{p[cls_idx]:.3f}', f'{r[cls_idx]:.3f}', f'{ap[cls_idx, 0]:.3f}',f'{ap[cls_idx, :].mean():.3f}'])print(table)

六、输出结果

        此外,查看FLOPs参数,用命令:

python tools/analysis_tools/get_flops.py work_dirs/faster-rcnn_r50_fpn_1x_coco-0.937-best0.965/faster-rcnn_r50_fpn_1x_coco.py

结果如下图:

相关文章:

新手学习yolov8目标检测小记2--对比实验中经典模型库MMDetection使用方法(使用自己的数据集训练,并转换为yolo格式评价指标)

一、按照步骤环境配置 pip install timm1.0.7 thop efficientnet_pytorch0.7.1 einops grad-cam1.4.8 dill0.3.6 albumentations1.4.11 pytorch_wavelets1.3.0 tidecv PyWavelets -i https://pypi.tuna.tsinghua.edu.cn/simple pip install -U openmim -i https://pypi.tuna.t…...

Kubernetes开发环境minikube | 开发部署apache tomcat web单节点应用

minikube是一个主要用于开发与测试Kubernetes应用的运行环境 本文主要描述在minikube运行环境中部署J2EE tomcat web应用 minikube start --force minikube status 如上所示,在Linux中启动minikube运行环境 service docker start docker version service docker …...

浙江安吉成新的分布式光伏发电项目应用

摘 要:分布式光伏发电站是指将光伏发电组件安装在用户的建筑物屋顶、空地或其他适合的场地上,利用太阳能进行发电的一种可再生能源利用方式,与传统的大型集中式光伏电站相比,分布式光伏发电具有更灵活的布局、更低的建设成本和更高…...

Git - 记录一次由于少输入了一个命令导致的更改丢失

Git - 记录一次由于少输入了一个参数导致的更改丢失 前言 某晚我激情开发了几个小时,中途没有进行commit存档。准备睡觉时,我想创建一个新的分支并将今晚所有更改提交到新分支上(似乎应该开发时候就创建?)。 然后因…...

【Nginx】设置https和http同时使用同一个端口访问

以下是一个同时使用 HTTP 和 HTTPS 并通过 8070 端口的配置示例: server {listen 8070;server_name your_domain.com;location / {root /var/www/html;index index.html;} }server {listen 8070 ssl;server_name your_domain.com;# SSL 证书和私钥的路径ssl_certif…...

Vue 组件开发:构建高效可复用的 UI 构建块

在现代前端开发中,Vue.js 凭借其简洁的 API、渐进式框架设计和强大的生态系统,已经成为众多开发者的首选。Vue 组件化开发是其核心特性之一,它允许我们将复杂的 UI 拆分成多个独立、可复用的组件,从而提高代码的可维护性和可扩展性…...

【Uniapp-Vue3】v-if条件渲染及v-show的选择对比

如果我们想让元素根据响应式变量的值进行显示或隐藏可以使用v-if或v-show 一、v-show 另一种控制显示的方法就是使用v-show,使用方法和v-if一样,为true显示,为false则不显示。 二、v-if v-if除了可以像v-show一样单独使用外,还…...

浏览器报错:您的连接不是私密连接,Kubernetes Dashboard无法打开

问题描述 部署完成Kubernetes Dashboard后,打开HTTPS的web页面,Chrome和Edge浏览器都无法正常加载页面,会提示您的连接不是私密连接的报错。 ​​​​​​​​​​​​ 原因: 浏览器不信任这些自签名的ssl证书,为了…...

asp.net core 属性路由和约定路由

在 ASP.NET Core 中,Web API 中的路由(Route)用于确定客户端请求的 URL 与服务器端处理逻辑之间的映射关系。路由机制在 Web API 的开发中非常重要,它帮助定义和管理不同请求路径如何触发特定的控制器和操作方法。 1. 路由概述 …...

机器学习之模型评估——混淆矩阵,交叉验证与数据标准化

目录 混淆矩阵 交叉验证 数据标准化 0-1标准化 z 标准化 混淆矩阵 混淆矩阵(Confusion Matrix)是一种用于评估分类模型性能的工具。 它是一个二维表格,其中行表示实际的类别,列表示模型预测的类别。 假设我们有一个二分类问题&…...

Java实现UDP与TCP应用程序

三、Java实现UDP应用程序 3.1 InetAddress类 java.net.InteAddress类是用于描述IP地址和域名的一个Java类; 常用方法如下: public static InetAddress getByName(String host):根据主机名获取InetAddress对象public String getHostName()…...

[python3]Excel解析库-calamine,10倍openpyxl性能

calamine 是一个用于读取多种电子表格格式(如 Excel、LibreOffice Calc 等)的 Python 库。它支持 .xls, .xlsx, .ods 和 .csv 文件格式,提供了简单易用的 API 来加载和处理电子表格数据。calamine 的一大特点是它的轻量级和高效性&#xff0c…...

Clisoft SOS设置Server和Project

Clisoft SOS设置Server和Project 一、关于SOS Servers、Clients、Projects和Work Areas 以下三个图是官方文档中介绍的三种情况 图1:带有两个客户端的SOS服务器 图2:使用本地缓存服务器 图3:远程设计团队的缓存服务器 因为SOS软件需要…...

基于FPGA的出租车里程时间计费器

基于FPGA的出租车里程时间计费器 功能描述一、系统框图二、verilog代码里程增加模块时间增加模块计算价格模块上板视频演示 总结 功能描述 (1);里程计费功能:3公里以内起步价8元,超过3公里后每公里2元,其中…...

AnaConda下载PyTorch慢的解决办法

使用Conda下载比较慢,改为pip下载 复制下载链接到迅雷下载 激活虚拟环境,安装whl,即可安装成功 pip install D:\openai.wiki\ChatGLM2-6B\torch-2.4.1cu121-cp38-cp38-win_amd64.whl...

Hello 2025(A-C)

补题链接&#xff1a;Dashboard - Hello 2025 - Codeforces A. MEX Table 思路 除了含0的列和行其他的都是0&#xff0c;输出max(n,m)1即可 代码 #include<bits/stdc.h> using namespace std;#define vcoistnt ios_base::sync_with_stdio(false); cin.tie(NULL); co…...

Burpsuite20241102macM1版安装

1、安装jdk11 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" brew update brew install openjdk11 echo export PATH"/opt/homebrew/opt/openjdk11/bin:$PATH" >> ~/.zshrc source ~/.zshrc j…...

jenkins入门10--自动化构建

build periodically&#xff1a;设定类似cron周期性时间触发构建 * * * * * (五颗星&#xff0c;中间用空格隔开&#xff09; 第一颗表示分钟&#xff0c;取值0~59 第二颗表示小时&#xff0c;取值0~23 第三颗表示一个月的第几天&#xff0c;取值1~31 第四颗表示第几月&#xf…...

Java基础概念

自动装箱 Integer i 10; //装箱 int n i; //拆箱 普通数据类型‌&#xff1a;直接在栈内存中分配空间&#xff0c;存储的是具体的值。‌包装类‌&#xff1a;作为对象在堆内存中分配空间。包装类实际上是对普通数据类型的封装&#xff0c;每个包装类都包含了对应的数据类…...

57.在 Vue 3 中使用 OpenLayers 点击选择 Feature 设置特定颜色

在 Web 开发中&#xff0c;地图应用是非常常见的需求&#xff0c;而 OpenLayers 是一个非常强大的地图库&#xff0c;它提供了丰富的地图操作功能。今天&#xff0c;我们将一起学习如何在 Vue 3 中结合 OpenLayers 使用点击事件来选择地图上的 Feature&#xff0c;并设置特定的…...

HTML——61. 单行文本框和密码输入框(主讲input元素的type属性)

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>单行文本框和密码输入框</title></head><body><!--input元素的type属性&#xff1a;(必须要有)--> <!--单行文本框:1.type"text"2.可…...

h264之多视点mvc编码及解码过程(JMVC平台举例)

h264标准参考平台JMVC是针对MVC标准的&#xff0c;JMVC支持多视点编码、合流、多视点解码操作。可以利用JMVC生成h264 mvc码流和解码。 JMVC的下载地址是&#xff1a;jvet / JMVC GitLabH.264/AVC multi-view coding (MVC) extension JMVC reference softwarehttps://vcgit.hh…...

深度学习blog-深刻理解线性变换和矩阵

深度学习中避免不了矩阵运算&#xff0c;或者张量&#xff08;其实是矩阵数组&#xff09;运算。卷积是矩阵加、乘法&#xff0c;注意力也是一样。本质都一样&#xff0c;所谓注意力&#xff0c;卷积、滤波&#xff0c;是对不必了解数学的人说的&#xff0c;底层都是矩阵运算&a…...

C语言 扫雷程序设计

目录 1.main函数 2.菜单打印menu函数 3.游戏game函数 4.宏定义 5.界面初始化 6.打印界面 7.设置雷 8.统计排查坐标周围雷的个数 9.排查雷 10.总代码 test.c代码 game.h代码 game.c代码 结语&#xff1a; 一个简单的扫雷游戏&#xff0c;通过宏定义可以修改行列的…...

[笔记] Jenkins 安装与配置全攻略:Ubuntu 从零开始搭建持续集成环境

随着 DevOps 流程的普及&#xff0c;持续集成&#xff08;CI&#xff09;和持续交付&#xff08;CD&#xff09;已成为现代软件开发中不可或缺的一部分。Jenkins 作为一款开源的自动化服务器&#xff0c;广泛应用于 CI/CD 管道的构建与管理。它不仅支持多种编程语言和工具链&am…...

【51单片机零基础-chapter3:按键:独立按键|||附带常见C语句.逻辑运算符】

将unsigned char var0;看作沟通二进制和十进制的桥梁 var是8位,初始为0000 0000; 同时可以进行十进制的运算 逻辑运算 位运算 & 按位与(有0则0) | 按位或(有1则1) ~ 按位非 ^ 按位异或(相同则1,不同为0) <<按位左移 >>按位右移 位运算符解释: 0011 1100 <&…...

深入浅出:深层网络处理技术的教学指南

引言 在人工智能的浪潮中&#xff0c;深层网络处理技术&#xff08;Deep Learning&#xff09;无疑是最耀眼的明星之一。无论是图像识别、自然语言处理&#xff0c;还是语音识别&#xff0c;深层网络都展现出了强大的能力。然而&#xff0c;对于初学者来说&#xff0c;深层网络…...

深入浅出Node.js-1(node.js入门)

全新专栏带你快速掌握node.js Node.js入门 html,css,js 30年了 nodejs环境 09年出现 15年 nodejs为我们解决了2个方面的问题&#xff1a; 【锦上添花】让我们前端工程师拥有了后端开发能力&#xff08;开接口&#xff0c;访问数据库&#xff09; - 大公司BFF&#xff08;5…...

Django的runserver

当年执行 python manage runserver命令时 1. 先执行 runserver 中的 handle方法 2. 执行 self.run()方法 3. 执行 self.inner_run() 3.1 inner_run 下 run方法的封装 3.1.1 接着看 handle 怎么来的 封装了一个方法 接着找返回函数 3.1.2在 basehttp 下 3.1.3 get_wsgi_appl…...

MySQL 存储引擎

InnoDB InnoDB是MySQL的默认存储引擎&#xff0c;自MySQL 5.5版本起开始使用。它提供了具有提交、回滚和崩溃恢复能力的事务安全&#xff08;ACID兼容&#xff09;存储引擎。 主要特性&#xff1a; 事务支持&#xff1a;完全支持ACID&#xff08;原子性、一致性、隔离性、持久…...

React知识盲点——组件通信、性能优化、高级功能详解(大纲)

组件通信 React 组件通信详解 在 React 中&#xff0c;组件通信是一个核心概念&#xff0c;主要指的是如何让不同的组件共享和传递数据。React 提供了多种机制来实现组件间的数据传递和状态共享。以下是几种常见的组件通信方式&#xff0c;包括&#xff1a;父子组件通信&…...

Maven 详细配置:Maven 项目 POM 文件解读

Maven 是 Java 开发领域中广泛使用的项目管理和构建工具&#xff0c;通过其核心配置文件——POM&#xff08;Project Object Model&#xff09;文件&#xff0c;开发者能够定义项目的基本信息、依赖关系、插件配置以及构建生命周期等关键要素。POM 文件不仅是 Maven 项目的核心…...

selenium

pythonselenium selenium是一个第三方库&#xff0c;python有很多库&#xff1b; 1、什么是ui自动化? 通过模拟手工操作用户ui页面的方式&#xff0c;用代码去实现自动化操作和验证的行为。 2、ui自动化的优点&#xff1f; &#xff08;1&#xff09;解决重复性的功能测试…...

网络安全:设备原理与操作

设备型号概述 网络安全企业有哪些&#xff1f; 国外&#xff1a;思科&#xff0c;Juniper&#xff0c;惠普&#xff0c;3Com&#xff0c;。。。。 国内&#xff1a;华为&#xff0c;中性&#xff0c;锐捷&#xff0c;蓝盾&#xff0c;绿盟&#xff0c;山石网科&#xff0c;36…...

pytorch中nn.Conv2d详解及参数设置原则

文章目录 基础参数1. in_channels (输入通道数)2. out_channels (输出通道数)3. kernel_size (卷积核大小)4. stride (步幅)5. padding (填充)6. dilation (膨胀)7. groups (分组卷积)8. bias (偏置) 如何设置参数&#xff1f;1. **in_channels 和 out_channels&#xff08;输入…...

select下拉框,首次进入页面没有显示value的情况

bug场景&#xff1a; 类似这种bug情况排查如下&#xff1a; 首先 理解含义 options就是存放键值对的&#xff0c;id就是key&#xff0c;对上了它就自动把label显示 而且如果你用来当作key和label的字段&#xff0c;与后端返回的不一致&#xff0c;还可以进行更改 其次 排查接…...

接口项目操作图-thinkphp6-rabbitmq

一、用户开户流程 用户首次需要联系商务开通账户&#xff0c;需要提供手机号及来访问的IP。开好户之后&#xff0c;平台方将提供用户访问的key值及header头部参数的公钥加密文件、body访问参数以及返回数据的公私钥加解密文件。 二、用户请求流程 用户将拿到的key值进行rsa公钥…...

thinkphp6.0常用设计模式实例

单例模式 (Singleton) 场景&#xff1a;确保一个类只有一个实例&#xff0c;并提供一个全局访问点。 实际业务&#xff1a;数据库连接、日志记录器、配置管理等。 ThinkPHP 6.0 实现&#xff1a; namespace app\common;class DatabaseConnection {private static $instance …...

微服务保护——Sentinel

什么是微服务保护&#xff1f; 微服务保护是一系列用于保障微服务架构稳定、可靠运行的策略与技术手段&#xff0c;在复杂的分布式微服务系统里&#xff0c;它能避免局部故障引发连锁反应&#xff0c;从而维持整个系统的可用性&#xff0c;主要涵盖以下几个关键部分&#xff1a…...

php 多进程那点事,用 swoole 如何解决呢 ?

在 PHP 中&#xff0c;多进程的处理通常会遇到一些挑战&#xff0c;比如资源共享、进程间通信、性能优化等。Swoole 是一个高性能的协程和多进程框架&#xff0c;旨在为 PHP 提供异步、并发、协程等功能&#xff0c;解决了传统 PHP 环境中的多进程管理问题。通过使用 Swoole&am…...

STM32+ADC+DMA快速循环转换

测试平台&#xff1a;STM32F405RGT6 uint32_t AD_Buf[100]{0}; HAL_ADC_Start_DMA(&hadc2,(uint32_t *)AD_Buf,100); while(1) {printf("AD_Buf:%d\n",AD_Buf[0]); }...

移动电商的崛起与革新:以开源AI智能名片2+1链动模式S2B2C商城小程序为例的深度剖析

摘要&#xff1a;本文旨在探讨移动电商的崛起背景、特点及其对传统电商模式的革新影响&#xff0c;并以开源AI智能名片21链动模式S2B2C商城小程序为具体案例&#xff0c;深入分析其在移动电商领域的创新实践。随着移动互联网技术的飞速发展&#xff0c;移动电商已成为电商行业的…...

QT实现 端口扫描暂停和继续功能 3

上篇QT给端口扫描工程增加线程2-CSDN博客 为按钮pushButton_Stop添加clicked事件&#xff0c;功能为暂停扫描&#xff0c;并在暂停后显示继续按钮&#xff0c;点击继续按钮之后继续扫描 1.更新UI 添加继续按钮 点击转到槽则会自动声明 2. 更新 MainWindow.h 需要新增的部分…...

C_字符数组存储汉字字符串及其索引

字符串就是字符数组&#xff0c;可以定义一个char类型的数组来存储字符串。 如果要存储多个字符串则可以定义一个char类型的二维数组。 存储多个汉字字符串的话&#xff0c;可以考虑用char类型的二维数组。 不过要注意&#xff0c;一个汉字在内存中占用的字节数确实大于一个…...

Linux标准IOday1

1:思维导图 2:将 student.c这个练习题&#xff0c;改成链表后实现 头文件link.h #ifndef __STRUCT_H__ #define __STRUCT_H__ #include <stdio.h> #include <stdlib.h> typedef struct Student{char name[20];double math;double chinese;double english;double…...

SEO内容优化:如何通过用户需求赢得搜索引擎青睐?

在谷歌SEO优化中&#xff0c;内容一直是最重要的因素之一。但要想让内容真正发挥作用&#xff0c;关键在于满足用户需求&#xff0c;而不是简单地堆砌关键词。谷歌的算法越来越智能化&#xff0c;更注重用户体验和内容的实用性。 了解目标用户的需求。通过工具如Google Trends…...

API调用淘宝京东商品详情接口示例参考,json格式数据示例

以下是API调用淘宝和京东商品详情接口的JSON格式数据示例&#xff1a; 淘宝商品详情接口JSON数据示例 淘宝商品详情接口&#xff08;通常称为item_get或类似的名称&#xff09;是淘宝开放平台提供的一个API接口&#xff0c;允许开发者根据商品的ID&#xff08;Item ID&#x…...

css实现垂直文本

效果 知识 writing-mode: <value>; 可选值 horizontal-tb: 默认值。文本从左到右&#xff08;或从右到左&#xff09;排列&#xff0c;然后从上到下。vertical-rl: 文本从上到下排列&#xff0c;然后从右到左。适用于垂直书写的方向&#xff0c;如日语和中文。vertica…...

【AI日记】25.01.07

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】【读书与思考】 AI 参加&#xff1a;kaggle 比赛 Forecasting Sticker Sales 读书 书名&#xff1a;国家为什么会失败阅读原因&#xff1a;2024 年诺贝尔经济学奖得主的力作&#xff0c;之前我已经读过他另一…...

logback日志

一、使用两个以上spring环境变量做三目操作 <springProperty name"application_name" scope"context" source"spring.application.name"/><springProperty name"trace_app_name" scope"context" source"sprin…...