2010-04-02 71 views
6

我是SVM新手,我试图使用Python接口来libsvm来对包含mean和stddev的样本进行分类。但是,我收到了无意义的结果。使用LibSVM计算平均值/ 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)。但显然这将是一个痛苦,因为我将不得不维护和持续第二个文件以及序列化的分类器。有没有人看到更简单的方法?

+0

如果您删除概率估计值(即删除“概率= 1”,将prediction_probability更改为仅预测并删除pred_probability),则您的代码似乎可行。 – dmcer 2010-04-02 19:51:29

+0

@dmcer,辉煌。相反,只要每个标签至少有两个样本,就可以保留概率估计值。奇怪它不适用于每个标签的单个样本。如果您发表评论作为答案,那么我会将其标记为接受的答案。 – Cerin 2010-04-03 00:41:34

回答

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) 

预测与概率估计不工作,如果你加倍的训练数据集(即包括每个数据点的两倍) 。但是,我无法找到模型的参数,因此多概率预测可能仅适用于原始的四个训练点。

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 =(意思是说, std)对,在D中找到那个距离(d,p)最小的q并返回该类。

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

+0

谢谢。我认为可能比支持正态/高斯分布的SVM更好。不过,我还打算将这些高斯特征与其他任意特征一起使用,因此使用专门的距离度量的k-nn将不合适。 – Cerin 2010-04-03 00:26:29

+0

实际上有些方法可以从班级标签中学习这种距离度量。也许你想结账Sam Roweis的邻里组件分析工作。 – bayer 2010-04-06 12:44:31