项目参考AAAI Association for the Advancement of Artificial Intelligence
研究背景与意义
随着城市化进程的加速和人口规模的不断增长,地铁作为一种高效、便捷的交通工具在现代城市中扮演着重要的角色。然而,地铁系统的正常运行往往受到各种因素的影响,例如设备故障、人员拥堵、安全隐患等。因此,开发一种能够实时监测地铁屏幕异常状态的预警系统对于提高地铁运行的安全性和效率具有重要意义。
目前,随着计算机视觉和深度学习技术的快速发展,人们已经提出了许多基于图像和视频分析的异常检测方法。然而,传统的方法往往需要大量的人工标注数据和复杂的特征工程,且在复杂场景下的准确率和鲁棒性有限。因此,如何利用深度学习技术来提高地铁屏幕异常状态预警系统的准确性和实时性成为一个研究热点。
近年来,YOLOv5作为一种高效的目标检测算法在计算机视觉领域取得了显著的成果。然而,由于YOLOv5网络结构的特殊性,其在处理小目标和复杂背景下的性能仍然有待提高。因此,本研究旨在融合上下文扩展和特征细化网络CAM,改进YOLOv5算法,以提高地铁屏幕异常状态预警系统的准确性和实时性。
具体而言,本研究将通过引入上下文扩展和特征细化网络CAM来改进YOLOv5算法。上下文扩展可以帮助模型更好地理解图像中的语义信息,从而提高目标检测的准确性。特征细化网络CAM可以帮助模型更好地捕捉目标的细节特征,从而提高目标检测的鲁棒性。通过融合这两种改进方法,我们可以期望在地铁屏幕异常状态预警系统中获得更好的性能。
本研究的意义主要体现在以下几个方面:
首先,通过改进YOLOv5算法,我们可以提高地铁屏幕异常状态预警系统的准确性和实时性。这将有助于提高地铁运行的安全性和效率,减少因异常状态而引发的事故和延误。
其次,本研究的方法可以为其他类似的目标检测任务提供借鉴和参考。上下文扩展和特征细化网络CAM是通用的改进方法,可以应用于其他领域的目标检测任务,如交通监控、工业安全等。
最后,本研究的成果可以为深度学习技术在实际应用中的推广和应用提供参考。通过在地铁屏幕异常状态预警系统中的应用,我们可以验证和优化改进方法的有效性和实用性,为深度学习技术在其他领域的应用提供经验和指导。
综上所述,本研究旨在融合上下文扩展和特征细化网络CAM,改进YOLOv5算法,以提高地铁屏幕异常状态预警系统的准确性和实时性。通过该研究,我们可以为地铁运行的安全性和效率提供支持,为深度学习技术在实际应用中的推广和应用提供参考。
融合上下文扩展和特征细化网络CAM改进YOLOv5的地铁屏幕异常状态预警系统_哔哩哔哩_bilibili
首先,我们需要收集所需的图片。这可以通过不同的方式来实现,例如使用现有的公开数据集ScreenDatasets。
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 [email protected] [email protected]:.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 [email protected] [email protected]:.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 [email protected] [email protected]:.95: 100%|███████ | 187/213 [00:52<00:00, 4.04it/s]
all 3395 17314 0.996 0.957 0.0957 0.0845
下面是对代码的逐文件分析:
首先是class CAM(nn.Module)
,这是一个继承自nn.Module
的类。该类有一个构造函数__init__
,接受一个参数inc
和一个可选参数fusion
,并进行一些初始化操作。其中,fusion
的取值只能是'weight'
、'adaptive'
或'concat'
。该类还定义了一些成员变量,包括conv1
、conv2
、conv3
、fusion_1
、fusion_2
、fusion_3
和fusion_4
。最后,该类还定义了一个forward
方法,接受一个参数x
,并根据fusion
的取值进行不同的操作,返回结果。
接下来是elif m is CAM
,这是一个条件语句,判断m
是否是CAM
类的实例。如果是,执行相应的操作。
最后是一个YAML文件,定义了一些参数和模型结构。其中,nc
表示类别的数量,depth_multiple
表示模型深度的倍数,width_multiple
表示层通道的倍数,anchors
表示锚框的尺寸。
以上是代码中最核心的部分,可以将其封装为一个类。
CAM.py是一个Python程序文件,包含了一个名为CAM的类。CAM类继承自nn.Module类,用于定义一个特征融合模块。
CAM类的构造函数__init__接受两个参数:inc和fusion。其中,inc表示输入通道数,fusion表示特征融合方式。fusion参数只能取’weight’、'adaptive’或’concat’三个值。
CAM类定义了一系列的卷积层和融合层。其中,conv1、conv2和conv3分别表示三个卷积层,fusion_1、fusion_2和fusion_3分别表示三个融合层。如果fusion参数为’adaptive’,则还会定义一个名为fusion_4的卷积层。
CAM类还定义了前向传播函数forward。在forward函数中,输入x经过三个卷积层得到x1、x2和x3。根据fusion参数的不同,选择不同的特征融合方式。如果fusion为’weight’,则将x1、x2和x3经过对应的融合层后相加;如果fusion为’adaptive’,则将x1、x2和x3经过对应的融合层后进行softmax操作得到权重,然后将x1、x2和x3分别乘以对应的权重后相加;如果fusion为’concat’,则将x1、x2和x3经过对应的融合层后在通道维度上进行拼接。
在文件的最后部分,还有一个elif语句,判断如果变量m等于CAM,则将变量ch[f]赋值给变量c1,如果args[0]等于’concat’,则将变量ch[f]乘以3赋值给变量c2。
文件的最后部分是一个YAML格式的配置文件,用于配置YOLOv5模型的相关参数。其中包括类别数、模型深度倍数、模型通道倍数以及锚点等信息。还定义了YOLOv5模型的backbone和head部分的结构和参数配置。
class WIoU_Scale:
''' monotonous: {
None: origin v1
True: monotonic FM v2
False: non-monotonic FM v3
}
momentum: The momentum of running mean'''
iou_mean = 1.
monotonous = False
_momentum = 1 - 0.5 ** (1 / 7000)
_is_train = True
def __init__(self, iou):
self.iou = iou
self._update(self)
@classmethod
def _update(cls, self):
if cls._is_train: cls.iou_mean = (1 - cls._momentum) * cls.iou_mean + \
cls._momentum * self.iou.detach().mean().item()
@classmethod
def _scaled_loss(cls, self, gamma=1.9, delta=3):
if isinstance(self.monotonous, bool):
if self.monotonous:
return (self.iou.detach() / self.iou_mean).sqrt()
else:
beta = self.iou.detach() / self.iou_mean
alpha = delta * torch.pow(gamma, beta - delta)
return beta / alpha
return 1
def bbox_iou(box1, box2, xywh=True, GIoU=False, DIoU=False, CIoU=False, SIoU=False, EIoU=False, WIoU=False, Focal=False, alpha=1, gamma=0.5, scale=False, eps=1e-7):
# Returns Intersection over Union (IoU) of box1(1,4) to box2(n,4)
# Get the coordinates of bounding boxes
if xywh: # transform from xywh to xyxy
(x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)
w1_, h1_, w2_, h2_ = w1 / 2, h1 / 2, w2 / 2, h2 / 2
b1_x1, b1_x2, b1_y1, b1_y2 = x1 - w1_, x1 + w1_, y1 - h1_, y1 + h1_
b2_x1, b2_x2, b2_y1, b2_y2 = x2 - w2_, x2 + w2_, y2 - h2_, y2 + h2_
else: # x1, y1, x2, y2 = box1
b1_x1, b1_y1, b1_x2, b1_y2 = box1.chunk(4, -1)
b2_x1, b2_y1, b2_x2, b2_y2 = box2.chunk(4, -1)
w1, h1 = b1_x2 - b1_x1, (b1_y2 - b1_y1).clamp(eps)
w2, h2 = b2_x2 - b2_x1, (b2_y2 - b2_y1).clamp(eps)
# Intersection area
inter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp(0) * \
(b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)).clamp(0)
# Union Area
union = w1 * h1 + w2 * h2 - inter + eps
if scale:
self = WIoU_Scale(1 - (inter / union))
# IoU
# iou = inter / union # ori iou
iou = torch.pow(inter/(union + eps), alpha) # alpha iou
if CIoU or DIoU or GIoU or EIoU or SIoU or WIoU:
cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1) # convex (smallest enclosing box) width
ch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1) # convex height
if CIoU or DIoU or EIoU or SIoU or WIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1
c2 = (cw ** 2 + ch ** 2) ** alpha + eps # convex diagonal squared
rho2 = (((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4) ** alpha # center dist ** 2
if CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47
v = (4 / math.pi ** 2) * (torch.atan(w2 / h2) - torch.atan(w1 / h1)).pow(2)
with torch.no_grad():
alpha_ciou = v / (v - iou + (1 + eps))
if Focal:
return iou - (rho2 / c2 + torch.pow(v * alpha_ciou + eps, alpha)), torch.pow(inter/(union + eps), gamma) # Focal_CIoU
else:
return iou - (rho2 / c2 + torch.pow(v * alpha_ciou + eps, alpha)) # CIoU
elif EIoU:
rho_w2 = ((b2_x2 - b2_x1) - (b1_x2 - b1_x1)) ** 2
rho_h2 = ((b2_y2 - b2_y1) - (b1_y2 - b1_y1)) ** 2
cw2 = torch.pow(cw ** 2 + eps, alpha)
ch2 = torch.pow(ch ** 2 + eps, alpha)
if Focal:
return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2), torch.pow(inter/(union + eps), gamma) # Focal_EIou
else:
return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2) # EIou
elif SIoU:
# SIoU Loss https://arxiv.org/pdf/2205.12740.pdf
s_cw = (b2_x1 + b2_x2 - b1_x1 - b1_x2) * 0.5 + eps
s_ch = (b2_y1 + b2_y2 - b1_y1 - b1_y2) * 0.5 + eps
sigma = torch.pow(s_cw ** 2 + s_ch ** 2, 0.5)
sin_alpha_1 = torch.abs(s_cw) / sigma
sin_alpha_2 = torch.abs(s_ch) / sigma
threshold = pow(2, 0.5) / 2
sin_alpha = torch.where(sin_alpha_1 > threshold, sin
这个程序文件名为iou.py,主要包含了一个名为WIoU_Scale的类和一个名为bbox_iou的函数。
WIoU_Scale类有以下属性和方法:
bbox_iou函数有以下参数和功能:
最后,程序中还包含了一些yolov8和yolov5的代码,用于计算IoU损失。
class YOLOv5Trainer:
def __init__(self, hyp, opt, device, callbacks):
self.hyp = hyp
self.opt = opt
self.device = device
self.callbacks = callbacks
self.save_dir = Path(opt.save_dir)
self.epochs = opt.epochs
self.batch_size = opt.batch_size
self.weights = opt.weights
self.single_cls = opt.single_cls
self.evolve = opt.evolve
self.data = opt.data
self.cfg = opt.cfg
self.resume = opt.resume
self.noval = opt.noval
self.nosave = opt.nosave
self.workers = opt.workers
self.freeze = opt.freeze
self.w = self.save_dir / 'weights'
self.last = self.w / 'last.pt'
self.best = self.w / 'best.pt'
self.plots = not self.evolve and not opt.noplots
self.cuda = self.device.type != 'cpu'
self.data_dict = None
self.loggers = None
self.train_path = None
self.val_path = None
self.nc = None
self.names = None
self.is_coco = None
self.model = None
self.amp = None
self.freeze = None
self.optimizer = None
self.scheduler = None
self.ema = None
self.best_fitness = None
self.start_epoch = None
def train(self):
self.callbacks.run('on_pretrain_routine_start')
(self.w.parent if self.evolve else self.w).mkdir(parents=True, exist_ok=True)
if isinstance(self.hyp, str):
with open(self.hyp, errors='ignore') as f:
self.hyp = yaml.safe_load(f)
LOGGER.info(colorstr('hyperparameters: ') + ', '.join(f'{k}={v}' for k, v in self.hyp.items()))
self.opt.hyp = self.hyp.copy()
if not self.evolve:
yaml_save(self.save_dir / 'hyp.yaml', self.hyp)
yaml_save(self.save_dir / 'opt.yaml', vars(self.opt))
self.data_dict = self.check_dataset(self.data)
self.train_path, self.val_path = self.data_dict['train'], self.data_dict['val']
self.nc = 1 if self.single_cls else int(self.data_dict['nc'])
self.names = {0: 'item'} if self.single_cls and len(self.data_dict['names']) != 1 else self.data_dict['names']
self.is_coco = isinstance(self.val_path, str) and self.val_path.endswith('coco/val2017.txt')
self.check_suffix(self.weights, '.pt')
self.pretrained = self.weights.endswith('.pt')
if self.pretrained:
self.weights = self.attempt_download(self.weights)
ckpt = torch.load(self.weights, map_location='cpu')
self.model = Model(self.cfg or ckpt['model'].yaml, ch=3, nc=self.nc, anchors=self.hyp.get('anchors')).to(self.device)
exclude = ['anchor'] if (self.cfg or self.hyp.get('anchors')) and not self.resume else []
csd = ckpt['model'].float().state_dict()
csd = intersect_dicts(csd, self.model.state_dict(), exclude=exclude)
self.model.load_state_dict(csd, strict=False)
LOGGER.info(f'Transferred {len(csd)}/{len(self.model.state_dict())} items from {self.weights}')
else:
self.model = Model(self.cfg, ch=3, nc=self.nc, anchors=self.hyp.get('anchors')).to(self.device)
self.amp = self.check_amp(self.model)
self.freeze = [f'model.{x}.' for x in (self.freeze if len(self.freeze) > 1 else range(self.freeze[0]))]
for k, v in self.model.named_parameters():
v.requires_grad = True
if any(x in k for x in self.freeze):
LOGGER.info(f'freezing {k}')
v.requires_grad = False
self.gs = max(int(self.model.stride.max()), 32)
self.imgsz = self.check_img_size(self.opt.imgsz, self.gs, floor=self.gs * 2)
if self.RANK == -1 and self.batch_size == -1:
self.batch_size = self.check_train_batch_size(self.model, self.imgsz, self.amp)
self.loggers.on_params_update({'batch_size': self.batch_size})
self.nbs = 64
self.accumulate = max(round(self.nbs / self.batch_size), 1)
self.hyp['weight_decay'] *= self.batch_size * self.accumulate / self.nbs
self.optimizer = self.smart_optimizer(self.model, self.opt.optimizer, self.hyp['lr0'], self.hyp['momentum'], self.hyp['weight_decay'])
if self.opt.cos_lr:
self.lf = one_cycle(1, self.hyp['lrf'], self.epochs)
else:
self.lf = lambda x: (1 - x / self.epochs) * (1.0 - self.hyp['lrf']) + self.hyp['lrf']
self.scheduler = lr_scheduler.LambdaLR(self.optimizer, lr_lambda=self.lf)
self.ema = ModelEMA(self.model) if self.RANK in {-1, 0} else None
self.best_fitness, self.start_epoch = 0.0, 0
if self.pretrained:
if self.resume:
self.best_fitness, self.start_epoch, self.epochs = self.smart_resume(ckpt, self.optimizer, self.ema, self.weights, self.epochs, self.resume)
del ckpt, csd
if self.cuda and self.RANK == -1 and torch.cuda.device_count() > 1:
LOGGER.warning(
'WARNING ⚠️ DP not recommended, use torch.distributed.run for best DDP Multi-GPU results.\n'
'See Multi-GPU Tutorial at https://docs.ultralytics.com/yolov5/tutorials/multi_gpu_training to get started.'
)
self.model = torch.nn.DataParallel(self.model)
if self.opt.sync_bn and self.cuda and self.RANK != -1:
self.model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(self.model).t
......
train.py是一个用于训练YOLOv5模型的程序文件。该程序文件可以在自定义数据集上训练YOLOv5模型,并支持单GPU和多GPU分布式训练。
程序文件中的主要功能包括:
该程序文件还提供了一些辅助函数,用于检查数据集、模型权重、模型配置文件等的合法性,并提供了一些工具函数,如计算损失、计算精度指标、绘制训练曲线等。
该程序文件还支持使用多GPU进行分布式训练,并提供了一些命令行参数用于配置分布式训练的参数。
该程序文件还支持从预训练模型权重开始训练,或者从头开始训练。可以根据需要选择不同的训练方式。
最后,该程序文件还提供了一些辅助函数,用于处理日志记录、模型保存、模型加载等操作。
class ObjectDetection:
def __init__(self):
FILE = Path(__file__).resolve()
ROOT = FILE.parents[0] # YOLOv5 root directory
if str(ROOT) not in sys.path:
sys.path.append(str(ROOT)) # add ROOT to PATH
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
from models.common import DetectMultiBackend
from utils.augmentations import letterbox
from utils.datasets import IMG_FORMATS, VID_FORMATS, LoadImages, LoadStreams
from utils.general import (LOGGER, check_file, check_img_size, check_imshow, check_requirements, colorstr,
increment_path, non_max_suppression, print_args, scale_coords, strip_optimizer, xyxy2xywh)
from utils.plots import Annotator, colors, save_one_box
from utils.torch_utils import select_device, time_sync
def load_model(
weights=ROOT / 'best.pt', # model.pt path(s)
data=ROOT / 'data/coco128.yaml', # dataset.yaml path
device='', # cuda device, i.e. 0 or 0,1,2,3 or cpu
half=False, # use FP16 half-precision inference
dnn=False, # use OpenCV DNN for ONNX inference
):
# Load model
device = select_device(device)
model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data)
stride, names, pt, jit, onnx, engine = model.stride, model.names, model.pt, model.jit, model.onnx, model.engine
# Half
half &= (pt or jit or onnx or engine) and device.type != 'cpu' # FP16 supported on limited backends with CUDA
if pt or jit:
model.model.half() if half else model.model.float()
return model, stride, names, pt, jit, onnx, engine
self.model, self.stride, self.names, self.pt, self.jit, self.onnx, self.engine = load_model()
def run(self, img, imgsz=(640, 640), conf_thres=0.55, iou_thres=0.05, max_det=1000, device='', classes=None, agnostic_nms=False, augment=False, half=False):
cal_detect = []
device = select_device(device)
names = self.model.module.names if hasattr(self.model, 'module') else self.model.names # get class names
# Set Dataloader
im = letterbox(img, imgsz, self.stride, self.pt)[0]
# Convert
im = im.transpose((2, 0, 1))[::-1] # HWC to CHW, BGR to RGB
im = np.ascontiguousarray(im)
im = torch.from_numpy(im).to(device)
im = im.half() if half else im.float() # uint8 to fp16/32
im /= 255 # 0
......
这个程序文件是一个使用PyQt5编写的地铁屏幕异常状态预警系统。它使用YOLOv5模型进行目标检测,可以实时检测地铁屏幕的状态,并根据检测结果显示在界面上。
程序文件中的主要功能包括:
程序的运行流程大致如下:
整个程序的目的是实现地铁屏幕异常状态的预警,通过目标检测技术检测地铁屏幕的状态,并在界面上显示检测结果。用户可以选择文件进行检测,也可以进行实时检测。
class ContextAggregation(nn.Module):
"""
Context Aggregation Block.
Args:
in_channels (int): Number of input channels.
reduction (int, optional): Channel reduction ratio. Default: 1.
conv_cfg (dict or None, optional): Config dict for the convolution
layer. Default: None.
"""
def __init__(self, in_channels, reduction=1):
super(ContextAggregation, self).__init__()
self.in_channels = in_channels
self.reduction = reduction
self.inter_channels = max(in_channels // reduction, 1)
conv_params = dict(kernel_size=1, act_cfg=None)
self.a = ConvModule(in_channels, 1, **conv_params)
self.k = ConvModule(in_channels, 1, **conv_params)
self.v = ConvModule(in_channels, self.inter_channels, **conv_params)
self.m = ConvModule(self.inter_channels, in_channels, **conv_params)
self.init_weights()
def init_weights(self):
for m in (self.a, self.k, self.v):
caffe2_xavier_init(m.conv)
constant_init(self.m.conv, 0)
def forward(self, x):
n, c = x.size(0), self.inter_channels
# a: [N, 1, H, W]
a = self.a(x).sigmoid()
# k: [N, 1, HW, 1]
k = self.k(x).view(n, 1, -1, 1).softmax(2)
# v: [N, 1, C, HW]
v = self.v(x).view(n, 1, c, -1)
# y: [N, C, 1, 1]
y = torch.matmul(v, k).view(n, c, 1, 1)
y = self.m(y) * a
return x + y
这是一个名为yolov5-ContextAggregation.py的程序文件。该文件定义了一个名为ContextAggregation的类,用于实现上下文聚合块。该类接受输入通道数和通道缩减比例作为参数,并定义了一系列卷积操作。在初始化函数中,通过调用init_weights函数对模型参数进行初始化。在前向传播函数中,首先对输入进行一系列卷积操作,然后计算注意力权重并将其应用于特征图,最后将注意力加权的特征图与输入相加并返回结果。
此外,文件中还包含了YOLOv5模型的参数设置,包括类别数、模型深度倍数、层通道倍数和锚框信息。模型的主干网络和头部网络结构也在文件中定义,其中包括一系列卷积、上采样和拼接操作。最后,通过调用ContextAggregation类和Detect类来构建完整的YOLOv5模型。
整体功能和构架概述:
该项目是一个视觉项目,旨在开发一个地铁屏幕异常状态预警系统。该系统使用了融合上下文扩展和特征细化网络CAM改进的YOLOv5模型进行目标检测,并使用上下文聚合块进行特征融合。此外,还包括了分类和分割模块,用于对检测到的目标进行分类和分割。系统还提供了一个用户界面,用于实时显示检测结果。
下表整理了每个文件的功能:
文件路径 | 功能 |
---|---|
CAM.py | 定义特征融合模块 |
iou.py | 计算IoU损失和相关指标 |
train.py | 训练YOLOv5模型 |
ui.py | 实现地铁屏幕异常状态预警系统的用户界面 |
yolov5-ContextAggregation.py | 定义上下文聚合块 |
classify/predict.py | 使用YOLOv5模型进行分类推断 |
classify/train.py | 训练分类模型 |
classify/val.py | 在验证集上评估分类模型 |
models/common.py | 定义一些通用的模型组件 |
models/experimental.py | 定义一些实验性的模型组件 |
models/tf.py | 定义与TensorFlow相关的模型组件 |
models/yolo.py | 定义YOLOv5模型 |
models/init.py | 模型初始化文件 |
segment/predict.py | 使用YOLOv5模型进行分割推断 |
segment/train.py | 训练分割模型 |
segment/val.py | 在验证集上评估分割模型 |
utils/activations.py | 定义激活函数 |
utils/augmentations.py | 定义数据增强方法 |
utils/autoanchor.py | 自动计算锚框 |
utils/autobatch.py | 自动批处理 |
utils/callbacks.py | 定义训练过程中的回调函数 |
utils/dataloaders.py | 定义数据加载器 |
utils/datasets.py | 定义数据集类 |
utils/downloads.py | 下载相关的功能 |
utils/general.py | 定义一些通用的辅助函数 |
utils/loss.py | 定义损失函数 |
utils/metrics.py | 定义评估指标 |
utils/plots.py | 绘制相关的图表 |
utils/torch_utils.py | Torch相关的辅助函数 |
utils/triton.py | Triton相关的辅助函数 |
utils/init.py | 工具函数初始化文件 |
utils/aws/resume.py | AWS相关的恢复功能 |
utils/aws/init.py | AWS初始化文件 |
utils/flask_rest_api/example_request.py | Flask REST API示例请求 |
utils/flask_rest_api/restapi.py | Flask REST API相关功能 |
utils/loggers/init.py | 日志记录初始化文件 |
CAM来源于一篇ICLR 2022的会议论文,ICLR由Lecun,Hinton和Bengio三位神经网络的元老联手发起。近年来随着深度学习在工程实践中的成功,ICLR会议也在短短的几年中发展成为了神经网络的顶会。哎,我也想发这样的文章,今天我们就来看看这篇文章的成功之处,虽然论文给了代码但是现在打不开或者无法访问到,裂开了! 但是这篇文章的实验做得还是挺充分的,可视化也挺好,属于比较不错的一类,自己达不到的水平,哈哈!
微小物体由于分辨率低、体积小,很难被探测到。网络的局限性和训练数据集的不平衡是导致微小目标检测性能不佳的主要原因该文提出了一种复合结构的FPN,它包含一个上下文扩展模块和一个特征细化模块。上下文增强模块利用扩展卷积提取不同接收域的上下文信息,并将其集成到FPN中,对微小物体的上下文信息进行改进。特征细化模块结合了空间自适应融合和信道自适应融合,从信道和空间维度抑制冲突特征,突出有用特征。此外,为了防止训练不平衡,提出了一种微小对象的复制-减少-粘贴数据增强方法。
金字塔结构FPN可以在一定程度上缓解信息扩散问题,通过水平融合低分辨率特征图和高分辨率特征图。但是,直接融合不同密度的信息会导致语义冲突,限制了多尺度特征的表达,容易使微小的物体淹没在冲突的信息中。同时,在当前的经典公共数据集中,微小对象的注释数量远远少于较大目标的注释数量(Chen et al, 2020)。因此,在训练过程中,网络的收敛方向不断向较大的目标倾斜,导致对微小目标的性能较差。因此作者从这两方面作为突破口进行改进来提升小目标检测。为解决微小物体特征分散(意思就是小物体分布的非常散,并不是聚集在一起的)和层间语义差异(不同的层所代表的语义信息会产生混乱,比如说深层特征中包含的小目标信息较少,但是有时候影响到大目标的预测结果)的问题,提出了一种结合上下文增强和特征细化的特征金字塔复合神经网络结构。提出的算法框架如下图所示。在整体网络结构。CAM和FRM是该网络的主要组成部分。CAM向FPN注入上下文信息,FRM对FPN的冲突信息进行过滤。
微小目标检测需要上下文信息。我们提出使用具有不同扩张卷积率的扩张卷积来获取不同接受域的上下文信息,以丰富FPN的上下文信息。如下图所示,这就是常用的ASPP,当然如果只是这样引用肯定是不行的,所以作者做了下面的事情。
作者又将这样的ASPP模块的融合方式通过下面三种方式进行实验,其中(a)和(c)方式就是一般的进行相加和拼接,几种不同的特征的权重是相同的,而对于(b)方式就是将最终结果再通过一个注意力机制进行重要性分析。一般来说第二种方式是比较不错的,因为这种方法我是在其他论文上见过的,在那篇小目标检测论文中,(b)的方式是一个创新点部分。但是在这篇文章中,作者通过实验直接得到(c)的融合方式对小目标的检测是最好的。如下表所示。
这一部分的内容就是用来过滤冲突信息,防止微小物体的特征直接被淹没在冲突信息中,因为在FPN中对不同尺度进行融合,容易产生大量的冗余信息和冲突信息,从而降低了多尺度表达能力。具体结构如下图所示:
该结构分为两种模块,分别是通道上的过滤模块以及空间上的过滤模块,对于通道净化模块,作者首先使用两种池化方法,然后将自适应平均池和自适应最大池相结合,获得更精细的图像全局特征。空间过滤模块 通过softmax生成各位置相对于通道的相对权重。至于一些公式,其实还是得看源码。当然其实也并不是特别的难,就是对不同的特征层自适应的赋予不同的权重。
通过C5 之后经过3个卷积核大小为3*3,扩张卷积率为1、3、5的卷积,然后三个卷积再进行融合。这样做融合不同感受野获得的特征,丰富上下文信息,获得更好的特征提取效果,便于微笑目标的检测,
融合有三种策略(代码中在yaml文件中可以选择)。方法(a)是加权融合,方法(b)是自适应融合 ,即假设输入的大小可以表示为(bs,C,H,W),可以通过执行卷积运算来获得(bs,3,H,W)的空间自适应权重连接和Softmax。三个通道与三个输入一一对应,通过计算加权和,可以将上下文信息聚合到输出,方法(c)是做concatenation,例:a = np.array([[1,2],[3,4]]),b =np.arrat([[5,6]]),c=np.concatenate((a,b)),c=array([1,2],[3,4],[5,6]).
### yolov5 cam yaml
nc: 1 # number of classes
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.25 # layer channel multiple
anchors:
- [10,13, 16,30, 33,23] # P3/8
- [30,61, 62,45, 59,119] # P4/16
- [116,90, 156,198, 373,326] # P5/32
# YOLOv5 v6.0 backbone
backbone:
# [from, number, module, args]
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
[-1, 3, C3, [128]],
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
[-1, 6, C3, [256]],
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
[-1, 9, C3, [512]],
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
[-1, 3, C3, [1024]],
[-1, 1, SPPF, [1024, 5]], # 9
]
# YOLOv5 v6.0 head
head:
[[-1, 1, Conv, [512, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 6], 1, Concat, [1]], # cat backbone P4
[-1, 3, C3, [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, C3, [256, False]], # 17 (P3/8-small)
[-1, 1, Conv, [256, 3, 2]],
[[-1, 14], 1, Concat, [1]], # cat head P4
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
[-1, 1, Conv, [512, 3, 2]],
[10, 1, CAM, ['weight']],
[[-2, -1], 1, Concat, [1]], # cat head P5
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
[[17, 20, 24], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
]
epoch:训练纪元数。
train/box_loss、、、train/obj_loss:train/cls_loss训练期间边界框、对象和类预测的损失指标。
metrics/precision、metrics/recall、metrics/mAP_0.5、metrics/mAP_0.5:0.95:不同 IoU 阈值下的精度、召回率和平均精度 (mAP)。
val/box_loss、、、val/obj_loss:val/cls_loss验证期间边界框、对象和类预测的损失指标。
x/lr0, x/lr1, x/lr2:模型中不同层或阶段的学习率。
为了可视化和分析这些数据,我们将创建几个图表来了解这些指标在不同时期的趋势。此分析将帮助我们了解模型的性能在训练和验证过程中如何演变。绘图后,我将对结果进行详细分析。让我们从可视化开始。
import matplotlib.pyplot as plt
# Plotting settings
plt.figure(figsize=(20, 15))
plt.subplots_adjust(hspace=0.5)
# Training loss plots
plt.subplot(3, 2, 1)
plt.plot(data['epoch'], data['train/box_loss'], label='Box Loss')
plt.plot(data['epoch'], data['train/obj_loss'], label='Object Loss')
plt.plot(data['epoch'], data['train/cls_loss'], label='Class Loss')
plt.title('Training Losses')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
# Validation loss plots
plt.subplot(3, 2, 2)
plt.plot(data['epoch'], data['val/box_loss'], label='Box Loss')
plt.plot(data['epoch'], data['val/obj_loss'], label='Object Loss')
plt.plot(data['epoch'], data['val/cls_loss'], label='Class Loss')
plt.title('Validation Losses')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
# Precision, Recall, and mAP plots
plt.subplot(3, 2, 3)
plt.plot(data['epoch'], data['metrics/precision'], label='Precision')
plt.plot(data['epoch'], data['metrics/recall'], label='Recall')
plt.title('Precision and Recall')
plt.xlabel('Epoch')
plt.ylabel('Metric Value')
plt.legend()
plt.subplot(3, 2, 4)
plt.plot(data['epoch'], data['metrics/mAP_0.5'], label='[email protected]')
plt.plot(data['epoch'], data['metrics/mAP_0.5:0.95'], label='[email protected]:0.95')
plt.title('Mean Average Precision (mAP)')
plt.xlabel('Epoch')
plt.ylabel('mAP')
plt.legend()
# Learning rate plots
plt.subplot(3, 2, 5)
plt.plot(data['epoch'], data['x/lr0'], label='LR 0')
plt.plot(data['epoch'], data['x/lr1'], label='LR 1')
plt.plot(data['epoch'], data['x/lr2'], label='LR 2')
plt.title('Learning Rates')
plt.xlabel('Epoch')
plt.ylabel('Learning Rate')
plt.legend()
# Show all plots
plt.show()
可视化提供了模型在训练周期内各种指标的性能的全面概述。现在,让我们详细分析这些结果:
框、对象和类损失(训练):随着时代的进展,所有三个损失指标(框、对象和类)都显示出下降趋势。这表明该模型正在有效地学习,提高了其预测边界框、识别对象和正确分类的能力。
框、对象和类损失(验证):验证损失也会随着时间的推移而减少,反映了训练损失。这是一个积极的信号,表明该模型没有过度拟合,并且可以很好地推广到未见过的数据。
精确度和召回率:精确度一开始相当低,但后来显着增加,表明模型在做出正确的积极预测方面变得越来越好。召回率也增加,表明模型查找所有相关案例的能力不断提高。
[email protected] 和 [email protected]:0.95:两个 mAP 指标都会随着时间的推移而增加。[email protected] 始终高于 [email protected]:0.95,这是预期的,因为后者是一个更严格的指标。这些指标的不断改进表明该模型在准确定位和分类对象方面的熟练程度不断提高。
学习率 ( x/lr0、x/lr1和x/lr2) 似乎随着时间的推移而降低,这在训练深度学习模型中很常见。这种逐渐减少有助于微调模型的权重,特别是当它接近最佳性能时。
混淆矩阵显示出近乎完美的分类,具有很高的真阳性率和非常低的假阴性率,表明该模型在识别阳性类方面非常有效。一些误报的存在表明,虽然模型对正类高度敏感,但它偶尔会将负实例错误分类为正实例。
F1 分数是精确率和召回率的调和平均值,当类别分布不均匀时,可以在两者之间实现平衡。F1 得分曲线在不同的置信度阈值上显示出较高的得分,这意味着该模型在其决策阈值上保持了精度和召回率之间的良好平衡。
精确率-召回率 (PR) 曲线提供了不同阈值设置下精确率和召回率之间权衡的详细视图。靠近右上角的曲线表示模型同时实现了高精度和召回率。提供的 PR 曲线靠近右上角,表明模型具有较高的曲线下面积 (AUC),通常表明模型性能良好。
不同置信水平的精度曲线仍然很高,这表明即使置信阈值发生变化,真阳性在阳性中的比例仍然保持一致。
召回曲线表明,该模型能够识别不同置信阈值下的大多数真阳性,表明检测阳性类别的鲁棒性。
显示标签实例的条形图表明存在明显的类不平衡,其中一个类的实例多于另一类。类别不平衡通常会导致模型偏向多数类别。在散点图中,标记数据点的分布可以指示模型可能用于做出决策的数据点集群。相关图可以表明不同标签之间的相关性,表明某些标签可能同时出现。
损失减少、精确度、召回率和 mAP 增加的总体趋势表明训练和验证阶段成功。该模型正在按预期进行学习,并在多个时期内提高其检测和分类能力。
训练和验证指标的并行改进表明了良好的泛化性。训练和验证性能之间没有显着差异,这通常表明过度拟合。
学习率策略似乎是有效的,有助于模型的逐步稳定改进。
数据增强:如果尚未实施,数据增强技术可以进一步增强模型的稳健性。
超参数调整:尝试不同的学习率、批量大小甚至模型架构可以产生更好的性能。
先进技术:实施先进技术,如转移学习(如果适用)或探索不同的骨干网络进行特征提取也可能是有益的。
总而言之,结果是有希望的,显示了模型的有效学习和泛化能力。损失的减少以及精度、召回率和 mAP 指标的增加表明模型在地铁屏幕异常状态预警系统中表现良好。进一步的改进和实验可能会带来更强大的性能。
下图完整源码&数据集&环境部署视频教程&自定义UI界面
参考博客《融合上下文扩展和特征细化网络CAM改进YOLOv5的地铁屏幕异常状态预警系统》
[1]韩宝明,李亚为,鲁放,等.2021年世界城市轨道交通运营统计与分析综述[J].都市快轨交通.2022,35(1).DOI:10.3969/j.issn.1672-6073.2022.01.002 .
[2]任松,朱倩雯,涂歆玥,等.基于深度学习的公路隧道衬砌病害识别方法[J].浙江大学学报(工学版).2022,56(1).DOI:10.3785/j.issn.1008-973X.2022.01.010 .
[3]龙学军,张杰,谭志国.基于视觉的隧道裂缝检测技术研究[J].现代计算机.2021,(30).DOI:10.3969/j.issn.1007-1423.2021.30.016 .
[4]王冰,李洋,王文斌,等.城市轨道交通智能运维技术发展及智能基础设施建设方法研究[J].现代城市轨道交通.2020,(8).
[5]薛亚东,李宜城.基于深度学习的盾构隧道衬砌病害识别方法[J].湖南大学学报(自然科学版).2018,(3).DOI:10.16339/j.cnki.hdxbzkb.2018.03.012 .
[6]佚名.《城市轨道交通2020年度统计和分析报告》发布[J].隧道建设(中英文).2021,(4).691.
[7]陈湘生,徐志豪,包小华,等.隧道病害监测检测技术研究现状概述[J].隧道与地下工程灾害防治.2020,(3).
[8]包叙定.同绘智慧城轨图共筑交通强国梦《中国城市轨道交通智慧城轨发展纲要》解读[J].城市轨道交通.2020,(4).24-29.DOI:10.14052/j.cnki.china.metros.2020.04.004 .
[9]折昌美.地铁隧道复杂裂缝病害的图像识别算法研究[D].2019.
[10]Li, Dawei,Xie, Qian,Gong, Xiaoxi,等.Automatic defect detection of metro tunnel surfaces using a vision-based inspection system[J].Advanced engineering informatics.2021.47DOI:10.1016/j.aei.2020.101206 .