FaceBoxes: A CPU Real-time Face Detector with High Accuracy
使用了 SSD 的方法,提取网络不同深度的 feature map 完成人脸分类+bbox回归的任务,也有 RPN 的思想,设置 anchor 提取 proposal,增加检测性能,使用大步长快速缩减特征维度,实现快速计算,使用 C.ReLU 增加少量计算量但增加 channel 数量,防止因为特征维度小而造成检测性能的快速下降;提取网络不同深度、不同维度大小的 feature map 进行人脸检测,可以实现多尺度人脸的检测。
加速:Rapidly Digested Convolutional Layers (RDCL)
- 缩减输入维度:在卷积层和 pooling 层设置大步长快速降低输入的维度,减少计算量,文中 stride=(4, 2, 2, 2);
- 选择合适大小的卷积核:选择较大的卷积核加速计算,文中 kernel szie = (77, 55 ,3*3);
- 减少输出的 channel 数量:减少 channel 数量同样可以减少计算量,文中还用到 C.ReLU ,可以减少计算量,但是性能仅有小幅下降,可以忽略不计;
C . R e L U = [ R e L U ( x ) , R e L U ( − x ) ] C.ReLU=[ReLU(x), ReLU(-x)] C.ReLU=[ReLU(x),ReLU(−x)]
防止检测性能的大幅下降的做法:Multiple Scale Convolutional Layers (MSCL):
- 提取网络不同深度的 feature map 进行人脸检测,靠近输入层的 feature map 用于检测小尺度人脸,靠近输出层的 feature map 用于检测大尺度人脸;
- 提取的不同深度的 feature map 的宽度不同,用到了 Inception 结构,多分支进行不同卷积等操作,实现不同感受野的融合,增强对不同尺度人脸的检测;
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/3b3de68fd099fa23bbed19d7a87ef374.png)
Anchor densification strategy: 增加 anchor 稠密的操作
- aspect ratio: 1:1
- anchor 的大小:
Inception:32, 64, 128 像素
Conv3_2:256 像素
Conv4_2:512 像素 - anchor 稠密策略:
A d e n s i t y = A s a c l e / A i n t e r v a l A_{density}=A_{sacle}/A_{interval} Adensity=Asacle/Ainterval
其中, A d e n s i t y A_{density} Adensity 是 anchor 的尺度, A i n t e r v a l A_{interval} Ainterval 是 anchor 的间隔,代码中默认 anchor 的间隔分别为 32, 32, 32, 64, 128, 对应的密度为 1, 2, 4, 4, 4,存在明显的不同尺度的 anchor 密度不平衡的问题,相较于大尺度 anchor ( 128 × 128 , 256 × 256 , 512 × 512 128\times 128,256\times 256, 512\times 512 128×128,256×256,512×512),小尺度的 anchor ( 32 × 32 , 64 × 64 32\times 32, 64\times 64 32×32,64×64) 过于稀疏,会导致小尺度人脸的召回率过低。
为了解决不平衡的问题,对一种 anchor 稠密 n n n倍,在一个感受野的中心均匀放置 A n u m b e r = n 2 A_{number}=n^2 Anumber=n2 个 anchor 用于预测,而不是放置1个。文中稠密 32 × 32 32\times 32 32×32 的 anchor 4 倍,稠密 64 × 64 64 \times 64 64×64 的 anchor 2倍,保证不同尺度的 anchor 有相同的密度,不同尺度的人脸可以匹配相同数量的 abchor;
模型
![网络结构](https://i-blog.csdnimg.cn/blog_migrate/7d5d08b86023ba64f16a105479967263.png)
- 通过使用较大的卷积核和步长来快速缩减 feature map 的大小
- 使用较大的卷积核
模型部分的 pytorch 实现:
def conv_bn_relu(in_channels,out_channels,kernel_size,stride=1,padding=0):
return nn.Sequential(
nn.Conv2d(in_channels,out_channels,kernel_size=kernel_size,padding=padding,stride=stride),
nn.BatchNorm2d(out_channels),
nn.ReLU(True)
)
class Inception(nn.Module):
def __init__(self):
super(Inception,self).__init__()
self.conv1 = conv_bn_relu(128,32,kernel_size=1)
self.conv2 = conv_bn_relu(128,32,kernel_size=1)
self.conv3 = conv_bn_relu(128,24,kernel_size=1)
self.conv4 = conv_bn_relu(24,32,kernel_size=3,padding=1)
self.conv5 = conv_bn_relu(128,24,kernel_size=1)
self.conv6 = conv_bn_relu(24,32,kernel_size=3,padding=1)
self.conv7 = conv_bn_relu(32,32,kernel_size=3,padding=1)
def forward(self,x):
x1 = self.conv1(x)
x2 = F.max_pool2d(x,kernel_size=3,stride=1,padding=1)
x2 = self.conv2(x2)
x3 = self.conv3(x)
x3 = self.conv4(x3)
x4 = self.conv5(x)
x4 = self.conv6(x4)
x4 = self.conv7(x4)
output = torch.cat([x1,x2,x3,x4],1)
return output
class FaceBox(nn.Module):
input_size = 1024 # 使用较大的输入(1024)增强对小目标的检测效果
def __init__(self):
super(FaceBox, self).__init__()
#model
# 7*7 的卷积核,stride=4,快速缩小 feature map 缩小 4 倍
self.conv1 = nn.Conv2d(3,24,kernel_size=7,stride=4,padding=3)
self.bn1 = nn.BatchNorm2d(24)
# 5*5 的卷积核,stride=2, feature map 缩小 2 倍
self.conv2 = nn.Conv2d(48,64,kernel_size=5,stride=2,padding=2)
self.bn2 = nn.BatchNorm2d(64)
self.inception1 = Inception()
self.inception2 = Inception()
self.inception3 = Inception()
self.conv3_1 = conv_bn_relu(128,128,kernel_size=1)
# stride=2, feature map 缩小 2 倍
self.conv3_2 = conv_bn_relu(128,256,kernel_size=3,stride=2,padding=1)
self.conv4_1 = conv_bn_relu(256,128,kernel_size=1)
# stride=2, feature map 缩小 2 倍
self.conv4_2 = conv_bn_relu(128,256,kernel_size=3,stride=2,padding=1)
self.multilbox = MultiBoxLayer()
def forward(self,x):
hs = []
# input: 3*1024*1024
# 24*256*256
x = self.conv1(x)
x = self.bn1(x)
# C.ReLU 增加 channel 数量 48*256*256
x = F.relu(torch.cat((F.relu(x), F.relu(-x)),1))
# feature map = 48*128*128
x = F.max_pool2d(x,kernel_size=3,stride=2,padding=1)
# 64*64*64
x = self.conv2(x)
x = self.bn2(x)
# 128*64*64
x = F.relu(torch.cat((F.relu(x), F.relu(-x)),1))
# 128*32*32
x = F.max_pool2d(x,kernel_size=3,stride=2,padding=1)
# 128*32*32
x = self.inception1(x)
#128*32*32
x = self.inception2(x)
# 128*32*32
x = self.inception3(x)
# 提取第三个 inception 结构的输出的 feature map 用于小尺寸人脸检测
hs.append(x)
# 128*32*32
x = self.conv3_1(x)
# 128*16*16
x = self.conv3_2(x)
# 提取 feature map 用作人脸检测
hs.append(x)
# 128*16*16
x = self.conv4_1(x)
# 256*8*8
x = self.conv4_2(x)
# 使用该层的 feature map 进行大尺寸人脸检测
hs.append(x)
loc_preds, conf_preds = self.multilbox(hs)
return loc_preds, conf_preds
训练
- 数据集:WIDER FACE 的子集
- 数据增强:
- Color distortion
- Random cropping
- Scale transformation
- Horizontal flipping
- Face-box filter:过滤掉小于20像素的人脸
- 损失函数:
- 分类:softmax
- 回归:smooth L1
L l o c ( t u , v ) = ∑ y ∈ { x , y , w , h } s m o o t h L 1 ( t i u − v i ) L_{loc}(t^u, v)=\sum_{y\in \{x,y,w,h\}} smooth_{L_1}(t_i^u-v_i) Lloc(tu,v)=∑y∈{x,y,w,h}smoothL1(tiu−vi),其中, { 0.5 x 2 i f ∣ x ∣ < 1 ∣ x ∣ − 0.5 o t h e r w i s e \left\{\begin{matrix} 0.5x^2 & if |x|<1 \\ |x|-0.5 & otherwise \end{matrix}\right. {0.5x2∣x∣−0.5if∣x∣<1otherwise
结果
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/d352fdccef06f6de0e154bcaa45a3530.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/85f95b50f8774ea9521b11443ba29fa6.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/fe9b52f6a6646c92303f59a91d8afb18.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/353d0e049b46fef451bd9aa5613e46c5.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/a55a50ceed279c9d2ea637331310e1e8.png)