scikit的GridSearch和Python通常不会释放内存

我做了一些奇怪的观察,发现我的GridSearches在几个小时后仍然失败,我最初不知道为什么。 我随着时间的推移对内存使用情况进行了监控,并看到它以几千兆字节(〜6 Gb)开始并持续增长,直到达到最大值时才崩溃。 128 GB的硬件可以采取。 我正在试验随机森林分类大量的文本文件。 为了简单起见 – 弄清楚发生了什么 – 我回到了朴素的贝叶斯。

我正在使用的版本是

  • Python 3.4.2
  • scikit-learn 0.15.2

我在GitHub的scikit-issue列表上发现了一些关于这个主题的讨论: https : //github.com/scikit-learn/scikit-learn/issues/565和https://github.com/scikit-learn/scikit -learn /拉/ 770

这听起来像是已经成功解决了!

所以,我正在使用的相关代码是

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循环来实现网格search的快速和肮脏的方式

 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') 

到现在为止还挺好。 网格search运行并将结果写入标准输出。 但是,一段时间后,它又会超过128GB的内存上限。 与scikit中的GridSearch相同的问题。 经过一番实验,我终于明白了

 gc.collect() len(gc.get_objects()) # particularly this part! 

在for循环解决问题和内存使用保持在6.5 Gb的持续时间约10小时。

最后,我得到它与上述修复工作,但是,我很好奇听到你的想法可能会导致这个问题,你的提示和build议!

0.15.2中的RandomForest不支持稀疏input。

升级sklearn并再次尝试…希望这将允许多个副本,最终被消耗更less的内存。 (并加快速度)

我看不到你的确切代码,但是我现在面临类似的问题。 这是值得一试。 当我们将数值从一个可变数组或列表复制到另一个variables中时,很容易发生类似的内存破坏,然后我们使用append或类似的方法修改新的数组或列表。它同时在后台增加原始对象。

所以这是一个指数级的过程,所以过了一段时间,我们的记忆就已经消失了。 我能够,也许你可以避免这种现象与deepcopy()的原始对象的价值传递。

我有类似的问题,我用类似的过程炸毁内存,然后我设法保持在10%的内存负载。

更新:现在我看到与pandasDataFrame的代码片段。 会有这样一个价值观问题容易。

我不熟悉GridSearch先生,但我会build议当内存和巨大的列表是一个问题写一个小的自定义生成器。 它可以重复使用所有的项目,只需使用一个任何列表。 如果在这里首先阅读这篇文章,最好的生成器文章,我已经find了。 我把它全部打入一页一页的去了,读完之后你有什么问题我也可以试试

https://www.jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/

不需要: for p1 in parameterset1:

尝试

  def listerator(this_list): i = 0 while True: yield this_list[i] i += 1 

'yield'这个词(在声明中的任何地方)使得它成为一个生成器,而不是一个常规函数。 这个过程说,我等于0,而我真的要做的东西,他们要我得到this_list [0],在这里,如果你需要我,我会等着你在i += 1 。 下一次被调用时,它会启动并执行i += 1 ,并且注意到它仍然处于while循环中,并给出this_list [1],并logging它的位置( 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 

让我们看看我们是否可以在一个for使用它:

 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 

i = seed or 0是一个查找种子,但种子默认为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 

那看起来很熟悉吧? 现在你可以逐个处理万亿个值,select重要的值写入磁盘,而不会让系统崩溃。 请享用!