简介这份Matlab源码包围绕ORL人脸数据库与人脸识别任务设计适合正在学习模式识别、计算机视觉的初学者也适合需要快速搭建人脸识别实验的开发者。项目完整覆盖数据加载、图像预处理、PCA特征降维、LDA判别分析、分类预测等经典环节逻辑清晰可作为课程设计或算法对比的参考基线其中PCA用于特征降维LDA通过类间与类内散度提取判别方向二者组合可有效处理小样本识别问题。压缩包共8个文件其中5个.m源码文件对应各处理模块1个.mat文件存放人脸样本数据另附README说明与docx文档便于查阅整体仅174KB轻量易部署。已有1486人学习下载验证了其在教学与项目中的实用价值。代码经过测试校正下载后可直接复现人脸识别全流程结合README等说明能理清PCA、LDA经典算法的实现细节既便于新手按步骤上手也为开发者二次改造提供了简洁基础。1. ORL 人脸数据库为什么人脸识别的入门实验总拿这 400 张图说话ORL 人脸数据库在 MATLAB 人脸识别实验里的地位有点像 MNIST 在图像分类里的地位样本量不大、格式简单、标注干净任何一套新写出来的人脸识别算法都会先拿它验证一遍基本逻辑。它由 40 名对象各 10 张共 400 张人脸图像组成统一为 92×112 像素、256 级灰度覆盖表情、戴镜和轻微头部角度的变化。量级小而维度高正好用来检验 PCA 这类降维算法把 10304 像素压到几十个特征再分类用 MATLAB 写下来不到 80 行训练按毫秒计。接下来按“读数据 → 特征提取 → 分类评估 → 调参”的顺序把这个实验从头到尾拆开讲顺便说明哪些参数直接影响识别率、哪些做法会让结论失真。适合正在用 MATLAB 上手人脸识别算法以及需要一份稳定基线做对照实验的工程师和学生。2. 用 MATLAB 读取 ORL 图像并组织训练样本目录扫描、灰度图矢量化与预处理2.1 ORL 数据集的目录结构与关键参数ORL 数据集的镜像版本很多常见解压形态是一个根目录下挂 s1 到 s40 共四十个子目录每个子目录内是 1.pgm 到 10.pgm。早期官方包里还会附带 readme 等文本文件但实验用到的只有这 400 张图。也有部分镜像转成 jpg 或打包成 .mat读取代码大体一致区别只在 imread 的扩展名和 .mat 内部的变量结构。之所以先交代目录是因为用 MATLAB 读取时唯一稳定可靠的身份信息就是“第几个子目录 第几张图”。不要用文件名里的编号代替标签很多镜像做过改名但 s 目录层级基本没动过。ORL 的关键参数如下参数取值对实验的影响对象数40分类器输出 40 类最终混淆矩阵是 40×40每对象样本数10决定训练/测试划分上限最多每类留 9 张做训练图像尺寸92×112展平后每个样本 10304 维是 PCA 的输入规模灰度级256常见 8-bit 灰度读取后为 uint8文件格式PGMimread 可直接读无需格式转换2.2 读取 40×10 张图像的 MATLAB 代码下面的代码把 400 张图全部读入每张图展平成一行向量最终得到一个 400×10304 的矩阵 X以及对应的 400×1 标签列 Y。% 读取 ORL 全部 400 张灰度图每张图展平成一行 rootDir ORL; numSub 40; % 对象数 numFace 10; % 每对象图像数 X zeros(numSub * numFace, 92 * 112); % 预分配400×10304 Y zeros(numSub * numFace, 1); % 标签列 row 0; for s 1:numSub folder fullfile(rootDir, sprintf(s%d, s)); for k 1:numFace row row 1; img imread(fullfile(folder, sprintf(%d.pgm, k))); X(row, :) reshape(img, 1, []); % 二维图像转行向量 Y(row) s; % 标签取对象编号 1~40 end end读取逻辑有三个要点。reshape 按列优先把 92×112 矩阵展开成 1×10304 向量所有样本统一使用同一展开规则后续 PCA 不关心像素排列是否符合视觉习惯只要训练测试一致即可。标签直接取子目录编号s1 对应 1s40 对应 40省去手工维护类别名。预分配用 zeros 先建好矩阵避免循环里动态拼接400 张图总共约 3MB影响不大但换到更大数据集时动态拼接会明显拖慢速度。2.3 预处理灰度转换、直方图均衡与尺寸缩放常见的做法有三类。第一种是类型转换imread 取到的是 uint8数据范围 0~255直接算 PCA 也能跑但协方差数值量级偏大。通常用 im2double 把图像转成 double 并归一化到 0~1避免特征值数量级过大带来的数值问题。第二种是直方图均衡化可以用 histeq 对每张图做增强。ORL 的光照条件整体比较均匀这个操作带来的识别率提升往往在 1 个百分点以内不做也不影响流程跑通。真正的收益来自第三种预处理尺寸缩放。把 92×112 用 imresize 缩到 46×56像素维度从 10304 降到 2576特征值计算和内存占用都省一大截而识别率通常只下降 1~2 个百分点有时反而因为去掉高频噪声而持平。如果你的机器配置一般或者后面要反复做交叉验证先在预处理阶段缩尺寸是性价比最高的做法。2.4 切分训练集与测试集按类内编号而不是随机乱序ORL 每人 10 张有固定顺序不同编号的图像在表情和角度上并不连续。做训练测试划分时最常见的是“每类取前 k 张训练、后 10-k 张测试”这样每个人出现次数相等类别分布无偏。下面这段代码生成两个索引向量kTrain 5; trainIdx zeros(numSub * kTrain, 1); testIdx zeros(numSub * (numFace - kTrain), 1); it 1; ie 1; for s 1:numSub base (s - 1) * numFace; trainIdx(it : it kTrain - 1) (base 1 : base kTrain); it it kTrain; testIdx(ie : ie (numFace - kTrain) - 1) (base kTrain 1 : base numFace); ie ie (numFace - kTrain); end注意(base1:basekTrain)生成的是行向量赋值给列向量时需要加转置这是初学者最容易遇到的索引报错点。划分后可以用intersect(trainIdx, testIdx)检查交集空集才是干净划分。随机乱序切分理论上也可以但 ORL 样本少随机种子一变结果就波动按编号固定切分更利于实验复现。3. PCA 特征脸原理与 MATLAB 实现把 ORL 的 10304 维压到几十维3.1 为什么 PCA 是 ORL 任务的首选基准算法人脸识别的常见出发点有两种一种是直接拿像素做距离一种是先提取特征再分类。ORL 只有 400 个样本每个样本 10304 维直接算像素级距离不仅噪声大而且几乎所有样本对的欧氏距离都挤在同一量级区分度很差。PCA 在这里的作用是找一组正交方向让数据投影后方差尽可能大的方向排在前面把维度压到几十维同时保留大部分结构信息。“特征脸”这个叫法是因为 PCA 的投影方向按图像尺寸还原后看起来像某种人脸轮廓。在 ORL 这种小样本高维数据上PCA 不需要标签参与训练逻辑简单、可解释性强因此几乎所有经典人脸识别实验都先以 PCA 作为基线。3.2 小样本协方差矩阵的数学变形绕开 10304×10304 的特征值分解PCA 的核心是求协方差矩阵的特征向量。设训练样本矩阵 X 有 n 行 d 列每行是一个中心化后的样本协方差矩阵 C Xc×Xc/(n-1) 的尺寸是 d×d。在 ORL 上 d10304直接对这样大小的矩阵做 eig内存占用大计算也慢。但 ORL 的特点是 n 远小于 d所以可以用转置技巧先求 S Xc×Xc/(n-1)尺寸只有 n×n。若 v 是 S 的特征向量那么 Xc×v 就是原协方差矩阵 C 的特征向量方向。这一步在数学上等价于用奇异值分解 SVD 做 PCA但显式写出来更容易理解。得到方向后统一做列归一化就得到一组单位正交的 PCA 投影方向。3.3 PCA 训练的 MATLAB 代码把上面的思路写成函数输入训练矩阵和要保留的主成分个数输出投影方向 U、训练均值 mu 和特征值 valsfunction [U, mu, vals] pca_faces(Xtrain, numC) % Xtrain: n×d 矩阵每行一个训练样本 % numC: 保留的主成分个数 mu mean(Xtrain, 1); Xc Xtrain - mu; n size(Xc, 1); [V, D] eig(Xc * Xc); % n×n 小矩阵不构造 d×d vals diag(D); [vals, order] sort(vals, descend); V V(:, order); U Xc * V; % 映射回 d 维空间 U U ./ vecnorm(U, 2, 1); % 单位化得到正交基 U U(:, 1:min(numC, size(U, 2))); end调用方式也很直接[U, mu] pca_faces(Xtrain, 100); projTrain (Xtrain - mu) * U;eig 对小矩阵返回的特征值按升序排列所以 sort 之后要翻转成降序取前面 numC 列。vecnorm 是 R2017 以后引入的函数如果 MATLAB 版本较老把归一化那行换成U U ./ sqrt(sum(U.^2, 1));即可。提示这里对 U 做单位化等价于保留 PCA 方向但不做白化。如果想保留各维度原本的方差尺度删掉归一化那行投影坐标在欧氏距离中会自动放大方差大的方向。训练之后还可以把前 16 个特征脸画出来检查学到的是不是有意义的模式figure; for i 1:16 subplot(4, 4, i); face reshape(U(:, i), 112, 92); imshow(im2uint8(mat2gray(face)), []); endmat2gray 做 min-max 拉伸是因为特征向量的像素值有正有负直接显示会是一篇灰色噪声im2uint8 只用于显示不参与后续计算。3.4 主成分个数怎么选方差、识别率曲线与上限主成分个数是可以直接影响识别率的参数。常用判断标准有两个一是按特征值计算累计方差贡献率cumsum(vals)/sum(vals)二是直接跑识别率曲线。两者的最优位置并不总重合因为方差覆盖率关注的是重构误差而分类关注的是类间可分性。在 ORL 上做 PCA 最近邻实验时保留 30~50 个主成分已经能覆盖大部分方差识别率进入稳定区继续加到 100 左右通常会再涨一点但涨幅很小。若是用全部 400 张图像训练特征值上限是 399若是每类取 5 张训练上限是 199。接近上限意味着把噪声也一并保留测试集识别率反而可能下降。保留主成分数方差占比参考分类效果倾向10约 60%特征太少部分类内差异丢失30~50约 90%ORL 上比较常用的区间10095% 上下识别率进入稳定区接近上限接近 100%包含噪声信息容易过拟合方差占比的具体数值会随训练集划分浮动表格里给的是粗略经验值。实际项目中更稳妥的做法是拿验证集跑一列 numC选识别率峰值点而不是机械地卡 95% 方差。4. 最近邻分类器与识别率评测ORL 人脸识别实验从训练到评估4.1 测试样本投影前必须减掉训练均值PCA 子空间的坐标原点是训练集均值测试样本进入这个空间前必须先减掉同一个均值。错误写法是直接projTest Xtest * U;正确写法是projTest (Xtest - mu) * U;。mu 必须来自训练集不能用测试集参与计算否则测试集信息会经由均值泄漏进特征空间。这个细节在 ORL 这种 400 张的小数据集上尤其容易出错因为很多人习惯一次性载入全部图像整体减均值后再切分训练测试。表面上看代码没报错但 PCA 子空间已经见过测试样本的全局分布评估结果会虚高。训练和测试的边界必须从预处理阶段就画清楚。4.2 三种距离度量在 ORL 上的对比投影完毕后的分类器常用最近邻也就是算测试样本与所有训练样本的距离取最小值对应的类别。MATLAB 的 pdist2 可以直接算多种距离距离pdist2 参数ORL 上表现特征欧氏距离euclidean基线选择维度方差差异被保留余弦距离cosine对整张图的亮度缩放不敏感相关系数距离correlation与余弦接近计算量略大选择原则是先用欧氏距离跑出基线再对比余弦距离。ORL 上两者通常相差 1~2 个百分点没有绝对优劣。如果预处理阶段做了直方图均衡整图的对比度被拉伸过欧氏距离往往更稳定如果图像亮度在不同样本间有明显波动余弦距离会更稳。4.3 留一交叉验证的完整 MATLAB 流程下面这段代码做的是“每类留一张”的交叉验证第 fold 轮把每个人的第 fold 张图像作为测试集其余 9 张训练跑完 10 轮正好覆盖全部 400 张。numC 100; accFold zeros(10, 1); for fold 1:10 testIdx fold : 10 : 400; % 每人第 fold 张 trainIdx setdiff(1:400, testIdx); Xtrain X(trainIdx, :); Ytrain Y(trainIdx); Xtest X(testIdx, :); Ytest Y(testIdx); [U, mu] pca_faces(Xtrain, numC); % PCA 只在训练集上学习 projTrain (Xtrain - mu) * U; projTest (Xtest - mu) * U; D pdist2(projTest, projTrain, euclidean); [~, minIdx] min(D, [], 2); pred Ytrain(minIdx); accFold(fold) mean(pred Ytest); end acc mean(accFold); fprintf(ORL PCANN: %.2f%% ± %.2f%%\n, acc*100, std(accFold)*100);fold 从 1 到 10 时fold:10:400取出的是每个人同一编号的图像每轮测试样本正好 40 个。setdiff 的写法可读性好400 个样本的规模下开销完全可以接受。关键点在于 PCA 必须在循环内部每轮重新计算某一轮的投影方向只来自当前训练集。如果把 PCA 放在循环外对全部 400 张图做一次再进循环切分评估结果就失去了意义。使用 numC100、欧氏距离的配置在 ORL 上跑出来的典型结果是 97% 上下的平均识别率各 fold 之间会有几个点的波动取均值±标准差比只报最好一轮要严谨得多。4.4 从识别率到混淆矩阵的评估方法识别率只给一个数字不足以定位错误来源。可以把 10 轮的预测结果按原始样本序号存回一个 400 长度的向量再用 confusionchart 画混淆矩阵predAll zeros(400, 1); % 每轮循环内记录predAll(testIdx) pred; figure; confusionchart(Y, predAll);40 类的混淆矩阵是 40×40逐格看容易眼花重点看两类错误一是集中在某几个对象上说明这些对象的类内差异本身太大比如戴眼镜与不戴眼镜的图像被切成两类二是分散在所有类别中说明主成分个数不足或分类器本身区分度不够。ORL 上最常见的错误样本往往来自头部角度偏大的人脸图像这类样本在少数主成分里与其他人更接近。5. 调参要点与常见坑ORL 人脸识别实验的训练样本数、特征数上限和评估严谨性如果你把上面的流程反复跑会发现影响结果的因素按重要程度排序大概是训练样本数 主成分数 距离度量 预处理。训练样本数的影响最直观见下面这组经验数据每类训练张数训练样本总数PCA 特征数上限典型测试识别率1403970% 上下312011990% 上下520019995%~97%936035999% 上下每类只留 1 张训练时类内方差完全没有信息分类器本质上是拿整张图的粗略模板做匹配识别率很低增加到 3 张后类内分布开始成型识别率大幅上升再往后边际收益放缓剩余错误集中在少数头部角度特殊的人脸样本上。做实验对比时至少取 3 张训练才有区分度5 张是比较稳妥的默认值。PCA 特征数上限同样有硬规则特征向量的个数最多等于训练样本数减一。原因在于中心化后的数据矩阵秩最大是 n-1协方差矩阵的非零特征值数量不会超过这个数字。如果你把 numC 设成 200但训练集只有 120 个样本U 实际拿到 119 列后面用U(:, 1:numC)会直接报索引越界。代码里的min(numC, size(U, 2))就是为这个情况兜底。最后是三个会让实验结论失真的常见错误。第一把 PCA 放在切分之前对全部 400 张图求投影方向再划分训练测试测试集信息已经参与了特征空间构建。第二测试样本减均值时用了全体均值或测试集均值而不是训练集均值。第三随机划分只跑几次取其中最好的一次报为最终结果正确做法是固定划分方式或做完整交叉验证报告均值和标准差。如果你还想把识别率再往上推最常见的路子不是换深度学习模型而是把最近邻分类器前面的 PCA 换成 LDA也就是 Fisherfaces。LDA 利用了标签信息投影维数的上限从样本数减一变更为类别数减一在 ORL 上是 39 维。这个组合在 ORL 上的识别率通常比纯 PCA 最近邻再高 1 到 3 个百分点实现上只需要把第 4 章流程里的 pca_faces 换成 LDA 投影函数。本文还有配套的精品资源点击获取