基于 Python 的降维技术实战



我们为什么需要减少维度?
高维数据集是具有大量列(或变量)的数据集。这样的数据集带来了许多数学或计算挑战。好消息是变量(或称为特征)通常是相关的 - 高维数据“表面上”被少数简单变量所控制。我们可以找到变量的子集,以表示数据中的相同级别的信息,或将变量转换为新的一组变量,而不会丢失很多信息。虽然高功率计算可以以某种方式处理高维数据,但在许多应用中,仍然需要降低原始数据的维度。
当我们考虑降维时,主成分分析(PCA)可能是最受欢迎的技术。在本文中,我将从 PCA 开始,然后陆续介绍其他维度减少技术。每个技术都会附上 Python 代码。
减少维度也可以找到异常值
数据科学家可以使用降维技术来识别异常。为什么?难道我们只是想减少维度吗?直觉在于异常值本身。D.M.Hawkins 说:“异常值是指观测结果与其他观测结果相差太大,以引起它被不同机制产生的怀疑。”一旦降维到较少的主维度,模式就被识别出来,然后就会显示出异常值。我们可以说异常值检测是降维副的产品,如文章异常检测到 AutoEncoders 易于易于易于修复[1]。
主成分分析(PCA:Principal Component Analysis)
主成分分析(PCA)的概念是减少由大量相关变量组成的数据集的维度,同时保留尽可能多的数据方差。PCA 找到了一组新的变量,即原始变量只是它们的线性组合。新变量称为主成分(PCs)。这些主要成分是正交:在 3-D 情况下,主成分彼此垂直。x 不能由 y 表示,也不能由 z 呈现。
图(a)显示了 PCA 的直觉:它“旋转”轴更好地与您的数据对齐。第一个主成分将捕获数据中的大部分方差,然后后跟第二个,第三等。结果是,新数据将具有更少的维度。

图(a):PCA
让我们使用鸢尾花数据集来说明 PCA:
# Use the iris dataset to illustrate PCA:import pandas as pdurl = “https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"# load dataset into Pandas DataFramedf = pd.read_csv(url, names=['sepal length','sepal width','petal length','petal width','target'])df.head()
鸢尾花数据集

请注意,此 Iris 数据集附带目标变量。在 PCA 中,您只能在没有目标 Y 变量的情况下转换 x 变量。
标准化:所有变量应在应用 PCA 之前应在相同的比例上,否则,具有大值的特征将主导结果。这一点进一步解释了我的帖子避免这些致命建模错误,可能会浪费你职业生涯[2]。下面我使用scikit-standardscaler - 学习将数据集的特征标准化到单位刻度上(平均值= 0 和方差= 1)。
from sklearn.preprocessing import StandardScalervariables = ['sepal length', 'sepal width', 'petal length', 'petal width']x = df.loc[:, variables].valuesy = df.loc[:,['target']].valuesx = StandardScaler().fit_transform(x)x = pd.DataFrame(x)
标准化特征
原始数据中有四个特征。所以 PCA 将提供相同数量的主成分。
from sklearn.decomposition import PCApca = PCA()x_pca = pca.fit_transform(x)x_pca = pd.DataFrame(x_pca)x_pca.head()
鸢尾花数据集的主要组成部分
每个主成分解释的差异是什么?使用pca.explate_variance_ratio_返回方差的向量:
explained_variance = pca.explained_variance_ratio_explained_variance
它显示了第一个主要成分占 72.22%的差异,第二个,第三和第四个占 23.9%,3.68%和 0.51%的差异。我们可以说 72.22 + 23.9 = 96.21%的信息由第一和第二主成分捕获。我们经常希望只保留重要的特征并放弃微不足道的特征。拇指(经验)规则保持顶部的主成分,捕捉重要的方差和忽视小的。
我们可以使用前两个组件绘制结果。让我们将目标变量 y 附加到新数据 x_pca:
x_pca['target']=yx_pca.columns = ['PC1','PC2','PC3','PC4','target']x_pca.head()

结果显示数据在新空间中可分离。
import matplotlib.pyplot as pltfig = plt.figure()ax = fig.add_subplot(1,1,1)ax.set_xlabel('Principal Component 1')ax.set_ylabel('Principal Component 2')ax.set_title('2 component PCA')targets = ['Iris-setosa', 'Iris-versicolor', 'Iris-virginica']colors = ['r', 'g', 'b']for target, color in zip(targets,colors):    indicesToKeep = x_pca['target'] == target    ax.scatter(x_pca.loc[indicesToKeep, 'PC1'] , x_pca.loc[indicesToKeep, 'PC2'] , c = color , s = 50)ax.legend(targets)ax.grid()

KPCA(Kernel PCA)
PCA 仅适用于线性变换。而KPCA支持非线性变换。首先将原始数据映射到某些非线性特征空间(通常更高的维度),然后应用 PCA 以提取该空间中的主成分。这可以通过图(b)来理解。左侧的图形显示了使用任何线性变换不能分离蓝色和红色点。但是,如果所有点都投射到 3D 空间上,则结果变得线性可分离!然后,我们将 PCA 应用于分离成分。
直觉来自哪里?为什么分离在高维空间中更容易变得更容易?这必须返回 Vapnik-Chervonenkis(VC)理论。它将映射到更高的维度空间通常提供更大的分类效果。

图(b):KPCA
以下 Python 代码使由红色和蓝色点组成的圆形曲线。显然,没有办法将红色和蓝色点用一条直线分离(线性分离)。
print(__doc__)import numpy as npimport matplotlib.pyplot as pltfrom sklearn.decomposition import PCA, KernelPCAfrom sklearn.datasets import make_circlesnp.random.seed(0)X, y = make_circles(n_samples=400, factor=.3, noise=.05)plt.figure(figsize=(10,10))plt.subplot(2, 2, 1, aspect='equal')plt.title("Original space")reds = y == 0blues = y == 1plt.scatter(X[reds, 0], X[reds, 1], c="red",s=20, edgecolor='k')plt.scatter(X[blues, 0], X[blues, 1], c="blue",s=20, edgecolor='k')plt.xlabel("$x_1$")plt.ylabel("$x_2$")

但是,当我们将圆圈投射到更高的维度空间并使用 PCA 分开时,针对第一和第二主成分的数据观察是可分离的!以下结果是针对第一和第二主成分绘制点的结果。我画一条线来分隔红色和蓝色点。在 KernelPCA 中,我们指定内核='RBF',即径向基函数[3],或 euclidean 距离。RBFS 通常用作机器学习技术中的内核,例如支持向量机(SVM)[4]。
kpca = KernelPCA(kernel="rbf", fit_inverse_transform=True, gamma=10)X_kpca = kpca.fit_transform(X)pca = PCA()X_pca = pca.fit_transform(X)plt.scatter(X_kpca[reds, 0], X_kpca[reds, 1], c="red",s=20, edgecolor='k')plt.scatter(X_kpca[blues, 0], X_kpca[blues, 1], c="blue",s=20, edgecolor='k')x = np.linspace(-1, 1, 1000)plt.plot(x, -0.1*x, linestyle='solid')plt.title("Projection by KPCA")plt.xlabel(r"1st principal component in space induced by $\phi$")plt.ylabel("2nd component")

如果我们将内核指定为“线性”, 如下面的代码(KernelPCA(kernel="linear")),则成为只有线性变换的标准 PCA,红色和蓝色点不可分离。
kpca = KernelPCA(kernel="linear", fit_inverse_transform=True, gamma=10)X_kpca = kpca.fit_transform(X)pca = PCA()X_pca = pca.fit_transform(X)plt.scatter(X_kpca[reds, 0], X_kpca[reds, 1], c="red",s=20, edgecolor='k')plt.scatter(X_kpca[blues, 0], X_kpca[blues, 1], c="blue",s=20, edgecolor='k')x = np.linspace(-1, 1, 1000)plt.plot(x, -0.1*x, linestyle='solid')plt.title("Projection by KPCA")plt.xlabel(r"1st principal component in space induced by $\phi$")plt.ylabel("2nd component")

线性判别分析(LDA:Linear Discriminant Analysis)
LDA 的起源与 PCA 不同。PCA 是一种无监督的学习方法,将原始特征转换为一组新特征。我们不关心新的一组特征是否可以为目标变量提供最佳的区分能力。相比之下,线性判别分析(LDA)寻求保持与所属变量尽可能多的辨别力,同时将原始数据矩阵突出到较低维空间上。LDA 是一种有监督学习技术的类型。它利用从属变量中的类将预测器的空间划分为区域。所有区域应该有线性边界。这就是名称中线性由来。该模型预测,区域内的所有观察属于来自相关变量的相同类别。
LDA 通过三个主要步骤中实现了上述目标。
首先,它计算从属变量的不同类别之间的可分离性,称为级别方差,如(1)所示。
其次,它计算每个类的平均值和样本之间的距离,该距离被称为级别方差,如(2)所示。
最后,它通过此标准构造了较低维度的空间:最大化类别之间的方差并最小化类别内方差。该标准的解决方案是计算特征值和特征向量。得到的特征向量代表了新空间的方向,相应的特征值代表了特征向量的长度。因此,每个特征向量代表 LDA 空间的一个轴,并且特征值代表该特征向量的长度。

图:LDA.
我将使用在 Kaggle 中的数据集红葡萄酒质量[5],此数据集具有 11 个输入变量和一个输出变量quality。
import matplotlib.pyplot as pltfrom sklearn.decomposition import PCAfrom sklearn.discriminant_analysis import LinearDiscriminantAnalysiswine = pd.read_csv('winequality-red.csv')wine.head()

为了简单起见,我将输出变量重新组建为三个值,
wine['quality2'] = np.where(wine['quality']<=4,1, np.where(wine['quality']<=6,2,3))

以下代码执行 PCA 和 LDA。
X = wine.drop(columns=['quality','quality2'])y = wine['quality2']target_names = np.unique(y)target_namespca = PCA(n_components=2)X_r = pca.fit(X).transform(X)lda = LinearDiscriminantAnalysis(n_components=2)X_r2 = lda.fit(X, y).transform(X)
然后绘制 PCA 和 LDA 的结果:
# Percentage of variance explained for each componentsprint('explained variance ratio (first two components): %s' % str(pca.explained_variance_ratio_))plt.figure()colors = ['navy', 'turquoise', 'darkorange']lw = 2for color, i, target_name in zip(colors, target_names, target_names): plt.scatter(X_r[y == i, 0], X_r[y == i, 1], color=color, alpha=.8, lw=lw, label=target_name)plt.legend(loc='best', shadow=False, scatterpoints=1)plt.title('PCA of WINE dataset')plt.figure()for color, i, target_name in zip(colors, target_names, target_names): plt.scatter(X_r2[y == i, 0], X_r2[y == i, 1], alpha=.8, color=color, label=target_name)plt.legend(loc='best', shadow=False, scatterpoints=1)plt.title('LDA of WINE dataset')plt.show()

奇异值分解(SVD:Singular Value Decomposition)
SVD 是类似于 PCA 的数据摘要方法。它从数据中提取重要特征。但 SVD 还有一个优势:将原始数据集重建为小型数据集。所以它具有广泛的应用,如图像压缩等。例如,如果您有32*32=1024像素图像,则 SVD 可以将其概括为 66 像素。66 像素可以检索32*32像素图像而不会错过任何重要信息。
SVD 在线性代数中发挥了重要作用,但在吉尔伯特·斯特朗(Gilbert Strang)的经典教科书《线性代数及其应用》(linear algebra and Its Applications)中,它似乎“不像它应该的那样出名”。为了正确地引入奇异值分解,必须从矩阵运算开始。如果 A 是对称实n*n矩阵,则存在一个正交矩阵V和一个对角矩阵D

列V是 A 的特征向量,D的对角线条目是 A 的特征值。该过程称为矩阵a的特征值分解,或EVD。它告诉我们如何选择 Orthonormal 基础,以便转换由具有最简单形式的矩阵表示,即对角线。(对于想要越过步骤的读者来对角度化矩阵,这里[6]是一个很好的示例。)术语正式意味着两个载体是正交或垂直的。
列V是 A 的特征向量,D的对角项是 A 的特征值。这个过程称为矩阵A的特征值分解,或EVD。它告诉我们如何选择标准正交基底,这样变换就可以用一个矩阵来表示它的最简单形式,也就是对角线形式。(对于想要了解矩阵对角化步骤的读者,可以参考这里[7]。
扩展对称矩阵,SVD 适用于任意实数mxn矩阵A*。给定一个实数m×n矩阵a,存在一个正交的m×m矩阵 U,一个正交m×m矩阵 V,一个对角的m×n矩阵 Σ,

注意,一个正交矩阵是一个方阵,其自身及其逆矩阵的乘积是单位矩阵。对角线矩阵是指除对角线以外的所有元素都为零的矩阵。
下面我将再次使用鸢尾花数据集向您展示如何应用 SVD。
from numpy import *import operatorimport matplotlib.pyplot as pltimport pandas as pdfrom numpy.linalg import *url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"# load dataset into Pandas DataFramedf = pd.read_csv(url, names=['sepal length','sepal width','petal length','petal width','target'])# Only the X variablesdata = df[['sepal length','sepal width','petal length','petal width']]#calculate SVDn = 2 # We will take two Singular ValuesU, s, V = linalg.svd( data )# eye() creates a matrix with ones on the diagonal and zeros elsewhereSig = mat(eye(n)*s[:n])newdata = U[:,:n]newdata = pd.DataFrame(newdata)newdata.columns=['SVD1','SVD2']newdata.head()

您可以将 SVD 的结果与 PCA 的结果进行比较。两者都达到了类似的结果。
# Add the actual target to the data in order to plot itnewdata['target']=df['target']fig = plt.figure()ax = fig.add_subplot(1,1,1)ax.set_xlabel('SVD 1')ax.set_ylabel('SVD 2')ax.set_title('SVD')targets = ['Iris-setosa', 'Iris-versicolor', 'Iris-virginica']colors = ['r', 'g', 'b']for target, color in zip(targets,colors):    indicesToKeep = newdata['target'] == target    ax.scatter(newdata.loc[indicesToKeep, 'SVD1'] , newdata.loc[indicesToKeep, 'SVD2'] , c = color , s = 50)ax.legend(targets)ax.grid()
图:SVD.
t 分布-随机邻近嵌入(t-SNE:t-distributed Stochastic Neighbor Embedding)
t-SNE 由Laurens Van der Maaten 和 Geoggrey Hinton[8]开发的。它是一种用于可视化的机器学习算法,其呈现在两或三维的低维空间中嵌入高维数据。

将上述三维瑞士卷呈现为二维的最佳方式是什么?直观地我们希望“展开”瑞士卷到一个平坦的蛋糕。在数学中,它意味着类似的观点将成为附近的点,异常点将成为距离点。
图(C)显示了另一个例子。它是一个三维四面体,数据点聚类在顶点。如果我们只是像 Panel (a)那样将三维图折叠成二维图,它就不能很好地工作,因为组(a)变成了中心集群。相比之下,Panel (B)可能是一个更好的 2d 展示,它保留了 Cluster (a)-(E)之间的距离,同时保留了每个 Cluster 中点的局部距离。t-SNE 是一种非线性降维技术,用于保持局部邻域。如果一组点在 t-SNE 图上聚在一起,我们可以相当肯定这些点彼此很接近。

图(c):t-SNE
t-SNE 对点之间的相似性进行建模。它是如何定义相似性的?首先,它是由点Xi和Xj之间的欧氏距离定义的。其次,定义为“数据点i与点j的相似度是点i根据高斯分布下的概率选择其他邻居时,将数据点j作为其邻居的条件概率p”的条件概率。在下面的条件表达式中,如果点j比其他点更接近点i,那么它被选中的概率就更高(注意负号)。

t-SNE 的目标是通过点Yi和Yj之间的低维空间q来匹配上述j和i的条件概率p,如下图所示。概率q遵循长尾 Student-t 分布,这就是 t-SNE 中的T的由来。

下一步是找到Yi,使得分布q尽可能接近分配p。t-SNE 使用梯度下降技术,一种寻找值的优化技术。
下面我展示了如何与鸢尾花数据集一起使用 t-SNE 技术。
from sklearn.manifold import TSNEfrom sklearn.datasets import load_irisfrom sklearn.decomposition import PCAimport matplotlib.pyplot as pltiris = load_iris()X_tsne = TSNE(learning_rate=100).fit_transform(iris.data)X_pca = PCA().fit_transform(iris.data)plt.figure(figsize=(10, 5))plt.subplot(121)plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=iris.target)plt.subplot(122)plt.scatter(X_pca[:, 0], X_pca[:, 1], c=iris.target)

有关 t-SNE 更多信息,这篇文章“如何更有效地使用 t-SNE[9]”提供了更多的讨论。
参考资料
[1]
异常检测到 AutoEncoders 易于易于易于修复: https://towardsdatascience.com/anomaly-detection-with-autoencoder-b4cdce4866a6[2]
避免这些致命建模错误,可能会浪费你职业生涯: https://towardsdatascience.com/avoid-these-deadly-modeling-mistakes-that-may-cost-you-a-career-b9b686d89f2c[3]
径向基函数: https://en.wikipedia.org/wiki/radial_basis_function[4]
支持向量机(SVM): https://en.wikipedia.org/wiki/support-vector_machine[5]
红葡萄酒质量: https://www.kaggle.com/piyushgeyal443/red-wine-dataset#winequalityInfo.txt[6]
这里: https://yutsumura.com/how-to-diagonalize-a-matrix-step-by-step-explanonation/[7]
这里: https://yutsumura.com/how-to-diagonalize-a-matrix-step-by-step-explanation/[8]
Laurens Van der Maaten 和 Geoggrey Hinton: http://www.cs.toronto.edu/~hinton/absps/tsne.pdf[9]
如何更有效地使用 t-SNE: https://distill.pub/2016/misread-tsne/
原文:Dimension Reduction Techniques with Python
链接:https://towardsdatascience.com/dimension-reduction-techniques-with-python-f36ca7009e5c?gi=836dd28c32df
作者:Dr. Dataman
到顶部