平衡线性 SVM 赢得除 One vs All 之外的所有类别

数据挖掘 分类 scikit-学习 多类分类 阶级失衡
2022-03-08 19:22:57

我正在使用不平衡数据训练正常平衡的 线性 SVM,并使用 F1 分数进行测试。

(通过平衡线性支持向量机,我的意思是每个观测值的权重与其频率成反比,因此它从少数类“过采样”而从多数类“欠采样”。)

毫不奇怪,使用多类数据集并将其作为一对一二元问题单独解决,平衡线性 SVM 在每个类上都击败了不平衡线性 SVM:

                   normal vs balanced
target 01 - scores: 0.272 vs 0.608
target 02 - scores: 0.391 vs 0.587
target 03 - scores: 0.433 vs 0.546
target 04 - scores: 0.659 vs 0.655
target 05 - scores: 0.000 vs 0.257
target 06 - scores: 0.431 vs 0.475
target 07 - scores: 0.000 vs 0.249
target 08 - scores: 0.000 vs 0.053
target 09 - scores: 0.576 vs 0.155
target 10 - scores: 0.000 vs 0.550
OneVsRest - scores: 0.565 vs 0.540

但是在使用 OneVsRest 分类器(最后一行)时,使用 F1 分数的平均值,不平衡线性 SVM 击败了线性 SVM。这发生在我的很多数据集中。

这是我使用的代码:

# -*- coding: utf-8 -*-

from sklearn.svm import LinearSVC
from sklearn.multiclass import OneVsRestClassifier
from sklearn.cross_validation import StratifiedShuffleSplit
from sklearn.metrics import f1_score
import numpy as np

d = np.loadtxt('yeast.csv', delimiter=',')
X = d[:, 0:-1]
y = d[:, -1]

print '                     none vs balanced'
for target in np.unique(y):
    scores = [0] * 2
    k = 6
    for tr, ts in StratifiedShuffleSplit(y, k, 0.2):
        for i, w in enumerate([None, 'balanced']):
            _y = (y == target).astype(int)
            yp = LinearSVC(class_weight=w).fit(X[tr], _y[tr]).predict(X[ts])
            scores[i] += f1_score(_y[ts], yp) / k
    print 'target %02d - scores: %.3f vs %.3f' % (target, scores[0], scores[1])

scores = [0] * 2
for tr, ts in StratifiedShuffleSplit(y, k, 0.2):
    for i, w in enumerate([None, 'balanced']):
        m = OneVsRestClassifier(LinearSVC(class_weight=w))
        yp = m.fit(X[tr], y[tr]).predict(X[ts])
        scores[i] += f1_score(y[ts], yp, pos_label=None, average='weighted') / k
print 'OneVsRest - scores: %.3f vs %.3f' % (scores[0], scores[1])

作为数据集,我在这里使用了Yeast (UCI),在这里可以找到一个 sklearn-ready 版本

结果:

                     none vs balanced
target 01 - scores: 0.241 vs 0.612
target 02 - scores: 0.402 vs 0.604
target 03 - scores: 0.444 vs 0.552
target 04 - scores: 0.666 vs 0.650
target 05 - scores: 0.000 vs 0.286
target 06 - scores: 0.370 vs 0.500
target 07 - scores: 0.000 vs 0.280
target 08 - scores: 0.000 vs 0.082
target 09 - scores: 0.563 vs 0.147
target 10 - scores: 0.000 vs 0.511
OneVsRest - scores: 0.567 vs 0.543

这不是很奇怪吗?我知道 OneVsRest 有很多联系,因此将使用分数,由到分离超平面的距离决定。尽管如此,为什么平衡的线性 SVM 在不断赢得每一场战斗的时候却输掉了这场战争?

编辑:我认为一个模型赢得了几乎每一场“战斗”,而不是“战争”这一事实与 One Vs Rest 使用置信度得分这一事实有关,而不是实际分类,这是有道理的,因为它避免了平局,当 score[k1] > score[k2] 我们可以假设它更喜欢 k1 > k2。参见维基百科

1个回答

我认为以下综合示例解释了正在发生的事情:

from sklearn.base import clone
from sklearn.svm import SVC
import numpy as np
from sklearn.datasets import make_blobs

X, y = make_blobs(1000, centers=[[1, 1], [-1, -1], [1, -1]], cluster_std=0.8)

models = [
    ('normal', SVC()),
    ('balanced', SVC(class_weight='balanced')),
]

import matplotlib.pyplot as plot
plot.ioff()

for i, (name, m) in enumerate(models):
    for k in np.unique(y):
        m = clone(m).fit(X, (y == k).astype(int))

        xx, yy = np.meshgrid(np.arange(-3, 3, 0.1), np.arange(-3, 3, 0.1))
        z = m.predict(np.c_[xx.ravel(), yy.ravel()])
        z = z.reshape(xx.shape)
        colors = ['blue', 'green', 'red']
        plot.contour(xx, yy, z, colors=colors[k])
    plot.scatter(X[:, 0], X[:, 1], c=y)
    plot.title(name)
    plot.show()

正常支持向量机 平衡支持向量机

在单独考虑每个模型时,平衡 SVM 显然更胜一筹,至少按照 F1 分数衡量,它平等地惩罚假阳性和假阴性。

但是 One-vs-Rest 使用最大分数,在这种情况下由到决策超平面的距离给出。目前尚不清楚为什么在某些情况下情况会更糟,但很明显,一个模型有可能在单个类别中击败另一个模型,但在 One-vs-Rest 中输了:相对距离建模的好坏是 One 中最重要的-vs-休息。