2010-04-02 47 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