一、PCA的定义

PCA 是一种通过正交变换将原始变量转化为一组新的、线性不相关的变量(主成分),这些主成分按数据方差大小排序,前几个主成分可以近似代替原始数据。

二、PCA的主要思想

2.1 数据降维与信息压缩

  • 原始数据可能存在冗余或高度相关性(如多个特征间线性相关),PCA 通过线性组合将原始特征转换为少数几个互不相关的新特征(主成分),其中每个主成分是原始特征的线性组合,且按方差从大到小排序。
  • 方差越大的主成分包含的原始数据信息越多,因此只需保留前 k 个主成分(k<< 原始维度),即可在损失较少信息的前提下实现降维。

2.2  最大方差理论

  • 第一个主成分(PC1)是原始数据中方差最大的方向,代表数据的主要变化趋势;第二个主成分(PC2)与 PC1 正交(不相关),且是剩余方差中最大的方向,依此类推。
  • 通过最大化主成分的方差,PCA 确保降维后的数据尽可能保留原始数据的分布特征。

2.3 正交变换与协方差矩阵

  • PCA 通过对原始数据的协方差矩阵进行特征值分解或奇异值分解(SVD),求解正交变换矩阵(由特征向量构成),将原始数据投影到新的正交坐标系(主成分轴)上。
  • 变换后的数据在新坐标系下各主成分之间协方差为零(即不相关),实现特征去冗余。

 三、PCA算法流程总结

PCA算法的输入为n维样本集目标降维维数为n’;输出为降维后的新样本集Y 1.  对所有的样本进行中心化,使得数据的均值为0。
2. 计算样本的协方差矩阵,协方差矩阵能同时表现不同维度间的相关性及各个维度上的方差。
3. 对协方差矩阵进行特征值分解,得到特征值和特征向量。
4. 取出最大的n’个特征值对应的特征向量,这些特征向量将构成新的低维空间的基。
5. 标准化特征向量,得到特征向量矩阵w,确保其具有良好的数学性质。
6. 转化样本集中的每个样本,将原始样本投影到新的低维空间中。
7. 得到输出新样本集,完成降维操作。

四、代码实现

面部识别代码

import numpy as np
import os
from PIL import Image
import matplotlib.pyplot as plt


# 1. 加载ORL_Faces数据集
def load_images(base_path):
    data = []
    labels = []
    image_paths = []  # 保存图片路径以便后续显示
    if not os.path.exists(base_path):
        raise FileNotFoundError(f"数据集目录不存在: {base_path}")

    print(f"正在查找数据集: {base_path}")
    for i in range(1, 41):  # s1 to s40
        folder = os.path.join(base_path, f's{i}')
        if not os.path.exists(folder):
            print(f"警告: 子文件夹不存在,跳过: {folder}")
            continue
        print(f"正在处理文件夹: {folder}")
        for filename in os.listdir(folder):
            if filename.endswith('.pgm'):
                img_path = os.path.join(folder, filename)
                try:
                    img = Image.open(img_path).convert('L')  # 转换为灰度图
                    img_array = np.array(img).flatten()  # 展平为一维向量
                    data.append(img_array)
                    labels.append(i)  # 记录类别
                    image_paths.append(img_path)  # 记录图片路径
                except Exception as e:
                    print(f"加载图片 {img_path} 出错: {e}")
    if not data:
        raise ValueError("未找到任何有效的 .pgm 图片。")
    return np.array(data), np.array(labels), image_paths


# 2. 手动实现PCA
def manual_pca(X, n_components):
    # 标准化数据
    X_mean = np.mean(X, axis=0)
    X_std = np.std(X, axis=0)
    X_std_data = (X - X_mean) / X_std

    # 计算协方差矩阵
    cov_matrix = np.cov(X_std_data.T)

    # 特征值分解
    eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)

    # 按特征值从大到小排序
    idx = np.argsort(eigenvalues)[::-1]
    eigenvalues = eigenvalues[idx]
    eigenvectors = eigenvectors[:, idx]

    # 计算解释方差比
    explained_variance_ratio = eigenvalues / np.sum(eigenvalues)
    print(f"解释方差比: {np.sum(explained_variance_ratio[:n_components]):.4f}")

    # 选择前n_components个特征向量
    selected_vectors = eigenvectors[:, :n_components]

    # 投影到主成分空间
    X_pca = np.dot(X_std_data, selected_vectors)

    # 返回重建所需的数据
    return X_pca, selected_vectors, X_mean, X_std, explained_variance_ratio


# 3. 可视化原始和重建图片
def visualize_reconstruction(original_data, reconstructed_data, image_paths, img_shape, num_samples=5):
    # 随机选择 num_samples 张图片
    indices = np.random.choice(original_data.shape[0], num_samples, replace=False)

    # 设置支持中文的字体
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
    plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

    # 创建子图
    fig, axes = plt.subplots(num_samples, 2, figsize=(8, num_samples * 4))
    if num_samples == 1:
        axes = [axes]  # 确保单张图片时 axes 可迭代

    for i, idx in enumerate(indices):
        # 原始图片
        original_img = original_data[idx].reshape(img_shape)
        axes[i, 0].imshow(original_img, cmap='gray')
        axes[i, 0].set_title(f'原始图片 (ID: {idx})')
        axes[i, 0].axis('off')

        # 重建图片
        reconstructed_img = reconstructed_data[idx].reshape(img_shape)
        axes[i, 1].imshow(reconstructed_img, cmap='gray')
        axes[i, 1].set_title(f'PCA 重建图片 (ID: {idx})')
        axes[i, 1].axis('off')

    plt.suptitle('原始图片与 PCA 重建图片对比')
    plt.tight_layout(rect=[0, 0, 1, 0.95])
    plt.show()


# 4. 主程序
def main():
    # 数据集路径
    base_path = 'ORL_Faces'  # 请确认实际路径
    try:
        X, y, image_paths = load_images(base_path)
    except Exception as e:
        print(f"加载图片失败: {e}")
        return

    # 保存原始数据和图片尺寸
    X_original = X.copy()
    img_shape = (112, 92)  # ORL Faces 图片尺寸,通常为 112x92

    # 应用手动PCA
    n_components = 50  # 降维后的维度
    X_pca, selected_vectors, X_mean, X_std, explained_variance = manual_pca(X, n_components)

    # 重建数据
    X_reconstructed = np.dot(X_pca, selected_vectors.T)  # 逆投影
    X_reconstructed = X_reconstructed * X_std + X_mean  # 恢复标准化前的尺度

    # 设置支持中文的字体
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    # 可视化前两个主成分
    plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='viridis')
    plt.xlabel('第一个主成分')
    plt.ylabel('第二个主成分')
    plt.title('ORL Faces 数据集的手动 PCA 结果')
    plt.colorbar(label='类别')
    plt.show()

    # 可视化原始和重建图片
    visualize_reconstruction(X_original, X_reconstructed, image_paths, img_shape, num_samples=5)

    # 保存降维后的数据
    np.save('X_pca_manual.npy', X_pca)


if __name__ == '__main__':
    main()

五、总结

PCA作为一种强大的数据降维技术,在数据分析领域具有广泛的应用价值。其核心思想是通过线性变换将原始数据转换为低维表示,同时保留数据中的主要变化模式。通过PCA,我们可以降低数据维度、去除噪声和冗余信息,提高数据分析的效率和准确性。在实际应用中,PCA已被广泛应用于图像识别与处理、基因组学数据分析、金融市场分析等多个领域,为这些领域的发展提供了有力的支持。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐