joblib 转储上的 MemoryError

MemoryError on joblib dump

我有以下代码片段 运行 训练文本分类模型。我对它进行了相当多的优化,它 运行 非常流畅,但是它仍然使用大量 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'))

我在这一行遇到了内存错误:

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

此时执行,训练完成,分类器已经转储。它应该由垃圾收集器收集,以防需要更多内存。除此之外,连compressing the data都不是,joblib为什么要分配那么多内存。

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

更新:

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

我必须对矢量化器进行 pickle,以便稍后在分类过程中使用它,这样我就可以生成提交给分类器的稀疏矩阵。我将post这里是我的分类码:

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)

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

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

然后加载它并用于 re-create 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,尽管用于其他目的。您可能更幸运地在链接的存储库中报告您的问题,但请确保包含一个会导致过多内存使用问题的数据集以使其可重现。

最后你可以只使用HashingVectorizer,正如前面评论中提到的那样。

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