以下可重现脚本用于计算 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.800000000000000004这个脚本的问题在于它只在train_input = [x[0] for x in X_train]
时起作用,这基本上总是第一个单词。 一旦更改为train_input = X_train
(或train_input
简单地替换为X_train
),脚本将返回:
值错误: 无法将大小为 10 的数组调整为形状 (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
类(以及大多数其他scikit-learn模型)使用2D数据(n_samples, n_features)
。这意味着它需要一个一维数组的集合(每行(样本一个),其中数组的元素包含特征值)。
在数据中,单个单词将是一维数组,这意味着单个句子(样本)将是二维数组。这意味着完整的数据(此处的句子集合)将是二维数组的集合。即使如此,由于每个句子可以具有不同数量的单词,因此无法将其组合成单个3-D数组。
-
其次,gensim 中的
W2VTransformer
看起来像一个 scikit-learn 兼容类,但事实并非如此。它试图遵循"scikit-learn API约定"来定义方法fit()
,fit_transform()
和transform()
。它们与scikit-learnPipeline
不兼容。您可以看到
fit()
和fit_transform()
的输入参数要求是不同的。-
fit()
:X(str 的迭代对象可迭代对象)– 输入语料库。
X 可以只是标记列表的列表,但对于较大的语料库,请考虑直接从 磁盘/网络。参见 word2vec 中的 BrownCorpus、Text8Corpus 或 LineSentence 模块,用于此类示例。
-
fit_transform()
:X (形状 [n_samples, n_features] 的 numpy 数组)– 训练集。
-
如果你想使用scikit-learn,那么你需要有2-D形状。您需要"以某种方式合并"单个句子的词向量,以形成该句子的一维数组。这意味着你需要通过执行以下操作来形成一种句子向量:
- 单个单词的总和
- 单个单词的平均值
- 基于频率、TF-IDF 等对单个单词进行加权平均。
- 使用其他技术,如Sent2Vec,Paragraph2Vec,Doc2VEC等。
注意:- 我现在注意到你正在根据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()
会自动执行此操作。