理解 scikit 学习预测的随机森林内存需求

Posted

技术标签:

【中文标题】理解 scikit 学习预测的随机森林内存需求【英文标题】:understanding scikit learn Random Forest memory requirement for prediction 【发布时间】:2016-11-03 14:36:28 【问题描述】:

我有一组 2000 个经过训练的随机回归树(来自 scikit learn 的随机森林回归器,n_estimators=1)。使用multiprocessing 在大型数据集(~100000*700000 = 70GB @ 8 位)上并行训练树(50 个核心),共享内存就像一个魅力。请注意,我没有使用 RF 的内置多核支持,因为我事先进行了功能选择。

问题:在并行测试大型矩阵 (~20000*700000) 时,我总是耗尽内存(我可以访问具有 500 GB RAM 的服务器)。

我的策略是将测试矩阵放在内存中并在所有进程之间共享。 根据statement by one of the developers,测试的内存要求是 2*n_jobs*sizeof(X),在我的情况下,另一个因素 *4 是相关的,因为 8 位矩阵条目在 RF 内部向上转换为 float32。

根据数字,我认为我需要进行测试: 14GB 用于在内存中保存测试矩阵 + 50(=n_jobs)*20000(n_samples)*700(=n_features)*4(向上转换为浮点数)*2 字节 = 14 gb + 5.6 gb = ~21GB 内存。

但它总是会爆炸到数百 GB。 我在这里想念什么? (我在最新版本的 scikit-learn,所以旧的内存问题应该解决了)

观察: 仅在一个内核上运行以进行测试的内存使用量在 30 到 100 GB 之间波动(由 free 测量)

我的代码:

#----------------
#helper functions
def initializeRFtest(*args):
    global df_test, pt_test #initialize test data and test labels as globals in shared memory
    df_test, pt_test = args


def star_testTree(model_featidx):
    return predTree(*model_featidx)

#end of helper functions
#-------------------

def RFtest(models, df_test, pt_test, features_idx, no_trees):
    #test trees in parallel
    ncores = 50
    p = Pool(ncores, initializer=initializeRFtest, initargs=(df_test, pt_test))
    args = itertools.izip(models, features_idx)
    out_list = p.map(star_testTree, args)
    p.close()
    p.join()
    return out_list

def predTree(model, feat_idx):
    #get all indices of samples that meet feature subset requirement
    nan_rows = np.unique(np.where(df_test.iloc[:,feat_idx] == settings.nan_enc)[0])
    all_rows = np.arange(df_test.shape[0])
    rows = all_rows[np.invert(np.in1d(all_rows, nan_rows))]    #discard rows with missing values in the given features

    #predict
    pred = model.predict(df_test.iloc[rows,feat_idx])
    return predicted

#main program
out = RFtest(models, df_test, pt_test, features_idx, no_trees)

编辑: 另一个观察: 在对测试数据进行分块时,程序运行平稳,内存使用量大大减少。这是我用来让程序运行的。 更新predTree函数的代码sn-p:

def predTree(model, feat_idx):
    # get all indices of samples that meet feature subset requirement
    nan_rows = np.unique(np.where(test_df.iloc[:,feat_idx] == settings.nan_enc)[0])
    all_rows = np.arange(test_df.shape[0])
    rows = all_rows[np.invert(np.in1d(all_rows, nan_rows))]    #discard rows with missing values in the given features

    # predict height per valid sample
    chunksize = 500
    n_chunks = np.int(math.ceil(np.float(rows.shape[0])/chunksize))


    pred = []
    for i in range(n_chunks):
        if n_chunks == 1:
            pred_chunked = model.predict(test_df.iloc[rows[i*chunksize:], feat_idx])
            pred.append(pred_chunked)
            break
        if i == n_chunks-1:
            pred_chunked = model.predict(test_df.iloc[rows[i*chunksize:], feat_idx])
        else:
            pred_chunked = model.predict(test_df.iloc[rows[i*chunksize:(i+1)*chunksize], feat_idx])
        print pred_chunked.shape
        pred.append(pred_chunked)
    pred = np.concatenate(pred)

    # populate matrix
    predicted = np.empty(test_df.shape[0])
    predicted.fill(np.nan)
    predicted[rows] = pred
    return predicted

【问题讨论】:

您的 2000 个经过训练的随机回归树占用了多少内存?是否为 50 个核心中的每一个都复制它们? @BrianO'Donnell 你的意思是模型的大小吗?我无法再访问该模型,但它的大小绝对可以管理。 是的,模型的大小。 遗憾的是,我无法再查看确切的大小,但如果我没有完全弄错的话,它是在 MB 中 【参考方案1】:

我不确定内存问题是否与args = itertools.izip(models, features_idx)itertools.izip 的使用无关,这可能会触发在所有线程中创建迭代器及其参数的副本。你试过只用zip吗?

另一个假设可能是垃圾收集效率低下 - 在您需要时不会触发。我会检查在predTree 中的model.predict 之前运行gc.collect() 是否没有帮助。

还有第三个潜在原因(可能是最可信的)。让我引用Python FAQ on How does Python manage memory?:

在当前版本的 CPython 中,循环内对 x 的每个新分配都会释放先前分配的资源。

在您的分块函数中,您正是这样做的——重复分配给pred_chunked

【讨论】:

以上是关于理解 scikit 学习预测的随机森林内存需求的主要内容,如果未能解决你的问题,请参考以下文章

偏移随机森林分类器 scikit 学习

在 scikit 学习随机森林模型中,预期和预测的数组最终相同

scikit学习随机森林分类器概率阈值

随机森林分类 - SciKit 与 Weka 的 100 个特征预测

来自 scikit-garden 的分位数随机森林的预测速度非常慢

如何在 Python scikit-learn 中输出随机森林中每棵树的回归预测?