All Revision Notes

COM6005 Statistical Modeling · Module 7

Principal Component Analysis (PCA)

COM6005 · Module 7 · Source: Lecture_PCA_L02_Complete.pdf (15 slides)

Contents · 7 sections

🎯 本講重點 (Learning outcomes)

  • 分清 Feature selection(揀部分 predictors)同 Feature extraction(transform data,例如 PCA)。
  • 明白 PCA 係將 data transform 去新嘅 coordinates (axes):PC1 variance 最大,PC2 次之……而且 axes 互相 perpendicular。
  • 用 swarm of fireflies 嘅比喻理解 PC1、PC2。
  • 知道 PCA 數學上係搵 covariance matrix 嘅 eigenvalues 同 eigenvectors,以及佢哋喺 sklearn 對應邊個 attribute。
  • 明白點解 PCA 之前要 Standardization,識用 StandardScaler。
  • PCA + Regression:regress Y on 頭 k 個 PCs,用 CV error 揀 k。

⭐ 考試必溫 Top-5

  1. Feature selection = dump 一啲、keep 一啲 predictors;Feature extraction = transform data(例:用 internal score 代替 midterm + assignment + final)。PCA 屬 extraction。
  2. PC1 = maximum variance 方向(longest stretch);PC2 要 90° perpendicular to PC1,capture 下一個最大 variance。
  3. PCA = 搵 covariance matrix 嘅 eigenvalues(importance → explained_variance_ratio_)同 eigenvectors(transform data → components_)。
  4. PCA 之前要 standardize(mean 0、SD 1),否則大 scale 嘅 feature(例如 income in dollars)會 dominate PC1。
  5. PCA regression:對 k=1,2,3,…k=1,2,3,\dots 計 CV error,揀 CV error 最細嘅 k。

1. Feature extraction vs Feature selection(slide 2)

當 p 好大,好難將所有 predictors 都放入 regression model(Module 6:p = 20 已經有超過一百萬個 subsets)。有兩類處理方法:

⭐ 定義比較
Feature selectionFeature extraction
做法選擇 predictors:some are dumped, some are keptTransform data,砌出新 variables
例子Best subset、forward / backward selection(Module 6)用「internal score」代替 midterm + assignment + final 去 predict public exam score
方法CpC_p、AIC、BIC、CVPrincipal Component Analysis (PCA)
結果嘅 variables原本嘅 predictors(易解釋)原本 predictors 嘅 combinations(較難解釋)
💡 直覺:internal score 例子

midterm、assignment、final 三科分數通常高度相關(勤力嘅學生三樣都高)。與其三個都放入 model,不如合成一個 internal score,例如 0.3×midterm+0.2×assignment+0.5×final0.3\times\text{midterm}+0.2\times\text{assignment}+0.5\times\text{final}。三個 variables 變一個,大部分資訊都保留。PCA 做嘅事類似,只不過 weights 由 data 自動決定,目標係保留最多 variance。

2. PCA 嘅概念(slides 3–5)

⭐ Concepts(slide 3)
  • PCA 係一個簡化複雜 data 嘅工具:搵出 data spread out the most(highest variance) 嘅方向。
  • PCA = 將原本 data transform 去新 coordinates (axes)。
  • 新嘅第 1 條 axis 最重要,第 2 條相對冇咁重要(in the sense of variance),第 3 條又冇第 2 條咁重要,如此類推。
  • 新 axes 之間互相 perpendicular。

2.1 Swarm of Fireflies 比喻(slides 4–5)

想像一間房入面有一群螢火蟲,佢哋唔係亂飛,而係形成一個長形、傾斜、雪茄形嘅雲。每隻螢火蟲嘅位置由三個 coordinates 決定:Width (X)、Depth (Y)、Height (Z),即係 3D data。

想將 3D 簡化做較低 dimension 時,唔應該直接掉咗 Height (Z) 或者 Depth (Y),因為雪茄係斜放嘅,三個方向都有資訊。PCA 叫你轉換角度,喺房入面畫新嘅線。

⭐ How PCA works(slide 5)
  1. PC1:The Longest Stretch(largest variance)。沿住螢火蟲雲最長嘅部分畫一條直線,呢條線代表 direction of maximum variance(data points 最分散嘅方向)。
  2. PC2:The Next Best Direction。睇返剩低嘅 spread,畫第二條線,佢必須同 PC1 成 90 度(perpendicular),capture 下一個最大嘅 spread。

PC3 就係同 PC1、PC2 都垂直嘅方向,capture 剩低最少嘅 spread。如果雪茄好幼長,PC1 已經 capture 大部分 variance,只用 PC1(1D)或者 PC1 + PC2(2D)都唔會失去太多資訊,呢個就係 dimension reduction。

💡 數字例子(2D)

兩個 standardized variables x1,x2x_1,x_2,covariance matrix:

Σ=(2112)\Sigma=\begin{pmatrix}2&1\\1&2\end{pmatrix}

Eigenvalues 係 λ1=3\lambda_1=3、λ2=1\lambda_2=1,對應 eigenvectors v1=12(1,1)v_1=\tfrac{1}{\sqrt2}(1,1)、v2=12(1,−1)v_2=\tfrac{1}{\sqrt2}(1,-1)。

  • PC1 方向係 (1,1)(1,1)(兩個 variables 一齊升嘅斜線),PC2 係 (1,−1)(1,-1),兩者垂直(dot product =1−1=0=1-1=0)。
  • PC1 嘅 explained variance ratio =33+1=75%=\frac{3}{3+1}=75\%;PC2 =25%=25\%。
  • 一個 data point (x1,x2)=(2,1)(x_1,x_2)=(2,1) 嘅 PC1 score =12(2+1)≈2.12=\tfrac{1}{\sqrt2}(2+1)\approx2.12,PC2 score =12(2−1)≈0.71=\tfrac{1}{\sqrt2}(2-1)\approx0.71。

3. PCA in Python(slides 6–7)

3.1 Example code:Fireflies(slide 6)

import numpy as np
import pandas as pd
from sklearn.decomposition import PCA

swarm_df = pd.read_csv('fireflies.csv')     # 3 columns:X, Y, Z

pca = PCA(n_components=3)                    # 保留 3 個 PCs
fireflies_3d = pca.fit_transform(swarm_df)   # fit + 轉去新 coordinates

print(swarm_df.head())
print(f"PC1 (Longest stretch): {pca.explained_variance_ratio_[0]*100:.1f}%")
print(f"PC2: {pca.explained_variance_ratio_[1]*100:.1f}%")
print(f"PC3: {pca.explained_variance_ratio_[2]*100:.1f}%")
print(f"Total kept in 3D: {sum(pca.explained_variance_ratio_)*100:.1f}%")   # 3 個 PCs 全保留 = 100%

pca.components_[0]            # PC1 嘅 loadings / weights(eigenvector)
pca.explained_variance_[0]    # PC1 嘅 variance magnitude(eigenvalue)
📝 Slide 6 圖表

Slide 6 有 import plotly.graph_objects as go,但 extracted code 冇畫圖部分;fireflies.csv 亦唔喺 course folder。以下係一個可以自己 run 嘅版本,用 random data 模擬雪茄形雲。

import numpy as np
import pandas as pd
from sklearn.decomposition import PCA

rng = np.random.default_rng(0)
t = rng.normal(0, 5, 500)                       # 沿住雪茄長軸嘅位置(spread 大)
swarm_df = pd.DataFrame({
    'X': t + rng.normal(0, 0.5, 500),
    'Y': 0.8 * t + rng.normal(0, 0.5, 500),     # 斜放:Y、Z 都跟住 t 變
    'Z': 0.5 * t + rng.normal(0, 0.5, 500),
})
pca = PCA(n_components=3).fit(swarm_df)
print(pca.explained_variance_ratio_.round(3))   # PC1 大約 99%,PC2、PC3 好細
print(pca.components_[0].round(2))              # PC1 方向,大約同 (1, 0.8, 0.5) 成比例

3.2 數學:eigenvalues 同 eigenvectors(slide 7)

⭐ PCA 嘅數學同 sklearn 對應
  • PCA 數學上即係搵 covariance matrix 嘅 eigenvalues 同 eigenvectors。
  • Eigenvalues 表示 transformed data 嘅 importance → pca.explained_variance_ratio_
  • Eigenvectors 用嚟 transform 原本 data → pca.components_
  • Python:pca = PCA(n_components=3);pca_result = pca.fit_transform(x)
Attribute內容數學
explained_variance_每個 PC 嘅 varianceEigenvalue λj\lambda_j
explained_variance_ratio_每個 PC 佔 total variance 嘅比例λj/∑iλi\lambda_j/\sum_i\lambda_i
components_每一 row 係一個 PC 嘅 loadings / weightsEigenvector vjv_j
fit_transform(X)每個 observation 喺新 axes 上嘅 scores(X−Xˉ) vj(X-\bar X)\,v_j
⚠️ 細節
  • Slide 7 話 eigenvalues「given by explained_variance_ratio_」。嚴格嚟講,explained_variance_ 先係 eigenvalues 本身,explained_variance_ratio_ 係將佢哋 normalize 成比例(加埋 = 1)。兩者都反映 importance,排序一樣。
  • components_ 嘅 row 係 PC(唔係 column):components_[0] 係 PC1 嘅 weights,長度 = 原本 features 數目。
  • Eigenvectors 互相垂直,正好對應「新 axes are perpendicular」。

4. Standardization(slides 8–10)

⭐ 點解 PCA 前要 Standardize?(slide 9)
  • PCA 搵嘅係 maximize variance 嘅方向。
  • 如果 features 嘅 scale / units 完全唔同(例如 income in dollars、age in years),numerical variance 大嘅 feature 會完全 dominate PC1。
  • Standardizing(centering and scaling)將所有 features 變成 mean = 0、standard deviation = 1,確保每個 feature 公平貢獻。
z=x−xˉsz=\frac{x-\bar x}{s}
💡 數字例子

Income 嘅 SD = 20,000 dollars → variance =4×108=4\times10^8;Age 嘅 SD = 10 years → variance = 100。PCA 會覺得 income 方向 spread 大到不得了,PC1 幾乎就係 income axis,age 嘅資訊被忽略。其實係因為「dollar」呢個單位數值大,唔係 income 真係重要啲。Standardize 之後,兩者 variance 都係 1,PCA 就會睇佢哋之間嘅 correlation 結構。

Python(slide 10):

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()            # get the object
X_scaled = scaler.fit_transform(X)   # X_scaled:每個 column mean = 0、SD = 1

5. 用 PCs 做 regression(slides 11–15)

(Slide 11 同 slide 8 一樣係 section title;slide 12 係棒球員同 MLB pension 嘅插圖,作為 Baseball data 例子嘅引入,唔涉及考試內容。)

⭐ Combining PCA and regression(slide 13)
  1. Regress Y against 頭 k 個 principal components。
  2. 計 cross-validation error。
  3. 對 k=1,2,3,…k=1,2,3,\dots 重複,揀 CV error 最細嘅 k。

sklearn 有現成 functions 做 PCA + Regression。

💡 點解咁做?

PCs 互相 uncorrelated(垂直),所以冇 multicollinearity 問題;而且頭幾個 PCs 已包含大部分 variance,用少數 PCs 代替大量 predictors 可以減少 overfitting。k 呢個「model size」點揀,就用返 Module 6 嘅 CV 諗法:k 太細會 underfit,太大就同用晒所有 predictors 差唔多。

5.1 Example:Hitters(Baseball)data(slides 14–15)

  • ISLP package 嘅 Hitters data:19 predictors(包括 categorical),n=322n=322。
  • Response Y:球員嘅 Salary。
# pip install ISLP
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from ISLP import load_data
from sklearn.decomposition import PCA
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler

HC = load_data('Hitters')
HC = HC.dropna()                           # 刪走有 null 嘅 rows(Salary 有 missing)
X = HC.drop(columns=['Salary'])            # 移除 target column
X = pd.get_dummies(X, drop_first=True)     # categorical → dummy variables(statistics approach)
y = HC['Salary']

scaler = StandardScaler()                  # Standardization
X_scaled = scaler.fit_transform(X)

pca = PCA(n_components=10)
X_pca = pca.fit_transform(X_scaled)
cv_scores = cross_val_score(LinearRegression(), X_pca, y, cv=10,
                            scoring='neg_mean_squared_error')

plt.plot(range(1, len(cv_scores) + 1), -cv_scores, marker='o')
plt.xlabel('Number of Principal Components')
plt.ylabel('Mean Squared Error')
plt.title('Cross-validation result (MSEP)')
plt.show()
Step作用
dropna()Hitters 有部分 Salary 係 missing,刪走嗰啲 rows
get_dummies(drop_first=True)Categorical(League、Division、NewLeague)變 dummy;drop first category 做 baseline,同 Module 4b 嘅 dummy variable 做法一致
StandardScalerPCA 前先 standardize(Section 4)
cross_val_score(..., cv=10)10-fold CV,score 係負 MSE;-cv_scores 變返 MSE
MSEPMean Squared Error of Prediction,即 CV 估計嘅 prediction MSE
⚠️ Slide 14–15 code 要留意嘅地方
  • Slide 14 將結果存做 y_cv_pred,下一行就用 cv_scores,變數名唔一致;跟 slide 15 用 cv_scores 就啱。
  • Slide 15 嘅 cv_scores 係 10 個 folds 嘅 score(全部用 10 個 PCs),所以 x-axis 其實係 fold number,唔係「Number of Principal Components」。要做到 slide 13 嘅「for k = 1, 2, 3, … 揀 k」,要 loop k,每次只用頭 k 個 PCs,再平均 10 個 folds:
msep = []
for k in range(1, 11):
    s = cross_val_score(LinearRegression(), X_pca[:, :k], y, cv=10,
                        scoring='neg_mean_squared_error')
    msep.append(-s.mean())                 # 第 k 個點 = 用 k 個 PCs 嘅 CV MSE
best_k = int(np.argmin(msep)) + 1
plt.plot(range(1, 11), msep, marker='o')
plt.xlabel('Number of Principal Components'); plt.ylabel('CV MSE')
plt.show()

另外,scaler 同 PCA 係喺成個 dataset 上 fit 完先做 CV,test folds 嘅資訊會輕微漏入 preprocessing。更嚴謹嘅做法係將 StandardScaler、PCA、LinearRegression 放入同一個 Pipeline(Module 6 slide 23 嘅諗法)再做 CV。

自我測驗 Self-test

Q1. Feature selection 同 feature extraction 有咩分別?各舉一個例子。

Selection:保留部分原有 predictors,其餘 dump 走,例如 best subset、forward selection。Extraction:transform data 產生新 variables,例如用 internal score 代替 midterm + assignment + final,或者 PCA。

Q2. 用 fireflies 比喻解釋 PC1 同 PC2。

螢火蟲形成斜放嘅雪茄形雲。PC1 係穿過雲最長部分嘅直線,即 maximum variance 方向。PC2 係同 PC1 成 90° 嘅線,capture 剩低 spread 入面最大嘅方向。

Q3. 點解唔可以直接掉咗 Height (Z) 去將 3D 變 2D?

雪茄形雲係斜放,Z 同 X、Y 都有關,資訊分佈喺三個原本 axes 上。直接掉 Z 會失去部分 spread。PCA 換一套新 axes,令頭幾條 axes 集中最多 variance,再掉最唔重要嗰條。

Q4. 3 個 PCs 嘅 eigenvalues 係 6、3、1。計 explained variance ratio。用頭 2 個 PCs 保留幾多 variance?

Total = 10。PC1 = 60%,PC2 = 30%,PC3 = 10%。頭 2 個 PCs 保留 90%。

Q5. pca.components_、pca.explained_variance_、pca.explained_variance_ratio_ 分別係咩?

components_:eigenvectors(每 row 係一個 PC 嘅 loadings / weights),用嚟 transform data。explained_variance_:eigenvalues,每個 PC 嘅 variance。explained_variance_ratio_:eigenvalue ÷ 總和,即每個 PC 嘅 importance 比例。

Q6. 一個 dataset 有 income(dollars)同 age(years),唔 standardize 直接做 PCA 會點?

Income 嘅數值 variance 遠大於 age,PC1 幾乎完全由 income 決定,age 被忽略。呢個係單位造成,唔係真正重要性。要先 standardize(mean 0、SD 1)。

Q7. 一個 feature 嘅 mean = 50、SD = 8。x = 66 standardize 之後係幾多?

z=(66−50)/8=2z=(66-50)/8=2。

Q8. 描述 PCA + regression 點揀 k。

對 k=1,2,3,…k=1,2,3,\dots:用頭 k 個 PCs 做 regression,計 CV error(例如 10-fold CV MSE)。揀 CV error 最細嘅 k。

Q9. Slide 15 嘅 plot x-axis 寫「Number of Principal Components」,但 code 有咩問題?點改?

cross_val_score(..., X_pca, cv=10) 回傳嘅係 10 個 folds 嘅 score(每個都用 10 個 PCs),所以 x-axis 其實係 fold number。應該 loop k = 1…10,每次用 X_pca[:, :k],取 -scores.mean() 做該 k 嘅 MSEP。

Q10. PCs 互相垂直對 regression 有咩好處?

PCs 之間 uncorrelated,所以冇 multicollinearity;而且頭幾個 PCs 已包含大部分 variance,可以用較少 variables 建 model,減低 overfitting 風險。

一頁總結 Cheat sheet

⭐ 考前速記
  • Large p → Feature selection(dump / keep predictors)或 Feature extraction(transform,例:internal score)。PCA = extraction。
  • PCA:transform 去新 axes;PC1 = maximum variance(longest stretch),PC2 ⊥\perp PC1 = next largest variance,PC3 …;axes 互相 perpendicular。
  • Fireflies:3D 雪茄形雲,唔好直接掉 Z / Y,要換角度畫新線。
  • 數學:covariance matrix 嘅 eigenvalues(importance → explained_variance_ratio_ =λj/∑λi=\lambda_j/\sum\lambda_i;explained_variance_ =λj=\lambda_j)同 eigenvectors(transform → components_,每 row 一個 PC)。
  • PCA(n_components=k).fit_transform(X) → PC scores。
  • 先 standardize:z=(x−xˉ)/sz=(x-\bar x)/s,mean 0、SD 1;StandardScaler().fit_transform(X)。否則大 scale feature dominate PC1。
  • PCA regression:regress Y on 頭 k 個 PCs → CV error → 試 k=1,2,3,…k=1,2,3,\dots → 揀 CV error 最細。
  • Hitters:19 predictors、n=322n=322、Y = Salary;dropna → get_dummies(drop_first=True) → StandardScaler → PCA → cross_val_score(cv=10, scoring='neg_mean_squared_error')。