6

我是 SVM 的新手,我正在尝试使用 Python 的libsvm接口对包含均值和标准差的样本进行分类。但是,我得到了荒谬的结果。

此任务是否不适合 SVM,或者我在使用 libsvm 时是否有错误?下面是我用来测试的简单 Python 脚本:

#!/usr/bin/env python
# Simple classifier test.
# Adapted from the svm_test.py file included in the standard libsvm distribution.
from collections import defaultdict
from svm import *
# Define our sparse data formatted training and testing sets.
labels = [1,2,3,4]
train = [ # key: 0=mean, 1=stddev
    {0:2.5,1:3.5},
    {0:5,1:1.2},
    {0:7,1:3.3},
    {0:10.3,1:0.3},
]
problem = svm_problem(labels, train)
test = [
    ({0:3, 1:3.11},1),
    ({0:7.3,1:3.1},3),
    ({0:7,1:3.3},3),
    ({0:9.8,1:0.5},4),
]

# Test classifiers.
kernels = [LINEAR, POLY, RBF]
kname = ['linear','polynomial','rbf']
correct = defaultdict(int)
for kn,kt in zip(kname,kernels):
    print kt
    param = svm_parameter(kernel_type = kt, C=10, probability = 1)
    model = svm_model(problem, param)
    for test_sample,correct_label in test:
        pred_label, pred_probability = model.predict_probability(test_sample)
        correct[kn] += pred_label == correct_label

# Show results.
print '-'*80
print 'Accuracy:'
for kn,correct_count in correct.iteritems():
    print '\t',kn, '%.6f (%i of %i)' % (correct_count/float(len(test)), correct_count, len(test))

该域似乎相当简单。我希望如果它被训练知道 2.5 的平均值意味着标签 1,那么当它看到 2.4 的平均值时,它应该返回标签 1 作为最可能的分类。但是,每个内核的准确度为 0%。为什么是这样?

一些旁注,有没有办法隐藏终端中 libsvm 转储的所有详细训练输出?我搜索了 libsvm 的文档和代码,但找不到任何方法来关闭它。

此外,我曾想在我的稀疏数据集中使用简单的字符串作为键(例如 {'mean':2.5,'stddev':3.5})。不幸的是,libsvm 只支持整数。我尝试使用字符串的长整数表示(例如 'mean' == 1109110110971110),但 libsvm 似乎将这些截断为正常的 32 位整数。我看到的唯一解决方法是维护一个单独的“密钥”文件,将每个字符串映射到一个整数('mean'=0,'stddev'=1)。但显然这会很痛苦,因为我必须与序列化分类器一起维护和持久化第二个文件。有没有人看到更简单的方法?

4

2 回答 2

5

问题似乎来自将多类预测与概率估计相结合。

如果您将代码配置为不进行概率估计,它实际上可以工作,例如:

<snip>
# Test classifiers.
kernels = [LINEAR, POLY, RBF]
kname = ['linear','polynomial','rbf']
correct = defaultdict(int)
for kn,kt in zip(kname,kernels):
  print kt
  param = svm_parameter(kernel_type = kt, C=10) # Here -> rm probability = 1
  model = svm_model(problem, param)
  for test_sample,correct_label in test:
      # Here -> change predict_probability to just predict
      pred_label = model.predict(test_sample)
      correct[kn] += pred_label == correct_label
</snip>

通过此更改,我得到:

--------------------------------------------------------------------------------
Accuracy:
        polynomial 1.000000 (4 of 4)
        rbf 1.000000 (4 of 4)
        linear 1.000000 (4 of 4)

如果您将训练集中的数据加倍(即,将每个数据点包含两次),使用概率估计进行预测确实有效。但是,无论如何我都找不到对模型进行参数化的方法,以便具有概率的多类预测仅适用于原始的四个训练点。

于 2010-04-03T04:25:06.173 回答
3

如果您对执行此操作的不同方式感兴趣,可以执行以下操作。这种方式理论上更合理,但并不那么简单。

通过提及均值和标准差,似乎您指的是您假设以某种方式分布的数据。例如,您观察到的数据是高斯分布的。然后,您可以使用对称 Kullback-Leibler_divergence作为这些分布之间的距离度量。然后,您可以使用类似k 近邻的方法进行分类。

对于两个概率密度 p 和 q,只有当 p 和 q 相同时,才具有 KL(p, q) = 0。然而,KL 不是对称的 - 所以为了有一个适当的距离测量,你可以使用

距离(p1, p2) = KL(p1, p2) + KL(p1, p2)

对于高斯,KL(p1, p2) = { (μ1 - μ2)^2 + σ1^2 - σ2^2 } / (2.σ2^2) + ln(σ2/σ1)。(我从这里偷的,你也可以在这里找到偏差:)

长话短说:

给定一个 (mean, std, class) 元组的训练集 D 和一个新的 p = (mean, std) 对,找到 D 中距离 (d, p) 最小的 q 并返回该类。

对我来说,使用多个内核的 SVM 方法感觉更好,因为分类的方式不是那么随意。

于 2010-04-02T21:44:19.627 回答