聪明的人脸识别1——Keras 搭建自己的Facenet人脸识别平台

  • 学习前言
  • 什么是Facenet
  • 源码下载
  • Facenet的实现思路
    • 一、预测部分
      • 1、主干网络介绍
      • 2、根据初步特征获得长度为128的特征向量
      • 3、l2标准化
      • 4、构建分类器(用于辅助Triplet Loss的收敛)
    • 二、训练部分
      • 1、数据集介绍
      • 2、LOSS组成
  • 训练自己的Facenet人脸识别算法

学习前言

最近又学了我最喜欢的retinaface,以前也了解过facenet,但是没有训练过,可以去学习一下!

什么是Facenet

谷歌人脸识别算法,发表于 CVPR 2015,利用相同人脸在不同角度等姿态的照片下有高内聚性,不同人脸有低耦合性,提出使用 cnn + triplet mining 方法,在 LFW 数据集上准确度达到 99.63%。

通过 CNN 将人脸映射到欧式空间的特征向量上,实质上:不同图片人脸特征的距离较大;通过相同个体的人脸的距离,总是小于不同个体的人脸这一先验知识训练网络。

测试时只需要计算人脸特征EMBEDDING,然后计算距离使用阈值即可判定两张人脸照片是否属于相同的个体。

简单来讲,在使用阶段,facenet即是:
1、输入一张人脸图片
2、通过深度卷积网络提取特征
3、L2标准化
4、得到一个长度为128特征向量。

源码下载

https://github.com/bubbliiiing/facenet-keras

Facenet的实现思路

一、预测部分

1、主干网络介绍


facenet的主干网络起到提取特征的作用,原版的facenet以Inception-ResNetV1为主干特征提取网络。

本文一共提供了两个网络作为主干特征提取网络,分别是mobilenetv1和Inception-ResNetV1,二者都起到特征提取的作用,为了方便理解,本博文中会使用mobilenetv1作为主干特征提取网络。

MobilenetV1模型是Google针对手机等嵌入式设备提出的一种轻量级的深层神经网络,其使用的核心思想便是depthwise separable convolution(深度可分离卷积块)。

深度可分离卷积块由两个部分组成,分别是深度可分离卷积和1x1普通卷积,深度可分离卷积的卷积核大小一般是3x3的,便于理解的话我们可以把它当作是特征提取,1x1的普通卷积可以完成通道数的调整。

下图为深度可分离卷积块的结构示意图:

深度可分离卷积块的目的是使用更少的参数来代替普通的3x3卷积。

我们可以进行一下普通卷积和深度可分离卷积块的对比:

对于普通卷积而言,假设有一个3×3大小的卷积层,其输入通道为16、输出通道为32。具体为,32个3×3大小的卷积核会遍历16个通道中的每个数据,最后可得到所需的32个输出通道,所需参数为16×32×3×3=4608个。

对于深度可分离卷积结构块而言,假设有一个深度可分离卷积结构块,其输入通道为16、输出通道为32,其会用16个3×3大小的卷积核分别遍历16通道的数据,得到了16个特征图谱。在融合操作之前,接着用32个1×1大小的卷积核遍历这16个特征图谱,所需参数为16×3×3+16×32×1×1=656个。

可以看出来深度可分离卷积结构块可以减少模型的参数。

如下就是MobileNet的结构,其中Conv dw就是分层卷积,在其之后都会接一个1x1的卷积进行通道处理,

import math
import numpy as np
import tensorflow as tffrom keras import backend
from keras import backend as K
from keras.preprocessing import image
from keras.models import Model
from keras.layers.normalization import BatchNormalization
from keras.layers import Conv2D, Add, ZeroPadding2D, GlobalAveragePooling2D, Dropout, Dense, Lambda
from keras.layers import MaxPooling2D,Activation,DepthwiseConv2D,Input,GlobalMaxPooling2D
from keras.applications import imagenet_utils
from keras.applications.imagenet_utils import decode_predictions
from keras.utils.data_utils import get_filedef _conv_block(inputs, filters, kernel=(3, 3), strides=(1, 1)):x = Conv2D(filters, kernel,padding='same',use_bias=False,strides=strides,name='conv1')(inputs)x = BatchNormalization(name='conv1_bn')(x)return Activation(relu6, name='conv1_relu')(x)def _depthwise_conv_block(inputs, pointwise_conv_filters,depth_multiplier=1, strides=(1, 1), block_id=1):x = DepthwiseConv2D((3, 3),padding='same',depth_multiplier=depth_multiplier,strides=strides,use_bias=False,name='conv_dw_%d' % block_id)(inputs)x = BatchNormalization(name='conv_dw_%d_bn' % block_id)(x)x = Activation(relu6, name='conv_dw_%d_relu' % block_id)(x)x = Conv2D(pointwise_conv_filters, (1, 1),padding='same',use_bias=False,strides=(1, 1),name='conv_pw_%d' % block_id)(x)x = BatchNormalization(name='conv_pw_%d_bn' % block_id)(x)return Activation(relu6, name='conv_pw_%d_relu' % block_id)(x)def relu6(x):return K.relu(x, max_value=6)def MobileNet(inputs, embedding_size=128, dropout_keep_prob=0.8, alpha=1.0, depth_multiplier=1):x = _conv_block(inputs, 32, strides=(2, 2))x = _depthwise_conv_block(x, 64, depth_multiplier, block_id=1)x = _depthwise_conv_block(x, 128, depth_multiplier, strides=(2, 2), block_id=2)x = _depthwise_conv_block(x, 128, depth_multiplier, block_id=3)x = _depthwise_conv_block(x, 256, depth_multiplier, strides=(2, 2), block_id=4)x = _depthwise_conv_block(x, 256, depth_multiplier, block_id=5)x = _depthwise_conv_block(x, 512, depth_multiplier, strides=(2, 2), block_id=6)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=7)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=8)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=9)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=10)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=11)x = _depthwise_conv_block(x, 1024, depth_multiplier, strides=(2, 2), block_id=12)x = _depthwise_conv_block(x, 1024, depth_multiplier, block_id=13)

2、根据初步特征获得长度为128的特征向量


利用主干特征提取网络我们可以获得一个特征层,它的shape为(batch_size, h, w, channels),我们可以将其取全局平均池化,方便后续的处理(batch_size, channels)。

我们可以将平铺后的特征层进行一个神经元个数为128的全连接此时我们相当于利用了一个长度为128的特征向量代替输入进来的图片。这个长度为128的特征向量就是输入图片的特征浓缩。

x = GlobalAveragePooling2D()(x)
x = Dropout(1.0 - dropout_keep_prob, name='Dropout')(x)
x = Dense(classes, use_bias=False, name='Bottleneck')(x)
x = BatchNormalization(momentum=0.995, epsilon=0.001, scale=False,name='BatchNorm_Bottleneck')(x)

3、l2标准化


在获得一个长度为128的特征向量后,我们还需要进行l2标准化的处理。

这个L2标准化是为了使得不同人脸的特征向量可以属于同一数量级,方便比较。

在进行l2标准化前需要首先计算2-范数:
∣ ∣ x ∣ ∣ 2 = ∑ i = 1 N x i 2 ||\textbf{x}||_2 =\sqrt{\sum_{i=1}^Nx_i^2} x2=i=1Nxi2

,也就是欧几里得范数,即向量元素绝对值的平方和再开方。

L2标准化就是每个元素/L2范数;

在keras代码中,只需要一行就可以实现l2标准化的层。

x= Lambda(lambda  x: K.l2_normalize(x, axis=-1))(x)
# 创建模型
model = Model(inputs, x, name='inception_resnet_v1')

到这里,我们输入进来的图片,已经变成了一个经过l2标准化的长度为128的特征向量了!

4、构建分类器(用于辅助Triplet Loss的收敛)

当我们完成第三步后,我们已经可以利用这个预测结果进行训练和预测了。

但是由于仅仅只是用Triplet Loss会使得整个网络难以收敛,本文结合Cross-Entropy Loss和Triplet Loss作为总体loss。

Triplet Loss用于进行不同人的人脸特征向量欧几里得距离的扩张,同一个人的不同状态的人脸特征向量欧几里得距离的缩小。
Cross-Entropy Loss用于人脸分类,具体作用是辅助Triplet Loss收敛。

想要利用Cross-Entropy Loss进行训练需要构建分类器,因此对第三步获得的结果再次进行一个全连接用于分类。

构建代码如下,当我们在进行网络的训练的时候,可使用分类器辅助训练,在预测的时候,分类器是不需要的:

def facenet(input_shape, num_classes=None, backbone="mobilenet", mode="train"):inputs = Input(shape=input_shape)if backbone=="mobilenet":model = MobileNet(inputs)elif backbone=="inception_resnetv1":model = InceptionResNetV1(inputs)else:raise ValueError('Unsupported backbone - `{}`, Use mobilenet, inception_resnetv1.'.format(backbone))if mode == "train":x = Dense(num_classes)(model.output)x = Activation("softmax", name = "Softmax")(x)combine_model = Model(inputs,[x, model.output])return combine_modelelif mode == "predict":return modelelse:raise ValueError('Unsupported mode - `{}`, Use train, predict.'.format(mode))

二、训练部分

1、数据集介绍

我们使用的数据集是CASIA-WebFace数据集,我已经对其进行了预处理,将其属于同一个人的图片放到同一个文件夹里面,并且进行了人脸的提取和人脸的矫正。

数据集里面有很多的文件夹,每一个文件夹里面存放同一个人的不同情况下的人脸。不同文件夹存放不同人的脸。

这是\0000045文件夹里面的人脸,属于同一个人。

这是\0000099文件夹里面的人脸,属于同一个人。

2、LOSS组成

facenet使用Triplet Loss作为loss。
Triplet Loss的输入是一个三元组

  • a:anchor,基准图片获得的128维人脸特征向量
  • p:positive,与基准图片属于同一张人脸的图片获得的128维人脸特征向量
  • n:negative,与基准图片不属于同一张人脸的图片获得的128维人脸特征向量

我们可以将anchor和positive求欧几里得距离,并使其尽量小。
我们可以将negative和positive求欧几里得距离,并使其尽量大。

我们所使用的公式为。
L = m a x ( d ( a , p ) − d ( a , n ) + m a r g i n , 0 ) L=max(d(a,p)−d(a,n)+margin,0) L=max(d(a,p)d(a,n)+margin,0)
d(a,p)就是anchor和positive的欧几里得距离。
d(a,n)就是negative和positive的欧几里得距离。
margin是一个常数。

d(a,p)前面为正符号,所以我们期望其越来越小。
d(a,n)前面为负符号,所以我们期望其越来越大。

即我们希望,同一个人的不同状态的人脸特征向量欧几里得距离小。
不同人的人脸特征向量欧几里得距离大。

但是由于仅仅只是用Triplet Loss会使得整个网络难以收敛,本文结合Cross-Entropy Loss和Triplet Loss作为总体loss。

Triplet Loss用于进行不同人的人脸特征向量欧几里得距离的扩张,同一个人的不同状态的人脸特征向量欧几里得距离的缩小。
Cross-Entropy Loss用于人脸分类,具体作用是辅助Triplet Loss收敛。

训练自己的Facenet人脸识别算法

博客中所使用的例子为CASIA-WebFace数据集。

下载数据集,放在根目录下的dataset文件夹下。
运行根目录下的txt_annotation.py,生成训练所需的cls_train.txt。

cls_train.txt中每一行都存放了一张图片和它对应的类别(需要类别是因为训练时会用交叉熵损失辅助收敛。)

下载facenet_inception_resnetv1.h5或者facenet_mobilenet.h5放在model_data文件夹内。

在train.py中指定合适的模型预训练权重路径。facenet_inception_resnetv1.h5是我已经训练过的基于inception_resnetv1的facenet网络;
facenet_mobilenet.h5是我已经训练过的基于mobilenet的facenet网络。

运行train.py开始训练。

聪明的人脸识别1——Keras 搭建自己的Facenet人脸识别平台相关推荐

  1. 聪明的人脸识别3——Pytorch 搭建自己的Facenet人脸识别平台

    聪明的人脸识别3--Pytorch 搭建自己的Facenet人脸识别平台 学习前言 什么是Facenet 源码下载 Facenet的实现思路 一.预测部分 1.主干网络介绍 2.根据初步特征获得长度为 ...

  2. 使用tf.keras搭建mnist手写数字识别网络

    使用tf.keras搭建mnist手写数字识别网络 目录 使用tf.keras搭建mnist手写数字识别网络 1.使用tf.keras.Sequential搭建序列模型 1.1 tf.keras.Se ...

  3. TensorFlow高阶 API: keras教程-使用tf.keras搭建mnist手写数字识别网络

    TensorFlow高阶 API:keras教程-使用tf.keras搭建mnist手写数字识别网络 目录 TensorFlow高阶 API:keras教程-使用tf.keras搭建mnist手写数字 ...

  4. 憨批的语义分割重制版11——Keras 搭建自己的HRNetV2语义分割平台

    憨批的语义分割重制版11--Keras 搭建自己的HRNetV2语义分割平台 学习前言 什么是HRNetV2模型 代码下载 HRNetV2实现思路 一.预测部分 1.主干网络介绍 a.Section- ...

  5. facenet 人脸识别原理理解(三)

    在前两篇文章已经介绍了facenet人脸识别代码的使用和具体操作,但相关的原理还是没有说,这篇文章进行简单的讲解一下. 1. 原理 在人脸识别中,当我们需要加在图片数据库入新的一张人脸图片时,是怎么做 ...

  6. (大佬)睿智的目标检测13——Keras搭建mtcnn人脸检测平台

    原文链接:https://blog.csdn.net/weixin_44791964/article/details/103530206 睿智的目标检测13--Keras搭建mtcnn人脸检测平台 学 ...

  7. ​​​​​​​CV:利用cv2(加载人脸识别xml文件及detectMultiScale函数得到人脸列表)+keras的load_model(加载表情hdf5、性别hdf5)并标注

    CV:利用cv2+自定义load_detection_model(加载人脸识别xml文件及detectMultiScale函数得到人脸列表)+keras的load_model(加载表情hdf5.性别h ...

  8. cnn神经网络可以用于数据拟合吗_使用Keras搭建卷积神经网络进行手写识别的入门(包含代码解读)...

    本文是发在Medium上的一篇博客:<Handwritten Equation Solver using Convolutional Neural Network>.本文是原文的翻译.这篇 ...

  9. matlab人脸识别样本库建立,facenet 人脸识别(二)——创建人脸库搭建人脸识别系统...

    搭建人脸库 选择的方式是从百度下载明星照片 照片下载,downloadImageByBaidu.py # coding=utf-8 """ 爬取百度图片的高清原图 &qu ...

最新文章

  1. spring入门详细教程(五)
  2. Java高效读取大文件
  3. 上传文件大小超过服务器限制是什么意思,求助:文件上传 当文件大小大于限制时弹出提示框...
  4. NET Core微服务之路:SkyWalking+SkyApm-dotnet分布式链路追踪系统的分享
  5. C# 实现Winform全屏后不遮挡任务栏,显示任务栏
  6. 2014年英语一作文partA
  7. 新版:全世界最前沿的125科学问题
  8. mysql 索引能不能太多,mysql索引太多了?
  9. 内存超频时序怎么调_电脑内存条专业科普,内存选购、内存品牌、内存安装、内存时序体质、内存超频频率详细讲解...
  10. java webinf lib jar_java web项目中classes文件夹下的class和WEB-INF/lib中jar里的class文件加载顺序...
  11. 简析Windows Linux的内核映射
  12. 「山东城商行联盟数据库准实时数据采集系统」入选2021中国大数据应用样板案例
  13. Nmap学习8 - 端口扫描实验
  14. js中的getDate() getMonth() getFullYear()方法;js如何获取当前日期/年月日
  15. 将HDC保存为BMP文件
  16. 僵尸网络_僵尸网络钓鱼
  17. python中关系运算符惰性求值_python对象属性惰性取值
  18. IP数据包在网络中的传输过程
  19. 2022年中国前10大互联网公司广告营收榜
  20. 高级前端基础-JavaScript抽象语法树AST

热门文章

  1. 轮胎企业RFID生产线管理(MES系统)应用
  2. 微信小程序加载 FengMap地图
  3. python做三维图片挑战眼力_腾讯实习挑战赛30强WriteUp
  4. # linux mini版 玩成DVD版(哈哈哈哈)
  5. 21天教你学会C++(搞笑)
  6. 开发兼容Win7的Ms Agent程序
  7. rem 手机端rem布局 (淘宝)
  8. 12、循环结构(While循环,do while循环,for循环)
  9. 云计算成未来发展主推动力,关于IaaS未来发展的四个猜想 | 燕麦企业云盘
  10. node各个版本的下载地址