Learning Objectives
- 說明主成分是投影後變異最大的方向,並用 numpy 算出來
- 從解釋變異比例判斷可以保留幾個主成分
- 說明 PCA 為什麼通常要先標準化
- 用 PCA 把資料投影到兩維來視覺化,並說明它不看標籤帶來的限制
特徵太多,畫不出來
兩個特徵可以畫散布圖,三個勉強可以。上一課的生活習慣資料有 4 個特徵,等一下要用的手寫數字有 64 個,畫不出來,也很難看出資料的結構。
主成分分析(principal component analysis,PCA)的做法是換一組座標軸:
- 第一主成分:把所有點投影到一條軸上,讓投影後的變異最大的那個方向;
- 第二主成分:和第一主成分垂直的方向裡,變異最大的那個;
- 依此類推,有幾個特徵就有幾個主成分。
變異大表示點在這個方向上散得開,保留了比較多資料之間的差異。只留前面幾個主成分,就能用比較少的數字描述每一筆資料,這叫降維。
這個方法的歷史很長:Pearson 在 1901 年從「找最貼近資料點的直線與平面」的角度提出,Hotelling 在 1933 年發展出現在的代數形式,並使用「主成分」這個名稱。
先從兩個特徵開始,用 numpy 算一次。
逐步看:用 numpy 算主成分
八筆模擬資料:讀書時數與小考分數,兩者高度相關。程式分成四步,注意 values 與解釋變異比例。
主成分與 scikit-learn
剛才看到的幾件事,正好是使用 PCA 要記得的重點:
- 主成分是原本特徵的加權組合,不是挑掉某些特徵。每個主成分都用到所有特徵。
- 主成分之間互相垂直,投影後的資料彼此不相關。
- 方向不分正反:轉 180 度是同一條軸。主成分向量的正負號沒有意義,不同軟體可能相反,解讀時要看正負相對的關係。
- 解釋變異比例:每個主成分占總變異的比例,全部加起來是 1。
- 幾乎沒有相關的資料,轉到哪個方向變異都差不多,第一主成分也解釋不了多少,這時降維的效果有限。
scikit-learn 的寫法:
pca = PCA(n_components=2).fit(X)
scores = pca.transform(X) # 每一筆投影後的座標
pca.explained_variance_ratio_ # 解釋變異比例
pca.components_ # 每一列是一個主成分(特徵的權重)
PCA 會置中,但不會縮放,要標準化得自己先做。n_components 不設時保留所有主成分。接下來用 habits.csv(程式產生的模擬資料,不是任何學校的真實資料)看標準化的差別。
逐步看:標準化前後的差別
四個特徵的生活習慣資料。比較沒有標準化與標準化之後的解釋變異比例,再看前兩個主成分用了哪些特徵。
手寫數字:64 個特徵的資料
接下來用 sklearn.datasets.load_digits()。這不是本課程產生的模擬資料,而是真實的公開資料集:UCI 機器學習資料庫(UCI Machine Learning Repository)的 Optical Recognition of Handwritten Digits,scikit-learn 內建的是其中的測試資料。
- 共 1797 筆,類別是數字 0~9;
- 原本是 32×32 的黑白點陣圖,每 4×4 一格數有幾個黑點,變成 8×8、每格 0~16 的整數;
- 攤平成一列就是 64 個特徵。
每張圖是 64 維空間裡的一個點。它們在 64 維裡是怎麼分布的?PCA 可以把它投影到兩維畫出來看。
逐步看:64 維投影到 2 維
注意兩個地方:PCA.fit 只給 X,沒有給 y;畫圖時才用 y 上色。這份資料每一格的範圍相同(0~16),所以這裡沒有先標準化。
逐步看:留幾個主成分才夠
累加解釋變異比例,看留幾個主成分能保留多少變異;再把第一張圖壓縮後還原,用眼睛看差別。
PCA 不看標籤
PCA 是非監督式的:它只管變異,不知道你要預測什麼。這帶來一個常被忽略的問題:變異最大的方向,不一定對預測有用。 丟掉變異小的主成分時,可能剛好把最有用的資訊丟掉。下面的範例故意做出這種情況。
逐步看:變異大不等於有用
兩個特徵的模擬資料:第 1 個特徵變異很大但是純雜訊,第 2 個變異很小卻決定類別。分別用原始特徵、只留第一主成分、只留第二主成分來分類。
把 PCA 當前處理時
PCA 也常放在模型前面,減少特徵數量。這時候要照第 8 課與第 14 課的規矩:只用訓練資料 fit,測試資料用同一個 PCA 轉換,否則就是資料洩漏。放進第 16 課的 Pipeline 最不容易出錯,交叉驗證時每一折也會自動重新 fit:
model = make_pipeline(StandardScaler(), PCA(n_components=20), LogisticRegression(max_iter=2000))
model.fit(X_train, y_train)
model.score(X_test, y_test)
留幾個主成分是超參數,要用交叉驗證比較,不要用測試資料挑,也不要只看解釋變異比例決定。
另外,主成分是所有特徵的加權組合,模型的係數因此不容易對應回原本的特徵。需要解釋模型時,這是降維的代價。
實作:寫一個 explained_ratio 函數
完成 explained_ratio(X):回傳每個主成分的解釋變異比例,由大到小。步驟:置中、算共變異數矩陣、算特徵值、由大到小排、除以總和。
np.linalg.eigvalsh(cov) 只回傳特徵值,由小到大排,這裡用它就夠了。
小測驗:PCA
下面程式會印出什麼?
import numpy as np
values = np.array([6.0, 3.0, 1.0])
ratio = values / values.sum()
print(np.cumsum(ratio).round(1).tolist())
第一主成分是什麼樣的方向?
PCA().fit(df) 的 df 有「年收入(元)」和「年齡」兩欄,沒有先標準化,最可能得到什麼?
某個分類問題,第一主成分解釋 95% 的變異,你只留它來訓練模型,測試準確率卻接近亂猜。最可能的原因是?
把 PCA 當作模型的前處理,下面哪一個做法正確?
兩台電腦算出的第一主成分,一個是 [0.6, 0.8],另一個是 [-0.6, -0.8],代表什麼?
重點整理
- 主成分是投影後變異最大的方向,彼此垂直;數學上是共變異數矩陣的特徵向量,特徵值就是投影後的變異。
- 解釋變異比例告訴你每個主成分保留多少變異,累加後可以幫助決定留幾個,但不能單獨決定。
PCA只置中、不縮放:特徵單位不同時通常要先標準化,否則數字範圍大的特徵會主導結果。- 投影到兩維適合用來看資料的結構,但可能丟掉大部分變異(手寫數字只留 28.5%)。
- PCA 不看標籤:變異大不等於對預測有用。當前處理時只用訓練資料 fit,留幾個主成分用交叉驗證比較。
- 下一課開始進入神經網路,從只有一個神經元的感知器開始。