主成分分析-PCA
1. 数据的降维
高维数据
除了图片、文本数据,我们在实际工作中也会面临更多高维的数据。比如在评分卡模型构建过程中,我们通常会试着衍生出很多的特征,最后就得到上千维、甚至上万维特征; 在广告点击率预测应用中,拥有几个
亿特征也是常见的事情; 在脑科学或者基因研究中,特征数甚至可能更多; 所以,如何更有效地处理这些高维的特征就变成了一个非常重要的问题。
数据降维
除了有效利用高维的数据之外,我们也可以思考一个问题:“高维数据,那么多特征真的都有用吗?” 这就类似于一个人的社交质量并不取决于有多少朋友,而在于朋友质量,在建模过程中也适用这个道理。特征越
多并不代表学出来的模型越好,我们更需要关注特征对预测任务的相关性或者价值,有些特征甚至可能成为噪声,反而影响模型的效果。
在数据挖掘领域,有这样的一句话:“Better Representation always lies in a lower dimensional space”,究竟如何理解这句话呢?
低维空间的表示
- 可视化:R100 ——> R2/ R3
- 更高效地利用资源 R1000 ——> R20
- 更强的泛化能力 R1000 ——> R20
- 去除噪声
如何去寻找更低维空间的表示?
- 数据的降维
- 特征的选择
2. PCA的核心思想
关于PCA的几个问题
PCA(Principal Component Analysis)作为一种重要的降维算法有着非常广泛的应用。PCA经常用来做数据的可视化、或者用来提高预测模型的效果。 对于PCA降维算法来讲,有几个核心问题需要弄清楚:
- PCA降维的核心思想是什么? 它是依赖于什么条件做降维?
- 什么叫主成分(principal component)?
以上图片中的是二维数据的可视化展示。我们现在希望把上述数据映射到一维的空间, 或者可以理解为希望用一维来表示数据但同时保留原始数据的核心特点。
在图中给出了4条直线分别代表的是我们希望选择的坐标方向:

对于给定的二维数据,基于PCA映射到二维空间
主成分的选取数量决定降维到多大的空间
通常来讲,如果原始数据的维度为100维,我们则可以计算出100个特征向量(或者说100个主成分向量)。具体降维到多少就看我们要选择其中多少个主成分,如果选取了其中10个主成分就意味着把原始100维的数据映射到了10维的空间;
如果选择了其中5个主成分,意味着把数据映射到了5维的空间。 这里有个问题需要考虑:选择多少维是合适的?
3. PCA的一些细节
PCA的推导过程
整个推导的核心是寻找PCA里的主成分;协方差矩阵 - Covariance Matrix
给定数据 D = {x1, x2, x3 ..., xn} ,xi ∈ Rd ,寻找主成分u1, u2...
需要选取多少个主成分
问题:取多少个主成分 ?
对于10维的数据,假如我们计算出了10个特征值:【30,29,28,21,18,17,14,10,5,1】,需要取多少个特征值呢?
特征值实际上代表了包含了多少信息量,从大到小排序后,选择前K个特征值使得信息量占比大于90%或者95%
import numpy as np import matplotlib.pyplot as plt eigenvars = np.array([30, 29, 28, 21, 18, 17, 14, 10, 5, 1]) plt.plot(range(1, 11), eigenvars.cumsum()/np.sum(eigenvars)) plt.show()
PCA的实现
import numpy as np import pandas as pd from sklearn.datasets import load_iris # 直接从sklearn导入iris数据
from sklearn.preprocessing import StandardScaler data = load_iris() # 读取特征、标签
X = data.data y = data.target # 做数据的归一化(必要的步骤!!!)
X_scaled = StandardScaler().fit_transform(X) # 计算数据的covariance矩阵 cov = (1/n)* X'*X (X' 是 X的转置), n为样本总数
cov_matrix = np.matmul(X_scaled.T,X_scaled)/len(X_scaled) # 根据cov_matrix计算出特征值与特征向量,调用linalg.eig函数 # https://numpy.org/doc/stable/reference/generated/numpy.linalg.eig.html # eigenvalues存放所有计算出的特征值,从大到小的顺序 # eigenvectors存放所有对应的特征向量,这里 eigenvectors[:,0]表示对应的第一个特征向量 # 注:是列向量
eigenvalues, eigenvectors = np.linalg.eig(cov_matrix) # 根据计算好的特征向量,计算每个向量的重要性
import matplotlib.pyplot as plt explained_variances = [] for i in range(len(eigenvalues)): explained_variances.append(eigenvalues[i] / np.sum(eigenvalues)) plt.plot(range(1,5),np.array(explained_variances).cumsum()) plt.title('Explained Variance',fontsize=15) plt.xlabel('Number of Principle Components', fontsize=10) plt.show() # 把数据映射到二维的空间(使用前两个特征向量)
pca_project_1 = X_scaled.dot(eigenvectors.T[0]) #基于第一个特征向量的维度值
pca_project_2 = X_scaled.dot(eigenvectors.T[1]) # 基于第二个特征向量的维度值 # 构造新的二维数据
res = pd.DataFrame(pca_project_1, columns=['PCA_dim1']) res['PCA_dim2'] = pca_project_2 res['Y'] = y print (res.head()) import seaborn as sns # 仅用第一个维度做可视化,从结果中会发现其实已经分开的比较好了
plt.figure(figsize=(20, 10)) sns.scatterplot(res['PCA_dim1'], [0] * len(res), hue=res['Y'], s=200) # 用前两个维度做可视化,从结果中发现分开的比较好
plt.figure(figsize=(20, 10)) sns.scatterplot(res['PCA_dim1'], res['PCA_dim2'], hue=res['Y'], s=200)
PCA的缺点
- PCA实际上做了线性转换,对于非线性数据效果不好;
- 必须要做特征的归一化(把数据挪到中心的位置);
f1:(0.1,0.2,0.4,0.7,0.3,0.6);
f2:(35,35.3,37,36.3,36,36.3,37,37);
- 部分信息会丢失;
- 可解释性比较弱;
4. 其他降维的方法
- kernel PCA (可处理非线性的数据)
- ICA(Independent component analysis)
- t-SNE (用于词向量可视化 )
- MF(Matrix Factorization) (矩阵分解,推荐系统)
- Deep Learning (深度学习 )
kernel PCA
ICA(Independent component analysis)
t-SNE
MF(Matrix Factorization)
Deep Learning
5. 基于PCA的人脸识别
基于PCA的人脸识别
在这个案例中,我们将使用PCA做人脸识别,主要分为两步:
- 利用PCA把64*64大小的图片映射到低维空间
- 在低维空间利用随机森林做分类
通过这个案例会学到:
- 如何展示图片
- 如果在图片上应用PCA
- 如何可视化特征值的ratio
- 如何选择合适的主成分个数
- 如何可视化eigenface
- 如何把数据降维
- 如果在降维的数据上训练分类模型
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns sns.set() # 直接从sklearn导入人脸识别数据
from sklearn.datasets import fetch_olivetti_faces data = fetch_olivetti_faces() print(data.keys()) # dict_keys(['data', 'images', 'target', 'DESCR'])
inputs=data.data target=data.target images=data.images print(inputs.shape) # 400张人脸图片,每个图片像素为64*64 # (400, 4096)
# 显示其中几张图片
plt.figure(figsize=(20,20)) # 设置fig大小
for i in range(10,30): # 输出其中20张图片
plt.subplot(4,5,i-9) # 每行五张图片,总共四行
plt.imshow(data.images[i], cmap=plt.cm.gray) plt.show()
# 导入相应的库
from sklearn.decomposition import PCA from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(inputs, target, random_state=365) # 利用PCA把原来4096(64*64)维度的数据映射到100维上
pca = PCA(n_components=200, whiten=True) # 在训练数据上学习主成分,并把训练数据转换成100维的数据
X_train = pca.fit_transform(X_train) # 在测试数据上做同样的转换
X_test = pca.transform(X_test) # 可视化前100个主成分(占比)
plt.plot(range(1,201), pca.explained_variance_ratio_.cumsum()) plt.title('Explained Variance',fontsize=15) plt.xlabel('Number of Principle Components', fontsize=10) plt.show() # 从结果图中可以看出,当拥有100个主成分时已经将近接近了95%,所以我们可以选定利用前100个主成分
# 在这里,我们可视化主成分。对于人脸识别,我们把每一个主成分也称之为 # eigenface
plt.figure(figsize=(20,20)) # 设置fig大小
for i in range(0,20): # 输出前20个主成分
plt.subplot(4,5,i+1) # 每行五张图片,总共四行
# 可视化每一个主成分,可视化时需要reshape成64*64的矩阵形式,这样才能输出为图片
plt.imshow(pca.components_[i].reshape(64,-1),cmap=plt.cm.gray) plt.show()
# 利用随机森林图像的分类
from sklearn.ensemble import RandomForestClassifier clf = RandomForestClassifier(n_estimators=500, random_state=0) clf.fit(X_train, y_train) # 计算一下分类准确率
accuracy_train= clf.score(X_train, y_train) accuracy_test= clf.score(X_test, y_test) print('Accuracy - train data: {}'.format(accuracy_train)) print('Accuracy - test data : {}'.format( accuracy_test)) # 计算一下f1-score
from sklearn.metrics import f1_score test_pre=clf.predict(X_test) train_pre=clf.predict(X_train) f1_train=f1_score(y_train,train_pre,average='weighted') f1_test=f1_score(y_test,test_pre,average='weighted') print("f1 score - train data : {}" .format(f1_train)) print("f1 score - test data : {}" .format(f1_test))
Accuracy - train data: 1.0
Accuracy - test data : 0.94
f1 score - train data : 1.0
f1 score - test data : 0.9379365079365078