当前位置: 首页 > news >正文

基于分类算法的学习失败预警(下)

文章目录

  • 前言
  • 解答
  • 1.程序的恢复
    • 1.1数据的保存
    • 1.2数据加载
    • 1.3模型搭建
    • 1.4网格搜索
  • 2.结果可视化
    • 2.1ROC曲线
    • 2.2准确率
    • 2.3查全率
    • 2.4 OOB曲线
    • 2.5参数变化可视化
  • 3.特征重要性分析
    • 3.1特征重要性统计
    • 3.2可视化
    • 3.3列名匹配
  • 备注
  • 结语

前言

书接上回,基于分类算法的学习失败预警(上),我们到底选择训练集上效果好的模型,还是测试集上效果好的模型呢?如何判断它是否过拟合了呢?本篇博客将给出答案,同时介绍一些将结果进行可视化的方法。

解答

在机器学习中,选择模型时应优先考虑测试集上效果更好的模型,而判断模型是否过拟合需要结合训练集和测试集的表现进行分析。这里我们简单回顾一下什么是训练集,什么是测试集

训练集:用于模型参数学习,目标是让模型尽可能拟合训练数据。
测试集:用于评估模型的泛化能力(即对新数据的适应能力),反映模型在真实场景中的表现。

因此可以得出一下结论:

  • 若模型在训练集和测试集上表现均优异,说明模型既充分学习了训练数据的规律,又具备良好的泛化能力。
  • 若模型在训练集上表现好、测试集上表现差,则可能存在过拟合。
    综上所述,我们应该选择在训练集上表现更好的模型,而不是通过网格搜索得出的在验证集上效果最好的模型。

验证集(Validation Set)是机器学习流程中重要的中间数据集,其核心功能是辅助模型选择和超参数调优,确保模型在最终测试前具备最佳泛化能力。

注:这里的验证集我们并没有显著划分表明,而是在训练集上通过多折交叉验证得出的

1.程序的恢复

1.1数据的保存

因为上次程序没有写完,同时从头开始运行时间又太长了,因此我们选择将预处理完成的数据进行保存(即重新运行到该步骤即可),加入如下代码将数据保存:

train=pd.concat([pd.DataFrame(x_train,columns=df_downsample.columns[:-1]),pd.DataFrame(y_train,columns=df_downsample.columns[-1:])],axis=1)
train.to_csv("train.csv",index=False)
test=pd.concat([pd.DataFrame(x_test,columns=df_downsample.columns[:-1]),pd.DataFrame(y_test,columns=df_downsample.columns[-1:])],axis=1)
test.to_csv("test.csv",index=True)

后面就基于上述文件展开操作(建议新建一个文件操作),这里index忘记改了,后面会把这列数据删除的。

1.2数据加载

将保存的数据加载进来,代码如下:

import pandas as pd

train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
train_x=train.drop(['SState'],axis=1)
train_y=train['SState']
test_x=test.drop(['SState'],axis=1)
test_x=test_x.iloc[:,1:]
test_y=test['SState']
train_x.shape,test_x.shape

运行结果:
在这里插入图片描述
这里在测试集时加了一个切片操作,主要因为之前存储的时候改了index参数

1.3模型搭建

这里采用之前选择的参数,构建随机森林,代码如下:

from sklearn.metrics import recall_score
from sklearn.ensemble import RandomForestClassifier

clasifier = RandomForestClassifier(criterion = 'entropy', random_state = 1, n_estimators = 50,max_depth=20,min_samples_leaf=4,max_features=5)
clasifier.fit(train_x, train_y)
y_pred = clasifier.predict(test_x)
recall_score(test_y, y_pred)

运行结果:

在这里插入图片描述
这里发现召回率和之前并不一样,即使将random_state=1设置和之前一样。
这里我们再次用之前的遍历方式寻找最优参数:

params={
    'criterion':['gini','entropy'],
    'n_estimators':[50,100,150,200],
    'max_depth':[5,10,15,20],
   'min_samples_split':range(2,10,2),
    'max_features':[5,10,20]
}
results=pd.DataFrame(columns=['criterion','n_estimators','max_depth','min_samples_split','max_features','recall'])
for criterion in params['criterion']:
    for n_estimators in params['n_estimators']:
        for max_depth in params['max_depth']:
            for min_samples_split in params['min_samples_split']:
                for max_features in params['max_features']:
                    clf=RandomForestClassifier(criterion=criterion,n_estimators=n_estimators,max_depth=max_depth,min_samples_split=min_samples_split,max_features=max_features,random_state=1)
                    clf.fit(train_x,train_y)
                    y_pred=clf.predict(test_x)
                    recall=recall_score(test_y,y_pred)
                    row=pd.DataFrame({'criterion':criterion,'n_estimators':n_estimators,'max_depth':max_depth,'min_samples_split':min_samples_split,'max_features':max_features,'recall':recall},index=[0])
                    results=pd.concat([results,row],ignore_index=True)
# results.sort_values(by='recall',ascending=False)
results

运行结果:
在这里插入图片描述
从结果来看,,和之前的参数相比,参数发生了很大的变化,甚至基决策树的类型也发生了变化。我们将相关参数设置成最优的参数,运行的结果却仍不相同。
在这里插入图片描述
这是什么原因呢?
因为随机森林是基于Bagging算法实现的,基学习器的数据集之间的差异以及选择特征不同导致结果可能不同。

1.4网格搜索

这里我们使用网格搜索寻找最优参数组合,代码如下:

from sklearn.metrics import make_scorer
from sklearn.model_selection import StratifiedKFold, GridSearchCV

clf=RandomForestClassifier(oob_score=True,random_state=1)
refit_score=make_scorer(recall_score)
skf=StratifiedKFold(n_splits=3)
grid_search=GridSearchCV(clf,param_grid=params,cv=skf,scoring=refit_score,n_jobs=-1)
grid_search.fit(train_x,train_y)
print(grid_search.best_params_)
print(grid_search.best_score_)

运行结果:
在这里插入图片描述
这里对模型进行评估:

from sklearn.metrics import confusion_matrix, accuracy_score, roc_auc_score, f1_score

y_pred=grid_search.predict(test_x)
# recall_score(test_y,y_pred)
print(pd.DataFrame(confusion_matrix(test_y,y_pred),columns=['pred_0','pred_1'],index=['real_0','real_1']))
print("accuracy",accuracy_score(test_y,y_pred))
print("recall",recall_score(test_y,y_pred))
print("auc",roc_auc_score(test_y,y_pred))
print("f1",f1_score(test_y,y_pred))

运行结果:
在这里插入图片描述
对比之前的模型:
在这里插入图片描述
从测试集的结果来看,两者差异并不是太大

2.结果可视化

首先,定义绘图参数、对图表标题、坐标标签字体大小等进行设置以控制输出较好的显示效果,并将参数更新到matplotlib中,具体代码如下:

from matplotlib import pyplot as plt
parms={
    'legend.fontsize': 'x-large',
    'figure.figsize': (12, 9),
    'axes.labelsize': 'x-large',
    'axes.titlesize':'x-large',
    'xtick.labelsize':'x-large',
    'ytick.labelsize':'x-large'
}
plt.rcParams.update(parms)

2.1ROC曲线

这里我们将测试集分成三份,,分别绘制三分样本的曲线,并将曲线用不同形式和色彩进行绘制。
具体代码如下:

import numpy as np
tprs=[]
aucs=[]
mean_fpr=np.linspace(0,1,100)
skf=StratifiedKFold(n_splits=3)
linetypes=['--',':','-.','-']
i=0
for train,test in skf.split(x_test,y_test):
    probas_=grid_search.predict_proba(x_test[test])
    fpr,tpr,thresholds=roc_curve(y_test[test],probas_[:,1])
    # tprs.append(interp1d(fpr,tpr)(mean_fpr))
    interp_tpr = interp1d(fpr, tpr)(mean_fpr)
    tprs.append(interp_tpr)
    # tprs.append(interp1d(mean_fpr,fpr,tpr))
    tprs[-1][0]=0.0
    roc_auc=auc(fpr,tpr)
    aucs.append(roc_auc)
    plt.plot(fpr,tpr,lw=1.5,alpha=0.8,label='ROC fold %d (AUC=%0.3f)'%(i,roc_auc),linestyle=linetypes[i])
    i+=1

plt.plot([0,1],[0,1],linestyle='--',lw=1,color='r',label='Chance',alpha=.6)
mean_tpr=np.mean(tprs,axis=0)
mean_tpr[-1]=1.0
mean_auc=auc(mean_fpr,mean_tpr)
std_auc=np.std(aucs)
plt.plot(mean_fpr,mean_tpr,color='b',label=r'Mean ROC (AUC=%0.3f $\pm$ %0.3f)'%(mean_auc,std_auc),lw=2,alpha=.8)
std_tpr=np.std(tprs,axis=0)
tprs_upper=np.minimum(mean_tpr+std_tpr,1)
tprs_lower=np.maximum(mean_tpr-std_tpr,0)
plt.fill_between(mean_fpr,tprs_lower,tprs_upper,color='grey',alpha=.15,label=r'$\pm$ 1 std. dev.')
plt.xlim([-0.02,1.02])
plt.ylim([-0.02,1.02])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
plt.legend(loc='lower right')
plt.show()

运行结果:
在这里插入图片描述

实线部分为平均ROC曲线,其下的面积用AUC的值表示,随机森林算法的AUC均值可达到0.967,可见,平均ROC曲线效果较好。

为了迭代计算随机森林评估器的准确率和查全率,将3份数据的平均准确率和平均查全率变化趋势可视化。

2.2准确率

代码如下:

results = grid_search.cv_results_
plt.plot(results['mean_train_accuracy_score'])
plt.plot(results['mean_test_accuracy_score'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['mean_train_accuracy_score','mean_test_accuracy_score'], loc='lower right')
plt.show()

运行结果:
在这里插入图片描述
随着迭代次数的变多,训练集准确率不断提高,测试集准确率也不断提高,但低于训练集的准确率

2.3查全率

代码部分:

results = grid_search.cv_results_
plt.plot(results['mean_train_recall_score'])
plt.plot(results['mean_test_recall_score'])
plt.title('model recall')
plt.ylabel('recall')
plt.xlabel('epoch')
plt.legend(['mean_train_recall_score','mean_test_recall_score'], loc='lower right')
plt.show()

运行结果:
在这里插入图片描述
通过结果发现,随着迭代次数的升高,训练集和测试集的召回率都在升高,测试集的召回率波动较大

  • 训练准确率和查全率均不断提升,在达到较高值之后提升较慢,测试准确率和查全率整体趋势与之相近,但均低于训练准确率。

2.4 OOB曲线

OOB 曲线(Out-of-Bag Curve)是随机森林模型中用于评估模型性能的一种工具,通过袋外数据(未参与单棵树训练的样本)计算误差率,并绘制误差率随决策树数量增加的变化趋势。

因为随机森林基于Bagging算法实现,每个基学习器的训练样本是从原数据集有放回采样,因此有些数据就没有被抽取到,称为袋外数据(OOB数据)。每个基学习器训练完成后,用其对应的OOB数据进行预测,累计所有基学习器的预测结果得到 OOB 误差率。
代码如下:

min_estimators= 1
max_estimators= 149
clf= grid_search.best_estimator_
errs=[]
for i in range(min_estimators,max_estimators+1):
    clf.set_params(n_estimators=i)
    clf.fit(x_train,y_train)
    oob_error=1-clf.oob_score_
    # y_pred=clf.predict(x_test)
    errs.append(oob_error)
plt.plot(errs,label='RandomForestClassifier')
plt.xlim(min_estimators, max_estimators)
plt.xlabel("n_estimators")
plt.ylabel("OOB error rate")
plt.legend(loc="upper right")
plt.show()

运行结果:
在这里插入图片描述
通过结果可以发现,随着评估器的增加,模型的误差逐渐下降,在超过80个评估器之后,基本达到最低值,其后下降趋势放缓,并趋于稳定。

2.5参数变化可视化

网格搜索过程中,使用sklearn中model_selection的validation_curve方法实现对节点再划分所需最小样本数(min_samples_split)的参数的变化情况进行分析。
代码如下:

from sklearn.model_selection import validation_curve
param_range= range(2,10)
train_scores, test_scores= validation_curve(grid_search.best_estimator_, x_train, y_train,param_name='min_samples_split',param_range=param_range,cv=3,scoring="recall", n_jobs=-1)
train_scores_mean= np.mean(train_scores, axis=1)
train_scores_std= np.std(train_scores, axis=1)
test_scores_mean= np.mean(test_scores, axis=1)
test_scores_std= np.std(test_scores, axis=1)
train_scores_mean.shape
# 设置字体为黑体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体字体
plt.title("验证曲线")
plt.xlabel("min_samples_split")
plt.ylabel("Score")
lw= 2
plt.semilogx(param_range, train_scores_mean, label="Training score",color="darkorange", lw=lw)
plt.fill_between(param_range, train_scores_mean-train_scores_std,train_scores_mean+ train_scores_std, alpha=0.2,
color="darkorange", lw=lw)
plt.semilogx(param_range, test_scores_mean, label="Cross-validation score",color="navy", lw=lw)
plt.fill_between(param_range, test_scores_mean-test_scores_std,test_scores_mean+ test_scores_std, alpha=0.2,
color="navy", lw=lw)
plt.legend(loc="best")
plt.show()

运行结果:
在这里插入图片描述
当 min_samples_split 较小时,模型在训练集和验证集上表现较均衡;但随着 min_samples_split 增大,训练集分数仍高,验证集分数下降,可能暗示模型泛化能力变差,存在过拟合倾向。
同理我们对最大深度进行统计。代码如下:

param_range= [5,10,15,20]
train_scores, test_scores= validation_curve(grid_search.best_estimator_, x_train, y_train,param_name='max_depth',param_range=param_range,cv=3,scoring="recall", n_jobs=-1)
train_scores_mean= np.mean(train_scores, axis=1)
train_scores_std= np.std(train_scores, axis=1)
test_scores_mean= np.mean(test_scores, axis=1)
test_scores_std= np.std(test_scores, axis=1)
plt.title("验证曲线")
plt.xlabel("max_depth")
plt.ylabel("Score")
lw= 2
plt.semilogx(param_range, train_scores_mean, label="Training score",color="darkorange", lw=lw)
plt.fill_between(param_range, train_scores_mean-train_scores_std,train_scores_mean+ train_scores_std, alpha=0.2,
color="darkorange", lw=lw)
plt.semilogx(param_range, test_scores_mean, label="Cross-validation score",color="navy", lw=lw)
plt.fill_between(param_range, test_scores_mean-test_scores_std,test_scores_mean+ test_scores_std, alpha=0.2,
color="navy", lw=lw)
plt.legend(loc="best")
plt.show()

运行结果:
在这里插入图片描述
随着最大深度的增加,训练集和验证集上的召回率不断增加,当max_depth=10时两者性能趋于稳定。

3.特征重要性分析

3.1特征重要性统计

对模型的输入特征进行重要性分析,并按照重要程度进行排序代码如下:

classifier = grid_search.best_estimator_
importances= classifier.feature_importances_
indices = np.argsort(importances)[::-1]
for f in range(x.shape[1]):
    print("%d. feature %d (%f)" % (f + 1, indices[f],
                                   importances[indices[f]]))
                                  

运行结果:
在这里插入图片描述

3.2可视化

为了更加详细和具体的展示重要性,采用柱状图的形式将其可视化出来,代码如下:

plt.figure()
plt.title("Feature importances")
std = np.std([tree.feature_importances_ for tree in classifier.estimators_], axis=0)
plt.bar(range(x.shape[1]),importances[indices],color="r",yerr=std[indices],align='center')
plt.xticks(range(x.shape[1]), indices)
plt.xlim([-1, x.shape[1]])
plt.show()

运行结果:
在这里插入图片描述

3.3列名匹配

为了方便直观阅读,采用如下代码将特征的序号与列名进行对应,按照从低到高的顺序进行排序,结果存于result_importances字段中。

results_importances=list(zip(df.columns[0:len(df.columns.tolist())-1],classifier.feature_importances_))
results_importances.sort(key=lambda x:x[1])
results_importances

运行结果:
在这里插入图片描述

通过结果可以看出,模型中最重要的前五个特征分别是EXAM_AH_SCORE、COURSE_WORKCOUNT、EXAM_HOMEWORK、BROWSER_COUNT、EXAM_MIDDLE_SCORE,可见,形考(EXAM_AH_SCORE)成绩对于学习成败非常关键,而剩余的其他几项均与学生的努力程度相关。

备注

如果感兴趣,可以使用其他的分类器实现类别分类,对比一下性能。下述是我对比的结果。
在这里插入图片描述
通过结果发现,还是随机森林性能综合较好。

结语

本案例的基于分类算法的学习失败预警,也是一个二分类问题,因此有相当多的模型可供选择,不限于本篇博客所举例的,本篇博客主要带你了解机器学习实践的过程,相对之前案例,增加了数据集的划分,网格搜索、数据可视化等内容。因为本人所学有限,也遇到了一些问题,欢迎交流!!!

本案例参考教材:《Python机器学习实战案例(第二版)》赵卫东、董亮

相关文章:

  • 函数的介绍
  • 降低时间复杂度---特殊方程的正整数解(双指针)
  • java八股文之常见的集合
  • DeepSeek 模型的成本效益深度解析:低成本、高性能的AI新选择
  • 深入理解【双指针】:从基础概念到实际例题
  • 【实测】单卡跑满血版DeepSeek|CSGHub集成KTransformers
  • 算法——广度优先搜索——跨步迷宫
  • Spark大数据分析与实战笔记(第四章 Spark SQL结构化数据文件处理-01)
  • Java 并发集合:ConcurrentHashMap 深入解析
  • 「C++输入输出」笔记
  • 上取整,下取整,四舍五入
  • IC/ID卡的卡号的不同格式的转换
  • created在vue3 script setup中的写法
  • redis搭建一主一从+keepalived(虚拟IP)实现高可用
  • 【8】分块学习笔记
  • 修改War包文件
  • PTA C语言程序设计 第三章
  • linux Redhat9.5采用DNS主从实现跨网段解析
  • 批量删除 PPT 中的所有图片、某张指定图片或者所有二维码图片
  • 【Java】——方法的使用(从入门到进阶)
  • 巴基斯坦外长:印巴停火
  • 解放军仪仗分队参加白俄罗斯纪念苏联伟大卫国战争胜利80周年阅兵活动
  • 印度一战机在巴基斯坦旁遮普省被击落,飞行员被俘
  • 花2万多在海底捞办婚礼,连锁餐企要抢酒楼的婚宴生意?
  • 青岛双星名人集团管理权之争:公司迁址,管理层更迭
  • 中科院院士魏辅文已卸任江西农业大学校长