主成分分析 (PCA)

一、特征维度约减 (降维)      

        主成分分析离不开降维,那么什么是降维呢?    

        特征维度约减,又称降维,是指通过特征变换或者投影矩阵实现特征空间的压缩,目的是将高维特征向量映射到低维子空间中,同时使低维特征能保留原始数据的关键信息,缓解维度灾难,提升模型泛化能力。特征选择是维度约减的另一种形式。

        为什么要降维?        

        ① 大多数机器学习算法在高维空间中表现不够鲁棒,查询速度与精度随着维度增加而降低,降维能提升分类或识别的精度;(噪声消除)

        ② 在高维数据中,有价值的维度往往很少;(消除冗余信息)

        ③ 降维能够让数据的存储与检索变得高效

        ④ 低维特征更容易可视化、便于理解。(增强可解释性)

        降维方法主要分为线性方法,比如主成分分析 (PCA) 、线性判别分析 (LDA) ;非线性方法,比如等距映射 (Isomap) 、局部线性嵌入 (LLE) 。其中PCA是无监督方法,LDA是监督方法。

二、主成分分析简介

        主成分分析 (Principal Component Analysis,PCA) 是一种常用的降维技术,属于无监督方法,用于将高维数据转换为低维表示,同时尽可能保留原始数据的关键信息。

  • 核心目标:通过线性变换,将原始高维向量通过投影矩阵,投射到低维空间,重新组合成一组新向量,这些向量称为主成分,它们具有无关性正交的特点;其中前几个主成分能够捕获原始数据的大部分方差 (即信息) 。

  • 直观理解:例如,在二维平面上有一组散点数据,PCA 会找到一条 “最佳” 直线 (第一主成分),使数据在该直线上的投影方差最大;再找一条与第一条直线垂直的直线 (第二主成分),捕获剩余的方差,以此类推。

  • 关键性质:① 主成分互不相关 (任意两个主成分的协方差为0) 、② 方差递减 (第一主成分的方差最大,后续主成分的方差依次递减) 、③ 降维可逆性 (在保留主要主成分的情况下,可通过逆变换近似还原原始数据) 。

三、PCA的算法流程

3.1 数据标准化

        ① 假设样本数为 n ,对每个特征值 (列) ,计算其均值 μ 与标准差 σ ;

        eg:对特征 j (第 j 列) ,均值为:u_{j} = \frac{1}{n}\sum_{i = 1}^{n}x_{ij} ;(遍历该列所有样本,求和后除以n)

标准差为:\sigma _{j} = \sqrt{\frac{1}{n-1}\sum_{i=1}^{n}(x_{ij}-\mu _{j})^{2}} ;(除以 n-1 ,进行无偏估计修正)

        ② 对每个样本 i 的第 j 个特征,代入公式:x_{ij}^{norm} = \frac{x_{ij} - \mu _{j}}{\sigma _{j}} (即减去均值、除以标准差)

把每个特征都 “缩放” 到均值为 0、标准差为 1,消除不同特征之间的量纲差异,确保各特征对结果

影响均衡。

3.2 计算协方差矩阵

        对标准化后的数据矩阵 X ,计算协方差矩阵 Σ ,公式为:\sum = \frac{1}{n-1}X^{T}X

        X^{T} 是标准化数据矩阵的转置矩阵, 除以 n-1 进行无偏估计修正。

        协方差矩阵描述了特征间的线性关系,对角线元素为特征 j 与自身的协方差,即 j 的方差,非对角线元素为特征 i 与特征 j 之间的协方差,其中 i ≠ j。

协方差 Cov ( i , j ) > 0 :特征 i 与 j 正相关,即一个增大,另一个倾向增大;

协方差 Cov ( i , j )  = 0 :特征 i 与 j 线性无关;

协方差 Cov ( i , j ) > 0 :特征 i 与 j 负相关,即一个增大,另一个倾向减小。

3.3 求解特征值与特征向量

        对协方差矩阵做特征分解,得到特征值 \lambda _{i} (表示对应方向方差大小) 和特征向量 v_{i} (代表数据

投影的 “新坐标轴方向” )特征值和特征向量满足等式:\sum \cdot v_{i} = \lambda _{i} \cdot v_{i} 。

       含义:用协方差矩阵 Σ 对向量 v_{i} 做线性变换后,结果相当于对 v_{i} 进行 “数乘缩放”,缩放系数就是 \lambda _{i} 。

        如果原始数据是 d 维,那么就有 d 个特征值 ;\lambda _{i} 越大,表示方差越大,说明这个方向 “包含的信息越多” ;不同特征值对应的特征向量天然正交 (协方差矩阵是对称矩阵的性质) 。

3.4 选择主成分

         将所有特征值按从大到小排序 (假设有 d 个特征) ;

        ② 单个主成分的解释方差比例:第 i 个主成分 (对应第 i 大的特征值 \lambda _{i} ) ,它的解释方差比例

公式为:p_{i} = \frac{\lambda _{i}}{\sum_{j=1}^{d}\lambda _{j}} ;

        ③ 累计解释方差比例:把前 k 个主成分的解释方差比例相加,公式:p_{k} = \frac{\sum_{i=1}^{k}\lambda _{i}}{\sum_{j=1}^{d}\lambda _{j}}

它衡量的是前 k 个主成分总共能 “解释” 原始数据方差 (信息) 的百分比;

        ④ 找到最小的 k ,让累计解释方差比例 ≥ 预设阈值 (常见90%、95%,可根据需求调整) ,然后选取前 k 个特征值对应的特征向量作为主成分,k 即为降维后的维度。

3.5 数据投影

        选定的主成分按列构成矩阵 V_{k} ( d × k 矩阵) ,这个矩阵就定义了一个从原始高维空间到低维空间的映射关系,用它对原始数据作线性变换,设降维后的矩阵为 Z ,则有  Z = X \cdot V_{k} 。

        这样就实现了将标准化后的 n × d 维数据矩阵 X (n个样本,d个特征) ,通过线性变换投影到由前 k 个特征向量组成的 k 维子空间中,降维矩阵 Z 是一个 n × k 维矩阵,Z 的第 i 行第 j 列元素表示第 i 个样本在第 j 个主成分上的投影值。

四、示例:人脸识别

4.1 数据加载和预处理

        给定数据集:大文件夹为ORL_Faces,包含40个子文件夹 (s1,s2...) ,每个文件夹存放了同个人不同角度的十张灰度人脸图像,数据集展示结果:

        遍历每个子目录 (s1~s40) ,读取每张PGM格式图像,将图像转换为灰度图并展平为一维数组(112×92=10304个像素) ,构建数据集faces ( 形状:[400,10304] ) 和标签 labels ( 形状:[400] ) ,使用train_test_split按一定比例将数据集划分为训练集和测试集 ( 默认75%为训练集、25%为测试集 )。

4.2 模型训练和方差分析 

        模型训练:初始化 PCA 模型,保留若干个主成分 (这里保留 150 个) 并应用白化处理 (whiten=True),通过过 sklearn 提供的 fit_transform 方法计算训练集的主成分并转换数据。

        方差分析:计算每个主成分的解释方差比例,再计算前 k 个主成分的累积方差比例,设定阈值为90%,计算达到 90% 累积方差所需的主成分数量。

4.3 结果可视化

        PCA 的components_ 属性包含前若干个 (前面保留的主成分数目,这里为150) 主成分,每个主成分是一个长度为10304的向量,将这些向量重塑为112×92的图像矩阵,即 “特征脸” ,使用 matplotlib绘制特征脸(可以根据需求决定绘制的特征个数和排列方法,这里设置为绘制10个特征脸,按2行5列排列) ,每个特征脸代表数据集中的一种主要变化模式 ( 如光照、面部轮廓等 ) 。

        通过 pca.transform 将训练集投影到低维空间 (150 维 ) ,通过 pca.inverse_transform 将降维后的数据恢复到原始空间 (10304 维 ) ,可视化原始人脸与重建人脸的对比,验证降维过程中的信息保留程度。

        绘制主成分数量与累积方差比例的关系曲线,直观展示降维效果,例如,如果 60 个主成分即可解释 90% 的方差,说明原始数据中的大部分信息可由少数特征表示;打印主成分的解释方差比例和累积方差比例 (这里只打印前10个) ,输出达到 90% 累积方差所需的主成分数量,辅助选择合适的 k 值。

4.4 降维后的人脸识别模型训练与评估

        使用 K 近邻分类器,设置 k = 1 ,在 PCA 降维后的训练数据上训练模型,在测试集上进行预测并计算准确率,高准确率表明 PCA 有效提取了人脸的关键特征,即降低维度的同时成功保留了判别信息。

4.5 代码实现

import os
import numpy as np
from PIL import Image
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt

#设置中文,解决负号显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams["axes.unicode_minus"] = False

#加载人脸图像数据
def load_faces(data_dir):
    faces, labels = [], []
    for person_id in range(1, 41):
        person_dir = os.path.join(data_dir, f"s{person_id}")
        if not os.path.exists(person_dir):
            continue
        for img_id in range(1, 11):
            img_path = os.path.join(person_dir, f"{img_id}.pgm")
            try:
                img = Image.open(img_path).convert('L')
                faces.append(np.array(img).flatten())
                labels.append(person_id)
            except:
                continue
    return np.array(faces), np.array(labels)

#绘制特征脸
def plot_eigenfaces(eigenfaces, shape, n=10):
    fig, axes = plt.subplots(2, 5, figsize=(8,5))
    for i, ax in enumerate(axes.flat):
        ax.imshow(eigenfaces[i].reshape(shape), cmap='gray')
        ax.set_title(f'特征脸 {i+1}', fontsize=9)
        ax.axis('off')
    plt.tight_layout()
    plt.suptitle('前10个特征脸', fontsize=12)
    plt.subplots_adjust(top=0.9)  #为标题留出空间
    plt.show()

#对比原始人脸和重建人脸
def plot_reconstruction(original, reconstructed, shape, n=5):
    fig, axes = plt.subplots(2, n, figsize=(8,5))
    for i in range(n):
        axes[0, i].imshow(original[i].reshape(shape), cmap='gray')
        axes[1, i].imshow(reconstructed[i].reshape(shape), cmap='gray')
        axes[0, i].set_title('原始', fontsize=9)
        axes[1, i].set_title('重建', fontsize=9)
        axes[0, i].axis('off')
        axes[1, i].axis('off')
    plt.tight_layout()
    plt.suptitle('人脸重建对比', fontsize=12)
    plt.subplots_adjust(top=0.9)  #为标题留出空间
    plt.show()

#绘制PCA累计解释方差比例曲线
def plot_variance_curve(pca):
    explained_variance = pca.explained_variance_ratio_
    cumulative_variance = np.cumsum(explained_variance)
    plt.figure(figsize=(8, 5))
    plt.plot(range(1, len(cumulative_variance) + 1), cumulative_variance, 'b-')
    plt.axhline(y=0.9, color='r', linestyle='--', label='90%阈值')
    plt.xlabel('主成分数量')
    plt.ylabel('累计解释方差比例 (%)')
    plt.title('PCA累计解释方差比例曲线', fontsize=12)
    plt.legend()
    plt.grid(True)
    plt.tight_layout()
    plt.show()

#打印PCA计算结果统计信息
def print_pca_statistics(pca):
    explained_variance = pca.explained_variance_
    explained_variance_ratio = pca.explained_variance_ratio_
    cumulative_variance = np.cumsum(explained_variance_ratio)
    print("PCA计算结果:")
    print(f"总主成分数量: {len(explained_variance)}")
    print("\n前10个主成分的解释方差比例:")
    for i in range(min(10, len(explained_variance))):
        print(f"主成分 {i+1}: 方差={explained_variance[i]:.4f}, 解释方差比例={explained_variance_ratio[i]:.4%}, 累计解释方差比例={cumulative_variance[i]:.4%}")
    n_components_90 = np.argmax(cumulative_variance >= 0.9) + 1
    print(f"\n累计解释方差比例达到90%需要的主成分数量: {n_components_90}")

def main():
    #数据目录路径
    data_dir = r'C:\Users\阿蒲\Desktop\机器学习数据\ORL_Faces'
    #加载数据
    faces, labels = load_faces(data_dir)
    img_shape = (112, 92)
    #划分数据集
    X_train, X_test, y_train, y_test = train_test_split(
        faces, labels, test_size=0.25, random_state=42  )
    #PCA降维
    pca = PCA(n_components=150, whiten=True)
    X_train_pca = pca.fit_transform(X_train)
    X_test_pca = pca.transform(X_test)
    #打印PCA统计信息
    print_pca_statistics(pca)
    #可视化特征脸
    plot_eigenfaces(pca.components_, img_shape)
    #可视化重建效果
    X_train_reconstructed = pca.inverse_transform(X_train_pca)
    plot_reconstruction(X_train, X_train_reconstructed, img_shape)
    #绘制方差贡献率曲线
    plot_variance_curve(pca)
    #训练分类器
    knn = KNeighborsClassifier(n_neighbors=1)
    knn.fit(X_train_pca, y_train)
    #评估模型
    accuracy = knn.score(X_test_pca, y_test)
    print(f"人脸识别准确率: {accuracy:.2%}\n")

if __name__ == "__main__":
    main()

运行测试结果截图: 

五、实验小结

        PCA可以通过降维,实现噪声过滤、数据压缩、线性结构提取等,因此可以应用在多种场景,比如图像处理与计算机视觉、金融与经济数据分析、生物信息学与基因表达分析、自然语言处理与文本分析、工业与传感器数据处理。

优点:

① 无监督学习,无需标签数据;

② 降低数据复杂性,识别最重要的多个特征;

③ 计算效率高,适用于大规模数据;

④ 可解释性较强,通过特征值和特征向量分析数据结构。

缺点:

① 假设数据具有线性关系,对非线性结构处理效果差;

② 主成分的实际物理意义可能不明确 (尤其在复杂数据中) ;

③ 对异常值敏感,标准化处理可部分缓解;

④ 可能损失有用信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值