偏最小二乘判別PLS-DA的python實現(基於sklearn,附完整程式碼實現)
偏最小二乘判別PLS-DA的python實現(基於sklearn,附完整程式碼實現)
前兩天收到了論文的拒稿意見,其中一條是“PLSDA的表示錯誤,應為PLS-DA”,好吧,以後都寫PLS-DA!虛心接受專家意見。
由於之前偷懶,都是用PLS toolbox完成相關偏最小二乘法的資料分析工作,藉此機會,就把PLS-DA的python實現好好嘮嘮。查過不少資料中,沒有詳細說調包sklearn實現的,廢話不多說,進入正題。
sklearn中的偏最小二乘函式為PLSRegression(),這是一個迴歸函式,如果直接拿來做分類,顯然得不到想要的結果。呼叫格式如下:
from sklearn。cross_decomposition import PLSRegression
model = PLSRegression()
解決方法是:把標籤矩陣(比如0,1,2,3,4的一個列向量)使用get_dummies()函式轉換為類別矩陣,拿我的資料舉例:
import numpy as np
from sklearn。cross_decomposition import PLSRegression
from sklearn。model_selection import train_test_split
from sklearn。preprocessing import MinMaxScaler #歸一化
from sklearn。metrics import confusion_matrix,recall_score,classification_report,accuracy_score
import pandas as pd
import matplotlib。pyplot as plt
import seaborn as sns
from sklearn。model_selection import GridSearchCV
import warnings
from sklearn。model_selection import validation_curve
from sklearn。model_selection import KFold
#讀取特徵矩陣
spec = pd。read_excel(‘correct_spec。xlsx’)
x = np。array(spec)
#我這裡的特徵x形狀: (939, 150)
#做一個標籤向量,標籤y形狀: (939,)
y1 = np。zeros((189,1))
y2 = 1*np。ones((188,1))
y3 = 2*np。ones((185,1))
y4 = 3*np。ones((188,1))
y5 = 4*np。ones((189,1))
y = np。vstack((y1,y2,y3,y4,y5))
y = y。ravel()
#先做一個數據集的劃分
train_X,test_X, train_y, test_y = train_test_split(x, y, test_size=0。2)
#然後對y進行轉換
train_y = pd。get_dummies(train_y)
#建模
model = PLSRegression(n_components=8)
model。fit(train_X,train_y)
#預測
y_pred = model。predict(test_X)
#將預測結果(類別矩陣)轉換為數值標籤
y_pred = np。array([np。argmax(i) for i in y_pred])
#模型評價
print(‘測試集混淆矩陣為:\n’,confusion_matrix(test_y,y_pred))
print(‘平均分類準確率為:\n’,accuracy_score(test_y,y_pred))
接下里的問題就是函式中
主成分數n_components的取值
,如果直接使用網格搜尋函式GridSearchCV()對n_components的值在一個範圍內進行搜尋,比如(1,20)一般情況下是行不通的,會發生過擬合,直接搜尋到指定範圍內的最大值。
這時候想到的是使用validation_curve()函式繪製驗證曲線,來選擇這個超引數,但是依然會報錯,原因大致是:這是一個迴歸函式,我們用它來做分類,y值不連續,所以我就要報錯了,叭叭叭叭······ 即使按照上述方法事先轉換y的型別,再呼叫validation_curve依然不行······
所以這裡只能自己寫一個驗證函式用來調參,選取原則就是訓練集和驗證集的得分最高時候對應的主成分數,且驗證集的得分不能高於訓練集。
def accuracy_component(xc,xv,yc,yv,component,n_fold):
k_range = np。linspace(1, component,component)
kf=KFold(n_splits=n_fold,random_state=None, shuffle=True)
accuracy_validation=np。zeros((1,component))
accuracy_train=np。zeros((1,component))
for j in range(component):
p=0
acc=0
model_pls=PLSRegression(n_components=j+1)
model_pls。fit(xc,yc_labels)
y_pred = model_pls。predict(xv)
y_pred = np。array([np。argmax(i) for i in y_pred])
accuracy_train[:,j]=accuracy_score(yv,y_pred)
for train_index, test_index in kf。split(xc):
X_train, X_test = xc[train_index], xc[test_index]
y_train, y_test = yc[train_index], yc[test_index]
YC_labels = pd。get_dummies(y_train)
YV_labels=pd。get_dummies(y_test)
model_1=PLSRegression(n_components=j+1)
model_1。fit(X_train,YC_labels)
Y_pred = model_1。predict(X_test)
Y_pred = np。array([np。argmax(i1) for i1 in Y_pred])
acc=accuracy_score(y_test,Y_pred)+acc
p=p+1
accuracy_validation[:,j]=acc/p
print(accuracy_validation)
plt。plot(k_range, accuracy_validation。T, ‘o-’,label=“Training”,color=“r”)
plt。plot(k_range, accuracy_train。T, ‘o-’,label=“Cross-validation”,color=“b”)
plt。xlabel(“N components”)
plt。ylabel(“Score”)
plt。legend(loc=“best”)
plt。rc(‘font’,family=‘Times New Roman’)
plt。rcParams[‘font。size’] = 10
plt。show()
return accuracy_validation,accuracy_train
得到最佳主成分n_components,重複上述過程,對模型進行訓練即可,於是直接調包又香了