机器学习中降维技术Python示例
为什么需要降维?
高维机器学习数据集是具有大量列(或变量)的数据集。高维机器学习数据集对计算提出了相应的挑战。通常变量(或称为特征)是相关的。我们希望找到一个变量子集来表示数据中相同级别的信息,或者在不丢失太多信息的情况下将变量转换为一组新的变量。虽然高性能计算可以处理高维数据,但在许多应用中仍然需要降低原始数据的维度。
当进行降维时,主成分分析(PCA)可能是最流行的技术。在本文中,我将从PCA开始,然后继续介绍其他降维技术。
主成分分析(PCA)
主成分分析(PCA)的思想是减少由大量相关变量组成的数据集的维数,同时尽可能保持数据集的方差。PCA找到一组新的变量,原来的变量只是它们的线性组合。新的变量称为主成分(PC)。这些主成分是正交的:在三维情况下,主成分彼此垂直。X不能用Y表示或者Y不能用Z表示。
图(A)显示了PCA的直觉:它“旋转”坐标轴,以便更好地与数据对齐。第一个主成分将捕获数据中的大部分方差,然后是第二个、第三个,依此类推。因此,新的数据将具有更少的维度。
图(A):PCA
让我们使用iris机器学习数据集来说明PCA:
# Use the iris dataset to illustrate PCA: import pandas as pd url = “https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data" # load dataset into Pandas DataFrame df = pd.read_csv(url, names=[‘sepal length’,’sepal width’,’petal length’,’petal width’,’target’]) df.head()
IRIS数据集
在应用PCA之前,所有的变量应该在相同的范围内,否则一个值较大的特征将会主导结果。下面我在scikit-learn中使用StandardScaler将机器学习数据集的特征标准化到单位尺度(均值= 0,方差= 1)。
from sklearn.preprocessing import StandardScaler variables = [‘sepal length’, ‘sepal width’, ‘petal length’, ‘petal width’] x = df.loc[:, variables].values y = df.loc[:,[‘target’]].values x = StandardScaler().fit_transform(x) x = pd.DataFrame(x)
标准化特征
原始数据集中有四个特征。所以PCA会提供相同数量的主成分。
from sklearn.decomposition import PCA pca = PCA() x_pca = pca.fit_transform(x) x_pca = pd.DataFrame(x_pca) x_pca.head()
IRIS数据集的主成分
每个主要组成部分解释的方差是什么?使用pca.explained_variance_ratio_返回的波动的向量:
每一个主成分解释的方差是什么呢?使用主成分分析。explained_variance_ratio_返回方差向量:
explained_variance = pca.explained_variance_ratio_ explained_variance
array([0.72770452, 0.23030523, 0.03683832, 0.00515193])
它显示第一个主成分占72.22%的方差,第二,第三和第四个占23.9%,3.68%和0.51%的方差。我们可以说第一和第二主成分捕获了72.22 + 23.9 = 96.21%的信息。我们经常希望只保留重要的特征并删除无关紧要的特征。经验法则是,保留能捕获显著方差的主成分,忽略较小的方差。
我们可以使用前两个成分绘制结果。让我们将目标变量y附加到新数据x_pca:
x_pca[‘target’]=y x_pca.columns = [‘PC1’,’PC2',’PC3',’PC4',’target’] x_pca.head()
结果显示数据在新空间中是可分离的。
import matplotlib.pyplot as plt fig = 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()
Kernel PCA(KPCA)
主成分分析应用线性变换,这是它的局限性。KPCA将主成分分析扩展到非线性。首先将原始数据映射到某个非线性特征空间(通常是高维空间),然后应用PCA提取该空间的主成分。这可以通过图(B)来理解,左图中蓝点和红点是不能用任何线性变换来分割的。但是如果所有的点都投影到三维空间上,结果就变成线性可分的了!然后我们应用PCA来分离成分。
直觉从何而来呢?为什么投影到高维空间更容易分离成分呢?这要追溯到VC (Vapnik-Chervonenkis)理论。它表示,映射到高维空间通常提供更强的分类能力。
图(B):KPCA
以下Python代码使圆形图由红色和蓝色点组成。显然,没有办法用线分开红色和蓝色点(线性分离)。
print(__doc__) import numpy as np import matplotlib.pyplot as plt from sklearn.decomposition import PCA, KernelPCA from sklearn.datasets import make_circles np.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 == 0 blues = y == 1 plt.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中,我们指定kernel ='rbf',它是径向基函数,或Eclidiean距离。RBF通常用作机器学习技术中的核,例如支持向量机(SVM)。
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”)
如果我们将核指定为如下Python代码所示的“linear”(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)
LDA的起源不同于PCA。PCA是一种将原始特征转化为一组新特征的无监督机器学习方法。我们并不关心新特征集是否能够为目标变量提供最佳的区分能力。相反,线性判别分析(LDA)在将原始数据矩阵投影到较低维空间的同时,尽可能地保留因变量的判别能力。LDA是一种监督机器学习技术。它利用因变量中的类将预测器的空间划分为区域。所有区域都应该有线性边界。因此,linear这个名字就来源于此。该模型预测一个区域内的所有观测值都属于同一类因变量。
LDA通过三个主要步骤实现上述目标。首先,它计算因变量的不同类之间的可分性,称为类间方差,如图LDA的(1)所示。其次,它计算每个类的均值和样本之间的距离,称为类内方差,如(2)所示。然后用这个标准构造低维空间:最大化类间方差并最小化类内方差。该标准的解决方案是计算特征值和特征向量。得到的特征向量表示新空间的方向,相应的特征值表示特征向量的长度。因此,每个特征向量表示LDA空间的一个轴,并且特征值表示该特征向量的长度。
图:LDA
我将在Kaggle比赛中使用“ 红酒质量 ”数据集(https://www.kaggle.com/piyushgoyal443/red-wine-dataset#wineQualityInfo.txt)。该数据集有11个输入变量和一个输出变量'quality'。
import matplotlib.pyplot as plt from sklearn.decomposition import PCA from sklearn.discriminant_analysis import LinearDiscriminantAnalysis wine = 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_names pca = 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 components print(‘explained variance ratio (first two components): %s’ % str(pca.explained_variance_ratio_)) plt.figure() colors = [‘navy’, ‘turquoise’, ‘darkorange’] lw = 2 for 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)
SVD是一种类似于PCA的数据汇总方法。它从数据中提取重要的特征。SVD还有一个优点:将原始机器数据集重构为小数据集。因此,它具有广泛的应用,如图像压缩。例如,如果您有一个32*32 = 1,024像素的图像,SVD可以将其汇总为66像素。66个像素可以检索到32*32像素的图像,不会遗漏重要信息。
SVD在线性代数中发挥了重要作用,但似乎“并不像它应该的那样着名”,如Gilbert Strang的经典教科书“线性代数及其应用”中所述。要正确引入SVD,必须从矩阵运算开始。如果A是一个实对称n×n的矩阵,存在正交矩阵V和对角线d,使得
SVD在线性代数中一直起着重要的作用。为了正确地引入奇异值分解,必须从矩阵运算开始。如果A是一个对称实n×n矩阵,则存在一个正交矩阵V和一个对角矩阵D,使得
列V是A的特征向量,D的对角元素是A的特征值。这个过程被称为矩阵A的特征值分解,或EVD,它告诉我们如何选择标准正交基,以便转换由具有最简单形式的矩阵表示,即对角矩阵。
通过对对称矩阵的扩展,奇异值分解适用于任何实的m×n个矩阵甲。给定m ×n实矩阵A,存在m×m正交矩阵U,正交矩阵m×m V和对角矩阵m×nΣ,使得
注意,正交矩阵是方形矩阵,它自身的乘积和它的逆矩阵是一个单位矩阵。一个对角矩阵是一个除对角线外所有元素都为零的矩阵。
下面我将再次使用iris机器学习数据集向您展示如何应用SVD。
from numpy import * import operator import matplotlib.pyplot as plt import pandas as pd from numpy.linalg import * url = “https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data" # load dataset into Pandas DataFrame df = pd.read_csv(url, names=[‘sepal length’,’sepal width’,’petal length’,’petal width’,’target’]) # Only the X variables data = df[[‘sepal length’,’sepal width’,’petal length’,’petal width’]] #calculate SVD n = 2 # We will take two Singular Values U, s, V = linalg.svd( data ) # eye() creates a matrix with ones on the diagonal and zeros elsewhere Sig = 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 it newdata[‘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-SNE
t-SNE是由Laurens van der Maaten和Geoggrey Hinton开发的。它是一种在二维或三维的低维空间中嵌入高维数据的可视化机器学习算法。
将上述三维瑞士卷呈现为二维的最佳方式是什么呢?直觉上,我们希望将瑞士卷“展开”成扁平的蛋糕。在数学中,它意味着相似的点会变成附近的点,不同的点会变成距离点。
图(C)显示了另一个示例。它是一个三维四面体,顶点处有数据点聚类。如果我们只是像Panel (a)那样将三维图折叠成二维图,那么它就不能很好地工作,因为group (a)变成了中心聚类。相比之下,Panel (B)可能是更好一些,它保留了Cluster (A)-(E)之间的距离。t -SNE是一种非线性降维技术,旨在保护局部邻域。如果一组点聚集在一个t-SNE图上,我们可以相当肯定这些点彼此很接近。
图(C):t-SNE
t-SNE对点之间的相似性进行了建模。它如何定义相似性呢?首先,它由点Xi和Xj之间的欧氏距离定义。其次,定义为条件概率,即“数据点i与点j相似度为条件概率p,即如果在高斯分布下,根据其他neighbors的概率选取数据j作为其neighbor,则该点i将选取数据j作为其neighbor。”在下面的条件表达式中,如果点j比其他点更接近点i,那么选择它的概率就更高(注意负号)。
t-SNE旨在通过点Yi和Yj之间的低维空间q匹配j和i之间的上述条件概率p ,如下所示。
下一步是找到Yi,使得分布q尽可能接近分布p。t-SNE使用使用梯度下降法技术(一种优化技术)来查找值。
下面我将演示t-SNE技术如何与iris数据集一起使用。
from sklearn.manifold import TSNE from sklearn.datasets import load_iris from sklearn.decomposition import PCA import matplotlib.pyplot as plt iris = 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)