【机器学习】主成分分析法(PCA)

【机器学习】主成分分析法(PCA)

一、摘要

本文主要讲述了主成分分析法(PCA)的原理和应用。PCA通过选择最重要的特征,将高维数据映射到低维空间,同时保持数据间的关系,实现降维和去噪。通过具体的例子和图示解释了PCA的原理,并比较了两种降维方案,解释了为什么选择将数据映射到x轴上可以得到更好的分类效果。最后强调了在PCA之前需要对所有样本的均值进行归零处理。通过这篇文章,读者可以深入了解PCA的原理和应用,并掌握如何使用PCA进行数据降维和去噪,提高分类效果。

二、主成分分析的基本概念

  1. 定义
    PCAPrincipal Component Analysis)即主成分分析,是机器学习中一种常用的数据分析技术,PCA 是一种无监督的线性降维算法它通过线性变换将原始数据转换为一组新的特征 ,这些新特征 被称为主成分,它们是原始特征的线性组合,并且在尽可能保留原始数据信息的前提下实现数据维度的降低
  2. 作用
    • 数据降维 :在很多实际应用中,数据的维度可能非常高,这会导致计算量增大、模型训练时间变长以及可能出现的过拟合等问题。PCA 可以将高维数据映射到低维空间,在保留大部分关键信息的同时,大大减少数据的维度,提高计算效率和模型性能。
    • 去除噪声和冗余 :原始数据中往往存在一些噪声和冗余信息,这些信息可能会干扰模型的训练和预测。PCA 通过提取主成分,可以去除一些与其他特征高度相关的冗余信息,以及一些对数据整体结构影响较小的噪声,使数据更加干净和易于处理。
    • 数据可视化 :对于高维数据,很难直接进行可视化展示。通过 PCA 将数据降维到二维或三维空间后,可以方便地进行数据可视化,帮助人们直观地理解数据的分布和结构,发现数据中的潜在规律和异常点。
  3. 底层原理
    • 协方差矩阵与特征值分解首先计算原始数据的协方差矩阵,协方差矩阵可以衡量各个特征之间的相关性。然后对协方差矩阵进行特征值分解,得到特征值特征向量特征值表示对应主成分方差大小特征向量则表示主成分方向
    • 主成分的选取 :按照特征值从大到小顺序特征向量进行排序,选择前k个特征向量 作为主成分,其中k是降维后想要得到的维度。这些主成分所对应的特征值较大,意味着它们能够解释原始数据中较大比例的方差,即包含了原始数据的大部分重要信息。
    • 数据投影 :将原始数据投影到选取的主成分张成子空间上,就得到了降维后的数据。具体来说,对于原始数据中的每个样本,通过与主成分对应的特征向量进行线性变换,将其映射到新的低维空间中。
  4. 补充方差的概念
    1. 方差的意义:方差是数据点与其平均值之间差异的平方的平均数。它反映了数据分布的分散程度。
    2. 方差大 vs 方差小
      • 方差大:数据点远离均值,分布广泛,样本显得稀疏。
      • 方差小:数据点接近均值,集中分布,样本显得紧密。
    3. 图形化理解 :高方差的数据通常在图表中表现为更长的尾巴或更大的范围,而低方差则集中在中间区域。
      结论:方差越大,样本间越稀疏;方差越小,样本间越紧密。

三、主成分分析的数学模型

  1. PCA的目标是找到一个方向向量w,使得所有样本映射到该方向后方差最大。而PCA寻找最大方差方向,进行数据降维。

  2. 映射后的样本方差计算公式为每个样本与方向向量w的点积的平方和除以样本数量m。 映射过程可以通过几何方式解释,即将每个样本点垂直投影到目标方向向量w上。投影长度等于样本点与方向向量w的点积结果,反映了样本在该方向上的分布情况。

  3. 当均值为零时,方差计算公式进一步简化为向量模的平方和除以m。

五、主成分分析法目标函数公式推导(梯度上升法求解目标函数)

  1. 目标函数对每一个w求偏导数梯度):

  2. 通过向量点乘 后简化公式如下:

  3. 通过进一步向量化点乘 处理后,可以简化如下所示:

  4. 最后来看下简化的梯度

六、梯度上升法求解目标函数第一个主成分

  1. 生成测试数据集

    python 复制代码
    import numpy as np
    import matplotlib.pyplot as plt
    
    # 生成数据
    X = np.empty((100, 2))
    X[:, 0] = np.random.uniform(0., 100., size=100)
    X[:, 1] = 0.75 * X[:, 0] + 3. + np.random.normal(0, 10., size=100)
    
    # 绘制数据散点图
    plt.scatter(X[:, 0], X[:, 1])
    plt.show()

    效果:

  2. 均值归零处理

    python 复制代码
    # 在进行PCA之前需要实现均值归一划处理,也就是将均值归零
    # 1. 定义均值归零的函数,定义了一个名为demean的函数,该函数接受一个数据集X作为参数。
    def demean(X):
        # return X - np.mean(X, axis=0) 是函数的主体部分。
        # np.mean(X, axis=0)计算数据集X每列的均值,axis=0表示沿着列方向计算。
        # 然后用原始数据集X减去每列的均值,实现对数据的去均值操作,即让每列数据的均值变为 0 ,最后返回去均值后的数据。
        return X - np.mean(X, axis=0) 
        
    # 2. 调用函数,并赋值给X_demean
    X_demean = demean(X)
    
    # 3. plt.scatter(X_demean[:, 0], X_demean[:, 1]) 使用matplotlib库的scatter函数绘制散点图,横坐标为去均值后数据X_demean的第一列,纵坐标为第二列。
    plt.scatter(X_demean[:, 0], X_demean[:, 1])
    plt.show()

    效果:

    验证下均值归零是否正常:

    python 复制代码
    # 验证下均值归零是否正常
    print(np.mean(X_demean[:, 0])) # 计算第一列均值
    print(np.mean(X_demean[:, 1])) # 计算第二列均值

    结果:

    1.1723955140041652e-14 约等于0

    2.5579538487363606e-15 约等于0

  3. 目标函数、梯度上升法等对应公式的代码实现

    python 复制代码
    # 1. 实现目标函数:找到w,使得方差最大的函数,接受权重向量w和数据集X作为参数
    def f(w, X):
        return np.sum((X.dot(w)**2)) / len(X)
    
    # 2. 求梯度,即目标函数f的解析梯度函数,用于计算梯度。这里通过数学推导得出的梯度公式,X.T.dot(X.dot(w) * 2. / len(X))实现对目标函数求导后的计算,返回梯度向量。
    def df_math(w, X):
        return X.T.dot(X.dot(w) * 2. / len(X))
    
    # 3. 该函数是通过有限差分法近似计算梯度,用于调试和验证df_math函数的正确性。
    #    epsilon是一个很小的数,用于计算数值梯度。通过分别对权重向量w的每个维度加上和减去epsilon,计算对应的函数值差,再除以2 * epsilon来近似该维度上的梯度,最终返回一个近似梯度向量。
    def df_debug(w, X, epsilon=0.0001):
        res = np.empty(len(w))
        for i in range(len(w)):
            w_1 = w.copy()
            w_1[i] += epsilon
            w_2 = w.copy()
            w_2[i] -= epsilon
            res[i] = (f(w_1, X) - f(w_2, X)) / (2 * epsilon)
        return res
    
    # 4. 该函数用于将输入的向量w进行归一化处理,使其成为单位向量。
    #    np.linalg.norm(w)计算向量w的范数(长度),通过将向量w的每个元素除以其范数,得到方向不变但长度为 1 的单位向量。
    def direction(w):
        return w / np.linalg.norm(w)
    
    # 5. 定义gradient_ascent函数,实现梯度上升算法:
    # def gradient_ascent(df, X, initial_w, eta, n_iters = 1e4, epsilon=1e-8),实现梯度上升算法。参数含义如下:
    # df:计算目标函数梯度的函数。
    # X:数据集。
    # initial_w:初始权重向量。
    # eta:学习率,控制每次权重更新的步长。
    # n_iters:最大迭代次数,默认值为10000(1e4)。
    # epsilon:收敛阈值,默认值为1e-8,用于判断算法是否收敛。
    # w = direction(initial_w),对初始权重向量initial_w进行归一化处理,得到初始的单位权重向量w。
    # cur_iter = 0,初始化当前迭代次数为0。
    # while cur_iter < n_iters:,开始迭代循环,只要当前迭代次数小于最大迭代次数就继续执行:
    # gradient = df(w, X),调用梯度计算函数df,计算当前权重w下的梯度。
    # last_w = w,保存上一次的权重向量。
    # w = w + eta * gradient,根据梯度上升公式更新权重向量,沿着梯度方向移动eta倍的梯度长度。
    # w = direction(w),对更新后的权重向量w再次进行归一化处理,保持其为单位向量。
    # if(abs(f(w, X) - f(last_w, X)) < epsilon): break,计算当前权重和上一次权重对应的目标函数值之差的绝对值,如果小于收敛阈值epsilon,则认为算法收敛,跳出循环。
    # cur_iter += 1,每次迭代结束后,迭代次数加1。
    # return w,循环结束后,返回最终得到的权重向量。
    def gradient_ascent(df, X, initial_w, eta, n_iters = 1e4, epsilon=1e-8):
        w = direction(initial_w)
        cur_iter = 0
        while cur_iter < n_iters:
            gradient = df(w, X)
            last_w = w
            w = w + eta * gradient
            w = direction(w)
            if(abs(f(w, X) - f(last_w, X)) < epsilon):
                break
            cur_iter += 1
        return w

    验证代码正确性:

    python 复制代码
    # 测试梯度上升法求解结果的正确性
    # 初始化权重向量
    # 生成一个长度等于数据特征维度(这里是 2)的随机权重向量。不能用 0 向量开始,可能是因为用 0 向量开始在梯度上升法迭代中会导致一些问题,比如收敛缓慢或无法收敛。
    initial_w = np.random.random(X.shape[1])  # 注意2:不能用0向量开始
    print(initial_w)
    
    # 设置梯度上升法中的学习率,控制每次权重更新的步长。
    eta = 0.001
    
    # 注意3:不能使用StandardScaler标准化数据
    print(gradient_ascent(df_debug, X_demean, initial_w, eta))
    print(gradient_ascent(df_math, X_demean, initial_w, eta))

    结果如下:

    [0.55799828 0.70512732]

    [0.76320173 0.64616029] df_debug函数求得的数据

    [0.76320173 0.64616029] df_math函数求得的数据,和df_debug函数求得的数据是一致的。

  4. 求解第一主成分

    python 复制代码
    # 调用gradient_ascent函数,传入梯度计算函数df_math、去均值后的数据集X_demean、初始权重向量initial_w和学习率eta,通过梯度上升法计算得到最终的权重向量w。
    w = gradient_ascent(df_math, X_demean, initial_w, eta)
    
    # 绘制w向量在二维坐标平面上的图像,这就是我们通过梯度上升法找到的第一个主成分
    plt.scatter(X_demean[:, 0], X_demean[:, 1]) # 横坐标为X_demean的第一列数据,纵坐标为第二列数据
    plt.plot([0, w[0]*30], [0, w[1]*30], color='r') # 由于w是单位向量,对于的数字太小了(1),为了图像好效果明显些,这里分别在横纵坐标轴各自乘以30作为系数
    plt.show()

    效果如下:

七、求解前n个主成分及PCA在数据预处理中的处理步骤(后续实现)

  1. 数据标准化 :首先对原始数据进行标准化处理,将每个特征的均值变为0,方差变为1。这是因为PCA对数据的尺度比较敏感,如果不同特征的尺度差异较大,可能会导致某些特征在协方差计算中占据主导地位,影响主成分的提取效果。均值归零的处理办法:
    • 在进行主成分分析之前,需要对所有样本进行均值归零处理。
    • 均值归零处理通过减去样本整体均值,使样本在每个维度上的均值变为零。
    • 处理后的方差计算公式得到简化,方便后续计算。
  2. 计算协方差矩阵:对标准化后的数据计算协方差矩阵,协方差矩阵能够反映各个特征之间的线性关系。
  3. 特征值分解:对协方差矩阵进行特征值分解,得到特征值和特征向量。
  4. 确定主成分个数:根据具体的应用需求和数据特点,确定需要保留的主成分个数k。常见的方法是根据累计方差贡献率来确定,即选择使得累计方差贡献率达到一定阈值(如80%、90%等)的最小k值。
  5. 构建主成分矩阵:选取前k个最大特征值对应的特征向量,构建一个大小为n×k的主成分矩阵,其中n是原始数据的特征维度。
  6. 数据降维:将原始数据与主成分矩阵相乘,得到降维后的数据。降维后的数据维度为k,且尽可能保留了原始数据的重要信息。
相关推荐
奇墨 ITQM3 分钟前
奇墨科技FinOps云成本优化:精细化IT成本分摊重塑企业云财务管理
大数据·运维·人工智能·科技·云计算
声光界5 分钟前
定义未来!广东天谱科技集团有限公司荣获“GAS科创奖-产品创新奖”!
人工智能·科技·音视频·声学·声学技术
一只积极向上的小咸鱼7 分钟前
PyTorch 和 Python关系
人工智能·pytorch·python
訾博ZiBo38 分钟前
AI日报 - 2025年3月14日
人工智能
程序猿阿伟38 分钟前
《鸿蒙系统下AI模型训练加速:时间成本的深度剖析与优化策略》
人工智能·华为·harmonyos
源之缘-OFD先行者42 分钟前
WPF 与 GMap.NET 结合实现雷达目标动态显示与地图绘制
信息可视化·.net·wpf
2501_907136821 小时前
单机DeepSeek做PPT,YYDS!
人工智能·微信·电脑·powerpoint
TMT星球1 小时前
从技术创新到全球布局:MOVA割草机器人以尖端科技定义智能园艺
大数据·人工智能·机器人
有个人神神叨叨1 小时前
metagpt agent 时代的公司
人工智能·ai
轻松Ai享生活2 小时前
AI+IDE扩展:从"人机互怼"到"代码开挂"的真香现场
人工智能·敏捷开发