使用e1071包的SVM调参后准确率不升反降,求原因及解决办法
关于SVM调参后准确率骤降的问题分析与解决建议
我来帮你拆解这个问题——从你的代码和混淆矩阵来看,调参后的模型直接退化到只预测多数类(类别2),准确率刚好等于测试集中类别2的占比(47.6%),这是核心症结所在。下面我梳理可能的原因,再给出具体的操作建议:
可能的原因
- 特征未做尺度归一化:SVM(尤其是径向基核)对特征尺度极其敏感。如果你的特征之间数值范围差异悬殊,模型会偏向于尺度大的特征,导致学习失效;极端参数下甚至会直接退化到预测多数类。
- 参数搜索范围极端不合理:你尝试的
gamma=10^10、cost=10^100这类参数完全脱离实际。gamma过大时,径向基核会变得非常"尖锐",每个样本都会成为支持向量,模型彻底过拟合;cost过大则会让模型为了不犯任何错误而过拟合,而你最初选的cost=0.1过小,模型对错误的容忍度太高,直接选择最省心的多数类预测。 - 类别不平衡导致CV结果失真:从测试集的混淆矩阵来看,类别2的样本占比接近48%,如果训练集也是类似分布,普通的10折交叉验证可能无法捕捉到少数类的信息,导致
tune.svm选出的参数偏向于优化整体准确率(即预测多数类)。 - 交叉验证的评估指标问题:
tune.svm默认用分类准确率作为评估指标,在类别不平衡的场景下,这个指标会误导模型选择预测多数类的策略,而非真正的泛化能力。
具体操作建议
1. 先做特征标准化处理
这是SVM任务的必备步骤,一定要先对训练集的特征做标准化,再用同样的均值和标准差转换测试集:
# 对训练集特征标准化(排除目标变量rating) train_scaled <- scale(train[, -which(names(train) == "rating")]) # 合并标准化后的特征和目标变量 train_scaled_df <- cbind(data.frame(rating = train$rating), train_scaled) # 用训练集的标准化参数处理测试集 test_scaled <- scale(test[, -which(names(test) == "rating")], center = attr(train_scaled, "scaled:center"), scale = attr(train_scaled, "scaled:scale")) test_scaled_df <- cbind(data.frame(rating = test$rating), test_scaled)
之后用标准化后的数据集重新训练和调参。
2. 调整合理的参数搜索范围
放弃极端的参数值,选择SVM常用的参数区间:
# 合理的gamma和cost范围,搭配分层交叉验证适配类别不平衡 tune_result <- tune.svm(rating ~ ., data = train_scaled_df, kernel = "radial", gamma = 10^c(-3, -2, -1, 0, 1), # 0.001到10 cost = 10^c(-2, -1, 0, 1, 2), # 0.01到100 cross = 10, sampling = "stratified") summary(tune_result)
3. 解决类别不平衡问题
- 设置类别权重:在
svm函数中给少数类更高的权重,强制模型关注少数类:
# 假设类别1和3是少数类,设置对应权重 model_weighted <- svm(rating ~ ., data = train_scaled_df, kernel = "radial", gamma = tune_result$best.parameters$gamma, cost = tune_result$best.parameters$cost, class.weights = c("1" = 2, "2" = 1, "3" = 2))
- 换用更鲁棒的评估指标:调参时不要只依赖准确率,可以手动实现分层交叉验证,计算F1分数、AUC等对类别不平衡更友好的指标,再选择最优参数。
4. 检查模型状态
训练后查看模型的支持向量数量,判断是否过拟合/欠拟合:
summary(model_weighted)
如果支持向量数量接近训练集样本数,说明过拟合;如果过少,说明欠拟合,需要调整参数范围。
5. 尝试其他核函数
如果径向基核效果一直不好,可以试试线性核或多项式核,看看是否更适配你的数据:
# 线性核调参示例 tune_linear <- tune.svm(rating ~ ., data = train_scaled_df, kernel = "linear", cost = 10^c(-2, -1, 0, 1, 2), cross = 10, sampling = "stratified")
内容的提问来源于stack exchange,提问作者AdeeThyag
相关产品推荐
相关产品推荐

