暂时未有相关云产品技术能力~
本文来自公众号CV技术指南的YOLO系列专栏。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、最新论文解读、各种技术教程、CV招聘信息发布等。YOLOv5 网络模型结构与之前的 YOLOv3、YOLOv4 不同,v3、v4 除了有完整的大模型之外,只有一个轻量级的 tiny 模型,值得注意的是,在 tiny 中,只有两个输出层。而 YOLOv5 则具备四种网络模型:YOLOv5s、YOLOv5m、YOLOv5l、YOLOv5x 四种模型。它们是通过 depth_multiple 和 width_multiple 来控制网络的宽度和深度,这类似 EfficientNet 的思想。其中,YOLOv5s 在该系列中是深度最小,并且特征图的宽度也最小的网络。其他的网络是在此基础上进行不断地加深、加宽。 YOLOv5 基础组件CBL 由 Conv + BN + Leaky_ReLU 组成。Res unit 是借鉴残差结构的思想来构建网络的。CBM 是残差模块中的子模块。Conv + BN + Mish 激活函数 。CSP1_X 由 CSPNet 演变而来,该模块由 CBL 模块、Res unit、Conv、还有 Concate 组成,其中X代表有X个这个模块。CSP2_X 也是由 CSPNet 网络组成,该模块由 Conv 和 X个 Res unit Concate 而成。SPP 从 YOLOv3 起,就有使用 SPP 模块。SPP 是 通过采用 1 × 1、5 × 5、9 × 9 和 13 × 13 的最大池化方式,来进行多尺度特征的融合。新版的 YOLOv5 中使用的是升级版的 SPP结构,SPPF。它是将原本并行的 MaxPool 替换成了串行 MaxPool。其中串行两个 5 x 5 大小的 MaxPool 和一个 9 x 9 大小的 MaxPool 是等价的,而串行三个 5 x 5 大小的 MaxPool 层和一个 13 x 13 大小的 MaxPool 是等价的。并行和串行的效果一样,但串行的效率更高。YOLOv5 知识点总体来说,YOLOv5 和 YOLOv4 差不多,但还是做了一些调整、优化。在 YOLOv5 中涉及到的知识:Mosaic数据增强、自适应锚框计算、自适应图片缩放、Focus 结构、CSP 结构、FPN + PAN 结构、GIOU_Loss。 欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。自适应锚框计算像之前的 YOLOv3、YOLOv4,对于不同的数据集,都会计算先验框 anchor。然后在网络训练时,网络会在 anchor 的基础上进行预测,然后输出预测框,再和标签框进行对比,最后就进行梯度地反向传播。在 YOLOv3、YOLOv4 中,训练不同的数据集时,是使用单独的脚本进行初始锚框的计算,在 YOLOv5 中,则是将此功能嵌入到整个训练代码里中。所以在每次训练开始之前,它都会根据不同的数据集来自适应计算 anchor。如果你觉得计算的锚框效果并不好,那你也可以在代码中将此功能关闭。自适应的计算具体过程:1. 获取数据集中所有目标的宽和高。2. 将每张图片中按照等比例缩放的方式到 resize 指定大小,这里保证宽高中的最大值符合指定大小。3. 将 bboxes 从相对坐标改成绝对坐标,这里乘以的是缩放后的宽高。4. 筛选 bboxes,保留宽高都大于等于两个像素的 bboxes。5. 使用 k-means 聚类三方得到n个 anchors,与v3、v4 操作一样。6. 使用遗传算法随机对 anchors 的宽高进行变异。倘若变异后的效果好,就将变异后的结果赋值给 anchors;如果变异后效果变差就跳过,默认变异1000次。这里是使用 anchor_fitness 方法计算得到的适应度 fitness,然后再进行评估。 自适应图片缩放自适应图片缩放-针对不同的目标检测算法而言,我们通常需要执行图片缩放操作,即将原始的输入图片缩放到一个固定的尺寸,再将其送入检测网络中。YOLO 系列算法中常用的尺寸包括416 * 416,608 * 608 等尺寸。原始的缩放方法存在着一些问题,因为在实际的使用中的很多图片的长宽比不同,所以在进行缩放填充之后,两端的黑边大小都不相同,但是如果填充过多,则会存在大量的信息冗余,从而影响整体的推理速度。为了进一步提升推理速度,YOLOv5 提出一种方法能够自适应的添加最少的黑边到缩放之后的图片中。效果如下图所示:1. 根据原始图片大小与输入到网络图片大小计算缩放比例。2. 根据原始图片大小与缩放比例计算缩放后的图片大小。3. 计算黑边填充数值。需要注意的是:该操作仅在模型推理阶段执行,模型训练阶段仍然和传统的方法相同,将原始图片裁剪到 416 x 416 大小;YOLOv3 与 YOLOv4 中默认填充的数值是 (0,0,0),而 YOLOv5 中默认填充的数值是 (114,114,114);该操作仅仅针对原始图片的短边而言,仍然将长边裁剪到416。 BackboneFocus结构Focus 模块,输入通道扩充了4倍,作用是可以使信息不丢失的情况下提高计算力。Focus 是在 YOLOv5 中提出来的,它先将特征图进行分块切片操作,然后再将结果 Concat 起来,再送入后面模块。在新版中,YOLOv5 将Focus 模块替换成了一个 6 x 6 的卷积层。两者的计算量是等价的,但是对于一些 GPU 设备,使用 6 x 6 的卷积会更加高效。在 YOLOv5s 中,608 x 608 x 3 的图片经 Focus 处理,会变成 304 x 304 x 12 的特征图,这样一定程度上提高了特征图的操作。然后会再经过一次32个卷积核变成 304 x 304 x 32 的特征图。在 YOLOv5s 中,Focus 最后使用了大小为32的卷积核,但是其他结构如 YOLO5m 是使用更大数目的卷积核。 CSP结构YOLOv5 与 YOLOv4 的不同点在于,YOLOv4 只有主干网络使用了 CSP结构, 而在 YOLOv5 中,设计了两种 CSP 结构。其中,CSP1_X 应用于 Backbone,另一种 CSP2_X 则是应用于 Neck 中。 NeckYOLOv5 对 FPN + PAN 的结构作了一些改动。在这里使用了另外一种 CSP2_X 的结构,加强网络特征融合的能力。 Head在训练阶段,YOLOv5 与 YOLOv4 一样,采用其中的了 CIOU_Loss。在推理阶段,YOLOv4 在 DIOU_Loss 的基础上采用了 DIOU_nms 的方式,而 YOLOv5 是采用加权 nms 的方式。 NMS 非极大值抑制NMS 的本质是搜索局部极大值,抑制非极大值元素。非极大值抑制,主要就是用来抑制检测时冗余的框。因为在目标检测中,在同一目标的位置上会产生大量的候选框,这些候选框相互之间可能会有重叠,所以我们需要利用非极大值抑制找到最佳的目标边界框,消除冗余的边界框。 大致算法流程为:1. 对所有预测框的置信度降序排序2. 选出置信度最高的预测框,确认其为正确预测,并计算他与其他预测框的 IOU3. 根据步骤2中计算的 IOU 去除重叠度高的,IOU > threshold 阈值就直接删除4. 剩下的预测框返回第1步,直到没有剩下的为止NMS 一次处理只会一个类别,所以如果有N个类别,那么就需要执行N次。 SoftNMS当两个目标靠的非常近时,置信度低的会被置信度高的框所抑制,那么当两个目标靠的十分近的时候就只会识别出一个 bbox。为了解决这个问题,可以使用 softNMS。它的基本思想是用稍低一点的分数来代替原有的分数,而不是像 nms 一样直接置零。训练策略YOLOv5 也使用了许多训练策略。多尺度训练。如果网络的输入是416 x 416。那么训练的时候就会从 0.5 x 416 到 1.5 x 416 中任意取值,但所取的值都是32的整数倍。训练开始前会使用 warmup 进行训练。在模型预训练阶段,先使用较小的学习率训练一些epochs或者steps (如4个 epoch 或10000个 step),再修改为预先设置的学习率进行训练。使用了 cosine 学习率下降策略。采用了 EMA 更新权重,相当于训练时给参数赋予一个动量,这样更新起来就会更加平滑。使用了 amp 进行混合精度训练。能够减少显存的占用并且加快训练速度,但是需要 GPU 支持。 损失函数YOLOv5 的损失依旧是由 Classes loss、Objectness loss、Location loss组成:Location loss 采用的是 CIOU loss,这里只会计算正样本的定位损失。Classes loss 和 Objectness loss 采用的是 BCE loss。其中 Classes loss 也只会计算正样本的分类损失。Objectness loss 是使用所有样本进行反向传播的计算,并且这里用的是网络预测的目标边界框与 GT Box 的CIOU。 总结与分析YOLOv5 是 one stage 的目标检测算法,该算法在 YOLOv4 的基础上添加了一些新的改进思路,使得其速度与精度都得到了极大的性能提升,具体包括:输入端的 Mosaic 数据增强、自适应锚框计算、自适应图片缩放操作、Focus 结构、CSP 结构、SPP 结构、FPN + PAN 结构、CIOU_Loss 等等。除此之外,YOLOv5 中的各种改进思路也可以使用到其它的目标检测算法中。本文来自公众号CV技术指南的YOLO系列专栏。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、最新论文解读、各种技术教程、CV招聘信息发布等。
M5Product DatasetM5Product 数据集是一个大规模的多模态预训练数据集,具有针对电子产品的粗粒度和细粒度注释。600 万个多模态样本、5k个属性和2400 万个值5 种模式-图像 文本 表 视频 音频600 万个类别注释,包含6k个类别广泛的数据源(100 万商户提供)Ego4D在全球 74 个地点和 9 个国家/地区收集的大规模、以自我为中心的数据集和基准套件,包含超过 3,670 小时的日常生活活动视频。使用七种不同的现成头戴式摄像机捕获数据:GoPro、Vuzix Blade、Pupil Labs、ZShades、OR-DRO EP6、iVue Rincon 1080 和 Weeview。除了视频,部分 Ego4D 还提供其他数据模式:3D 扫描、音频、凝视、立体、多个同步的可穿戴相机和文本叙述。Daily Multi-Spectral Satellite DatasetDynamicEarthNet 数据集包含每日 Planet Fusion 图像,以及两年内全球 75 个地区的每月土地覆盖类别。七个土地覆盖类别以时间一致的方式手动注释。还提供了 Sentinel 2 图像。该数据集是第一个大规模的多类和多时态变化检测基准,我们希望它能促进地球观测和计算机视觉领域的多时态研究新浪潮。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。VCSL (Video Copy Segment Localization) dataset与现有的受视频级标注或小规模限制的复制检测数据集相比,VCSL 不仅具有两个数量级的片段级标记数据,16 万个真实视频副本对包含超过 28 万个本地复制片段对,而且涵盖各种视频类别和广泛的视频时长。每个收集的视频对中的所有复制片段都是手动提取的,并附有精确注释的开始和结束时间戳。Rope3DRope3D目标检测数据集是首个同时具有图像和点云3D联合标注的大规模、多视角的路侧数据集,共50009帧图像数据以及对应的2D&3D标注结果。基于该数据集,可以进行路端单目3D检测任务的研究。EDS 数据集EDS 数据集针对由机器硬件参数引起的难以察觉的域间偏移问题研究,包含了来自 3 台不同 X 光机器的 14219 张图片, 其中 10 类物品, 共计 31655 个目标实例,均由专业标注人员进行标注。FineDiving本数据集收集了奥运会、世界杯、世锦赛以及欧锦赛的跳水项目比赛视频。每个比赛视频都提供了丰富的内容,包括所有运动员的跳水记录、不同视角的慢速回放等。我们构建了一个由语义和时间结构组织的细粒度视频数据集,其中每个结构都包含两级注释。对于语义结构,动作级标签描述了运动员的动作类型,步骤级标签描述了过程中连续步骤的子动作类型,其中每个动作过程中的相邻步骤属于不同的子动作类型。子动作类型的组合产生动作类型。在时间结构中,动作级标签定位运动员执行的完整动作实例的时间边界。在此注释过程中,我们丢弃所有不完整的动作实例并过滤掉慢速播放。步骤级标签是动作过程中连续步骤的起始帧。PIAA 数据库个性化图像美学评估 (PIAA) 由于其高度主观性而具有挑战性。人们的审美取决于多种因素,包括形象特征和主体性格。现有的 PIAA 数据库在注释多样性方面,特别是在学科方面受到限制,已不能满足日益增长的 PIAA 研究需求。为了解决这一难题,我们对个性化图像美学进行了迄今为止最全面的主观研究,并引入了一个新的具有丰富属性的个性化图像美学数据库(PARA),该数据库由 438 个主题的 31,220 张带有注释的图像组成。PARA 具有丰富的标注,包括 9 个面向图像的客观属性和 4 个面向人的主观属性。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读、CV招聘信息。CV技术指南创建了一个免费的知识星球。关注公众号添加编辑的微信号可邀请加入。
本文来自公众号CV技术指南的YOLO系列专栏。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、最新论文解读、各种技术教程、CV招聘信息发布等。概述Attention机制目的在于聚焦有用的信息,并减少不重要信息的比重。Attention机制可以分为6大类,包括4个基础类别和2个组合类别。4个基础类别分别是通道注意力(channel attention),空间注意力(spatial attention),时间注意力(temporal attention),分支注意力(branch attention)。2个组合类别即通道与空间的组合,空间与时间的组合。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。本文来自公众号CV技术指南的YOLO系列专栏。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、最新论文解读、各种技术教程、CV招聘信息发布等。关注公众号可邀请加入免费版的知识星球和技术交流群。
在讲如何搭建之前,先回顾一下Transformer在计算机视觉中的结构是怎样的。这里以最典型的ViT为例。本文来自公众号CV技术指南的YOLO系列专栏。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、最新论文解读、各种技术教程、CV招聘信息发布等。关注公众号可邀请加入免费版的知识星球和技术交流群。如图所示,对于一张图像,先将其分割成NxN个patches,把patches进行Flatten,再通过一个全连接层映射成tokens,对每一个tokens加入位置编码(position embedding),会随机初始化一个tokens,concate到通过图像生成的tokens后,再经过transformer的Encoder模块,经过多层Encoder后,取出最后的tokens(即随机初始化的tokens),再通过全连接层作为分类网络进行分类。下面我们就根据这个流程来一步一步介绍如何搭建一个Transformer模型。分块目前有两种方式实现分块,一种是直接分割,一种是通过卷积核和步长都为patch大小的卷积来分割。直接分割直接分割即把图像直接分成多块。在代码实现上需要使用einops这个库,完成的操作是将(B,C,H,W)的shape调整为(B,(H/P *W/P),P*P*C)。from einops import rearrange, repeat from einops.layers.torch import Rearrange self.to_patch_embedding = nn.Sequential( Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = patch_height, p2 = patch_width), nn.Linear(patch_dim, dim), )这里简单介绍一下Rearrange。Rearrange用于对张量的维度进行重新变换排序,可用于替换pytorch中的reshape,view,transpose和permute等操作。举几个例子欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。#假设images的shape为[32,200,400,3] #实现view和reshape的功能 Rearrange(images,'b h w c -> (b h) w c')#shape变为(32*200, 400, 3) #实现permute的功能 Rearrange(images, 'b h w c -> b c h w')#shape变为(32, 3, 200, 400) #实现这几个都很难实现的功能 Rearrange(images, 'b h w c -> (b c w) h')#shape变为(32*3*400, 200)从这几个例子看可以看出,Rearrange非常简单好用,这里的b, c, h, w都可以理解为表示符号,用来表示操作变化。通过这几个例子似乎也能理解下面这行代码是如何将图像分割的。Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = patch_height, p2 = patch_width)这里需要解释的是,一个括号内的两个变量相乘表示的是该维度的长度,因此不要把"h"和"w"理解成图像的宽和高。这里实际上h = H/p1, w = W/p2,代表的是高度上有几块,宽度上有几块。h和w都不需要赋值,代码会自动根据这个表达式计算,b和c也会自动对应到输入数据的B和C。后面的"b (h w) (p1 p2 c)"表示了图像分块后的shape: (B,(H/P *W/P),P*P*C)这种方式在分块后还需要通过一层全连接层将分块的向量映射为tokens。在ViT中使用的就是这种直接分块方式。 卷积分割卷积分割比较容易理解,使用卷积核和步长都为patch大小的卷积对图像卷积一次就可以了。self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size) x = self.proj(x).flatten(2).transpose(1, 2) # B Ph*Pw C在swin transformer中即使用的是这种卷积分块方式。在swin transformer中卷积后没有再加全连接层。 Position EmbeddingPosition Embedding可以分为absolute position embedding和relative position embedding。在学习最初的transformer时,可能会注意到用的是正余弦编码的方式,但这只适用于语音、文字等1维数据,图像是高度结构化的数据,用正余弦不合适。在ViT和swin transformer中都是直接随机初始化一组与tokens同shape的可学习参数,与tokens相加,即完成了absolute position embedding。在ViT中实现方式:self.pos_embedding = nn.Parameter(torch.randn(1, num_patches + 1, dim)) x += self.pos_embedding[:, :(n + 1)] #之所以是n+1,是因为ViT中选择随机初始化一个class token,与分块得到的tokens拼接。所以patches的数量为num_patches+1。在swin transformer中的实现方式:from timm.models.layers import trunc_normal_ self.absolute_pos_embed = nn.Parameter(torch.zeros(1, num_patches, embed_dim)) trunc_normal_(self.absolute_pos_embed, std=.02)在TimeSformer中的实现方式:self.pos_emb = torch.nn.Embedding(num_positions + 1, dim)以上就是简单的使用方法,这种方法属于absolute position embedding。还有更复杂一点的方法,以后有机会单独搞一篇文章来介绍。感兴趣的读者可以先去看看这篇论文《ICCV2021 | Vision Transformer中相对位置编码的反思与改进》。 EncoderEncoder由Multi-head Self-attention和FeedForward组成。Multi-head Self-attentionMulti-head Self-attention主要是先把tokens分成q、k、v,再计算q和k的点积,经过softmax后获得加权值,给v加权,再经过全连接层。用公式表示如下: 所谓Multi-head是指把q、k、v再dim维度上分成head份,公式里的dk为每个head的维度。具体代码如下:class Attention(nn.Module): def __init__(self, dim, heads = 8, dim_head = 64, dropout = 0.): super().__init__() inner_dim = dim_head * heads project_out = not (heads == 1 and dim_head == dim) self.heads = heads self.scale = dim_head ** -0.5 self.attend = nn.Softmax(dim = -1) self.dropout = nn.Dropout(dropout) self.to_qkv = nn.Linear(dim, inner_dim * 3, bias = False) self.to_out = nn.Sequential( nn.Linear(inner_dim, dim), nn.Dropout(dropout) ) if project_out else nn.Identity() def forward(self, x): qkv = self.to_qkv(x).chunk(3, dim = -1) q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> b h n d', h = self.heads), qkv) dots = torch.matmul(q, k.transpose(-1, -2)) * self.scale attn = self.attend(dots) attn = self.dropout(attn) out = torch.matmul(attn, v) out = rearrange(out, 'b h n d -> b n (h d)') return self.to_out(out)这里没有太多可以解释的地方,介绍一下q、k、v的来源,由于这是self-attention,因此q=k=v(即tokens),若是普通attention,则k= v,而q是其它的东西,例如可以是另一个尺度的tokens,或视频领域中的其它帧的tokens。 FeedForward这里不用多介绍。class FeedForward(nn.Module): def __init__(self, dim, hidden_dim, dropout = 0.): super().__init__() self.net = nn.Sequential( nn.Linear(dim, hidden_dim), nn.GELU(), nn.Dropout(dropout), nn.Linear(hidden_dim, dim), nn.Dropout(dropout) ) def forward(self, x): return self.net(x)把上面两者组合起来就是Encoder了。class Transformer(nn.Module): def __init__(self, dim, depth, heads, dim_head, mlp_dim, dropout = 0.): super().__init__() self.layers = nn.ModuleList([]) for _ in range(depth): self.layers.append(nn.ModuleList([ PreNorm(dim, Attention(dim, heads = heads, dim_head = dim_head, dropout = dropout)), PreNorm(dim, FeedForward(dim, mlp_dim, dropout = dropout)) ])) def forward(self, x): for attn, ff in self.layers: x = attn(x) + x x = ff(x) + x return xdepth指的是Encoder的数量。PreNorm指的是层归一化。class PreNorm(nn.Module): def __init__(self, dim, fn): super().__init__() self.norm = nn.LayerNorm(dim) self.fn = fn def forward(self, x, **kwargs): return self.fn(self.norm(x), **kwargs)分类方法数据通过Encoder后获得最后的预测向量的方法有两种典型。在ViT中是随机初始化一个cls_token,concate到分块后的token后,经过Encoder后取出cls_token,最后将cls_token通过全连接层映射到最后的预测维度。#生成cls_token部分 from einops import repeat self.cls_token = nn.Parameter(torch.randn(1, 1, dim)) cls_tokens = repeat(self.cls_token, '1 n d -> b n d', b = b) x = torch.cat((cls_tokens, x), dim=1) ################################ #分类部分 self.mlp_head = nn.Sequential( nn.LayerNorm(dim), nn.Linear(dim, num_classes) ) x = x.mean(dim = 1) if self.pool == 'mean' else x[:, 0] x = self.to_latent(x) return self.mlp_head(x)在swin transformer中,没有选择cls_token。而是直接在经过Encoder后将所有数据取了个平均池化,再通过全连接层。self.avgpool = nn.AdaptiveAvgPool1d(1) self.head = nn.Linear(self.num_features, num_classes) if num_classes > 0 else nn.Identity() x = self.avgpool(x.transpose(1, 2)) # B C 1 x = torch.flatten(x, 1) x = self.head(x)组合以上这些就成了一个完整的模型class ViT(nn.Module): def __init__(self, *, image_size, patch_size, num_classes, dim, depth, heads, mlp_dim, pool = 'cls', channels = 3, dim_head = 64, dropout = 0., emb_dropout = 0.): super().__init__() image_height, image_width = pair(image_size) patch_height, patch_width = pair(patch_size) num_patches = (image_height // patch_height) * (image_width // patch_width) patch_dim = channels * patch_height * patch_width assert pool in {'cls', 'mean'}, 'pool type must be either cls (cls token) or mean (mean pooling)' self.to_patch_embedding = nn.Sequential( Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = patch_height, p2 = patch_width), nn.Linear(patch_dim, dim), ) self.pos_embedding = nn.Parameter(torch.randn(1, num_patches + 1, dim)) self.cls_token = nn.Parameter(torch.randn(1, 1, dim)) self.dropout = nn.Dropout(emb_dropout) self.transformer = Transformer(dim, depth, heads, dim_head, mlp_dim, dropout) self.pool = pool self.to_latent = nn.Identity() self.mlp_head = nn.Sequential( nn.LayerNorm(dim), nn.Linear(dim, num_classes) ) def forward(self, img): x = self.to_patch_embedding(img) b, n, _ = x.shape cls_tokens = repeat(self.cls_token, '1 n d -> b n d', b = b) x = torch.cat((cls_tokens, x), dim=1) x += self.pos_embedding[:, :(n + 1)] x = self.dropout(x) x = self.transformer(x) x = x.mean(dim = 1) if self.pool == 'mean' else x[:, 0] x = self.to_latent(x) return self.mlp_head(x)数据的变换以上的代码都是比较简单的,整体上最麻烦的地方在于理解数据的变换。首先输入的数据为(B, C, H, W),在经过分块后,变成了(B, n, d)。在CNN模型中,很好理解(H,W)就是feature map,C是指feature map的数量,那这里的n,d哪个是通道,哪个是图像特征?回顾一下分块的部分Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = patch_height, p2 = patch_width)根据这个可以知道n为分块的数量,d为每一块的内容。因此,这里的n相当于CNN模型中的C,而d相当于features。一般情况下,在Encoder中,我们都是以(B, n, d)的形式。在swin transformer中这种以卷积的形式分块,获得的形式为(B, C, L),然后做了一个transpose得到(B, L, C),这与ViT通过直接分块方式获得的形式实际上完全一样,在Swin transformer中的L即为ViT中的n,而C为ViT中的d。因此,要注意的是在Multi-head self-attention中,数据的形式是(Batchsize, Channel, Features),分成多个head的是Features。前面提到,在ViT中会concate一个随机生成的cls_token,该cls_token的维度即为(B, 1, d)。可以理解为通道数多了个1。 以上就是Transformer的模型搭建细节了,整体上比较简单,大家看完这篇文章后可以找几篇Transformer的代码来理解理解。如ViT, swin transformer, TimeSformer等ViT:https://github.com/lucidrains/vit-pytorch/blob/main/vit_pytorch/vit.py swin: https://github.com/microsoft/Swin-Transformer/blob/main/models/swin_transformer.py TimeSformer:https://github.com/lucidrains/TimeSformer-pytorch/blob/main/timesformer_pytorch/timesformer_pytorch.py下一篇我们将介绍如何写train函数,以及包括设置优化方式,设置学习率,不同层设置不同学习率,解析参数等。本文来自公众号CV技术指南的YOLO系列专栏。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、最新论文解读、各种技术教程、CV招聘信息发布等。关注公众号可邀请加入免费版的知识星球和技术交流群。
本文来自公众号CV技术指南的YOLO系列专栏。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、最新论文解读、各种技术教程、CV招聘信息发布等。关注公众号可邀请加入免费版的知识星球和技术交流群。YOLO的设计理论YOLO 全称叫 You Only Look Once。是目标检测中 one stage 的典型之作。此外,目标检测的流派还有 two-stage,如 RCNN 系列;以及anchor free,如cornnet、centernet。其实 YOLO 就是通过一系列的卷积操作来实现端到端的目标检测。YOLO 会将图片划分为 S x S 的网格(grid),每个网格负责检测落入其中的目标,最后输出所含目标的边框(bounding box)、定位的位置信息、以及所有类别的置信度。基本流程如下:1. 首先将输入图片 resize 到固定大小。2. 输入到网络中,最后得到预测结果检测到的目标。3. 使用非极大抑制算法来过滤冗余目标。 非极大值抑制算法(nms)不仅仅是YOLO才会使用到nms,其实在大多数目标检测算法都会使用nms。它主要是为了解决一个目标被重复检测的现象。如下图,我们可以看到人脸被重复检测了。虽然每个框都检测对了,但我们只需要得到一个框,即最好的那一个框。那么采用NMS就可以实现这样的效果。首先从所有的检测框中找到置信度最大的那个,然后遍历剩余的框,计算其与最大框之间的IOU。如果其值大于一定阈值,则表示重合度过高,那么就将该框就会被剔除;然后对剩余的检测框重复上述过程,直到处理完所有的检测框。YOLOv1其实 YOLOv1 和后续的 YOLO 算法最大的不同是 YOLOv1 并没有使用锚点框(anchor box),在 YOLOv1 中是没有 anchor 这个概念的,严格上来说 YOLOv1 属于 anchor free。算法思想1. 将图像划分为 S x S 的网格,论文中的设置是 7 x 7 的网格。如果某个物体的中心落在这个网格中,那么这个网格就负责预测这个物体。2. 然后每个网格预测 B 个边框,论文中的设置是2个边框,即预测出 7 x 7 x 2 个边框,这个边框负责预测物体的位置。3. 每个边框除了要预测物体的位置之外,还要附带预测一个置信度。这里的置信度指的是边框的概率,无关目标属于哪一个类别,表示的是边框内是否有物体。4. 每个网格还要预测C个类别的分数。比如说在 VOC 数据集上则会预测出20个类别。5. 因此,对于 VOC 数据集来说,YOLOv1 的网络最后是输出预测位置(xywh)+置信度以及类别分数,所以输出为 7 x 7 x (5 + 5 + 20)。6. 最后一层全连接层用线性激活函数,其余层采用 Leaky ReLU损失函数可以看到损失函数有三个部分组成,分别是边框损失,置信度损失,以及类别损失。并且三个损失函数都使用均方差损失函数(MSE)。有趣的说法有一个有趣的说法:YOLO将检测问题当做回归任务来解决,而不是分类任务。大家可以想下是为什么?不足之处1. 因为每个网格单元只预测两个框,并且只有一个类别。即每个网格只会选择出 IOU 最高的那个目标,所以如果每个网格包含多个物体,就只能检测出其中一个,所以对于拥有群体小目标的图片来说,比如鸟群,检测效果会很差。也可能是因为这种方式导致减少了对同一目标的多次检测,最终识别物体位置精准性差,召回率低。2. 当出现新尺寸的目标时,效果也会变差,原因是 YOLOv1 使用简单的特征来预测边界框,并没有使用 anchor,导致定位的不准确。3. 最后的输出层为全连接层。因为全连接层输出大小是固定的,所以导致图像的输入大小也必须固定,这在一定程度上来说有局限性。4. MSE 在处理大边框和小边框时,赋予的权重是一样的。假设同样是预测与实际相差25个像素点,但是对于大边界框来说,小错误通常是无足轻重的,甚至可以忽略不计,但对于小边界框来说,这个数值对于它的影响就很大了。 YOLOv2YOLOv2 也叫 YOLO9000,因为使用了 COCO 数据集以及 Imagenet 数据集来联合训练,最终可以检测9000个类别。算法思想1. 使用 Darknet19 作为网络的主干网络。Darknet19 有点类似 VGG,在 Darknet19 中,使用的是 3 x 3 大小的卷积核,并且在每次Pooling 之后都增加一倍通道数,以及将特征图的宽高缩减为原来的一半。网络中有19个卷积层,所以叫 Darknet19,以及有5个 Max Pooling 层,所以这里进行了32倍的下采样。2. 采用了 Batch Normal 层来加速训练时的收敛速度,并且使用了 Batch Normal 就可以从去掉 Dropout,而不会产生过拟合。在 Batch Normal 的论文中讲道, Batch Normal 作用和 Dropout 的作用是类似的。3. 使用了先验框 anchor,基于 Kmeans 的聚类方法来根据数据集的标签来自动提取先验框的信息,所以可根据不同的数据集设置 anchor 。当 Cluster IOU 选择值为5时,Avg IOU比不用聚类的方法要高一些。选择值为9的时候,Avg IOU有更加明显地提升。4. 使用了更高的分辨率,训练时从 224 x 224 提升到了 448 x 448。并且采用了随机裁剪、旋转、颜色变换、饱和度变换, 曝光度变换等数据增强的操作。5. 可以进行多尺度训练,每迭代10个 batch,随机更换尺寸320、352...608,注意这里都为32的倍数,因为在 Darknet19 中进行了32倍的下采样操作。6. 使用了 Pass through,类似 Pixel-shuffle,融合高层和低层的信息,这样可以保留一些细节信息,这样可以更好检测小物体。具体来说,就是进行一拆四的操作,直接传递到池化后的特征图中,进行卷积后再叠加两者,最后一起作为输出特征图进行输出。通过使用 Pass through 层来检测细粒度特征使 mAP 提升了1个点。7. 网络去掉了最后一个卷积层,而加上了三个 3 x 3 卷积层,分别预测大尺度、中尺度、小尺度的物体。其中每个卷积层有1024个卷积核,并且每个卷积层紧接着一个 1 x 1 卷积层。每个 anchor 预测5个边界框,所以对于 VOC 数据集来说,每个边界框会输出5个坐标的相关信息和20个类别的相关信息。位置的预测在 YOLOv2 中的位置预测中,加入了 sigmoid 的约束是因为没有约束的话,预测的边界框容易向任何方向偏移任意大小,可能出现这个 anchor 的中心点落在了其他的 anchor 的区域上。这就导致每个位置预测的边界框可以落在图片的任意位置,容易导致模型训练的不稳定性,在训练的时候要花很长时间才可以得到正确的偏移。在 YOLOv2 中,就是预测边界框中心点是相对于对应网格的左上角位置进行相对偏移,为了将边界框中心点约束在当前网格中,使用 sigmoid 函数处理偏移值,这样预测的偏移值在(0,1)范围内。这里将每个网格的尺度看做是1的基本单位。 YOLOv3啥也不多说,光看这张图,就可以感受到 YOLOv3 的强大。仿佛在说:我不是针对谁,我是说,在座的各位都是xx~YOLOv3 采用了作者自己设计的 Darknet-53 作为主干网络,Darknet-53 借鉴了残差网络的思想,与 Resnet101、Resnet152 相比,在精度上差不多的同时,有着更快的速度。在下采样操作中使用了步长为2的卷积来代替传统的池化操作;在特征融合方面,为了提高小目标的检测性能,引入了类似 FPN 的多尺度特征融合方法,特征图在经过上采样后与前面层的输出进行 concat 操作,这样可以融合浅层特征和深层特征,使得 YOLOv3 在小目标的精度上有了很大的提升。并且使用逻辑回归替代 softmax 作为分类器,为的就是解决多标签分类问题,比如一个物体既属于A类,又属于B类。算法思想1. YOLOv3 的输出依旧分为三个部分,首先是置信度、然后是坐标信息,最后是分类信息。在推理的时候,特征图会等分成 S x S 的网格,通过设置置信度阈值对网格进行筛选,如果某个格子上存在目标,那么这个格子就负责预测该物体的置信度、坐标和类别信息。2. 使用了残差模型的思想:Darknet-53。这个是类似 Resnet 的结构,不断堆叠残差结构,并且没有最大池化层。既然没有池化来进行下采样,那么下采样的操作就通过两个步长的卷积操作完成。所以整体来看,网络全部由卷积搭建而成,也就是全卷积网络。基本部件DBL:Conv + BN + Leaky ReLU、残差结构 res_unit。3. 多尺度预测,采用类似 FPN 融合的方式,在三个不同尺寸大小的特征层上预测不同尺度信息,每个特征层三种尺度,所以最后为9个,从 YOLOv2 的5个变成9个。4. 大目标输出维度:13 x 13 x 255,其中255 = ( 80 + 5 ) × 3;中目标输出维度:26 × 26 × 255;小目标输出维度:52 × 52 × 255。这里的80个类别是因为使用了COCO 数据集。5. 采用了新的正负样本匹配策略:如果重合度高但不是最高的,则依旧抛弃。只选取重合度最高的。6. 分类器损失采用二分类交叉损失熵 binary cross-entropy loss(BCE),而不是使用 softmax,因为有的目标可能存在重叠的类别标签,也就是多标签分类。如 SUV 又是车又是 SUV,而 softmax 只会输出最大的那一个类别。损失函数损失函数:置信度损失使用的是 BCE、类别损失也是使用 BCE、定位损失使用均方差损失函数(MSE)。只有正样本才参与类别损失,定位损失还有置信度损失三者的计算,但是负样本只参与置信度损失。并且三个损失函数之间加入了平衡系数,来平衡损失。YOLOv3 快的原因YOLOv3 和 SSD 比网络更加深了,虽然 anchor 比 SSD 少了许多,但是加深的网络深度明显会增加更多的计算量,那么为什么 YOLOv3会比 SSD 快3倍?因为 SSD 的 backbone 使用的是 VGG16,YOLOv3 用的其最新原创的 Darknet,Darknet-53 与 Resnet 的网络结构,Darknet-53 会先用 1 x 1 的卷积核对 feature 进行降维,随后再使用 3 x 3 的卷积核升维。在这个过程中,这样可以大大降低参数的计算量以及模型的大小,有点类似于低秩分解。究其原因还是做了很多优化,比如用卷积替代全连接,1 x 1 卷积减小计算量等。论文链接笔者以前都是在博客、公众号上看别人的论文讲解,去消化别人咀嚼过的知识。但后来别人还是建议我多多去读原论文,从最开始的地方去理解,这样才会有自己的独特见解,以及会理解得更加透彻。所以我还是会建议大家多去看看原论文,以下就是原论文的链接:YOLOv1: https://arxiv.org/abs/1506.02640 YOLOv2: https://arxiv.org/abs/1612.08242 YOLOv3: https://arxiv.org/abs/1804.02767希望大家看完本篇文章后,能对YOLO有个更深的理解。后续我会继续更新YOLOv4的解析,大家敬请期待。本文来自公众号CV技术指南的YOLO系列专栏。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、最新论文解读、各种技术教程、CV招聘信息发布等。关注公众号可邀请加入免费版的知识星球和技术交流群。
热力图可视化方法的原理在一个神经网络模型中,图片经过神经网络得到类别输出,我们并不知道模型是根据什么来作出预测的,换言之,我们需要了解图片中各个区域对模型作出预测的影响有多大。这就是热力图的作用,它通过得到图像不同区域之间对模型的重要性而生成一张类似于等温图的图片。热力图可视化方法经过了从CAM,GradCAM,到GradCAM++的过程,比较常用的是GradCAM算法。 CAMCAM论文:Learning Deep Features for Discriminative LocalizationCAM的原理是取出全连接层中得到类别C的概率的那一维权值,用W表示。然后对GAP前的feature map进行加权求和,由于此时feature map不是原图像大小,在加权求和后还需要进行上采样,即可得到Class Activation Map。CAM有个很致命的缺陷,它的结构是由CNN + GAP + FC + Softmax组成。也就是说如果想要可视化某个现有的模型,对于没有GAP的模型来说需要修改原模型结构,并重新训练,相当麻烦,且如果模型很大,在修改后重新训练不一定能达到原效果,可视化也就没有意义了。因此,针对这个缺陷,其后续有了改进版Grad-CAM。GradCAMGrad-CAM论文:Grad-CAM: Visual Explanations from Deep Networks via Gradient-based LocalizationGrad-CAM的最大特点就是不再需要修改现有的模型结构了,也不需要重新训练了,直接在原模型上即可可视化。原理:同样是处理CNN特征提取网络的最后一层feature maps。Grad-CAM对于想要可视化的类别C,使最后输出的类别C的概率值通过反向传播到最后一层feature maps,得到类别C对该feature maps的每个像素的梯度值,对每个像素的梯度值取全局平均池化,即可得到对feature maps的加权系数alpha,论文中提到这样获取的加权系数跟CAM中的系数的计算量几乎是等价的。接下来对特征图加权求和,使用ReLU进行修正,再进行上采样。使用ReLU的原因是对于那些负值,可认为与识别类别C无关,这些负值可能是与其他类别有关,而正值才是对识别C有正面影响的。具体公式如下:Grad-CAM后续还有改进版Grad-CAM++,其主要的改进效果是定位更准确,更适合同类多目标的情况,所谓同类多目标是指一张图像中对于某个类出现多个目标,例如七八个人。改进方法是对加权系数的获取提出新的方法,该方法很复杂,这里不介绍。 GradCAM的使用教程这份代码来自GradCAM论文作者,原链接中包含了很多其它的CAM,这里将GradCAM摘出来对其做一个使用说明。使用流程使用起来比较简单,仅了解主函数即可。if __name__ == "__main__": imgs_path = "path/to/image.png" model = models.mobilenet_v3_large(pretrained=True) model.load_state_dict(torch.load('model.pth')) model = model.cuda().eval() #target_layers指的是需要可视化的层,这里可视化最后一层 target_layers = [model.features[-1]] img, data = image_proprecess(imgs_path) data = data.cuda() cam = GradCAM(model=model, target_layers=target_layers) #指定可视化的类别,指定为None,则按照当前预测的最大概率的类作为可视化类。 target_category = None grayscale_cam = cam(input_tensor=data, target_category=target_category) grayscale_cam = grayscale_cam[0, :] visualization = show_cam_on_image(np.array(img) / 255., grayscale_cam) plt.imshow(visualization) plt.xticks() plt.yticks() plt.axis('off') plt.savefig("path/to/gradcam_image.jpg")如上代码所示,仅需要自主设置输入图片,模型,可视化层,可视化类别即可,其它的部分可完全照用。下面介绍细节部分。 数据预处理这里跟上次可视化特征图的代码一样,将图片读取,resize,转化为Tensor,格式化,若只有一张图片,则还需要将其扩展为四维。def image_proprecess(img_path): img = Image.open(img_path) data_transforms = transforms.Compose([ transforms.Resize((384, 384), interpolation=3), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) data = data_transforms(img) data = torch.unsqueeze(data,0) img_resize = img.resize((384,384)) return img_resize,dataGradCAMGradCAM这个类是按照前面第一节中介绍的原理封装的,因此了解原理后再了解这个类的代码就比较简单了。class GradCAM: def __init__(self, model, target_layers, reshape_transform=None): self.model = model.eval() self.target_layers = target_layers self.reshape_transform = reshape_transform self.cuda = use_cuda self.activations_and_grads = ActivationsAndGradients( self.model, target_layers, reshape_transform) """ Get a vector of weights for every channel in the target layer. Methods that return weights channels, will typically need to only implement this function. """ @staticmethod def get_cam_weights(grads): return np.mean(grads, axis=(2, 3), keepdims=True) @staticmethod def get_loss(output, target_category): loss = 0 for i in range(len(target_category)): loss = loss + output[i, target_category[i]] return loss def get_cam_image(self, activations, grads): weights = self.get_cam_weights(grads) weighted_activations = weights * activations cam = weighted_activations.sum(axis=1) return cam @staticmethod def get_target_width_height(input_tensor): width, height = input_tensor.size(-1), input_tensor.size(-2) return width, height def compute_cam_per_layer(self, input_tensor): activations_list = [a.cpu().data.numpy() for a in self.activations_and_grads.activations] grads_list = [g.cpu().data.numpy() for g in self.activations_and_grads.gradients] target_size = self.get_target_width_height(input_tensor) cam_per_target_layer = [] # Loop over the saliency image from every layer for layer_activations, layer_grads in zip(activations_list, grads_list): cam = self.get_cam_image(layer_activations, layer_grads) cam[cam < 0] = 0 # works like mute the min-max scale in the function of scale_cam_image scaled = self.scale_cam_image(cam, target_size) cam_per_target_layer.append(scaled[:, None, :]) return cam_per_target_layer def aggregate_multi_layers(self, cam_per_target_layer): cam_per_target_layer = np.concatenate(cam_per_target_layer, axis=1) cam_per_target_layer = np.maximum(cam_per_target_layer, 0) result = np.mean(cam_per_target_layer, axis=1) return self.scale_cam_image(result) @staticmethod def scale_cam_image(cam, target_size=None): result = [] for img in cam: img = img - np.min(img) img = img / (1e-7 + np.max(img)) if target_size is not None: img = cv2.resize(img, target_size) result.append(img) result = np.float32(result) return result def __call__(self, input_tensor, target_category=None): # 正向传播得到网络输出logits(未经过softmax) output = self.activations_and_grads(input_tensor) if isinstance(target_category, int): target_category = [target_category] * input_tensor.size(0) if target_category is None: target_category = np.argmax(output.cpu().data.numpy(), axis=-1) print(f"category id: {target_category}") else: assert (len(target_category) == input_tensor.size(0)) self.model.zero_grad() loss = self.get_loss(output, target_category) loss.backward(retain_graph=True) # In most of the saliency attribution papers, the saliency is # computed with a single target layer. # Commonly it is the last convolutional layer. # Here we support passing a list with multiple target layers. # It will compute the saliency image for every image, # and then aggregate them (with a default mean aggregation). # This gives you more flexibility in case you just want to # use all conv layers for example, all Batchnorm layers, # or something else. cam_per_layer = self.compute_cam_per_layer(input_tensor) return self.aggregate_multi_layers(cam_per_layer) def __del__(self): self.activations_and_grads.release() def __enter__(self): return self def __exit__(self, exc_type, exc_value, exc_tb): self.activations_and_grads.release() if isinstance(exc_value, IndexError): # Handle IndexError here... print( f"An exception occurred in CAM with block: {exc_type}. Message: {exc_value}") return True简要说明一下整体在做什么,先通过下方的ActivationsAndGradients获取模型推理过程中的梯度和激活函数值,计算要可视化的类的loss(其它类的都忽略),通过这个loss计算可视化类对应的梯度图,将其进行全局平均池化获得每个feature maps通道的加权系数,与feature maps进行通道上加权,并在通道上做均值获得单通道图,再ReLU即输出对应的图。注:此图还不是热力图,还需要与原图相加才能获得最终的热力图。GradCAM这个类主要就是先定义,再调用执行。定义须输入网络和需要可视化的层,执行则需要输入图片和可视化的类别。执行返回的是区域重要性图。cam = GradCAM(model=model, target_layers=target_layers) #指定可视化的类别,指定为None,则按照当前预测的最大概率的类作为可视化类。 target_category = None grayscale_cam = cam(input_tensor=data, target_category=target_category)获取推理过程中的梯度主要是通过以下这个类来完成。这里不多介绍class ActivationsAndGradients: """ Class for extracting activations and registering gradients from targeted intermediate layers """ def __init__(self, model, target_layers, reshape_transform): self.model = model self.gradients = [] self.activations = [] self.reshape_transform = reshape_transform self.handles = [] for target_layer in target_layers: self.handles.append( target_layer.register_forward_hook( self.save_activation)) # Backward compatibility with older pytorch versions: if hasattr(target_layer, 'register_full_backward_hook'): self.handles.append( target_layer.register_full_backward_hook( self.save_gradient)) else: self.handles.append( target_layer.register_backward_hook( self.save_gradient)) def save_activation(self, module, input, output): activation = output if self.reshape_transform is not None: activation = self.reshape_transform(activation) self.activations.append(activation.cpu().detach()) def save_gradient(self, module, grad_input, grad_output): # Gradients are computed in reverse order grad = grad_output[0] if self.reshape_transform is not None: grad = self.reshape_transform(grad) self.gradients = [grad.cpu().detach()] + self.gradients def __call__(self, x): self.gradients = [] self.activations = [] return self.model(x) def release(self): for handle in self.handles: handle.remove()然后就是将GradCAM输出的重要性图在原图上显示,通过下面这个函数完成。def show_cam_on_image(img: np.ndarray, mask: np.ndarray, use_rgb: bool = False, colormap: int = cv2.COLORMAP_JET) -> np.ndarray: """ This function overlays the cam mask on the image as an heatmap. By default the heatmap is in BGR format. :param img: The base image in RGB or BGR format. :param mask: The cam mask. :param use_rgb: Whether to use an RGB or BGR heatmap, this should be set to True if 'img' is in RGB format. :param colormap: The OpenCV colormap to be used. :returns: The default image with the cam overlay. """ heatmap = cv2.applyColorMap(np.uint8(255 * mask), colormap) if use_rgb: heatmap = cv2.cvtColor(heatmap, cv2.COLOR_BGR2RGB) heatmap = np.float32(heatmap) / 255 if np.max(img) > 1: raise Exception( "The input image should np.float32 in the range [0, 1]") cam = heatmap + img cam = cam / np.max(cam) return np.uint8(255 * cam)前面介绍的仅仅是分类任务的热力图可视化,但对于目标检测,语义分割等这些包含多任务的应用如何做? 其它类型任务的热力图可视化在gradCAM论文作者给出的代码中还介绍了如何可视化目标检测、语义分割、transformer的代码。由于作者提供了使用方法,这里不多介绍,直接给出作者写得教程。Notebook tutorial: Class Activation Maps for Object Detection with Faster-RCNNNotebook tutorial: Class Activation Maps for Semantic SegmentationHow it works with Vision/SwinT transformers欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、最新论文解读、各种技术教程、CV招聘信息发布等。关注公众号可邀请加入免费版的知识星球和技术交流群。
搭建CNN网络首先来看一个CNN网络 (以YOLO_v1的一部分层为例)。class Flatten(nn.Module): def __init__(self): super(Flatten,self).__init__() def forward(self,x): return x.view(x.size(0),-1) class Yolo_v1(nn.Module): def __init__(self, num_class): super(Yolo_v1,self).__init__() C = num_class self.conv_layer1=nn.Sequential( nn.Conv2d(in_channels=3,out_channels=64,kernel_size=7,stride=1,padding=7//2), nn.BatchNorm2d(64), nn.LeakyReLU(0.1), nn.MaxPool2d(kernel_size=2,stride=2) ) self.conv_layer2=nn.Sequential( nn.Conv2d(in_channels=64,out_channels=192,kernel_size=3,stride=1,padding=3//2), nn.BatchNorm2d(192), nn.LeakyReLU(0.1), nn.MaxPool2d(kernel_size=2,stride=2) ) #为了简便,这里省去了很多层 self.flatten = Flatten() self.conn_layer1 = nn.Sequential( nn.Linear(in_features=7*7*1024,out_features=4096), nn.Dropout(0.5),nn.LeakyReLU(0.1)) self.conn_layer2 = nn.Sequential(nn.Linear(in_features=4096,out_features=7*7*(2*5 + C))) self._initialize_weights() def forward(self,input): conv_layer1 = self.conv_layer1(input) conv_layer2 = self.conv_layer2(conv_layer1) flatten = self.flatten(conv_layer2) conn_layer1 = self.conn_layer1(flatten) output = self.conn_layer2(conn_layer1) return output def _initialize_weights(self): for m in self.modules(): if isinstance(m, nn.Conv2d): n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels m.weight.data.normal_(0, math.sqrt(2. / n)) if m.bias is not None: m.bias.data.zero_() elif isinstance(m, nn.BatchNorm2d): m.weight.data.fill_(1) m.bias.data.zero_() elif isinstance(m, nn.Linear): m.weight.data.normal_(0, 0.01) m.bias.data.zero_()搭建网络有几个要点:自定义类要继承torch.nn.Module。有时候自己设计了一些模块,为了使用更方便,通常额外定义一个类,就像这里的Flatten,自定义的类也要继承torch.nn.Module。完成init函数和forward函数。其中__init__函数完成网络的搭建,forward函数完成网络的前传路径。完成所有层的参数初始化,一般只有卷积层,归一化层,全连接层要初始化,池化层没有参数。 __init__函数构建网络层有几种方式,一种是pytorch官方已经有了定义的网络,如resnet,vgg,Inception等。一种是自定义层,例如自己设计了一个新的模块。首先是使用pytorch官方库已经支持的网络,这些网络放在了torchvision.models中,下面选择自己需要的一个。以下只列举了2D 模型的一部分,还有视频类的3D 模型。import torchvision.models as models resnet18 = models.resnet18(pretrained = True) alexnet = models.alexnet() vgg16 = models.vgg16() squeezenet = models.squeezenet1_0() densenet = models.densenet161() inception = models.inception_v3() googlenet = models.googlenet() shufflenet = models.shufflenet_v2_x1_0() mobilenet_v2 = models.mobilenet_v2() mobilenet_v3_large = models.mobilenet_v3_large() mobilenet_v3_small = models.mobilenet_v3_small() resnext50_32x4d = models.resnext50_32x4d() wide_resnet50_2 = models.wide_resnet50_2() mnasnet = models.mnasnet1_0() efficientnet_b0 = models.efficientnet_b0() efficientnet_b1 = models.efficientnet_b1() efficientnet_b2 = models.efficientnet_b2() regnet_y_400mf = models.regnet_y_400mf() regnet_y_800mf = models.regnet_y_800mf() vit_b_16 = models.vit_b_16() vit_b_32 = models.vit_b_32() vit_l_16 = models.vit_l_16() vit_l_32 = models.vit_l_32() convnext_tiny = models.convnext_tiny() convnext_small = models.convnext_small() convnext_base = models.convnext_base() convnext_large = models.convnext_large()若需要加载该网络在ImageNet上预训练的模型,则在括号内设置参数pretrained=True即可。但这种方式有个不好的问题在于这些预训练模型并不是在本地,因此每次运行都会从网上读取加载模型,非常浪费时间。因此,可以去它官网(https://pytorch.org/)上把那个模型下载到本地,通过下面指令完成加载。resnet50.load_state_dict(torch.load('/path/to/resnet50.pth'))另一种自定义层的,一般可以通过torch.nn.Sequential()来构建,在中间插入卷积层、归一化层、激活函数层、池化层即可。例如下方这种是最常用的。self.conv_layer1=nn.Sequential( nn.Conv2d(in_channels=3,out_channels=64,kernel_size=7,stride=1,padding=7//2), nn.BatchNorm2d(64), nn.LeakyReLU(0.1), nn.MaxPool2d(kernel_size=2,stride=2), nn.Conv2d(in_channels=3,out_channels=64,kernel_size=7,stride=1,padding=7//2), nn.BatchNorm2d(64), nn.LeakyReLU(0.1), nn.MaxPool2d(kernel_size=2,stride=2), )当网络很深时,上面这种方式构建比较麻烦,例如resnet,总不可能就按找上面这种方式这么写50层。就把它们共同的部分给构建出来,然后通过传参来设置不同的层。例如:1.下面这里先构建一个基本的几层作为一个类,每一层的参数(不同输入输出通道数,卷积核大小,有无池化)都通过传参来设置。class BasicBlock(nn.Module): expansion = 1 def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1, base_width=64, dilation=1, norm_layer=None): super(BasicBlock, self).__init__() self.conv1 = conv3x3(inplanes, planes, stride) self.bn1 = norm_layer(planes) self.relu = nn.ReLU(inplace=True) self.conv2 = conv3x3(planes, planes) self.bn2 = norm_layer(planes) self.downsample = downsample self.stride = stride def forward(self, x): identity = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) if self.downsample is not None: identity = self.downsample(x) out += identity out = self.relu(out) return out2.下面是设置不同的层。注:上面和下面都不是一个完整的代码,只是用来说明这种很多层的构建方式。layers = [] layers.append(block(self.inplanes, planes, stride, downsample, self.groups, self.base_width, previous_dilation, norm_layer)) self.inplanes = planes * block.expansion for _ in range(1, blocks): layers.append(block(self.inplanes, planes, groups=self.groups, base_width=self.base_width, dilation=self.dilation, norm_layer=norm_layer)) return nn.Sequential(*layers)forward函数这里就是网络的传播路径了,一般就是一路往下传就是。return的内容就是网络的输出。def forward(self,x): x = self.conv_layer1(x) x = self.conv_layer2(x) x = sexlf.flatten(x) x = self.conn_layer1(x) output = self.conn_layer2(x) return output如果想将中间某几层的输出拿出来,做一下特征金字塔,可以像下面这么写。def forward(self,x): conv_layer1 = self.conv_layer1(x) conv_layer2 = self.conv_layer2(conv_layer1) conv_layer3 = self.conv_layer2(conv_layer2) conv_layer4 = self.conv_layer2(conv_layer3) FP = self.YourModule(conv_layer1,conv_layer2,conv_layer3,conv_layer4) flatten = self.flatten(FN) conn_layer1 = self.conn_layer1(flatten) output = self.conn_layer2(conn_layer1) return output像可视化特征图里,想要可视化某一层的特征图,就可以像下面这么写。def forward(self,x): x = self.conv_layer1(x) feature = self.conv_layer2(x) x = sexlf.flatten(feature) x = self.conn_layer1(x) output = self.conn_layer2(x) return feature,output初始化网络初始化网络是要放在init函数里完成,分为两类,一类是随机初始化,一类是加载预训练模型。随机初始化关于随机初始化,目前主要有多种方式:Normal Initialization, Uniform Initialization,Xavier Initialization,He Initialization (也称 kaiming Initialization),LeCun Initialization。关于这些初始化方法,可以看这篇文章《神经网络的初始化方法总结 | 又名“如何选择合适的初始化方法”》。我们一般使用Kaiming Initialization。下面是一种方式,直接按自定义的方式初始化。def _initialize_weights(self): for m in self.modules(): if isinstance(m, nn.Conv2d): n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels m.weight.data.normal_(0, math.sqrt(2. / n)) if m.bias is not None: m.bias.data.zero_() elif isinstance(m, nn.BatchNorm2d): m.weight.data.fill_(1) m.bias.data.zero_() elif isinstance(m, nn.Linear): m.weight.data.normal_(0, 0.01) m.bias.data.zero_()也可以选择pytorch实现了的初始化。for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): nn.init.constant_(m.weight, 1) nn.init.constant_(m.bias, 0)还可以像下面这么写:from torch.nn import init def weights_init_kaiming(m): classname = m.__class__.__name__ # print(classname) if classname.find('Conv') != -1: init.kaiming_normal_(m.weight.data, a=0, mode='fan_in') # For old pytorch, you may use kaiming_normal. elif classname.find('Linear') != -1: init.kaiming_normal_(m.weight.data, a=0, mode='fan_out') init.constant_(m.bias.data, 0.0) elif classname.find('BatchNorm2d') != -1: init.normal_(m.weight.data, 1.0, 0.02) init.constant_(m.bias.data, 0.0) def weights_init_classifier(m): classname = m.__class__.__name__ if classname.find('Linear') != -1: init.normal_(m.weight.data, std=0.001) init.constant_(m.bias.data, 0.0) self.conv_layer1=nn.Sequential( nn.Conv2d(in_channels=3,out_channels=64,kernel_size=7,stride=1,padding=7//2), nn.BatchNorm2d(64), nn.LeakyReLU(0.1), nn.MaxPool2d(kernel_size=2,stride=2) ) self.conv_layer1.apply(weights_init_kaiming)反正随便选择一种就好。加载预训练模型初始化加载预训练模型一般是在train文件里写,但有些网络由于是使用现成的backbone网络,例如使用了resnet50,然后后面加了自定义的模块,所以它想要resnet50预训练模型初始化backbone,而其它层做随机初始化,那加载预训练模型就是在网络定义中做的。因此,既然这里提到了初始化,就干脆写在这里。最简单的就是直接整个模型都加载。resnet50.load_state_dict(torch.load('/path/to/resnet50.pth'))但也有一些情况下,我只想加载其中一部分层的参数。剩下一部分由于已经改变参数了,无法加载预训练模型,所以要选择上面的随机初始化。这里有必要来说明网络的每一层是如何表示的。下面以一个例子来说明。class Flatten(nn.Module): def __init__(self): super(Flatten,self).__init__() def forward(self,x): return x.view(x.size(0),-1) class YourNet(nn.Module): def __init__(self,stride=2, pool='avg'): super(YourNet, self).__init__() self.resnet50 = models.resnet50(pretrained=False) self.model.load_state_dict(torch.load('/path/to/resnet50.pth')) self.flatten = Flatten() self.conn_layer1 = nn.Sequential( nn.Linear(in_features=7 * 7 * 1024, out_features=4096), nn.Dropout(0.5), nn.LeakyReLU(0.1) ) self.conn_layer2 = nn.Sequential(nn.Linear(in_features=4096, out_features=7 * 7 * (2 * 5 + 20))) def forward(self,x): #这里省略 if __name__ == "__main__": model = YourNet() for name, value in model.named_parameters(): print(name)这里简单定义了一个网络。在最后面有这两行:for name, value in model.named_parameters(): print(name)这两行的输出就是打印网络层的名字,实际上加载预训练模型时,也是按照这个名字来加载的。下面是一部分输出。resnet50.conv1.weight resnet50.bn1.weight resnet50.bn1.bias resnet50.layer1.0.conv1.weight resnet50.layer1.0.bn1.weight resnet50.layer1.0.bn1.bias resnet50.layer1.0.conv2.weight resnet50.layer1.0.bn2.weight resnet50.layer1.0.bn2.bias resnet50.layer1.0.conv3.weight resnet50.layer1.0.bn3.weight resnet50.layer1.0.bn3.bias resnet50.layer1.0.downsample.0.weight resnet50.layer1.0.downsample.1.weight resnet50.layer1.0.downsample.1.bias ... ... resnet50.layer4.2.bn3.weight resnet50.layer4.2.bn3.bias resnet50.fc.weight resnet50.fc.bias conn_layer1.0.weight conn_layer1.0.bias conn_layer2.0.weight conn_layer2.0.bias在预训练模型中就是这样,key即为网络层的名字,value即为它们对应的参数。因此,加载预训练模型可以按照下面这种方式加载。pretrained_dict = torch.load('/path/to/resnet50.pth') pretrained_dict.pop('fc.weight') pretrained_dict.pop('fc.bias') #自己的模型参数变量 model_dict = model.state_dict() #去除一些不需要的参数 pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict} #参数更新 model_dict.update(pretrained_dict) # 加载我们真正需要的state_dict model.load_state_dict(model_dict)自己定义的一些层是不会出现在pretrained_dict中,因此会将其剔除,从而只加载了pretrained_dict中有的层。 本文介绍了如何搭建神经网络,构建网络的几种方式,前向传播的过程,几种初始化方式,如何加载预训练模型的指定层等内容。下一篇我们将介绍如何写train函数,以及包括设置优化方式,设置学习率,不同层设置不同学习率,解析参数等。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、最新论文解读、各种技术教程、CV招聘信息发布等。关注公众号可邀请加入免费版的知识星球和技术交流群。
写在前面的话特征图可视化是很多论文所需要做的一份工作,其作用可以是用于证明方法的有效性,也可以是用来增加工作量,给论文凑字数。具体来说就是可视化两个图,使用了新方法的和使用之前的,对比有什么区别,然后看图写论文说明新方法体现的作用。吐槽一句,有时候这个图 论文作者自己都不一定能看不懂,虽然确实可视化的图有些改变,但并不懂这个改变说明了什么,反正就吹牛,强行往自己新方法编的故事上扯,就像小学一年级的作文题--看图写作文。之前知乎上有一个很热门的话题,如果我在baseline上做了一点小小的改进,却有很大的效果,这能写论文吗?这种情况最大的问题就在于要如何写七页以上,那一点点的改进可能写完思路,公式推理,画图等内容才花了不到一页,剩下的内容如何搞?可视化特征图!!!这一点可以在我看过的甚多论文上有所体现,反正我是没看明白论文给的可视化图,作者却能扯那么多道道。这应该就是用来增加论文字数和增加工作量的。总之一句话,可视化特征图是很重要的工作,最好要会。 初始化配置这部分先完成加载数据,修改网络,定义网络,加载预训练模型。加载数据并预处理这里只加载一张图片,就不用通过classdataset了,因为classdataset是针对大量数据的,生成一个迭代器一批一批地将图片送给网络。但我们仍然要完成classdataset中数据预处理的部分。数据预处理所必须要有的操作是调整大小,转化为Tensor格式,归一化。至于其它数据增强或预处理的操作,自己按需添加。def image_proprecess(img_path): img = Image.open(img_path) data_transforms = transforms.Compose([ transforms.Resize((384, 384), interpolation=3), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) data = data_transforms(img) data = torch.unsqueeze(data,0) return data这里由于只加载一张图片,因此后面要使用torch.unsqueeze将三维张量变成四维。 修改网络假如你要可视化某一层的特征图,则需要将该层的特征图返回出来,因此需要先修改网络中的forward函数。具体修改方式如下所示。def forward(self, x): x = self.model.conv1(x) x = self.model.bn1(x) x = self.model.relu(x) x = self.model.maxpool(x) feature = self.model.layer1(x) x = self.model.layer2(feature) x = self.model.layer3(x) x = self.model.layer4(x) return feature,x定义网络并加载预训练模型def Init_Setting(epoch): dirname = '/mnt/share/VideoReID/share/models/Methods5_trial1' model = siamese_resnet50(701, stride=1, pool='avg') trained_path = os.path.join(dirname, 'net_%03d.pth' % epoch) print("load %03d.pth" % epoch) model.load_state_dict(torch.load(trained_path)) model = model.cuda().eval() return model这部分需要说明的是最后一行,要将网络设置为推理模式。 可视化特征图这部分主要是将特征图的某一通道转化为一张图来可视化def visualize_feature_map(img_batch,out_path,type,BI): feature_map = torch.squeeze(img_batch) feature_map = feature_map.detach().cpu().numpy() feature_map_sum = feature_map[0, :, :] feature_map_sum = np.expand_dims(feature_map_sum, axis=2) for i in range(0, 2048): feature_map_split = feature_map[i,:, :] feature_map_split = np.expand_dims(feature_map_split,axis=2) if i > 0: feature_map_sum +=feature_map_split feature_map_split = BI.transform(feature_map_split) plt.imshow(feature_map_split) plt.savefig(out_path + str(i) + "_{}.jpg".format(type) ) plt.xticks() plt.yticks() plt.axis('off') feature_map_sum = BI.transform(feature_map_sum) plt.imshow(feature_map_sum) plt.savefig(out_path + "sum_{}.jpg".format(type)) print("save sum_{}.jpg".format(type))这里一行一行来解释。1. 参数img_batch是从网络中的某一层传回来的特征图,BI是双线性插值的函数,自定义的,下面会讲。2. 由于只可视化了一张图片,因此img_batch是四维的,且batchsize维为1。第三行将它从GPU上弄到CPU上,并变成numpy格式。3. 剩下部分主要完成将每个通道变成一张图,以及将所有通道每个元素对应位置相加,并保存。 双线性插值由于经过多次网络降采样,后面层的特征图往往变得只有7x7,16x16大小。可视化后特别小,因此需要将它上采样,这里采样的方式是双线性插值。因此,这里给一份双线性插值的代码。class BilinearInterpolation(object): def __init__(self, w_rate: float, h_rate: float, *, align='center'): if align not in ['center', 'left']: logging.exception(f'{align} is not a valid align parameter') align = 'center' self.align = align self.w_rate = w_rate self.h_rate = h_rate def set_rate(self,w_rate: float, h_rate: float): self.w_rate = w_rate # w 的缩放率 self.h_rate = h_rate # h 的缩放率 # 由变换后的像素坐标得到原图像的坐标 针对高 def get_src_h(self, dst_i,source_h,goal_h) -> float: if self.align == 'left': # 左上角对齐 src_i = float(dst_i * (source_h/goal_h)) elif self.align == 'center': # 将两个图像的几何中心重合。 src_i = float((dst_i + 0.5) * (source_h/goal_h) - 0.5) src_i += 0.001 src_i = max(0.0, src_i) src_i = min(float(source_h - 1), src_i) return src_i # 由变换后的像素坐标得到原图像的坐标 针对宽 def get_src_w(self, dst_j,source_w,goal_w) -> float: if self.align == 'left': # 左上角对齐 src_j = float(dst_j * (source_w/goal_w)) elif self.align == 'center': # 将两个图像的几何中心重合。 src_j = float((dst_j + 0.5) * (source_w/goal_w) - 0.5) src_j += 0.001 src_j = max(0.0, src_j) src_j = min((source_w - 1), src_j) return src_j def transform(self, img): source_h, source_w, source_c = img.shape # (235, 234, 3) goal_h, goal_w = round( source_h * self.h_rate), round(source_w * self.w_rate) new_img = np.zeros((goal_h, goal_w, source_c), dtype=np.uint8) for i in range(new_img.shape[0]): # h src_i = self.get_src_h(i,source_h,goal_h) for j in range(new_img.shape[1]): src_j = self.get_src_w(j,source_w,goal_w) i2 = ceil(src_i) i1 = int(src_i) j2 = ceil(src_j) j1 = int(src_j) x2_x = j2 - src_j x_x1 = src_j - j1 y2_y = i2 - src_i y_y1 = src_i - i1 new_img[i, j] = img[i1, j1]*x2_x*y2_y + img[i1, j2] * \ x_x1*y2_y + img[i2, j1]*x2_x*y_y1 + img[i2, j2]*x_x1*y_y1 return new_img #使用方法 BI = BilinearInterpolation(8, 8) feature_map = BI.transform(feature_map)main函数流程上面介绍了各个部分的代码,下面就是整体流程。比较简单imgs_path = "/path/to/imgs/" save_path = "/save/path/to/output/" model = Init_Setting(120) BI = BilinearInterpolation(8, 8) data = image_proprecess(out_path + "0836.jpg") data = data.cuda() output, _ = model(data) visualize_feature_map(output, save_path, "drone", BI)可视化效果图欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、最新论文解读、各种技术教程、CV招聘信息发布等。关注公众号可邀请加入免费版的知识星球和技术交流群。
(零) 概述浮躁是人性的一个典型的弱点,很多人总擅长看别人分享的现成代码解读的文章,看起来学会了好多东西,实际上仍然不具备自己从零搭建一个pipeline的能力。在公众号(CV技术指南)的交流群里(群内交流氛围不错,有需要的请关注公众号加群),常有不少人问到一些问题,根据这些问题明显能看出是对pipeline不了解,却已经在搞项目或论文了,很难想象如果基本的pipeline都不懂,如何分析代码问题所在?如何分析结果不正常的可能原因?遇到问题如何改?Pytorch在这几年逐渐成为了学术上的主流框架,其具有简单易懂的特点。网上有很多pytorch的教程,如果是一个已经懂的人去看这些教程,确实pipeline的要素都写到了,感觉这教程挺不错的。但实际上更多地像是写给自己看的一个笔记,记录了pipeline要写哪些东西,却没有介绍要怎么写,为什么这么写,刚入门的小白看的时候容易云里雾里。鉴于此,本教程尝试对于pytorch搭建一个完整pipeline写一个比较明确且易懂的说明。本教程将介绍以下内容:准备数据,自定义classdataset,分布式训练的数据加载方式,加载超大数据集的改进思路。搭建模型与模型初始化。编写训练过程,包括加载预训练模型、设置优化器、设置损失函数等。可视化并保存训练过程。编写推理函数。 (一)数据读取classdataset的定义先来看一个完整的classdatasetimport torch.utils.data as data import torchvision.transforms as transforms class MyDataset(data.Dataset): def __init__(self,data_folder): self.data_folder = data_folder self.filenames = [] self.labels = [] per_classes = os.listdir(data_folder) for per_class in per_classes: per_class_paths = os.path.join(data_folder, per_class) label = torch.tensor(int(per_class)) per_datas = os.listdir(per_class_paths) for per_data in per_datas: self.filenames.append(os.path.join(per_class_paths, per_data)) self.labels.append(label) def __getitem__(self, index): image = Image.open(self.filenames[index]) label = self.labels[index] data = self.proprecess(image) return data, label def __len__(self): return len(self.filenames) def proprecess(self,data): transform_train_list = [ transforms.Resize((self.opt.h, self.opt.w), interpolation=3), transforms.Pad(self.opt.pad, padding_mode='edge'), transforms.RandomCrop((self.opt.h, self.opt.w)), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ] return transforms.Compose(transform_train_list)classdataset的几个要点:classdataset类继承torch.utils.data.dataset。classdataset的作用是将任意格式的数据,通过读取、预处理或数据增强后以tensor的形式输出。其中任意格式的数据可能是以文件夹名作为类别的形式、或以txt文件存储图片地址的形式、或视频、或十几帧图像作为一份样本的形式。而输出则指的是经过处理后的一个batch的tensor格式数据和对应标签。classdataset主要有三个函数要完成:__init__函数、__getitem__ 函数和__len__函数。 __init__函数init函数主要是完成两个静态变量的赋值。一个是用于存储所有数据路径的变量,变量的每个元素即为一份训练样本,(注:如果一份样本是十几帧图像,则变量每个元素存储的是这十几帧图像的路径),可以命名为self.filenames。一个是用于存储与数据路径变量一一对应的标签变量,可以命名为self.labels。假如数据集的格式如下:#这里的0,1指的是类别0,1 /data_path/0/image0.jpg /data_path/0/image1.jpg /data_path/0/image2.jpg /data_path/0/image3.jpg ...... /data_path/1/image0.jpg /data_path/1/image1.jpg /data_path/1/image2.jpg /data_path/1/image3.jpg可通过per_classes = os.listdir(data_path) 获得所有类别的文件夹,在此处per_classes的每个元素即为对应的数据标签,通过for遍历per_classes即可获得每个类的标签,将其转换成int的tensor形式即可。在for下获得每个类下每张图片的路径,通过self.join获得每份样本的路径,通过append添加到self.filenames中。 __getitem__ 函数getitem 函数主要是根据索引返回对应的数据。这个索引是在训练前通过dataloader切片获得的,这里先不管。它的参数默认是index,即每次传回在init函数中获得的所有样本中索引对应的数据和标签。因此,可通过下面两行代码找到对应的数据和标签。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。image = Image.open(self.filenames[index])) label = self.labels[index]获得数据后,进行数据预处理。数据预处理主要通过 torchvision.transforms 来完成,这里面已经包含了常用的预处理、数据增强方式上面这里介绍了最常用的几种,主要就是resize,随机裁剪,翻转,归一化等。最后通过transforms.Compose(transform_train_list)来执行。 除了这些已经有的数据增强方式外,在《数据增强方法总结》中还介绍了十几种特殊的数据增强方式,像这种自己设计了一种新的数据增强方式,该如何添加进去呢?下面以随机擦除作为例子。class RandomErasing(object): """ Randomly selects a rectangle region in an image and erases its pixels. 'Random Erasing Data Augmentation' by Zhong et al. See https://arxiv.org/pdf/1708.04896.pdf Args: probability: The probability that the Random Erasing operation will be performed. sl: Minimum proportion of erased area against input image. sh: Maximum proportion of erased area against input image. r1: Minimum aspect ratio of erased area. mean: Erasing value. """ def __init__(self, probability=0.5, sl=0.02, sh=0.4, r1=0.3, mean=[0.4914, 0.4822, 0.4465]): self.probability = probability self.mean = mean self.sl = sl self.sh = sh self.r1 = r1 def __call__(self, img): if random.uniform(0, 1) > self.probability: return img for attempt in range(100): area = img.size()[1] * img.size()[2] target_area = random.uniform(self.sl, self.sh) * area aspect_ratio = random.uniform(self.r1, 1 / self.r1) h = int(round(math.sqrt(target_area * aspect_ratio))) w = int(round(math.sqrt(target_area / aspect_ratio))) if w < img.size()[2] and h < img.size()[1]: x1 = random.randint(0, img.size()[1] - h) y1 = random.randint(0, img.size()[2] - w) if img.size()[0] == 3: img[0, x1:x1 + h, y1:y1 + w] = self.mean[0] img[1, x1:x1 + h, y1:y1 + w] = self.mean[1] img[2, x1:x1 + h, y1:y1 + w] = self.mean[2] else: img[0, x1:x1 + h, y1:y1 + w] = self.mean[0] return img return img如上所示,自己写一个类RandomErasing,继承object,在call函数里完成你的操作。在transform_train_list里添加上RandomErasing的定义即可。transform_train_list = [ transforms.Resize((self.opt.h, self.opt.w), interpolation=3), transforms.Pad(self.opt.pad, padding_mode='edge'), transforms.RandomCrop((self.opt.h, self.opt.w)), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) RandomErasing(probability=self.opt.erasing_p, mean=[0.0, 0.0, 0.0]) #添加到这里 ]__len__函数len函数主要就是返回数据长度,即样本的总数量。前面介绍了self.filenames的每个元素即为每份样本的路径,因此,self.filename的长度就是样本的数量。通过return len(self.filenames)即可返回数据长度。 验证classdatasettrain_dataset = My_Dataset(data_folder=data_folder) train_loader = DataLoader(train_dataset, batch_size=16, shuffle=False) print('there are total %s batches for train' % (len(train_loader))) for i,(data,label) in enumerate(train_loader): print(data.size(),label.size())分布式训练的数据加载方式前面介绍的是单卡的数据加载,实际上分布式也是这样,但为了高速高效读取,每张卡上也会保存所有数据的信息,即self.filenames和self.labels的信息。只是在DistributedSampler 中会给每张卡分配互不交叉的索引,然后由torch.utils.data.DataLoader来加载。dataset = My_Dataset(data_folder=data_folder) sampler = DistributedSampler(dataset) if is_distributed else None loader = DataLoader(dataset, shuffle=(sampler is None), sampler=sampler)数据读取的完整流程结合上面这段代码,在这里,我们介绍以下读取数据的整个流程。首先定义一个classdataset,在初始化函数里获得所有数据的信息。classdataset中实现getitem函数,通过索引来获取对应的数据,然后对数据进行预处理和数据增强。在模型训练前,初始化classdataset,通过Dataloader来加载数据,其加载方式是通过Dataloader中分配的索引,调用getitem函数来获取。关于索引的分配,在单卡上,可通过设置shuffle=True来随机生成索引顺序;在多机多卡的分布式训练上,shuffle操作通过DistributedSampler来完成,因此shuffle与sampler只能有一个,另一个必须为None。 超大数据集的加载思路问题所在再回顾一下上面这个流程,前面提到所有数据信息在classdataset初始化部分都会保存在变量中,因此当面对超大数据集时,会出现内存不足的情况。思路将切片获取索引的步骤放到classdataset初始化的位置,此时每张卡都是保存不同的数据子集。通过这种方式,可以将内存用量减少到原来的world_size倍(world_size指卡的数量)。参考代码class RankDataset(Dataset): ''' 实际流程 获取rank和world_size 信息 -> 获取dataset长度 -> 根据dataset长度产生随机indices -> 给不同的rank 分配indices -> 根据这些indices产生metas ''' def __init__(self, meta_file, world_size, rank, seed): super(RankDataset, self).__init__() random.seed(seed) np.random.seed(seed) self.world_size = world_size self.rank = rank self.metas = self.parse(meta_file) def parse(self, meta_file): dataset_size = self.get_dataset_size(meta_file) # 获取metafile的行数 local_rank_index = self.get_local_index(dataset_size, self.rank, self.world_size) # 根据world size和rank,获取当前epoch,当前rank需要训练的index。 self.metas = self.read_file(meta_file, local_rank_index) def __getitem__(self, idx): return self.metas[idx] def __len__(self): return len(self.metas) ##train for epoch_num in range(epoch_num): dataset = RankDataset("/path/to/meta", world_size, rank, seed=epoch_num) sampler = RandomSampler(datset) dataloader = DataLoader( dataset=dataset, batch_size=32, shuffle=False, num_workers=4, sampler=sampler)但这种思路比较明显的问题时,为了让每张卡上在每个epoch都加载不同的训练子集,因此需要在每个epoch重新build dataloader。总结本篇文章介绍了数据读取的完整流程,如何自定义classdataset,如何进行数据增强,自己设计的数据增强如何写,分布式训练是如何加载数据的,超大数据集的数据加载改进思路。相信读完本文的读者对数据读取有了比较清晰的认识,下一篇将介绍搭建模型与模型初始化。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、最新论文解读、各种技术教程、CV招聘信息发布等。关注公众号可邀请加入免费版的知识星球和技术交流群。
CNN解释器这是一个中国博士发布的名叫CNN解释器的在线交互可视化工具。主要对于那些初学深度学习的小白们 理解关于神经网络是如何工作很有帮助,如卷积过程,ReLU过程,平均池化过程,中间每一层的特征图的样子,都可以看到,相当于给了一个显微镜,可以随意对任意一层,任何一项操作的前后变化,观察得清清楚楚。显示卷积的过程中前后特征图的变化,中间的操作CNN是如何输出预测的还可以在线上传图片,看到一张图片在经过每一层的卷积,池化,激活后的变化,最后输出预测结果。特征图、卷积核、热力图的可视化项目可视化特征图https://github.com/waallf/Viusal-feature-map 可视化卷积核https://keras.io/examples/vision/visualizing_what_convnets_learn/https://blog.keras.io/how-convolutional-neural-networks-see-the-world.html Grad-CAMhttps://github.com/ramprs/grad-cam 热力图https://github.com/heuritech/convnets-keras 下面这个项目是同时包含特征图可视化,卷积核可视化和热力图的一个链接:https://github.com/raghakot/keras-vis网络结构可视化工具总结Netscope用于可视化模型结构的在线工具,仅支持caffe的prototxt文件可视化。需要自己写prototxt格式的文件。ConvNetDraw这个工具用两个图可直接说明,第一个是输入,第二个是输出PlotNeuralNet这个稍微麻烦一点点,效果图如下NN-SVG这个非常简单,它是一个网页,在左侧输入网络的层数大小,就可以在右边输出网络结构。网络结构手动画图工具很多新手会问的一个问题,论文中那些网络结构图是如何画的。这里解答一下,我所了解的主要是用PPT, VISIO。当然也可以使用上面那几个。
BackgroundVision Transformer(ViT)是第一个可以直接应用于图像分类的全Transformer模型。具体地说,ViT将每个图像分割成固定长度的14×14或16×16块(也称为tokens);然后ViT应用Transformer层对这些tokens之间的全局关系进行建模以进行分类。尽管ViT证明了全Transformer架构在视觉任务中很有前途,但在中型数据集(例如ImageNet)上从头开始训练时,其性能仍逊于类似大小的CNN对等架构(例如ResNets)。论文假设,这种性能差距源于ViT的两个主要局限性:1)通过硬分裂对输入图像进行简单的tokens化,使得ViT无法对图像的边缘和线条等局部结构进行建模,因此它需要比CNN多得多的训练样本(如JFT-300M用于预训练)才能获得类似的性能;2)ViT的注意力骨干没有很好地像用于视觉任务的CNN那样的设计,如ViT具有冗余性和特征丰富度有限的缺点,导致模型训练困难。为了验证论文的假设,论文进行了一项初步研究,通过图2中的可视化来调查ViTL/16和ResNet5的获知特征的差异。论文观察ResNet的功能,捕捉所需的局部结构(边、线、纹理等)。从底层(Cv1)逐渐向中间层(Cv25)递增。然而,ViT的特点却截然不同:结构信息建模较差,而全局关系(如整条狗)被所有的注意块捕获。这些观察结果表明,当直接将图像分割成固定长度的tokens时,原始 ViT忽略了局部结构。此外,论文发现ViT中的许多通道都是零值(在图2中以红色突出显示),这意味着ViT的主干不如ResNet高效,并且在训练样本不足的情况下提供有限的特征丰富度。图2.在ImageNet上训练的ResNet50、ViT-L/16和论文提出的T2T-VIT-24的功能可视化。绿色框突出显示学习的低级结构特征,如边和线;红色框突出显示值为零或过大的无效要素地图。注意:这里为ViT和T2T-ViT可视化的特征图不是attention图,而是从tokens重塑的图像特征。 创新思路论文决意设计一种新的full-Transformer视觉模型来克服上述限制。1)与ViT中使用的朴素tokens化不同,论文提出了一种渐进式tokens化模块,将相邻tokens聚合为一个tokens(称为tokens-to-token模块),该模块可以对周围tokens的局部结构信息进行建模,并迭代地减少tokens的长度。具体地说,在每个tokens-to-token(T2T)步骤中,transformer层输出的tokens被重构为图像(restructurization),然后图像被分割成重叠(soft split)的tokens,最后周围的tokens通过flatten分割的patches被聚集在一起。因此,来自周围patches的局部结构被嵌入要输入到下一transformer层的tokens中。通过迭代进行T2T,将局部结构聚合成tokens,并通过聚合过程减少tokens的长度。2)为了寻找高效的Vision Transformer主干,论文借鉴了CNN的一些架构设计来构建Transformer层,以提高功能的丰富性,论文发现ViT中通道较少但层数较多的“深度窄”架构设计在同等型号和MAC(Multi-Adds)的情况下性能要好得多。具体地说,论文研究了宽ResNet(浅宽VS深窄结构)、DenseNet(密集连接)、ResneXt结构、Ghost操作和通道注意。论文发现其中,深窄结构对于ViT是最有效和最有效的,在几乎不降低性能的情况下显著地减少了参数数目和MACs。这也表明CNNs的体系结构工程可以为Vision Transformer的骨干设计提供帮助。基于T2T模块和深度窄骨干网架构,论文开发了tokens-to-token Vision Transformer(T2T-ViT),它在ImageNet上从头开始训练时显著提高了性能,而且比普通ViT更轻便。 MethodsT2T-ViT由两个主要部分组成(图4):1)一个层次化的“Tokens-to-Token模块”(T2T模块),用于对图像的局部结构信息进行建模,并逐步减少tokens的长度;2)一个有效的“T2T-ViT骨干”,用于从T2T模块中提取对tokens的全局关注关系。在研究了几种基于CNN的体系结构设计后,对主干采用深窄结构,以减少冗余度,提高特征丰富性。图4.T2T-ViT的整体网络架构。在T2T模块中,首先将输入图像soft split为patches,然后将其展开为token T0序列。在T2T模块中,token的长度逐渐减小(在这里使用两次迭代和输出Tf)。然后,T2T-VIT主干将固定token作为输入并输出预测。两个T2T块与图3相同,PE为位置嵌入。 Tokens-to-TokenTokens-to-Token(T2T)模块旨在克服ViT中简单tokens化的限制。它将图像逐步结构化为表征,并对局部结构信息进行建模,这样可以迭代地减少表征的长度。每个T2T流程有两个步骤:重组和Soft Split(SS)(图3)。图3.T2T流程图解。经过变换和reshape后,tokens Ti被重构为图像Ii,然后重叠split为tokens Ti+1。具体地说,如粉色面板中所示,输入Ii的四个tokens(1、2、4、5)被串联以形成一个tokens 在Ti+1。T2T transformer可以是普通的transformer 层或有限GPU存储器中的像Performer层这样的其他高效transformer。在进行soft split时,每个块的大小为k×k,在图像上叠加s个,其中k−类似于卷积运算中的步长。因此,对于重建图像I_∈_rh×w×c,soft split后的输出tokens的长度为每个分割patches的大小为k×k×c。将空间维度上的所有patches展平,以To表示。在soft split之后,为下一个T2T过程馈送输出tokens。通过迭代进行上述重构和soft split,T2T模块可以逐步减少tokens的长度,并转换图像的空间结构。T2T模块中的迭代过程可以表示为对于输入image I0,首先应用soft split将其分割为tokens:T1=SS(I0)。在最终迭代后,T2T模块的输出tokens Tf具有固定的长度,因此T2T-ViT的主干可以对Tf上的全局关系进行建模。 T2T-ViT Backbone论文探索了不同的VIT体系结构设计,并借鉴了CNN的一些设计,以提高骨干网的效率,增强学习特征的丰富性。由于每个transformer层都有跳跃连接,一个简单的想法是采用如DenseNet的密集连接来增加连通性和特征丰富性,或者采用Wide-ResNets或ResNeXt结构来改变VIT主干中的通道尺寸和头数。论文探讨了从CNN到VIT的五种架构设计:密集连接如DenseNet;深-窄与浅-宽结构如宽ResNet];通道注意如挤压-激励(SE)网络;多头注意层中更多的分头如ResNeXt;Ghost操作如Ghost Net。实验发现:1)采用简单降低通道维数的深窄结构来减少通道中的冗余,增加层深来提高VIT中的特征丰富度,模型尺寸和MACs都有所减小,但性能有所提高;2)SE块的通道关注度也提高了VIT,但效果不如深窄结构。 基于这些发现,论文为T2T-VIT骨干网设计了一种深窄结构。具体地说,它具有较小的通道数和隐藏维度d,但具有更多的层b。对于T2T模块最后一层定长的Token,论文在其上拼接一个类Token,然后添加正弦位置嵌入(PE),与VIT一样进行分类:T2T-ViT ArchitectureT2T-VIT的结构细节。T2T-VIT-14/19/24的型号尺寸与ResNet50/101/152相当。T2T-VIT-7/12的型号大小与MobileNetV1/V2相当。对于T2T transformer 层,在有限的GPU内存下,论文采用了T2T-VITT-14的transformer层和T2T-VIT-14的Performer层。对于VIT,‘S’表示小,‘B’表示基本,‘L’表示大。‘VIT-S/16’是原始VIT-B/16的变体,具有更小的MLP大小和层深。 Conclusion如图1所示,论文的215M参数和5.2G MACS的T2T-ViT在ImageNet上可以达到81.5%的TOP-1准确率,远远高于ViT的48.6M参数和10.1G MACs的TOP-1准确率(78.1%)。这一结果也高于流行的类似大小的CNN,如具有25.5M参数的ResNet50(76%-79%)。此外,论文还通过简单地采用更少的层来设计T2T-ViT的精简变体,取得了与MobileNets(图1)相当的结果。T2T-VIT与VIT在ImageNet上从头训练的比较将CNN中的一些常用设计移植到VIT&T2T-VIT中,包括DenseNet、Wide-ResNet、SE模块、ResNeXt、Ghost操作。相同的颜色表示相应的迁移。所有模型都是在ImageNet上从头开始训练的。
Backgound深度学习通过在大规模、平衡和精选的数据集(如ImageNet和Kinetics上训练强大的神经网络,在图像和视频领域的视觉识别方面取得了显著的进展。与这些人工平衡的数据集不同,真实世界的数据总是遵循长尾分布,这使得收集平衡数据集更具挑战性,特别是对于样本稀少的自然类。然而,由于数据分布高度不平衡,直接从长尾数据中学习会导致显著的性能退化。缓解长尾训练数据造成的恶化的一系列常见方法是基于类别重新平衡策略,包括重新采样训练数据和设计成本敏感的重新加权损失函数。这些方法可以有效地减少训练过程中头部类的支配,从而产生更精确的分类决策边界。然而,由于原始数据分布是扭曲的,而过度参数化的深层网络很容易拟合这种合成分布,因此它们经常面临过拟合尾类的风险。图1.真实世界的数据总是遵循长尾数据分布,这是由几个样本丰富的头部类(即蓝色立方体)主导的,但也包含许多数据稀缺的尾类(即绿立方体和黄色立方体),称为原始分布。直接从长尾数据学习可能会导致性能显著下降。处理不平衡问题的一种常见方法是重新采样,即随机地从头类中丢弃图像,并从尾类中重复采样图像(相同的图像用唯一的罗马数字标记),从而产生均匀分布。这种策略可能会导致尾部类过多,而头部类不合适。为了克服这些问题,最近的工作分离了表示学习和分类器训练的任务。该两阶段训练方案首先学习原始数据分布下的视觉表示,然后在类均衡采样下训练冻结特征的线性分类器。事实证明,这个简单的两阶段训练方案能够处理过度拟合的问题,并在标准的长尾基准上设定了新的SOTA性能。然而,这种两阶段训练方案不能很好地处理不平衡的标签分配问题,特别是在表征学习阶段。 创新思路论文的目标是设计一种新的长尾视觉识别学习范式,以期共享两种长尾识别方法的优点,即对过拟合问题具有较强的鲁棒性,并有效地处理不平衡标签问题。为了达到这一目标,论文的想法是研究如何将标签相关性纳入到一个多阶段的训练方案中。受到模型压缩中知识蒸馏工作的启发,作者观察到由教师网络产生的软标签能够捕获类之间的内在关系,这可能有助于通过将知识从头部类转移到尾部类来进行长尾识别,如图1所示。因此,软标签为标签建模的多阶段训练策略提供了一种实用的解决方案。基于上述分析,论文提出了一种概念上简单但特别有效的长尾视觉识别的多阶段训练方案,称为蒸馏自监督(Self Supervision to Distillation, SSD)。SSD的主要贡献有两个方面:(1)用于学习有效的长尾识别网络的自蒸馏框架;(2)自监督引导的蒸馏标签生成模块,为自蒸馏提供更少偏向但更多信息的软标签。具体地说,首先将多级长尾训练流水线简化在一个简单的自精馏框架内,在该框架中我们能够自然地自动挖掘标签关系,并结合这种固有的标签结构来提高多级训练的泛化性能。然后,为了进一步提高自精馏框架的健壮性,论文从长尾训练集本身出发,提出了一种自监督的增强精馏标签生成模块。自监督学习能够在没有标签的情况下学习有效的视觉表征,并且可以平等对待每幅图像,从而缓解了标签分布不平衡对软标签生成的影响。 综上所述,论文的主要贡献如下:提出了一种简单有效的多阶段训练框架(SSD)。在该框架中,通过将软标签建模引入特征学习阶段,共享了再平衡采样和解耦训练策略的优点。提出了一种自监督引导的软标签生成模块,该模块可以同时从数据域和标签域生成鲁棒的软标签。这些软标签通过从头到尾传递知识来提供有效的信息。SSD在三个具有挑战性的长尾识别基准测试(包括ImageNet-LT、CIFAR100-LT和iNaturist 2018数据集)上实现了SOTA性能。 Methods蒸馏自监控(SSD)的总体框架如图2所示。SSD由三个步骤组成:(1)自监督引导的特征学习;(2)中间软标签生成;(3)联合训练和自蒸馏。图2.自监督蒸馏(SSD)框架的管道。论文首先使用实例平衡抽样同时训练标签监督和自监督下的初始教师网络。然后,通过类均衡采样细化类决策边界,在视觉表示的基础上训练一个单独的线性分类器。这种新的分类器产生训练样本的软标签,用于自蒸馏。最后,在前一阶段的软标签和原始训练集中的硬标签的混合监督下训练一个自精馏网络。由于硬标签和软标签在是否偏向头部类的问题上存在语义鸿沟,论文对这两种监督分别采用了两个分类头。 自监督引导的特征学习在这一阶段,在原始的长尾数据分布下训练网络进行分类任务。分类任务由两部分组成:一是传统的C-Way分类任务,其目的是将图像分类到C语义类别;二是纯粹从数据本身出发的平衡的自监督分类任务。虽然C-way分类任务提供了丰富的语义信息,但它也受到长尾标签的影响。尾部类的样本可能会被数据丰富的类淹没,从而导致表示不足的问题。因此,论文构造了均衡的自监督分类任务,例如预测图像旋转和实例判别,这些任务在不受标签影响的情况下平等地考虑每幅图像。旋转预测识别{0◦,90◦,180◦,270◦}之间的旋转角度。实例判别将每个图像视为一个单一类别,该类别等同于TON-WAY分类,其中N是训练集中的图像数量。 中间软标签生成在此阶段,需要在冻结要素之上的类平衡设置下调优分类器,以生成提取的标签。论文选择了可学习权重缩放(LWS)方法,因为它在各种设置下都有良好的性能。它学习重新调整分类器的权重,以避免类头部的倾向。在给定图像的情况下,微调的分类器提供了相对平衡和软的标签,集成了标签驱动和数据驱动的信息,作为下一步自蒸馏的教师监督。 联合训练和自蒸馏由于表示和分类器是在不同的采样策略下分别训练的,因此整个网络可能不是最优的。然而,在分类器学习阶段对骨干网络进行直接微调会损害泛化能力。相反,论文提出在原有的长尾数据分布下,结合原始标签和平衡提取标签的混合监督,联合训练另一个骨干网络和分类器。在这个阶段对网络进行初始化,因为以前的表示仍然是相对有偏差的,很难通过微调来摆脱局部极小值。此外,其他自训练论文也发现类似的结论,即从头开始训练学生比由老师初始化学生要好。在学习混合监督后,最终的模型可以获得比教师模型更高的性能,此外,额外的分类器微调步骤是可选的,但建议进一步提高性能(IV-分类器微调)。 通过自我监督增强特征学习在特征学习的第一阶段,论文选择使用标准监督任务和自监督任务以多任务学习的方式训练骨干网络。由于标签的高度偏见,监督任务可能会忽略数据稀缺类的图像,而自监督任务会平等对待每个样本,而不受长尾标签的影响。形式上,设θ为共享骨干网的参数,ω为监督任务的参数,ω为自监督任务的选择参数。然后,有标签的输入图像的自监督任务损失函数可表示为L_self(x;θ,ω_Self),L_sup(x,y;θ,ω_sup)表示监督交叉熵损失。这一阶段的总损失如下所示:选择旋转预测和实例判别作为自监督代理任务。网络可以通过解决这些代理任务来学习正确地表示图像。 旋转预测预测图像旋转是一种简单而有效的经典的自监督任务。给定一个图像,将其在{0◦,90◦,180◦,270◦}之间随机旋转一个角度,以获得旋转的x‘。这两个图像同时发送到网络。原始图像用于原始的交叉熵损失。选择旋转的x‘来预测旋转度,该旋转度可以表示为一个4向平衡分类问题。在这种情况下,特定参数ω自身被实现为传统的4向线性分类器。 实例判别在实例判别任务中,将每幅图像看作一个不同的类别,并学习一个非参数分类器对每幅图像进行分类。形式化地称图像的ℓ2-归一化嵌入,v‘i是从具有不同变换的图像副本中提取的ℓ2归一化嵌入。实例判别的损失可能是:其中τ是温度,K是作为底片样本的其他图像的数量,可以从内存库和当前小批次中检索。论文建立了一个带有特征队列的动量网络,以产生大量的负样本,并利用MLP投影头ω自身将主干输出转换到低维特征空间。 基于自蒸馏的长尾识别论文用硬标签和软标签来表示训练图像。目标是学习一个嵌入函数F,它编码成特征向量f=F(x;θ),以及两个分类器G_hard和G_soft,特征向量f将被发送到两个线性分类器G_hard和G_soft,以得到输出逻辑z_hard=G_hard(F)和z_sof t=G_soft(F)。z~表示教师模型的输出,则软标签为:T是默认设置为2的温度。然后,知识蒸馏损失写成:对于硬标签监督,使用标准交叉熵损失Lce。因此,最终的损失是这两个损失的组合:Conclusion在三个长尾识别基准:ImageNet-LT、CIF AR100-LT和iNaturist 2018上取得了SOTA结果。在不同的数据集上,SSD比强大的LWS基准性能高出2.7%到4.5%。1. ImageNet-LT数据集的TOP-1精度。与以ResNeXt-50为主干的SOTA方法进行比较。2. 在不平衡因子为100、50和10的CIFAR100-LT数据集上的TOP-1精度。与以ResNet-32为骨干网络的最新方法进行了比较。
Backgound卷积核不擅长对图像内容和特征的长期相关性进行建模,因为它们只处理局部邻域,无论是在空间上还是在时间上。目前流行的追踪器,包括离线siamese追踪器和在线学习模型,几乎都是建立在卷积运算的基础上。因此,这些方法只能很好地对图像内容的局部关系进行建模,但仅限于捕获远程的全局交互。这样的缺陷可能会降低模型处理全局上下文信息对于定位目标对象很重要的场景的能力,例如经历大规模变化或频繁进出视图的对象。空间信息和时间信息对于目标跟踪都是重要的。前者包含用于目标定位的对象外观信息,而后者包含对象跨帧的状态变化。以前的siamese跟踪器只利用空间信息进行跟踪,而在线方法使用历史预测进行模型更新。虽然这些方法很成功,但它们并没有明确地对空间和时间之间的关系进行建模。 Contribution 受最近的检测transformer(DETR)的启发,论文提出了一种新的端到端跟踪结构,采用编码器-解码器transformer来提高传统卷积模型的性能。新架构包含三个关键组件:编码器、解码器和预测头。1. 编码器接受初始目标对象、当前图像和动态更新模板的输入。编码器中的self-attention模块通过输入的特征依赖关系来学习输入之间的关系。由于模板图像在整个视频序列中被更新,因此编码器可以捕获目标的空间和时间信息。2. 解码器学习嵌入的查询以预测目标对象的空间位置。3. 使用基于角点的预测头来估计当前帧中目标对象的边界框。同时,学习记分头来控制动态模板图像的更新。总而言之,这项工作有三个贡献。1. 提出了一种新的致力于视觉跟踪的transformer架构。它能够捕获视频序列中空间和时间信息的全局特征依赖关系。提出使用动态更新模板。2. 整个方法是端到端的,不需要余弦窗口、bounding box平滑等后处理步骤,大大简化了现有的跟踪流水线。3. 提出的跟踪器在五个具有挑战性的短期和长期基准上实现SOTA性能,同时以实时速度运行。 Methods论文提出了一种用于视觉跟踪的时空transformer网络,称为STARK。论文基于一种简单的基线方法,该方法直接应用原始编解码器变压器进行跟踪,且只考虑了空间信息。论文扩展基线以学习用于目标定位的空间和时间表示,引入了一个动态模板和一个更新控制器来捕捉目标对象的外观变化。Baseline方法图2为baseline方法 baseline主要由三个部分组成:卷积主干、编解码器转换器和bounding box预测头。原图像先通过CNN backbone进行降维和降采样,完了再进行Flatten 和Concatenate得到向量,向量再加入正弦位置嵌入,作为transformer的Encoder输入。随机初始化一个查询向量,Decoder将目标查询和来自编码器的增强特征序列作为输入。与DETR采用100个对象查询不同,论文只向解码器输入一个查询来预测目标对象的一个bounding。此外,由于只有一个预测,论文去掉了DETR中用于预测关联的匈牙利算法。目标查询可以关注模板上的所有位置和搜索区域特征,从而学习最终边界框预测的鲁棒表示。DETR采用三层感知器预测目标坐标。然而,正如GFLoss所指出的那样,直接回归坐标等同于拟合狄拉克增量分布,它没有考虑数据集中的模糊性和不确定性。这种表示方式不灵活,对目标跟踪中的遮挡和杂乱背景等挑战也不够稳健。为了提高box估计的质量,通过估计box角点的概率分布,设计了一种新的预测头。如图3所示,首先从编码器的输出序列中提取搜索区域特征,然后计算搜索区域特征与解码器输出嵌入的相似度。最后特征序列会reshape成3维,通过L层Conv-BN-ReLU的全卷积网络输出两个概率图,一个概率图为bounding box左上角的坐标,一个概率图为bounding box右下角的坐标,跟DETR一样,这里不多细讲。 本文方法论文提出的时空跟踪框架。粉色突出显示了与纯空间架构的区别。与仅使用第一帧和当前帧的基线方法不同,时空方法引入了从中间帧采样的动态更新模板作为附加输入(论文的唯一贡献),如图所示。除了初始模板的空间信息外,动态模板还可以捕捉目标外观随时间的变化,提供额外的时间信息。三元组的特征图被扁平化和拼接,然后发送到编码器。该编码器通过在空间和时间维度上对所有元素之间的全局关系建模来提取可区分的时空特征。在跟踪过程中,有些情况下不应更新动态模板。例如,当目标被完全遮挡或移出视线时,或者当跟踪器漂移时,裁剪的模板是不可靠的。为简单起见,论文认为只要搜索区域包含目标,就可以更新动态模板。为了自动确定当前状态是否可靠,论文添加了一个简单的分数预测头,它是一个三层感知器,然后是Sigmoid激活。如果得分高于阈值τ,则认为当前状态可靠。训练和推理正如最近的工作所指出的那样,定位和分类的联合学习可能会导致这两个任务的次优解,这有助于将定位和分类解耦。因此,论文将训练过程分为两个阶段,将定位作为首要任务,将分类作为次要任务。具体地说,在第一阶段,除了分数头外,整个网络都进行了端到端的训练,只使用与定位相关的损失。在这个阶段,确保所有的搜索图像都包含目标对象,并让模型学习定位能力。在第二阶段,仅利用定义为如下的二进制交叉熵损失来优化分数头并且冻结所有其他参数以避免影响定位能力。这样,最终的模型在经过两个阶段的训练后,既学习了定位能力,又学习了分类能力。在推理过程中,在第一帧中初始化两个模板和对应的特征。然后,裁剪搜索区域并将其送入网络,生成一个边界框和置信度分数。仅当达到更新间隔并且置信度分数高于阈值τ时,才更新动态模板。为了提高效率,论文将更新间隔设置为Tu 帧。新的模板被从原始图像中裁剪出来,然后馈送到主干中进行特征提取。 Conclusion与以前的长期跟踪器相比,提出的方法的框架要简单得多。具体地说,以前的方法通常由多个组件组成,例如基本跟踪器、目标验证模块和全局检测器。相比之下,提出的方法只有一个以端到端方式学习的网络。大量的实验表明,提出的方法在短期和长期跟踪基准上都建立了新的SOTA性能。例如,论文的时空transformer跟踪器在GOT-10K和LaSOT上分别比Siam R-CNN高3.9%(AO score)和2.3%(Success)。此外,论文的跟踪器可以实时运行,在Tesla V100图形处理器上比Siam R-CNN(30V.S.5fps)快6倍,如图所示与LaSOT上SOTA的比较。将Success性能与Frame-PerSecond(Fps)跟踪速度进行了可视化比较。 在多个数据集上与其它SOTA方法的比较速度、计算量和参数
Background目标检测通常被表示为通过联合优化目标分类和定位的多任务学习问题。由于分类和定位的学习机制不同,两个任务学习到的特征的空间分布可能不同,当使用两个单独的分支进行预测时,会导致一定程度的错位。最近的一级目标检测器试图通过聚焦于目标的中心来预测两个独立任务的一致输出。他们假设位于对象中心的锚(即,无锚检测器的锚点,或基于锚的检测器的锚盒)可能给出分类和定位两者的更准确的预测。例如,最近的FCOS和ATSS都使用中心度分支来增强从对象中心附近的锚点预测的分类分数,并为相应锚点的定位损失分配更大的权重。此外,FoveaBox将对象的预定义中心区域内的锚视为正样本。这样的启发式设计已经取得了很好的效果,但这些方法可能会受到两个限制:(1)分类和定位独立。目前的单级检测器通过两个独立的分支(即头部)并行独立地进行目标分类和定位。这种由两个分支组成的设计可能会导致两个任务之间缺乏交互,从而导致在执行它们时预测不一致。如图1的“result”栏所示,TSS检测器(左上角)识别“餐桌”的对象(用红色块显示的锚点表示),但更准确地定位“披萨”的另一个对象(红色边界框)。图1:由ATSS(顶行)和TOOD(底行)预测的检测结果(‘Result’)以及分类得分(‘Score’)和定位得分(‘IoU’)的空间分布图示。(2)与任务无关的样本分配。大多数无锚点检测器使用基于几何的分配方案来选择对象中心附近的锚点进行分类和定位,而基于锚点的检测器通常通过计算锚框和ground truth之间的IoUs来分配锚盒。然而,用于分类和定位的最佳锚点通常是不一致的,并且可能根据对象的形状和特征而变化很大。广泛使用的样本分配方案是与任务无关的,因此可能很难对这两个任务做出准确而一致的预测,如图1中ATSS的 ‘Score’ 和 ‘IOU’ 分布所示。‘Result’列还说明最佳定位锚(绿色块)的空间位置可能不在对象的中心,并且它与最佳分类锚(红色块)不能很好地对齐。因此,在非最大值抑制(NMS)过程中,精确的bounding box可能会被精度较低的bounding box所抑制。 创新思路为了解决这些局限性,论文提出了一种任务对齐的一阶段目标检测(Task-aligned One-stage Object Detection, TOOD),旨在通过设计一种新的头部结构和面向对齐的学习方法来更精确地对齐这两个任务:针对传统的一步法目标检测中分类和定位分别采用两个分支并行实现的特点,设计了一种任务对齐头(T-Head),以增强两个任务之间的交互性。这使得这两项任务能够更协作地工作,进而更准确地调整它们的预测。T-Head在概念上很简单:它计算任务交互特征,并通过一种新颖的任务对齐预测器(TAP)进行预测。然后,它根据任务对齐学习提供的学习信号对两个预测的空间分布进行对齐,如下所述。为了进一步克服未对齐问题,论文提出了一种任务对齐学习(TAL)来明确两个任务的最优锚点之间的距离。它是通过设计一个样本分配方案和一个与任务相关的损失来执行的。样本分配通过计算每个锚点的任务对齐度来收集训练样本(正样本或负样本),而任务对齐损失逐渐统一最佳锚点,以便在训练期间预测分类和定位。因此,在推断时,可以保留分类得分最高并且共同具有最精确定位的边界框。提出的T-Head和学习策略可以协同工作,在分类和定位两个方面做出高质量的预测。论文的主要贡献可以概括为:(1)设计了一种新的T-Head,在保持分类和定位特征的同时,增强了分类和定位之间的交互,并进一步将两个任务在预测上对齐;(2)论文提出了TAL,在识别出的任务对齐锚点上显式地对齐两个任务,并为所提出的预测器提供学习信号;(3)论文在MSCOCO上进行了广泛的实验,TOOD达到了51.1AP,超过了现有的单级检测器,如ATSS。定性结果进一步验证了任务对齐方法的有效性。 Methods与最近的单级探测器类似,TOOD具有一个“主干-FPN-头”的整体流水线。此外,考虑到效率和简单性,TOOD在每个位置使用一个锚点(与ATSS相同),其中“锚”是指anchor-free检测器的锚点,或者是anchor-based检测器的锚盒。图2. TOOD的整体学习机制。首先,T-Head对FPN特征进行预测。其次,使用预测来计算每个锚点处的任务对齐度量,基于该度量,TAL为T-Head产生学习信号。最后,T-Head对分类和定位的分布进行了相应的调整。具体地说,对齐程度最高的锚点通过“Prob”(概率图)获得更高的分类分数,并通过学习的“偏移量”获得更准确的边界框预测。如图2所示,T-Head和TAL可以协作改进两项任务的一致性。具体地说,T-Head首先对FPN特征进行分类和定位预测。然后,TAL基于一种新的任务对齐度量来计算任务对齐信号,该度量度量测量两个预测之间的对齐程度。最后,T-Head在反向传播过程中使用TAL计算的学习信号自动调整其分类概率和定位预测。 Task-aligned Head为了设计一种高效的head结构,以改进单级探测器中head的传统设计(如图3(A)所示)。论文通过考虑两个方面来实现这一点:(1)增加两个任务之间的交互,(2)增强检测器学习比对的能力。T-Head如图3(B)所示,它有一个简单的特征提取器和两个任务对齐的预测器(TAP)。图3.传统的并行头和提出的T-Head之间的比较。为了增强分类和定位之间的交互,论文使用特征提取器来学习来自多个卷积层的任务交互特征堆栈,如图3(B)中的蓝色部分所示。这种设计不仅方便了任务的交互,而且为这两个任务提供了多层次的特征和多尺度的有效感受野。形式上,Xfpn表示FPN特征。特征提取器使用具有激活函数的N个连续卷积层来计算任务交互特征:其中,conv k和δ分别指第k个卷积层和ReLU函数。因此,论文利用head的单个分支从FPN特征中提取丰富的多尺度特征。然后,将计算出的任务交互特征送入两个TAP进行分类定位。 Task-aligned Predictor(TAP)论文对计算出的任务交互特征进行目标分类和定位,这两个任务可以很好地感知彼此的状态。然而,由于单一分支的设计,任务交互功能不可避免地在两个不同的任务之间引入了一定程度的功能冲突,这在其它论文中也有讨论。图4 Task-aligned Predictor(TAP)直观地说,对象分类和定位的任务具有不同的目标,因此聚焦于不同类型的特征(例如,不同的层次或感受野)。因此,论文提出了一种层注意机制(layer attention mechanism),通过在层级动态计算任务特征来鼓励任务分解。如图4所示,针对每个分类或定位任务分别计算特定于任务的功能:其中wk是可学习层注意w的第k个元素,它是根据跨层任务交互特征计算出来的,能够捕获层之间的依赖关系:其中X inter是通过平均池化获得。最后,从每个Xtask中预测分类或定位的结果:其中X task是X_k task的拼接特征,conv1是用于降维的1×1卷积层。然后,使用Sigmoid函数将Z task转换为密集分类分数P(H×W×80),或对象边界框B(H×W×4),其具有distance-to-bbox的转换。 Prediction Alignment在预测阶段,通过调整两个预测的空间分布来进一步明确地对齐这两个任务:P和B。与以往只能基于分类特征或局部特征调整分类预测的中心度分支或IOU分支不同,论文通过使用计算的任务交互特征联合考虑两个任务来对这两个预测进行对齐。值得注意的是,分别对这两个任务执行对齐方法。如图4所示,论文使用空间概率图M∈(H×W×1)来调整分类预测:其中M是从交互特征计算的,从而允许它学习在每个空间位置的两个任务之间的一致性程度。同时,通过交互特征进一步学习空间偏移量映射O∈(H×W×8),用于调整每个位置的预测bounding box,从而对定位预测进行对齐。具体地说,学习的空间偏移使对齐程度最高的锚点能够识别其周围的最佳边界预测:值得注意的是,每个通道的偏移量都是独立学习的,这意味着对象的每个边界都有自己的学习偏移量。这允许对四个边界进行更准确的预测,因为它们中的每一个都可以单独从其附近最精确的锚点学习。因此,不仅协调了这两个任务,而且通过为每边确定一个精确的锚点来提高定位精度。对齐图M和O是从交互特征堆栈中自动学习的:T-Head是一个独立的模块,可以在没有TAL的情况下正常工作。它可以方便地以即插即用的方式应用于各种一级物体检测器,以提高检测性能。 Task Alignment LearningTAL与以前的方法有两个不同之处。首先,从任务对齐的角度,它基于设计的度量动态地选择高质量的锚。其次,它同时考虑锚点分配和权重。它包括一个样本分配策略和专门为协调这两项任务而设计的新损失。为了应对NMS,训练实例的锚点分配应该满足以下规则:(1)对齐的锚点应该能够预测高分类分数,并进行精确的联合定位;(2)对齐错误的锚点应该具有低分类分数并随后被抑制。基于这两个目标,论文设计了一个新的锚点对齐度量来明确地度量锚点级别的任务对齐程度。对准度量被集成到样本分配和损失函数中,以动态地改进每个锚点处的预测。其中,s和u分别表示分类分数和IOU值。α和β用于控制锚点对齐度量中这两个任务的影响。对于每个实例,选择取值最大的锚点作为正样本,而将剩余的锚点作为负样本。同样,训练是通过计算专门为调整分类和定位任务而设计的新损失函数来执行的。 Task-aligned Loss为了显式提高对齐锚点的分类得分,同时降低未对齐锚点(即t值较小)的分类得分,我们在训练过程中使用了替换正锚点的二进制标签。使用归一化t,即ˆt来代替正锚点的二进制标签,其中ˆt由以下两个性质来归一化:(1)确保硬实例的有效学习(对于所有对应的正锚点,这些硬实例通常具有较小的值);(2)基于预测bounding box的精度来保持实例之间的排序。因此,论文采用简单的实例级归一化来调整ˆt的规模:ˆt的最大值等于每个实例中的最大IOU值(u)。分类任务的损失函数定义如下:采用焦点损失进行分类,以缓解训练期间负样本和正样本之间的不平衡。其中i表示对应于一个实例的第i个锚点与负值锚点,j表示来自负负锚点的第j个锚点,γ是聚焦参数。与分类目标类似,根据ˆt重新加权为每个锚点计算的bounding box回归损失,并且GIoU loss(L_GIoU)可以重新表示如下:其中b和~b表示预测的边界框和相应的ground truth。TAL的总训练损失是L_cli和L_reg的总和。 Conclusion1. 论文在MS-Coco上进行了广泛的实验,TOOD在MS-CoCO上实现了51.1AP的单模型单尺度测试。这大大超过了最近的单阶段检测器,如ATSS(47.7AP)、GFL(48.2AP)和PAA(49.0AP),它们的参数和FLOPs更少。2.不同训练样本分配方案之间的比较。‘Pos/neg’:正/负锚定分配。‘Weight’:锚定权重分配。‘fixed’:固定分配。‘ada’:自适应赋值。在这里,TAP根据最后head tower的分类和定位特征对齐预测。3.不同检测器中不同head结构的比较。4.可视化
ViT回顾在讲计算机视觉中transformer的模型创新总结之前,先有必要对它整体模型进行回顾。在本文选取了最常用的ViT。如图所示,对于一张图像,先将其分割成NxN个patches,把patches进行Flatten,再通过一个全连接层映射成tokens,对每一个tokens加入位置编码(position embedding),会随机初始化一个tokens,concate到通过图像生成的tokens后,再经过transformer的Encoder模块,经过多层Encoder后,取出最后的tokens(即随机初始化的tokens),再通过全连接层作为分类网络进行分类。这个过程中存在很多值得改进的空间,下面我们看看其它论文是如何思考并改进的。以下内容按照上面这个实现过程对每个步骤的改进来排序。改进思路1. 分块的改进渐进采样式vision transformer提出问题ViT采用了一种朴素的标记化(tokenization)方案,该方案将一幅图像分割成一系列规则间隔的patches,这些patches被线性投影到tokens中。通过这种方式,图像被转换成数百个视觉tokens。然而,这种tokens化方案的局限性是显而易见的。首先,硬分割可能会分离出一些高度相关的区域,这些区域应该用同一组参数建模,这破坏了固有的对象结构,并使输入patches的信息量变得较少。图显示猫头被分成几个部分,导致仅基于一个部分的识别挑战。其次,tokens被放置在规则网格上,而与底层图像内容无关。图显示,大多数网格聚焦在不感兴趣的背景上,这可能导致感兴趣的前景对象淹没在干扰信号中。 改进思路人类视觉系统以一种完全不同的方式组织视觉信息,而不是一次不加区别地处理整个场景。取而代之的是,它循序渐进地、选择性地将注意力集中在视觉空间的有趣部分,无论何时何地需要它,而忽略不感兴趣的部分,随着时间的推移,结合来自不同注视的信息来理解场景。受上述过程的启发,论文提出了一种新的基于transformer的渐进采样(Progressive Sampling)模块,该模块能够学习从哪里看图像,以缓解ViT中简单的tokens化方案带来的问题。论文提出的模块不是从固定位置采样,而是以迭代的方式更新采样位置。如图所示,在每次迭代中,当前采样步骤的tokens被馈送到transformer编码层,并预测一组采样偏移量以更新下一步的采样位置。该机制利用transformer的能力来捕获全局信息,通过结合本地上下文和当前tokens的位置来估计对感兴趣区域的偏移量。这样,注意力就会像人类视觉一样,一步一步地集中到图像的可辨别区域。 2.相对位置编码的反思与改进提出问题transformer位置表示的编码方法主要有两类。一个是绝对的,另一个是相对的。绝对方法将输入tokens的绝对位置从1编码到最大序列长度。也就是说,每个位置都有单独的编码向量。然后将编码向量与输入Tokens组合,以将位置信息输入给模型。相对位置方法对输入tokens之间的相对距离进行编码,并学习tokens之间的成对关系。相对位置编码(relative position encoding, RPE)通常通过具有与self-attention模块中的 query 和 key 交互的可学习参数的查询表来计算。这样的方案允许模块捕获Tokens之间非常长的依赖关系。相对位置编码在自然语言处理中被证明是有效的。然而,在计算机视觉中,这种效果仍然不清楚。最近很少有文献对其进行阐述,但在Vision Transformer方面却得出了有争议的结论。例如,Dosovitski等人观察到相对位置编码与绝对位置编码相比没有带来任何增益。相反,Srinivaset等人发现相对位置编码可以诱导明显的增益,优于绝对位置编码。此外,最近的工作声称相对位置编码不能和绝对位置编码一样好用。这些工作对相对位置编码在模型中的有效性得出了不同的结论,这促使我们重新审视和反思相对位置编码在Vision Transformer中的应用。另一方面,语言建模采用原始相对位置编码,输入数据为一维单词序列。但对于视觉任务,输入通常是2D图像或视频序列,其中像素具有高度空间结构。目前尚不清楚:从一维到二维的扩展是否适用于视觉模型;方向信息在视觉任务中是否重要? 改进思路1.论文分析了相对位置编码中的几个关键因素,包括相对方向、上下文的重要性、query、key、value和相对位置嵌入之间的交互以及计算代价。该分析对相对位置编码有了全面的理解,并为新方法的设计提供了经验指导。2.提出了一种高效的相对编码实现方法,计算成本从原始O()降低到O(nkd)(其中k<<n),适用于高分辨率输入图像,如目标检测、语义分割等Tokens数可能非常大的场合。3.综合考虑效率和通用性,提出了四种新的vision transformer的相对位置编码方法,称为image RPE(IRPE)。这些方法很简单,可以很容易地插入self-attention层。实验表明,在不调整任何超参数和设置的情况下,该方法在ImageNet和COCO上分别比其原始模型DeiTS和DETR-ResNet50提高了1.5%(top-1ACC)和1.3%(MAP)。4.实验证明,在图像分类任务中,相对位置编码可以代替绝对编码。同时,绝对编码对于目标检测是必要的,其中像素位置对于目标定位是重要的。 3.Encoder的改进关于Encoder的改进,大部分都是在将transformer用于具体任务时,针对各个任务的特点或出现的问题进行改进的。虽然不一定是一个通用的模型,但其在改进过程中体现的改进思路仍值得学习和借鉴。TransFER提出问题表情识别具有类内相似性小、类间相似性大的特点。同时,需要提取不同的局部表征来对不同的表情进行分类。即使某些局部块(patches)不可见,更多样化的局部块也可以发挥作用。同时,不同的局部块可以相互补充。例如,如图所示,仅根据嘴巴区域(列2)很难区分惊讶(第1行)和愤怒(第2行)。我们提出的TransFER模型探索了不同的关系感知面部部位,如眼睛(第3列,第1行)和眉毛之间的区域(第3列,第2行),这有助于区分这些不同的表情。因此,应该在全局范围内探索不同局部块(patches)之间的关系,突出重要的块(patches),抑制无用的块(patches)。 改进思路论文提出了TransFER模型来学习不同关系感知的FER局部表示。首先,提出了随机丢弃注意力图的多注意丢弃算法(Multi-Attention Dropping, MAD)。通过这种方式,推动模型去探索除最具区分性的局部斑块之外的综合局部斑块,自适应地聚焦于不同的局部斑块。当某些部位因姿势变化或遮挡而不可见时,此方式特别有用。其次,Vision Transformer(VIT)适用于FER,称为VIT-FER,用于对多个局部块之间的连接进行建模。由于采用全局范围对每个局部块进行增强,充分挖掘了多个局部块之间的互补性,提高了识别性能。第三,多头自我注意(multi-head self-attention)使VIT能够在不同位置共同关注来自不同信息子空间的特征。然而,由于没有明确的指导,可能会建立冗余关系。为解决这一问题,提出了随机丢弃一个自我注意的多头自我注意丢弃(Multi-head Self-Attention Dropping, MSAD)方法。在这种情况下,如果放弃了self-attention,模型就被迫从其他地方学习有用的关系。因此,不同局部块之间的丰富关系被挖掘出来,从而使FER受益。结合新的MAD和MSAD模块,提出了最终的体系结构,称为TransFER。如图所示,与VIT-FER基线(列2)相比,TransFER定位更多样化的关系局部表示(列3),从而区分这些不同的表达式。它在几个FER基准上达到了SOTA性能,显示了它的有效性。 SOTR提出问题transformer用于语义分割方面还在一些不足。一方面,transformer在提取低层特征时表现不佳,导致对小目标的错误预测。另一方面,由于特征映射的广泛性,需要大量的内存和时间,特别是在训练阶段。改进思路为了克服这些缺点,论文提出了一种创新的自下而上模型SOTR,该模型巧妙地结合了CNN和transformer的优点。SOTR的重点是研究如何更好地利用transformer提取的语义信息。为了降低传统self-attention机制的存储和计算复杂度,论文提出了双注意力,它采用了传统注意力矩阵的稀疏表示。1.论文提出了一种创新的CNN-Transformer-hybrid实例分割框架,称为SOTR。它可以有效地对局部连接和远程依赖进行建模,利用输入域中的CNN主干和transformer编码器,使它们具有高度的表现力。更重要的是,SOTR通过直接分割对象实例而不依赖于box检测,大大简化了整个流水线。2.设计了双注意力,这是一种新的position-sensitive self-attention机制,是为transformer量身定做的。与原来的transformer相比,SOTR这种设计良好的结构在计算量和内存上都有很大的节省,特别是对于像实例分割这样的密集预测的大输入。3.除了纯粹基于transformer的模型外,提出的SOTR不需要在大数据集上进行预训练,就可以很好地推广归纳偏差。因此,SOTR更容易应用于数据量不足的情况。4.在MS Coco基准上,SOTR的性能达到了使用ResNet-101-FPN主干的AP的40.2%,在精确度上超过了大多数最SOTA方法。此外,由于twin transformer对全局信息的提取,SOTR在中型物体(59.0%)和大型物体(73.0%)上表现出明显更好的性能。PnP-DETR提出问题将transformer网络应用于图像特征映射可能在计算上代价高昂,这主要是由于对长展平的特征向量的注意操作。这些特征可能是冗余的:除了感兴趣的对象之外,自然图像通常包含巨大的背景区域,这些背景区域可能在相应的特征表示中占据很大一部分;而且,一些区分特征向量可能已经足以检测对象。现有的提高transformer效率的工作主要集中在加速注意操作上,很少考虑上面讨论的空间冗余。改进思路为了解决上述局限性,论文开发了一个可学习的轮询和池化(Poll and Pool, PnP)采样模块。它的目的是将图像特征图压缩成由精细特征向量和少量粗略特征向量组成的抽象特征集。从输入特征图中确定性地采样精细特征向量,以捕捉精细前景信息,这对于检测目标是至关重要的。粗略特征向量聚合来自背景位置的信息,所产生的上下文信息有助于更好地识别和定位对象。然后,transformer对细粗特征空间内的信息交互进行建模,并获得最终结果。由于抽象集比直接扁平化的图像特征图短得多,因此transformer的计算量大大减少,并且主要分布在前景位置。这种方法与提高transformer效率的方法是正交的,可以进一步与它们结合得到更有效的模型。提出问题CNN 以大空间尺寸和小通道尺寸的特征开始,并逐渐增加通道尺寸,同时减小空间尺寸。由于称为空间池化的层,这种维度转换是必不可少的。现代 CNN 架构,包括 AlexNet、ResNet和 EfficientNet,都遵循这一设计原则。池化层与每一层的感受野大小密切相关。 一些研究表明,池化层有助于网络的表现力和泛化性能。 然而,与 CNN 不同的是,ViT 不使用池化层,而是在所有层中使用相同大小的空间。 改进思路首先,论文验证了 CNN 上池化层的优势。实验表明,池化层证明了 ResNet 的模型能力和泛化性能。为了将池化层的优势扩展到 ViT,论文提出了一种基于池化的视觉transformers (PiT)。PiT 是一种与池化层相结合的transformer架构。它可以像在 ResNet 中一样减少 ViT 结构中的空间大小。最后,为了分析 ViT 中池化层的效果,论文测量了 ViT 的空间交互比,类似于卷积架构的感受野大小。论文展示了池化层具有控制自注意力层中发生的空间交互大小的作用,这类似于卷积架构的感受野控制。Swin Transformer 提出问题论文试图扩展Transformer的适用性,使其可以作为计算机视觉的通用主干,就像它在NLP中所做的那样,也可以像CNNs在视觉中所做的那样。论文提到,将transformer在语言领域的高性能转换到视觉领域的重大挑战可以用这两种模式之间的差异来解释。这些不同之处之一涉及到规模。与作为语言transformer中处理的基本元素的单词tokens不同,视觉元素在尺度上可以有很大的变化,这是一个在诸如目标检测之类的任务中受到关注的问题。在现有的基于transformer的模型中,tokens都是固定比例的,这一特性不适合这些视觉应用。另一个不同之处在于,与文本段落中的文字相比,图像中像素的分辨率要高得多。存在许多视觉任务,如语义分割,需要在像素级别进行密集预测,这对于高分辨率图像上的Transformer来说是很困难的,因为它的self-attention的计算复杂度是图像大小的二次方。 改进思路为了克服这些问题,论文提出了一种通用的Transformer骨干网,称为Swin Transformer,它构造了分层的特征映射,并且计算复杂度与图像大小成线性关系。如图1(A)所示,Swin Transformer通过从小块(灰色轮廓)开始,逐渐合并更深的Transformer层中的相邻块来构建分层表示。有了这些分层的特征图,Swin Transformer模型可以方便地利用先进的技术进行密集预测,如特征金字塔网络(FPN)或U-Net。线性计算复杂度是通过在分割图像(红色轮廓)的非重叠窗口内局部计算self-attention来实现的。每个窗口中的patches数量是固定的,因此复杂度与图像大小成线性关系。这些优点使得Swin Transformer适合作为各种视觉任务的通用主干,而不是以前基于Transformer的架构,后者生成单一分辨率的特征地图,并且具有二次方复杂性。 Swin Transformer的一个关键设计元素是窗口分区在连续的self-attention层之间的移动,如图2所示。移动的窗口桥接了前一层的窗口,提供了它们之间的连接,显著增强了建模能力。这种策略在实际延迟方面也是有效的:一个窗口内的所有query patch都共享相同的key集,这便于硬件中的内存访问。相反,较早的基于滑动窗口的self-attention方法由于不同query像素的不同key集而在一般硬件上受到低延迟的影响。实验表明,所提出的移位窗口方法比滑动窗口方法具有更低的延迟,但在建模能力上是相似的。事实证明,移位窗口方法对于全MLP体系结构也是有益的。对于视频中的transformer,主要改进思路是将patches在空间上和时间上分开进行attention。这里列举一篇。TimeSformer提出问题视频理解与NLP有很多的相似的地方。首先,视频和语句都具有序列性;而且,一个字只能与语句中其它字联系才能理解,在视频行为中一个片段也需要与视频的上下文相关联。于是,论文期望NLP中这种long-range self-attention模型可以在视频模型中也有很高的表现。在视频领域,2D或3D卷积是用来提取时空特征的主流操作,但卷积操作比较明显的一个问题是感受野是有限的,若要获得全局感受野,需要堆叠很多层卷积层,它们的信息传播路径比较长。而self-attention这种操作可以很轻松地获得全局感受野,捕获局部和长范围的依赖关系。卷积操作的另一个问题是受到内存的限制,特别是视频领域,往往需要在高分辨率和长范围帧之间权衡。而最近几年一些研究者的工作指出Transformer可以比CNN获得更快的训练和推理,因此在同样计算量预算下,transformer可以使用更大学习容量。标准的self-attention需要计算所有tokens相互之间的相似性,这样的做法就是计算量比较大,因此需要考虑如何利用self-attention来处理图像块。论文比较了这方面的几种处理方式:Joint Space-Time Attention、Sparse Local Global Attention 和Axial Attention。这几种方式的共同点是采用ViT中的方式将图像进行分块,而它们之间的区别在于如何用self attention来处理这些块。论文提出Divided attention的方式具有最好的表现。 改进思路4.增加DecoderDETR中似乎没有介绍为何这么设计结构,而只是在说要做一个End-to-End的transformer模型。因此这里只介绍一下它的结构。
ViT通过简单地将图像分割成固定长度的tokens,并使用transformer来学习这些tokens之间的关系。tokens化可能会破坏对象结构,将网格分配给背景等不感兴趣的区域,并引入干扰信号。为了缓解上述问题,本文提出了一种迭代渐进采样策略来定位区分区域。在每次迭代中,当前采样步骤的嵌入被馈送到transformer编码层,并预测一组采样偏移量以更新下一步的采样位置。渐进抽样是可微的。当与视觉transformer相结合时,获得的PS-ViT网络可以自适应地学习到哪里去看。PS-ViT既有效又高效。在ImageNet上从头开始训练时,PS-VIT的TOP-1准确率比普通VIT高3.8%,参数减少了大约4倍,FLOP减少了10倍。Backgroundtransformer最初是为处理中等大小的序列而量身定做的,并且具有二次计算复杂度。序列长度。它们不能直接用于处理具有大量像素的图像。为了克服计算复杂性问题,ViT采用了一种朴素的标记化(tokenization)方案,该方案将一幅图像分割成一系列规则间隔的patches,这些patches被线性投影到tokens中。通过这种方式,图像被转换成数百个视觉tokens,这些视觉tokens被馈送到transformer编码层的堆栈中进行分类。ViT取得了很好的效果,特别是在大规模数据集上进行了预训练,这证明了全transformer结构是一种很有前途的视觉任务替代方案。然而,这种tokens化方案的局限性是显而易见的。首先,硬分割可能会分离出一些高度相关的区域,这些区域应该用同一组参数建模,这破坏了固有的对象结构,并使输入patches的信息量变得较少。图显示猫头被分成几个部分,导致仅基于一个部分的识别挑战。其次,tokens被放置在规则网格上,而与底层图像内容无关。图显示,大多数网格聚焦在不感兴趣的背景上,这可能导致感兴趣的前景对象淹没在干扰信号中。 创新思路人类视觉系统以一种完全不同的方式组织视觉信息,而不是一次不加区别地处理整个场景。取而代之的是,它循序渐进地、选择性地将注意力集中在视觉空间的有趣部分,无论何时何地需要它,而忽略不感兴趣的部分,随着时间的推移,结合来自不同注视的信息来理解场景。受上述过程的启发,论文提出了一种新的基于transformer的渐进采样(Progressive Sampling)模块,该模块能够学习从哪里看图像,以缓解ViT中简单的tokens化方案带来的问题。论文提出的模块不是从固定位置采样,而是以迭代的方式更新采样位置。如图所示,在每次迭代中,当前采样步骤的tokens被馈送到transformer编码层,并预测一组采样偏移量以更新下一步的采样位置。该机制利用transformer的能力来捕获全局信息,通过结合本地上下文和当前tokens的位置来估计对感兴趣区域的偏移量。这样,注意力就会像人类视觉一样,一步一步地集中到图像的可辨别区域。MethodsProgressive SamplingViT规则地将一幅图像分成16×16块,这些块被线性投影到一组标记中,而不考虑图像区域的内容重要性和对象的整体结构。为了更好地关注图像的感兴趣区域,减轻图像结构破坏的问题,提出了一种新的渐进式采样模型。由于它的可微性,它是通过后续基于vision transformer的图像分类任务自适应驱动的。渐进式采样模块的体系结构 在每个迭代中,给定采样位置Pt和特征映射F,对初始Tokens T't和特征映射F进行采样,并将其与基于pt生成的位置编码Pt和上一次迭代的输出Tokens Tt−1进行元素级相加,然后送入一个编码层来预测当前迭代的Tokens Tt.。通过一个基于Tt的全连接层预测偏移量矩阵,将Tt与Pt相加,得到下一次迭代的采样位置Pt+1。上面的过程迭代了N次。在每次迭代中,通过将采样位置与上次迭代的偏移向量相加来更新采样位置。Pt+1 = Pt + Ot, 其中Ot表示在迭代t处预测的采样位置矩阵和偏移矩阵。对于第一次迭代,我们将p1初始i化为规则间隔的位置,就像在ViT中所做的那样。具体地说,第i个位置由其中π和π将位置索引分别映射到行索引和列索引。Sh和Sw分别为其轴向和轴向的步长。然后在输入特征图的采样位置对初始tokens进行采样,如下所示由于Pt的元素是小数,所以采样是通过双线性插值运算来实现的,该运算是可微的。输入特征图F和采样位置Pt。初始采样tokens、上次迭代的输出tokens和当前采样位置的位置编码在被馈送到一个编码层以获得当前迭代的输出tokens之前,被进一步以元素方式相加。将采样位置的归一化绝对坐标投影到一个嵌入空间作为位置嵌入。最后,预测除上一次迭代之外的下一次迭代的采样位置偏移量,如下所示其中Mt是用于预测采样偏移矩阵的可学习线性变换。Overall Architecture渐进式采样Vision Transformer(PS-VIT)的总体架构 在给定输入图像的情况下,首先提取其特征图F。然后,在渐进式采样模块中的自适应位置pi处,对tokens Ti进行渐进式和迭代式采样。渐进采样模块的最终输出tokens TN被填充分类tokens Tcls,并进一步馈送到vision tranformer模块以细化Tcls,最终在分类模块中进行分类。Conclusion论文提出的渐进式采样是可区分的,并且可以很容易地插入ViT而不是硬分裂,以构建端到端的vision transformer,并使用称为PSViT的渐进式采样网络来构建端到端的vision transformer。由于任务驱动的训练,PS-ViT倾向于对与语义结构相关的对象区域进行采样。此外,与简单的tokens化相比,它更关注前景对象,而对模糊背景的关注较少。1. 当在ImageNet上从头开始训练时,提出的PS-VIT优于当前基于transformer的SOTA方法。具体地说,它在ImageNet上达到了82.3%的TOP1准确率,在只有Deit约1/4参数和1/2 FLOP的情况下,准确率比Deit更高。如图所示,论文观察到,与基于transformer的SOTA网络ViT和Deit相比,PS-ViT明显更好、更快、参数效率更高。2. 与其他SOTA 的对比3.比较PS-VIT和SOTA网络在FLOP和速度方面的效率4. 渐进式采样模块中抽样位置的可视化。箭头的起点是初始采样位置(P1),而箭头的终点是最终采样位置(P4)。
Motivation论文试图扩展Transformer的适用性,使其可以作为计算机视觉的通用主干,就像它在NLP中所做的那样,也可以像CNNs在视觉中所做的那样。论文提到,将其在语言领域的高性能转换到视觉领域的重大挑战可以用这两种模式之间的差异来解释。这些不同之处之一涉及到规模。与作为语言transformer中处理的基本元素的单词tokens不同,视觉元素在尺度上可以有很大的变化,这是一个在诸如目标检测之类的任务中受到关注的问题。在现有的基于transformer的模型中,tokens都是固定比例的,这一特性不适合这些视觉应用。另一个不同之处在于,与文本段落中的文字相比,图像中像素的分辨率要高得多。存在许多视觉任务,如语义分割,需要在像素级别进行密集预测,这对于高分辨率图像上的Transformer来说是很困难的,因为它的self-attention的计算复杂度是图像大小的二次方。 创新思路为了克服这些问题,论文提出了一种通用的Transformer骨干网,称为Swin Transformer,它构造了分层的特征映射,并且计算复杂度与图像大小成线性关系。如图1(A)所示,Swin Transformer通过从小块(灰色轮廓)开始,逐渐合并更深的Transformer层中的相邻块来构建分层表示。有了这些分层的特征图,Swin Transformer模型可以方便地利用先进的技术进行密集预测,如特征金字塔网络(FPN)或U-Net。线性计算复杂度是通过在分割图像(红色轮廓)的非重叠窗口内局部计算self-attention来实现的。每个窗口中的patches数量是固定的,因此复杂度与图像大小成线性关系。这些优点使得Swin Transformer适合作为各种视觉任务的通用主干,而不是以前基于Transformer的架构,后者生成单一分辨率的特征地图,并且具有二次方复杂性。Swin Transformer的一个关键设计元素是窗口分区在连续的self-attention层之间的移动,如图2所示。移动的窗口桥接了前一层的窗口,提供了它们之间的连接,显著增强了建模能力。这种策略在实际延迟方面也是有效的:一个窗口内的所有query patch都共享相同的key集,这便于硬件中的内存访问。相反,较早的基于滑动窗口的self-attention方法由于不同query像素的不同key集而在一般硬件上受到低延迟的影响。实验表明,所提出的移位窗口方法比滑动窗口方法具有更低的延迟,但在建模能力上是相似的。事实证明,移位窗口方法对于全MLP体系结构也是有益的。MethodsOverall ArchitectureSwin Transformer架构的概述如图3所示,它展示了tiny版本(Swin-T)。图3.(a)Swin Transformer(Swin-T)的架构;(b)两个连续的Swin Transformer块(用公式表示(3))。W-MSA和SW-MSA分别是具有规则和移位窗口配置的多头自注意模块。它首先通过patch分割模块(如ViT)将输入的RGB图像分割成不重叠的patch。每个patch都被视为一个“token”,其特征被设置为原始像素RGB值的串联。在实现中,论文使用了4×4的块大小,因此每个块的特征维度是4×4×3=48。将线性嵌入层应用于该原始值特征以将其投影到任意维度(表示为C)。在这些patch tokens上应用了几个带有修改的self-attention计算的transformer block (Swin Transformer block)。transformer块保持tokens数(H/4×W/4),与线性嵌入一起称为“Stage1”。为了产生分层表示,随着网络的深入,通过patch合并层来减少tokens的数量。第一个patch合并层将每组2×2相邻patch的特征进行拼接,并在4C维拼接的特征上应用线性层。这将tokens的数量减少了2×2=4的倍数(2倍下采样),并且输出维度被设置为2C。然后应用Swin Transformer块进行特征变换,分辨率保持为H/8×W/8。这第一个块的拼接和特征变换称为“Stage2”。重复“Stage3”和“Stage4”两次,输出分辨率分别为H/16×W/16和H/32×W/32。这些Stage共同产生具有与典型卷积网络(如VGG和ResNet)相同的特征映射分辨率的分层表示。因此,该体系结构可以方便地取代现有方法中的骨干网络,用于各种视觉任务。 欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。Swin Transformer Block: Swin Transformer通过将transformer块中的标准多头self-attention(MSA)模块替换为基于移位窗口的模块,在保持其他层不变的情况下构建Swin Transformer。 如图3(b)所示,Swin Transformer模块由一个基于移位窗口的MSA模块和一个中间带有GELU非线性的两层MLP组成。在每个MSA模块和每个MLP之前应用LayerNorm(LN)层,并且在每个模块之后应用残差连接。 基于移位窗口的self-attention非重叠窗口中的self-attention: 为有效建模,论文提出在局部窗口中计算self-attention。窗口被布置成以不重叠的方式均匀地分割图像。假设每个窗口包含M×M个patch,全局MSA模块和基于h×w patch图像的窗口的计算复杂度分别为其中,前者与patch数HW为平方关系,后者在M固定时是线性的(缺省情况下设置为7)。全局self-attention计算对于大型硬件来说通常是负担不起的,而基于窗口的self-attention是可伸缩的。 在连续块中移动窗口分区: 基于窗口的self-attention模块缺少跨窗口的连接,这限制了其建模能力。为了在保持非重叠窗口计算效率的同时引入跨窗口连接,论文提出了一种移位窗口划分方法,该方法在连续Swin Transformer块中的两种划分配置之间交替。在Swin Transformer架构中计算self-attentioin的移位窗口方法的图示。在Layer1(左)中,采用规则的窗口划分方案,并在每个窗口内计算自我关注。在下一层l+1(右)中,窗口分区被移位,从而产生新窗口。新窗口中的self-attention计算跨越了层l中先前窗口的边界,提供了它们之间的连接。如图所示,第一个模块使用从左上角像素开始的规则窗口划分策略,将8×8特征图均匀划分为大小为4×4(M=4)的2×2个窗口。然后,下一模块通过将窗口从规则划分的窗口移位(M/2,M/2)(向下取整)像素来采用从前一层的窗口移位的窗口配置。使用移位窗口分区方法,连续的Swin Transformer块计算为其中,ˆzl和zl分别表示块1的(S)WMSA模块和MLP模块的输出特征;W-MSA和SW-MSA分别表示使用规则和移位窗口分区配置的基于窗口的多头self-attention。移位窗口划分方法引入了前一层相邻非重叠窗口之间的连接,在图像分类、目标检测和语义分割中被发现是有效的。 移位的高效批处理计算:移位窗口分区的一个问题是,它将在移位中产生更多窗口,从h/M x w/M(向上取整)到(h/M + 1) x (w/M+1)(向上取整),并且一些窗口将比MxM更小。一个原始的解决方案是将较小的窗口填充到M×M的大小,并在计算注意力时屏蔽填充的值。当规则分区中的窗口数量较小时,例如2×2,使用这种朴素的解决方案增加的计算量是相当可观的(2×2→3×3,是2.25倍)。在这里,论文提出了更有效的批处理计算方法,即向左上角方向循环移动,如图所示。在这种转移之后,批处理窗口可能由特征图中不相邻的几个子窗口组成,因此采用mask机制将self-attention计算限制在每个子窗口内。在循环移位的情况下,批处理窗口的数量与常规窗口划分的数量相同,因此也是有效的。 相对位置偏差:在计算self-attention时,在计算相似度时将每个头部的相对位置偏差B(大小为M^2×M^2)包括在内:其中Q,K,V大小为M^2 x d;d的大小为query/key,M^2是一个窗口中的patches数量。由于沿每个轴的相对位置在[−M+1,M−1]范围内,将较小尺寸的偏置矩阵ˆB(大小为(2M−1)×(2M−1))参数化,并且B中的值取自ˆB。如表所示,论文提到,与没有这种bias项或使用绝对位置嵌入的同行相比,有显著的改进。进一步向输入添加绝对位置嵌入会略微降低性能,因此在论文的实现中不采用它。在预训练中学习到的相对位置偏差还可以用于通过双三次插值来初始化具有不同窗口大小的微调模型。 Architecture Variants论文构建了名为Swin-B的基本模型,其模型大小和计算复杂度与ViTB/Deit-B相似。还提出了Swin-T、Swin-S和Swin-L,它们的模型规模和计算复杂度分别约为0.25×、0.5×和2倍。请注意,Swin-T和Swin-S的复杂度分别与ResNet-50(Deit-S)和ResNet-101相似。默认情况下,窗口大小设置为M=7。对于所有实验,每个头的query维度为D=32,每个MLP的扩展层为α=4。这些模型变体的体系结构超参数包括:Conclusion论文提出的Swin Transformer在图像分类、目标检测和语义分割等识别任务中取得了较好的性能。它在三个任务上的延迟与Vit/Deit和ResNe(X)t模型相比要高得多。1. 不同骨干网在ImageNet-1K分类上的比较。2. 其在COCO测试开发集上的58.7box AP和51.1mask AP超过了之前SOTA结果+2.7box AP(无外部数据的复制-粘贴)和+2.6mask AP(DetectoRS)。3.在ADE20K语义分割上,它在Val集合上获得了53.5mIoU,比之前的SOTA(SETR])提高了+3.2mIoU。在ImageNet-1K图像分类上达到了87.3%的TOP-1正确率。4. 不同的self-attention计算方法和实现在V100 GPU上的真实速度。
Background现代实例分割方法通常建立在CNN上,并遵循先检测后分割的范式,该范式由用于识别和定位所有对象的检测器和用于生成分割掩码的掩码分支组成。这种分割思想的成功归功于以下优点,即translation equivariance和location,但面临以下障碍:1)由于感受野有限,CNN在高层视觉语义信息中相对缺乏特征的连贯性来关联实例,导致在大对象上的分割结果次优;2)分割质量和推理速度都严重依赖目标检测器,在复杂场景下性能较差。为了克服这些缺点,最近的许多研究倾向于摆脱先检测后分割的方式,转向自下而上的策略,该策略学习每个像素的嵌入和实例感知特征,然后使用后处理技术根据嵌入特性将它们连续分组为实例。因此,这些方法可以很好地保留位置和局部相干信息。然而,自下而上模型的主要缺点是聚类不稳定(如fragmented和joint masks),对不同场景的数据集的泛化能力较差。此外,transformer能够容易地捕获全局特征,并自然地建模远程语义依赖。特别是,self-attention是transformer的关键机制,它广泛地聚合了来自整个输入域的特征和位置信息。因此,基于transformer的模型可以更好地区分具有相同语义类别的重叠实例,这使得它们比CNN更适合于高层视觉任务。然而,这些基于transformer的方法仍然存在不足。一方面,transformer在提取低层特征时表现不佳,导致对小目标的错误预测。另一方面,由于特征映射的广泛性,需要大量的内存和时间,特别是在训练阶段。 Contributions为了克服这些缺点,论文提出了一种创新的自下而上模型SOTR,该模型巧妙地结合了CNN和transformer的优点。SOTR的重点是研究如何更好地利用transformer提取的语义信息。为了降低传统self-attention机制的存储和计算复杂度,论文提出了双注意力,它采用了传统注意力矩阵的稀疏表示。1.论文提出了一种创新的CNN-Transformer-hybrid实例分割框架,称为SOTR。它可以有效地对局部连接和远程依赖进行建模,利用输入域中的CNN主干和transformer编码器,使它们具有高度的表现力。更重要的是,SOTR通过直接分割对象实例而不依赖于box检测,大大简化了整个流水线。2.设计了双注意力,这是一种新的position-sensitive self-attention机制,是为transformer量身定做的。与原来的transformer相比,SOTR这种设计良好的结构在计算量和内存上都有很大的节省,特别是对于像实例分割这样的密集预测的大输入。3.除了纯粹基于transformer的模型外,提出的SOTR不需要在大数据集上进行预训练,就可以很好地推广归纳偏差。因此,SOTR更容易应用于数据量不足的情况。4.在MS Coco基准上,SOTR的性能达到了使用ResNet-101-FPN主干的AP的40.2%,在精确度上超过了大多数最SOTA方法。此外,由于twin transformer对全局信息的提取,SOTR在中型物体(59.0%)和大型物体(73.0%)上表现出明显更好的性能。 MethodsSOTR是一种CNN-Transformer混合实例分割模型,它可以同时学习2D表示并轻松捕获远程信息。它遵循直接分割的范例,首先将输入特征映射划分为patches,然后在动态分割每个实例的同时预测每个patch的类别。具体地说,该模型主要由三部分组成:1)骨干模块,用于从输入图像中提取图像特征,特别是低层特征和局部特征;2)transformer,用于建模全局和语义依赖关系;3)多级上采样模块,用于将生成的特征图与相应的卷积核进行动态卷积运算,生成最终的分割掩模。SOTR构建在简单的FPN主干上,只需最少的修改。该模型将FPN特征P2-P6展平,并在将它们送入transformer之前用位置嵌入来补充它们。在transformer之后增加了两个头,用于预测实例类并产生动态卷积核。多级上采样模块将FPN中的P2-P4特征和变压器中的P5特征作为输入,使用图中红框内所示的动态卷积操作生成最终的预测。 Twin attentionself-attention在时间和内存上的计算量都是二次方,在图像等高维序列上会产生更高的计算代价,并且阻碍了模型在不同环境下的可伸缩性。为了缓解这个问题,论文提出了twin attention(孪生注意力)机制,将注意矩阵简化为稀疏表示。策略主要是将感受野限制在设计好的固定步幅的区块模式上。它首先计算每列中的attention,同时保持不同列中的元素独立。该策略可以在水平尺度上聚合元素之间的上下文信息(如图(1))。然后,在每一行中执行类似的注意,以充分利用垂直范围内的特性交互(如图(2))。这两个尺度中的注意力依次相连,成为最后一个,它有一个全局接受场,覆盖了两个维度上的信息。给定FPN的特征图Fi为H×W×C(FPN的第i层),SOTR首先将特征图Fi切分成N∗N个patches ,其中Pi为N×N×C,然后沿垂直和水平方向将它们堆叠成固定的块。将位置嵌入添加到块中以保留位置信息,即列和行的位置嵌入空间为1∗N∗C和N∗1∗C。两个attention层都采用多头注意机制。为了便于多层连接和后处理,twin attention中的所有子层都产生N×N×C输出。孪生注意机制能有效地把内存和计算复杂度从O((H×W)^2)降低到(H×W^2+W×H^2)。 Transformer layer基于编码器的三个不同的transformer 层作为基本构建块(如下图所示)。原始transformer层类似于NLP(图(a))中使用的编码器,它由两部分组成:1)层归一化后的多头自我注意机制;2)层归一化后的多层感知。此外,使用残差连接来连接这两个部分。最后,可以得到多维序列特征,作为这些transformer层的K串联连接的输出,以便在不同的功能头中进行后续预测。为了在计算成本和特征提取效果之间取得最佳折中,作者遵循了最初的transformer层设计,在pure twin transformer层中只用双注意力代替多头注意力(图(b))。为了进一步提高twin transformer的性能,还设计了hybrid twin transformer 层,如图3(C)所示。它在每个孪生注意模块上增加了两个3×3的卷积层,由一个Leaky RELU层连接。通过引入卷积运算,可以对注意机制进行有益的补充,更好地捕捉局部信息,增强特征表示能力。 Functional head来自transformer模块的特征图被输入到不同的functional head以进行后续预测。所述class head包括单层线性层,用于输出N×N×M的分类结果,其中M为类的个数。由于每个patches只为中心落在patch中的单个对象分配一个类别,如YOLO,论文使用多级预测,并在不同的特征级别上共享头部,以进一步提高不同尺度对象的模型性能和效率。kernel head也由线性层组成,与class head平行,以输出N×N×D张量用于后续掩码生成,其中张量表示具有D个参数的N×N卷积核。在训练期间,Focal loss被应用于分类,而对这些卷积核的所有监督来自最终的掩模损失。 Mask要构造掩码特征表示用于实例感知和位置敏感的分割,一种简单的方法是对不同尺度的每个特征图进行预测。然而,这会增加时间和资源。受Panoptic FPN的启发,论文设计了多级上采样模块,将FPN的每层和transformer的特征合并成一个统一的掩模特征。首先,从transformer中获取具有位置信息的相对低分辨率特征图P5,并与FPN中的P2-P4相结合进行融合。对于每个尺度的特征图,分3×3Conv、Group Norm和ReLU几个阶段进行操作。然后,P3-P5对(H/4,W/4)分辨率分别进行2×、4×、8×的双线性上采样。最后,在处理后的P2-P5相加后,执行逐点卷积和上采样,以产生最终的统一的H×W特征图。例如掩码预测,SOTR通过对上述统一特征映射执行动态卷积运算来为每个patch生成掩码。从kernel head给定预测的卷积内核K(N×N×d),每个kernel负责相应patch中实例的掩码生成。具体操作如下:其中,∗表示卷积运算,Z是最终生成的掩码。应该注意的是,D的值取决于卷积核的形状,也就是说,D=λ^2C,其中λ是kernel大小。最终实例分割掩码可以由Matrix NMS产生,并且每个掩码由Dice Loss独立地监督。 Conclusion1.原始transformer、pure twin transformer和hybrid twin transformer之间的对比如上表所示,提出的pure hybrid twin transformers在所有指标上都大大超过了原始transformer,这意味着twin transformer架构不仅成功地捕获了垂直和水平维度上的远程依赖关系,而且更适合与CNN主干相结合来学习图像的特征和表示。对于pure transformer和twin transformer,后者的效果要好得多。论文认为这是因为3∗3Conv能够提取局部信息并改进特征表达,从而提高了twin transformer的合理性。 2.mask的可视化3.与其他方法的详细比较在两种情况下,SOTR比Mask R-CNN和BlendMask的性能更好:1)形状复杂、容易被其他模型遗漏的物体(例如火车前的胡萝卜、卧象、小车厢里的司机),Mask R-CNN和BlendMask无法将其检测为阳性实例。2)相互重叠的物体(例如列车前面的人),两者不能用准确的边界将它们分开。SOTR能够用更清晰的边界预测掩模,而SOLOv2倾向于将目标分割成单独的部分(例如,将列车分为头部和身体),有时无法从图像中排除背景。由于transformer的引入,SOTR可以更好地获得全面的全局信息,避免了物体上的这种分裂。此外,与SOTR相比,SOLOv2通过将不存在的对象指定为实例,具有较高的误报率。4.实时性比较
使用示例先介绍一个大家比较关心的问题--复不复杂,好不好实现。PatrickStar与模型定义无关,在PyTorch脚本上添加几行代码可以带来端到端的加速。Background现在人工智能的共识是采用PTMs(Pre-Trained Models)作为任务的骨干,而不是在与任务相关的数据集上从头开始训练模型。PTMs的高性能伴随着众多的参数,这对计算和存储资源提出了巨大的要求。由于大型的模型数据在单个GPU的内存无法存储,所以最常用的数据并行技术不适用于PTM(比较常见的例子是2012年的AlexNet)。通过利用并行训练在多个GPU内存之间分配模型数据,例如ZeRO、模型并行和流水线并行,最新进展使得PTM规模的增长成为可能。SOTA解决方案是将它们组合成3D并行,它可以在数千个GPU上将PTM扩展到数万亿个参数。 创新思路作者观察到在PTM训练期间必须管理的两类训练数据:模型数据由参数、梯度和优化器状态组成,它们的足迹与模型结构定义相关;非模型数据由operator生成的中间张量组成。非模型数据根据训练任务的配置(如批次大小)动态变化。模型数据和非模型数据相互竞争GPU内存。现有的解决方案在不考虑非模型数据的情况下,在CPU和GPU内存之间静态划分模型数据,并且它们的内存布局对于不同的训练配置是恒定的。这种静态的分区策略导致了几个问题。首先,当GPU内存或CPU内存不足以满足其相应的模型数据要求时,即使当时其他设备上仍有内存可用,系统也会崩溃。其次,当数据在张量粒度的不同内存空间之间传输时,通信效率低下,当可以将模型数据提前放置到目标计算设备上时,CPU-GPU通信量是不必要的。针对上述问题,本文提出了一种名为PatrickStar的异构训练系统。PatrickStar通过以细粒度方式管理模型数据来更有效地使用异构内存,从而克服了这些缺点。作者将模型数据张量组织成块,即大小相同的连续内存块。在训练期间,根据组块的张量状态来动态编排组块在异构存储空间中的分布。通过重用不共存的块,PatrickStar还比SOTA解决方案进一步降低了模型数据的内存占用。作者使用预热迭代(warm-up iteration)在运行时收集模型数据的可用GPU内存的统计信息。设计了一种高效的块回收策略和基于统计数据的设备感知操作符放置策略,以减少CPU-GPU的数据移动量。使用零冗余优化器,基于块的内存管理可以通过块的GPU内通信(intra-GPU communication)有效地与数据并行共生。 Contributions1.从头开始构建了一个基于块的内存管理DNN训练系统,名为PatrickStar。与现有的异构训练方法相比,该方法通过缩小非模型内存空间、提高内存效率和降低CPU-GPU通信量,支持更大的模型规模和更高的计算效率。2.基于块的管理与零冗余优化器数据并行自然是共生的。基于块的集体通信模式降低了GPU内带宽需求和提高了带宽利用率。3.作者在拥有8x V100 GPU、240 GB和120 GB DRAM内存CPU的云计算节点上对系统进行了评估。在240 GB内存的情况下,PatrickStar训练了一个120亿参数的类似GPT的模型,这是DeepSpeed最大模型规模的1.5倍。在120 GB内存的情况下,PatrickStar的模型比例是DeepSpeed的4倍。4.PatrickStar的计算效率比DeepSpeed更高,在8x GPU上实现了超线性伸缩。 Design Overview论文设计了一个并行PTM训练系统PatrickStar,它的工作原理如下图所示。PatrickStar通过将模型数据分块管理并存储在异构空间中,提高了现有异构训练的模型规模和效率。图中的圆圈表示参数的元素,它们在内存中以块的形式排列。当操作符的计算被触发时(右图的彩色部分),PatrickStar将参数所在的块安排到所需的计算设备上。论文提出了在异构内存空间中高效编排块的优化方案。此外,它还可以与零冗余优化器相结合,扩展到多个GPU。必要时,在训练过程中将组块移动到所需设备。 System design on a single GPUPatrickStar作为PyTorch和异构内存之间的中间件,如上图所示。系统由在预处理阶段工作的静态模块和在训练阶段工作的运行时模块组成。PatrickStar的静态模块在训练前进行处理。基于神经网络结构,构造了张量与组块之间的映射模式。在训练过程中,运行时模块通过将张量重定向到所管理的基于块的存储空间来接管PyTorch的内存访问,并使用块管理器来智能地管理异构存储空间中的块。 Preprocessing Stage在训练开始之前,在预处理阶段为模型数据的每个张量分配一块块空间,并生成块-张量映射模式。一个有效的映射模式需要具备以下三个特征:1.增加张量访问的局部性。2.降低峰值内存消耗。3.对并行友好。论文提出了一个高效的映射模式。简单来说,根据模型数据中张量的类型,将组块分为四种类型,即参数FP16列表、参数FP32列表、动量列表和方差列表,共14M字节(M为参数数)。组块具有相同的大小,因此不同的组块可以重用相同的内存,并且有利于并行训练中的集体通信。特别的是,PatrickStar没有分配分级FP16列表。梯度FP16张量可以重用参数FP16列表的块空间,消除了梯度FP16张量对参数FP16张量的依赖。与最小模型数据内存占用为16M字节的ZeRO-Offload相比,PatrickStar减少了内存占用。此外,它还分配了额外的GPU内存,用于保存要移动到CPU的渐变,而PatrickStar则消除了这一开销。因此,与其他PTM训练方案相比,PatrickStar的模型数据存储空间最小。 Training Stage在训练过程中,PatrickStar需要正确高效地编排异构存储空间中的数据块。论文介绍了导块正确移动的机制,并介绍了一种优化策略以提高其效率。此机制比较复杂,论文有非常具体的介绍,这里不予解读。 Scaling to Multiple GPUSPatrickStar使用多进程在多个GPU之间执行数据并行化。假设进程数为nproc。每个进程负责单个GPU,而所有进程共享CPU。进程的异构内存空间由GPU的全部内存和1/nproc的CPU内存空间组成。该进程以与零冗余优化器相同的方式管理位于其本地异构存储空间中的总块的1/nproc。本地空间中的块称为本地块,而不在本地空间中的块称为远程块。如图所示,通信组由块列表的nproc连续块组成,其中每个块属于不同的进程。根据前面的块张量映射模式,我们可以设计一种以最小的进程间通信量实现数据并行的通信方案。进程只需要在FWD和BWD阶段传送参数FP16和梯度FP16。需要最大量数据(包括动量、方差、参数FP32和梯度FP16)的ADAM阶段在本地执行。论文中有非常详细的过程介绍与算法,这里不予解读。与相关工作相比,PatrickStar获得了更低的GPU内带宽需求和更高的带宽利用率。根据成本模型,PatrickStar的带宽要求为2(p−1)/p×2m(all-gathers)+(p−1)/p×2m(reduce-scatter)=6(p−1)/p×M,其中p为并行度,M为参数个数。在ZeRO-Offload和ZeRO-DP中,每层的参数由单个进程拥有并广播给其余进程。与all-gathers相比,广播将数据传输集中在单个GPU上,并未充分利用聚合带宽。基于广播的带宽要求为4(p−1)/p×2M(广播)+(p−1)/p×2M=10(p−1)/p×M,比PatrickStar提高了2/3。虽然Zero-Infinity也采用了all-gathers的方式,但PatrickStar的带宽利用率仍然较高。已经证明,将序列张量布置为大块(bucket)来传输的分组化策略可以导致更高的带宽利用率,因为它将在每次通信中传输更多的数据。PatrickStar中基于块的方法自然是分块的,而ZeRO-Infinity的传输单元是张量。PatrickStar进一步避免了数据复制开销,以提高性能。 Optimization将模型数据托管在GPU内存中相比,异构训练方法引入了额外的CPU-GPU数据移动开销。对基于块的内存管理进行了优化,使其更加高效和强大。首先,它可以以细粒度的方式布局运算符,使内存密集型运算符不在他们的首选设备上,但它可以减少数据移动量,提高系统的整体效率。为此,PatrickStar提出了一种Device-aware Opetator Placement优化。其次,当数据不能永久驻留在运营商的计算设备上时,如果不使用,数据块就会被逐出。除了在更大的模型规模上进行改进外,PatrickStar还通过块逐出策略将块逐出量降至最低。为了实现上述优化,我们必须在训练过程中很好地了解每台设备上有多少GPU内存空间可以分配给块,这在本节中称为可分块内存(Chunkable Memory)。因此,PatrickStar提供了在运行时收集可分块内存统计信息的方法。这些方案细节均在论文有详细介绍。 ConclusionPatrickStar通过缩小非模型内存空间、提高内存效率和降低CPU-GPU通信量,支持更大的模型规模和更高的计算效率。PyTorch、DeepSpeed和PatrickStar在同一GPU上的训练吞吐量。OOM表示超内存。在多个GPU上使用DP训练PyTorch、DeepSpeed和PatrickStar的吞吐量。 论文提出了一种创新的异构训练系统,称为PatrickStar。它将模型数据组织成块,从而在异构存储空间中更灵活地编排它们。该系统与零冗余优化器数据并行是共生的。PatrickStar成功降低了PTM培训的硬件要求,并更高效地扩展到多个GPU。在云计算平台的8xGPU 240 GB CPU节点上,它在最大模型规模上比SOTA提高了2倍,速度也比SOTA快。未来的一些工作可以在PatrickStar上进行研究。首先,通过对非模型数据检查点和卸载策略的联合优化,可以得到更好的块清除方法。其次,基于组块的异构方法可以与其他并行训练方法相结合进行多节点伸缩。
Background目标检测是一项基本的计算机视觉任务,其目的是识别图像中的目标实例,并使用精确的边界框对其进行定位。现代检测器主要利用代理学习目标(proxy learning objectives)来处理该集合预测任务,即,回归距预定义锚框的偏移量或距网格位置的边界。这些启发式设计不仅使模型设计复杂化,而且还需要手工制作的后处理来消除重复。最近的一种方法DETR消除了这些手工设计,实现了端到端的目标检测。它在卷积特征图上建立了一个有效的集合预测框架,并显示出与faster R-CNN检测器相当的性能。特征图在空间维度上被展平为一维特征向量。然后,transformer利用其强大的注意机制对它们进行处理,以生成最终的检测列表。尽管简单而有效,但将transformer网络应用于图像特征映射可能在计算上代价高昂,这主要是由于对长展平的特征向量的注意操作。这些特征可能是冗余的:除了感兴趣的对象之外,自然图像通常包含巨大的背景区域,这些背景区域可能在相应的特征表示中占据很大一部分;而且,一些区分特征向量可能已经足以检测对象。现有的提高transformer效率的工作主要集中在加速注意操作上,很少考虑上面讨论的空间冗余。 创新思路为了解决上述局限性,论文开发了一个可学习的轮询和池化(Poll and Pool, PnP)采样模块。它的目的是将图像特征图压缩成由精细特征向量和少量粗略特征向量组成的抽象特征集。从输入特征图中确定性地采样精细特征向量,以捕捉精细前景信息,这对于检测目标是至关重要的。粗略特征向量聚合来自背景位置的信息,所产生的上下文信息有助于更好地识别和定位对象。然后,transformer对细粗特征空间内的信息交互进行建模,并获得最终结果。由于抽象集比直接扁平化的图像特征图短得多,因此transformer的计算量大大减少,并且主要分布在前景位置。这种方法与提高transformer效率的方法是正交的,可以进一步与它们结合得到更有效的模型。 Contributions总结起来,本文的主要贡献在于:1. 识别了DETR模型中图像特征图的空间冗余问题,该问题导致transformer网络计算量过大。因此,提出对特征映射进行抽象,以显著降低模型运算量。2. 为了实现特征提取,设计了一种新颖的两步轮询池采样模块。该算法首先利用poll采样器提取前景精细特征向量,然后利用pool采样器获取上下文粗特征向量。3. 构建了PnP-DETR,该变换在抽象的细粗特征空间上进行操作,并自适应地将计算分布在空间域。通过改变精细特征集的长度,PnP-DETR算法效率更高,在单一模型下实现了即时计算和性能折衷。4. PnP抽样模块是通用的,是端到端学习的,没有像地区提案网络那样的明确监督。论文进一步在全景分割和最近的ViT模型上对其进行了验证,并显示出一致的效率增益。这种方法为未来研究使用transformer的视觉任务的有效解决方案提供了有用的见解。 MethodsPnP-DETR结构图 feature abstration论文提出了一种特征抽象方案来解决网格结构化表征均匀地分布在空间位置上的限制。具体来说就是把CNN输出的feature maps用紧凑特征表示的两组特征向量来代替作为transformer部分的输入,细节如下图所示。精细特征集Ff是从feature maps离散采样的,包含识别和检测对象所必需的精细信息。粗略特征集Fc是通过聚集来自多个空间位置的信息并编码背景上下文信息而获得的。它们一起形成一个抽象集合F∗:F* = Ff U Fc。F∗对检测图像内的对象所需的所有高层信息进行编码,并将其传递给transformer以生成目标检测结果。Poll and Pool (PnP) Sampling上述抽象方案需要解决两个挑战:1)精集需要确定性的二进制采样,这是不可微的。手工设计的采样器可以用一些中间目标来学习,例如,区域提议网络或点提议网络,然而,这与端到端学习不兼容,并且手工采样规则可能不是最优的。2)提取仅关注背景上下文信息的紧凑、粗略的特征集是困难的。论文将抽象方案分为两个步骤,并开发了轮询采样器和池化采样器来实现。采样器是确定性的,是端到端学习的,计算量可以忽略不计。 Poll Sampler由于显式学习二进制采样器是不可行的,论文提出了一个采样排序策略。我们使用小型元评分网络来预测每个空间特征位置(i,j)的信息性分数:分数越大,f_ij向量的信息量越大。接下来对它们排序,得到一个分数向量Sl,向量的长度l为feature maps的HxW。取排序后的TopN , N = alpha * l。此alpha用来控制比例。为了能够使用反向传播学习ScoringNet,将预测的信息量得分Sl作为对采样的精细特征集的调制因子:作者发现在调制前对特征向量进行归一化可以稳定ScoringNet的学习,因此实际上先对fl做了一个LayerNorm,再与Sl相乘。 Pool Sampler上面的轮询采样器提取了精细的特征集。剩余的特征向量主要对应于背景区域。为了将它们压缩成一个总结上下文信息的小特征集,论文设计了一个池化采样器,它对剩余的特征向量进行加权汇集,以获得固定数量的背景上下文特征向量。这部分地受到双线性汇集和双重注意操作的启发,其中生成全局描述符以捕获特征图的二阶统计量。公式太多,用一句话来解释Pool采样的主要操作:使用一个可学习的加权向量W^a,与Poll采样过后剩余的向量Fr进行相乘,得到一个聚合权重向量a_r,再使用Softmax对聚合向量a_r进行归一化;与此并列的是,使用一个可学习的加权向量W^v,与Poll采样过后的剩余向量Fr相乘,得到一个映射后的向量F'r;将归一化后的a_r聚合向量和F'r相乘,即可得到Pool Sampler的输出文献[34]表明,上下文信息是识别目标的关键,不同尺度的金字塔特征能更好地聚合上下文信息。通过动态生成聚合权重,池化采样器能够自由获取不同尺度的上下文信息。也就是说,一些特征向量可以捕获局部上下文,而其他特征向量可以编码全局上下文。论文通过可视化聚集权重实证地展示了池采样器的这种能力。与轮询采样器中的精细集合Ff一起,获得所需的抽象集合F∗。请注意,PnP模块也可以在transformer层之后应用,而不仅仅是卷积特征图。 密集预测任务的反向投影PnP模块将图像特征映射从2D坐标空间缩减到抽象空间,这不能用于密集预测任务,如图像分割。为了解决这一局限性,论文提出将编码器输出的特征向量投影回2D坐标空间。具体地说,精细特征向量散布回采样位置;粗略特征向量首先过聚合权重扩散回通的原始2D空间:然后分散回Poll采样器的未采样位置。然后,将所获得的2D特征图用于密集预测。 Conclusion论文在COCO基准上进行了大量的实验,结果表明PnP-DETR有效地降低了成本,实现了动态计算和性能折中。在没有花里胡哨的情况下,单个PnP-DETR-DC5可获得42.7 AP,transformer计算量减少72%,而与43.3 AP基线和竞争性43.1 AP相比,transformer计算量减少56%。进一步用全景分割和最近的vision transformer模型(ViT)验证了效率增益。例如,PnP-ViT在精确度仅下降0.3的情况下实现了近一半的FLOP减少。使用PnP-DETR-R50可视化来自池化采样器的Poll样本位置和示例聚合权重图。第一列:输入图像;第二/第三列:轮询采样器的得分映射及其相应的样本映射;最后两列:来自池化采样器的示例聚合权重映射,其中前者聚合全局上下文,而后者聚合局部上下文。
Background目前已经提出了许多使transformers适应视觉任务的方法。在自然图像领域,transformers已被证明在标准视觉任务(如ImageNet分类、以及目标检测和语义分割)上优于CNN。与卷积相比,transformers的中心注意力机制提供了几个关键的优势:(1)它捕获远程关系,(2)它具有通过动态计算的self-attention权重(捕获Tokens之间的关系)进行自适应建模的能力,(3)它提供了一种内置的突显性,使人们能够洞察模型关注的是什么。然而,有证据表明,vision transformer需要非常大的数据集才能超越CNN中,只有当谷歌的3亿张私人图像数据集JFT-300M用于预训练时,ViT的好处才变得明显起来。他们对这种规模的数据的依赖是transformers广泛应用的障碍。这个问题在医学成像领域尤其严重,那里的数据集较小,而且往往伴随着不太可靠的标签。CNN和ViTs一样,在数据稀缺时性能较差。标准的解决方案是使用迁移学习:通常,模型在较大的数据集(如ImageNet)上预先训练,然后使用较小的、专门的数据集针对特定任务进行微调。在ImageNet上预先训练的CNN通常在最终性能和减少的训练时间方面都优于那些在医学领域从头开始训练的CNN。自监督是一种处理未标记数据的学习方法,最近得到了广泛的关注。研究表明,在微调前对目标域中的CNN进行自监督预训练可以提高性能。ImageNet的初始化有助于自监督CNN更快地收敛,通常具有更好的预测性能。这些处理医学图像领域缺乏数据的技术已被证明对CNN有效,但尚不清楚vision transformer是否也有类似的好处。一些研究表明,使用ImageNet对CNN进行医学图像分析的预训练并不依赖于特征复用(feature reuse)(遵循传统观点),而是由于更好的初始化和权重调整。这让人质疑transformers是否能从这些技术中获益。如果他们这样做了,几乎没有什么能阻止ViTs成为医学图像的主导架构。在这项工作中,论文探索ViTs是否可以很容易地替代CNN用于医学成像任务,以及这样做是否有优势。论文考虑一个典型从业者的用例,它配备了有限的计算预算和访问传统医学数据集的权限,着眼于“即插即用(plug-and-play)”的解决方案。为此,论文在三个主流的公开数据集上进行了实验。通过这些实验,得出以下结论:在ImageNet上预先训练的ViTs在数据有限的情况下表现出与CNN相当的性能。在应用标准训练方案和设置时,迁移学习有利于ViTs。当自监督的预训之后是监督的微调时,ViTs的表现要好于CNN。这些发现表明,医学图像分析可以从CNN无缝过渡到ViTs,同时获得更好的可解释性特性。 Methods论文调查的主要问题是ViTs是否可以作为CNN的即插即用替代品用于医疗诊断任务。为此,进行了一系列实验,以比较ViTs和CNN在相似条件下的差异,将超参数调整保持在最低限度。为了确保比较的公正性和可解释性,选择了具有代表性的ResNet50,以及带有16x16 Tokens的Deit-S作为ViT。之所以选择这些型号,是因为它们在参数数量、内存需求和计算方面具有可比性。如上所述,当数据不太丰富时,CNN依赖初始化策略来提高性能,医学图像就是这种情况。标准的方法是使用迁移学习-用ImageNet上预训练的权重来初始化模型,并在目标域上微调。因此,论文考虑了三种初始化策略:(1)随机初始化权重,(2)使用监督ImageNet预训练权值的迁移学习,(3)在目标数据集上的自监督预训练,在初始化之后,如(2)所示。将这些策略应用于三个标准医学成像数据集,以覆盖不同的目标域:APTOS 2019-在此数据集中,任务是将糖尿病视网膜病变图像分类为5类疾病严重程度。Aptos 2019包含3662张高分辨率视网膜图像。ISIC 2019-任务是将25,333张皮肤镜图像从九种不同的皮肤损伤诊断类别中分类。CBIS-DDSM-此数据集包含10,239张乳房X光照片,任务是检测乳房X光照片中是否存在肿块。 ExperimentsCNN与ViTs在不同初始化策略下的比较1. 随机初始化的transformer有用吗?首先将Deit-S与具有随机初始化权重的ResNet50进行比较(Kaiming初始化)。上表中的结果表明,在这种设置下,CNN在所有方面都远远超过ViTs。这些结果与之前在自然图像领域的观察结果一致,在自然图像领域,在有限数据上训练的ViTs表现优于类似大小的CNN,这一趋势被归因于ViT缺乏归纳偏差。由于大多数医学成像数据集的大小适中,随机初始化的ViTs的用处似乎是有限的。 2. ImageNet上的预训练transformer在医学图像领域工作吗?在医学图像数据集上,随机初始化在实践中很少使用。标准步骤是通过用ImageNet预先训练的权重初始化网络,然后对来自目标域的数据进行微调来训练CNN。在这里,论文调查了这种方法是否可以有效地应用于ViTs。为了测试这一点,论文使用已在ImageNet上以完全监督的方式预先训练的权重来初始化所有模型。然后,使用上述过程进行微调。上表中的结果表明,CNN和ViTs都从ImageNet初始化中获得了显著的好处。事实上,ViT似乎受益更多,因为它们的表现与CNN不相上下。这表明,当使用ImageNet进行初始化时,CNN可以用ViTs代替,而不会影响使用中等大小训练数据的医学成像任务的性能。 3.医学图像领域的transformer使用自监督是否有益?最近的自我监督学习方案,如Dino和BYOL,都采用监督学习的方法。此外,如果将它们用于预训练和有监督的微调,它们可以达到新的SOTA。虽然这一现象在较大的数据系统中已经在CNN和ViTs中得到证实,但目前还不清楚ViTs的自我监督预训练是否有助于医学成像任务,特别是在中等和低尺寸数据上。为了验证这一点,论文采用了Dino的自监督学习方案,该方案可以很容易地应用于CNN和ViTs。Dino使用自蒸馏(self-distillation)来鼓励学生和教师网络在不同的扩充输入的情况下产生相似的表示。自监督预训练从ImageNet初始化开始,然后按照原论文作者建议的默认设置对目标医疗领域数据应用自我监督学习-除了三个小的变化:(1)基本学习率设置为0.0001,(2)初始权重衰减设置为10‘5,并使用余弦进度表增加到10’4,以及(3)使用的均方根均值为0.99。CNN和ViTs使用相同的设置;两者都使用256的批次大小进行了300个周期的预训练,然后进行了微调。上表中报告的结果显示,ViTs和CNN在自监督的预训练中都表现得更好。在这种情况下,ViTs的表现似乎优于CNN,尽管差距很小。对自然图像的研究表明,VITS和CNN之间的差距将随着更多的数据而扩大。 Conclusion本文研究比较了CNN和ViTs在三种不同初始化策略下在医学图像任务中的表现。研究了自监督预训练对医学图像领域的影响。结果表明,ViTs和CNNS的改善幅度很小,但持续不变。虽然使用自监督ViTs获得了最佳的整体性能,但有趣的是,在这种低数据区域中,我们还没有看到有利于先前在具有更多数据的自然图像领域中报告的ViTs的强大优势,例如在中,由于专家标注的成本,很少有大的标记的医学图像数据集,但是可能收集大量未标记的图像。这表明,这是一个诱人的机会,可以将自监督应用于大型医学图像数据集,其中只有一小部分被标记。总结发现,对于医学图像领域:正如预期的那样,如果简单地从头开始训练,在低数据制度下,ViTs比CNN更糟糕。迁移学习弥合了CNN和ViTs之间的性能差距;性能相似。通过自监督的预训练+微调获得最佳性能,ViTs与同类CNN相比略有优势。
BackgroundTransformer的核心是self-attention,它能够按顺序对tokens之间的关系进行建模。然而,self-attention有一个固有的缺陷-它不能捕获输入tokens的顺序。因此,合并位置信息的显式表示对于Transformer特别重要,因为模型在其他方面完全不受序列排序的影响,这对于对结构化数据进行建模是不可取的。transformer位置表示的编码方法主要有两类。一个是绝对的,另一个是相对的。绝对方法将输入tokens的绝对位置从1编码到最大序列长度。也就是说,每个位置都有单独的编码向量。然后将编码向量与输入Tokens组合,以将位置信息输入给模型。相对位置方法对输入tokens之间的相对距离进行编码,并学习tokens之间的成对关系。相对位置编码(relative position encoding, RPE)通常通过具有与self-attention模块中的 query 和 key 交互的可学习参数的查询表来计算。这样的方案允许模块捕获Tokens之间非常长的依赖关系。相对位置编码在自然语言处理中被证明是有效的。然而,在计算机视觉中,这种效果仍然不清楚。最近很少有文献对其进行阐述,但在Vision Transformer方面却得出了有争议的结论。例如,Dosovitski等人观察到相对位置编码与绝对位置编码相比没有带来任何增益。相反,Srinivaset等人发现相对位置编码可以诱导明显的增益,优于绝对位置编码。此外,最近的工作声称相对位置编码不能和绝对位置编码一样好用。这些工作对相对位置编码在模型中的有效性得出了不同的结论,这促使我们重新审视和反思相对位置编码在Vision Transformer中的应用。另一方面,语言建模采用原始相对位置编码,输入数据为一维单词序列。但对于视觉任务,输入通常是2D图像或视频序列,其中像素具有高度空间结构。目前尚不清楚:从一维到二维的扩展是否适用于视觉模型;方向信息在视觉任务中是否重要? Contributions本文首先回顾了现有的相对位置编码方法,然后针对二维图像提出了新的编码方法。做了以下贡献。1.分析了相对位置编码中的几个关键因素,包括相对方向、上下文的重要性、query、key、value和相对位置嵌入之间的交互以及计算代价。该分析对相对位置编码有了全面的理解,并为新方法的设计提供了经验指导。2.提出了一种高效的相对编码实现方法,计算成本从原始O()降低到O(nkd)(其中k<<n),适用于高分辨率输入图像,如目标检测、语义分割等Tokens数可能非常大的场合。3.综合考虑效率和通用性,提出了四种新的vision transformer的相对位置编码方法,称为image RPE(IRPE)。这些方法很简单,可以很容易地插入self-attention层。实验表明,在不调整任何超参数和设置的情况下,该方法在ImageNet和COCO上分别比其原始模型DeiTS和DETR-ResNet50提高了1.5%(top-1ACC)和1.3%(MAP)。4.实验证明,在图像分类任务中,相对位置编码可以代替绝对编码。同时,绝对编码对于目标检测是必要的,其中像素位置对于目标定位是重要的。 Methods首先,为了研究编码是否可以独立于输入嵌入,论文引入了两种相对位置模式:偏置模式(Bias Mode)和上下文模式(Contextual Mode)。与传统的裁剪函数(Clip function)不同,论文提出了一种分段函数(Piecewise function)来将相对位置映射到编码。之后,为了研究方向性的重要性,论文设计了两种非定向方法和两种定向方法。 Bias Mode和Contextual Mode以前的相对位置编码方法都依赖于输入嵌入。它带来了一个问题,即编码是否可以独立于输入?论文引入了相对位置编码的偏置模式和上下文模式来研究这一问题。前者与输入嵌入无关,后者考虑与query、key或value的交互。用一个统一的公式来表示,即其中b_ij是2D相对位置编码,用来定义偏置或上下文模式。对于偏置模式,b_ij = r_ij,其中r_ij是可学习标量,并且表示位置i和j之间的相对位置权重。对于上下文模式,其中r_ij是与query嵌入交互的可训练向量。上下文模式有多个变体,这里不一一例举,有需要者请看论文。 Piece Index Function在描述二维相对位置权值之前,首先引入一个多对一函数,将一个相对距离映射为有限集合中的一个整数,然后以该整数为索引,在不同的关系位置之间共享编码。这样的索引函数可以极大地减少长序列(例如高分辨率图像)的计算成本和参数数量。尽管在[18]中使用的裁剪函数h(X)=max(−β,min(β,x))也降低了成本,但是将相对距离大于β的位置分配给相同的编码。这种方法不可避免地遗漏了远程相对位置的上下文信息。论文引入了一个分段函数g(x):R→{y∈Z|−β≤y≤β},用于索引到相应编码的相对距离。该函数基于一个假设,即较近的邻居比较远的邻居更重要,并通过相对距离来分配注意力。它表示为其中[·]是舍入运算,Sign()确定数字的符号,即正输入返回1,负输入返回-1,反之返回0。α确定分段点,β控制输出在[−β,β]范围内,γ调整对数部分的曲率。将分段函数h(X)与剪裁函数h(X)=min(−β,max(β,x))进行比较。在图2中,裁剪函数h(X)分布均匀的注意力,省略远距离的位置,但分段函数g(x)根据相对距离分布不同的注意力水平。作者认为应该保留远程位置的潜在信息,特别是对于高分辨率图像或需要远程特征依赖的任务,因此选择g(X)来构造映射方法。 2D相对位置计算1.欧氏距离方法(Euclidean Method):计算两个相对位置的欧氏距离,将距离通过一个可学习的偏置标量或上下文向量映射到相应的编码。 2.量化方法(Quantization Method ):在上述欧氏距离方法中,较近的两个相对距离不同的邻居可以映射到同一个索引中,例如二维相对位置(1,0)和(1,1)都映射到索引1中,而应该将最近的邻居分开。因此,需要将欧式距离量化,即不同的实数映射成不同的整数。quant(·)将一组实数{0,1,1.41,2,2.24,...}映射为一组整数{0,1,2,3,4,...}。此方法也是非定向的。 3.交叉法(Cross Method)。像素的位置方向对图像也很重要,因此提出了有向映射方法。这种方法被称为Cross方法,它分别在水平和垂直方向上计算编码,然后对它们进行汇总。该方法如下给出,其中p˜xi(i,j)和p˜yi(i,j)在偏置模式下都是可学习标量,或者在上下文模式下都是可学习向量。与SASA中的编码类似,相同的偏移量在x轴或y轴上共享相同的编码,但主要区别在于我们使用分段函数根据相对距离来分配注意力。 4.乘积法(Product Method)。如果一个方向上的距离相同,无论是水平距离还是垂直距离,交叉方法都会将不同的相对位置编码到同一嵌入中。此外,交叉法带来额外的计算开销。为了提高效率和包含更多的方向性信息,论文设计了乘积方法,其公式如下一个高效的实现方法在上下文模式中,以上所有的方法都有一个共同的部分:。计算这个部分需要时间复杂度O(),其中n和d分别表示输入序列的长度和特征通道的数目。由于I(i,j)的多对一特性,集合I(i,j)的大小K通常小于vision transformer。因此,论文提供如下高效实现:它花费O(nkd)的时间复杂度预计算所有的z_i,t,然后通过映射t=i(i,j)将zi_,t赋给那个共同表达式。赋值运算的时间复杂度为O(N^2),其代价比预计算过程小得多。因此,相对位置编码的计算成本也从原来的 O() 降低到 O(nkd)。 Conclusion1. 四种方法的两种模式之间的比较。在vision transformer中,有向方法(交叉和乘积)通常比无向方法(欧式距离和量化)表现得更好。这一现象说明了方向性对于vision transformer是很重要的,因为图像像素具有高度的结构化和语义相关性。无论使用哪种方法,上下文模式都实现了优于偏置模式的性能。潜在的原因可能是上下文模式改变了带有输入特征的编码,而偏置模式保持静态。 2.相对位置编码可以在不同头部之间共享或不共享的结果比较。对于偏置模式,当在头部之间共享编码时,准确度会显著下降。相比之下,在上下文模式下,两个方案之间的性能差距可以忽略不计。这两种方法的平均TOP-1准确率都达到了80.9%。论文推测,不同的头部需要不同的相对位置编码(RPE)来捕捉不同的信息。在上下文模式下,每个头部可以通过公式计算自己的RPE。当处于偏置模式时,共享RPE强制所有头部对patches给予相同的关注。 3.分段函数和裁剪函数的比较在图像分类任务中,这两个函数之间的性能差距非常小,甚至可以忽略不计。然而,在目标检测任务中,裁剪函数比分段函数差。其根本原因在于,当序列长度较短时,这两个函数非常相似。分段函数是有效的,特别是当序列大小远远大于buckets的数量时。(注:作者把P_I(i,j)作为一个bucket(桶),用于存储相对位置权重)与分类相比,目标检测使用分辨率高得多的输入,导致输入序列长得多。因此,推测当输入序列较长时,应该使用分段函数,因为它能够将不同的注意力分配到距离相对较大的位置,而当相对距离大于β时,裁剪函数分配相同的编码。 4.在ImageNet上与其它SOTA模型的比较
Background在过去的几十年里,面部表情识别(FER)在计算机视觉研究领域受到了越来越多的关注,因为它对于让计算机理解人类的情感并与人类进行交互具有重要意义。尽管FER最近取得了很好的性能,但它仍然是一个具有挑战性的任务,主要原因有两个:1)类间相似度很大。来自不同类的表达式可能只表现出一些细微的差异。如图1所示,惊讶(第1排)和愤怒(第2排)的嘴巴相似。区分它们的关键线索在于眼睛和眼睛之间的区域;2)类内小的相似性。属于同一类别的表情可能会有截然不同的外观,不同的种族、性别、年龄和文化背景会有所不同。现有的研究成果可以分为两类:基于全局的方法和基于局部的方法。针对基于全局的方法,人们提出了许多损失函数来增强特征的表示能力。然而,由于这些方法使用的是全局人脸图像作为输入,它们可能会忽略一些在区分不同表情类别中起重要作用的关键人脸区域。为了解决这一问题,人们提出了许多基于局部的方法来学习人脸不同部位的区分特征,这些方法可以分为两类:基于标志点的方法和基于注意力的方法。一些方法在landmarks周围裁剪的面部部位提取特征。然而,有几个问题:1)预先定义的面部作物可能不能灵活地描述局部细节,因为不同的图像可能会有所不同。这是因为人脸的重要部位可能会出现在不同的位置,特别是对于姿势变化或视点变化的人脸;2)对于受各种挑战性因素影响的人脸,例如强烈的光照变化、较大的姿势变化和严重的遮挡,人脸标志点检测可能不准确甚至失败。因此,有必要捕捉面部重要部位,抑制无用部位。 创新思路为了解决Background中的问题,一些方法应用了注意力机制。然而,他们可能对相似的面部部位有多余的反应,而忽略了在FER中起重要作用的其他潜在的区别性部位。这个问题对于遮挡或姿势变化较大的面部尤其严重,因为有些面部部位是看不见的。因此,需要提取不同的局部表征来对不同的表情进行分类。即使某些局部块(patches)不可见,更多样化的局部块也可以发挥作用。同时,不同的局部块可以相互补充。例如,如图所示,仅根据嘴巴区域(列2)很难区分惊讶(第1行)和愤怒(第2行)。我们提出的TransFER模型探索了不同的关系感知面部部位,如眼睛(第3列,第1行)和眉毛之间的区域(第3列,第2行),这有助于区分这些不同的表情。因此,应该在全局范围内探索不同局部块(patches)之间的关系,突出重要的块(patches),抑制无用的块(patches)。为了实现上述两个目标,论文提出了TransFER模型来学习不同关系感知的FER局部表示。首先,提出了随机丢弃注意力图的多注意丢弃算法(Multi-Attention Dropping, MAD)。通过这种方式,推动模型去探索除最具区分性的局部斑块之外的综合局部斑块,自适应地聚焦于不同的局部斑块。当某些部位因姿势变化或遮挡而不可见时,此方式特别有用。其次,Vision Transformer(VIT)适用于FER,称为VIT-FER,用于对多个局部块之间的连接进行建模。由于采用全局范围对每个局部块进行增强,充分挖掘了多个局部块之间的互补性,提高了识别性能。第三,多头自我注意(multi-head self-attention)使VIT能够在不同位置共同关注来自不同信息子空间的特征。然而,由于没有明确的指导,可能会建立冗余关系。为解决这一问题,提出了随机丢弃一个自我注意的多头自我注意丢弃(Multi-head Self-Attention Dropping, MSAD)方法。在这种情况下,如果放弃了self-attention,模型就被迫从其他地方学习有用的关系。因此,不同局部块之间的丰富关系被挖掘出来,从而使FER受益。结合新的MAD和MSAD模块,提出了最终的体系结构,称为TransFER。如图所示,与VIT-FER基线(列2)相比,TransFER定位更多样化的关系局部表示(列3),从而区分这些不同的表达式。它在几个FER基准上达到了SOTA性能,显示了它的有效性。 Contribution1.应用VIT自适应地刻画面部不同部位之间的关系,称为VIT-FER,展示了VIT-FER对FER的有效性。这是探索Transformers并研究关系感知(relation-aware)的局部块对FER的重要性的第一次努力。2.引入多头自注意丢弃算法(MSAD),随机删除自我注意模块,迫使模型学习不同局部块之间的丰富关系。3.设计了一种多注意丢弃算法(MAD)来消除注意图,推动模型从最具区分度之外的每个面部部位提取全面的局部信息。4.在几个具有挑战性的数据集上的实验结果表明了提出的TransFER模型的有效性和实用性。 TransFERTransFER的总体架构如图所示,它主要由茎(stem) CNN、局部CNN和多头自我注意丢弃(MSAD)组成。茎CNN用于提取特征图。这里采用IR-50,因为它具有很好的通用性。Local CNN如前所述,在给定面部图像的情况下,首先使用茎CNN来提取特征图。然后,利用多空间注意力自动捕获局部块。然而,如果没有适当的指导,不能保证全面的辨别性面部部位被定位。如果模型只关注少数有区别的面部部分,当这些部分难以识别或完全遮挡时,FER会受到性能下降的影响,特别是对于姿势变化较大或遮挡较强的人脸。为了解决这一问题,开发了局部CNN来提取由MAD引导的各种局部特征。框架如上图所示,主要由三个步骤组成,具体如下。首先,生成多注意力图。X∈R(h×w×c)表示输入特征图,其中h、w和c分别表示特征图的高度、宽度和数量。由于LANet允许模型自动定位重要的面部部位,因此它在多个局部分支中使用,如上图所示。LANet由两个1×1卷积层组成。第一层1x1卷积输出c/r特征图,其中缩减率用于降低特征图的维数,后继的RELU层用于增强非线性。第二层将特征图数目减少到1,并通过Sigmoid函数(记为ASMI)生成关注图。假设存在B个LANet分支,则生成注意图[M1,M2,...,MB],其中Mi∈R(h×w×1)。其次,MAD迫使多个局部分支探索不同的有用面部部位。一般来说,它接受几个数据分支作为输入,并通过将此分支中的值设置为零(不更改输入形状)随机删除一个分支。因此,MAD将注意力图作为输入,随机将一个注意力图设置为零,并输出注意力图。第三,将多个注意力图聚合在一起,生成一个注意力图。具体地说,使用基于元素的最大值操作来聚合多个关注图。最后,将Mout与原始特征图X逐元素相乘。因此,原始特征图中的不重要区域被抑制,反之亦然。 Multi-Attention DroppingDropout是为了防止神经网络过拟合而提出的。它将特征向量或特征地图调整为输入。在训练过程中,输入的一些元素被随机设置为零,概率为来自伯努利分布的样本。如果有多个通道,则每个通道将独立归零。受此启发,针对FER任务提出了一种类似丢弃的操作,称为多注意丢弃。与标准Dropout算法相比,本文提出的MAD算法采用一组特征映射(或向量)作为输入,并将每个特征映射作为一个整体来处理。如图所示,在训练过程中,从完全设置为零的均匀分布中选择一个特征图。根据概率p执行丢弃操作。删除的特征图将不会在以下图层中激活。因此,这是一种类似丢弃的停止梯度操作,该操作可以引导局部CNN探索不同的、具有区分性的面部部位。因此,可以定位均匀分布的面部部位,从而产生有利于FER的全面的局部表示。 Multi-head Self-Attention Dropping多头自注意丢弃(MSAD)主要由一个在每个多头自我注意模块后面注入MAD的transformer encoder和一个MLP分类头组成,就像vision transformer(VIT)那样。经过局部CNN后,生成包含不同局部块信息的特征图Xout ∈ R(h×w×c)。为了捕捉多个局部块之间的丰富关系,使用了包含M个编码块的Transformer。由图所示,由于此处除了在Multi-head Self Attention后添加了MAD模块以外, Transformer Encoder的结构基本没变,因此这里不多介绍。 ConclusionMAD与Dropout、Drop Block、Spatial Dropout的比较TransFER与其它SOTA模型的比较注意可视化在来自AffectNet数据集的一些示例面部图像上的不同表情。(A)-(G)分别表示愤怒、厌恶、恐惧、快乐、中性、悲伤和惊讶。(I)-(Iv)表示表1中的四种训练策略,(I)表示基准策略,(Ii)表示使用局部CNN而不使用MAD进行训练,(Iii)表示使用局部CNN和MAD进行训练,以及(Iv)表示TransFER,使用局部CNN和MSAD进行训练。在应用MAD和MSAD之后,整个框架可以专注于更具区分性的面部区域。
视频监控中的多目标跟踪(MTT)是一项重要而富有挑战性的任务,由于其在各个领域的潜在应用而引起了研究人员的广泛关注。多目标跟踪任务需要在每帧中单独定位目标,这仍然是一个巨大的挑战,因为目标的外观会立即发生变化,并且会出现极端的遮挡。除此之外,多目标跟踪框架需要执行多个任务,即目标检测、轨迹估计、帧间关联和重新识别。已经提出了各种方法,并做出了一些假设,以将问题约束在特定问题的上下文中。本文对利用深度学习表征能力的MTT模型进行了综述。多目标跟踪分为目标检测和跟踪两个主要任务。为了区分组内对象,MTT算法将唯一ID与在特定时间内保持特定于该对象的每个检测到的对象相关联。然后利用这些ID来生成被跟踪对象的运动轨迹。目标检测的精度决定了目标跟踪系统的有效性。MTT模型的精度受比例变化、频繁的id切换、旋转、光照变化等因素的影响很大。图1显示了MTT算法的输出。此外,多目标跟踪系统中存在背景杂波、后移、航迹初始化和终止等复杂任务。为了克服这些问题,研究人员利用深度神经网络,提出了多种策略。 MTT算法的分类根据对象的初始化方式,MOT实现可分为基于检测(DBT)或无检测跟踪(Detection free tracking, DFT)。然而,MTT模型是围绕基于检测的训练进行标准化的,其中检测(识别帧中的对象)是作为预跟踪步骤来检索的。由于DBT中需要一个目标检测器来识别目标,因此性能在很大程度上取决于检测器的质量,因此选择一个检测框架是至关重要的。 无检测跟踪(DFT)检测器的输出通常被用作跟踪器的输入,跟踪器的输出被提供给运动预测算法,该算法预测物体在接下来的几秒钟内将移动到哪里。然而,在无检测跟踪中,情况并非如此。基于DFT的模型要求必须在第一帧中手动初始化固定数量的对象,然后必须在随后的帧中对这些对象进行定位。DFT是一项困难的任务,因为关于要跟踪的对象的信息有限,而且这些信息不清楚。结果,初始边界框仅与背景中的感兴趣对象近似,并且对象的外观可能随着时间的推移而急剧改变。 欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。在线跟踪(Online tracking)在线跟踪算法,也称为顺序跟踪,根据过去和现在的信息生成对当前帧的预测。这种类型的算法以分步方式处理帧。在一些应用中,例如自动驾驶和机器人导航,这些信息是必不可少的。 批次跟踪(Batch tracking)为了确定给定帧中的对象身份,批次跟踪(离线跟踪)技术使用前一帧的信息。它们经常使用全局数据,从而提高了跟踪质量;但是,由于计算和内存的限制,并不总是能够一次处理所有帧。 深度学习算法大多数算法共有的主要步骤如下:目标检测(Object Detection)阶段:通过分析输入帧,使用边界框在一系列帧中定位目标。运动预测(Motion Prediction)阶段:分析检测以提取外观、运动或交互特征。亲和度(Affinity)计算阶段:将提取的特征用于检测对之间的相似度/距离计算。关联(Association)阶段:通过向对应于相同目标的检测提供相同的ID,在关联中利用相似性/距离度量。 检测阶段检测阶段主要用的是目标检测中的一些算法。YOLO单卷积神经网络在一次评价中直接从全图中预测多个bounding boxes和类概率,在全图上训练并直接优化检测性能,同时学习目标的泛化表示。然而,YOLO对边界框预测施加了严格的空间约束,限制了模型可以预测的相邻项目的数量。成群出现的小物件,如鸟类,对于此模型也同样有问题。faster R-CNN,一个由全深度CNN组成的单一统一对象识别网络,提高了检测的准确性和效率,同时减少了计算开销。该模型集成了一种在区域方案微调之间交替的训练方法,使得统一的、基于深度学习的目标识别系统能够以接近实时的帧率运行,然后在保持固定目标的同时微调目标检测。在某些监视画面中,遮挡是十分频繁,以至于不可能像在人类的情况下那样检测对象的整个形状。为了解决这个问题,Khan等人提出了经过训练仅检测头部位置的时间一致性模型(temporal consistency model)。同样,一些技术也被探索到只跟踪头部位置,而不是整个身体形状。Bewley在EL29上提出了framework SORT,以利用基于CNN的检测的力量,在MOT前景中,它在速度和准确性方面都取得了同类最好的性能,它专注于帧到帧的预测和关联。通过将从聚合信道特征(Aggregated Channel Features, ACF)获得的检测替换为Faster RCNN计算的检测,基于卡尔曼滤波器和匈牙利算法的体系结构,它变得能够被评为性能最好的。在某些情况下,CNN在检测步骤中被用于构建目标边界框之外的其他目的。对于多目标(如汽车)的跟踪,结合鲁棒检测和二分类器的新策略,对于多车辆的鲁棒和精确识别,Min提出了升级的ViBe。当ViBe算法被用来识别汽车时,CNN用它来消除假阳性。它能有效地抑制动态噪声,并能快速去除鬼影和物体的残留阴影。 运动预测(Motion Prediction)阶段深度模型用于研究诸如时间和空间注意图或时间顺序之类的MOT特征时,性能可以得到改善。一些基于端到端深度学习的模型,不仅可以提取外观描述符的特征,还可以提取运动信息的特征。Wang等人提出了最早在MOT管道中应用DL的方法之一。该系统充分利用了单目标跟踪器的优点,在不影响计算能力的前提下解决了由于遮挡造成的漂移问题;为了提高提取特征,网络采用了两层堆叠的编码器,然后利用支持向量机计算亲和度。目标的可见性图被学习,然后被用来推断空间注意图,该空间注意图随后被用来对特征进行加权。此外,可见性贴图还可用于估计遮挡状态。这就是所谓的时间注意过程。最常用的基于CNN的方法可进一步分为:用于特征提取的经典CNN和siamese CNN。 经典CNNKim等人声称多假设跟踪(Multiple Hypotheses Tracking, MHT)技术与现有的视觉跟踪视角是兼容的。现代基于检测的跟踪技术的进步和用于物体外观的高效特征表示的发展为MHT过程提供了新的可能性。他们通过整合一个正则化的最小二乘框架来改进MHT,该框架用于在线训练每个跟踪目标的外观模型。Wojke等人提出了对SORT的改进,虽然在高帧率下获得了较好的精度和精度,但产生了相对较多的单位移位。Wojke等人通过整合外观运动信息对其进行了改进,通过将关联度量替换为卷积神经网络(CNN),克服了这个问题。卷积神经网络经过训练,可以在大规模的行人重识别数据集中区分行人。与SORT相比,升级的跟踪系统有效地将身份翻转的次数从1423次减少到781次。这减少了约45%,在保持实时速度的同时实现了具有竞争力的性能。 Siamese CNNSiamese CNN已经被证明在MOT中很有用,因为跟踪阶段的特征学习的目的是确定检测和跟踪之间的相似性。Leal-taxe等人提出了一种两阶段匹配检测方法的策略,为行人跟踪中的目标关联挑战提供了新的视角。在这种情况下,他们将CNN的概念应用到多人跟踪中,并提出学习两个检测是否属于同一轨迹的判断,以避免手动设计特征进行数据关联。模型的学习框架分为两个阶段。CNN在Siamese 结构中进行预训练,以测量两个大小相等的图像区域的相似性,然后将CNN与收集到的特征进行合并以产生预测。通过将跟踪问题描述为线性规划,并将深度特征和运动信息与梯度增强方法相结合,它们很好地解决了跟踪问题。 亲和度(Affinity)计算阶段虽然一些实现使用深度学习模型来立即生成亲和度分数,而不需要特征之间的显式距离度量,但仍然有其他方法通过对CNN获得的特征应用一些距离度量来计算跟踪和检测之间的亲和度。米兰等人解决了神经网络环境中数据关联和轨迹估计的难题。在线MOT任务中跟踪目标的状态估计采用由观测预测和更新组成的递归贝叶斯滤波器,该模型扩展了RNN对该过程进行建模,将目标状态、现有观测及其对应的匹配矩阵以及存在前景作为输入输入到网络中。该模型输出目标的预测状态和更新结果,以及判断目标是否终止的存在概率,取得了较好的跟踪效果。Chen等人建议计算采样粒子和跟踪目标之间的亲和力,而不是计算目标和探测器之间的亲和力。取而代之的是,使用与被跟踪对象不一致的检测来创建新的轨迹并恢复丢失的对象。尽管它是一个在线监测算法,但在发表时,它能够在MOT15上获得最好的结果,既使用公共检测,也使用私人检测。 跟踪/关联阶段在一些MTT模型中已经使用深度学习来改进关联步骤。Ma等人在扩大Siamese跟踪器网络时,采用了双向GRU来决定在何处终止跟踪器。对于每一次检测,网络提取轨迹特征并将其发送到双向GRU网络,双向GRU网络的输出在欧几里德空间中短暂汇集以提供轨迹的整体特征。在跟踪过程中,根据双向GRU输出之间的局部距离,生成子轨,然后将其拆分成小的子轨;最后,考虑到时间池全局方面的相似性,将这些子轨重新连接到长轨迹。在MOT16数据集上,此方法获得的结果与最新SOTA水平相当。勒恩等人提出了一种使用多个深层RL(强化学习) 智能体完成关联任务的协同实现方案。预测网络和决策网络是该模型的两个关键组成部分。利用最新的跟踪轨迹,CNN被用作预测网络,并被训练以预测新帧中的目标运动。 其它方法除了基于以上四个步骤的模型,还存在一些其它的方法。Jiang等人利用Deep RL代理完成了bounding boxes回归,提高了跟踪算法的效率;采用VGG-16CNN进行外观提取,提取的特征保存并使用目标最近10次运动的历史记录,然后集成网络预测bounding boxes运动、缩放以及终止动作等多种备选结果之一。在MOT15数据集上,在几种最先进的MOT算法上使用这种bounding boxes回归方法,提高了2到7个绝对MoTA点,使其在公共检测方法中名列前茅。Xiang等人部署MetricNet进行行人跟踪,将亲和力模型与贝叶斯滤波器得到的轨迹估计相结合。利用VGG-16CNN对目标进行再识别训练,提取特征并进行bounding boxes回归,运动模型分为两部分,一部分以轨迹坐标作为输入,另一部分结合检测框进行贝叶斯滤波,并在MOT16和MOT15上输出目标的更新位置,该算法在在线方法中分别获得了最好的和次佳的得分。无模型单目标跟踪(model free single object tacking) SOT算法的最新进展极大地推动了SOT在多目标跟踪(MOT)中的应用,以提高恢复能力并减少对外部检测器的依赖。另一方面,SOT算法通常被设计成将目标与其周围环境区分开来,当目标在空间上与类似的伪像混合时,它们经常会遇到问题,就像在MOT中看到的那样。Chu等人提出了一种模型来解决鲁棒性和消除对外部检测器的依赖问题。他们在算法中使用了三种不同的CNN实现了一个模型。集成PafNet以区分背景和跟踪对象。该部分对跟踪目标进行区分,另一个集成的CNN是卷积层,它决定了跟踪模型是否需要刷新。使用支持向量机分类器和匈牙利技术,使用非关联检测来从目标遮挡中恢复。该算法在MOT15和MOT16数据集上进行了测试,第一种方法产生了最好的总体结果,第二种方法产生了在线方法中最好的结果。 评估指标最相关的是Classical metrics 和 CLEAR MOT metrics。Classical metrics指出了算法可能遇到的缺陷,如多目标跟踪(MT)轨迹、多丢失(ML)轨迹、ID切换等。CLEAR MOT metrics有MOTA(多对象跟踪精度)和MOTP(多对象跟踪精度)。MOTA将假阳性、假阴性和失配率合并为单个值,从而产生总体良好的跟踪性能。尽管有一些缺陷和抱怨,但这是迄今为止使用最广泛的评估方法。MOTP描述了使用边界框重叠和/或距离测量来跟踪对象的精确度。 基准数据集基准数据集包括 MOTChallenger、KITTI、UADETRAC。MOTChallest数据集是目前可用的最大、最完整的行人跟踪数据集,为训练深度模型提供了更多的数据。MOT15是最初的MOT挑战数据集,它的特点是视频具有一系列属性,模型需要更好地推广这些属性才能获得好的结果。MOT16和MOT19是其他修改版本。 基准结果如下为Gioele等人列出在MOT ChallengeMOT15数据集和MOT16数据集上测试的公开结果,这些数据集记录自相应的出版物,以便对本工作中提到的方法之间的结果进行清晰的比较。由于检测质量对性能有影响,因此将研究结果分为基于公共检测的模型和基于私有检测的模型。这些方法分为两类:在线和离线。发布的参考文档的年份、其操作模式、MOTA、MOTP、IDF1、主要跟踪(MT)和主要丢失(ML)指标,以百分比表示;假阳性(FP)、假阴性(FN)、ID开关(IDS)和碎片(Frag)的绝对数;以每秒帧数(Hz)表示的算法速度。对于每个度量,向上的箭头(↑)表示更高的分数,而向下的箭头(↓)表示相反的分数。在运行相同模式(批处理/在线)的模型中强调最佳性能,并且每个统计数据都以粗体突出显示。我们只在表2和表3中列出了从本综述中访问的模型获得的结果。在现实中,使用深度学习和具有在线处理模式的模型产生了最大的结果。然而,这可能是更加重视建立在线方法的结果,这在MOT深度学习研究社区中变得越来越流行。大量的碎片化是在线方法中经常出现的问题,这在MOTA得分中没有反映出来。当遮挡或探测丢失时,在线算法不会向前看,不会重新识别丢失的目标,也不会插入视频中丢失的轨迹片段。 结论本文对利用深度学习解决MTT问题的方法进行了简要的探索。这项研究讨论了使用深度学习来解决MTT问题的四个步骤中的每一个步骤的解决方案,使SOTA的MOT技术的总数达到n。对MOT算法的评估,包括评估措施和来自可访问数据集的基准结果,进行了简要的讨论。单对象跟踪器最近受益于将深度模型引入全局图优化算法,从而产生了高性能的在线跟踪器;另一方面,批处理技术受益于将深度模型引入全局图优化算法。
创新思路现有的语义分割模型基本都基于FCN,FCN是由编码器和解码器组成的架构。编码器用于特征表示学习,而解码器用于对编码器产生的特征表示进行像素级分类。在这两者中,特征表示学习(即编码器)可以说是最重要的模型组件。与大多数其他为图像理解而设计的 CNN 一样,编码器由堆叠的卷积层组成。由于对计算成本的关注,特征图的分辨率逐渐降低,因此编码器能够学习更多抽象/语义视觉概念,感受野逐渐增加。然而,编码器有一个基本限制,即学习远程依赖信息对于无约束场景图像中的语义分割至关重要,由于仍然有限的感受野而变得具有挑战性。为了克服上述限制,最近引入了许多方法。一种方法是直接操纵卷积操作。这包括大内核大小、多孔卷积和图像/特征金字塔。另一种方法是将注意力模块集成到 FCN 架构中。这样的模块旨在对特征图中所有像素的全局交互进行建模。当应用于语义分割时,一个常见的设计是将注意力模块与 FCN 架构相结合,注意力层位于顶部。无论采用哪种方法,标准编码器解码器 FCN 模型架构都保持不变。最近,已经尝试完全摆脱卷积并部署注意力模型。最近,一些SOTA方法表明将 FCN 与注意力机制相结合是学习远程上下文信息的更有效策略。这些方法将注意力学习限制在更小的输入尺寸的更高层,因为它的复杂度是特征张量的像素数的平方。这意味着缺乏对较低级别特征张量的依赖学习,导致次优表示学习。为了克服这个限制,论文旨在重新思考语义分割模型设计并贡献一个替代方案,用纯transformer代替基于堆叠卷积层的编码器,逐渐降低空间分辨率,从而产生一种新的分割模型,称为 SEgmentation TRansformer (SETR)。这种单独的transformer编码器将输入图像视为由学习的补丁嵌入表示的图像补丁序列,并使用全局自注意力模型转换该序列以进行判别特征表示学习。 MethodsSegmentation transformers (SETR)首先将图像分解为固定大小的补丁网格,形成补丁序列。将线性嵌入层应用于每个补丁的扁平像素向量,然后获得一系列特征嵌入向量作为transformer的输入。给定从编码器transformer学习的特征,然后使用解码器来恢复原始图像分辨率。至关重要的是,编码器transformer的每一层都没有空间分辨率的下采样,而是全局上下文建模,从而为语义分割问题提供了一个全新的视角。Image to sequence此处没什么创新,将图像分成16块,每块通过flatten操作变成向量,向量的长度为HW/16,分块的目的是为了缩小向量的长度,否则计算量太大。为了学习到像素之间的空间信息,将对每个像素进行位置编码,再与向量相加。transformer encoder此处没什么创新,与原始transformer一样,由multi-head self-attention (MSA) 和 Multilayer Perceptron(MLP) 块组成。MSA与MLP是transformer的基本部分,此处对于MSA与MLP的介绍略过。重点介绍下面论文的创新部分。Decoder designer为了评估编码器部分的特征表示,论文设计了三种解码器方式。在此之前需要将编码器的输出Z从向量reshape成H/16 x W/16 x C的形状。1. 原始上采样 (Naive unsampling) 解码器将编码器输出的特征映射到类别空间,做法是采用了一个简单的 2 层网络将通道数变为类别数量。其架构为:1 × 1 conv + 同步BatchNorm(w/ ReLU)+ 1 × 1 conv。之后,简单地将输出双线性上采样到完整的图像分辨率,然后是具有像素级交叉熵损失的分类层。当使用这个解码器时,这种模型表示为 SETR-Naive。2. Progressive UPsampling (PUP) 论文考虑一种渐进式上采样策略,而不是可能会引入嘈杂预测的一步上采样策略,该策略交替使用 conv 层和上采样操作。为了最大限度地减轻对抗效应,我们将上采样限制为 2 倍。因此,总共需要 4 次操作才能从 H/ 16 × W /16 转换到图像原分辨率。这个过程的更多细节在图 1(b) 中给出。使用此解码器时,将模型表示为 SETR-PUP。3. Multi-Level feature Aggregation (MLA) 第三种设计的特点是多级特征聚合(图 c)与特征金字塔网络类似。然而,我们的解码器根本不同,因为每个 SETR 层的特征表示 Zl共享相同的分辨率,没有金字塔形状。具体来说,我们将来自 M 层的特征表示 {Zm} (m ∈ { Le /M , 2 Le/ M , · · · , M Le /M }) 作为输入,这些特征表示从 M 层均匀分布在具有步骤长为 Le /M 。然后部署 M 个流,每个流都专注于一个特定的选定层。在每个流中,我们首先将编码器的特征 Zl 从 HW /256 × C 的 2D 形状reshape为 3D 特征图 H/ 16 × W/ 16 × C。一个 3 层(kernel大小为 1 × 1、3 × 3 和 3 × 3) 网络,第一层和第三层的特征通道分别减半,第三层后通过双线性操作将空间分辨率提升4倍。为了增强不同流之间的交互,我们在第一层之后通过逐元素添加引入了自上而下的聚合设计。在逐元素添加功能之后应用额外的 3 × 3 conv。在第三层之后,我们通过通道级连接从所有流中获得融合特征,然后将其双线性上采样 4 倍至全分辨率。使用此解码器时,将模型表示为 SETR-MLA。Conclusion三种解码器方式之间的结果对比,结果表明SETR-PUP方式最好。与其它SOTA模型的对比。SETR 在 ADE20K (50.28% mIoU)、Pascal Context (55.83% mIoU) 和 Cityscapes 上的竞争结果上取得了最新SOTA结果。特别是,在提交当天就在竞争激烈的ADE20K测试服务器排行榜中获得了第一名。效果可视化
Background近年来,随着卷积神经网络的发展,目标检测已被基于anchor-based的检测器所主导,大致可分为one-stage方法和two-stage方法。他们都首先在图像上平铺大量预设的anchor,然后预测类别并将这些anchor的坐标细化一次或几次,最后输出这些细化的anchors作为检测结果。由于two-stage方法比one-stage方法细化锚点数倍,因此前者具有更准确的结果,而后者具有更高的计算效率。常见检测基准的最新结果仍然由Anchor-based的检测器持有。由于 FPN和 Focal Loss的出现,最近的学术注意力已经转向Anchor-free检测器。Anchor-free检测器以两种不同的方式可以直接找到没有预设锚的目标。一种方法是首先定位几个预定义或自学习的关键点(keypoint),然后绑定目标的空间范围。我们将这种类型的Anchor-free检测器称为keypoint-based的方法。另一种方法是使用目标的中心点或中心区域来定义正样本,然后预测从正样本到目标边界的四个距离。我们称这种Anchor-free检测器为center-based的方法。这些Anchor-free检测器能够消除那些与锚相关的超参数,并实现了与Anchor-based的检测器相似的性能,使其在泛化能力方面更具潜力。在这两种Anchor-free检测器中,keypoint-based的方法遵循不同于Anchor-based的检测器的标准关键点估计管道。然而,center-based的检测器类似于Anchor-based的检测器,它将点视为预设样本而不是锚框。以one-stage anchor-based检测器RetinaNet和center-based anchor-free检测器FCOS为例,它们之间主要有3个区别:(1) 每个位置平铺的锚点。RetinaNet 在每个位置平铺多个锚框,而 FCOS 在每个位置平铺一个锚点。(2) 正负样本的定义。RetinaNet 重新排序IoU用于选择正负样本,而 FCOS 利用空间和尺度约束来选择样本。(3) 回归起始状态。RetinaNet 从预设的锚框回归目标边界框,而 FCOS 从锚点定位目标。正如 FCOS论文中所报告的,Anchor-free FCOS 的性能比Anchor-based的 RetinaNet 好得多,值得研究这三个差异中的哪一个是性能差距的重要因素。 创新思路这篇论文通过严格排除它们之间的所有实现不一致,以公平的方式调查anchor-based和anchor-free方法之间的差异。从实验结果可以得出结论,这两种方法的本质区别在于正负训练样本的定义,导致它们之间的性能差距。如果它们在训练时选择相同的正负样本,无论是从一个框还是一个点回归,最终的表现都没有明显的差距。因此,如何选择正负训练样本值得进一步研究。受此启发,论文提出了一种新的自适应训练样本选择 (Adaptive Training Sample Selection, ATSS),以根据目标特征自动选择正样本和负样本。它弥补了anchor-based和anchor-free检测器之间的差距。此外,通过一系列在MS COCO数据集上的实验,可以得出结论,不需要在图像上每个位置平铺多个anchors来检测物体。SOTA的 AP 50.7% 是通过应用新引入的 ATSS 而不引入任何开销来实现的。 Contribution这项工作的主要贡献可以概括为:表明anchor-based和anchor-free检测器之间的本质区别实际上是如何定义正负训练样本。提出自适应训练样本选择,根据目标的统计特征自动选择正负训练样本。证明在图像上的每个位置平铺多个锚点以检测对目标是无用的操作。在不引入任何额外开销的情况下,在 MS COCO 上实现SOTA性能。 Anchor-based和Anchor-free的区别分析去除不一致性论文使用one-stage anchor-based检测器RetinaNet和center-based anchor-free检测器FCOS作为实验对象,通过消除它们之间使用方法的不一致性,即把FCOS上的一些操作加到RetinaNet上,如GIoU loss、GroupNorm等,得到了两个基本一致的检测器--RetinaNet(#A=1)和FCOS。在去掉这些细节优化的影响后,两者只剩下了在分类和回归两个任务上的差别----定义正负样本的方法差别和回归的是anchor box或anchor point的差别。 分类的差别原始的正负样本选择做法:如图1(a),RetinaNet使用IOU将来自不同level的anchor box划分为正负样本,对于每个目标,在IOU>θp的所有anchor box中,选一个最大的作为正样本,所有IOU<θn的都认为是负样本,其他的都忽略掉。如图1(b),FCOS使用空间和尺度约束将anchor点分配到不同的level上,首先将所有在ground truth box内的anchor点作为候选点,然后基于预先对每个level设置的尺度范围来选择最终的正样本,没有选中的点就是负样本。实验的正负样本选择做法:交换各自的方案。如下表2所示,如果在RetinaNet(#A=1)使用空间和尺度约束的方式来代替IOU来选择正负样本,RetinaNet(#A=1)的performance可以提升到37.8%。而对于FCOS,如果使用IOU的策略在选择正负样本,那么performance会降到36.9%。这表明了正负样本的选择策略才是这两种方法的根本区别。回归的差别在正负样本确定之后,需要对正样本进行目标位置的回归。如图2所示,RetinaNet回归的是anchor box和ground truth的4个offset,而FCOS回归的是anchor点到4条边的距离。这表明RetinaNet的回归起点是一个框,而FCOS的回归起点是一个点。根据上面表2结果所示,当RetinaNet和FCOS使用相同的正负样本选择策略的时候,两者并没有明显的差别,这表明回归的起点并不是两个方法的本质区别。因此,one-stage anchor based目标检测方法和center-based anchor free的目标检测方法的本质区别在于正负样本的选取策略上。 Adaptive Training Sample Selection背景在训练目标检测器时,我们首先需要定义正负样本进行分类,然后使用正样本进行回归。根据前面的分析,定义正负样本是至关重要的,anchor free detection FCOS 改进了这一步。它引入了一种定义正样本和负样本的新方法,比传统的基于 IoU 的策略实现了更好的性能。以前的样本选择策略有一些敏感的超参数,例如anchor-based检测器中的 IoU 阈值和anchor-free检测器中的尺度范围。设置这些超参数后,所有的ground-truth box都必须根据固定的规则选择它们的正样本,这适用于大多数目标,但会忽略一些外部目标。因此,这些超参数的不同设置会产生非常不同的结果。关于ATSS算法的几点说明:根据锚框和目标之间的中心距离选择候选目标。对于 RetinaNet,锚框的中心越靠近目标的中心,IoU 越大。对于 FCOS,离目标中心越近的锚点将产生更高质量的检测。因此,离目标中心越近的锚点是更好的候选者。使用均值和标准差之和作为 IoU 阈值。维护不同目标之间的公平性。RetinaNet 和 FCOS 的策略往往对更大的目标有更多的正样本,导致不同目标之间的不公平。而ATSS统计结果表明,每个目标大约有 0.2 ∗ kL 个正样本,这与其尺度、纵横比和位置是没有关系的。几乎没什么超参数。 Conclusion使用新引入的 ATSS,SOTA检测器大幅提高到 50.7% 的 AP,而不会引入任何开销。
实例分割是一项具有挑战性的计算机视觉任务,需要预测对象实例及其每像素分割掩码。这使其成为语义分割和目标检测的混合体。自 Mask R-CNN 以来,实例分割的SOTA方法主要是 Mask RCNN 及其变体(PANet、Mask Score RCNN 等)。它采用先检测再分割的方法,先进行目标检测,提取每个目标实例周围的边界框,然后在每个边界框内部进行二值分割,分离前景(目标)和背景。除了检测然后分割(或逐检测分割)的自顶向下方法之外,还有其他一些实例分割方法。一个例子是通过将实例分割作为自底向上的像素分配问题来关注像素,就像在 SpatialEmbedding (ICCV 2019) 中所做的那样。但是这些方法通常比检测然后分割的 SOTA 具有更差的性能,我们不会在这篇文章中详细介绍。然而,Mask RCNN 速度非常慢,许多实时应用场合无法使用。此外,Mask RCNN 预测的掩码具有固定的分辨率,因此对于具有复杂形状的大目标来说不够精细。由于anchor-free目标检测方法(例如 CenterNet 和 FCOS)的进步,已经出现了一波关于单阶段实例分割的研究。其中许多方法比 Mask RCNN 更快、更准确,如下图所示。最近在 Tesla V100 GPU 上测试的单阶段方法的推理时间本文将回顾单阶段实例分割的最新进展,重点是掩码表示——实例分割的一个关键方面。 局部掩码和全局掩码在实例分割中要问的一个核心问题是实例掩码的表示或参数化——1)是使用局部掩码还是全局掩码,2)如何表示/参数化掩码。掩码表示:局部掩码和全局掩码主要有两种表示实例掩码的方法:局部掩码和全局掩码。全局掩码是我们最终想要的,它与输入图像具有相同的空间范围,尽管分辨率可能更小,例如原始图像的 1/4 或 1/8。它具有对大或小目标具有相同分辨率(因此具有固定长度特征)的天然优势。这不会牺牲更大目标的分辨率,固定分辨率有助于执行批处理以进行优化。局部掩码通常更紧凑,因为它没有作为全局掩码的过多边界。它必须与要恢复到全局掩码的掩码位置一起使用,并且局部掩码大小将取决于目标大小。但是要执行有效的批处理,实例掩码需要固定长度的参数化。最简单的解决方案是将实例掩码调整为固定图像分辨率,如 Mask RCNN 所采用的那样。正如我们在下面看到的,还有更有效的方法来参数化局部掩码。根据是使用局部掩码还是全局掩码,单阶段实例分割在很大程度上可以分为基于局部掩码( local-mask-based )和基于全局掩码( global-mask-based )的方法。 基于局部掩码的方法基于局部掩码的方法直接在每个局部区域上输出实例掩码。 显式编码的轮廓Bounding box 在某种意义上是一个粗糙的掩码,它用最小的边界矩形来逼近掩码的轮廓。ExtremeNet(Bottom-up Object Detection by Grouping Extreme and Center Points,CVPR 2019)通过使用四个极值点(因此是一个具有8个自由度的边界框而不是传统的4个DoF)进行检测,并且这种更丰富的参数化可以自然地扩展通过在其对应边缘上的两个方向上的极值点延伸到整个边缘长度的 1/4 的一段,到八边形掩模。从那时起,有一系列工作试图将实例掩码的轮廓编码/参数化为固定长度的系数,给定不同的分解基础。这些方法回归每个实例的中心(不一定是 bbox 中心)和相对于该中心的轮廓。ESE-Seg(Explicit Shape Encoding for Real-Time Instance Segmentation,ICCV 2019)为每个实例设计了一个内圆心半径形状签名,并将其与切比雪夫多项式拟合。PolarMask(PolarMask:Single Shot Instance Segmentation with Polar Representation,CVPR 2020)使用从中心以恒定角度间隔的光线来描述轮廓。FourierNet(FourierNet:Compact mask representation for instance segmentation using differentiable shape decoders)引入了使用傅立叶变换的轮廓形状解码器,并实现了比 PolarMask 更平滑的边界。各种基于轮廓的方法这些方法通常使用 20 到 40 个系数来参数化掩码轮廓。它们推理速度快且易于优化。但是,它们的缺点也很明显。首先,从视觉上看,它们都看起来——老实说——非常糟糕。它们无法精确描绘掩码,也无法描绘中心有孔的物体。这系列方法很有意思,但是前途渺茫。实例掩码的复杂拓扑或其轮廓的显式编码是难以处理的。 结构化 4D 张量TensorMask (TensorMask: A Foundation for Dense Object Segmentation, ICCV 2019) 是通过预测每个特征图位置的掩码来展示密集掩码预测思想的首批作品之一。TensorMask 仍然通过感兴趣区域而不是全局掩码来预测掩码,并且它能够在不运行目标检测的情况下运行实例分割。TensorMask 利用结构化的 4D 张量来表示空间域上的掩码(2D 迭代输入图像中的所有可能位置,2D 表示每个位置的掩码),它还引入了对齐表示和张量双锥体( aligned representation and tensor bipyramid )来恢复空间细节,但这些对齐操作使网络甚至比两阶段的 Mask R-CNN 还要慢。此外,为了获得良好的性能,它需要使用比标准 COCO 目标检测管道(6x schedule)长 6 倍的调度进行训练。紧凑型掩码编码自然的目标掩码不是随机的,类似于自然图像,实例掩码位于比像素空间低得多的内在维度。 MEInst(Mask Encoding for Single Shot Instance Segmentation,CVPR 2020)将掩码提炼为紧凑且固定的维度表示。通过使用 PCA 进行简单的线性变换,MEInst 能够将 28x28 的局部掩码压缩为 60 维的特征向量。 该论文还尝试在单级目标检测器(FCOS)上直接回归 28x28=784-dim 特征向量,并且在 1 到 2 个 AP 点下降的情况下也得到了合理的结果。 这意味着直接预测高维掩码(以每个 TensorMask 的自然表示)并非完全不可能,但很难优化。 掩码的紧凑表示使其更容易优化,并且在推理时运行速度也更快。 它与 Mask RCNN 最相似,可以直接与大多数其他目标检测算法一起使用。基于全局掩码的方法基于全局掩码( Global-mask-based )的方法首先基于整个图像生成中间和共享特征图,然后组合提取的特征以形成每个实例的最终掩码。这是最近的单阶段实例分割方法中的主流方法。 原型和系数( Prototypes and Coefficients )YOLACT(YOLACT:实时实例分割,ICCV 2019)是最早尝试实时实例分割的方法之一。YOLACT 将实例分割分解为两个并行任务,生成一组原型掩码并预测每个实例的掩码系数。原型掩码( prototype masks )是用 FCN 生成的,可以直接受益于语义分割的进步。系数被预测为边界框的额外特征。这两个并行步骤之后是组装步骤:通过矩阵乘法实现的简单线性组合和对每个实例的预测边界框的裁剪操作。裁剪操作减少了网络抑制边界框外噪声的负担,但如果边界框包含同一类的另一个实例的一部分,仍然会看到一些泄漏。原型掩码的预测对于确保最终实例掩码的高分辨率至关重要,这与语义分割相当。原型掩码仅依赖于输入图像,与类别和特定实例无关。这种分布式表示是紧凑的,因为原型掩码的数量与实例的数量无关,这使得 YOLACT 的掩码计算成本恒定(不像 Mask RCNN 的计算成本与实例数量成线性关系)。回顾 InstanceFCN(Instance-sensitivefully Convolutional Networks,ECCV 2016)和 MSRA 的后续研究 FCIS(Fully Convolutional Instance-aware Semantic Segmentation,CVPR 2017),它们似乎是 YOLACT 的一个特例。InstanceFCN 和 FCIS 都利用 FCN 生成多个实例敏感的分数图,其中包含目标实例的相对位置,然后应用组装模块输出目标实例。位置敏感的分数图可以被视为原型掩码,但 IntanceFCN 和 FCIS 使用一组固定的空间池操作来组合位置敏感的原型掩码,而不是学习线性系数。InstanceFCN [b] 和 FCIS [c] 使用固定池操作进行实例分割BlendMask (BlendMask: Top-Down Meets Bottom-Up for Instance Segmentation, CVPR 2020) 建立在 YOLACT 之上,但不是为每个原型掩码预测一个标量系数,BlendMask 预测一个低分辨率 (7x7) 注意力图来混合其中的掩码边界框。该注意力图被预测为附加到每个边界框的高维特征 (7x7=49-d)。有趣的是,BlendMask 使用的原型掩码是 4 个,但它甚至只对 1 个原型掩码起作用。 CenterMask(CenterMask:single shot instance segmentation with point representation,CVPR 2020)的工作方式几乎完全相同,并明确使用 1 个原型掩码(命名为全局显着图)。 CenterMask 使用 CenterNet 作为主干,而 BlendMask 使用类似的anchor-free和单级 FCOS 作为主干。CenterMask 的架构。BlendMask 有一个极其相似的管道。请注意,BlendMask 和 CenterMask 都进一步依赖于检测到的边界框。在与裁剪的原型蒙版混合之前,注意力图或掩码大小必须缩放到与边界框相同的大小。CondInst (Conditional Convolutions for Instance Segmentation) 更进一步,完全消除了对边界框的任何依赖。它没有组装裁剪的原型掩码,而是借用了动态过滤器的思想并预测了轻量级 FCN 头部的参数。FCN头部共有三层,共有169个参数。令人惊奇的是,作者表明,即使原型掩码是单独的 2-ch CoordConv,网络也能在 COCO 上达到 31 个 AP。我们将在下面的隐式表示部分讨论这个。BlendMask /CenterMask 和 CondInst 都是 YOLACT 的扩展。BlendMask/CenterMask 正在尝试将裁剪的原型掩码与每个 bbox 中的细粒度掩码混合。YOLACT 是 BlendMask 或 CenterMask 的一种特殊情况,其中注意力图的分辨率为 1x1。CondInst 正在尝试将裁剪的原型掩码与由动态预测过滤器组成的更深层次的卷积混合在一起。YOLACT 是 CondInst 的一种特殊情况,其中 FCN 是 1 1x1 conv 层。使用分支来预测原型掩码允许这些方法受益于使用语义分割的辅助任务(通常在 AP 中提升 1 到 2 个点)。它也可以自然地扩展到执行全景分割( panoptic segmentation )。关于表示每个实例掩码所需的参数,下面列出了一些技术细节。这些具有全局掩码和系数的方法每个实例掩码使用 32、196、169 个参数。YOLACT使用32个原型掩码+32-dim掩码系数+框裁剪;BlendMask 使用 4 个原型掩码 + 4 个 7x7 注意力图 + 框裁剪;CondInst 使用 coordConv + 3 1x1 动态 conv(169 个参数) SOLO 和 SOLOv2:按位置分割目标SOLO 是其中一种,值得拥有自己的部分。这些论文很有见地,而且写得很好。它们对我来说是一件艺术品(就像另一个我最喜欢的 CenterNet)。SOLOv1 的架构论文第一作者在知乎上回复了SOLO的动机,我引用如下:“语义分割预测图像中每个像素的语义类别。类似地,例如分割,我们建议预测每个像素的“实例类别”。现在关键的问题是,我们如何定义实例类别?”如果输入图像中的两个目标实例具有完全相同的形状和位置,则它们是同一个实例。任何两个不同的实例要么具有不同的位置或形状。由于形状一般难以描述,我们用尺寸近似形状。因此,“实例类别”由位置和大小定义。位置按其中心位置分类。SOLO 通过将输入图像划分为 S x S 单元格和 S² 类的网格来近似中心位置。通过将不同大小的目标分配到特征金字塔 (FPN) 的不同级别来处理大小。因此对于每个像素,SOLO 只需要决定将像素(和相应的实例类别)分配给哪个 SxS 网格单元和哪个 FPN 级别。所以SOLO只需要执行两个像素级别的分类问题,类似于语义分割。现在另一个关键问题是掩码是如何表示的?实例掩码直接由堆叠到 S² 通道中的全局掩码表示。这是一个巧妙的设计,可以同时解决许多问题。首先,许多先前的研究将 2D 掩码存储为扁平向量,当掩码分辨率增加导致通道数量激增时,这很快变得难以处理。全局掩码自然地保留了掩码像素内的空间关系。其次,全局掩码生成可以保持掩码的高分辨率。第三,预测掩码的数量是固定的,与图像中的目标无关。这类似于原型掩码的工作线,我们将在 SOLOv2 中看到这两个流如何合并。SOLO 将实例分割制定为仅分类问题,并删除任何依赖于回归的问题。这使得 SOLO 自然独立于目标检测。SOLO 和 CondInst 是直接操作全局掩码的两个作品,是真正的无边界框方法。SOLO 预测的全局掩码。掩码是冗余的、稀疏的并且对目标定位错误具有鲁棒性。 分辨率权衡( Resolution tradeoff )从 SOLO 预测的全局掩码中,我们可以看到掩码对定位误差相对不敏感,因为相邻通道预测的掩码非常相似。这带来了目标定位的分辨率(以及精度)和实例掩码之间的权衡。TensorMask 的 4D 结构化张量的想法在理论上很合理,但在当前 NHWC 张量格式的框架中很难在实践中实现。将具有空间语义的二维张量展平为一维向量不可避免地会丢失一些空间细节(类似于使用全连接网络进行语义分割),并且即使表示 128x128 的低分辨率图像也有其局限性。位置的 2D 或掩模的 2D 必须牺牲分辨率。大多数先前的研究都认为位置分辨率更重要并且对掩码尺寸进行下采样/压缩,从而损害了掩码的表现力和质量。TensorMask 试图取得平衡,但繁琐的操作导致训练和推理缓慢。SOLO 意识到我们不需要高分辨率的位置信息,并通过将位置压缩为粗略的 S² 网格来借用 YOLO。这样,SOLO 就保持了全局掩码的高分辨率。我天真地认为 SOLO 或许可以通过将 S² x W x H 全局掩码预测为附加到 YOLO 中每个 S² 网格的附加扁平 WH 维特征来工作。我错了——以全分辨率而不是扁平矢量来制定全局掩码实际上是 SOLO 成功的关键。 Decoupled SOLO 和 Dynamic SOLO如上所述,SOLO 在 S² 通道中预测的全局掩码非常冗余和稀疏。即使在 S=20 的粗分辨率下,也有 400 个通道,而且图片中的对象也不可能太多以至于每个通道都包含一个有效的实例掩码。在Decoupled SOLO 中,形状为 H x W x S² 的原始 M 张量被两个形状为 H x W x S 的张量 X 和 Y 替换。对于位于网格位置 (i, j) 的对象,M_ij 近似为 逐元素乘法 X_i ⊗ Y_j。这将 400 个通道减少到 40 个通道,实验表明性能没有下降。SOLO vs Decoupled SOLO vs SOLOv2现在很自然地会问,我们是否可以通过预测更少的掩码并预测每个网格单元的系数来组合它们来借鉴 YOLACT 的原型掩码想法?SOLOv2 正是这样做的。在 SOLOv2 中,有两个分支,一个特征分支和一个内核分支。特征分支预测 E 原型掩码,内核分支在每个 S² 网格单元位置预测大小为 D 的内核。正如我们在上面的 YOLACT 部分中看到的那样,这种动态过滤器方法是最灵活的。当 D=E 时,是原型掩码(或 1x1 conv)的简单线性组合,与 YOLACT 相同。该论文还尝试了 3x3 conv kernels(D=9E)。这可以通过预测轻量级多层 FCN 的权重和偏差(例如在 CondInst 中)更进一步。现在,由于全局掩码分支与其专用位置解耦,我们可以观察到新的原型掩码表现出比 SOLO 中的更复杂的模式。它们仍然对位置敏感,并且更类似于 YOLACT。 掩码的隐式表示CondInst 和 SOLOv2 中使用的动态滤波器的想法起初听起来很棒,但如果将其视为用于线性组合的系数列表的自然扩展,则实际上非常简单。还可以认为我们使用系数或注意力图对掩码进行了参数化,或者最终将其参数化为用于小型神经网络头部的动态滤波器。最近在 3D 学习中也探索了使用神经网络动态编码几何实体的想法。传统上,3D 形状要么使用体素、点云或网格进行编码。Occupancy Networks (Occupancy Networks: Learning 3D Reconstruction in Function Space, CVPR 2019) 提出将形状编码为神经网络,将深度神经网络的连续决策边界视为 3D 表面。网络接收 3D 中的一个点并判断它是否在编码的 3D 形状的边界上。这种方法允许在推理期间以任何分辨率提取 3D 网格。Occupancy Networks 中提出的隐式表示我们能否学习一个由每个目标实例的动态过滤器组成的神经网络,以便网络接收 2D 中的一个点并输出该点是否属于该目标掩码?这自然会输出一个全局掩码,并且可以具有任何所需的分辨率。 回顾 CondInst 的消融研究,证明即使没有原型掩码,也只有 CoordConv 输入(用于执行均匀空间采样)。由于此操作与原型掩码的分辨率分离,因此以更高分辨率单独输入 CoordConv 以获得更高分辨率的全局掩码以查看这是否会提高性能会很有趣。我坚信实例掩码的隐式编码是未来。只有 CoordConv 输入没有原型掩码,CondInst 也可以预测不错的性能 最后一句大多数单阶段实例分割工作都是基于anchor-free目标检测,如CenterNet和FCOS。也许不出所料,上述许多论文都来自阿德莱德大学创建 FCOS 的同一个实验室。他们最近在 https://github.com/aim-uofa/AdelaiDet/ 上开源了他们的平台。最近的许多方法都很快,并且可以实现实时或接近实时的性能 (30+ FPS)。NMS 通常是实时实例分割的瓶颈。为了实现真正的实时性能,YOLACT 使用 Fast NMS,SOLOv2 使用 Matrix NMS。后记预测实例掩码的高维特征向量是棘手的。几乎所有的方法都集中在如何将掩码压缩成低维表示。这些方法通常使用 20 到 200 个参数来描述一个掩码,取得不同程度的成功。我认为这是对表示掩码形状的最少参数数量的基本限制。手工设计的参数化轮廓并不是很有前途。局部掩码本质上取决于目标检测。希望能看到更多直接生成全局掩码的研究。掩码的隐式表示是富有表现力的、紧凑的并且可以以任何分辨率生成掩码。CondInst 有可能通过利用隐式表示的力量生成更高分辨率的全局掩码。SOLO 很简单,而 SOLOv2 又快又准。希望能看到更多沿着这条路线的未来研究。
一些新发布的数据集可以提供一个窗口,通过这些数据集可以了解试图解决的问题的复杂程度。公共领域中新发布的数据集可以很好地代表理解计算机视觉的发展以及有待解决的问题的新途径。本文简要总结了一些CVPR 2021 上发表的数据集论文,并通读了论文以提取一些重要的细节。 1. The Multi-Temporal Urban Development SpaceNet Dataset新的 SpaceNet 数据集包含每个月拍摄的建筑区域的卫星图像。目标是在空间时间序列的帮助下在全球范围内跟踪这种建筑活动。 由于其解决非常困难的全局问题的方法,这是 CVPR 中最有趣的数据集论文。该数据集试图使用卫星图像分析解决量化一个地区城市化的问题,这对于没有基础设施和财政资源来建立有效的民事登记系统的国家来说是一个巨大的帮助。该数据集主要是关于使用在 18 到 26 个月的时间跨度内捕获的卫星图像跟踪世界各地大约 101 个地点的建筑。随着时间的推移,有超过 1100 万条注释带有单个建筑物和施工现场的独特像素级标签。A.) 与 COCO 数据集对象相比,带注释的对象的大小非常小 B.) 在此数据集中,每张图像的标签数量太高。C.) 像云这样的遮挡(这里)会使跟踪探测变得困难。D.) Spacenet 数据集中单个图像中的带注释对象。 所有这些可能使它听起来像是一个更具挑战性的对象分割和跟踪问题。为了清楚起见,每帧大约有 30 多个对象。此外,与普通视频数据不同,由于天气、光照和地面季节性影响等原因,帧之间几乎没有一致性。这使得它比视频分类数据集(如 MOT17 和斯坦福无人机数据集)更加困难。虽然这可能是一个难题,但解决它对于全球福利来说是值得的。2. Towards Semantic Segmentation of Urban-Scale 3D Point Clouds: A Dataset, Benchmarks and ChallengesSensat Urban 数据集的整体图,包括英国约克市的连续区域,扩展到 3 平方公里。 今年的会议重点讨论了 3D 图像处理及其相应的方法。因此,这个名为 Sensat Urban 的数据集也不足为奇,只是这个摄影测量 3D 点云数据集比迄今为止可用的任何开源数据集都要大。它覆盖超过7.6公里。涵盖约克、剑桥和伯明翰的城市景观广场。每个点云都被标记为 13 个语义类之一。该数据集有可能推动许多有前途的领域的研究,如自动化区域测量、智慧城市和大型基础设施规划和管理。 Sensat Urban 数据集中的不同分割类别。 在论文中,他们还对点云中的颜色信息进行了实验,并证明了在色彩丰富的点云上训练的神经网络能够在测试集上更好地泛化。这实际上为该领域未来应用的发展提供了重要方向。 3.Spoken Moments: Learning Joint Audio-Visual Representations from Video Descriptions来自 MIT 音频字幕数据集的一些样本 [左] 在数据集中结合视听信息的提议架构 [右] 这是今年另一个最受欢迎的数据集,因为它对图像字幕和视频摘要问题采用了略有不同的方法。通常,对于此类任务,我们有像 COCO 这样的数据集,其中包含图像及其随附的文本标题。虽然这种方法已被证明是有前途的,但我们经常忘记,在口语方面对我们的视觉体验进行了很多丰富的总结。该数据集构建了一个包含 50 万个描述各种不同事件的短视频音频描述的语料库。然而,他们并没有止步于展示一个很棒的数据集,他们还提供了一个优雅的解决方案来使用自适应平均边距(AMM)方法来解决视频/字幕检索问题。 4.Conceptual 12M : Pushing Web-Scale Image-Text Pre-training to recognise Long-Tail visual concepts来自Conceptual 12M 数据集的一些图像标题对。虽然 alt-text 本身的信息量并不大,但它对于学习视觉概念的更广义的文本表示非常有帮助。 最近,由于预训练transformer和 CNN 架构的性能提升,模型预训练获得了极大的欢迎。通常,我们希望在一个类似的数据集上训练模型。然后使用迁移学习在下游任务上利用模型。到目前为止,唯一可用的用于预训练的大规模数据集是用于视觉+语言任务的 CC-3M 数据集,有 300 万个字幕。现在,谷歌研究团队通过放宽数据抓取的限制,将该数据集扩展到 1200 万个图像字幕对--Conceptual 12M。更有趣的是生成数据集的方法。在数据集管理期间使用 Google Cloud Natural Language API 和 Google Cloud Vision API 过滤任务对于任何未来的数据集管理任务来说都是一个很好的教训。使用 12M 数据集,图像字幕模型能够学习长尾概念,即数据集中非常具体且罕见的概念。训练方法的结果令人印象深刻,并在下面进行了可视化。在概念 12M 数据集上预训练的神经图像标题模型的预测示例很少。 5. Euro-PVI:密集城市中心的行人车辆交互实时车辆-行人行为示例。预测行人将采取什么样的轨迹来响应接近的车辆对于构建全自动自动驾驶汽车至关重要。虽然有很多关于完全自主的自动驾驶系统的讨论,但事实仍然是,它是一个非常困难的问题,需要同时实时解决多个问题。关键部分之一是使这些自主系统了解行人对其存在的反应,在密集环境中预测行人轨迹是一项具有挑战性的任务。因此,Euro-PVI 数据集旨在通过在行人和骑自行车者轨迹的标记数据集上训练模型来解决这个问题。早些时候,斯坦福无人机、nuScenes 和 Lyft L5 等数据集专注于附近车辆的轨迹,但这只是自主系统完整画面的一部分。Euro-PVI通过交互时的视觉场景、交互过程中的速度和加速度以及整个交互过程中的整体坐标轨迹等信息,提供了一个全面的交互图。Euro-PVI 数据集包含有关行人车辆交互的丰富信息,例如场景中所有参与者的视觉场景、速度和加速度。所有这些信息都必须由经过训练的模型映射到相关的潜在空间。为了解决潜在空间中轨迹和视觉信息的联合表示问题,同一篇论文还提出了 Joint-B-VAE 的生成架构,这是一种经过训练的变分自动编码器,用于对参与者的轨迹进行编码并将其解码为未来的合成轨迹。ground truth,Trajectron++ 预测的轨迹和联合 B-VAE 的预测轨迹(在同一数据集论文中提出)
当 Alex Krizhevsky、Ilya Sutskever 和 Geoffrey Hinton 在 2012 年设计 AlexNet 时,训练 6000 万参数模型需要五到六天的时间。八年后的 2020 年,微软 DeepSpeed 团队在不到 44 分钟的时间内成功训练了一个 3.5 亿参数的 Large-Bert 模型!九年后,我们现在看到,AlexNet 只是机器学习革命的冰山一角。今天,我们知道许多尚未开发的潜在训练技术和深度学习模型架构都在我们的掌握之中!不幸的是,由于数据的规模和新的深度学习模型架构的规模,其中许多进步对于普通研究人员来说就像多汁苹果对于没有梯子的水果采摘者一样难以获得。有这么多卓有成效的模型架构挂在深度学习潜力之树上,我们应该问自己,“我们如何才能达到它们?”答案很简单:要达到这些富有成效的架构,我们需要梯子!Alex Krizhevsky 构建了他自己的梯子来逐块到达 AlexNet,但是今天,像 PyTorch Lightning 这样的解决方案为您提供了自己的现成梯子——甚至是自动扶梯!本文介绍了如何使用 PyTorch Lightning 构建高效且快速的深度学习管道,还解释了这些优化如何通过显着加快研发实验周期来快速尝试各种研究想法!为什么优化深度学习管道很重要使用 PyTorch Lightning 加快实验周期的六种方法结果总结为什么优化深度学习管道很重要无论是在学术界还是在工业界从事研究,研发探索和尝试新想法的时间和资源总是有限的。随着数据集的规模和深度学习模型的复杂性不断增加,对最新机器学习模型和技术的实验日益复杂和耗时。如何应对这些挑战(并使研发周期更有效率)对项目的整体成功起着至关重要的作用。如今,存在各种解决方案来克服这些障碍,例如 Grid.ai、WandB 和 PyTorch Lightning。本文将重点介绍 PyTorch Lightning,并解释如何使用它使深度学习管道更快,并在需要最少代码更改的情况下在幕后提高内存效率。使用这些解决方案,可以使实验更具可扩展性和迭代速度更快,同时最大限度地减少潜在的错误。进行这些更改将减少实验所需的时间,节省的时间可以用来尝试更多的想法。 使用 PyTorch Lightning 加快实验周期的六种方法优化深度学习管道的六种方法:并行数据加载多GPU训练混合精度训练(Mixed precision training)分片训练(Sharded training)提前停止(Early stopping)模型评估和推理期间的优化对于每一种方法,我们都会简要解释它的工作原理,如何实现它,最后,分享我们是否发现它对我们的项目有帮助! 并行数据加载数据加载和增强步骤成为训练管道中的瓶颈是很常见的。典型的数据管道包含以下步骤:从磁盘加载数据即时创建随机增强将每个样本整理成批数据加载和增强过程非常容易并行,可以通过使用多个 CPU 进程并行加载数据来优化。这样一来,昂贵的 GPU 资源就不会在训练和推理过程中受到 CPU 的阻碍。为了尽快加载数据以训练深度学习模型,可以执行以下操作:将 DataLoader 中的 `num_workers` 参数设置为 CPU 的数量。使用 GPU 时,将 DataLoader 中的 `pin_memory` 参数设置为 True。这会将数据分配到页面锁定内存中,从而加快向 GPU 传输数据的速度。补充说明:如果处理流数据(即`IterableDataset`),还需要配置每个worker以独立处理传入的数据。种子初始化错误困扰着许多开源深度学习项目。为避免该错误,请在 `worker_init_fn` 中定义工作进程的进程种子。从 PyTorch Lightning 1.3 开始,这会使用 `seed_everything(123, workers=True)` 自动处理。从 PyTorch 1.8 开始,可以使用可选的 `prefetch_factor` 参数更好地控制加载性能行为。将此设置为更高的整数以提前加载更多批次,但会占用更大的内存。 使用分布式数据并行进行多 GPU 训练GPU 为 CPU 的训练和推理时间提供了巨大的加速。什么比 GPU 更好?多个GPU!PyTorch 中有一些范例可用于训练具有多个 GPU 的模型。两个更常见的范例是“DataParallel”和“DistributedDataParallel”,其中“DistributedDataParallel”是一种更具可扩展性的方法。在 PyTorch(和其他平台)中修改训练管道并非易事。人们必须考虑诸如以分布式方式加载数据以及权重、梯度和指标的同步等问题。使用 PyTorch Lightning,能够非常轻松地在多个 GPU 上训练 PyTorch 模型,几乎无需更改代码!混合精度默认情况下,输入张量以及模型权重以单精度 (float32) 定义。但是,某些数学运算可以半精度 (float16) 执行。这会显着提高速度并降低模型内存带宽,而不会牺牲模型性能。通过在 PyTorch Lightning 中设置混合精度标志,框架会在可能的情况下自动使用半精度,同时在其他地方保留单精度。通过最少的代码修改,能够将模型训练时间提高 1.5 到 2 倍。提前停止模型需要训练大量的 epoch,但实际上模型在训练过程的早期就很可能过度拟合了训练数据。因此,需要在训练管道中实施提前停止。提前停止被配置为在预定义的评估次数后验证损失停止减少时结束训练。通过这样做,不仅可以防止过度拟合,而且还可以节省时间,在数十个而不是数百个 epoch 内找到最佳模型。分片训练分片训练基于微软的 ZeRO 研究和 DeepSpeed 库,这使得训练大型模型具有可扩展性和简单性。这是通过使用各种内存和资源间通信优化来实现的。实际上,分片训练可以训练大型模型,否则这些模型将不适合单个 GPU 或在训练和推理期间使用更大的批次大小。PyTorch Lightning 在其 1.2 版本中引入了对分片训练的支持。在我们的用例中,我们没有观察到训练时间或内存占用的任何显着改进。但是,我们的见解可能无法推广到其他问题和设置,可能值得一试,尤其是当处理不使用单个 GPU 的大型模型时。 模型评估和推理期间的优化在模型评估和推理期间,模型的前向传递不需要梯度。因此,可以将评估代码包装在一个 `torch.no_grad` 上下文管理器中。这可以防止在前向传递期间存储梯度,从而减少内存占用。因此,可以将更大的批次输入模型中,从而实现更快的评估和推理。默认情况下,PyTorch Lightning 在幕后管理这些优化。 结果总结在我们的实验中,我们发现所有优化都独立地减少了训练深度学习模型的时间,除了分片训练,我们没有观察到任何速度或内存改进。 下表是改进深度学习管道所做的每项优化,以及观察到的性能提升。通过这些优化,我们把深度学习管道提高了10倍的速度,从两周节省到只要10小时。
在学习用于目标检测的卷积神经网络时,最难掌握的概念之一是锚框(anchor)的概念。它也是可以调整以提高数据集性能的最重要参数之一。事实上,如果锚框(anchor)没有正确调整,神经网络甚至永远不会知道某些极大、极小或不规则物体的存在,也永远没有机会检测到它们。幸运的是,你可以采取一些简单的步骤来确保不会落入这个陷阱。 什么是锚框(anchor box)?当使用 YOLO 或 SDD 等神经网络来预测图片中的多个目标时,该网络实际上会进行数以千计的预测,并且只显示它确定为目标的那些。多个预测以以下格式输出:预测 1:(X、Y、高度、宽度)、类别……预测 ~80,000:(X,Y,高度,宽度),类别其中 (X, Y, Height, Width) 称为“边界框(bounding box)”,或围绕目标的框。该框和目标类由人工注释者手动标记。在一个极其简化的示例中,假设我们有一个模型,它有两个预测并接收以下图像。我们需要告诉我们的网络它的每个预测是否正确,以便它能够学习。但是我们告诉神经网络它的预测应该是什么?预测的类应该是:预测1:梨预测2:苹果或者应该是:预测1:苹果预测2:梨如果网络预测:预测1:苹果预测2:苹果我们需要网络的两个预测器来判断他们的工作是预测梨还是苹果。要做到这一点,有几个工具。预测器可以专门研究特定大小的对象、具有特定纵横比(高与宽)的对象或图像不同部分的对象。 大多数网络使用所有三个标准。在我们的梨/苹果图像示例中,我们可以将预测 1 用于图像左侧的目标,将预测 2 用于图像右侧的目标。然后我们就会得到网络应该预测什么的答案:预测1:梨预测2:苹果 实践中的anchor box基于anchor-based的目标检测模型通常执行以下操作:1. 为每个预测器创建数千个“锚框”或“先验框”,代表它专门预测的目标的理想位置、形状和大小。2. 对于每个anchor box,计算哪个目标的bounding box有最高重叠除以非重叠。这称为 IOU (Intersection Over Union)。3. 如果最高IOU大于50%,告诉anchor box它应该检测给出最高IOU的目标。4. 否则如果IOU大于40%,告诉神经网络真正的检测是不明确的,不要从那个例子中学习。5. 如果最高IOU小于40%,那么anchor box应该预测没有目标。这在实践中效果很好,成千上万的预测器在决定它们的目标类型是否出现在图像中方面做得非常好。将RetinaNet 中的锚框可视化如下图所示,这里只展示了其中的 1%:使用默认锚框配置可能会创建过于专业化的预测器,并且图像中出现的目标可能无法使用任何锚框实现 50% 的 IOU。在这种情况下,神经网络永远不会知道这些目标存在,也永远不会学习预测它们。我们可以将锚框调整得更小,比如这个 1% 的样本:在 RetinaNet 配置中,最小的锚框尺寸是 32x32。这意味着许多比这更小的物体将不会被检测到。下面是来自 WiderFace 数据集的一个例子,我们将边界框与它们各自的锚框匹配,但有些却漏了:在这种情况下,只有四个ground truth边界框与锚框重叠。神经网络永远不会学习预测其他人脸。我们可以通过更改默认的锚框配置来解决这个问题。减少最小的锚框大小,所有的人脸至少与我们的一个锚框对齐,我们的神经网络可以学习检测它们!改进锚框配置作为一般规则,在深入训练模型之前,你应该了解以下有关数据集的问题:你希望能够检测到的最小尺寸的box是多少?你希望能够检测到的最大尺寸的box是多少?box可以做成什么形状?例如,汽车探测器可能有短而宽的锚框,只要汽车或摄像头不可能侧向转动。可以通过实际计算数据集中最极端的尺寸和纵横比来粗略估计这些。YOLO v3 是另一个目标检测器,它使用 K 均值来估计理想的边界框。另一种选择是学习锚框配置。一旦你想通了这些问题,你就可以开始设计你的锚框了。如果边界框和锚框的中心不同,得到的 IOU会很小。即使有小的锚框,如果锚框之间的步幅很宽,也可能会错过一些gound truth框。改善这种情况的一种方法是将 IOU 阈值从 50% 降低到 40%。David Pacassi Torrico 最近的一篇文章比较了当前人脸检测的 API 实现,强调了正确指定锚框的重要性。可以看到除了小脸外,算法都做得很好。下面是一些 API 根本无法检测到任何人脸的图片,但我们的新模型检测到了很多:这篇仅仅是了解anchor的文章,后续还会发一些关于深入理解anchor的文章,请继续关注公众号CV技术指南。
人体姿势骨架以图形格式表示人的方向。本质上,它是一组可以连接起来描述人的姿势的坐标。骨架中的每个坐标都称为零件(或关节或关键点)。两个部分之间的有效连接称为一对(或肢体)。请注意,并非所有零件组合都会产生有效的配对。下面显示了一个示例人体姿势骨架。左:人体姿势骨架的 COCO 关键点格式。右图:渲染的人体姿势骨架。 多年来,人们引入了几种人体姿势估计方法。最早(也是最慢)的方法通常是在只有一个人的图像中估计单个人的姿势。这些方法通常首先识别各个部分,然后在它们之间形成连接以创建姿势。自然,这些方法在许多图像包含多人的现实生活场景中并不是特别有用。 多人姿势估计多人姿态估计比单人情况更困难,因为图像中的位置和人数是未知的。通常,我们可以使用以下两种方法之一来解决上述问题:简单的方法是首先结合一个人检测器,然后估计各个部分,然后计算每个人的姿势。这种方法被称为自上而下的方法。另一种方法是检测图像中的所有部分(即每个人的部分),然后关联/分组属于不同人的部分。这种方法被称为自下而上的方法。顶部:典型的自上而下的方法。底部:典型的自下而上的方法。 通常,自顶向下方法比自底向上方法更容易实现,因为添加人员检测器比添加关联/分组算法简单得多。很难判断哪种方法具有更好的整体性能,因为它实际上归结为人员检测器和关联/分组算法中的哪种更好。在本文中,我们将重点介绍使用深度学习技术的多人人体姿态估计。在下一节中,我们将回顾一些流行的自上而下和自下而上的方法。 深度学习方法1. OpenPoseOpenPose 是最流行的自下而上的多人人体姿势估计方法之一,部分原因是它们有充分记录的 GitHub 实现代码。与许多自下而上的方法一样,OpenPose 首先检测属于图像中每个人的部分(关键点),然后将部分分配给不同的个人。下面显示的是 OpenPose 模型的架构。OpenPose 架构的流程图。 OpenPose 网络首先使用前几层(上述流程图中的 VGG-19)从图像中提取特征。然后将特征输入到卷积层的两个平行分支中。第一个分支预测一组 18 个置信度图,每个图代表人体姿势骨架的特定部分。第二个分支预测一组 38 个部件亲和域 (PAF),它表示部件之间的关联程度。使用 OpenPose 进行人体姿态估计的步骤。 后续阶段用于细化每个分支所做的预测。使用部件置信度图,在部件对之间形成二部图(如上图所示)。使用 PAF 值,可以修剪二部图中较弱的链接。通过上述步骤,可以估计人体姿势骨架并将其分配给图像中的每个人。 2. DeepCutDeepCut 是一种自下而上的多人人体姿态估计方法。作者通过定义以下问题来完成这项任务:产生一组 D 身体部位候选。该集合代表图像中每个人身体部位的所有可能位置。从上述候选身体部位集合中选择身体部位的子集。使用 C 身体部位类之一标记每个选定的身体部位。身体部位类表示部位的类型,例如“手臂”、“腿”、“躯干”等。划分属于同一个人的身体部位。该方法的图示。 上述问题通过将其建模为整数线性规划 ( Integer Linear Programming , ILP) 问题来共同解决。它是通过考虑具有域的二元随机变量的三元组 (x, y, z) 来建模的,如下图所示。二元随机变量的域。 考虑来自身体部位候选集 D 的两个身体部位候选者 d 和 d' 以及来自类别 C 的类别 c 和 c'。身体部位候选者是通过 Faster RCNN 或 Dense CNN 获得的。现在,我们可以开发以下语句集。如果 x(d,c) = 1,则表示候选身体部位 d 属于类 c。此外,y(d,d') = 1 表示候选身体部位 d 和 d' 属于同一个人。他们还定义了 z(d,d',c,c') = x(d,c) * x(d',c') * y(d,d')。如果上述值为1,则表示候选身体部位d属于c类,候选身体部位d'属于类别c',最后候选身体部位d,d'属于同一个人。最后一个语句可用于划分属于不同人的姿势。显然,上述陈述可以用线性方程表示为 (x,y,z) 的函数。这样就建立了整数线性规划( Integer Linear Programming , ILP),可以估计多人的姿态。对于确切的方程组和更详细的分析,请自行查看他们的论文。 3. RMPE (AlphaPose)RMPE 是一种流行的自上而下的姿态估计方法。作者认为自上而下的方法通常取决于人物检测器的准确性,因为姿势估计是在人物所在的区域上执行的。因此,定位和重复边界框预测中的错误会导致姿势提取算法执行欠佳。重复预测(左)和低置信边界框(右)的影响。 为了解决这个问题,作者提出使用对称空间transformer网络 (Symmetric Spatial Transformer Network, SSTN) 从不准确的边界框中提取高质量的单人区域。在这个提取的区域中使用单人姿势估计器 (SPPE) 来估计该人的人体姿势骨架。空间De-Transformer网络 (SDTN) 用于将估计的人体姿势重新映射回原始图像坐标系。最后,使用参数姿态非极大抑制 (NMS) 技术来处理冗余姿态推演问题。此外,作者引入了一个姿势引导建议生成器(Pose Guided Proposals Generator)来增加训练样本,从而更好地帮助训练 SPPE 和 SSTN 网络。RMPE 的显着特点是该技术可以扩展到人员检测算法和 SPPE 的任意组合。 4. Mask RCNNMask RCNN 是一种用于执行语义和实例分割的流行架构。该模型同时预测图像中各种目标的边界框位置和语义分割目标的掩码。基本架构可以很容易地扩展到人体姿态估计。描述 Mask RCNN 架构的流程图。 基本架构首先使用 CNN 从图像中提取特征图。区域提议网络 (Region Proposal Network, RPN) 使用这些特征图来获取存在对象的边界框候选者。边界框候选从 CNN 提取的特征图中选择一个区域(区域)。由于候选边界框可以有各种大小,因此使用称为 RoIAlign 的层来减小提取特征的大小,使它们都具有统一的大小。现在,这个提取的特征被传递到 CNN 的并行分支,用于边界框和分割掩码的最终预测。让我们专注于执行分段的分支。假设我们图像中的一个目标可以属于 K 个类中的一个。分割分支输出 K 个大小为 m x m 的二进制掩码,其中每个二进制掩码代表属于该类的所有目标。我们可以通过将每种类型的关键点建模为一个不同的类并将其视为分割问题来提取属于图像中每个人的关键点。同时,可以训练目标检测算法来识别人员的位置。通过结合人的位置信息以及他们的关键点集,我们获得了图像中每个人的人体姿势骨架。这种方法几乎类似于自顶向下的方法,但人员检测阶段与部件检测阶段并行执行。换句话说,关键点检测阶段和人物检测阶段是相互独立的。 应用姿态估计在无数领域都有应用,下面列出了其中的一些领域。1.活动识别跟踪一个人一段时间内姿势的变化也可用于活动、手势和步态识别。有几个相同的用例,包括:用于检测一个人是否跌倒或生病的应用程序。可以自主教授正确的锻炼方式、运动技巧和舞蹈活动的应用程序。可以理解全身手语的应用程序。(例如:机场跑道信号、交警信号等)。可以增强安全性和监视的应用程序。跟踪人的步态对于安全和监视目的很有用。 2. 动作捕捉和增强现实人体姿态估计的一个有趣应用是 CGI 应用。如果可以估计人体姿势,则可以将图形、样式、花哨的增强功能、设备和艺术品叠加在人身上。通过跟踪这种人体姿势的变化,渲染的图形可以在人物移动时“自然地贴合”他们。CGI 渲染示例。通过 Animoji 可以看到一个很好的视觉示例。尽管上面只跟踪了人脸的结构,但可以推断出一个人的关键点。可以利用相同的概念来渲染可以模仿人的运动的增强现实 (AR) 元素。 3. 训练机器人可以让机器人跟随正在执行动作的人体姿势骨架的轨迹,而不是手动编程机器人来跟随轨迹。人类教练可以通过演示来有效地教机器人某些动作。然后机器人可以计算如何移动其咬合架以执行相同的动作。 4. 控制台的运动跟踪姿势估计的一个有趣应用是踪人类主体在交互式游戏中的运动。通常,Kinect 使用 3D 姿势估计(使用 IR 传感器数据)来跟踪人类玩家的运动并使用它来渲染虚拟角色的动作。正在运行的 Kinect 传感器。 结论人体姿态估计领域取得了长足的进步,这使我们能够更好地为可能的无数应用提供服务。此外,在姿态跟踪等相关领域的研究可以大大提高其在多个领域的生产利用率。
为什么初始化很重要不正确初始化的权重会导致梯度消失或爆炸问题,从而对训练过程产生负面影响。对于梯度消失问题,权重更新很小,导致收敛速度变慢——这使得损失函数的优化变慢,在最坏的情况下,可能会阻止网络完全收敛。相反,使用过大的权重进行初始化可能会导致在前向传播或反向传播过程中梯度值爆炸。 常见的初始化方法1. 全零或等值初始化由于初始化的值全都相同,每个神经元学到的东西也相同,将导致“对称性(Symmetry)”问题。 2. 正态初始化(Normal Initialization)均值为零,标准差设置一个小值。这样的做好的好处就是有相同的偏差,权重有正有负。比较合理。例:2012年AlexNet使用“均值为零、标准差设置为0.01、偏差为1的高斯(正常)噪声进行初始化”的初始化方法。然而,这种正常的随机初始化方法不适用于训练非常深的网络,尤其是那些使用 ReLU激活函数的网络,因为之前提到的梯度消失和爆炸问题。 3. 均匀初始化(Uniform Initialization)均匀分布的区间通常为【-1/sqrt(fan_in),1/sqrt(fan_in)】其中fan_in表示输入神经元的数量,fan_out表示输出神经元的数量。4. Xavier Initialization来自论文《Understanding the difficulty of training deep feedforward neural networks》根据sigmoid函数图像的特点如果初始化值很小,那么随着层数的传递,方差就会趋于0,此时输入值也变得越来越小,在sigmoid上就是在0附近,接近于线性,失去了非线性。如果初始值很大,那么随着层数的传递,方差会迅速增加,此时输入值变得很大,而sigmoid在大输入值写倒数趋近于0,反向传播时会遇到梯度消失的问题。针对这个问题,Xavier 和 Bengio提出了“Xavier”初始化,它在初始化权重时考虑了网络的大小(输入和输出单元的数量)。这种方法通过使权重与前一层中单元数的平方根成反比来确保权重保持在合理的值范围内。Xavier 的初始化有两种变体。Xavier Normal:正态分布的均值为0、方差为sqrt( 2/(fan_in + fan_out) )。Xavier Uniform:均匀分布的区间为【-sqrt( 6/(fan_in + fan_out)) , sqrt( 6/(fan_in + fan_out)) 】。Xavier 初始化适用于使用tanh、sigmoid为激活函数的网络。 5. He Initialization激活函数的选择最终在决定初始化方法的有效性方面发挥着重要作用。激活函数是可微的,并将非线性特性引入神经网络,这对于解决机器学习和深度学习旨在解决的复杂任务至关重要。ReLU和leaky ReLU是常用的激活函数,因为它们对消失/爆炸梯度问题相对鲁棒。Xavier在tanh函数上表现可以,但对 ReLU 等激活函数效果不好,何凯明引入了一种更鲁棒的权重初始化方法--He Initialization。He Initialization也有两种变体:He Normal:正态分布的均值为0、方差为sqrt( 2/fan_in )。He Uniform:均匀分布的区间为【-sqrt( 6/fan_in) , sqrt(6/fan_in) 】 He Initialization适用于使用ReLU、Leaky ReLU这样的非线性激活函数的网络。 He Initialization和Xavier Initialization 两种方法都使用类似的理论分析:它们为从中提取初始参数的分布找到了很好的方差。该方差适用于所使用的激活函数,并且在不明确考虑分布类型的情况下导出。图来自何凯明的论文。论文展示了何凯明改进的初始化策略(红色)如何比 (P)ReLU 的 Xavier 方法(蓝色)更快地降低错误率。 有关 Xavier 和 He 初始化方法的证明,请参阅 Pierre Ouannes 的文章《如何初始化深度神经网络?Xavier 和 Kaiming 初始化》。 6. Pre-trained使用预训练的权重作为初始化,相比于其它初始化,收敛速度更快,起点更好。 除了以上的初始化方法外,还包括有LeCun Initialization。方法跟He Initialization和Xavier Initialization类似,但基本没怎么看见用,这里就不列出来了。 权重初始化仍然是一个活跃的研究领域。出现了几个有趣的研究项目,包括数据相关初始化、稀疏权重矩阵和随机正交矩阵初始化。
在本文中,我们试图更好地理解批量大小对训练神经网络的影响。具体而言,我们将涵盖以下内容:什么是Batch Size?为什么Batch Size很重要?小批量和大批量如何凭经验执行?为什么大批量往往性能更差,如何缩小性能差距? 什么是Batch Size?训练神经网络以最小化以下形式的损失函数:theta 代表模型参数m 是训练数据样本的数量i 的每个值代表一个单一的训练数据样本J_i 表示应用于单个训练样本的损失函数通常,这是使用梯度下降来完成的,它计算损失函数相对于参数的梯度,并在该方向上迈出一步。随机梯度下降计算训练数据子集 B_k 上的梯度,而不是整个训练数据集。B_k 是从训练数据集中采样的一批,其大小可以从 1 到 m(训练数据点的总数)。这通常称为批量大小为 |B_k| 的小批量训练。我们可以将这些批次级梯度视为“true”梯度的近似值,即整体损失函数相对于 theta 的梯度。我们使用小批量是因为它倾向于更快地收敛,因为它不需要完全遍历训练数据来更新权重。为什么Batch Size很重要?Keskar 等人指出,随机梯度下降是连续的,且使用小批量,因此不容易并行化 。使用更大的批量大小可以让我们在更大程度上并行计算,因为我们可以在不同的工作节点之间拆分训练示例。这反过来可以显着加快模型训练。然而,较大的批大小虽然能够达到与较小的批大小相似的训练误差,但往往对测试数据的泛化效果更差 。训练误差和测试误差之间的差距被称为“泛化差距”。因此,“holy grail”是使用大批量实现与小批量相同的测试误差。这将使我们能够在不牺牲模型准确性的情况下显着加快训练速度。 实验是如何设置的?我们将使用不同的批量大小训练神经网络并比较它们的性能。数据集:我们使用 Cats and Dogs 数据集,该数据集包含 23,262 张猫和狗的图像,在两个类之间的比例约为 50/50。由于图像大小不同,我们将它们全部调整为相同大小。我们使用 20% 的数据集作为验证数据,其余作为训练数据。评估指标:我们使用验证数据上的二元交叉熵损失作为衡量模型性能的主要指标。来自 Cats vs Dogs 数据集的示例图像基础模型:定义了一个受 VGG16 启发的基础模型,在其中重复应用 (convolution ->max-pool) 操作,使用 ReLU 作为卷积的激活函数。然后,将输出量展平并将其送入两个完全连接的层,最后是一个带有 sigmoid 激活的单神经元层,产生一个介于 0 和 1 之间的输出,它表明模型是预测猫(0)还是 狗 (1).训练:使用学习率为 0.01 的 SGD。一直训练到验证损失在 100 次迭代中都没有改善为止。 Batch Size如何影响训练?不同批次大小的训练和验证损失曲线每个批次大小获得的最佳损失左:每个epoch的平均时间。中间:直到验证损失收敛的epochs 数量。右图:直到验证损失收敛的总训练时间。从上图中,我们可以得出结论,batch size越大:训练损失减少的越慢。最小验证损失越高。每个时期训练所需的时间越少。收敛到最小验证损失所需的 epoch 越多。让我们一一了解这些。首先,在大批量训练中,训练损失下降得更慢,如红线(批量大小 256)和蓝线(批量大小 32)之间的斜率差异所示。其次,大批量训练比小批量训练实现更糟糕的最小验证损失。例如,批量大小为 256 的最小验证损失为 0.395,而批量大小为 32 时为 0.344。第三,大批量训练的每个 epoch 花费的时间略少——批量大小 256 为 7.7 秒,而批量大小 256 为 12.4 秒,这反映了与加载少量大批量相关的开销较低,而不是许多小批量依次。如果我们使用多个 GPU 进行并行训练,这种时间差异会更加明显。然而,大批量训练需要更多的 epoch 才能收敛到最小值——批量大小 256 为 958,批量大小 32 为 158。因此,大批量训练总体上花费的时间更长:批量大小 256 花费的时间几乎是 32 的四倍!请注意,我们没有在这里并行化训练——如果我们这样做了,那么大批量训练的训练速度可能与小批量训练一样快。如果我们并行化训练运行会发生什么?为了回答这个问题,我们使用 TensorFlow 中的 MirroredStrategy 在四个 GPU 上并行训练:with tf.distribute.MirroredStrategy().scope(): # Create, compile, and fit model # ...MirroredStrategy 将模型的所有变量复制到每个 GPU,并将前向/后向传递计算批量分发到所有 GPU。然后,它使用 all-reduce 组合来自每个 GPU 的梯度,然后将结果应用于每个 GPU 的模型副本。本质上,它正在划分批次并将每个块分配给 GPU。我们发现并行化使每个 epoch 的小批量训练速度稍慢,而它使大批量训练速度更快——对于 256 批大小,每个 epoch 需要 3.97 秒,低于 7.70 秒。然而,即使有 per-epoch 加速,它也无法在总训练时间方面匹配批量大小 32——当我们乘以总训练时间 (958) 时,我们得到大约 3700 秒的总训练时间,即 仍然远大于批大小 32 的 1915 秒。当跨 4 个 GPU 并行时,每个 epoch 的平均时间。到目前为止,大批量训练看起来并不值得,因为它们需要更长的时间来训练,并且训练和验证损失更严重。为什么会这样?有什么办法可以缩小性能差距吗? 为什么较小的批量性能更好?Keskar 等人对小批量和大批量之间的性能差距提出了一种解释:使用小批量的训练倾向于收敛到平坦的极小化,该极小化在极小化的小邻域内仅略有变化,而大批量则收敛到尖锐的极小化,这变化很大。平面minimizers 倾向于更好地泛化,因为它们对训练集和测试集之间的变化更加鲁棒 。取自 Keskar 等人 的平坦和尖锐最小值的概念图。此外,他们发现与大批量训练相比,小批量训练可以找到距离初始权重更远的最小值。他们解释说,小批量训练可能会为训练引入足够的噪声,以退出锐化minimizers 的损失池,而是找到可能更远的平坦minimizers 。 让我们验证这些假设。假设 1:与大批量最小化器相比,小批量minimizers 离初始权重更远。我们首先测量初始权重和每个模型找到的最小值之间的欧几里德距离。Distance from initial weights按层与初始权重的距离,批大小 32 和 256 的比较事实上,我们发现一般来说,批量越大,最小值越接近初始权重。(除了批量大小 128 比批量大小 64 离初始权重更远)。我们还在图 11 中看到,模型中的不同层都是如此。为什么大批量训练最终更接近初始权重?是否采取较小的更新步骤?让我们通过测量epoch距离——即epoch i 中的最终权重与epoch i 中的初始权重之间的距离——找出批量大小 32 和 256 的原因。左图:按批次大小划分的epoch距离。右:epoch距离的比率。上面的第一幅图显示,较大的批次大小确实确实在每个 epoch 中遍历的距离更短。第 32 批训练的 epoch 距离从 0.15 到 0.4 不等,而第 256 批训练的距离约为 0.02–0.04。事实上,正如我们在第二个图中所看到的,epoch距离的比率随着时间的推移而增加!但是为什么大批量训练每个 epoch 遍历的距离更短呢?是因为我们的批次较少,因此每个 epoch 的更新较少吗?还是因为每次批量更新遍历的距离更短?或者,答案是两者的结合?为了回答这个问题,让我们测量每个批量更新的大小。Distribution of batch update sizesMedian batch update norm for batch size 32: 3.3e-3 Median batch update norm for batch size 256: 1.5e-3我们可以看到,当批大小较大时,每次批更新较小。为什么会这样?为了理解这种行为,让我们设置一个虚拟场景,其中我们有两个梯度向量 a 和 b,每个表示一个训练示例的梯度。让我们考虑一下批量大小 = 1 的平均批量更新大小与批量大小 = 2 的情况相比如何。批量大小 1 (a+b) 和批量大小 2 ((a+b)/2) 之间更新步骤的比较如果我们使用 1 的批量大小,我们将在 a 的方向上迈出一步,然后是 b,最终在 a+b 表示的点上。(从技术上讲,b 的梯度将在应用 a 后重新计算,但我们现在先忽略它)。这导致平均批量更新大小为 (|a|+|b|)/2 — 批量更新大小的总和除以批量更新的数量。但是,如果我们使用批量大小为 2,批量更新将改为由向量 (a+b)/2 表示 — 图 12 中的红色箭头。因此,平均批量更新大小为 |(a+b)/ 2| / 1 = |a+b|/2。现在,让我们比较两个平均批量更新大小:批量大小 1 和批量大小 2 的平均批量更新大小的比较。在最后一行中,我们使用三角不等式来表明批量大小 1 的平均批量更新大小始终大于或等于批量大小 2 的平均批量更新大小。换句话说,为了使批量大小 1 和批量大小 2 的平均批量大小相等,向量 a 和 b 必须指向相同的方向,因为那是 |a| 的时候。+ |b| = |a+b|。我们可以将此参数扩展到 n 个向量——只有当所有 n 个向量都指向同一方向时,batch size=1 和 batch size=n 的平均批量更新大小才相同。然而,这几乎从来都不是这样的,因为梯度向量不太可能指向完全相同的方向。Minibatch update equation如果我们回到图 16 中的小批量更新方程,我们在某种意义上说,当我们扩大批量大小 |B_k| 时,梯度总和的大小相对较慢地扩大。这是因为梯度向量指向不同的方向,因此将批量大小(即要加在一起的梯度向量的数量)加倍并不会使生成的梯度向量总和的大小加倍。同时,我们除以分母 |B_k|这是两倍大,导致整体更新步骤更小。这可以解释为什么更大批量的批量更新往往更小——梯度向量的总和变得更大,但不能完全抵消更大的分母|B_k|。 假设 2:小批量训练找到更平坦的最小值现在让我们测量两个minimizers的锐度,并评估小批量训练找到更平坦的minimizers的说法。(请注意,第二个假设可以与第一个假设共存——它们并不相互排斥。)为此,我们从 Keskar 等人那里借用了两种方法。在第一个中,我们沿着小批量minimizers(批量大小 32)和大批量minimizers(批量大小 256)之间的线绘制训练和验证损失。这条线由以下等式描述:小批量minimizers和大批量minimizers之间的线性插值其中 x_l* 是大批量minimizers,x_s* 是小批量minimizers,alpha 是一个介于 -1 和 2 之间的系数。小批量minimizers (alpha=0) 和大批量minimizers (alpha=1) 之间的插值。大批量最小化器“更清晰”。正如我们在图中所见,小批量minimizers (alpha=0) 比大批量minimizers (alpha=1) 平坦得多,后者的变化更加剧烈。请注意,这是一种相当简单的锐度测量方法,因为它只考虑一个方向。因此,Keskar 等人提出了一个锐度度量,用于衡量损失函数在最小值附近的邻域内的变化程度。首先,我们定义邻域如下:最大化损失的约束框。其中 epsilon 是定义邻域大小的参数,x 是最小值(权重)。然后,我们将锐度度量定义为最小值附近的最大损失:锐度度量定义。其中 f 是损失函数,输入是权重。使用上面的定义,让我们计算各种批量大小下的最小化器的锐度,epsilon 值为 1e-3: 按批次大小的锐度得分这表明大批量最小化器确实更清晰,正如我们在插值图中看到的那样。最后,让我们尝试用 Li 等人制定的过滤器归一化损失可视化来绘制最小化器。这种类型的图选择两个与模型权重具有相同维度的随机方向,然后将每个卷积滤波器(或神经元,在 FC 层的情况下)归一化为与模型权重中的相应滤波器具有相同的范数。这确保了最小化器的锐度不受其权重大小的影响。然后,它沿着这两个方向绘制损失,图的中心是我们希望表征的最小值。批量大小为 32(左)和 256(右)的二维滤波器归一化图同样,我们可以从等高线图中看到,对于大批量最小化器,损失变化更加剧烈。 通过提高学习率可以提高大批量的性能吗在假设 1 中,我们看到大批量的更新大小和每个 epoch 的更新频率都较低,而在假设 2 中,我们看到大批量无法探索与小批量一样大的区域。知道了这一点,我们是否可以通过简单地提高学习率来使大批量训练表现更好?这种方法以前曾被建议过,例如 Goyal 等人提出:“线性缩放规则:当 minibatch 大小乘以 k 时,将学习率乘以 k。”让我们试试这个,批量大小为 32、64、128 和 256。我们将对批量大小 32 使用 0.01 的基本学习率,并相应地缩放其他批量大小。不同批次大小的训练和验证损失,调整学习率批量大小的最小训练和验证损失事实上,我们发现调整学习率确实消除了小批量和大批量之间的大部分性能差距。现在,批量大小 256 的验证损失为 0.352 而不是 0.395——更接近批量大小 32 的损失 0.345。提高学习率如何影响训练时间?由于大批量训练现在可以在与小批量训练大致相同的迭代次数中收敛,如图 25 中的左图所示,现在总体训练时间更短——批量大小 256 为 2197 秒,而批量为 3156 大小为 32。如果我们跨 4 个 GPU 并行化,则加速更加明显。 左:直到验证损失收敛的训练时期数。右图:直到收敛的总训练时间。这是否意味着大批量现在正在收敛到平面minimizers?如果我们绘制锐度分数,我们可以看到调整学习率确实使大批量最小化器更平坦: 有无学习率调整的锐度对比有趣的是,虽然调整学习率使大批量minimizers更平坦,但它们仍然比最小批量最小化器更锐利(4-7 与 1.14 相比)。为什么会这样仍然是未来调查的问题。较大批量的训练运行现在是否与小批量的初始权重相差甚远?调整前后按批次大小与初始权重的距离大多数情况下,答案是肯定的。如果我们看上面的图,调整学习率有助于缩小批量大小 32 与其他批量大小之间在与初始权重的距离方面的差距。(请注意,128 似乎是一个异常,其中增加学习率会降低距离——为什么会出现这种情况,有待未来调查。) 小批量训练总是优于大批量训练吗?鉴于上述观察和文献,如果我们保持学习率不变,我们可能会期望小批量训练总是优于大批量训练。事实上,事实并非如此,正如我们在使用学习率 0.08 时所看到的: 更高学习率下批量大小的验证损失在这里,我们看到批量大小 64 实际上优于批量大小 32!这是因为学习率和批量大小密切相关——小批量在较小的学习率下表现最好,而大批量在较大的学习率下表现最好。我们可以在下面看到这种现象:学习率对不同批次大小的 val 损失的影响。我们看到,0.01 的学习率对于批大小 32 是最好的,而 0.08 对于其他批大小是最好的。因此,如果您注意到大批量训练在相同学习率下优于小批量训练,这可能表明学习率大于小批量训练的最佳值。结论那么,这意味着什么?我们可以从这些实验中得到什么?线性缩放规则:当 minibatch 大小乘以 k 时,将学习率乘以 k。尽管我们最初发现大批量性能更差,但我们能够通过提高学习率来缩小大部分差距。我们看到这是由于较大的批次大小应用了较小的批次更新,这是由于批次内梯度向量之间的梯度竞争。选择合适的学习率时,较大的批量尺寸可以更快地训练,特别是在并行化时。对于大批量,我们不受 SGD 更新的顺序性质的限制,因为我们不会遇到与将许多小批量顺序加载到内存中相关的开销。我们还可以跨训练示例并行化计算。然而,当学习率没有针对较大的批量大小向上调整时,大批量训练可能比小批量训练花费的时间更长,因为它需要更多的训练时期来收敛。因此,您需要调整学习率以实现更大批量和并行化的加速。大批量,即使调整了学习率,在我们的实验中表现稍差,但需要更多的数据来确定更大的批量是否总体上表现更差。我们仍然观察到最小批量大小(val loss 0.343)和最大批量大小(val loss 0.352)之间的轻微性能差距。一些人认为小批量具有正则化效果,因为它们将噪声引入更新,帮助训练摆脱次优局部最小值的吸引力 。然而,这些实验的结果表明,性能差距相对较小,至少对于这个数据集。这表明,只要您为批量大小找到合适的学习率,您就可以专注于可能对性能产生更大影响的其他方面的训练。
实验一卷积是平移等变的:将输入图像平移 1 个像素,输出图像也平移 1 个像素(见图 1)。如果我们对输出应用全局平均池化(即对所有像素值求和),我们会得到一个平移不变模型:无论我们如何平移输入图像,输出都将保持不变。在 PyTorch 中,模型如下所示:y = torch.sum(conv(x), dim=(2, 3)) 输入 x,输出 y。 图 1:顶部:包含一个白色像素的输入图像(原始和 1 个像素移位版本)。中:卷积核。底部:输出图像及其像素总和。 是否可以使用此模型来检测图像中像素的绝对位置?对于像所描述的那样的平移不变模型,它应该是不可能的。让我们训练这个模型对包含单个白色像素的图像进行分类:如果像素在左上角,它应该输出 1,否则输出 0。训练很快收敛,在一些图像上测试二元分类器表明它能够完美地检测像素位置(见图 2)。图 2:顶部:输入图像和分类结果。底部:输出图像和像素总和。 模型如何学习对绝对像素位置进行分类?这仅可能由于我们使用的填充类型:图 3 显示了经过一些 epoch 训练后的卷积核当使用“same”填充(在许多模型中使用)时,内核中心在所有图像像素上移动(隐式假设图像外的像素值为 0)这意味着内核的右列和底行永远不会“接触”图像中的左上像素(否则内核中心将不得不移出图像)但是,当在图像上移动时,内核的右列和/或底行会接触所有其他像素我们的模型利用了像素处理方式的差异只有正(黄色)内核值应用于左上白色像素,从而只产生正值,这给出了正和对于所有其他像素位置,还应用了强负内核值(蓝色、绿色),这给出了负和图 3:3×3 卷积核。尽管模型应该是平移不变的,但事实并非如此。问题发生在由所使用的填充类型引起的图像边界附近。 实验二输入像素对输出的影响是否取决于其绝对位置?让我们再次尝试使用只有一个白色像素的黑色图像。该图像被送入由一个卷积层组成的神经网络(所有内核权重设置为 1,偏置项设置为 0)。输入像素的影响是通过对输出图像的像素值求和来衡量的。“valid”填充意味着完整的内核保持在输入图像的边界内,而“same”填充已经定义。图 4 显示了每个输入像素的影响。对于“valid”填充,结果如下所示:内核接触图像角点的位置只有一个,角点像素的值为 1 反映了这一点对于每个边缘像素,3×3 内核在 3 个位置接触该像素 3.对于一般位置的像素,有 9 个核位置,像素和核接触图 4:将单个卷积层应用于 10×10 图像。左:“same”填充。右:“valid”填充。 边界附近像素对输出的影响远低于中心像素,当相关图像细节靠近边界时,这可能会使模型失败。对于“same相同”填充,效果不那么严重,但从输入像素到输出的“路径”较少。最后的实验(见图 5)显示了当从 28×28 输入图像(例如,来自 MNIST 数据集的图像)开始并将其输入具有 5 个卷积层的神经网络(例如,一个简单的 MNIST 分类器可能看起来像这样)。特别是对于“valid”填充,现在存在模型几乎完全忽略的大图像区域。 图 5:将五个卷积层应用于 28×28 图像。左:“same”填充。右:“valid”填充。 结论这两个实验表明,填充的选择很重要,一些糟糕的选择可能会导致模型性能低下。有关更多详细信息,请参阅以下论文,其中还提出了如何解决问题的解决方案:1. MIND THE PAD – CNNS CAN DEVELOP BLIND SPOTS2. On Translation Invariance in CNNs: Convolutional Layers can Exploit Absolute Spatial Location
由于内存和计算资源有限,在嵌入式设备上部署卷积神经网络 (CNN) 很困难。特征图中的冗余是那些成功的 CNN 的一个重要特征,但在神经架构设计中很少被研究。 论文提出了一种新颖的 Ghost 模块,可以从廉价操作中生成更多的特征图。提出的 Ghost 模块可以作为即插即用的组件来升级现有的卷积神经网络。堆叠Ghost Module建立了轻量级的 GhostNet。GhostNet 可以实现比 MobileNetV3 更高的识别性能(例如 75.7% 的 top-1 准确率),并且在 ImageNet ILSVRC-2012 上具有相似的计算成本。出发点多年来,已经提出了一系列方法来研究紧凑型深度神经网络,例如网络剪枝、低位量化、知识蒸馏等。网络剪枝修剪神经网络中不重要的权重、利用正则化来修剪过滤器以获得高效的 CNN; 低位量化将权重和激活量化为 1 位数据,以实现大的压缩和加速比;知识蒸馏,将知识边缘从较大的模型转移到较小的模型。然而,这些方法的性能通常受预训练的神经网络作为其基线的上限。 训练好的深度神经网络的特征图中的丰富甚至冗余信息通常可以保证对输入数据的全面理解。例如,上图展示了 ResNet-50 生成的输入图像的一些特征图,并且存在许多相似的特征图对,就像彼此的幽灵。特征图中的冗余可能是成功的深度神经网络的一个重要特征。我们倾向于采用它们,而不是避免冗余的特征图,但以一种具有成本低的方式。这里补充一句:一个训好的正常大小的网络中,存在大量的冗余特征图,模型剪枝(或模型压缩)、正则化属于减少冗余特征图的方式,而这篇论文认为这些冗余信息会对于正确识别或检测具有重要的作用。推荐阅读《我们真的需要模型压缩吗》更好理解上面这段话。 主要贡献 引入了一个新的 Ghost 模块,通过使用更少的参数来生成更多的特征。 具体来说,深度神经网络中的一个普通卷积层会被分成两部分。第一部分涉及普通卷积,但它们的总数将受到严格控制。给定第一部分的内在特征图,然后应用一系列简单的线性操作来生成更多的特征图。在不改变输出特征图的大小的情况下,与普通卷积神经网络相比,这个 Ghost 模块所需的总体参数数量和计算复杂度有所降低。基于 Ghost 模块,建立了一个高效的神经架构,即 GhostNet。 首先替换基准神经架构中的原始卷积层以证明 Ghost 模块的有效性,然后验证 GhostNets 在几个基准视觉数据集上的优越性。实验结果表明,所提出的 Ghost 模块能够降低通用卷积层的计算成本,同时保持相似的识别性能,并且 GhostNets 可以在各种任务上超越SOTA高效深度模型,如 MobileNetV3 移动设备上的快速推理。 MethodsGhost module如上图所示,Ghost module先通过正常卷积,将input通道数减少,再通过一个depthwise卷积和identity(恒等变换)。1. 前面的卷积既可以使用1x1卷积,也可以使用正常的3x3或5x5卷积。2. 这里的Φ即为cheap operation,既可以是depthwise卷积,也可以是其它方式的卷积,例如分组卷积。这部分的作用就是生成了相似特征图。也就是前面说的,用成本更低的方式,保留了那些冗余信息。3. 恒等映射与 Ghost 模块中的线性变换并行以保留内在特征映射。 复杂度分析假设我们输入特征图的尺寸是h*w*c,输出特征图的尺寸是h’*w’*n,卷积核大小为k*k。在cheap operation变换中,我们假设特征图的channel是m,变换的数量是s,最终得到的新的特征图的数量是n,那么我们可以得到等式:n = m ∗ s由于Ghost的变换过程中最后存在一个恒等变换(Identity),所以实际有效的变换数量是s-1,所以上式可以得到如下公式:m ∗ ( s − 1 ) = n / s ∗ ( s − 1 )所以,理论的速度比为:理论的压缩比为:其中,s远小于c。Conclusion
这篇论文旨在以极低的计算成本解决性能大幅下降的问题。提出了微分解卷积,将卷积矩阵分解为低秩矩阵,将稀疏连接整合到卷积中。提出了一个新的动态激活函数-- Dynamic Shift Max,通过最大化输入特征图与其循环通道移位之间的多个动态融合来改善非线性。在这两个新操作的基础上,得到了一个名为 MicroNet 的网络系列,它在低 FLOP 机制中实现了比现有技术显着的性能提升。在 12M FLOPs 的约束下,MicroNet 在 ImageNet 分类上达到了 59.4% 的 top-1 准确率,比 MobileNetV3 高 9.6%。论文出发点高效 CNN 架构的最新进展成功地将 ImageNet 分类的计算成本从 3.8G FLOPs (ResNet-50) 降低了两个数量级到大约 40M FLOPs(例如 MobileNet、ShuffleNet),性能下降合理。然而,当进一步降低计算成本时,它们会遭受显着的性能下降。例如,当计算成本分别从 44M 下降到 21M 和 12M MAdds 时,MobileNetV3 的 top-1 准确率从 65.4% 大幅下降到 58.0% 和 49.8%。这篇论文的目标是将极低 FLOP 机制下的精度从 21M 降到 4M MAdds,这标志着计算成本降低到另一个数量级。处理极低计算成本(4M-21M FLOPs)的问题非常具有挑战性,考虑到输入数据大小为 224×224x3,在第一层 3 × 3 卷积、输出通道8的操作上就消耗了 2.7M MAdds。 剩余的资源太有限,无法设计有效分类所需的卷积层和 1,000 类分类器。 如上图所示,减少现有高效 CNN(例如 MobileNet 和 ShuffleNet)的宽度或深度的常见策略会导致严重的性能下降。这篇论文专注于新的算子设计,同时将输入分辨率固定为 224×224,预算成本为 4M FLOPs。 创新思路这篇论文从两个角度处理极低的 FLOPs:节点连接性(node connectivity)和非线性(non-linearity),这与网络宽度和深度有关。 首先,降低节点连接以扩大网络宽度为给定的计算预算提供了一个很好的权衡。其次,依靠改进的层非线性来补偿减少的网络深度,这决定了网络的非线性。这两个因素促使设计更有效的卷积和激活函数。 MethodsMicro-Factorized Convolution分为两部分:Micro-Factorized Pointwise Convolution和 Micro-Factorized Depthwise Convolution,两者再以不同方式组合。 Micro-Factorized Pointwise Convolution论文提出了微分解卷积 (MF-Conv) 将逐点卷积分解为两个组卷积层,其中组数 G 适应通道数 C 为:G = sqrt(C/R)其中 R 是两者之间的通道缩减比。对于给定的计算成本,该等式在通道数量和节点连接之间实现了良好的折衷。如上图所示,输入通道数C分为G组,G组再通过中间一个 (C/R × C/R )的置换矩阵Φ 降低通道数,这个置换矩阵类似于shufflenet中的打乱通道顺序的操作。 Micro-Factorized Depthwise Convolution这个部分是引用Inception_v2中的分解卷积,在使用Depthwise的基础上,将KxK卷积核分为Kx1和1xK两部分。 Micro-Factorized pointwise 和 depthwise 卷积可以以两种不同的方式组合:(a) 常规组合,和 (b) lite 组合。 前者只是将两个卷积连接起来。 上图所示的 lite 组合使用微分解深度卷积来扩展通道数量,通过为每个通道应用多个空间滤波器。 然后应用一组自适应卷积来融合和压缩通道数。 与其常规组合方式相比,它通过节省通道融合(pointwise)计算在学习空间过滤器(depthwise)上花费更多资源,经验证明这对于实现较低的网络层更有效。Dynamic Shift-Max考虑到Micro-Factorized pointwise 卷积更注重组内的连接,因此提出Dynamic Shift-Max,这是一种新的动态非线性,用于加强由Micro-Factorized创建的组之间的联系。Dynamic Shift-Max 输出 K 个融合的最大值,每个融合组合多个 (J) 组位移为其中J表示组数,i表示通道数,K表示融合后的输出数量。当J=K=2时,可以在准确率和复杂度之间取得较好的折衷。这个公式用一句话来解释就是,每J个组,对每组的x进行加权求和,共K个融合,然后取K个中的最大值作为第i个通道上的激活函数值。这样,DY-Shift-Max 实现了两种形式的非线性: (a) 输出 J 组的 K 个融合的最大值,以及 (b) 通过动态参数。第一个非线性是对 Micro-Factorized pointwise 卷积的补充,它侧重于每个组内的连接,加强组之间的连接。第二个使网络能够根据输入 x 调整这种强化。这两个操作增加了网络的表示能力,补偿了减少层数所带来的损失。 MicroNetConclusion在 12M FLOPs 的约束下,MicroNet 在 ImageNet 分类上达到了 59.4% 的 top-1 准确率,比 MobileNetV3 高 9.6%。对 ImageNet 分类的评估。左:top-1 准确率与 FLOPs。右图:top-1 准确率与延迟。注意添加了 Mo bileNetV3 ×0.75 以方便比较。MicroNet 优于 MobileNetV3,尤其是在计算成本极低的情况下(当 FLOPs 小于 15M 或延迟小于 9ms 时,top-1 精度提高 5% 以上)。动态 Shift-Max 与 ImageNet 上的其他激活函数的比较.
边缘 AI 是当今一个非常令人兴奋的领域,有很多发展和创新即将到来。多年来,机器学习预测有一个明显的趋势,即向下移动到更接近用户、不需要网络连接并且可以实时解决复杂问题(例如自动驾驶)的嵌入式硬件。有许多新框架和引擎的模型占用空间要小得多,专门设计用于在 Edge 设备上运行。此外,当用户的个人数据不离开边缘设备时,解决用户隐私和安全的非常重要的问题要容易得多。分析推理结果的复杂算法可以在边缘设备上执行,只将最终混淆的信息发送到云端(例如,某些异常情况的警报)。本文基于我们在 Darwin Edge 的工作,在那里我们专注于应用于医疗保健和制造的边缘 AI 应用程序。对于板卡的性能演示,我们使用了著名的开源 NCNN 高性能神经网络推理计算框架,针对移动和嵌入式平台进行了优化(https://github.com/Tencent/ncnn)。本文针对平台的交叉编译不需要太多工作(标准 cmake 配置和使用交叉编译工具构建并配置 sysroot),并且为了简单的板性能比较,NCNN 默认推理基准已经执行 (benchncnn)。NCNN 基准测试是静态构建的,它的大部分依赖项都是静态构建的,所以一般来说,让它在标准的嵌入式 Linux 系统中运行并不难。在我们的开发中,我们经常使用 Bonseyes 开发者平台 (https://www.bonseyes.com/),它可以使用相同的工具轻松交叉编译各种机器学习应用程序。Bonseyes 平台带有工具和 docker 镜像,可用于许多嵌入式平台的交叉编译环境。它支持平台映像的构建、目标板的设置和自定义应用程序的交叉编译,这是通过在容器内构建应用程序以与平台无关的方式执行的。 Raspberry PI 4B 型使嵌入式爱好应用程序易于访问和流行的著名平台也可用于中等复杂的机器学习应用程序。以实惠的价格约 50 美元,对于进入边缘计算的爱好者来说,它是一个不错的工具。Raspberry PI 4有 2 种基于 Linux 的操作系统发行版可用 - 官方 Raspberry Pi 发行版 Raspberry Pi OS (Raspbian) 和 Ubuntu Raspberry Pi 端口。Raspbian 在配置和管理目标板方面更加用户友好(对新手来说更容易),但 Ubuntu 对外部应用程序和库有更广泛的支持,可以访问标准的 Ubuntu ARM 包存储库。AI应用移植和交叉编译可能更方便。两个发行版都有 32 位和 64 位版本。根据我们的经验,64 位系统在机器学习应用程序基准测试中比 32 位系统快 50%。Raspberry PI 4 硬件规格通过在 Vulkan 1.0 的 Khronos 一致性测试套件中通过 100,000 次测试,为 Raspberry Pi 4 GPU 开发 Vulkan 驱动程序的开源计划已正式发布。不幸的是,Vulkan 驱动程序开发专注于 32 位平台,对 64 位操作系统的支持非常少。一般来说,Ubuntu(或其他支持良好的 Linux 发行版)是 Raspberry Pi 4 的良好交叉编译环境。GNU 交叉编译工具链可以从官方存储库(g++-aarch64-linux-gnu 和 gcc-aarch64-)轻松安装linux-gnu)。实现交叉编译环境的一种好方法是创建Docker镜像,在那里安装所有主机开发工具,并使用Ubuntu多平台存储库支持创建目标系统root,并让Ubuntu apt工具解析并下载目标板库的所有依赖项需要该 sysroot 才能完全发挥作用(在以后的文章中将详细介绍此主题)。下表给出了 Raspberry Pi 4B 板的 NCNN 基准测试结果:Jetson AGX Xavier主要针对边缘机器学习应用程序的 NVIDIA 旗舰产品基于 NVIDIA® Jetson AGX Xavier 模块。它是一个功能强大的平台,带有丰富的 NVIDIA 工具和库生态系统,用于开发 AI 应用程序。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。在 PC 工作站上运行的易于使用的 NVIDIA SDK 管理器支持下载和安装最新的可用操作系统(当前为 Ubuntu 18.04)以及与板载硬件相关的附加库和驱动程序。许多预装的库都额外支持 NVIDIA 硬件(例如 gstreamer 硬件编码/解码)。Jetson AGX Xavier 硬件规格具有 512 个 CUDA 核心的 Volta GPU 提供了不错的计算和处理能力。它支持 CUDA 计算能力 7.0。相比之下,著名的 NVIDIA GTX 1060 PC GPU 具有 Compute Capability 6.0 支持和 1280 个 CUDA 核心。此外,由于 CUDA 是第一个通用 GPU 编程平台,并且在过去十年中主要用于机器学习领域,因此该平台具有独特的优势,只需简单的交叉编译即可直接在板上轻松使用现有的 AI 框架和库。在目标上运行的 Ubuntu 系统支持使用 Ubuntu ARM 存储库中提供的许多软件包的 apt 工具轻松安装。一般而言,由于其功能强大、易于设置和封装可用,目标板本身可用于编译本机自定义应用程序,而无需在 PC 工作站上设置交叉编译环境。过程通常包括直接在板上克隆 github 存储库,运行 cmake 以检查缺少的依赖项,使用 apt-get 工具安装这些依赖项并执行重新配置,然后执行 make 以构建应用程序。在 AGX Xavier 上编译比在高端现代 PC 工作站上慢一个数量级,但对于中等规模的项目,这是一种测试和尝试新事物的简单方法。AGX Xavier 还支持 TensorRT,NVIDIA 引擎,用于运行优化的 AI 推理。它将标准模型格式(例如 ONNX)编译为可以在 GPU 或深度学习加速器上执行的高度优化的代码。更多关于 TensorRT 的信息可以在 NVIDIA 网站上找到。AGX Xavier 开发人员套件的价格约为 850 美元,这几乎使它成为 Edge AI 的第一个业余爱好套件。它更便宜(100 美元)的表亲 NVIDIA Jetson Nano 缺乏如此出色的 GPU(它基于 Maxvell 架构——128 核的 CUDA 计算能力 5.2)和支持 TensorRT 的深度学习加速器,但开发环境和工具/操作系统是相同的,所以这是一个很好的策略,从 Nano 板开始简单,然后当机器学习应用程序复杂性开始损害性能时,轻松切换到 AGX Xavier 平台。下表给出了 AGX Xavier 开发人员套件的 NCNN 基准测试结果:NXP i.MX 8 Multisensory Enablement Kit (MEK)恩智浦拥有一系列 i.MX 8 应用处理器,用于高级图形、成像、机器视觉和安全关键应用,以及一系列演示/目标平台,带有基于这些 CPU 的各种外设。NXP i.MX 8 Multisensory Enablement Kit (MEK) 是一个方便的机器学习应用平台。它非常强大,具有许多包含的硬件/扩展功能:以 1000+$ 的价格,它旨在用作专业平台。在恩智浦网站上有关于该板的大量文档,还有可用的 Linux 映像,可以使用 Etcher 等工具轻松将其刻录到 PC 上的 SD 卡。船上使用的操作系统是基于默认 Poky Linux 发行版的 Yocto Linux 的恩智浦定制。此自定义添加了许多 Edge AI 应用程序中常见的库(opencv、gstreamer、QT、工作 Vulkan 驱动程序)。从预烧的 SD 卡拆箱和启动 MEK 板很容易,但将工作站 PC 设置为交叉编译环境则需要更多的工作。它需要使用 Yocto 构建系统根和 SDK,可以交叉编译自定义应用程序以在该板上运行。Yocto 是一个非常复杂的构建系统,它在自定义电路板图像和系统库的各个方面的能力方面提供了强大的功能,但学习曲线陡峭,需要大量时间来理解和练习。自定义默认的 NXP iMX8 构建(例如,如果缺少某些额外的开源库)非常乏味。Yocto 下载源代码并从头开始构建所有内容(包括为构建计算机环境量身定制的 GNU 交叉编译工具链),在高端现代个人计算机上,这个初始构建工作需要一整天才能完成。在平台上执行的 NCNN 基准测试给出以下结果:Conclusion对于那些想要跳入令人兴奋的 Edge AI 领域的人来说,有许多易于使用的目标平台可供选择。我们比较了几个平台。出于爱好/演示目的,有便宜但支持机器学习的 Raspberry PI 4 和 NVIDIA Jetson Nano。Nvidia AGX Xavier 是 NVIDIA 的旗舰产品,具有便捷的工具和 Ubuntu 操作系统、良好的 GPU 和 CUDA 支持。NXP i.MX 8 是一个处理起来更复杂的平台,它针对专业的工业和商业应用。
MotivationBatchNorm 区别于其他深度学习算子的关键因素是它对批量数据而不是单个样本进行操作。BatchNorm 混合批次中的信息以计算归一化统计数据,而其他算子独立处理批次中的每个样本。因此,BatchNorm 的输出不仅取决于单个样本的属性,还取决于样本分组的方式。如上左图所示,按照采样大小,上中下三图表示BatchNorm的采样方式分别为entire dataser、mini-batches和subset of mini-batches。如上右图所示,按照采样风格,上中下三图表示BatchNorm的采样方式分别为entire domain、each domain和mixture of each domain。论文研究了 BatchNorm 中批处理的这些选择,证明了在不考虑批处理构建的不同选择的情况下,应用批处理规范可能会在许多方面产生负面影响,但可以通过在批处理方式上做出谨慎选择来提高模型性能。 Review of BatchNorm在一个mini-batches中,在每一BN层中,对每个通道计算它们的均值和方差,再对数据进行归一化,归一化的值具有零均值和单位方差的特点,最后使用两个可学习参数gamma和beta对归一化的数据进行缩放和移位。此外,在训练过程中还保存了每个mini-batches每一BN层每一通道的均值和方差,最后求所有mini-batches均值和方差的期望值,以此来作为推理过程中该BN层各自通道的均值和方差。 Whole Population as a Batch在训练期间,BatchNorm 使用mini-batch的样本计算归一化统计数据。但是,当模型用于测试时,通常不再有 mini-batch 的概念。最初提出BatchNorm是在测试时,特征应该通过在整个训练集上计算的总体统计数据 μ、σ 进行归一化。这里的 μ、σ 被定义为批次统计量 µ, σ 使用整个population作为“Batch”。广泛使用EMA 算法来计算 µ, σ,但它并不总是能准确地训练population数据,因此论文提出了新的算法PreciseBN。 Inaccuracy of EMAEMA: exponential moving average算法公式如下:由于以下原因,EMA会导致模型估计Population数据次优:当 λ 很大时,统计数据收敛缓慢。由于每次更新迭代仅对 EMA 贡献一小部分 (1-λ),因此 EMA 需要大量更新才能收敛到稳定的估计值。随着模型的更新,情况变得更糟:EMA 主要由过去的输入特征主导,随着模型的训练这些特征已经过时。当 λ 较小时,EMA 统计数据由较少数量的近期mini-batch主导,并不代表整个populatioin。PreciseBNPreciseBN通过以下两个步骤来近似Population统计数据。(固定)模型在许多小批量上应用来收集Batch统计数据;将per-batch统计数据聚合为总体统计数据。与EMA相比,PreciseBN多了两个重要的属性:统计数据完全根据固定模型状态计算,与使用模型历史状态的 EMA 不同;所有样本的权重相等。实验结论1.PreciseBN比BN更稳定。2.当batchsize很大时,EMA算法不稳定。作者认为不稳定性是由大批量训练中损害 EMA 统计收敛性的两个因素造成的:(1)32 倍大的学习率导致特征发生更剧烈的变化;(2) 由于总训练迭代次数减少,EMA 的更新次数减少了 32 倍。3.PreciseBN只需要个样本就可以得到稳定的结果。4.小Batch会累计误差。Batch in Training and Testing在训练和推理期间使用的Batch统计量不一致:训练期间使用mini-batch统计数据,推理期间使用训练期间所有mini-batch通过EMA算法近似得到的population统计数据。论文分析了这种不一致对模型性能的影响,并指出在某些情况下可以轻松消除不一致以提高性能。为了避免混淆,将SGD batch size或者total batch size定义为所有GPU上总的batch size大小,将normalization batch size定义为单个GPU上的batch size大小。(注:这一点在《归一化方法总结》一文中有提到,当使用多个GPU时,实际的mini-batch统计数据只基于batchsize/GPU数的样本上统计)normalization batch size对训练噪声和训练测试不一致性有直接影响:较大的Batch将mini-batch统计数据推向更接近总体统计数据,从而减少训练噪声和训练测试不一致为了便于分析,论文观察了3种不同评估方法的错误率:在训练集上对mini-batch统计量进行评估在验证集上对mini-batch统计量进行评估在验证集上对population统计量进行评估实验结论小的normalization batch size(例如 2 或 4)性能不佳,但如果使用mini-batch统计数据(蓝色曲线),该模型实际上具有不错的性能。结果表明,mini-batch统计和总体统计之间的巨大不一致是影响mini-batch性能的主要因素。另一方面,当normalization batch size较大时,小的不一致可以提供正则化以减少验证错误。这导致红色曲线比蓝色曲线表现更好.基于以上结论,论文给出两个消除不一致用来提高性能的方法Use Mini-batch in InferenceUse Population Batch in TrainingBatch from Different DomainsBatchNorm 模型的训练过程可以被视为两个独立的阶段:首先通过 SGD 学习特征,然后通过 EMA 或 PreciseBN 使用这些特征训练总体统计数据。我们将这两个阶段称为“SGD training”和“population statistics training”。在本节中,论文分析出现domain gap的两种情况:当模型在一个domain上训练但在其他domain上测试时,以及当模型在多个domain上训练时。这两者都会使 BatchNorm 的使用复杂化。实验结论当存在显着的域偏移时,模型在对评估中使用的domain会比使用 SGD 训练,进行总体统计训练后获得最佳错误率。直观地说,根据domain组成Batch可以减少训练测试的不一致并提高对新数据分布的泛化能力。BatchNorm 在mixture of multi-domain data上的domain-specific training在以前的工作中经常被提出,名称为“Domain-Specific BN”、“Split BN”、“Mixture BN”,“Auxiliary BN”,“Transferable Norm”。这些方法都包含以下三种选择中的一些。Domain-specific SGD trainingDomain-specific population statisticsDomain-specific affine transform通过消除上述三个选择,我们表明在 SGD training和population statistics training之间使用一致的策略很重要,尽管这种实现可能看起来不直观。Information Leakage within a Batch我在《归一化方法总结》中总结到,BN的三个缺陷之一便是当mini-batch中的样本非独立同分布时,性能比较差,作者认为这是由于Information Leakage导致的。论文实验发现,当使用random采样的mini-batch统计量时,验证误差会增加,当使用population统计量时,验证误差会随着epoch的增加逐渐增大,验证了BN信息泄露问题的存在。为了处理信息泄露问题,之前常见的做法是使用SyncBN,来弱化mini-batch内样本之间的相关性。另一种解决方法是在进入head之前在GPU之间随机打乱RoI features,这给每个GPU分配了一个随机的样本子集来进行归一化,同时也削弱了min-batch样本之间的相关性,如下图所示。如下图所示,实验证明 shuffling和 SyncBN 都有效地解决了信息泄漏问题,允许head在测试时很好地概括population statistics。在速度方面,对于深度模型,shuffle 需要较少的 cross-GPU 同步,但每次同步传输的数据比 SyncBN 层传输的数据多。因此,它们的相对效率因模型架构而异。据比SyncBN多。因此,shuffling和SyncBN的相对效率跟具体模型架构相关。总结本文回顾了BatchNorm算法;分析了使用mini-batches计算的统计数据和基于population作为batch计算的统计数据的效果,提出了PreciseBN近似统计算法,该算法相比于常用的EMA算法有更稳定的效果;分析了根据不同domain来组成mini-batch的效果差异;分析了处理mini-batch中的样本非独立同分布情况的两种方法。结合前面的三篇文章《Batch Normalization》、《可视化的BatchNorm--它的工作方式以及为什么神经网络需要它》、《归一化方法总结 | 又名"BN和它的后浪们"》,相信读者会对BatchNorm会有一个非常全面的认识,并进一步加深对神经网络的理解。
在人群中定位个体更符合后续高级人群分析任务的实际需求,而不是简单地计数。然而,现有的基于定位的方法依赖于作为学习目标的中间表示(即密度图或伪框)是违反直觉和容易出错的。论文提出了一个纯粹基于点的框架,用于联合人群计数和个体定位。对于这个框架,论文提出了一个新的度量标准,称为密度归一化平均精度 (density Normalized Average Precision --nAP),而不是仅仅报告图像级别的绝对计数误差,以提供更全面和更精确的性能评估。此外,论文在这个框架下设计了一个直观的解决方案,称为点对点网络(P2PNet)。P2PNet 丢弃了多余的步骤,直接预测一组point proposals来表示图像中的头部,与人类标注结果一致。通过彻底的分析,论文揭示了实现这种新颖想法的关键步骤是为这些proposals分配最佳学习目标。P2PNet 不仅在流行的计数基准上显着超越了SOTA方法,而且还实现了有前途的定位精度。 出发点在人群分析的所有相关具体任务中,人群计数是一个基本支柱,旨在估计人群中的个体数量。 然而,简单地给出一个数字显然远远不能支持后续更高层次的人群分析任务的实际需求,如人群跟踪、活动识别、异常检测、流量/行为预测等。 事实上,这个领域有一个明显的趋势,即除了简单的计数之外,更具有挑战性的细粒度估计(即个体的位置)。 具体来说,一些方法将人群计数视为头部检测问题,但在对小尺度头部进行劳动密集型注释上留下了更多的努力。 其他方法试图生成仅提供点注释的头部伪边界框,但这至少看起来很棘手或不准确。 同样试图直接定位个体,有几种方法在抑制或分裂过近的候选实例时陷入困境,由于头部尺度的极端变化,特别是对于高度拥挤的区域,它们容易出错。 在评估指标方面,一些有远见的工作鼓励采用补丁级别的指标进行细粒度评估,但它们仅提供了对定位的粗略衡量标准。 其他现有的定位感知指标要么忽略了人群中的显着密度变化,要么缺乏对重复预测的惩罚。 创新思路为了解决上述问题,论文提出了一个纯粹基于点的框架,用于联合计数和定位人群中的个人。 该框架直接使用点标注作为学习目标,同时输出点来定位个体,受益于点表示的高精度定位特性和相对便宜的标注成本。 论文提出了一种称为密度归一化平均精度 (density Normalized Average Precision --nAP) 的新指标,为定位和计数错误提供综合评估指标。nAP 指标支持框和点表示作为输入(即预测或注释),没有上述缺陷。 作为这个新框架下的直观解决方案,论文开发了一种新方法来直接预测一组具有图像中头部坐标及其置信度的point proposals。具体来说,论文提出了一个点对点网络 (P2PNet) 来直接接收一组带标注的头部点用于训练,并在推理过程中预测点。为了使这样的想法正确工作,论文深入研究了ground truth target分配过程,以揭示这种关联的关键。结论是,无论是多个proposals 与单个ground truth匹配的情况,还是相反的情况,都会使模型在训练期间混淆,导致高估或低估计数。因此,论文建议通过匈牙利算法进行一对一匹配,将point proposals与其ground truth target相关联,未匹配的proposals 应归类为负样本。凭经验表明,这种匹配有利于改进 nAP 指标,作为论文在新框架下解决方案的关键组成部分。这种简单、直观和高效的设计产生了SOTA的计数性能和有前途的定位精度。 Methods Purely Point-based Framework这里简要说明这种新框架的思路。给定一个有N个个体的图像,用N个点来表示个体的头部中心点。网络输出两个东西,一个是预测头部的中心点P,一个是该中心点的置信度C。目标是使预测点与ground truth尽可能地接近,并有足够高的置信度。与传统的计数方法相比,该框架提供的个体位置有助于那些基于运动的人群分析任务,如人群跟踪、活动识别、异常检测等 此外,该框架不依赖于劳动密集型标注、不准确的伪框或棘手的后处理,受益于原始点表示的高精度定位特性,特别是对于人群中高度拥挤的区域。因此,这个新框架由于其相对于传统人群计数的优势和实用价值而值得更多关注。 然而,由于存在严重的遮挡、密度变化和标注错误,处理这样的任务是非常具有挑战性的 ,这在 [13] 中甚至被认为是理想的但不可行的。 Density Normalized Average Precision一个预测点 pˆj 只有在它可以匹配到某个ground truth pi 时才被归类为 TP。 匹配过程由基于像素级欧几里德距离的准则 (ˆpj , pi) 指导。 然而,直接使用像素距离来测量亲和度忽略了人群之间大密度变化的副作用。 因此,为此匹配标准引入了密度归一化,以缓解密度变化问题。 简单说来就是引入最近邻K(取3)个点,将它们的距离归一化。 用公式表示如下:预测与ground truth匹配方案(a) 在为每个ground truth点选择最近的提议时,多个ground truth点可能与同一个提议匹配,这会导致计数低估。 (b) 在为每个提案选择最近的ground truth时,多个提案可能会与相同的地面实况点匹配,这会导致高估计数。 (c) 论文通过匈牙利算法进行一对一匹配没有以上两个缺陷,因此适合直接点预测。 P2PNetP2PNet 的整体架构 建立在 VGG16 之上,它首先引入了一个上采样路径来获得细粒度的深度特征图。 然后它利用两个分支同时预测一组点提议及其置信度分数。pipeline中的关键步骤是确保point proposals和ground truth点之间的一对一匹配,这决定了这些proposals的学习目标。 loss function如下:Conclusion
由于基于transformers的架构在计算机视觉建模方面具有创新性,因此对有效架构的设计约定的研究还较少。从 CNN 的成功设计原则出发,我们研究了空间维度转换的作用及其对基于transformers的架构的有效性。 我们特别关注CNNs的降维原理;随着深度的增加,传统的 CNN 会增加通道维度并减少空间维度。我们凭经验表明,这种空间降维也有利于transformers架构,并在原始 ViT 模型上提出了一种新型的基于池化的视觉transformers (Pooling-based Vision Transformer--PiT)。 我们表明 PiT 实现了针对 ViT 的改进模型能力和泛化性能。在广泛的实验中,我们进一步表明 PiT 在图像分类、目标检测和鲁棒性评估等多项任务上优于baseline。 出发点1. CNN 限制了空间交互,ViT 允许图像中的所有位置通过transformers层交互。2. 虽然ViT 是一种创新架构,并且已经证明了其强大的图像识别能力,但它沿用了NLP中的 Transformer 架构,没有任何变化。3. CNN 的一些基本设计原则在过去十年中已被证明在计算机视觉领域有效,但并未得到充分反映。因此,我们重新审视了 CNN 架构的设计原则,并研究了它们在应用于 ViT 架构时的功效。 创新思路CNN 以大空间尺寸和小通道尺寸的特征开始,并逐渐增加通道尺寸,同时减小空间尺寸。由于称为空间池化的层,这种维度转换是必不可少的。现代 CNN 架构,包括 AlexNet、ResNet和 EfficientNet,都遵循这一设计原则。 池化层与每一层的感受野大小密切相关。 一些研究表明,池化层有助于网络的表现力和泛化性能。 然而,与 CNN 不同的是,ViT 不使用池化层,而是在所有层中使用相同大小的空间。 首先,我们验证了 CNN 上池化层的优势。我们的实验表明,池化层证明了 ResNet 的模型能力和泛化性能。为了将池化层的优势扩展到 ViT,我们提出了一种基于池化的视觉transformers (PiT)。 PiT 是一种与池化层相结合的转换器架构。它可以像在 ResNet 中一样减少 ViT 结构中的空间大小。我们还研究了 PiT 与 ViT 相比的优势,并确认池化层也提高了 ViT 的性能。 最后,为了分析 ViT 中池化层的效果,我们测量了 ViT 的空间交互比,类似于卷积架构的感受野大小。我们展示了池化层具有控制自注意力层中发生的空间交互大小的作用,这类似于卷积架构的感受野控制。 Methods网络架构维度配置的示意图 我们将 ResNet50 、Vision Transformer (ViT) 和基于池化的 Vision Transformer (PiT) 可视化;(a) ResNet50 从输入到输出逐渐下采样特征;(b) ViT 不使用池化层,因此所有层都保持特征维度;(c) PiT 涉及将层汇集到 ViT 中。 Pooling-based Vision Transformer(PiT)PiT 架构的池化层 PiT 使用基于深度卷积的池化层,以小参数实现通道乘法和空间缩减。Effects of the pooling layer in vision transformer (ViT) 我们在网络架构的各个方面将我们的基于池化的视觉transformer (PiT) 与原始 ViT 进行了比较。PiT 在容量、泛化性能和模型性能方面优于 ViT。 Spatial interactio self-attention层在交互token数量上也有限制,因此交互区域是根据空间大小来确定的。 我们使用 ImageNet 上的预训练模型测量了 ViT 和 PiT 的空间交互区域。空间交互的标准是基于注意力矩阵的 soft-max 之后的分数。我们使用 1% 和 10% 作为阈值,计算超过阈值的交互发生的空间位置的数量,并通过将交互位置的数量除以空间标记的总大小来计算空间交互比率。 在 ViT 的情况下,交互作用平均在 20%-40% 之间,并且由于没有池化层,因此数值不会因层而有显着变化。PiT 减少了token的数量,同时通过池化增加了头部。 因此,如图 5 (a) 所示,早期层的交互率很小,但后一层显示出接近 100% 的交互率。为了与 ResNet 进行比较,我们将阈值更改为 10%,结果如图 5 (b) 所示。在 ResNet 的情况下,3x3 卷积意味着 3x3 空间交互。因此,我们将 3x3 除以空间大小,并将其作为近似值与注意力的交互率进行比较。虽然 ViT 的交互率在各层中是相似的,但 ResNet 和 PiT 的交互率随着它通过池化层而增加。 Architecture该表显示了 ViT 和 PiT 的spatial sizes, number of blocks, number of heads, channel size, 和FLOPs。PiT 的结构设计为尽可能与 ViT 相似,并具有更少的 GPU 延迟。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。 Conclusion我们验证了 PiT 在各种任务上提高了 ViT 的性能。在 ImageNet 分类中,PiT 和在各种规模和训练环境下都优于 ViT。此外,我们还比较了 PiT 与各种卷积架构的性能,并指定了 Transformer 架构优于 CNN 的规模。 我们使用检测头进一步测量 PiT 在目标检测方面的性能。 基于 ViT 和 PiT 的 DETR在 COCO 2017 数据集上进行训练,结果表明 PiT 作为主干架构甚至比 ViT 更适合图像分类以外的任务。最后,我们通过稳健性基准验证了 PiT 在各种环境中的性能。
视频实例分割(VIS)是一项需要同时对视频中感兴趣的对象进行分类、分割和跟踪的任务。本文提出了一种新的基于 Transformers 的视频实例分割框架 VisTR,它将 VIS 任务视为直接的端到端并行序列解码/预测问题。 给定一个由多个图像帧组成的视频片段作为输入,VisTR 直接输出视频中每个实例的掩码序列。它的核心是一种新的、有效的实例序列匹配和切分策略,在序列层面对实例进行整体监控和切分。VisTR从相似性学习的角度对实例进行划分和跟踪,大大简化了整个过程,与现有方法有很大不同。 VisTR 在现有的 VIS 模型中速度最快,效果最好的是在 YouTubeVIS 数据集上使用单一模型的方法。这是研究人员首次展示了一种基于 Transformer 的更简单、更快的视频实例分割框架,实现了具有竞争力的准确性。 出发点SOTA方法通常会开发复杂的pipeline来解决此任务。 Top-down的方法遵循tracking-by-detection范式,严重依赖图像级实例分割模型和复杂的人工设计规则来关联实例。 Bottom-up的方法通过对学习的像素嵌入进行聚类来分离对象实例。由于严重依赖密集预测质量,这些方法通常需要多个步骤来迭代地生成掩码,这使得它们很慢。因此,非常需要一个简单的、端到端可训练的 VIS 框架。 在这里,我们更深入地了解视频实例分割任务。 视频帧包含比单个图像更丰富的信息,例如运动模式和实例的时间一致性,为实例分割和分类提供有用的线索。 同时,更好地学习实例特征可以帮助跟踪实例。 本质上,实例分割和实例跟踪都与相似性学习有关:实例分割是学习像素级的相似性,实例跟踪是学习实例之间的相似性。 因此,在单个框架中解决这两个子任务并相互受益是很自然的。 在这里,我们的目标是开发这样一个端到端的 VIS 框架。该框架需要简单,在没有花里胡哨的情况下实现强大的性能。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。主要贡献我们提出了一种基于 Transformers 的新视频实例分割框架,称为 VisTR,它将 VIS 任务视为直接的端到端并行序列解码/预测问题。该框架与现有方法有很大不同,大大简化了整个流程。VisTR从相似度学习的新角度解决了VIS。实例分割是学习像素级的相似性,实例跟踪是学习实例之间的相似性。因此,在相同的实例分割框架中无缝自然地实现了实例跟踪。VisTR 成功的关键是实例序列匹配和分割的新策略,它是为我们的框架量身定制的。这种精心设计的策略使我们能够在序列级别作为一个整体来监督和分割实例。VisTR 在 YouTube-VIS 数据集上取得了强劲的成绩,在 57.7 FPS 的速度下实现了 38.6% 的 mask mAP,这是使用单一模型的方法中最好和最快的。 Methods整个 VisTR 架构如图 2 所示。它包含四个主要组件:一个用于提取多个帧的紧凑特征表示的 CNN 主干,一个用于对像素级和实例级特征的相似性进行建模的编码器-解码器 Transformer,一个实例 用于监督模型的序列匹配模块,以及一个实例序列分割模块。 Transformer EncoderTransformer 编码器用于对片段中所有像素级特征之间的相似性进行建模。 首先,对上述特征图应用 1×1 卷积,将维度从 C 减少到 d (d < C),从而产生新的特征图f1。 为了形成可以输入到 Transformer 编码器中的剪辑级特征序列,我们将 f1 的空间和时间维度展平为一维,从而得到大小为 d × (T·H·W) 的 2D 特征图。请注意,时间顺序始终与初始输入的顺序一致。每个编码器层都有一个标准架构,由一个多头自注意力模块和一个全连接前馈网络 (FFN) 组成。 Transformer DecoderTransformer 解码器旨在解码可以表示每帧实例的顶部像素特征,称为实例级特征。受 DETR的启发,我们还引入了固定数量的输入嵌入来从像素特征中查询实例特征,称为实例查询。 假设模型每帧解码 n 个实例,那么对于 T 帧,实例查询数为 N = n · T。实例查询是模型学习的,与像素特征具有相同的维度。以编码器 E 的输出和 N 个实例查询 Q 作为输入,Transformer 解码器输出 N 个实例特征,在图 2 中用 O 表示。 整体预测遵循输入帧顺序,不同图像的实例预测顺序为相同的。因此,可以通过将相应索引的项直接链接来实现对不同帧中实例的跟踪。 Instance Sequence Matching解码器输出的固定数量的预测序列是乱序的,每帧包含n个实例序列。本文与DETR相同,使用匈牙利算法进行匹配。 虽然是实例分割,但是在目标检测中需要用到bounding box,方便组合优化计算。通过FFN计算归一化的bounding box中心、宽度和高度,即全连接。 通过softmax计算bounding box的label。最终得到n×T个边界框。使用上面得到标签概率分布和边界框来匹配实例序列和gournd truth。最后计算Hungarian算法的loss,考虑标签的概率分布和bounding box的位置。损失基本遵循DETR的设计,使用L1损失和IOU损失。以下公式是训练的损失。它由标签损失、边界框和实例序列组成。Conclusion下图展示了 VisTR 在 YouTube VIS 验证数据集上的可视化。每行包含从同一视频中采样的图像。VisTR 可以很好地跟踪和分割具有挑战性的实例,例如:(a) 重叠实例,(b) 实例之间的相对位置变化,© 由相同类型的相似实例引起的混淆,以及 (d) 不同姿势的实例。
GDN 这个算子是这个理论中的核心非线性函数,表达式如下(公式不重要,如果你不喜欢这些该死的符号,你可以直接跳过这一节。):上标(k)和(k+1)表示层数,w和u是多通道图像的输入和输出,下标i是通道数。β 和 γ 是我要训练的参数。假设我们有 N 个通道,那么 γ 是一个 N × N 矩阵,β 是一个 N × 1 向量。乍一看,这个功能与 cudnn 和所有深度学习框架都很好地支持的批量归一化 (BN) 或局部响应归一化 (LRN) 非常相似。但相信我,不要让你的眼睛欺骗你。这是非常不同的。(注意大除法是元素除法。) 前向不会消耗太多计算能力,而后向会消耗我 GPU 的大部分能量。现在让我们看看后面。我需要计算 3 个梯度,∇β、∇γ 和 ∇u。我知道人们第一次看到这个的感觉,因为我第一次看到这个怪物时也想自杀。 但如果我能为所有这些狗屎画一幅画,你会感觉更舒服。 首先,我们可以很容易地注意到输入可以看作是一个长度为 m x n 的向量。其次,(blabla...)^(-3/2) 出现在所有这些梯度中。这意味着我们可以只计算该术语 1 次,并将它们缓存以备后用。我们称其为“(blabla...)^(-1/2)”矩阵 D 。最后,δ 是传播到前一层的误差。Fig 1. Computation of γ 经过一些简化,它更清楚了,对吧? 我知道仍然需要一些解释。 对于等式的右侧,每个矩形都是由我们上面提到的矩阵堆叠而成的向量。 D 是 GDN 公式中的分母项,还记得我们刚刚提到的“(blabla...)^(-1/2)”吗? 与一些高级算法不同,这种计算对大多数人来说非常直观,我们可以轻松编写 CPU 程序来处理它。只要稍微了解一下 CUDA,每个人都可以将他们的 CPU 代码移植到 GPU。但是,如果您可以选择不同的组织来启动内核,则速度会有很大的不同。 1. 不仅仅是天真的算法。 我称这种方法“不只是天真”是因为这是我用过的第一种方法。即使使用小尺寸图像作为输入,它也几乎耗尽了我所有的 GPU 内存,并实现了最慢的性能。没有利用任何内存重用,我只是垂直和水平复制所有这些小矩形以获得更大的矩阵,如下图所示,并启动许多一维组织的内核。然后将它们相加。Fig 2. Less than naive Algo. 该算法唯一的优点是不需要在每个CUDA线程中计算索引,因为线程id只是唯一对应的内存索引。所以你需要做的就是一些乘法,然后使用 cublas 将每个小彩色矩形与 1 向量(一个充满所有 1 的向量)的点积相加。但是正如你所看到的,矩形的大小并不像我这里画的那么小,大小和图像一样。对于这张图片中的每个向量,大小将为 N x N x imageSize x batchSize。很明显,我们浪费了 (N-1) x N x imageSize x batchSize x 4 个字节,更不用说浪费在访问所有这些冗余全局内存上的时间了。 欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。2. 朴素算法。 对于第一种算法,我每次迭代只能在我的网络中训练不到 4 张大小为 128 x 128 的图像,时间几乎为 2 秒。(我的 GPU 是 GTX 1080。)这个现实迫使我改进我的算法,否则,我必须等待近 2 个月才能得到我的结果。 因为我需要启动的内核数量肯定比我GPU中的CUDA内核多很多,所以不管我用什么方法,cuda驱动都会把这些任务序列化。然后我决定不复制所有这些记忆。相反,我将启动 N x 一维组织的 N x imageSize 内核 N 次(N 是通道总数)。 Fig 3. Without memory replication 可以看出,改进是显而易见的。因为,我们不再需要大量复制数据。 GPU 中的全局内存访问非常昂贵。内存访问模式也很简单,因为当您获得线程 id 时,只需使用一个 mod 操作就可以获得内存索引(内存索引 = 线程 id % imageSize)。但是,在这种方法中,由于内核仍然是一维组织的,并且我们使用for循环来启动所有这些内核,那么我们可能无法从GPU更智能的调度算法中受益,尽管我已经尝到了血的滋味.现在,通过这个小小的改变,2 个月的训练时间可以缩短到将近 2 周。 3. 更智能的组织算法。 到目前为止,我还没有考虑过共享内存的威力,因为对我来说,通常设计一个好的内核模式是枯燥和头痛的。显然,一维内核模式是最容易编写的代码。然而,更好的性能值得更仔细的设计。令我惊讶的是,本节中的算法实现了第二个算法的 3 倍速度。 回到图 1,可以看到前 3 个右侧矩阵的第一行 δ0、w0 和 D0 是相同的。因此,我们可以在一个块中计算一行 γ,对于每个块我们可以启动 imageSize 个线程,并且对于每个线程我们可以使用 for 循环计算所有通道。Fig 5. Computation in one block 所以从图 5 来看,将 δ0、w0 和 D0 放在共享内存中是非常直观的,而对于线程 i,它从 0 到 N-1 读取 N 个通道中的一个像素与 δ0、w0 和 D0 相乘 分享回忆。伪代码如下:blockId = blockIdx.x; threadId = threadIdx.x;shareDelta <- delta[blockId]; shareW <- W[blockId]; shareD <- D[blockId]; _synchronize();for(i = 0; i < N-1; i++) { result[threadIdx i*imgSize] = shareDelta[threadId] * shareW[threadId] * shareD[threadId] * W[threadId + i*imgSize]; }Algo 2 选择行主计算而不是列主计算是因为在一个网格中计算一行,我们可以共享 3 个向量 δ0、w0 和 D0。但是如果我们像在 Algo 中那样计算一列,我们只能共享 1 个向量 w0。(再次参见图 1。)。 在这段代码片段中,没有 if ... else ... 块。这在并行计算中非常重要。因为所有线程都是并行运行的,理想的情况是所有这些线程同时完成它们的工作。但是如果有 if ... else ... 阻塞,分支会让这些线程做不同的任务,以便它们在不同的时间完成。然后计算时间将由最慢的线程决定。 无索引计算也是一个优势。通过设计一维模式,我们必须使用线程id来计算内存索引,但这里不需要将blockId和threadId转换为一维内存索引来访问数据。 最后,因为我的数据存储在列major中,这意味着,像向量δ0一样,这个向量中的所有元素都是连续存储的。所以它受益于全局内存合并机制。全局内存也是cuda中的一个重要概念。在硬件方面,16个cuda内核被组织在一个warp中。当其中一个线程访问数据时,例如上图中的 a1,数据总线不仅会传输 a1,还会将 a1~a32 传输到缓存中,以加速其他 15 个内核的数据访问。因此,当我读取全局数据以共享内存时,每 32 个字节我只读取一次,所有其他字节都从缓存中读取,速度快了数百。多亏了时空局域性理论。 4. 多一点改进 今天突然发现其实我不需要共享内存,但是可以使用const内存。因为对于向量δ0、w0和D0,一个block中的每个线程只需要访问一次。所以在for循环之前,我们实际上可以将元素缓存在const内存中。另一个糖是因为每个线程只访问一个元素,不需要线程同步。代码如下:blockId = blockIdx.x; threadId = threadIdx.x;const float constDelta = delta[blockId * imgSize + threadId]; const float constW = W[blockId * imgSize + threadId]; const float constD = D[blockId * imgSize + threadId];for(i = 0; i < N-1; i++) { result[threadIdx + i*imgSize] = constDelta * constW * constD * W[threadId + i*imgSize]; }从上面的代码可以看出,constDelta、constW、constD可以从本地内存中重复使用N次,本地内存总是存储在本地寄存器中。因此,带宽大于共享内存。 Reduce Operation 我讲的所有算法都没有完成,因为我从上述算法中得到的实际上都是原始γ,如下所示:我需要在左侧累积每个向量以获得一个元素。第一个选择是 cublas API,cublasSsbmv。此函数将进行矩阵向量乘法。所以我们可以把左边的向量看成一个矩阵,将它与一个全1向量相乘,得到γ的一行梯度。并重复N次以获得最终结果。但我注意到还有其他 API cublasSgemmBatched。此函数可以进行批量矩阵向量乘法。然后我做了一个实验来测试哪个更快: N 个矩阵向量乘法 VS 批处理矩阵向量乘法的 for 循环。 结果表明for循环要快得多。但是我不知道原因,也许是因为我这里的 N 太小(N = 256)。我不会展示如何计算 ∇β 和 ∇u,因为它们类似于 ∇γ。我知道必须有比我更进一步的优化或更好的设计。CUDA 优化对于不深入了解 GPU 组织的人来说通常是困难的。熟悉 CPU 的程序员总是受益于现代操作系统和强大的编译器。然而,GPU 在编写足够的代码方面与 CPU 有很大不同和复杂性,尽管它比以前使用图形着色器进行计算要方便得多。生态环境的完善还需要几年时间。
Introduction顾名思义,通过姿势估计,我们尝试从图像中推断出物体或人的姿势。这涉及识别和定位身体上的关键点。由于身体的小关节、遮挡和缺乏上下文、旋转和方向,关键点的识别是一项非常具有挑战性的任务。在本文其余部分将主要关注人体姿势估计的情况下,膝盖、肘部、肩部和手腕等主要关节代表这些关键点。在分类方面,姿态估计器可以分为以下几类:维度(2D 与 3D)单姿势和多姿势(检测一个或多个物体)方法论(基于关键点与基于实例)我们可以使用 2D 姿态估计器预测图像或视频帧中关键点的 2D 位置,而 3D 姿态估计器通过在预测中添加深度将图像中的对象转换为 3D 对象。显然,使用 3D 更具挑战性。单姿态估计器通常以检测和跟踪一个人或物体为目标,而多姿态估计方法检测和跟踪多个人或物体。就方法论而言,广义上讲,我们发现模型尝试检测特定关键点的所有实例,然后尝试将关键点分组为骨架。基于实例的姿态估计器首先使用对象检测器来检测对象的实例,然后估计每个裁剪区域内的关键点。在文献中,这也通常称为自下而上与自上而下的方法。自上而下的方法包括在图像上应用人物检测器,对于每个检测到的人,使用单人姿势估计器进行关键点推断。如果您的人物检测器失败,那么您的姿势估计也会失败。此外,所需的处理量与人数成正比。自下而上受这些缺点的影响较小,但将关键点检测候选与个人关联起来,仍然具有挑战性。 DeepPose在本文中,作者首次将深度神经网络 (DNN) 应用于人体姿势估计挑战。下面,我们找到了所使用的架构。使用输入图像,可以回归每个身体关节及其位置。通过将原始的初始姿态估计传递给此类 DNN 的级联,可以进一步完善联合预测,从而实现 SOTA 结果。 Deep(er) Cut使用 DeepCut,图像中未知人数的姿态估计问题被重新表述为优化问题。问题是:在图像中创建一组所有候选身体部位,从中选择一个子集。从这个子集中,对每个身体部位(例如,手臂、腿和头)进行分类将同一个人的身体部位聚集在一起。然后通过将其建模为整数线性规划问题来解决这 3 个问题。为了找到图像中的所有身体部位,使用了 Fast R-CNN 的改编版本(AFR-CNN)。具体来说,适应性包括用可变形部件模型 (DPM) 替换选择性搜索建议生成并改变检测大小以允许 DPM 捕获更多上下文。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。从 70 年代的研究开始,他们处理对视觉对象进行一些描述的问题,如何在实际照片中找到这个对象?在真正的工程形式中,一个对象是由一组以可变形配置排列的部件组成的。通过以可变形配置排列的部件集合来表示人类。然后分别对每个零件的外观进行建模。一对零件由弹簧表示以引入必要的变形能力。怀疑使用 DPM 可能不是最理想的(确实如此),而是训练了基于 VGG 的密集 CNN 构建。然后将身体部位的检测重新表述为多标签分类。该模型为每个候选输出一个部分概率得分图。此外,与其他分割任务类似,使用步幅为 8 的扩张卷积来进行更精细的部分定位。DeeperCut 基于 DeepCut 的 Dense-CNN,但使用 ResNet 骨干网代替。与 VGG 主干类似,32px 的原始步长太大了。然而,由于内存限制,使用空洞算法是不可行的。通过移除最后一层来调整 ResNet 架构,减少第一个卷积层的步幅以防止下采样。在第 5 次卷积中,所有 3x3 卷积都添加了空洞。层和反卷积层用于上采样。DeeperCut 还受益于更大的感受野来推断附近其他部分的位置。这种见解称为图像条件成对项,可以计算部件的成对概率。成对part-to-part预测:通过计算每对的成本,回归的偏移量和角度用作特征来训练逻辑回归,从而产生成对概率 DeepCut 为图像中的所有候选身体部位解决了一个 ILP 实例,DeeperCut 提出了一种增量 3 阶段优化,其中:ILP 解决了头肩问题肘部/手腕被添加到第 1 阶段的解决方案中,并重新优化了 ILP将剩余的身体部位添加到第 2 阶段的解决方案中,并重新优化 ILP 用于人体姿态估计的卷积网络和图形模型的联合训练在本文中,检测pipeline由卷积网络和马尔可夫随机场 (MRF) 组成。与之前类似,ConvNet 架构用于身体部位定位。架构如下图所示:Multi-Resolution Sliding-Window With Overlapping Receptive Fields该架构通过使用滑动窗口方法处理输入图像,从而产生表示每个关节位置可能性的像素热图。有 2 个重叠的多分辨率字段,一个是 64x64 输入(上卷积),另一个是下采样到 64x64 的 128x128 输入,从而将更多“上下文”输入到下卷积路径中。然后在传入网络之前使用局部对比度归一化 (LCN) 对两者进行归一化。作者提到,使用重叠场的主要优势在于能够以相对较低的权重增加看到图像的更大部分。 此外,通过使用 LCN,两个窗口之间的重叠光谱内容最小。由于这需要相当大的计算能力,因此模型进行了改进,如下所示。保留了多分辨率(下 ConvNet)和滑动窗口(上 ConvNet)的概念。高上下文和低分辨率输入需要比滑动窗口模型的步长一半。因此,需要处理 4 个下采样图像。滑动窗口的特征图被复制,其中添加和交错低分辨率特征图,导致输出热图低于输入。Part-Detector 将输出许多解剖学上不正确的姿势,因为没有对身体关键点的隐式约束进行建模。通过使用高级空间模型在连接互连和姿势的解剖一致性方面创建约束,巧妙地解决了这一问题。该空间模型被表述为 MRF 模型。通过首先训练一个部分检测器并重用产生的热图输出来训练空间模型,我们能够训练一个 MRF,它将在图模型中制定联合依赖关系。最后,在统一模型(Part-Detector + Spatial Model)上进行微调和反向传播。 使用卷积网络的高效对象定位基于前面提到的工作,这项研究实现了一个多分辨率的 ConvNet 来估计图像小区域内的联合偏移位置。下面,我们找到了架构,并且很容易看到与之前讨论的架构的相似之处。此外,还添加了一个 Spatial Dropout 层。发现由于特征图中的强空间相关性,应用标准 dropout 并不能防止过度拟合。解决方案是删除整个一维特征图,以促进特征图之间的独立性。与之前类似,(粗)热图被传递到 MRF,它将过滤掉解剖学上不可行的姿势。下一步是恢复由于池化而丢失的空间信息。这是通过使用另一个 ConvNet 来细化粗热图的结果来实现的。 卷积姿势机器卷积姿势机器 (Convolutional Pose Machines--CPM) 继承并建立在姿势机器 (PM) 架构上,该架构将来自身体部位和不同尺度的丰富空间交互整合到一个模块化和顺序框架中。正如我们将看到的,CPM 通过利用卷积架构进一步提升 PM,该架构学习图像和空间上下文的特征表示。正如我们在下面看到的,PM 是一种顺序预测算法,它模拟消息传递的机制来预测每个身体部位的置信度。基本原理是每个身体部位的估计置信度在每个阶段迭代提高。消息传递可以理解为一个概率分类序列,其中预测器(无论是哪种类型的多类分类器)的输出成为下一个预测器的输入。Architecture of a 1 Stage Pose Machine (a) and a 2 Stage Pose Machine (b)在每个阶段,分类器根据先前分类器的输出和图像的特征,为每个身体部位以置信度预测位置。然后,对于每个阶段,都会对预测进行细化。最后,我们可以观察到,对于每个图像,通过以不同的比例重用图像来创建分层表示。在第 1 级,如图像中所见,对整个模型进行粗略表示,而第 2 级表示身体部位的组成,最后第 3 级,即最精细的表示,由关键点周围的区域构成。在所有层次结构级别上训练每个阶段的单个多类预测器。这意味着训练每个预测器以从特征向量输出每个关键点的一组置信度,特征向量可以来自任何层次级别。在 (a) 行下方,我们可以观察如何通过连接位置 z 中的置信度分数来构建每个身体部位的置信度之间的空间相关性,从而生成矢量化补丁。为了获得远程交互,应用非极大值抑制来获得每个关键点/身体部位的峰值列表(高置信度位置),从中可以计算极坐标中的偏移量。用 CNN 替换预测和特征提取部分,然后产生我们的 CPM,一个端到端的架构。Architecture of the Pose Machine (a &amp; b) and Convolutional Pose Machine (c &amp; d) 该架构的第一阶段基于输入图像从不断增长的感受野中创建特征图。随后的阶段将使用输入图像和前一阶段的特征图来细化每个身体部位的预测。使用中间损失层可以防止梯度在训练过程中消失。正如论文中所述,后续预测器可以利用先前的特征图作为某些部分应该在哪里的强线索,从而有助于消除错误的估计。通过逐渐增加感受野,该模型可以学习在特征图中结合上下文信息,从而使其能够学习身体部位的复杂关系,而无需对任何表示人体的图形模型进行建模。 堆积沙漏网络由于需要在每个尺度上捕获信息,因此开发了一种新颖的 CNN 架构,其中处理所有尺度的特征以捕获人体的空间关系。局部信息对于识别身体部位是必要的,而解剖学理解在不同的尺度上得到更好的识别。 Architecture of an hourglass module 在上图中,我们可以立即看出自下而上和自上而下处理的对称分区。这种类型的架构之前讨论过语义分割,只是它被称为 conv-deconv 或encoder-decoder架构。通常,一组卷积和最大池化层处理输入特征。在每个最大池化层之后,我们将网络分支出来,并将另一组卷积和最大池化层应用于原始特征输入。在上图中,每个块由一组卷积和最大池化层组成。 conv层的精确配置非常灵活。从 ResNets 的成功来看,作者最终在每个块中实现了一个残差模块。一旦达到最低分辨率,就会启动解码器或自上而下的方法,其中网络有效地组合了不同尺度的特征。最后,在图像中不可见,应用两个 1x1 卷积来生成一组热图,其中每个热图预测关键点存在的概率。通过创建一系列沙漏模块,其中一个的输出馈送另一个的输入,获得了一种重新评估特征和高阶空间关系的机制。与以前类似,它提供了具有中间损失函数的关键。按原样,损失(或监督)只能在上采样阶段之后提供。因此,无法在更大的全局环境中重新评估这些特征。这意味着,如果我们希望网络改进预测,这些预测不仅必须是局部尺度的,而且必须具有更大的尺度,以使预测能够在更大的图像上下文中相关联。下面,我们可以观察到提出的解决方案:在生成的热图上应用损失的中间监督过程概述(蓝色) 生成中间热图,对其应用损失,然后使用 1x1 conv 将这些热图重新映射到特征,并将它们与先前沙漏模块输出的特征相结合。训练是在 8 个沙漏模块的序列上完成的,其中彼此不共享权重。在热图上使用均方损失,每个模块使用相同的损失函数和基本事实。 OpenPoseOpenPose 也是第一个用于实时关键点检测的开源库,是一种改进的 CMUPose。在 CMUPose 中,提出了第一个使用部件亲和域 (Part Affinity Fields--PAF) 的自底向上姿态估计器。给定输入图像,表示关键点出现在每个像素上的概率的热图是,并生成部分亲和力的向量场。 两者都是由下面观察到的 2 分支多级 CNN 产生的。输入图像通过微调 VGG 的前 10 层,从中生成特征图 F。然后将此特征图 F 用作每个分支的第一阶段的输入。分支 1(顶部分支)预测关键点的置信度图,而分支 2 预测部分亲和力字段。通过连接来自两个分支的先前预测和特征图 F 来细化置信度图和亲和力字段。在阶段结束时,L2 损失应用于估计和真实值之间。正如之前经常看到的,置信度图是 2D 热图,表达了关键点存在于给定像素的信念。部分亲和字段是二维矢量字段,它编码从肢体的一个部分到另一部分的方向。这种特征表示的优势在于它保留了肢体支撑区域的位置和方向信息。执行非最大抑制,我们获得一组候选身体部位位置。然后可以将其中的每一个分配给几个人。使用线积分计算来量化沿曲线的场对亲和场的影响,身体部位与人类相匹配。在 CMUPose 工作的基础上,OpenPose 仅利用 PAF 进行姿势估计任务,从而消除了身体部位的置信度。下面,我们可以观察到 PAF 首先被编码,它代表了部分到部分的关联,然后被输入到 CNN 中以推断检测置信度图。Architecture of multi-stage OpenPose通过用 3 个连续的 3x3 内核替换 7x7 卷积层来增加网络深度,这些内核输出连接。在计算方面,处理减半,因为不再需要在每个阶段细化 PAF 和置信度图。相反,首先将 PAF 细化并传递到下一阶段,然后再细化置信度图。如果处理 PAF,则可以推断出身体部位的位置,但反之则不然。 (Higher)HRNet讨论了一种新颖的架构,其中从高到低分辨率的子网络是并行连接的,而不是像大多数现有解决方案那样串联连接,这些解决方案保持高分辨率表示。HRNet architecture 通过跨子网络的多尺度融合获得丰富的高分辨率特征,使得每个从高到低的分辨率表示从其他并行表示接收信息。 下采样通过使用跨步卷积发生,而上采样通过 1x1 卷积和最近邻上采样发生。热图从主要的高分辨率分支回归。基于这项初步工作,Higher HRNet 解决了 2 个主要挑战:如何在不牺牲大人物的推理性能的情况下提高小人物的推理性能?如何生成高分辨率热图用于小人物的关键点检测? 使用 HRNet 作为主干,HigherHRNet(下图)添加了一个反卷积模块,其中热图是从更高分辨率的特征图预测的。stem是 2 个 3x3 conv 层的序列,将分辨率降低四分之一,然后输入通过 HRNet 主干。4x4 反卷积层,然后是 BatchNorm 和 ReLU,将特征和预测热图作为输入,并生成两倍于输入大小的特征图。在 de-conv 层之后添加残差块 (4) 以细化高分辨率特征图。最后,通过使用双线性插值对低分辨率特征图进行上采样来聚合特征金字塔的热图,并通过对所有热图求平均值来获得最终预测。 PifPafPifPaf 的开发目标是估计城市环境中拥挤人群中的人体姿势,使其适用于自动驾驶汽车、送货机器人等。下面,我们观察到 ResNet 主干与 2 个头一起使用:部分强度场 (Part Intensity Field--PIF) 预测关键点的位置、大小及其置信度,而部分关联场 (Part Association Field--PAF) 预测关键点之间的关联。PifPaf Architecture 更具体地说,PIF 输出一个置信度,一个向量分量指向最近的关键点,具有扩展因子和比例。如下所示,置信度图非常粗糙。因此,通过将其与生成更高分辨率置信图的矢量场融合,可以改进该置信图的定位。然后可以从这个领域学习关节的尺度或空间范围。这种规模和上述传播有助于提高不同体型的人的姿势估计性能。Left: confidence map, Middle: vector field, Right: fused confidence map 通过尝试连接一对关键点关联,使用 PAF 将关节位置自下而上连接成姿势。这 19 个关联的示例是:左脚踝到左膝左臀到右臀鼻子到右眼AF 关联左肩和左髋 对于给定的特征图,在每个位置,关键点关联的两个向量的原点被 PAF 预测为置信度(左上图)。高于 0.5 的关联置信度显示在右侧。 最后,解码器获取两个字段(PIF 和 PAF)并将它们转换为一组代表人体骨骼的坐标 (17)。贪心算法通过降低置信度来创建所有关键点类型的优先级队列。这些点用作候选(种子),它们从队列中弹出,并在 PAF 字段的帮助下添加到其他关节的连接。由于当前和下一个关键点之间可能发生双重连接,因此对 PAS 关联进行评分。最后,对每个关键点类型应用非最大抑制来生成人体骨骼。 DirectPose提出了第一个多人姿势估计器,其中关键点注释用于端到端的训练,而对于推理,该模型能够将输入映射到每个单独实例的关键点,而无需进行任何框检测。基于无锚物体检测的出现,它立即回归目标边界框的两个角,研究人员解决了这种检测技术是否可用于检测关键点的问题。基本原理是检测任务可以重新表述为具有 2 个以上角点的特殊边界框。他们证明它表现不佳,主要是因为只有一个特征向量用于回归所有关键点。他们通过使用一个用于关键点检测的输出分支扩展全卷积单阶段对象检测 (FCOS) 架构来解决这一挑战。FCOS Architecture FCOS 以每像素的方式重新制定目标检测任务。与语义分割类似,FCOS 将输入图像上的像素视为训练样本,而不是基于锚的检测器中的锚框。落入边界框基本事实的像素被认为是积极的,并获得以下内容:基本事实的类标签一个 4D 向量,表示从该位置到边界框四个边的距离,用作该位置的回归目标 使用特征金字塔网络 (FPN) 可确保对不同尺度的对象大小具有更好的鲁棒性。由主干 (ResNet50) 生成的特征图后跟 1x1 卷积。特征层P3、P4、P5、P6和P7的步长分别为8、16、32、64和128。除P6和P7外,各自的横向连接和自上而下的路径通过加法合并。多级预测还处理两个不同大小的不同边界框相互重叠的可能性。FCOS 使用以下阈值限制不同特征图级别的回归:0、64、128、256、512 和所有特征级别(P3 到 P7)的无穷大。这些阈值表示特征级别 Pn 需要回归的最大距离。如果仍然出现重叠的边界框,则选择最小的边界框。由于不同的特征级别回归不同的尺寸范围,因此需要不同的头部。最后,由于远离对象中心的许多低质量的预测边界框,作者引入了中心度的概念。这个头部根据边界框的 4 个边的位置预测归一化距离。DirectPose 将关键点视为具有 K 个角点的非常特殊的边界框。然而,在他们的实验中,由于特征与预测的关键点之间缺乏对齐,观察到了较差的性能。这是因为许多关键点远离特征向量的感受野中心。随着输入信号越来越偏离感受野的中心,特征对该输入的响应强度逐渐衰减。因此,提出了关键点对齐模块(Keypoint Align Module--KPAM)。以一个 256 通道的特征图作为输入,KPAM 会密集地滑动这个特征图。定位器,顾名思义,定位特征向量预测关键点实例的位置的索引,特征样本从中采样长度为 256 的特征向量。对于第 n 个关键点,第 n 个卷积层将作为输入第 n 个特征向量并将预测相对于采样特征向量位置的坐标。通过对来自 Locator 和来自 KPAlign 的 K 个偏移量求和,我们获得需要重新缩放以匹配原始特征图的坐标。最后使用了一个小的调整,其中始终存在于一个区域(鼻子、眼睛和耳朵)中的关键点被分组并使用相同的特征向量。最后,我们可以看到 KPAM 如何替换前面提到的 FCOS 架构的边界框模块。我们确实观察到一个额外的热图分支,它被用作辅助任务/损失,使基于回归的任务更加可行。 DirectPose Architecture Conclusion显然,估计姿态的任务是相当大的挑战。 一再证明自下而上的方法优于自上而下的方法,但需要将关键点与人相关联。 这种生成最终实例感知关键点的分组或组装过程可以使用启发式、人体骨骼建模(图形结构)和/或堆叠置信度图来完成。此外,当认为未知数量的人可以出现在图像上的任何地方和任何比例时,复杂性就会爆炸。人机交互、关节和当然遮挡使关键点组装过程变得复杂。姿势估计在人机交互、动作识别、监视、图片理解、威胁预测、机器人、AR 和 VR、动画和游戏等领域有重要应用。
在之前写的一篇计算机视觉入门路线文章中,我推荐大家在不用任何框架、只使用numpy这种包的情况下,从零实现一个卷积神经网络。其中一个很重要的因素就是在这个过程中大家会了解到卷积过程在底层中是如何优化实现的,其主流的方法就是GEMM。这篇博客比较细致地介绍了什么是GEMM,以及它的优缺点。我大部分时间都在考虑如何让神经网络的深度学习更快、更高效。在实践中,这意味着要关注一个名为GEMM的函数。它是1979年首次创建的BLAS(基本线性代数子程序)库的一部分,直到我开始尝试优化神经网络之前,我从未听说过它。为了解释为什么它如此重要,这是我朋友杨庆嘉论文的图表所有以fc(完全连接)或卷积)开头的层都使用GEMM实现,几乎所有时间(95%的GPU版本,89%的CPU)都花在这些层上。那么什么是GEMM呢?它代表全局矩阵到矩阵的乘法,它本质上完全按照它在tins上所说的那样,将两个输入矩阵乘法在一起,得到一个输出矩阵。它和我在三维图形世界中使用的矩阵操作类型之间的区别在于,它所工作的矩阵通常非常大。例如,典型网络中的单个层可能需要将256行、1152列矩阵乘以1152行、192列矩阵,以产生256行、192列的结果。天真地说,这需要5700万层(256x1152x192)浮点操作,在现代架构中可以有几十个这样的层,所以我经常看到网络需要几十亿FLOP来计算一帧。下面是我绘制的一个关系图,以帮助我可视化它的工作原理:完全连接的图层全连接层是已经存在了几十年的经典神经网络,并且可以最简单地从如何使用GEMM开始。FC层的每个输出值查看输入层中的每个值,将它们全部乘以该输入索引的相应权重,并将结果相加以得到其输出。根据上图的说明,具体情况如下:欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。有“k”输入值,也有“n”神经元,每个输入值都有自己的学习权值集。有“n”输出值,每个神经元都有一个输出值,通过做其权值和输入值的点积来计算。 卷积图层使用GEMM进行卷积层并不是一个明显的选择。卷积层将其输入视为二维图像,每个像素都有多个通道,很像具有宽度、高度和深度的经典图像。与我习惯处理的图像不同,通道的数量可以是数百个,而不仅仅是RGB或RGBA!卷积操作通过获取一些“核”的权重来产生其输出。并在图像中应用它们。下面是输入映像和单个内核的外观: 每个卷积核都是一个三维数组,其深度与输入图像相同,但其宽度和高度要小得多,通常就像7×7一样。为了产生结果,卷积核将应用到输入图像的点网格。在应用它的每个点,所有相应的输入值和权重相乘,然后在该点相加产生单个输出值。以下是视觉效果:您可以将这个操作看作是一个边缘检测器。卷积核包含一个权重模式,并且当它所查看的输入图像的部分具有类似的模式时,它会输出一个高值。当输入与模式不匹配时,结果是该位置的数较低。以下是一些典型的模式,它们是由网络的第一层学习的。因为第一层的输入是RGB图像,所有这些核也可以可视化为RGB,它们显示网络正在寻找的原始模式。这些96个内核中的每一个在输入上以网格模式应用,结果是一系列96个二维数组,它们被视为具有96个通道深度的输出图像。如果您习惯了像Sobel操作符这样的图像处理操作,您可以想象它们都有点像针对图像中不同重要模式优化的边缘检测器,因此每个通道都是输入中这些模式发生位置的映射。您可能已经注意到,我对内核应用的网格类型很模糊。它的关键控制因素是一个称为“步幅”的参数,它定义了内核应用程序之间的间距。例如,随着步幅为1,256×256输入图像将在每个像素处应用一个内核,并且输出将是与输入图像相同的宽度和高度。只要迈出4步,相同的输入图像就只能每四个像素应用一次内核,因此输出将只有64×64。典型的步幅值小于内核的大小,这意味着在可视化内核应用程序的图表中,其中很多值实际上会在边缘重叠。 GEMM如何处理卷积解决方案这似乎是一个相当专门的操作。它涉及大量的乘法和求和,比如完全连接层,但不清楚如何或为什么要将其变成GEMM的矩阵乘法。最后我将讨论动机,但这个操作是如何用矩阵乘法表示的第一步是将来自一个实际上是一个三维数组的图像的输入变成一个二维数组,我们可以像一个矩阵一样处理。应用每个内核的地方是图像中的一个小三维多维方体,因此我们提取每个输入值的多维方体,并将它们作为一列复制到一个矩阵中。这被称为im2col,对于图像到列,我相信从一个原始的Matlab函数,下面是我如何可视化它的:如果步幅小于内核大小,你可能会对我们进行此转换时发生的内存大小的扩展感到震惊。这意味着包含在重叠核站点中的像素将在矩阵中被复制,这看起来效率低下,但实际上利大于弊。现在您有了矩阵形式的输入图像,您可以对每个内核的权重执行同样的操作,将三维多维数据集序列化为行,作为乘法的第二个矩阵。以下是最终的GEMM的外观:这里的“k”是每个补丁和内核中的值数,因此它是内核宽度*内核高度”深度。结果矩阵是“patches数”列高,按“kernels数”行宽计算。这个矩阵实际上被随后的操作视为一个三维数组,以内核维数作为深度,然后根据它们在输入图像中的原始位置将斑块分割回行和列。 为什么GEMM适用于卷积希望您现在可以看到如何将卷积层表示为矩阵乘法,但仍然不清楚您为什么要这么做。简而言之,答案是,科学程序员的格式世界已经花了几十年时间优化代码来执行大矩阵到矩阵乘法,而非常规则的内存访问模式的好处超过了浪费的存储成本。这篇来自Nvidia的论文(文末附下载方式)很好地介绍了一些不同的方法,但他们也描述了为什么他们最终以一个修改版本的GEMM作为他们最喜欢的方法。同时对相同的内核批处理大量输入图像还有很多优点,本文关于《Caffe con troll》的论文(文末附下载方式)使用了非常好的效果。GEMM方法的主要竞争对手是使用傅里叶变换在频率空间中进行操作,但在卷积中使用步进使其难以如此有效。好消息是,拥有一个单一的、被充分理解的算法(即GEMM)占据了我们的大部分时间,这为优化速度和功率的使用提供了一条非常清晰的路径,无论是通过更好的软件实现,还是通过定制硬件来很好地运行操作。因为深度网络已被证明对跨语音、NLP和计算机视觉的大量应用程序有用,所以我期待看到未来几年的巨大改进,就像对3D游戏的广泛需求通过迫使顶点和像素处理操作的革命,推动了GPU的革命。
目标检测的预测框经过了滑动窗口、selective search、RPN、anchor based等一系列生成方法的发展,到18年开始,开始流行anchor free系列,CornerNet算不上第一篇anchor free的论文,但anchor freee的流行却是从CornerNet开始的,其中体现的一些思想仍值得学习。看过公众号以往论文解读文章的读者应该能感觉到,以往论文解读中会有不少我自己的话来表述,文章写得也很简练。但这篇论文的写作实在很好,以至于这篇解读文章几乎就是对论文的翻译,几乎没有改动。论文提出了 CornerNet,这是一种新的目标检测方法,我们使用单个卷积神经网络将对象边界框检测为一对关键点,即左上角和右下角。 通过将对象检测为成对的关键点,我们无需设计一组在先前单级检测器中常用的锚框。 除了我们的新范式外,我们还引入了corner pooling,这是一种新型的池化层,可帮助网络更好地定位角点。实验表明,CornerNet 在 MS COCO 上实现了 42.1% 的 AP,优于所有现有的单级检测器。 论文出发点|anchor box的缺陷 目标检测中SOTA模型中一个常见组成部分是锚框,它是各种大小和纵横比的框,用作检测候选框。Anchor box广泛应用于one-stage检测器中,可以在效率更高的情况下获得与two-stages检测器极具竞争力的结果。one-stage检测器将锚框密集地放置在图像上,并通过对锚框进行评分并通过回归细化其坐标来生成最终的框预测。 但是使用锚框有两个缺点。 首先,我们通常需要一组非常大的锚框,例如 在 DSSD 中超过 40k,在 RetinaNet 中超过 100k。这是因为检测器被训练来对每个锚框是否与一个ground truth框充分重叠进行分类,并且需要大量的anchor box来确保与大多数ground truth框有足够的重叠。结果,只有一小部分锚框会与ground truth重叠; 这会造成正负锚框之间的巨大不平衡并减慢训练速度。 其次,锚框的使用引入了许多超参数和设计选择。 这些包括多少个box、多大scale和多大aspect ratios。 这种选择主要是通过临时启发式进行的,当与多尺度体系结构相结合时会变得更加复杂,多尺度体系即单个网络在多个分辨率下进行单独预测,每个尺度使用不同的特征和自己的一组锚框。 methods受到 Newell 等人提出的关联嵌入方法的启发。谁在多人人体姿势估计的背景下检测和分组关键点。论文提出了 CornerNet,这是一种新的one-stage目标检测方法,无需锚框。 欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。我们将一个对象检测为一对关键点——边界框的左上角和右下角。我们使用单个卷积网络来预测同一对象类别的所有实例的左上角的热图、所有右下角的热图以及每个检测到的角的嵌入向量。嵌入用于对属于同一对象的一对角进行分组——网络经过训练以预测它们的相似嵌入。 这种方法极大地简化了网络的输出并消除了设计锚框的需要。 下图说明了方法的整体流程CornerNet 的另一个新颖组件是corner pooling,这是一种新型的池化层,可帮助卷积网络更好地定位边界框的角点。 边界框的角通常在对象之外——考虑圆形的情况以及下面图(中)示例。在这种情况下,不能基于局部证据来定位角。 相反,要确定像素位置是否有左上角,我们需要水平向右看对象的最上边界,垂直向下看最左边界。 基于这一点,我们提出了corner pooling。 它输入两个特征图; 在每个像素位置,它最大池化第一个特征图右侧的所有特征向量,最大池化第二个特征图正下方的所有特征向量,然后将两个合并的结果加在一起。我们假设检测角点比边界框中心或提案更有效的两个原因。 首先,一个box的中心可能更难定位,因为它取决于目标的所有 4 个边,而定位一个角取决于 2 个边,因此更容易,对于corner pooling更是如此,它编码了一些关于角的定义的明确的先验知识。 其次,角提供了一种更有效的方法来密集离散框的空间:我们只需要 O(wh) 个角来表示 O(wh)^2 个可能的锚框。 一些细节整体实现在 CornerNet 中,我们将一个对象检测为一对关键点——边界框的左上角和右下角。卷积网络预测两组热图来表示不同对象类别的角的位置,一组用于左上角,另一组用于右下角。每组热图都有C个通道,C为类别数量(不含背景),每个通道是关于一个类别角点位置的二进制掩码。 该网络还为每个检测到的角点预测一个嵌入向量,使得来自同一对象的两个角点的嵌入之间的距离很小。 为了产生更紧密的边界框,网络还预测偏移量以稍微调整角的位置。 使用预测的热图、嵌入和偏移量,我们应用一个简单的后处理算法来获得最终的边界框。使用沙漏网络作为 CornerNet 的骨干网络。沙漏网络之后是两个预测模块。一个模块用于左上角,而另一个用于右下角。每个模块都有自己的corner pooling模块,用于在预测热图、嵌入和偏移之前从沙漏网络中池化特征。 与许多其他目标检测器不同,我们不使用不同尺度的特征来检测不同尺寸的物体。 我们只将这两个模块应用于沙漏网络的输出。 对于每个Corner,有一个ground truth正位置,所有其他位置都是负位置。 在训练期间,我们不是对负位置进行同等惩罚,而是减少对正位置半径内的负位置的惩罚。 这是因为一对错误的角点检测,如果它们靠近各自的ground truth位置,仍然可以产生一个与ground truth框充分重叠的框。 我们通过对象的大小来确定半径,方法是确保半径内的一对点将生成一个具有至少 t IoU 的边界框,并带有ground truth标注。 Corners分组使用“pull”损失训练网络对角点进行分组,使用“push”损失来分离角点:Corner Pooling预测模块沙漏网络CornerNet 使用沙漏网络作为其骨干网络。沙漏网络首先被引入用于人体姿势估计任务。它是一个完全卷积的神经网络,由一个或多个沙漏模块组成。沙漏模块首先通过一系列卷积和最大池化层对输入特征进行下采样。然后通过一系列上采样和卷积层将特征上采样回原始分辨率。由于最大池化层中的细节丢失,因此添加了跳过层以将细节带回上采样特征。沙漏模块在单个统一结构中捕获全局和局部特征。当多个沙漏模块堆叠在网络中时,沙漏模块可以重新处理特征以捕获更高级别的信息。这些特性也使沙漏网络成为目标检测的理想选择。事实上,目前很多检测器已经采用了类似于沙漏网络的网络。 结论实验表明,CornerNet 在 MS COCO 上实现了 42.1% 的 AP,优于所有现有的单级检测器。
早期方法 (2015-2019)自2015年以来,人们提出了各种深度学习中的目标检测方法,给该领域带来了巨大的影响。这些方法主要分为一阶段方法和两阶段方法两类。其一般处理过程包括:1.使用CNN主干提取深度特征图2.为特征映射的每个像素生成各种锚点3.计算锚点和ground truth之间的IoU,选择其中的一部分进行训练4.使用回归(IoU和L1)和分类(框内的对象类)的loss对模型进行训练5.使用非极大值抑制(NMS)对推理结果进行过程后处理,以删除重复的预测框在上述一般过程中,one-stage和two-stages方法的唯一区别是在训练过程中是否为region proposal动态标记anchors。例如,在Faster-RCNN中,根据锚和ground truth之间的IoU给anchors作正或负的标记。如果IoU足够大,比如0.7,锚给正标签,否则如果IoU足够小,比如0.3,给出负标签。因此,在推理期间,只将正锚定用于目标检测处理。这种技术在原论文中被称为区域建议网络(RPN)。在像SSD、YOLO和RetinaNet这样的one-stage方法中,不存在RPN,以便在推理过程中处理所有的锚点。分类置信度的阈值用于过滤大多数锚,而只有具有高分类可能性的锚被保留用于最终的后处理。在训练过程中,锚的数量非常巨大。在two-stages的方法中,RPN帮助集中关注正锚点,这节省了计算时间和资源。然而,RPN是复杂的,训练它也需要时间和资源。在one-stage的方法中,尽管必须处理所有的锚点,但总的计算时间仍然更小。由于two-stages方法的复杂性和速度较低,人们倾向于开发出更容易实现、更有效的新的one-stage方法。什么是NMS以及为什么需要它在上述早期的方法中,锚被用来与ground truth相匹配。因此,可能会发生多对一的匹配:几个锚与一个ground truth相匹配。如上所述,在一阶段和两阶段的方法中,几种不同的锚可能与同一个ground truth有较大的IoU。在推理过程中,它们也可以回归到具有高分类置信度的同一对象。因此,删除重复anchor,NMS后处理是必要的。NMS处理过程:1.预测的anchors根据分类置信度进行排序2.选择最大置信度的anchor3.删除所有与所选anchor的IoU大于预定义阈值的其它anchor4.从1开始重复,直到不存在anchors在推理结果中,许多与许多目标对应的anchor被混合在一起。一旦以置信度进行排序,可能会发生以下情况:其中为两个对象A和B预测三个anchor。三个anchor的编号为1、2、3,分类置信度分别为0.8、0.75、0.7。在这里,为同一对象A预测两个anchor,因此应该移除一个具有较低可信度的anchor。在这种情况下,去除anchor 2,anchor 1和3用于最终预测。为什么会发生这种情况?回想一下训练过程中的多对一匹配:anchor 1和2同时与对象A匹配,计算损失并反向传播梯度,告诉模型anchor 1和2都是对象A的有效候选对象。然后这个模型只是预测它被训练成什么。因此,如果我们将多对一修改为一对一,并且在训练过程中只使用一个anchor来匹配一个ground truth,推理结果会有所不同吗?回想一下,在多对一范式中,对于一个对象,会选择具有大IoU的anchor来与它进行匹配。想想一对一的范式,其中只选择IoU最高的anchor进行匹配,而所有其他anchors都是负的,并与背景匹配。我们是否可以得到一个模型,它能够以一对一的匹配风格直接预测所有对象的所有anchor,而不需要NMS后处理? 最近的新方法(2019-2020)幸运的是,上述问题的答案是肯定的。最近,人们一直在开发新的one-stage方法,使目标检测比以前更容易。主要思想有两方面: 1.不要使用anchor,而使用每像素预测2.不要使用NMS后处理,改为使用一对一的训练人们不会使用根据空间比例和对象大小而变化的anchors,而是倾向于通过使用语义分割等每像素的预测来降低复杂性。一种典型的方法是FCOS,其中最终特征图中的每个像素都用一个对象框进行预测,使其成为一个完全卷积网络(FCN)。用于目标检测的FCN不仅简化了任务本身,而且还将其与语义分割、关键点检测等其他FCN任务结合起来,用于多任务的应用。我们可以看到,对于ground truth框内的每个像素,都可以分配一个标签:(l、r、t、b),表示ground truth框向左、右、上、下边界的像素之间的距离。因此,训练仍然是多对一的,NMS后处理仍然需要得到最终的预测结果。虽然FCOS简化了目标检测并性能良好,但它仍然不是端到端的。为了使目标检测任务端到端,人们必须有不同的思考。自2020年以来,随着transformer的普及,人们倾向于用Vision Transformer进行目标检测,结果也很好。一个典型的方法是DETR,本文将不会讨论它。我将在这里讨论的是另一个并行的工作:OneNet,它将FCOS扩展为用于目标检测的端到端FCN。如上所述,为什么NMS是必要的主要原因是在训练中使用了多对一范式。为了使它端到端没有NMS,应该使用一对一的训练范式来代替。回想一下,在早期的方法中,预测和ground truth是匹配的,它们之间只有几何损失(IoU和L1)用于反向传播。因此,为了增加训练数据的方差,需要多对一匹配,因为可以找到许多几何损失相似的候选对象,并匹配相应的ground truth。这个候选框并不是唯一的。另一方面,如果我们坚持使用几何损失最低的候选模型进行一对一匹配,该模型可能会过拟合,并且根本不具备很好的泛化能力。OneNet的作者认识到了这个问题,并使用了两种损失:几何损失和分类损失,以将候选框与ground truth相匹配。与几何损失不同,分类损失对相应的ground truth是唯一的。例如,在目标的高级深度特征图中,我们可以找到一个最能表示目标类的唯一像素。虽然许多像素的几何损失与相应的ground truth相似的几何损失,但最佳分类损失的像素是唯一的。因此,我们可以将这两种损失结合起来,得到训练中唯一一个综合损失最低的候选框。如原论文所述,只有具有最小损失的候选框才能匹配相应的目标,其他目标都是负的,并与背景匹配。 预测结果比较多对一的结果一对一的结果第一行是早期多对一模型的预测结果,而第二行是一对一模型(OneNet)的预测结果。我们可以清楚地看到,许多冗余的预测框存在于多个一对一的结果中,而它们则在一对一的结果中消失。讨论利用一对一的训练范式,OneNet首先实现了端到端的目标检测。这一进展被认为是对损失和模型优化的深刻理解,这也有助于提高深度学习的可解释性。
问题定义图1是一个边界检测的例子,顾名思义,边界检测是从图像中检测对象边界的任务。这是一个不适定的问题,因为问题设置本身存在歧义。如图所示,对于室内房间图像(左),ground truth(中)定义房间内的ground truth对象边界,并且预测(右)估计房间的对象边界。然而,我们可以看到,估计的边界远不止是ground truth,包括来自房间布局、窗帘,甚至沙发纹理的不必要的边界线。提取干净且有意义的对象边界并不容易。 原始方法边界检测的一个直接解决方案是将其视为语义分割问题。在标注中简单地将边界为1和其他区域标记为0,我们可以将其表示为一个二分类语义分割问题,以二值交叉熵损失为损失函数。然而,它有两个原因:高度不平衡的标签分布和每像素交叉熵损失的内在问题。 Cross Entropy Loss的局限性当使用交叉熵损失时,标签的统计分布对训练精度起着很重要的作用。标签分布越不平衡,训练就越困难。虽然加权交叉熵损失可以减轻难度,但改进并不显著,交叉熵损失的内在问题也没有得到解决。在交叉熵损失中,损失按每像素损失的平均值计算,每像素损失按离散值计算,而不知道其相邻像素是否为边界。因此,交叉熵损失只考虑微观意义上的损失,而不是全局考虑,这还不足以预测图像水平。如图2所示。对于输入图像(左),比较了交叉熵损失(中)和加权交叉熵损失(右)的预测。右边的边界比中间的要好得多,但预测的边界并不干净,肮脏的草地纹理边界仍然存在。Dice LossDice Loss起源于Sørensen-Dice系数,这是20世纪40年代用来测量两个样本之间的相似性的统计数据。它是由米勒塔里等人带到计算机视觉的。2016年进行三维医学图像分割。图3 骰子系数上式显示了骰子系数方程,其中pi和gi分别表示对应的像素预测值和ground truth。在边界检测场景中,pi和gi的值为0或1,表示像素是否为边界,是的时候值为1,否则值为0。因此,分母是预测和ground truth的总边界像素的和,数值是正确预测的边界像素的和,因为只有当pi和gi值匹配时(两个值1)才递增。图4是图3的另一个视图。从集理论的角度来看,其中骰子系数(DSC)是两个集合之间重叠的度量。例如,如果两组A和B完全重叠,DSC的最大值为1。否则,DSC开始减少,如果两个组完全不重叠,则最小值为0。因此,DSC的范围在0到1之间,越大越好。因此,我们可以使用1-DSC作为骰子损失来最大化两组之间的重叠。在边界检测任务中,ground truth边界像素和预测的边界像素可以被视为两个集合。通过利用Dice Loss,这两组被训练一点地重叠。如图4所示。分母考虑全局尺度上的边界像素的总数,而数值考虑局部尺度上的两个集合之间的重叠。因此,Dice Loss在本地和全局上都考虑了损失信息,这对于高精度至关重要。 结果如图5所示。使用Dice Loss(c列)的预测结果比其他方法(d、e列)具有更高的精度。特别是对于薄边界,因为只有当预测的边界像素与ground truth薄边界重叠,并且在其他区域没有预测的边界像素时,才可以减少Dice Loss。
卷积神经网络(CNN)在计算机视觉领域取得了显著的成功,几乎成为所有计算机视觉任务中的一种通用和主导方法。受transformer在自然语言处理(NLP)的成功,许多研究人员正试图探索transformer的应用,一些工作模型视觉任务作为字典查找问题可学习查询,并使用transformer decoder作为一个特定任务的头的CNN主干,如VGG和ResNet。而一些现有技术已经将注意力模块纳入到CNNs中Introduction论文提出了一种使用transformer模型的无卷积主干网络,称为金字塔视觉transformer (Pyramid Vision Transformer--PVT),它可以在许多下游任务中作为一个通用的backbone,包括图像级预测和像素级密集预测。 不同架构的比较,其中“Conv”和“TF-E”分别表示卷积和transformer编码器。(a) 表明,许多CNN backbones都使用金字塔结构进行密集的预测任务,如目标检测(DET)、语义和实例分割(SEG)。(b) 表明,最近提出的视觉transformer(ViT)是一种专门为图像分类(CLS)设计的“柱状”结构。(c) 说明,通过结合CNNs的金字塔结构,我们提出了金字塔视觉transformer (PVT),它可以用作许多计算机视觉任务的通用backbone,扩大了ViT的范围和影响。此外,我们的实验还表明,PVT可以很容易地与DETR结合,建立一个端到端目标检测系统,没有卷积和人工设计的组件,如密集anchor和非极大值抑制(NMS)。 具体而言,如上图所示,与ViT不同,PVT通过以下方式克服了传统transformer的困难:(1)以细粒度的图像补丁(即每个补丁4×4)作为输入来学习高分辨率表示,这对密集的预测任务至关重要。(2)在网络深度增加时减少transformer的序列长度,显著降低计算消耗。(3)采用空间减少注意(SRA)层来进一步降低学习高分辨率特征图的资源成本。 Method与CNN骨干网络类似,所提出的方法有四个阶段来生成不同尺度的特征图。所有的阶段都共享一个类似的架构,它由一个补丁嵌入层和Li transformer编码器层组成。拟建金字塔视觉transformer (PVT)的整体结构。整个模型分为四个阶段,每个阶段由一个补丁嵌入层和一个Li-layer transformer encoder组成。遵循金字塔结构,四个阶段的输出分辨率逐渐从步长4缩小到步长32。在第一阶段,给定大小为H×W×3的输入图像,他们首先将其分为(HW)/4²补丁,每个补丁的大小为4×4×3。然后,他们将扁平的补丁输入到一个线性投影中,并得到大小为(HW)/4²×C1的嵌入补丁(embedded patches)。之后,嵌入块和位置嵌入通过L1层transformer encoder,输出reshape为特征图F1,尺寸为H/4×W/4×C1。同样,使用来自前一阶段的特征图作为输入,它们获得以下特征图F2、F3和F4,其相对于输入图像的步进为8、16和32像素。所提出的方法需要处理高分辨率特征图,论文提出了一个空间减少注意力(SRA)层来取代编码器中传统的多头注意(MHA)层。与MHA类似,SRA还接收一个查询Q、一个关键K和一个值V作为输入,并输出一个改进的特性。不同的是,我们的SRA将在注意力操作之前减少K和V的空间尺度,这在很大程度上降低了计算/内存开销。第一阶段SRA详情如下:这些是线性投影的参数。N_i是第一阶段transformer encoder的编号。因此,每个头的尺寸等于Ci/Ni。SR(·)是空间缩减操作,其定义为:利用特征金字塔{F1、F2、F3、F4},该方法可以很容易地应用于大多数下游任务,包括图像分类、目标检测和语义分割。 结论总的来说,所提出的PVT具有以下优点。首先,与传统的CNN backbones相比,感受野在深度增加时会增加,PVT总是产生一个全局感受野,比CNNs的局部感受野更适合于检测和分割。其次,与ViT相比,由于金字塔结构的进步,所提出的方法更容易插入到许多具有代表性的密集预测pipeline中。第三,利用PVT,我们可以结合PVT与其他为不同任务设计的transformer decoder,构建一个无卷积管道。实验提出了第一个端到端目标检测检测pipeline,PVT+DETR,它是完全无卷积的。它在2017年COCO上达到34.7,优于基于ResNet50的原始DETR。
MobileNet_v3成就 MobileNet_v3-Large在ImageNet分类中实现了比MobileNet_v2低百分之20的时延,同时高了3.2%精度。MobileNet_v3-small比MobileNet_v2在时延接近的情况下,高了6.6%的精度。 MobileNet_v3-Large在COCO数据集检测上,在比v2快了25%,而精度却几乎一样。 MobileNet_v3-Large LRASPP比MobileNet_v2 RASPP在Cityscapes分割数据集上快了34%。应该可以说是不负众望了。MobileNet_v3主要内容 MobileNet_v3着眼于赋予设备高效地实现高精度、低时延的能力,而不是发送数据到服务器,经过服务器推理后再发送回设备。为此,论文提出了以下内容:1) 补充搜索技术2) 适用于移动设备环境的非线性的新有效版本3) 新的高效网络设计4) 新的高效分割解码器(指的是语义分割) 01 NAS 和NetAdapt 当前,在人工智能界内有一句名言,所谓人工智能,是越人工越智能。现有的一些网络所具备的高效的性能离不开人对这个网络模型的精心设计与调参。最近几年,如何让计算机根据需求和现有硬件条件自动生成网络已经进入重要研究方向。 NAS-神经网络架构搜索。NAS通过设定一个空间,选择一个搜索策略在这个空间内搜索,采用一个评估策略对搜索效果进行评估,最终选择评估效果最好的结构。 在早期,NAS主要进行单元级搜索,使用一些预定义的操作集合,(如卷积、池化等)此外还包括,每一层的卷积核的大小,步长大小,卷积核数量等,这些集合构成了搜索空间,使用一个搜索策略来选择操作集合,在训练集训练这些操作集合组成的网络,并在验证集上评估当达到某个阈值将会停止。 最近,开始进行Block级的搜索,例如Inception结构,就是一个Block,在搜索空间中使用了不同大小,不同数量的block。搜索策略直接搜索这些block组成网络,进行测试。 MobileNet_v3使用了用于block搜索的平台感知NAS(PlatformAware NAS for Blockwise Search)构成全局网络结构,使用NetAdapt对网络结构进行layer-level的finetune。使用的搜索目标是 ACC(m) × [LAT(m) / TAR]^w, 这里ACC(m)是模型精度,LAT(m)是延时,TAR是目标延时,w是超参数。之所以不怎么介绍NAS,是因为NAS需要数以百计甚至上千的GPU来训练数月,例如 NASnet需要在 800 个 GPU 上训练 28 天,对于普通人来说实在有些遥不可及,当然最近也出了一些只需要少量GPU就能训练的算法,但至少现在还没到那种能满足普遍需求的时候。如有需要,公众号中有NAS的一篇较为全面的综述,可扫描文末二维码关注公众号。 网络改进 在搜索得到的网络的基础上,提出了一些改进的方案。在网络的开始和结束部分,重新设计了计算量大的层,此外还提出了一种新的非线性,即h-swish,h-swish是最近swish的改进版本,计算速度更快,也更易于量化。 02 Redesigning Expensive Layers 在MobileNet_v2的Inverted bottleneck结构中使用1x1卷积作为最后一层,用于扩大到一个更高维的特征空间,这一层对于提供丰富的特征用于预测极为重要,但也增加了延时。 为了降低延时,并保持高维空间特征,MobileNet_v3中把这一层移到了平均池化层的后面,在最后的特征集现在只需要计算1x1的分辨率,而不是原来的7x7。这种设计选择的结果是,在计算和延迟方面,特性的计算变得几乎是免费的。 一旦降低了该特性生成层的成本,就不再需要以前的瓶颈投影层来减少计算量。该观察允许删除前一个瓶颈层中的投影和过滤层,从而进一步降低计算复杂度。原始阶段和优化后的阶段如上图所示。 另一个耗时的层是第一层卷积,当前的移动模型倾向于在一个完整的3x3卷积中使用32个滤波器来构建初始滤波器库进行边缘检测。通常这些过滤器是彼此的镜像。mobileNet_v3对这一层使h-swish非线性激活函数,其好处是使用了h-swish后滤波器的数量可以减少到16,而同时能够保持与使用ReLU或swish的32个滤波器相同的精度。这节省了额外的3毫秒和1000万 MAdds。 03 Nonlinearities 在一篇语义分割的论文中提出了使用swish的非线性函数来代替ReLU函数,它可以显著提高神经网络的精度,其定义为:swish x = x · σ(x),这里σ(x)是sigmoid函数。然而swish虽然提高了精度,但sigmoid函数计算是极为昂贵的,在嵌入式移动端不适合它的存在,因此,MobileNet_V3提出了计算更为简便的h-swish函数,其定义如下: 如下图所示,这里的h是hard的意思,简单来说就是没那么平滑。经过实践检验,sigmoid和swish函数与其hard版本的函数在精度上并没有什么明显的差异。但是hard版本的计算却简便得多。 一般来说,在不同的设备上,对于sigmoid函数都是采用近似的方式实现,(例如,在高数中采用划分矩形求面积来近似实现积分,这里的近似是使用某些实现方式近似实现sigmoid函数),因此不同的实现方式会导致潜在的精度损失。而几乎所有的软件和硬件设备都可以直接实现ReLU6,因此这里的h-swish将不会出现sigmoid的情况,此外,h-swish可以通过分段函数实现(这是对h-swish的优化,下图是与没有优化的实验效果对比),这样会降低内存访问次数,从而大大降低了等待时间成本。 论文中提到,在较深的层次中使用更能发挥swish的大多数好处,因此在MobileNet_v3结构中 中高层才使用h-swish。此外,h-swish即便使用了这些优化,但仍然引入了一些延迟成本,如下图所示,使用分段函数作为h-swish的优化比没有优化效果明显要好,节约了6ms(大概百分之10), 而相比于ReLU, 优化后的h-swish仅引入了1ms的成本,但精度更高。04 Large squeeze-and-excite 先简要介绍一下这个模块,论文中并没有介绍。 这是一个注意力机制中的一个模块SE,即插即用,用于现有的一些网络。其分为两个部分,一个是Squeeze部分,通过AdaptiveAvgPool2d将前一层的空间分辨率压缩为1x1,第二个部分是Excitation部分,对压缩后的部分进行线性变化(Linear),再ReLU,再Linear(通道数要与下一层通道数一致),再Sigmoid。将得到的一个1x1x下一层通道数 的层与原先网络直接卷积得到的下一层逐像素相乘,这样的好处是有些特征会被突出,有些特征会被抑制,从而实现模型的自注意力机制。 回到本文,MobileNet_v3中加入了这个模块,但进行了一些调整。一个是将sigmoid换成了h-swish。第二个是将第二Linear的通道数变为下一层的1/4。但这样就没办法逐像素相乘了,因为通道数不匹配,然后我去找了MobileNet_v3的代码,发现它在通道数变为1/4后又使用expand_as将其扩展成了下一层的通道数。MobileNet_v3结构:实验结论 在本文开头已经介绍了MobileNet_v3的成就,记住那些即可,这里只给出图,就不多介绍了,无非就是不同的超参数下其精度和延时有何变化,在某某数据集中与某某模型对比。稍微看看足以
01Linear Bottlenecks 如上图所示,MobileNet_v2提出ReLU会破坏在低维空间的数据,而高维空间影响比较少。因此,在低维空间使用Linear activation代替ReLU。如下图所示,经过实验表明,在低维空间使用linear layer是相当有用的,因为它能避免非线性破坏太多信息。 此外,如果输出是流形的非零空间,则使用ReLU相当于是做了线性变换,将无法实现空间映射,因此MobileNet_v2使用ReLU6实现非零空间的非线性激活。 上方提出使用ReLU会破坏信息,这里提出ReLU6实现非零空间的非线性激活。看起来有些难以理解。这里提出我自己的理解。 根据流形学习的观点,认为我们所观察到的数据实际上是由一个低维流形映射到高维空间的。由于数据内部特征的限制,一些高维中的数据会产生维度上的冗余,实际上这些数据只要比较低的维度的维度就能唯一的表示。图像分布是在高维空间,神经网络中使用非线性激活函数实现将高维空间映射回低维流形空间。而这里提出使用ReLU6即增加了神经网络对非零空间的映射,否则,在非零空间使用ReLU相当于线性变换,无法映射回流形低维空间。而前文提出的使用线性激活函数来代替ReLU是在已经映射后的流形低维空间。 区别就是ReLU6是在将高维空间映射到流形低维空间时使用,Linear layer是在映射后的流形低维空间中使用。 其使用的如下表所示02 Inverted Residuals MobileNet_v1中的结构如下左图,MobileNet_v2如下右图。 MobileNet_v2是在2018年发表的,此时ResNet已经出来了,经过几年的广泛使用表明,shortcut connection和Bottlenck residual block是相当有用的。MobileNet_v2中加入了这两个结构。 但不同的是,ResNet中的bottleneck residual是沙漏形的,即在经过1x1卷积层时降维,而MobileNet_v2中是纺锤形的,在1x1卷积层是升维。这是因为MobileNet使用了Depth wise,参数量已经极少,如果使用降维,泛化能力将不足。 此外,在MobileNet_v2中没有使用池化来降维,而是使用了步长为2的卷积来实现降维,此外如上图所示,步长为2的block没有使用shortcut connection。这里的t是膨胀因子,取6。 Inverted residuals block 与ResNet中的residuals block对比如下图所示:图来源于网络 ResNet中residual block是两端大,中间小。而MobileNet_v2是中间大,两端小,刚好相反,作者把它取名为Inverted residual block。 整体结构如下图所示: 论文里提到Bottleneck有19层,但其给出的结构图中却只有17层。 MobileNet_v2相比与MobileNet_v1,参数量有所增加,主要增加在于Depth wise前使用1x1升维。此外,在CPU上的推理速度也比后者慢,但精度更高。
2022年04月