W2VTransformer:仅适用于一个词作为输入?

W2VTransformer: Only works with one word as input?

以下可重现脚本用于计算 Word2Vec 分类器在 gensim 中使用 W2VTransformer 包装器的准确性:

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

此脚本的问题在于它 train_input = [x[0] for x in X_train] 时有效,这基本上始终只有第一个单词。 一旦更改为 train_input = X_train(或 train_input 简单地替换为 X_train),脚本 returns:

ValueError: cannot reshape array of size 10 into shape (10,10)

我该如何解决这个问题,即分类器如何处理多个输入词?

编辑:

显然,与 D2V 相比,W2V 包装器无法处理可变长度的火车输入。这是一个有效的 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)

这在技术上不是答案,但不能写在评论中,所以在这里。这里有多个问题:

  • LogisticRegression class(以及大多数其他 scikit-learn 模型)使用二维数据 (n_samples, n_features)

    这意味着它需要一组一维数组(每行一个(样本),其中数组的元素包含特征值)。

    在您的数据中,单个单词将是一个一维数组,这意味着单个句子(样本)将是一个二维数组。这意味着完整的数据(这里是句子的集合)将是二维数组的集合。即使这样,由于每个句子可以有不同数量的单词,它也不能组合成一个 3-d 数组。

  • 其次,gensim 中的 W2VTransformer 看起来像一个 scikit-learn 兼容的 class,但它不是。它试图遵循 "scikit-learn API conventions" 来定义方法 fit()fit_transform()transform()。它们与 scikit-learn 不兼容 Pipeline

    可以看到fit()fit_transform()的输入参数要求是不一样的。

    • fit():

      X (iterable of iterables of str) – The input corpus.

      X can be simply a list of lists of tokens, but for larger corpora, consider an iterable that streams the sentences directly from disk/network. See BrownCorpus, Text8Corpus or LineSentence in word2vec module for such examples.

    • fit_transform():

      X (numpy array of shape [n_samples, n_features]) – Training set.

如果你想使用 scikit-learn,那么你需要有二维形状。您将需要 "somehow merge" 单个句子的词向量来为该句子形成一维数组。这意味着您需要通过执行以下操作来形成一种句子向量:

  • 单个词的总和
  • 单个单词的平均值
  • 基于频率、tf-idf 等对单个词进行加权平均
  • 使用其他技术,如 sent2vec、paragraph2vec、doc2vec 等

注意:- 我现在注意到 you were doing this thing based on D2VTransformer。如果你想使用 sklearn,这应该是正确的方法。

该问题中的问题是这一行(因为该问题现已删除):

X_train = vectorizer.fit_transform(X_train)

在这里,你用已经计算出的词向量覆盖了原来的X_train(词列表),因此出现了错误。

否则,您可以使用其他允许顺序输入可变大小的工具/库(keras、tensorflow)。例如,可以在此处配置 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() 会自动完成。