本文使用SVM进行人脸识别,并分析了选取不同的核函数(Kernel)以及penalty coefficient(C)对识别准确率的影响。
最后选择合适的核函数(Kernel)以及C使我们的识别率达到一个较高的水平。本文的代码实现基Python3.6 sklearn库。本文所用到的两个标准人脸库:ORL+Yale2
1、特征降维
ORL库每幅图片的大小是112*92,当用SVM进行人脸识别的时候,我们一般选择的处理方式是将图片展平(flatten)成一维数组,即112*92=10304。也即我们将每个图片当做一个实例,假设我们有400张图片,那么我们的训练集的大小将会是一个400*10304的一个二维矩阵,可以看出我们的特征维度有1W多维,虽然SVM可以处理这么高的特征维度,但是训练的时间代价就比较高,现在只是400幅图片,如果是上万张图片,训练时间可能会很久。
由于图片本身的像素之间存在着冗余性,局部邻域像素间存在的相似性,用PCA进行降维分析是一个不错的选择。但是我们需要降维到多少个特征呢?这个一般需要我们实验测试一下,对于每一个特定的kernel,都从[10 20 30 40 50]
分别测试降到不同维度时SVM表现的性能。结果如下图所示:
这里选择Components = 30,那么我们的数据集降维之后的大小就是400*30,极大的减少了数据量。
2、降维效果
那么降维之后效果如何呢?我们选择的30维的特征能否表示一幅人脸的主要特征呢?我们看看降维后的数据集中随机抽取40个实例,通过reshape回原图像尺寸,得到的Eigen Face(特征脸)
可以看到特征脸比较模糊,但是图像的大概轮廓是有了。当components增大后,得到的特征脸肯定是越来越清晰,但是准确率不一定会提高。因为参数越多,越可能会出现过拟合。
3、分析选择合适的核函数
我们通过选择了sklearn.SVM库中的四个核函数,linear, poly, rbf, sigmoid。通过交叉验证分析它们的准确性,选择一个表现最高的核作为我们最后的识别。
可以看到线性核函数表现性能最好,且线性核函数处理更快
4、确定惩罚系数C对不同的核函数的影响(注意结果都是在默认配置下的SVM上得到的)
可以看到C=10以后再增大,结果基本不变
5、训练+测试
通过以上实验分析,最终确定kernel='linear', C=10, Components=30。随机抽取40张预测结果如下图显示
在ORL库上的Accuracy为:97%
6、同样的步骤在Yale2人脸库上进行,Components=30。Eigen Face如图所示:
7、kernel='linear', C=10,Components=30,预测结果如图所示:
在Yale2上的准确率为92.8%:
8、注意事项
在使用RBF核的时候,特征要归一化处理。一般来说,使用任何一个分类器,都可以先将特征进行归一化处理,有时候可以加快收敛速度(例如梯度下降算法),有些时候将严重影响算法的性能(RBG 等)。
# !/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: wsw
# 用SVC实现人脸识别
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score, ShuffleSplit
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import numpy as np
import os
import re
import matplotlib.pyplot as plt
import random
path = r'E:\pythonfile_withpycharm\SVMLearning\faceLibrary\人脸库\ORL'
path2 = r'E:\pythonfile_withpycharm\SVMLearning\faceLibrary\人脸库\Yale2'
# 将piclist中后缀名字为.bmp图片的名字选取出来
def getPicList(filepath):
filelist = os.listdir(filepath)
piclist = []
for pic in filelist:
# os.path.splitext()用于分离文件名字和扩展名
if os.path.splitext(pic)[1] == '.bmp':
piclist.append(pic)
return piclist
pass
# 得到人脸数据
def getFaceData(picPath, piclist):
faceData = []
labels = []
count = 1
for picname in piclist:
p = plt.imread(picPath + '\\' + picname)
# 得到每幅人脸数据的大小
if count:
m, n = p.shape
count -= 1
# 将一个图片数据展成一行
data = p.ravel()
faceData.append(data)
# 搜索每个图片的类别,以下划线为结束符
pattern = re.compile('(\d+)_')
res = re.findall(pattern, picname)
# 将字符串转换为数字
label = int(res[0])
labels.append(label)
faceData = np.array(faceData)
labels = np.array(labels)
return faceData, labels, m, n
pass
# 对faceData数据降维
def dimensionReduce(faceData, labels, ncomponents):
pca = PCA(n_components=ncomponents)
reduceddata = pca.fit_transform(faceData, labels)
# print('降维后特征的方差占比:')
# print(pca.explained_variance_ratio_)
return reduceddata, pca
pass
# 用svc进行训练
def callSVC(data, labels, kernel, C=1):
# 切分数据集
xtrain, xtest, ytrain, ytest = train_test_split(data, labels, test_size=0.25, random_state=33)
# print(xtrain.shape, xtest.shape)
# 寻找测试集在数据集中的行索引
rowindex = []
for i, x in enumerate(xtest):
index = np.where(data == x)
rowindex.append([index[0][0], i])
# 将训练数据经行归一化处理
ss = StandardScaler()
xtrain = ss.fit_transform(xtrain)
xtest = ss.transform(xtest)
# 定义svc
svc = SVC(kernel=kernel, C=C)
# 进行10折交叉验证
cv = ShuffleSplit(n_splits=10, test_size=0.25, random_state=33)
scores = cross_val_score(svc, xtrain, ytrain, cv=cv, verbose=0)
print('验证集平均准确率:', np.mean(scores))
svc.fit(xtrain, ytrain)
print('训练集拟合程度:', svc.score(xtrain, ytrain))
predict = svc.predict(xtest)
print('测试集准确率:', svc.score(xtest, ytest))
return rowindex, ytest, predict
pass
# 可视化的显示部分人脸识别结果
def showFaceRecognization(facesubset, labelsubset, predictsubset, m, n):
plt.figure(figsize=(10, 6))
for i, facedata in enumerate(facesubset):
plt.subplot(4, 10, i + 1)
face = np.array(facedata).reshape(m, n)
plt.imshow(face)
plt.xticks([])
plt.yticks([])
plt.title('class%d' % labelsubset[i])
plt.xlabel('predict%d' % predictsubset[i])
# plt.show()
pass
# 显示经过PCA降维之后的特征脸
def showEigFace(reduceddata, pca, m, n):
plt.figure(figsize=(10, 6))
plt.title('Eig Face')
# 随机选取40个特征脸经行显示
nums = reduceddata.shape[0]
indexes = random.sample(range(nums), 40)
for i, index in enumerate(indexes):
plt.subplot(4, 10, i + 1)
plt.xticks([])
plt.yticks([])
eigface = pca.inverse_transform(reduceddata[index, :])
eigface = eigface.reshape(m, n)
plt.imshow(eigface)
# plt.show()
pass
# PCA降维性能分析
def pcaAnalysis(estimator, facedata, labels, kernel):
ncomponents = [10, 20, 30, 40, 50]
Accuracy = []
for ncomponent in ncomponents:
pca = PCA(n_components=ncomponent)
reduceddata = pca.fit_transform(facedata, labels)
xtrain, xtest, ytrain, ytest = train_test_split(reduceddata, labels, test_size=0.25, random_state=3)
# 数据归一化处理
ss = StandardScaler()
xtrain = ss.fit_transform(xtrain)
xtest = ss.transform(xtest)
estimator.fit(xtrain, ytrain)
Accuracy.append(estimator.score(xtest, ytest))
plt.figure(figsize=(8, 6))
plt.title('%s Kernel Accuracy With Different Components' % kernel)
plt.bar(ncomponents, Accuracy, color=['r', 'g', 'b', 'm', 'c'])
plt.xlabel('components')
plt.yticks(np.linspace(0, 1, 20))
# 给图形添加真实标注
for a, b in zip(ncomponents, Accuracy):
plt.text(a, b + 0.01, '%f' % b, ha='center', va='bottom', fontsize=7)
pass
# 得到PCA分析结果
def getPCAResult(facedata, labels):
kernels = ['linear', 'rbf', 'sigmoid', 'poly']
for kernel in kernels:
svc = SVC(kernel=kernel)
pcaAnalysis(svc, facedata, labels, kernel)
pass
# 不同的核函数性能分析
def getAccuracyWithKernel(reduceddata, labels):
kernels = ['linear', 'sigmoid', 'rbf', 'poly']
ss = StandardScaler()
xtrain, xtest, ytrain, ytest = train_test_split(reduceddata, labels, test_size=0.25, random_state=3)
xtrain = ss.fit_transform(xtrain)
xtest = ss.transform(xtest)
val_scores = []
test_scores = []
# 10折交叉验证
for kernel in kernels:
svc = SVC(kernel=kernel)
seed = random.randint(0, 40)
cv = ShuffleSplit(n_splits=10, test_size=0.1, random_state=seed)
val_scores.append(np.mean(cross_val_score(svc, xtrain, ytrain, cv=cv)))
svc.fit(xtrain, ytrain)
test_scores.append(svc.score(xtest, ytest))
plt.figure(figsize=(8, 6))
plt.bar(range(1, 5), val_scores, width=0.2, color='c', label='validation')
plt.bar(np.arange(1, 5) + 0.2, test_scores, width=0.2, color='m', label='test')
plt.legend(loc='best')
plt.yticks(np.linspace(0, 1, 20))
plt.xticks([])
# 标注数据
x = np.arange(1, 5) + 0.2
for a, b, kernel in zip(x, test_scores, kernels):
plt.text(a, b + 0.01, '%.3f' % b, ha='center', va='bottom', fontsize=7)
plt.text(a, -0.05, kernel, ha='center', va='bottom', fontsize=12)
for a, b in zip(range(1, 5), val_scores):
plt.text(a, b + 0.01, '%.3f' % b, ha='center', va='bottom', fontsize=7)
pass
# 测试惩罚参数C的影响
def getAccuracyWithC(reduceddata, labels):
clist = [0.001, 0.01, 0.1, 1, 10, 100]
kernels = ['linear', 'sigmoid', 'rbf', 'poly']
ss = StandardScaler()
xtrain, xtest, ytrain, ytest = train_test_split(reduceddata, labels, test_size=0.25, random_state=3)
xtrain = ss.fit_transform(xtrain)
xtest = ss.transform(xtest)
plt.figure(figsize=(8, 6))
color = ['r', 'g', 'b', 'y']
for i, kernel in enumerate(kernels):
test_scores = []
for c in clist:
svc = SVC(kernel=kernel, C=c)
svc.fit(xtrain, ytrain)
test_scores.append(svc.score(xtest, ytest))
plt.semilogx(clist, test_scores, '-p', c=color[i], label='%s' % kernel)
plt.legend(loc='best')
plt.yticks(np.linspace(0, 1, 20))
plt.title('Accuracy with different value of C')
plt.xlabel('Value Of C')
pass
def main(filepath):
piclists = getPicList(filepath)
facedata, labels, m, n = getFaceData(filepath, piclists)
# 得到PCA分析结果
getPCAResult(facedata, labels)
reducedData, pca = dimensionReduce(facedata, labels, 30)
# 得到不同核函数的效果
getAccuracyWithKernel(reducedData, labels)
# 得到不同C的效果
getAccuracyWithC(reducedData, labels)
rowindex, ytest, predict = callSVC(reducedData, labels, kernel='linear', C=1)
# 随机抽取40个人脸进行显示
index = random.sample(rowindex, 40)
index = np.array(index)
facesubset = facedata[index[:, 0], :]
labelsubset = labels[index[:, 0]]
predictsubset = predict[index[:, 1]]
# 可视化预测结果
showFaceRecognization(facesubset, labelsubset, predictsubset, m, n)
# 可视化降维后特征脸
showEigFace(reducedData, pca, m, n)
plt.show()
main(path2)