将语料流传输到管道中的矢量化器

时间:2019-01-16 08:37:46

标签: scikit-learn streaming gensim corpus

我有一个庞大的语言语料库,我使用sklearn tfidf矢量化器和gensim Doc2Vec来计算语言模型。我的总语料库大约有100,000个文档,当我超过一定阈值时,我意识到Jupyter笔记本将停止计算。我猜想在应用了网格搜索和交叉验证步骤之后,内存已满。

对于Doc2Vec,以下示例脚本已经停止运行:

%%time
import pandas as pd
import numpy as np
from tqdm import tqdm
from sklearn.externals import joblib

from sklearn.linear_model import LogisticRegression
from sklearn.feature_extraction.text import TfidfVectorizer
from gensim.sklearn_api import D2VTransformer

from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from gensim.utils import simple_preprocess

np.random.seed(1)

data = pd.read_csv('https://pastebin.com/raw/dqKFZ12m')
X_train, X_test, y_train, y_test = train_test_split([simple_preprocess(doc) for doc in data.text],
                                                    data.label, random_state=1)

model_names = [
               'TfidfVectorizer',
               'Doc2Vec_PVDM',
              ]

models = [
    TfidfVectorizer(preprocessor=' '.join, tokenizer=None, min_df = 5),
    D2VTransformer(dm=0, hs=0, min_count=5, iter=5, seed=1, workers=1),
]

parameters = [
              {
              'model__smooth_idf': (True, False),
              'model__norm': ('l1', 'l2', None)
              },
              {
              'model__size': [200],
              'model__window': [4]
              }
              ]

for params, model, name in zip(parameters, models, model_names):

    pipeline = Pipeline([
      ('model', model),
      ('clf', LogisticRegression())
      ])

    grid = GridSearchCV(pipeline, params, verbose=1, cv=5, n_jobs=-1)
    grid.fit(X_train, y_train)
    print(grid.best_params_)

    cval = cross_val_score(grid.best_estimator_, X_train, y_train, scoring='accuracy', cv=5, n_jobs=-1)
    print("Cross-Validation (Train):", np.mean(cval))

print("Finished.")

是否有一种方法可以“流式处理”文档中的每一行,而不是将全部数据加载到内存中?还是另一种提高内存效率的方法?我读了一些有关该主题的文章,但找不到包含管道示例的文章。

1 个答案:

答案 0 :(得分:2)

只有100,000个文档,除非它们非常庞大,否则不一定要将数据加载到内存中会引起问题。请特别注意:

  • 在开始scikit-learn管道/网格搜索之前,已经成功完成了文档的加载和标记化,并且内存使用率的进一步提高是在必需重复的备用模型中, not 原始文档
  • scikit-learn API倾向于假定训练数据已完全存储在内存中-因此,即使最里面的gensim类(Doc2Vec)对任意大小的流数据感到满意,也很难将其适应scikit-learn

因此,您应该在别处查找,并且所显示的代码还有其他问题。

我经常遇到scikit-learn尝试并行性时的内存或锁定问题(通过类似n_jobs的参数启用),特别是在Jupyter笔记本内部。它分叉完整的OS进程,这往往会消耗大量内存。 (每个子流程都会获得父流程内存的完整副本,该副本可能会有效共享-直到子流程开始移动/更改事物为止。)有时一个流程或进程间通信失败,而主流程只是在等待做出回应–这似乎使Jupyter笔记本电脑特别困惑。

因此,除非您有大量的内存并且绝对需要scikit-learn并行性,否则我建议先尝试使n_jobs=1起作用,然后再尝试更多的工作。

相反,workers类(和Doc2Vec)中的D2VTransformer使用轻量级线程,因此您至少应使用workers=3,也许使用8(如果您至少有那么多核心,而不是您现在使用的workers=1

但是:在代码中,您正在执行许多冗余的,价值不明确的操作。从未使用过来自初始训练测试拆分的测试集。 (也许您正在考虑将其保留为最终验证集?这是对未来看不见的数据进行最终结果性能的良好估算的最严格方法,但是在许多情况下,数据是有限的,而且这种估算并不像很重要,因为只有有限的数据才能做到最好。)

GridSearchCV本身会进行5次火车/测试拆分,这是其工作的一部分,完成后会在其属性中记住其最佳结果。

因此,您无需再次执行cross_val_score()-您可以从GridSearchCV中读取结果。