手动实现PCA算法

100 阅读6分钟

PCA算法

PCA原理

PCA算法,即主成分分析方法,是一种常用的数据分析方法。通过线性变换将原始数据转换成一组新的变量,即主成分。通过主成分最大程度上保存原始数据的方差信息,同时实现数据的降维

主要思想:将n维特征映射到k维上,这k维是全新的正交特征,也被称为主成分,是在原n维特征的基础上重新构造出来的k特征。

具体实现:从原始是空间中顺序地找到一组相互正交的坐标轴,新的坐标轴的选取与数据本身是密切相关的。坐标选择如下表:

新坐标轴选择依据
第一个新坐标轴原始数据中方差最大的方向
第二个新坐标轴与第一个坐标轴正交的平面中使得方差最大的方向
第三个新坐标轴与第1,2个轴正交的平面中方差最大的方向
……依次类推,可以得到n个这样的坐标轴。

通过这种方式获得的新的坐标轴,大部分方差都包含在前面k个坐标轴中,后面的坐标轴所含的方差几乎为0。所以可以忽略余下的坐标轴,只保留前面k个含有绝大部分方差的坐标轴

事实上,这相当于只保留包含绝大部分方差的维度特征,而忽略包含方差几乎为0的特征维度,实现对数据特征的降维处理。有助于简化数据结构、减少计算量,可提取数据中最有价值的信息。

算法流程

以下是手动PCA算法实现的流程

数据挖掘-PCA算法实现流程.drawio

根据PCA算法是实现流程可知,PCA算法是核心目标是"降维+保留信息"。即将高维数据映射到低维空间内,解决高维度数据带来的问题。PCA通过线性变换将原始数据转换成一组新的变量(即"坐标轴"),即主成分

实验实现

  1. 1. 实验目的
    分别进行手动实现PCA算法和用sklearn进行PCA算法的实现,实现对算法的学习和理解。
    要求:手动实现PCA算法,标准化数据,确保每个特征的均值为零;将数据降维到2

  2. 2. 数据准备
    使用sklearn库中的iris作为实验数据集,其中每个样本有4个特征参数,分别为花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性。

  3. 3. 实验流程手动实现PCA算法实验流程:

    数据挖掘-手动实现PCA算法.drawio

    sklearn进行PCA算法:实现方法比较简单,直接使用scikit-learnPCA类实现降维,最后用matplotlib进行数据可视化即可。

  4. 4. 代码实现
    数据集选取
    选用鸢尾花数据集(Iris dataset),通过sklearn.datasets中的load_iris()函数进行加载,数据存储在X(特征数据)和y(目标数据)中。

    iris = load_iris()
    X = iris.data
    y = iris.target
    

    方法一:手动实现PCA算法
    ①数据标准化

    # 数据去中心化,使得数据的均值变为 0
    def standardize_data(X):
        mean = np.mean(X, axis=0)
        X_centered = X - mean
        return X_centered
    
    X_centered = standardize_data(X)
    print("中心化后的数据(前五行):")
    print(X_centered[:5])
    

    ②求协方差矩阵代码

    def calculate_covariance_matrix(X):
        cov_matrix = np.cov(X, rowvar=False)
        return cov_matrix
    cov_matrix = calculate_covariance_matrix(X_centered)
    print("协方差矩阵:")
    print(cov_matrix)
    

    ③求特征值和特征向量代码

    # 求特征值和特征向量
    # 特征值λ和特征向量v满足方程Av = λv
    def calculate_eigenvalues_eigenvectors(cov_matrix):
        eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)
        return eigenvalues, eigenvectors
    
    eigenvalues, eigenvectors = calculate_eigenvalues_eigenvectors(cov_matrix)
    print("特征值:\n", eigenvalues)
    print("特征向量:\n", eigenvectors)
    

    ④将数据降到2

    # 将数据降到2维
    # 按照特征值的大小对特征向量进行排序,选择前K个特征向量,被选中的特征向量所构成的矩阵就是投影矩阵
    # 将中心化后的数据与投影矩阵相乘
    def reduce_dimension(X, k):
        idx = np.argsort(eigenvalues)[::-1][:k]
        selected_eigenvectors = eigenvectors[:, idx]
        # 为方便对比两种方法的结果,尝试保持特征向量方向与sklearn一致
        pca = PCA(n_components=k)
        pca.fit(X_centered)
        sklearn_eigenvectors = pca.components_.T
        for i in range(k):
            if np.dot(selected_eigenvectors[:, i], sklearn_eigenvectors[:, i]) < 0:
                selected_eigenvectors[:, i] *= -1
        X_reduced = X_centered.dot(selected_eigenvectors)
        return X_reduced
    
    X_reduced_manual = reduce_dimension(X, 2)
    print("手动实现PCA降维后的数据(前五行):")
    print(X_reduced_manual[:5])
    

    方法二:用sklearn进行PCA算法

    # 导入模块
    from sklearn.decomposition import PCA
    

    算法实现

    pca = PCA(n_components=2)
    X_reduced_sklearn = pca.fit_transform(X)
    print("使用sklearn实现PCA降维后的数据(前五行):")
    print(X_reduced_sklearn[:5])
    

    结果可视化

    # 4. 可视化原始数据和降维后的数据
    plt.figure(figsize=(125))
    
    # 原始数据
    plt.subplot(1, 3, 1)
    plt.scatter(X[:, 0], X[:, 1]c=y)
    plt.title('Original Data')
    
    # 手动实现PCA降维后的数据
    plt.subplot(1, 3, 2)
    plt.scatter(X_reduced_manual[:, 0], X_reduced_manual[:, 1]c=y)
    plt.title('PCA (Manual) Reduced Data')
    
    # sklearn实现PCA降维后的数据
    plt.subplot(1, 3, 3)
    plt.scatter(X_reduced_sklearn[:, 0], X_reduced_sklearn[:, 1]c=y)
    plt.title('PCA (sklearn) Reduced Data')
    
    plt.show()
    

    完整实现代码

    import numpy as np
    from sklearn.datasets import load_iris
    from sklearn.decomposition import PCA
    import matplotlib.pyplot as plt
    
    
    # 1. 数据加载
    iris = load_iris()
    X = iris.data
    y = iris.target
    
    # 2. 方法一:手动实现PCA
    # 数据去中心化,使得数据的均值变为 0
    def standardize_data(X):
        mean = np.mean(X, axis=0)
        X_centered = X - mean
        return X_centered
    
    X_centered = standardize_data(X)
    print("中心化后的数据(前五行):")
    print(X_centered[:5])
    
    # 求协方差矩阵
    def calculate_covariance_matrix(X):
        cov_matrix = np.cov(X, rowvar=False)
        return cov_matrix
    cov_matrix = calculate_covariance_matrix(X_centered)
    print("协方差矩阵:")
    print(cov_matrix)
    
    
    # 求特征值和特征向量
    # 特征值λ和特征向量v满足方程Av = λv
    def calculate_eigenvalues_eigenvectors(cov_matrix):
        eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)
        return eigenvalues, eigenvectors
    
    eigenvalues, eigenvectors = calculate_eigenvalues_eigenvectors(cov_matrix)
    print("特征值:\n", eigenvalues)
    print("特征向量:\n", eigenvectors)
    
    # 将数据降到2维
    # 按照特征值的大小对特征向量进行排序,选择前K个特征向量,被选中的特征向量所构成的矩阵就是投影矩阵
    # 将中心化后的数据与投影矩阵相乘
    def reduce_dimension(X, k):
        idx = np.argsort(eigenvalues)[::-1][:k]
        selected_eigenvectors = eigenvectors[:, idx]
        # 为方便对比两种方法的结果,尝试保持特征向量方向与sklearn一致
        pca = PCA(n_components=k)
        pca.fit(X_centered)
        sklearn_eigenvectors = pca.components_.T
        for i in range(k):
            if np.dot(selected_eigenvectors[:, i], sklearn_eigenvectors[:, i]) < 0:
                selected_eigenvectors[:, i] *= -1
        X_reduced = X_centered.dot(selected_eigenvectors)
        return X_reduced
    
    X_reduced_manual = reduce_dimension(X, 2)
    print("手动实现PCA降维后的数据(前五行):")
    print(X_reduced_manual[:5])
    
    # 3. 方法二:使用sklearn进行PCA
    pca = PCA(n_components=2)
    X_reduced_sklearn = pca.fit_transform(X)
    print("使用sklearn实现PCA降维后的数据(前五行):")
    print(X_reduced_sklearn[:5])
    
    # 4. 可视化原始数据和降维后的数据
    plt.figure(figsize=(125))
    
    # 原始数据
    plt.subplot(1, 3, 1)
    plt.scatter(X[:, 0], X[:, 1]c=y)
    plt.title('Original Data')
    
    # 手动实现PCA降维后的数据
    plt.subplot(1, 3, 2)
    plt.scatter(X_reduced_manual[:, 0], X_reduced_manual[:, 1]c=y)
    plt.title('PCA (Manual) Reduced Data')
    
    # sklearn实现PCA降维后的数据
    plt.subplot(1, 3, 3)
    plt.scatter(X_reduced_sklearn[:, 0], X_reduced_sklearn[:, 1]c=y)
    plt.title('PCA (sklearn) Reduced Data')
    
    plt.show()
    

    实验结果

    image-20251031031242295

    中心化后的数据(前五行):
    [[-0.74333333  0.44266667 -2.358      -0.99933333]
     [-0.94333333 -0.05733333 -2.358      -0.99933333]
     [-1.14333333  0.14266667 -2.458      -0.99933333]
     [-1.24333333  0.04266667 -2.258      -0.99933333]
     [-0.84333333  0.54266667 -2.358      -0.99933333]]
    协方差矩阵:
    [[ 0.68569351 -0.042434    1.27431544  0.51627069]
     [-0.042434    0.18997942 -0.32965638 -0.12163937]
     [ 1.27431544 -0.32965638  3.11627785  1.2956094 ]
     [ 0.51627069 -0.12163937  1.2956094   0.58100626]]
    特征值:
     [4.22824171 0.24267075 0.0782095  0.02383509]
    特征向量:
     [[ 0.36138659 -0.65658877 -0.58202985  0.31548719]
     [-0.08452251 -0.73016143  0.59791083 -0.3197231 ]
     [ 0.85667061  0.17337266  0.07623608 -0.47983899]
     [ 0.3582892   0.07548102  0.54583143  0.75365743]]
    手动实现PCA降维后的数据(前五行):
    [[-2.68412563  0.31939725]
     [-2.71414169 -0.17700123]
     [-2.88899057 -0.14494943]
     [-2.74534286 -0.31829898]
     [-2.72871654  0.32675451]]
    使用sklearn实现PCA降维后的数据(前五行):
    [[-2.68412563  0.31939725]
     [-2.71414169 -0.17700123]
     [-2.88899057 -0.14494943]
     [-2.74534286 -0.31829898]
     [-2.72871654  0.32675451]]