Facenet细节剖析
paper facenet & mobilefacenet 实现细节剖析
人脸识别项目 细节剖析
1. 图片输入的大小是多少?最后的 embedding 是多少? 为什么?
图片输入大小设置为 160x160。why? 随着图片分辨率的增大,验证集的准确率会上升,但是对应的运算量也会显著上升。160 是一个相对合适的分辨率大小。
(facenet 原论文尝试了 40x40, 80x80, 112x112, 160x160, 256x256)
embedding 的大小为 128, 实验测得,当超过 128 时候,验证集的准确率已经没有上升,反而有所下降。(64 -> 128 -> 256 -> 512)。why? 128D向量拥有足够的容量hold 住大规模的人脸数据集(百万人脸级别), 并且相对紧凑。
2. 基本原理
选取三元组(anchor, pos, neg),其中,x和p是同一类,x和n是不同类。那么学习的过程就是学到一种表示,对于尽可能多的三元组,使得anchor和pos的距离,小于anchor和neg的距离
3. 主干网络:
原始论文中使用的是 googlenet、github上的facenet使用的 inception resnet v1、mobilefacenet 使用的是 mobilenet v2
(1) GoogleNet
原论文中使用的是 GoogleNet, Googlenet 的主要涉及在于以下几点:
- Inception Module中包含3种不同尺寸的卷积和1个最大池化,增加了网络对不同尺度的适应性。
第一个分支对输入进行 1x1卷积,1x1卷积可以跨通道组织信息,提高网络的表达能力,同时可以对输出通道升维和降维。
第二个分支先使用了 1x1 卷积,然后连接 3x3 卷积,相当于进行两次特征变换。
第三个分支和第二个分支类似,先是使用了1x1 的卷积,然后连接 5x5 的卷积。
最后一个分支则是3x3 最大池化后直接使用1x1卷积。
Inception Module 的4个分支在最后通过一个聚合操作合并。
- 去除了最后的全连接层,用全局平均池化层来取代它。
(2) Mobilenet v2
另一篇论文则使用了 mobilenetv2 (输入变成112) 结构作为主要网络:主要修改点如下所示:
Inception 结构替换为 mobilenet v2 结构:
mobilenet 的结构要点:(1)skip-connection 跳层连接 (2) 将传统卷积分解为逐通道卷积(先进行逐通道卷积但是通道之间不相加,然后使用1x1 卷积对通道进行整合), 并在前面加一个1x1 的卷积进行升维。(3) 将最后的 ReLU 替换为 Linear(非线性在高维有溢出,但是在低维不如线性好)
用全局可分离卷积替代原有的全局池化层。why? 特征图上的中心点的感受野和边角的感受野是不同的,中心点的感受野包括了完整的图片,边角点的感知域却只有部分的图片, 不应该视为同等重要。这样会导致性能的下降。
使用 arcface 替换掉 triplet 损失函数。
小细节:通道扩张倍数变小(facenet 是 1024, mobilenets 则是 512);使用prelu代替relu;使用batch Normalization。
4. 预处理工作:
(1) 数据集的整理和清洗(DDM 智能猫眼[大约10W张,6千人的样子-> 猫眼300台,每台20人]的真实数据 和 CASIA 人脸数据)
- 类间过滤: 清洗掉距离与类中心小于0.5 的负样本(对一个类别,先计算类中心,然后用所有样本,与其进行比较)
图片的过滤:将经过人脸检测(MTCNN/FaceBoxes) 返回的人脸置信度 小于 0.75 的图片直接过滤掉
按图像数量阈值进行数据划分(人脸低于10张的过滤掉)
- 类间距离问题:有些可能是名字重合的,找出来合并在一起
(2) 人脸识别前的处理:
人脸对齐?可以有也可以没有,影响不是很大 how?( 通过仿射变换将原本的五个landmark缩放旋转到固定的位置)
from skimage import transform as trans # src src = np.array([ [30.2946, 51.6963], [65.5318, 51.5014], [48.0252, 71.7366], [33.5493, 92.3655], [62.7299, 92.2041] ], dtype=np.float32 ) if image_size[1]==112: src[:,0] += 8.0 # dst dst = landmark.astype(np.float32) tform = trans.SimilarityTransform() tform.estimate(dst, src) M = tform.params[0:2,:] # warpAffine warped = cv2.warpAffine(img, M, (image_size[1],image_size[0]), borderValue = 0.0)
模糊过滤?(将检测出来的人脸使用拉普拉斯做卷积运算,然后计算方差, 将方差小于 400 过滤掉)
- 人脸置信度,这里设置置信度0.8,人脸置信度太小可能不是人脸,直接过滤掉。
- 对图片进行标准化处理:(x-均值)/标准差
5. 后处理(如何进行分类?)
facenet 使用欧式距离来度量是否是一个人。使用十折交叉验证,我们设定阈值为 1.24。
mobilenet 使用相似性余弦度量:阈值设定为 0.7。
6. 参数调整:(没有怎么调整参数, 使用默认参数,替换掉了数据集而已)
优化器:
RMSProp
tf.train.RMSPropOptimizer(learning_rate, decay=0.9, momentum=0.9, epsilon=1.0)
初始学习率:0.1
学习率下降方式:exponential_decay
# Learning rate schedule: Maps an epoch number to a learning rate 0: 0.1 # 初始学习率 300: 0.01 400: 0.001 1000: 0.0001
margin α=0.2
7. why facenet ?
softmax不直接,(三元组直接优化距离),因⽽而性能也不不好。 softmax产⽣生的特征表示向量量都很⼤大,⼀一般超过1000维。
faceNet并没有像DeepFace和DeepID那样需要对⻬。
faceNet得到最终表示后不不⽤用像DeepID那样需要再训练模型进⾏分类,直接计算距离就好了了,简单⽽而有效。
8. 模型的大小:
线上模型: model_size: (Inception resnet v2: 186M) Param:7.5M
线下模型:(手机端) model_size: 4.1M param: 0.99M
8. 训练技巧
- 三元组的选择
在一个minibatch中,我们根据当时的 embedding,选择一次三元组,在这些三元组上计算triplet-loss, 再对embedding进行更新,不断重复,直到收敛或训练到指定迭代次数。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!