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