① 照節次讀:每節先讀說明,動手玩互動元件——先預測結果,再按按鈕驗證。 ② 對照講義:每個 §徽章都標了 ISLP 節號與講義頁碼,細節與完整推導請回講義與課本。 ③ 每節做 quiz:答錯就回到該節重讀,不要往下跳;錯的選項也寫了「錯在哪」。 ④ 最後翻關鍵詞彙卡自測術語,並用 REF 總覽當速查表。標「ESL 進階」的節是課堂沒細講的延伸,第一輪可略過。
前面十幾週,每一章都有一個 y。有了 y,一切都好辦: 切出測試集、算 MSE 或錯誤率、用交叉驗證挑超參數——「哪個模型比較好」有客觀答案。
這一章把 y 拿掉。手上只剩 X₁, X₂, …, Xp,問題變成: 這批資料本身有什麼結構?能不能用兩個座標軸就把 6830 個基因的樣本畫在紙上? 這 64 個細胞株是不是可以分成幾個自然的群?
麻煩的地方是:沒有 y,就沒有對錯。你沒辦法交叉驗證一個分群結果, 因為沒有「正確的群」可以比。所以非監督式學習比監督式主觀得多, 它的定位通常是探索式資料分析(exploratory data analysis)—— 產出的是值得進一步檢驗的假設,不是結論。
PCA 與分群都在「化簡資料」,但化簡的方式不同,這個分工先記住:
| 產出什麼 | 問的問題 | 要先決定什麼 | 本頁的節 | |
|---|---|---|---|---|
| PCA | 連續的低維座標(每筆資料一組新座標) | 哪幾個方向的變異最大? | 留幾個主成分 M | P01–P06 |
| 分群 | 離散的群標籤(每筆資料一個編號) | 哪些觀測值彼此相似? | K,或樹要切在哪 | P07–P09 |
為什麼非監督式學習「無法用交叉驗證來驗證結果」?
先想一個很現實的問題:p = 10 個變數,兩兩畫散佈圖有 45 張,你看不完; 而且每一張都只含一小部分資訊。有沒有辦法用兩張圖就把大部分結構看完?
PCA 的答案是:不要看原始的座標軸,去找資料變異最大的那個方向。 第一主成分是所有標準化線性組合裡樣本變異數最大的那一個:
$$Z_1 = \phi_{11} X_1 + \phi_{21} X_2 + \cdots + \phi_{p1} X_p, \qquad \text{s.t.} \sum_{j=1}^{p} \phi_{j1}^2 = 1$$那些係數 $\phi_{j1}$ 叫做負荷量(loading),合起來是負荷向量 $\phi_1$。 為什麼要限制平方和等於 1?因為不限制的話,把係數全部乘 100 變異數就變 10000 倍, 「最大」就沒有意義了。把資料先置中(每欄減掉平均)之後,要解的是
$$\max_{\phi_{11},\dots,\phi_{p1}} \left\{ \frac{1}{n}\sum_{i=1}^{n} \Big(\sum_{j=1}^{p} \phi_{j1} x_{ij}\Big)^2 \right\} \quad \text{s.t.} \quad \sum_{j=1}^{p} \phi_{j1}^2 = 1$$括號裡的東西就是第 i 筆資料投影到 $\phi_1$ 上的值,叫做得分(score) $z_{i1}$。因為資料置中過,得分的平均是 0,所以上式就是得分的樣本變異數。 負荷向量是新座標軸的方向,得分是每筆資料在新座標軸上的位置——這兩個詞不要混。
下面這個元件就是把上面那個最佳化問題「用手轉一遍」:拖動角度,看投影後的變異數怎麼變。
找完第一主成分之後,第二主成分是所有跟 $Z_1$ 不相關的線性組合裡變異最大的那一個。 「與 $Z_1$ 不相關」這個條件等價於「方向 $\phi_2$ 與 $\phi_1$ 垂直」, 所以主成分就是一組互相垂直的新座標軸,總共最多有 $\min(n-1,\,p)$ 個。
PCA() → 取出負荷量array([[ 0.53589947, 0.58318363, 0.27819087, 0.54343209],
[-0.41818087, -0.1879856 , 0.87280619, 0.16731864],
[-0.34123273, -0.26814843, -0.37801579, 0.81777791],
[-0.6492278 , 0.74340748, -0.13387773, -0.08902432]])components_ 的每一列是一個負荷向量。第一列 [0.536, 0.583, 0.278, 0.543] 在 Murder/Assault/Rape 上幾乎一樣重、UrbanPop 明顯較輕——所以 PC1 大致就是「整體暴力犯罪率」。第二列幾乎全押在 UrbanPop(0.873),那是「都市化程度」。注意 PCA() 預設只置中、不縮放,所以標準化要自己先做(第 19 格)。
Ch12-unsup-lab-zh.ipynb · 儲存格 19、21、27、29
USArrests 的 pcaUS.components_ 是 4×4、scores 是 50×4。哪個描述正確?
算完 PCA 之後,最常畫的圖是 biplot(雙標圖): 同一張圖上同時放得分(點)與負荷量(箭頭)。 ISLP 圖 12.1 就是 USArrests 的 biplot——50 個州當點,4 個變數當箭頭。
讀法有三條,記住就夠用:
下面這個 biplot 用的是課本的資料。真正要玩的是那個 toggle: 按下「未標準化」,整張圖會變形。
Assault 是「每十萬人的件數」,數字本來就大得多。不標準化的話 PC1 幾乎等於 Assault 自己。
scikit-learn 沒有內建 biplot,所以 lab 用 ax.scatter 畫得分、ax.arrow 畫負荷量,再用 s_ = 2 把箭頭放長一點(否則負荷量都在 ±1 以內,跟得分的尺度差太多,會縮成一小坨)。箭頭長度只是為了看得清楚,可以自己乘上任何常數。
第 2 行與第 3 行把第二個主成分的得分與負荷量同時乘上 −1。同時翻兩邊,圖只是上下鏡射,任何結論都不變——這正是 P05 要講的符號不唯一。本頁的 biplot 直接用儲存格 29 那組負荷量,跟課本表 12.1 的數字逐位相同。
Ch12-unsup-lab-zh.ipynb · 儲存格 31、33
在 USArrests 的 biplot 上,Murder 與 UrbanPop 兩支箭頭夾角接近 90°。這代表什麼?
到目前為止主成分的定義是「變異最大的方向」。現在換一個完全不同的角度看它, 結論會一模一樣——這件事很值得多花五分鐘。
第一主成分的負荷向量所定義的那條直線,是 p 維空間中離所有資料點平均平方距離最近 的那條線。前兩個主成分張出的平面,是離所有資料點最近的那個平面(ISLP 圖 12.2 左)。 前 M 個主成分張出的是最近的 M 維超平面。
把「最近」寫成最佳化問題就清楚了。置中後的資料矩陣 $\mathbf{X}$, 在所有 $x_{ij} \approx \sum_{m=1}^{M} a_{im} b_{jm}$ 這種形式的近似裡, 找殘差平方和最小的那一組:
$$\min_{A \in \mathbb{R}^{n\times M},\, B \in \mathbb{R}^{p\times M}} \left\{ \sum_{j=1}^{p} \sum_{i=1}^{n} \Big( x_{ij} - \sum_{m=1}^{M} a_{im} b_{jm} \Big)^2 \right\}$$解出來的 $\hat a_{im}$ 就是得分 $z_{im}$、$\hat b_{jm}$ 就是負荷量 $\phi_{jm}$。 也就是說:「變異最大」與「近似誤差最小」是同一個問題的兩種寫法。 ISLP 式 12.11 把這件事寫得很漂亮:
$$\underbrace{\sum_{j=1}^{p} \frac{1}{n} \sum_{i=1}^{n} x_{ij}^2}_{\text{資料的總變異}} = \underbrace{\sum_{m=1}^{M} \frac{1}{n} \sum_{i=1}^{n} z_{im}^2}_{\text{前 } M \text{ 個主成分的變異}} + \underbrace{\frac{1}{n} \sum_{j=1}^{p} \sum_{i=1}^{n} \Big( x_{ij} - \sum_{m=1}^{M} z_{im}\phi_{jm} \Big)^2}_{M \text{ 維近似的 MSE}}$$左邊是固定的,所以中間變大就等於右邊變小。這也是為什麼下一節的 PVE 可以直接讀成「近似的 $R^2$」。
numpy.linalg.svd 比自己算 $\mathbf{X}^{\mathsf T}\mathbf{X}$ 的特徵向量穩定得多,
而且下一節的矩陣補全就是靠它一步步逼近的。
components_ 的關係array([[-0.53589947, -0.58318363, -0.27819087, -0.54343209],
[-0.41818087, -0.1879856 , 0.87280619, 0.16731864],
[ 0.34123273, 0.26814843, 0.37801579, -0.81777791],
[ 0.6492278 , -0.74340748, 0.13387773, 0.08902432]])V 的每一列就是負荷向量,只差符號。跟上一節儲存格 29 的 components_ 比:第 1、3、4 列整列變號,第 2 列一模一樣。
lab 儲存格 51 又印了一次 components_,但那時第 33 格已經把 PC2 翻號了,所以第 2 列跟儲存格 29 不同——不是印錯,是同一個物件被就地改過。儲存格 53 與 54 也是同一件事:U * D 跟 scores 差整組符號。
Ch12-unsup-lab-zh.ipynb · 儲存格 48、50、51
因為它們通往不同的用途。
「變異最大」的說法讓你解讀主成分:負荷量告訴你這個方向由哪些變數組成,PC1 是「整體犯罪率」、PC2 是「都市化」這種話就是從這裡讀出來的。
「近似最好」的說法讓你把 PCA 當工具用。既然前 M 個主成分是最佳的秩 M 近似,那它就可以拿來壓縮(存 M 個得分而不是 p 個原值)、去雜訊(NCI60 那種資料常先取前幾個主成分再分群)、以及最直接的——填補缺失值。下一節 P06 的矩陣補全整個建立在這個解釋上,從「變異最大」那邊完全看不出來要怎麼做。
式 12.11 說「總變異 = 前 M 個主成分的變異 + M 維近似的 MSE」。由此可以推出什麼?
壓到 2 維畫出來很方便,但丟掉了多少東西?這個問題的答案叫做 解釋變異比例(proportion of variance explained, PVE)。
置中後資料的總變異是 $\sum_{j=1}^{p} \frac{1}{n}\sum_{i=1}^{n} x_{ij}^2$, 第 m 個主成分的變異是 $\frac{1}{n}\sum_{i=1}^{n} z_{im}^2$,所以
$$\mathrm{PVE}_m = \frac{\sum_{i=1}^{n} z_{im}^2}{\sum_{j=1}^{p}\sum_{i=1}^{n} x_{ij}^2} = 1 - \frac{\mathrm{RSS}_M}{\mathrm{TSS}}\Big|_{M=m} - \text{(前 } m-1 \text{ 個的部分)}$$所有 $\min(n-1,p)$ 個 PVE 加起來剛好是 1。累積 PVE 就是「前 M 個主成分留住了幾成」, 由上一節的式 12.11,它同時也是「用前 M 個主成分近似資料矩陣」的 $R^2$。
explained_variance_ratio_array([0.62006039, 0.24744129, 0.0891408 , 0.04335752])
三格印的是同一件事的三種寫法:scores.std(0, ddof=1) 是得分的標準差、explained_variance_ 是它的平方、explained_variance_ratio_ 是再除以總和。第一個 0.62006 就是課本說的「第一主成分解釋了 62.0% 的變異」。lab 儲存格 41/43 用 cumsum() 畫出累積版,就是課本圖 12.3 右。
Ch12-unsup-lab-zh.ipynb · 儲存格 35、37、39
USArrests 的四個 PVE 是 0.620、0.247、0.089、0.043。如果我只留前兩個主成分,那 50×4 的資料矩陣被近似得多好?
這一節只有兩件事,但兩件都會在實務上咬人:做 PCA 之前要不要標準化, 以及算出來的符號可以信到什麼程度。
PCA 找的是「變異最大」的方向。問題是變異數跟單位有關: USArrests 的 Assault 是「每十萬人的件數」,變異數 6945;Murder 也是每十萬人,但只有 18.97。 不標準化的話,第一主成分幾乎整支押在 Assault 上(負荷量 0.995), 其他三個變數等於沒參與。回到 P02 那個 biplot 元件把 toggle 切到「未標準化」就看得到。
更糟的是這個結果是任意的。如果 Assault 改成「每一百人的件數」, 數值全部除以 1000,變異數變成原來的百萬分之一,它就從主宰者變成陪襯。 沒有人希望分析結論取決於別人當年怎麼選單位,所以慣例是先標準化。
StandardScaler(),但也在旁邊註明
「這裡其實可以合理主張不要縮放」——這是判斷題,不是規則題。
| 情況 | 要不要標準化 | 為什麼 |
|---|---|---|
| 變數單位不同(USArrests、房價資料) | 要 | 否則 PC1 只是「數字最大的那個變數」 |
| 同單位但量級差很多(收入 vs 年齡) | 要 | 同上,量級差就是單位差的變形 |
| 同單位、尺度差異有意義(基因表現、像素) | 看情況 | 縮放會把真實的變異差異抹掉 |
| 已經是比例或分數(0–1 之間) | 通常不用 | 尺度已經可比 |
| 變數是 0/1 指示變數 | 小心 | 標準化會放大罕見類別,考慮別的方法 |
負荷向量描述的是一個方向。把 $\phi_1$ 整支乘上 $-1$, 它指的還是同一條直線,只是箭頭朝反邊;投影後的變異數 $\mathrm{Var}(-Z) = \mathrm{Var}(Z)$ 也沒變。所以最佳化問題有兩個一樣好的解,套件挑哪一個是實作細節。
關鍵在於要一起翻:近似式用的是乘積 $z_{im}\phi_{jm}$,
兩個都乘 $-1$ 乘積不變,重建出來的資料一模一樣。lab 儲存格 33 就是這樣做的
(scores[:,1] *= -1 與 components_[1] *= -1 成對出現)。
Murder 18.970465 Assault 6945.165714 UrbanPop 209.518776 Rape 87.729159 dtype: float64
6945 對 18.97,差了 366 倍。看到這種數字就知道非標準化不行了。
注意載入方式是 get_rdataset('USArrests').data(statsmodels 去抓 R 的資料集),不是 load_data()——USArrests 不在 ISLP 套件裡。資料的索引是州名,所以 mean()/var() 是逐欄算的。
Ch12-unsup-lab-zh.ipynb · 儲存格 11、15、17
因為最佳化問題只約束了「方向」與「長度」,沒有約束「朝哪一邊」。$\phi_1$ 與 $-\phi_1$ 定義同一條直線;投影後 $Z_1$ 與 $-Z_1$ 的變異數相同,所以兩者都是最佳解,套件回傳哪一個取決於底層的 LAPACK 實作。同一份資料用 numpy.linalg.svd 與 sklearn 的 PCA() 跑,就可能拿到整組相反的符號(lab 儲存格 50 與 51 就差在這裡)。
不影響任何實質結論,但會影響你「怎麼說」。如果 PC1 的負荷量全是正的,你會說「PC1 高 = 犯罪率高」;符號翻掉之後,同一個主成分要說成「PC1 高 = 犯罪率低」。得分也一起翻,所以哪些州靠在一起、哪些州離得遠——完全一樣。
實務上的兩個建議:(1)自己定一個約定並寫在報告裡,例如「讓負荷量總和為正」或「讓某個指標變數的負荷量為正」;(2)比較兩次分析的結果時,先對齊符號再比,不然會誤以為結果不穩定。
因為 PCA 的目標函數是變異數,而變異數的大小跟單位有關。USArrests 的 Assault 變異數 6945、Murder 只有 18.97,不標準化的話 PC1 的負荷量在 Assault 上是 0.995、在 Murder 上是 0.042——第一主成分退化成「Assault 換個名字」,PCA 什麼都沒做。
更關鍵的是:這個結果會隨著單位改變。把 Assault 的單位從「每十萬人」改成「每百人」,它的變異數變成百萬分之一,立刻讓位給 UrbanPop。結論不該取決於資料當初是用什麼單位記錄的,所以標準化在這裡不是技巧,是為了讓答案有意義。
不該標準化的情形:變數同單位、而且變異數的差異本身是你想保留的資訊。基因表現量、影像的像素值、同一種感測器的多個通道都屬於這一類。還有一種情形是資料已經是比例(每欄都在 0 到 1 之間),再標準化沒什麼好處。判斷的準則很簡單:問自己「如果某一欄乘上 1000,我希望結論改變嗎?」不希望就標準化。
同一份 USArrests,A 同學算出 PC1 的負荷量是 [0.54, 0.58, 0.28, 0.54],B 同學算出 [-0.54, -0.58, -0.28, -0.54]。發生了什麼事?
手上的資料矩陣有缺失值,怎麼辦?兩個常見的做法都不太好: 整列刪掉太浪費(也不現實——缺一格就丟掉一整個州), 用該欄的平均填補則完全沒有用到變數之間的相關。
P03 說過前 M 個主成分是資料矩陣的最佳秩 M 近似。 那反過來想:如果 $x_{ij} \approx \sum_m z_{im}\phi_{jm}$, 那缺掉的那一格也可以用這個式子算出來。 這就是矩陣補全(matrix completion)。
問題是要算主成分得先有完整的矩陣,要有完整的矩陣得先補值——雞生蛋蛋生雞。 ISLP 的解法是輪流做(演算法 12.1):先用欄平均粗填, 算主成分、用低秩近似覆蓋缺失格、再算主成分…直到目標函數不再下降。 只在觀測到的格子上算誤差:
$$\min_{A,B} \sum_{(i,j)\in\mathcal{O}} \Big( x_{ij} - \sum_{m=1}^{M} a_{im} b_{jm} \Big)^2$$$\mathcal{O}$ 是觀測到的位置集合。跟 P03 的式子唯一的差別就是求和範圍—— 但這一改就沒有封閉解了,只能迭代。
np.random.seed(15) 不同,所以相關係數不會剛好是 0.7114。lab 的實跑數字在下面的 .deck-extra 卡裡。Iteration: 1, MSS:0.395, Rel.Err 5.99e-01 Iteration: 2, MSS:0.382, Rel.Err 1.33e-02 Iteration: 3, MSS:0.381, Rel.Err 1.44e-03 Iteration: 4, MSS:0.381, Rel.Err 1.79e-04 Iteration: 5, MSS:0.381, Rel.Err 2.58e-05 Iteration: 6, MSS:0.381, Rel.Err 4.22e-06 Iteration: 7, MSS:0.381, Rel.Err 7.65e-07 Iteration: 8, MSS:0.381, Rel.Err 1.48e-07 Iteration: 9, MSS:0.381, Rel.Err 2.95e-08
讀法:low_rank(Xhat, M=1) 是步驟 2(a)(用 SVD 取秩一近似)、Xhat[ismiss] = Xapp[ismiss] 是 2(b)(只覆蓋缺失格)、mss 是 2(c) 的目標函數。
MSS 從 0.395 掉到 0.381 就幾乎不動了,第 8 輪相對誤差跌破 1e-7 收工。注意分母用的是 mss0 而不是 mss——這樣收斂輪數就不會因為把整個 X 乘上一個常數而改變。
挖法也有講究:先隨機選 20 個州、每州再隨機挑一個變數,所以每一列至少留三個觀測值。整列都空的話,什麼方法都補不出來。
Ch12-unsup-lab-zh.ipynb · 儲存格 56、58、60、62、64
np.float64(0.7113567434297361)
20 個補值與真值的相關係數 0.711。lab 儲存格 68–69 換成 fancyimpute 的 SoftImpute(max_rank=1) 再跑一次,相關係數幾乎一樣——說明這支三十行的迴圈沒有偷工減料,而真的要上線時直接用套件(它有更好的收斂控制與正則化)就好。
Ch12-unsup-lab-zh.ipynb · 儲存格 66、68、69
演算法 12.1 的步驟 2(b) 只把缺失的格子換成低秩近似值,觀測到的格子保持原值。為什麼不乾脆全部換掉?
換一種化簡方式:不找低維座標,直接把資料分成 K 群。 好的分群是「群內盡量像」,寫成式子就是把群內變異的總和最小化:
$$\min_{C_1,\dots,C_K} \left\{ \sum_{k=1}^{K} W(C_k) \right\}, \qquad W(C_k) = \frac{1}{|C_k|} \sum_{i,i' \in C_k} \sum_{j=1}^{p} (x_{ij} - x_{i'j})^2$$$W(C_k)$ 是第 k 群內所有兩點之間的平方歐氏距離總和除以群的大小。 看起來要算 $|C_k|^2$ 個距離,但 ISLP 式 12.18 給了一個很好用的恆等式:
$$\frac{1}{|C_k|} \sum_{i,i' \in C_k} \sum_{j=1}^{p} (x_{ij} - x_{i'j})^2 = 2 \sum_{i \in C_k} \sum_{j=1}^{p} (x_{ij} - \bar x_{kj})^2$$右邊只需要算每個點到群心的距離。這個恆等式不只省算力, 它直接告訴你演算法該長什麼樣:
KMeans()array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1], dtype=int32)資料是刻意造的:前 25 筆的平均被平移過,所以真的有兩群。labels_ 幾乎完美地把前 25 與後 25 分開——但注意群的編號是任意的,0 與 1 交換不代表結果不同。這也是為什麼比較兩種分群結果要用 pd.crosstab,不能直接比對標籤。
Ch12-unsup-lab-zh.ipynb · 儲存格 103、105、107
(76.85131986999251, 75.06261242745386)
inertia_ 就是群內平方和(式 12.17 要最小化的那個)。n_init=1 得到 76.85、n_init=20 得到 75.06——同一份資料、同一個 K、同一個 random_state,差別只在試了幾組初始值。
76.85 是一個局部極小,不是錯誤,程式不會警告你。所以 lab 的建議是:n_init 設 20 或 50,並且一定要設 random_state 讓結果可重現。
Ch12-unsup-lab-zh.ipynb · 儲存格 113
因為演算法 12.2 的第 1 步是隨機指派。之後的每一步都只保證目標函數下降,不保證下降到全域最小——它會滑進離初始位置最近的那個「盆地」就停住。把 n 筆資料分成 K 群大約有 $K^n$ 種方式,要真的找到全域最小得全部列舉,這不可能,所以我們接受局部極小。
ISLP 圖 12.9 把同一份資料跑六次,得到三個不同的局部極小:目標函數 235.8(四次)、320.9、310.9。其中 235.8 明顯把三群分得最開。重點是:如果你只跑一次,剛好抽到 320.9 那組初值,程式不會告訴你有問題。
標準做法就是多重初始化:跑很多組不同的初值,回報目標函數最小的那一次。scikit-learn 的 n_init 就是這件事(預設 10,lab 建議設 20 或 50)。另外一定要設 random_state——不是為了「挑一個好看的種子」,而是為了讓別人能重現你的數字。上面那張元件的「換初始值」按鈕,按幾次就會看到這個現象。
為什麼 K-means 的群內平方和一定會單調下降,最後一定會停?
K-means 有個明顯的麻煩:你得先決定 K。 階層式分群不必——它一次把 1 到 n 群的所有結果都算出來,畫成一棵樹, 你要幾群就切在對應的高度。
做法(凝聚式,agglomerative,也叫 bottom-up)簡單到不像演算法:
樹狀圖(dendrogram)的讀法有一條鐵律,很多人第一次都會讀錯:
剩下的問題是:兩群之間的相異度怎麼定?這叫做 連結方式(linkage),四種常見的定義在下面那張表。 換 linkage,樹的形狀會整個變——這是這一節最重要的實驗。
select:把 linkage 換成 single,看樹的形狀怎麼垮掉。
| Linkage | 群間相異度的定義 | 樹的形狀 | 評語 |
|---|---|---|---|
| Complete | 兩群之間最大的那個距離 | 平衡、群大小相近 | 最常用;對離群值不算敏感 |
| Average | 所有跨群配對距離的平均 | 平衡 | 最常用;統計上性質較好 |
| Single | 兩群之間最小的那個距離 | 鏈狀、拖尾 | 容易產生一大群 + 一堆孤兒,少用 |
| Centroid | 兩群形心之間的距離 | 可能出現反轉 | 基因體學常用,但反轉讓樹難以解讀 |
前面一直用歐氏距離。但有時候你在意的是輪廓的形狀而不是高低: 兩位顧客一個買很多、一個買很少,但買的品項比例一致—— 歐氏距離很大,相關係數距離($1 - r_{ii'}$)很小。 ISLP 圖 12.15 就是這個對比。哪個對,取決於你的科學問題,沒有預設答案。
AgglomerativeClustering 與 cut_treearray([[0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 2, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 2,
0, 2, 2, 3, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, 3,
3, 3, 3, 3, 3, 3]])distance_threshold=0 加 n_clusters=None 是「把整棵樹算完、先不要切」的寫法。scikit-learn 不直接給 scipy 畫圖要的 linkage matrix,所以要用 ISLP.cluster.compute_linkage() 轉一次。color_threshold=-np.inf 是關掉 dendrogram() 預設的自動上色(預設會暗示一個切法,容易誤導)。cut_tree(..., n_clusters=4) 回傳每一筆資料的群編號。也可以給 height=5 用高度切——本頁那條橘色虛線做的就是這件事。
Ch12-unsup-lab-zh.ipynb · 儲存格 117、119、123、127
在一棵樹狀圖上,第 3 號與第 7 號葉子左右緊鄰,但它們所在的分支要到高度 8 才合併;第 3 號與第 20 號隔了很遠,卻在高度 2 就合併了。誰跟第 3 號比較相似?
演算法都很乾淨,麻煩全在做決定的地方。ISLP §12.4.3 把它們列成一張清單, 每一項都會實質改變結果:
下面這個元件是課本圖 12.16 的可玩版本:一家網路商店只賣兩種東西——襪子與電腦。 八位顧客的購買紀錄一樣,只是換一種尺度,K = 2 的分群就換一組答案。
沒有。這不是敷衍,是這一類方法的本質限制。 任何時候把資料丟去分群,它都會給你群——即使資料是純雜訊。 真正想問的是「這些群在獨立的新資料上也會出現嗎」, 文獻上有給群一個 p 值的做法,但沒有共識(細節在 ESL)。
能做的是幾件比較樸素的事:
| 做法 | 怎麼做 | 在檢查什麼 |
|---|---|---|
| 換設定重跑 | 換 linkage、換距離、換 K、標準化與否 | 哪些結構每次都出現(那些比較可信) |
| 抽子樣本重跑 | 隨機丟掉 10–20% 的資料再分群一次 | 分群對擾動穩不穩(通常不太穩) |
| 對照外部標籤 | 有領域標籤時用 crosstab 或 ARI 比對 | 分群有沒有抓到已知的結構(這是事後檢查,不是調參依據) |
| 看得出解釋嗎 | 每一群的變數平均長什麼樣,能不能講成一句話 | 群有沒有實質意義,還是只是切開了連續的雲 |
K-means 0 1 2 3 HClust 0 1 20 10 9 1 0 7 0 0 2 8 0 0 0 3 0 0 9 0
同一份 NCI60(64 個細胞株 × 6830 個基因)、同樣切 4 群,兩種方法的結果只是「略有不同」而不是相同:K-means 的第 3 群等於階層式的第 2 群,但 K-means 的第 0 群混了階層式第 0 群的一部分加上整個第 1 群。
先看群編號是任意的(所以要用 crosstab 而不是直接比標籤)。lab 儲存格 172 另外把階層式的 4 群對上真實癌症類型:所有白血病落在同一群,但乳癌散在三群——分群抓到了一部分結構,不是全部。
Ch12-unsup-lab-zh.ipynb · 儲存格 152、172、176
沒有一個像測試誤差那樣的單一數字。原因很直接:測試誤差需要正確答案,而分群問題裡「正確的群」並不存在(如果存在,那就是分類問題了)。
常見的內部指標(silhouette、Calinski–Harabasz、gap statistic)能算,但它們量的是幾何上的緊密與分離,不是「這些群是不是真的」。一份純雜訊的資料照樣可以有不錯的 silhouette;反過來,兩個真實但形狀狹長交錯的子群,silhouette 會很難看。所以這些指標可以用來在同一個方法內部比較 K,不能用來宣告「分群成功」。
比較誠實的做法是三件事併用:(1)穩定性——換設定、抽子樣本重跑,看哪些群每次都在;(2)可解釋性——每一群能不能用領域語言講成一句話;(3)外部驗證——在獨立的新資料上重做一次,或對上事後才知道的標籤。ISLP 的結語值得抄下來:分群結果不該當成資料的絕對真相,而是形成科學假設的起點。
差在產出的東西是連續還是離散。PCA 給每一筆資料一組新的連續座標(得分),資料在低維空間裡還是一片雲;分群給每一筆資料一個離散的群編號,雲被切成幾塊。
對應的假設也不同。PCA 假設「大部分變異集中在少數幾個方向」,它不假設資料裡有子群——如果真的只有一片橢圓形的雲,PCA 照樣給你很有用的答案。分群則假設「資料由幾個同質的子群組成」,如果實際上是連續漸變的,切出來的界線就是人造的。
實務上兩者常常串起來用,而且順序有講究:先 PCA 再分群是很常見的做法(lab 儲存格 178 就對 NCI60 的前五個得分向量做階層式分群),理由是前幾個主成分可以看成資料的低雜訊版本。反過來也有用:分群完之後,用前兩個主成分的散佈圖把群畫出來,因為 p > 2 的時候你沒別的辦法看。
資料裡有兩三個明顯的離群值(例如那位買 60 雙襪子的顧客)。對 K-means 與階層式分群,下面哪個處理方式最站得住腳?
這一節是課堂沒細講的延伸(講義 12 · p.39–53),第一輪可以直接跳過去看 EX 練習。t-SNE 在論文裡到處都是,值得知道它會怎麼騙人。
PCA 是線性投影:它只能把資料壓到一個平面上。 可是很多高維資料的結構是彎的——想像一張捲起來的紙, 紙上相鄰的兩點在三維空間裡可能隔得很遠,而 PCA 只會把整捲紙壓扁,把不該相鄰的點壓在一起。 流形學習(manifold learning)就是假設資料落在一個低維的彎曲流形上, 想辦法把它攤平。
最有名的是 t-SNE(t-distributed stochastic neighbor embedding)。 它的想法完全不是「找方向」,而是「保住鄰居關係」:
第 2 步為什麼要換成 t 分佈?因為高維空間「裝得下」的鄰居比低維多得多, 硬要用高斯核會讓所有點擠在一起(crowding problem); t 分佈的尾巴重,允許中距離的點被推得比較遠,圖才會散開。
load_digits,8×8 灰階)用三種方法壓到 2 維,顏色是真實的數字標籤——標籤沒有參與計算,只用來上色。perplexity = 5 再切到 30,看同一份資料可以長得多不一樣。
transform()(新資料無法投影到既有的嵌入上,
openTSNE 之類的套件才另外提供近似做法),
也不像 PCA 有負荷量可以解讀「這個方向由哪些變數組成」。transform(),
lab 儲存格 86–94 就示範了「先 UMAP 再分類」,
而且 SVC 的正確率從 0.62 拉到 0.98。但那已經是監督式的評估了,
能這樣調就是因為有 y 可以看。
TSNE[t-SNE] Computing 91 nearest neighbors... [t-SNE] Indexed 1797 samples in 0.001s... [t-SNE] Computed neighbors for 1797 samples in 0.393s... [t-SNE] Computed conditional probabilities for sample 1000 / 1797 [t-SNE] Computed conditional probabilities for sample 1797 / 1797 [t-SNE] Mean sigma: 11.585657 [t-SNE] KL divergence after 250 iterations with early exaggeration: 61.325920 [t-SNE] KL divergence after 1000 iterations: 0.753624
init="pca" 是重要的細節:用 PCA 的結果當初始位置,比隨機初始化穩定得多,也讓結果比較可重現(配上 random_state=0)。
輸出的兩個 KL 散度值得注意:早期誇張階段(early exaggeration)250 輪之後是 61.3,跑完 1000 輪降到 0.754。KL 散度只能用來比較同一份資料的不同次執行,它不是「分得好不好」的分數。
lab 後面還示範了 openTSNE(更快)、UMAP(有 transform())與 PHATE(保留軌跡結構)。
Ch12-unsup-lab-zh.ipynb · 儲存格 71、72、74、75
一張 t-SNE 圖上,A 團與 B 團距離很遠,A 團看起來比 B 團大三倍。可以下什麼結論?
下面幾題取自 ISLP §12.6 的課後習題,題號都對得回課本。先自己想過再點選項; 每個選項——包含錯的——都寫了為什麼。想看完整解答再對照下面2個站。
課本第 1 題要你先證明恆等式 12.18,再用它說明演算法 12.2 每一輪都讓目標函數 12.17 下降。這個論證的關鍵是什麼?
single linkage 與 complete linkage 各做一棵樹。在 single 的樹上$\{{5\}}$ 與 $\{{6\}}$ 這兩群在某個高度合併;complete 的樹上它們也會合併。哪一棵的合併位置比較高?
課本第 5 題:用圖 12.16 的三種尺度各跑一次 K = 2(襪子與電腦),分別預期看到什麼?
課本第 8 題要你在 USArrests 上用兩種方式算 PVE:(a) 讀 explained_variance_ratio_;(b) 拿 components_ 直接套式 12.10。提示裡特別警告了什麼?
考前把這一頁掃過去就好。
| PCA | K-means | 階層式分群 | |
|---|---|---|---|
| 產出 | 連續的低維座標(得分) | K 個離散群標籤 | 一棵樹(1 到 n 群都在裡面) |
| 要先決定 | M(留幾個主成分) | K | 相異度、linkage、切在哪 |
| 有隨機性嗎 | 沒有(只差符號) | 有(初始指派) | 沒有 |
| 結果唯一嗎 | 唯一,最多差符號 | 局部極小,換初值會變 | 唯一(但畫法有 2ⁿ⁻¹ 種) |
| 要標準化嗎 | 幾乎一定要 | 幾乎一定要 | 幾乎一定要 |
| 對離群值 | 會被拉走(變異最大化) | 強迫入群,會扭曲 | 常自己掛高處,吃掉群額度 |
| 常見用途 | 視覺化、去雜訊、補值、當特徵 | 市場區隔、量化子群 | 探索階層結構、基因體學 |
| Linkage | 群間相異度 | 形狀 | 反轉? | 建議 |
|---|---|---|---|---|
| Complete | 跨群距離的最大值 | 平衡 | 不會 | 預設首選 |
| Average | 跨群距離的平均 | 平衡 | 不會 | 預設首選 |
| Single | 跨群距離的最小值 | 鏈狀拖尾 | 不會 | 少用,除非真的要找細長結構 |
| Centroid | 兩群形心的距離 | 不定 | 會 | 基因體學常用,讀圖要小心 |
| 名稱 | 式子 | 備註 |
|---|---|---|
| 第一主成分 | $\max \frac1n\sum_i(\sum_j \phi_{j1}x_{ij})^2$ s.t. $\sum_j\phi_{j1}^2=1$ | 式 12.3 |
| 得分 | $z_{im} = \sum_{j=1}^{p} \phi_{jm} x_{ij}$ | 式 12.2、12.4 |
| 最佳低維近似 | $\min_{A,B}\sum_{j}\sum_i (x_{ij}-\sum_m a_{im}b_{jm})^2$ | 式 12.6,解就是主成分 |
| PVE | $\dfrac{\sum_i z_{im}^2}{\sum_j\sum_i x_{ij}^2} = 1-\dfrac{\mathrm{RSS}}{\mathrm{TSS}}$ | 式 12.10,加起來是 1 |
| 變異分解 | 總變異 = 前 M 個 PC 的變異 + M 維近似的 MSE | 式 12.11 |
| 矩陣補全 | $\min_{A,B}\sum_{(i,j)\in\mathcal O}(x_{ij}-\sum_m a_{im}b_{jm})^2$ | 式 12.12,只在觀測格上算 |
| K-means 目標 | $\min\sum_k \frac{1}{|C_k|}\sum_{i,i'\in C_k}\sum_j (x_{ij}-x_{i'j})^2$ | 式 12.17 |
| 關鍵恆等式 | $\frac{1}{|C_k|}\sum_{i,i'\in C_k}\sum_j (x_{ij}-x_{i'j})^2 = 2\sum_{i\in C_k}\sum_j (x_{ij}-\bar x_{kj})^2$ | 式 12.18,演算法 12.2 的根據 |
| 主成分 | PC1 | PC2 | PC3 | PC4 |
|---|---|---|---|---|
| 負荷量 Murder | 0.536 | −0.418 | −0.341 | −0.649 |
| 負荷量 Assault | 0.583 | −0.188 | −0.268 | 0.743 |
| 負荷量 UrbanPop | 0.278 | 0.873 | −0.378 | −0.134 |
| 負荷量 Rape | 0.543 | 0.167 | 0.818 | −0.089 |
| 得分的變異數 | 2.5309 | 1.0100 | 0.3638 | 0.1770 |
| PVE | 62.0% | 24.7% | 8.9% | 4.3% |
| 累積 PVE | 62.0% | 86.8% | 95.7% | 100% |
負荷量與 lab 儲存格 29 的
components_ 逐位相同、PVE 與儲存格 39 的
explained_variance_ratio_ 相同。四個變數的原始變異數是
18.97 / 6945.17 / 209.52 / 87.73(儲存格 17)——所以標準化不是選項而是必要。
n_init)、樹狀圖只能看高度不能看左右、
結果對標準化與 linkage 都很敏感。換設定多跑幾次,看什麼結構每次都出現。
本頁「預期輸出」逐字取自課程 lab notebook(老師在課程環境實跑);圖表用的烘焙資料由 tools/frames/ 在固定種子下產生,環境為 numpy 1.24.4 · pandas 2.3.2 · scikit-learn 1.6.1 · scipy 1.13.1 · statsmodels 0.14.2 · ISLP 0.4.0 · pygam 0.10.1。每張卡下方的「來源」標了 lab 的儲存格編號,可以直接回去對。
詞彙卡取自本章講義與 ISLP 第 12 章,正面是中文術語(附英文原名)。 先看正面、心裡默想定義,再翻面對答案;洗牌後再過一輪,直到每張都能不看答案講出來。