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