全新神经网络架构KAN回归分析:PDP(部分依赖图)、ICE(个体条件期望)解释教程

由于复制粘贴会损失图片dpi请移步公众号原文观看获得更好的观感效果(关注公众号获得更多优质文章及数据)

全新神经网络架构KAN回归分析:PDP(部分依赖图)、ICE(个体条件期望)解释教程 (qq.com)icon-default.png?t=N7T8https://mp.weixin.qq.com/s?__biz=Mzk0NDM4OTYyOQ==&mid=2247485291&idx=1&sn=272deb538f16eb699079e62fa5748559&chksm=c3242bf9f453a2ef954fe7fe078092c7063c8497c5d616d1af6d6bd37fc6b33199651de4837c&token=1010973939&lang=zh_CN#rd

Kolmogorov-Arnold 网络 (KAN) 是多层感知器 (MLP) 的最有可能的替代品,KAN 与 MLP 一样具有强大的数学基础:MLP 基于通用近似定理,而 KAN 基于 Kolmogorov-Arnold 表示定理,KAN 和 MLP 是双重的:KAN 在边缘具有激活函数,而 MLP 在节点上具有激活函数,这个简单的变化使KAN在模型准确性和可解释性方面都比MLP更好(有时要好得多!)具体参考往期文章KAN:Kolmogorov–Arnold Networks分类模型实现,接下来将利用KAN建立回归模型并对该模型进行PDP(部分依赖图)、ICE(个体条件期望)解释,目前并没有相关库支持直接对KAN模型进行相关的解释如SHAP、PDP、ICE解释等,这里将根据PDP、ICE的原理进行实现,具体的原理参考往期文章PDP(部分依赖图)、ICE(个体条件期望)解释机器学习模型保姆级教程

代码实现

数据读取并处理

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt 
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False
df = pd.read_excel('数据.xlsx')

from sklearn.model_selection import train_test_split
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)
# 将 DataFrame 和 Series 转换为 np.array
# 数据集标准化
x_mean = X_train.mean()
x_std =  X_train.std()
y_mean = y.mean()
y_std = y.std()
X_train = (X_train - x_mean)/x_std
y_train = (y_train-y_mean)/y_std
X_test = (X_test - x_mean)/x_std
y_test = (y_test - y_mean)/y_std

import torch
X_trai = X_train.to_numpy()
X_tes = X_test.to_numpy()
y_trai = y_train.to_numpy()
y_tes = y_test.to_numpy()
# 转换为pytorch张量
dataset = {}
dataset['train_input'] = torch.from_numpy(X_trai).double()
dataset['test_input'] = torch.from_numpy(X_tes).double()
dataset['train_label'] = torch.from_numpy(y_trai[:, None]).double()
dataset['test_label'] = torch.from_numpy(y_tes[:, None]).double()

读取Excel数据,分割数据集为训练集和测试集,将特征和目标变量标准化后,并转换为PyTorch张量,以便于KAN模型的训练

KAN模型建立

from kan import *
# initialize KAN with G=3
model = KAN(width=[8,2,1], grid=3, k=3)
model(dataset['train_input'])
model.plot(beta=100)

这里创建一个KAN:8D输入(自变量),1D输出(因变量),2个隐藏的神经元,三次样条 (k=3),3个网格间隔 (grid=3),读者可以利用网格细化来最大限度地提高 KAN 的拟合功能能力,修改网格间隔得到更细粒度的KAN,以及修改其它参数来增加模型拟合度,这里就不去展示如何去进行模型调参,接下来训练模型

model.train(dataset, opt="LBFGS", steps=20)
model.plot(beta=100)

训练模型后对模型进行可视化,对比模型初始可视化可以发现激活函数明显不一样了,这就是KAN对激活函数学习的一个结果,相应的会在代码文件下生成文件figures,为各个激活函数的详细可视化,这里的坐标为各个激活函数位置存在一一对应的关系,具体参考文末给出的链接,通过这些坐标可以对模型可视化进行修改等

接下来输出这个模型具体的公式

模型公式输出

lib = ['x','x^2','x^3','x^4','exp','log','sqrt','tanh','sin','tan','abs']
model.auto_symbolic(lib=lib)
formula = model.symbolic_formula()[0][0]
formula

图片

模型预测评价

prediction = []

def acc(formula, X):
    batch = X.shape[0]  # 获取批量大小
    for i in range(batch):
        subs_dict = {
            'x_1': X[i, 0],
            'x_2': X[i, 1],
            'x_3': X[i, 2],
            'x_4': X[i, 3],
            'x_5': X[i, 4],
            'x_6': X[i, 5],
            'x_7': X[i, 6],
            'x_8': X[i, 7]
        }
        # 使用给定的公式对当前样本进行预测,并将结果转换为浮点数
        predict = float(formula.subs(subs_dict))
        prediction.append(predict)  # 将预测结果添加到列表中   
    return prediction

test_pred = acc(formula, dataset['test_input'])

y_test_h = y_test*y_std+y_mean
pred_test_h = np.array(test_pred)*y_std+y_mean

import seaborn as sns
colors = sns.color_palette("husl", 3)

plt.figure(figsize=(15,5),dpi=300)
plt.scatter(y_test_h, pred_test_h, label='测试集', alpha=0.3, color=colors[2])
plt.xlabel('真实值')
plt.ylabel('预测值')
plt.legend()
plt.tight_layout()
plt.show()

from sklearn import metrics
mse = metrics.mean_squared_error(y_test_h, pred_test_h)
rmse = np.sqrt(mse)
mae = metrics.mean_absolute_error(y_test_h, pred_test_h)
r2 = metrics.r2_score(y_test_h, pred_test_h)
def mean_absolute_percentage_error(y_true, y_pred):
    return np.mean(np.abs((y_true - y_pred) / y_true)) * 100
mape = mean_absolute_percentage_error(y_test_h, pred_test_h)

print(f"Mean Squared Error (MSE): {mse:.4f}")
print(f"Root Mean Squared Error (RMSE): {rmse:.4f}")
print(f"Mean Absolute Error (MAE): {mae:.4f}")
print(f"R-squared (R2): {r2:.4f}")
print(f"Mean Absolute Percentage Error (MAPE): {mape:.4f}%")

图片

到这一步我们就完成了KAN模型的评价,利用拟合的公式对测试集数据进行预测,并进行真实值、预测值散点可视化,最后输出模型各种评价指标,如果不满这个拟合效果,可以对模型进行参数调整以得到最优模型

PDP部分依赖图绘制

import seaborn as sns
# 定义部分依赖函数
def partial_dependence(feature_idx, feature_values, formula, dataset):
    predictions = []
    for value in feature_values:
        subs_dict = {
            'x_{}'.format(i+1): value if i == feature_idx else dataset['test_input'][0, i]
            for i in range(dataset['test_input'].shape[1])
        }
        predict = float(formula.subs(subs_dict))
        predictions.append(predict)
    return np.array(predictions)

# dataset 是你的数据集,formula 是你的模型公式

# 选择要绘制部分依赖图的特征索引和范围
feature_index_pdp = 0  # 假设选择第一个特征进行 PDP
feature_values_pdp = np.linspace(min(dataset['test_input'][:, feature_index_pdp]), max(dataset['test_input'][:, feature_index_pdp]), num=50)

# 计算部分依赖
predictions_pdp = partial_dependence(feature_index_pdp, feature_values_pdp, formula, dataset)

# 绘制部分依赖图(PDP)
# 设置Seaborn样式
sns.set(style="whitegrid")
plt.figure(figsize=(15, 5))
plt.plot(feature_values_pdp, predictions_pdp, marker='o', linestyle='-', color='b', linewidth=2, markersize=6)
# 添加标题和轴标签,并设置字体大小
plt.title('Partial Dependence Plot (PDP) for Feature {}'.format(feature_index_pdp), fontsize=16)
plt.xlabel('Feature Value', fontsize=14)
plt.ylabel('Average Prediction', fontsize=14)
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)
plt.grid(True, linestyle='--', alpha=0.7)
# 添加背景和框架
sns.despine(left=True, bottom=True)
plt.show()

图片

由于KAN模型没有支持直接绘制PDP的库这里根据原理进行绘制PDP,这里只着重说明参数num=50代表设置分辨率为50,表示在所选特征的范围内均匀生成50个取值点,然后PDP的对象是测试集的第一个特征,其它参数代码均有注释,最后利用KAN拟合得到的公式进行预测并进行可视化,觉得可视化不美观读者可自行调整,要想详细理解这个过程最好还是要理解PDP原理PDP(部分依赖图)、ICE(个体条件期望)解释机器学习模型保姆级教程链接里存在如何解释PDP图这里就不过多阐述

ICE个体条件期望图绘制

# 定义个体条件期望(ICE)函数
def ice(feature_idx, feature_values, formula, dataset):
    ice_predictions = []
    for i in range(dataset['test_input'].shape[0]):
        individual_predictions = []
        for value in feature_values:
            subs_dict = {
                'x_{}'.format(j+1): value if j == feature_idx else dataset['test_input'][i, j]
                for j in range(dataset['test_input'].shape[1])
            }
            predict = float(formula.subs(subs_dict))
            individual_predictions.append(predict)
        ice_predictions.append(individual_predictions)
    return np.array(ice_predictions)
# 选择要绘制个体条件期望(ICE)的特征索引和范围
feature_index_ice = 0  # 假设选择第一个特征进行 ICE
feature_values_ice = np.linspace(min(dataset['test_input'][:, feature_index_ice]), max(dataset['test_input'][:, feature_index_ice]), num=10)

# 计算个体条件期望(ICE)
ice_predictions = ice(feature_index_ice, feature_values_ice, formula, dataset)

# 绘制个体条件期望(ICE)图
plt.figure(figsize=(15, 5))
for i in range(len(ice_predictions)):
    plt.plot(feature_values_ice, ice_predictions[i], color='grey', alpha=0.2)
plt.xlabel('Feature Value')
plt.ylabel('Prediction')
plt.title('Individual Conditional Expectation (ICE) Plot for Feature {}'.format(feature_index_ice))
plt.grid(True)
plt.show()

图片

2D PDP绘制

from mpl_toolkits.mplot3d import Axes3D
# 定义二维部分依赖函数
def partial_dependence_2d(feature_idx1, feature_idx2, feature_values1, feature_values2, formula, dataset):
    predictions = np.zeros((len(feature_values1), len(feature_values2)))
    for i, value1 in enumerate(feature_values1):
        for j, value2 in enumerate(feature_values2):
            subs_dict = {
                'x_{}'.format(k+1): value1 if k == feature_idx1 else value2 if k == feature_idx2 else dataset['test_input'][0, k]
                for k in range(dataset['test_input'].shape[1])
            }
            predict = float(formula.subs(subs_dict))
            predictions[i, j] = predict
    return predictions
# 选择要绘制二维部分依赖图的特征索引和范围
feature_index1 = 0  # 第一个特征索引
feature_index2 = 1  # 第二个特征索引
feature_values1 = np.linspace(min(dataset['test_input'][:, feature_index1]), max(dataset['test_input'][:, feature_index1]), num=10)
feature_values2 = np.linspace(min(dataset['test_input'][:, feature_index2]), max(dataset['test_input'][:, feature_index2]), num=10)
# 计算二维部分依赖
predictions_2d_pdp = partial_dependence_2d(feature_index1, feature_index2, feature_values1, feature_values2, formula, dataset)
# 绘制二维部分依赖图(2D PDP)
sns.set(style="whitegrid")
fig = plt.figure(figsize=(14, 10))
ax = fig.add_subplot(111, projection='3d')
# 创建网格
X, Y = np.meshgrid(feature_values1, feature_values2)
surf = ax.plot_surface(X, Y, predictions_2d_pdp, cmap='viridis', edgecolor='k', linewidth=0.5, alpha=0.9)
fig.colorbar(surf, ax=ax, shrink=0.5, aspect=5, pad=0.1)
ax.set_xlabel('Feature {}'.format(feature_index1), fontsize=14, labelpad=10)
ax.set_ylabel('Feature {}'.format(feature_index2), fontsize=14, labelpad=10)
ax.set_zlabel('Average Prediction', fontsize=14, labelpad=10)
ax.set_title('2D Partial Dependence Plot (PDP) for Features {} and {}'.format(feature_index1, feature_index2), fontsize=16, pad=20)
ax.tick_params(axis='both', which='major', labelsize=12)
# 设置视角
ax.view_init(elev=60, azim=120)
plt.tight_layout()
plt.show()

图片

这里的2D PDP和通过PartialDependenceDisplay绘制的2D PDP可视化并不是一致,仅供读者参考,这里是同时改变两个选定特征的值,而固定其他特征的值,计算预测结果,从而绘制二维部分依赖图(2D PDP),展示这两个特征对预测结果的联合影响

往期推荐

KAN:Kolmogorov–Arnold Networks分类模型实现

论文可视化设计:掌握 Seaborn 15 种精美数据可视化

PDP(部分依赖图)、ICE(个体条件期望)解释机器学习模型保姆级教程

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

梯度提升集成:LightGBM与XGBoost组合预测

ARIMA与Prophet的完美结合:AutoARIMAProphet时序模型

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

欢迎关注、点赞、转发~

Python机器学习AI 个人观点,仅供参考

  • 22
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Python机器学习AI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值