【火炉炼AI】机器学习053-数据降维绝招-PCA和核PCA

96
炼丹老顽童
2018.10.31 11:20 字数 1893

【火炉炼AI】机器学习053-数据降维绝招-PCA和核PCA

(本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )

主成分分析(Principal Component Analysis, PCA)可以说是数据降维的绝招,不仅在人口统计学,数量地理学,分子动力学模拟,数学建模等领域有着重要的应用,而且在机器学习领域,PCA也是非常常用的一种数据降维方法。

首先来理解什么叫数据降维:假设有一个项目,要从外观上来判断某个人是男人还是女人,我们可以提取各种各样的特征,比如身高,一般而言,男人的身高要比女人高,但也有特例,比如头发长度,有无胡须,脸型,衣服颜色,身材,体重,肤色,BMI指标。。。。等等,如果有需要,你可以为这个项目提取几百甚至几千个特征,假如你一口气提取了200个特征,每一个特征都反映了一个人的某一个方面的信息,那么,用机器学习的方法对这200个特征进行建模,并用训练集来训练,则会非常耗时,而且得到的模型结构庞大,故而我们需要减少特征数量,但是我们不希望减少特征数量的同时把重要的信息给弄丢了。将特征数量从几百上千降低到几十的过程就是数据降维。

在上面这个项目中,有很多特征之间是由关联的,比如BMI指标就是身高/体重的平方,那么很明显,BMI指标一个特征就包括了身高和体重两个特征,BMI和这两个指标之间具有非常强的相关性。同理,200个特征之间也可能有很多变量之间具有相关性,所以这些相关特征之间所反映的信息是一样的,PCA的作用就是对所有的这200个特征,去除相关性非常强的特征,建立尽可能少的新特征,使得这些新特征之间是两两不相关,并且这些新特征在反应项目的信息方面会尽可能保持原有的信息。

关于PCA的理论推导和更深层次的数学逻辑,请参考博文PCA算法


1. 用PCA对数据集降维

为了看到降维效果,此处我们用随机数自动生成一个数据集,前两个特征向量都是随机数,后三个特征向量是前两个特征计算组合而来。如下为代码

# 假如某个项目有5个特征,这五个特征分别为:
f1=np.random.normal(size=250)
f2=np.random.normal(size=250)
# 后面的三个特征是前面两个特征演变而来,即与前面两特征具有很强的相关性
f3=2*f1+3*f2
f4=4*f1-f2
f5=f3+2*f4

很多时候,我们要查看数据集中各特征向量之间的相关性,如下

# 将这些特征组合成数据集
dataset_X=np.c_[f1,f2,f3,f4,f5]
# 计算各特征列之间的相关系数
df=pd.DataFrame(dataset_X,columns=['f1','f2','f3','f4','f5'])
print(df.corr())

-------------------------------------输---------出--------------------------------

     f1        f2        f3        f4        f5

f1 1.000000 -0.002496 0.528931 0.966354 0.994370
f2 -0.002496 1.000000 0.847342 -0.259627 0.103485
f3 0.528931 0.847342 1.000000 0.292844 0.615884
f4 0.966354 -0.259627 0.292844 1.000000 0.933656
f5 0.994370 0.103485 0.615884 0.933656 1.000000

--------------------------------------------完-------------------------------------

可以明显看出,数据集中有很多特征之间具有比较强的相关性,比如f1-f5,f1-f4等。

所以我们可以用PCA来进行降维:

# 可以看出f1-f5,f1-f4,f2-f3等之间具有强相关性,故而可以用PCA降维
from sklearn import decomposition
pca=decomposition.PCA()
pca.fit(dataset_X) # 用PCA降维
# 打印降维后的新特征
variances=pca.explained_variance_
print(variances) # 可以理解成该特征的重要性,后面三个数字非常小,即特征不重要

-------------------------------------输---------出--------------------------------

[1.15552796e+02 1.14453854e+01 3.08872295e-31 8.39043564e-32
1.18268234e-32]

--------------------------------------------完-------------------------------------

# 故而可以为重要性设置一个阈值,小于该阈值的认为该特征不重要,可删除
thresh=0.8
useful_features=variances>thresh
print(useful_features) # 标记为True的表示重要特征,要保留,False则删除

-------------------------------------输---------出--------------------------------

[ True True False False False]

--------------------------------------------完-------------------------------------

一旦我们通过PCA进行了降维,就需要将原来的高维数据集转换为低维数据集,然后用低维数据集来建模

useful_features_num=np.sum(useful_features) # 计算True的个数

# 进行PCA降维之后的新数据集为:
pca.n_components=useful_features_num # 即设置PCA的新特征数量为n_components
new_dataset_X=pca.fit_transform(dataset_X)
print('before PCA, dataset shape: ', dataset_X.shape)
print('after PCA, dataset shape: ', new_dataset_X.shape)

-------------------------------------输---------出--------------------------------

before PCA, dataset shape: (250, 5)
after PCA, dataset shape: (250, 2)

--------------------------------------------完-------------------------------------

PCA的优点和缺点:

优点:1,对数据进行降维处理,我们可以对新求出的“主元”向量的重要性进行排序,根据需要取前面最重要的部分,将后面的维数省去,可以达到降维从而简化模型或是对数据进行压缩的效果。同时最大程度的保持了原有数据的信息。

2,完全无参数限制,处理结果只与数据相关,而与用户无关:在PCA的计算过程中完全不需要人为的设定参数或是根据任何经验模型对计算进行干预,最后的结果只与数据相关,与用户是独立的。

缺点:1,PCA以线性方式工作,如果数据集不是以线性方式组织的,那么PCA效果就很差,此时,我们可以根据先验知识对数据预先进行非线性转换,将非线性数据集转换到相信空间中,这种分析方式叫做Kernel-PCA,也叫核PCA,它解决了PCA只能处理线性数据集的缺点,又结合一些先验知识的约束,是目前比较流行的方法。

2,有的数据集的分布并不满足高斯分布,在非高斯分布的情况下,PCA得到的主要特征可能并不是最优的,在寻找主要特征时不能将方差作为衡量重要性的标准,此时要根据数据的分布情况选择合适的描述完全分布的变量,根据一定的概率分布公式来计算两个特征数据分布的相关性,这种分析方式叫独立主元分解(ICA)。

可以参考博文: 主成分分析(Principal components analysis)-最小平方误差解释


2. 用核PCA对数据集降维

前面提到,PCA虽好,但也不是放之四海而皆准,对于非线性方式组织的数据集,PCA方法可能效果较差,此时需要用核PCA方法来解决。

我们来看一个典型的非线性方式组织的数据集:

# 准备数据集
from sklearn.datasets import make_circles
dataset_X,dataset_y=make_circles(n_samples=500,factor=0.2,noise=0.04)

该数据集的二维分布图为:

image

那么如果我们用普通PCA对这个数据集进行降维,会得到什么样的结果了?

# 如果用普通的PCA来降维
from sklearn.decomposition import PCA
pca = PCA()
dataset_X_pca = pca.fit_transform(dataset_X)
print(dataset_X_pca.shape)
visual_2D_dataset(dataset_X_pca,dataset_y,'PCA transformed dataset')
# 从图中几乎看不出PCA降维前后有啥区别
image

如果用核PCA来降维,能够将数据集变成线性可分,如下:

# 用核PCA方法来降维
from sklearn.decomposition import KernelPCA
kernel_pca = KernelPCA(kernel="rbf", fit_inverse_transform=True, gamma=10)
X_kernel_pca = kernel_pca.fit_transform(dataset_X)
print(X_kernel_pca.shape) # 2维特征变成了465维,降维?增维?
visual_2D_dataset(X_kernel_pca[:,:2],dataset_y,'Kernel PCA transformed dataset')
image

很明显,得到的新数据集便是线性可分。

话说,怎么经过核PCA降维之后,原先的2个特征怎么增加到465个特征?降维还是增维?呵呵。

那么从这个核PCA得到的新数据集能够返回到原来的数据集了?

# 如何从Kernel PCA得到的数据集反向计算出原始的数据集
dataset_X_inverse = kernel_pca.inverse_transform(X_kernel_pca)
print(dataset_X_inverse.shape)
visual_2D_dataset(dataset_X_inverse,dataset_y,'inversed dataset_X from KernelPCA')
image

可以看出,恢复之后的数据集的分布情况和原始数据集的分布情况相同。

########################小**********结###############################

1,对数据集进行降维可以很好地解决特征数太多导致的训练太耗时,模型结构臃肿的问题,降维方法有很多种,其中的PCA降维方式可以解决具有线性方式的数据集,而核PCA方法可以对具有非线性方式组织的数据集进行降维。

#################################################################


注:本部分代码已经全部上传到(我的github)上,欢迎下载。

参考资料:

1, Python机器学习经典实例,Prateek Joshi著,陶俊杰,陈小莉译

机器学习
Gupao