主成分分析(Principal Component Analysis,PCA)是最常用的一种降维方法,通常用于高维数据集的探索与可视化,还可以用作数据压缩和预处理等。PCA可以把具有相关性的高维变量合成为线性无关的低维变量,称为主成分。主成分能够尽可能保留原始数据的信息
详细介绍可以参考: 《主成分分析(PCA)原理详解(转载)》
一、数据降维
以 sklearn.datasets 的 load_wine 酒数据为例,将数据特征降维到2维,并可视化降维后的数据集,还给出原始特征与PCA主成分之间的关系。
注释1:
sklearn.decomposition.PCA
函数原型:sklearn.decomposition.PCA(self, n_components=None, copy=True, whiten=False, svd_solver='auto', tol=0.0, iterated_power='auto', random_state=None)
参数解释:
n_components: 这个参数可以帮我们指定希望PCA降维后的特征维度数目。
1.最常用的做法是直接指定降维到的维度数目,此时n_components是一个大于等于1的整数;即如果希望降维后保留原特征90%的信息,那么可以设置该值为0.9
2.也可以指定主成分的方差和所占的最小比例阈值,让PCA类自己去根据样本特征方差来决定降维到的维度数,此时n_components是一个(0,1]之间的数;
3.还可以将参数设置为'mle'(极大似然估计), 此时PCA类会用MLE算法根据特征的方差分布情况自己去选择一定数量的主成分特征来降维;
4.也可以用默认值,即不输入n_components,此时n_components=min(样本数,特征数)。
copy: 表示是否在运行算法时,将原始数据复制一份。默认为True,则运行PCA算法后,原始数据的值不会有任何改变。因为是在原始数据的副本上进行运算的。
whiten:白化。所谓白化,就是对降维后的数据的每个特征进行标准化,让方差都为1。对于PCA降维本身来说,一般不需要白化。如果你PCA降维后有后续的数据处理动作,可以考虑白化。默认值是False,即不进行白化。
svd_solver:即指定奇异值分解SVD的方法,由于特征分解是奇异值分解SVD的一个特例,一般的PCA库都是基于SVD实现的。有4个可以选择的值:{‘auto’, ‘full’, ‘arpack’, ‘randomized’}。
1.'randomized' 一般适用于数据量大,数据维度多同时主成分数目比例又较低的PCA降维,它使用了一些加快SVD的随机算法。
2.'full' 则是传统意义上的SVD,使用了scipy库对应的实现。
3.'arpack' 和randomized的适用场景类似,区别是randomized使用的是scikit-learn自己的SVD实现,而arpack直接使用了scipy库的sparse SVD实现。当svd_solve设置为'arpack'时,保留的成分必须少于特征数,即不能保留所有成分。
4.默认是'auto',即PCA类会自己去在前面讲到的三种算法里面去权衡,选择一个合适的SVD算法来降维。一般来说,使用默认值就够了。
注意:当设置 n_components == 'mle'时,需要和参数svd_solver一起使用,且svd_solver需要选择 'full' 参数;即pca = PCA(n_components = 'mle',svd_solver='full');同时要保证输入数据的样本数多于特征数才可执行成功。
另外,有两个PCA类的成员值得关注。第一个是explained_variance_,它代表降维后的各主成分的方差值,方差值越大,则说明越是重要的主成分。第二个是explained_variance_ratio_,它代表降维后的各主成分的方差值占总方差值的比例,这个比例越大,则越是重要的主成分。
注释2:
matplotlib.pyplot.matshow
函数原型:matshow(A, fignum=None, **kw)
这是一个绘制矩阵的函数:matplotlib.pyplot.matshow(A, fignum=None, **kwargs)
A是绘制的矩阵,一个矩阵元素对应一个图像像素。
from sklearn.datasets import load_wine
from sklearn.preprocessing import StandardScaler #导入数据预处理工具
from sklearn.decomposition import PCA #导入PCA
import matplotlib.pyplot as plt #导入画图工具
#载入红酒数据
wine = load_wine()
#对红酒数据进行预处理
scaler = StandardScaler()
X = wine.data
y = wine.target
X_scaled = scaler.fit_transform(X)
print('红酒数据样本数和特征数:', X_scaled.shape)
#设置主成分数量为 2 ,便于可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
print('降维后,红酒数据样本数和特征数:', X_pca.shape)
#数据特征可视化
#将三个分类中的主成分提取出来
X0 = X_pca[wine.target ==0]
X1 = X_pca[wine.target ==1]
X2 = X_pca[wine.target ==2]
#绘制散点图
plt.scatter(X0[:,0],X0[:,1],c='b',s=60,edgecolors='k')
plt.scatter(X1[:,0],X1[:,1],c='g',s=60,edgecolors='k')
plt.scatter(X2[:,0],X2[:,1],c='r',s=60,edgecolors='k')
#设置图注
plt.legend(wine.target_names, loc='best')
plt.xlabel('component 1')
plt.xlabel('component 2')
plt.show()
执行结果为:
红酒数据样本数和特征数: (178, 13)
降维后,红酒数据样本数和特征数: (178, 2)
在之前的帖子中,为了可视化红酒数据,总是只取前2个特征,砍掉其余,其实是不科学的,现在使用PCA之后,将数据特征降维至二维,轻松实现可视化,又不会丢失太多信息
主成分与各特征值之间的关系
在上图中,颜色由深至浅代表一个从 -0.5~0.4的数值,在两个主成分中,分贝涉及了13个特征,如果某个特征对应的数字是正数,说明它和主成分之间是正相关的关系,如果是负数则相反。
二、使用PCA 进行特征提取
使用 LFW人脸识别数据。LFW 人脸识别数据集包含了若干张JPEG图片,是从网站上搜集的一些名人的照片,每张照片都是一个人的脸部。这个数据集最初创建是为了训练机器学习算法,看给出两张照片,算法能否判断出这两个人是否是同一个人;后来,对机器学习提出了更高的要求,给出一张不在数据集里的照片,让机器判断这张照片书否属于该数据集中的某一个人,并叫出TA的名字。
注释:
1.关于该数据集的详细介绍,可以参考这篇《Dataset之LFW:LFW人脸数据库的简介、安装、使用方法之详细攻略》
2.由于外网太慢,下载真的太难了,最好直接下到本地使用,我再这里下载的本地数据集,如果你需要,给我留言,我分享给你
3.关于该数据集在本地的使用,请参考《解决sklearn中 fetch_lfw_people安装失败问题》
接下来,使用该数据集,在数据未经处理的情况下,训练一个神经网络;再使用PCA中的数据白化功能处理人脸数据集,再训练神经网络模型,对比结果
注释:
关于白化处理的原理以及作用,可以参考以下两篇帖子《# 白化(Whitening): PCA 与 ZCA (转)
》 和《数据处理中白化Whitening的作用图解分析》
#特征提取
from sklearn.decomposition import PCA #导入PCA
import matplotlib.pyplot as plt #导入画图工具
from sklearn.datasets import fetch_lfw_people
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
#载入人脸数据(本地提前下载好的,不然太慢了)
faces = fetch_lfw_people(min_faces_per_person=20, resize=0.8)
image_shape = faces.images[0].shape
#将照片打印出来
fig, axes = plt.subplots(3, 4, figsize=(12,9), subplot_kw={'xticks':(), 'yticks':()})
for target, image, ax in zip(faces.target, faces.images, axes.ravel()):
#print(target,faces.target_names[target])
ax.imshow(image, cmap=plt.cm.gray)
ax.set_title(faces.target_names[target])
#显示图像
#plt.show()
#数据未经处理,训练神经网络
X_train, X_test, y_train, y_test = train_test_split(faces.data/255, faces.target, random_state=62)
#训练神经网络
mlp = MLPClassifier(hidden_layer_sizes=[100,100], random_state=62, max_iter=400)
mlp.fit(X_train, y_train)
print('未经过预处理的数据模型识别准确率为:{:.2f}'.format(mlp.score(X_test, y_test)))
#使用白化功能处理人脸数据
pca = PCA(whiten=True, n_components=0.9, random_state=62) #保留90%的原始特征信息
pca.fit(X_train)
X_train_whiten = pca.transform(X_train)
X_test_whiten = pca.transform(X_test)
print('====================')
print('原始数据形态:{}'.format(X_train.shape))
print('白化后的数据形态:{}'.format(X_train_whiten.shape))
#使用白化后的数据进行神经网络模型训练
mlp.fit(X_train_whiten, y_train)
print('====================')
print('经过白化后的数据模型识别准确率为:{:.2f}'.format(mlp.score(X_test_whiten, y_test)))
执行结果为:
LFW人脸数据集中的部分照片如下图所示:
未经过预处理的数据模型识别准确率为:0.46
====================
原始数据形态:(2267, 7500)
白化后的数据形态:(2267, 107)
====================
经过白化后的数据模型识别准确率为:0.57
从结果可以看到。模型的准确率轻微的提高了一些,说明PCA 的白化功能对提高神经网络的准确率是有一定的帮助的。
三、使用NMF进行 特征提取
NMF(Non-Negative Matrix Factorization)非负矩阵分解,是一个无监督学习,原始的矩阵中的所有数值必须大于或者等于0,分解之后的矩阵中的数据也是大于或者等于0的,简单理解为, NMF 是在
一堆特征值混乱无序的堆放空间中,从坐标原点出(0,0)出发引出一个或者几个向量,用这些向量,尽可能的把原始特征的信息表达出来。
与PCA的不同:
1.如果我们降低NMF的成分数量,它会生成新的成分,而新的成分和原来的成分是完全不一样的。
2.NMF中的成分是无序的,这点和PCA是不同的。
以下用NMF对LFW人脸数据集进行特征提取,再重新训练神经网络模型,对比查看结果。
#特征提取
from sklearn.decomposition import PCA #导入PCA
import matplotlib.pyplot as plt #导入画图工具
from sklearn.datasets import fetch_lfw_people
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
from sklearn.decomposition import NMF
#载入人脸数据(本地提前下载好的,不然太慢了)
faces = fetch_lfw_people(min_faces_per_person=20, resize=0.8)
image_shape = faces.images[0].shape
#将照片打印出来
fig, axes = plt.subplots(3, 4, figsize=(12,9), subplot_kw={'xticks':(), 'yticks':()})
for target, image, ax in zip(faces.target, faces.images, axes.ravel()):
#print(target,faces.target_names[target])
ax.imshow(image, cmap=plt.cm.gray)
ax.set_title(faces.target_names[target])
#显示图像
#plt.show()
#数据未经处理,训练神经网络
X_train, X_test, y_train, y_test = train_test_split(faces.data/255, faces.target, random_state=62)
#训练神经网络
mlp = MLPClassifier(hidden_layer_sizes=[100,100], random_state=62, max_iter=400)
mlp.fit(X_train, y_train)
print('未经过预处理的数据模型识别准确率为:{:.2f}'.format(mlp.score(X_test, y_test)))
#使用NMF处理数据
nmf = NMF(n_components=107, random_state=62)
nmf.fit(X_train)
X_train_nmf = nmf.transform(X_train)
X_test_nmf = nmf.transform(X_test)
print('====================')
print('原始数据形态:{}'.format(X_train.shape))
print('nmf后的数据形态:{}'.format(X_train_nmf.shape))
mlp.fit(X_train_nmf, y_train)
print('====================')
print('经过nmf后的数据模型识别准确率为:{:.2f}'.format(mlp.score(X_test_nmf, y_test)))
执行结果如下:
原始数据形态:(2267, 7500)
nmf后的数据形态:(2267, 107)
====================
经过nmf后的数据模型识别准确率为:0.58
可见,进过NMF 处理后的数据训练的神经网络模型准确率和PCA处理后的模型准确率基本持平。