Cálculo de la coincidencia más cercana a la media / pareja de Stddev con LibSVM

Soy nuevo en SVMs, y estoy tratando de usar la interfaz de Python para libsvm para clasificar una muestra que contiene una media y stddev. Sin embargo, estoy obteniendo resultados sin sentido.

¿Es esta tarea inadecuada para los SVM o hay un error en mi uso de libsvm? A continuación se muestra el sencillo script de Python que estoy usando para probar:

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

El dominio parece bastante simple. Yo esperaría que si está entrenado para saber una media de 2.5 significa la etiqueta 1, entonces cuando vea una media de 2.4, debería devolver la etiqueta 1 como la clasificación más probable. Sin embargo, cada kernel tiene una precisión del 0%. ¿Por qué es esto?

Un par de notas laterales, ¿hay alguna manera de ocultar toda la salida de entrenamiento detallada que libsvm libera en el terminal? He buscado en la documentación y el código de libsvm, pero no puedo encontrar ninguna manera de desactivar esto.

Además, quería usar cadenas simples como las claves en mi conjunto de datos dispersos (por ejemplo, {‘mean’: 2.5, ‘stddev’: 3.5}). Desafortunadamente, libsvm solo soporta enteros. Intenté usar la representación de enteros largos de la cadena (por ejemplo, ‘mean’ == 1109110110971110), pero libsvm parece truncarlos con enteros normales de 32 bits. La única solución que veo es mantener un archivo de “clave” separado que asigna cada cadena a un entero (‘mean’ = 0, ‘stddev’ = 1). Pero obviamente eso será una molestia ya que tendré que mantener y persistir un segundo archivo junto con el clasificador serializado. ¿Alguien ve una manera más fácil?

El problema parece venir de la combinación de predicción multiclase con estimaciones de probabilidad.

Si configura su código para no hacer estimaciones de probabilidad, realmente funciona , por ejemplo:

  # 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  

Con este cambio, me sale:

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

La predicción con estimaciones de probabilidad funciona si se duplican los datos en el conjunto de entrenamiento (es decir, se incluye cada punto de datos dos veces). Sin embargo, no pude encontrar de todos modos parametrizar el modelo de modo que la predicción multiclase con probabilidades funcionaría solo con los cuatro puntos de entrenamiento originales.

Si está interesado en una forma diferente de hacer esto, puede hacer lo siguiente. Esta forma es teóricamente más sólida, pero no tan sencilla.

Al mencionar mean y std, parece como si se refiriera a datos que se supone que están distribuidos de alguna manera. Por ejemplo, los datos que usted observa son gaussianos distribuidos. A continuación, puede utilizar la separación de Kullback-Leibler simetrizada como una medida de distancia entre esas distribuciones. Luego puedes usar algo como el vecino k más cercano para clasificar.

Para dos densidades de probabilidad p y q, tiene KL (p, q) = 0 solo si p y q son iguales. Sin embargo, KL no es simétrico, por lo que para tener una medida de distancia adecuada, puede usar

distancia (p1, p2) = KL (p1, p2) + KL (p1, p2)

Para los gaussianos, KL (p1, p2) = {(μ1 – μ2) ^ 2 + σ1 ^ 2 – σ2 ^ 2} / (2.σ2 ^ 2) + ln (σ2 / σ1). (Lo robé desde aquí , donde también puedes encontrar una desviación 🙂

Larga historia corta:

Dado un conjunto de entrenamiento D de tuplas (media, estándar, clase) y un nuevo par p = (media, estándar), encuentre q en D para la cual la distancia (d, p) es mínima y devuelva esa clase.

Para mí eso se siente mejor cuando el SVM se acerca con varios núcleos, ya que la forma de clasificar no es tan arbitraria.