基于PCA的人脸识别技术
基于pca的人脸识别楼下早餐店老板最近总夸我“记性越来越差脸越来越灵”——以前他得眯着眼睛记我常要热豆浆加半颗茶叶蛋现在机器“叮”一声屏幕上跳出我半年前拍的皱巴巴会员照直接自动扣钱递卡套。这种瞬间总让我有点恍惚高中啃《数学选修》里那堆“特征脸”时哪能想到这玩意儿居然成了帮我省掉报菜名以及偶尔忘带手机尴尬的救星其实早餐机用的大概率不是纯PCA但它绝对是“从一堆像素里扒人脸核心特征”的祖师爷级算法——说白了就是把一张几百几千像素的人脸压缩成几十几百个小数字还能靠这些小数字认出“这是谁”。先别慌着回忆矩阵正交变换我们用大白话加个超简化的“像素脸”例子玩明白。假设现在有个超小的像素摄像头只能拍3x39个像素的黑白照片黑是0白是1半黑半白我们用0.5这种中间数方便计算。现在我找了三个“模特”拍了两张照片模特1是个只有眼睛白、其他全黑的小圆脸哦不对是方块脸两张是[[0, 1, 0],[1, 1, 1],[0, 1, 0]]模特2是只有额头和下巴白的长脸两张是[[1, 1, 1],[0, 0, 0],[1, 1, 1]]模特3是左半脸黑、右半脸白的阴阳脸两张是[[0, 0, 0.5, 1, 1, 1],[0, 0, 0.5, 1, 1, 1],[0, 0, 0.5, 1, 1, 1]基于pca的人脸识别]哦等下阴阳脸搞错了尺寸统一3x3改改[[0, 0.5, 1],[0, 0.5, 1],[0, 0.5, 1]]现在第一步把所有照片拍扁——PCA才不管你是圆是长是阴阳它只认一串数字。模特1的第一张就是[0,1,0,1,1,1,0,1,0]第二张差不多哦为了让例子更真实给模特1的第二张加个小“表情变化”——比如左眼眨了一下左上角1变成0.8[0,0.8,0,1,1,1,0,1,0]。第二步求“平均脸”——把刚才6张3人x2拍扁的像素加起来除以6。算一下大概额头第一行的三个数模特1两张00 模特2两张11 模特3两张00/6不对模特3额头第一个是0第二个0.5第三个1哦对分每个位置加比如第1个像素左上角00 11 00 2 → 2/6≈0.33第2个第一行中间10.8 11 0.50.54.8 → 4.8/60.8第3个右上角00 11 114 → 4/6≈0.67第4个第二行第一个11 00 002 → 0.33……以此类推最后得到一串9个数字的“平均脸”大概就是三个模特揉成一团、没有明显表情/左右偏的样子。第三步把每张脸减去平均脸——这一步叫“去中心化”去掉所有人脸的“共性”剩下的就是“个性”比如小圆脸剩下的就是眼睛周围比平均亮、额头下巴暗长脸剩下的是额头下巴亮、中间暗阴阳脸剩下的是左边暗右边亮。第四步也是最硬核但我们可以“偷懒”理解的一步——找主成分PC也就是网上常说的“特征脸”。这些主成分其实就是刚才剩下的“个性像素串”里最能区分不同人的方向比如第一个主成分可能是“左右脸谁亮”刚好戳中阴阳脸第二个是“上下脸谁亮”戳中长脸和小圆脸的区别第三个可能是“表情——比如眼睛眨不眨”戳中模特1两张照片的区别……到这里我们就可以把原来9维的像素串投影到前k个主成分上变成k维的小数字了。比如k3每张脸就从9个数字变成3个还能画在三维空间里看——阴阳脸在一个轴长脸和小圆脸在另一个轴模特1的两张照片在第三个轴附近完美分开。哦对了不能光说不练上一段Python代码试试手——这次不用超简化的3x3用现成的小数据集sklearn.datasets.fetchlfwpeople就是LFWLabeled Faces in the Wild里的名人脸每张是250x250的但我们可以缩小点不然算死电脑。import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import fetch_lfw_people from sklearn.model_selection import train_test_split from sklearn.decomposition import PCA from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score lfw_people fetch_lfw_people(min_faces_per_person20, resize0.25) X lfw_people.data # 拍扁后的像素串(n_samples, n_pixels)这里应该是(1560, 32*321024) y lfw_people.target # 每个人的编号 target_names lfw_people.target_names # 编号对应的名字比如Tony Blair, George W Bush这些 # 先看看数据集长啥样画9张平均脸不对画9张原始名人脸 plt.figure(figsize(10, 10)) for i in range(9): plt.subplot(3, 3, i1) plt.imshow(X[i].reshape(32, 32), cmapgray) plt.title(target_names[y[i]], fontsize8) plt.axis(off) plt.show() # 2. 求平均脸PCA其实会自动帮我们去中心化但手动算一下看看更直观 mean_face X.mean(axis0) plt.figure(figsize(4, 4)) plt.imshow(mean_face.reshape(32, 32), cmapgray) plt.title(Average Face of LFW (at least 20 pics)) plt.axis(off) plt.show() # 3. 找主成分取前150个吧一般LFW人脸识别k取100-200就行 pca PCA(n_components150, whitenTrue) # whitenTrue就是把投影后的主成分标准化方便后面分类 X_pca pca.fit_transform(X) # 训练PCA 把原始X投影到150维 # 看看前9个主成分也就是特征脸长啥样有意思的地方来了 plt.figure(figsize(10, 10)) for i in range(9): plt.subplot(3, 3, i1) plt.imshow(pca.components_[i].reshape(32, 32), cmapgray) plt.title(fPC {i1}, fontsize8) plt.axis(off) plt.show() # 4. 来个简单的KNN分类试试人脸识别效果 # 先把数据分成训练集和测试集 X_train, X_test, y_train, y_test train_test_split(X_pca, y, test_size0.3, random_state42) # 训练KNN knn KNeighborsClassifier(n_neighbors5) knn.fit(X_train, y_train) # 预测测试集 y_pred knn.predict(X_test) # 看看准确率 print(fAccuracy: {accuracy_score(y_test, y_pred):.2f}) # 画几张预测对的和错的 def plot_prediction(indices, title): plt.figure(figsize(10, 5)) for i, idx in enumerate(indices): plt.subplot(2, 5, i1) # 这里要注意X_pca是投影后的要恢复原始脸得用pca.inverse_transform # 不过我们这里直接用X_test对应的原始X更清楚 original_idx np.where(X_pca X_test[idx])[0][0] # 偷懒找原始索引的方法别学太死 plt.imshow(X[original_idx].reshape(32, 32), cmapgray) true_name target_names[y[original_idx]] pred_name target_names[y_pred[idx]] plt.title(fTrue: {true_name}\nPred: {pred_name}, fontsize6) plt.axis(off) plt.suptitle(title, fontsize12) plt.show() # 找前5个预测对的 correct_indices np.where(y_pred y_test)[0][:5] # 找前5个预测错的 wrong_indices np.where(y_pred ! y_test)[0][:5] plot_prediction(correct_indices, Correct Predictions) plot_prediction(wrong_indices, Wrong Predictions)等下跑这段代码的话首先得安装numpy matplotlib scikit-learn这三个库然后耐心等LFW下载第一次可能慢是从国外服务器拉的换个镜像或者挂个小梯子也行。那这段代码的结果大概是啥样的首先前9张原始名人脸应该能认出几个政客然后平均脸——哦对LFW里很多美国人白人尤其是George W Bush照片特别多大概有500多张所以平均脸会有点像他但又模糊不清所有五官都揉在一起然后最有意思的特征脸来了PC1大概是“整体亮度头发多少”PC2是“左右脸不对称或者性别”哦不对性别在PCA k比较小的时候可能体现不出来但k大一点会有PC3可能是“表情比如皱眉或者笑”然后准确率大概在0.7-0.8之间主要是因为我们把照片缩得太小了而且KNN是个很简单的分类器要是换成SVM支持向量机以前LFW人脸识别常用的准确率能冲到0.85-0.9。再看看预测错的那些脸要么是光线太暗要么是表情太夸张比如吐舌头、戴墨镜要么是和另一个人长得太像——这也是纯PCA的缺点它是线性的、基于全局像素的只看“整体五官怎么分布”不管“局部细节比如眼睛旁边的痣、牙齿的排列”也不管“姿势变化比如歪头、侧脸”“光照变化比如背光、侧光”——这些现在都是用深度学习比如CNN卷积神经网络解决的但PCA作为入门算法能把“降维特征提取分类”这一套流程讲得明明白白绝对是每个想学机器学习/计算机视觉的人必踩的坑哦不必走的路。最后再补个冷知识PCA用于人脸识别的方法最早是由Sirovich和Kirby在1987年提出来的当时叫“Eigenfaces特征脸”后来Turk和Pentland在1991年把它改进成了实用的人脸识别系统——那时候深度学习还没影呢这个算法火了好多年直到2014年Facebook发布DeepFace用CNN的准确率和人差不多才慢慢淡出主流。但直到现在很多小场景比如考勤机的辅助识别、人数不多的门禁系统还是会用PCAKNN/SVM因为它简单、不需要大量数据训练、跑起来快。