PCA 降噪失效,哪一段程式碼需要修改?
在郵遞區號自動辨識的研究中,研究人員收集了一份手寫數字影像資料集(UCI sklearn digits, 8×8 灰階),共 1797 筆。研究人員把含有雜訊的手寫數字影像存放在變數 noisy 中,部分資料經視覺化後外觀如附圖所示。他們嘗試使用 PCA 進行降噪,並希望能保留影像的主要特徵,同時去除影像中的雜訊。
程式碼如下:
程式碼 A: import numpy as np; noisy = np.random.normal(digits.data, 4)
程式碼 B: from sklearn.decomposition import PCA; pca = PCA()
程式碼 C: pca.fit(noisy)
程式碼 D: components = pca.transform(noisy)
程式碼 E: filtered = pca.inverse_transform(components)
當程式執行後,觀察到影像仍然含有明顯的雜訊。研究人員懷疑是程式中某步驟的設定不正確,導致 PCA 沒有發揮降噪的作用,需要修改程式碼才能讓降噪有效。請問哪一段程式碼需要修改,才能讓 PCA 對 noisy 影像有效去噪?
郵遞區號辨識專案的研究人員對手寫數字影像加了雜訊,然後試著用 PCA 來降噪。整個流程分為五段程式碼:A 加雜訊、B 建立 PCA 物件、C 訓練 PCA、D 轉換到 PCA 空間、E 轉換回原始空間。執行後,降噪效果完全沒有,影像雜訊依然明顯。
問你:五段程式碼中,哪一段有問題,導致 PCA 降噪完全無效?
一句話總結
PCA 降噪失效的原因是程式碼 B:PCA() 沒有指定 n_components 參數,等於保留所有主成分,這樣 transform + inverse_transform 就是把資料原封不動轉一圈回來,雜訊完全沒有被去除。
先感受問題:PCA 為什麼能降噪,又為什麼失效?
郵遞區號辨識專案的研究人員拿到了 1,797 張 8×8 手寫數字影像,每張有 64 個像素特徵。他們人工加入標準差為 4 的高斯雜訊,想用 PCA 降噪:先把 64 維特徵壓縮到「最重要的前 K 個主成分」,再還原回來——還原時,次要主成分(包含雜訊的部分)就被丟掉了,影像變乾淨。
這個邏輯完全正確,但有一個關鍵前提:必須指定「保留幾個主成分(K)」。如果 PCA() 沒有指定 n_components,sklearn 預設保留所有 64 個主成分,等於沒有降維,transform 後再 inverse_transform 就是完整的資料還原,什麼雜訊都沒有去掉。
PCA 降噪為什麼沒有效?五段程式碼逐一診斷
- 程式碼 A(加雜訊)正確:np.random.normal(digits.data, 4) 為每個像素值加入均值為 0、標準差為 4 的高斯雜訊,這是標準的雜訊模擬做法,沒有問題。
- 程式碼 B(建立 PCA 物件)有問題:PCA() 不指定 n_components,等於 n_components=None,sklearn 預設保留 min(樣本數, 特徵數) = min(1797, 64) = 64 個主成分,等於保留全部,沒有任何降維或降噪效果。
- 程式碼 C(訓練 PCA)正確:pca.fit(noisy) 對加了雜訊的資料學習主成分方向,這步沒有問題。
- 程式碼 D(轉換)正確:pca.transform(noisy) 把資料投影到主成分空間,邏輯正確。
- 程式碼 E(還原)正確:pca.inverse_transform(components) 從主成分空間還原到原始空間,邏輯正確。但因為 B 沒降維,D 和 E 就等於無意義的往返,雜訊沒有去除。
修改 PCA(n_components=K),只保留前 K 個主成分
正確的修改方式是把程式碼 B 改成 pca = PCA(n_components=0.95) 或 pca = PCA(n_components=32):
- n_components=0.95:保留能解釋 95% 變異量的最少主成分數,sklearn 自動決定 K 的值(digits 資料集約需 20-30 個主成分)
- n_components=32:明確保留前 32 個主成分,丟棄後 32 個(通常包含高頻雜訊)
保留主要主成分後,次要主成分(方差小、通常對應雜訊)在 inverse_transform 時不再還原,達到降噪效果。對 digits 資料集,保留約 20-30 個主成分通常就能有效去噪,同時保留數字的基本形狀。
這就是選項 B 講的:程式碼 B 需要修改,加上 n_components 參數。
技術版:PCA 降噪原理與 n_components 參數
中級考試大概率會考程式碼跟公式,所以這部分你還是要學。但如果現在學起來很痛苦,可以先跳過,等讀完其他題目回頭再來。
想像你有一張 64 格的樂高拼圖,每格代表一個像素。加了雜訊後,有些格子的顏色略微偏離了原本的樣子。
PCA 降噪的邏輯是:先把這 64 格「重新編碼」成幾個最重要的「主題風格」(例如「整體亮度」「左右對比」「上下對比」)。保留前幾個最重要的主題,丟掉後面的「細碎干擾」(雜訊通常集中在不重要的主題裡),再還原回來,影像就變乾淨了。
但如果你保留全部 64 個主題,等於什麼都沒有丟棄,拼圖還原後完全一樣,雜訊當然也回來了。這就是 PCA() 不指定 n_components 的問題。
| 白話說法 | 程式碼 |
|---|---|
| 加高斯雜訊(標準差 4) | noisy = np.random.normal(digits.data, 4) |
| 建立 PCA(錯誤:保留全部) | pca = PCA() ← 等於 PCA(n_components=None) |
| 建立 PCA(正確:保留 95% 變異) | pca = PCA(n_components=0.95) |
| 訓練 PCA 找主成分方向 | pca.fit(noisy) |
| 投影到主成分空間(降維) | components = pca.transform(noisy) |
| 還原到原始空間(降噪) | filtered = pca.inverse_transform(components) |
- n_components
- PCA 保留的主成分數,可以是整數(保留 K 個)或 0-1 的浮點數(保留解釋 X% 變異量所需的最少成分)
- explained_variance_ratio_
- 每個主成分解釋的變異比例,用 pca.explained_variance_ratio_.cumsum() 可看累積解釋量
- transform
- 把原始資料投影到主成分空間,輸出維度 = n_components
- inverse_transform
- 從主成分空間還原到原始空間,如果 n_components < 原始維度,還原的資料是近似值
- 雜訊集中在低方差成分
- 雜訊通常是隨機的、低相關的,對應方差小的主成分;訊號是有結構的,對應方差大的主成分
PCA 流程:
1. fit(X):計算 X 的協方差矩陣,找出特徵向量(主成分方向)
→ 按特徵值(解釋變異量)由大到小排列
2. transform(X):X 投影到前 K 個主成分
X_pca = X · W_K^T (W_K 是前 K 個主成分向量)
X_pca.shape = (n_samples, K)
3. inverse_transform(X_pca):從 K 維還原到原始維度
X_rec = X_pca · W_K + μ (μ 是訓練時的均值)
X_rec.shape = (n_samples, 64)
→ K < 64 時,X_rec 是近似值,丟棄了後 64-K 個成分的資訊
程式碼 B 的問題:
PCA() → n_components = min(1797, 64) = 64(全部保留)
transform + inverse_transform = 完美還原 = 雜訊也回來
修正後(n_components=0.95):
sklearn 自動選 K ≈ 26 個成分(解釋 95% 變異)
丟棄後 38 個成分,雜訊大量存在其中
inverse_transform 的 X_rec 明顯比 noisy 乾淨
- 為什麼 PCA() 不指定 n_components 時,降噪效果是零?
- n_components=0.95 和 n_components=26 有什麼差異?各自適合什麼場景?
- 雜訊為什麼通常集中在「低方差的主成分」?
- 如果把 n_components 設得太小(例如 3),降噪後影像會有什麼問題?
- pca.fit(noisy) 和 pca.fit(clean_data) 之後用 transform(noisy),降噪效果有差嗎?
為什麼其他選項是錯的
字面在說什麼:加雜訊的方式有問題,需要修改。
為什麼不對:np.random.normal(digits.data, 4) 是標準的高斯雜訊模擬:第一個參數是均值(用原始資料值),第二個參數是標準差(4),為每個像素獨立採樣雜訊。這是正確且常見的做法,對 PCA 降噪的後續步驟沒有影響。問題不在這裡。
誰會選錯:不熟悉 np.random.normal 的參數順序,以為「標準差 4 太大」或「參數填錯了」,懷疑雜訊本身加得有問題。
字面在說什麼:pca.fit(noisy) 用含雜訊的資料訓練 PCA 是錯誤的,應該用乾淨資料。
為什麼不對:用 noisy 資料訓練 PCA 是正確做法。降噪 PCA 的邏輯是:從含雜訊的資料中學習主要變異方向,訊號(數字形狀)的變異量遠大於隨機雜訊的變異量,所以前幾個主成分仍然主要捕捉訊號,後面的主成分才是雜訊。用乾淨資料訓練雖然理論上更好,但實務中通常沒有乾淨資料,用 noisy 資料是標準做法。
誰會選錯:直覺上覺得「訓練資料有雜訊,PCA 學到的主成分也會有雜訊污染」,但實際上訊號的方差仍遠大於雜訊,PCA 仍能找到正確方向。
字面在說什麼:pca.transform(noisy) 的用法有問題。
為什麼不對:pca.transform(noisy) 把含雜訊的影像投影到主成分空間,是標準的降噪流程。問題不在這裡,而在 B 沒有限制主成分數,所以 transform 保留了所有維度,後面的 inverse_transform 才能完整還原雜訊。D 的操作本身沒有錯。
誰會選錯:看到「把含雜訊的資料做 transform」就覺得哪裡怪怪的,其實這正是降噪的必要步驟,問題不在 D。
同個考點下次怎麼變形
直覺:怎麼知道應該保留幾個主成分來降噪?
答案:畫累積解釋變異量圖(Explained Variance Ratio 折線圖),找到「肘點」(elbow point):曲線從陡峭變平緩的地方。累積解釋 80-95% 變異量的主成分數通常是好的選擇。對 digits 資料集,約 20-30 個主成分就能解釋 95% 的變異。
直覺:PCA 降噪(用 inverse_transform 還原)和 PCA 降維(直接使用壓縮後的特徵)有什麼差異?
答案:降噪是「壓縮後再還原」,輸出和輸入維度相同,但去除了低方差成分;降維是「壓縮後直接使用」,輸出維度小於輸入,用在機器學習的特徵工程中。兩者都用 transform,差異只在有沒有做 inverse_transform。
直覺:如果把程式碼 A 的標準差從 4 改成 20,同樣的 n_components=0.95 還有效嗎?
答案:效果會下降。雜訊越強,雜訊的方差越大,會混入前幾個主成分,n_components=0.95 保留的成分就會包含更多雜訊。此時需要降低 n_components(保留更少成分、更激進去噪),或調低 0.95 的門檻。降噪能力有上限,雜訊極強時 PCA 降噪不再適用。
直覺:同樣的 PCA 降噪流程,用在 MNIST(28×28 = 784 維)上,n_components 應設多少?
答案:MNIST 784 維中,通常 50-100 個主成分就能解釋 95% 以上的變異。可以用 `pca = PCA(n_components=0.95)` 讓 sklearn 自動決定,或先用全量 PCA 畫折線圖再手動選。維度更高意味著可以保留更多主成分而仍有降噪效果。
直覺:怎麼量化 PCA 降噪的效果?
答案:計算降噪後影像和原始乾淨影像之間的均方誤差(MSE):`mse = np.mean((filtered - digits.data) ** 2)`。也可以用 SSIM(結構相似性指標)或 PSNR(峰值訊雜比)。同時計算 noisy 和 clean 的 MSE 作為基準,比較降噪前後的改善幅度。
想再往下看,這 5 個
- 主成分分析(PCA)本題核心,透過保留高方差主成分、丟棄低方差成分實現降噪,n_components 是控制降噪強度的關鍵參數。
- 降維處理(Dimensionality Reduction)PCA 降噪的理論基礎,高維空間中訊號集中在少數維度,雜訊分散在其餘維度。
- 奇異值分解(SVD)PCA 的數學基礎,sklearn 的 PCA 底層就是對資料矩陣做 SVD,主成分對應奇異向量。
- 自編碼器(Autoencoder)深度學習版的 PCA 降噪,用神經網路的 encoder-decoder 結構實現更強的非線性降噪能力。
- 資料前處理(Data Preprocessing)PCA 降噪是資料前處理的一環,實務上還需要先標準化(StandardScaler)再做 PCA,才能避免量綱不一致問題。