化學資訊學入門與實作:PCA 與 PLS 的介紹與應用

Chemistry with data magic
14 min readJul 6, 2024

--

在 ”化學資訊學入門與實作:線性回歸模型的介紹與應用”系列 中,我們使用線性回歸模型對 ESOL 數據集進行建模,也說明了線性回歸手法的一些優缺點。

在使用線性回歸中,常會遇到過度擬合 (Over-fitting) 的問題,這也是線性回歸的弱點之一。這次我們要介紹一些手法來解決線性回歸的弱點。

多重共線性的問題

在 ”化學資訊學入門與實作:線性回歸模型的介紹與應用”,我們發現 Ordinary Least Squares (OLS) 的過度擬合問題相當嚴重,其中的原因是 OLS 無法處理多重共線性的問題。那什麼是多重共線性?我們用個簡單的例子來解釋,y = ax1 + bx2 兩個輸入變數 (x1, x2) 及一個輸出變數 (y) 來說明多重共線性的問題。

  • 如果 x1 和 x2 是兩個獨立的變數,也就是說這兩個變數的相關性很低,互相不會受到影響。 那我們可以把 y = ax1 + bx2 表示成下圖橘色的回歸面。
  • 但如果 x1 和 x2 是不是獨立的變數,這兩個變數間存在著相關性 (例如:x1 = cx2 + d),那我們可能會找到多個回歸面。使用 OLS 的時候,可能只是找出多個回歸面中的其中一個,這也是造成過度擬合的原因之一。這種情況稱作“多重共線性”

主成分分析 (Principal Component Analysis, PCA)

關於 PCA 的數學原理部分,我們後面會再來解釋,由於 Scikit-learn 實裝的PCA 和原本的理論有一些不同,所以我們會著重在應用的部分。先從結論來說,PCA 可以達到以下的效果:

  • 降低多元(多維)資料集的維數
  • 以比原始維度更低的維度表達資料集,以免遺失資料集中盡可能多的訊息
  • 數據可視化
  • 數據軸的旋轉

首先從數據軸的旋轉來講解,假設我們有個相關性較高 (也就是具有共線性性) 的數據,例如上圖的 X1身高和 X2 體重存在著線性的相關。使用 PCA 之後,可以得到兩個新的軸 PC1 和 PC2 。通常第 1 主成份軸 (PC1) 會是數據中變異數 (variance) 最大的軸,然後第 2 主成份軸 (PC2) 會是與 PC1 垂直的軸。從結果來看,就像是原本的身高-體重 (X1-X2) 圖旋轉了 45 度,也達到了數據軸旋轉的效果。

另一方面,PC1 也保存了數據大部分的特性,PC2 則沒有什麼重要的資訊。換句話說,原本的身高-體重 (X1-X2)是 2 維的數據,使用 PCA 之後只要使用 1 維的 PC1 就大概能代表了這組數據,也就是達成比原始維度更低的維度表達資料集,以免遺失資料集中盡可能多的訊息

此外,身高-體重 (X1-X2)是個共線性的關係,同時使用 X1 和 X2 建模時會有過度擬合的問題,但使用 PCA 後我們可以得到兩個獨立的新變數 (PC1, PC2) 就可以解決多重共線性的問題。

題外話:PCA 與 SVD (Singular Value Decomposition)

在 PCA 裡,很重要的部分是找到主成份軸,也就是變異數最大的軸。在數學式裡,我們可以把數據表示成散布矩陣,其中 X 是原本的變數,L 是特徵值 (eigen value) ,V 是特徵向量 (eigenvector) 也就是主成份。只要把下面的式子解出來,就得到了 PCA 的結果。

實際上,PCA 的算法其實不是很有效率,為了更有效率的計算主成份,SVD (奇異值分解,singular value decomposition) 算是一個偷吃步。雖然 PCA 和 SVD 是不一樣的兩個東西和算法,但最後的結果卻殊途同歸,下面我們來比較一下這兩個手法。

首先,我們比較一下兩個手法使用的數學式子,可以看出兩個手法使用不同的矩陣。PCA 是要尋找特徵值和特徵量量。SVD 的 A 矩陣 (也就是 X ) 用右下角的圖示來表達其目的。

另一個觀點,不用數學式子來推敲,而是看看 PCA 和 SVD 分別是怎麼找到 向量 V 的。從數據軸旋轉的結果來看兩個手法,PCA 是找到變異數最大的軸變成向量 V。而 SVD 進行了旋轉-縮放-旋轉的步驟,把向量 V 轉換回向量 A,也改變了數據的分佈。

回到數學的部分,如果我們嘗試著改寫散布矩陣 S ,其中 X 的部分用 A 矩陣做表示,我們可以把算是寫成:

而這裡我們會發現 PCA 裡的特徵值會等於 SVD 奇異值的平方。

為了驗證這兩個手法是否得到相同的結果。我們使用了兩組數據做了簡單的驗證。紅色數據點是原始數據,X1 和 X2 有著共線性的數據點,左圖是簡單的 X1 = X2,PCA 的結果為藍色,SVD 的結果為綠色。由於兩個結果重複,PCA 的數據點被 SVD 給蓋掉,實際上是相同的結果。而右圖是 X1 = X2 + random noise,數據點比較沒有在一條直線上。PCA 和 SVD 的結果雖然看起來不一樣,實際上也是相同結果的鏡像。

由於 PCA 需要計算變異數,在數據量比較大的情況下,變異數計算會發很多的時間,所以有些算法可能是採用 SVD 再去推算特徵值和特徵向量。

主成分回歸 (Principal Component Regression, PCR)

在上面我們提到了主成份分析可以解決多重共線性的問題,如果我們使用主成份 (PC1, PC2, PC3 … etc.) 來建立一個回歸模型,也可以稱為主成分回歸 (PCR)。我們比較一下 OLS 和 PCR 不一樣的地方:

PCR 的優點是保證了各主成分的獨立性(相關性為0),因此模型會比較穩定。可以避免 OLS 中出現的模型不穩定。

PCR 的缺點是個主成分僅由原始變數 X 所決定,因此不一定適合對目標變數 y 建模。有時候模型變得不好解釋,因為不是直接對 X 做解釋而是主成份做解釋。另外,PCR 也容易受到雜訊的影響,有時候模型的準確性也會降低。

我們用個簡單的例子來解釋 PCR 的缺點。假設 X1 是工作時間、X2 是睡眠時間,我們可以知道 X1 和 X2 是完全相關,PC1 則會是對角線,然後可以找出各個數據點在 PC1 上的值。(這裡不是嚴謹的 PCA ,只是簡單的示意圖)換句話說,X1 和 X2 非獨立變數,而 PC1 可以大概掌握數據的訊息,進而達到降維的效果。

另外,我們假設現在設定一個目標變數 (收入 Y) ,用 Y = 2000 * X1 來取得和數據點相關的目標變數。

然後再來看看,使用 PC1 對 Y 建模會有什麼樣的結果。我們會發現模型的效果不是非常好。

這個簡單的數據以及建模反應了 PCR 的弱點:

  • PCA 的降維效果會失去一些訊息,有時候會影響模型的準確度。
  • 主成份 (PC) 的解釋性不強,原本用勞動時間就可以解釋收入的關係,但 PC1 卻不能解釋什麼東西影響收入。
  • PCA 會被雜訊影響,PC1 被睡眠時間的數值影響,收入不需要考慮睡眠時間。

原本 PCR 是想把多變量的問題簡化,但是在某些期況下卻幫了倒忙,反而讓模型變得更複雜,得不到準確度較高的模型。接下來,我們會介紹另一個手法來解決 PCR 的問題。

偏最小平方回歸 (Partial least squares regression, PLS 回歸)

PLS 回歸與 PCR 有些相似,但不是尋找 X 之間變異數最大的 PC,而是將數據 X 和 Y 投影到新空間來尋找一個線性回歸模型。從概念上來看,PCR 和 PLS 都是對主成份 T 和目標變數 Y 間使用 OLS 進行建模。PCA 抽出來的主成份考慮 X 的訊息。另一方面,PLS 的主成份不只考慮 X 也加入了 Y 的訊息,所以可以避免 PCR 的一些問題。

PLS 有以下的優點:

  • 建立包含與 y 相關的 X 的模型變得更加容易,從而提高了準確性和可解釋性。
  • 即使變數的數量大於樣本數量,也可以進行計算。
  • 建立回歸方程式時不易受雜訊影響

利用 ESOL 數據集進行建模

先導入需要用到的一些 library,將 ESOL 數據集載入為pandas.DataFrame。再來是利用 PandasTools.AddMoleculeColumnToFrame 把 DataFrame 中 smiles 格式的分子轉換回 Mol 的形式。

from rdkit import rdBase, Chem
from rdkit.Chem import AllChem, Draw, PandasTools
from rdkit.Chem.Draw import IPythonConsole
import pandas as pd
import numpy as np

df = pd.read_csv('esol.csv')

PandasTools.AddMoleculeColumnToFrame(frame=df, smilesCol='smiles')

我們利用 RDKit 來計算 Morgan fingerprint 。子構造範圍設定為 radius = 2,向量長度為 512 位元。然後將資料分割成 training data 和 test data。

from sklearn.model_selection import train_test_split

morgan_fps = []
for mol in df.ROMol:
fp = [x for x in AllChem.GetMorganFingerprintAsBitVect(mol, 2, 512)]
morgan_fps.append(fp)
morgan_fps = np.array(morgan_fps)
target = df['measured log solubility in mols per litre']

X_train, X_test, y_train, y_test = train_test_split(morgan_fps, target, random_state=0)

OLS 線性回歸:

我們使用 sklearn.linear_model 裡的 LinearRegression 來建立 OLS 模型:

from sklearn.linear_model import LinearRegression
lr = LinearRegression().fit(X_train, y_train)
print('acc on train: {:.3f}'.format(lr.score(X_train, y_train)))
print('acc on test: {:.3f}'.format(lr.score(X_test, y_test)))

acc on train: 0.882
acc on test: 0.007

Training data 的預測準確度很高,但對 test data 的預測準確度卻是為 0 。這就是我們所提到的 OLS 的過度擬合 (Overfitting) 問題。

PCR 主成分回歸:

在 sklearn 中沒有 PCR 的模組,所以我們需要先建立一個 PCA 來抽取出主成份。在 PCA 的函數中,我們需要設定 n_components ,一個簡單的方法就是指定一個小數,n_components = 0.99 代表自動選擇主成份的數量包含了 99% 的資訊量。在這次的數據集中,380 個主成份代表了 99% 的訊息量。數據的維度也從 512 維降到 380 維。

from sklearn.decomposition import PCA

pca=PCA(n_components=0.99)
pc_train = pca.fit_transform(X_train)
pc_test= pca.transform(X_test)

print ('no. of components: {}'.format(len(pca.components_)))

no. of components: 380

再來就是用 LinearRegression 來建立 OLS 模型:

lr = LinearRegression().fit(pc_train, y_train)
print('acc on train: {:.3f}'.format(lr.score(pc_train, y_train)))
print('acc on test: {:.3f}'.format(lr.score(pc_test, y_test)))

acc on train: 0.818
acc on test: 0.502

雖然Training data 的預測準確度還是比 test data 的預測準確度要來得高,但和 OLS 的結果比較起來, test data 的準確度從 0 上升到 0.502 。PCA 的降維與獨立主成份的效果,解決了 OLS 嚴重的過度擬合 (Overfitting) 問題。

PLS 偏最小平方回歸:

我們使用 sklearn.cross_decomposition 裡的 PLSRegression 來建立 PLS 模型,但需要注意的一點,PLS 和 PCA 一樣需要指定 n_components ,但是不能像是 PCA 一樣指定 99% 資訊量這種用法,需要 n_components 當成一個超參數用 GridSearchCV 來尋找最佳的 n_components。

from sklearn.cross_decomposition import PLSRegression
from sklearn.model_selection import GridSearchCV

parameters = {'n_components':[i for i in range(2,300)]}
pls = GridSearchCV(PLSRegression(),parameters)
pls.fit(X_train,y_train)
model = pls.best_estimator_
print('acc on train: {:.3f}'.format(model.score(X_train, y_train)))
print('acc on test: {:.3f}'.format(model.score(X_test, y_test)))

acc on train: 0.698
acc on test: 0.556

PLS的結果可以看出,Training data 的預測準確度和 test data 的預測準確變得比較接近, test data 的準確度為 0.556 比 PCR 和 OLS 都要來得高。所以 PLS 也可以改善過度擬合 (Overfitting) 問題。我們來確認一下 GridSearchCV 結果,最佳的PLS 到底用了多少 component?

PLS 的最佳模型只用了 3 個 component,所以 PLS 也達到了降維的效果,從原本 512 維降到 3 維。(這次的結果可能有些 under-fitting )

結語

在 ”化學資訊學入門與實作:線性回歸模型的介紹與應用”系列 中,我們提到了 OLS 的過度擬合問題,利用正則化有助於改善過度擬合問題,也衍伸出了 Ridge、Lasso 和 ElasticNet 這三種線性回歸的改良版。在這次的文章中,我們提到了 PCA 這個方法來改善多重共線性造成的過度擬合問題,PCA 可以有效地降維且確保原本數據的資訊量。另一方面,PLS 可以改善 PCA 不擅長回歸的問題,也是線性回歸的另一種改良版。

--

--

Chemistry with data magic

I am working on improving material developments by creating machine learning analytical tools for chemical data to accelerate the material discovery.