使用哪种伽马回归模型进行外推?

机器算法验证 回归 伽马分布 助推
2022-04-01 10:13:04

我正在寻找一个能够满足这些要求的回归模型:

  1. 我的目标变量遵循指数分布,所以据我了解,我应该使用伽马损失函数。我已经尝试过XGBoostLightGBMpyGAMStatsModels GLM的这个功能,它们似乎都运行良好。
  2. 该模型应该能够至少对特征进行一些推断。在我的训练数据中,一个特征只有值 = 1、2、3...、10,我对 = 0 的预测非常感兴趣。线性模型似乎表现良好,但基于树的模型(XGBoost、LightGBM)在这方面非常糟糕。xexexe
  3. 一些非线性或其他类型的灵活性以适应输入变量之间的复杂关系。例如,使用 BayesianRidge,我在训练和测试集上获得了几乎相同的性能,这告诉我应该能够通过增加复杂性来构建更好的模型。

我的数据集包含大约一百万个样本,我使用了 10 个特征(离散的和连续的)。将来我可能会获得 10-20 个新功能。

这是我到目前为止的结果。因为我希望模型在关于输入 x 的外推上表现良好,所以我的测试集由 =1 的所有样本组成。xe

  • 线性回归 softplus link 训练损失:0.949 测试损失:0.673
  • 贝叶斯岭回归训练损失:0.931 测试损失:0.653
  • 带有 softplus 链接的 Bayesin Ridge 训练损失:0.949 测试损失:0.673
  • 弹性网络训练损失:0.930 测试损失:0.660
  • 带有 softplus 链接的弹性网络 训练损失:0.946 测试损失:0.655
  • Elastic Net CV 训练损失:0.931 测试损失:0.654
  • 带有日志链接的 Elastic Net CV 训练损失:0.984 测试损失:0.688
  • 带有 softplus 链接的 Elastic Net CV 训练损失:0.947 测试损失:0.664
  • 具有日志链接和伽马损失的 GLM 训练损失:0.927 测试损失:0.657
  • 具有日志链接伽马损失的 GAM 训练损失:0.927 测试损失:0.657
  • LGBMRegressor (max_depth 3) 训练损失:0.899 测试损失:0.678

所有模型在测试集上的损失似乎都比训练集好得多。通常这表明过度拟合,但我认为原因是其他值处的值xexe

最初,我进行了完全随机的训练测试拆分。基于这些结果,LGBM 是插值模型,但现在很明显,其他模型在外插方面的表现优于它。基于这些结果,当前的模型都没有过拟合。我认为该模型应该只对具有线性或其他非常简单的依赖关系,但可能会增加模型在其他特征方面的复杂性。xe

2个回答

OP 在探索各种不同的技术方面做得很好。正如所评论的,鉴于响应变量是 Gamma 分布的,因此考虑 GLM 和/或 GAM 用于 Gamma 分布变量是有意义的。特别是对于 GAM 的使用,如果计算负担看起来太多,我们可能要考虑限制 GAM 使用的基函数(在pyGAM此处使用的情况下,通过设置s(..., n_splines=X)whereX小于默认值来实现。20

纠正的要点是使用评估每种方法的误差。通过交叉验证进行的简单随机重采样为我们提供了“插值”而不是“外推”性能的指示。在这里,给定,我们专注于预测因此,在我们的验证集中xe={1,,10}xe=0xe=1xe={2,,10}在我们的训练集中。请注意,样本内错误对于外推任务具有相当大的误导性;没有“过度拟合”本身,因为验证集和训练集没有指代相同的样本/总体。就此而言,我们将简单模型(弹性网络回归和贝叶斯岭回归)作为我们表现最佳的例程这一事实并不令人惊讶。当外推时,大多数赌注都关闭了(例如,请参阅 CV 线程:外推有什么问题?)并且通常简单的方法胜过复杂的方法(例如,请参阅 CV 线程Best method for short time-series)。

最后一点,对我们的性能指标的可变性进行估计总是谨慎的。如果可能的话,我们应该留出一些观察结果,将我们的候选模型拟合到剩余的数据中,并在我们留出的数据中评估模型。这应该重复多次。实际上,所描述的是用于模型选择的嵌套交叉验证;唯一特别的是,对于每个循环,保留集是这样的(再一次)CV 有一个很好的线索:模型选择的嵌套交叉验证模型选择和交叉验证:正确的方法xe=1. 简而言之,外部循环将用于评估特定模型的性能(例如 Ridge 回归),而内部循环将用于选择最佳模型(Ridge 情况下的正则化参数)。嵌套 CV 的简化且非常简洁的 Python 示例如下:λ

myS = cross_val_score(GridSearchCV(linear_model.ElasticNet(), param_grid, cv=5), 
                      myX, myY, cv=5)
print("CV scores: ", myS)
print("Mean CV scores & Std. Dev.: {:.3f} {:.3f}".format(myS.mean(), myS.std()))

线性模型非常适合外推,因为它们不会做出可能无法在训练集之外泛化的过于复杂的假设。不幸的是,这也意味着他们无法充分利用外推时未更改的其他功能的全部潜力。

由于仅针对输入进行外推,我们可以将所有输入拆分为向量和矩阵xeXxeXother

一种方法是根据的值添加更多功能。这些可能是这些特征、平方值、对数等之间的交互。这可能会很好地工作,特别是如果您对每个特征应该如何对输出做出贡献有一个很好的想法。Xother

还可以构建一个自定义模型,它仅适合关于使用更复杂的关系这是一个使用 keras 构建的示例模型,该模型适合具有 4 个隐藏层的神经网络,然后将最后一个隐藏层与 x_e 连接,使用带有 softplus 激活的最后一个层(以确保预测为正)。使用 kerasxeXotherXotherxe

from keras.layers import Input, Dense, Lambda, concatenate
from keras.models import Model
import keras.backend as K
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler


def loss(y_true, y_pred):
    return -K.log(1/y_pred) + y_true/y_pred


all_inputs = Input(shape=(n_inputs,))

# Only the first feature will be used for extrapolation
x_e = Lambda(lambda x: x[:,:1], output_shape=(1,))(all_inputs) 

x_other = Lambda(lambda x: x[:,1:], output_shape=(n_inputs-1,))(all_inputs)
hidden_1 = Dense(20, activation='relu')(x_other)
hidden_2 = Dense(20, activation='relu')(hidden_1)
hidden_3 = Dense(10, activation='relu')(hidden_2)
hidden_4 = Dense(20, activation='relu')(hidden_3)

merged = concatenate([hidden_3, x_e])
preds = Dense(1,activation='softplus')(merged)

model = Model(inputs=all_inputs, outputs=preds)
model.compile(optimizer='adam', loss=loss, metrics=['mse'])

model = Pipeline([
        ('scale', StandardScaler()),
        ('keras', model),
])

使用与此类似的模型(我为我的特定问题添加了一些自定义),我能够获得 0.653 的测试损失。到目前为止,该模型似乎也是训练集中插值的最佳模型。