目录
准备自定义数据集
本期我们开始学习在自定义数据集上进行训练,此贴持续更新(大体完结),记录自己使用非MMDetection官方提供的数据集训练预定义模型的学习历程,大家可以点赞收藏追更哈哈哈!
MMDetection 一共支持三种形式应用新数据集:
- 将数据集重新组织为 COCO 格式。
- 将数据集重新组织为一个中间格式。
- 实现一个新的数据集。
我们通常建议使用前面两种方法,因为它们通常来说比第三种方法要简单。
本次自定义数据集使用的是由我研究生所在课题组共享文件中师兄上传的cars数据集,该数据集为YOLO格式,21个自定义车辆类别。需要转换为COCO数据集才能在MMDetection中应用。下面先来讲解一下COCO格式的数据集
COCO标注格式数据集
{
"images": [image],
"annotations": [annotation],
"categories": [category]
}
这是一个典型的 COCO 格式数据集 JSON 文件结构,用于存储目标检测、实例分割等任务所需的数据。下面逐行给出详细的注释:
"images": [image]
:images
键表示该数据集中包含的所有图像信息。[image]
是一个包含多个image
对象(即图像元数据)的数组,每个对象代表一张图像。
image = {
"id": int,
"width": int,
"height": int,
"file_name": str,
}
-
"id": int
:- 图像的唯一标识符(ID),通常为整数。
-
"width": int
:- 图像的宽度(像素)。
-
"height": int
:- 图像的高度(像素)。
-
"file_name": str
:- 图像文件的完整路径或相对路径(相对于数据集根目录),以字符串形式表示。
"annotations": [annotation]
"annotations": [annotation]
:annotations
键表示该数据集中包含的所有标注信息。[annotation]
是一个包含多个annotation
对象(即图像标注元数据)的数组,每个对象代表一张图像上的一个或多个目标实例的标注。
annotation = {
"id": int,
"image_id": int,
"category_id": int,
"segmentation": RLE or [polygon],
"area": float,
"bbox": [x,y,width,height], # (x, y) 为 bbox 左上角的坐标
"iscrowd": 0 or 1,
}
-
"id": int
:- 标注的唯一标识符(ID),通常为整数。
-
"image_id": int
:- 该标注所对应的图像 ID,与
images
数组中的某个图像id
相对应。
- 该标注所对应的图像 ID,与
-
"category_id": int
:- 标注目标所属的类别 ID,与
categories
数组中的某个类别id
相对应。
- 标注目标所属的类别 ID,与
-
"segmentation": RLE or [polygon]
:- 实例分割信息。可以采用两种格式之一:
- RLE(Run-Length Encoding):一种压缩表示方法,用于存储像素级的二进制掩码。
- [polygon]:一个包含多个多边形顶点坐标的数组,每个多边形描述了目标实例的轮廓。
- 实例分割信息。可以采用两种格式之一:
-
"area": float
:- 标注区域的面积(像素数量),可用于计算目标的大小。
-
"bbox": [x,y,width,height]
:- 边界框(Bounding Box)信息,表示为
[x, y, width, height]
,其中:(x, y)
是边界框左上角在图像上的坐标(原点为左上角,单位为像素)。width
和height
分别是边界框的宽和高(像素)。
- 边界框(Bounding Box)信息,表示为
-
"iscrowd": 0 or 1
:- 标记该标注是否表示一个密集的“人群”或物体集合。值为:
0
:单个明确的目标实例。1
:表示密集人群或多个重叠目标的集合,通常在实例分割任务中使用。
- 标记该标注是否表示一个密集的“人群”或物体集合。值为:
"categories": [category]
"categories": [category]
:categories
键表示数据集中所有类别的信息。[category]
是一个包含多个category
对象的数组,每个对象代表一个类别。
category = {
"id": int,
"name": str,
"supercategory": str,
}
-
"id": int
:- 类别的唯一标识符(ID)。
-
"name": str
:- 类别的名称,通常为一个简洁易懂的字符串。
-
"supercategory": str
:- 类别的上级类别(如果存在)。例如,如果类别是“狗”,其上级类别可能是“动物”。对于没有明显上级类别的类别,此字段可以留空或设为与
name
相同。
- 类别的上级类别(如果存在)。例如,如果类别是“狗”,其上级类别可能是“动物”。对于没有明显上级类别的类别,此字段可以留空或设为与
YOLO标注格式数据集
YOLO(You Only Look Once)数据集的格式与COCO数据集有所不同,它主要由两部分组成:图像文件和对应的标注文件。下面详细介绍YOLO数据集的格式:
图像文件
YOLO数据集中的图像文件通常以常见的图像格式(如.jpg
、.png
等)存储,与COCO数据集一样,它们可以按照任意合理的目录结构组织,但关键是要与对应的标注文件保持一致的目录结构和文件名,以便模型训练时能够正确地将图像与标注信息关联起来。
标注文件
YOLO数据集的标注信息不存储在JSON文件中,而是单独为每张图像创建一个文本文件(.txt
格式),文件名与对应的图像文件名相同,仅扩展名不同。每个标注文件包含多行文本,每行描述图像中一个目标实例的信息,格式如下:
[class_id] [x_center] [y_center] [width] [height]
-
class_id
:整数,表示目标所属的类别ID。类别ID应与模型训练时使用的类别列表对应,通常从0开始计数。例如,如果类别列表中“狗”的索引为1,那么“狗”的class_id
就是1。 -
x_center
:浮点数,表示边界框(Bounding Box)中心点在图像宽度上的归一化坐标,取值范围为0到1。计算公式为:x_center = (边界框左上角x坐标 + 边界框宽度 / 2) / 图像宽度
。 -
y_center
:浮点数,表示边界框中心点在图像高度上的归一化坐标,取值范围也为0到1。计算公式为:y_center = (边界框左上角y坐标 + 边界框高度 / 2) / 图像高度
。 -
width
:浮点数,表示边界框宽度的归一化值,取值范围为0到1。计算公式为:width = 边界框宽度 / 图像宽度
。 -
height
:浮点数,表示边界框高度的归一化值,取值范围为0到1。计算公式为:height = 边界框高度 / 图像高度
。
例如,一个包含两个目标(类别ID分别为0和1)的YOLO标注文件可能如下所示:
0 0.½ 0.4 0.⅓ 0.⅔
1 0.⅔ 0.⅓ 0.⅕ 0.⅖
这里假设类别ID 0对应“猫”,类别ID 1对应“狗”。第一行描述了一个“猫”实例,其边界框中心位于图像宽度的50%、高度的40%,边界框宽高分别占图像宽高的三分之一。第二行描述了一个“狗”实例,其位置和大小信息类似。
目录结构
YOLO数据集通常将图像文件和标注文件分别放在两个子目录中,以便管理和维护:
data/
├── images/
│ ├── image_001.jpg
│ ├── image_002.jpg
│ └── ...
└── labels/
├── image_001.txt
├── image_002.txt
└── ...
这里,images
目录存放图像文件,labels
目录存放标注文件。每个标注文件(如image_001.txt
)与同名的图像文件(如image_001.jpg
)对应,描述了该图像中目标的位置和类别信息。
总结来说,YOLO数据集格式包括图像文件和对应的文本标注文件。图像文件存储图像数据,标注文件以文本形式记录每个目标的类别ID以及边界框的中心坐标和宽高比。这些信息以归一化数值表示,便于模型直接使用。图像文件和标注文件需保持一致的文件名和目录结构,以便模型训练时正确关联。
YOLO格式转COCO格式
我在别的CSDN博客上找了三份代码,分别有不同的报错。其中【目标检测】YOLO格式数据集txt标注转换为COCO格式JSON该文章的代码可行,我们根据实际情况修改好之后直接运行即可。
import os
import json
from PIL import Image
# 设置数据集路径
dataset_path = "/home/miqi/mmdetection/data/cars"
images_path = os.path.join(dataset_path, "images")
labels_path = os.path.join(dataset_path, "labels")
# 类别映射
categories = [
{"id": 0, "name": "car"},
{"id": 1, "name": "suv"},
{"id": 2, "name": "bus"},
{"id": 3, "name": "van"},
{"id": 4, "name": "bicycle"},
{"id": 5, "name": "electric vehicle"},
{"id": 6, "name": "motorbike/motorcycle"},
{"id": 7, "name": "tricycle"},
{"id": 8, "name": "take-out car"},
{"id": 9, "name": "trolleye"},
{"id": 10, "name": "stroller"},
{"id": 11, "name": "ambulance"},
{"id": 12, "name": "fire truck"},
{"id": 13, "name": "police car"},
{"id": 14, "name": "sweeper"},
{"id": 15, "name": "garbage truck"},
{"id": 16, "name": "pickup truck"},
{"id": 17, "name": "light truck"},
{"id": 18, "name": "heavy truck"},
{"id": 19, "name": "crane"},
{"id": 20, "name": "machinery vehicle"}
# 添加更多类别
]
# YOLO格式转COCO格式的函数
def convert_yolo_to_coco(x_center, y_center, width, height, img_width, img_height):
x_min = (x_center - width / 2) * img_width
y_min = (y_center - height / 2) * img_height
width = width * img_width
height = height * img_height
return [x_min, y_min, width, height]
# 初始化COCO数据结构
def init_coco_format():
return {
"images": [],
"annotations": [],
"categories": categories
}
# 处理每个数据集分区
for split in ['train', 'val']:
coco_format = init_coco_format()
annotation_id = 1
for img_name in os.listdir(os.path.join(images_path, split)):
if img_name.lower().endswith(('.png', '.jpg', '.jpeg')):
img_path = os.path.join(images_path, split, img_name)
label_path = os.path.join(labels_path, split, img_name.replace("jpg", "txt"))
img = Image.open(img_path)
img_width, img_height = img.size
image_info = {
"file_name": img_name,
"id": len(coco_format["images"]) + 1,
"width": img_width,
"height": img_height
}
coco_format["images"].append(image_info)
if os.path.exists(label_path):
with open(label_path, "r") as file:
for line in file:
category_id, x_center, y_center, width, height = map(float, line.split())
bbox = convert_yolo_to_coco(x_center, y_center, width, height, img_width, img_height)
annotation = {
"id": annotation_id,
"image_id": image_info["id"],
"category_id": int(category_id) + 1,
"bbox": bbox,
"area": bbox[2] * bbox[3],
"iscrowd": 0
}
coco_format["annotations"].append(annotation)
annotation_id += 1
# 为每个分区保存JSON文件
with open(f"/home/miqi/mmdetection/data/cars/{split}_coco_format.json", "w") as json_file:
json.dump(coco_format, json_file, indent=4)
在此代码中需要修改的地方有
1. 修改为你自己的自定义数据集路径
# 设置数据集路径
dataset_path = "/home/miqi/mmdetection/data/cars"
images_path = os.path.join(dataset_path, "images")
labels_path = os.path.join(dataset_path, "labels")
2. 根据自定义数据集中的类别写入类别映射
# 类别映射
categories = [
{"id": 0, "name": "car"},
{"id": 1, "name": "suv"},
{"id": 2, "name": "bus"},
{"id": 3, "name": "van"},
{"id": 4, "name": "bicycle"},
{"id": 5, "name": "electric vehicle"},
{"id": 6, "name": "motorbike/motorcycle"},
{"id": 7, "name": "tricycle"},
{"id": 8, "name": "take-out car"},
{"id": 9, "name": "trolleye"},
{"id": 10, "name": "stroller"},
{"id": 11, "name": "ambulance"},
{"id": 12, "name": "fire truck"},
{"id": 13, "name": "police car"},
{"id": 14, "name": "sweeper"},
{"id": 15, "name": "garbage truck"},
{"id": 16, "name": "pickup truck"},
{"id": 17, "name": "light truck"},
{"id": 18, "name": "heavy truck"},
{"id": 19, "name": "crane"},
{"id": 20, "name": "machinery vehicle"}
# 添加更多类别
]
3. 如果你的数据集中含有测试集,列表中再加一个‘test’
# 处理每个数据集分区
for split in ['train', 'val']: # ['train', 'test', 'val']
coco_format = init_coco_format()
annotation_id = 1
4. 修改json文件
保存为自己指定的路径
# 为每个分区保存JSON文件
with open(f"/home/miqi/mmdetection/data/cars/{split}_coco_format.json", "w") as json_file:
json.dump(coco_format, json_file, indent=4)
准备配置文件
请注意官方给的样例使用的为Mask R-CNN模型,Mask R-CNN 是一个用于实例分割(instance segmentation)的深度学习模型,其核心任务是识别图像中每个目标实例的类别并为其生成精细的像素级掩码(mask),以区分图像中属于不同目标的各个像素。在使用 Mask R-CNN 训练或推理时,数据集需要与segmentation
相关的字段,若没有将会出现以下报错:
报错
gt_mask = instance[‘mask‘] KeyError: ‘mask‘
,表明数据集中没有segmentation相关字段,参考该文章
并且官方给出的配置文件样例并不能直接修改,否则还可能出现类似以下报错:
报错
KeyError: must contain the key “type“, but got {\‘num_classes\‘: 2}\nNone
,参考该文章
以下为官方给出的样例
# 新配置继承了基本配置,并做了必要的修改
_base_ = ['../common/ms_3x_coco.py', '../_base_/models/faster-rcnn_r50_fpn.py']
# 我们还需要更改 head 中的 num_classes 以匹配数据集中的类别数
model = dict(
roi_head=dict(
type='StandardRoIHead',
bbox_roi_extractor=dict(
type='SingleRoIExtractor',
roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
out_channels=256,
featmap_strides=[4, 8, 16, 32]),
bbox_head=dict(
type='Shared2FCBBoxHead',
in_channels=256,
fc_out_channels=1024,
roi_feat_size=7,
num_classes=21,
bbox_coder=dict(
type='DeltaXYWHBBoxCoder',
target_means=[0., 0., 0., 0.],
target_stds=[0.1, 0.1, 0.2, 0.2]),
reg_class_agnostic=False,
loss_cls=dict(
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
loss_bbox=dict(type='L1Loss', loss_weight=1.0)))
)
# 修改数据集相关配置
data_root = 'data/cars/'
metainfo = {
'classes': ('car', 'suv', 'bus',' van', 'bicycle',
'electric', 'vehicle', 'motorbike/motorcycle',
'tricycle', 'take-out car', 'trolley,stroller',
'ambulance', 'fire truck', 'police car',' sweeper',
'garbage truck', 'pickup truck', 'light truck',
'heavy truck', 'crane', 'machinery vehicle' ),
'palette':
[(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230), (106, 0, 228),
(0, 60, 100), (0, 80, 100), (0, 0, 70), (0, 0, 192), (250, 170, 30),
(100, 170, 30), (220, 220, 0), (175, 116, 175), (250, 0, 30),
(165, 42, 42), (255, 77, 255), (0, 226, 252), (182, 182, 255),
(0, 82, 0), (120, 166, 157), (110, 76, 0) ]
}
train_dataloader = dict(
batch_size=1,
dataset=dict(
data_root=data_root,
metainfo=metainfo,
ann_file='labels/train/train_coco_format.json',
data_prefix=dict(img='images/train/')))
val_dataloader = dict(
dataset=dict(
data_root=data_root,
metainfo=metainfo,
ann_file='labels/val/val_coco_format.json',
data_prefix=dict(img='images/val/')))
test_dataloader = val_dataloader
# 修改评价指标相关配置
val_evaluator = dict(ann_file=data_root + 'labels/val/val_coco_format.json')
test_evaluator = val_evaluator
# 使用预训练的 Mask R-CNN 模型权重来做初始化,可以提高模型性能
load_from = '/home/miqi/mmdetection/checkpoints/faster_rcnn_r50_fpn_mstrain_3x_coco_20210524_110822-e10bd31c.pth'
我们需要根据自己的自定义数据集的特点准备一个配置文件来成功加载数据集
单纯的目标检测模型是指那些专注于识别图像中各类目标的存在及其位置,但并不涉及像素级语义分割(即生成每个目标的精确像素级掩码)的深度学习模型。这类模型的主要任务是确定目标的类别(如“汽车”、“行人”、“狗”等)以及它们在图像中的边界框(bounding box)坐标。以下是一些典型的单纯目标检测模型:
-
YOLO系列:
- YOLO (You Only Look Once):YOLO模型以其独特的单次前向传播预测所有目标位置和类别的设计而闻名,实现了实时目标检测。YOLOv1之后,该系列不断演进,包括YOLOv2、YOLOv3、YOLOv4、YOLOv5,以及最近的YOLOv8,持续提升检测精度和速度。
-
SSD (Single Shot MultiBox Detector):
- SSD也是一种端到端的目标检测模型,它在不同尺度的特征图上直接预测边界框和类别概率,从而实现多尺度目标检测。与YOLO类似,SSD追求高效检测,适用于实时应用场景。
-
Faster R-CNN:
- Faster R-CNN是对R-CNN系列的改进,引入了Region Proposal Network (RPN) 来生成候选区域,然后将这些候选区域送入后续的卷积网络进行分类和精确定位。尽管Faster R-CNN包含RPN步骤,但它在最终输出时仅关注边界框和类别,不生成像素级掩码。
-
RetinaNet:
- RetinaNet是一种基于Focal Loss的单阶段目标检测器,旨在解决目标检测中的类别不平衡问题。它同样只输出目标的类别标签和对应的边界框,而不进行实例分割。
-
EfficientDet:
- EfficientDet是Google提出的一种结合了EfficientNet作为主干网络的多尺度特征融合目标检测模型,它在保持较高检测精度的同时,显著提升了计算效率。如同其他单纯目标检测模型,EfficientDet仅输出目标类别和边界框。
-
CenterNet:
- CenterNet是一种基于关键点检测的框架,通过预测目标中心点、宽高以及类别来实现目标检测。这种设计简化了检测过程,且无需使用Anchor机制。
-
DETR (DEtection TRansformer):
- DETR引入了Transformer架构到目标检测任务中,通过直接预测一组固定数量的边界框及其关联的类别标签,摒弃了传统的滑窗或Anchor-based方法。虽然DETR在结构上有所不同,但它仍然是一个单纯的目标检测模型。
这些模型在设计上均专注于识别图像中的目标位置(边界框)和类别,而不涉及像素级别的语义分割,因此被称为“单纯的目标检测模型”。它们在诸如自动驾驶、视频监控、图像检索、智能安防等领域有着广泛应用。
配置文件的实际应用展示
参考官方给出的样例,我的配置文件如下,放在文件目录mmdetection/configs/cars/faster-rcnn_r50_fpn_ms-3x_coco_cars.py
中
# 新配置继承了基本配置,并做了必要的修改
_base_ = ['../common/ms_3x_coco.py', '../_base_/models/faster-rcnn_r50_fpn.py']
# 修改数据集相关配置
data_root = 'data/cars/'
metainfo = {
'classes': ('car', 'suv', 'bus',' van', 'bicycle',
'electric', 'vehicle', 'motorbike/motorcycle',
'tricycle', 'take-out car', 'trolley,stroller',
'ambulance', 'fire truck', 'police car',' sweeper',
'garbage truck', 'pickup truck', 'light truck',
'heavy truck', 'crane', 'machinery vehicle' ),
'palette':
[(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230), (106, 0, 228),
(0, 60, 100), (0, 80, 100), (0, 0, 70), (0, 0, 192), (250, 170, 30),
(100, 170, 30), (220, 220, 0), (175, 116, 175), (250, 0, 30),
(165, 42, 42), (255, 77, 255), (0, 226, 252), (182, 182, 255),
(0, 82, 0), (120, 166, 157), (110, 76, 0) ]
}
# 自定义数据集中这一步很关键,这决定你的自定义类别映射是否正确且预测类别和预期一致
train_dataloader = dict(dataset=dict(metainfo=metainfo))
test_dataloader = dict(dataset=dict(metainfo=metainfo))
# 使用预训练的 Faster R-CNN 模型权重来做初始化
load_from = '/home/miqi/mmdetection/checkpoints/faster_rcnn_r50_fpn_mstrain_3x_coco_20210524_110822-e10bd31c.pth'
在此代码中需要修改的地方有
1. 修改继承的配置文件:即选择自己需要的预训练模型配置文件
由此进入 此处我只是随便选了一个Faster-RCNN的预训练模型,没有任何讲究!
即可在蓝色下划线config中进入以下界面,复制其中的代码
# 新配置继承了基本配置,并做了必要的修改
_base_ = ['../common/ms_3x_coco.py', '../_base_/models/faster-rcnn_r50_fpn.py']
2. 更改 head 中的 num_classes 以匹配数据集中的类别数
深入配置文件去修改:在配置文件的_base_
中顺藤摸瓜找到mmdetection/configs/common/ms_3x_coco.py
。在该配置文件中关于类别数,我根据自定义的数据集的类别数设置为21,出现在bbox_head
。这意味着模型将针对21个类别进行目标检测任务。如果你需要调整类别数以适应不同的数据集,只需更改这这个地方的num_classes值即可。
在bbox_head
部分,num_classes=21
的设置。这意味着模型将针对21个不同的类别进行边界框(bbox)的分类。bbox_head
负责对候选区域(RoIs)进行分类和回归,这里的num_classes
参数定义了分类分支的输出维度,即模型将预测每个RoI属于这21个类别中的哪一类。
bbox_head=dict(
type='Shared2FCBBoxHead',
in_channels=256,
fc_out_channels=1024,
roi_feat_size=7,
num_classes=21, # 修改此处类别数
bbox_coder=dict(
type='DeltaXYWHBBoxCoder',
target_means=[0., 0., 0., 0.],
target_stds=[0.1, 0.1, 0.2, 0.2]),
reg_class_agnostic=False,
loss_cls=dict(
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
loss_bbox=dict(type='L1Loss', loss_weight=1.0))
如果目标检测包含实例分割任务,则还需要修改mask_head
中的num_classes
。在mask_head
部分,你也能看到num_classes=21
的设置。这意味着模型将为这21个类别中的每个实例生成对应的像素级分割掩模。mask_head
负责预测每个RoI内属于各个类别的像素,这里的num_classes
参数决定了掩模分支的输出通道数,即模型将为每个RoI生成21个二值掩模(每个掩模对应一个类别)。比如:
mask_head=dict(
type='FCNMaskHead',
num_convs=4,
in_channels=256,
conv_out_channels=256,
num_classes=21, # 修改类别数
loss_mask=dict(
type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))
3. 修改数据集相关配置
根据自定义数据集种类名称修改数据及相关配置,且需要将下面的’palette’
也进行适当的修改,这个是不同类别框的颜色,只要保持数量和类别数量一致即可
# 修改数据集相关配置
metainfo = {
'classes': ('car', 'suv', 'bus',' van', 'bicycle',
'electric', 'vehicle', 'motorbike/motorcycle',
'tricycle', 'take-out car', 'trolley,stroller',
'ambulance', 'fire truck', 'police car, sweeper',
'garbage truck', 'pickup truck', 'light truck',
'heavy truck', 'crane', 'machinery vehicle' ),
'palette':
[(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230), (106, 0, 228),
(0, 60, 100), (0, 80, 100), (0, 0, 70), (0, 0, 192), (250, 170, 30),
(100, 170, 30), (220, 220, 0), (175, 116, 175), (250, 0, 30),
(165, 42, 42), (255, 77, 255), (0, 226, 252), (182, 182, 255),
(0, 82, 0), (120, 166, 157), (110, 76, 0) ]
}
# 自定义数据集中这一步很关键,这决定你的自定义类别映射是否正确且预测类别和预期一致
train_dataloader = dict(dataset=dict(metainfo=metainfo))
test_dataloader = dict(dataset=dict(metainfo=metainfo))
注意,如果是yolo
系列模型,train_dataloader/test_datalader
请改为train_dataset/test_dataset
# 修改数据集相关配置
metainfo = {
'classes': ('cat', 'dog' ),
'palette':
[(220, 20, 60), (110, 76, 0) ]
}
# 自定义数据集中这一步很关键,这决定你的自定义类别映射是否正确且预测类别和预期一致
train_dataset = dict(dataset=dict(metainfo=metainfo))
test_dataset = dict(dataset=dict(metainfo=metainfo))
4. 修改_base_
的另一个配置文件mmdetection/configs/common/ms_3x_coco.py
中自定义数据集图片和标签的位置路径
# dataset settings
dataset_type = 'CocoDataset'
data_root = 'data/cars/' # 修改数据集根目录
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='RepeatDataset',
times=3,
dataset=dict(
type=dataset_type,
data_root=data_root,
ann_file='labels/train/train_coco_format.json', # 修改
data_prefix=dict(img='images/train/'), # 修改
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='labels/val/val_coco_format.json', # 修改
data_prefix=dict(img='images/val/'), # 修改
test_mode=True,
pipeline=test_pipeline,
backend_args=backend_args))
test_dataloader = val_dataloader
val_evaluator = dict(
type='CocoMetric',
ann_file=data_root + 'labels/val/val_coco_format.json', # 修改
metric='bbox',
backend_args=backend_args)
test_evaluator = val_evaluator
5. 预训练模型权重下载路径(根据实验需要进行修改)
# 使用预训练的 Mask R-CNN 模型权重来做初始化,可以提高模型性能
load_from = 'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth'
也可以直接放下载好的模型权重的文件路径
load_from = '/home/miqi/mmdetection/checkpoints/faster_rcnn_r50_fpn_mstrain_3x_coco_20210524_110822-e10bd31c.pth'
多GPU训练
万事俱备,用nvitop实时监控空闲的GPU之后,选择合适的GPU卡位进行多GPU训练
CUDA_VISIBLE_DEVICES=1,7,8\
./tools/dist_train.sh\
configs/cars/faster-rcnn_r50_fpn_ms-3x_coco_cars.py\
3
出现训练日志就代表模型预训练工作成功进行了
训练完成
以下是评估结果的解读:
Average Precision (AP)
:这是衡量模型在不同IoU阈值下的平均精度。可以看到,整体的AP值为0.132,这意味着模型在所有类别上的平均精度较低。AP@IoU=0.50:0.95
:这个指标表示在IoU阈值从0.50到0.95范围内计算的平均精度。整体的AP值为0.132,说明模型在高重叠度的边界框检测方面表现不佳。AP@IoU=0.50
:这个指标表示在IoU阈值为0.50时计算的平均精度。整体的AP值为0.200,这表明模型在中等重叠度的边界框检测方面稍微好一些。AP@IoU=0.75
:这个指标表示在IoU阈值为0.75时计算的平均精度。整体的AP值为0.146,这表明模型在高重叠度的边界框检测方面仍然较差。AP@small, medium, large
:这些指标分别表示在不同大小的物体上的平均精度。可以看到,对于小物体,AP值为0.055;对于中等大小的物体,AP值为0.176;对于大物体,AP值为0.172。这表明模型在检测小物体方面表现最差,而在检测中等和大型物体方面表现稍好一些。Average Recall (AR)
:这是衡量模型在不同IoU阈值和不同数量的最大检测数(maxDets)下的平均召回率。可以看到,在所有类别上的平均召回率为0.362,这表明模型在召回率方面表现一般。AR@small, medium, large
:这些指标分别表示在不同大小的物体上的平均召回率。可以看到,对于小物体,AR值为0.201;对于中等大小的物体,AR值为0.433;对于大物体,AR值为0.444。这表明模型在检测中等和大型物体方面的召回率较高,但在检测小物体方面的召回率较低。
因此,这次深度学习模型预训练的结果并不理想。模型在检测小物体方面表现较差,整体的平均精度和召回率都较低。这可能需要进一步优化模型架构、调整超参数或增加训练数据来提高性能。不过没关系,通过本次课程,好歹我们从头到到位掌握了自定义数据集格式的转换,以及使用自定义数据集进行训练预定义模型的操作。后续模型的更改优化和超参数等设置,以及如何提高模型平均精度等问题,会在下一节课在标准数据集中训练自定义模型中和大家一起学习进步。
训练好的模型权重保存位置记录在mmdetection/work_dirs/faster-rcnn_r50_fpn_ms-3x_coco_cars/last_checkpoint
,我们可以尝试进一步利用训练好的权重进行测试等操作!
利用训练好的模型权重预测单张图片
在mmdetection
目录下使用如下终端命令
python demo/image_demo.py demo/FAFU.jpg work_dirs/faster-rcnn_r50_fpn_ms-3x_coco_cars/faster-rcnn_r50_fpn_ms-3x_coco_cars.py --weights work_dirs/faster-rcnn_r50_fpn_ms-3x_coco_cars/iter_8316.pth --device cpu
- demo目录下预存好的需要预测的图片
- 模型权重匹配的总配置文件
- 本帖中通过自定义数据集训练的模型权重
- 选择在CPU上进行测试
在mmdetection/outputs/vis
下查看预测的图片
可见该模型预测单张图片表现很随机,只能捕捉到第二个车辆的类别。有可能是数据集本身问题,需要增大数据集的规模或是对数据集图片进行预处理。也有可能是预训练模型框架选择的问题,后续需要通过更换backbone和neck等model组件以及合理的超参数设置来不断优化模型训练权重。
下一步请继续观看如何通过自定义模型组件来提高模型在数据集上的训练精度!