基于Scikit-learn使用FeatureUnion为文本分类添加非文本特征
把文本特征和非文本特征结合的scikit-learn实现方案
嘿,我懂你现在的需求——已经搞定了基础的文本分类,现在想加个非文本的number_feature来提升准确率,还想测试FeatureUnion对吧?刚好scikit-learn里有现成的工具能帮你把两种特征无缝结合,我给你一步步讲清楚怎么做~
1. 先模拟你的数据集
首先咱们先把你描述的数据集用代码模拟出来,方便后续测试:
import pandas as pd # 按照你的要求,每个label对应固定的number_feature data = { 'label': ['sandwich', 'greeting', 'goodbye', 'sandwich', 'greeting', 'goodbye'], 'message': [ 'I want a turkey sandwich', 'Hello there!', 'See you later', 'Can I get a ham sandwich?', 'Hi friend', 'Bye for now' ], 'number_feature': [1, 2, 3, 1, 2, 3] } df = pd.DataFrame(data)
2. 拆分特征与目标变量
这一步很基础,把用于预测的特征(message和number_feature)和目标标签(label)分开:
X = df[['message', 'number_feature']] y = df['label']
3. 构建特征融合的分类流水线
这里有两种方法,我推荐用官方现在主推的ColumnTransformer,比旧的FeatureUnion更直观;不过既然你提到要测试FeatureUnion,我也会把这个方法列出来。
方法一:用ColumnTransformer(推荐)
这个工具能明确指定哪一列用哪种特征处理逻辑,然后自动合并结果,非常省心:
from sklearn.compose import ColumnTransformer from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.naive_bayes import MultinomialNB from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score # 先拆分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) # 定义文本特征的处理流程:用TF-IDF提取文本特征 text_transformer = Pipeline(steps=[ ('tfidf', TfidfVectorizer(stop_words='english')) # 去掉英文停用词,效果更好 ]) # 定义数值特征的处理流程: # 因为你的number_feature是每个类别对应固定值,属于类别型数值,不需要缩放,直接传递就行 # 如果是真实的连续数值,可以换成StandardScaler()做标准化 numeric_transformer = Pipeline(steps=[ ('passthrough', 'passthrough') ]) # 合并两个特征处理流程 preprocessor = ColumnTransformer( transformers=[ ('text_features', text_transformer, 'message'), # 处理message列 ('numeric_features', numeric_transformer, ['number_feature']) # 处理number_feature列 ]) # 构建完整的分类流水线:特征预处理 + 分类器 clf = Pipeline(steps=[ ('preprocessor', preprocessor), ('classifier', MultinomialNB()) # 你也可以换成其他分类器,比如SVM、RandomForest ]) # 训练模型并预测 clf.fit(X_train, y_train) y_pred = clf.predict(X_test) # 输出准确率 print(f"加入number_feature后的准确率: {accuracy_score(y_test, y_pred):.2f}")
因为你的number_feature和label是一一对应的,理论上加入这个特征后准确率会接近100%,刚好能验证特征融合的效果~
方法二:用FeatureUnion(旧方法,用于测试)
FeatureUnion是对处理后的特征矩阵进行合并,需要先自定义一个工具来提取DataFrame的指定列:
from sklearn.pipeline import FeatureUnion from sklearn.base import BaseEstimator, TransformerMixin # 自定义列选择器,用来从DataFrame中提取指定列并转换成合适的格式 class ColumnSelector(BaseEstimator, TransformerMixin): def __init__(self, column): self.column = column def fit(self, X, y=None): return self def transform(self, X): # 如果是数值列,转成二维数组;文本列直接返回一维数组给TF-IDF处理 if isinstance(X[self.column].values[0], (int, float)): return X[self.column].values.reshape(-1, 1) else: return X[self.column].values # 构建文本特征处理流水线 text_pipeline = Pipeline([ ('selector', ColumnSelector('message')), ('tfidf', TfidfVectorizer(stop_words='english')) ]) # 构建数值特征处理流水线 numeric_pipeline = Pipeline([ ('selector', ColumnSelector('number_feature')), ('passthrough', 'passthrough') # 同样,因为是类别型数值,直接传递 ]) # 合并两个特征流水线的结果 feature_union = FeatureUnion([ ('text_features', text_pipeline), ('numeric_features', numeric_pipeline) ]) # 完整分类流水线 clf = Pipeline([ ('features', feature_union), ('classifier', MultinomialNB()) ]) # 训练预测 clf.fit(X_train, y_train) y_pred = clf.predict(X_test) print(f"用FeatureUnion实现的准确率: {accuracy_score(y_test, y_pred):.2f}")
4. 后续优化建议
- 如果你的
number_feature是真实的连续数值(不是和label绑定的),可以试试用StandardScaler或者MinMaxScaler做标准化,提升部分分类器的效果; - 文本特征部分可以试试调整
TfidfVectorizer的参数,比如ngram_range=(1,2)加入二元词组,或者自定义停用词; - 分类器也可以换成
LogisticRegression、RandomForestClassifier等,对比不同模型的效果。
内容的提问来源于stack exchange,提问作者borb183
相关产品推荐
相关产品推荐

