从0梳理1场CV缺陷检测赛事!

↑↑↑关注后” 星标 “Datawhale

每日干货 &  每月组队学习 ,不错过

Datawhale干货 

作者:江保祥,厦门大学

一、布匹缺陷检测比赛分析

1. 赛题背景

去年的广东工业大赛已入选到全球人工智能技术大赛热身赛,大赛聚焦布匹疵点智能检测,要求选手研究开发高效可靠的计算机视觉算法,提升布匹疵点检验的准确度,降低对大量人工的依赖,提升布样疵点质检的效果和效率。

比赛地址: https://tianchi.aliyun.com/competition/entrance/531864/introduction

2. 比赛要求

要求算法既要检测布匹是否包含疵点,又要给出疵点具体的位置和类别,既考察疵点检出能力、也考察疵点定位和分类能力。

3. 评估指标

赛题分数计算方式:0.2ACC+0.8mAP

ACC:是有瑕疵或无瑕疵的分类指标,考察瑕疵检出能力。

其中提交结果name字段中出现过的测试图片均认为有瑕疵,未出现的测试图片认为是无瑕疵。

mAP:参照PASCALVOC的评估标准计算瑕疵的mAP值。

4. 提交说明

平台采用了基于GPU计算资源的提交镜像的方式,将本地代码打包成镜像提交,推送至阿里云容器镜像仓库后,在天池提交页面中输入镜像地址、用户名和仓库密码。由比赛平台拉取镜像运行, 运行结束即可在成绩页面查询运行日志及评测结果。

二、比赛数据分析

1. 数据大小

数据大小 官方一共提供了9576张图片用于训练其中有瑕疵图片5913张,无瑕疵图片3663张 瑕疵类别共有34个类别,在最终提交结果上对一些相似类别进行了合并后,共分为15个瑕疵类别。图片尺寸:4096 * 1696。

2. 比赛难点

种类较多,且数据分布不均 缺陷形状具有极端的长宽比 图片尺寸较大,部分缺陷尺寸小,小目标问题。

三、快速实现比赛Baseline

完整代码已开源 或后台回复 缺陷检测 下载

开源地址:https://github.com/datawhalechina/team-learning-cv/tree/master/DefectDetection

视频讲解: https://www.bilibili.com/video/BV1dK4y1Q7dc

1. 开源框架选择

任务分析

此次任务是布匹瑕疵检测,首先考虑的应该是目标检测框架。当前目标检测主要分为one-stage和two-stage两种类型,以YOLO,SSD等框架为代表的one-stage速度快,以Faster-RCNN为代表的two-stage框架精度高。基于本次任务时间有限制在1小时内,因此采用单阶段YOLOV5的方案

环境配置

# pip install -U -r requirements.txt
#Output:
Cython
numpy==1.17
opencv-python
torch>=1.4
matplotlib
pillow
tensorboard
PyYAML>=5.3
torchvision
scipy
tqdm

训练设置

$ python train.py --data coco.yaml --cfg yolov5s.yaml --weights '' --batch-size 64
yolov5m                                40
yolov5l                                24
yolov5x                                16

测试设置

$ python detcet.py --source ./inference/images/ --weights yolov5s.pt  --conf 0.4

2. 数据预处理

数据格式转换

代码详见 convertTrainLabel.py ,部分代码及分析如下

#读取比赛数据标签文件
josn_path = "./train_data/guangdong1_round2_train2_20191004_Annotations/Annotations/anno_train.json"
image_path = "./train_data/guangdong1_round2_train2_20191004_images/defect/"
with open(josn_path, 'r') as f:
temps = tqdm(json.loads(f.read()))
for temp in temps:
name = temp["name"].split('.')[0]
path = os.path.join(image_path, name, temp["name"])
im = cv2.imread(path)
sp = im.shape
image_h, image_w = sp[0], sp[1]
x_l, y_l, x_r, y_r = temp["bbox"]
#获取标签对应的类别一共15种
if temp["defect_name"]=="沾污":
defect_name = '0'
elif temp["defect_name"]=="错花":
defect_name = '1'
.......
#标注格式转换 江都区并存入列表
x_center = (x_l + x_r)/(2*image_w)
y_center = (y_l + y_r)/(2*image_h)
w = (x_r - x_l)/(image_w)
h = (y_r - y_l)/(image_h)
name_list.append(temp["name"])
c_list.append(defect_name)
image_h_list.append(image_w)
image_w_list.append(image_h)
x_center_list.append(x_center)
y_center_list.append(y_center)
w_list.append(w)
h_list.append(h)
.....
#读取列表 list 数据,并划分训练集和验证集
index = list(set(name_list))
print(len(index))
for fold in [0]:
val_index = index[len(index) * fold // 5:len(index) * (fold + 1) // 5]
print(len(val_index))
for num, name in enumerate(name_list):
print(c_list[num], x_center_list[num], y_center_list[num], w_list[num], h_list[num])
row = [c_list[num], x_center_list[num], y_center_list[num], w_list[num], h_list[num]]
if name in val_index:
path2save = 'val/'
else:
path2save = 'train/'
#数据写入 yolov5文件格式
if not os.path.exists('convertor/fold{}/labels/'.format(fold) + path2save):
os.makedirs('convertor/fold{}/labels/'.format(fold) + path2save)
with open('convertor/fold{}/labels/'.format(fold) + path2save + name.split('.')[0] + ".txt", 'a+') as f:
for data in row:
f.write('{} '.format(data))
f.write('\n')
if not os.path.exists('convertor/fold{}/images/{}'.format(fold, path2save)):
os.makedirs('convertor/fold{}/images/{}'.format(fold, path2save))
sh.copy(os.path.join(image_path, name.split('.')[0], name),
'convertor/fold{}/images/{}/{}'.format(fold, path2save, name))

3. 模型训练

数据路径设置:编辑一个数据路径文件夹yaml文件

例如:data/coco128.yaml

# train and val datasets (image directory or *.txt file with image paths)
train: ./process_data/images/train/
val: ./process_data/images/val/
# number of classes
nc: 15
# class names
names: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12','13', '14', '15']

模型文件选择:yolov5x.yaml, yolov5m.yaml, yolov5l.yaml yolov5s.yaml文件

可以选择合适的模型文件,从左到右精度下降,但是速率增大

nc: 15  # number of classes
depth_multiple: 1.33  # model depth multiple
width_multiple: 1.25  # layer channel multiple
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
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]],
...........

模型训练

$ python train.py --data coco.yaml --cfg yolov5s.yaml --weights '' --batch-size 64
yolov5m                                40
yolov5l                                24
yolov5x                                16

4. 测试模型并生成结果

detect.py 的输出结果的格式修改成提交结果的格式

#将输出结果的格式变成比赛需要提交的格式,并存入list,方便后面写入result.json文件中
if save_json:
name = os.path.split(txt_path)[-1]
print(name)
x1, y1, x2, y2 = float(xyxy[0]), float(xyxy[1]), float(xyxy[2]), float(xyxy[3])
bbox = [x1, y1, x2, y2]
img_name = name
conf = float(conf)
#add solution remove other
result.append(
{'name': img_name+'.jpg', 'category': int(cls+1), 'bbox': bbox,
'score': conf})
print("result: ", {'name': img_name+'.jpg', 'category': int(cls+1), 'bbox': bbox,'score': conf})
#写入result.json文件中
if save_json:
if not os.path.exists(save_dir):
os.makedirs(save_dir)
with open(os.path.join(save_dir, "result.json"), 'w') as fp:
json.dump(result, fp, indent=4, ensure_ascii=False)

最后就是docker生成镜像,并提交镜像,至此就实现了比赛的Baseline了

四、改进思路

在实现比赛的Baseline后,可以说是完成了第一步,后面如果想要获取好的成绩就需要我们根据比赛的任务,比赛的难点。进行调整方案,修改网络,修改策略。

前面我们提到该布匹缺陷检测任务的难点主要有:

  • 数据种类分布不均匀

  • 缺陷具有极端的长宽比

  • 小目标问题

1. 数据种类分布不均匀

解决思路:

  • 过采样种类较少的样本

  • 数据扩增:在训练方面,除了常规的数据增强之外,我们观察到原始的标注存在不准确的情况,为了使网络适应这种不确定性,我们在训练时随机对原始的标注框进行了抖动,是网络能够学习这种不确定性

2. 缺陷具有极端的长宽比

解决思路:

  • anchor 设置:考虑到样本的长宽比差异较大,通过聚类分析可以发现,原始的anchor并不能满足当前任务的需要,通过增加anchor数目,提高检测性能。

  • 可变形卷积:增强特征提取能力,提高检测性能 方法:在 backbone结构的最后一个block采用可变形卷积核 优点:可变形卷积能够计算每个点的偏移,从最合适的地方取特征进行卷积

3. 小目标问题

解决思路:

  • 针对小目标的扩增方式:Copy-Pasted 也就是将小目标贴到图像中的任意位置并生成新的标注,并且粘贴的小目标可以进行随机变换(缩放,翻折,旋转等),这种方式通过增加每个图像中小目标的数量,匹配的 anchor 的数量也会随之增加,这进而提升了小目标在训练阶段对 loss 计算的贡献。

  • 多尺度训练:多尺度训练(Multi Scale Training, MST)通常是指设置几种不同的图片输入尺度,训练时从多个尺度中随机选取一种尺度,将输入图片缩放到该尺度并送入网络中

  • FPN 增加融合因子 Effective Fusion Factor in FPN for Tiny Object Detection

4.涨分Tricks

在实现对网络的改进后,我们可以使用一些比赛的涨分技巧

半监督学习

利用训练集训练好的模型预测测试集,将预测结果作为伪标签加入训练

测试增强

对检测图片进行翻折、旋转、色彩增强,然后分别对这些扩增图片进行预测,将多个预测结果进行融合

模型集成

多种模型进行预测,将一张图片的多个结果进行融合

阅读原文 可以参与缺陷检测实践

“整理不易, 三连

Datawhale
我还没有学会写个人说明!
上一篇

用Pytorch轻松实现28个视觉Transformer,开源库 timm 了解一下!(附代码解读)

下一篇

为什么 Python 的 f-string 可以连接字符串与数字?

你也可能喜欢

评论已经被关闭。

插入图片