Вычисление ближайшего совпадения с парой среднее / стандартное с LibSVM - PullRequest
6 голосов
/ 02 апреля 2010

Я новичок в SVM, и я пытаюсь использовать интерфейс Python для 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). Но очевидно, что это будет проблемой, так как мне придется поддерживать и сохранять второй файл вместе с сериализованным классификатором. Кто-нибудь видит более легкий путь?

Ответы [ 2 ]

5 голосов
/ 03 апреля 2010

Проблема, похоже, исходит от сочетания мультиклассового прогнозирования с оценками вероятности.

Если вы сконфигурируете свой код, чтобы не делать вероятностные оценки, он действительно работает , например ::10000*

<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 голосов
/ 03 апреля 2010

Если вас интересует другой способ сделать это, вы можете сделать следующее. Этот способ теоретически более обоснован, но не так прост.

При упоминании средних и стандартных значений создается впечатление, что вы ссылаетесь на данные, которые, как вы предполагаете, каким-либо образом распространяются. Например, данные, которые вы наблюдаете, распределены по Гауссу. Затем вы можете использовать Symmetrised Kullback-Leibler_divergence в качестве меры расстояния между этими распределениями. Затем вы можете использовать что-то вроде k-ближайший сосед для классификации.

Для двух плотностей вероятности p и q KL (p, q) = 0, только если p и q одинаковы. Тем не менее, 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). (Я украл это у здесь , где вы также можете найти отклонение:)

Короче говоря:

Учитывая обучающий набор D из набора (mean, std, class) и новую пару p = (mean, std), найдите q в D, для которого расстояние (d, p) минимально, и верните этот класс.

Мне кажется, что это лучше, чем подход SVM с несколькими ядрами, поскольку способ классификации не так уж и произвольн.

...