SMOTE平衡数据集后LightGBM训练仅单类问题修复请求
问题背景
原数据集为二分类不平衡数据集,Target变量中类别"0"有61471条记录,类别"1"有4456条记录。为缓解不平衡,使用SMOTE对训练集进行过采样处理。
SMOTE处理情况
执行SMOTE代码
trainSMOTE <- SMOTE(target ~ ., train, perc.over = 400, k = 5) dim(trainSMOTE)
输出结果:57928 67
平衡验证
table(trainSMOTE$target)
输出结果:
0 1 35648 22280
可见SMOTE后数据集已达到平衡状态。
LightGBM训练异常情况
训练代码片段
创建LightGBM数据集
train_data <- lgb.Dataset(data.matrix(trainSMOTE[, -9]), label = trainSMOTE[, trainSMOTE$target]) test_data <- lgb.Dataset(data.matrix(test[, -9]), label = test[, test$target])
模型参数定义
params <- list( objective = "binary", metric = 'auc', boosting_type = "gbdt", num_leaves = 100, learning_rate = 0.05, feature_fraction = 0.9, bagging_fraction = 0.8, bagging_freq = 5, min_data_in_leaf = 50, max_depth = -1, verbose = -1 )
启动训练
model <- lgb.train(params = params, data = train_data, valids = list(test = test_data), early_stopping_rounds = 50)
模型报错信息
[LightGBM] [Warning] Contains only one class[LightGBM].
[Info] Number of positive: 57928, number of negative: 0.
[LightGBM] [Warning] Auto-choosing col-wise multi-threading, the overhead of testing was 0.050367 seconds.
You can setforce_col_wise=trueto remove the overhead.
[LightGBM] [Info] Total Bins 15250.
[LightGBM] [Info] Number of data points in the train set: 57928, number of used features: 65.
[LightGBM] [Info] [binary:BoostFromScore]: pavg=1.000000 -> initscore=34.539576.
[LightGBM] [Info] Start training from score 34.539576.
[LightGBM] [Info] [binary:BoostFromScore]: pavg=1.000000 -> initscore=34.539576.
[LightGBM] [Warning] Stopped training because there are no more leaves that meet the split requirements.
[1] "[1]: test's auc:0.5"...........
问题原因
核心错误出在标签列的提取逻辑:
代码中label = trainSMOTE[, trainSMOTE$target]的写法完全错误,trainSMOTE$target是一个长度为57928的向量,用它作为列索引时,会对每一行提取对应索引的列值,最终得到的不是标签列,而是一个混乱的矩阵,导致LightGBM识别到的所有样本标签被错误统一为同一类。
解决方案
修正标签提取逻辑
将标签列的提取方式改为直接取目标列,以下两种方式均可:
方式1:按列名提取(更健壮,避免列索引变化)
# 训练集 train_data <- lgb.Dataset( data = data.matrix(trainSMOTE[, -which(names(trainSMOTE) == "target")]), label = trainSMOTE$target ) # 测试集 test_data <- lgb.Dataset( data = data.matrix(test[, -which(names(test) == "target")]), label = test$target )
方式2:按列索引提取(已知target是第9列时使用)
# 训练集 train_data <- lgb.Dataset( data = data.matrix(trainSMOTE[, -9]), label = trainSMOTE[, 9] ) # 测试集 test_data <- lgb.Dataset( data = data.matrix(test[, -9]), label = test[, 9] )
额外验证步骤
在创建数据集前,先确认标签分布是否正确,避免再次出错:
# 确认训练集标签分布 table(trainSMOTE$target) # 确认提取的label是否与原标签一致 table(trainSMOTE[, 9]) # 对应方式2的列索引
内容的提问来源于stack exchange,提问作者Guillermo Mansilla

