Я плохо знаком с SVMs, и я пытаюсь использовать интерфейс Python для libsvm для классификации образца, содержащего среднее и stddev. Однако я получаю бессмысленные результаты.
Действительно ли эта задача является несоответствующей для SVMs или является там ошибкой в моем использовании 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, но я не могу найти способ выключить это.
Кроме того, я хотел использовать простые строки в качестве ключей в моем редком наборе данных (например, {'означают ':2.5, 'stddev ':3.5}). К сожалению, libsvm только поддерживает целые числа. Я пытался использовать представление длинного целого строки (например, 'средний' == 1109110110971110), но libsvm, кажется, усекает их к нормальным 32-разрядным целым числам. Единственное обходное решение, которое я вижу, должно поддержать отдельный "ключевой" файл, который отображается, каждая строка к целому числу ('имейте в виду' =0, 'stddev' =1). Но очевидно это будет болью, так как я должен буду поддержать и сохранить второй файл наряду с сериализированным классификатором. Кто-либо видит более легкий путь?
Проблема, похоже, возникает из-за комбинирования многоклассового предсказания с оценками вероятности.
Если вы сконфигурируете свой код так, чтобы он не делал оценок вероятности, он действительно работает , например:
<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)
Прогноз с оценками вероятности действительно работает, если вы удвоите данные в обучающий набор (т.е. включать каждую точку данных дважды). Однако я не смог найти способа параметризовать модель, чтобы мультиклассовое предсказание с вероятностями работало только с исходными четырьмя точками обучения.
Если вас интересует другой способ сделать это, вы можете сделать следующее. Этот способ теоретически более надежен, но не так прост.
Упоминая среднее и стандартное, создается впечатление, что вы ссылаетесь на данные, которые, по вашему мнению, каким-то образом распределяются. Например, данные, которые вы наблюдаете, распределены по Гауссу. Затем вы можете использовать симметричное расхождение Кульбака-Лейблера в качестве меры расстояния между этими распределениями. Затем вы можете использовать что-то вроде k-ближайший сосед для классификации.
Для двух плотностей вероятностей p и q KL (p, q) = 0, только если p и q совпадают. Однако KL не является симметричным - поэтому, чтобы иметь правильную меру расстояния, вы можете использовать
distance (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 (среднее, стандартное, класс) кортежей и новый p = (mean, std), найдите q в D, для которого расстояние (d, p) минимально, и верните этот класс.
Мне кажется, что подход SVM с несколькими ядрами лучше, поскольку способ классификации не такой уж и произвольный.