首页 > 解决方案 > ValueError:发现样本数量不一致的输入变量:[6, 80]

问题描述

我无法在管道中处理以下代码(它是管道imblearn

features = training_data.loc[:, training_data.columns[:-1]]
labels = training_data.loc[:, training_data.columns[-1:]]


X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2)
print(X_train.shape, y_train.shape)

形状的打印输出是:(80, 6) (80, 1)

algorithms = [   
    svm.LinearSVC(),
    ensemble.RandomForestClassifier(),

]

def train(algorithm, X_train, y_train):
    model = Pipeline([
        ('vect', TfidfVectorizer()),
        ('smote', SMOTETomek()),
        ('chi', SelectKBest(chi2, k=1000)),
        ('classifier', algorithm)
    ])
    model.fit(X_train, y_train)
    return model

score_dict = {}
algorithm_to_model_dict = {}
for algorithm in algorithms:
    model = train(algorithm, X_train, y_train)
    score = model.score(X_test, y_test)
    score_dict[algorithm] = int(score * 100)
    algorithm_to_model_dict[algorithm] = model

特征和标签都是文本(我正在做文本分析)。

正在从fit调用中引发异常

我究竟做错了什么?

标签: pythonpandasmachine-learningscikit-learnnlp

解决方案


发生这种情况是因为您的管道中有一个文本转换器对象。这种方法的问题是管道会将整个数据帧传递给TfidfVectorizer. 但是,文本转换器需要scikit-learn一维输入。

传递 2d 数据框会TfidfVectorizer导致一些奇怪的处理,它会将列名误认为是文档。您可以使用这个简单的示例进行检查:

X = pd.DataFrame({
    'f1': ['This is doc1', 'This is doc2',
           'This is doc3', 'This is doc4', 'This is doc5'],
    'f2': [0, 1, 1, 0, 0]
})

vec = TfidfVectorizer()
print(vec.fit(X).get_feature_names())

>>> ['f1', 'f2']

这解释了为什么错误消息指出样本数量不一致:TfidfVectorizer认为数据框中的 6 列是样本,它们的名称是特征。

如果要TfidfVectorizer在管道中使用,则必须确保仅将带有文本文档的列传递给它。您可以通过将其包装在 a 中来实现ColumnTransformer

# if only one column needs to be transformed
transformer = ColumnTransformer(
    [('vec', TfidfVectorizer(), column)],   # column should be a string or int
    remainder='passthrough'
)

# if more than one column needs to be transformed (discouraged, see Note below)
transformer = ColumnTransformer(
    [('vec', TfidfVectorizer(), col_1),  # col_1 should be a string or int
     ...
     ('vec', TfidfVectorizer(), col_n)],   # col_n should be a string or int
    remainder='passthrough'
)

column用索引或必须转换的列的名称替换上面的TfidfVectorizer内容,它只会处理这个特定的列。这remainder='passthrough'将确保其他列保持原样并与结果连接。然后,您可以像这样在管道中使用它:

model = Pipeline([
    ('vect', transformer),
    ('smote', SMOTETomek()),
    ('chi', SelectKBest(chi2, k=1000)),
    ('classifier', algorithm)
])

笔记

如果您必须使用文本转换多个列,您应该考虑将列条目合并到一个单独的组合文档中,并且只转换这个组合文档。否则,每一列都将被处理一个新的词汇表,尽管这些词汇表可能在某种程度上重叠,你最终可能会得到一个非常高的维度/很多特征。


推荐阅读