我有一个大型语言语料库,我使用sklearn-tfidf矢量器和gensim-Doc2Vec来计算语言模型。我的语料库总共有大约100000个文档,我意识到一旦我超过某个阈值,我的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.")
有没有一种方法可以"流式"处理文档中的每一行,而不是将全部数据加载到内存中?还是另一种提高内存效率的方法?我读了几篇关于这个主题的文章,但没有发现任何包含管道示例的文章
只有100000个文档,除非它们是巨大的,否则不一定是将数据加载到内存中会给您带来问题。特别注意:
- 加载&在开始scikit学习管道/网格搜索之前,文档的标记化就已经成功了,内存使用量的进一步增加是在必要重复的替代模型中,而不是在原始文档中
- scikit-learn API倾向于假设训练数据完全在内存中——因此,即使最内部的gensim类(
Doc2Vec
)对任意大小的流式数据感到满意,也很难将其适应scikit-learn
因此,您应该另眼相看,您显示的代码还有其他问题。
scikit-learn尝试并行(通过类似n_jobs
的参数启用)时,我经常遇到内存或锁定问题,尤其是在Jupyter笔记本电脑中。它分叉了完整的操作系统进程,这往往会增加内存使用量。(每个子进程都会获得父进程内存的完整副本,这些内存可能会被有效共享,直到子进程开始移动/更改事物。)有时一个进程或进程间通信失败,主进程只能等待响应,这似乎特别让Jupyter笔记本感到困惑。
因此,除非你有大量的内存,并且绝对需要scikit学习并行性,否则我建议你先尝试使用n_jobs=1
,然后再尝试更多的工作。
相比之下,Doc2Vec
类的workers
(和D2VTransformer
)使用较轻的线程,并且您应该至少使用workers=3
,也许使用8(如果您至少有那么多内核,而不是现在使用的workers=1
)
但同时:您正在代码中执行一系列价值不明确的冗余操作。从未使用过最初列车测试拆分后的测试集。(也许你想把它作为最终验证集放在一边?这是对未来看不见的数据的最终结果表现进行良好估计的最严格方法,但在许多情况下,数据是有限的,这种估计不如在有限的数据下尽可能做到最好重要。)
作为其工作的一部分,GridSearchCV
本身进行了5向训练/测试拆分,完成后其最佳结果将在其特性中被铭记。
所以你不需要再做cross_val_score()
——你可以从GridSearchCV
中读取结果。