Google是如何将目标检测SSD魔改为专用人脸检测BlazeFace

Google是如何将目标检测SSD魔改为专用人脸检测BlazeFace

flyfish

SSD

简述SSD 具体看这里
这里的SSD指的是Single Shot MultiBox Detector
Single Shot:
字面意思是单次扫描,这意味着目标定位和分类任务在网络的一次前向传播中完成。single shot为什么有个single呢,single shot也就是one stage,还有two stage,分别是执行目标定位的区域建议网络和用于检测建议区域中的目标类型的分类器。two stage在实时系统计算太贵,one stage 的方法就可以部署到轻量级的硬件中
MultiBox:
这是Szegedy等人开发的边界框回归技术的名称。这是一种技术,将预测目标的边界框描述为回归问题,其中检测到的目标的坐标被回归到ground truth的坐标。此外,对于每个预测框,为各种目标类型生成分数。Prior可以作为预测的可行起点,因为它们是仿照ground truth的,这个是想对于从无到有直接拟合目标的边框来说的。因此,预测的框将与prior box一样多,其中大多数框将不包含任何目标.
检测器:
这里的检测器是网络对检测到的目标进行分类的目标检测器

SSD的组建分三块
基础网络(Base network),可以说是backbone,
附加层( Extra feature layers)
预测层(Prediction layers

四个重要设计

BlazeFace模型架构是围绕下面讨论的四个重要设计注意事项构建的

一 增大感受野(Enlarging the receptive field sizes)

关于 MobileNet v1 具体看这里

深度可分离卷积分为
1、深度卷积(Depthwise Convolution)
2、逐点卷积(Pointwise Convolution)

关于 MobileNet v2 具体看这里

MobileNet v2在Depthwise convolution之前添加一层Pointwise convolution
层级结构是这样
1、Pointwise convolution(expansion convolution)
2、Depthwise convolution
3、Pointwise convolution (projection convolution)
深度增加的叫 expansion,也就是常说的升维
深度减少的叫 projection,也就是所说的降维
上面这两个都是pointwise convolution

有这样的一场比赛,谁的计算量少谁获胜。
关于比较,可以这样比 ,普通卷积和深度可分离卷积 他们的计算量对比
深度可分离卷积的计算量少,他获胜
深度可分离卷积 又分深度卷积和逐点卷积,深度卷积和逐点卷积的计算量对比
深度卷积获胜,逐点卷积失败。逐点卷积就分析总结自己失败的原因,具体总结是这样的

虽然大多数现代卷积神经网络结构(包括MobileNet v1,v2两个版本)都倾向于在模型图上到处使用3×3卷积核,但我们注意到,深度可分离的卷积计算主要由其逐点部分主导。

s × s × c s \times s \times c s×s×c的输入张量上,应用可分离卷积操作,其中, k × k k \times k k×k的depthwise卷积包括 s 2 c k 2 次 s^2ck^2次 s2ck2乘加运算,随后的 1 × 1 1 \times 1 1×1的pointwise卷积用进行 s 2 c d s^2cd s2cd次乘加运算,d是输出通道数,两者比值是 k 2 : d k^2 : d k2:d 倍。
这一观察结果意味着增加深度部分的核大小相对便宜。在模型架构 bottleneck中使用5×5个内核,这样内核大小的是增加了 ,将所需的bottleneck数量减少也能使感受野(receptive field)达到指定大小。
根据这点 就可以设计block了,block总得有个名字就叫BlazeBlock

该网络的设计目标是 在手机上用的,并且针对手机GPU友好的。
在这里插入图片描述
看图有个option,那就是在写代码时根据标志变量是否加层
MobileNet v2的bottleneck包含由非线性分隔的depth-increasing expansion和depth-decreasing projection。为了适应中间张量中较少的通道数量,我们交换了这些阶段,以便bottleneck中的residual connection在“expanded”(增加的)通道分辨率下操作。最后,深度卷积的低开销允许我们在这两个逐点卷积之间引入另一个这样的层,从而进一步加速receptive field大小的进展。这构成了double BlazeBlock的本质,它被用作BlazeFace较高抽象层的选择bottleneck。
上面这么多话,表达就一句对着MobileNet v2 看,把expansion和projection的位置互换,先降维再升维,又加一个5*5 DW进一步降低网络开销。
BlazeBlock层级结构类似是这样

1、Pointwise convolution (projection convolution) 降维
2、Depthwise convolution
3、Pointwise convolution(expansion convolution)升维
4、Depthwise convolution

  (4): BlazeBlock(
    (conv1): Sequential(
      (0): Conv2d(24, 24, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2), groups=24)
      (1): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (2): Conv2d(24, 24, kernel_size=(1, 1), stride=(1, 1))
      (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
    (act): ReLU(inplace=True)
  )
  (5): BlazeBlock(
    (conv1): Sequential(
      (0): Conv2d(24, 24, kernel_size=(5, 5), stride=(2, 2), padding=(2, 2), groups=24)
      (1): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (2): Conv2d(24, 48, kernel_size=(1, 1), stride=(1, 1))
      (3): BatchNorm2d(48, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
    (act): ReLU(inplace=True)
    (mp): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )

具体代码看这里

import torch
import torch.nn as nn
import torch.nn.init as init
import torch.nn.functional as F
from torch.autograd import Function



class BlazeBlock(nn.Module):
    def __init__(self, inp, oup1, oup2=None, stride=1, kernel_size=5):
        super(BlazeBlock, self).__init__()
        self.stride = stride
        assert stride in [1, 2]
        
        # double-block is used when oup2 is specified.
        self.use_double_block = oup2 is not None
        # pooling is used when stride is not 1
        self.use_pooling = self.stride != 1
        # change padding settings to insure pixel size is kept.
        if self.use_double_block:
            self.channel_pad = oup2 - inp
        else:
            self.channel_pad = oup1 - inp
        padding = (kernel_size - 1) // 2
        
        # mobile-net like convolution function is defined.
        self.conv1 = nn.Sequential(
            # dw
            # https://discuss.pytorch.org/t/depthwise-and-separable-convolutions-in-pytorch/7315
            # if groups=inp, it acts as depth wise convolution in pytorch
            nn.Conv2d(inp, inp, kernel_size=kernel_size, stride=stride, padding=padding, groups=inp, bias=True),
            nn.BatchNorm2d(inp),
            # piecewise-linear convolution.
            nn.Conv2d(inp, oup1, 1, 1, 0, bias=True),
            nn.BatchNorm2d(oup1),
        )
        self.act = nn.ReLU(inplace=True)
        
        # for latter layers, use resnet-like double convolution.
        if self.use_double_block:
            self.conv2 = nn.Sequential(
                nn.ReLU(inplace=True),
                # dw
                nn.Conv2d(oup1, oup1, kernel_size=kernel_size, stride=1, padding=padding, groups=oup1, bias=True),
                nn.BatchNorm2d(oup1),
                # pw-linear
                nn.Conv2d(oup1, oup2, 1, 1, 0, bias=True),
                nn.BatchNorm2d(oup2),
            )

        if self.use_pooling:
            self.mp = nn.MaxPool2d(kernel_size=self.stride, stride=self.stride)

    def forward(self, x):
        h = self.conv1(x)
        if self.use_double_block:
            h = self.conv2(h)

        # skip connection
        if self.use_pooling:
            x = self.mp(x)
        if self.channel_pad > 0:
            x = F.pad(x, (0, 0, 0, 0, 0, self.channel_pad), 'constant', 0)
        return self.act(h + x)

# initialize weights.
def initialize(module):
    # original implementation is unknown
    if isinstance(module, nn.Conv2d):
        nn.init.kaiming_normal_(module.weight.data)
        nn.init.constant_(module.bias.data, 0)
    elif isinstance(module, nn.BatchNorm2d):
        nn.init.constant_(module.weight.data, 1)
        nn.init.constant_(module.bias.data, 0)

二、特征提取

SSD原始的特征提取是这样的
在这里插入图片描述
将SSD论文中的原图简化一下表达
在这里插入图片描述再简化下
在这里插入图片描述

mobilenet SSD的提取是这样的
在这里插入图片描述
在论文中我们会看到channel resolution ,就是通道的深度,也可以说通道的大小,别被resolution误导了,以为是屏幕大小,那应该就是图片大小
如果是图片大小,论文中会写 spatial resolution,这个就是图片的大小,高度和宽度
分别用highest和lowest用来形容resolution的,意义最大和最小,翻译是 最高,最低。就像我们平常自然语言说速度快慢,而专业术语说是速度大小不能用快慢。
意思是相同的,看在什么地方表达。

特征提取器用于手机的前置摄像头。该特征提取器必须考虑较小范围的目标尺度,因此它具有较低的计算需求。提取器采用 128×128 像素的 RGB 输入,包括一个 2D 卷积和 5 个single BlazeBlock 和 6 个double BlazeBlock 组成,完整如。最大通道分辨率为 96,而最低空间分辨率为 8×8(与 SSD 相比,它将分辨率一直降低到 1×1)。
在这里插入图片描述

三、Anchor scheme(Anchor的改进方案)

当提到Anchor时,不仅SSD得出来,Faster-R-CNN也得出来,因为他们是同一个东西,但是到了不同的论文就有不同的叫法,因为根据不同的论文翻译,导致汉语里也是不同的叫法。
SSD里叫Prior,Priox Box,Default Box,先验框,默认框
Faster-R-CNN里叫anchor,锚,锚点
相当于给预定义的固定大小的基本边框起一个名字,叫什么名字呢?各家有各家的说法,YOLO里也是有这个idea的
看上去好多,是的,很多,论文用了一种名词,代码实现时,又用了另一种名词,越造越多啊。
如果非要将这些名词加以区别,只要在大量书籍中加以区别定义,达到共识。现在各种书籍重复的部分相当于共识。

锚框(anchor box)是覆盖在图像上不同空间位置( spatial location)、比例(scale)和纵横比(aspect ratio)的方框的集合,充当ground truth图像上的参考点(reference point)。

锚框(anchor box)是bounding box中的一种,就像直角三角形是三角形的一种。

对模型进行训练,使其为每个锚框做出两个预测:
1、每个锚框的离散类别预测。
2、对偏移量的连续预测,锚框需要移动多少偏移量才能拟合ground truth边界框
离散(discrete)、连续(continuous)
离散和连续 可以这样解释
离散是指其数值只能用自然数或整数单位计算的.例如员工个数,相当于类别是可数的。
连续是在一定区间内可以任意取值的变量,可取无限个数值,相当于偏移量可以取任意数值。
看后面的classifier和regressor都是对应什么样的值,classifier对应离散,regressor对应连续
在这里插入图片描述Google的BlazeFace 采用锚框方案如下:

1、将特征图下采样到 8 × 8 8\times 8 8×8
2、用 8 × 8 8\times 8 8×8 中的6个anchor替换原来 8 × 8 8\times 8 8×8 4 × 4 和 2 × 2 4\times 4 和 2\times 2 4×42×2 分辨率中的每个像素对应的2个anchor。
3、由于人脸长宽比的变化有限,将锚框固定为1:1纵横比
特征映射、特征图(feature map),翻译特征映射更好,如果翻译成特征图,还得考虑这个图是map还是graph,达不到望文生义的效果,如果习惯了说成特征图,说特征映射就别扭了。
通常的做法是根据目标尺度范围在多个分辨率级别定义锚框。积极的下采样也是优化计算资源的手段。典型的 SSD 模型使用来自 1 × 1 、 2 × 2 、 4 × 4 、 8 × 8 和 16 × 16 1\times 1、2\times 2、4\times 4、8\times 8 和 16\times 16 1×12×24×48×816×16 特征图大小的预测。然而,池化金字塔网络(Pooling Pyramid Network,PPN)架构的成功意味着在达到某个特征映射分辨率(feature map resolution)之后,额外的计算可能是多余的。

PPN 是一种单级卷积目标检测器,与传统的SSD非常相似,只是做了一些简单的改变。该预测头被设计成重量轻,运行速度快,同时保持与普通SSD相当的检测精度。
SSD与Pooling Pyramid Network,PPN 对比如下
在这里插入图片描述
PPN对SSD的改进有两点

对于feature map的降采样操作,采用max pooling来代替原有的卷积。所以金字塔要加个池化(pooling)
相比于原来的SSD中,不同feature map采用独立的conv来预测类别分数和边框位置,PPN采用一个共享的conv来得到类别分数和边框位置
那就给个全称 共享预测层池化金字塔

四、后处理机制

由于上述anchor机制中特征提取器未将分辨率降低到8×8以下,所以与给定目标重叠的anchor数量将会下降。在典型的SSD的NMS场景中,只有一个胜出的anchor用于算法输出,当将这样的模型应用于后续视频帧时,预测往往在不同的锚之间波动,人脸框抖动明显。
NMS(Non-Maximum Suppression )的原来作用如下
考虑到在推理时的SSD正向传播期间生成了大量的框,必须通过应用称为非最大抑制的技术来修剪大多数边框:丢弃置信损失阈值小于0.01和IOU小于0.45的框,并且仅保留前N个预测。这确保网络只保留最可能的预测,而删除噪音较大的预测。(0.01,0.45是这里举的例子,也可以是其他值)
为了降低这种效应,这里不再使用NMS,代之一种blending策略,将边界框的回归参数估计为重叠预测之间的加权平均值。它几乎不会产生之前NMS部分的成本。作者说对于在视频中的面部检测任务,此调整导致准确度提高10%

参考

WeightedNonMaxSuppression
Pooling Pyramid Network for Object Detection
A Pytorch SSD implementation
Detectron(facebook的一个NMS实现)
Soft NMS(另一个版本的NMS
A Faster RCNN Implementation.
SSD github
SSD论文
BlazeFace论文

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西笑生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值