关键词:ICIP挑战赛、方针检测、Yolov7、数据增强、剪枝技能
1. 布景介绍
蚊虫是某种病毒、疾病的传达者,可是目前世界上并没有针对蚊虫传达疾病的疫苗或许特定的抗病毒药物,因而,目前抗击这些疾病的最佳办法是操控和消除可能的蚊虫疫源地。蚊虫在干净的死水中繁衍。所以,任何储存水的容器都是潜在的滋生地。
为了消除潜在的蚊虫滋生地,经过无人机去捕获高分辨率、多角度的图画或视频,然后经过运用核算机视觉等技能来剖析进程,定位相关的蚊虫滋生地。
2. 数据集
视频格式的数据:帧宽2000,帧长4000。
标示:xml格式。
3. 点评指标
1.每个方针的AP值
2.整体方针的MAP值
4. 项目难点和解决办法
- 由所以高空拍照,图画尺度非常大,物体的疏密分布不均匀。
解决方案: 对输入图画切开,分红多组进行练习。
- 方针的色彩、阳光、草地的环境会影响检测精度。
解决方案: HSV通道色彩改换、亮度改换、对比度改换。
- 练习集标签和点评标签不符。
例如,有6种点评标签:瓶子、井等;可是练习集的标签为:有水的瓶子和无水的瓶子;有水的井和无水的井等。这样就把6种标签细化成了经核算是15种标签。咱们的理解是,这样做的原因是,细粒度标签便利专家对蚊虫滋生地的检测,这也契合此项意图立意。
解决方案: 尽管提交时依照要求依然运用6种标签,但咱们为了契合立意,练习时依照15种标签进行练习模型。
5. 完成办法和思路
咱们的思路是:
- 视频数据预处理:
-
视频分段、抽帧、帧率调整:运用 opencv库中的
cv2.imwriter()。 -
滑窗法裁剪:滑窗的方法,将每一帧20004000巨细的图片分割成640640的小patch。为了避免把一个物体分割到了2个patch中,咱们采纳overlap的方法,即裁剪时相邻的patch重叠64的像素值。
-
图画去噪:运用了opencv库中的均值滤波
cv2.blur()、高斯滤波cv2.gaussianBlur()。 -
数据增强:运用albumentations库中的裁剪、翻转、变形、对比度调整等操作;运用YOLOv7自带的mosaic、mixup 等操作。
-
经过YOLOv7完成方针检测任务
-
练习战略
-
推理战略
5.1 数据预处理
本次竞赛,数据增强运用:对比度改换、色彩变化、mosaic、mixup、cutout、cutmix
数据增强(Tricks):albumentations库
几许改换:包括翻转,旋转,移位,裁剪,变形,缩放等各类操作。
色彩改换:
- 噪声
- 模糊
- 超像素法
- HSV对比度改换
- 随机擦除法:对图片上随机选取一块区域,随机地擦除图画信息。
- RGB色彩扰动:将图片从RGB色彩空间转换到另一色彩空间,添加或减少色彩参数后回来RGB色彩空间。
- 超像素法:在最大分辨率处生成图画的若干个超像素,并将其调整到原始巨细,再将原始图画中一切超像素区域按必定比例替换为超像素,其他区域不改动。
- 边界检测:检测图画中的一切边缘,将它们标记为黑白图画,再将成果与原始图画叠加。
mosaic:核算中心点,进行四张图片的填充。
mixup:将两个图画混合起来。
cutout:随机裁剪掉图画的一部分。
cutmix:将随机图画的一个矩形部分剪切下来,然后将其粘贴到相关图画的相同方位。
5.2 练习战略
本次竞赛,练习运用:Focal loss、Warmup、自适应调整学习率、RMSProp、K-Fold交叉验证、Label Smothing、剪枝
练习战略(Tricks):
Warmup:练习初期由于离方针较远,一般选择大的学习率,可是运用过大的学习率简单导致不安稳。所以能够做一个学习率热身阶段,在开端的时分先运用一个较小的学习率,然后当练习进程安稳的时分再把学习率调回去。
学习率衰减战略:1.指数衰减 2.固定步长衰减 3.多步长衰减 4.余弦退火衰减 5.自适应调整学习率(ReduceLROnPlateau):当特定的度量指标,如练习丢失、验证丢失或准确率不再变化时,学习率就会改动,例如变成原来的1/2。
优化算法optimizer:RMSProp。AdaGrad算法的思维是,每一次更新参数时(一次迭代),不同的参数运用不同的学习率。RMSProp算法经过修正AdaGrad得来,其意图是针对梯度平方和累积越来越大的问题会导致梯度消失的问题。
标签滑润:
- one-hot:关于丢失函数,需求猜测概率去拟合实在概率,而拟合one-hot的实在概率函数会带来两个问题:(1)无法确保模型的泛化能力,很简单形成过拟合;(2)1和0的概率会使模型过于信任猜测的类别。
- Lable smoothing:添加了模型泛化能力,必定程度上避免过拟合。
K-Fold交叉验证:将数据集等比例划分红K份,其中1份作为验证集,别的K-1份作为练习集;实验K次,每次的验证集选取与之前不同的一份。
K-means聚类完成的Anchor尺度:autoanchor.py文件。
IoU loss:即定位丢失函数。
Focal loss:
- 布景:
针关于本次竞赛中正负样本分配不均衡的问题。因为大图中布景比较多,会导致划分出来的很多patch只要布景,没有方针。当负样本比正样本多时,会使网络对负样本产生更好的作用,对正样本的益处不大,因而咱们对丢失函数做出改善,在丢失函数前添加权重因子 t\alpha_t,操控正负样本的Loss,使网络愈加关注正样本的检测状况。 链接在此
- 公式:
FL(pt)=−t(1−pt)log(pt)FL(p_t)=-\alpha_t(1-p_t)^\gamma log(p_t) t∈[0,1]\alpha_t∈[0,1]
正样本,t=\alpha_t=\alpha,一般取0.75。
负样本,t=1−\alpha_t=1-\alpha,则为0.25。
Lable Smoothing:
- 布景:
咱们在核算loss前会将每个类别的 label 转换成 one-hot,比方:3个类别的标签分别为0、1、2。则三个类别对应的 one-hot 标签分别为[1, 0, 0]、[0, 1, 0]、[0, 0, 1]。因而咱们核算Loss时,会出现这样的状况。
BCE核算Loss公式: CrossEntropy(ytrue,ypred)=−(ytrue[i]∗log(ypred[i]))CrossEntropy(y_{true}, y_{pred}) = – (y_{true[i]} * log(y_{pred[i]}))
其中,ytrue[i]y_{true[i]}表示实在标签中第ii个样本的标签向量;ypred[i]y_{pred[i]}表示模型猜测的第ii个样本的概率向量。
第ii个样本的 Loss=−[1∗log0.7+0∗log0.2+0∗log0.1]Loss=-[1*log0.7+0*log0.2+0*log0.1]
可见只要 ytruey_{true}=1 那一维度参加了Loss的核算,其他的都忽略了。这样会形成:
- 在练习数据较少不足以表征一切样本特征的状况下,实在的标签与其他的标签之间的信息被无法被学习到,模型的判断非常绝对,简单导致模型的过拟合;
- 在有噪声的数据集时(比方有错误标签的状况),更简单受到影响。
经过在one-hot Label中参加噪声的方法,能够弥补信息熵较少的问题,添加了信息量,提供了练习数据中类别之间的关系,因而就能够到达按捺过拟合的作用。
- 核算办法:
将原始 Label 进行 Label Smothing 核算公式:
P′(yi)=(1−)P(yi)+nP'(y_i)=(1-\alpha)P(y_i)+\frac{\alpha}{n}
其中,P(yi)P(y_i)是第ii个类别的原始标签,如第i=0类别是[1, 0, 0];P′(yi)P'(y_i)是 Label Smothing 后的标签,如[0.93, 0.03, 0.03];\alpha 是滑润系数,通常取值为 0.1 或 0.2,如0.1;n是标签的数量,如3。
5.3 推理战略
本次竞赛,推理运用:TTA、WBF
揣度战略(Tricks):
无监督学习:Plabel,伪标签。伪标签的界说来自于半监督学习,半监督学习的核心思维是经过借助无标签的数据来源进步有监督进程中的模型功能。无监督学习进程:
- 1.模型对练习集数据练习。
- 2.模型对测验集数据猜测得到猜测标签(Plabel)。
- 3.将(测验集数据,猜测标签)和(练习集数据,标签)再一起练习。
TTA:测验时期的数据增强,例如旋转、翻转、裁剪等。经过TTA,改换出测验图片的多种形式,然后将测验成果进行交融,取均匀值或许投票成果。这样会进步模型的准确性和安稳性。
OOF:平衡P和R。还记得吗,咱们在核算mAP操作时,计数TP和FP的时分会设定IoU阈值,这个阈值是固定的,可是运用OOF中,咱们寻找最优阈值,使终究mAP最高。注:在验证集才会到核算mAP这一步,练习集都是核算loss。到了测验集的时分,就能够运用验证集调试出的这个阈值了。
NMS:
- Soft NMS:不会粗鲁地删除一切IOU大于阈值的框,而是下降其置信度。
- DIoU NMS:用DIoU替换IoU。
- WBF:权重框交融。去除重复框和NMS是相同的作用。该算法经过对不同方针检测模型的猜测进行交融来进步体系的功能。
模型交融:
- 单个模型的交融: 不同epoch进行交融;不同fold交融;不同参数交融。
- 多个模型的交融:取并集,避免漏检;取交集,避免误检。
TTA: TTA 称为 Test Time Augmentation(测验时刻数据增强)
在YOLOv5中的设置augment=True: 运用办法:
python val.py --weights yolov5x.pt --data coco.yaml --img 832 --augment
在models/yolo.py中:
class DetectionModel(nn.Module):
def __init__(self, cfg='yolov5s.yaml', ch=3, nc=None, anchors=None): # model, input channels, number of classes
super().__init__()
...
# 假如直接传入的是dict则无需处理; 假如不是则运用yaml.safe_load加载yaml文件
with open(cfg, errors='ignore') as f:
self.yaml = yaml.safe_load(f) # model dict
...
# 创立网络模型
# self.model: 初始化的整个网络模型(包括Detect层结构)
# self.save: 一切层结构中from不等于-1的序号,并排好序 [4, 6, 10, 14, 17, 20, 23]
self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch]) # model, savelist
...
def forward(self, x, augment=False, profile=False, visualize=False): # debug同样需求第三次才干正常跳进来
if augment: # use Test Time Augmentation(TTA), 假如打开会对图片进行scale和flip
return self._forward_augment(x) # augmented inference, None
return self._forward_once(x, profile, visualize) # single-scale inference, train
# 运用TTA进行推理(当然仍是会调用普通推理完成前向传达)
def _forward_augment(self, x):
img_size = x.shape[-2:] # height, width
s = [1, 0.83, 0.67] # scales
f = [None, 3, None] # flips (2-ud上下flip, 3-lr左右flip)
y = [] # outputs
# 这儿相当于对输入x进行3次不同参数的测验数据增强推理, 每次的推理结构都保存在列表y中
for si, fi in zip(s, f):
# scale_img缩放图片尺度
# 经过普通的双线性插值完成,依据ratio来操控图片的缩放比例,最终经过pad 0补齐到原图的尺度
xi = scale_img(x.flip(fi) if fi else x, si, gs=int(self.stride.max()))
yi = self._forward_once(xi)[0] # forward:torch.Size([1, 25200, 25])
# cv2.imwrite(f'img_{si}.jpg', 255 * xi[0].cpu().numpy().transpose((1, 2, 0))[:, :, ::-1]) # save
# _descale_pred将推理成果康复到相对原图图片尺度, 只对坐标xywh:yi[..., :4]进行康复
# 假如f=2,进行上下翻转; 假如f=3,进行左右翻转
yi = self._descale_pred(yi, fi, si, img_size)
y.append(yi) # [b, 25200, 25] / [b, 18207, 25] / [b, 12348, 25]
# 把第一层的后面一部分的猜测成果去掉, 也把最终一层的前面一部分的猜测成果去掉
# [b, 24000, 25] / [b, 18207, 25] / [b, 2940, 25]
# 筛除的可能是重复的部分吧, 进步运转速度(有了解的朋友请告诉我一下)
y = self._clip_augmented(y) # clip augmented tails
return torch.cat(y, 1), None # augmented inference, train
# 普通推理
def _forward_once(self, x, profile=False, visualize=False):
# y列表用来保存中间特征图; dt用来记载每个模块履行10次的均匀时长
y, dt = [], [] # outputs
# 对sequence模型进行遍历操作, 不断地对输入x进行处理, 中间成果需求保存的时分别的存储到列表y中
for m in self.model:
# 假如仅仅对前一个模块的输出进行操作, 则需求提取直接保存的中间特征图进行操作,
# 一般是concat处理, 对当前层与之前曾进行一个concat再卷积; detect模块也需求提取3个特征层来处理
if m.f != -1: # if not from previous layer
x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers
# profile参数打开会记载每个模块的均匀履行10次的时长和flops用于剖析模型的瓶颈, 进步模型的履行速度和下降显存占用
if profile:
self._profile_one_layer(m, x, dt)
# 运用当前模块对特征图进行处理
# 假如是concat模块: 则x是一个特征图列表, 则对其进行拼接处理, 再交给下一个卷积模块;
# 假如是C3, Conv等普通的模块: 则x是单一特征图
# 假如是detct模块: 则x是3个特征图的列表 (练习与推理回来的内容不一样)
x = m(x) # run
# self.save: 把一切层结构中from不是-1的值记下并排序 [4, 6, 10, 14, 17, 20, 23]
y.append(x if m.i in self.save else None) # save output
# 特征可视化
if visualize:
feature_visualization(x, m.type, m.i, save_dir=visualize)
return x
# 翻转数据增强
def _descale_pred(self, p, flips, scale, img_size):
# de-scale predictions following augmented inference (inverse operation)
if self.inplace:
p[..., :4] /= scale # de-scale xywh坐标缩放回原来巨细
# f=2,进行上下翻转
if flips == 2:
p[..., 1] = img_size[0] - p[..., 1] # de-flip ud
# f=3,进行左右翻转
elif flips == 3:
p[..., 0] = img_size[1] - p[..., 0] # de-flip lr
else:
x, y, wh = p[..., 0:1] / scale, p[..., 1:2] / scale, p[..., 2:4] / scale # de-scale
if flips == 2:
y = img_size[0] - y # de-flip ud
elif flips == 3:
x = img_size[1] - x # de-flip lr
p = torch.cat((x, y, wh, p[..., 4:]), -1)
return p
# 这儿y的一个包括3个子列表的列表, 经过对输入图画x进行了3次不同尺度的改换, 所以得到了3个inference结构
# 这儿看不太懂, 不过大概做的工作便是对第一个列表与最终一个列表的成果做一些过滤处理
# 把第一层的后面一部分的猜测成果去掉, 也把最终一层的前面一部分的猜测成果去掉, 然后剩余的concat为一个部分
def _clip_augmented(self, y):
# Clip YOLOv5 augmented inference tails
nl = self.model[-1].nl # Detect(): number of detection layers (P3-P5)
g = sum(4 ** x for x in range(nl)) # grid points
e = 1 # exclude layer count
i = (y[0].shape[1] // g) * sum(4 ** x for x in range(e)) # indices: (25200 // 21) * 1 = 1200
y[0] = y[0][:, :-i] # large: (1,25200,25) -> (1,24000,25)
i = (y[-1].shape[1] // g) * sum(4 ** (nl - 1 - x) for x in range(e)) # indices: (12348 // 21) * 16 = 9408
y[-1] = y[-1][:, i:] # small: (1,12348,25) -> (1,2940,25)
return y
...
# 经过普通的双线性插值完成,依据ratio来操控图片的缩放比例,最终经过pad 0补齐到原图的尺度
def scale_img(img, ratio=1.0, same_shape=False, gs=32): # img(16,3,256,416)
# scales img(bs,3,y,x) by ratio constrained to gs-multiple
if ratio == 1.0:
return img
else:
h, w = img.shape[2:]
s = (int(h * ratio), int(w * ratio)) # new size
img = F.interpolate(img, size=s, mode='bilinear', align_corners=False) # resize
if not same_shape: # pad/crop img
h, w = [math.ceil(x * ratio / gs) * gs for x in (h, w)]
return F.pad(img, [0, w - s[1], 0, h - s[0]], value=0.447) # value = imagenet mean
WBF:
WBF与NMS之间的不同之处在于:NMS删除猜测框,而WBF兼并猜测框。
- WBF的进程为:
1.排序:对一切的框依照置信度从高到低进行排序。得到的列表为(置信度,坐标)的形式。
2.聚类和兼并:设定调集称号叫做cluster调集,每个cluster中是标示相同物体的框(1or多个)。设定调集称号叫做fusions调集,每个fusion中是兼并之后的框(1个)。
取排序后的框,该框依次与fusionsA、fusionB中的框比较IoU,若该框与某个fusion中的框IoU大于阈值,代表这个框与该fusion中的框是标示的同一个物体,将这个框追加相应cluster中,将这个框与该fusion做加权交融:
若该框与fusionsA、fusionB调集中一切fusion中的框IoU均小于阈值,那么将这个框设为新的cluster调集,如clusterC,将这个框设为新的fusion调集,如fusionC。
3.循环一切排序后的框,直到结束。
6. 调参东西
NNI(Neural Network Intelligence)是一个轻量但强壮的东西,协助用户主动进行特征工程,神经网络架构查找,超参调优以及模型紧缩。
1.修正三种文件:
- 定制查找空间:search_space.json.需求的超参范围。
- 更新代码:更新的是练习代码.py。
- 更新配置文件:config.yaml.查找空间的方位。
2.NNI结合Baseline的具体步骤:
第五步:练习文件指py运转文件。上报指标便是要练习的方针,例如:loss、map。首要依据这个上报方针进行调参的。
第七步:调用yaml文件,运转流程:yaml→json→py






