YOLOv7-Pose 基于YOLOv7的关键点模型

目前人体姿态估计总体分为Top-down和Bottom-up两种,与目标检测不同,无论是基于热力图或是基于检测器处理的关键点检测算法,都较为依赖计算资源,推理耗时略长,今年出现了以YOLO为基线的关键点检测器。玩过目标检测的童鞋都知道YOLO以及各种变种目前算是工业落地较多的一类检测器,其简单的设计思想,长期活跃的社区生态,使其始终占据着较高的话题度。

【演变】

在ECCV 2022和CVPRW 2022会议上,YoLo-Pose和KaPao(下称为yolo-like-pose)都基于流行的YOLO目标检测框架提出一种新颖的无热力图的方法,类似于很久以前谷歌使用回归计算关键点的思想,yolo-like-pose一不使用检测器进行二阶处理,二部使用热力图拼接,虽然是一种暴力回归关键点的检测算法,但在处理速度上具有一定优势。

kapao

去年11月,滑铁卢大学率先提出了 KaPao:Rethinking Keypoint Representations: Modeling Keypoints and Poses as Objects for Multi-Person Human Pose Estimation,基于YOLOv5进行关键点检测,该文章目前已被ECCV 2022接收,该算法所取得的性能如下:

paper:https://arxiv.org/abs/2111.08557

code:https://github.com/wmcnally/kapao

yolov5-pose

今年4月,yolo-pose也挂在了arvix,在论文中,通过调研发现 HeatMap 的方式普遍使用L1 Loss。然而,L1损失并不一定适合获得最佳的OKS。且由于HeatMap是概率图,因此在基于纯HeatMap的方法中不可能使用OKS作为loss,只有当回归到关键点位置时,OKS才能被用作损失函数。因此,yolo-pose使用oks loss作为关键点的损失

相关代码在https://github.com/TexasInstruments/edgeai-yolov5/blob/yolo-pose/utils/loss.py也可见到:

 if self.kpt_label:
                    #Direct kpt prediction
                    pkpt_x = ps[:, 6::3] * 2. – 0.5
                    pkpt_y = ps[:, 7::3] * 2. – 0.5
                    pkpt_score = ps[:, 8::3]
                    #mask
                    kpt_mask = (tkpt[i][:, 0::2] != 0)
                    lkptv += self.BCEcls(pkpt_score, kpt_mask.float()) 
                    #l2 distance based loss
                    #lkpt += (((pkpt-tkpt[i])*kpt_mask)**2).mean()  #Try to make this loss based on distance instead of ordinary difference
                    #oks based loss
                    d = (pkpt_x-tkpt[i][:,0::2])**2 + (pkpt_y-tkpt[i][:,1::2])**2
                    s = torch.prod(tbox[i][:,-2:], dim=1, keepdim=True)
                    kpt_loss_factor = (torch.sum(kpt_mask != 0) + torch.sum(kpt_mask == 0))/torch.sum(kpt_mask != 0)
                    lkpt += kpt_loss_factor*((1 – torch.exp(-d/(s*(4*sigmas**2)+1e-9)))*kpt_mask).mean()

yolov7-pose

上个星期,YOLOv7的作者也放出了关于人体关键点检测的模型,该模型基于YOLOv7-w6

目前作者提供了.pt文件和推理测试的脚本,有兴趣的童靴可以去看看,本文的重点更偏向于对yolov7-pose.pt进行onnx文件的抽取和推理。

【yolov7-pose + onnxruntime】

首先下载好官方的预训练模型,使用提供的脚本进行推理:

% weigths = torch.load('weights/yolov7-w6-pose.pt')
% image = cv2.imread('sample/pose.jpeg')
!python pose.py 

一、yolov7-w6 VS yolov7-w6-pose

首先看下yolov7-w6使用的检测头

二、修改export脚本

如果直接使用export脚本进行onnx的抽取一定报错,在上一节我们已经看到pose.pt模型使用的检测头为IKeypoint,那么脚本需要进行相应更改:在export.py的这个位置插入:

 # 原代码:
    for k, m in model.named_modules():
        m._non_persistent_buffers_set = set()  # pytorch 1.6.0 compatibility
        if isinstance(m, models.common.Conv):  # assign export-friendly activations
            if isinstance(m.act, nn.Hardswish):
                m.act = Hardswish()
            elif isinstance(m.act, nn.SiLU):
                m.act = SiLU()
     model.model[-1].export = not opt.grid  # set Detect() layer grid export
                
    # 修改代码:
    for k, m in model.named_modules():
        m._non_persistent_buffers_set = set()  # pytorch 1.6.0 compatibility
        if isinstance(m, models.common.Conv):  # assign export-friendly activations
            if isinstance(m.act, nn.Hardswish):
                m.act = Hardswish()
            elif isinstance(m.act, nn.SiLU):
                m.act = SiLU()
        elif isinstance(m, models.yolo.IKeypoint):
            m.forward = m.forward_keypoint  # assign forward (optional)
            # 此处切换检测头
    model.model[-1].export = not opt.grid  # set Detect() layer grid export

forward_keypoint在原始的yolov7 repo源码中有,作者已经封装好,但估计是还没打算开放使用。

使用以下命令进行抽取:python export.py –weights ‘weights/yolov7-w6-pose.pt’ –img-size 960 –simplify True

三、onnxruntime推理

onnxruntime推理代码:

import onnxruntime
import matplotlib.pyplot as plt
import torch
import cv2
from torchvision import transforms
import numpy as np
from utils.datasets import letterbox
from utils.general import non_max_suppression_kpt
from utils.plots import output_to_keypoint, plot_skeleton_kpts

device = torch.device("cpu")

image = cv2.imread('sample/pose.jpeg')
image = letterbox(image, 960, stride=64, auto=True)[0]
image_ = image.copy()
image = transforms.ToTensor()(image)
image = torch.tensor(np.array([image.numpy()]))

print(image.shape)
sess = onnxruntime.InferenceSession('weights/yolov7-w6-pose.onnx')
out = sess.run(['output'], {'images': image.numpy()})[0]
out = torch.from_numpy(out)

output = non_max_suppression_kpt(out, 0.25, 0.65, nc=1, nkpt=17, kpt_label=True)
output = output_to_keypoint(output)
nimg = image[0].permute(1, 2, 0) * 255
nimg = nimg.cpu().numpy().astype(np.uint8)
nimg = cv2.cvtColor(nimg, cv2.COLOR_RGB2BGR)
for idx in range(output.shape[0]):
    plot_skeleton_kpts(nimg, output[idx, 7:].T, 3)

# matplotlib inline
plt.figure(figsize=(8, 8))
plt.axis('off')
plt.imshow(nimg)
plt.show()
plt.savefig("tmp")

推理效果几乎无损,但耗时会缩短一倍左右,另外有几个点:

  • image = letterbox(image, 960, stride=64, auto=True)[0] 中stride指的是最大步长,yolov7-w6和yolov5s下采样多了一步,导致在8,16,32的基础上多了64的下采样步长
  • output = non_max_suppression_kpt(out, 0.25, 0.65, nc=1, nkpt=17, kpt_label=True) ,nc 和 kpt_label 等信息在netron打印模型文件时可以看到
  • 所得到的onnx相比原半精度模型大了将近三倍,后续排查原因
  • yolov7-w6-pose极度吃显存,推理一张960×960的图像,需要2-4G的显存,训练更难以想象

ParseNet: Looking Wider to See Better

论文地址: https://arxiv.org/abs/1506.04579

代码: https://github.com/weiliu89/caffe

U形的编解码结构奠定了深度学习语义分割的基础,随着基线模型的表现越来越好,深度学习语义分割关注的焦点开始由原先的编解码架构下上采样如何更好的恢复图像像素转变为如何更加有效的利用图像上下文信息和提取多尺度特征。因而催生出语义分割的第二个主流的结构设计:多尺度结构。接下来的几篇论文解读将对重在关注图像上下文信息和多尺度特征的结构设计网络进行梳理,包括ParseNet、PSPNet、以空洞卷积为核心的Deeplab系列、HRNet以及其他代表性的多尺度设计。

自从全卷积网络(Fully Convolutional Networks, FCN)和UNet提出以来,主流的改进思路是围绕着编解码结构来进行的。但又一些改进在当时看来却不是那么“主流”,其中有一些是针对如何提升网络的全局信息提取能力来进行改进的。FCN提出之后,一些学者认为FCN忽略了图像作为整张图的全局信息,因而在一些应用场景下不能有效利用图像的语义上下文信息。图像全局信息除了增加对图像的整体理解之外,还有助于模型对局部图像块的判断,此前一种主流的方法是将概率图模型融入到CNN训练中,用于捕捉图像像素的上下文信息,比如说给模型加条件随机场(Conditional Random Field,CRF),但这种方式会使得模型难以训练并且变得低效。

针对如何高效利用图像的全局信息问题,相关研究在FCN结构的基础上提出了ParseNet,一种高效的端到端的语义分割网络,旨在利用全局信息来指导局部信息判断,并且引入太多的额外计算开销。提出ParseNet的论文为ParseNet: Looking Wider to See Better,发表于2015年,是在FCN基础上基于上下文视角的一个改进设计。在语义分割中,上下文信息对于提升模型表现非常关键,在仅有局部信息情况下,像素的分类判断有时候会变得模棱两可。尽管理论上深层卷积层的会有非常大的感受野,但在实际中有效感受野却小很多,不足以捕捉图像的全局信息。ParseNet通过全局平均池化的方法在FCN基础上直接获取上下文信息,图1为ParseNet的上下文提取模块,具体地,使用全局平均池化对上下文特征图进行池化后得到全局特征,然后对全局特征进行L2规范化处理,再对规范化后的特征图反池化后与局部特征图进行融合,这样的一个简单结构对于语义分割质量的提升的巨大的。如图2所示,ParseNet能够关注到图像中的全局信息,保证图像分割的完整性。

关于全局特征与局部特征的融合,ParseNet给出两种融合方式:早期融合(early fusion)和晚期融合(late fusion)。早期融合就是图6-1中所展现的融合方式,对全局特征反池化后直接与局部特征进行融合,然后在进行像素分类。而晚期融合则是把全局特征和局部特征分别进行像素分类后再进行某种融合,比如说进行加权。但无论是早期融合还是晚期融合,如果选取的归一化方式合适,其效果是差不多的。

下图是ParseNet在VOC 2012数据集上的分割效果,可以看到,ParseNet的分割能够明显关注到图像全局信息。

补充:反卷积(Deconvolution)、上采样(UNSampling)与上池化(UnPooling)

图(a)表示UnPooling的过程,特点是在Maxpooling的时候保留最大值的位置信息,之后在unPooling阶段使用该信息扩充Feature Map,除最大值位置以外,其余补0。

与之相对的是图(b),两者的区别在于UnSampling阶段没有使用MaxPooling时的位置信息,而是直接将内容复制来扩充Feature Map。从图中即可看到两者结果的不同。

图(c)为反卷积的过程,反卷积是卷积的逆过程,又称作转置卷积。最大的区别在于反卷积过程是有参数要进行学习的(类似卷积过程),理论是反卷积可以实现UnPooling和unSampling,只要卷积核的参数设置的合理。

2、FCN 全卷积网络 Fully Convolutional Networks

FCN对图像进行像素级的分类,从而解决了语义级别的图像分割(semantic segmentation)问题。与经典的CNN在卷积层之后使用全连接层得到固定长度的特征向量进行分类(全联接层+softmax输出)不同,FCN可以接受任意尺寸的输入图像,采用反卷积层对最后一个卷积层的feature map进行上采样, 使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类。

简单的来说,FCN与CNN的区别在把于CNN最后的全连接层换成卷积层,输出的是一张已经Label好的图片。

YOLOv7来临:论文详读和解析+训练自己数据集

2022年7月,YOLOv7来临,

论文链接:https://arxiv.org/abs/2207.02696

代码链接:https://github.com/WongKinYiu/yolov7

文章摘自https://mp.weixin.qq.com/s/5qK1FIU7qp0Sv3IE49-t_w

在v7论文挂出不到半天的时间,YOLOv3和YOLOv4的官网上均挂上了YOLOv7的链接和说明,由此看来大佬们都比较认可这款检测器。

官方版的YOLOv7相同体量下比YOLOv5精度更高,速度快120%(FPS),比 YOLOX 快180%(FPS),比 Dual-Swin-T 快1200%(FPS),比 ConvNext 快550%(FPS),比 SWIN-L快500%(FPS)。在5FPS到160FPS的范围内,无论是速度或是精度,YOLOv7都超过了目前已知的检测器,并且在GPU V100上进行测试, 精度为56.8% AP的模型可达到30 FPS(batch=1)以上的检测速率,与此同时,这是目前唯一一款在如此高精度下仍能超过30FPS的检测器。另外,YOLOv7所获得的成果不止于此,例如:

  • YOLOv7-e6 (55.9% AP, 56 FPS V100 b=1) by +500% FPS faster than SWIN-L Cascade R-CNN (53.9% AP, 9.2 FPS A100 b=1)
  • YOLOv7-e6 (55.9% AP, 56 FPS V100 b=1) by +550% FPS faster than ConvNeXt-RCNN (55.2% AP, 8.6 FPS A100 b=1)
  • YOLOv7-w6 (54.6% AP, 84 FPS V100 b=1) by +120% FPS faster than YOLOv5-X6-v6.1 (55.0% AP, 38 FPS V100 b=1)
  • YOLOv7-w6 (54.6% AP, 84 FPS V100 b=1) by +1200% FPS faster than Dual-Swin-RCNN (53.6% AP, 6.5 FPS V100 b=1)
  • YOLOv7 (51.2% AP, 161 FPS V100 b=1) by +180% FPS faster than YOLOX-X (51.1% AP, 58 FPS V100 b=1)

本文做出的贡献如下:

  1. 设计了几种可训练的bag-of-freebies,使实时检测器可以在不提高推理成本的情况下大大提高检测精度;
  2. 对于目标检测的发展,作者发现了两个新的问题,即模块重参化如何高效替代原始模块,以及动态标签分配策略如何处理好不同输出层的分配。因此在本文中提出了方法进行解决。
  3. 作者为实时探测器提出了“扩展”和“复合缩放”(extend” and “compound scaling”)方法,可以更加高效地利用参数和计算量,同时,作者提出的方法可以有效地减少实时探测器50%的参数,并且具备更快的推理速度和更高的检测精度。(这个其实和YOLOv5或者Scale YOLOv4的baseline使用不同规格分化成几种模型类似,既可以是width和depth的缩放,也可以是module的缩放)

2.1 实时检测器

目前最先进的实时探测器主要基于YOLO和FCOS,如果需要研发更先进的实时检测器,通常需要具备以下特征:

  • (1)更快和更高效的网络架构;
  • (2)更有效的特征积分方法;
  • (3)更准确的检测方法;
  • (4)更鲁棒的损失函数;
  • (5)更有效的标签分配方法;
  • (6)更有效的训练方式。

2.2 模型重参化

模型重参化策略在推理阶段将多个模块合并为一个计算模块,可以看作是一种集成技术(model ensemble,其实笔者觉得更像是一种基于feature的distillation),可以将其分为模块级集成和模型级集成两类。对于模型级重新参数化有两种常见的操作:

  • 一种是用不同的训练数据训练多个相同的模型,然后对多个训练模型的权重进行平均。
  • 一种是对不同迭代次数下模型权重进行加权平均。

模块重参化是近年来一个比较流行的研究课题。这种方法在训练过程中将一个整体模块分割为多个相同或不同的模块分支,但在推理过程中将多个分支模块集成到一个完全等价的模块中。然而,并不是所有提出的重参化模块都可以完美地应用于不同的架构。考虑到这一点,作者开发了新的重参数化模块,并为各种架构设计了相关的应用程序策略。下图是作者使用重参化实现构建的多个module,按照分组数不同进行排列,为什么作者会选择32的分组数,应该搞过部署的佬们会清楚一些,模块参考:https://github.com/WongKinYiu/yolov7/blob/main/models/common.py~

2.3 模型缩放

模型缩放通过扩大或缩小baseline,使其适用于不同的计算设备。模型缩放方法通常包括不同的缩放因子,如:

  • input size(输入图像大小)
  • depth(层数)
  • width(通道数)
  • stage(特征金字塔数量)

从而在网络的参数量、计算量、推理速度和精度方面实现很好的权衡。网络架构搜索(NAS)也是目前常用的模型缩放方法之一

三、模型设计架构

3.1 高效的聚合网络

在大多数关于设计高效网络的论文中,主要考虑的因素是参数量、计算量和计算密度。但从内存访存的角度出发出发,还可以分析输入/输出信道比、架构的分支数和元素级操作对网络推理速度的影响(shufflenet论文提出)。在执行模型缩放时还需考虑激活函数,即更多地考虑卷积层输出张量中的元素数量。

图2(b)中CSPVoVNet是VoVNet的一个变体。除了考虑上述几个设计问题外,CSPVoVNet的体系结构还分析了梯度路径,使不同层能够学习更多样化的特征。上面描述的梯度分析方法还能使推理速度更快、模型更准确(看下图!其实和Resnext有点像,但比它复杂一些)。

  • 图2(c)中的ELAN出于以下设计考虑——“如何设计一个高效的网络?”得出结论是:通过控制最短最长梯度路径,更深的网络可以有效地进行学习并更好地收敛。
  • 因此,在本文中,作者提出了基于ELAN的扩展版本E-ELAN,其主要架构如图2(d)所示。在大规模ELAN中,无论梯度路径长度和计算模块数量如何,都达到了稳定的状态。但如果更多计算模块被无限地堆叠,这种稳定状态可能会被破坏,参数利用率也会降低。本文提出的E-ELAN采用expand、shuffle、merge cardinality结构,实现在不破坏原始梯度路径的情况下,提高网络的学习能力。

在体系结构方面,E-ELAN只改变了计算模块中的结构,而过渡层的结构则完全不变。作者的策略是利用分组卷积来扩展计算模块的通道和基数,将相同的group parameter和channel multiplier用于计算每一层中的所有模块。然后,将每个模块计算出的特征图根据设置的分组数打乱成G组,最后将它们连接在一起。此时,每一组特征图中的通道数将与原始体系结构中的通道数相同。最后,作者添加了G组特征来merge cardinality。除了维护原始的ELAN设计架构外,E-ELAN还可以指导不同的分组模块来学习更多样化的特性。(难以置信,要是在CPU上运行,分分钟可能爆)

3.2 基于连接的模型的模型缩放

缩放这个就不说了,和YOLOv5、Scale YOLOv4、YOLOX类似。要不就depth and width,要不就module scale,可参考scale yolov4的P4、P5、P5结构。

四、可训练的赠品礼包(bag-of-freebies)

4.1 卷积重参化

尽管RepConv在VGG上取得了优异的性能,但将它直接应用于ResNet和DenseNet或其他网络架构时,它的精度会显着降低。作者使用梯度传播路径来分析不同的重参化模块应该和哪些网络搭配使用。通过分析RepConv与不同架构的组合以及产生的性能,作者发现RepConv中的identity破坏了ResNet中的残差结构和DenseNet中的跨层连接,这为不同的特征图提供了梯度的多样性(题外话,之前在YOLOv5 Lite上做过此类实验,结果也是如此,因此v5Lite-g的模型也是砍掉了identity,但分析不出原因,作者也没给出具体的分析方案,此处蹲坑)。

基于上述原因,作者使用没有identity连接的RepConv结构。图4显示了作者在PlainNet和ResNet中使用的“计划型重参化卷积”的一个示例。

4.2 辅助训练模块

深度监督是一种常用于训练深度网络的技术,其主要概念是在网络的中间层增加额外的辅助头,以及以辅助损失为指导的浅层网络权重。即使对于像ResNet和DenseNet这样收敛效果好的网络结构,深度监督仍然可以显着提高模型在许多任务上的性能(这个和Nanodet Plus相似,按笔者理解可以当成是深层局部网络的ensemble,最后将辅助头和检测头的权重做融合)。图5(a)和(b)分别显示了“没有”和“有”深度监督的目标检测器架构,在本文中,作者将负责最终的输出头称为引导头,将用于辅助训练的头称为辅助头。

接下来讨论标签分配的问题。在过去,在深度网络的训练中,标签分配通常直接指的是ground truth,并根据给定的规则生成hard label(未经过softmax)。然而近年来,以目标检测为例,研究者经常利用网络预测的质量分布来结合ground truth,使用一些计算和优化方法来生成可靠的软标签(soft label)。例如,YOLO使用bounding box预测和ground truth的IoU作为软标签。

在本文中,作者将网络预测结果与ground truth一起考虑后再分配软标签的机制称为“标签分配器”。无论辅助头或引导头,都需要对目标进行深度监督。那么,‘’如何为辅助头和引导头合理分配软标签?”,这是作者需要考虑的问题。目前最常用的方法如图5(c)所示,即将辅助头和引导头分离,然后利用它们各自的预测结果和ground truth执行标签分配。

本文提出的方法是一种新的标签分配方法,通过引导头的预测来引导辅助头以及自身。换句话说,首先使用引导头的prediction作为指导,生成从粗到细的层次标签,分别用于辅助头和引导头的学习,具体可看图5(d)和(e)。

Lead head guided label assigner: 引导头引导“标签分配器”预测结果和ground truth进行计算,并通过优化(在utils/loss.py的SigmoidBin()函数中,传送门:https://github.com/WongKinYiu/yolov7/blob/main/utils/loss.py 生成软标签。这组软标签将作为辅助头和引导头的目标来训练模型。(之前写过一篇博客,【浅谈计算机视觉中的知识蒸馏】]https://zhuanlan.zhihu.com/p/497067556)详细讲过soft label的好处)这样做的目的是使引导头具有较强的学习能力,由此产生的软标签更能代表源数据与目标之间的分布差异和相关性。此外,作者还可以将这种学习看作是一种广义上的余量学习。通过让较浅的辅助头直接学习引导头已经学习到的信息,引导头能更加专注于尚未学习到的残余信息。

Coarse-to-fine lead head guided label assigner: Coarse-to-fine引导头使用到了自身的prediction和ground truth来生成软标签,引导标签进行分配。然而,在这个过程中,作者生成了两组不同的软标签,即粗标签和细标签,其中细标签与引导头在标签分配器上生成的软标签相同,粗标签是通过降低正样本分配的约束,允许更多的网格作为正目标(可以看下FastestDet的label assigner,不单单只把gt中心点所在的网格当成候选目标,还把附近的三个也算进行去,增加正样本候选框的数量)。原因是一个辅助头的学习能力并不需要强大的引导头,为了避免丢失信息,作者将专注于优化样本召回的辅助头。对于引导头的输出,可以从查准率中过滤出高精度值的结果作为最终输出。然而,值得注意的是,如果粗标签的附加权重接近细标签的附加权重,则可能会在最终预测时产生错误的先验结果。

4.3 其他可训练的bag-of-freebies

  1. Batch normalization:目的是在推理阶段将批归一化的均值和方差整合到卷积层的偏差和权重中。
  2. YOLOR中的隐式知识结合卷积特征映射和乘法方式:YOLOR中的隐式知识可以在推理阶段将计算值简化为向量。这个向量可以与前一层或后一层卷积层的偏差和权重相结合。
  3. EMA Model:EMA 是一种在mean teacher中使用的技术,作者使用 EMA 模型作为最终的推理模型。

五、实验

5.1 实验环境

作者为边缘GPU、普通GPU和云GPU设计了三种模型,分别被称为YOLOv7-Tiny、YOLOv7和YOLOv7-W6。同时,还使用基本模型针对不同的服务需求进行缩放,并得到不同大小的模型。对于YOLOv7,可进行颈部缩放(module scale),并使用所提出的复合缩放方法对整个模型的深度和宽度进行缩放(depth and width scale),此方式获得了YOLOv7-X。对于YOLOv7-W6,使用提出的缩放方法得到了YOLOv7-E6和YOLOv7-D6。此外,在YOLOv7-E6使用了提出的E-ELAN,从而完成了YOLOv7-E6E。由于YOLOv7-tincy是一个面向边缘GPU架构的模型,因此它将使用ReLU作为激活函数。作为对于其他模型,使用SiLU作为激活函数。

选择当前先进的检测器YOLOR作为基线。在相同设置下,表1显示了本文提出的YOLOv7模型和其他模型的对比。从结果中可以看出:

  • 与YOLOv4相比,YOLOv7的参数减少了75%,计算量减少了36%,AP提高了1.5%。
  • 与最先进的YOLOR-CSP相比,YOLOv7的参数少了43% ,计算量少了15%,AP高了0.4%。
  • 在小模型的性能中,与YOLOv4-tiny相比,YOLOv7-Tiny减少了39%的参数量和49%的计算量,但保持相同的AP。
  • 在云GPU模型上,YOLOv7模型仍然具有更高的AP,同时减少了19%的参数量和33%的计算量。

5.3 与sota算法的比较

本文将所提出的方法与通用GPU上或边缘GPU上最先进的的目标检测器进行了比较

  • 比较YOLOv7-Tiny-SiLU和YOLOv5-N(v6.1),YOLOv7-Tiny-SiLU在速度上快127帧,准确率提高10.7%。
  • YOLOv7在帧率为161帧时有51.4%的AP,而相同AP的PP-YOLOE-L只有78帧,且参数l少41%。
  • YOLOv7-X在114FPS时,比YOLOv5-L(v6.1)99FPS的推理速度更快,同时可以提高3.9%的AP。
  • YOLOv7-X与YOLOv5-X(v6.1)相比,YOLOv7-X的推理速度要快31fps。此外,在参数量和计算量方面,YOLOv7-X比YOLOv5-X(v6.1)减少了22%的参数和8%的计算量,但AP提高了2.2%。
  • 使用输入分辨率1280,YOLOv7与YOLOR进行比较,YOLOv7-W6的推理速度比YOLOR-P6快8FPS,检测率也提高了1%的AP。
  • 至于YOLOv7-E6和YOLOv5-X6(v6.1)比较时,前者的AP增益比后者高0.9%,但参数减少45%,计算量减少63%,推理速度提高了47%。
  • YOLOv7-D6的推理速度与YOLOR-E6接近,但AP提高了0.8%。
  • YOLOv7-E6E的推理速度与YOLOR-D6接近,但AP提高了0.3%。

六、结论

本文提出了一种新的实时检测器。在研究过程中,本文发现了重参化模块的替换问题和动态标签的分配问题。为了解决这一问题,提出了一种可训练的bag-of-freebies策略来提高目标检测的精度。基于此,本文开发的YOLOv7系列目标检测模型获得了最先进的结果。

训练自己数据:

数据集准备:准备coco类型数据 ,新建MyDataCoco.yaml

# COCO 2017 dataset http://cocodataset.org

# download command/URL (optional)
# download: bash ./scripts/get_coco.sh

# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
train:yolov7/data/train.txt  # 118287 images
val:yolov7/data/val.txt  # 5000 images
test:yolov7/data/test.txt  # 20288 of 40670 images, submit to https://competitions.codalab.org/competitions/20794

# number of classes
nc: 10

# class names
names: ['lighthouse',
'sailboat',
'buoy',
'railbar',
'cargoship',
'navalvessels',
'passengership',
'dock',
'submarine',
'fishingboat' ]

 results:

YOLOX: Exceeding YOLO Series in 2021

https://yolox.readthedocs.io/en/latest/quick_run.html

论文地址:https://arxiv.org/abs/2107.08430

github:https://github.com/Megvii-BaseDetection/YOLOX

网络结构可视化: https://blog.csdn.net/nan355655600/article/details/119329848

YOLOX 是 YOLO 的无锚版本,设计更简单但性能更好!它旨在弥合研究和工业界之间的差距。

YOLO系列始终追求实时应用的最佳速度和精度取舍,提取了当时可用的最先进的检测技术(例如,anchor用于YOLOv2,残差网络用于YOLOv3),并优化最佳实践的实现。

然而在过去的两年中,目标检测学术界的主要进展集中在anchor_free检测器,高级标签分配策略和端到端(NMS-free)检测器,这些研究成果还没有被集成在YOLO系列中,YOLOv4和YOLOv5目前还是使用了anchor_based及手动的指定训练分配规则(比如anchor相关的设置)。

作者还认为,YOLOv4和YOLOv5中对anchor有点过度的优化,所以重新将YOLOv3-SPP版本作为优化起点。原因是,YOLOv3由于计算资源有限,在各种实际应用中软件支持不足,仍然是行业中应用最广泛的探测器之一。

1、网络结构:

先看下Yolov3、Yolov4、Yolov5的网络结构图,而后面的Yolox网络,都是在此基础上延伸而来的。

① Yolov3网络结构图

preview

Yolov3是在2018年提出,也是工业界使用非常广泛的目标检测算法。

不过在Yolox系列中的,Yolox-Darknet53模型,采用的Baseline基准网络,采用的并不是Yolov3版本,而是改进后的Yolov3_spp版本。

而Yolov3和Yolov3_spp的不同点在于,Yolov3的主干网络后面,添加了spp组件,这里需要注意。

② Yolov4网络结构图

上图是DarknetAB大神,在2020年提出的Yolov4算法。

在此算法中,网络的很多地方,都进行了改进。

比如输入端:采用Mosaic数据增强;

Backbone:采用了CSPDarknet53、Mish激活函数、Dropblock等方式;

Neck:采用了SPP(按照DarknetAB的设定)、FPN+PAN结构;

输出端:采用CIOU_Loss、DIOU_Nms操作。

因此可以看出,Yolov4对Yolov3的各个部分,都进行了很多的整合创新。

③ Yolov5网络结构图

而在Yolov5网络中,和Yolov4不同,最大的创新点在于,作者将网络结构,做成了可选择配置的方式。

比如主干网络结构,根据各个网络的宽度、高度不同,可以分为Yolov5s、Yolov5l、Yolov5s、Yolo5x等版本。

这种转变,在目标检测领域,引领了一股网络拆分的热潮。

本文的Yolox算法,也从这个角度出发,将Yolox模型,变为多种可选配的网络,比如标准网络结构和轻量级网络结构。

(1)标准网络结构:Yolox-s、Yolox-m、Yolox-l、Yolox-x、Yolox-Darknet53。

(2)轻量级网络结构:Yolox-Nano、Yolox-Tiny。

在实际的项目中,大家可以根据不同项目需求,进行挑选使用。

从上面的描述中,我们可以知道Yolox整体的改进思路:

(1)基准模型:Yolov3_spp

选择Yolov3_spp结构,并添加一些常用的改进方式,作为Yolov3 baseline基准模型;

(2)Yolox-Darknet53

对Yolov3 baseline基准模型,添加各种trick,比如Decoupled Head、SimOTA等,得到Yolox-Darknet53版本;

(3)Yolox-s、Yolox-m、Yolox-l、Yolox-x系列

对Yolov5的四个版本,采用这些有效的trick,逐一进行改进,得到Yolox-s、Yolox-m、Yolox-l、Yolox-x四个版本;

(4)轻量级网络

设计了Yolox-Nano、Yolox-Tiny轻量级网络,并测试了一些trick的适用性;

基准模型:Yolov3_spp

在设计算法时,为了对比改进trick的好坏,常常需要选择基准的模型算法。

而在选择Yolox的基准模型时,作者考虑到:

Yolov4和Yolov5系列,从基于锚框的算法角度来说,可能有一些过度优化,因此最终选择了Yolov3系列。

不过也并没有直接选择Yolov3系列中,标准的Yolov3算法,而是选择添加了spp组件,进而性能更优的Yolov3_spp版本。

以下是论文中的解释:

Considering YOLOv4 and YOLOv5 may be a little over-optimized for the anchor-based pipeline, we choose YOLOv3 [25] as our start point (we set YOLOv3-SPP as the default YOLOv3)。

为了便于理解,在前面Yolov3结构图的基础上,添加上spp组件,变为下图所示的Yolov3_spp网络。

大家可以看到,主干网络Backbone后面,增加了一个SPP组件。

当然在此基础上,对网络训练过程中的很多地方,都进行了改进,比如:

(1)添加了EMA权值更新、Cosine学习率机制等训练技巧

(2)使用IOU损失函数训练reg分支,BCE损失函数训练cls与obj分支

(3)添加了RandomHorizontalFlip、ColorJitter以及多尺度数据增广,移除了RandomResizedCrop。

在此基础上,Yolov3_spp的AP值达到38.5,即下图中的Yolov3 baseline。

Yolox-Darknet53

我们在前面知道,当得到Yolov3 baseline后,作者又添加了一系列的trick,最终改进为Yolox-Darknet53网络结构

上图即是Yolox-Darknet53网络结构图。

为了便于分析改进点,我们对Yolox-Darknet53网络结构进行拆分,变为四个板块:

① 输入端:Strong augmentation数据增强

② BackBone主干网络:主干网络没有什么变化,还是Darknet53。

③ Neck:没有什么变化,Yolov3 baseline的Neck层还是FPN结构。

④ Prediction:Decoupled Head、End-to-End YOLO、Anchor-free、Multi positives。

在经过一系列的改进后,Yolox-Darknet53最终达到AP47.3的效果。

下面我们对于Yolox-Darknet53的输入端、Backbone、Neck、Prediction四个部分,进行详解的拆解。

输入端:

(1)Strong augmentation

在网络的输入端,Yolox主要采用了Mosaic、Mixup两种数据增强方法。

而采用了这两种数据增强,直接将Yolov3 baseline,提升了2.4个百分点。

① Mosaic数据增强

Mosaic增强的方式,是U版YOLOv3引入的一种非常有效的增强策略。

而且在Yolov4、Yolov5算法中,也得到了广泛的应用。

通过随机缩放随机裁剪随机排布的方式进行拼接,对于小目标的检测效果提升,还是很不错的。

② MixUp数据增强

MixUp是在Mosaic基础上,增加的一种额外的增强策略。

主要来源于2017年,顶会ICLR的一篇论文《mixup: Beyond Empirical Risk Minimization》。当时主要应用在图像分类任务中,可以在几乎无额外计算开销的情况下,稳定提升1个百分点的分类精度。

而在Yolox中,则也应用到目标检测中,代码在yolox/datasets/mosaicdetection.py这个文件中。

其实方式很简单,比如我们在做人脸检测的任务

先读取一张图片,图像两侧填充,缩放到640*640大小,即Image_1,人脸检测框为红色框。

再随机选取一张图片,图像上下填充,也缩放到640*640大小,即Image_2,人脸检测框为蓝色框。

然后设置一个融合系数,比如上图中,设置为0.5,将Image_1和Image_2,加权融合,最终得到右面的Image。

从右图可以看出,人脸的红色框和蓝色框是叠加存在的。

我们知道,在Mosaic和Mixup的基础上,Yolov3 baseline增加了2.4个百分点

不过有两点需要注意:

(1)在训练的最后15个epoch,这两个数据增强会被关闭掉。

而在此之前,Mosaic和Mixup数据增强,都是打开的,这个细节需要注意。

(2)由于采取了更强的数据增强方式,作者在研究中发现,ImageNet预训练将毫无意义,因此,所有的模型,均是从头开始训练的。

2 Backbone

Yolox-Darknet53的Backbone主干网络,和原本的Yolov3 baseline的主干网络都是一样的。

都是采用Darknet53的网络结构

3 Neck

在Neck结构中,Yolox-Darknet53和Yolov3 baseline的Neck结构,也是一样的,都是采用FPN的结构进行融合。

如下图所示,FPN自顶向下,将高层的特征信息,通过上采样的方式进行传递融合,得到进行预测的特征图。

Prediction层

在输出层中,主要从四个方面进行讲解:Decoupled HeadAnchor Free标签分配、Loss计算。

(1)Decoupled Head

我们先来看一下Decoupled Head,目前在很多一阶段网络中都有类似应用,比如RetinaNet、FCOS等

而在Yolox中,作者增加了三个Decoupled Head,俗称“解耦头”

大白这里从两个方面对Decoupled Head进行讲解:

① 为什么使用Decoupled Head?

② Decoupled Head的细节?

从上图右面的Prediction中,我们可以看到,有三个Decoupled Head分支。

① 为什么使用Decoupled Head?

在了解原理前,我们先了解下改进的原因。为什么将原本的Yolo head,修改为Decoupled Head呢?

我们先看一张论文中的表格:

在前面3.2.1 基准网络中,我们知道Yolov3 baseline的AP值为38.5。

作者想继续改进,比如输出端改进为End-to-end的方式(即无NMS的形式)。

但意外的发现,改进完之后的AP值只有34.3

而在2020年12月份,旷视科技发表的《End-to-End Object Detection with Fully Convolution Network》中。

在对FCOS改进为无NMS时,在COCO上,达到了与有NMS的FCOS,相当的性能。

那这时就奇怪了,为什么在Yolo上改进,会下降这么多?

在偶然间,作者将End-to-End中的Yolo Head,修改为Decoupled Head的方式。

惊喜的发现,End-to-end Yolo的AP值,从34.3增加到38.8。

那End-to-end的方式有效果,Yolov3 baseline中是否也有效果呢?

然后作者又将Yolov3 baseline 中Yolo Head,也修改为Decoupled Head。

发现AP值,从38.5,增加到39.6

当然作者在实验中还发现,不单单是精度上的提高。替换为Decoupled Head后,网络的收敛速度也加快了。

但是需要注意的是:将检测头解耦,会增加运算的复杂度。

因此作者经过速度和性能上的权衡,最终使用 1个1×1 的卷积先进行降维,并在后面两个分支里,各使用了 2个3×3 卷积,最终调整到仅仅增加一点点的网络参数。

而且这里解耦后,还有一个更深层次的重要性:

Yolox的网络架构,可以和很多算法任务,进行一体化结合。

比如:

(1)YOLOX + Yolact/CondInst/SOLO ,实现端侧的实例分割。

(2)YOLOX + 34 层输出,实现端侧人体的 17 个关键点检测。

② Decoupled Head的细节?

了解了Decoupled Head的来源,再看一下Decoupled Head的细节。

我们将Yolox-Darknet53中,Decoupled Head①提取出来,经过前面的Neck层,这里Decouple Head①输入的长宽为20*20。

从图上可以看出,Concat前总共有三个分支

(1)cls_output:主要对目标框的类别,预测分数。因为COCO数据集总共有80个类别,且主要是N个二分类判断,因此经过Sigmoid激活函数处理后,变为20*20*80大小。

(2)obj_output:主要判断目标框是前景还是背景,因此经过Sigmoid处理好,变为20*20*1大小。

(3)reg_output:主要对目标框的坐标信息(x,y,w,h)进行预测,因此大小为20*20*4。

最后三个output,经过Concat融合到一起,得到20*20*85的特征信息。

当然,这只是Decoupled Head①的信息,再对Decoupled Head②和③进行处理。

Decoupled Head②输出特征信息,并进行Concate,得到40*40*85特征信息。

Decoupled Head③输出特征信息,并进行Concate,得到80*80*85特征信息

再对①②③三个信息,进行Reshape操作,并进行总体的Concat,得到8400*85的预测信息。

并经过一次Transpose,变为85*8400大小的二维向量信息。

这里的8400,指的是预测框的数量,而85是每个预测框的信息(reg,obj,cls)。

有了预测框的信息,下面我们再了解,如何将这些预测框和标注的框,即groundtruth进行关联,从而计算Loss函数,更新网络参数呢?

(2)Anchor-free

这里就要引入Anchor的内容,目前行业内,主要有Anchor Based和Anchor Free两种方式。

在Yolov3、Yolov4、Yolov5中,通常都是采用Anchor Based的方式,来提取目标框,进而和标注的groundtruth进行比对,判断两者的差距。

① Anchor Based方式

比如输入图像,经过Backbone、Neck层,最终将特征信息,传送到输出的Feature Map中。

这时,就要设置一些Anchor规则,将预测框和标注框进行关联。

从而在训练中,计算两者的差距,即损失函数,再更新网络参数。

比如在下图的,最后的三个Feature Map上,基于每个单元格,都有三个不同尺寸大小的锚框。

这里为了更形象的展示,以大白Yolov3视频中,输入图像大小416*416为例。

当输入为416*416时,网络最后的三个特征图大小为13*13,26*26,52*52。

我们可以看到,黄色框为小狗的Groundtruth,即标注框。

而蓝色的框,为小狗中心点所在的单元格,所对应的锚框,每个单元格都有3个蓝框。

当采用COCO数据集,即有80个类别时。

基于每个锚框,都有x、y、w、h、obj(前景背景)、class(80个类别),共85个参数。

因此会产生3*(13*13+26*26+52*52)*85=904995个预测结果。

如果将输入从416*416,变为640*640,最后的三个特征图大小为20*20,40*40,80*80。

则会产生3*(20*20+40*40+80*80)*85=2142000个预测结果。

② Anchor Free方式

而Yolox-Darknet53中,则采用Anchor Free的方式。

我们从两个方面,来对Anchor Free进行了解。

a.输出的参数量

我们先计算下,当得到包含目标框所有输出信息时,所需要的参数量?

这里需要注意的是:

最后黄色的85*8400,不是类似于Yolov3中的Feature Map,而是特征向量。

从图中可知,当输入为640*640时,最终输出得到的特征向量是85*8400。

我们看下,和之前Anchor Based方式,预测结果数量相差多少?

通过计算,8400*85=714000个预测结果,比基于Anchor Based的方式,少了2/3的参数量。

b.Anchor框信息

在前面Anchor Based中,我们知道,每个Feature map的单元格,都有3个大小不一的锚框。

那么Yolox-Darknet53就没有吗?

其实并不然,这里只是巧妙的,将前面Backbone中,下采样的大小信息引入进来。

比如上图中,最上面的分支,下采样了5次,2的5次方为32

并且Decoupled Head①的输出,为20*20*85大小。

因此如上图所示:

最后8400个预测框中,其中有400个框,所对应锚框的大小,为32*32。

同样的原理,中间的分支,最后有1600个预测框,所对应锚框的大小,为16*16。

最下面的分支,最后有6400个预测框,所对应锚框的大小,为8*8。

当有了8400个预测框的信息,每张图片也有标注的目标框的信息。

这时的锚框,就相当于桥梁。

这时需要做的,就是将8400个锚框,和图片上所有的目标框进行关联,挑选出正样本锚框。

而相应的,正样本锚框所对应的位置,就可以将正样本预测框,挑选出来。

这里采用的关联方式,就是标签分配。

(3)标签分配

当有了8400个Anchor锚框后,这里的每一个锚框,都对应85*8400特征向量中的预测框信息。

不过需要知道,这些预测框只有少部分是正样本,绝大多数是负样本。

那么到底哪些是正样本呢?

这里需要利用锚框和实际目标框的关系,挑选出一部分适合的正样本锚框。

比如第3、10、15个锚框是正样本锚框,则对应到网络输出的8400个预测框中,第3、10、15个预测框,就是相应的正样本预测框。

训练过程中,在锚框的基础上,不断的预测,然后不断的迭代,从而更新网络参数,让网络预测的越来越准。

那么在Yolox中,是如何挑选正样本锚框的呢?

这里就涉及到两个关键点:初步筛选SimOTA。

① 初步筛选

初步筛选的方式主要有两种:根据中心点来判断根据目标框来判断

这部分的代码,在models/yolo_head.py的get_in_boxes_info函数中。

a. 根据中心点来判断:

规则:寻找anchor_box中心点,落在groundtruth_boxes矩形范围的所有anchors。

比如在get_in_boxes_info的代码中,通过groundtruth的[x_center,y_center,w,h],计算出每张图片的每个groundtruth的左上角、右下角坐标。

为了大家更容易理解,大白以人脸检测的任务绘制图片:

通过上面的公式,可以对左面人脸图片,计算出左上角(gt_l,gt_t),右下角(gt_r,gt_b)。

groundtruth的矩形框范围确定了,再根据范围去选择适合的锚框。

这里再绘制一个锚框的中心点,(x_center,y_center)。

而右面的图片,就是寻找锚框和groundtruth的对应关系。

即计算锚框中心点(x_center,y_center),和人脸标注框左上角(gt_l,gt_t),右下角(gt_r,gt_b)两个角点的相应距离。

比如下面代码图片中的前四行代码

而在第五行,将四个值叠加之后,通过第六行,判断是否都大于0?

就可以将落在groundtruth矩形范围内的所有anchors,都提取出来了。

因为ancor box的中心点,只有落在矩形范围内,这时的b_l,b_r,b_t,b_b都大于0。

b.根据目标框来判断:

除了根据锚框中心点,和groundtruth两边距离判断的方式外,作者还设置了根据目标框判断的方法。

规则:以groundtruth中心点为基准,设置边长为5的正方形,挑选在正方形内的所有锚框。

同样在get_in_boxes_info的代码中,通过groundtruth的[x_center,y_center,w,h],绘制了一个边长为5的正方形。

为了大家容易理解,大白还是以人脸检测的任务绘制图片:

在左面的人脸图片中,基于人脸标注框的中心点,利用上面的公式,绘制了一个边长为5的正方形。左上角点为(gt_l,gt_t),右下角点为(gt_r,gt_b)。

这时groundtruth正方形范围确定了,再根据范围去挑选锚框。

而右面的图片,就是找出所有中心点(x_center,y_center)在正方形内的锚框。

在代码图片中的前四行代码,也是计算锚框中心点,和正方形两边的距离。

通过第五行的叠加,再在第六行,判断c_l,c_r,c_t,c_b是否都大于0?

就可以将落在边长为5的正方形范围内,所有的anchors,都提取出来了,因为这时的c_l,c_r,c_t,c_b都大于0。

经过上面两种挑选的方式,就完成初步筛选了,挑选出一部分候选的anchor,进入下一步的精细化筛选。

② 精细化筛选

而在精细化筛选中,就用到论文中提到的SimOTA了:

从提升效果上来看,引入SimOTA后,AP值提升了2.3个百分点,还是非常有效的。

而SimOAT方法的提出,主要来源于旷视科技,2021年初CVPR上的一篇论文:《Ota: Optimal transport assignment for object detection》

我们将SimOTA的前后流程进行拆解,看一下是如何进行精细化筛选的?

整个筛选流程,主要分为四个阶段:

a.初筛正样本信息提取

b.Loss函数计算

c.cost成本计算

d.SimOTA求解

为了便于理解,我们假定图片上有3个目标框,即3个groundtruth。

再假定目前在做的项目是对人脸和人体检测,因此检测类别是2。

上一节中,我们知道有8400个锚框,但是经过初步筛选后,假定有1000个锚框是正样本锚框。

a.初筛正样本信息提取

初筛出的1000个正样本锚框的位置,我们是知道的。

所有锚框的位置,和网络最后输出的85*8400特征向量是一一对应。

所以根据位置,可以将网络预测的候选检测框位置bboxes_preds前景背景目标分数obj_preds类别分数cls_preds等信息,提取出来。

上面的代码位于yolo_head.py的get_assignments函数中。

以前面的假定信息为例,代码图片中的bboxes_preds_per_image因为是候选检测框的信息,因此维度为[1000,4]

obj_preds因为是目标分数,所以维度是[1000,1]

cls_preds因为是类别分数,所以维度是[1000,2]

b.Loss函数计算

针对筛选出的1000个候选检测框,和3个groundtruth计算Loss函数。

计算的代码,也在yolo_head.py的get_assignments函数中。

首先是位置信息的loss值:pair_wise_ious_loss

通过第一行代码,可以计算出3个目标框,和1000个候选框,每个框相互之间的iou信息pair_wise_ious,因为向量维度为[3,1000]。

再通过-torch.log计算,得到位置损失,即代码中的pair_wise_iou_loss。

然后是综合类别信息和目标信息的loss值:pair_wise_cls_loss

通过第一行代码,将类别的条件概率和目标的先验概率做乘积,得到目标的类别分数。

再通过第二行代码,F.binary_cross_entroy的处理,得到3个目标框和1000个候选框的综合loss值,即pair_wise_cls_loss,向量维度为[3,1000]。

c.cost成本计算

有了reg_loss和cls_loss,就可以将两个损失函数加权相加,计算cost成本函数了。

这里涉及到论文中提到的一个公式:

相应的,对应于yolo_head.py的get_assignments函数中的代码:

可以看出,公式中的加权系数,即代码中的3。

d.SimOTA

有了上面的一系列信息,标签分配问题,就转换为了标准的OTA问题。

但是经典的Sinkhorn-Knopp算法,需要多次迭代求得最优解。

作者也提到,该算法会导致25%额外训练时间,所以采用一种简化版的SimOTA方法,求解近似最优解。这里对应的函数,是get_assignments函数中的self.dynamic_k_matching:

其中的流程如下:

第一步:设置候选框数量

首先按照cost值的大小,新建一个全0变量matching_matrix,这里是[3,1000]。

通过上面第二行代码,设置候选框数量为10。

再通过第三行代码,从前面的pair_wise_ious中,给每个目标框,挑选10个iou最大的候选框。

因为前面假定有3个目标,因此这里topk_ious的维度为[3,10]。

第二步:通过cost挑选候选框

下面再通过topk_ious的信息,动态选择候选框,这里是个关键。

代码如dynamic_k_matching函数中,下图所示:

为了便于大家理解,大白先把第一行制作成图示效果。

这里的topk_ious,是3个目标框和预测框中,最大iou的10个候选框:

经过torch.clamp函数,得到最终右面的dynamic_ks值。

我们就知道,目标框1和3,给他分配3个候选框,而目标框2,给它分配4个候选框。

那么基于什么标准分配呢?

这时就要利用前面计算的cost值,即[3,1000]的损失函数加权信息。

在for循环中,针对每个目标框挑选,相应的cost值最低的一些候选框。

比如右面的matching_matrix中,cost值最低的一些位置,数值为1,其余位置都为0。

因为目标框1和3,dynamic_ks值都为3,因此matching_matrix的第一行和第三行,有3个1。

而目标框2,dynamic_ks值为4,因此matching_matrix的第二行,有4个1。

第三步:过滤共用的候选框

不过在分析matching_matrix时,我们发现,第5列有两个1。

这也就说明,第五列所对应的候选框,被目标检测框1和2,都进行关联。

因此对这两个位置,还要使用cost值进行对比,选择较小的值,再进一步筛选。

这里为了便于理解,还是采用图示的方式:

首先第一行代码,将matching_matrix,对每一列进行相加。

这时anchor_matching_gt中,只要有大于1的,说明有共用的情况。

上图案例中,表明第5列存在共用的情况。

再利用第三行代码,将cost中,第5列的值取出,并进行比较,计算最小值所对应的行数,以及分数。

我们将第5列两个位置,假设为0.4和0.3。

经过第三行代码,可以找到最小的值是0.3,即cost_min为0.3,所对应的行数,cost_argmin为2。

经过第四行代码,将matching_matrix第5列都置0。

再利用第五行代码,将matching_matrix第2行,第5列的位置变为1。

最终我们可以得到3个目标框,最合适的一些候选框,即matching_matrix中,所有1所对应的位置。

(4)Loss计算

经过第三部分的标签分配,就可以将目标框和正样本预测框对应起来了。

下面就可以计算两者的误差,即Loss函数。

计算的代码,位于yolo_head.py的get_losses函数中。

我们可以看到:

检测框位置的iou_loss,Yolox中使用传统的iou_loss,和giou_loss两种,可以进行选择。

而obj_loss和cls_loss,都是采用BCE_loss的方式。

当然除此之外,还有两点需要注意

a.在前面精细化筛选中,使用了reg_loss和cls_loss,筛选出和目标框所对应的预测框。

因此这里的iou_loss和cls_loss,只针对目标框和筛选出的正样本预测框进行计算。

而obj_loss,则还是针对8400个预测框。

b.在Decoupled Head中,cls_outputobj_output使用了sigmoid函数进行归一化,

但是在训练时,并没有使用sigmoid函数,原因是训练时用的nn.BCEWithLogitsLoss函数,已经包含了sigmoid操作。

而在推理过程中,是使用Sigmoid函数的。

文章来源:https://zhuanlan.zhihu.com/p/397993315

yolov6 又快又准的目标检测框架

多年来,YOLO 系列一直是高效目标检测的行业标准。YOLO 社区蓬勃发展,丰富了其在众多硬件平台和丰富场景中的使用。在这份技术报告力求将其极限推向新的高度,以坚定不移的行业应用心态向前迈进。

考虑到真实环境中对速度和准确性的不同要求,作者广泛研究了来自工业界或学术界的最新目标检测进展。具体来说,从最近的网络设计、训练策略、测试技术、量化和优化方法中大量吸收了一些想法。最重要的是,整合思想和实践,构建了一套不同规模的部署网络,以适应多样化的用例。

在 YOLO 作者的慷慨许可下,作者将其命名为 YOLOv6。作者也热烈欢迎用户和贡献者进一步增强。YOLOv6-N 在 NVIDIA Tesla T4 GPU 上以 1234 FPS 的吞吐量在 COCO 数据集上达到 35.9% 的 AP。YOLOv6-S 以 495 FPS 的速度达到 43.5% 的 AP,优于同规模的其他主流检测器(YOLOv5-SYOLOX-S 和 PPYOLOE-S)。

YOLOv6-S 量化版本甚至带来了 869 FPS 的最新 43.3% AP。此外,与具有相似推理速度的其他检测器相比,YOLOv6-M/L 还实现了更好的准确度性能(即 49.5%/52.3%)。

近日,美团视觉智能部研发了一款致力于工业应用的目标检测框架 YOLOv6,能够同时专注于检测的精度和推理效率。在研发过程中,视觉智能部不断进行了探索和优化,同时吸取借鉴了学术界和工业界的一些前沿进展和科研成果。在目标检测权威数据集 COCO 上的实验结果显示,YOLOv6 在检测精度和速度方面均超越其他同体量的算法,同时支持多种不同平台的部署,极大简化工程部署时的适配工作。特此开源,希望能帮助到更多的同学。

YOLOv6 是美团视觉智能部研发的一款目标检测框架,致力于工业应用。本框架同时专注于检测的精度和推理效率,在工业界常用的尺寸模型中:YOLOv6-nano 在 COCO 上精度可达 35.0% AP,在 T4 上推理速度可达 1242 FPS;YOLOv6-s 在 COCO 上精度可达 43.1% AP,在 T4 上推理速度可达 520 FPS。在部署方面,YOLOv6 支持 GPU(TensorRT)、CPU(OPENVINO)、ARM(MNN、TNN、NCNN)等不同平台的部署,极大地简化工程部署时的适配工作。

目前,项目已开源至Github,传送门:YOLOv6。欢迎有需要的小伙伴们Star收藏,随时取用。

精度与速度远超 YOLOv5 和 YOLOX 的新框架

目标检测作为计算机视觉领域的一项基础性技术,在工业界得到了广泛的应用,其中 YOLO 系列算法因其较好的综合性能,逐渐成为大多数工业应用时的首选框架。至今,业界已衍生出许多 YOLO 检测框架,其中以 YOLOv5[1]、YOLOX[2] 和 PP-YOLOE[3] 最具代表性,但在实际使用中,我们发现上述框架在速度和精度方面仍有很大的提升的空间。基于此,我们通过研究并借鉴了业界已有的先进技术,开发了一套新的目标检测框架——YOLOv6。该框架支持模型训练、推理及多平台部署等全链条的工业应用需求,并在网络结构、训练策略等算法层面进行了多项改进和优化,在 COCO 数据集上,YOLOv6 在精度和速度方面均超越其他同体量算法,相关结果如下图 1 所示:

图1-1 YOLOv6 各尺寸模型与其他模型性能对比
图1-1 YOLOv6 各尺寸模型与其他模型性能对比
图1-2 YOLOv6 与其他模型在不同分辨率下性能对比
图1-2 YOLOv6 与其他模型在不同分辨率下性能对比

图 1-1 展示了不同尺寸网络下各检测算法的性能对比,曲线上的点分别表示该检测算法在不同尺寸网络下(s/tiny/nano)的模型性能,从图中可以看到,YOLOv6 在精度和速度方面均超越其他 YOLO 系列同体量算法。

图 1-2 展示了输入分辨率变化时各检测网络模型的性能对比,曲线上的点从左往右分别表示图像分辨率依次增大时(384/448/512/576/640)该模型的性能,从图中可以看到,YOLOv6 在不同分辨率下,仍然保持较大的性能优势。

2. YOLOv6关键技术介绍

YOLOv6 主要在 BackBone、Neck、Head 以及训练策略等方面进行了诸多的改进:

  • 设计了更高效的 Backbone 和 Neck :受到硬件感知神经网络设计思想的启发,基于 RepVGG style[4] 设计了可重参数化、更高效的骨干网络 EfficientRep Backbone 和 Rep-PAN Neck。
  • 优化设计了更简洁有效的 Efficient Decoupled Head,在维持精度的同时,进一步降低了一般解耦头带来的额外延时开销。
  • 在训练策略上,我们采用Anchor-free 无锚范式,同时辅以 SimOTA[2] 标签分配策略以及 SIoU[9] 边界框回归损失来进一步提高检测精度。

将 YOLOv6 的主要方面总结如下:

  • 针对不同场景中的工业应用重新设计了一系列不同规模的网络。不同规模的架构各不相同,以实现最佳的速度和准确性权衡,其中小型模型具有简单的单路径主干,大型模型建立在高效的多分支块上。
  • 为 YOLOv6 注入了一种self-distillation策略,在分类任务和回归任务上都执行。同时,动态调整来自教师和标签的知识,以帮助学生模型在所有训练阶段更有效地学习知识。
  • 广泛验证标签分配、损失函数和数据增强技术的先进检测技术,并有选择地采用它们以进一步提高性能。
  • 在 RepOptimizer 和通道蒸馏的帮助下改进了检测的量化方案,这带来了具有 43.3% 的 COCO AP 和 869 FPS 的吞吐量的快速准确的检测器,批量大小为 32。

2.1 Hardware-friendly 的骨干网络设计

YOLOv5/YOLOX 使用的 Backbone 和 Neck 都基于 CSPNet[5] 搭建,采用了多分支的方式和残差结构。对于 GPU 等硬件来说,这种结构会一定程度上增加延时,同时减小内存带宽利用率。下图 2 为计算机体系结构领域中的 Roofline Model[8] 介绍图,显示了硬件中计算能力和内存带宽之间的关联关系。

图2 Roofline Model 介绍图
图2 Roofline Model 介绍图

于是,我们基于硬件感知神经网络设计的思想,对 Backbone 和 Neck 进行了重新设计和优化。该思想基于硬件的特性、推理框架/编译框架的特点,以硬件和编译友好的结构作为设计原则,在网络构建时,综合考虑硬件计算能力、内存带宽、编译优化特性、网络表征能力等,进而获得又快又好的网络结构。对上述重新设计的两个检测部件,我们在 YOLOv6 中分别称为 EfficientRep Backbone Rep-PAN Neck,其主要贡献点在于:

  1. 引入了 RepVGG[4] style 结构。
  2. 基于硬件感知思想重新设计了 Backbone 和 Neck。

RepVGG[4] Style 结构是一种在训练时具有多分支拓扑,而在实际部署时可以等效融合为单个 3×3 卷积的一种可重参数化的结构(融合过程如下图 3 所示)。通过融合成的 3×3 卷积结构,可以有效利用计算密集型硬件计算能力(比如 GPU),同时也可获得 GPU/CPU 上已经高度优化的 NVIDIA cuDNN 和 Intel MKL 编译框架的帮助。

实验表明,通过上述策略,YOLOv6 减少了在硬件上的延时,并显着提升了算法的精度,让检测网络更快更强。以 nano 尺寸模型为例,对比 YOLOv5-nano 采用的网络结构,本方法在速度上提升了21%,同时精度提升 3.6% AP。

图3 Rep算子的融合过程[4]
图3 Rep算子的融合过程[4]

EfficientRep Backbone:在 Backbone 设计方面,我们基于以上 Rep 算子设计了一个高效的Backbone。相比于 YOLOv5 采用的 CSP-Backbone,该 Backbone 能够高效利用硬件(如 GPU)算力的同时,还具有较强的表征能力。

下图 4 为 EfficientRep Backbone 具体设计结构图,将 Backbone 中 stride=2 的普通 Conv 层替换成了 stride=2 的 RepConv层。同时,将原始的 CSP-Block 都重新设计为 RepBlock,其中 RepBlock 的第一个 RepConv 会做 channel 维度的变换和对齐。另外,我们还将原始的 SPPF 优化设计为更加高效的 SimSPPF。

图4 EfficientRep Backbone 结构图
图4 EfficientRep Backbone 结构图

Rep-PAN:在 Neck 设计方面,为了让其在硬件上推理更加高效,以达到更好的精度与速度的平衡,我们基于硬件感知神经网络设计思想,为 YOLOv6 设计了一个更有效的特征融合网络结构。

Rep-PAN 基于 PAN[6] 拓扑方式,用 RepBlock 替换了 YOLOv5 中使用的 CSP-Block,同时对整体 Neck 中的算子进行了调整,目的是在硬件上达到高效推理的同时,保持较好的多尺度特征融合能力(Rep-PAN 结构图如下图 5 所示)。

图5 Rep-PAN 结构图
图5 Rep-PAN 结构图

2.2 更简洁高效的 Decoupled Head

在 YOLOv6 中,我们采用了解耦检测头(Decoupled Head)结构,并对其进行了精简设计。原始 YOLOv5 的检测头是通过分类和回归分支融合共享的方式来实现的,而 YOLOX 的检测头则是将分类和回归分支进行解耦,同时新增了两个额外的 3×3 的卷积层,虽然提升了检测精度,但一定程度上增加了网络延时。

因此,我们对解耦头进行了精简设计,同时综合考虑到相关算子表征能力和硬件上计算开销这两者的平衡,采用 Hybrid Channels 策略重新设计了一个更高效的解耦头结构,在维持精度的同时降低了延时,缓解了解耦头中 3×3 卷积带来的额外延时开销。通过在 nano 尺寸模型上进行消融实验,对比相同通道数的解耦头结构,精度提升 0.2% AP 的同时,速度提升6.8%。

图6 Efficient Decoupled Head 结构图
图6 Efficient Decoupled Head 结构图

2.3 更有效的训练策略

为了进一步提升检测精度,我们吸收借鉴了学术界和业界其他检测框架的先进研究进展:Anchor-free 无锚范式 、SimOTA 标签分配策略以及 SIoU 边界框回归损失。

Anchor-free 无锚范式

YOLOv6 采用了更简洁的 Anchor-free 检测方法。由于 Anchor-based检测器需要在训练之前进行聚类分析以确定最佳 Anchor 集合,这会一定程度提高检测器的复杂度;同时,在一些边缘端的应用中,需要在硬件之间搬运大量检测结果的步骤,也会带来额外的延时。而 Anchor-free 无锚范式因其泛化能力强,解码逻辑更简单,在近几年中应用比较广泛。经过对 Anchor-free 的实验调研,我们发现,相较于Anchor-based 检测器的复杂度而带来的额外延时,Anchor-free 检测器在速度上有51%的提升。

SimOTA 标签分配策略

为了获得更多高质量的正样本,YOLOv6 引入了 SimOTA [4]算法动态分配正样本,进一步提高检测精度。YOLOv5 的标签分配策略是基于 Shape 匹配,并通过跨网格匹配策略增加正样本数量,从而使得网络快速收敛,但是该方法属于静态分配方法,并不会随着网络训练的过程而调整。

近年来,也出现不少基于动态标签分配的方法,此类方法会根据训练过程中的网络输出来分配正样本,从而可以产生更多高质量的正样本,继而又促进网络的正向优化。例如,OTA[7] 通过将样本匹配建模成最佳传输问题,求得全局信息下的最佳样本匹配策略以提升精度,但 OTA 由于使用了Sinkhorn-Knopp 算法导致训练时间加长,而 SimOTA[4]算法使用 Top-K 近似策略来得到样本最佳匹配,大大加快了训练速度。故 YOLOv6 采用了SimOTA 动态分配策略,并结合无锚范式,在 nano 尺寸模型上平均检测精度提升 1.3% AP。

SIoU 边界框回归损失

为了进一步提升回归精度,YOLOv6 采用了 SIoU[9] 边界框回归损失函数来监督网络的学习。目标检测网络的训练一般需要至少定义两个损失函数:分类损失和边界框回归损失,而损失函数的定义往往对检测精度以及训练速度产生较大的影响。

近年来,常用的边界框回归损失包括IoU、GIoU、CIoU、DIoU loss等等,这些损失函数通过考虑预测框与目标框之前的重叠程度、中心点距离、纵横比等因素来衡量两者之间的差距,从而指导网络最小化损失以提升回归精度,但是这些方法都没有考虑到预测框与目标框之间方向的匹配性。SIoU 损失函数通过引入了所需回归之间的向量角度,重新定义了距离损失,有效降低了回归的自由度,加快网络收敛,进一步提升了回归精度。通过在 YOLOv6s 上采用 SIoU loss 进行实验,对比 CIoU loss,平均检测精度提升 0.3% AP。

3. 实验结果

经过以上优化策略和改进,YOLOv6 在多个不同尺寸下的模型均取得了卓越的表现。下表 1 展示了 YOLOv6-nano 的消融实验结果,从实验结果可以看出,我们自主设计的检测网络在精度和速度上都带来了很大的增益。

表1 YOLOv6-nano 消融实验结果
表1 YOLOv6-nano 消融实验结果

下表 2 展示了 YOLOv6 与当前主流的其他 YOLO 系列算法相比较的实验结果。从表格中可以看到:

表2 YOLOv6各尺寸模型性能与其他模型的比较
表2 YOLOv6各尺寸模型性能与其他模型的比较
  • YOLOv6-nano 在 COCO val 上 取得了 35.0% AP 的精度,同时在 T4 上使用 TRT FP16 batchsize=32 进行推理,可达到 1242FPS 的性能,相较于 YOLOv5-nano 精度提升 7% AP,速度提升 85%。
  • YOLOv6-tiny 在 COCO val 上 取得了 41.3% AP 的精度, 同时在 T4 上使用 TRT FP16 batchsize=32 进行推理,可达到 602FPS 的性能,相较于 YOLOv5-s 精度提升 3.9% AP,速度提升 29.4%。
  • YOLOv6-s 在 COCO val 上 取得了 43.1% AP 的精度, 同时在 T4 上使用 TRT FP16 batchsize=32 进行推理,可达到 520FPS 的性能,相较于 YOLOX-s 精度提升 2.6% AP,速度提升 38.6%;相较于 PP-YOLOE-s 精度提升 0.4% AP的条件下,在T4上使用 TRT FP16 进行单 batch 推理,速度提升 71.3%。

在海面图片(自己的训练集)上的训练:

is_coco: False
# Classes
nc: 10  # number of classes
names: ['lighthouse',
'sailboat',
'buoy',
'railbar',
'cargoship',
'navalvessels',
'passengership',
'dock',
'submarine',
'fishingboat']  # class names

yolov6s 结果: coco_detection_metrics ——COCO检测指标

Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.779
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.992
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.922
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.661
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.710
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.817
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.697
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.801
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.813
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.686
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.740
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.847

Average Precision (AP)和Average Recall (AR)等等这些都是啥意思?

  • IoU=0.50意味着IoU大于0.5被认为是检测到。
  • IoU=0.50:0.95意味着IoU在0.5到0.95的范围内被认为是检测到。
  • 越低的IoU阈值,则判为正确检测的越多,相应的,Average Precision (AP)也就越高。参考上面的第二第三行。
  • small表示标注的框面积小于32 * 32
  • medium表示标注的框面积同时小于96 * 96
  • large表示标注的框面积大于等于96 * 96
  • all表示不论大小,我都要。
  • maxDets=100表示最大检测目标数为100。

 Average Precision (AP)和Average Recall (AR)值里面有-1是什么情况?

参考:https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/cocoeval.py#L52

标注里面没有此类型的目标框,则Average PrecisionAverage Recall值为-1。

Recall   召回率(查全率)。表示正确识别物体A的个数占测试集中物体A的总个数的百分数,即所有正例中预测正确的概率,Recall = tpr = TP / (TP+FN)

Precision 精确率(查准率)。表示正确识别物体A的个数占总识别出的物体个数n的百分数,即预测为正例中预测正确的概率,Precision = TP / (TP+FP)

以下12个指标用于表征COCO上物体检测器的性能:

Average Precision (AP):

AP                              % AP at IoU=0.50:0.05:0.95 (primary challenge metric)

APIoU=.50                   % AP at IoU=0.50 (PASCAL VOC metric)

APIoU=.75                   % AP at IoU=0.75 (strict metric)

AP Across Scales:

APsmall                       % AP for small objects: area < 322

APmedium                   % AP for medium objects: 322 < area < 962

APlarge                        % AP for large objects: area > 962

Average Recall (AR):

ARmax=1                     % AR given 1 detection per image

ARmax=10                   % AR given 10 detections per image

ARmax=100                 % AR given 100 detections per image

AR Across Scales:

ARsmall                       % AR for small objects: area < 322

ARmedium                   % AR for medium objects: 322 < area < 962

ARlarge                        % AR for large objects: area > 962

1)除非另有说明,否则AP和AR在多个交汇点(IoU)值上取平均值。具体来说,我们使用10个IoU阈值0.50:0.05:0.95。这是对传统的一个突破,其中AP是在一个单一的0.50的IoU上计算的(这对应于我们的度量APIoU=.50 )。超过均值的IoUs能让探测器更好定位(Averaging over IoUs rewards detectors with better localization.)。

2)AP是所有类别的平均值。传统上,这被称为“平均精确度”(mAP,mean average precision)。我们没有区分AP和mAP(同样是AR和mAR),并假定从上下文中可以清楚地看出差异。

3)AP(所有10个IoU阈值和所有80个类别的平均值)将决定赢家。在考虑COCO性能时,这应该被认为是最重要的一个指标

4)在COCO中,比大物体相比有更多的小物体。具体地说,大约41%的物体很小(面积<322),34%是中等(322 < area < 962)),24%大(area > 962)。测量的面积(area)是分割掩码(segmentation mask)中的像素数量。

5)AR是在每个图像中检测到固定数量的最大召回(recall),在类别和IoU上平均。AR与提案评估(proposal evaluation)中使用的同名度量相关,但是按类别计算。

6)所有度量标准允许每个图像(在所有类别中)最多100个最高得分检测进行计算。

7)除了IoU计算(分别在框(box)或掩码(mask)上执行)之外,用边界框和分割掩码检测的评估度量在所有方面是相同的。

测试速度:

img show:

中兴大赛

中兴捧月大赛 :https://zte.hina.com/zte/index

图像去噪赛题背景图像去噪是机器视觉领域重要任务,图像去噪模块在安防,自动驾驶,传感,医学影像,消费电子等领域都是重要的前端图像处理模块。消费级电子产品(例如手机)出于成本考虑,在低照度和高ISO条件下,噪声对成像质量的降级更加严重。对于传统图像处理算法,常见去噪算法包含双边(bilateral)滤波,NLM (non local mean)滤波,BM3D,多帧(3D)降噪方案等多种方案,产品实现上需要兼顾性能和复杂度。
AI可进一步提升图像主客观质量在学术和工业界得到了广泛认证。对于手机产品,AI正快速补充和替代传统手机ISP(Image signal processing)中的痛点难点,例如可进行AI-based去噪,动态范围增强,超分辨,超级夜景,甚至AI ISP等。

提交说明

1. 参赛者需要根据举办方提供的10张noisy图片提交相应10张denoise图片存放至文件夹“data”下,命名方式为denoise0.dng至denoise9.dng,注意上传denoise RAW图值域为[black_level, white_level] = [1024,16383],可参照baseline代码;
2. 参赛者需要提交模型文件和参数文件至文件夹“algorithm/models/”下,模型文件命名方式为network.py,参数文件命名pytorch对应model.pth,tensorflow对应model.h5。模型参数文件大小限制为50M;
3. 若使用非AI方法,算法文件提交至以上相同路径,文件命名为alg.py;
4. 参赛者需要提交文档报告阐述所使用方法,文档存放在algorithm二级目录下;
5. data和algorithm按照二级目录结构进行放置,将二级目录放置于命名为result的一级目录内,将一级目录result压缩成.zip格式上传;

赛题简介本次题目围绕手机图片RAW域去噪问题,参赛者算法方案使用基于AI或传统图像处理算法均可。
比赛目标是提升举办方提供给参赛者10张noisy图片的PSNR和SSIM指标。为了方便参赛者轻松上手流程,举办方为参赛者提供baseline代码示例,以及training dataset(200张图片)以帮助参赛者更好地提升算法性能。根据参赛者所提交算法的原创性,额外有5% bonus分数浮动。

比赛排名 55/1159

项目参考 论文: Simple Baselines for Image Restoration

参考:Simple Baselines for Image Restoration  
单位:旷视  
代码:https://github.com/megvii-research/NAFNet  
论文:https://arxiv.org/abs/2204.0467

项目介绍:

一、网络结构:

1.1使用类Unet结构:

如下图,Unet 网络结构是对称的,形似英文字母 U 所以被称为 Unet。通过拼接的方式将不同层次的特征进行通道拼接。其中网络中主要使用了NAFBlock块。U-Net和FCN非常的相似,U-Net比FCN稍晚提出来,但都发表在2015年,和FCN相比,U-Net的第一个特点是完全对称,也就是左边和右边是很类似的,而FCN的decoder相对简单,只用了一个deconvolution(反卷积)的操作,之后并没有跟上卷积结构。第二个区别就是skip connection,FCN用的是加操作(summation),U-Net用的是叠操作(concatenation)。这些都是细节,重点是它们的结构用了一个比较经典的思路,也就是编码和解码(encoder-decoder),早在2006年就被Hinton大神提出来发表在了nature上.

当时这个结构提出的主要作用并不是分割,而是压缩图像和去噪声。输入是一幅图,经过下采样的编码,得到一串比原先图像更小的特征,相当于压缩,然后再经过一个解码,理想状况就是能还原到原来的图像。这样的话我们存一幅图的时候就只需要存一个特征和一个解码器即可。这个想法我个人认为是很漂亮了。同理,这个思路也可以用在原图像去噪,做法就是在训练的阶段在原图人为的加上噪声,然后放到这个编码解码器中,目标是可以还原得到原图。

后来把这个思路被用在了图像分割的问题上,也就是现在我们看到的U-Net结构,在它被提出的三年中,有很多很多的论文去讲如何改进U-Net或者FCN,不过这个分割网络的本质的拓扑结构是没有改动的。举例来说,ICCV上凯明大神提出的Mask RCNN. 相当于一个检测,分类,分割的集大成者,我们仔细去看它的分割部分,其实使用的也就是这个简单的FCN结构。说明了这种“U形”的编码解码结构确实非常的简洁,并且最关键的一点是好用。

采用Unet的好处我感觉是:网络层越深得到的特征图,有着更大的视野域,浅层卷积关注纹理特征,深层网络关注本质的那种特征,所以深层浅层特征都是有格子的意义的;另外一点是通过反卷积得到的更大的尺寸的特征图的边缘,是缺少信息的,毕竟每一次下采样提炼特征的同时,也必然会损失一些边缘特征,而失去的特征并不能从上采样中找回,因此通过特征的拼接,来实现边缘特征的一个找回。

最后,将网络输出和input做加和,这样实际上是用网络做噪声的预测,想比直接输出图像,输出噪声的实际效果好,个人认为,如果输出的是图像,那么即使用unet结构,在进行conv、layernormal过程中还会造成图像的细节特征损失,对于生成的图像细节方面会差一些。总之,我认为直接预测图像的task会比预测噪声的难度大。

下图1是本次设计的图像去噪网络结构:

Figure 1 NFnet网络结构

二、NAFBlock块

(使用论文Simple Baselines for Image Restoration中的NAFBlock模块)

NAFBlock结构介绍:

  1. Normalization:Layer Normalization
  2. 加速训练(可以使用更大的learning rate)
  3. 防止exploding/vanishing gradients.
  4. 减小参数的initialization对训练的影响
  5. 提高训练效果
  6. layerNorm关注整幅图,也没有超过单张的范围,LN将每个训练样本都归一化到了相同的分布上,某种意义上避免了平滑

对于Normalization,文章采用了Transformer里被通常采用的LayerNorm,并通过实验发现其能提点。其实传统意义上,除了早期的方法,底层视觉一般是不太会增加归一化层,认为其会降点而且让图像模糊,我个人理解这可能和BatchNorm的特性有关,一方面BatchNorm本身训练测试阶段由于统计量不同,就会导致领域不适应问题。另外不同于high-level task倾向于寻找一致性表示,底层视觉的任务与之相反,往往是倾向于学习图片特定性以增强细节的恢复效果(比如之前有人通过捕获图像分布(正态分布)的sigma以增强边缘区域的效果),batchNorm由于是batch内做attention,其实很容易将其他图片的信息引入,忽略了恢复图像的特定信息,导致性能下降。所以之前底层视觉里面用的比较多的norm是instance Norm(比较多的是在风格迁移,图像恢复这边有HI-Net就是用IN),因为只关注同一个图片同一channel内的信息,所以某种意义上避免了平滑,layerNorm关注整幅图,也没有超过单张的范围,所以能够work还是蛮make sense的。

归一化技术在high-level任务中已被广泛应用,但在low-level任务中应用极少。但是,依托于Transformer,LN得到了越来越多的应用。基于该事实,我们猜想:LN可能是达成SOTA复原器的关键,故在上述模块中添加了LN(见上面图示)。LN的引入使得训练更平滑,甚至可以将学习率放大10倍。更大的学习率可以带来显著性能提升。

在Transformer中,数据过Attention层和FFN层后,都会经过一个Add & Norm处理。其中,Add为residule block(残差模块),数据在这里进行residule connection(残差连接)。而Norm即为Normalization(标准化)模块。Transformer中采用的是Layer Normalization(层标准化)方式。

在图片视频分类等特征提取网络中大多数情况BN效果优于IN,在生成式类任务中的网络IN优于BN。

BN适用于判别模型中,比如图片分类模型。因为BN注重对每个batch进行归一化,从而保证数据分布的一致性,而判别模型的结果正是取决于数据整体分布。但是BN对batchsize的大小比较敏感,由于每次计算均值和方差是在一个batch上,所以如果batchsize太小,则计算的均值、方差不足以代表整个数据分布;

IN适用于生成模型中,比如图片风格迁移。因为图片生成的结果主要依赖于某个图像实例,所以对整个batch归一化不适合图像风格化中,在风格迁移中使用Instance Normalization不仅可以加速模型收敛,并且可以保持每个图像实例之间的独立。

  • Activation:simple gate 引入非线性

(对特征进行了channel-split,分成两个C/2个通道的特征,并相乘)

尽管ReLU是最常用的激活函数,现有SOTA方案中采用GELU进行代替。由于GELU可以保持降噪性能相当且大幅提升去模糊性能,故我们采用GELU替代ReLU,但作者认为 GELU太复杂:因此提出了简化版的GELU。

作者也是从High-Level Task 找到的灵感,将现在大火的GLU和GELU引入并做了简化。 文章先给出了GLU的数学形式:,之后文章认为GELU是GLU的一种特殊情况(这个可以看原文,比较直观),于是只关注于GLU本身。虽然GLU可以提升模型效果,但是也会增加计算量,于是作者为降低计算量,所以对GLU进行了简化。GLU的计算量主要来自于sigmoid和映射函数(上图)。因为GLU本身是具备非线性这一性质的(我个人理解是(元素积)element-wise multiplication引入的),所以文章删除了sigmoid。为了减少计算量,映射函数更是直接删除,同时对特征进行了channel-split,分成两个C/2个通道的特征,并相乘,具体是上面的图。 由于这个简化的simple gate引入了非线性,所以常用的ReLU自然也不需要再加入到网络中了,这也就是为什么这篇文章提出的方法叫做 Nonlinear Activation Free Network (NAFNet)

补充:channel-split 思想来自Channel-Wise Convolutions

【ChannelNets: Compact and Efficient Convolutional Neural Networks via Channel-Wise Convolutions:论文提出channel-wise卷积的概念,将输入输出的维度连接进行稀疏化而非全连接,区别于分组卷积的严格分组,让卷积在channel维度上进行滑动,能够更好地保留channel间的信息交流。基于channel-wise卷积的思想,论文进一步提出了channel-wise深度可分离卷积,并基于该结构替换网络最后的全连接层+全局池化的操作,搭建了ChannelNets。Channel-wise卷积的核心在于输入和输出连接的稀疏化,每个输出仅与部分输入相连,概念上区别于分组卷积,没有对输入进行严格的区分,而是以一定的stride去采样多个相关输入进行输出(在channel维度滑动),能够降少参数量以及保证channel间一定程度的信息流。】

GELU与GLU的实现可以发现:GELU是GLU的一种特例。我们从另一个角度猜想:GLU可视作一种广义激活函数,它是可以用于替代非线性激活函数。提出了一种简化版GLU变种(见上图):直接将特征沿通道维度分成两部分并相乘。

  • Simplified Channel Attention

注意力机制可以说是近年来最火热的研究领域之一,其有效性得到了充分的验证

通过保留通道注意力的两个重要作用(全局信息聚合、通道信息交互),我们提出了如上图的简化版通道注意力。

对于attention,上述的simple Gate操作虽然可以有效减少计算量,但是作者认为channel-wise的操作(导致channel间的信息阻隔)丢失了channel之间的信息,所以在后面的attention上,作者使用了简化的channel attention,减少计算量的同时引入channel的交互,这个看图就可以直接明白。这个其实对我个人有点启发,因为从swin到restormer(用于高分辨率图像恢复的高效Transformer),多少能隐隐的感受到,其实tranformer的全局attention可能没有想象的那么重要,swin里切成window-based仍然可以保持很好的效果,restormer里面干脆放弃了spatial的MSA(多头self注意力)而使用深度卷积和传统的spatial attention(空间注意力),有可能CA对恢复任务更重要一些(有待证明)。

4、1*1卷积

1×1卷积实际上是对每个像素点,在不同的channels上进行线性组合(信息整合),且保留了图片的原有平面结构,调控depth,从而完成升维或降维的功能。

最后,有了上述的基本改进,并将上面的模块组合在了一起。

其他说明:

1、参考论文:

Simple Baselines for Image Restoration,是目前去噪效果比较好的网络。

Github代码实现:https://github.com/megvii-research/NAFNet

2、损失函数:

       使用L1损失、mse损失(Fourier_loss)、

psnrloss【参考https://github.com/megvii-research/NAFNet中提供的psnrloss】、以及相邻像素损失

L1损失:用来预测generate和real 之间的像素级别误差

MSE损失(Fourier_loss):计算generate和real的fft变换后的频域信息。

参考论文:Fouier Space Losses for Efficient Perceptual Image Super-Resolution,在改论文中利用transformer实现图像去雨,提出了Fourier Space Losses,单张图像超分方法在重构高分辨率图像时缺失高频细节。这通常通过有监督的训练来执行,其中使用已知核对真实图像 y 进行下采样,例如 bicubic,得到LR输入图像x。虽然这种方法能够在某种应用中尽可能恢复频率信息,但高频信息却难以恢复,容易出现模糊情况。近几年,许多研究者使用GAN,用于学习高频空间的分布。丢失了频谱空间的高频信息。因此,文章提出了一种用于频域的损失函数。首先,将真实图像和生成图像经过Hann window预处理。接着,计算傅里叶频域损失函数,包括L1范数度量的频谱差异,以及相位角差异

对generate和real进行fft变换,时域变换到频域

参考代码:https://github.com/zzksdu/fourierSpaceLoss/blob/master/Fourier_loss.py

Psnrloss:参考NAFnet。

相邻像素损失:

使用L1loss,比较相邻行之间的像素loss:

Step1:

对gt:求相邻行像素的l1损失,记为 gloss。

Step2:

对denoise求相邻行像素的l1损失,记为 dloss。

Step3:对gloss和dloss求L1损失。

【因为图像在进行预处理时候进行了行列交织,所以再求此损失时候,需要先对图像进行复原,再求相邻像素损失】

3、优化函数 SGD or Adam

SGD虽然训练时间更长,容易陷入鞍点,但是在好的初始化和学习率调度方案的情况下,结果更可靠。SGD现在后期调优时还是经常使用到,但SGD的问题是前期收敛速度慢。SGD前期收敛慢的原因: SGD在更新参数时对各个维度上梯度的放缩是一致的,并且在训练数据分布极不均衡时训练效果很差。而因为收敛慢的问题应运而生的自适应优化算法Adam、AdaGrad、RMSprop 等,但这些自适应的优化算法泛化能力可能比非自适应方法更差,虽然可以在训练初始阶段展现出快速的收敛速度,但其在测试集上的表现却会很快陷入停滞,并最终被 SGD 超过。 实际上,在自然语言处理和计算机视觉方面的一些最新的工作中SGD(或动量)被选为优化器,其中这些实例中SGD 确实比自适应方法表现更好。

主流认为:Adam等自适应学习率算法对于稀疏数据具有优势,且收敛速度很快;但精调参数的SGD(+Momentum)往往能够取得更好的最终结果。

Improving Generalization Performance by Switching from Adam to SGD 提出了Adam+SGD 组合策略。前期用Adam,享受Adam快速收敛的优势;后期切换到SGD,慢慢寻找最优解。这一方法以前也被研究者们用到,不过主要是根据经验来选择切换的时机和切换后的学习率。这篇文章把这一切换过程傻瓜化,给出了切换SGD的时机选择方法,以及学习率的计算方法,效果看起来也不错。

torch.optim.lr_scheduler.StepLR:这是比较常用的等间隔动态调整方法,该方法的原理为:每隔step_size个epoch就对每一参数组的学习率按gamma参数进行一次衰减。

3、训练结果:

Batch Size=1,梯度变来变去,非常不准确,网络很难收敛。

Figure 1 loss损失函数值

Figure 2  训练过程中的psnr测试值

Figure 3   训练过程SSIM 测试值

4、数据集处理

对数据进行加噪处理、数据随机翻转等预处理。

因为显存有限,所以将img(4,1736,2312)裁剪为 1736/4, 2312/4的大小送入网络中进行训练。在进行验证/生成去噪图片时,同样裁剪噪声图片分批送入网络,注意,裁剪需要多裁剪20个tensor,然后拼接成完整图片并写入文件,这样拼接成的图片不会有分割线 。

[加噪处理效果不好,我使用的噪声正则项是torch正态分布,原本任务就是去噪,raw域噪声的分布应该跟正态分布不贴合,增加噪声后可能会导致模型效果变差。

数据翻转:这里需要注意label和noise应该使用相同的seed。

因为显存有限,需要裁剪图像,这里我认为如果只是简单的裁剪图像,会导致裁剪前后的相邻像素信息损失,因此我借鉴了unet:Overlap-tile 重叠切片的思想,同时,在生成test图像时候,也要对其进行拼接。]

5、使用加载模型:

from network import NFnet3

net = NFnet3()

net.load_state_dict(torch.load(modelpath))

6、test数据集去噪结果:

Figure 6 原始噪声图像
Figure 7 去噪后图像

去噪结果思考:

对于噪声比较小的图像,去噪效果比较好,而且不会破坏原有图像的结构,但对于噪声特别大的图像:纹理效果不是很好,一些细节处理的不太好。

此外,我认为,如果输入裁剪后的图像能在大一些,效果应该会好一些。或者现在小的图像进行预训练,在使用大图像进行微调,会好一些。

参考论文:

[1] Chen L, Chu X, Zhang X, et al. Simple Baselines for Image Restoration[J]. arXiv preprint arXiv:2204.04676, 2022.

[2] Huang H, Lin L, Tong R, et al. Unet 3+: A full-scale connected unet for medical image segmentation[C]//ICASSP 2020-2020 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP). IEEE, 2020: 1055-1059.

[3] Wang Y, Huang H, Xu Q, et al. Practical deep raw image denoising on mobile devices[C]//European Conference on Computer Vision. Springer, Cham, 2020: 1-16.

[4] Ba, J.L., Kiros, J.R., Hinton, G.E.: Layer normalization. arXiv preprintarXiv:1607.06450 (2016)

[5] Chen, H., Wang, Y., Guo, T., Xu, C., Deng, Y., Liu, Z., Ma, S., Xu, C., Xu, C., Gao,W.: Pre-trained image processing transformer. In: Proceedings of the IEEE/CVFConference on Computer Vision and Pattern Recognition. pp. 12299–12310 (2021)

[6] Cheng, S., Wang, Y., Huang, H., Liu, D., Fan, H., Liu, S.: Nbnet: Noise basis learning for image denoising with subspace projection. In: Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition. pp. 4896–4906 (2021)

[7] Language Modeling with Gated Convolutional Networks

更新:最近Swin Transformer的提出,就有人利用该结构和unet,实现了图像去噪:

SUNet: Swin Transformer UNet for Image Denoising

https://arxiv.org/abs/2202.14009

MAE–transformer模型预训练

假设我们想从图像中识别出不同种类的椅子,然后将购买链接推荐给用户。一种可能的方法是先找出100种常见的椅子,为每种椅子拍摄1,000张不同角度的图像,然后在收集到的图像数据集上训练一个分类模型。这个椅子数据集虽然可能比Fashion-MNIST数据集要庞大,但样本数仍然不及ImageNet数据集中样本数的十分之一。这可能会导致适用于ImageNet数据集的复杂模型在这个椅子数据集上过拟合。同时,因为数据量有限,最终训练得到的模型的精度也可能达不到实用的要求。

为了应对上述问题,一个显而易见的解决办法是收集更多的数据。然而,收集和标注数据会花费大量的时间和资金。例如,为了收集ImageNet数据集,研究人员花费了数百万美元的研究经费。虽然目前的数据采集成本已降低了不少,但其成本仍然不可忽略。

另外一种解决办法是应用迁移学习(transfer learning),将从源数据集学到的知识迁移到目标数据集上。例如,虽然ImageNet数据集的图像大多跟椅子无关,但在该数据集上训练的模型可以抽取较通用的图像特征,从而能够帮助识别边缘、纹理、形状和物体组成等。这些类似的特征对于识别椅子也可能同样有效。

本节我们介绍迁移学习中的一种常用技术:微调(fine tuning)。如图9.1所示,微调由以下4步构成。

  1. 在源数据集(如ImageNet数据集)上预训练一个神经网络模型,即源模型。
  2. 创建一个新的神经网络模型,即目标模型。它复制了源模型上除了输出层外的所有模型设计及其参数。我们假设这些模型参数包含了源数据集上学习到的知识,且这些知识同样适用于目标数据集。我们还假设源模型的输出层跟源数据集的标签紧密相关,因此在目标模型中不予采用。
  3. 为目标模型添加一个输出大小为目标数据集类别个数的输出层,并随机初始化该层的模型参数。
  4. 在目标数据集(如椅子数据集)上训练目标模型。我们将从头训练输出层,而其余层的参数都是基于源模型的参数微调得到的。

当目标数据集远小于源数据集时,微调有助于提升模型的泛化能力。

代码实现微调:

pretrained_net = models.resnet18(pretrained=True)
pretrained_net.load_state_dict(torch.load('/home/kesci/input/resnet185352/resnet18-5c106cde.pth'))

下面打印源模型的成员变量fc。作为一个全连接层,它将ResNet最终的全局平均池化层输出变换成ImageNet数据集上1000类的输出。

print(pretrained_net.fc)

输出:Linear(in_features=512, out_features=1000, bias=True)

可见此时pretrained_net最后的输出个数等于目标数据集的类别数1000。所以我们应该将最后的fc成修改我们需要的输出类别数:

pretrained_net.fc = nn.Linear(512, 2)
print(pretrained_net.fc)

此时,pretrained_netfc层就被随机初始化了,但是其他层依然保存着预训练得到的参数。由于是在很大的ImageNet数据集上预训练的,所以参数已经足够好,因此一般只需使用较小的学习率来微调这些参数,而fc中的随机初始化参数一般需要更大的学习率从头训练。PyTorch可以方便的对模型的不同部分设置不同的学习参数,我们在下面代码中将fc的学习率设为已经预训练过的部分的10倍。

output_params = list(map(id, pretrained_net.fc.parameters()))
feature_params = filter(lambda p: id(p) not in output_params, pretrained_net.parameters())

lr = 0.01
optimizer = optim.SGD([{'params': feature_params},
                       {'params': pretrained_net.fc.parameters(), 'lr': lr * 10}],
                       lr=lr, weight_decay=0.001)

记录: 在MAE的微调训练中,提供了两种 微调

  1. Linear probing: 锁死transformer的参数,只训练CIFAR10的那个Linear层。
  2. Fine-tuning: 接着训练transformer的参数,同时也训练CIFAR10的那个Linear。

论文做了MAE各个部分的不同设置对比实验,这些实验能够揭示MAE更多的特性。首先是masking ratio,从下图可以看到,最优的设置是75%的masking ratio,此时linear probing和finetune效果最好,这比之前的研究要高很多,比如BEiT的masking ratio是40%。另外也可以看到linear probing和finetune的表现不一样,linear probing效果随着masking ratio的增加逐渐提高直至一个峰值后出现下降,而finetune效果在不同making ratio下差异小,masking ratio在40%~80%范围内均能表现较好。 ​

preview

Pytorch Image Models –timm快速使用

原文:Getting Started with PyTorch Image Models (timm): A Practitioner’s Guide – 2022.02.02

中文教程: https://www.aiuai.cn/aifarm1967.html

Github: rwightman/pytorch-image-models

PyTorch Image Models(timm) 是一个优秀的图像分类 Python 库,其包含了大量的图像模型(Image Models)、Optimizers、Schedulers、Augmentations 等等.里面提供了许多计算机视觉的SOTA模型,可以当作是torchvision的扩充版本,并且里面的模型在准确度上也较高。

timm 提供了参考的 training 和 validation 脚本,用于复现在 ImageNet 上的训练结果;以及更多的 官方文档 和 timmdocs project.

timm的安装

关于timm的安装,我们可以选择以下两种方式进行:

  1. 通过pip安装
pip install timm
  1. 通过git与pip进行安装
git clone https://github.com/rwightman/pytorch-image-models
cd pytorch-image-models && pip install -e .

如何查看预训练模型种类

  1. 查看timm提供的预训练模型 截止到2022.3.27日为止,timm提供的预训练模型已经达到了592个,我们可以通过timm.list_models()方法查看timm提供的预训练模型(注:本章测试代码均是在jupyter notebook上进行)
import timm
avail_pretrained_models = timm.list_models(pretrained=True)
len(avail_pretrained_models)
  1. 查看特定模型的所有种类 每一种系列可能对应着不同方案的模型,比如Resnet系列就包括了ResNet18,50,101等模型,我们可以在timm.list_models()传入想查询的模型名称(模糊查询),比如我们想查询densenet系列的所有模型。
all_densnet_models = timm.list_models("*densenet*")
all_densnet_models

我们发现以列表的形式返回了所有densenet系列的所有模型。

['densenet121',
 'densenet121d',
 'densenet161',
 'densenet169',
 'densenet201',
 'densenet264',
 'densenet264d_iabn',
 'densenetblur121d',
 'tv_densenet121']
  1. 查看模型的具体参数 当我们想查看下模型的具体参数的时候,我们可以通过访问模型的default_cfg属性来进行查看,具体操作如下
model = timm.create_model('resnet34',num_classes=10,pretrained=True)
model.default_cfg
{'url': 'https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/resnet34-43635321.pth',
 'num_classes': 1000,
 'input_size': (3, 224, 224),
 'pool_size': (7, 7),
 'crop_pct': 0.875,
 'interpolation': 'bilinear',
 'mean': (0.485, 0.456, 0.406),
 'std': (0.229, 0.224, 0.225),
 'first_conv': 'conv1',
 'classifier': 'fc',
 'architecture': 'resnet34'}

除此之外,我们可以通过访问这个链接 查看提供的预训练模型的准确度等信息。

使用和修改预训练模型

在得到我们想要使用的预训练模型后,我们可以通过timm.create_model()的方法来进行模型的创建,我们可以通过传入参数pretrained=True,来使用预训练模型。同样的,我们也可以使用跟torchvision里面的模型一样的方法查看模型的参数,类型/

import timm
import torch

model = timm.create_model('resnet34',pretrained=True)
x = torch.randn(1,3,224,224)
output = model(x)
output.shape
torch.Size([1, 1000])
  • 查看某一层模型参数(以第一层卷积为例)
model = timm.create_model('resnet34',pretrained=True)
list(dict(model.named_children())['conv1'].parameters())
[Parameter containing:
 tensor([[[[-2.9398e-02, -3.6421e-02, -2.8832e-02,  ..., -1.8349e-02,
            -6.9210e-03,  1.2127e-02],
           [-3.6199e-02, -6.0810e-02, -5.3891e-02,  ..., -4.2744e-02,
            -7.3169e-03, -1.1834e-02],
            ...
           [ 8.4563e-03, -1.7099e-02, -1.2176e-03,  ...,  7.0081e-02,
             2.9756e-02, -4.1400e-03]]]], requires_grad=True)]
            
  • 修改模型(将1000类改为10类输出)
model = timm.create_model('resnet34',num_classes=10,pretrained=True)
x = torch.randn(1,3,224,224)
output = model(x)
output.shape
torch.Size([1, 10])
  • 改变输入通道数(比如我们传入的图片是单通道的,但是模型需要的是三通道图片) 我们可以通过添加in_chans=1来改变
model = timm.create_model('resnet34',num_classes=10,pretrained=True,in_chans=1)
x = torch.randn(1,1,224,224)
output = model(x)

模型的保存

timm库所创建的模型是torch.model的子类,我们可以直接使用torch库中内置的模型参数保存和加载的方法,具体操作如下方代码所示

torch.save(model.state_dict(),'./checkpoint/timm_model.pth')
model.load_state_dict(torch.load('./checkpoint/timm_model.pth'))

使用示例

# replace
# optimizer = torch.optim.Adam(model.parameters(), lr=0.01)

# with
optimizer = timm.optim.AdamP(model.parameters(), lr=0.01)

for epoch in num_epochs:
    for batch in training_dataloader:
        inputs, targets = batch
        outputs = model(inputs)
        loss = loss_function(outputs, targets)

        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
        
        
#
optimizer = timm.optim.Adahessian(model.parameters(), lr=0.01)

is_second_order = (
    hasattr(optimizer, "is_second_order") and optimizer.is_second_order
)  # True

for epoch in num_epochs:
    for batch in training_dataloader:
        inputs, targets = batch
        outputs = model(inputs)
        loss = loss_function(outputs, targets)

        loss.backward(create_graph=second_order)
        optimizer.step()
        optimizer.zero_grad()

CVPR 2022 | 重新审视池化:你的感受野不是最理想的

作者丨简单来源丨CV技术指南

本文提出了一种简单而有效的动态优化池操作( Dynamically Optimized Pooling operation),称为DynOPool,它通过学习每一层感受野的最佳大小和形状来优化特征映射的端到端比例因子。

前言

本文提出了一种简单而有效的动态优化池操作( Dynamically Optimized Pooling operation),称为DynOPool,它通过学习每一层感受野的最佳大小和形状来优化特征映射的端到端比例因子。深度神经网络中任何类型的调整大小模块都可以用DynOPool操作以最小的成本替换。此外,DynOPool通过引入一个限制计算成本的附加损失项来控制模型的复杂性。

Pooling Revisited: Your Receptive Field is Suboptima

论文:https://arxiv.org/abs/2205.15254

公众号后台回复“DynOPool”获取论文PDF

背景

尽管深度神经网络在计算机视觉、自然语言处理、机器人、生物信息学等各种应用中取得了前所未有的成功,但最优网络结构的设计仍然是一个具有挑战性的问题。而感受野的大小和形状决定了网络如何聚集本地信息,并对模型的整体性能产生显著影响。神经网络中的许多组成部分,例如用于卷积和池化运算的内核大小和步长,都会影响感受野的配置。然而,它们仍然依赖于超参数,现有模型的感受野会导致形状和大小不理想

本文通过介绍固定大小和形状的传统感受野是次优的问题,讨论了DynOPool如何通过CIFAR-100上的VGG-16玩具实验解决这个问题。

固定大小和形状的传统感受野存在的问题:

1.不对称分布的信息

最佳感受野形状会根据数据集中固有的空间信息不对称性而改变。而大多数情况下固有的不对称性是不可测量的。此外,通常用于预处理的输入大小调整有时也会导致信息不对称。在人工设计的网络中,图像的长宽比经常被调整以满足模型的输入规格。然而,这种网络中的感受野不是用来处理操作的。

为了验证所提出的方法,作者在CIFAR-stretch-V上进行实验,如图1(a)所示,相较于人工设计模型,形状通过DynOPool动态优化的特征映射通过在水平方向上提取更具有价值的信息提高性能。

图片

图1  用来自CIFAR-100的三个不同的合成数据集进行玩具实验:(a)随机裁剪垂直拉伸的图像 (b)在4×4网格中平铺缩小的图像 (c)放大缩小的图像。

2.密集分布或稀疏分布信息

局部性是设计最优模型的组成部分。CNN通过级联的方式聚合局部信息来学习图像的复杂表示。而局部信息的重要性很大程度上取决于每个图像的属性。例如,当一个图像被模糊化时,大多数有意义的微观模式,如物体的纹理,都会被抹去。在这种情况下,最好在早期层中扩展感受野,集中于全局信息。另一方面,如果一幅图像在局部细节中包含大量类特定的信息,例如纹理,则识别局部信息将会更加重要。

为了验证假设,作者构建了CIFAR-100数据集的两个变体,CIFAR-tile和CIFAR-large,如图1(b)和(c)所示。作者模型在很大程度上优于人工设计的模型。

贡献

为了缓解人工构建的体系结构和操作的次优性,作者提出了动态优化池操作(DynOPool),这是一个可学习的调整大小模块,可以替代标准的调整大小操作。该模块为在数据集上学习的操作找到感受野的最佳比例因子,从而将网络中的中间特征图调整为适当的大小和形状。

论文的主要贡献:

1、解决了深度神经网络中现有尺度算子依赖于预定超参数的局限性。指出了在中间特征图中寻找最佳空间分辨率和感受野的重要性。

2、提出了一个可学习的调整尺寸大小的模块DynOPool,它可以找到中间特征图的最佳比例因子和感受域。DynOPool使用学习到的比例因子识别某一层的最佳分辨率和感受野,并将信息传播到后续层,从而在整个网络中实现规模优化。

3、证明了在图像分类和语义分割任务中,使用DynOPool的模型在多个数据集和网络架构上优于基线算法。它还显示了精度和计算成本之间的理想权衡。

方法

1.动态优化池(DynOPool)

图片

图2 DynOPool中的调整大小模块

模块通过优化一对输入和输出特征映射之间的比例因子r来优化查询点q的位置以及获得中间特征映射的最佳分辨率。DynOPool在不影响其他算子的情况下,自适应控制较深层接收域的大小和形状。

图片

图3 DynOPool整个的优化过程

针对比例因子r梯度不稳定,会产生梯度爆炸导致训练过程中分辨率发生显著变化的问题,使用a重新参数化r如下:

图片

2.模型复杂性约束

为了最大化模型的精度,DynOPool有时会有较大的比例因子,增加了中间特征图的分辨率。因此,为了约束计算代价,减少模型规模,引入了一个额外的损失项LGMACs,它由每次训练迭代t的分层GMACs计数的简单加权和给出,如下所示:

图片

实验

图片

表1 人工设计模型与使用DynOPool模型的精度(%)和GMACs比较

图片

图4 在VGG-16上使用人工设计的Shape Adaptor与使用DynOPool的训练模型可视化

图片

表2 在CIFAR-100数据集上DynOPool和Shape Adaptor的比较

图片

表3 在ImageNet数据集上EfficientNet-B0+DynOPool的性能

图片

表4 基于PascalVOC的HRNet-W48语义分割结果

结论

作者提出了一种简单而有效的动态优化池操作(DynOPool),它通过学习每个层中感受野的理想大小和形状来优化端到端的特征映射的比例因子,调整中间特征图的大小和形状,有效提取局部细节信息,从而优化模型的整体性能;

DynOPool还通过引入一个额外的损失项来限制计算成本,从而控制模型的复杂性。实验表明,在多个数据集上,该模型在图像分类和语义分割方面均优于基线网络。

NVIDIA训练深度学习模型加速:APEX库

最近在跑目标检测和图像分类模型,发现很多时候教程里需要 安装apex库,于是我就去网上搜索一下这个,发现apex大有来头;

官方:

https://nvidia.github.io/apex/amp.html

https://docs.nvidia.com/deeplearning/performance/mixed-precision-training/index.html

APEX 是来自英伟达 (NVIDIA) 的一个很好用的深度学习加速库。由英伟达开源,完美支持PyTorch框架,用于改变数据格式来减小模型显存占用的工具。其中最有价值的是 amp (Automatic Mixed Precision) ,将模型的大部分操作都用 Float16 数据类型测试,一些特别操作仍然使用 Float32。并且用户仅仅通过三行代码即可完美将自己的训练代码迁移到该模型。实验证明,使用 Float16 作为大部分操作的数据类型,并没有降低参数,在一些实验中,反而由于可以增大 Batch size,带来精度上的提升,以及训练速度上的提升。

使用理由

使用精度低于32位浮点的数值格式有许多好处。首先,它们需要更少的内存,从而能够训练和部署更大的神经网络。其次,它们需要较少的内存带宽,从而加快数据传输操作。第三,数学运算在降低精度方面运行得更快,特别是在具有TensorCore支持的GPU上。混合精度训练(Mixed Precision Training)实现了所有这些好处,同时确保与完全精度训练相比,不会丢失特定任务的准确性。它这样做的方法是识别需要完全精度的步骤,只对这些步骤使用32位浮点,而在其他地方使用16位浮点。

在PyTorch中的使用:
首先需要安装其apex库(我还没装过),其github地址:https://github.com/NVIDIA/apex。
然后在训练的脚本(代码)中简单添加几句就可以了

from apex import amp

amp.init()
amp.init_trainer(trainer)
with amp.scale_loss(loss, trainer) as scaled_loss:
   autograd.backward(scaled_loss) 

APEX的配置

前提是你安装好了CUDA和CUDNN,以及你的系统是Ubuntu系统。

git clone https://github.com/NVIDIA/apex
cd apex
pip install -v --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext" 

Apex 还通过以下方式支持仅 Python 构建 (Pytorch 0.4 需要)。

pip install -v --disable-pip-version-check --no-cache-dir ./

安装之后,clone下来的apex文件夹就可以删除了。

查看能否正确导入apex:

from apex import amp