基于子空间人脸识别算法的基本流程
- 读取人脸图片数据库的图像及标签,并进行灰度化处理;若已经是灰度处理过则不用进行灰度化处理;
- 将读入的图像先转化为二维矩阵,然后按照列进行合并堆叠,得到原始数据矩阵,如果数据中各个特征的值相差较大的话,可以对原始矩阵进行归一化或者数据标准化处理;
- 使用PCA算法对原始数据矩阵进行特征分析与降维,提取出主要特征;
- 将使用PCA降维处理得到的主成分二维矩阵数据结合SVM的分类器进行建模,得到训练好的模型;
- 接下来做预测分析,读取待识别的图像测试集,将其转化为与训练集中的同样的向量表示,遍历训练集,寻找到与待识别图像的差值小于阈值的图像,作为识别结果。
主要功能模块简介
-
数据预处理模块:由于图片的bmp格式或者pgm格式,所以需要先将读取进来的图片转化为二维矩阵,并保存为数据文件。目的是将图片信息切分为特征和标签,便于我们进行训练模型。
-
数据加载模块:将处理后的数据从文件中读入,同时切分为训练集和测试集。
-
模型训练模块:对训练集和测试集进行划分比例,接着使用PCA算法进行降维,再创建SVM分类器对训练集
行训练,得到训练好的SVM模型。使用该模型进行预测分析,计算出准确率。
-
绘制ROC曲线模块:根据学习器的预测结果对样例进行排序,并逐个作为正例进行预测,以假正例率为横轴,真正例率为纵轴可得到ROC曲线。
-
模型指标评估模块:返回F1-分数、precision和recall等评估参数。
实验设置
实验环境
- Win10 + python3.8
实验步骤
-
读取图片数据,进行数据预处理:
-
读入的是图片,所以针对其尺寸将其转为矩阵,如ORL每张图片大小是92112,共有400张,所以将所有图片转化为一个(400, 92112)的二维矩阵,即大小为(400, 10304);同理,Yale每张图片大小是100*100,共有165张,所以对应的二维矩阵是(165,10000);
-
将得到的二维矩阵保存到文件中,便于下次的读取;
-
-
切分训练集和测试集:
-
对于ORL人脸库,训练集和测试集之比设置为70%:30%;
对于Yale人脸库,训练集和测试集之比设置为67.5%:32.5%;
-
通过在一定的范围内迭代参数,ORL-svm模型最优随机种子random_state设置为14,效果如下图所示;Yale-svm模型最优随机种子random_state设置为13;
-
- 对数据集进行降维:
- 通过在一定的范围内迭代参数,最优维度设置均降至50维,迭代参数择优的做法同上;
- 建立SVM支持向量机模型:
- 径向基核函数采用高斯核函数,即设置kernel=’rbf’ ;
- 对训练集进行训练;
- 对测试集进行验证测试,做预测分析;
- 对模型性能进行评估;
- 记录实验结果。
数据集简介
-
ORL人脸库:由英国剑桥大学AT&T实验室创建,包含40个不同个体,每个个体包含10张不同姿态的人脸图像,共400张面部图像,部分人脸图像包括了姿态,表情和面部饰物的变化,其深度旋转和平面旋转可达20度;ORL人脸数据库中每个采集对象的10幅样本图片都经过归一化处理的灰度图像,图像尺寸均为92×112,图像背景为黑色。
-
YALE人脸数据库:由耶鲁大学计算视觉与控制中心创建,包含包含15个个体,每个个体包含11张不同姿态的人脸图像,共165张图片,包含光照、表情和姿态的变化。Yale人脸数据库中每个个体采集的样本包含更明显的光照、表情和姿态以及遮挡变化。
模型评估
准确率
-
ORL人脸数据集:99.167%
-
F1-分数:0.9917
-
Recall召回率:0.9917
-
-
Yale人脸数据集准确率:96.297%
-
F1-分数:0.9630
-
Recall召回率:0.9630
-
ORL数据集的模型性能评估:
-
ROC曲线是评估模型效果的重要工具,其X轴为假阳性率,Y轴为真阳性率即召回率recall,其意义在于,在真阳性率时,模型同时判错阳性的样本比例,因此曲线越陡,越表示模型效果好。ROC曲线下AUC面积越大表示模型效果越好,我们可以利用sklearn 中的roc_curve函数方便的画ROC曲线。
-
下图是ORL人脸库使用测试集验证的模型ROC曲线图:
Yale数据集的模型性能评估:
- Yale人脸库使用测试集验证的模型ROC曲线图 :
- 通过观察模型的f1-score,recall,准确率可以发现,两个人脸库训练的模型识别效果比较好,准确率达到均超过95%;从ORL数据集训练的模型ROC曲线可以看到曲线较陡,且下部分围成的面积较大,所以模型的效果较好;对于Yale数据集训练的模型ROC曲线,效果相对于ORL而言没有那么好,这其中的原因可能是Yale的样本具有更明显的光照、表情和姿态以及遮挡变化。
结果分析
实现其他人脸识别算法并与子空间算法进行比较与讨论
- 对比子空间算法与其他人脸识别算法的效果,此处以ORL人脸库为例。
- 使用神经网络进行人脸识别,设置两个全连接隐藏层,优化器采用Adam。模型收敛时结果达到90%,相对于子空间算法而言,效果没有那么好。
- 模型已经收敛 :
总结
通过对ORL和Yale人脸数据集的识别实验,我总结出了一下结论:
-
从实验结果来看,通过不断优化模型参数,两个人脸库在测试集上的识别准确率都超过了95%,甚至ORL的识别准确率高达99.167%,这说明PCA降维结合svm分类器得到的效果是很好的。
-
人脸图像数据识别的准确率与人脸样本复杂程度有关系。比如ORL的人脸样本是相对于Yale的人脸样本,其姿势更加端正,而且面部表情的变化没有那么大,所以识别起来更加容易一些。另外样本的环境如果越复杂,识别起来难度就越大。
-
调参方面需要技巧。在构建模型时,模型的参数往往对模型效果具有较大的影响,如果通过设置多层遍历参数的方式来择优参数,那样时间复杂度会达到t(t>=2)次方级别。因此可以先固定几个较优的参数,然后针对某一个参数进行迭代,这样使得时间复杂度降到O(n),大大提高了调参效率。
代码实现
ORL人脸库识别核心源码
# orl_face_recognition.py
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
# 得到模型的评估指标,F1-分数,召回率,ROC曲线
from sklearn.metrics import classification_report, roc_curve, auc, f1_score, recall_score
class FaceRecognition:
# 初始化参数
def __init__(self, photo_path, save_file=\'data.txt\'):
"""
:param photo_path: 图片路径
:param save_file: 将图片转化为二维数据的文件名
"""
self.path = photo_path
self.save_file = save_file
self.y_test = None
self.y_predict = None
self.model = None # 保存最终训练得到的模型
# 处理数据,将图片数据转化为二维矩阵
def handle_data(self):
# 标签列添加到矩阵的最后一列
label_list = []
# 将每一行的特征向量进行堆叠,最后得到(400,10305)大小的二维特征矩阵
stack_matrix = np.array([[0]])
for i in range(1, 41):
# 加入每张图片的标签
label_list.append(i)
class_matrix = np.array(label_list, ndmin=2)
for j in range(1, 11):
self.path = photo_path.format(i, j)
x = Image.open(self.path)
# 转换为narray的结构,并转为二维矩阵
data = np.reshape(np.asarray(x), (1, -1))
# print(x_data.shape) # 得到的维度是(1, 10304)
one_data = np.column_stack((data, class_matrix))
# 第一次不合并
if i == 1 and j == 1:
stack_matrix = one_data
continue
stack_matrix = np.row_stack((stack_matrix, one_data))
label_list.pop()
np.savetxt(self.save_file, stack_matrix)
# 加载读入数据
def load_data(self):
file = self.save_file
# 读入处理后的图片二维矩阵文件
train_data = np.loadtxt(file)
data = train_data[:, :10304] # 取出特征数据
target = train_data[:, -1] # 取出标签数据
return data, target
# 训练模型,返回准确率和模型
def train_model(self, n_components=50, random_state=14):
"""
:param n_components: PCA降维的维度
:param random_state: 设置随机种子,调整后得到最佳模型
:return: 返回准确率和模型
"""
x_data, y_data = self.load_data()
x_train, x_test, y_train, self.y_test = train_test_split(x_data, y_data,
test_size=0.3,
random_state=random_state)
# 利用PCA将特征降至50维
pca = PCA(n_components=n_components)
x_train = pca.fit_transform(x_train)
self.model = SVC(kernel=\'rbf\', C=10) # C是惩罚参数
self.model.fit(x_train, y_train)
# 利用在训练集上进行降维的PCA对测试数据进行降维,保证转换矩阵相同
x_test_pca = pca.transform(x_test)
self.y_predict = self.model.predict(x_test_pca)
score = self.model.score(x_test_pca, self.y_test)
print(classification_report(self.y_test, self.y_predict))
return score, self.model
# 画ROC图
def draw_ROC(self):
fpr, tpr, thresholds = roc_curve(self.y_test, self.y_predict, pos_label=40)
roc_auc = auc(fpr, tpr)
plt.title(\'ROC\')
plt.plot(fpr, tpr, \'b\', label=\'AUC = %0.4f\' % roc_auc)
plt.legend(loc=\'lower right\')
plt.plot([0, 1], [0, 1], \'r--\')
plt.ylabel(\'TPR\')
plt.xlabel(\'FPR\')
plt.show()
# 返回模型评估参数, 打印出F1-分数和召回率评估参数
def model_evaluation(self):
print(\'recall: %.4f\' % recall_score(self.y_test, self.y_predict, average=\'micro\'))
print(\'f1-score: %.4f\' % f1_score(self.y_test, self.y_predict, average=\'micro\'))
if __name__ == \'__main__\':
# 传入图片路径和需要保存的文件名
photo_path = \'./ORL/s{}_{}.bmp\'
save_file = \'data.txt\'
recognition = FaceRecognition(photo_path=photo_path, save_file=save_file)
recognition.handle_data()
recognition.load_data()
acc, model = recognition.train_model(50, 14)
print(\'测试集上的预测准确率为:{}\'.format(acc))
recognition.draw_ROC()
recognition.model_evaluation()
Yale人脸库识别核心源码
# yale_face_recognition.py
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
# 得到模型的评估指标,F1-分数,召回率,ROC曲线
from sklearn.metrics import classification_report, roc_curve, auc, f1_score, recall_score
class FaceRecognition:
# 初始化参数
def __init__(self, photo_path, save_file=\'yale_data.txt\'):
"""
:param photo_path: 图片路径
:param save_file: 将图片转化为二维数据的文件名
"""
self.path = photo_path
self.save_file = save_file
self.y_test = None
self.y_predict = None
self.model = None # 保存最终训练得到的模型
# 处理数据,将图片数据转化为二维矩阵
def handle_data(self):
# 标签列添加到矩阵的最后一列
label_list = []
# 将每一行的特征向量进行堆叠,最后得到(165,10000)大小的二维特征矩阵
stack_matrix = np.array([[0]])
for i in range(1, 16):
# 加入每张图片的标签
label_list.append(i)
class_matrix = np.array(label_list, ndmin=2)
for j in range(1, 12):
self.path = photo_path.format(i, j)
x = Image.open(self.path)
# 转换为narray的结构,并转为二维矩阵
data = np.reshape(np.asarray(x), (1, -1))
# print(x_data.shape) # 得到的维度是(1, 10304)
one_data = np.column_stack((data, class_matrix))
# 第一次不合并
if i == 1 and j == 1:
stack_matrix = one_data
continue
stack_matrix = np.row_stack((stack_matrix, one_data))
label_list.pop()
np.savetxt(self.save_file, stack_matrix)
# 加载读入数据
def load_data(self):
file = self.save_file
# 读入处理后的图片二维矩阵文件
train_data = np.loadtxt(file)
data = train_data[:, :10000] # 取出特征数据
target = train_data[:, -1] # 取出标签数据
return data, target
# 训练模型,返回准确率和模型,并打印出F1-分数和召回率等评估参数
def train_model(self, n_components=50, random_state=13):
"""
:param n_components: PCA降维的维度
:param random_state: 设置随机种子,调整后得到最佳模型
:return: 返回准确率和模型
"""
x_data, y_data = self.load_data()
x_train, x_test, y_train, self.y_test = train_test_split(x_data, y_data, test_size=0.325, random_state=random_state)
# 利用PCA将特征降至50维
pca = PCA(n_components=n_components, whiten=True)
x_train = pca.fit_transform(x_train)
self.model = SVC(kernel=\'rbf\', C=50) # C是惩罚参数
self.model.fit(x_train, y_train)
# 利用在训练集上进行降维的PCA对测试数据进行降维,保证转换矩阵相同
x_test_pca = pca.transform(x_test)
self.y_predict = self.model.predict(x_test_pca)
score = self.model.score(x_test_pca, self.y_test)
print(classification_report(self.y_test, self.y_predict))
return score, self.model
# 画ROC图
def draw_ROC(self):
fpr, tpr, thresholds = roc_curve(self.y_test, self.y_predict, pos_label=15)
roc_auc = auc(fpr, tpr)
plt.title(\'ROC\')
plt.plot(fpr, tpr, \'b\', label=\'AUC = %0.4f\' % roc_auc)
plt.legend(loc=\'lower right\')
plt.plot([0, 1], [0, 1], \'r--\')
plt.ylabel(\'TPR\')
plt.xlabel(\'FPR\')
plt.show()
# 返回模型评估参数
def model_evaluation(self):
print(\'recall: %.4f\' % recall_score(self.y_test, self.y_predict, average=\'micro\'))
print(\'f1-score: %.4f\' % f1_score(self.y_test, self.y_predict, average=\'micro\'))
if __name__ == \'__main__\':
# 传入图片路径和需要保存的文件名
photo_path = \'./Yale/{}/s{}.bmp\'
save_file = \'yale_data.txt\'
recognition = FaceRecognition(photo_path=photo_path, save_file=save_file)
recognition.handle_data()
recognition.load_data()
acc, model = recognition.train_model()
print(\'测试集上的预测准确率为:{}\'.format(acc))
recognition.draw_ROC()
recognition.model_evaluation()