第5章 -PCA

目录

1. 向量表示与基变换

1.1 内积

1.2 基

1.3 基变换的矩阵表示

2. 最大可分性

2.1 方差

2.2 协方差

2.3 协方差矩阵

2.4 矩阵对角化

2.5 补充

4. PCA算法求解步骤

5. PCA降维实例(二维降一维):

6. 细节 

6.1 零均值化

6.2 与 SVD 的对比

7 实战一(梯度上升实现PCA)

7.1 步骤:

7.2 代码:

8 实战二(PCA中的解释方差比例)

8.1 步骤:

8.2 代码:

8.3 结果分析:

8.4 降低维度的好处:

9 实战三(PCA降维应用)

9.1 步骤:

9.2 代码:


PCA(Principal Component Analysis) 是一种常见的数据分析方式,常用于高维数据的降维,可用于提取数据的主要特征分量。

PCA 的数学推导可以从最大可分型和最近重构性两方面进行,前者的优化条件为划分后方差最大,后者的优化条件为点到划分平面距离最小,这里我将从最大可分性的角度进行证明。

部分内容转载自【机器学习】降维——PCA(非常详细) - 知乎

1. 向量表示与基变换

我们先来介绍些线性代数的基本知识。

1.1 内积

两个向量的 A 和 B 内积我们知道形式是这样的:

内积运算将两个向量映射为实数,其计算方式非常容易理解,但我们无法看出其物理含义。接下来我们从几何角度来分析,为了简单起见,我们假设 A 和 B 均为二维向量,则:

其几何表示见下图:

我们看出 A 与 B 的内积等于 A 到 B 的投影长度乘以 B 的模。

如果假设 B 的模为 1,即让 |B|=1 ,那么就变成了: 

也就是说,A 与 B 的内积值等于 A 向 B 所在直线投影的标量大小。

这就是内积的一种几何解释,也是我们得到的第一个重要结论。在后面的推导中,将反复使用这个结论。

1.2 基

在我们常说的坐标系种,向量 (3,2) 其实隐式引入了一个定义:以 x 轴和 y 轴上正方向长度为 1 的向量为标准。向量 (3,2) 实际是说在 x 轴投影为 3 而 y 轴的投影为 2。注意投影是一个标量,所以可以为负。

所以,对于向量 (3, 2) 来说,如果我们想求它在 (1,0),(0,1) 这组基下的坐标的话,分别内积即可。当然,内积完了还是 (3, 2)。

所以,我们大致可以得到一个结论,我们要准确描述向量,首先要确定一组基,然后给出在基所在的各个直线上的投影值,就可以了。为了方便求坐标,我们希望这组基向量模长为 1。因为向量的内积运算,当模长为 1 时,内积可以直接表示投影。然后还需要这组基是线性无关的,我们一般用正交基,非正交的基也是可以的,不过正交基有较好的性质

1.3 基变换的矩阵表示

这里我们先做一个练习:对于向量 (3,2) 这个点来说,在和 这组基下的坐标是多少?

我们拿 (3,2) 分别与之内积,得到 这个新坐标。

我们可以用矩阵相乘的形式简洁的表示这个变换:

左边矩阵的两行分别为两个基,乘以原向量,其结果刚好为新基的坐标。推广一下,如果我们有 m 个二维向量,只要将二维向量按列排成一个两行 m 列矩阵,然后用“基矩阵”乘以这个矩阵就可以得到了所有这些向量在新基下的值。例如对于数据点(1,1),(2,2),(3,3) 来说,想变换到刚才那组基上,则可以这样表示: 

我们可以把它写成通用的表示形式:

其中p_{i}是一个行向量,表示第 i 个基, \alpha _{i}是一个列向量,表示第 j 个原始数据记录。实际上也就是做了一个向量矩阵化的操作。 

上述分析给矩阵相乘找到了一种物理解释:两个矩阵相乘的意义是将右边矩阵中的每一列向量\alpha _{i}变换到左边矩阵中以每一行行向量为基所表示的空间中去。也就是说一个矩阵可以表示一种线性变换。

2. 最大可分性

上面我们讨论了选择不同的基可以对同样一组数据给出不同的表示,如果基的数量少于向量本身的维数,则可以达到降维的效果。

但是我们还没回答一个最关键的问题:如何选择基才是最优的。或者说,如果我们有一组 N 维向量,现在要将其降到 K 维(K 小于 N),那么我们应该如何选择 K 个基才能最大程度保留原有的信息?

一种直观的看法是:希望投影后的投影值尽可能分散,因为如果重叠就会有样本消失。当然这个也可以从熵的角度进行理解,熵越大所含信息越多

2.1 方差

我们知道数值的分散程度,可以用数学上的方差来表述。一个变量的方差可以看做是每个元素与变量均值的差的平方和的均值,即:

为了方便处理,我们将每个变量的均值都化为 0 ,因此方差可以直接用每个元素的平方和除以元素个数表示:

于是上面的问题被形式化表述为:寻找一个一维基,使得所有数据变换为这个基上的坐标表示后,方差值最大。

2.2 协方差

在一维空间中我们可以用方差来表示数据的分散程度。而对于高维数据,我们用协方差进行约束,协方差可以表示两个变量的相关性。为了让两个变量尽可能表示更多的原始信息,我们希望它们之间不存在线性相关性,因为相关性意味着两个变量不是完全独立,必然存在重复表示的信息。

协方差公式为:

由于均值为 0,所以我们的协方差公式可以表示为:

当样本数较大时,不必在意其是 m 还是 m-1,为了方便计算,我们分母取 m。

当协方差为 0 时,表示两个变量线性不相关,但并非完全独立。为了让协方差为 0,我们选择第二个基时只能在与第一个基正交的方向上进行选择,因此最终选择的两个方向一定是正交的。

至此,我们得到了降维问题的优化目标:将一组 N 维向量降为 K 维,其目标是选择 K 个单位正交基,使得原始数据变换到这组基上后,各变量两两间协方差为 0,而变量方差则尽可能大(在正交的约束下,取最大的 K 个方差)。

2.3 协方差矩阵

针对我们给出的优化目标,接下来我们将从数学的角度来给出优化目标。

我们看到,最终要达到的目的与变量内方差及变量间协方差有密切关系。因此我们希望能将两者统一表示,仔细观察发现,两者均可以表示为内积的形式,而内积又与矩阵相乘密切相关。于是我们有:

假设我们只有 a 和 b 两个变量,那么我们将它们按行组成矩阵 X:

然后:

我们可以看到这个矩阵对角线上的分别是两个变量的方差,而其它元素是 a 和 b 的协方差。两者被统一到了一个矩阵里。

我们很容易被推广到一般情况:

设我们有 m 个 n 维数据记录,将其排列成矩阵X_{n,m},设C=\frac{1}{m}XX^{T},则 C 是一个对称矩阵,其对角线分别对应各个变量的方差,而第 i 行 j 列和 j 行 i 列元素相同,表示 i 和 j 两个变量的协方差

2.4 矩阵对角化

根据我们的优化条件,我们需要将除对角线外的其它元素化为 0,并且在对角线上将元素按大小从上到下排列(变量方差尽可能大),这样我们就达到了优化目的。这样说可能还不是很明晰,我们进一步看下原矩阵与基变换后矩阵协方差矩阵的关系。

设原始数据矩阵 X 对应的协方差矩阵为 C,而 P 是一组基按行组成的矩阵,设 Y=PX,则 Y 为 X 对 P 做基变换后的数据。设 Y 的协方差矩阵为 D,我们推导一下 D 与 C 的关系:

这样我们就看清楚了,我们要找的 P 是能让原始协方差矩阵对角化的 P。换句话说,优化目标变成了寻找一个矩阵 P,满足PCP^{T}是一个对角矩阵,并且对角元素按从大到小依次排列,那么 P 的前 K 行就是要寻找的基,用 P 的前 K 行组成的矩阵乘以 X 就使得 X 从 N 维降到了 K 维并满足上述优化条件。 

至此,我们离 PCA 还有仅一步之遥,我们还需要完成对角化。

由上文知道,协方差矩阵 C 是一个是对称矩阵,在线性代数中实对称矩阵有一系列非常好的性质:

  1. 实对称矩阵不同特征值对应的特征向量必然正交。
  2. 设特征向量 \lambda重数为 r,则必然存在 r 个线性无关的特征向量对应于 \lambda,因此可以将这 r 个特征向量单位正交化。

由上面两条可知,一个 n 行 n 列的实对称矩阵一定可以找到 n 个单位正交特征向量,设这 n 个特征向量为 ,我们将其按列组成矩阵: 

则对协方差矩阵 C 有如下结论:

其中 Λ 为对角矩阵,其对角元素为各特征向量对应的特征值(可能有重复)。

到这里,我们发现我们已经找到了需要的矩阵 P: 。 

P 是协方差矩阵的特征向量单位化后按行排列出的矩阵,其中每一行都是 C 的一个特征向量。如果设 P 按照 Λ 中特征值的从大到小,将特征向量从上到下排列,则用 P 的前 K 行组成的矩阵乘以原始数据矩阵 X,就得到了我们需要的降维后的数据矩阵 Y。

2.5 补充

(1) 拉格朗日乘子法

在叙述求协方差矩阵对角化时,我们给出希望变化后的变量有:变量间协方差为 0 且变量内方差尽可能大。然后我们通过实对称矩阵的性质给予了推导,此外我们还可以把它转换为最优化问题利用拉格朗日乘子法来给予推导。(相关推导过程请参见原文链接)

(2) 最近重构性

以上的证明思路主要是基于最大可分性的思想,通过一条直线使得样本点投影到该直线上的方差最大。除此之外,我们还可以将其转换为线型回归问题,其目标是求解一个线性函数使得对应直线能够更好地拟合样本点集合。这就使得我们的优化目标从方差最大转化为平方误差最小,因为映射距离越短,丢失的信息也会越小。区别于最大可分性,这是从最近重构性的角度进行论证。

4. PCA算法求解步骤

PCA是一种线性降维算法,总结一下PCA的算法步骤:

5. PCA降维实例(二维降一维):

λ1=2,λ2=2/5 

关于特征值及其特征向量的求法可以参见数学基础类:如何求矩阵的特征值和特征向量_矩阵的特征值和特征向量怎么求-CSDN博客

降维投影结果如下图所示: 

6. 细节 

6.1 零均值化

当对训练集进行 PCA 降维时,也需要对验证集、测试集执行同样的降维。而对验证集、测试集执行零均值化操作时,均值必须从训练集计算而来,不能使用验证集或者测试集的中心向量。

其原因也很简单,因为我们的训练集时可观测到的数据,测试集不可观测所以不会知道其均值,而验证集在大部分情况下是在处理完数据后再从训练集中分离出来,一般不会单独处理。如果真的是单独处理了,不能独自求均值的原因是和测试集一样。

为啥测试集是不可观测的呢?为什么先归一化,再划分训练测试集是错误的呢?

答:

谈不上绝对的错误。特别是当你手头已经有一份训练和测试数据。在真正的部署过程中,测试数据实际上就是那些源源不断刚刚出现的数据,你不知道它什么分布,也不知道它出现什么样的数值。所以你要用训练数据得到的均值和标准偏差,去转换它。这更加贴近部署的实际。

严格来说,测试数据你是不能去看它的,不能去统计它的各项指标,这种苛刻的自我限定,就好像测试数据真的是来自未来。当然有时候你离线建模,划分测试数据,实际上叫验证数据,主要目的是为了迭代优化算法超参数,你可以严格自我要求,也可以活个稀泥。当训练和测试数据差别不大时,先归一化还是先划分,差别实际不大。

如果我们在训练或验证过程中使用测试集的信息,例如计算其均值或其他统计指标,那么我们就会暴露模型对测试集的特定特征进行过度拟合,从而导致评估结果的偏差。

具体讨论可见数据预处理的归一化手段应该如何应用到训练集,测试集和验证集中? - 知乎

另外我们也需要保证一致性,我们拿训练集训练出来的模型用来预测测试集的前提假设就是两者是独立同分布的,如果不能保证一致性的话,会出现 Variance Shift 的问题。

6.2 与 SVD 的对比

请参见原文链接

7 实战一(梯度上升实现PCA)

使用梯度上升算法实现主成分分析,并将数据投影到新的主成分方向上。通过可视化数据点和主成分方向的直线,可以观察到数据在新的主成分方向上的分布情况。

7.1 步骤:

  1. 通过 np.empty 创建一个大小为 (100, 2) 的数组 X,用于存储随机生成的二维数据。其中 X[:, 0] 是在区间 [0, 100) 内均匀分布的随机数,X[:, 1] 是根据线性关系 0.75 * X[:, 0] + 3. 生成的带有高斯噪声的数据。

  2. 使用 plt.scatter 绘制 X 数据的散点图,并使用 plt.show 显示图形。这展示了生成的随机数据的分布。

  3. 定义函数 demean(X),用于对数据进行零均值化操作。该函数计算每个特征的均值,并从数据中减去这些均值,使得数据的均值为零。

  4. 调用 demean(X) 对数据集 X 进行零均值化处理,将结果保存在 X_demean 中。

  5. 使用 plt.scatter 绘制经过零均值化处理后的数据 X_demean 的散点图,并使用 plt.show 显示图形。这展示了经过零均值化处理后数据的分布,此时数据的均值接近于零。

  6. 定义函数 f(w, X),用于计算在给定方向 w 下的目标函数值。该函数计算数据集 X 在方向 w 上的投影值的平方和,再除以样本数量,得到均方误差。

  7. 定义函数 df_math(w, X),用于计算在给定方向 w 下的梯度。该函数通过解析方法计算梯度,使用矩阵运算加速计算。

  8. 定义函数 df_debug(w, X, epsilon=0.0001),用于通过数值方法近似计算梯度。该函数在调试和验证解析梯度计算的正确性时使用,通过微小的变化来估计梯度。

  9. 定义函数 direction(w),用于计算参数向量 w 的单位方向向量。

  10. 定义函数 gradient_ascent(df, X, initial_w, eta, n_iters=1e4, epsilon=1e-8),执行梯度上升算法。该函数使用给定的梯度函数 df、数据集 X、初始参数向量 initial_w、学习率 eta 和其他参数执行梯度上升算法。在每次迭代中,通过更新参数向量 w 来最大化目标函数值。循环终止的条件是目标函数值的变化小于给定的阈值 epsilon 或达到最大迭代次数 n_iters

  11. 使用 np.random.random(X.shape[1]) 创建一个随机的初始参数向量 initial_w,其长度与数据集 X 的特征数相同。

  12. 设置学习率 eta 为 0.001。

  13. 调用 gradient_ascent(df_math, X_demean, initial_w, eta) 执行梯度上升算法,找到数据在新的主成分方向上的投影。

  14. 使用 plt.scatter 绘制经过零均值化处理后的数据 X_demean 的散点图。

  15. 使用 plt.plot 绘制一条直线,起点为原点 (0, 0),终点为 (w[0]*30, w[1]*30),颜色为红色。该直线表示最优参数向量 w 的方向,即数据在新的主成分方向上的投影。

  16. 使用 plt.showplt.show` 显示图形。

  17. 根据相同的流程,生成另一个数据集 X2,并对其进行零均值化处理。

  18. 使用 gradient_ascent 找到 X2_demean 数据在新的主成分方向上的投影。

  19. 使用 plt.scatter 绘制经过零均值化处理后的数据 X2_demean 的散点图。

  20. 使用 plt.plot 绘制一条直线,起点为原点 (0, 0),终点为 (w2[0]*30, w2[1]*30),颜色为红色。该直线表示最优参数向量 w2 的方向,即数据在新的主成分方向上的投影。

  21. 使用 plt.show 显示图形。

7.2 代码:

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()

# demean处理(零均值化操作)
def demean(X):
    return X - np.mean(X, axis=0)
X_demean = demean(X)
plt.scatter(X_demean[:,0], X_demean[:,1])
plt.show()

# 梯度上升实现PCA,找到w斜线
#X.dot(w)表示X在方向向量w上的投影
def f(w, X):
    return np.sum((X.dot(w)**2)) / len(X)#将投影值平方的总和除以样本数量,计算得到均方误差

#该函数的目的是计算在给定方向 w 下的梯度,用于更新参数 w
def df_math(w, X):
    return X.T.dot(X.dot(w)) * 2. / len(X)#

#通过数值方法(数值微分)来近似计算梯度,用于调试和验证解析梯度计算的正确性
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

# 计算向量 w 的单位方向向量
def direction(w):
    return w / np.linalg.norm(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) ## 注意1:每次求一个单位方向
        if(abs(f(w, X) - f(last_w, X)) < epsilon):
            break

        cur_iter += 1

    return w

#创建一个随机的初始参数向量 initial_w,其长度与数据集 X 的特征数相同
initial_w = np.random.random(X.shape[1]) ## 注意2:不能用0向量开始
eta = 0.001#学习率
w = gradient_ascent(df_math, X_demean, initial_w, eta)
plt.scatter(X_demean[:,0], X_demean[:,1])
plt.plot([0, w[0]*30], [0, w[1]*30], color='r')
plt.show()

# 将散点映射到新的w上
X2 = np.empty((100, 2))
X2[:,0] = np.random.uniform(0., 100., size=100)
X2[:,1] = 0.75 * X2[:,0] + 3.
X2_demean = demean(X2)
w2 = gradient_ascent(df_math, X2_demean, initial_w, eta)
plt.scatter(X2_demean[:,0], X2_demean[:,1])
plt.plot([0, w2[0]*30], [0, w2[1]*30], color='r')
plt.show()

8 实战二(PCA中的解释方差比例)

目的:使用PCA(主成分分析)对手写数字数据集进行特征降维,并使用K近邻分类器对降维后的数据进行分类,然后用R2评估降维前后以及解释方差阈值设置为0.6以及0.95前后的R2得分变化。

什么是解释方差比例以及什么是解释方差阈值?解释方差阈值的大小设置是如何影响主成分数量的?

答:在PCA中,解释方差比例是指每个主成分所能解释的方差在总方差中所占的比例。当我们设置解释方差比例阈值为0.95时,它表示我们希望选择足够多的主成分,使得这些主成分的累计解释方差比例达到至少95%。

具体来说,当我们设置解释方差比例阈值为0.95时,PCA会按照每个主成分解释方差的大小进行排序,然后选择最少的主成分数量,以满足累计解释方差比例达到至少95%的要求。这些主成分的累计解释方差比例将尽可能接近或超过95%。

因此,解释方差比例阈值并不直接决定主成分的数量,但它会影响选择的主成分数量,以使得累计解释方差比例达到或超过指定的阈值。

8.1 步骤:

  1. 导入所需的库和数据集(sklearn、numpy、matplotlib)以及加载手写数字数据集(digits)。

  2. 划分数据集为训练集和测试集,使用 train_test_split 函数。

  3. 创建一个K近邻分类器(KNeighborsClassifier),并在未经过PCA处理的数据上进行训练和测试,计算准确率得分(score1)。

  4. 导入PCA模块(from sklearn.decomposition import PCA),创建一个PCA对象,设置主成分数量为2(n_components=2),并在训练集上进行拟合。

  5. 使用训练集和测试集经过PCA转换后的数据,创建一个新的K近邻分类器,进行训练和测试,计算准确率得分(score2)。

  6. 打印降维后每个主成分所占的信息量比例(explained_variance_ratio_)和每个主成分解释的方差(explained_variance_)。

  7. 创建另一个PCA对象,将解释方差阈值设置为0.6,拟合训练集数据。

  8. 打印使用解释方差阈值0.6所能得到的主成分数量(n_components_)。

  9. 使用前N个主成分对训练集和测试集进行转换,并使用K近邻分类器进行训练和测试,计算准确率得分(score3)。

  10. 创建另一个PCA对象,将解释方差阈值设置为0.95,拟合训练集数据。

  11. 打印使用解释方差阈值0.95所能得到的主成分数量(n_components_)。

  12. 使用前N个主成分对训练集和测试集进行转换,并使用K近邻分类器进行训练和测试,计算准确率得分(score4)。

8.2 代码:

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
digits = datasets.load_digits()
X = digits.data
y = digits.target

# 未使用PCA前对手写数字数据集进行分类
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train, y_train)
score1 = knn_clf.score(X_test, y_test)
print("未使用PCA前对手写数字数据集进行分类得到的分数:",score1)

# 使用PCA主成分分析,取前两个主成分,再进行分类
# decomposition 模块是 Scikit-learn 库中用于进行数据降维和特征提取的模块
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(X_train)
X_train_reduction = pca.transform(X_train)
X_test_reduction = pca.transform(X_test)
knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train_reduction, y_train)
score2 = knn_clf.score(X_test_reduction, y_test)
print("使用PCA前(设置2个主成分)对手写数字数据集进行分类得到的分数:",score2)

print("降维后每个主成分所占的信息量比例:",pca.explained_variance_ratio_) #解释方差的比例
print("每个主成分解释的方差:",pca.explained_variance_)#解释方差

# sklearn中的PCA算法支持传入一个小于1的数来表示我们希望能解释多少比例的主成分
pca = PCA(0.6)
pca.fit(X_train)
print('使用0.6作为解释方差阈值所能得到的主成分数量:',pca.n_components_) # 取前N个主成分

# 使用前N个主成分,进行分类
X_train_reduction = pca.transform(X_train)
X_test_reduction = pca.transform(X_test)
knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train_reduction, y_train)
score3 = knn_clf.score(X_test_reduction, y_test)
print("使用0.6作为解释方差阈值的PCA对手写数字数据集进行分类得到的分数:",score3)

# sklearn中的PCA算法支持传入一个小于1的数来表示我们希望能解释多少比例的主成分
pca = PCA(0.95)
pca.fit(X_train)
print('使用0.95作为解释方差阈值所能得到的主成分数量:',pca.n_components_) # 取前N个主成分

# 使用前N个主成分,进行分类
X_train_reduction = pca.transform(X_train)
X_test_reduction = pca.transform(X_test)
knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train_reduction, y_train)
score4 = knn_clf.score(X_test_reduction, y_test)
print("使用0.6作为解释方差阈值的PCA对手写数字数据集进行分类得到的分数:",score4)

8.3 结果分析:

  1. 未使用PCA前对手写数字数据集进行分类得到的准确率为 0.9867,即约为 98.67%。这意味着在原始的 64 维特征空间中,K近邻分类器可以很好地对手写数字进行分类。

  2. 使用PCA降维到 2 维后,对手写数字数据集进行分类得到的准确率为 0.6067,即约为 60.67%。这表明将数据降维到只有 2 个主成分时,分类模型的性能显著下降。降维到较低维度可能会导致信息丢失,从而影响模型的分类能力。

  3. 降维后每个主成分所占的信息量比例分别为 [0.1457, 0.1374],表示前两个主成分分别解释了总方差的约 14.57% 和 13.74%。这表明前两个主成分只能解释数据集中很小一部分的方差,其中的信息量较少。

  4. 按照解释方差阈值为 0.6 进行降维时,得到的主成分数量为 7。这意味着通过保留解释方差达到 60% 的主成分,数据被降低到了 7 维。

  5. 使用解释方差阈值为 0.6 进行降维后,对手写数字数据集进行分类得到的准确率为 0.9489,即约为 94.89%。相较于只保留 2 个主成分,保留了 7 个主成分时分类准确率有所提高,但仍然低于原始 64 维特征空间的分类准确率。

  6. 按照解释方差阈值为 0.95 进行降维时,得到的主成分数量为 28。这意味着通过保留解释方差达到 95% 的主成分,数据被降低到了 28 维。

  7. 使用解释方差阈值为 0.95 进行降维后,对手写数字数据集进行分类得到的准确率为 0.98,即约为 98%。相较于保留较少主成分的情况,保留了更多主成分时分类准确率有所提高,接近原始 64 维特征空间的分类准确率。

综上所述,PCA降维可以在一定程度上减少数据维度,但需要权衡降维后的信息损失和分类性能。选择合适的主成分数量或解释方差阈值可以在降低维度的同时保持较高的分类准确率。

8.4 降低维度的好处:

在这个特定的示例中,降低维度可能没有明显的好处。因为原始的手写数字数据集只有64维,已经相对较低。在这种情况下,使用PCA对数据进行降维可能会导致信息的损失,从而降低分类性能。

然而,在其他情况下,降低维度可以带来以下好处:

  1. 减少计算开销:高维数据集可能需要更长的时间来进行训练和测试。通过降低维度,可以减少计算开销,加快模型的训练和预测速度。

  2. 去除冗余特征:某些特征可能对目标变量的预测能力较弱或与其他特征高度相关。通过降维,可以去除这些冗余特征,提高模型的简洁性和可解释性。

  3. 可视化:降维可以将高维数据映射到二维或三维空间,便于可视化观察。这有助于发现数据的结构、聚类情况和异常点等信息。

  4. 解决维度灾难:在高维空间中,数据点之间的距离变得稀疏,导致模型的泛化能力下降。通过降维,可以减少维度灾难的影响,提高模型的性能。

  5. 降噪:当数据受到噪声影响时,最小特征值对应的特征向量往往与噪声有关,将它们舍弃能在一定程度上起到降噪的效果。

需要注意的是,降低维度也可能带来一些潜在的问题,例如信息损失和降维误差。因此,在应用PCA或其他降维技术时,需要仔细评估降维对模型性能的影响,并权衡维度减少和信息保留之间的平衡。

9 实战三(PCA降维应用)

目的:使用 PCA 对手写数字数据集进行降维并对手写笔图像进行降噪处理。

9.1 步骤:

  1. 加载手写数字数据集:使用 datasets.load_digits() 函数加载手写数字数据集,其中 X 是特征矩阵,y 是目标变量。

  2. PCA降维可视化:使用 PCA 类进行降维,将手写数字数据集投影到二维空间。指定 n_components=2 表示只保留两个主成分。然后使用 pca.transform(X) 将数据集降维得到 X_reduction,其中每个样本都在二维空间中表示。通过循环绘制散点图,根据目标变量 y 的不同类别,以不同颜色显示降维后的数据点。

  3. 添加噪声并创建示例图像:将原始图像数据 X 添加高斯噪声,生成噪声图像 noisy_digits。然后从每个数字类别中选择前10个样本作为示例图像,并将它们组合成一个名为 example_digits 的数组。

  4. 绘制降噪前的图像:使用 plot_digits() 函数绘制降噪前的示例图像,以展示包含噪声的图像。

  5. 降噪处理:使用 PCA 对噪声图像 noisy_digits 进行降维。在这里,指定 PCA(0.5) 表示保留足够的主成分,以保留原始数据的50%的方差。通过 pca.transform(example_digits) 对示例图像进行特征化转换,得到特征化后的数据 components。然后使用 pca.inverse_transform(components) 对特征化后的数据进行逆转换,将其重新映射回原始图像空间,得到降噪后的图像 filtered_digits

  6. 绘制降噪后的图像:使用 plot_digits() 函数绘制降噪后的示例图像,以展示去除噪声后的图像。

9.2 代码:

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
digits = datasets.load_digits()
X = digits.data
y = digits.target

# 使用PCA降维将手写数字数据集可视化在二维空间中
from sklearn.decomposition import PCA
pca = PCA(n_components=2)#指定2个主成分
pca.fit(X)#对数据集X进行降维
X_reduction = pca.transform(X)
#X_reduction[y==i,0] 表示选择目标变量y等于i的数据点在第一维度上的值。
#alpha=0.8 是设置散点的透明度,为了防止覆盖
for i in range(10):
    plt.scatter(X_reduction[y==i,0], X_reduction[y==i,1], alpha=0.8)
plt.show()

# PCA降维实现降噪
# 图片增加噪声,创建噪声图像
noisy_digits = X + np.random.normal(0, 4, size=X.shape)
#选择噪声图像中数字类别为0的前10个样本作为示例图像
example_digits = noisy_digits[y==0,:][:10]
#将每个类别的前10个样本添加到example_digits中
for num in range(1,10):
    example_digits = np.vstack([example_digits, noisy_digits[y==num,:][:10]])
def plot_digits(data):
    #创建一个10x10的子图网格,其中每个子图用于展示一个手写数字图像。
    #fig 是整个图形对象,axes 是一个包含所有子图对象的数组。
    fig, axes = plt.subplots(10, 10, figsize=(10, 10),
                             subplot_kw={'xticks':[], 'yticks':[]},
    gridspec_kw=dict(hspace=0.1, wspace=0.1))
    for i, ax in enumerate(axes.flat):
        ax.imshow(data[i].reshape(8, 8),#被重新调整为8x8的形状
                  cmap='binary', interpolation='nearest',#设置图像的颜色映射为二值化(黑白),设置图像的插值方式为最近邻插值
                  clim=(0, 16))#设置图像的像素值范围在0到16之间
    plt.show()
#降噪前的图片
plot_digits(example_digits)

# 降噪
pca = PCA(0.5).fit(noisy_digits)#在噪声上降维
components = pca.transform(example_digits)#对样例数据进行特征化转换,将数据投影到 PCA 的主成分空间中
filtered_digits = pca.inverse_transform(components)#对特征化后的数据进行逆转换,将其重新映射回原始图像空间。逆转换的目的是尽可能恢复原始数据的细节和特征
plot_digits(filtered_digits)

下图为手写数据集降到2维可视化以后的结果。每个颜色代表一个数字在降维到二维空间中的分布情况。仔细观察后可以发现,很多数字的区分还是比较明细的。比如如果只是区分蓝色的数字和紫色的数字,那么使用二个维度就足够了。 

下图为降噪前后的图片变化,可以看到图片的质量(清晰度)提高了不少,更加容易识别是哪张图片了。

降噪的原理是通过降维和去除噪声成分来恢复图像的清晰度和细节。在使用 PCA 进行降噪时,其基本原理如下:

  1. 噪声特征的捕捉:噪声在图像中引入了高频成分和冗余信息。通过 PCA,我们可以将图像数据转换为主成分空间,其中主成分按照重要性排序。高频噪声成分通常在主成分空间中对应较小的特征值。

  2. 降维:选择一定比例的主成分来保留数据的主要信息。通过保留较高重要性的主成分,并丢弃对应较小特征值的主成分,我们可以实现降维,减少数据的维度。

  3. 去噪过程:在主成分空间中,通过丢弃或缩小噪声成分的权重,可以减少噪声对图像的影响。这是通过保留较大的主成分特征值,相应地减小噪声成分对应的主成分权重来实现的。

  4. 逆转换:将经过降维和去噪处理后的特征化数据通过逆转换映射回原始图像空间。逆转换会根据保留的主成分权重和特征向量,重构降噪后的图像。这样做可以尽可能恢复原始图像的细节和特征,去除噪声的影响。

总结起来,PCA 降噪的基本原理是通过在主成分空间中降低噪声的权重,减少噪声成分的影响,并通过逆转换将特征化后的数据重新映射回原始图像空间,从而实现去噪和恢复图像的目的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值