RStudio中SVM模型预测单条用户输入报contrasts错误如何解决
问题描述
R中搭建的SVM模型对原始数据集内的行执行预测时可正常运行,以取数据集第一行为例,运行如下代码:
model <- readRDS("prediction_model.rds") result <- predict(model, data[1,]) print(result)
得到输出结果为:
4 Overweight_Level_I Levels: Insufficient_Weight Normal_Weight Obesity_Type_I Obesity_Type_II Obesity_Type_III Overweight_Level_I Overweight_Level_II
但搭建Shiny应用接收用户输入,构建如下单条输入数据框:
user_input <- data.frame(Gender="Male", Age=20, Height=1.6, Weight=60, family_history_with_overweight="yes", FAVC="yes", FCVC=1, NCP=1, CAEC="no", SMOKE="yes", CH2O=3, SCC="yes", FAF=0, TUE=2, CALC="no", MTRANS="Automobile")
执行相同预测代码:
model <- readRDS("prediction_model.rds") result <- predict(model, user_input)
时触发如下报错:
Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : contrasts can be applied only to factors with 2 or more levels
推测报错原因是用户输入构成的数据框每列仅存在1个唯一值,需要可行的解决方案。
报错核心原因
这个报错和单条数据每列只有1个值没有直接关系,本质是分类变量的因子水平和模型训练时的水平不匹配:
用data.frame()构建用户输入时,传入的字符串会被自动转换为因子类型,但这类因子的水平(levels)仅包含当前输入的单个取值,和模型训练时识别到的全量分类水平完全不一致。预测阶段模型需要将分类变量转换为对比矩阵(即报错中提到的contrasts计算逻辑),仅含1个水平的因子无法完成该转换,就会抛出错误。
之前取原始数据集第一行预测能正常运行,是因为原始数据集的分类列本身已经存储了全量因子水平,哪怕只截取1行,列的levels属性仍然完整,不会触发转换错误。
可行解决方案
注意:不要尝试通过给单条输入拼接伪造的多类别数据凑因子水平,会导致因子水平顺序和训练时不一致,直接造成预测结果错误。
以下方案都可以从根源解决问题:
- 方案1:提前持久化训练集的因子水平,预测前手动对齐
模型训练完成后,先把训练集所有分类列的因子水平保存到本地:
在Shiny应用中构建完用户输入数据框后,先加载保存的水平规则,逐列对齐分类变量的因子水平:# 提取训练集中所有因子列的水平,train_data是你训练模型用的原始数据集 factor_levels <- lapply(train_data[, sapply(train_data, is.factor)], levels) saveRDS(factor_levels, "factor_levels.rds")factor_levels <- readRDS("factor_levels.rds") for (col_name in names(factor_levels)) { user_input[[col_name]] <- factor(user_input[[col_name]], levels = factor_levels[[col_name]]) } # 对齐后再执行预测即可正常运行 result <- predict(model, user_input) - 方案2:借助训练集结构自动继承因子水平
构建用户输入时先关闭字符串自动转因子的默认设置:
然后取训练集特征部分(去掉因变量列)生成1行空结构,和用户输入按行拼接,就能自动继承所有列的属性和因子水平,最后取拼接后的第二行就是处理好的待预测数据:user_input <- data.frame(Gender="Male", Age=20, Height=1.6, Weight=60, family_history_with_overweight="yes", FAVC="yes", FCVC=1, NCP=1, CAEC="no", SMOKE="yes", CH2O=3, SCC="yes", FAF=0, TUE=2, CALC="no", MTRANS="Automobile", stringsAsFactors = FALSE)# train_x是训练集去掉因变量后的纯特征数据框 input_template <- train_x[1, ][NA, ] # 生成和训练集同结构的空行 processed_input <- rbind(input_template, user_input)[2, ] result <- predict(model, processed_input) - 方案3:建模时固定预处理流程
如果使用caret、tidymodels等框架建模,可以将分类变量编码、因子水平设置、缺失值处理等预处理逻辑全部纳入建模工作流,将工作流和模型一起保存为R对象。预测时直接将原始用户输入传入工作流的预测函数,会自动完成所有预处理步骤,从根源避免水平不匹配、变量类型错误等问题。
内容的提问来源于stack exchange,提问作者ryoshi007
相关产品推荐
相关产品推荐

