理论基础
本算法流程就是用主成分分析(Pca)对人脸数据集进行降维,得到数个人脸特征向量。对于任意一个人脸样本,将样本数据向特征向量投影,得到的投影系数作为人脸的特征表示。使用支持向量机(SVM)对这些不同的投影系数向量分类,来进行人脸识别。
(1)人脸识别经典算法:特征脸(Eigenface)人脸识别之特征脸
(2)主成分分析:主成分分析PCA
(3)支持向量机:支持向量机SVM
实例分析
Scikit实例使用的是Labeled Faces in the Wild(LFW)数据集,大约有220M,为了避免下载麻烦或者处理数据较慢,本例使用了英国剑桥大学的AT&T人脸数据:AT&T数据集下载. 该数据集大小不到5M,有40类样本,每类中包含同一个人的10张图像(112*92)。
首先是读入数据集,将每一幅图像拉成一列,组成数据集合(112*92,400),并保存每一列数据对应的人脸标号,以及原图的高度和宽度,为了处理后还原显示。下面为数据读入代码,读入图像使用了opencv的imread函数,并将数据转换了为numpy的array便于后续操作。
PICTURE_PATH = "D:\\Data\\"
def get_Image():
for i in range(1,41):
for j in range(1,11):
path = PICTURE_PATH + "\\s" + str(i) + "\\"+ str(j) + ".pgm"
img = cv2.imread(path)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
h,w = img_gray.shape
img_col = img_gray.reshape(h*w)
all_data_set.append(img_col)
all_data_label.append(i)
return h,w
all_data_set = []
all_data_label = []
h,w = get_Image()
X = array(all_data_set)
y = array(all_data_label)
n_samples,n_features = X.shape
n_classes = len(unique(y))
target_names = []
for i in range(1,41):
names = "person" + str(i)
target_names.append(names)
print("Total dataset size:")
print("n_samples: %d" % n_samples)
print("n_features: %d" % n_features)
print("n_classes: %d" % n_classes)
然后进行数据集的划分,一部分用于训练集,另一部分用于测试集。本例使用四分之三的数据用于训练,四分之一数据用于测试。
random_state:随机数种子——其实就是该组随机数的编号,在需要重复试验的时候,保证得到一组一样的随机数。比如每次都为1,其他参数一样的情况下你得到的随机数组是一样的。当为None时,产生的随机数组也会是随机的。
随机数的产生取决于种子,随机数和种子之间的关系遵从以下两个规则:种子不同,产生不同的随机数;种子相同,即使实例不同也产生相同的随机数。
random_state 设置相同,那么当别人重新运行你的代码的时候就能得到完全一样的结果,复现和你一样的过程。如果你设置为 None,则会随机选择一个种子。
#split into a training and testing set
X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.25, random_state=42)
下面是主成分分析的部分,重点是选取保留主成分的个数,不同个数特征向量的检测性能不同,本文最后会给出一些测试结果。暂时先取前20个。
n_components = 20
print("Extracting the top %d eigenfaces from %d faces"
% (n_components, X_train.shape[0]))
t0 = time()
pca = PCA(n_components=n_components, svd_solver='randomized', #选择一种svd方式
whiten=True).fit(X_train) #whiten是一种数据预处理方式,会损失一些数据信息,但可获得更好的预测结果
print("done in %0.3fs" % (time() - t0))
eigenfaces = 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) #得到训练集投影系数
X_test_pca = pca.transform(X_test) #得到测试集投影系数
print("done in %0.3fs" % (time() - t0))
这前20个特征向量就是特征脸,可以显示一些出来看看:
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)
plt.xticks(())
plt.yticks(())
eigenface_titles = ["eigenface %d" % i for i in range(eigenfaces.shape[0])]
plot_gallery(eigenfaces, eigenface_titles, h, w)
plt.show()
在上一步,得到了特征脸,并且得到了训练集和测试集在特征向量的投影系数,下面就是利用训练集的(投影系数+标号)训练出一个SVM分类器,用于测试集的识别啦。本例SVM使用的是最常用的高斯核(也称RBF),如果你熟悉SVM方程的话,应该知道SVM需要确定的两个重要参数C和gama,其中C为惩罚因子,越大模型对训练数据拟合程度越高,gama是高斯核参数。一般采用网格搜索的方式及C和gama各给一系列值,分别训练模型,选择最优的C和gama。例如C : 2.-5,2.-3,…,2.^15 , gama:2.-15,2.-13,…,2.^3(随便举得例子)。此外,由于防止对训练数据过拟合,一般寻找最优参数的模型检验方式是交叉检验(cross-validation),即初始化多组训练数据和测试数据取平均结果,GridSearchCV函数正是如此。
print("Fitting the classifier to the training set")
t0 = time()
param_grid = {'C': [1e3, 5e3, 1e4, 5e4, 1e5],
'gamma': [0.0001, 0.0005, 0.001, 0.005, 0.01, 0.1], }
clf = GridSearchCV(SVC(kernel='rbf', class_weight='balanced'), param_grid)
#class_weight='balanced'表示调整各类别权重,权重与该类中样本数成反比,
#防止模型过于拟合某个样本数量过大的类
clf = clf.fit(X_train_pca, y_train)
print("done in %0.3fs" % (time() - t0))
print("Best estimator found by grid search:")
print(clf.best_estimator_)
核心部分已经完成了,最后就是用训练好的SVM分类器去做识别了。
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))
print(classification_report(y_test, y_pred, target_names=target_names))
print(confusion_matrix(y_test, y_pred, labels=range(n_classes)))
左图输出结果的表头,右图为输出结果的表尾,几项指标含义分别为(查准率/查全率/F1值/测试样本数),有关这几项含义可以看看:评价指标。另外就是不知道为什么只有39类,可是划分数据时是有第40的,调试了好一会儿,也没找到错在哪了。
再可视化一下测试结果:
# plot the result of the prediction on a portion of the test set
def title(y_pred, y_test, target_names, i):
pred_name = target_names[y_pred[i]-1]
true_name = target_names[y_test[i]-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)
最后再说一下主成分数对识别结果性能的影响:
这是n=10的测试结果,比n=20更好。
这是n=5的测试结果,比n=20更差。
这两组测试说明,选择主成分数不能过大也不能过小,太多则表示的过于具体,太小则表示的过于笼统,检测效果均不佳,实际使用时要根据实验结果找到最好的主成分数。
以上分代码没写载入库文件,完整代码:
# -*- coding:utf-8 -*-
from __future__ import print_function
from time import time
import logging,cv2
import matplotlib.pyplot as plt
from numpy import *
from sklearn.model_selection import train_test_split,GridSearchCV
from sklearn.metrics import classification_report,confusion_matrix
from sklearn.decomposition import PCA
from sklearn.svm import SVC
PICTURE_PATH = "./att_faces"
def get_Image():
for i in range(1, 41):
for j in range(1, 11):
path = PICTURE_PATH + "\\s" + str(i) + "\\" + str(j) + ".pgm"
img = cv2.imread(path)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
h, w = img_gray.shape
img_col = img_gray.reshape(h * w)
all_data_set.append(img_col)
all_data_label.append(i)
return h, w
all_data_set = []
all_data_label = []
h, w = get_Image()
X = array(all_data_set)
y = array(all_data_label)
n_samples, n_features = X.shape
n_classes = len(unique(y)) #numpy.unique()
target_names = []
for i in range(1, 41):
names = "person" + str(i)
target_names.append(names)
print("Total dataset size:")
print("n_samples: %d" % n_samples)#400
print("n_features: %d" % n_features)#10304
print("n_classes: %d" % n_classes)#40
# split into a training and testing set
'''
random_state:随机数种子——其实就是该组随机数的编号,在需要重复试验的时候,保证得到一组一样的
随机数。比如每次都为1,其他参数一样的情况下你得到的随机数组是一样的。当为None时,产生的随机数组
也会是随机的。
随机数的产生取决于种子,随机数和种子之间的关系遵从以下两个规则:种子不同,产生不同的随机数;
种子相同,即使实例不同也产生相同的随机数。
'''
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)
n_components = 10
print("Extracting the top %d eigenfaces from %d faces"% (n_components, X_train.shape[0]))
t0 = time()
#选择一种svd方式,whiten是一种数据预处理方式,会损失一些数据信息,但可获得更好的预测结果
pca = PCA(n_components=n_components, svd_solver='randomized',whiten=True).fit(X_train)
print("done in %0.3fs" % (time() - t0))
eigenfaces = 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)#得到训练集投影系数
X_test_pca = pca.transform(X_test)#得到测试集投影系数
print("done in %0.3fs" % (time() - t0))
print("Fitting the classifier to the training set")
t0 = time()
'''C为惩罚因子,越大模型对训练数据拟合程度越高,gama是高斯核参数'''
param_grid = {'C': [1e3, 5e3, 1e4, 5e4, 1e5],
'gamma': [0.0001, 0.0005, 0.001, 0.005, 0.01, 0.1], }
'''
class_weight='balanced'
表示调整各类别权重,权重与该类中样本数成反比,防止模型过于拟合某个样本数量过大的类
'''
clf = GridSearchCV(SVC(kernel='rbf', class_weight='balanced'), param_grid)
clf = clf.fit(X_train_pca, y_train)
print("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))
print(classification_report(y_test, y_pred, target_names=target_names))
print(confusion_matrix(y_test, y_pred, labels=range(n_classes)))
def plot_gallery(images, titles, h, w, n_row=3, n_col=3):
"""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)
#cmap: 颜色图谱(colormap), 默认绘制为RGB(A)颜色空间
plt.imshow(images[i].reshape((h, w)), cmap=plt.cm.gray)
plt.title(titles[i], size=12)
plt.xticks(())
plt.yticks(())
# plot the result of the prediction on a portion of the test set
def title(y_pred, y_test, target_names, i):
pred_name = target_names[y_pred[i] - 1]
true_name = target_names[y_test[i] - 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 the gallery of the most significative eigenfaces
eigenface_titles = ["eigenface %d" % i for i in range(eigenfaces.shape[0])]
plot_gallery(eigenfaces, eigenface_titles, h, w)
plt.show()
imshow()函数格式为:
matplotlib.pyplot.imshow(X, cmap=None)
-
X: 要绘制的图像或数组。
-
cmap: 颜色图谱(colormap), 默认绘制为RGB(A)颜色空间。
其它可选的颜色图谱如下列表:
颜色图谱 | 描述 |
---|---|
autumn | 红-橙-黄 |
bone | 黑-白,x线 |
cool | 青-洋红 |
copper | 黑-铜 |
flag | 红-白-蓝-黑 |
gray | 黑-白 |
hot | 黑-红-黄-白 |
hsv | hsv颜色空间, 红-黄-绿-青-蓝-洋红-红 |
inferno | 黑-红-黄 |
jet | 蓝-青-黄-红 |
magma | 黑-红-白 |
pink | 黑-粉-白 |
plasma | 绿-红-黄 |
prism | 红-黄-绿-蓝-紫-…-绿模式 |
spring | 洋红-黄 |
summer | 绿-黄 |
viridis | 蓝-绿-黄 |
winter | 蓝-绿 |
来源 :https://blog.csdn.net/mingtian715/article/details/54380623
https://blog.csdn.net/vicdd/article/details/52647906