ICIP挑战赛目标检测Yolov7数据增强剪枝技术解决方案

释放双眼,带上耳机,听听看~!
本文介绍了ICIP挑战赛目标检测任务中的解决方案,包括使用Yolov7进行目标检测、数据增强和剪枝技术等,以解决高空拍摄、图像颜色和环境影响、标签不符等问题,并提供了整体实现思路和评价指标。

关键词:ICIP挑战赛、目标检测、Yolov7、数据增强、剪枝技术

1. 背景介绍

蚊虫是某种病毒、疾病的传播者,但是目前世界上并没有针对蚊虫传播疾病的疫苗或者特定的抗病毒药物,因此,目前抗击这些疾病的最佳方法是控制和消除可能的蚊虫疫源地。蚊虫在干净的死水中繁殖。所以,任何储存水的容器都是潜在的滋生地。

为了消除潜在的蚊虫滋生地,通过无人机去捕获高分辨率、多角度的图像或视频,然后通过应用计算机视觉等技术来分析过程,定位相关的蚊虫滋生地。

2. 评价指标

1.每个对象的AP值

2.整体对象的MAP值

3. 项目难点和解决方法

  1. 由于是高空拍摄,图像尺寸非常大,物体的疏密分布不均匀。

解决方案: 对输入图像切割,分成多组进行训练。

  1. 目标的颜色、阳光、草地的环境会影响检测精度。

解决方案: HSV通道颜色变换、亮度变换、对比度变换。

  1. 训练集标签和评价标签不符。

例如,有6种评价标签:瓶子、井等;但是训练集的标签为:有水的瓶子和无水的瓶子;有水的井和无水的井等。这样就把6种标签细化成了经计算是15种标签。我们的理解是,这样做的原因是,细粒度标签方便专家对蚊虫滋生地的检测,这也符合此项目的立意。

解决方案: 虽然提交时按照要求仍然使用6种标签,但我们为了符合立意,训练时按照15种标签进行训练模型。

4. 整体实现思路

  1. 视频数据预处理

  2. 通过YOLOv7实现目标检测任务

  3. 训练策略

  4. 推理策略

4.1 数据预处理

数据格式:

视频格式的数据:帧宽2000,帧长4000。

标注:每个视频对应一个xml的标注文件。

数据预处理:

  • 视频分段、抽帧、帧率调整:使用 opencv库中的cv2.imwriter()

  • 滑窗法裁剪:滑窗的方式,将每一帧2000×4000大小的图片分割成640×640的小patch。为了防止把一个物体分割到了2个patch中,我们采取overlap的方式,即裁剪时相邻的patch重叠64的像素值。

  • 图像去噪:使用了opencv库中的均值滤波 cv2.blur()、高斯滤波 cv2.gaussianBlur()

  • 数据增强:使用albumentations库中的裁剪、翻转、变形、对比度调整等操作;使用YOLOv7自带的mosaic、mixup 等操作。

  • 标签处理:

<track id="0" label="tire" source="manual">
  <box frame="63" outside="0" occluded="0" keyframe="1" xtl="2608.00" ytl="0.00" xbr="2696.00" ybr="27.00" z_order="0">
  </box>
  <box frame="64" outside="0" occluded="0" keyframe="1" xtl="2601.00" ytl="0.00" xbr="2698.00" ybr="60.00" z_order="0">
  </box>
  <box frame="65" outside="0" occluded="0" keyframe="1" xtl="2594.00" ytl="1.00" xbr="2692.00" ybr="102.00" z_order="0">
  </box>
  <box frame="66" outside="0" occluded="0" keyframe="1" xtl="2586.00" ytl="25.00" xbr="2691.00" ybr="128.00" z_order="0">
  </box>
<track>

提取每帧图片所对应的所有(目标id、目标的左上角坐标、目标的右下角坐标),并将其xml格式转换为每帧图片对应的txt格式文件。

数据增强(Tricks):albumentations库

  • 几何变换:包括翻转,旋转,移位,裁剪,变形,缩放等各类操作。

  • 颜色变换:

    • 噪声
    • 模糊
    • 超像素法
    • HSV对比度变换
    • 随机擦除法:对图片上随机选取一块区域,随机地擦除图像信息。
    • RGB颜色扰动:将图片从RGB颜色空间转换到另一颜色空间,增加或减少颜色参数后返回RGB颜色空间。
    • 超像素法:在最大分辨率处生成图像的若干个超像素,并将其调整到原始大小,再将原始图像中所有超像素区域按一定比例替换为超像素,其他区域不改变。
    • 边界检测:检测图像中的所有边缘,将它们标记为黑白图像,再将结果与原始图像叠加。
  • mosaic:计算中心点,进行四张图片的填充。

  • mixup:将两个图像混合起来。

  • cutout:随机裁剪掉图像的一部分。

  • cutmix:将随机图像的一个矩形部分剪切下来,然后将其粘贴到相关图像的相同位置。

4.2 YOLOv7模型结构

文章此在,记得顺一遍

4.3 训练策略

本次竞赛,训练使用:Focal loss、Warmup、自适应调整学习率、RMSProp、K-Fold交叉验证、Label Smothing

Focal loss

  • 背景:

针对于本次竞赛中正负样本分配不均衡的问题。因为大图中背景比较多,会导致划分出来的很多patch只有背景,没有目标。当负样本比正样本多时,会使网络对负样本产生更好的效果,对正样本的益处不大,因此我们对损失函数做出改进,在损失函数前增加权重因子 αtalpha_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。

Warmup

训练初期由于离目标较远,一般选择大的学习率,但是使用过大的学习率容易导致不稳定。所以可以做一个学习率热身阶段,在开始的时候先使用一个较小的学习率,然后当训练过程稳定的时候再把学习率调回去。

学习率衰减策略 scheduler.step()

1.指数衰减 2.固定步长衰减 3.多步长衰减 4.余弦退火衰减

5.自适应调整学习率(ReduceLROnPlateau):当特定的度量指标,如训练损失、验证损失或准确率不再变化时,学习率就会改变,例如变成原来的1/2。

优化算法 optimizer.step()

链接在此,请回头复习第4章 优化器

RMSProp。AdaGrad算法的思想是,每一次更新参数时(一次迭代),不同的参数使用不同的学习率。RMSProp算法通过修改AdaGrad得来,其目的是针对梯度平方和累积越来越大的问题会导致梯度消失的问题。

K-Fold交叉验证

将数据集等比例划分成K份,其中1份作为验证集,另外K-1份作为训练集;实验K次,每次的验证集选取与之前不同的一份。

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个样本的概率向量。

ICIP挑战赛目标检测Yolov7数据增强剪枝技术解决方案

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的计算,其他的都忽略了。这样会造成:

  1. 在训练数据较少不足以表征所有样本特征的情况下,真实的标签与其他的标签之间的信息被无法被学习到,模型的判断非常绝对,容易导致模型的过拟合;
  2. 在有噪声的数据集时(比如有错误标签的情况),更容易受到影响。

通过在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。

4.4 推理策略

本次竞赛,推理使用:TTA、WBF、模型融合

推断策略(Tricks):

ICIP挑战赛目标检测Yolov7数据增强剪枝技术解决方案

TTA

TTA 称为 Test Time Augmentation(测试时间数据增强)。测试时期的数据增强,例如旋转、翻转、裁剪等。通过TTA,变换出测试图片的多种形式,然后将测试结果进行融合,取平均值或者投票结果。这样会提高模型的准确性和稳定性。

在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

几种改进NMS的方法:

  1. Soft NMS:不会粗鲁地删除所有IOU大于阈值的框,而是降低其置信度

  2. DIoU NMS:用DIoU替换IoU。

  3. WBF:权重框融合。去除重复框和NMS是相同的作用。该算法通过对不同目标检测模型的预测进行融合来提高系统的性能。

WBF与NMS之间的不同之处在于:NMS删除预测框,而WBF合并预测框。

  • WBF的过程为:

1.排序:对所有的框按照置信度从高到低进行排序。得到的列表为(置信度,坐标)的形式。

2.聚类和合并:设定集合名称叫做cluster集合,每个cluster中是标注相同物体的框(1or多个)。设定集合名称叫做fusions集合,每个fusion中是合并之后的框(1个)。

取排序后的框,该框依次与fusionsA、fusionB中的框比较IoU,若该框与某个fusion中的框IoU大于阈值,代表这个框与该fusion中的框是标注的同一个物体,将这个框追加相应cluster中,将这个框与该fusion做加权融合:

ICIP挑战赛目标检测Yolov7数据增强剪枝技术解决方案

若该框与fusionsA、fusionB集合中所有fusion中的框IoU均小于阈值,那么将这个框设为新的cluster集合,如clusterC,将这个框设为新的fusion集合,如fusionC。

3.循环所有排序后的框,直到结束。

模型融合

  • 单个模型的融合:
    不同epoch进行融合;不同fold融合;不同参数融合。
  • 多个模型的融合:取并集,防止漏检;取交集,防止误检。

5. 调参工具

NNI(Neural Network Intelligence)是一个轻量但强大的工具,帮助用户自动进行特征工程,神经网络架构搜索,超参调优以及模型压缩。

ICIP挑战赛目标检测Yolov7数据增强剪枝技术解决方案

1.修改三种文件:

  • 定制搜索空间:search_space.json.需要的超参范围。
  • 更新代码:更新的是训练代码.py。
  • 更新配置文件:config.yaml.搜索空间的位置。

ICIP挑战赛目标检测Yolov7数据增强剪枝技术解决方案

2.NNI结合Baseline的具体步骤:

ICIP挑战赛目标检测Yolov7数据增强剪枝技术解决方案

第五步:训练文件指py运行文件。上报指标就是要训练的目标,例如:loss、map。主要根据这个上报目标进行调参的。

第七步:调用yaml文件,运行流程:yaml→json→py

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

实时的软件生成 —— Prompt 编程打通低代码的最后一公里?

2023-12-22 9:56:14

AI教程

NLP模型发展的转折点及BERT的重要性

2023-12-22 10:11:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索