特征抽取

实验目的

本次实验的实验目的是对一数据集进行分类,因为数据集中存在着噪声数据,需要对数据进行特征抽取,并使用线性回归,决策树,神经网络,SVM以及XGBoost分别实现对模型的预测并进行评估。

实验原理

特征抽取和特征选择

对实验的训练数据集进行观察可得:对于每一条样本,有与之相关的120个feature(个别feature可能是na),但是对于训练有效的特征不足20个,如果对原数据集进行训练,可能会导致过拟合,训练过慢,效果较差等结果,因此要对数据进行特征抽取,选取对训练有益的特征

本次实验中,特征抽取使用了如下两种方法:

基于决策树抽取

本部分借助sklearn中的DecisionTreeClassifier进行处理,因为每个特征具有对应的权重,对于分类结果帮助较大的feature的权重大,为了筛选出有用的信息,可以选择决策树中权重大的点,处理思路为:

  • 将数据集划分为训练集和测试集,得到训练集对应的决策树,在测试集上测试准确率
  • 如果测试集上的准确率大于25.5%(表明此次训练的结果较为有效),记录下此时所有feature对应的权重
  • 重复上述过程,直到收集到的权重数大于100,将各个feature得到的权重相加,得到新的权重
  • 根据新的权重,保留最大的二十个feature用于训练

类似的,不仅仅是基于决策树,基于XGBoost,SVM等通过多次构建模型找到权重最大的特征

RFE方法

与上一种抽取方法类似,RFE也是借助反复构建模型寻找较好的特征,不同的是RFE借助递归的思路,依次找出特征中最好的特征并再下一轮训练中排除当前特征,在剩余的特征上重复训练,直到找出20个最好的特征。

RFE也可以采取线性回归,SVM等模型作为底层模型实现特征选择,本次实验中RFE借助sklearn库中的RFE函数以及线性回归函数作为数据抽取的方法

Relief-F方法

Relief算法是用于两类数据的分类问题的特征权重计算方法,而Relief-F作为Relief算法拓展,可以实现多分类问题的特征权重计算,算法的主要步骤为:

  • 将所有样本按类别分类,对于每类样本,分别抽取一个样本记作x
  • 对于每一个x,在该类中找到x的k个最近邻,在不包含该类别的训练集中也找出k个最近邻
  • 根据近邻及所属的类别计算特征的权重
  • 对特征的权重进行派别,得到合适的特征

多分类任务

在前面的实验中,已经完成了对二分类任务的逻辑回归、SVM模型,为了适应本实验的任务,要将其进行适当修改,以便实现多分类任务。

多分类学习可以有二分类学习器集成而来,有三种方法:

  • 一对一
  • 一对其余
  • 多对多

其中,本实验采用了多对多的方式进行训练,由于本实验分类为四类,可以将多对多的分类学习简化设计如下:

  • 设计不同的二分类器,每个二分类器将两个类别视作正类,另外两个类别视为负类,因此共需要六个二分类学习器
  • 对于每个分类器,因为其对称性,正类和负类的权重可以视为一致的,在预测时,对于一个样本统计,将某一类别视为正类的概率,得到的最大的概率对应的类别视作该样本的预测值

实验步骤

数据预处理

  • 从数据集中读取数据

  • 将空数据填充为该特征中其他不为空的数据的中位数

    for column in list(feature.columns[feature.isnull().sum() > 0]):
        val = feature[column].median()
        feature[column].fillna(val, inplace=True)
  • 正则化数据

  • 将数据集随机分为训练集和测试集(7:3)

特征抽取

  • 基于决策树划分,得到权重最大的特征对应的序号

    while 1:
        ...重新划分训练集和测试集
        dtree=tree.DecisionTreeClassifier(splitter="random",min_samples_leaf=3)
        dtree=dtree.fit(train_data,train_label)
        test_res=dtree.predict(test_data)
        sum=0
        for i in range(test_label.size):
            if(test_label[i]==test_res[i]):
                sum+=1
        if sum/test_label.size>0.255:
            weight+=dtree.feature_importances_
            t+=1
        if t>100:
            break
    max_indexs = heapq.nlargest(20, range(len(weight)), weight.take)
  • 借助RFE划分

    from sklearn.feature_selection import RFE
    from sklearn.linear_model import LinearRegression as LR
    linear_model=LR()
    s= RFE(linear_model, n_features_to_select=20, step=1).fit(Data, Label)  
    Data = s.transform(Data)

线性回归

在LogisticRegression中添加:

def multi_fit(self,X,y):
    yi=np.zeros((6,y.size))
    ### set yi
    theta=[]
    J=np.zeros(3000)
    for i in range(0,6):
        t,j,temp=self.fit(X,yi[i])
        theta.append(t)
        J+=j
    return theta,J

def multi_pred(self,X,multi_theta):
    m=X.shape[0]
    pre=[]
    X=np.hstack((np.ones((m,1)),X))  
    for i in range(0,6):
        theta=multi_theta[i]
        p=np.dot(X,theta)
        p=p.reshape(-1,1)
        pre.append(p)
    return pre

进行训练、预测、绘制图像

lr=LogisticRegression()
theta,loss=lr.multi_fit(train_data,train_label)
preds=lr.multi_pred(test_data,theta)
test_r=[preds[0]+preds[1]+preds[2],preds[0]+preds[3]+preds[4],preds[1]+preds[3]+preds[5],preds[2]+preds[4]+preds[5]]
x = np.arange(1,len(loss)+1)
plt.plot(x,loss)
plt.xlabel(u"times")
plt.ylabel(u"loss-value")
plt.show()
sum=0
for i in range(test_label.size):
    for j in range(0,4):
        if test_label[i]==j:
            if test_r[j][i]>=test_r[0][i] and test_r[j][i]>=test_r[1][i] and test_r[j][i]>=test_r[2][i] and test_r[j][i]>=test_r[2][i]:
                sum+=1
sum/test_label.size

决策树

调用sklearn.tree中的DecisionTreeClassifier实现:

from sklearn import tree
dtree=tree.DecisionTreeClassifier()
dtree=dtree.fit(train_data,train_label)
test_res=dtree.predict(test_data)

神经网络

调用sklearn中的neural_network实现:

from sklearn import neural_network as NN
nn=NN.MLPClassifier()
nn=nn.fit(train_data,train_label)
test_res=nn.predict(test_data)

SVM

处理方式与线性回归的模型相似

XGBoost

调用xgboost.sklearn库实现XGBoost

xgb=XGB()
xgb=xgb.fit(train_data,train_label)
test_res=xgb.predict(test_data)

实验结果

特征抽取

  • 基于决策树进行特征选择

    所有节点的权重数据:根据观察不难看出,不同特征的权重分布存在差距,大部分分布在0.8-1.0之间

  • RFE特征抽取

    对RFE分别借助线性回归,SVM和随机森林的底层模型训练,发现线性回归训练时间较短,另外两个模型需要较长时间的训练,且这几类模型对训练结果的预测效果没有明显的影响,故选取线性回归模型进行RFE处理。

    对特征的重要性进行排名,如下所示:筛选去前20个特征,进行后续训练和模型预测

模型训练结果

五个模型最佳参数的训练结果:

模型 训练时间 准确率
LogisticRegression 15s 27.4%
DecisionTree 0.4s 27.0%
NeuralNetwork 2.3s 26.9%
SVM 33s 26.3%
XGBoost 4.5s 25.8%

总体来说,线性回归的训练效果较好,但几种模型均在较低准确率的较小范围内波动,并没有实现较好的结果。

对于不同的特征选择模型,甚至不进行特征选择,训练准确率的差距较小,在后续调参过程中不做区分。由于DecisionTree和NeuralNetwork模型的训练效果较好并且训练时间短,本次实验将重点分析该两类模型的调参效果。

逻辑回归

迭代3000次,得到的损失函数如下所示:

该次训练结果准确率:26.8%

可以看出,模型训练的损失是逐渐递减至收敛的,但是损失的下降有限:从2.88下降到2.86,仅有2%的提升,说明逻辑回归无法较好的学习到模型哦那个的分布特征。

决策树

使用GridSearchCV寻找最优参数:

param = [{'criterion':['gini','entropy']},
         {'criterion':['gini'],'max_depth':[3,5,6],'min_samples_leaf':[2,3,5],'min_impurity_decrease':[0.1,0.2,0.5]},
         {'max_depth': [30,60,100], 'min_impurity_decrease':[0.1,0.2,0.5]}]
grid = GridSearchCV(tree.DecisionTreeClassifier(),param_grid=param,cv=6)
grid.fit(train_data,train_label)

得到的最优参数:

此时最优结果:26.8%

对参数max_depth取不同值统计准确率

重复几次即可发现,max_depth参数对准确率的影响的结果不明显,更多是随机的因素。(splitter=”random”)

同样,对于参数min_sample_leaf,也有类似的结果

神经网络

一次训练过程中的损失率:

同样,使用GridSearchCV寻找最优参数:

from sklearn.model_selection import GridSearchCV
param = [{'hidden_layer_sizes':[(50,50),(30,30),(100, )],'alpha':[0.1,0.001,0.0001],'learning_rate_init':[0.1,0.005,0.001]}]
grid = GridSearchCV(NN.MLPClassifier(),param_grid=param,cv=6)
grid.fit(train_data,train_label)

此时的准确率为26.4%

SVM

实验中,该SVM模型采用梯度下降法实现。

此次训练的准确率:25.4%

由图可见,SVM模型的训练收敛速度较快,大约10次即可收敛,损失由8500下降到了3500,但是在测试集的准确率仍较低,说明SVM可能对训练集存在过拟合,也无法较好的学习到数据集中的数据。

XGBoost

对应的参数和准确率:

boost gamma max_depth max_delta_step 准确率
gblinear - - 0 25.6%
gbtree 0 - 0 25.2%
gbtree 0 3 0 25.7%
gbtree 0.1 3 0 25.4%
gbtree 0.1 3 1 25.8%
gbtree 0 3 1 25.4%

可见,准确率最高的一组参数为:boost=’gbtree’,gamma=0.1,max_depth=3,max_delta_step=1

实验分析

从实验结果的角度分析,本次实验的实验结果较不理想,训练效果最好的一组数据为借助RFE进行特征抽取,线性回归多分类模型(3000轮迭代),最终的准确率为27.4%,其他的模型均在26%上下浮动,对于四分类问题,这样的效果无疑是较差的,根本原因在于数据特征的抽取效果较差,例如本实验中采用了三种不同的模型来进行特征选择,每次选出的特征之间的重合率较低,并未找到真正有效的特征,学习器无法学到数据的分布特征,因此无论是后续调参还是更改模型,都无法得到较好的结果。