主成分分析-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