MAPIE——模型不可知的预测区间估计库
MAPIE 是一个功能强大的 Python 库,具有以下显著特点:
模型不可知性
MAPIE 是模型不可知的,因此它能够与各种机器学习模型进行兼容,无论是简单的线性模型还是复杂的深度学习模型。这种灵活性使得 MAPIE 成为广泛应用于不同模型架构的理想选择。
预测区间估计
主打功能是为机器学习模型提供可靠的置信区间或预测区间。MAPIE 使用先进的统计技术,确保对模型预测的不确定性进行准确而可靠的估计。
交叉验证
MAPIE 提供了交叉验证技术,旨在提高区间估计的准确性。通过评估模型在不同数据子集上的性能,MAPIE 可以提供更为稳健的区间估计,使用户能够更全面地了解模型的预测能力。
适用于分类和回归问题
MAPIE 不仅适用于回归问题,还可以用于分类问题。无论您的任务是什么,MAPIE 都能够为您提供可靠的预测区间估计,使您对模型的输出更有信心。
易于集成和使用
MAPIE 被设计为易于与现有的机器学习工作流程集成。它提供直观的 API 和与 scikit-learn 相似的接口,使得将 MAPIE 整合到现有项目中变得简单而直观。无论您是专业数据科学家还是初学者,MAPIE 都为您提供了友好的工具,助您轻松使用预测区间估计功能。
代码示例
安装MAPIE
pip install mapie
生成余弦函数数据
import matplotlib.pyplot as plt
import numpy as np
# 定义一个函数,返回输入数组的余弦值
def np_cos(x):
return np.cos(x)
# 生成带有常数噪声的一维数据
def get_1d_data_with_constant_noise(funct, min_x, max_x, n_samples, noise):
# 设置随机数生成器的种子以保证可重复性
np.random.seed(59)
# 在指定范围内生成n_samples个均匀分布的样本点,并打乱顺序
X_train = np.linspace(min_x, max_x, n_samples)
np.random.shuffle(X_train)
# 在训练数据范围外生成更密集的测试数据点
X_test = np.linspace(min_x, max_x, n_samples * 5)
# 计算训练数据、测试数据和网格上的函数值
y_train, y_mesh, y_test = funct(X_train), funct(X_test), funct(X_test)
# 为训练数据和测试数据添加正态分布的噪声
y_train += np.random.normal(0, noise, y_train.shape[0])
y_test += np.random.normal(0, noise, y_test.shape[0])
# 返回训练数据、测试数据以及函数在网格上的值
return (
X_train.reshape(-1, 1), y_train, X_test.reshape(-1, 1), y_test, y_mesh
)
# 定义生成数据的参数
min_x, max_x, n_samples, noise = -10, 10, 200, 0.3
# 生成带有噪声的一维数据
X_train, y_train, X_test, y_test, y_mesh = get_1d_data_with_constant_noise(
np_cos, min_x, max_x, n_samples, noise
)
# 绘制散点图和拟合的曲线
plt.xlabel("x")
plt.ylabel("y")
plt.scatter(X_train, y_train, color="C9") # 绘制训练数据的散点图
_ = plt.plot(X_test, y_mesh, color="C1") # 绘制拟合的曲线
plt.show()
这段代码使用NumPy生成一维数据,包含了带有常数噪声的训练数据,并使用Matplotlib进行可视化。
模型定义
# 导入所需的第三方库
# 如果尚未安装这些库,请使用以下pip命令进行安装
# pip install scikit-learn xgboost tensorflow scikeras
from sklearn.preprocessing import PolynomialFeatures # 多项式特征处理
from sklearn.linear_model import LinearRegression # 线性回归模型
from sklearn.pipeline import Pipeline # 管道,用于将多个步骤封装成一个操作流程
from xgboost import XGBRegressor # XGBoost回归模型
from tensorflow.keras.layers import Dense # Keras神经网络模型的全连接层
from tensorflow.keras.models import Sequential # Keras神经网络模型的顺序模型
from scikeras.wrappers import KerasRegressor # 将Keras模型包装成scikit-learn兼容的回归模型
# 创建多项式回归模型的Pipeline
polyn_model = Pipeline(
[
("poly", PolynomialFeatures(degree=10)), # 添加10次多项式特征
("linear", LinearRegression(fit_intercept=False)) # 线性回归模型,fit_intercept=False表示不使用截距
]
)
# 创建XGBoost回归模型
xgb_model = XGBRegressor(
max_depth=2,
n_estimators=100,
tree_method="hist",
learning_rate=0.1
)
def mlp():
"""
创建一个包含两个隐藏层的MLP模型
"""
model = Sequential([
Dense(units=20, input_shape=(1,), activation="relu"), # 第一个隐藏层,20个神经元,ReLU激活函数
Dense(units=20, activation="relu"), # 第二个隐藏层,20个神经元,ReLU激活函数
Dense(units=1) # 输出层,1个神经元
])
model.compile(loss="mean_squared_error", optimizer="adam") # 编译模型,使用均方误差损失和Adam优化器
return model
# 使用scikeras将Keras模型包装成回归模型
mlp_model = KerasRegressor(
build_fn=mlp,
epochs=500 # 训练轮数
)
开始使用MAPIE
from mapie.regression.regression import MapieRegressor
models = [polyn_model, xgb_model, mlp_model]
model_names = ["polyn", "xgb", "mlp"]
y_pred, y_pis = {}, {}
for name, model in zip(model_names, models):
mapie = MapieRegressor(model, method="plus", cv=5)
mapie.fit(X_train, y_train)
"""
关于mapie.predict的返回值的文档↓
Returns
-------
Union[NDArray, Tuple[NDArray, NDArray]]
- NDArray of shape (n_samples,) if ``alpha`` is ``None``.
- Tuple[NDArray, NDArray] of shapes (n_samples,) and
(n_samples, 2, n_alpha) if ``alpha`` is not ``None``.
- [:, 0, :]: Lower bound of the prediction interval.
- [:, 1, :]: Upper bound of the prediction interval.
"""
y_pred[name], y_pis[name] = mapie.predict(X_test, alpha=0.05)
MapieRegressor的method参数——不同模型选择策略。
-
“naive”(天真的):
- 基于训练集的一致性得分进行选择。
- 这种方法可能是相对简单和直观的。它可能只关注模型在训练数据上的表现,而忽略了在验证集或测试集上的性能。这种方法可能会过度拟合训练数据,因为它只考虑了在模型训练时的性能。
-
“base”(基础):
- 基于验证集的一致性得分进行选择。
- 这个策略更加注重模型在验证集上的性能。验证集通常用于调整模型的超参数,因此这种方法可能会更好地反映模型在未见过的数据上的泛化性能。然而,它仍然忽略了在测试集上的表现。
-
“plus”(附加):
- 基于验证一致性得分和测试集的预测进行选择。
- 这个方法结合了验证集上的性能评估和测试集上的模型预测。这可以提供更全面的模型评估,因为它考虑了模型在未见过的数据上的表现。这个方法可能是相对更为准确的模型选择策略,特别是当我们对模型的泛化性能有更全面的了解时。
绘制结果图
def plot_1d_data(
X_train, # 训练集的输入数据
y_train, # 训练集的实际输出数据
X_test, # 测试集的输入数据
y_test, # 测试集的实际输出数据
y_sigma, # 模型预测的标准差,表示不确定性范围
y_pred, # 模型的预测输出
y_pred_low, # 模型预测的下限置信区间
y_pred_up, # 模型预测的上限置信区间
ax: plt.Axes=None, # Matplotlib轴对象,用于绘制图形
title=None # 图形标题
):
# 设置图形的x轴和y轴标签
ax.set_xlabel("x")
ax.set_ylabel("y")
# 用灰色填充真实置信区间
ax.fill_between(X_test, y_pred_low, y_pred_up, alpha=0.2)
# 绘制训练数据散点图
ax.scatter(X_train, y_train, color="black", alpha=0.2, label="Training data")
# 绘制真实置信区间
ax.plot(X_test, y_test, color="green", label="True confidence intervals")
# 用虚线绘制真实置信区间的上下限
ax.plot(X_test, y_test - y_sigma, color="gray", ls="--")
ax.plot(X_test, y_test + y_sigma, color="gray", ls="--")
# 绘制模型的预测输出及其置信区间
ax.plot(
X_test, y_pred, color="red", alpha=0.8, label="Prediction intervals"
)
# 如果提供了标题,则设置图形标题
if title is not None:
ax.set_title(title)
# 显示图例
ax.legend()
# 创建一个包含多个子图的图形
n_figs = len(model_names)
fig, axs = plt.subplots(1, 3, figsize=(15, 4))
coords = [axs[0], axs[1], axs[2]]
# 遍历模型名称和对应的子图,绘制每个模型的数据图
for name, coord in zip(model_names, coords):
plot_1d_data(
X_train.ravel(),
y_train.ravel(),
X_test.ravel(),
y_mesh.ravel(),
# 正常曲线下面积的95% 在平均值的大约1.96标准差之内。
np.full((X_test.shape[0]), 1.96 * noise).ravel(),
y_pred[name].ravel(),
y_pis[name][:, 0, 0].ravel(),
y_pis[name][:, 1, 0].ravel(),
ax=coord,
title=name
)
# 调整子图的布局以确保它们不重叠
plt.tight_layout()
# 显示图形
plt.show()
在这张图片里,填充颜色的区间是MAPIE的预测区间估计结果,上下的虚线则是95%的目标覆盖率下的噪音与生成的余弦值的结果。
对于多项式回归和极端梯度上升回归这两个训练结果良好的模型,可以看出MAPIE做出了较为准确的模型不可知的预测区间估计,而对于欠拟合的多重感知机回归模型,MAPIE也做出了相应的估计,预测出了欠拟合模型特有的预测结果不确定的区间。