摘要
新型冠状病毒可以通过空气中的飞沫、气溶胶等载体进行传播,在公共场所下正确佩戴口罩可以有效地防止病毒的传播。本实验介绍了一种自然场景下人脸口罩佩戴检测方法,该方法对RetinaFace算法进行了改进,增加了口罩人脸识别检测任务,优化了损失函数。在特征金字塔网络中引入了一种改进的自注意力机制,增强了特征图的表达能力。建立了包含3000张图片的数据集,并进行手工标注,用于网络训练。实验结果表明该算法可以有效进行口罩佩戴检测,在自然场景视频中也取得了不错的检测效果。
目录
第1章引言 1
1.1国内研究现状分析 1
1.2国外研究现状分析 .1
第2章RetinaFace算法原理 2
2.1预测部分 3
2.2.1主干网络Mobilene介绍…………………… 3
2.2.2 FPN特征金字塔…………………… 4
2.2.3 SSH进一步加强特征提取…………………… 5
2.2.4从特征中获取预测结果…………………… 6
2.2.5对预测结果进行解码…………………… 6
2.2训练部分 7
2.2.1 真实框的处理…………………… 7
2.2.2 利用处理完的真实框与对应图片的预测结果计算loss…………………… 7
第3章改进RetinaFace算法用于口罩佩戴检测 9
3.1改进的自注意力机制 9
3.2多任务联合损失 10
第4章实验分析 11
4.1数据集分析 11
4.2网络模型训练 11
4.3实验结果分析 12
第5章 总结与心得体会 14
5.1实验总结 14
5.2实验心得体会 14
参考文献 16
附录1:关键代码 17
2.2训练部分
2.2.1真实框的处理
真实框的处理过程可以分为3步:
1、计算所有真实框和所有先验框的重合程度,和真实框iou大于0.35的先验框被认为可以用于预测获得该真实框。
2、对这些和真实框重合程度比较大的先验框进行编码的操作,所谓编码,就是当我们要获得这样的真实框的时候,网络的预测结果应该是怎么样的。
3、编码操作可以分为三个部分,分别是分类预测结果,框的回归预测结果和人脸关键点的回归预测结果的编码。
2.2.2利用处理完的真实框与对应图片的预测结果计算loss
loss的计算分为三个部分:
1、BoxSmoothLoss:获取所有正标签的框的预测结果的回归loss。
2、MultiBoxLoss:获取所有种类的预测结果的交叉熵loss。
3、LamdmarkSmoothLoss:获取所有正标签的人脸关键点的预测结果的回归loss。
由于在Retinaface的训练过程中,正负样本极其不平衡,即存在对应真实框的先验框可能只有若干个,但是不存在对应真实框的负样本却有几千上万个,这就会导致负样本的loss值极大,因此我们可以考虑减少负样本的选取,常见的情况是取七倍正样本数量的负样本用于训练。
在计算loss的时候要注意,BoxSmoothLoss计算的是所有被认定为内部包含人脸的先验框的loss,而LamdmarkSmoothLoss计算的是所有被认定为内部包含人脸同时包含人脸关键点的先验框的loss。(在标注的时候有些人脸框因为角度问题以及清晰度问题是没有人脸关键点的)。
多任务损失函数
对于一个训练的先验框i,多任务联合损失函数定义为:
L= (1)
其中,是分类损失函数,是先验框中包含预测目标的概率,∈(0,1)分别表示是负先验框和正先验框。是目标检测框回归损失函数,其中={,}i表示与正先验框相关的预测框的坐标信息,同理={}表示与负先验框相关的预测框的坐标信息。是面部标志回归损失函数,其中={}i和={}分别表示正先验框中预测的五个人脸标志点和标注的五个人脸标志点。Lpixel表示的是面部密集点回归损失函数。λ1、λ2和λ3表示的是损失平衡权值参数,在RetinaFace算法中分别设置为0.25、0.1和0.01,意味着在有监督的学习中再加关注检测框和面部标志点的信息。
本文来转载自:http://www.biyezuopin.vip/onews.asp?id=16536
1. MobileNet关键代码:
import time
import torch
import torch.nn as nn
import torchvision.models._utils as _utils
import torchvision.models as models
import torch.nn.functional as F
from torch.autograd import Variable
def conv_bn(inp, oup, stride = 1, leaky = 0):
return nn.Sequential(
nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
nn.BatchNorm2d(oup),
nn.LeakyReLU(negative_slope=leaky, inplace=True)
)
def conv_dw(inp, oup, stride, leaky=0.1):
return nn.Sequential(
nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),
nn.BatchNorm2d(inp),
nn.LeakyReLU(negative_slope= leaky,inplace=True),
nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
nn.LeakyReLU(negative_slope= leaky,inplace=True),
)
class MobileNetV1(nn.Module):
def __init__(self):
super(MobileNetV1, self).__init__()
self.stage1 = nn.Sequential(
conv_bn(3, 8, 2, leaky = 0.1), # 3
conv_dw(8, 16, 1), # 7
conv_dw(16, 32, 2), # 11
conv_dw(32, 32, 1), # 19
conv_dw(32, 64, 2), # 27
conv_dw(64, 64, 1), # 43
)
self.stage2 = nn.Sequential(
conv_dw(64, 128, 2), # 43 + 16 = 59
conv_dw(128, 128, 1), # 59 + 32 = 91
conv_dw(128, 128, 1), # 91 + 32 = 123
conv_dw(128, 128, 1), # 123 + 32 = 155
conv_dw(128, 128, 1), # 155 + 32 = 187
conv_dw(128, 128, 1), # 187 + 32 = 219
)
self.stage3 = nn.Sequential(
conv_dw(128, 256, 2), # 219 +3 2 = 241
conv_dw(256, 256, 1), # 241 + 64 = 301
)
self.avg = nn.AdaptiveAvgPool2d((1,1))
self.fc = nn.Linear(256, 1000)
def forward(self, x):
x = self.stage1(x)
x = self.stage2(x)
x = self.stage3(x)
x = self.avg(x)
# x = self.model(x)
x = x.view(-1, 256)
x = self.fc(x)
return x












