W2VTransformer: работает только с одним словом в качестве ввода? - PullRequest
0 голосов
/ 01 января 2019

Следующий воспроизводимый скрипт используется для вычисления точности классификатора Word2Vec с оболочкой W2VTransformer в gensim:

import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from gensim.sklearn_api import W2VTransformer
from gensim.utils import simple_preprocess

# Load synthetic data
data = pd.read_csv('https://pastebin.com/raw/EPCmabvN')
data = data.head(10)

# Set random seed
np.random.seed(0)

# Tokenize text
X_train = data.apply(lambda r: simple_preprocess(r['text'], min_len=2), axis=1)
# Get labels
y_train = data.label

train_input = [x[0] for x in X_train]

# Train W2V Model
model = W2VTransformer(size=10, min_count=1)
model.fit(X_train)

clf = LogisticRegression(penalty='l2', C=0.1)
clf.fit(model.transform(train_input), y_train)

text_w2v = Pipeline(
    [('features', model),
     ('classifier', clf)])

score = text_w2v.score(train_input, y_train)
score

0.80000000000000004

Проблема сэтот сценарий заключается в том, что он only работает, когда train_input = [x[0] for x in X_train], что по сути всегда является только первым словом.При изменении на train_input = X_train (или train_input, просто заменяемое на X_train), скрипт возвращает:

ValueError: невозможно преобразовать массив размера 10 в форму (10,10)

Как я могу решить эту проблему, т.е. как классификатор может работать с более чем одним словом ввода?

Редактировать:

По-видимому,Оболочка W2V не может работать с входом поезда переменной длины по сравнению с D2V.Вот рабочая версия D2V:

import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.metrics import accuracy_score, classification_report
from sklearn.pipeline import Pipeline
from gensim.utils import simple_preprocess, lemmatize
from gensim.sklearn_api import D2VTransformer

data = pd.read_csv('https://pastebin.com/raw/bSGWiBfs')

np.random.seed(0)

X_train = data.apply(lambda r: simple_preprocess(r['text'], min_len=2), axis=1)
y_train = data.label

model = D2VTransformer(dm=1, size=50, min_count=2, iter=10, seed=0)
model.fit(X_train)

clf = LogisticRegression(penalty='l2', C=0.1, random_state=0)
clf.fit(model.transform(X_train), y_train)

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

y_pred = pipeline.predict(X_train)
score = accuracy_score(y_train,y_pred)
print(score)

1 Ответ

0 голосов
/ 02 января 2019

Технически это не ответ, но его нельзя записать в комментариях, так что вот оно.Здесь есть несколько проблем:

  • LogisticRegression класс (и большинство других моделей scikit-learn) работают с двумерными данными (n_samples, n_features).

    Это означает, что ему нужен набор 1-мерных массивов (по одному для каждой строки (выборки), в котором элементы массива содержат значения признаков).

    В ваших данных одно слово будет массивом 1-го числа, что означает, что одно предложение (образец) будет массивом 2-го числа.Это означает, что полные данные (коллекция предложений здесь) будут представлять собой набор двумерных массивов.Даже в этом, поскольку каждое предложение может иметь различное количество слов, оно не может быть объединено в один трехмерный массив.

  • Во-вторых, W2VTransformer в gensim выглядит как класс, совместимый с scikit-learn, но это не так.Он пытается следовать «соглашениям scikit-learn API» для определения методов fit(), fit_transform() и transform().Они не совместимы с scikit-learn Pipeline.

    Вы можете видеть, что требования к входным параметрам fit() и fit_transform() различны.

    • fit():

      X (итерируемый из итераций str) - входной корпус.

      X может быть просто списком списков токенов, но для более крупных корпусов рассмотрим итерацию, которая направляет предложения непосредственно с диска / сети.См. BrownCorpus, Text8Corpus или LineSentence в модуле word2vec для таких примеров.

    • fit_transform():

      X (numpy массив формы [n_samples, n_features]) - обучающий набор.

Если вы хотите использовать scikit-learn, товам нужно будет иметь 2-ую форму.Вам нужно будет «как-то объединить» слова-векторы для одного предложения, чтобы сформировать 1-й массив для этого предложения.Это означает, что вам нужно сформировать своего рода вектор предложений, выполнив:

  • сумма отдельных слов
  • среднее значение отдельных слов
  • взвешенное усреднение отдельныхслова, основанные на частоте, tf-idf и т. д.
  • с использованием других методов, таких как sent2vec, параграф2vec, doc2vec и т. д.

Примечание : - Я заметил, что вы делали это на основе D2VTransformer.Это должен быть правильный подход, если вы хотите использовать sklearn.

Проблема в этом вопросе заключалась в следующей строке (поскольку этот вопрос теперь удален):

X_train = vectorizer.fit_transform(X_train)

Здесь вы перезаписываетеваш исходный X_train (список слов) с уже рассчитанными векторами слов и, следовательно, с этой ошибкой.

Или же вы можете использовать другие инструменты / библиотеки (керас, тензор потока), которые позволяют последовательно вводить переменный размер.Например, LSTM могут быть сконфигурированы здесь, чтобы принимать переменные входные данные и маркер окончания, чтобы отметить конец предложения (образец).

Обновление :

ВПриведенное выше решение позволяет заменить строки:

model = D2VTransformer(dm=1, size=50, min_count=2, iter=10, seed=0)
model.fit(X_train)

clf = LogisticRegression(penalty='l2', C=0.1, random_state=0)
clf.fit(model.transform(X_train), y_train)

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

y_pred = pipeline.predict(X_train)

на

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

pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_train)

Нет необходимости подбирать и преобразовывать отдельно, поскольку pipeline.fit() автоматически сделает это.

...