Pergunta

Eu sou novo no SVMS e estou tentando usar a interface Python para libsvm para classificar uma amostra contendo uma média e stddev. No entanto, estou obtendo resultados sem sentido.

Esta tarefa é inadequada para SVMS ou há um erro no meu uso do LIBSVM? Abaixo está o script python simples que estou usando para testar:

#!/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))

O domínio parece bastante simples. Eu esperaria que, se for treinado para saber uma média de 2,5 significa o rótulo 1, então, quando vê uma média de 2,4, ele deve retornar o rótulo 1 como a classificação mais provável. No entanto, cada núcleo tem precisão de 0%. Por que é isso?

Algumas notas laterais, existe uma maneira de ocultar toda a saída de treinamento detalhado despejada pelo LIBSVM no terminal? Eu pesquisei os documentos e o código do LIBSVM, mas não consigo encontrar nenhuma maneira de desativar isso.

Além disso, eu queria usar strings simples como chaves no meu conjunto de dados esparsos (por exemplo, 'Mean': 2.5, 'Stddev': 3.5}). Infelizmente, o LIBSVM suporta apenas números inteiros. Tentei usar a representação inteira longa da string (por exemplo, 'média' == 1109110110971110), mas o libsvm parece truncá-los a números inteiros normais de 32 bits. A única solução alternativa que vejo é manter um arquivo "chave" separado que mapeia cada string para um número inteiro ('média' = 0, 'stddev' = 1). Mas, obviamente, isso será uma dor, pois terei que manter e persistir um segundo arquivo junto com o classificador serializado. Alguém vê uma maneira mais fácil?

Foi útil?

Solução

O problema parece estar proveniente da combinação de previsão multiclasse com estimativas de probabilidade.

Se você configurar seu código para não fazer estimativas de probabilidade, ele realmente funciona, por exemplo:

<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>

Com essa mudança, eu entendo:

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

Previsão com estimativas de probabilidade funciona, se você dobrar os dados no conjunto de treinamento (ou seja, inclua cada ponto de dados duas vezes). No entanto, não consegui encontrar de qualquer maneira para parametrizar o modelo para que a previsão multiclasse com probabilidades funcionasse apenas com os quatro pontos de treinamento originais.

Outras dicas

Se você estiver interessado em uma maneira diferente de fazer isso, poderá fazer o seguinte. Dessa forma, é teoricamente mais sólida, mas não tão direta.

Ao mencionar a média e a DST, parece que você se refere aos dados que você assume ser distribuído de alguma forma. Por exemplo, os dados que você observador são distribuídos gaussianos. Você pode então usar o Symmetrizado Kullback-Leibler_divergence como uma medida de distância entre essas distribuições. Você pode então usar algo como vizinho mais antigo classificar.

Para duas densidades de probabilidade p e q, você tem Kl (p, q) = 0 somente se p e q forem iguais. No entanto, KL não é simétrico - então, para ter uma medida de distância adequada, você pode usar

Distância (P1, P2) = KL (P1, P2) + KL (P1, P2)

Para gaussianos, KL (P1, P2) = {(μ1 - μ2)^2 + σ1^2 - σ2^2} / (2.σ2^2) + ln (σ2 / σ1). (Eu roubei isso de aqui, onde você também pode encontrar um desvio :)

Longa história curta:

Dado um conjunto de treinamento D de (médio, std, classe) tuplas e um novo par p = (média, std), encontre que q em D para qual distância (d, p) é mínima e retorne essa classe.

Para mim, isso parece melhor à medida que a abordagem do SVM com vários núcleos, pois a maneira de classificar não é tão arbitrária.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top