前言
如何用人工"智障"快速的识别人脸呢?首先我们拿到一张图片,需要去看看图片中是否有人脸,如果有人脸,我们需要把人脸截取出来,放入到特征提取网络中去提取特征,再把提取好的特征向量进行分类,这样就实现人脸识别了。是不是也没有想象中的那么难呢?
本篇博客使用 mtcnn 作为人脸定位的网络,facenet 作为特征提取网络,使用 svm 作为分类器,将 facenet 提取到的 128 维特征向量,用于训练 svm,通过 svm 实现人脸的识别。这样简单的人脸识别系统就完成了。
接下来我会简要的介绍一下,mtcnn ,facenet 和 svm 的原理。
本篇博文只是教大家搭建一个简单的人脸识别系统,并没有解决网络单次训练的问题。 单次训练就是说,训练完后,当新的人脸存入到数据库中,不许要重新训练网络,只需要少量的几张图片就可以准确识别。本文用的方法,当新的人脸进来是需要重新训练 svm 分类器。所以比较简单。
一、原理介绍
我们是站在巨人的肩膀上,完成自己的任务,mtcnn 和 facenet 使用别人已经预训练好的网络模型,我们只训练 svm。
1.mtcnn
mtcnn (Multi-task convolutional neural network,多任务卷积神经网络),将人脸区域检测和关键点检测放在了一起,检测的关键掉包括眼睛,鼻子,嘴角,五个关键点。
我们来看一下他的工作流程:
首先呢,将原图裁剪成不同的尺寸,再 resize 成 12*12,再输入到 P-Net 中。
P-Net(Proposal Network)
P-Net 的网络结构如上图所示,通过浅层的全卷积网络来获得 Bounding-Box 回归向量,并使用 NMS(非极大值抑制)进行大部分窗口的过滤,
R-Net(Refine Network)
图片经过 P-Net 之后,会产生很多的预测窗口,将预测窗口全部送入到 R-Net 中,通过 R-Net 后,会消除掉质量很差的候选框,最后对剩下的候选框进行 Bounding-Box 回归和 NMS 进一步优化预测结果。最后将输出较为可信的区域给 O-Net。
O-Net (Output Network)
这一层会产生更细致的人脸信息,最后还回产生 5 个关键点 landmark。
2. facenet
facenet 是谷歌提出的人脸算法,提出 cnn+triplet loss mining 的方法,在在 LFW 数据集上,准确率为 0.9963,在 YouTube Faces DB 数据集上,准确率为 0.9512。我们来看一下 facenet 的网络结构:
DEEP ARCHITECTURE 是一个卷积网络,它可以是 Mobilenet 或者是 ResnetV1,主要作用就是用于特此区域 在通过 L2 正则化,再 embeding 成一个 128 维的向量,再用 triplet loss 计算损失。但再实际的训练中,还要引入一个优化器来帮助网络收敛。
facenet 是将人脸特征映射到 128 维的特征空间中,然后通过计算欧式距离来判断分类。上图就是 triplet loss 的学习过程。
3.SVM(Support Vector Machine)
SVM 支持向量机,将 facenet 提取到的 128 维的特征向量,输入到支持向量机中进行训练,用支持向量机来做分类判断。那有人可能就要问了,为什么不在 facenet 后之间链接全连接层来做分类呢。因为这样的做的话,需要训练的参数会很多,如果你要去识别 1w 个人 那么参数就是 128*1w。如果每个人只提供的图片不多。那么网络是很难训练的。对于支持向量机,则需要很少的训练数据,就可以达到一个还不错的效果。支持向量机是将低维线性不可分的向量,映射到高维可分。介绍玩原理,我们可以来看如何实现的了。
二、人脸识别实现
1.准备工作
引入 mtcnn
就在代码文件的 mtcnn 文件夹
将图中的文件夹下载后放入工程目录中。或者直接使用 pip 安装:
pip install mtcnn
要求 OpenCV>=4.1 Keras>=2.0.0 先面试测试 mtcnn 的一段代码:
from mtcnn import MTCNN
import cv2
test_img = cv2.imread("imgs/img.png")
detector = MTCNN()
result=detector.detect_faces(test_img)
print(result[0]['box'])
for item in result:x,y,width,height = item['box']confidence=item['confidence']confidence=round(confidence,3)test_img = cv2.rectangle(test_img,(x,y),(x+width,y+height),color=(0,255,0),thickness=2)test_img = cv2.putText(test_img,str(confidence),(x,y-10),color=(0,0,255),fontScale=3,fontFace=cv2.FONT_HERSHEY_PLAIN,thickness=2)cv2.imshow('img',test_img)
cv2.waitKey()
运行结果:
下载 facenet 的权重文件:
下载链接:
https://pan.baidu.com/s/1DC929csx8Vtadbuk7YNTCw
提取码:atdf 放入到工程的 weights 目录下:
安装 sklearn
pip install sklearn -i https://pypi.tuna.tsinghua.edu.cn/simple
这里我们使用 sklearn 机器学习框架。
2.训练和预测
数据准备
首先我们将需要识别的人的照片,放入到以他们名字命名的目录下,用英文进行命名。如下图所示:
照片中必须只包含识别对象的单一人脸,如下图:
下面是不合法图片:
训练
训练过程如下图所示:
SVM 模型保存成 pkl 文件。 我们这里主要看训练 SVM 的过程,其他步骤的代码在 代码文件中会展示。
``` from sklearn.metrics import accuracy_score from sklearn.metrics import accuracy_score from sklearn.preprocessing import LabelEncoder # 将离散的数据转换到0~classes-1 from sklearn.preprocessing import Normalizer from sklearn.svm import SVC from get_feature import get_feature import pickle
def train(): #过去特征和对应的标签,有 X_train, y_train, X_test, y_test = get_feature() print(X_train.shape, y_train.shape, X_test.shape, y_test.shape) # 样本的特征值除以个特征值的平方和,归一化 in_encoder = Normalizer() X_train = in_encoder.transform(X_train) #编码 X_test = in_encoder.transform(X_test) #编码 # lable_encoder out_encoder = LabelEncoder() out_encoder.fit(y_train) y_train = out_encoder.transform(y_train) y_test = out_encoder.transform(y_test)
#定义支持向量机,使用线性核
model = SVC(kerne