文本分类多输入特征场景下SMOTE过采样失败求助
多输入NLP文本分类任务中SMOTE重采样后的特征匹配问题
问题背景
我正在处理一个文本分类任务:输入包含两个特征,分别是文本(字符串类型)和语言(取值为"EN""FR""DE"等的字符串类型),输出为不平衡的分类变量。
现有预处理与建模步骤
1. 文本特征处理
使用Keras Tokenizer分词并做padding:
from keras.preprocessing.text import Tokenizer from tensorflow.keras.preprocessing.sequence import pad_sequences tokenizer = Tokenizer() tokenizer.fit_on_texts(X_train_text) X_train_sequences = tokenizer.texts_to_sequences(X_train_text) X_test_sequences = tokenizer.texts_to_sequences(X_test_text) X_train_padded = pad_sequences(X_train_sequences, maxlen=max_len, padding='post') X_test_padded = pad_sequences(X_test_sequences, maxlen=max_len, padding='post')
2. 语言特征编码
使用OneHotEncoder对语言变量编码(注意:代码存在变量名笔误,market_encoder需替换为language_encoder):
from sklearn.preprocessing import LabelEncoder, OneHotEncoder language_encoder = OneHotEncoder(handle_unknown="infrequent_if_exist") X_train_lang_encoded = language_encoder.fit_transform(X_train_lang.values.reshape(-1, 1)) X_test_lang_encoded = language_encoder.transform(X_test_lang.values.reshape(-1, 1))
3. SMOTE过采样
仅对文本特征执行过采样:
from imblearn.over_sampling import SMOTE oversampler = SMOTE(random_state=seed, k_neighbors=3) X_train_resampled, y_train_resampled = oversampler.fit_resample(X_train_padded, y_train)
4. 多输入神经网络构建
构建双输入模型,但训练时重采样后的文本数据与原语言特征样本数不匹配,导致拼接失败:
from tensorflow.keras.layers import Dense, LSTM, Embedding, Input, Dropout, Conv1D, MaxPooling1D, Flatten, GlobalMaxPooling1D, SimpleRNN, Bidirectional, Concatenate from tensorflow.keras.losses import SparseCategoricalCrossentropy from tensorflow.keras.metrics import SparseCategoricalAccuracy from tensorflow.keras.models import Model from tensorflow.keras.optimizers import Adam input_text = Input(shape=(max_len,), dtype="int32", name="input_text") embedding = Embedding(input_dim=vocab_size, output_dim=128, input_length=max_len)(input_text) lstm = Bidirectional(LSTM(64, return_sequences=False))(embedding) dropout = Dropout(0.5)(lstm) input_market = Input(shape=(X_train_lang_encoded.shape[1],), dtype='float32', name='input_market') concat = Concatenate()([dropout, input_market]) output = Dense(len(label_encoder.classes_), activation="softmax")(concat) model = Model(inputs=[input_text, input_market], outputs=output) optimizer = Adam(learning_rate=1e-3) loss = SparseCategoricalCrossentropy(from_logits=False) metric = SparseCategoricalAccuracy("accuracy") model.compile(optimizer=optimizer, loss=loss, metrics=[metric]) # 此处因样本数不匹配报错 history = model.fit( [X_train_resampled, X_lang_encoded], y_train_resampled, validation_data=([X_test_padded, X_test_lang_encoded], y_test), epochs=10, batch_size=16 )
尝试的无效方案及报错
方案1:对语言特征手动重复采样
X_train_lang_resampled = np.repeat(X_train_lang_encoded, smote.sample_indices_.shape[0] X_train_lang_encoded.shape[0], axis=0)
'SMOTE' object has no attribute 'sample_indices_'
方案2:合并特征后重采样再拆分
X_train_combined = np.hstack((X_train_padded, X_train_lang_encoded)) X_train_resampled_combined, y_train_resampled = smote.fit_resample(X_train_combined, y_train) X_train_resampled = X_train_resampled_combined[:, :max_len] X_train_lang_resampled = X_train_resampled_combined[:, max_len:]
all the input arrays must have same number of dimensions, but the array at index 0 has 2 dimension(s) and the array at index 1 has 1 dimension(s)
解决方案
方法1:利用imblearn多输入特征重采样
imblearn的SMOTE支持直接传入列表形式的多特征输入,只要每个特征的样本数一致。步骤如下:
import numpy as np from imblearn.over_sampling import SMOTE # 1. 稀疏矩阵转密集数组(OneHotEncoder默认返回稀疏矩阵) X_train_lang_encoded_dense = X_train_lang_encoded.toarray() # 2. 组合两个特征为列表(每个元素对应一个模型输入) X_train_combined = [X_train_padded, X_train_lang_encoded_dense] # 3. 执行SMOTE重采样 oversampler = SMOTE(random_state=seed, k_neighbors=3) X_train_resampled_list, y_train_resampled = oversampler.fit_resample(X_train_combined, y_train) # 4. 拆分重采样后的特征 X_train_resampled_text = X_train_resampled_list[0] X_train_resampled_lang = X_train_resampled_list[1] # 模型训练(使用匹配的重采样特征) history = model.fit( [X_train_resampled_text, X_train_resampled_lang], y_train_resampled, validation_data=([X_test_padded, X_test_lang_encoded.toarray()], y_test), epochs=10, batch_size=16 )
方法2:手动同步重采样索引(备选)
如果需要单独处理文本特征,可通过SMOTE的indices_属性获取重采样样本对应的原始索引,再提取匹配的语言特征:
from imblearn.over_sampling import SMOTE # 对文本特征重采样 oversampler = SMOTE(random_state=seed, k_neighbors=3) X_train_resampled_text, y_train_resampled = oversampler.fit_resample(X_train_padded, y_train) # 获取重采样样本对应的原始索引 sampled_indices = oversampler.indices_ # 提取匹配的语言特征 X_train_lang_encoded_dense = X_train_lang_encoded.toarray() X_train_resampled_lang = X_train_lang_encoded_dense[sampled_indices] # 模型训练 history = model.fit( [X_train_resampled_text, X_train_resampled_lang], y_train_resampled, validation_data=([X_test_padded, X_test_lang_encoded.toarray()], y_test), epochs=10, batch_size=16 )
额外注意点
- 测试集的语言特征同样需要转换为密集数组后传入模型;
- 修正原代码中
market_encoder的笔误,统一替换为language_encoder,避免未定义变量错误。
内容的提问来源于stack exchange,提问作者Sandra Sukarieh
相关产品推荐
相关产品推荐

