【问题标题】:MemoryError on joblib dumpjoblib 转储上的 MemoryError
【发布时间】:2018-03-26 18:05:51
【问题描述】:

我运行以下 sn-p 来训练文本分类模型。我对它进行了相当多的优化,它运行得非常顺利,但是它仍然使用了大量的 RAM。我们的数据集非常庞大(1300 万个文档 + 1800 万个词汇表),但在我看来,执行时抛出错误的点非常奇怪。脚本:

encoder = LabelEncoder()
y = encoder.fit_transform(categories)
classes = list(range(0, len(encoder.classes_)))

vectorizer = CountVectorizer(vocabulary=vocabulary,
                             binary=True,
                             dtype=numpy.int8)

classifier = SGDClassifier(loss='modified_huber',
                           n_jobs=-1,
                           average=True,
                           random_state=1)

tokenpath = modelpath.joinpath("tokens")
for i in range(0, len(batches)):
    token_matrix = joblib.load(
        tokenpath.joinpath("{}.pickle".format(i)))
    batchsize = len(token_matrix)
    classifier.partial_fit(
        vectorizer.transform(token_matrix),
        y[i * batchsize:(i + 1) * batchsize],
        classes=classes
    )

joblib.dump(classifier, modelpath.joinpath('classifier.pickle'))
joblib.dump(vectorizer, modelpath.joinpath('vectorizer.pickle'))
joblib.dump(encoder, modelpath.joinpath('category_encoder.pickle'))
joblib.dump(options, modelpath.joinpath('extraction_options.pickle'))

我在这一行得到了 MemoryError:

joblib.dump(vectorizer, modelpath.joinpath('vectorizer.pickle'))

在执行的这一点上,训练完成并且分类器已经被转储。它应该由垃圾收集器收集,以防需要更多内存。除此之外,如果它甚至不是compressing the data,为什么joblib要分配这么多内存。

我对 python 垃圾收集器的内部工作没有深入的了解。我应该强制 gc.collect() 还是使用 'del' 语句来释放那些不再需要的对象?

更新:

我尝试使用 HashingVectorizer,尽管它大大减少了内存使用量,但矢量化速度较慢,因此不是一个很好的选择。

我必须腌制矢量化器,以便稍后在分类过程中使用它,这样我就可以生成提交给分类器的稀疏矩阵。我将在这里发布我的分类代码:

extracted_features = joblib.Parallel(n_jobs=-1)(
    joblib.delayed(features.extractor) (d, extraction_options) for d in documents)

probabilities = classifier.predict_proba(
    vectorizer.transform(extracted_features))

predictions = category_encoder.inverse_transform(
    probabilities.argmax(axis=1))

trust = probabilities.max(axis=1)

【问题讨论】:

  • 你能用HashingVectorizer代替吗?你的vocabulary 是什么类型的?为什么首先需要腌制矢量化器?
  • @krassowski 我更新了我的问题以包含有关分类过程的更多详细信息。此外,词汇表是一组字符串,包含从文档中提取的所有特征

标签: python-3.x scikit-learn out-of-memory joblib


【解决方案1】:

如果您向CountVectorizer 提供自定义词汇表,那么稍后在分类期间重新创建它应该不是问题。当您提供一组字符串而不是映射时,您可能希望使用已解析的词汇表,您可以通过以下方式访问:

parsed_vocabulary = vectorizer.vocabulary_
joblib.dump(parsed_vocabulary, modelpath.joinpath('vocabulary.pickle'))

然后加载它并用于重新创建CountVectorizer

vectorizer = CountVectorizer(
    vocabulary=parsed_vocabulary,
    binary=True,
    dtype=numpy.int8
)

注意这里不需要使用joblib;标准泡菜应该执行相同的操作;您可以使用任何可用的替代方法获得更好的结果,值得一提的是 PyTables。

如果这也占用了大部分内存,您应该尝试使用原始的vocabulary 来重新创建矢量化器;目前,当提供一组字符串作为词汇表时,矢量化器只是将集合转换为排序列表,因此您不必担心可重复性(尽管在生产中使用之前我会仔细检查)。或者您可以自行将集合转换为列表。

总结一下:因为你没有fit()这个Vectorizer,所以使用CountVectorizer的全部附加值就是它的transform()方法;由于所需的全部数据是词汇表(和参数),因此您可能会减少仅对词汇表进行腌制的内存消耗,无论是否已处理。

当您要求从官方来源获取答案时,我想指出您:https://github.com/scikit-learn/scikit-learn/issues/3844,其中 scikit-learn 的所有者和贡献者提到重新创建 CountVectorizer,尽管是出于其他目的。在链接的 repo 中报告问题可能会更好,但请确保包含会导致内存使用过多问题的数据集以使其可重现。

最后你可以使用前面评论中提到的HashingVectorizer

PS:关于gc.collect() 的使用——在这种情况下我会试一试;关于技术细节,你会发现很多关于 SO 解决这个问题的问题。

【讨论】:

  • krassowski,感谢您的详细回答。很抱歉,我偏离了我的问题的主要主题,导致您为我使用的方法提供替代方案。我真的很重视您的提示,并且可能会使用它们,但我真正想知道的是,如果我在转储矢量化器之前调用 gc.collect,分类器是否会被 gc 收集。另外,我不明白 joblib.dump 如何将内存使用量增加到进程被杀死的程度。如果没有可用内存,不应该调用 gc 吗?如果调用了,为什么不能释放内存?
  • 如果没有保存对该对象的引用,则在调用 gc.collect() 时将收集任何 python 对象,因此需要使用 del;如果你想让垃圾收集器在内存耗尽之前运行,你最好手动调用它,而不是希望它自己工作。 python中的gc循环运行,而不是内存满的时候;更准确地说,它在执行一定数量的内存分配/释放操作时开始;查看更多文档或stackoverflow.com/a/22440880/6646912
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-24
  • 1970-01-01
  • 2016-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多