scikit-garden的分位数随机森林在进行预测时非常缓慢。

10

我开始使用scikit-garden软件包中的分位数随机森林(QRFs)。以前,我使用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')

以下是输出结果: RF和QRF训练及预测的时间比较


你可以添加数据和完整的代码吗? - seralouk
@seralouk 我已经添加了一个代码示例。 - Tim Williams
你有相同的问题。@TimWilliams,你找到解决方案了吗? - ssh26
很遗憾,@ssh26,没有。如果你有任何进展,请告诉我。 - Tim Williams
2个回答

7
我不是这个或任何分位数回归软件包的开发人员,但我已经查看了scikit-garden和quantRegForest/ranger的源代码,并且对为什么R版本要快得多有一些想法:
编辑:在相关的github问题上,lmssdd提到该方法的表现明显比论文中的“标准程序”差。我没有详细阅读论文,因此请持怀疑态度对待此答案。
skgarden/quantregforest方法差异的解释
skgarden的predict函数的基本思想是保存与所有叶子节点对应的y_train值。然后,在预测新样本时,收集相关的叶子节点和对应的y_train值,并计算该数组的(加权)分位数。R版本采取了一种捷径:它们仅保存每个叶子节点上随机选择的单个y_train值。这有两个优点:它使得收集相关y_train值变得更简单,因为每个叶子节点上始终只有一个值。其次,由于每个叶子节点都具有完全相同的权重,因此它使得分位数计算变得更简单。
由于每个叶子节点仅使用单个(随机)值而不是全部值,因此这是一种近似方法。根据我的经验,如果您有足够多的树(至少50-100颗左右),这对结果几乎没有影响。但是,我不知道这种近似方法的精度如何。
TL;DR:如何使skgarden predict更快
下面是一个实现较简单的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()

1
Quantile Regression Forest(分位数回归森林)与标准的随机森林回归器之间的主要区别在于,分位数变体必须:
1. 存储(所有)训练响应(y)值,并在训练期间将它们映射到叶节点。
2. 检索响应值以计算一个或多个分位数(例如中位数)进行预测。
前者会减慢训练时间,因为需要创建数据结构来存储训练样本并将其映射到叶节点,后者会减慢预测时间,因为分位数需要从数据结构中检索训练响应值,并对其进行排序以计算分位数。这两种方式都需要进行大量循环迭代,在Python中可能会很慢。
为了解决这些问题,提供了快速、Cython优化、支持scikit-learn的Quantile Regression Forests实现,可以在此处找到:https://github.com/zillow/quantile-forest

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接