BDD100K是伯克利发布的开放式驾驶视频数据集,其中包含10万个视频和10个任务(因为把交通灯的颜色也区分了出来,实际上是13类分类任务),目的是方便评估自动驾驶图像识别算法的的进展。该数据集具有地理,环境和天气多样性,从而能让模型能够识别多种场景,具备更多的泛化能力。
由于硬件限制,我只里训练其中的图片数据,数据集按Train、Valid、Test三个部分,比例可以按照7:2:1分配,共近10张图片。
Bdd100k的标签是由Scalabel生成的JSON格式,虽然伯克利提供Bdd100k数据集的标签查看及标签格式转化工具。由于没有直接从bdd100k转换成YOLO的工具,因此我们首先得使用将bdd100k的标签转换为coco格式,然后再将coco格式转换为yolo格式。
YOLO家族独特的标签数据集格式为:每个图片文件.jpg,都有同一命名的标签文件.txt。标签文件中每个对象独占一行,格式为
<object-class> <x> <y> <width> <height>。
其中:
表示对象的类别序号:从0 到 (classes-1)。
参照图片宽度和高度的相对比例(浮点数值),从0.0到1.0。(1)bdd 转化为coco格式
我的目的是识别包括不同颜色交通灯在内的所有交通对象,因此我们需要对原版的bdd2coco.py进行一些修改,以获取交通灯颜色并产生新的类别。这是修改完的核心代码:
for label in i['labels']:
annotation = dict()
category=label['category']
if (category == "traffic light"):
color = label['attributes']['trafficLightColor']
category = "tl_" + color
if category in id_dict.keys():
empty_image = False
annotation["iscrowd"] = 0
annotation["image_id"] = image['id']
x1 = label['box2d']['x1']
y1 = label['box2d']['y1']
x2 = label['box2d']['x2']
y2 = label['box2d']['y2']
annotation['bbox'] = [x1, y1, x2-x1, y2-y1]
annotation['area'] = float((x2 - x1) * (y2 - y1))
annotation['category_id'] = id_dict[category]
annotation['ignore'] = 0
annotation['id'] = label['id']
annotation['segmentation'] = [[x1, y1, x1, y2, x2, y2, x2, y1]]
annotations.append(annotation)
在完成bdd100k格式到yolo格式的转换后,会获得两个文件:
(2)Coco 转化 yolo格式
在完成先前的转换之后,我们需要将训练集和验证集的coco格式标签转换为yolo格式。注意需要分别指定训练集和验证集图片位置,对应的coco标签文件位置,及生成yolo标签的目标位置:
config_train ={
"datasets": "COCO",
"img_path": "bdd100k_images/bdd100k/images/100k/train",
"label": "labels/bdd100k_labels_images_det_coco_train.json",
"img_type": ".jpg",
"manipast_path": "./",
"output_path": "labels/trains/",
"cls_list": "bdd100k.names",
}
config_valid ={
"datasets": "COCO",
"img_path": "bdd100k_images/bdd100k/images/100k/val",
"label": "labels/bdd100k_labels_images_det_coco_val.json",
"img_type": ".jpg",
"manipast_path": "./",
"output_path": "labels/valids/",
"cls_list": "bdd100k.names",
}
除此之外,我们还得将所有的类别写入bdd100k.names文件:
person
rider
car
bus
truck
bike
motor
tl_green
tl_red
tl_yellow
tl_none
traffic sign
train
运行Bdd_preprocessing中的完整代码可以完成Bdd100k格式标签到YOLO标签格式的转换。
Yolov5 需要的Pytorch版本>=1.5, Python版本3.7, CUDA版本10.2,可以把所需要的库放入requirement.txt文件,通过在shell中运行pip install -r requirement.txt
命令,可以自动安装所有依赖项。
Cython
numpy==1.17
opencv-python
torch>=1.5.1
matplotlib
pillow
tensorboard
PyYAML>=5.3
torchvision
scipy
tqdm
Yolov5的默认YAML文件coco.yaml 中是coco数据集所有的类对象名称和类数量(80)。由于我是基于bdd100k数据集来训练检测少量特定交通物体的模型,不需要训练检测80类网络的模型,所以得重新创建一个uc_data.yaml文件来描述bdd100k数据集的数据特性。由于模型的输出不是coco数据集的80个类,而是13类,因此修改此处的输出类别数量为13。
train: bdd100k/images/train
val: bdd100k/images/valid
test: bdd100k/images/test
nc: 13
names: ['person','rider','car','bus','truck','bike','motor','tl_green','tl_red','tl_yellow','tl_none','t_sign','train']
Yolov5通过models文件家中的cfg文件*.yaml
来调整训练模型的结构。这里需要修改模型的对象预测层输出类别数量为13,也可以直接修改YAML文件下各个组件的细节(如数字),来重新定义自己的模型架构。
# YOLO V5s
# parameters
nc: 13 # number of classes
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.50 # layer channel multiple
# anchors
anchors:
- [116,90, 156,198, 373,326] # P5/32
- [30,61, 62,45, 59,119] # P4/16
- [10,13, 16,30, 33,23] # P3/8
# YOLOv5 backbone
backbone:
# [from, number, module, args]
[[-1, 1, Focus, [64, 3]], # 0-P1/2
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
[-1, 3, BottleneckCSP, [128]],
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
[-1, 9, BottleneckCSP, [256]],
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
[-1, 9, BottleneckCSP, [512]],
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
[-1, 1, SPP, [1024, [5, 9, 13]]],
]
# YOLOv5 head
head:
[[-1, 3, BottleneckCSP, [1024, False]], # 9
[-1, 1, Conv, [512, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 6], 1, Concat, [1]], # cat backbone P4
[-1, 3, BottleneckCSP, [512, False]], # 13
[-1, 1, Conv, [256, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 4], 1, Concat, [1]], # cat backbone P3
[-1, 3, BottleneckCSP, [256, False]],
[-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1]], # 18 (P3/8-small)
[-2, 1, Conv, [256, 3, 2]],
[[-1, 14], 1, Concat, [1]], # cat head P4
[-1, 3, BottleneckCSP, [512, False]],
[-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1]], # 22 (P4/16-medium)
[-2, 1, Conv, [512, 3, 2]],
[[-1, 10], 1, Concat, [1]], # cat head P5
[-1, 3, BottleneckCSP, [1024, False]],
[-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1]], # 26 (P5/32-large)
[[], 1, Detect, [nc, anchors]], # Detect(P5, P4, P3)
]
迁移学习(Transfer learning) 顾名思义就是就是把已学训练好的模型参数迁移到新的模型来帮助新模型训练。下图为针对不同场景的迁移学习:
现在的情况,符合上述第三种,通常只需要使用预训练的权重初始化网络,然后直接从头开始训练,从而更快的使模型有效收敛。但是由于之前没有人公开过对于Bdd100k数据集使用Yolov5预训练权重和不使用其训练权重的对比,并且 COCO数据集80类,而Bdd100k数据集13类,两者大部分类是不相似的。于是我分别使用YOLO V5s预训练权重和不使用其训练权重来训练基于Bdd100k数据集的对象识别网络,并对比它们的效果。
由于Bdd100k的数据集比较大,训练时间较长,因此只使用Yolov5s来训练基于Bdd100k自动驾驶数据集的对象检测深度网络。
现在,就可以训练了,下面是训练的一些重要参数:
#训练参数(基于bdd100k数据集):
img: 图像尺寸640
batch-size:32
epochs:300
data:bdd100k数据集路径及分类数配置uc_data.yaml
cfg:模型文件配置custom_yolov5s.yaml,至少修改类别数量
weights:预训练权重
cache-images: 将预处理后的训练数据全部存储在RAM中,能够加快训练速度
optimizer:SGD
lr0:0.01
Momentum: 0.937
weight_decay::0.0005
iou-thres:NMS的IOU阈值,设置为0.5
device: GPU
我将从如目标检测最常见几种指标Precision、Recal、F1 score、mAP评估模型的性能
其中,TP、TN、FP、FN表示如下:
True positives (TP): 正样本被正确识别为正样本,
True negatives(TN): 负样本被正确识别为负样本
False positives(FP): 假的正样本,即负样本被错误识别为正样本
False negatives(FN): 假的负样本,即正样本被错误识别为负样本
Precision、Recal、F1 score、mAP、[email protected]含义如下:
这几部分Loss结果如下:
val loss:
从上面的结果中,可以看出(蓝色是迁移学习,橙色没采用迁移学习):
至此,我基本上了解了Yolov5的网络结构,并且基于Bdd100k数据集训练了属于自己的自动驾驶对象检测模型。我认为Yolov5是个非常棒的开源对象检测网络,可能多人考虑到Yolov5的创新性不足,对Yolov5而议论纷纷,我觉得不管它现阶段配不配的上V5的名称,但既然称之为Yolov5,定有很多非常不错的地方值得我们学习。不可否认,它真的是一个快速而且强大的目标检测器。
我把BDD100K数据集地址上传到网盘上了,地址如下:
链接:https://pan.baidu.com/s/158XBxqDXIIAWkbHbja4cVQ
提取码:738o
想要本文实现代码的同学,可联系博主本人!