PCA+SVM实现ORL/Yale人脸库识别

时间:2024-02-24 08:41:43

基于子空间人脸识别算法的基本流程

  • 读取人脸图片数据库的图像及标签,并进行灰度化处理;若已经是灰度处理过则不用进行灰度化处理;
  • 将读入的图像先转化为二维矩阵,然后按照列进行合并堆叠,得到原始数据矩阵,如果数据中各个特征的值相差较大的话,可以对原始矩阵进行归一化或者数据标准化处理;
  • 使用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()