多维数组怎么降维_从零开始的机器学习实用指南(八):降维

本文介绍了机器学习中的维度灾难问题,及其对模型训练和预测的影响。接着,讨论了几种主要的降维方法,包括PCA(主成分分析)、核PCA(Kernel PCA)和LLE(局部线性嵌入),并阐述了每种方法的基本原理和应用场景。PCA通过找到数据最大方差的方向进行投影,核PCA利用核技巧实现非线性降维,而LLE则侧重于保持数据的局部线性结构。这些方法在处理高维数据和数据可视化方面具有重要作用。
摘要由CSDN通过智能技术生成

a72a3b94703a103cb6c291c382132bf0.png

许多机器学习有数以千计甚至百万级别的特征,这不仅会让训练变得很慢,也可能让找到最优解变得很难,这种问题被称为维度灾难(curse of dimensionality)

真实世界中,有时候我们并不需要这么多维度,例如,MINIST的边缘位置常常是空白的,有的两个相邻的位置常常是相关的,扔掉其中的一些也不会损失太多的信息。

降维常常都是以损失信息为代价,类似于压缩一张图片。我们通常可以先试着用原始的数据,如果太慢再考虑降维,偶尔,降维可以去掉一些噪声和无关的细节来让结果变得更好,但这通常不会发生

另外。降维常常可以在方便可视化的角度变得更好,降低到二维或者三维可以方便观察,比如提取聚类的一些信息。

维度灾难

8dbb08c0268af9386cac498f3037ebe1.png

我们总是习惯于观察三维以下的物体,但是物体在高维空间的表现常常不同。

比如,在一个1×1正方形单元中随机取一个点,离所有边界大于 0.001(靠近中间位置)的概率为 0.4%。但是在一个 1,0000 维的单位超正方体,这种可能性超过了 99.999999%。在高维超正方体中,大多数点都分布在边界处。

还有一个更麻烦的区别:如果你在一个平方单位中随机选取两个点,那么这两个点之间的距离平均约为 0.52。如果您在单位 3D 立方体中选取两个随机点,平均距离将大致为 0.66。但是,在一个 1,000,000 维超立方体中随机抽取两点呢?那么,平均距离,信不信由你,大概为 408.25!

那么意味着,我们的一个新样本,距离机器学习的其他样本距离可能非常远,所以预测的可靠性远远不如低维度,过拟合的风险也更高。

理论上,我们可以通过增加样本来填满高维的空间,但是,所需要的样本是指数增长的。如果只有 100 个特征(比 MNIST 问题要少得多)并且假设它们均匀分布在所有维度上,那么如果想要各个临近的训练实例之间的距离在 0.1 以内,需要比宇宙中的原子还要多的训练实例。

降维的主要方法

投影(Projection)

事实上,样本常常不是均匀分布的,许多特征可能是常数,许多特征可能是高度相关的,结果是,可能他们接近于一个低维子空间,比如,图中的点接近于某个二维平面,我们可以投影到这个平面上,如下图,我们刚刚将数据集的维度从 3D 降低到了 2D。

01463378ebf7aabdbb08e0835f8b58bf.png

4934e212eea0fa652e76604e00b776ae.png

但是,也有一些问题是投影无法解决的,比如,下图是一个类似瑞士蛋糕卷的形状

8c95027dba84a631fa4a3fe6da3c6257.png

我们真正希望的是,如下图右,把蛋糕卷展开的形状,而投影,比如沿着x3方向投射,可能会造成重叠在一起的不好结果,如左

e3108db9bd5ee63846e891b8ee5b6426.png
投射到平面的压缩(左)vs 展开瑞士卷(右)

流形学习

瑞士卷一个是二维流形的例子。它类似于一个二维平面,不过在三维空间下是卷曲的。

许多降维算法对样本所在的流形进行建模从而达到降维目的;这叫做流形学习。它依赖于流形假设(manifold hypothesis),大多数现实世界的高维数据集大都靠近一个更低维的流形。这种假设经常在实践中被证实。

比如MNIST数据集,如果你只是随机的生成,只有非常少的一部分是满足数字的,换句话说,MNIST里面图片的自由度比完全随机低的多,这些约束把样本压缩到某个流形里面。

流形学习常常有另外一个假设,就是决策在流形中比原来的空间简单了(比如下图上),但这也不是绝对的,图中的例子,决策边界反而变得复杂了(比如下图下)

7a8f196a835dd4bf601cccf375db96cd.png

下面会介绍一些具体的降维方法。

PCA

主成分分析(Principal Component Analysis,PCA)是目前为止最流行的降维算法。首先它找到接近数据集分布的超平面,然后将所有的数据都投影到这个超平面上。

首先的任务就是找到这个超平面,我们希望损失尽可能地小

一种思路是选择方差最大的,因为数据的方差大,噪音的方差小。正如你所看到的,投影到实线上保留了最大方差,证明这种选择的另一种方法是,选择这个轴使得将原始数据集投影到该轴上的均方距离最小。

664372e51f3af0b0374d660e424244b9.png

主成分

PCA 寻找训练集中可获得最大方差的轴。在上图中只是一条直线,但是在一个高维的空间,

将会是相互正交的许多个轴,定义第i个轴的单位矢量被称为第i个主成分(PC)。为c1,c2....cn

27d313f429ed9c803196742b9051263e.png

那么如何找到训练集的主成分呢?幸运的是,有一种称为奇异值分解(SVD)的标准矩阵分解技术,可以将训练集矩阵X分解为三个矩阵U·Σ·V^T的点积,其中V^T包含我们想要的所有主成分

一旦确定了所有的主成分,你就可以通过将数据集投影到由前d个主成分构成的超平面上,从而将数据集的维数降至d维。

投影到超平面上,可以简单地通过计算训练集矩阵X和wd的点积,wd定义为包含前d个主成分的矩阵(即由V^T的前d列组成的矩阵)

下面是Numpy的实现

X_centered=X-X.mean(axis=0)
U,s,V=np.linalg.svd(X_centered)
c1=V.T[:,0]
c2=V.T[:,1]

W2=V.T[:,:2]
X2D=X_centered.dot(W2)
PCA 假定数据集以原点为中心。Scikit-Learn 的 PCA类负责为您的数据集中心化处理。但是,如果您自己实现 PCA(如前面的示例所示),或者如果您使用其他库,不要忘记首先要先对数据做中心化处理。

Sklearn库可以自动实现这个过程

可以使用components_访问每一个主成分,第一个主成分则可以写成pca.components_.T[:,0]

from sklearn.decomposition import PCA

pca=PCA(n_components=2)
X2D=pca.fit_transform(X)

另一个非常有用的信息是每个主成分的方差解释率,explained_variance_ratio_变量获得。它表示位于每个主成分轴上的数据集方差的比例。

>>> print(pca.explained_variance_ratio_)
array([0.84248607, 0.14631839])

这表明第一个轴占84%,第二个占16%,第三个仅有不到1.2%,第三个包含的信息已经不多了。通常我们倾向于选择加起来到方差解释率能够达到足够占比(例如 95%)的维度的数量。

在sklearn中,可以巧妙地把n_components设置为想要保留的比例(0-1之间),这样可以很容易到达这一点,另外,也可以画图寻找一个合适的拐点

pca=PCA(n_components=0.95)
X_reduced=pca.fit_transform(X)

44da88a3ce1f92ff37980d6cf771051d.png

例如用PCA在保留95%方差下压缩原来784维特征,只有 150 多个特征!大大的缩小了数据集的规模。

可以试着去还原数据集,结果发现,虽然有细微差别,但是损失的信息非常微小

pca=PCA(n_components=154)
X_mnist_reduced=pca.fit_transform(X_mnist)
X_mnist_recovered=pca.inverse_transform(X_mnist_reduced)

97250581b20076b456ffd4b5ba7fec24.png
初始vs还原后

增量PCA(Incremental PCA)

普通的PCA在做奇异值分解要把所有的训练集加载到内存中。而增量PCA(IPCA)可以将训练集分批,并一次只对一个批量使用 IPCA 算法。这对大型训练集非常有用,并且可以在线应用 PCA

from sklearn.decomposition import IncrementalPCA

n_batches=100
inc_pca=IncrementalPCA(n_components=154)
for X_batch in np.array_spplit(X_mnist,n_batches):
    inc_pca.partial_fit(X_batch) #这里是partial_fit而不是fit
X_mnist_reduced=inc_pca.transform(X_mnist)

或者使用Numpy的memmap。它允许您操作存储在磁盘上二进制文件中的大型数组,就好像它完全在内存中;该类仅在需要时加载内存中所需的数据。

X_mm=np.memmap(filename,dtype='float32',mode='readonly',shape=(m,n))
batch_size=m//n_batches
inc_pca=IncrementalPCA(n_components=154,batch_size=batch_size)
inc_pca.fit(X_mm)

随机 PCA

Scikit-Learn 提供了另一种执行 PCA 的选择,称为随机 PCA。这是一种随机算法,可以快速找到前d个主成分的近似值。它的计算复杂度是O(m × d^2) + O(d^3),而不是O(m × n^2) + O(n^3),所以当d远小于n时,它比之前的算法快得多。

rnd_pca=PCA(n_components=154,svd_solver='randomized')
X_reduced=rnd_pca.fit_transform(X_mnist)

核 PCA(Kernel PCA)

在介绍支持向量机的时候,介绍了核技巧,可以帮助隐式的把样本映射到了高维空间,这样SVM可以处理非线性的分类和回归了。同样的技巧可以应用于 PCA,从而可以执行复杂的非线性投影来降低维度。这就是所谓的核 PCA(kPCA)。它通常能够很好地保留投影后的簇,有时甚至可以展开分布近似于扭曲流形的数据集。

在Sklearn中提供了这样的接口。

from sklearn.decomposition import KernelPCA

rbf_pca=KernelPCA(n_components=2,kernel='rbf',gamma=0.04)
X_reduced=rbf_pca.fit_transform(X)

12ff04a952a02d6c1b8e0f9689dd4900.png

核技巧涉及到了超参数的选择,但是无监督学习不像监督学习那样,可以简单的用交叉验证确定超参数。

一种方法是,如果无监督学习是某个监督学习的准备步骤,可以创建了一个两步的流水线,然后用网格搜索确定超参数,下面的例子是先降维再做逻辑回归:

from sklearn.model_selection import GridSearchCV 
from sklearn.linear_model import LogisticRegression 
from sklearn.pipeline import Pipeline

clf = Pipeline([
        ("kpca", KernelPCA(n_components=2)),
        ("log_reg", LogisticRegression())
])
param_grid = [{
        "kpca__gamma": np.linspace(0.03, 0.05, 10),
        "kpca__kernel": ["rbf", "sigmoid"]
    }]
grid_search = GridSearchCV(clf, param_grid, cv=3)
grid_search.fit(X, y)

>>> print(grid_search.best_params_)
{'kpca__gamma': 0.043333333333333335, 'kpca__kernel': 'rbf'}

另一种方法是,选择产生最低重建误差的核和超参数。但是比普通的PCA还原起来要复杂,因为经过了RBF函数映射,重建的点(下图右下)在特征空间而不是原始空间。例如RBF特征空间是无限多维的,因此,无法计算真实的重建误差。幸运的是,可以在原始空间中找到一个贴近重建点的点。这被称为重建前图像(reconstruction pre-image)(下图左下)

96557c744a8d541d345a96f73de1554e.png

在sklearn中,设置了fit_inverse_transform = True,Scikit-Learn 将自动执行此操作(默认为False,因此要设置)。然后可以寻找最好的超参数了。

rbf_pca = KernelPCA(n_components = 2, kernel="rbf", gamma=0.0433,fit_inverse_transform=True)
X_reduced = rbf_pca.fit_transform(X)
X_preimage = rbf_pca.inverse_transform(X_reduced)

>>> from sklearn.metrics import mean_squared_error
>>> mean_squared_error(X, X_preimage) 32.786308795766132

LLE

局部线性嵌入(Locally Linear Embedding)是另一种非常有效的非线性降维方法。这是一种流形学习技术,不依赖于像以前算法那样的投影。它特别擅长展开扭曲的流形,尤其是在没有太多噪音的情况下。

如图,是LLE展开瑞士卷

 from sklearn.manifold import LocallyLinearEmbedding

lle=LocallyLinearEmbedding(n_components=2,n_neighbors=10)
X_reduced=lle.fit_transform(X)

cf89846a9e776c78ea062ad0726c59b7.png

LLE 首先识别其最近的k个邻居(例子中k=10),然后尝试把这个点重构为这些邻居的线性函数,希望这个函数和原来的值距离和最小

380d7835ff5a835731f2d2541b6b30c8.png

如果不是k个邻居里面,权重为0,如下图:

ed01ad61d32422979c33230dfe8855cb.png

然后是降维到d维度空间里去,同时尽可能的保留这些局部关系。如果

在这个
d维空间的图,公式如下:

简单来说,第一步是找到这个权重,第二步是根据这个权重找到对应的位置。

LLE建立低维表示为O(d m^2)。不幸的是,最后一项m^2使得这个算法在处理大数据集的时候表现较差。

其他降维方法

还有很多其他的降维方法,Scikit-Learn 支持其中的好几种。这里是其中最流行的:

  • 多维缩放(MDS)在尝试保持实例之间距离的同时降低了维度
  • Isomap 通过将每个实例连接到最近的邻居来创建图形,然后在尝试保持实例之间的测地距离时降低维度。
  • t-分布随机邻域嵌入(t-Distributed Stochastic Neighbor Embedding,t-SNE)可以用于降低维​​度,同时试图保持相似的实例临近并将不相似的实例分开。它主要用于可视化,尤其是用于可视化高维空间中的实例(例如,可以将MNIST图像降维到 2D 可视化)。
  • 线性判别分析(Linear Discriminant Analysis,LDA)实际上是一种分类算法,但在训练过程中,它会学习类之间最有区别的轴,然后使用这些轴来定义用于投影数据的超平面。LDA 的好处是投影会尽可能地保持各个类之间距离,所以在运行另一种分类算法(如 SVM 分类器)之前,LDA 是很好的降维技术。

251f92031e64b199d9cef4721a08c439.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值