scikit&x27;s的GridSearch和Python通常不会释放内存

scikit&x27;s的GridSearch和Python通常不会释放内存,python,memory,scikit-learn,Python,Memory,Scikit Learn,我做了一些奇怪的观察,我的网格搜索在几个小时后一直失败,起初我不知道为什么。随着时间的推移,我监控了内存的使用情况,发现它从几Gb(~6 Gb)开始,并不断增加,直到达到硬件所能承受的最大128 Gb时,节点崩溃。 我在试验随机森林对大量文本文档进行分类。为了简单起见——为了弄清楚发生了什么——我回到了NaiveBayes 我使用的版本是 Python 3.4.2 scikit学习0.15.2 我在GitHub上的scikit问题列表中找到了一些关于这个主题的相关讨论:和 听起来好像已经成

我做了一些奇怪的观察,我的网格搜索在几个小时后一直失败,起初我不知道为什么。随着时间的推移,我监控了内存的使用情况,发现它从几Gb(~6 Gb)开始,并不断增加,直到达到硬件所能承受的最大128 Gb时,节点崩溃。 我在试验随机森林对大量文本文档进行分类。为了简单起见——为了弄清楚发生了什么——我回到了NaiveBayes

我使用的版本是

  • Python 3.4.2
  • scikit学习0.15.2
我在GitHub上的scikit问题列表中找到了一些关于这个主题的相关讨论:和

听起来好像已经成功解决了

因此,我使用的相关代码是

grid_search = GridSearchCV(pipeline, 
                           parameters, 
                           n_jobs=1, # 
                           cv=5, 
                           scoring='roc_auc',
                           verbose=2,
                           pre_dispatch='2*n_jobs',
                           refit=False)  # tried both True and False

grid_search.fit(X_train, y_train)  
print('Best score: {0}'.format(grid_search.best_score_))  
print('Best parameters set:') 
出于好奇,我后来决定通过嵌套for循环以快速而肮脏的方式进行网格搜索

for p1 in parameterset1:
    for p2 in parameterset2:
        ...
            pipeline = Pipeline([
                        ('vec', CountVectorizer(
                                   binary=True,
                                   tokenizer=params_dict[i][0][0],
                                   max_df=params_dict[i][0][1],
                                   max_features=params_dict[i][0][2],
                                   stop_words=params_dict[i][0][3],
                                   ngram_range=params_dict[i][0][4],)),
                         ('tfidf', TfidfTransformer(
                                      norm=params_dict[i][0][5],
                                      use_idf=params_dict[i][0][6],
                                      sublinear_tf=params_dict[i][0][7],)),
                         ('clf', MultinomialNB())])

            scores = cross_validation.cross_val_score(
                                        estimator=pipeline,
                                        X=X_train, 
                                        y=y_train, 
                                        cv=5, 
                                        scoring='roc_auc',
                                        n_jobs=1)

           params_dict[i][1] = '%s,%0.4f,%0.4f' % (params_dict[i][1], scores.mean(), scores.std())
           sys.stdout.write(params_dict[i][1] + '\n')
到目前为止还不错。网格搜索运行并将结果写入标准输出。但是,一段时间后,它又超过了128 Gb的内存上限。与scikit中的GridSearch存在相同的问题。经过一些实验,我终于发现

gc.collect()
len(gc.get_objects()) # particularly this part!
for循环解决了这个问题,在大约10小时的运行时间内,内存使用率始终保持在6.5 Gb


最后,我得到了上面的修复,但是,我很好奇听到你的想法,什么可能会导致这个问题和你的提示和建议

0.15.2中的RandomForest不支持稀疏输入


升级sklearn并重试…希望这将允许最终制作的多个副本消耗更少的内存。(并加快速度)

我看不到您的确切代码,但我现在面临着类似的问题。 值得一试。 当我们将值从可变数组或类似列表的对象复制到另一个变量,创建原始数组或列表的副本,然后使用append或类似的方法修改新数组或列表,增加数组或列表的大小,同时在后台增加原始对象时,很容易发生类似的内存膨胀

这是一个指数过程,过了一段时间我们的内存就用完了。我可以,也许你可以通过
deepcopy()
传递原始对象的值来避免这种现象

我也遇到了类似的问题,我用一个类似的过程破坏了内存,然后我设法保持10%的内存负载

更新:
现在我看到了带有DataFrame的代码片段。这样的valuecopy问题很容易出现。

先生,我不熟悉GridSearch,但我建议在内存和大量列表出现问题时,编写一个小型自定义生成器。它可以重复用于您的所有项目,只需使用一个包含任何列表的项目即可。如果在下面的解决方案之外实现,请首先阅读本文,这是我找到的最好的生成器文章。我把它全部打进去,然后一块一块地走,你看完后有什么问题我也可以试试

不需要: 参数集1中p1的

试一试

“yield”字(声明中的任何地方)使其成为生成器,而不是常规函数。这句话的意思是我等于0,而我确实要做一些事情,他们希望我给出这个列表[0],给你,如果你再次需要我,我会在
i+=1
等你。下次调用它时,它会拾取并执行
i+=1
,注意到它仍在while循环中,并给出此_列表[1],并记录其位置(
i+=1
,它将在那里等待,直到再次调用)。请注意,当我给它一次列表并制作一个生成器(这里是x)时,它将耗尽您的列表

In [141]: x = listerator([1,2,3])

In [142]: next(x)
Out[142]: 1

In [143]: next(x)
Out[143]: 2

In [144]: next(x)
Out[144]: 3

In [148]: next(x)
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-148-5e4e57af3a97> in <module>()
----> 1 next(x)

<ipython-input-139-ed3d6d61a17c> in listerator(this_list)
      2     i = 0
      3     while True:
----> 4             yield this_list[i]
      5             i += 1
      6 

IndexError: list index out of range
这很有效。现在,即使列表元素不存在,它也不会盲目地尝试返回列表元素。从你所说的,我几乎可以保证你将需要能够播种它(从某个地方拾取,或从某个地方新鲜开始):

i=seed或0
是一个查找seed的东西,但是seed默认为None,因此通常只从逻辑位置0开始,即列表的开头

你怎么能在不使用(几乎)任何内存的情况下使用这个野兽

parameterset1 = [1,2,3,4]
parameterset2 = ['a','b','c','d']

In [224]: for p1 in listerator(parameterset1):
    for p2 in listerator(parameterset2):
        print p1, p2
   .....:         
1 a
1 b
1 c
1 d
2 a
2 b
2 c
2 d
3 a
3 b
3 c
3 d
4 a
4 b
4 c
4 d

看起来很眼熟吧?现在,您可以一个接一个地处理一万亿个值,选择重要的值写入磁盘,并且永远不会炸毁您的系统。享受吧

这太奇怪了。请您在github上提交一个新版本,包括使用随机生成的数据(甚至是常量数据,例如
np.ones(shape=(n_样本,n_特征),dtype=np.float)
)复制问题的脚本。当然,没有问题。我将导致此问题的一些代码上载到,并在此处打开了一个问题:。谢谢在过去,我还发现sklearn中的一些东西(通常带有随机林)消耗了太多内存。根据问题的不同,我不得不解决它。一个评论是,对于tfidf/文档问题,GradientBoostingClassifier可能比RandomForest提供更好的结果。另外,我非常确定tfidf转换器将返回一个稀疏矩阵(todo:确保您的版本中有此矩阵)…因此您需要更新sklearn,因为0.15.2中的RandomForest不支持稀疏输入。您是如何使用
gc.collect()
len(gc.get_objects())
中的
GridSearchCV()
方法来解决它的?这种方法不会有正确的循环,因此,没有地方放置您提到的2行?谢谢提示。是的,我使用toarray()调用将稀疏数组转换为密集数组,这肯定非常昂贵。然而,这个问题也发生在“便宜”的朴素贝叶斯或逻辑回归分类器(Sgd)上。不知怎的,垃圾收集出现了问题。我应该在Python3.4.3和scikit学习版0.16.1I上再试一次,我相信NB和LR也支持稀疏输入。您可以尝试将pre_dispatch='2*n_jobs'的值更改为1I。我必须查看scikit了解
In [221]: for val in listerator([1,2,3,4]):
   .....:     print val
   .....:     
1
2
3
4
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-221-fa4f59138165> in <module>()
----> 1 for val in listerator([1,2,3,4]):
      2     print val
      3 

<ipython-input-220-263fba1d810b> in listerator(this_list, seed)
      2         i = seed or 0
      3         while True:
----> 4             yield this_list[i]
      5             i += 1

IndexError: list index out of range
def listerator(this_list):
   i = 0
   while True:
       try:
           yield this_list[i]
       except IndexError:
           break
       i += 1

In [223]: for val in listerator([1,2,3,4]):
    print val
   .....:     
1
2
3
4
def listerator(this_list, seed=None):
   i = seed or 0
   while True:
       try:
           yield this_list[i]
       except IndexError:
           break
       i += 1

In [150]: l = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]

In [151]: x = listerator(l, 8)

In [152]: next(x)
Out[152]: 9

In [153]: next(x)
Out[153]: 10

In [154]: next(x)
Out[154]: 11
parameterset1 = [1,2,3,4]
parameterset2 = ['a','b','c','d']

In [224]: for p1 in listerator(parameterset1):
    for p2 in listerator(parameterset2):
        print p1, p2
   .....:         
1 a
1 b
1 c
1 d
2 a
2 b
2 c
2 d
3 a
3 b
3 c
3 d
4 a
4 b
4 c
4 d