如何在训练-测试场景中有效应用调优后的SVM模型
解决训练-测试场景下调优SVM失效的问题
这问题太典型了——全数据集上模型表现得倍儿棒,但一拆分训练测试集就拉胯,甚至直接躺平全预测成多数类,核心问题出在数据拆分逻辑、类别权重的计算时机和调优流程的合理性上,咱们一步步来解决:
1. 先搞清楚为啥拆分后效果崩了
你最后测试集的混淆矩阵显示,模型把所有样本都预测成了9类,这大概率是两个原因:
- 首先检查你的
createDataPartition是否真的做了分层抽样:虽然函数默认是分层,但如果你的少数类(-1、0)样本数太少,抽样后训练集里的这些类可能还是少得可怜,模型根本学不会识别它们的特征。 - 其次,你用的
class.weights是基于全数据集的样本占比计算的,但训练集的类别分布和全数据集肯定不一样,用错权重等于给模型指了歪路,自然学偏了。
先验证下训练集和测试集的类别分布:
train_index <- createDataPartition(dtm$type, p=0.75, list=FALSE, strata=dtm$type) train <- dtm[train_index,] test <- dtm[-train_index,] # 查看分布 prop.table(table(train$type)) prop.table(table(test$type))
如果训练集里-1、0类的占比和全数据集偏差很大,那第一步就要把抽样逻辑掰正。
2. 修正类别权重的计算方式
类别权重的核心是给少数类更高的权重,但这个权重必须基于当前训练集的分布计算,绝对不能用全数据集的!
正确的计算逻辑(两种常用方式任选):
# 基于训练集计算类别权重 train_class_counts <- table(train$type) # 方式1:1/该类在训练集的占比(简单直接) class_weights <- setNames(1 / prop.table(train_class_counts), names(train_class_counts)) # 方式2:总样本数/(类别数*该类样本数)(更均衡的加权方式) class_weights <- setNames(nrow(train)/(length(levels(train$type)) * train_class_counts), names(train_class_counts))
这样计算出来的权重才是适配训练集的,能真正帮模型重视少数类。
3. 重新走正确的调优流程
你之前的tune是直接用全数据集做的,这属于数据泄露——调优过程用到了测试集的信息,所以全数据集上效果好,但放到真实训练测试场景就失效了。正确的流程应该是:
# 1. 先分层拆分训练/测试集,锁死测试集不动 train_index <- createDataPartition(dtm$type, p=0.75, list=FALSE, strata=dtm$type) train <- dtm[train_index,] test <- dtm[-train_index,] x_train <- subset(train, select=-type) y_train <- train$type # 2. 基于训练集计算类别权重 train_class_counts <- table(y_train) class_weights <- setNames(nrow(train)/(4 * train_class_counts), names(train_class_counts)) # 3. 仅在训练集上做交叉验证调优(绝对不能碰测试集) svm_tune <- tune(svm, train.x=x_train, train.y=y_train, class.weights = class_weights, ranges=list(cost=10^(-1:2), gamma=c(.25, .5, 1)), # 可以根据需要调整搜索范围 tunecontrol = tune.control(cross=10)) # 10折交叉验证找最佳参数 # 4. 用最佳参数在整个训练集上训练模型 best_svm <- svm(type ~ ., data=train, cost=svm_tune$best.parameters$cost, gamma=svm_tune$best.parameters$gamma, class.weights = class_weights) # 5. 最后用测试集评估真实性能 pred_test <- predict(best_svm, newdata=test) confusionMatrix(pred_test, test$type)
4. 针对极端不平衡数据的额外优化
你的数据里9类占了78%,-1和0类占比极低(2.5%和0.5%),仅靠权重可能不够,试试这些方法:
- 过采样少数类:用SMOTE算法生成少数类的合成样本,比如
DMwR包的SMOTE函数,让训练集的类别分布更均衡。 - 换用更合理的评估指标:准确率在不平衡数据里毫无意义,重点看少数类的召回率(比如-1类有多少被正确识别)、F1分数或者ROC-AUC,这些指标才能反映模型的真实性能。
- 尝试线性核SVM:你的特征是文本DTM(高维稀疏),线性核在这种场景下往往比径向基核效果更好,计算也更快,可以把
kernel="linear"加入调优范围试试。
内容的提问来源于stack exchange,提问作者Banjo
相关产品推荐
相关产品推荐

