我想将SVM应用于一个不平衡的数据集,回答1, 回答2 建议可以通过调整fitcsvm
函数的参数来实现。尽管SVM可能不是处理不平衡数据的好选择,但我出于教育目的想看看结果。
由于我的数据自然不平衡,标记为1
(真实类别)的样本数量远少于0
(假类别),只有2%的样本被标记为1
。我如何调整SVM的参数以对真实类别的误分类错误施加更大的惩罚?
-
数据集有1473个样本(98%)标记为
0
,27个样本(2%)标记为1
。 -
训练数据有1000个样本标记为
0
,12个样本标记为1
。 - 测试数据有473个样本(97%)标记为
0
,15个样本(3%)标记为1
。我通过使用下面的成本矩阵c
对1
施加了两倍的惩罚:
c=[0 2.2;1 0];model = fitcsvm(train_x,train_y,'KernelFunction', 'rbf', 'Cost',c);[predLabel,score] = predict(model,test_x);
结果是
标签0的精确度: 9.692623e-01标签1的精确度: NaN标签0的召回率: 1标签1的召回率: 0准确率 = 96.9%平均误差 = 0.03
混淆矩阵是
473 0 15 0
predict
向量中的答案都是1
标签。显然,成本矩阵没有正确工作。我不太明白从成本矩阵来看,我是在惩罚多数类0
的误分类,还是少数类1
的误分类。为什么第一行和第一列的元素=0,而另一个是2。请帮助我解答这个问题。
回答:
这可以使用一些测试数据来展示,例如以下内容:
rng(42)X = randn(1000, 2);y = rand(1000, 1) >= 0.98;X(y==1, :) = X(y==1, :) + [2, 2];
由于类别不平衡,一个简单的带有高斯核函数的SVM不会工作得很好:
model = fitcsvm(X, y, 'KernelFunction', 'rbf')confusionmat(y, model.predict(X))ans = 979 2 14 5
正如你已经认识到的,'Cost'
参数可以用来通过对少数类误分类施加更高的惩罚来补偿不平衡。在二维情况下,成本矩阵的构建如下:
[ Cost(0, 0), Cost(0, 1) Cost(1, 0), Cost(1, 1) ]
现在,Cost(0, 0)
是将属于类0
的样本分类为类0
的成本。这是正确的分类,因此通常成本设置为0。接下来,Cost(0, 1)
是将属于类0
的点分类为类1
的成本,即错误分类。
在你的例子中,类0
比类1
更容易出现,所以我们应该对将来自多数类0
的样本分类为少数类1
施加低惩罚,而对将来自少数类1
的样本分类为多数类0
施加高惩罚。因此,Cost(0, 1)
应该低,而Cost(1, 0)
应该高。
通过设置c = [0, 2.2; 1, 0]
,你做了相反的事情——你建议fitcsvm
函数宁愿将少数类样本分类为多数类而不是相反:
c = [0, 2.2; 1, 0];model = fitcsvm(X, y, 'KernelFunction', 'rbf', 'Cost', c);confusionmat(y, model.predict(X))ans = 981 0 19 0
如果你在成本矩阵c
中使用相同的权重,但交换Cost(0, 1)
和Cost(1, 0)
,那么期望的效果就会发生:
c = [0, 1; 2.2, 0];model = fitcsvm(X, y, 'KernelFunction', 'rbf', 'Cost', c);confusionmat(y, model.predict(X))ans = 973 8 7 12
这确实改善了我们的结果:总体上我们有相似的误分类数量:15个而不是16个总误分类,但使用新模型,我们的19个少数类样本中有12个是正确的,而之前只有5个。