使用LibSVM计算与Mean / Stddev对的最近匹配

时间:2010-04-02 15:29:25

标签: python artificial-intelligence machine-learning svm libsvm

我是SVM的新手,我正在尝试使用Python接口libsvm来对包含mean和stddev的示例进行分类。但是,我得到了荒谬的结果。

此任务不适合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)。但显然这将是一个痛苦,因为我将不得不维护和持久化第二个文件以及序列化分类器。有没有人看到更简单的方法?

2 个答案:

答案 0 :(得分: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)

如果您将训练集中的数据加倍(即,将每个数据点包括两次),则使用概率估计进行预测会起作用。但是,我无法找到参数化模型,因此具有概率的多类预测只适用于原始的四个训练点。

答案 1 :(得分:3)

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

通过提及mean和std,好像你指的是你假定以某种方式分发的数据。例如,您观察到的数据是高斯分布的。然后,您可以使用Symmetrised Kullback-Leibler_divergence作为这些分布之间的距离度量。然后,您可以使用类似k-nearest neighbour的内容进行分类。

对于两个概率密度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)。 (我从here偷了那个,你也可以在那里找到一个偏差:)

长话短说:

给定训练集D(均值,标准,类)元组和新的p =(mean,std)对,在D中找到q,其中距离(d,p)是最小的并返回该类。 / p>

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