Datawhale AI 夏令营 CV Task 1 笔记

CSDN 2024-09-15 10:01:01 阅读 83

如果您喜欢这篇文章,欢迎访问我的个人博客查看原文并参与讨论与互动。【原文链接】

赛题解读

在这里插入图片描述

赛题目标

随着城市化进程的加快,城市管理面临着前所未有的挑战。占道经营、垃圾堆放和无照经营游商等问题对城市管理提出了更高的要求。本赛道聚焦城市违规行为的智能检测,通过研究开发高效可靠的计算机视觉算法,提升违规行为检测识别的准确度,降低对大量人工的依赖,提升检测效果和效率,从而推动城市治理向更高效、更智能、更文明的方向发展,为居民创造一个安全、和谐、可持续的居住环境。

赛事资源分析

初赛提供城管视频监控数据与对应违规行为标注。违规行为包括垃圾桶满溢、机动车违停、非机动车违停等。视频数据为mp4格式,标注文件为json格式,每个视频对应一个json文件。

<code>frame_id:违规行为出现的帧编号event_id:违规行为IDcategory:违规行为类别bbox:检测到的违规行为矩形框的坐标,[xmin,ymin,xmax,ymax]形式

标注示例

[

{

"frame_id": 20,

"event_id": 1,

"category": "机动车违停",

"bbox": [200, 300, 280, 400]

},

{

"frame_id": 20,

"event_id": 2,

"category": "机动车违停",

"bbox": [600, 500, 720, 560]

},

{

"frame_id": 30,

"event_id": 3,

"category": "垃圾桶满溢",

"bbox": [400, 500, 600, 660]

}

]

违法评判标准

机动车违停 非机动车违停 垃圾满溢
机动车在设有禁止停车标志、标线的路段停车,或在非机动车道、人行横道、施工地段等禁止停车的地方停车。 非机动车(如自行车、‌电动车等)‌未按照规定停放在指定的非机动车停车泊位或停车线内,‌而是在非机动车禁停区域或未划定的区域(消防通道、盲道、非机动车停车区线外、机动车停车区等)随意停放。 生活垃圾收集容器内垃圾超过三分之二以上即为满溢。‌垃圾桶无法封闭、脏污且周边有纸屑、污渍、塑料、生活垃圾及杂物堆放。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

评分规则介绍

使用F1score、MOTA指标来评估模型预测结果。对每个json文件得到两个指标的加权求和,最终得分为所有文件得分取均值。12

其中,F1 Score 是 Precision(精度)和 Recall(召回率)的调和平均值,专注于检测模型的准确性和完整性。

F

1

_

s

c

o

r

e

=

2

×

R

e

c

a

l

l

×

P

r

e

c

i

s

i

o

n

R

e

c

a

l

l

+

P

r

e

c

i

s

i

o

n

F1\_score = 2 \times \frac{Recall \times Precision}{Recall + Precision}

F1_score=2×Recall+PrecisionRecall×Precision​

精度:检测出的正样本中有多少是正确的。模型预测的目标中有多少是真实目标,减少 假阳性( FP) 的比例。召回率:所有真实的正样本中有多少被正确检测出。模型未遗漏多少目标,减少假阴性(FN) 的比例。

F1 Score 是在 Precision 和 Recall 之间找到一个平衡的度量。当这两个指标均衡时,F1 Score 较高。因此,它同时考虑了避免错误检测的目标以及尽量多的检测出所有目标

MOTA(Multi-Object Tracking Accuracy,多目标追踪准确率)是多目标追踪任务的评估标准,它专注于减少错误检测(假阳性和假阴性)以及 ID 追踪错误(ID Switches, IDSW)

M

O

T

A

=

1

F

N

+

F

P

+

I

D

S

W

G

T

MOTA = 1 - \frac{\sum FN + \sum FP + \sum IDSW}{\sum GT}

MOTA=1−∑GT∑FN+∑FP+∑IDSW​

MOTA 主要关注:

减少漏检(FN):保证模型能够检测出所有目标,尽量不要遗漏。减少误检(FP):保证模型预测出的目标是真实目标,避免错误检测。减少 ID 错误(IDSW):在多目标追踪任务中,减少不同目标的 ID 错误。

最终评分的权重分配则要求模型在检测性能和追踪性能之间取得平衡。重点是模型的检测能力(F1 Score),其次是其在多目标场景下的表现(MOTA

s

c

o

r

e

=

0.85

×

F

1

_

s

c

o

r

e

+

0.15

×

M

O

T

A

score = 0.85 \times F1\_score + 0.15 \times MOTA

score=0.85×F1_score+0.15×MOTA

任务提交格式

初赛任务是根据给定的城管视频监控数据集,进行城市违规行为的检测。违规行为主要包括垃圾桶满溢、机动车违停、非机动车违停等。选手需要能够从视频中分析并标记出违规行为,提供违规行为发生的时间和位置信息。

目标检测系统 - YOLO

在这里插入图片描述

You Only Look Once(YOLO),是一种流行的实时目标检测系统,由Joseph Redmon等人在2015年提出。YOLO模型的核心思想是将目标检测任务视为一个单一的回归问题,通过一个卷积神经网络(CNN)直接从图像像素到边界框坐标和类别概率的映射。YOLO模型经过了多次迭代,包括YOLOv2(YOLO9000)、YOLOv3和YOLOv4等版本,每个版本都在性能和速度上有所提升,同时也引入了一些新的技术,如更深的网络结构、更好的锚框机制、多尺度特征融合等。

在这里插入图片描述

YOLO的特点

端到端训练:YOLO 将目标检测任务视为单一的回归问题,可以在一次前向传播中同时预测多个目标的类别和边界框坐标,避免了其他目标检测算法中冗长的候选框生成和分类过程。高效的实时性:由于 YOLO 的网络结构设计和直接回归方法,YOLO 在运行速度上非常快,特别适用于实时场景下的目标检测任务。全局推理:YOLO 对整幅图像进行检测,而不像其他方法仅在局部区域进行预测。这使得 YOLO 对复杂背景和不规则物体具有较好的鲁棒性。

标注格式

YOLO使用的标注格式是每张图像一个文本文件,文件名与图像文件名相对应。

<code><class> <x_center> <y_center> <width> <height>

<class>:类别的索引号。<x_center>:边界框中心点的 x 坐标,相对于图像宽度的归一化比例。<y_center>:边界框中心点的 y 坐标,相对于图像高度的归一化比例。<width>:边界框的宽度,相对于图像宽度的归一化比例。<height>:边界框的高度,相对于图像高度的归一化比例。

在这里插入图片描述

YOLO标注格式的转换

YOLO 的标注文件只需要记录归一化的坐标,适用于不同大小的图像。

<code>x_min, y_min, x_max, y_max = bbox

x_center = (x_min + x_max) / 2 / img_width

y_center = (y_min + y_max) / 2 / img_height

width = (x_max - x_min) / img_width

height = (y_max - y_min) / img_height

模型训练

from ultralytics import YOLO

# 设置模型版本

model = YOLO("yolov8n.pt")

# 设定数据集和训练参数

results = model.train(data="yolo-dataset/yolo.yaml", epochs=2, imgsz=1080, batch=16)code>

在模型训练中,可以通过调整以下参数来改进模型:

训练轮次(epoch)

epoch决定了模型遍历整个数据集的次数。增加训练的 epoch 可以让模型更好地学习数据中的模式,减少欠拟合现象。但过多的 epoch 可能导致过拟合,影响泛化能力。 批量大小(batch):

批量大小决定了每次训练中传递到模型的图像数量。增加 batch size 通常可以使模型的权重更新更加稳定,但需要更多的显存。适当的批量大小可以帮助模型更快收敛,但过大可能导致内存不足。 学习率(lr0lrf):

学习率控制模型权重更新的速度。如果学习率过高,模型可能会跳过最优解;如果学习率过低,模型训练速度会很慢,并且可能陷入局部最优。

训练日志

每一个epoch会返回3个损失函数:

边界框回归损失 box_loss

评估模型预测的边界框与真实边界框之间的差异。该损失越小,说明模型在定位物体方面越精确。 分类损失 cls_loss 是分类损失,用于评估类别预测的准确性。

衡量模型在类别预测上的准确性。较小的 cls_loss 表明模型能更准确地预测每个目标的类别。 防御性损失 dfl_loss

是一种旨在提高模型泛化能力的损失函数,主要与特征提取和边界框回归的细节优化有关。

其中,边界框回归损失和分类损失的降低有助于提高 F1 Score,而防御性损失通过减少模型的误报和漏报,则间接影响 MOTA 和 F1 Score。

超参数调整

在完成了模型训练任务后我还尝试了超参数的调整,我首先添加了学习率来提高模型的泛化能力,从lr0 = 0.001开始逐步提高学习率并观察其表现的变化。留意到学习率对模型的影响并不大,我还是选择了保留lr0 = 0.1以及lrf = 0.1这一较大的学习率设置,在保证收敛速度的同时,尽量避免过拟合情况发生的可能性。接着,我还调整了批量大小以及训练轮次。最终,我的训练模型超参数设置如下

model = YOLO("yolov8n.pt")

results = model.train(data="yolo-dataset/yolo.yaml", epochs=50, imgsz=1080, batch=32, lr0=0.1, lrf=0.01)code>

相比初始的模型训练参数设置,调参后的模型在多个类别上的检测效果都有所提升,尤其是精度(Precision)显著提高,对正样本的检测更加准确。以下是调参后的训练日志截图:

在这里插入图片描述

代码实现

数据读取

<code>train_anno = json.load(open('训练集(有标注第一批)/标注/45.json', encoding='utf-8'))code>

train_anno[0], len(train_anno)

pd.read_json('训练集(有标注第一批)/标注/45.json')

video_path = '训练集(有标注第一批)/视频/45.mp4'

cap = cv2.VideoCapture(video_path)

while True:

# 读取下一帧

ret, frame = cap.read()

if not ret:

break

break

int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

bbox = [746, 494, 988, 786]

pt1 = (bbox[0], bbox[1])

pt2 = (bbox[2], bbox[3])

color = (0, 255, 0)

thickness = 2 # 线条粗细

cv2.rectangle(frame, pt1, pt2, color, thickness)

frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

plt.imshow(frame)

数据转换

if not os.path.exists('yolo-dataset/'):

os.mkdir('yolo-dataset/')

if not os.path.exists('yolo-dataset/train'):

os.mkdir('yolo-dataset/train')

if not os.path.exists('yolo-dataset/val'):

os.mkdir('yolo-dataset/val')

dir_path = os.path.abspath('./') + '/'

with open('yolo-dataset/yolo.yaml', 'w', encoding='utf-8') as up:code>

up.write(f'''

path: { dir_path}/yolo-dataset/

train: train/

val: val/

names:

0: 非机动车违停

1: 机动车违停

2: 垃圾桶满溢

3: 违法经营

''')

train_annos = glob.glob('训练集(有标注第一批)/标注/*.json')

train_videos = glob.glob('训练集(有标注第一批)/视频/*.mp4')

train_annos.sort(); train_videos.sort();

category_labels = ["非机动车违停", "机动车违停", "垃圾桶满溢", "违法经营"]

for anno_path, video_path in zip(train_annos[:5], train_videos[:5]):

print(video_path)

anno_df = pd.read_json(anno_path)

cap = cv2.VideoCapture(video_path)

frame_idx = 0

while True:

ret, frame = cap.read()

if not ret:

break

img_height, img_width = frame.shape[:2]

frame_anno = anno_df[anno_df['frame_id'] == frame_idx]

cv2.imwrite('./yolo-dataset/train/' + anno_path.split('/')[-1][:-5] + '_' + str(frame_idx) + '.jpg', frame)

if len(frame_anno) != 0:

with open('./yolo-dataset/train/' + anno_path.split('/')[-1][:-5] + '_' + str(frame_idx) + '.txt', 'w') as up:

for category, bbox in zip(frame_anno['category'].values, frame_anno['bbox'].values):

category_idx = category_labels.index(category)

x_min, y_min, x_max, y_max = bbox

x_center = (x_min + x_max) / 2 / img_width

y_center = (y_min + y_max) / 2 / img_height

width = (x_max - x_min) / img_width

height = (y_max - y_min) / img_height

if x_center > 1:

print(bbox)

up.write(f'{ category_idx} { x_center} { y_center} { width} { height}\n')

frame_idx += 1

!wget http://mirror.coggle.club/yolo/yolov8n-v8.2.0.pt -O yolov8n.pt

!mkdir -p ~/.config/Ultralytics/

!wget http://mirror.coggle.club/yolo/Arial.ttf -O ~/.config/Ultralytics/Arial.ttf

训练模型

import os

os.environ["CUDA_VISIBLE_DEVICES"] = "0"

import warnings

warnings.filterwarnings('ignore')

from ultralytics import YOLO

model = YOLO("yolov8n.pt")

results = model.train(data="yolo-dataset/yolo.yaml", epochs=52, imgsz=1080, batch=32, lr0=0.1, lrf=0.01)code>

结果导出

category_labels = ["非机动车违停", "机动车违停", "垃圾桶满溢", "违法经营"]

if not os.path.exists('result/'):

os.mkdir('result')

from ultralytics import YOLO

model = YOLO("runs/detect/train/weights/best.pt")

import glob

for path in glob.glob('测试集/*.mp4'):

submit_json = []

results = model(path, conf=0.05, imgsz=1080, verbose=False)

for idx, result in enumerate(results):

boxes = result.boxes # Boxes object for bounding box outputs

masks = result.masks # Masks object for segmentation masks outputs

keypoints = result.keypoints # Keypoints object for pose outputs

probs = result.probs # Probs object for classification outputs

obb = result.obb # Oriented boxes object for OBB outputs

if len(boxes.cls) == 0:

continue

xywh = boxes.xyxy.data.cpu().numpy().round()

cls = boxes.cls.data.cpu().numpy().round()

conf = boxes.conf.data.cpu().numpy()

for i, (ci, xy, confi) in enumerate(zip(cls, xywh, conf)):

submit_json.append(

{

'frame_id': idx,

'event_id': i+1,

'category': category_labels[int(ci)],

'bbox': list([int(x) for x in xy]),

"confidence": float(confi)

}

)

with open('./result/' + path.split('/')[-1][:-4] + '.json', 'w', encoding='utf-8') as up:code>

json.dump(submit_json, up, indent=4, ensure_ascii=False)

!\rm result/.ipynb_checkpoints/ -rf

!\rm result.zip

!zip -r result.zip result/

如果您喜欢这篇文章,欢迎访问我的个人博客查看原文并参与讨论与互动。【原文链接】


注1:若真实目标框与预测框IOU大于0.5,则判定目标正确识别。若MOTA指标为负,则该类别精度得分为0。 ↩︎

注2:若该视频中没有某个类别的目标,则此类别计算均值时,忽略该视频。 ↩︎



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。