实战项目--基于梯度提升树算法的多特征分类任务

123 阅读20分钟

前言

笔者在前不久参加了梧桐杯大数据创新大赛,在该赛事的省级初赛中笔者最终选用了梯度提升树模型来完成初赛要求的多特征二分类任务,那么接下来笔者就为大家讲解一下该项目以及其中用到的梯度提升树算法。

赛题任务说明

投诉预测模型需要选手从普通用户中区分出投诉用户群,本次挑战赛设置了更具挑战性的任务,相比其他竞赛,本次竞赛所提供的数据类型较多,包含但不限于客户通话信息、收入信息、流量信息、到访营业厅、拨打热线、装维服务、质差等数据,更需要考察数据处理能力的竞赛任务。

数据集说明

屏幕截图 2024-10-13 004730.png

由于涉及到用户隐私以及赛事禁令,具体数据集无法公布与给出,大家可拿其他开源数据集来跑这个模型,只要是多特征二分类的基本上没问题。

模型成果说明

经过特征选择,特征缩放,超参数调优,超参数调优,K折交叉验证和置信度阈值重训练等等实验和处理后,本团队的最终模型可以高效地从客户通话信息、收入信息、流量信息、到访营业厅、装维服务、质差等特征数据中进行学习训练,更快,更准确地判断分类输入模型的每一个用户的预测投诉情况,及时帮助运营商发现数据安全存在的隐患,及时解决问题。

算法选用模型说明

在本次比赛中,本团队首先利用Sklearn库中提供的多种机器学习模型进行简单的训练评估后得到每个模型的Baseline,通过对比选出最优模型---梯度提升决策树模型,而后对其进行特征选择,超参数调优(网格搜索法),K折交叉验证和高置信样本拟合等等实验后形成最终模型,并利用该模型完成了数据特征分析和分类的任务。下文中我会给出模型的所有源码以及相关实验的源码

一、梯度提升决策树模型简介


梯度提升决策树(Gradient Boosting Decision Tree, GBDT)是一种集成学习算法,用于回归和分类任务。它通过迭代地构建决策树来最小化损失函数,并逐步提升模型的性能。GBDT的核心思想是将多个弱学习器(通常是决策树)组合成一个强学习器。在每一步迭代中,算法会添加一棵新的决策树,这棵树尝试纠正前一轮所有树的预测残差。每棵树都是基于梯度下降方法来选择的,以优化损失函数(损失函数衡量的是模型预测值与实际值之间的差异)。GBDT算法在每一步都会更新模型,以减少这个差异。此外,GBDT还引入了正则化项,如树的深度和叶子节点的数量,以防止过拟合。这种算法在我们许多机器学习竞赛和实际应用中表现出色,因为它能够处理各种类型的数据,并且通常具有很好的预测性能。

二、模型结构说明


Source Code:

# 模型训练

# 配置环境
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score, classification_report
from sklearn.preprocessing import StandardScaler

# 加载数据
data = pd.read_csv('train.csv')
data = data.drop(columns=['USER_ID_MD5','FLAG_FLOW_OVER','FLAG_PHONE_CALL_ZC'])
# 删除第一个特征USER_ID_MD5是因为他表示的是用户的ID,于训练过程无影响无意义
# 删除其他两个特征FLAG_FLOW_OVER和FLAG_PHONE_CALL_ZC是因为在上文使用随机森林算法评估出这两个特征的影响因子过小,删除以便于构建强特征
data = data.dropna()
data.replace([np.inf, -np.inf], np.nan, inplace=True)
data.dropna(inplace=True)

# 最后一列是标签,其余是特征
X = data.iloc[:, :-1]
y = data.iloc[:, -1]

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=42)

# 特征缩放
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# 创建GBDT模型
gbdt = GradientBoostingClassifier(n_estimators=185, learning_rate=0.1, max_depth=5, random_state=42)

# 训练模型
gbdt.fit(X_train, y_train)

# 预测测试集的概率
y_proba = gbdt.predict_proba(X_test)

# 设置置信度阈值
confidence_threshold = 0.4

# 计算测试集的置信度
y_confidence = y_proba.max(axis=1)

# 筛选高置信度样本
high_confidence_samples = y_confidence > confidence_threshold

# 获取高置信度的样本和标签
X_high_confidence = X_test[high_confidence_samples]
y_high_confidence = y_test[high_confidence_samples]

# 使用高置信度样本重新训练模型
gbdt.fit(X_high_confidence, y_high_confidence)

# 再次预测测试集
y_pred = gbdt.predict(X_test)

# 评估模型
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy}")
print(classification_report(y_test, y_pred))

# 模型保存
import joblib
joblib.dump(gbdt, 'gbdt_model.pkl')

Result:

Accuracy: 0.7608174119143278
              precision    recall  f1-score   support

           0       0.78      0.73      0.76     17496
           1       0.74      0.79      0.77     17101

    accuracy                           0.76     34597
   macro avg       0.76      0.76      0.76     34597
weighted avg       0.76      0.76      0.76     34597

借助于Sklearn,Pandas 和 Numpy等软件包和库,我们团队在经过前期大量的实验之后,形成了最终的梯度提升决策树模型,下面是这个模型训练的结构。

Explain:

  1. 环境配置

    • 导入所需的Python库,包括数据处理库numpypandas,模型训练相关的sklearn库中的train_test_splitGradientBoostingClassifieraccuracy_scoreclassification_report,以及特征缩放的StandardScaler
  2. 数据加载与预处理(特征选择实验)

    • 使用pandasread_csv函数从CSV文件中加载数据,并删除不需要的列(这里指的是对于模型训练无影响无作用的列USER_ID_MD5和经过特征选择后的两个特征FLAG_FLOW_OVER,FLAG_PHONE_CALL_ZC
  3. 训练集与测试集划分

    • 将数据集中的最后一列作为标签y,其余列作为特征X。使用train_test_split函数将数据集划分为训练集(80%)和测试集(20%),并设置随机种子以保证结果的可复现性。
  4. 特征缩放

    • 使用StandardScaler对特征进行标准化处理,使得每个特征的均值为0,标准差为1。这一步对于我们这种机器学习模型算法来说是必要的,因为它们对特征的尺度敏感。
  5. 模型初始化(超参数调优实验)

    • 创建GradientBoostingClassifier实例,设置弱学习器的数量(n_estimators)、每个弱学习器的贡献度(learning_rate)、决策树的最大深度(max_depth)、随机种子(random_state) 、节点分裂所需的最小样本数(min_samples_split) 以及叶子节点的最小样本数 (min_samples_leaf) 等等参数。
    • 超参数调优:我们在实验中运用网格搜索的方法不断缩小模型最佳参数的范围从而更好地提高模型的性能。
  6. K折交叉验证(检测模型过拟合)

    • 使用cross_val_score进行5折交叉验证,一方面可以更准确更全面地评估模型的性能,另一方面也能测试模型是否过拟合,便于及时调整。
  7. 模型训练

    • 调用fit方法使用训练集数据训练模型。
  8. 预测与置信度计算

    • 使用predict_proba方法预测测试集样本的类别概率,并设置置信度阈值(confidence_threshold)。
    • 计算每个样本的置信度,即预测概率中最大值。
  9. 高置信度样本筛选并重新训练模型

    • 根据置信度阈值筛选出预测置信度较高的样本,并使用这些样本再次训练模型。
  10. 最终预测,模型评估和模型保存

    • 使用重新训练的模型对整个测试集进行预测。
    • 使用accuracy_score计算模型在测试集上的准确率。
    • 使用classification_report生成性能报告,包括精确度、召回率、F1分数等指标。
    • 使用Pythonjoblib库来保存训练好的机器学习模型
  11. 模型预测与预测结果提交

    • 调用我们训练好的模型对A/B榜的测试集数据进行预测,并在测试集的最右边增加一列用于存放每一个样本的预测结果,当所有样本数据预测结束后只保留用户ID和预测结果这两列数据,去除其他数据后提交该最终预测结果。

综上,整个模型的核心就是使用梯度提升方法来处理分类问题。我们通过设置置信度阈值并筛选高置信度样本进行重新训练来提高模型的整体性能,同时为了检测模型是否过度拟合这些高置信度样本,我们使用了K折交叉验证技术来检测模型的过拟合情况(当K折出来的准确率和模型预测的准确率存在较大差异时有较大过拟合风险)。

三、模型实验说明


3.1 特征工程(特征选择实验)

  • 考虑到强特征的构建以及模型对每个特征的学习处理情况,我们使用随机森林分类器(Random Forest Classifier)来评估特征的重要性进而对数据集特征进行选择。
  • 随机森林是一种基于决策树的集成学习方法,它通过构建多棵决策树并取其模式的平均值来提高预测准确性和控制过拟合。在训练过程中,随机森林自动为每个特征赋予一个重要性得分,这个得分基于该特征在构建单个决策树时对模型预测能力的提升程度。
  • 首先,我们对数据进行了预处理,包括移除无关特征、处理缺失值和无穷大值。然后,我们初始化了一个随机森林模型,并将其应用于特征和标签上。训练完成后,模型会输出每个特征的重要性,这些信息被存储在一个DataFrame中,并按照重要性降序排列。最后,我们使用matplotlib库生成了一个条形图,直观地展示了每个特征的重要性,其中特征按其对模型的贡献程度从高到低排列,使得最重要的特征位于图表的顶部。这种可视化有助于理解哪些特征在模型决策中起着关键作用,对于特征选择和模型解释性分析非常有用。

详细过程笔者在上一篇文章中已给出,大家可前往了解,这里笔者给出传送门--实战项目--随机森林模型评估特征的重要性

3.2 超参数调优(网格搜索实验)

Source Code:

# 实验2--超参数调优--为了找到模型最佳参数
# 利用网格搜索不断缩小模型最佳参数的范围从而提高模型性能,这里为了加快训练速度就不用K折交叉验证了,但是这个过程可能较长

# 配置环境
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score, classification_report
from sklearn.preprocessing import StandardScaler

# 加载数据
data = pd.read_csv('train.csv')

# 移除用户ID列
data = data.drop(columns=['USER_ID_MD5'])

# 检查并处理NaN值,可省去
data.dropna(inplace=True)

# 检查并处理无穷大值,可省去
data.replace([np.inf, -np.inf], np.nan, inplace=True)
data.dropna(inplace=True)

# 最后一列是标签,其余是特征
X = data.iloc[:, :-1]
y = data.iloc[:, -1]

# 特征缩放
scaler = StandardScaler()
X = scaler.fit_transform(X)

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 创建GBDT模型
gbdt = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1, max_depth=5, random_state=42,min_samples_split=2,min_samples_leaf=1)

# 设置参数网格
param_grid = {
    'n_estimators': [100, 200, 300],
    'learning_rate': [0.01, 0.1, 0.2],
    'max_depth': [3, 5, 7],
    'min_samples_split': [2, 4, 6],
    'min_samples_leaf': [1, 2, 4],
    'max_features': ['sqrt', 'log2', None],
    'subsample': [0.8, 0.9, 1.0]
}

# 使用GridSearchCV找到最佳参数
grid_search = GridSearchCV(gbdt, param_grid, cv=5, scoring='accuracy', n_jobs=-1)
grid_search.fit(X_train, y_train)

# 查看最佳参数和最佳分数
print("Best parameters:", grid_search.best_params_)
print("Best cross-validation score:", grid_search.best_score_)

# 使用最佳参数的模型进行预测
best_gbdt = grid_search.best_estimator_
y_pred = best_gbdt.predict(X_test)

# 评估模型
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy}")
print(classification_report(y_test, y_pred))

Result:

//由于这个实验模型的训练时间较长,所以结果我就不给出了,
//但是这个模型运行是完全没问题的,大家可以复制后于本地环境尝试

Explain:

  • 本模型使用的网格搜索是通过 GridSearchCV 类实现的,它是 scikit-learn 库中用于模型参数优化的一个功能强大的工具。网格搜索的核心是通过遍历预定义的一组参数,以及所有可能的参数组合,来找到使模型性能最优化的参数值。下面是模型实验中网格搜索部分的详细说明:
  1. 定义参数网格

    • param_grid 是一个字典,包含了所有需要调整的参数及其对应的取值列表。我们在该实验中定义了 n_estimators(树的数量)、learning_rate(学习率)、max_depth(树的最大深度)等参数的不同取值。
  2. 创建 GBDT 模型

    • GradientBoostingClassifier 是梯度提升分类器,它是 scikit-learn 中的一个用于分类任务的 GBDT 实现。
  3. 创建 GridSearchCV 对象

    • grid_search = GridSearchCV(gbdt, param_grid, cv=5, scoring='accuracy', n_jobs=-1) 创建了一个 GridSearchCV 对象。
    • 第一个参数是基学习器 gbdt
    • param_grid 是上面定义的参数网格。
    • cv=5 指定了交叉验证的折数为5,这意味着数据将被分为5份,模型将在其中的4份上进行训练,并在剩下的1份上进行测试,这个过程将重复5次,确保每份数据都有机会作为测试集。
    • scoring='accuracy' 指定了评分标准是准确率。
    • n_jobs=-1 表示使用所有可用的CPU核心来加速搜索过程。
  4. 执行网格搜索

    • grid_search.fit(X_scaled, y) 启动网格搜索过程,它会遍历所有参数组合,使用交叉验证来评估每一组参数的性能。
  5. 输出最佳参数和得分

    • grid_search.best_params_ 属性包含了表现最佳的参数组合。
    • grid_search.best_score_ 属性包含了最佳参数组合在交叉验证中的得分。
  6. 使用最佳参数重新训练模型

    • best_gbdt = grid_search.best_estimator_ 这行代码获取了最佳模型的实例。
    • best_gbdt.fit(X_scaled, y) 使用最佳参数重新在全部训练数据上训练模型。
  7. 预测和评估

    • y_pred = best_gbdt.predict(X_scaled) 使用最佳模型进行预测。
    • accuracy = accuracy_score(y, y_pred) 计算模型的准确率。
    • classification_report(y, y_pred) 提供了模型性能的更详细报告,包括每个类的精确度、召回率、F1分数等。

通过这个过程,我们可以不断缩小模型最佳参数的范围,从而提高模型在独立测试集上的性能。因为网格搜索是一个计算密集型过程,特别是当参数网格很大或数据集很大时,因此使用 n_jobs=-1 可以显著加速搜索过程。

3.3 K折交叉验证实验(测试模型过拟合)

Source Code:

# 模型训练

# 配置环境
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score, classification_report
from sklearn.preprocessing import StandardScaler

# 加载数据
data = pd.read_csv('train.csv')
data = data.drop(columns=['USER_ID_MD5'])
data = data.dropna()  #可省去
data.replace([np.inf, -np.inf], np.nan, inplace=True)  #可省去
data.dropna(inplace=True) #可省去

# 最后一列是标签,其余是特征
X = data.iloc[:, :-1]
y = data.iloc[:, -1]

# 特征缩放
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 创建GBDT模型
gbdt = GradientBoostingClassifier(
    n_estimators=200,  # 可以尝试增加或减少弱学习器的数量
    learning_rate=0.1,  # 可以尝试更小的值,如0.01或0.05
    max_depth=5,  # 可以尝试增加或减少决策树的最大深度
    random_state=42,
    max_features='auto',  # 可以尝试'sqrt', 'log2'或具体的数值
    min_samples_split=2,  # 可以尝试增加这个值
    min_samples_leaf=1,  # 可以尝试增加这个值
    subsample=1.0  # 可以尝试小于1的值,如0.8或0.9
)

# K折交叉验证(检验模型过拟合)
kfold = 5
cv_scores = cross_val_score(gbdt, X_scaled, y, cv=kfold, scoring='accuracy')

# 输出交叉验证结果
print(f"K-fold cross-validation scores: {cv_scores}")
print(f"Average cross-validation score: {np.mean(cv_scores)}")

# 训练模型
gbdt.fit(X_scaled, y)

# 预测测试集的概率
y_proba = gbdt.predict_proba(X_scaled)

# 设置置信度阈值
confidence_threshold = 0.4

# 计算测试集的置信度
y_confidence = y_proba.max(axis=1)

# 筛选高置信度样本
high_confidence_samples = y_confidence > confidence_threshold

# 获取高置信度的样本和标签
X_high_confidence = X_scaled[high_confidence_samples]
y_high_confidence = y[high_confidence_samples]

# 使用高置信度样本重新训练模型
gbdt.fit(X_high_confidence, y_high_confidence)

# 评估模型
y_pred = gbdt.predict(X_scaled)
accuracy = accuracy_score(y, y_pred)
print(f"Accuracy: {accuracy}")
print(classification_report(y, y_pred))

Result:

K-fold cross-validation scores: [0.7276787  0.72740411 0.72709667 0.7291489  0.72265981]
Average cross-validation score: 0.7267976382500814
Accuracy: 0.7312171392069186
              precision    recall  f1-score   support

           0       0.75      0.69      0.72    174287
           1       0.71      0.77      0.74    171680

    accuracy                           0.73    345967
   macro avg       0.73      0.73      0.73    345967
weighted avg       0.73      0.73      0.73    345967

Explain: 关于测试模型是否过拟合,我们可以看:

  1. K折输出的每一个结果之间的Accuracy是否相差不大
  2. K折的所有Accuracy与模型最终的Accuracy是否相差不大

如果这两个都相差不大的话说明模型没有过拟合,还有进一步深度训练的空间

四、模型优势与创新点

1.梯度提升决策树算法:

模型采用了梯度提升决策树算法。梯度提升决策树(Gradient Boosting Decision Tree,GBDT)是一种集成学习算法,用于回归和分类任务。它通过迭代地构建决策树来最小化损失函数,并逐步提升模型的性能。GBDT的核心思想是将多个弱学习器(通常是决策树)组合成一个强学习器。在每一步迭代中,算法会添加一棵新的决策树,这棵树尝试纠正前一轮所有树的预测残差。每棵树都是基于梯度下降方法来选择的,以优化损失函数(损失函数衡量的是模型预测值与实际值之间的差异)。GBDT算法在每一步都会更新模型,以减少这个差异。此外,GBDT还引入了正则化项,如树的深度和叶子节点的数量,以防止过拟合。这种算法在我们许多机器学习竞赛和实际应用中表现出色,因为它能够处理各种类型的数据,并且通常具有很好的预测性能,所以这次我选用该算法。

2.特征工程:

特征选择:在实验过程中,本团队搭建并使用随机森林算法来评估特征的重要性,从而为构建一个强大的分类模型进行特征选择。这个过程有助于我们识别和保留对模型预测能力贡献最大的特征,从而提高模型的性能和效率。

特征缩放: 在该模型中,我们通过StandardScaler来实现特征缩放。这是一个标准化处理,它将每个特征的值转换为具有零均值和单位方差的形式。这样做的目的是为了消除不同特征之间由于量纲和数值范围不同而导致的偏差,确保模型不会因特征的数值范围差异而对某些特征产生过度依赖。特征缩放在许多机器学习算法中都是一个重要的预处理步骤,尤其是对于那些基于距离计算的算法,如我们的梯度提升决策树(GBDT)。在该模型中,特征缩放有助于提高模型的训练效率和预测性能。

3.超参数调优(网格搜索):

在本模型中,超参数调优是通过网格搜索(GridSearchCV)进行的,这是一种通过遍历预定义的参数网格来寻找最佳模型参数的方法。为了提高模型的性能,我们设置了多个可能的参数值,包括决策树的数量(n_estimators)、学习率(learning_rate)、树的最大深度(max_depth)、分裂内部节点所需的最小样本数(min_samples_split)、叶子节点的最小样本数(min_samples_leaf)、寻找最佳分割时要考虑的特征数量(max_features)以及用于拟合基学习器的样本比例(subsample)。网格搜索通过5折交叉验证来评估每一组参数组合的性能,并选择出使交叉验证得分最高的参数组合。虽然这个过程可能会耗费较长时间,但它是提高模型泛化能力和预测精度的关键步骤。最终,我们团队通过该方法找到了较为精确的最佳参数组合,大大优化了模型性能。

4.K折交叉验证:

在本模型中,我们通过GridSearchCV来实现K折交叉验证。它是一种评估模型泛化能力的技术。在交叉验证过程中,训练数据被随机分成K个大小相等的互斥子集,每次迭代中,其中一个子集被用作验证集,而剩下的K-1个子集被用作训练集。这个过程重复K次,每次选择不同的子集作为验证集。这种方法有助于减少模型评估的偏差,并提供了一个更可靠的性能估计。通过使用交叉验证,我们可以更有信心地选择出在未见数据上表现最佳的模型参数,并同时检测模型的过拟合情况。

5.置信度阈值重训练

在本模型中,置信度阈值重训练技术被用于提升模型的预测性能。首先,模型在整个训练数据集上进行训练,并预测每个样本的分类概率。然后,设置一个置信度阈值(例如0.4),只有当模型对样本的预测概率超过这个阈值时,才认为模型对该样本的预测是高置信度的。接着,从训练数据中筛选出这些高置信度的样本,并使用这些样本对模型进行再次训练。这样做的目的是让模型更加关注那些它非常有信心预测正确的样本,从而可能提高模型在这些区域的准确性。最后,使用重新训练后的模型对所有样本进行预测,并评估其性能。这项方法有助于提高模型在那些它原本就预测得很好的样本上的性能,从而提高模型的整体性能。

五、模型预测

Source Code:

# 模型预测

# 配置环境
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler

# 加载训练好的模型
import joblib
gbdt = joblib.load('gbdt_model.pkl')

# 加载新的预测数据集
test_data = pd.read_csv('test_b_x.csv')

# 检查并处理NaN值
test_data.fillna(test_data.mean(), inplace=True)  # 使用填充值填充NaN值,可省去

# 检查并处理无穷大值
test_data.replace([np.inf, -np.inf], np.nan, inplace=True)
test_data.fillna(test_data.mean(), inplace=True)  # 再次填充可能出现的NaN值,可省去

# 特征缩放
scaler = StandardScaler()
# 选择数值列进行缩放
numerical_cols = test_data.select_dtypes(include=[np.number]).columns
X_test = test_data[numerical_cols]
X_test_scaled = scaler.fit_transform(X_test)

# 使用训练好的模型进行预测
y_pred = gbdt.predict(X_test_scaled)

# 将预测结果添加到数据集中作为新的列
test_data['FLAG_USER'] = y_pred

# 保存结果
test_data.to_csv('test_b_x_with_predictions.csv', index=False)

六、Summary

以上就是笔者基于梯度提升树算法的多特征分类任务的项目具体内容,未经允许禁止转载!!!

最终该模型的实测效果得分在全国排行榜中位列--53名,还是不错的。欢迎大家点赞,收藏,交流和关注,O(∩_∩)O谢谢!