文本分类中训练测试数据集分离时如何避免特征数量不匹配?
问题描述
我正在开展文本分类任务,计划使用两个独立数据集分别训练与测试,不希望合并数据集以防数据泄漏(data leakage)。训练数据集约16000行,测试数据集仅约1000行。
使用CountVectorizer进行文本向量化时,因两个数据集词汇表不同,生成的特征列数量不一致,预测阶段出现以下错误:
ValueError: X has 55229 features, but DecisionTreeClassifier is expecting 387964 features as input.
现有两种解决思路:1. 为较小的x_test添加零填充列;2. 使用scikit-learn pipeline。我的代码片段如下:
# read dfs df_1 = pd.read_csv("data1.csv",header=0) # for training, has text, and class columns df_2 = pd.read_csv("data2.csv",header=0) # for testing, has text, and class columns # vectorise CV1 = CountVectorizer(ngram_range=(1,3), stop_words="english").fit(df_1['text']) x_train = CV1.transform(df_1['text']) y_train = df_1['class'] CV2 = CountVectorizer(ngram_range=(1,3), stop_words="english").fit(df_2['text']) x_test = CV2.transform(df_2['text']) y_test = df_test['class'] ## shapes of objects ## x_test (1589, 55229), y_test(1589,) ## x_train (16716, 387964), y_train(16716,) # build classifier and predict classifier = DecisionTreeClassifier(random_state=1234) model = classifier.fit(x_train,y_train) y_pred = model.predict(x_test) # error ValueError: X has 55229 features, but DecisionTreeClassifier is expecting 387964 features as input.
解决方案
方法1:基于训练集词汇表对齐测试集特征
核心逻辑是仅用训练集拟合CountVectorizer,测试集直接使用这个已拟合的转换器做转换——这样测试集会自动对齐训练集的特征维度,训练集中未出现的词汇会被忽略,对应特征列自动填充0,无需手动处理。
修改后的代码:
# 读取数据 df_1 = pd.read_csv("data1.csv", header=0) df_2 = pd.read_csv("data2.csv", header=0) # 仅用训练集拟合CountVectorizer cv = CountVectorizer(ngram_range=(1,3), stop_words="english").fit(df_1['text']) # 转换训练集和测试集 x_train = cv.transform(df_1['text']) y_train = df_1['class'] x_test = cv.transform(df_2['text']) # 复用训练集的词汇表转换测试集 y_test = df_2['class'] # 修正原代码中df_test的拼写错误 # 训练模型并预测 classifier = DecisionTreeClassifier(random_state=1234) model = classifier.fit(x_train, y_train) y_pred = model.predict(x_test)
方法2:使用scikit-learn Pipeline(推荐)
Pipeline可以将特征工程和模型训练打包成一个整体,确保整个流程中只有训练集数据用于拟合转换器,从根源避免数据泄漏,同时代码更简洁、可复用性更强。
代码示例:
from sklearn.pipeline import Pipeline # 读取数据 df_1 = pd.read_csv("data1.csv", header=0) df_2 = pd.read_csv("data2.csv", header=0) x_train = df_1['text'] y_train = df_1['class'] x_test = df_2['text'] y_test = df_2['class'] # 构建Pipeline,串联特征工程和模型 pipeline = Pipeline([ ('vectorizer', CountVectorizer(ngram_range=(1,3), stop_words="english")), ('classifier', DecisionTreeClassifier(random_state=1234)) ]) # 训练Pipeline(仅用训练集拟合所有组件) pipeline.fit(x_train, y_train) # 直接用Pipeline完成测试集的特征转换与预测 y_pred = pipeline.predict(x_test)
关键注意事项
- 绝对不能用测试集拟合任何特征工程组件(比如CountVectorizer),这是数据泄漏的典型场景,会导致模型泛化能力严重下降。
- 两种方法中,Pipeline是更推荐的工业级方案,它能避免手动操作中可能出现的疏漏,同时便于后续扩展(比如添加TF-IDF、模型调参等步骤)。
内容的提问来源于stack exchange,提问作者J1_balding
相关产品推荐
相关产品推荐

