glmnet报错‘某二项/多项类别样本量不足8’问题排查求助
问题排查:弹性网训练报错"one multinomial or binomial class has fewer than 8 observations; dangerous ground"
背景
编写弹性网(elastic net)分类模型脚本,数据集共22行,执行以下操作后触发报错:
- 将响应变量转为因子类型
- 提取"age"到"ErythritolArea"列作为预测变量列表
pred.names.min - 按65%/35%划分训练集与测试集
- 设置
trainControl为留一交叉验证(LOOCV)+随机搜索调参 - 调用
train函数训练glmnet模型
报错信息:
"one multinomial or binomial class has fewer than 8 observations; dangerous ground"
已确认所有预测变量均为数值型,但无法理解报错原因,请求排查。
复现代码
library(dplyr) library(tidyverse) library(glmnet) library(caret) # 创建示例数据集 df <- data.frame( "L_TartaricacidArea" = c(1,2,6,4,9,2,1,2,2,1,8), "Hydroxymethyl_5_furancarboxylicacidArea_2" = c(2,4,6,1,8,9,1,9,2,6,4,1,2,6,4,9,2,1,2,2,1,8), "BMIfactor" = c(1,8,6,1,2,9,2,2,9,2,1,2,4,6,1,8,9,1,9,2,6,4), "age" = c(0,4,8,1,2,7,4,9,9,2,2,1,8,6,1,2,9,2,2,9,2,1), "Anhydro_1.5_D_glucitolArea" = c(8,5,8,6,2,9,2,8,9,4,2,0,4,8,1,2,7,4,9,9,2,2), "LevoglucosanArea" = c(6,2,9,2,8,6,1,8,2,1,2,8,5,8,6,2,9,2,8,9,4,2), "HexadecanolArea_1" = c(4,9,2,1,2,9,2,1,6,1,2,6,2,9,2,8,6,1,8,2,1,2), "EthanolamineArea" = c(6,4,9,2,1,2,4,6,1,8,2,4,9,2,1,2,9,2,1,6,1,2), "OxoglutaricacidArea_2" = c(4,7,8,2,5,2,7,6,9,2,4,6,4,9,2,1,2,4,6,1,8,2), "AminopentanedioicacidArea_3" = c(2,5,5,5,2,9,7,5,9,4,4,4,7,8,2,5,2,7,6,9,2,4), "XylitolArea" = c(6,8,3,5,1,9,9,6,6,3,7,2,5,5,5,2,9,7,5,9,4,4), "DL_XyloseArea" = c(6,9,5,7,2,7,0,1,6,6,3,6,8,3,5,1,9,9,6,6,3,7), "ErythritolArea" = c(6,7,4,7,9,2,5,5,8,9,1,6,9,5,7,2,7,0,1,6,6,3), "hpresponse1" = c(1,0,1,1,0,1,1,0,0,1,0,0,1,0,1,1,1,0,1,0,0,1), "hpresponse2" = c(1,0,1,0,0,1,1,1,0,1,0,1,0,1,1,0,1,0,1,0,0,1) ) # 将响应变量转为因子 df$hpresponse1 <- as.factor(df$hpresponse1) df$hpresponse2 <- as.factor(df$hpresponse2) # 提取预测变量列名 pred.start.min <- which(colnames(df) == "age") pred.stop.min <- which(colnames(df) == "ErythritolArea") pred.names.min <- colnames(df)[pred.start.min:pred.stop.min] # 划分训练集与测试集(65%/35%) set.seed(2) n <- floor(nrow(df)*0.65) train_ind <- sample(seq_len(nrow(df)), size = n) trainingset <- df[train_ind,] testingset <- df[-train_ind,] # 设置训练控制参数:LOOCV+随机搜索 tcontrol <- trainControl(method = "LOOCV", search="random") # 训练模型 elastic_model <- train(as.matrix(trainingset[, pred.names.min]), trainingset$hpresponse1, data = trainingset, method = "glmnet", trControl = tcontrol)
问题原因分析
- 训练集类别样本量不足:总数据集22行,65%训练集约14行。运行
table(trainingset$hpresponse1)会发现其中一类样本量少于8。报错里的"fewer than 8 observations"指的是训练集内的某一类别样本数,而非总数据集的样本量。 - LOOCV的放大效应:LOOCV每次留一个样本验证,当训练集本身类别样本量极少时,交叉验证过程中会出现某类样本数进一步减少(甚至为0)的情况,触发glmnet的警告/报错。
解决办法
- 取消训练集划分,用全数据集做LOOCV:避免小样本下的类别失衡,直接用全部22行数据训练。
- 改用分层抽样划分训练集:保证训练集内类别比例与原数据集一致,避免某类样本量过少。
- 更换交叉验证方式:改用k折交叉验证(比如k=5),但需确保每折内类别分布合理。
示例修改代码(用全数据集训练+LOOCV):
# 设置训练控制参数 tcontrol <- trainControl(method = "LOOCV", search="random") # 用全数据集训练模型 elastic_model <- train(as.matrix(df[, pred.names.min]), df$hpresponse1, data = df, method = "glmnet", trControl = tcontrol)
内容的提问来源于stack exchange,提问作者stephr
相关产品推荐
相关产品推荐

