使用 scikit-learn(或任何其他 python 框架)集成不同类型的回归器

机器算法验证 回归 scikit-学习 集成学习
2022-01-15 20:29:45

我正在尝试解决回归任务。我发现 3 种模型对不同的数据子集效果很好:LassoLARS、SVR 和 Gradient Tree Boosting。我注意到,当我使用所有这 3 个模型进行预测,然后制作一个“真实输出”表和我的 3 个模型的输出时,我看到每次至少有一个模型非常接近真实输出,尽管还有 2 个模型可能比较远。

当我计算最小可能误差时(如果我从每个测试示例的“最佳”预测器中进行预测),我得到的误差远小于任何模型单独的误差。所以我考虑尝试将这 3 个不同模型的预测组合成某种集合。问题是,如何正确地做到这一点?我所有的 3 个模型都是使用 scikit-learn 构建和调整的,它是否提供了某种可用于将模型打包成集成的方法?这里的问题是我不想只对所有三个模型的预测进行平均,我想通过加权来做到这一点,其中应根据特定示例的属性确定加权。

即使 scikit-learn 不提供这样的功能,如果有人知道如何解决这个任务,那就太好了 - 为数据中的每个示例计算每个模型的权重。我认为这可能是由建立在所有这 3 个模型之上的单独回归器来完成的,它将尝试为 3 个模型中的每一个输出最佳权重,但我不确定这是否是最好的方法。

4个回答

实际上,scikit-learn确实提供了这样的功能,尽管实现起来可能有点棘手。这是一个建立在三个模型之上的平均回归器的完整工作示例。首先,让我们导入所有需要的包:

from sklearn.base import TransformerMixin
from sklearn.datasets import make_regression
from sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.neighbors import KNeighborsRegressor
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import LinearRegression, Ridge

然后,我们需要将我们的三个回归模型转换为转换器。这将允许我们将他们的预测合并到一个单一的特征向量中FeatureUnion

class RidgeTransformer(Ridge, TransformerMixin):

    def transform(self, X, *_):
        return self.predict(X).reshape(len(X), -1)


class RandomForestTransformer(RandomForestRegressor, TransformerMixin):

    def transform(self, X, *_):
        return self.predict(X).reshape(len(X), -1)


class KNeighborsTransformer(KNeighborsRegressor, TransformerMixin):

    def transform(self, X, *_):
        return self.predict(X).reshape(len(X), -1)

现在,让我们为我们的 frankenstein 模型定义一个构建器函数:

def build_model():
    ridge_transformer = Pipeline(steps=[
        ('scaler', StandardScaler()),
        ('poly_feats', PolynomialFeatures()),
        ('ridge', RidgeTransformer())
    ])

    pred_union = FeatureUnion(
        transformer_list=[
            ('ridge', ridge_transformer),
            ('rand_forest', RandomForestTransformer()),
            ('knn', KNeighborsTransformer())
        ],
        n_jobs=2
    )

    model = Pipeline(steps=[
        ('pred_union', pred_union),
        ('lin_regr', LinearRegression())
    ])

    return model

最后,让我们拟合模型:

print('Build and fit a model...')

model = build_model()

X, y = make_regression(n_features=10)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

model.fit(X_train, y_train)
score = model.score(X_test, y_test)

print('Done. Score:', score)

输出:

Build and fit a model...
Done. Score: 0.9600413867438636

为什么要麻烦以这种方式使事情复杂化?好吧,这种方法允许我们使用标准scikit-learn模块优化模型超参数,例如GridSearchCVor RandomizedSearchCV此外,现在可以轻松地从磁盘保存和加载预训练模型。

好的,在花了一些时间在谷歌上搜索之后,我发现即使使用 scikit-learn,我也可以在 python 中进行加权。考虑以下内容:

我训练了一组我的回归模型(如提到的 SVR、LassoLars 和 GradientBoostingRegressor)。然后我在训练数据上运行所有这些数据(用于训练这 3 个回归量中的每一个的相同数据)。我使用我的每个算法得到示例的预测,并将这 3 个结果保存到带有“predictedSVR”、“predictedLASSO”和“predictedGBR”列的 pandas 数据框中。我将最后一列添加到这个我称之为“预测”的数据帧中,这是一个真实的预测值。

然后我只是在这个新数据框上训练一个线性回归:

 #df - dataframe with results of 3 regressors and true output

 from sklearn linear_model
 stacker= linear_model.LinearRegression()
 stacker.fit(df[['predictedSVR', 'predictedLASSO', 'predictedGBR']], df['predicted'])

因此,当我想对新示例进行预测时,我只需分别运行 3 个回归器中的每一个,然后执行以下操作:

 stacker.predict() 

关于我的 3 个回归器的输出。并得到一个结果。

这里的问题是,我正在平均找到回归变量的最佳权重,对于我将尝试进行预测的每个示例,权重都是相同的。

如果有人对如何使用当前示例的功能进行堆叠(加权)有任何想法,很高兴听到它们。

如果您的数据有明显的子集,您可以运行类似 k-means 的聚类算法,然后将每个分类器与其表现良好的聚类相关联。当一个新的数据点到达时,确定它在哪个集群中并运行相关的分类器。

您还可以使用与质心的反距离来为每个分类器获取一组权重,并使用所有分类器的线性组合进行预测。

一旦您的所有模型都经过充分训练并表现良好,我会通过执行以下操作来完成一种加权:

  1. 在大量看不见的测试数据上运行所有模型
  2. 为每个模型在每个类的测试集上存储 f1 分数
  3. 当您使用集成进行预测时,每个模型都会为您提供最可能的类别,因此请通过该模型在该类别上的 f1 分数来加权置信度或概率。如果您正在处理距离(例如在 SVM 中),只需对距离进行归一化以获得一般置信度,然后继续进行每类 f1 加权。

您可以通过测量一段时间内的正确百分比来进一步调整您的合奏。一旦您对一个非常大的新数据集进行评分,您可以以 0.1 为步长绘制阈值,例如,如果使用该阈值进行评分,则相对于正确百分比,以了解什么阈值会给您带来 95% 的正确率对于第 1 类,依此类推。您可以在新数据进入时不断更新测试集和 f1 分数,并跟踪漂移,在阈值或准确度下降时重建模型。