使用SVM预测分类概率时结果不符合预期的问题咨询
你用SVM做分类时,发现训练集中的样本[10,10]对应标签1,但调用predict_proba得到的却是均匀分布的概率,完全不符合预期的[0 1 0 0 0 0 0]。这个问题其实是由SVM的概率估计逻辑和你的数据集特性共同导致的,咱们一步步理清楚:
为什么会出现这种情况?
1. 每个类别仅1个样本,模型无法学习有效边界
你的数据集里0到6每个标签都只有1条样本,SVM是靠最大化类别间间隔来分类的,这种极端小样本+单样本类别的场景下,模型根本没法学到不同类别之间的差异,更别说准确估计概率了。
而且SVM的predict_proba并非原生支持的功能,它是通过Platt缩放或者成对比较的方式间接计算概率的,这两种方法都需要足够的样本来校准概率模型,样本太少的话校准直接失效,就只能输出近似均匀的概率了。
2. 默认参数不适合这种极端场景
默认的SVC用的是RBF核,这个核的gamma参数默认是scale,会根据样本特征的方差计算。但每个类别只有1个样本时,方差计算完全没有意义,导致模型根本没法区分不同样本,自然输出的概率也没参考价值。
该怎么解决?
方案1:直接用predict()获取类别,别纠结概率
如果你只是需要确认这个样本的分类结果,直接用predict()就好,它会输出模型认为最可能的标签,完全符合你的预期:
from sklearn import svm X = [[0, 0], [10, 10],[20,30],[30,30],[40, 30], [80,60], [80,50]] y = [0, 1, 2, 3, 4, 5, 6] clf = svm.SVC() clf.fit(X, y) pred_label = clf.predict([[10, 10]]) print(pred_label) # 输出 [1],完美命中预期标签
方案2:调整参数+概率校准(如果必须要概率)
如果确实需要概率值,首先最好能给每个类别增加样本量(这是根本解决办法)。如果暂时没法加样本,可以试试调整SVM参数,再配合概率校准:
from sklearn import svm from sklearn.calibration import CalibratedClassifierCV X = [[0, 0], [10, 10],[20,30],[30,30],[40, 30], [80,60], [80,50]] y = [0, 1, 2, 3, 4, 5, 6] # 改用线性核,增大C值让模型更拟合训练数据 base_clf = svm.SVC(kernel='linear', C=10.0) # 用CalibratedClassifierCV做概率校准 clf = CalibratedClassifierCV(base_clf, cv="prefit") clf.fit(X, y) prob = clf.predict_proba([[10, 10]]) print(prob)
(注:因为样本量还是太小,这个结果可能依然不是完美的[0,1,0,...],但会比之前的均匀分布更接近预期)
方案3:换用k近邻分类器,直接得到精确概率
如果必须在单样本类别的场景下拿到100%对应标签的概率,SVM不是最佳选择。试试k近邻分类器,当k=1时,它会给训练集中的样本输出100%的对应类别概率:
from sklearn.neighbors import KNeighborsClassifier X = [[0, 0], [10, 10],[20,30],[30,30],[40, 30], [80,60], [80,50]] y = [0, 1, 2, 3, 4, 5, 6] clf = KNeighborsClassifier(n_neighbors=1) clf.fit(X, y) prob = clf.predict_proba([[10, 10]]) print(prob) # 输出 [[0. 1. 0. 0. 0. 0. 0.]],完全符合你的预期
内容的提问来源于stack exchange,提问作者Vidya Marathe

