使用ktrain与BERT进行文本分类时,SMOTE处理不平衡数据集触发维度错误的问题
问题分析与解决方法
错误原因
你遇到的ValueError: Found array with dim 3. Estimator expected <= 2.本质原因是SMOTE算法的设计限制:
- SMOTE是为传统机器学习的2维特征矩阵(形状为
[样本数量, 特征数量])设计的,它只能处理扁平化的数值特征。 - 而ktrain的
texts_from_df方法在使用BERT预训练模式时,返回的x_train是3维张量(形状通常为[样本数量, 最大序列长度, 特征维度],或者是包含input_ids、attention_mask等多个2D张量的组合),完全不满足SMOTE的输入要求。
简单来说:SMOTE看不懂BERT的序列输入,它只认识扁平的特征向量。
解决方法
根据你的项目场景,推荐三种可行的解决方案,按实现复杂度从低到高排序:
方案1:在BERT预处理前对原始数据做过采样
跳过直接对BERT输入做SMOTE,转而在原始文本数据层面做过采样,再喂给ktrain处理。这里推荐用RandomOverSampler(比SMOTE更适合文本场景,因为SMOTE无法直接生成有意义的文本):
# 先对原始训练数据集做过采样 from imblearn.over_sampling import RandomOverSampler # 初始化过采样器 ros = RandomOverSampler(random_state=42) # 对训练集的文本列和标签列进行过采样 train_text_resampled, train_label_resampled = ros.fit_resample( data_train[["Tweet"]], data_train["Sentiment"] ) # 合并回完整的DataFrame data_train_resampled = pd.concat([train_text_resampled, train_label_resampled], axis=1) # 用重采样后的数据集生成BERT输入 (x_train,y_train),(x_test,y_test),preprocess =text.texts_from_df( train_df=data_train_resampled, text_column="Tweet", label_columns = "Sentiment", val_df = data_test, maxlen = 400, preprocess_mode = 'bert' )
这种方法的优点是简单直接,不需要修改模型逻辑,适合快速验证效果。
方案2:给BERT模型设置类别权重(推荐)
不需要做任何数据采样,直接在模型训练时通过损失函数的权重来平衡类别。ktrain支持在创建分类器时指定class_weight参数:
# 生成BERT输入(不需要提前采样) (x_train,y_train),(x_test,y_test),preprocess =text.texts_from_df( train_df=data_train, text_column="Tweet", label_columns = "Sentiment", val_df = data_test, maxlen = 400, preprocess_mode = 'bert' ) # 创建分类器时指定class_weight='balanced',自动给少数类分配更高权重 model = text.text_classifier( 'bert', (x_train, y_train), preproc=preprocess, class_weight='balanced' ) learner = ktrain.get_learner( model, train_data=(x_train, y_train), val_data=(x_test, y_test), batch_size=8 ) # 后续正常训练即可 learner.fit_onecycle(2e-5, 3)
这种方法是处理类别不平衡的更优方案,因为它不会引入数据冗余或合成无意义的样本,而是让模型主动关注少数类。
方案3:使用文本专用的数据增强方法
如果想生成全新的少数类文本样本(而非重复现有样本),可以用文本数据增强技术,比如EDA(Easy Data Augmentation),或者用预训练语言模型生成新文本:
- EDA操作包括:同义词替换、随机插入同义词、随机交换词语、随机删除词语
- 你可以手动实现这些逻辑,或者用现成的库(比如
nlpaug)
示例(用nlpaug做同义词替换):
import nlpaug.augmenter.word as naw # 初始化增强器,用WordNet做同义词替换 aug = naw.SynonymAug(aug_src='wordnet') # 对少数类样本进行增强 minority_class = data_train[data_train["Sentiment"] == "你的少数类标签"] augmented_texts = aug.augment(minority_class["Tweet"].tolist(), n=3) # 每个样本生成3个增强版本 # 把增强后的样本合并到训练集 augmented_df = pd.DataFrame({ "Tweet": augmented_texts, "Sentiment": ["你的少数类标签"] * len(augmented_texts) }) data_train_resampled = pd.concat([data_train, augmented_df], axis=0) # 再用这个数据集生成BERT输入
这种方法能丰富训练数据,但需要注意增强后的文本要保持语义一致性,避免引入噪声。
内容的提问来源于stack exchange,提问作者DevLeb2022
相关产品推荐
相关产品推荐

