python - 来自 scikit-garden 的分位数随机森林在做出预测时非常缓慢

标签 python scikit-learn random-forest quantile-regression

我已经开始使用 scikit-garden 包中的分位数随机森林 (QRF)。之前我使用 sklearn.ensemble 中的 RandomForestRegresser 创建常规随机森林。

QRF 的速度似乎与数据集较小的常规 RF 相当,但随着数据量的增加,QRF 的预测速度比 RF 慢得多。

这是预期的吗?如果是这样,有人可以解释为什么做出这些预测需要这么长时间和/或就如何更及时地获得分位数预测提出任何建议。

请参阅下面的玩具示例,我在其中测试了各种数据集大小的训练和预测时间。

import matplotlib as mpl
mpl.use('Agg')
from sklearn.ensemble import RandomForestRegressor
from skgarden import RandomForestQuantileRegressor
from sklearn.model_selection import train_test_split
import numpy as np
import time
import matplotlib.pyplot as plt

log_ns = np.arange(0.5, 5, 0.5) # number of observations (log10)
ns = (10 ** (log_ns)).astype(int)
print(ns)
m = 14 # number of covariates
train_rf = []
train_qrf = []
pred_rf = []
pred_qrf = []

for n in ns:
    # create dataset
    print('n = {}'.format(n))
    print('m = {}'.format(m))
    rndms = np.random.normal(size=n)
    X = np.random.uniform(size=[n,m])
    betas = np.random.uniform(size=m)
    y = 3 +  np.sum(betas[None,:] * X, axis=1) + rndms

    # split test/train
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

    # random forest
    rf = RandomForestRegressor(n_estimators=1000, random_state=0)
    st = time.time()
    rf.fit(X_train, y_train)
    en = time.time()
    print('Fit time RF = {} secs'.format(en - st))
    train_rf.append(en - st)

    # quantile random forest
    qrf = RandomForestQuantileRegressor(random_state=0, min_samples_split=10, n_estimators=1000)
    qrf.set_params(max_features = X.shape[1] // 3)
    st = time.time()
    qrf.fit(X_train, y_train)
    en = time.time()
    print('Fit time QRF = {} secs'.format(en - st))
    train_qrf.append(en - st)


    # predictions
    st = time.time()
    preds_rf = rf.predict(X_test)
    en = time.time()
    print('Prediction time RF = {}'.format(en - st))
    pred_rf.append(en - st)

    st = time.time()
    preds_qrf = qrf.predict(X_test, quantile=50)
    en = time.time()
    print('Prediction time QRF = {}'.format(en - st))
    pred_qrf.append(en - st)

fig, ax = plt.subplots()
ax.plot(np.log10(ns), train_rf, label='RF train', color='blue')
ax.plot(np.log10(ns), train_qrf, label='QRF train', color='red')
ax.plot(np.log10(ns), pred_rf, label='RF predict', color='blue', linestyle=':')
ax.plot(np.log10(ns), pred_qrf, label='QRF predict', color='red', linestyle =':')
ax.legend()
ax.set_xlabel('log(n)')
ax.set_ylabel('time (s)')
fig.savefig('time_comparison.png')

这是输出: Time comparison of RF and QRF training and predictions

最佳答案

我不是这个或任何分位数回归包的开发人员,但我查看了 scikit-garden 和 quantRegForest/ranger 的源代码,并且我对为什么 R 版本要快得多:

编辑:On a related github issue ,lmssdd 提到此方法的性能如何比论文中的“标准程序”差得多。我没有详细阅读这篇论文,所以对这个答案持怀疑态度。

skgarden/quantregforest方法差异说明

skgarden的基本理念predict功能是保存所有y_train对应于所有叶子的值。然后,在预测新样本时,您收集相关的叶子和相应的 y_train值,并计算该数组的(加权)分位数。 R 版本走捷径:它们只保存一个随机选择的 y_train。每个叶节点的值。这有两个好处:它使相关的收集 y_train values 简单得多,因为每个叶节点中总是只有一个值。其次,它使分位数计算变得更加简单,因为每片叶子都具有完全相同的权重。

由于每片叶子只使用一个(随机)值而不是所有值,因此这是一种近似方法。根据我的经验,如果你有足够多的树(至少 50-100 棵左右),这对结果的影响很小。但是,我对数学的了解还不够多,无法准确地说出这个近似值有多好。

TL;DR:如何让 skgarden 预测更快

下面是更简单的 R 分位数预测方法的实现,用于 RandomForestQuantileRegressor 模型。请注意,该函数的前半部分是为每片叶子选择随机 y_train 值的(一次性)过程。如果作者要在 skgarden 中实现这个方法,他们会顺理成章地把这部分移到 fit 中。方法,只留下最后 6 行左右,这使得速度更快 predict方法。同样在我的示例中,我使用的是从 0 到 1 的分位数,而不是从 0 到 100。

def predict_approx(model, X_test, quantiles=[0.05, 0.5, 0.95]):
    """
    Function to predict quantiles much faster than the default skgarden method
    This is the same method that the ranger and quantRegForest packages in R use
    Output is (n_samples, n_quantiles) or (n_samples, ) if a scalar is given as quantiles
    """
    # Begin one-time calculation of random_values. This only depends on model, so could be saved.
    n_leaves = np.max(model.y_train_leaves_) + 1  # leaves run from 0 to max(leaf_number)
    random_values = np.zeros((model.n_estimators, n_leaves))
    for tree in range(model.n_estimators):
        for leaf in range(n_leaves):
            train_samples = np.argwhere(model.y_train_leaves_[tree, :] == leaf).reshape(-1)
            if len(train_samples) == 0:
                random_values[tree, leaf] = np.nan
            else:
                train_values = model.y_train_[train_samples]
                random_values[tree, leaf] = np.random.choice(train_values)
    # Optionally, save random_values as a model attribute for reuse later

    # For each sample, get the random leaf values from all the leaves they land in
    X_leaves = model.apply(X_test)
    leaf_values = np.zeros((X_test.shape[0], model.n_estimators))
    for i in range(model.n_estimators):
        leaf_values[:, i] = random_values[i, X_leaves[:, i]]

    # For each sample, calculate the quantiles of the leaf_values
    return np.quantile(leaf_values, np.array(quantiles), axis=1).transpose()

关于python - 来自 scikit-garden 的分位数随机森林在做出预测时非常缓慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51483951/

相关文章:

python - Scikit Learn KDE 方差增加一

apache-spark - 处理 spark mllib 分类器中的 null/NaN 值

Python:为什么我不能将元组解包到字典中?

python - lightGBM 预测相同的值

python - 从数据文件中获取带有列标题和数据列的 python 映射

machine-learning - 为什么基于树的模型不需要对名义数据进行one-hot编码?

python - 如何在sklearn中的RandomForest中的不同迭代中获得相同的结果

python - 我应该使用多个变量还是一本字典

python - 决策树中特定类的 Sklearn 决策规则

python - 使用 SVM 回归的 Scikit-learn 网格搜索