Потоковая передача на векторизатор в конвейере - PullRequest
0 голосов
/ 16 января 2019

У меня большой языковой корпус, и я использую sklearn tfidf vectorizer и 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 голосов
/ 17 января 2019

Только с 100 000 документов, если они не гигантские, это не обязательно загрузка данных в память, которая вызывает у вас проблемы. Обратите особое внимание:

  • загрузка и токенизация документов уже прошла успешно, прежде чем вы даже начнете поиск / поиск по сетке scikit-learn, и дальнейшее увеличение использования памяти происходит в альтернативно повторяемых моделях с обязательным повторением, не оригинал документы
  • API-интерфейсы scikit-learn обычно предполагают, что обучающие данные полностью находятся в памяти - поэтому, хотя самые внутренние классы gensim (Doc2Vec) довольны потоковыми данными произвольного размера, их труднее адаптировать к scikit-learn

Так что вы должны искать в другом месте, и есть другие проблемы с вашим показанным кодом.

У меня часто возникали проблемы с памятью или блокировкой при попытках scikit-learn с параллелизмом (как разрешено с помощью параметров n_jobs), особенно в ноутбуках Jupyter. Он разветвляется на все процессы ОС, которые, как правило, увеличивают использование памяти. (Каждый подпроцесс получает полную копию памяти родительского процесса, которая может эффективно использоваться - до тех пор, пока подпроцесс не начнет перемещать / изменять вещи.) Иногда происходит сбой одного процесса или межпроцессного взаимодействия, и основной процесс просто остается в ожидании для ответа - который, кажется, особенно смущает ноутбуки Jupyter.

Так что, если у вас нет тонны памяти и вам абсолютно не нужен параллелизм научения научным навыкам, я бы рекомендовал сначала попытаться заставить работать с n_jobs=1 - и только позже экспериментировать с большим количеством рабочих мест.

Напротив, workers класса Doc2VecD2VTransformer) используют более легкие нити, и вы должны использовать по крайней мере workers=3 и, возможно, 8 (если у вас есть хотя бы столько ядер). вместо workers=1, который вы используете сейчас.

Но также: вы делаете кучу лишних действий неясной ценности в вашем коде. Набор тестов из начального сплит-теста никогда не использовался. (Возможно, вы думали о том, чтобы оставить его в качестве окончательного набора проверки? Это самый строгий способ получить точную оценку эффективности вашего конечного результата на будущих невидимых данных, но во многих контекстах данные ограничены, и эта оценка не такая важно просто делать все возможное с ограниченными данными.)

* * * * * * * * * * GridSearchCV сам выполняет разделение поезда / теста в 5 направлениях как часть своей работы, и его лучшие результаты запоминаются в его свойствах, когда это делается.

Так что вам не нужно делать cross_val_score() снова - вы можете прочитать результаты с GridSearchCV.

...