当我尝试使用支持向量机(SVM
)提取预测概率时,遇到了以下问题。通常,分类算法的概率阈值是0.5。但我需要分析SVM
机器学习算法的准确率随概率阈值的变化情况。
我在R
中使用了caret
包,并采用了留一法交叉验证(LOOCV)。
首先,我拟合了一个常规的SVM模型,没有提取类别概率。因此,它只会存储预测的类别标签。
数据来源:https://www.kaggle.com/uciml/pima-indians-diabetes-database
require(caret)set.seed(123)diabetes <- read.csv("C:/Users/Downloads/228_482_bundle_archive/diabetes.csv")fitControl1 <- trainControl( method = "LOOCV",savePredictions = T,search = "random")diabetes$Outcome=factor(diabetes$Outcome)modelFitlassocvintm1 <- train((Outcome) ~ Pregnancies+BloodPressure+Glucose + BMI+DiabetesPedigreeFunction +Age , data=diabetes, method = "svmRadialSigma", trControl = fitControl1, preProcess = c("center", "scale"), tuneGrid=expand.grid( .sigma=0.004930389, .C=9.63979626))
为了提取预测概率,我需要在trainControl
中指定classProbs = T
。
set.seed(123)fitControl2 <- trainControl( method = "LOOCV",savePredictions = T,classProbs = T)diabetes$Outcome=factor(diabetes$Outcome)modelFitlassocvintm2 <- train(make.names(Outcome) ~ Pregnancies+BloodPressure+Glucose + BMI+DiabetesPedigreeFunction +Age , data=diabetes, method = "svmRadialSigma", trControl = fitControl2, preProcess = c("center", "scale"), tuneGrid=expand.grid( .sigma=0.004930389, .C=9.63979626))
modelFitlassocvintm1
和modelFitlassocvintm2
唯一的区别在于trainControl
中包含了classProbs = T
。
如果我比较modelFitlassocvintm1
和modelFitlassocvintm2
的预测类别,在0.5的概率阈值下它们应该相同。但事实并非如此。
table(modelFitlassocvintm2$pred$X1 >0.5,modelFitlassocvintm1$pred$pred) 0 1 FALSE 560 0 TRUE 8 200
当我进一步调查这8个不同的值时,得到了以下结果。
subs1=cbind(modelFitlassocvintm2$pred$X1,modelFitlassocvintm2$pred$pred,modelFitlassocvintm1$pred$pred)subset(subs1,subs1[,2]!=subs1[,3]) [,1] [,2] [,3][1,] 0.5078631 2 1[2,] 0.5056252 2 1[3,] 0.5113336 2 1[4,] 0.5048708 2 1[5,] 0.5033003 2 1[6,] 0.5014327 2 1[7,] 0.5111975 2 1[8,] 0.5136453 2 1
似乎当预测概率接近0.5时,modelFitlassocvintm1
和modelFitlassocvintm2
中的预测类别存在差异。我在使用不同数据集的svm
时也观察到了类似的差异。
这可能是什么原因?我们不能相信svm
的预测概率吗?通常,svm根据样本相对于超平面的位置将其分类为-1或1。因此,依赖svm的预测概率不是一个好主意吗?
回答:
正如desertnaut在评论中所指出的,SVM不是概率分类器;它们实际上不产生概率。
一种创建概率的方法是直接训练带有logit链接函数和正则化最大似然评分的核分类器。然而,使用最大似然评分进行训练将产生非稀疏的核机器。相反,在训练SVM后,会训练一个额外的S形函数的参数,将SVM的输出映射到概率上。参考论文:Probabilistic Outputs for Support Vector Machines and Comparisons to Regularized Likelihood Methods
Caret中的method = "svmRadialSigma"
内部使用了kernlab::ksvm
,并带有kernel = "rbfdot"
参数。为了使此函数生成概率,需要prob.model = TRUE
参数。从该函数的帮助文档中可以看到:
prob.model 如果设置为TRUE,将构建一个用于计算类别概率的模型,或者在回归情况下,计算拟合在残差上的拉普拉斯分布的缩放参数。拟合是在通过对训练数据进行3折交叉验证创建的输出数据上完成的。详细信息请参见参考文献。(默认值:FALSE)
参考的详细信息:
在分类中,当prob.model为TRUE时,会对数据进行3折交叉验证,并对结果决策值f拟合一个S形函数。
显然,当需要后验概率时,分类模型会发生一些非常具体的变化。这与仅输出决策值是不同的。
由此可以推导出,根据S形函数的拟合,一些决策值可能会与在没有prob.model
(prob.model = FALSE
)的情况下运行[kernlab::ksvm
]时不同,这就是你在发布的示例中观察到的现象。
如果有两个以上的类别,情况会变得更加复杂。
进一步阅读:
Including class probabilities might skew a model in caret?
Isn’t caret SVM classification wrong when class probabilities are included?
Why are probabilities and response in ksvm in R not consistent?