基于SMOTE的XGBoost处理不平衡FTIR光谱数据问题求助
不平衡FTIR光谱分类:SMOTE+XGBoost问题排查与优化方案
问题概述
处理FTIR光谱.mat格式数据集的不平衡分类任务时,采用SMOTE+XGBoost模型遇到以下问题:
- SMOTE过采样后,所有交叉验证折的混淆矩阵中class 0仍未被有效识别
- 尝试加入样本权重后,触发729次拟合失败的
ValueError - 测试Borderline SMOTE、SVMSMOTE+EasyEnsemble等方法后,F1分数依旧偏低,怀疑核心原因是XGBoost中class 0的权重配置失衡
相关代码与报错
代码片段1:初始SMOTE+XGBoost实现
from imblearn.over_sampling import SMOTE from xgboost import XGBClassifier from sklearn.model_selection import cross_val_predict import scipy.io # 加载数据 data = scipy.io.loadmat('ftir_data.mat') X = data['features'] y = data['labels'].ravel() # 全局SMOTE过采样 smote = SMOTE(random_state=42) X_res, y_res = smote.fit_resample(X, y) # XGBoost训练与交叉验证 xgb = XGBClassifier(objective='multi:softmax', num_class=3, random_state=42) y_pred = cross_val_predict(xgb, X_res, y_res, cv=5)
代码片段2:样本权重尝试(触发报错)
import numpy as np from sklearn.model_selection import KFold # 计算样本权重 sample_weights = np.where(y == 0, 10.0, 1.0) # 交叉验证传递权重 kf = KFold(n_splits=5, shuffle=True, random_state=42) for train_idx, test_idx in kf.split(X_res, y_res): X_train, X_test = X_res[train_idx], X_res[test_idx] y_train, y_test = y_res[train_idx], y_res[test_idx] w_train = sample_weights[train_idx] xgb.fit(X_train, y_train, sample_weight=w_train) # 此处触发ValueError
报错信息
ValueError: Invalid parameter sample_weight for estimator XGBClassifier. Check the list of available parameters with `estimator.get_params().keys()`.
类别分布
[类别分布柱状图:class 0占比<5%,class 1/2占比均>45%]
解决方案与修改建议
1. 修复核心错误:避免数据泄露+正确传递样本权重
你之前先对整个数据集做SMOTE再交叉验证,会导致验证集信息泄露,这是class 0无法被有效识别的关键原因之一。必须在每个交叉验证折的训练集上单独执行过采样,同时修正样本权重的传递方式:
from imblearn.over_sampling import SMOTE from xgboost import XGBClassifier from sklearn.model_selection import KFold from sklearn.metrics import f1_score import numpy as np import scipy.io # 加载数据 data = scipy.io.loadmat('ftir_data.mat') X = data['features'] y = data['labels'].ravel() # 交叉验证(每个fold内单独处理过采样) kf = KFold(n_splits=5, shuffle=True, random_state=42) scores = [] class_weights = len(y) / (len(np.unique(y)) * np.bincount(y)) # 基于类别占比计算权重 for train_idx, test_idx in kf.split(X, y): X_train, X_test = X[train_idx], X[test_idx] y_train, y_test = y[train_idx], y[test_idx] # 仅在训练集上执行SMOTE smote = SMOTE(random_state=42) X_train_res, y_train_res = smote.fit_resample(X_train, y_train) # 生成训练集样本权重(给class 0更高权重) train_weights = class_weights[y_train_res] # 初始化XGBoost,配置多分类参数 xgb = XGBClassifier( objective='multi:softprob', # 输出概率,便于调整分类阈值 num_class=len(np.unique(y)), scale_pos_weight=class_weights, # 全局类别权重 learning_rate=0.05, max_depth=3, # 限制树深,避免过拟合高维光谱数据 eval_metric='mlogloss', random_state=42 ) # 正确传递样本权重 xgb.fit(X_train_res, y_train_res, sample_weight=train_weights) # 预测与评估 y_pred = xgb.predict(X_test) scores.append(f1_score(y_test, y_pred, average='weighted')) print(f"平均加权F1分数: {np.mean(scores):.4f}")
2. 优化XGBoost权重配置
- 改用
objective='multi:softprob':输出每个类别的概率,后续可通过调整分类阈值(如提高class 0的概率阈值)提升其识别率 scale_pos_weight参数:传入基于类别占比计算的权重数组(如[10,1,1]),直接给class 0赋予更高的训练权重- 调整模型复杂度:FTIR光谱维度高,需降低
max_depth、提高learning_rate,配合subsample、colsample_bytree减少过拟合
3. 改进采样策略
- 针对极度不平衡的class 0,尝试ADASYN:自适应生成难分类样本,比SMOTE更聚焦于少数类的边界样本
- 尝试SMOTEENN:过采样后移除与多数类重叠的噪声样本,提升样本质量
- 结合欠采样:用
RandomUnderSampler对多数类进行欠采样,再配合SMOTE过采样,避免过采样导致的噪声积累
4. FTIR光谱特征工程优化
- 降维处理:用PCA或t-SNE降低光谱维度,减少冗余特征与噪声
- 信号预处理:对光谱做基线校正、Savitzky-Golay平滑处理,提升有效信号的辨识度,帮助模型捕捉class 0的特征差异
5. 模型融合提升效果
- 采用EasyEnsemble:集成多个基于欠采样训练的XGBoost模型,每个模型在不同的多数类子集上训练,提升对少数类的识别稳定性
- 投票融合:结合XGBoost、LightGBM、CatBoost的预测结果,利用不同模型的优势平衡类别识别能力
内容的提问来源于stack exchange,提问作者Rahul Suresh
相关产品推荐
相关产品推荐

