一、引言
在机器学习领域,XGBoost 表现出色,具有高效性、准确性、灵活性和良好的防过拟合能力。高效性使其能快速处理大规模复杂数据,降低训练时间成本。通过组合弱学习器提高准确性和泛化能力。其支持多种任务和自定义指标,参数调优选项丰富。内置正则化机制防止过拟合。同时,SHAP 对模型解释起关键作用,能计算特征的 SHAP 值来明确特征对预测结果的贡献,帮助理解模型决策。
二、数据准备和模型训练
1. 导入所需库
为后续的工作准备好所需的工具,我们需要引入如 numpy 、pandas 用于数据处理,xgboost 用于模型构建,用于模型解释的shap,用于可视化的seaborn和matplotlib,以及 sklearn 中的一些模块用于数据划分、评估指标计算等。
from sklearn.datasets import load_iris
import pandas as pd
from sklearn.model_selection import train_test_split
import xgboost as xgb
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt
import shap
2. 加载数据集并划分
首先加载了鸢尾花数据集。然后将数据集的数据部分转换为 DataFrame
格式,并添加了目标列 target
。接着,分别定义了特征 X
和目标 y
,其中 X
是去除了 target
列的数据集,y
就是 target
列。之后,对数据集进行了两次划分。第一次将数据集按照 7:3 的比例划分为临时训练集 X_temp
、测试集 X_test
、临时训练集对应的目标 y_temp
和测试集对应的目标 y_test
,并根据 y
的分布进行分层抽样。第二次将临时训练集按照 8:2 的比例进一步划分为训练集 X_train
、验证集 X_val
、临时训练集对应的目标 y_temp
划分出的训练集目标 y_train
和验证集目标 y_val
,同样根据 y_temp
的分布进行分层抽样。这样的划分有助于在模型训练中分别进行训练、验证和最终测试,以评估模型的性能和泛化能力。例如,在训练模型时,可以使用 X_train
和 y_train
,在验证模型时使用 X_val
和 y_val
,在最终评估模型效果时使用 X_test
和 y_test
。
# 加载鸢尾花数据集
iris = load_iris()
iris_df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
iris_df['target'] = iris.target
X = iris_df.drop(['target'], axis=1)
y = iris_df['target']
# 数据集划分
X_temp, X_test, y_temp, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.2, stratify=y_temp, random_state=42)
3. 设置XGBoost参数并训练模型
首先设置了 XGBoost 模型的一系列参数,然后使用这些参数创建了一个 XGBoost 分类器模型,并在训练集上进行训练,同时在验证集上进行评估,最后在测试集上进行预测并输出评估指标报告。
# 设置 XGBoost 参数
params_xgb = {
'learning_rate': 0.1,
'booster': 'gbtree',
'objective': 'multi:softprob',
'num_class': 3,
'max_depth': 4,
'seed': 42,
'colsample_bytree': 0.8,
'subsample': 0.8,
'n_estimators': 100,
'eval_metric': 'mlogloss'
}
# 训练模型
model_xgb = xgb.XGBClassifier(**params_xgb)
model_xgb.fit(X_train, y_train, eval_set=[(X_val, y_val)], verbose=False)
参数的设置对于机器学习模型的性能至关重要,尤其是在使用像XGBoost这样的梯度提升框架时。以下是对XGBoost参数的解释和建议值的改写,更好地理解每个参数的作用和如何选择它们:
学习率(Learning Rate):
-
控制模型在每次迭代中更新的幅度。
-
较低的学习率通常意味着模型需要更多的迭代次数以达到收敛,但可以提供更稳定的收敛过程。
-
建议范围:0.01 - 0.3。
树的数量(Number of Trees):
-
也称为
n_estimators
,它决定了要构建的树的数量。 -
更多的树可以提高模型的复杂度和拟合能力,但也可能导致过拟合。
-
建议范围:100 - 1000。
树的最大深度(Max Depth):
-
限制树的深度可以防止模型过于复杂,从而避免过拟合。
-
较深的树可以捕捉更复杂的模式,但也可能捕捉到噪声。
-
建议范围:3 - 10。
子节点所需的最小权重和(Minimum Child Weight):
-
用于控制树的分裂,较大的值可以防止模型在树的深层分裂。
-
建议范围:1 - 10。
样本采样比例(Subsample):
-
控制用于训练每棵树的数据的比例,可以用于防止过拟合。
-
较低的采样比例可以引入随机性,有助于模型的泛化。
-
建议范围:0.5 - 1。
特征采样比例(Colsample By Tree):
-
控制每棵树训练时使用的特征的比例。
-
降低比例可以减少模型对特定特征的依赖,提高模型的鲁棒性。
-
建议范围:0.5 - 1。
最小损失函数下降值(Gamma):
-
指定节点分裂所需的最小损失函数下降值,较大的值可以减少树的复杂度。
-
建议范围:0 - 5。
L2正则化项(Lambda):
-
控制模型的复杂度,防止过拟合。
-
L2正则化项可以惩罚过大的权重,使模型更加平滑。
-
建议范围:0 - 5。
L1正则化项(Alpha):
-
与L2正则化类似,但L1正则化倾向于产生更稀疏的权重矩阵。
-
这有助于特征选择,因为它可以将不重要的特征权重压缩至零。
-
建议范围:0 - 5。
为了找到最佳的参数组合,可以使用自动化的超参数优化技术,如网格搜索(Grid Search)或随机搜索(Random Search),甚至更高级的方法,如贝叶斯优化(Bayesian Optimization)。这些方法可以帮助你系统地遍历参数空间,找到最佳的参数设置以提高模型的性能。
4. 预测并评估模型
对训练好的 XGBoost 模型进行预测和评估,并以多种方式展示评估结果。首先,使用 model_xgb.predict(X_test) 对测试集 X_test 进行预测,得到预测结果 y_pred 。然后,通过 print(classification_report(y_test, y_pred)) 输出分类报告,该报告包含了诸如准确率、召回率、F1 值等针对每个类别的详细评估指标,能全面了解模型在不同类别上的表现。接下来,计算测试集真实标签 y_test 和预测结果 y_pred 之间的混淆矩阵 conf_matrix 。之后,使用 plt.figure(figsize=(10, 7)) 设置绘图的尺寸。再通过 sns.heatmap 绘制混淆矩阵的热力图,annot=True 表示在图中显示数值,fmt='d' 规定数值的格式为整数,cmap='YlGnBu' 设定颜色映射。最后,为图形添加了 x 轴标签 Predicted Label 、y 轴标签 True Label 和标题 Confusion Matrix 。
# 预测并评估模型
y_pred = model_xgb.predict(X_test)
print(classification_report(y_test, y_pred)) # 输出模型报告, 查看评价指标
# 绘制混淆矩阵热力图
conf_matrix = confusion_matrix(y_test, y_pred) # 输出混淆矩阵
plt.figure(figsize=(10, 7)) # 混淆矩阵热力图
sns.heatmap(conf_matrix, annot=True, fmt='d', cmap='YlGnBu')
plt.xlabel('Predicted Label')
plt.ylabel('True Label')
plt.title('Confusion Matrix')
例如,如果混淆矩阵中主对角线上的数值较大,说明模型在大多数情况下能够正确预测类别;如果存在某行或某列的数值较大但不在主对角线上,说明模型在该类别上的预测存在较多错误。通过结合分类报告和混淆矩阵热力图,可以更直观和全面地评估模型的性能和存在的问题。
三、模型解释
5. 使用SHAP解释模型和提取每个类别的SHAP值
使用SHAP库解释模型的输出。SHAP值可以帮助我们理解每个特征对模型预测的影响。
# 使用 SHAP 解释模型
explainer = shap.Explainer(model_xgb)
shap_values = explainer(X_test)
# 提取每个类别的 SHAP 值
shap_values_class_1 = shap_values[:, :, 0]
shap_values_class_2 = shap_values[:, :, 1]
shap_values_class_3 = shap_values[:, :, 2]
首先,通过 shap.Explainer(model_xgb) 创建了一个 Explainer 对象 explainer ,用于解释 model_xgb 模型。然后,使用 explainer(X_test) 计算了测试集 X_test 的 SHAP 值,并将结果存储在 shap_values 中。接下来,通过切片操作从 shap_values 中提取出每个类别的 SHAP 值,分别存储shap_values_class_1,shap_values_class_2 和 shap_values_class_3 中。例如,如果我们想要分析某个特征对于第一类的影响程度,可以查看 shap_values_class_1 中对应特征的 SHAP 值。较大的正值表示该特征的存在增加了模型预测为第一类的可能性,较大的负值则表示降低了这种可能性。通过这种方式,可以深入理解模型是如何根据输入特征做出分类决策的。
6. 绘制每个类别的SHAP值总结图
# 绘制 SHAP 总结图
plt.figure()
plt.title('Class 1 SHAP Summary')
shap.summary_plot(shap_values_class_1, X_test, plot_type="dot", cmap="viridis")
plt.figure()
plt.title('Class 2 SHAP Summary')
shap.summary_plot(shap_values_class_2, X_test, plot_type="dot", cmap="viridis")
plt.figure()
plt.title('Class 3 SHAP Summary')
shap.summary_plot(shap_values_class_3, X_test, plot_type="dot", cmap="viridis")
用于绘制不同类别的 SHAP 总结图。首先,通过 plt.figure() 为每个图创建一个新的绘图区域。然后,为每个绘图设置标题,分别为 'Class 1 SHAP Summary' 、 'Class 2 SHAP Summary' 和 'Class 3 SHAP Summary' ,表明所绘制的是对应类别的 SHAP 总结。接着,使用 shap.summary_plot 函数绘制 SHAP 总结图。对于每个类别,传递相应的 SHAP 值(如 shap_values_class_1 、 shap_values_class_2 、 shap_values_class_3 )以及测试集 X_test 。 plot_type="dot" 表示以点的形式展示, cmap="viridis" 设定了颜色映射方案。
例如,在 'Class 1 SHAP Summary' 图中,每个点代表一个特征,其位置和颜色反映了该特征对第一类预测的影响程度和方向。通过观察这些图,可以直观地比较不同特征在不同类别中的重要性和影响模式,帮助我们理解模型是如何根据特征来区分不同类别的。
7. 绘制SHAP依赖图
#绘制 SHAP 交互图
shap.dependence_plot('petal width (cm)', shap_values.values[:, :, 0], X_test)
通过 shap.dependence_plot 函数,以 'petal width (cm)' 这个特征为例,展示了该特征与 SHAP 值(shap_values.values[:, :, 0] )之间的关系,并且基于测试集 X_test 进行绘制。
例如,如果在图中呈现出随着 petal width (cm) 值的增加,SHAP 值也呈现出明显的上升或下降趋势,这就表明这个特征对于模型的预测结果有着显著的影响。反之,如果趋势不明显或者比较平缓,则说明该特征的影响相对较小或者较为复杂。通过这种交互图,可以更直观地理解某个特定特征如何影响模型的输出。
8. 绘制SHAP交互作用图
shap_interaction_values = explainer.shap_interaction_values(X_test)
shap.summary_plot(shap_interaction_values[:, :, :, 0], X_test)
SHAP交互作用图展示了所有特征间的交互作用对预测的综合影响。每个点代表一个数据样本,点的位置和颜色表示特征值和交互值的大小。点的分布情况显示了不同特征值的交互效果和密度。点越集中,说明该特征在某个取值范围内的影响较大。
9. 创建并绘制SHAP热图
# 创建并绘制SHAP热图,展示前10个样本的SHAP值热图,解释各个特征对这些样本的预测影响。
shap_values = explainer(X_val.iloc[0:10, :])
# 需要指定个类别的基准值,这里是第一个类别的基准值
expected_value = explainer.expected_value[0]
# 创建SHAP解释对象
shap_explanation = shap.Explanation(
values=shap_values[:, :, 0][0:10, :], # 选择第一个类别的SHAP值
base_values=expected_value,
data=X_val.iloc[0:10, :],
feature_names=iris.feature_names
)
#绘制热图
plt.figure(figsize=(10, 8))
shap.plots.heatmap(shap_explanation)
plt.show()
创建并绘制了一个 SHAP 热图,用于展示鸢尾花数据集的前 10 个样本中各个特征对模型预测的影响。首先,通过 explainer(X_val.iloc[0:10, :]) 计算了验证集前 10 个样本的 SHAP 值,并将其存储在 shap_values 中。然后,获取了第一个类别的基准值 expected_value 。接着,创建了 shap.Explanation 对象 shap_explanation ,其中包含了选择的第一个类别的 SHAP 值、基准值、数据和特征名称。最后,使用 plt.figure(figsize=(10, 8)) 设置了绘图的大小,并通过 shap.plots.heatmap(shap_explanation) 绘制了热图,使用 plt.show() 显示图形。
例如,在热图中,颜色越红表示该特征对预测结果的正向影响越大,颜色越蓝表示负向影响越大。通过观察热图,可以直观地看出哪些特征对这前 10 个样本的预测结果影响较为显著,以及影响的方向是正还是负。这有助于深入理解模型在处理这些特定样本时是如何依据特征进行决策的。
三、运行结果解释
-
混淆矩阵展示了模型在测试集上的分类效果。对角线上的值表示正确分类的数量,而其他位置的值表示分类错误的数量。通过混淆矩阵,我们可以直观地了解模型的准确性和错误类型。
-
SHAP总结图展示了各个特征对不同类别的影响力。点的颜色表示特征值的大小,点的位置表示SHAP值的大小。通过这些图,我们可以看到哪些特征对模型预测的重要性最大。
-
SHAP交互图展示了特征间的交互作用对预测的影响。通过观察这些图,我们可以理解特征间的复杂关系,以及某些特征在不同特征值下的表现。
-
SHAP热图展示了多个样本的特征影响力分布情况。每个格子表示一个特征对某个样本预测的影响力大小。这可以帮助我们比较不同样本的特征影响力。