简化维度降低:PCA 理论及 Scikit-Learn 实现

原文:towardsdatascience.com/dimensionality-reduction-made-simple-pca-theory-and-scikit-learn-implementation-9d07a388df9e

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/9632548cc1b27fb80fc2a6f0ca85110b.png

图片来源:unsplash.com.

在小说平面国中,生活在二维世界的角色在遇到三维生物时会感到困惑,无法理解。我使用这个类比来说明在机器学习中处理涉及数千甚至数百万维度的(即特征)问题时发生的类似现象:出现令人惊讶的现象,这对我们的机器学习模型有灾难性的影响

我相信你至少有一次被现代机器学习问题中涉及的大量特征所震惊。每个数据科学从业者迟早都会面临这个挑战。本文将探讨最常用维度降低算法的理论基础和 Python 实现:主成分分析(PCA)。

为什么我们需要减少特征的数量?

现在涉及数千甚至数百万个特征的数据库很常见。向数据库中添加新特征可以带来有价值的信息,然而,它们会减慢训练过程并使找到好的模式和解决方案变得更难。在数据科学中,这被称为维度诅咒,它通常导致数据解释偏差和预测不准确

机器学习从业者如我们,可以受益于这样一个事实:对于大多数机器学习问题,特征的数量可以持续减少。例如,考虑一张图片:靠近边界的像素通常不包含任何有价值的信息。然而,在机器学习问题中安全地减少特征数量的技术并不简单,需要我在本文中提供解释。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/2adf54d2dc086e5ac71d97b2675f2c0d.png

作者图片。

我将展示的工具不仅简化了计算工作量并提高了预测准确性,而且还可以作为图形化可视化高维数据的工具。因此,它们对于与别人交流你的见解至关重要。

幸运的是,上述提到的技术,即降维技术,存在并且可以轻松地成为每个数据科学家的工具箱的一部分。此外,降维技术可以通过**Scikit-Learn(sklearn)库轻松地在Python**编程中实现。

维度诅咒

维度诅咒代表了数据科学中最大的挑战之一,因为它使得算法计算密集。但究竟是什么呢?

当数据集中的维度数量显著增加时,可用的数据点变得越来越稀疏,导致一系列复杂的计算挑战。作为人类,我们难以概念化超过 3 个或有时 2 个维度。这种限制导致数据在高维空间中的行为非常出乎意料

例如,考虑一个 2D 的 1×1 正方形。如果我们随机选择它内部的一个点,它位于边界的非常近处的概率很小。数值上,它位于距离边界 0.001 距离内的概率为 0.4%。相反,在一个更高维度的单位超立方体中,随着边数的增加,随机点位于边界附近的概率呈指数增长。在一个 10,000 维度的超立方体中,相同的概率增加到 99.99999%以上。这意味着在高维空间中,它很可能位于边界附近。

以下实验甚至更令人惊讶(或者在我看来是令人不安的)。考虑一个常见的二维单位正方形。在正方形内部随机选择的两个点之间的距离,平均来说等于 0.52。如果你相信我告诉你,用 100,000 维度的单位超立方体做同样的实验,平均距离为 129.09,你会相信我吗?我可以用以下 Montecarlo 模拟来证明这一点。

import numpy as np
test_dimensions = [2, 100000]
print('Average distance between 2 points in a unit hypercube')
for n_dim in test_dimensions:
    distances = []
    for i in range(100000):
        rand_point_1 = np.random.uniform(size=n_dim)
        rand_point_2 = np.random.uniform(size=n_dim)
        dist = np.linalg.norm(rand_point_1-rand_point_2)
        distances.append(dist)
    avg_distance = np.mean(distances)
    print('{} dimensions: {}'.format(n_dim, avg_distance))

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/aa18ff291044779cc522429abd520ebb.png

这个概念甚至可以通过以下可视化更好地表示:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/88ae883ec049d0b79012e1d84554ba71.png

作者提供的图片。

放下这些有趣的模拟,我们可以为数据科学从业者推断出一个主要含义:在高维空间中存在大量空间

结果是,高维数据集几乎肯定非常稀疏,训练实例彼此之间相距甚远。使用这样的训练数据,你的机器学习模型过拟合的风险很高。

虽然从理论上讲,我们可以添加新的训练示例,但在实践中,这不足以应对特征数量的增加。事实上,为了保持恒定的密度,所需的训练点数量需要随着特征数量的增加呈指数增长。

只举一个例子,为了在具有 100 个特征的训练数据集中保持每个训练点之间的距离为 0.1,我们需要比宇宙中原子数量还要多的训练点数量。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/3a770911af310979a808cbbd4743b438.png

图片来源:pixabay.com.

唯一可行的方法是减少特征数量,我将提供不同的技术来做到这一点,同时避免丢失太多信息。

主成分分析(PCA)

主成分分析PCA)可能是降维中最受欢迎的算法。在其核心功能中,它将数据投影到超平面上,旨在使旋转后的特征在统计上不相关。旋转通常随后通过选择这些新投影特征的一部分,基于它们在描述数据中的重要性来进行。

由于 PCA 基于投影,了解这个操作是基本的。以下部分解释了投影操作

投影

在实际场景中,大量的训练实例靠近原始训练集超空间的一个低维子空间。原因如下:

  • 特征之间的相关性:许多特征高度相关。

  • 常量值:许多特征假设接近常量值。

为了说明这个概念,考虑以下二维数据集:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/bb915f3dc9a3cd9862612fdc4dbb0eb3.png

作者图片

考虑到特征 _x1 与特征 _x2 之间的强相关性,我们可以将数据集投影到一个低维平面上,在这种情况下,是一条线。考虑到由黑色箭头标识的方向,称为组件,如果我们旋转数据集,使得组件 1 (_p1) 水平,我们得到:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/361639145c386ae5be1a9f7a66d5370e.png

作者图片。

由于组件 _p2 并没有增加太多信息,我们只能在组件 _p1 上进行数据投影。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/0b87418d0d46241dfc795c372c07baca.png

作者图片。

这本质上就是投影所做的工作。考虑到一个三维数据集,其效果甚至更加清晰。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/9289ec27d54870197721f79c5b937f36.png

作者图片。

我们可以将原始数据投影到一个二维平面上,创建两个新的特征 _p1,_p2

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4f2fc27f963068990b8d26b11ec9892f.png

作者图片。

在机器学习中,没有免费的午餐,因此投影并不总是适合所有类型的数据集。如果数据在原始特征空间中属于一个旋转或扭曲的平面,那么没有任何投影操作可以在低维空间中保留原始信息。

以瑞士卷数据集为例:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4f8251e787a4a2c6c55b4aa2745e774d.png

作者图片。

它由一个自身卷曲的二维平面组成。在这种情况下,我们无法有效地将数据投影到平面上而不损失相当一部分原始信息。

为了避免如此大的信息损失,我们将在稍后看到一种不同的方法,该方法旨在“展开”数据。

主成分

正如我所预期的,PCA 包括将数据投影到低维超平面。为了尽可能保留信息,PCA 不会将数据投影到任何随机的超平面上。相反,PCA 选择数据投影具有最大方差的超平面。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/cd6d2f8084909093829fe5262febbcb4.png

图片由作者提供。

如果我们再次考虑一个二维示例,就很容易理解这一点。

PCA 随后考虑所有与最大方差轴正交的轴,并在它们之间选择剩余方差较大的一个。

这个过程会迭代与原始数据集维度数量一样多的次数。每个识别的轴被称为数据集的主成分

现在的问题是,如何从数学上计算主成分。幸运的是,线性代数为我们提供了**奇异值分解**技术,通过该技术我们可以将原始训练集X分解为 3 个新矩阵的矩阵乘积:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/cbce3c8a2bf4abb9fecaae23915b7341.png

V 矩阵的列包含先前识别的所有主成分的方向。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4eb683f93564f9798f84d06940dc334e.png

虽然使用Numpy Python 库实现 SVD 很容易,但使用Scikit-learn(sklearn)模块实现 PCA 则更加简单。

在 Scikit-learn(sklearn)中,我首先需要创建一个PCA()对象,然后将其拟合到数据上并对其进行转换:

from sklearn.decomposition import PCA
from sklearn.datasets import make_swiss_roll

# Create synthetic data
X, t = make_swiss_roll(n_samples=1000, noise=0.2, random_state=42)

# Instantiate a PCA object
pca = PCA(n_components=1)

# Fit the PCA object on the data
pca.fit(X)

# Transform the data
X_transformed = pca.transform(X)

print('Original data shape: ', X.shape)
print('Transformed data shape: ', X_transformed.shape)

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/94d5de0da0ac4ba0c58925a2af48c5a0.png

如我们从输出中可以看到,我们将原始数据集的维度从 3 维降低到 1 维。这是因为我们在实例化PCA()对象时指定了n_components=1

除非我们进行降维以数据可视化,否则我们可能不知道变换后的训练集的最佳维度数量。

选择正确的维度数量以将训练集缩减到是降维中的一个重要问题。如果原始特征空间较窄,我们可能会尝试手动迭代方法,然而,在大多数实际情况下,特征空间很大,我们需要一个更稳健和自动化的方法。

为了介绍这种方法,我首先需要定义解释方差比(EVR)。简单来说,EVR 是指原始数据集中每个主成分所包含的方差百分比。

由于 Scikit-learn 提供了一个直观的explained_variance_ratio_属性,我们可以将这个概念应用到前面的例子中:

from sklearn.decomposition import PCA
from sklearn.datasets import make_swiss_roll

# Create synthetic data
X, t = make_swiss_roll(n_samples=1000, noise=0.2, random_state=42)

# Instantiate a PCA object
pca = PCA(n_components=1)

# Fit the PCA object on the data
pca.fit(X)

# Calculate the EVA of each PC
eva = pca.explained_variance_ratio_

print(eva)

与第一个主成分相关的方差大约是 40%。这告诉我们,通过将原始训练集的维度减少到仅一个特征,我们损失了大约 60%的方差。

选择缩减数据集维度数量的最直接方法是以下:使用所需数量的主成分,直到累积解释方差比达到给定的阈值

虽然这种方法可能仍然显得晦涩难懂,但一个例子会立即阐明它。在这个例子中,我将 PCA 应用于一个合成的数据集。

# Generate the dataset
num_instances = 10000
num_features = 784
X = np.random.rand(num_instances, num_features)

# Instantiate and train a PCA object
pca = PCA()
pca.fit(X)

# Compute the explained variance ratio
eva = pca.explained_variance_ratio_

print(np.round(eva, decimals=4)

通过打印训练好的 PCA 的解释方差比,我们看到第一个主成分“解释”了原始数据集 9.75%的方差,第二个主成分负责 7.16%的方差,以此类推。过了一段时间后,以下成分对原始数据集方差的解释变得可以忽略不计。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/85e289c624c2f2b493e43a3628cc53cc.png

选择累积 EVA 值加起来达到原始数据集 95%的方差的主成分是一种常见的做法:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/e8daddbf7da61c7f10567aa6bdba70cf.png

作者图片。

对于合成数据集,我们有 153 个主成分能够解释 95%的原始方差。这意味着,考虑到 784 个原始特征,我们可以安全地删除 631 个维度。换句话说,我们可以将原始训练集的维度减少 80%,而损失仅 5%的方差。

Scikit-learn(sklearn)允许我们指定我们想要达到的累积 EVA 阈值,而不是考虑的主成分数量:

pca = PCA(n_components=.95)
pca.fit(X)
X_transformed = pca.transform(X)

print(X_transformed.shape)

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/b89ef6bd9f7bb49af4c2362eb1a816c6.png

在这种情况下,我们自动得到一个由 153 个新特征组成的缩减数据集。

你可以在这个 GitHub 仓库中找到本帖中显示的所有代码(以及更多):

articles/dimensionality-reduction at main · andreoniriccardo/articles

结论

在这篇帖子中,我们探讨了这种强大且简单的无监督模型的原理。我们还了解到,通过 Scikit-Learn 库,在 Python 中实现它有多么简单。

理解 PCA 对机器学习的影响是至关重要的,因为它的应用范围涵盖多个行业。

我通过概述 PCA 模型的优缺点来结束这篇入门指南。

PCA 的主要优势在于其减少维度数量的效率,同时保留关键信息,降低计算工作量,并提高模型性能。值得注意的是,PCA去相关特征,这意味着结果主成分在统计上是不相关的

通过仅关注最相关的特征,PCA 还能够减少原始训练集中的噪声

然而,正如我之前提到的,PCA 并不适合每个任务。在某些情况下,例如瑞士卷例子,PCA 无法完全捕捉数据结构。应用 PCA 意味着将原始特征转换为主成分,从而失去了它们的可解释性

在这些限制下,在我的下一篇文章中,我将处理更复杂的降维算法,如核 PCALLE

当我到达这个指南的结尾时,我需要指出,尽管我已经触及了 PCA 的所有最相关方面,但其领域远远超出了这里提供的见解。因此,我建议深入研究这篇文章链接的资源和建议,并鼓励你亲自在你的数据集上尝试 PCA。你亲自动手探索将加深你的理解,并带来有价值的见解。


如果你喜欢这个故事,请考虑关注我,以便通知我的即将到来的项目和文章!

这里是我过去的一些项目:

使用 Scikit-Learn 和 Matplotlib 进行异常检测:实用指南

使用 NetworkX 进行社交网络分析:轻松入门

参考文献

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值