☰
MB_LBP+PCA+SVM人脸识别实战:从ORL库到代码复现与调参
2026/10/7 22:31:35 网站建设 项目流程

简介:这份PDF文献面向人脸识别方向的研究生、算法工程师及模式识别课程学习者,聚焦单一特征提取方法在光照、姿态与噪声干扰下识别率下降的问题,提供MB_LBP与PCA融合的完整研究思路。资源包内含1个PDF文件,约850KB,为期刊论文全文,涵盖算法原理推导、ORL人脸库实验设置与结果对比,便于读者系统理解局部纹理特征与全局降维如何协同工作。文中先以MB_LBP提取人脸局部特征,增强对光照和噪声的抵抗能力,再用PCA对特征降维以降低计算复杂度,最后通过SVM完成分类识别,实验显示相比传统PCA识别率提升13%以上。目前已有127人学习,适合需要撰写相关论文、复现实验或寻找特征融合方案参考的读者,可从中获取算法设计逻辑、实验验证方法与性能对比数据,为后续改进或工程落地提供依据。

1. 从一张 2016 年的 PDF 说起:MB_LBP+PCA 到底解决了什么

如果你手头正好有一份 ORL 人脸库,想跑一个不依赖深度学习框架、纯 CPU 就能出结果的识别基线,那这份 2016 年发表的《基于 MB_LBP 和 PCA 算法的人脸识别研究》值得翻出来细读。它做的事情很具体:把人脸图像先过一遍 MB_LBP 提取局部纹理,再用 PCA 把维度压下来,最后丢给 SVM 做分类。论文里给的结论是,在 ORL 库上比传统 PCA 识别率提高 13% 以上。这个数字放到今天不算惊艳,但它的价值在于——整套流程透明、可复现、每一步都能拆开看参数。适合谁?适合刚接触人脸识别、想搞明白“特征提取到底在提什么”的工程师,也适合需要在嵌入式或低算力场景下找一个轻量方案的从业者。你不需要 GPU,不需要百万级数据集,一台普通笔记本就能把这条链路跑通。下面我按“原理选型 → 动手复现 → 踩坑排查 → 进阶调参”的顺序,把这份 PDF 里的方法拆成能直接抄的步骤。

2. MB_LBP 与 PCA 的选型逻辑:为什么不是直接用 PCA

2.1 原始 LBP 的噪声问题与 MB_LBP 的改进思路

LBP 的核心操作很简单:取一个中心像素,跟周围 8 个邻域像素比灰度值,大于等于中心记 1,小于记 0,顺时针拼成一个 8 位二进制数,这个数就是该位置的 LBP 编码。整幅图扫一遍,统计直方图,就得到纹理特征。计算量小、对单调光照变化有一定鲁棒性,这是 LBP 被引入人脸识别的初衷。

但原始 LBP 有个硬伤:它只比较单个像素与单个像素。图像里一个孤立的噪声点,就能让中心像素的编码完全翻转。论文里提到的 MB_LBP(Multi-Block LBP)就是冲着这个问题去的——不再用单个像素,而是把中心像素扩展成一个矩形区域,周围邻域也各取一个矩形块,用块内平均灰度代替单点灰度做比较。这样相当于对图像做了一个微型均值滤波,噪声被摊平,编码稳定性明显提升。

我一般会这样理解:原始 LBP 是“点对点”,MB_LBP 是“块对块”。块的大小就是关键参数。论文里没有给出固定的块尺寸,因为作者自己也说了“分块大小的选取是 MB_LBP+PCA 算法有待讨论的问题”。实操中,块太小(比如 2×2)降噪效果有限,块太大(比如 8×8)会把眼睛、嘴角这些关键局部结构抹平。常见做法是从 3×3 或 4×4 起步,在验证集上看识别率曲线,找拐点。

2.2 PCA 降维在人脸识别中的角色与局限

PCA 做人脸识别的经典流程叫“特征脸”(Eigenfaces)。把每张人脸图像拉成一个列向量,所有训练样本求平均脸,每张图减去平均脸,得到差值向量。这些差值向量构成协方差矩阵,做奇异值分解,取前 k 个最大特征值对应的特征向量——它们就是“特征脸”。任何一张人脸都可以投影到这 k 个特征脸上,得到一组 k 维系数,这组系数就是降维后的特征表示。

PCA 的优点是全局降维能力强,能把几千维的像素空间压到几十维,计算复杂度大幅下降。但它的局限也很明显:它是线性变换,对局部纹理变化不敏感。两个人脸在全局灰度分布上可能很接近,但局部纹理(比如眼角皱纹、鼻翼阴影)差异很大,PCA 抓不住这些。所以论文的思路是:先用 MB_LBP 把局部纹理特征提出来,形成一组对光照和噪声更鲁棒的描述子,再用 PCA 对这组描述子降维。这样 PCA 处理的不再是原始像素,而是已经富含局部信息的特征向量,降维后的表示同时保留了全局统计特性和局部纹理特性。

2.3 SVM 分类器的选择理由

论文选 SVM 做最后一步分类,理由在原文里写得很清楚:SVM 擅长小样本、高维度、非线性分类。ORL 库只有 40 个人、每人 10 张图,总共 400 张,典型的小样本场景。MB_LBP+PCA 之后,每张图的特征维度虽然降了,但相对于样本数仍然偏高,SVM 的核函数机制正好能处理这种“维度高于样本数”的情况。

SVM 的核心是核函数。论文里列了线性核、多项式核、径向基核(RBF)和二次神经网络核。实操中 RBF 核最常用,因为它只有一个主要参数 gamma 需要调,而且对非线性边界的拟合能力够用。惩罚系数 C 控制间隔与误分类的权衡,C 大偏向严格分类训练集,C 小偏向泛化。这两个参数怎么设,后面第 5 章会给出具体网格搜索的代码。

提示:如果你之前只用过 PCA+SVM 做手写数字分类(比如 optdigits 数据集),会发现人脸识别的特征维度更高、样本更少,SVM 的 C 和 gamma 需要更保守一些,否则很容易过拟合。

3. 从 ORL 库到识别结果:完整复现流程与代码

3.1 环境准备与 ORL 人脸库加载

ORL 人脸库(也叫 AT&T 人脸库)是这套流程的标准测试集:40 个志愿者,每人 10 张灰度图,尺寸 92×112,包含轻微的姿态、表情和光照变化。文件格式是 PGM,按s1/1.pgm到s40/10.pgm组织。如果你手头没有现成的库,常见做法是从公开镜像下载后解压到data/orl/目录下。

依赖库只需要 numpy、scikit-learn、opencv-python 和 scikit-image。不需要深度学习框架。

pip install numpy scikit-learn opencv-python scikit-image

加载数据的代码不复杂,但有一个容易翻车的点:ORL 的 PGM 文件读进来是二维数组,拉平成一维向量时顺序必须一致,否则后面 PCA 投影全乱。

import os import numpy as np import cv2 def load_orl(data_dir): """ 加载 ORL 人脸库 data_dir: ORL 解压后的根目录,下面有 s1/ 到 s40/ 共40个子目录 返回: X (400, 10304) 每行是一张拉平的人脸, y (400,) 标签0-39 """ X, y = [], [] for subject_id in range(1, 41): subj_dir = os.path.join(data_dir, f"s{subject_id}") for img_name in range(1, 11): img_path = os.path.join(subj_dir, f"{img_name}.pgm") img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) # 统一尺寸,ORL原始就是92x112,这里做一次保险 img = cv2.resize(img, (92, 112)) X.append(img.flatten().astype(np.float32)) y.append(subject_id - 1) # 标签从0开始 return np.array(X), np.array(y)

这段代码的关键参数是cv2.IMREAD_GRAYSCALE,必须用灰度模式读,彩色模式读进来会变成三通道,拉平后维度对不上。img.flatten()的顺序是行优先,即先第一行从左到右,再第二行,这个顺序在训练和测试时必须一致。标签减 1 是为了让 SVM 的类别从 0 开始,虽然 SVM 对标签具体值不敏感,但统一成 0 起始方便后续混淆矩阵分析。

3.2 MB_LBP 特征提取的实现细节

MB_LBP 的实现比原始 LBP 多了一个“块平均”步骤。我一般会写一个通用函数,支持指定块大小和邻域半径。下面这个实现用的是圆形邻域采样加双线性插值,比论文里更通用一些,但核心逻辑一致:先对图像做块平均,再在平均后的图上做 LBP 编码。

from skimage.feature import local_binary_pattern def mb_lbp_features(X, block_size=3, radius=1, n_points=8): """ 对每张人脸图像提取MB_LBP直方图特征 X: (n_samples, n_features) 拉平的人脸向量 block_size: 块大小,论文中未固定,建议3或4 radius: LBP邻域半径 n_points: 邻域采样点数,8对应原始LBP 返回: (n_samples, n_bins) 每张图的LBP直方图 """ n_samples = X.shape[0] img_h, img_w = 112, 92 features = [] for i in range(n_samples): img = X[i].reshape(img_h, img_w).astype(np.uint8) # 块平均:用均值滤波模拟MB_LBP的块比较 # 注意:这里用均匀滤波,kernel大小=block_size img_block = cv2.blur(img, (block_size, block_size)) # 在块平均后的图上做LBP lbp = local_binary_pattern(img_block, n_points, radius, method='uniform') # 统计直方图,uniform模式有n_points+2个bin hist, _ = np.histogram(lbp.ravel(), bins=n_points + 2, range=(0, n_points + 2), density=True) features.append(hist) return np.array(features)

这里有几个参数需要说清楚。block_size控制块平均的窗口大小,论文里没有给出最优值,我实测下来 3×3 在 ORL 上比较稳,再大就会把眼睛和嘴巴的区域差异抹掉。n_points=8和radius=1对应原始 LBP 的 3×3 邻域,如果你把 radius 调到 2,采样点会落在更远的像素上,能捕捉更大尺度的纹理,但计算量增加。method='uniform'是关键,它把等价模式(0-1 跳变不超过 2 次)归为一类,非等价模式归为另一类,这样直方图 bin 数从 256 降到 10,既降维又保留了主要纹理信息。density=True做归一化,避免不同图像因尺寸或对比度差异导致直方图计数不可比。

3.3 PCA 降维与 SVM 分类的串联

MB_LBP 输出的直方图特征维度是 10(uniform 模式),400 张图就是 400×10 的矩阵。等等,10 维还需要 PCA 降维吗?这里有个容易误解的地方:论文里的 MB_LBP 是对整张图做一次 LBP 然后统计全局直方图,还是分块统计再拼接?原文写的是“将人脸图像划分为几个互不重叠的区域,利用 LBP 算子提取局部特征和直方图统计特征”,这意味着是分块 LBP 直方图拼接。如果按 4×4 分块,每块 10 个 bin,拼起来就是 160 维。这个维度下 PCA 降维才有意义。

我一般会这样处理:把 112×92 的图像分成 4×4 共 16 个不重叠子区域,每个子区域单独做 MB_LBP 直方图,然后首尾拼接。这样每张图的特征维度是 16×10=160。然后 PCA 降到 30-50 维,再送 SVM。

def blockwise_mb_lbp(X, grid=(4, 4), block_size=3, radius=1, n_points=8): """ 分块MB_LBP:将图像分成grid个不重叠区域,每块提取LBP直方图后拼接 grid: (rows, cols) 分块数 返回: (n_samples, grid[0]*grid[1]*(n_points+2)) """ n_samples = X.shape[0] img_h, img_w = 112, 92 cell_h, cell_w = img_h // grid[0], img_w // grid[1] n_bins = n_points + 2 features = [] for i in range(n_samples): img = X[i].reshape(img_h, img_w).astype(np.uint8) img_block = cv2.blur(img, (block_size, block_size)) lbp = local_binary_pattern(img_block, n_points, radius, method='uniform') hist_parts = [] for r in range(grid[0]): for c in range(grid[1]): cell = lbp[r*cell_h:(r+1)*cell_h, c*cell_w:(c+1)*cell_w] hist, _ = np.histogram(cell.ravel(), bins=n_bins, range=(0, n_bins), density=True) hist_parts.append(hist) features.append(np.concatenate(hist_parts)) return np.array(features)

分块数grid是这套流程里最需要调的参数。论文里提到“分块过小,图像存在噪声;分块过大,会使得图像粒度化,破坏原有的很多图像特征”。我的经验是 4×4 到 8×8 之间比较合适。4×4 时每块 28×23 像素,还能保留眼睛、鼻子的大致位置信息;8×8 时每块 14×11 像素,局部纹理更细但空间信息变弱。ORL 库姿态变化小,4×4 通常够用。

PCA 和 SVM 的串联用 scikit-learn 的 Pipeline 最省事:

from sklearn.decomposition import PCA from sklearn.svm import SVC from sklearn.pipeline import Pipeline from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.preprocessing import StandardScaler # 假设 X_lbp 是 blockwise_mb_lbp 的输出,y 是标签 X_train, X_test, y_train, y_test = train_test_split( X_lbp, y, test_size=0.3, random_state=42, stratify=y ) pipe = Pipeline([ ('scaler', StandardScaler()), # LBP直方图各bin量纲一致,但标准化有助于PCA ('pca', PCA(n_components=50)), # 先设50,后面网格搜索调 ('svm', SVC(kernel='rbf', C=10, gamma='scale')) ]) pipe.fit(X_train, y_train) accuracy = pipe.score(X_test, y_test) print(f"识别率: {accuracy:.4f}")

StandardScaler这一步不是必须的,因为 LBP 直方图每个 bin 都是归一化后的频率,量纲一致。但加上它不会有害,而且如果后续换用其他特征(比如原始像素),标准化就是必要的。PCA(n_components=50)是初始值,实际最优值需要用交叉验证找。SVC的gamma='scale'是 scikit-learn 的默认值,等于1/(n_features * X.var()),在特征维度变化时自动调整,比手动设固定值省心。

3.4 训练/测试划分与识别率验证

ORL 库的标准协议是每人取前 5 张做训练、后 5 张做测试,或者随机取 5 张训练、5 张测试。论文里没有明确说用了哪种划分,但“识别率提高 13%”这个结论需要在相同划分下对比 PCA 单独使用和 MB_LBP+PCA 两种方法。

我一般会跑两组对照:一组只用 PCA+SVM,一组用 MB_LBP+PCA+SVM,其他条件完全一致。下面是对比代码:

# 对照组:纯PCA pipe_pca_only = Pipeline([ ('scaler', StandardScaler()), ('pca', PCA(n_components=50)), ('svm', SVC(kernel='rbf', C=10, gamma='scale')) ]) pipe_pca_only.fit(X_train_raw, y_train) # X_train_raw是原始像素 acc_pca = pipe_pca_only.score(X_test_raw, y_test) # 实验组:MB_LBP+PCA pipe_mb_pca = Pipeline([ ('scaler', StandardScaler()), ('pca', PCA(n_components=50)), ('svm', SVC(kernel='rbf', C=10, gamma='scale')) ]) pipe_mb_pca.fit(X_train_lbp, y_train) # X_train_lbp是MB_LBP特征 acc_mb_pca = pipe_mb_pca.score(X_test_lbp, y_test) print(f"纯PCA识别率: {acc_pca:.4f}") print(f"MB_LBP+PCA识别率: {acc_mb_pca:.4f}") print(f"提升: {(acc_mb_pca - acc_pca)*100:.2f}%")

注意X_train_raw和X_train_lbp的维度不同,前者是 10304 维(92×112),后者是 160 维(4×4×10)。PCA 的n_components=50对两者都适用,但纯 PCA 从 10304 降到 50 信息损失更大,MB_LBP+PCA 从 160 降到 50 损失较小。这正是论文结论的底层原因:MB_LBP 先把局部纹理提出来,PCA 降维时保留的是纹理特征的统计结构,而不是原始像素的全局灰度分布。

注意:如果你的划分是随机的,每次跑出来的识别率会有波动。ORL 只有 400 张图,测试集 120 张,一个样本的差异就是 0.83%。建议跑 5 次不同随机种子取平均,或者直接用 5 折交叉验证。

4. 避坑与排查:复现时最容易翻车的五个点

4.1 识别率远低于论文值

现象:跑出来的 MB_LBP+PCA 识别率只有 70% 出头,论文说比 PCA 提高 13%,但你的 PCA 基线可能就有 85%,提升不明显甚至倒挂。

原因:最常见的原因是分块数grid设得不对。如果设成 1×1(整图一个直方图),MB_LBP 退化成全局 LBP,空间信息全丢,识别率自然低。另一个原因是 PCA 的n_components设得过大或过小——太大保留了噪声,太小丢了判别信息。

解决:先把grid固定为 4×4,n_components从 20 到 100 按步长 10 扫一遍,画识别率曲线。ORL 上通常 30-60 之间会出现峰值。如果曲线平坦,说明 LBP 特征本身判别力不够,检查block_size是否过大(超过 5 就会抹平细节)。

4.2 PGM 读取后图像尺寸不一致

现象:cv2.imread读某些 PGM 文件返回 None,或者读进来尺寸不是 92×112,导致flatten()后维度对不上,程序在np.array(X)处报错。

原因:ORL 库的 PGM 文件有 P5 和 P2 两种格式,OpenCV 对 P2(ASCII 编码)的支持不稳定。另外有些镜像版本的文件名大小写不一致。

解决:用skimage.io.imread替代cv2.imread,它对 PGM 格式的兼容性更好。或者在加载前统一用 PIL 读入再转 numpy。如果尺寸不对,用cv2.resize强制到 92×112,但要注意 resize 会引入插值伪影,最好在数据源头确认尺寸。

4.3 SVM 训练报“类别数少于 2”或预测全为同一类

现象:SVC.fit抛出 ValueError,提示需要至少两个类别;或者训练不报错但预测结果全是 0。

原因:train_test_split的stratify=y参数在每类样本数少于 2 时会报错。ORL 每人 10 张,如果测试集比例设得太大(比如 0.5),训练集每类可能只剩 5 张,stratify 仍然能工作。但如果你的数据加载代码把标签搞错了(比如所有标签都是 0),就会出这个问题。

解决:在train_test_split之前打印np.unique(y, return_counts=True),确认 40 个类别每类都有样本。如果标签确实只有一类,检查load_orl里的subject_id循环范围是不是写成了range(1, 1)这种低级错误。

4.4 PCA 降维后识别率反而下降

现象:MB_LBP 特征直接送 SVM(不经过 PCA)的识别率,比经过 PCA 之后还高。

原因:MB_LBP 分块直方图本身维度就不高(4×4×10=160),如果样本量足够(400 张),SVM 在高维空间里反而更容易找到分类超平面。PCA 降维虽然减少了计算量,但也可能把一些对分类有用的低方差方向丢掉了。

解决:这不是 bug,是特征维度和样本量匹配的问题。论文里 PCA 的作用是“降维以减少计算复杂度”,如果你的场景对计算速度不敏感,可以跳过 PCA 直接用 MB_LBP+SVM。但如果你要对比论文结论,还是得保留 PCA,并且把n_components调到能保留 95% 以上方差的值(PCA(n_components=0.95))。

4.5 交叉验证耗时过长

现象:用GridSearchCV调 SVM 的 C 和 gamma,配合 PCA 的n_components,参数组合上百种,跑一次要十几分钟甚至更久。

原因:每次参数组合都要重新做 PCA 和 SVM 训练,而 PCA 是对整个训练集做的,不能预计算一次反复用。

解决:分两步调参。先固定 SVM 的C=10, gamma='scale',只调 PCA 的n_components,找到最优值。然后固定 PCA,用GridSearchCV调 SVM 的C和gamma。这样总组合数从三维降到两个一维搜索,时间大幅缩短。另外GridSearchCV的cv参数设 3 就够了,ORL 数据量小,5 折和 3 折的结果差异不大。

5. 进阶调参:分块粒度与 SVM 核函数的联合优化

5.1 分块数对识别率的影响曲线

分块数grid是 MB_LBP 里最值得花时间调的参数。我一般会按下面的代码扫一遍 2×2 到 8×8,看识别率怎么变:

import matplotlib.pyplot as plt grids = [(2,2), (3,3), (4,4), (5,5), (6,6), (8,8)] accs = [] for g in grids: X_lbp = blockwise_mb_lbp(X, grid=g, block_size=3) X_tr, X_te, y_tr, y_te = train_test_split( X_lbp, y, test_size=0.3, random_state=42, stratify=y ) pipe = Pipeline([ ('scaler', StandardScaler()), ('pca', PCA(n_components=0.95)), # 保留95%方差 ('svm', SVC(kernel='rbf', C=10, gamma='scale')) ]) pipe.fit(X_tr, y_tr) acc = pipe.score(X_te, y_te) accs.append(acc) print(f"grid={g}, 特征维度={X_lbp.shape[1]}, 识别率={acc:.4f}") plt.plot([f"{g[0]}x{g[1]}" for g in grids], accs, marker='o') plt.xlabel('分块数') plt.ylabel('识别率') plt.title('MB_LBP分块数对识别率的影响') plt.show()

在 ORL 上,这条曲线通常是先升后降:2×2 时每块太大,局部纹理被平均掉;4×4 或 5×5 达到峰值;8×8 时每块只有 14×11 像素,LBP 直方图统计不稳定,识别率回落。峰值位置跟block_size有关——如果block_size设成 5,块平均的平滑范围更大,峰值会往更大的grid偏移。

5.2 SVM 核函数与参数网格搜索

SVM 的核函数选择对最终识别率影响很大。论文里列了四种核,我实测下来 RBF 在 ORL 上最稳,但多项式核在特定参数下也能接近。下面是对比代码:

from sklearn.model_selection import GridSearchCV # 先用最优grid提取特征 X_lbp_best = blockwise_mb_lbp(X, grid=(4,4), block_size=3) X_tr, X_te, y_tr, y_te = train_test_split( X_lbp_best, y, test_size=0.3, random_state=42, stratify=y ) # 定义参数网格 param_grid = [ {'svm__kernel': ['rbf'], 'svm__C': [1, 10, 100], 'svm__gamma': [0.001, 0.01, 0.1, 'scale']}, {'svm__kernel': ['poly'], 'svm__C': [1, 10], 'svm__degree': [2, 3], 'svm__gamma': ['scale']}, {'svm__kernel': ['linear'], 'svm__C': [1, 10, 100]} ] pipe = Pipeline([ ('scaler', StandardScaler()), ('pca', PCA(n_components=0.95)), ('svm', SVC()) ]) grid_search = GridSearchCV(pipe, param_grid, cv=3, n_jobs=-1, verbose=1) grid_search.fit(X_tr, y_tr) print(f"最优参数: {grid_search.best_params_}") print(f"最优交叉验证分数: {grid_search.best_score_:.4f}") print(f"测试集识别率: {grid_search.score(X_te, y_te):.4f}")

RBF 核的gamma控制单个样本的影响半径:gamma 大,影响范围小,容易过拟合;gamma 小,影响范围大,容易欠拟合。C控制误分类惩罚:C 大,训练集拟合更紧,泛化可能变差;C 小,间隔更宽,可能欠拟合。在 ORL 上,C=10, gamma='scale'通常是一个不错的起点,网格搜索往往会在C=10~100, gamma=0.001~0.01之间找到更优值。

多项式核的degree参数控制多项式阶数,degree=2 对应二次曲面,degree=3 对应三次曲面。ORL 的类间差异主要是全局结构和局部纹理的混合,二次多项式有时能比 RBF 略好,但计算量更大。线性核在 MB_LBP+PCA 特征上表现一般,因为特征已经经过非线性变换(LBP 编码本身就是非线性的),线性核的判别能力不够。

5.3 一个容易被忽略的细节:LBP 直方图的归一化方式

np.histogram的density=True做的是概率密度归一化,即每个 bin 的值等于该 bin 的计数除以总计数再除以 bin 宽度。对于 uniform LBP 的 10 个 bin,bin 宽度都是 1,所以density=True和手动除以总计数等价。但如果你用的不是 uniform 模式,而是method='default'(256 个 bin),density=True会把每个 bin 的值缩放到概率密度,不同图像之间的可比性更好。

我一般会显式做 L1 归一化,而不是依赖density参数:

hist, _ = np.histogram(cell.ravel(), bins=n_bins, range=(0, n_bins)) hist = hist.astype(np.float32) / (hist.sum() + 1e-6) # L1归一化,加小量防除零

加1e-6是防止某个子区域全为同一灰度值导致直方图只有一个 bin 非零、其余为零的情况。虽然概率极低,但在 ORL 的某些背景区域可能出现。

5.4 从 ORL 迁移到自建数据集时要注意什么

ORL 是受控环境下的标准库,姿态和光照变化都很轻微。如果你要把这套流程用到自建数据集(比如门禁场景的抓拍图),有几个参数需要重新调:

第一,block_size要调大。自建数据的噪声水平通常高于 ORL,3×3 的块平均可能不够,可以试 5×5 或 7×7。第二,grid要调小。自建数据的人脸对齐误差更大,分块太细会导致同一张脸的不同图像在相同子区域里内容不对应,直方图匹配度下降。4×4 或 3×3 更稳。第三,PCA 的n_components要调大。自建数据的类内差异更大,需要保留更多主成分来区分。可以用PCA(n_components=0.98)保留 98% 方差,而不是固定的 50 维。

从那以后我每次换数据集,都会先把grid、block_size、n_components这三个参数各扫一遍,画三条曲线,确认峰值位置再固定下来。这套 MB_LBP+PCA+SVM 的流程虽然来自 2016 年,但它的每一步都透明可控,调参时你知道自己在改什么、为什么改。对于想理解人脸识别底层逻辑的人来说,比直接调一个深度学习模型有价值得多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询