偏最小二乘判别PLS-DA的python实现(基于sklearn,附完整代码实现)

PLS-DA的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,重复上述过程,对模型进行训练即可,于是直接调包又香了

你可能感兴趣的:(python,机器学习,数据分析,python)