一个简单的完整人脸识别系统

from __future__ import print_function # 便于测试新版本函数from time import time # 从time模块导入time,因为有些步骤需要计时import logging # 打印出一些程序进展信息import matplotlib.pyplot as plt # 绘图的包,即最后将我们预测出来的人脸打印出来from sklearn.model_selection import train_test_splitfrom sklearn.datasets import fetch_lfw_peoplefrom sklearn.model_selection import GridSearchCVfrom sklearn.metrics import classification_reportfrom sklearn.metrics import confusion_matrixfrom sklearn.decomposition import PCAimport numpy as npfrom sklearn.svm import SVCfrom sklearn import svm# 打印输出日志信息logging.basicConfig(level=logging.INFO, format='%(asctime)s%(message)s')# 使用户外脸部数据集lfw(Labeled Faces in the Wild)# minfaces_per_person:int,可选默认无,提取的数据集仅保留包含min_faces_per_person不同图片的人的照片# resize调整每张人脸图片的比例,默认是0.5 ,min_faces_per_person=70lfw_people = fetch_lfw_people(min_faces_per_person=50, resize=0.4)# 返回数据集有多少个实例,h是多少,w是多少n_samples, h, w = lfw_people.images.shapeprint('h=',h)print('w=',w)X = lfw_people.data # X矩阵用来装特征向量,得到数据集的所有实例,每一行是一个实例,每一列是个特征值# X矩阵调用shape返回矩阵的行数和列数,n_features = X.shape[1] # X.shape[1]返回矩阵的列数,对应的特征向量的维度或者特征点多少# 获取特征结果集,提取每个实例对应的每个人脸Y = lfw_people.target # y为classlabel目标分类标记,即不同人的身份target_names = lfw_people.target_names # 数据集中有多少个人,以人名组成列表返回n_classes = target_names.shape[0] # shape[0]就是多少行,多少个人,多少类print("Total dataset size:") # 数据集中信息print("n_samples:%d" % n_samples) # 数据个数1288print("n_features:%d" % n_features) # 特征个数,维度1850print("n_classes:%d" % n_classes) # 结果集类别个数,即多少个人X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.20) # test_size=0.20表示随机抽取20%的测试集 五折交叉检验# 自己实现的PCA类及方法class GsnPCA:def __init__(self, n_components):self.n_components = n_components # 主成分的个数nself.components_ = None # 具体主成分self.dimensionReductionMatrix=Nonedef fit(self, X):def demean(X): # 均值归零return X - np.mean(X, axis=0)def Cov(X):return np.cov(X.transpose())print("step1:对样本去均值,进行中心化")X_demean=demean(X)print("step2:求特征协方差矩阵")sigma=Cov(X_demean)print("step3:求协方差矩阵的 特征值 和 特征向量")eigenValues, eigenVectors = np.linalg.eig(sigma)print("step4:将特征值从大到小排序,选择前n_components个")eigenFeatureList = []for i in range(len(eigenValues)):eigenFeatureList.append((eigenValues[i], eigenVectors[i]))# key=lambda tuple: tuple[0]表示按第1个元素(特征值)为排序标准 reverse=True表示降序排序eigenFeatureList = sorted(eigenFeatureList, key=lambda tuple: tuple[0], reverse=True)print("step5:合并前n_components个特征向量得到降维变换矩阵")self.dimensionReductionMatrix = np.array([eigenFeatureList[i][1] for i in range(n_components)])print(self.dimensionReductionMatrix)return self# 将X数据集映射到各个主成分分量中def transform(self, X):# print(self.components_.shape[1])# assert X.shape[1] == self.components_.shape[1]# return X.dot(self.components_.T)lower_X = np.matmul(X, self.dimensionReductionMatrix.transpose()).astype(np.float) # 避免出现复数return lower_Xdef inverse_transform(self, X):return X.dot(self.components_)# 采用PCA降维,原始数据的特征向量维度非常高,意味着训练模型的复杂度非常高# 保存的组件数目,也即保留下来的特征个数nn_components = 150print("Expecting the top %d EigenFaces from %faces" % (n_components, X_train.shape[0]))# 降维pca = PCA(n_components=n_components, whiten=True).fit(X_train)t0 = time()# 初始时间gsnpca = GsnPCA(n_components)gsnpca.fit(X_train)print("gsnpca done in %0.3fs" % (time() - t0))# 从人脸中提取特征点,对于人脸的一张照片提取的特征值名为eigenFaceseigenFaces = pca.components_.reshape((n_components, h, w))print("projecting the input data on the EigenFaces orthonormal basis")# t0 = time()# # 把训练集特征向量转化为更低维的矩阵# X_train_pca = pca.transform(X_train)# print(X_train_pca)X_train_pca = gsnpca.transform(X_train)print(X_train_pca)# 把测试集的特征向量转化为更低维的矩阵# X_test_pca = pca.transform(X_test)X_test_pca = gsnpca.transform(X_test)print("done in %0.3fs" % (time() - t0))# 后续# 训练一个支持向量机的分类model——构造分类器print("Fitting the classifier to the training set")t0 = time()# c是一个对错误部分的惩罚# gamma的参数对不同核函数有不同的表现,gamma表示使用多少比例的特征点# 使用不同的c和不同值的gamma,进行多个量的尝试,然后进行搜索,选出准确率最高模型param_grid = {'C': [1e3, 5e3, 1e4, 5e4, 1e5],'gamma': [0.0001, 0.0005, 0.001, 0.005, 0.01, 0.1]}# 调用SVM进行分类搜索哪对组合产生最好的归类精确度# kernel:rbf高斯径向基核函数 class_weight权重# 把所有我们所列参数的组合都放在SVC里面进行计算,最后看出哪一组函数的表现度最好clf = GridSearchCV(svm.SVC(kernel='rbf', class_weight='balanced'), param_grid=param_grid)clf = clf.fit(X_train_pca, Y_train)print("fit done in %0.3fs" % (time() - t0))print("Best estimator found by grid search:")print(clf.best_estimator_)##################进行评估准确率计算######################print("Predicting people's names on the test set")t0 = time()# 预测新的分类Y_pred = clf.predict(X_test_pca)print("done in %0.3fs" % (time() - t0))# 通过classification_report方法进行查看,可以得到预测结果中哪些是正确print(classification_report(Y_test, Y_pred, target_names=target_names))# confusion_matrix是建一个n*n的方格,横行和纵行分别表示真实的每一组测试的标记和测试集标记的差别# 混淆矩阵对角线表示的是正确的值,对角线数字越多表示准确率越高print(confusion_matrix(Y_test, Y_pred, labels=range(n_classes)))# 将测试标记过进行展示,即先弄一个通用的图片可视化函数:def plot_gallery(images, titles, h, w, n_row=3, n_col=4):"""Helper function to plot a gallery of portraits"""# 建立图作为背景# 自定义画布大小plt.figure(figsize=(1.8 * n_col, 2.4 * n_row))# 位置调整plt.subplots_adjust(bottom=0, left=.01, right=.99, top=.90, hspace=.35)for i in range(n_row * n_col):# 设置画布划分以及图像在画布上输出的位置plt.subplot(n_row, n_col, i + 1)# 在轴上显示图片plt.imshow(images[i].reshape((h, w)), cmap=plt.cm.gray)# 整个画板的标题plt.title(titles[i], size=12)# 获取或设置x、y轴的当前刻度位置和标签plt.xticks(())plt.yticks(())# 预测函数归类标签和实际归类标签打印# 返回预测人脸姓和测试人脸姓的对比titledef title(y_pred, y_test, target_names, i):# rsplit(' ',1)从右边开始以右边第一个空格为界,分成两个字符# 组成一个list# 此处代表把'姓'和'名'分开,然后把后面的姓提出来# 末尾加[-1]代表引用分割后的列表最后一个元素pred_name = target_names[y_pred[i]].rsplit(' ', 1)[-1]true_name = target_names[y_test[i]].rsplit(' ', 1)[-1]return 'predicted:%s\ntrue: %s' % (pred_name, true_name)# 预测出的人名prediction_titles = [title(Y_pred, Y_test, target_names, i) for i in range(Y_pred.shape[0])]# 测试集的特征向量矩阵和要预测的人名打印plot_gallery(X_test, prediction_titles, h, w) # 调用plot_gallery函数打印出实际是谁,预测的谁# 打印原图和预测的信息eigenFaces_titles = ["EigenFace %d" % i for i in range(eigenFaces.shape[0])]plot_gallery(eigenFaces, eigenFaces_titles, h, w) # 以及提取过特征的脸plt.show()