从2D到3D:部分依赖图(PDP)如何揭示特征组合对模型预测的综合影响

向量数据库

picture.image

背景

部分依赖图(PDP)是解释机器学习模型的一种工具,用来展示模型的预测结果如何随着一个或多个特征值的变化而变化,对于单个特征,PDP通过保持其他特征不变,分析该特征取值的不同对模型输出的影响,帮助理解模型如何利用该特征进行决策,sklearn.inspection库目前只支持单特征的PDP和两个特征的2D PDP可视化输出,但可以基于PDP的原理自行实现3D PDP的可视化,基础PDP参考往期文章——PDP(部分依赖图)、ICE(个体条件期望)解释机器学习模型保姆级教程

代码实现

数据加载与分割


          
import pandas as pd
          
import numpy as np
          
import matplotlib.pyplot as plt 
          
plt.rcParams['font.family'] = 'Times New Roman'
          
plt.rcParams['axes.unicode_minus'] = False
          
df = pd.read_excel('california.xlsx')
          

          
from sklearn.model_selection import train_test_split, KFold
          

          
X = df.drop(['price'],axis=1)
          
y = df['price']
          

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

picture.image

从Excel文件中读取数据,将特征变量与目标变量(房价 price)分离,并划分为训练集和测试集,用于机器学习模型的训练和评估。数据集中包括了房龄(HouseAge)、中位收入(MedInc)、平均房间数(AveRooms)等特征,目标变量为房价(price)

模型构建


          
from sklearn.metrics import root_mean_squared_error
          
from catboost import CatBoostRegressor
          

          
# CatBoost模型参数
          
params_cat = {
          
    'learning_rate': 0.02,       # 学习率,控制每一步的步长,用于防止过拟合。典型值范围:0.01 - 0.1
          
    'iterations': 1000,          # 弱学习器(决策树)的数量
          
    'depth': 6,                  # 决策树的深度,控制模型复杂度
          
    'eval_metric': 'RMSE',       # 评估指标,这里使用均方根误差(Root Mean Squared Error,简称RMSE)
          
    'random_seed': 42,           # 随机种子,用于重现模型的结果
          
    'verbose': 500               # 控制CatBoost输出信息的详细程度,每100次迭代输出一次
          
}
          

          
# 准备k折交叉验证
          
kf = KFold(n_splits=5, shuffle=True, random_state=42)
          
scores = []
          
best_score = np.inf
          
best_model = None
          

          
# 交叉验证
          
for fold, (train_index, val_index) in enumerate(kf.split(X_train, y_train)):
          
    X_train_fold, X_val_fold = X_train.iloc[train_index], X_train.iloc[val_index]
          
    y_train_fold, y_val_fold = y_train.iloc[train_index], y_train.iloc[val_index]
          

          
    model = CatBoostRegressor(**params_cat)
          
    model.fit(X_train_fold, y_train_fold, eval_set=(X_val_fold, y_val_fold), early_stopping_rounds=100)
          

          
    # 预测并计算得分
          
    y_val_pred = model.predict(X_val_fold)
          
    score = root_mean_squared_error(y_val_fold, y_val_pred)  # RMSE
          

          
    scores.append(score)
          
    print(f'第 {fold + 1} 折 RMSE: {score}')
          

          
    # 保存得分最好的模型
          
    if score < best_score:
          
        best_score = score
          
        best_model = model
          

          
print(f'最佳 RMSE: {best_score}')
      

picture.image

通过使用5折交叉验证来训练CatBoost回归模型,每一折模型都会基于训练集进行训练,并在验证集上进行评估,计算出对应的RMSE(均方根误差),在每一折训练中,模型会自动调整参数以避免过拟合,使用早停策略来选择最佳的迭代次数,最终,程序会输出每一折的RMSE得分,并保存得分最好的模型,最后输出整个交叉验证中最优模型的RMSE,表示模型在验证集上取得的最低预测误差,这一过程有助于评估模型的稳健性并防止过拟合,为PDP解释做准备

2D PDP解释


          
from sklearn.inspection import PartialDependenceDisplay
          
# 选择两个特征绘制2D PDP
          
features = ['MedInc', 'AveOccup']
          

          
# 使用 contour_kw 参数绘制2D PDP
          
fig, ax = plt.subplots(figsize=(10, 6), dpi=1200)
          
PartialDependenceDisplay.from_estimator(
          
    best_model,
          
    X_test,
          
    features=[features],
          
    kind='average',
          
    grid_resolution=50,
          
    contour_kw={'cmap': 'viridis', 'alpha': 0.8},
          
    ax=ax
          
)
          
plt.suptitle('2D Partial Dependence Plot for MedInc and AveOccup')
          
plt.savefig("2D Partial Dependence Plot for MedInc and AveOccup.pdf", format='pdf',bbox_inches='tight')
          
plt.show()
      

picture.image

使用PartialDependenceDisplay生成一个2D部分依赖图(PDP),用于展示两个特征(MedInc和AveOccup)对目标变量(房价)的影响,代码中的best_model是之前训练好的CatBoost模型,X_test是测试集中的特征数据,features指定了感兴趣的两个特征,即中位收入(MedInc)和平均家庭规模(AveOccup)

2D PDP图通过将MedInc(中位收入)和AveOccup(平均家庭规模)组合的不同值映射到房价预测结果的等高线图上,展示了模型对这两个特征共同作用的反应,图中的颜色表示房价的预测值,颜色从左到右逐渐变化,反映出随着中位收入增加,房价预测值也逐步增加,同时,等高线代表了不同的房价预测范围,等高线的数值越大,表示在这些区域中房价预测越高,通过观察可以发现,MedInc的增大对房价的影响更加显著,而AveOccup的影响相对较小,但它们的共同作用仍然对房价预测产生综合影响,这一可视化有助于直观地理解两个特征如何相互配合影响目标变量的趋势

3D PDP解释


          
from mpl_toolkits.mplot3d import Axes3D
          

          
def plot_3d_pdp_scatter(model, X, features, grid_resolution=20):
          
    """
          
    绘制三个特征的3D PDP散点图,不固定任何特征值。
          
    
          
    参数:
          
    - model: 训练好的机器学习模型(例如:随机森林,XGBoost等)。
          
    - X: 数据集(特征矩阵)。
          
    - features: 需要分析的三个特征名列表,格式为 ['feature1', 'feature2', 'feature3']。
          
    - grid_resolution: 网格分辨率,决定网格点的密度
          
    """
          
    # 获取三个特征的数据
          
    feature_1, feature_2, feature_3 = features
          
    
          
    # 构建网格
          
    feature_1_vals = np.linspace(X[feature_1].min(), X[feature_1].max(), grid_resolution)
          
    feature_2_vals = np.linspace(X[feature_2].min(), X[feature_2].max(), grid_resolution)
          
    feature_3_vals = np.linspace(X[feature_3].min(), X[feature_3].max(), grid_resolution)
          
    
          
    # 生成三维网格
          
    grid_1, grid_2, grid_3 = np.meshgrid(feature_1_vals, feature_2_vals, feature_3_vals)
          
    
          
    # 将网格数据转换为数据框的格式,传递给模型进行预测
          
    grid_points = np.c_[grid_1.ravel(), grid_2.ravel(), grid_3.ravel()]
          
    X_grid = np.zeros((grid_points.shape[0], X.shape[1]))  # 创建空白矩阵,维度与X相同
          
    X_grid[:, X.columns.get_loc(feature_1)] = grid_points[:, 0]
          
    X_grid[:, X.columns.get_loc(feature_2)] = grid_points[:, 1]
          
    X_grid[:, X.columns.get_loc(feature_3)] = grid_points[:, 2]
          
    
          
    # 对网格数据进行预测
          
    preds = model.predict(X_grid)
          
    
          
    # 绘制3D散点图
          
    fig = plt.figure(figsize=(10, 8),dpi=1200)
          
    ax = fig.add_subplot(111, projection='3d')
          
    
          
    # 使用散点图绘制三维特征,并使用预测值作为颜色
          
    sc = ax.scatter(grid_1.ravel(), grid_2.ravel(), grid_3.ravel(), c=preds, cmap='viridis', alpha=0.8)
          
    
          
    # 添加颜色条
          
    plt.colorbar(sc, ax=ax, label='Prediction')
          
    ax.set_xlabel(feature_1)
          
    ax.set_ylabel(feature_2)
          
    ax.set_zlabel(feature_3)
          
    
          
    plt.title(f'3D Partial Dependence Scatter Plot for {feature_1}, {feature_2}, and {feature_3}')
          
    plt.savefig("3D.pdf", format='pdf', bbox_inches='tight')
          
    plt.show()
          

          
features = ['MedInc', 'AveOccup', 'HouseAge']  
          
plot_3d_pdp_scatter(best_model, X_test, features)
      

picture.image

通过构建三维网格实现3D PDP(部分依赖图)的可视化,展示了三个特征(MedInc,AveOccup,HouseAge)对目标变量(房价)预测的影响,具体做法是,首先为每个特征构建取值范围并生成三维网格,然后将这些网格点组合成一组数据点,传递给训练好的模型进行预测。接着,代码使用三维散点图显示这些特征组合下的预测值,其中颜色代表房价预测值,颜色从深到浅依次表示房价从低到高,通过这种3D可视化,可以清晰地看到这三个特征对房价预测的共同作用和交互影响,例如,中位收入较高和房龄较新的区域往往房价预测较高,而家庭规模的影响较小但仍有一定作用,这种可视化方法帮助深入理解模型对多个特征组合的反应

固定特征的3D部分依赖图


          
def plot_3d_pdp(model, X, features, fixed_feature=None, fixed_value=None, grid_resolution=50):
          
    """
          
    绘制三个特征的3D PDP图,其中一个特征固定值,并加上热度条。
          

          
    参数:
          
    - model: 训练好的机器学习模型(例如:随机森林,XGBoost等)。
          
    - X: 数据集(特征矩阵)。
          
    - features: 需要分析的三个特征名列表,格式为 ['feature1', 'feature2', 'feature3']。
          
    - fixed_feature: 需要固定的特征名(默认为features列表中的第三个特征)。
          
    - fixed_value: 第三个特征的固定值(可以是其均值、中位数或任意值,默认为其均值)。
          
    - grid_resolution: 网格分辨率,决定网格点的密度。
          
    """
          
    feature_1, feature_2, feature_3 = features
          
    
          
    # 如果没有指定固定特征,默认将第三个特征固定
          
    if fixed_feature is None:
          
        fixed_feature = feature_3
          

          
    # 如果没有指定固定值,则使用该特征的均值
          
    if fixed_value is None:
          
        fixed_value = X[fixed_feature].mean()
          

          
    # 构建网格,针对两个未固定的特征
          
    if fixed_feature == feature_1:
          
        varying_features = [feature_2, feature_3]
          
    elif fixed_feature == feature_2:
          
        varying_features = [feature_1, feature_3]
          
    else:
          
        varying_features = [feature_1, feature_2]
          

          
    feature_1_vals = np.linspace(X[varying_features[0]].min(), X[varying_features[0]].max(), grid_resolution)
          
    feature_2_vals = np.linspace(X[varying_features[1]].min(), X[varying_features[1]].max(), grid_resolution)
          

          
    grid_1, grid_2 = np.meshgrid(feature_1_vals, feature_2_vals)
          

          
    # 构建网格数据,传递给模型进行预测
          
    grid_points = np.c_[grid_1.ravel(), grid_2.ravel()]
          
    X_grid = np.zeros((grid_points.shape[0], X.shape[1]))  # 创建空白矩阵,维度与X相同
          
    X_grid[:, X.columns.get_loc(varying_features[0])] = grid_points[:, 0]
          
    X_grid[:, X.columns.get_loc(varying_features[1])] = grid_points[:, 1]
          
    X_grid[:, X.columns.get_loc(fixed_feature)] = fixed_value  # 固定特征值
          

          
    # 对网格数据进行预测
          
    preds = model.predict(X_grid).reshape(grid_1.shape)
          

          
    # 绘制3D图像
          
    fig = plt.figure(figsize=(10, 8), dpi=1200)
          
    ax = fig.add_subplot(111, projection='3d')
          

          
    # 绘制等高线图
          
    surface = ax.plot_surface(grid_1, grid_2, preds, cmap='viridis', alpha=0.8)
          

          
    # 添加颜色热度条
          
    fig.colorbar(surface, ax=ax, shrink=0.5, aspect=10)
          

          
    ax.set_xlabel(varying_features[0])
          
    ax.set_ylabel(varying_features[1])
          
    ax.set_zlabel('Prediction')
          

          
    plt.title(f'3D Partial Dependence Plot for {varying_features[0]}, {varying_features[1]} with {fixed_feature}={fixed_value}')
          
    plt.savefig("3D one.pdf", format='pdf', bbox_inches='tight')
          
    plt.show()
          

          
features = ['MedInc', 'AveOccup', 'HouseAge']  
          
plot_3d_pdp(best_model, X_test, features, fixed_feature='HouseAge')
      

picture.image

在进行模型解释时,如果对所有特征进行可视化展示(如3个特征),会形成一个三维正方体图像,代表所有特征组合下的模型预测值,虽然这种全方位的可视化非常全面,但对于实际理解模型各特征如何结合影响预测来说,复杂度较高,因此,固定一个特征值可以简化分析,并展示另外两个特征的组合如何影响预测结果,通过固定某个特征,能更容易观察这两个特征对目标变量的影响,同时保持模型预测的复杂性

代码通过生成一个3D部分依赖图,固定HouseAge的特定值(默认是其均值或用户指定值),然后分析MedInc(中位收入)和AveOccup(平均家庭规模)对房价预测的影响。代码首先创建了一个包含MedInc和AveOccup的网格,将这两个特征在不同取值下组合,然后传递给模型进行预测,最后,通过3D等高线图将预测结果可视化,其中颜色表示房价的预测值

在图中,HouseAge被固定为31.14岁,表示房龄固定情况下中位收入(MedInc)和平均家庭规模(AveOccup)如何共同作用影响房价预测。颜色从深紫色到亮黄色逐渐变化,表明预测值从低到高,可以观察到,中位收入对房价预测的影响相对明显,随着MedInc的增大,房价预测值显著增加;而AveOccup的影响较为平缓,整体变化不大,通过这张图,能更直观地看到这两个特征的组合如何在特定房龄下影响房价预测

往期推荐

SCI图表复现:整合数据分布与相关系数的高级可视化策略

复现顶刊Streamlit部署预测模型APP

树模型系列:如何通过XGBoost提取特征贡献度

SHAP进阶解析:机器学习、深度学习模型解释保姆级教程

特征选择:Lasso和Boruta算法的结合应用

从基础到进阶:优化SHAP力图,让样本解读更直观

SCI图表复现:优化SHAP特征贡献图展示更多模型细节

多模型中的特征贡献度比较与可视化图解

SCI图表复现:特征相关性气泡热图展示

基于SHAP值的 BorutaShap 算法在特征选择中的应用与优化

复现SCI文章 SHAP 依赖图可视化以增强机器学习模型的可解释性

picture.image

picture.image

picture.image

微信号|deep_ML

欢迎添加作者微信进入Python、ChatGPT群

进群请备注Python或AI进入相关群

无需科学上网、同步官网所有功能、使用无限制

如果你对类似于这样的文章感兴趣。

欢迎关注、点赞、转发~

个人观点,仅供参考

0
0
0
0
关于作者

文章

0

获赞

0

收藏

0

相关资源
云原生环境下的日志采集存储分析实践
云原生场景下,日志数据的规模和种类剧增,日志采集、加工、分析的多样性也大大增加。面对这些挑战,火山引擎基于超大规模下的 Kubernetes 日志实践孵化出了一套完整的日志采集、加工、查询、分析、消费的平台。本次主要分享了火山引擎云原生日志平台的相关实践。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论