#Datawhale AI夏令营 #深度学习 #目标检测 #改进方法
优快云和datawhale社区的朋友们好,这两天接近开学了,行途劳顿甚为忙碌,本次笔记就简短一点,我将站在一个参赛者的视角尽我所能对城市违法行为检测中baseline原有代码提出一些改进思路,争取给大伙一些启发,争取帮助大伙取得竞赛成绩的提高
作为《2024 Datawhale AI夏令营 第五期 Task1:视频处理方法与物体检测模型》的续篇,本篇建立在之前对研究背景和baseline代码详细解读的基础之上,就不按背景-方法-实验的篇章架构进行展开了。让我们来梳理一下baseline中的一些瓶颈,并就实验的各个瓶颈展开思考与讨论。
当然,我个人能力有限啊,本次比赛所提出的问题也相对比较困难,如有不妥的表述,全当抛转引玉了,还请诸位大佬在参赛群和评论区多多指正。
1.如何读入更多数据?
相信大部分同学在对baseline代码有一定了解之后,都会尝试读入更多数据以获取更好的成绩,但主办方提供的资源终究是有限的,如何在有限的资源条件下读入更多数据呢?
我主要尝试了以下三种方法:
(1)扩容磁盘
最直接的方法当然是直接扩容磁盘,主办方最大可将磁盘扩容到4T,给了参赛选手更大的发挥空间
(2)及时释放视频文件
这主要是为了节省RAM中的内存空间,以处理训练集的代码为例,原先代码如下所示,
for anno_path, video_path in zip(train_annos[:25], train_videos[:25]):
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
在while循环结束,即将读取下一批次的视频时,我们可以使cap.release()来显式的释放视频文件,虽然python的垃圾回收机制可以自动回收未被引用的资源,但显式的释放内存终归没有坏处,最终修改代码如下所示:
for anno_path, video_path in zip(train_annos[:25], train_videos[:25]):
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
cap.release()
在实际进行实验的过程中,释放内存有效减少了实验因为内存特别是RAM中的内存溢满而导致的实验崩溃问题,有效节省了实验的时间。
(3)每n帧抽取1帧进行训练
考虑到城市违法行为主要包含违停和违法经营,其监测目标的运动速度并不快,每1帧进行训练与每n帧进行训练其实区别并不太大,当然n也不能取太大的值以免出现漏检问题。在这里,我们尝试每2帧或每3帧抽取1帧进行训练,这样可以节省大量空间,以将更多视频训练材料纳入训练范围,提升检测模型在限定资源条件下对不同场景的泛化能力。
最终修改代码如下所示,
for anno_path, video_path in zip(train_annos[:42], train_videos[:42]):
print(video_path)
anno_df = pd.read_json(anno_path)
cap = cv2.VideoCapture(video_path)
frame_idx = 0
frame_counter = 0 # 新增计数器,用于跟踪帧数
while True:
ret, frame = cap.read()
if not ret:
break
# 只有当计数器达到3时才处理帧
if frame_counter == 3:
frame_counter = 0 # 重置计数器
else:
frame_counter += 1 # 否则,增加计数器
continue # 跳过当前帧的处理
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
# 确保视频文件在处理结束后被释放
cap.release()
2.如何调整训练架构?
本次竞赛的目标是识别城市违规行为,事实上,我认为不仅要识别出违法目标主体,更重要的是要如何判定识别目标违法,而这是要考虑目标所处环境的。比方说,一辆汽车停在车位内是不违规的,而停在路口则是违规的需要标记出来,而这样的判断,仅凭检测出目标,我认为是很难做到全自动完成的。
实际上我对YOLO框架背后的原理了解并不算深刻,我就赶鸭子上架般谈谈我的浅显认识吧,原始的YOLO框架对目标所处环境的认识应该是十分有限的,为YOLO增加注意力模块,利用注意力机制一看便可看全局的信息处理能力,我认为是提升YOLO框架对于待识别目标周围环境的理解能力的一条行之有效的解决途径。
具体来讲也有两种方法:
(1)修改模型架构,添加自注意力层,从头开始训练
可以修改模型架构的yaml文件,从头开始训练,但这样会消耗大量资源,必须根据个人情况分情况进行处理。
(2)使用具有自注意力机制的预训练模型
可以寻找一些其他研究团队的研究成果,采用具有注意力机制的预训练模型进行进一步的训练。
3.如何调整训练与测试参数?
终于来到炼丹环节了,但其实YOLO v8可调的训练参数其实不多,关键包含以下内容:
(1)学习率
YOLO v8可以自动调节学习率,通过设置model(optimizer=‘auto’)即可自动调整学习率
(2)损失函数
YOLO v8比较不方便的一个地方是修改损失函数似乎并不能通过设置的方式进行修改,你得把官方文件git下来,到文件夹里修改,额,我目前还没找到什么更好的修改方法,反正这种方法对线上运行的环境那是相当不友好。。。不知道各位大佬有没有什么更好的方法欢迎在评论区分享哦~
(3)正则化
YOLO v8可以通过设置dropout进行正则化,这在训练数据质量较差的时候比较有用。
(4)置信度
在测试的时候置信度其实可以适当调高一点,过低置信度的检测对于这个问题其实没有太大意义,也不知道主办方的评分标准是什么样的。
4. 城市违规行为检测的局限与未来
最后的最后,不得不提本次比赛的一个窘境了,就是主办方的标注数据错误率实在有点高,作为学生的我们又不大可能有资源能力去直接把数据集标好,这就导致了大部分同学的成绩都集中在0.5以下,而这在实际应用中特别是执法部门的应用中还是需要进一步提高的。
这样的局限性就衍生出了两个重要的研究方向,一个是标注工具的演进,能否进一步提升标注工具的自动化程度,在平时执法的时候就非常方便的,半自动化标注出违规行为的位置并加密传送到数据中心,经年累月形成高质量的数据集,以辅助判罚人员进行半自动处罚或提醒,而不是集中大量人员和时间对数据进行标注,但这里面牵涉大量的规章制度、人事等问题就不是研究人员可以左右的了;另外一个方向则是zero-shot,不再进行标注了,直接基于巨型数据集训练出的模型,然后直接实现0样本训练的目标识别,目前腾讯AI实验室已取得重要进展,但距离完全实现语义、情景和目标的贯通,我认为还需要进一步的研究与探索。
本次分享发表了很多个人的理解,观点与建议,由于这也不是完整的科学研究论文,而是在开学搬行李之余写的一篇参赛随笔,很多都是不那么准确的,如果你有不同的意见,欢迎各位大佬在交流群和评论区积极交流沟通!