1.研究背景与意义
项目参考AAAI Association for the Advancement of Artificial Intelligence
研究背景与意义
随着交通工具的普及和道路交通量的增加,交通安全问题日益凸显。尤其是车辆未礼让行人的情况频繁发生,给行人的生命安全带来了严重威胁。因此,开发一种高效准确的车辆未礼让行人检测系统具有重要的现实意义。
近年来,深度学习技术在计算机视觉领域取得了巨大的突破,特别是目标检测方面。目标检测是计算机视觉中的一个重要任务,其目的是在图像或视频中准确地定位和识别出感兴趣的目标。YOLO(You Only Look Once)是一种流行的目标检测算法,其具有快速、准确和端到端的特点,被广泛应用于各种实际场景中。
然而,传统的YOLO算法在车辆未礼让行人检测方面存在一些局限性。首先,YOLO算法在目标尺度变化和遮挡情况下的检测效果较差。其次,YOLO算法对于小目标的检测效果不佳,而行人往往属于小目标。此外,YOLO算法对于目标的边界框回归不够精确,容易出现误检和漏检的情况。
为了解决上述问题,本研究提出了一种改进的YOLOv8算法,并融合了Gold-YOLO的思想,用于车辆未礼让行人检测。改进的YOLOv8算法在YOLOv3的基础上进行了优化和改进,以提高检测的准确性和鲁棒性。同时,融合了Gold-YOLO的思想,引入了更加有效的目标检测策略,进一步提升了检测性能。
本研究的意义主要体现在以下几个方面:
-
提高交通安全:车辆未礼让行人是导致交通事故的主要原因之一。通过开发一种高效准确的车辆未礼让行人检测系统,可以及时发现并预警车辆未礼让行人的情况,有效降低交通事故的发生率,提高交通安全水平。
-
优化交通流量:车辆未礼让行人不仅会导致交通事故,还会造成交通拥堵和交通流量的不畅。通过车辆未礼让行人检测系统的应用,可以及时发现并处理车辆未礼让行人的情况,减少交通拥堵,优化交通流量,提高道路通行效率。
-
推动智能交通发展:智能交通是未来交通发展的重要方向,而车辆未礼让行人检测系统是智能交通系统的重要组成部分。通过开发一种高效准确的车辆未礼让行人检测系统,可以为智能交通系统的建设和应用提供技术支持,推动智能交通的发展。
-
拓展目标检测技术:本研究提出的改进的YOLOv8算法和融合Gold-YOLO的思想,对于目标检测技术的发展具有一定的参考价值。通过优化和改进传统的YOLO算法,提高了检测的准确性和鲁棒性,为目标检测技术的研究和应用提供了新的思路和方法。
综上所述,本研究旨在开发一种高效准确的车辆未礼让行人检测系统,以提高交通安全、优化交通流量、推动智能交通发展,并为目标检测技术的研究和应用提供新的思路和方法。该研究对于改善交通安全状况、提高交通效率和推动智能交通发展具有重要的现实意义和深远的影响。
2.图片演示
3.视频演示
【改进YOLOv8】融合Gold-YOLO的车辆未礼让行人检测系统_哔哩哔哩_bilibili
4.YOLOv8 概述
具体到 YOLOv8 算法,其核心特性和改动可以归结为如下:
提供了一个全新的 SOTA 模型,包括 P5 640 和 P6 1280 分辨率 的目标检测网络和基于 YOLACT 的实例分割模型。和 YOLOv5 一样,基于缩放系数也提供了 N/S/M/L/X 尺度的不同大小模型,用于满足不同场景需求
骨干网络和 Neck 部分可能参考了 YOLOv7 ELAN 设计思想,将 YOLOv5 的 C3 结构换成了梯度流更丰富的 C2f 结构,并对不同尺度模型调整了不同的通道数,属于对模型结构精心微调,不再是无脑一套参数应用所有模型,大幅提升了模型性能。不过这个 C2f 模块中存在 Split 等操作对特定硬件部署没有之前那么友好了
Head 部分相比 YOLOv5 改动较大,换成了目前主流的解耦头结构,将分类和检测头分离,同时也从 Anchor-Based 换成了 Anchor-Free
Loss 计算方面采用了 TaskAlignedAssigner 正样本分配策略,并引入了 Distribution Focal Loss
训练的数据增强部分引入了 YOLOX 中的最后 10 epoch 关闭 Mosiac 增强的操作,可以有效 地 提升精度
从上面可以看出,YOLOv8 主要参考了最近提出的诸如 YOLOX、YOLOv6、YOLOv7 和 PPYOLOE 等算法的相关设计,本身的创新点不多,偏向工程实践,主推的还是 ultralytics 这个框架本身 。
下面将按照模型结构设计、Loss 计算、训练数据增强、训练策略和模型推理过程共 5 个部分详细介绍 YOLOv8 目标检测的各种改进,实例分割部分暂时不进行描述。
模型结构设计
以上为基于 YOLOv8 官方代码所绘制的模型结构图。如果你喜欢这种模型结构图风格,可以查看 MMYOLO 里面对应算法 README 中的模型结构图,目前已经支持了 YOLOv5、YOLOv6、YOLOX、RTMDet 和 YOLOv8。MMYOLO 中重构的 YOLOv8 模型对应结构图如下所示:
详细地址为: https://github.com/open-mmlab/mmyolo/blob/dev/configs/yolov8/README.md
在暂时不考虑 Head 情况下,对比 YOLOv5 和 YOLOv8 的 yaml 配置文件可以发现改动较小。
左侧为 YOLOv5-s,右侧为 YOLOv8-s
骨干网络和 Neck 的具体变化为:
第一个卷积层的 kernel 从 6x6 变成了 3x3
所有的 C3 模块换成 C2f,结构如下所示,可以发现多了更多的跳层连接和额外的 Split 操作
去掉了 Neck 模块中的 2 个卷积连接层
Backbone 中 C2f 的block 数从 3-6-9-3 改成了 3-6-6-3
查看 N/S/M/L/X 等不同大小模型,可以发现 N/S 和 L/X 两组模型只是改了缩放系数,但是 S/M/L 等骨干网络的通道数设置不一样,没有遵循同一套缩放系数。如此设计的原因应该是同一套缩放系数下的通道设置不是最优设计,YOLOv7 网络设计时也没有遵循一套缩放系数作用于所有模型
Head 部分变化最大,从原先的耦合头变成了解耦头,并且从 YOLOv5 的 Anchor-Based 变成了 Anchor-Free。其结构如下所示:
5.核心代码讲解
5.2 predict.py
封装为类后的代码如下:
from ultralytics.engine.predictor import BasePredictor
from ultralytics.engine.results import Results
from ultralytics.utils import ops
class DetectionPredictor(BasePredictor):
def postprocess(self, preds, img, orig_imgs):
preds = ops.non_max_suppression(preds,
self.args.conf,
self.args.iou,
agnostic=self.args.agnostic_nms,
max_det=self.args.max_det,
classes=self.args.classes)
if not isinstance(orig_imgs, list):
orig_imgs = ops.convert_torch2numpy_batch(orig_imgs)
results = []
for i, pred in enumerate(preds):
orig_img = orig_imgs[i]
pred[:, :4] = ops.scale_boxes(img.shape[2:], pred[:, :4], orig_img.shape)
img_path = self.batch[0][i]
results.append(Results(orig_img, path=img_path, names=self.model.names, boxes=pred))
return results
这个程序文件是一个名为predict.py的文件,它是一个基于检测模型进行预测的类DetectionPredictor的定义。该类继承自BasePredictor类,并包含一个postprocess方法用于后处理预测结果。该文件使用了Ultralytics YOLO库,采用AGPL-3.0许可证。
在postprocess方法中,首先对预测结果进行非最大值抑制处理,根据一定的置信度和IOU阈值来筛选出最终的预测结果。然后,将预测结果的边界框坐标进行缩放,以适应原始图像的尺寸。最后,将处理后的预测结果封装成Results对象,并返回一个Results对象列表。
该文件还包含一个示例用法,通过创建DetectionPredictor对象,并传入相应的参数,可以进行预测操作。
总之,这个程序文件定义了一个用于基于检测模型进行预测的类DetectionPredictor,并提供了相应的预测方法和后处理方法。
5.3 train.py
# Ultralytics YOLO 🚀, AGPL-3.0 license
from copy import copy
import numpy as np
from ultralytics.data import build_dataloader, build_yolo_dataset
from ultralytics.engine.trainer import BaseTrainer
from ultralytics.models import yolo
from ultralytics.nn.tasks import DetectionModel
from ultralytics.utils import LOGGER, RANK
from ultralytics.utils.torch_utils import de_parallel, torch_distributed_zero_first
class DetectionTrainer(BaseTrainer):
def build_dataset(self, img_path, mode='train', batch=None):
gs = max(int(de_parallel(self.model).stride.max() if self.model else 0), 32)
return build_yolo_dataset(self.args, img_path, batch, self.data, mode=mode, rect=mode == 'val', stride=gs)
def get_dataloader(self, dataset_path, batch_size=16, rank=0, mode='train'):
assert mode in ['train', 'val']
with torch_distributed_zero_first(rank):
dataset = self.build_dataset(dataset_path, mode, batch_size)
shuffle = mode == 'train'
if getattr(dataset, 'rect', False) and shuffle:
LOGGER.warning("WARNING ⚠️ 'rect=True' is incompatible with DataLoader shuffle, setting shuffle=False")
shuffle = False
workers = 0
return build_dataloader(dataset, batch_size, workers, shuffle, rank)
def preprocess_batch(self, batch):
batch['img'] = batch['img'].to(self.device, non_blocking=True).float() / 255
return batch
def set_model_attributes(self):
self.model.nc = self.data['nc']
self.model.names = self.data['names']
self.model.args = self.args
def get_model(self, cfg=None, weights=None, verbose=True):
model = DetectionModel(cfg, nc=self.data['nc'], verbose=verbose and RANK == -1)
if weights:
model.load(weights)
return model
def get_validator(self):
self.loss_names = 'box_loss', 'cls_loss', 'dfl_loss'
return yolo.detect.DetectionValidator(self.test_loader, save_dir=self.save_dir, args=copy(self.args))
def label_loss_items(self, loss_items=None, prefix='train'):
keys = [f'{prefix}/{x}' for x in self.loss_names]
if loss_items is not None:
loss_items = [round(float(x), 5) for x in loss_items]
return dict(zip(keys, loss_items))
else:
return keys
def progress_string(self):
return ('\n' + '%11s' *
(4 + len(self.loss_names))) % ('Epoch', 'GPU_mem', *self.loss_names, 'Instances', 'Size')
def plot_training_samples(self, batch, ni):
plot_images(images=batch['img'],
batch_idx=batch['batch_idx'],
cls=batch['cls'].squeeze(-1),
bboxes=batch['bboxes'],
paths=batch['im_file'],
fname=self.save_dir / f'train_batch{ni}.jpg',
on_plot=self.on_plot)
def plot_metrics(self):
plot_results(file=self.csv, on_plot=self.on_plot)
def plot_training_labels(self):
boxes = np.concatenate([lb['bboxes'] for lb in self.train_loader.dataset.labels], 0)
cls = np.concatenate([lb['cls'] for lb in self.train_loader.dataset.labels], 0)
plot_labels(boxes, cls.squeeze(), names=self.data['names'], save_dir=self.save_dir, on_plot=self.on_plot)
if __name__ == '__main__':
args = dict(model='./yolov8l-goldyolo.yaml', data='coco8.yaml', epochs=100)
trainer = DetectionTrainer(overrides=args)
trainer.train()
这个程序文件是一个用于训练目标检测模型的程序。它使用了Ultralytics YOLO库,其中包含了构建数据集、构建数据加载器、模型训练等功能。
主要的类是DetectionTrainer
,它继承自BaseTrainer
类,用于基于目标检测模型进行训练。该类包含了构建数据集、构建数据加载器、预处理数据、设置模型属性、获取模型、获取验证器等方法。
在main
函数中,创建了一个DetectionTrainer
对象,并传入了一些参数,如模型文件路径、数据集文件路径和训练轮数。然后调用train
方法开始训练模型。
整个程序的功能是使用YOLO模型进行目标检测模型的训练,并提供了一些辅助方法用于数据处理、模型设置和结果可视化等。
5.5 backbone\convnextv2.py
import torch
import torch.nn as nn
import torch.nn.functional as F
from timm.models.layers import trunc_normal_, DropPath
class LayerNorm(nn.Module):
def __init__(self, normalized_shape, eps=1e-6, data_format="channels_last"):
super().__init__()
self.weight = nn.Parameter(torch.ones(normalized_shape))
self.bias = nn.Parameter(torch.zeros(normalized_shape))
self.eps = eps
self.data_format = data_format
if self.data_format not in ["channels_last", "channels_first"]:
raise NotImplementedError
self.normalized_shape = (normalized_shape, )
def forward(self, x):
if self.data_format == "channels_last":
return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
elif self.data_format == "channels_first":
u = x.mean(1, keepdim=True)
s = (x - u).pow(2).mean(1, keepdim=True)
x = (x - u) / torch.sqrt(s + self.eps)
x = self.weight[:, None, None] * x + self.bias[:, None, None]
return x
class GRN(nn.Module):
def __init__(self, dim):
super().__init__()
self.gamma = nn.Parameter(torch.zeros(1, 1, 1, dim))
self.beta = nn.Parameter(torch.zeros(1, 1, 1, dim))
def forward(self, x):
Gx = torch.norm(x, p=2, dim=(1,2), keepdim=True)
Nx = Gx / (Gx.mean(dim=-1, keepdim=True) + 1e-6)
return self.gamma * (x * Nx) + self.beta + x
class Block(nn.Module):
def __init__(self, dim, drop_path=0.):
super().__init__()
self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim)
self.norm = LayerNorm(dim, eps=1e-6)
self.pwconv1 = nn.Linear(dim, 4 * dim)
self.act = nn.GELU()
self.grn = GRN(4 * dim)
self.pwconv2 = nn.Linear(4 * dim, dim)
self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
def forward(self, x):
input = x
x = self.dwconv(x)
x = x.permute(0, 2, 3, 1)
x = self.norm(x)
x = self.pwconv1(x)
x = self.act(x)
x = self.grn(x)
x = self.pwconv2(x)
x = x.permute(0, 3, 1, 2)
x = input + self.drop_path(x)
return x
class ConvNeXtV2(nn.Module):
def __init__(self, in_chans=3, num_classes=1000,
depths=[3, 3, 9, 3], dims=[96, 192, 384, 768],
drop_path_rate=0., head_init_scale=1.
):
super().__init__()
self.depths = depths
self.downsample_layers = nn.ModuleList()
stem = nn.Sequential(
nn.Conv2d(in_chans, dims[0], kernel_size=4, stride=4),
LayerNorm(dims[0], eps=1e-6, data_format="channels_first")
)
self.downsample_layers.append(stem)
for i in range(3):
downsample_layer = nn.Sequential(
LayerNorm(dims[i], eps=1e-6, data_format="channels_first"),
nn.Conv2d(dims[i], dims[i+1], kernel_size=2, stride=2),
)
self.downsample_layers.append(downsample_layer)
self.stages = nn.ModuleList()
dp_rates=[x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))]
cur = 0
for i in range(4):
stage = nn.Sequential(
*[Block(dim=dims[i], drop_path=dp_rates[cur + j]) for j in range(depths[i])]
)
self.stages.append(stage)
cur += depths[i]
self.norm = nn.LayerNorm(dims[-1], eps=1e-6)
self.head = nn.Linear(dims[-1], num_classes)
self.apply(self._init_weights)
self.channel = [i.size(1) for i in self.forward(torch.randn(1, 3, 640, 640))]
def _init_weights(self, m):
if isinstance(m, (nn.Conv2d, nn.Linear)):
trunc_normal_(m.weight, std=.02)
nn.init.constant_(m.bias, 0)
def forward(self, x):
res = []
for i in range(4):
x = self.downsample_layers[i](x)
x = self.stages[i](x)
res.append(x)
return res
该程序文件是一个实现了ConvNeXt V2模型的PyTorch模块。ConvNeXt V2是一个用于图像分类任务的卷积神经网络模型。
该程序文件包含以下几个类和函数:
-
LayerNorm类:实现了支持两种数据格式(channels_last和channels_first)的LayerNorm层。
-
GRN类:实现了全局响应归一化(GRN)层。
-
Block类:实现了ConvNeXtV2模型的基本块。
-
ConvNeXtV2类:实现了ConvNeXt V2模型。
-
update_weight函数:用于更新模型的权重。
-
convnextv2_atto函数:创建一个ConvNeXtV2模型实例,使用atto配置。
-
convnextv2_femto函数:创建一个ConvNeXtV2模型实例,使用femto配置。
-
convnextv2_pico函数:创建一个ConvNeXtV2模型实例,使用pico配置。
-
convnextv2_nano函数:创建一个ConvNeXtV2模型实例,使用nano配置。
-
convnextv2_tiny函数:创建一个ConvNeXtV2模型实例,使用tiny配置。
-
convnextv2_base函数:创建一个ConvNeXtV2模型实例,使用base配置。
-
convnextv2_large函数:创建一个ConvNeXtV2模型实例,使用large配置。
-
convnextv2_huge函数:创建一个ConvNeXtV2模型实例,使用huge配置。
这些函数可以根据需要选择不同的模型配置,并加载预训练的权重。
5.6 backbone\CSwomTramsformer.py
class CSWinTransformer(nn.Module):
def __init__(self, img_size=224, patch_size=4, in_chans=3, num_classes=1000, embed_dim=96, depths=[2, 2, 6, 2], num_heads=[3, 6, 12, 24], mlp_ratio=4., qkv_bias=True, qk_scale=None, drop_rate=0., attn_drop_rate=0., drop_path_rate=0., norm_layer=nn.LayerNorm):
super().__init__()
self.num_classes = num_classes
self.depths = depths
self.embed_dim = embed_dim
self.patch_size = patch_size
self.num_features = self.embed_dim * 4
self.norm1 = norm_layer(self.num_features)
self.proj1 = nn.Conv2d(in_chans, self.num_features, kernel_size=patch_size, stride=patch_size)
self.norm2 = norm_layer(embed_dim)
self.blocks = nn.ModuleList([
CSWinBlock(
dim=self.embed_dim, reso=img_size // patch_size, num_heads=num_heads[i],
mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale,
drop=drop_rate, attn_drop=attn_drop_rate, drop_path=drop_path_rate,
norm_layer=norm_layer, last_stage=(i == len(depths) - 1))
for i in range(len(depths))])
self.norm3 = norm_layer(self.embed_dim)
self.avgpool = nn.AdaptiveAvgPool2d(1)
self.head = nn.Linear(self.embed_dim, num_classes)
def forward_features(self, x):
x = self.proj1(x)
x = self.norm1(x)
x = x.flatten(2).transpose(1, 2)
x = self.norm2(x)
for blk in self.blocks:
x = blk(x)
x = self.norm3(x)
return x
def forward(self, x):
x = self.forward_features(x)
x = self.avgpool(x).squeeze(3).squeeze(2)
x = self.head(x)
return x
该程序文件是一个实现了CSWin Transformer模型的PyTorch模块。CSWin Transformer是一种用于图像分类任务的Transformer模型,具有较低的计算和内存开销。该模型包含了CSWinBlock模块和Merge_Block模块,以及一些辅助函数。
CSWinBlock模块定义了CSWin Transformer的基本组件。它包含了一个多头自注意力机制(LePEAttention),一个多层感知机(Mlp),以及一些归一化和残差连接操作。CSWinBlock模块将输入的图像分割成多个窗口,并对每个窗口进行自注意力计算和多层感知机计算,然后将结果进行残差连接。CSWinBlock模块可以堆叠多次以构建更深的CSWin Transformer模型。
Merge_Block模块用于将CSWin Transformer模型输出的多个窗口特征图合并成一个整体特征图。它使用一个卷积层将窗口特征图的尺寸减半,并进行归一化操作。
除了CSWinBlock和Merge_Block模块外,该程序文件还定义了一些辅助函数,用于将图像转换为窗口表示和将窗口表示转换为图像表示。
该程序文件还定义了一些预定义的CSWin Transformer模型,如CSWin_tiny、CSWin_small、CSWin_base和CSWin_large。这些模型具有不同的参数配置和模型大小,可以根据任务需求选择合适的模型进行训练和推理。
6.系统整体结构
以下是每个文件的功能的整理:
文件 | 功能 |
---|---|
export.py | 导出模型的功能 |
predict.py | 进行预测的功能 |
train.py | 训练模型的功能 |
ui.py | 图形界面的功能 |
backbone\convnextv2.py | ConvNeXtV2模型的定义和配置 |
backbone\CSwomTramsformer.py | CSWin Transformer模型的定义和配置 |
backbone\EfficientFormerV2.py | EfficientFormerV2模型的定义和配置 |
backbone\efficientViT.py | EfficientViT模型的定义和配置 |
backbone\fasternet.py | FasterNet模型的定义和配置 |
backbone\lsknet.py | LSKNet模型的定义和配置 |
backbone\repvit.py | RepVIT模型的定义和配置 |
backbone\revcol.py | RevCol模型的定义和配置 |
backbone\SwinTransformer.py | Swin Transformer模型的定义和配置 |
backbone\VanillaNet.py | VanillaNet模型的定义和配置 |
extra_modules\afpn.py | AFPN模块的定义 |
extra_modules\attention.py | 注意力机制模块的定义 |
extra_modules\block.py | 基础块模块的定义 |
extra_modules\dynamic_snake_conv.py | 动态蛇卷积模块的定义 |
extra_modules\head.py | 模型头部模块的定义 |
extra_modules\kernel_warehouse.py | 卷积核仓库模块的定义 |
extra_modules\orepa.py | OREPA模块的定义 |
extra_modules\rep_block.py | RepBlock模块的定义 |
extra_modules\RFAConv.py | RFAConv模块的定义 |
models\common.py | 通用模型函数和工具函数 |
models\experimental.py | 实验性模型的定义 |
models\tf.py | TensorFlow模型的定义和配置 |
models\yolo.py | YOLO模型的定义和配置 |
ultralytics… | Ultralytics库的各个模块和功能 |
utils… | 工具函数和辅助功能的定义和实现 |
以上是对每个文件的功能的概括和整理。请注意,这只是根据文件名和路径进行的初步分析,具体的功能和实现可能需要查看代码来确认。
7.数据集的采集&标注和整理
图片的收集
首先,我们需要收集所需的图片。这可以通过不同的方式来实现,例如使用现有的公开数据集TrafficDatasets和coco。
labelImg是一个图形化的图像注释工具,支持VOC和YOLO格式。以下是使用labelImg将图片标注为VOC格式的步骤:
(1)下载并安装labelImg。
(2)打开labelImg并选择“Open Dir”来选择你的图片目录。
(3)为你的目标对象设置标签名称。
(4)在图片上绘制矩形框,选择对应的标签。
(5)保存标注信息,这将在图片目录下生成一个与图片同名的XML文件。
(6)重复此过程,直到所有的图片都标注完毕。
由于YOLO使用的是txt格式的标注,我们需要将VOC格式转换为YOLO格式。可以使用各种转换工具或脚本来实现。
下面是一个简单的方法是使用Python脚本,该脚本读取XML文件,然后将其转换为YOLO所需的txt格式。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
classes = [] # 初始化为空列表
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
def convert(size, box):
dw = 1. / size[0]
dh = 1. / size[1]
x = (box[0] + box[1]) / 2.0
y = (box[2] + box[3]) / 2.0
w = box[1] - box[0]
h = box[3] - box[2]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return (x, y, w, h)
def convert_annotation(image_id):
in_file = open('./label_xml\%s.xml' % (image_id), encoding='UTF-8')
out_file = open('./label_txt\%s.txt' % (image_id), 'w') # 生成txt格式文件
tree = ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
cls = obj.find('name').text
if cls not in classes:
classes.append(cls) # 如果类别不存在,添加到classes列表中
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
bb = convert((w, h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
xml_path = os.path.join(CURRENT_DIR, './label_xml/')
# xml list
img_xmls = os.listdir(xml_path)
for img_xml in img_xmls:
label_name = img_xml.split('.')[0]
print(label_name)
convert_annotation(label_name)
print("Classes:") # 打印最终的classes列表
print(classes) # 打印最终的classes列表
整理数据文件夹结构
我们需要将数据集整理为以下结构:
-----data
|-----train
| |-----images
| |-----labels
|
|-----valid
| |-----images
| |-----labels
|
|-----test
|-----images
|-----labels
确保以下几点:
所有的训练图片都位于data/train/images目录下,相应的标注文件位于data/train/labels目录下。
所有的验证图片都位于data/valid/images目录下,相应的标注文件位于data/valid/labels目录下。
所有的测试图片都位于data/test/images目录下,相应的标注文件位于data/test/labels目录下。
这样的结构使得数据的管理和模型的训练、验证和测试变得非常方便。
模型训练
Epoch gpu_mem box obj cls labels img_size
1/200 20.8G 0.01576 0.01955 0.007536 22 1280: 100%|██████████| 849/849 [14:42<00:00, 1.04s/it]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|██████████| 213/213 [01:14<00:00, 2.87it/s]
all 3395 17314 0.994 0.957 0.0957 0.0843
Epoch gpu_mem box obj cls labels img_size
2/200 20.8G 0.01578 0.01923 0.007006 22 1280: 100%|██████████| 849/849 [14:44<00:00, 1.04s/it]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|██████████| 213/213 [01:12<00:00, 2.95it/s]
all 3395 17314 0.996 0.956 0.0957 0.0845
Epoch gpu_mem box obj cls labels img_size
3/200 20.8G 0.01561 0.0191 0.006895 27 1280: 100%|██████████| 849/849 [10:56<00:00, 1.29it/s]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|███████ | 187/213 [00:52<00:00, 4.04it/s]
all 3395 17314 0.996 0.957 0.0957 0.0845
8.Gold-YOLO
Preliminaries
YOLO系列的中间层结构采用了传统的FPN结构,其中包含多个分支用于多尺度特征融合。然而,它只充分融合来自相邻级别的特征,对于其他层次的信息只能间接地进行“递归”获取。
传统的FPN结构在信息传输过程中存在丢失大量信息的问题。这是因为层之间的信息交互仅限于中间层选择的信息,未被选择的信息在传输过程中被丢弃。这种情况导致某个Level的信息只能充分辅助相邻层,而对其他全局层的帮助较弱。因此,整体上信息融合的有效性可能受到限制。
为了避免在传输过程中丢失信息,本文采用了一种新颖的“聚集和分发”机制(GD),放弃了原始的递归方法。该机制使用一个统一的模块来收集和融合所有Level的信息,并将其分发到不同的Level。通过这种方式,作者不仅避免了传统FPN结构固有的信息丢失问题,还增强了中间层的部分信息融合能力,而且并没有显著增加延迟。
低阶聚合和分发分支 Low-stage gather-and-distribute branch
从主干网络中选择输出的B2、B3、B4、B5特征进行融合,以获取保留小目标信息的高分辨率特征。
高阶聚合和分发分支 High-stage gather-and-distribute branch
高级全局特征对齐模块(High-GD)将由低级全局特征对齐模块(Low-GD)生成的特征{P3, P4, P5}进行融合。
Transformer融合模块由多个堆叠的transformer组成,transformer块的数量为L。每个transformer块包括一个多头注意力块、一个前馈网络(FFN)和残差连接。采用与LeViT相同的设置来配置多头注意力块,使用16个通道作为键K和查询Q的头维度,32个通道作为值V的头维度。为了加速推理过程,将层归一化操作替换为批归一化,并将所有的GELU激活函数替换为ReLU。为了增强变换器块的局部连接,在两个1x1卷积层之间添加了一个深度卷积层。同时,将FFN的扩展因子设置为2,以在速度和计算成本之间取得平衡。
信息注入模块(Information injection module): 高级全局特征对齐模块(High-GD)中的信息注入模块与低级全局特征对齐模块(Low-GD)中的相同。在高级阶段,局部特征(Flocal)等于Pi,因此公式如下所示:
增强的跨层信息流动 Enhanced cross-layer information flow
为了进一步提升性能,从YOLOv6 中的PAFPN模块中得到启发,引入了Inject-LAF模块。该模块是注入模块的增强版,包括了一个轻量级相邻层融合(LAF)模块,该模块被添加到注入模块的输入位置。为了在速度和准确性之间取得平衡,设计了两个LAF模型:LAF低级模型和LAF高级模型,分别用于低级注入(合并相邻两层的特征)和高级注入(合并相邻一层的特征)。它们的结构如图5(b)所示。为了确保来自不同层级的特征图与目标大小对齐,在实现中的两个LAF模型仅使用了三个操作符:双线性插值(上采样过小的特征)、平均池化(下采样过大的特征)和1x1卷积(调整与目标通道不同的特征)。模型中的LAF模块与信息注入模块的结合有效地平衡了准确性和速度之间的关系。通过使用简化的操作,能够增加不同层级之间的信息流路径数量,从而提高性能而不显著增加延迟。
9.系统整合
下图完整源码&数据集&环境部署视频教程&自定义UI界面
参考博客《【改进YOLOv8】融合Gold-YOLO的车辆未礼让行人检测系统》
10.参考文献
[1]周晓彦,王珂,李凌燕.基于深度学习的目标检测算法综述[J].电子测量技术.2017,(11).DOI:10.3969/j.issn.1002-7300.2017.11.020 .
[2]李旭冬,叶茂,李涛.基于卷积神经网络的目标检测研究综述[J].计算机应用研究.2017,(10).DOI:10.3969/j.issn.1001-3695.2017.10.001 .
[3]高仕博,程咏梅,肖利平,等.面向目标检测的稀疏表示方法研究进展[J].电子学报.2015,(2).DOI:10.3969/j.issn.0372-2112.2015.02.018 .
[4]黄凯奇,陈晓棠,康运锋,等.智能视频监控技术综述[J].计算机学报.2015,(6).DOI:10.11897/SP.J.1016.2015.01093 .
[5]张娟,毛晓波,陈铁军.运动目标跟踪算法研究综述[J].计算机应用研究.2009,(12).DOI:10.3969/j.issn.1001-3695.2009.12.002 .
[6]侯志强,韩崇昭.视觉跟踪技术综述[J].自动化学报.2006,(4).
[7]万缨,韩毅,卢汉清.运动目标检测算法的探讨[J].计算机仿真.2006,(10).DOI:10.3969/j.issn.1006-9348.2006.10.056 .
[8]尹宏鹏,陈波,柴毅,等.基于视觉的目标检测与跟踪综述[J].自动化学报.2016,(10).DOI:10.16383/j.aas.2016.c150823 .
[9]Shelhamer, Evan,Long, Jonathan,Darrell, Trevor.Fully Convolutional Networks for Semantic Segmentation[J].IEEE Transactions on Pattern Analysis & Machine Intelligence.2017,39(6).640-651.
[10]Everingham, M,Winn, J,Van Gool, L,等.The Pascal Visual Object Classes (VOC) Challenge[J].International Journal of Computer Vision.2010,88(2).