背景
该文章提供了一种进阶的PDP可视化绘制,如图d、e、f、g,进阶的 PDP(部分依赖图)可视化,其特点在于提供了多维度的数据信息表达,使解释模型预测结果更加直观:
- PDP 曲线(虚线):部分依赖图展示了特征变量对模型输出的平均影响,通过固定其他变量,专注于特定特征的影响趋势
- 拟合曲线(实线):平滑后的曲线进一步清晰地揭示了特征变量与目标变量的非线性关系
- 置信区间(阴影部分):通过绘制误差范围来增强图表的统计解释性,帮助评估趋势的可靠性
通过这种设计,图表不仅展示了模型预测的结果趋势,还结合统计置信度和数据平滑技术,兼具科学性与直观性,这种可视化形式非常适合用于解释机器学习模型的复杂行为,特别是在环境科学、医学等领域的实际应用中,当然,在实现这种可视化之前,希望读者已经对 PDP(部分依赖图)和 ICE(个体条件期望)的基础概念和绘制原理有所了解,如果你还不熟悉这些内容,可以先阅读——PDP(部分依赖图)、ICE(个体条件期望)解释机器学习模型保姆级教程,帮助夯实基础,接下来,再从代码实现的角度,逐步解析如何生成这种进阶可视化图表
代码实现
数据整理
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['axes.unicode_minus'] = False
df = pd.read_excel('2024-11-19-公众号Python机器学习AI.xlsx')
from sklearn.model_selection import train_test_split, KFold
X = df.drop(['y'],axis=1)
y = df['y']
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
从 Excel 文件中读取数据,提取特征和目标变量,并将数据划分为训练集和测试集,为后续机器学习模型的训练和解释 做好准备,当然作者这里是模拟数据不是文献中的原始数据
模型构建
from hyperopt import fmin, tpe, hp
from xgboost import XGBRegressor
from sklearn.metrics import mean_squared_error
# 定义超参数搜索空间
parameter_space_xgb = {
'n_estimators': hp.choice('n_estimators', [50, 100, 200, 300]), # 决策树数量
'max_depth': hp.choice('max_depth', [3, 5, 10, 15]), # 树的最大深度
'learning_rate': hp.uniform('learning_rate', 0.01, 0.3), # 学习率
'subsample': hp.uniform('subsample', 0.5, 1.0), # 每棵树的样本采样比例
'colsample_bytree': hp.uniform('colsample_bytree', 0.5, 1.0), # 每棵树的特征采样比例
'gamma': hp.uniform('gamma', 0, 5) # 剪枝所需的最小损失减少量
}
# 定义目标函数
def objective(params):
# 使用超参数创建XGBoost回归模型
model = XGBRegressor(
n_estimators=params['n_estimators'],
max_depth=params['max_depth'],
learning_rate=params['learning_rate'],
subsample=params['subsample'],
colsample_bytree=params['colsample_bytree'],
gamma=params['gamma'],
random_state=42
)
# 在训练集上拟合模型
model.fit(X_train, y_train)
# 在测试集上预测
y_pred = model.predict(X_test)
# 计算均方误差(MSE)
mse = mean_squared_error(y_test, y_pred)
# 返回MSE,Hyperopt会最小化该目标值
return mse
# 运行超参数优化
best_params = fmin(
fn=objective, # 优化的目标函数
space=parameter_space_xgb, # 搜索空间
algo=tpe.suggest, # 贝叶斯优化算法
max_evals=100 # 最大评估次数
)
# 显示最优超参数组合
print("Best hyperparameters:", best_params)
# 使用最佳超参数组合重新训练模型
best_model_regression = XGBRegressor(
n_estimators=[50, 100, 200, 300][best_params['n_estimators']],
max_depth=[3, 5, 10, 15][best_params['max_depth']],
learning_rate=best_params['learning_rate'],
subsample=best_params['subsample'],
colsample_bytree=best_params['colsample_bytree'],
gamma=best_params['gamma'],
random_state=42
)
# 在训练集上训练模型
best_model_regression.fit(X_train, y_train)
通过 Hyperopt 进行贝叶斯优化,搜索 XGBoost 回归模型的最佳超参数组合,并使用该组合重新训练模型以最小化均方误差(MSE), 通过 Hyperopt 进行贝叶斯优化时,搜索得出的最佳超参数组合可能会返回类似 'n_estimators': 2 的结果,但在定义的超参数搜索空间中并没有直接将 n_estimators 设置为 2。实际上,这是因为在使用 hp.choice 定义搜索空间时,Hyperopt 返回的是列表中元素的索引,而不是具体的值,因此,当输出 'n_estimators': 2 时,这表示选择的是列表中索引为 2 的值,即 200,这种行为源于 hp.choice 的索引机制,而具体的决策树数量仍然由列表中相应的值决定,此外,由于贝叶斯优化的迭代过程具有随机性,每次搜索的结果可能会有所不同
基础PDP、ICE绘制
PartialDependenceDisplay.from_estimator 的核心参数与用法
PartialDependenceDisplay.from_estimator(
estimator, # 1. 估计器对象,通常是已经训练好的模型,例如决策树、随机森林、XGBoost 等。
X, # 2. 数据集,通常是验证集或测试集,用于计算特征的偏依赖。
features, # 3. 要绘制偏依赖图的特征。可以是特征名列表、特征索引,或特征名/索引的组合。
kind='average', # 4. 类型,默认为 'average',表示绘制平均偏依赖图;也就是PDP
# 其他可选值 'individual' 表示每个样本的单独偏依赖效果。也就是ICE
feature_names=None, # 5. 特征名映射,默认 `None`,直接使用 `X` 中的列名或特征索引。
# 可指定字典将索引映射为可读的名称。
grid_resolution=100, # 6. 网格分辨率,表示对特征的离散化步数,默认是 100。
# 值越高,曲线越平滑,但计算时间也越长。
percentiles=(0.05, 0.95),
# 7. 百分位数,用于限制连续特征值的绘制范围。
# 默认为 (0.05, 0.95),即特征值范围为第5%到第95%的值。
method='brute', # 8. 计算方法,默认为 'brute',直接计算偏依赖;
# 对于支持树的模型(如随机森林),还可设置为 'recursion' 提升性能。
subsample=1.0, # 9. 数据子采样比例,默认为 `1.0` 表示不采样;
# 可以通过设置小于 1 的值随机采样数据以加速计算。
n_jobs=None, # 10. 并行计算的线程数,默认为 `None`(单线程)。
# 设置为 -1 时,使用所有可用的 CPU 核心。
random_state=None, # 11. 随机种子,用于在 `subsample` 进行采样时保证结果可重复。
verbose=0, # 12. 冗余信息等级,默认为 0,不打印任何信息;
# 设置为 >0 时打印进度信息。
line_kw=None, # 13. 折线样式参数,传递给 matplotlib 的 `plot` 方法。
# 例如 `{'color': 'blue', 'linewidth': 2}`。
ax=None, # 14. Matplotlib 的轴对象。如果未提供,则自动创建新图。
**kwargs # 15. 其他绘图相关的参数,用于传递给 matplotlib。
)
使PartialDependenceDisplay.from_estimator参数方法生成模型的部分依赖图(PDP 或 ICE),以可视化特征对目标变量的独立影响
PDP绘制
from sklearn.inspection import PartialDependenceDisplay
features = ['X_1'] # 替换为你要绘制的特征
# model_lgb为训练模型 X_val为验证集 kind为average代表绘制PDP
PartialDependenceDisplay.from_estimator(best_model_regression, X_test, features, kind='average', grid_resolution=50)
plt.savefig("1.pdf", format='pdf', bbox_inches='tight', dpi=1200)
plt.show()
通过绘制部分依赖图(PDP)来可视化模型中X_1特征对目标变量的独立影响
ICE绘制
PartialDependenceDisplay.from_estimator(best_model_regression, X_test, features, kind='individual', grid_resolution=50)
plt.savefig("2.pdf", format='pdf', bbox_inches='tight', dpi=1200)
plt.show()
绘制X_1特征的个体条件期望(ICE)图,展示每个样本对目标变量的单独影响,它与PDP(部分依赖图)的关系在于:PDP是对所有ICE曲线的平均结果,因此ICE提供了个体级别(单样本)的偏依赖效果,而 PDP 则总结了整体趋势
PDP可视化进阶置信区间+拟合曲线
进阶可视化实现
"""
绘制一个或多个特征的偏依赖图(Partial Dependence Plot)。
参数:
- model: 训练好的机器学习模型
- X: 特征数据 (DataFrame)
- features: 要绘制的特征,可以是一个字符串(单个特征)或列表(多个特征)
- grid_resolution: 网格分辨率(默认50)
- sample_size: Rugplot中用于展示分布的样本数(默认100)
"""
plot_partial_dependence(best_model_regression, X_test, 'X_1')
定义函数实现绘制机器学习模型对指定特征的偏依赖图(Partial Dependence Plot),包括PDP、平滑实线以及样本分布的边际Rugplot、置信区间,代码与数据集获取:如需获取本文完整的源代码和数据集,请添加作者微信联系 ,接下来逐一解读这些部分原理实现
提取偏依赖数据用于可视化分析
pdp_result = partial_dependence(best_model_regression, X_test, 'X_1', kind="both", grid_resolution=50)
plot_x = pd.Series(pdp_result.grid_values[0]).rename('x') # 特征值
plot_y = pd.Series(pdp_result.average[0]).rename('y') # 平均偏依赖数据
plot_i = pdp_result.individual # 个体曲线数据
pdp_result
pdp_result包含三部分信息,分别是:
- grid_values:一个数组,表示特征的值范围(x 轴上的网格点),这些是偏依赖图中等距采样的特征值,用于计算偏依赖关系
- average:一个数组,表示每个网格点上所有样本的平均预测结果,这是平均偏依赖曲线对应的 y 轴值,也就是PDP绘制所需要的信息
- individual:一个数组,包含每个网格点上各样本的个体预测结果,这是个体偏依赖曲线对应的 y 值,也就是ICE绘制所需要的信息
接下来提取出pdp_result的三部分信息,便于后续分析和绘图,plot_x:包含所有网格点(特征值),
plot_y:每个网格点对应的平均偏依赖值,individual_curves:每列表示一个网格点的个体预测值,每行表示一个样本
平滑曲线绘制实现
# 平滑曲线
tck = splrep(plot_x, plot_y, s=30)
xnew = np.linspace(plot_x.min(), plot_x.max(), 300)
ynew = splev(xnew, tck, der=0)
# 绘制平均偏依赖曲线
plt.plot(xnew, ynew, linewidth=2)
plt.savefig("3.pdf", format='pdf', bbox_inches='tight', dpi=1200)
plt.show()
通过样条插值(splrep 和 splev)对平均偏依赖曲线进行平滑处理,利用特定的平滑参数(s=30)生成一条更加光滑的曲线,同时在指定范围内重新采样300个点来提高曲线的细腻度,最后将平滑后的曲线绘制出来,这样做的目的是更直观和美观地展示平均偏依赖关系,也就是PDP
绘制PDP及置信区间
# 准备个体数据
plot_df_list = []
if plot_i is not None: # 如果存在个体偏依赖数据
for a in plot_i[0]:
a2 = pd.Series(a)
df_i = pd.concat([plot_x, a2.rename('y')], axis=1)
plot_df_list.append(df_i)
plot_df = pd.concat(plot_df_list, ignore_index=True)
else:
plot_df = pd.DataFrame(columns=['x', 'y'])
sns.lineplot(data=plot_df, x="x", y="y", color='k', linewidth=1.5, linestyle='--', alpha=0.6)
plt.savefig("4.pdf", format='pdf', bbox_inches='tight', dpi=1200)
plt.show()
在理解这个可视化之前,需要对sns.lineplot函数有一定了解,sns.lineplot是Python中Seaborn库提供的一个用于绘制线图(line plot)的函数,其优势在于 自动聚合 :当多个点具有相同的x值时,lineplot自动计算均值, 置信区间 :默认显示置信区间,帮助了解数据的不确定性, 类别区分 :通过 hue、size 和 style 等参数,轻松区分数据类别,当然这里的第三个优势这里使用不到,接下来看看我们传入的参数
print("数据集维度:",X_test.shape)
plot_df
因为前文设置的网格分辨率grid_resolution=50,其测试集存在400个样本,所以很明显plot_df所存储的信息是400个样本各自的ICE信息,x是每50个数据点重复一次,y是每个样本在x对应下的目标值
sns.lineplot(data=plot_df.iloc[0:50], x="x", y="y", color='k', linewidth=1.5, linestyle='--', alpha=0.6)
plt.savefig("5.pdf", format='pdf', bbox_inches='tight', dpi=1200)
plt.show()
如这里可以提取第一个样本的数据绘制个体条件期望(ICE)图,展示第一个样本对目标变量的单独影响,最后结合sns.lineplot函数的自动聚合、置信区间,以及PDP是对所有ICE曲线的平均结果的原理,绘制最终结果
所以可视化里面的虚线实际上就是PDP,阴影是sns.lineplot函数对于重复的 x 值,sns.lineplot 会对相应的 y 值求均值(PDP),并基于这些均值计算置信区间,也就是PDP的置信区间,也可与进一步理解为ICE在这个区间类波动,最后组合这些就得到了文献中的进阶PDP可视化
绘制所有特征
可视化
通过本文的讲解,相信读者已经清楚地掌握了通过结合 PDP 可视化的进阶用法和拟合曲线的展示,不仅能直观地理解模型如何捕捉变量与目标之间的关系,还能通过置信区间评估模型预测的不确定性,为决策提供更加可靠的支持,代码与数据集获取:如需获取本文完整的源代码和数据集,请添加作者微信联系
往期推荐
复现SCI文章 SHAP 依赖图可视化以增强机器学习模型的可解释性
复现 Nature 图表——基于PCA的高维数据降维与可视化实践及其扩展
复现Nature图表——基于PCA降维与模型预测概率的分类效果可视化
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~
个人观点,仅供参考