PCA+SVM實現ORL/Yale人臉庫識別


基於子空間人臉識別算法的基本流程

  • 讀取人臉圖片數據庫的圖像及標簽,並進行灰度化處理;若已經是灰度處理過則不用進行灰度化處理;
  • 將讀入的圖像先轉化為二維矩陣,然后按照列進行合並堆疊,得到原始數據矩陣,如果數據中各個特征的值相差較大的話,可以對原始矩陣進行歸一化或者數據標准化處理;
  • 使用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()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM