Suppor Vector Machine ( Máquina de Soporte de Vectores) en Python

Sobre SVM

Las técnicas de Máquina de Soporte de Vectores (SVM) por alguna extraña razón es uno de los algoritmos que más interés despiertan y sobre todo el que creo que genera más sobre estimación. Es posible que no he usado suficiente la familia de algoritmos como para reconocer tanto su valor.

Hace no más de un año un amigo en una propuesta sugería usar esta familia de algoritmos sin revisar si realmente convenía hacer uso de ellos para el tipo de datos que se analizarían. La justificación de este amigo era que SVM eran los “mejores” algoritmos, lo cual es falso, no se puede calificar a una familia de algoritmos como los mejores, se requiere probar varios contra los datos para determinar cuales funcionan mejor con ellos.

Dos ejemplos que he compartido donde involucro SVM se pueden revisar en las entradas:

1.-Comparacion-entre-svm-naive-bayes-arboles-de-decision-y-metodos-lineales.

2.-Maquina-de-soporte-vectorial-svm-sopport-vector-machine

En las dos entradas uso SVM para clasificar, si bien sus resultados son buenos en las muestras de datos no resulta ser el mejor método al compararlo con otra familia de algoritmos. Lo que recomiendo es probar con más de una familia de algoritmos para analizar alguno de los problemas y elegir más de un estadístico o indicador que puedan servir para comparar la eficiencia de los algoritmos.

Si se tiene interés en conocer más detalles sobre los algoritmos recomiendo consultar la referencia [1,2,3,4], en orden con respecto a la dificultad del contenido y de las explicaciones. Los primeros 3 ejemplos se pueden encontrar en la documentación de la librería scikit-learn.

La relación entre las técnicas o algoritmos es la siguiente; el primero es Margen Maximal de Clasificación (MMC), su generalización es Soporte de Vectores para Clasificación (SVC) y esta a su vez se generaliza a SVM.

El primer ejemplo con datos simulados, son la imagen clásica para explicar como funciona SVM, pero lo que se muestra en sí es el caso MMC.

El código es el siguiente:

#MMC 
print(__doc__)

#Librerías requeridas

import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm

# Se crean los datos
np.random.seed(0)
X = np.r_[np.random.randn(70, 2) - [2, 2], np.random.randn(70, 2) + [2, 2]]
Y = [0] * 70 + [1] * 70

#Se estima el modelo
clf = svm.SVC(kernel='linear')
clf.fit(X, Y)

# Se construye la recta que separa las clases
w = clf.coef_[0]
a = -w[0] / w[1]
xx = np.linspace(-5, 5)
yy = a * xx - (clf.intercept_[0]) / w[1]


# support vectors
b = clf.support_vectors_[0]
yy_down = a * xx + (b[1] - a * b[0])
b = clf.support_vectors_[-1]
yy_up = a * xx + (b[1] - a * b[0])

#Gráfica
plt.plot(xx, yy, 'k-')
plt.plot(xx, yy_down, 'k--')
plt.plot(xx, yy_up, 'k--')

plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1],
 s=80, facecolors='none')
plt.scatter(X[:, 0], X[:, 1], c=Y, cmap=plt.cm.Paired)

plt.title('Recta separadora')
plt.axis('tight')
plt.show()

La gráfica es la siguiente:

MMC

A lo que se refiere el algoritmo con el nombre de Margen Maximal, es al margen que se obtiene entre las dos clases de datos, siendo el margen la distancia entre las líneas punteadas.

Esto en teoría es una optimización sobre el margen y la generalización se da cuando no se puede obtener una línea recta que no intercepte o que separe de manera clara las muestras de datos. El ejemplo clásico se hace con los datos Iris, los cuales permiten mostrar como se generaliza el uso de los margenes que separan los datos, cuando no son líneas rectas.

El código muestra el ejemplo de como se separan de manera no lineal las fronteras.

#Código

print(__doc__)

#Módulos
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm, datasets

#Se importan los datos
iris = datasets.load_iris()
X = iris.data[:, :2] 
y = iris.target

h = .02

#Parámetro de regularización
C = 1.0 

svc = svm.SVC(kernel='linear', C=C).fit(X, y)
rbf_svc = svm.SVC(kernel='rbf', gamma=0.7, C=C).fit(X, y)
poly_svc = svm.SVC(kernel='poly', degree=3, C=C).fit(X, y)
lin_svc = svm.LinearSVC(C=C).fit(X, y)

#Probé pero no me gustó el resultado con sigmoid
#sigmoid_svc=svm.SVC(kernel='sigmoid').fit(X,y)

#Se crean los marcos para las gráficas

x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),np.arange(y_min, y_max, h))

#Títulos para las gráficas
titles = ['SVC con kernel lineal',
 'Lineal SVC',
 'SVC con RBF kernel',
 'SVC con polinomio(grado 3) kernel']


for i, clf in enumerate((svc,lin_svc, rbf_svc, poly_svc)):
 # Se grafican las fronteras 
 plt.subplot(2, 2, i + 1)
 plt.subplots_adjust(wspace=0.4, hspace=0.4)

 Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])

 #Color en las gráficas
 Z = Z.reshape(xx.shape)
 plt.contourf(xx, yy, Z, cmap=plt.cm.Paired, alpha=0.8)

 #Puntos de entrenamiento
 plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired)
 plt.xlabel('Longitud Sepal')
 plt.ylabel('Peso Sepal')
 plt.xlim(xx.min(), xx.max())
 plt.ylim(yy.min(), yy.max())
 plt.xticks(())
 plt.yticks(())
 plt.title(titles[i])

plt.show()

La gráfica que se obtiene es la siguiente:

SVC_Iris

El otro uso es para estimar la regresión y un ejemplo visual que se aprecia rápido es en la regresión de datos en dos dimensiones, es decir; en el plano.

El código es el siguiente:

#Generación de datos

print(__doc__)
import numpy as np
from sklearn.svm import SVR
import matplotlib.pyplot as plt

#Generación de datos
X = np.sort(5 * np.random.rand(100, 1), axis=0)
y = np.sin(X).ravel()
y[::5] += 3 * (0.5 - np.random.rand(20))

#Modelos regresión fit

svr_rbf = SVR(kernel='rbf', C=1e3, gamma=0.1)
svr_lin = SVR(kernel='linear', C=1e3)
svr_poly = SVR(kernel='poly', C=1e3, degree=2)
y_rbf = svr_rbf.fit(X, y).predict(X)
y_lin = svr_lin.fit(X, y).predict(X)
y_poly = svr_poly.fit(X, y).predict(X)

#Mirando los resultados

plt.scatter(X, y, c='k', label='datos')
plt.hold('on')
plt.plot(X, y_rbf, c='g', label='Modelo RBF ')
plt.plot(X, y_lin, c='r', label='Modelo Lineal')
plt.plot(X, y_poly, c='b', label='Modelo Polinomial')
plt.xlabel('datos')
plt.ylabel('target')
plt.title('M. De Soport de Vectores')
plt.legend()
plt.show()

La gráfica que se muestra es la siguiente:

SVM_Regresión

En las primeras ocasiones que uno usa esta familia de algoritmo la confusión se genera con el concepto de “kernel”,  de modo no formal y burdo la idea de lo que hace el kernel es modificar el modo en el cual se logran definir los planos separadores o las fronteras entre clases de datos. Esto en palabras puede no parecer claro, pero el ejemplo natural para ejemplificar esto es generar una muestra de datos altamente no lineales y aplicar SVM con diferentes kernel lo cual mostrará como se comporta el algoritmo al cambiar el tipo de kernel. En el ejemplo hago una comparación con el kernel rbf, lineal, polinomial de grado 4 y 5, y el kernel sigmoid.

El código es el mismo para las cuatro estimaciones solo se cambiar una línea de código la cual para obtener las gráficas se comentan las no requeridas. El código es prácticamente una ligera modificación al ejemplo que se puede encontrar en scikit-learn.

El código para realizar este ejemplo es el siguiente:

#Código
#Module
print(__doc__)

import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
import time

start_time=time.time()
xx, yy = np.meshgrid(np.linspace(-3, 3, 1000),
 np.linspace(-3, 3, 1000))
np.random.seed(0)
X = np.random.randn(1000, 2)
Y = np.logical_xor(X[:, 0] > 0, X[:, 1] > 0)


# fit the model

#clf =SVC(kernel='poly',degree=5)
clf =SVC(kernel='poly',degree=4)
#clf =SVC(kernel='linear')
#clf=SVC()
clf.fit(X, Y)

# plot the decision function for each datapoint on the grid
Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

plt.imshow(Z, interpolation='nearest',
 extent=(xx.min(), xx.max(), yy.min(), yy.max()), aspect='auto',
 origin='lower', cmap=plt.cm.PuOr_r)
contours = plt.contour(xx, yy, Z, levels=[0], linewidths=2,
 linetypes='--')
plt.scatter(X[:, 0], X[:, 1], s=30, c=Y, cmap=plt.cm.Paired)
plt.xticks(())
plt.yticks(())
plt.axis([-3, 3, -3, 3])
plt.title('Kernel Polinomio grado 4')
plt.show()

print(" The time required is %f seconds "%(time.time()-start_time))

Las gráficas que se obtienen son las siguientes:

SVM_Kernel_Lineal SVM_Kernel_Polinomial_degree4
SVM_Kernel_RBF

SVM_Kernel_Polinomial_degree5

Lo que se observa en las 4 gráficas es que el modelo con kernel rbf y un kernel polinomial de grado 4, parecen ser los dos mejores modelos para clasificar los datos en dos clases.Se observa que el modelo lineal no es para nada apropiado y que le cuesta trabajo al SVM con ese kernel poder separar los datos. El único modo que considero o creo que sirve para detectar si los datos son altamente lineales o altamente no lineales es hacer una exploración en los datos.

Un par de ejemplos.

Tomando datos de la plataforma http://datos.gob.mx/,  hago un ejemplo burdo,  ya que los datos que tomo son sobre los delitos por estado o Entidad Federativa en distintas clasificaciones de delitos, estos datos cubren desde el mes de Enero del 2001 hasta diciembre del 2006.  Lo que hago es medir la media de todos los delitos por cada uno de los registros de los datos, después teniendo la media defino una escala simple para asignar una variable indicadora a los estados que tienen mayor y menos tasa delictiva.

Con lo anterior defino un dos clases, debido a que no uso los datos para hacer un  análisis cuidadoso sino solo para comparar el uso del algoritmo, entonces omito la relevancia de tener las fechas de la medición por mes y año, tomo una muestra aleatoria de aproximadamente el 70% para entrenar el algoritmo y el resto para probar la eficiencia.

En resumen las etapas son las siguientes:

  1. Procesar y limpiar los datos.
  2. Asignar la variable de clasificación o indicadora.
  3. Entrenar el algoritmo y comparar con el conjunto de prueba.
  4. Comparar contra otro algoritmo, Naive Bayes.

 Primero recomiendo descargar los datos, ya que se cuenta con ellos mediar la media por cada fila y teniendo esa tabla la cargamos a python.

#Procesamiento
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import os
from pandas import Series

#Cargamos los datos
os.chdir('Directorio donde se pusieron los datos')

Datos=pd.read_csv('Archivos_con_los_datos.csv',sep=',',decimal=',')

Datos.head()
Datos.shape

#Exploración

Datos['Media'].hist()
plt.xlabel('Valores de la Media de la Delincuencia por Estado')
plt.ylabel('Valor de la Media')
plt.title('Histograma')
plt.show()

#Limpiamos los datos por los Missing values

sum(Datos.isnull().values.ravel())
#31

#Cleaning of data 
Datos1=Datos.dropna()
Datos1.shape 
#(2273,27)

S=Datos1['Media']
#Exploración 2
S2=[log(y) for y in S]

plt.hist(S2,col='green')
plt.xlabel('Valores de la Media de la Delincuencia por Estado')
plt.ylabel('Valor de la Media')
plt.title('Histograma')
plt.show()

#Missing Values
sum(Datos.isnull().values.ravel())
#31

#Cleaning of data 
Datos1=Datos.dropna()
Datos1.shape 
#(2273,27)

Series(S).mean()
#9.32


Hist_Delic_Mex

Histograma de las Medias de la Delincuencia por Estado

Hist_Del_Mex_Log

Histograma de los Logaritmos de las Medias de la Delincuencia por Estado

Después de explorar de modo breve como se comportan los datos ahora medimos a media creamos una nueva variable y le asignamos un valor dependiendo de la medición de la media. Agrego también los valores del logaritmo de la media ya que esta variable muestra que el comportamiento de las medias tienen una distribución de colas pesadas.

#Nueva variable
Datos1['Log_Media']=S2

Datos1.head()
Datos1.var()
 
#total data
 
len(Datos1[Datos1['Media']>9.3169]['Log_Media'])
#569
Datos1['Indicador']=0
Datos1.ix[Datos1.Media>9.3169,'Indicador']=1

Datos1.shape
#(2273,29)

Ahora preparo los datos para entrenar el algoritmo y aplico SVM, tanto con un kernel lineal como con uno no lineal.

# Preparación de datos y SVM
from sklearn.svm import SVC
from sklearn import svm
from sklearn.naive_bayes import GaussianNB
import random 

rows=random.sample(Datos1.index,1536)

Datos_train=Datos1.ix[rows]
Datos_test=Datos1.drop(rows)

type(Datos_train)
X=np.asarray(Datos_train.icol(range(4,26)))
Y=np.asarray(Datos_train.icol(28))
X_test=np.asarray(Datos_test.icol(range(4,26)))
Y_test=np.asarray(Datos_test.icol(28))
X_test

#Aplico los algoritmos de SVM

#Kernel no lineal
clf=SVC()
clf.fit(X,Y)
len(Y_test)
#737
P=clf.predict(X_test)
sum(P==Y_test)/737.0
#0.778

#Kernel Lineal
lin_clf=svm.LinearSVC()
lin_clf.fit(X,Y)
Pred=lin_clf.predict(X_test)
sum(Pred==Y_test)/737.0
#0.9701

#Naive Bayes
gnb = GaussianNB()
Prediction=gnb.fit(X,Y).predict(X_test)
(Prediction!=Y_test).sum()
(Prediction==Y_test).sum()
683.0/737.0
#0.926

Se observa que el porcentaje de eficiencia de los algoritmos es el siguiente, para SVM con un kernel no lineal es del 77.8%, para SVM con un kernel lineal es del 97.01% y para el algoritmo Naive Bayes es de 92.6%.

Entonces en resumen, para esta muestra de datos se obtienen que SVM con un kernel lineal es el más eficiente como método de clasificación.

La moraleja de siempre es usar más de un algoritmo para una muestra de datos, ya que ninguno es el “mejor” solamente es más adecuada para cada cierto tipo de datos.

Referencias:

1.- An Introduction to Statistical Learning

2.-The Elements of Statistical Learning

3.-Machine Learning: A Probabilistic Perspective

4.-Pattern Recognition  and Machine Learning

Anuncios

Comparación entre Máquina de Soporte Vectorial, Naive Bayes, Árboles de Decisión y Métodos Lineales

De lo que trata esta entrada.

En la entrada no explico a detalles técnicas sobre cada una de los algoritmos empleados, pero pueden consultarse en las referencias o en las categorías Machine Learning en R project y Machine Learning en Python.

Para el ejemplo solo uso código en R, la intención es usar tres muestras de datos simulados para mostrar las estimaciones que realiza la función svm de la librería e1071, solo en el caso de usarla para clasificar. Y por último uso unos datos  de correos clasificados como spam o no-spam provenientes del repositorio UCI Machine Learning Repository los cuales se encuentran cargados en la librería kernlab de  R project para aplicar las técnicas y comparar svm con otras técnicas.

Ejemplo.-Solo Suppor Vector Machine

En este ejemplo solo hago dos muestras de datos simulados, con distribuciones  Gaussianas y la intención es compartir los detalles de qué es en rasgos generales lo que hace el algoritmo de maquina de soporte vectorial al clasificar datos, esto es para dos dimensiones. Es decir, sobre un plano ya que visualmente se vuelve claro y con esa idea se puede pensar en el tipo de cosas que hace el algoritmo en más dimensiones.

Los primeros datos no se mezclan, por lo cual es visualmente claro como separarlos.

#Datos
#Librerías requeridas para el ejemplo

library(ggplot2)
library(e1071)

#Datos de mezcla 1

N<-500
x1<- rnorm(N) * 0.1
y1<- rnorm(N) * 0.1
X1<- t(as.matrix(rbind(x1, y1)))
x2<- rnorm(N) * 0.1 + 0.5
y2<- rnorm(N) * 0.1 + 0.5
X2<- t(as.matrix(rbind(x2, y2)))
X<- as.matrix(rbind(X1, X2))
X=data.frame(X)
X['Clus']=1
X[500:1000,3]=2
ggplot(X,aes(x=x1,y=y1))+geom_point(size=3,aes(colour=factor(Clus)))+ggtitle('Mezcla 1')+theme(plot.title=element_text(lineheight = 2,face='bold'))+
 xlab('Variable X1')+ylab('Variable Y1')

La gráfica de los puntos se ve así:Mezcla1_Gas

Para la siguiente mezcla considero tres muestras donde las clasifico en dos categorías.

#Mezcla 2
N<-500
x1<- rnorm(500,mean=0,sd=3)*0.1 
y1<- rnorm(500,mean=0,sd=3)*0.1 
X1<- t(as.matrix(rbind(x1, y1)))
x3<- rnorm(250,mean=0,sd=3)*0.1 
y3<- rnorm(250,mean=0,sd=3)*0.1+0.7 
X3<- t(as.matrix(rbind(x3, y3)))
x2<- rnorm(N,mean=1.5,sd=4)*0.1 + 0.5
y2<- rnorm(N,mean=0,sd=3)*0.1 + 0.5
X2<- t(as.matrix(rbind(x2, y2)))
Datos2<- as.matrix(rbind(X1, X3,X2))
Datos2=data.frame(Datos2)
Datos2['Clus']=1
Datos2[600:1250,3]=2
ggplot(Datos2,aes(x=x1,y=y1,colour=factor(Clus)))+geom_point(size=3)+ggtitle('Mezcla 2')+theme(plot.title=element_text(lineheight = 2,face='bold'))+
 xlab('Variable X1')+ylab('Variable Y1')

Mez2_GaussLa gráfica de los datos se ve como la anterior imagen.

Los datos muestran comportamientos distintos, los primeros es claro que se puede separar por una recta, pero la gráfica de la segunda muestra no se ve claramente que lo mejor sea separar los datos por una recta.

Como una buena práctica se debe de tomar una muestra de datos para entrenar el algoritmo (train set) y una cantidad de datos de prueba(test set). Para los siguientes ejemplos considero todos los datos, para ilustrar cual sería la gráfica de los datos predichos por el modelo.

La técnica de SVM para clasificar tienen de fondo la idea de encontrar el mejor “hiperplano” por medio del cual separar los datos. En este ejemplo el concepto de hiperplano es una recta, es decir; los datos están en dos dimensiones (x,y) y se busca la mejor recta que separe los datos, si uno piensa en datos con tres dimensiones (x,y,z) que pueden pensarse como alto, largo y ancho lo que se busca es encontrar el mejor plano que separa los datos, por ello el nombre de hiperplano.

Uso la librería e1071 para estimar el algoritmo SVM con el kernel lineal y radial para la primera muestra de datos, para la segunda uso lineal, polinomial, radial y sigmoid. La intención de este ultimo es compara cual de las cuatro muestra una similitud gráfica más parecida a los datos originales y comparo la tasa de predicciones correctas. Existen más librerías para hacer uso de SVM, dejo en la referencia las ligas.

Primera muestra de datos.

#SVM con Kernel Lineal
#Se procede a dejar una semilla para hacer una elección del mejor valor de parámetro cost

set.seed (1)
#Se hace cross-validation de k-fold, con k=10. Esto mediante la función tune()

tune.out=tune(svm,factor(Clus)~.,data=Datos1,kernel="linear",type='C-classification',scale=FALSE,ranges=list(cost=c(0.001,0.01, 0.1, 1,5,10,100)))
tune.out
summary(tune.out)

#Se elige el mejor modelo con el mejor valor para el parámetro cost

bestmod=tune.out$best.model
summary(bestmod)
# Gráfica del mejor modelo con kernel lineal

plot(bestmod,Datos1)

#Predicción
Pred=predict(bestmod,Datos1)

table(predicción=Pred,Valores_reales=Datos1$Clus)

# Valores_reales
# predicción 1 2
#         1 499 1
#         2 0 500
#Con los valores obtenidos se tiene un 99.9% de eficiencia 

Algunas observaciones, el modelo pasa por un proceso de validación cruzada usando la función tune(), esto siempre es recomendable para elegir un buen modelo. El parámetro cost, significa el nivel de penalización que permite el modelo, en otras palabras el algoritmo busca la mejor recta que separe los datos y como tal el costo para encontrar esa recta requiere tolerar posibles datos que afectan a la estimación de dicha recta. Esto quizás es una explicación burda y mala, pero detrás de todo algoritmo de Machine Learning está un proceso de optimización el cual determina el valor de los parámetros que hacen que el algoritmo tenga el mejor valor posible.

La gráfica que se obtiene de este modelo es la siguiente:

SVM_Muestra1_ker-lineal

 

Esta gráfica muestra las dos clases que se buscaban definir.La eficiencia es muy alta, de 1000 datos clasifica correctamente el 99.9%.

Lo que se espera de otro kernel es que prevalezca la eficiencia de SVM y más aún que la gráfica de la clasificación sea casi igual a la obtenido cuando se usa un kernel lineal.

#Kernel radial
set.seed (1)
tune.out=tune(svm,factor(Clus)~.,data=Datos1,kernel="radial",type='C-classification',scale=FALSE,ranges=list(cost=c(0.001,0.01, 0.1, 1,5,10,100)))
tune.out
summary(tune.out)
bestmod_r=tune.out$best.model
summary(bestmod_r)
plot(bestmod_r,Datos1)
Pred=predict(bestmod_r,Datos1)
table(predicción=Pred,Valores_reales=Datos1$Clus)
# Valores_reales
# predicción 1 2
# 1 499 1
# 2 0 500
#Eficiencia de la clasificación 99.9%

Al obtener la gráfica del modelo por SVM se tiene:

SVM_Muestra1_ker-radial

Lo cual muestra una imagen muy similar a la obtenida con el kernel lineal. La tasa de eficiencia al clasificar es prácticamente la misma, 99.9%

Con la segunda muestra de datos pensar en separar por una recta no parece lo natural. Primero hago la estimación con cuatro tipo de kernels de la función svm y muestro la gráfica que regresa el modelo.

#Segunda muestra de datos
#Se usa primero el kernel lineal

set.seed (1)
tune.out=tune(svm,factor(Clus)~.,data=Datos2,kernel="linear",type='C-classification',scale=FALSE,ranges=list(cost=c(0.001,0.01, 0.1, 1,5,10,100)))
tune.out
summary(tune.out)
bestmod_l=tune.out$best.model
summary(bestmod_l)
plot(bestmod_l,Datos2)
Pred=predict(bestmod_l,Datos2)
table(predicción=Pred,Valores_reales=Datos2$Clus)
# Valores_reales
# predicción 1 2
# 1 504 99
# 2 95 552
#Eficiencia de la clasificación 84.4%

Se tiene la siguiente gráfica como resultado de implementar el algoritmo:

SVM_Mezcla2_ker-lineal

Si se compara los datos que predice el modelo con respecto a los originales se tienen lo siguiente:

Orig_vs_Ker-lineal

Se observa que la predicción muestra totalmente separadas las dos clases y cabe notar que se tiene el 84.4% eficiencia.

#Kernel polinomial

set.seed (1)
tune.out=tune(svm,factor(Clus)~.,data=Datos2,kernel="polynomial",type='C-classification',scale=FALSE,ranges=list(cost=c(0.001,0.01, 0.1, 1,5,10,100)))
tune.out
summary(tune.out)
bestmod_p=tune.out$best.model
summary(bestmod_p)
plot(bestmod_p,Datos2)
Pred=predict(bestmod_p,Datos2)
table(predicción=Pred,Valores_reales=Datos2$Clus)
# Valores_reales
# predicción 1 2
# 0 543 174
# 1 56 477
#Eficiencia de la clasificación 81.6%, con kernel polinomial

La gráfica que se obtiene del modelo es:

SVM_Mezcla2_ker-polin

Y otra vez haciendo una gráfica para comparar la predicción con los datos originales se tiene:

Orig_vs_Ker-pol

Se aprecia que no es tan definida la recta que separa las clases como en el ejemplo del kernel lineal, pero la eficiencia se reduce ya que es de 81.6%

#Kernel sigmoid

set.seed (1)
tune.out=tune(svm,factor(Clus)~.,data=Datos2,kernel="sigmoid",type='C-classification',scale=FALSE,ranges=list(cost=c(0.001,0.01, 0.1, 1,5,10,100)))
tune.out
summary(tune.out)
bestmod_s=tune.out$best.model
summary(bestmod_s)
#Informa del tipo de modelo, del que puede considerarse como el mejor modelo
plot(bestmod_s,Datos2)
Pred=predict(bestmod_s,Datos2)
table(predicción=Pred,Valores_reales=Datos2$Clus)
# Valores_reales
# predicción 1 2
#         1 509 102
#         2 90 549
#Eficiencia de la clasificación 84.6%, con kernel Sigmoid

La gráfica que se obtiene del modelo es la siguiente:

SVM_Mezcla2_ker-sigmoid

Haciendo la gráfica comparativa de predicción y datos originales se obtiene:Orig_vs_Ker-sigmoid

 

Se aprecia que es muy similar a la que se obtiene con el kernel lineal y hasta el nivel de eficiencia resulta muy aproximado, ya que es del 84.6%

#Kernel Radial
set.seed (1)
tune.out=tune(svm,factor(Clus)~.,data=Datos2,kernel="radial",type='C-classification',scale=FALSE,ranges=list(cost=c(0.001,0.01, 0.1, 1,5,10,100)))
tune.out
summary(tune.out)
bestmod_r=tune.out$best.model
summary(bestmod_r)
plot(bestmod_r,Datos2)
Pred=predict(bestmod_r,Datos2)
table(predicción=Pred,Valores_reales=Datos2$Clus)
# Valores_reales
# predicción 1 2
# 1 497 94
# 2 102 557
#Eficiencia de la clasificación 84.3%, con kernel lineal

La gráfica obtenida con este kernel es la siguiente:

SVM_Mezcla2_ker-radial

 

Y al comparar las predicciones con los datos originales se tiene:

Orig_vs_Ker-radial

Se aprecia que es similar a la obtenida por el kernel lineal y sigmoid, más aún la eficiencia resulta ser del 84.3%.

Entonces en resumen los datos al ser clasificados mediante SVM con distintos kernel resultó en estos datos resulta tener mejor eficiencia el kernel sigmoid, por un porcentaje mínimo sobre el lineal.

Para mostrar como se comporta SVM con una muestra de datos altamente no lineales o que resulta difícil separar por medio de una recta , genero una muestra más.

#Datos altamente no lineales

#Distribución uniforme 1250 valores
x1=runif(1250)-0.5
x2=runif (1250)-0.5
#Variable Indicadora
y=1*(x1^2-x2^2> 0)

#Gráfica donde se muestran los puntos pintados por etiqueta de y
#Se contruye un data.frame con los datos
X=data.frame(x1,x2,y)
ggplot(data=X,aes(x=x1,y=x2))+geom_point(aes(colour=factor(y),shape=factor(y)))+ggtitle('Datos Originales')+theme(plot.title=element_text(lineheight = 2,face='bold'))+
 xlab('Variable X1')+ylab('Variable X2')

La gráfica de los datos es la siguiente:

Datos_No-lineales

En esta muestra de datos se aprecia que separar las clases por una recta no parece lo más eficiente, para constatarlo estimo la clasificación con el kernel lineal.

#Kernel lineal
set.seed (1)
tune.out=tune(svm,factor(y)~.,data=X,kernel="linear",type='C-classification',scale=FALSE,ranges=list(cost=c(0.001,0.01, 0.1, 1,5,10,100)))
tune.out
summary(tune.out)
bestmod=tune.out$best.model
summary(bestmod)
#Informa del tipo de modelo, del que puede considerarse como el mejor modelo
plot(bestmod,X)
Pred=predict(bestmod,X)
table(predicción=Pred,Valores_reales=X$y)
# Valores_reales
# predicción 0 1
# 0 305 201
# 1 301 443
#Eficiencia de la clasificación 59.8%

La gráfica que se obtiene del SVM con kernel lineal es:

SVM_Mezcla3_ker-lineal

Haciendo la comparación entre datos originales y las predicciones se tiene lo siguiente:

Orig3_vs_Ker-lineal

Se aprecia que no es muy favorable usar el kernel lineal con estos datos, la eficiencia de la clasificación es del 59.4%

Para comparar con otro kernel uso el radial para hacer la clasificación.

#Kernel Radial

set.seed(1)
tune.out=tune(svm,factor(y)~.,data=X,kernel="radial",sacale=FALSE,type='C-classification',ranges=list(cost=c(0.001,0.01, 0.1, 1,5,10,100)))
tune.out
summary(tune.out)
bestmod=tune.out$best.model
summary(bestmod)
#Informa del tipo de modelo, del que puede considerarse como el mejor modelo
Pred=predict(bestmod,X)
table(predicción=Pred,Valores_reales=X$y)
# Valores_reales
# predicción 0 1
# 0 603 7
# 1 3 637
#Eficiencia de la clasificación 99.2%, con kernel radial

La gráfica que se obtiene con este kernel es la siguiente:

SVM_Mezcla3_ker-radial

Esta imagen muestra que las curvas que se obtienen con el kernel radial son parecidas a las de los datos originales. Haciendo la comparación entre los datos y las estimaciones se obtiene lo siguiente:

Orig3_vs_Ker-Radial

Se aprecia que es muy similar y más aún la eficiencia es del 99.2%, lo cual comparado con el lineal es sumamente mejor usar el kernel radial.

Ejemplo con datos de correos clasificados

Los datos se encuentran cargados en la librería kernelabs, con la cual también se puede estimar la máquina de soporte vectorial. Las comparaciones las hago con varias técnicas:

  • Naive Bayes
  • LDA y Regresión Logística
  • Redes Neuronales
  • Árboles de decisión y random forest

Antes de eso aplico con los cuatro kernel disponibles en svm de e1071 para elegir el que mejor clasificación realiza en la muestra de prueba (test set).

Primero reviso los datos.

#Exploración breve de los datos
#Se cargan primero las librerías

library(kernlab)
library(e1071)
library(MASS)
library(nnet)
librery(randomForest)
data(spam)

#Datos
head(spam)
dim(spam)
#4601 58
str(spam)

#############################################
#Preparación de datos

index=sort(sample.int(4601,1150))
spam.train=spam[-index,]
spam.test=spam[index,]

Se puede hacer algo más al explorar la información, diseñar algunas gráficas o usar alguna técnica de estadística de exploración.

Lo que se hace en el código anterior es dividir los datos entre una conjunto de entrenamiento y un conjunto de prueba.

Aplico los cuatro kernel para elegir el que mejor clasificación realiza.

#SVM con diferentes kernel
########################################
#Kernel lineal

svmfit=svm(type~.,data=spam.train,kernel="linear",cost=0.1,type='C-classification' )
summary(svmfit)
Pred=predict(svmfit,spam.test)
table(predicción=Pred,Valores_reales=spam.test$type)
#         Valores_reales
#predicción nonspam spam
#   nonspam 646 40
#   spam     40 424
#Eficiencia del modelo 93.04%
#######################################
#Kernel radial
svmfit=svm(type~.,data=spam.train,kernel="radial",cost=0.1,type='C-classification' )
summary(svmfit)

set.seed(1)
tune.out=tune(svm,type~.,data=spam.train,kernel="radial",type='C-classification',ranges=list(cost=c(0.001,0.01, 0.1, 1,10,100)))
tune.out
summary(tune.out)
bestmod_r=tune.out$best.model
summary(bestmod_r)
Pred=predict(bestmod_r,spam.test)
table(predicción=Pred,Valores_reales=spam.test$type)
#           Valores_reales
#predicción nonspam spam
#   nonspam     652 36
#   spam         34 428
#Eficiencia del modelo 93.91%
#################################################
#Kernel sigmoid
svmfit=svm(type~.,data=spam.train,kernel="sigmoid",cost=0.1,type='C-classification' )
summary(svmfit)
set.seed(1)
tune.out=tune(svm,type~.,data=spam.train,kernel="sigmoid",type='C-classification',ranges=list(cost=c(0.001,0.01, 0.1, 1,10,100)))
tune.out
summary(tune.out)
bestmod_s=tune.out$best.model
summary(bestmod_s)
Pred=predict(bestmod_s,spam.test)
table(predicción=Pred,Valores_reales=spam.test$type)
#           Valores_reales
#predicción nonspam spam
#    nonspam    649 78
#    spam        37 386
#Eficiencia del modelo 90%
################################################
#Kernel polynomial
svmfit=svm(type~.,data=spam.train,kernel="polynomial",cost=0.1,type='C-classification' )
summary(svmfit)
set.seed(1)
tune.out=tune(svm,type~.,data=spam.train,kernel="polynomial",type='C-classification',ranges=list(cost=c(0.001,0.01, 0.1, 1,10,100)))
tune.out
summary(tune.out)
bestmod_p=tune.out$best.model
summary(bestmod_p)
Pred=predict(bestmod_p,spam.test)
table(predicción=Pred,Valores_reales=spam.test$type)
#           Valores_reales
#predicción nonspam spam
#   nonspam     661 53
#   spam         25 411
#Eficiencia del modelo 93.2%

Se observa en las tasas de error que el mejor kernel de svm es el radial, así que considero este contra el cual comparo las otras técnicas.

Si bien no explico a detalle lo que hace el código en breve es estimar vía tune() la elección del mejor valor de cross-validation de varios valores.

Una técnica clásica para clasificar correos es Naive Bayes, la cual considera ciertas propiedades teóricas que permiten hacer un calculo rápido. Uso NaiveBayes de la librería e1071.

#Naive Bayes vs SVM elegido
NB=naiveBayes(type~.,data=spam.train)
Pred=predict(NB,spam.test)

table(predicción=Pred,Valores_reales=spam.test$type)
# Valores_reales
#predicción nonspam spam
# nonspam 362 18
# spam 324 446
#Se tiene una eficiencia del 70.2% de bien clasificados

Resulta que es mejor el modelo SVM ante el Naive Bayes por un porcentaje altamente mejor.

#Regresión Logística-LDA vs SVM elegido

Mod.lm=glm(type~.,data=spam.train,family='binomial')
summary(Mod.lm)
Mod.lmprob=predict(Mod.lm,newdata=spam.test,type="response")
Mod.pred=rep('nonspam',1150)
Mod.pred[Mod.lmprob>0.5]='spam'
table(Predicción=Mod.pred,Valores_Reales=spam.test$type)
#           Valores_Reales
#Predicción nonspam spam
#   nonspam 643 39
#   spam     43 425
#Predicción 92.86%
############################################
#Método LDA
lda.fit=lda(type~.,data=spam.train)
lda.pred=predict(lda.fit,spam.test)
lda.class=lda.pred$class
table(Predicción=lda.class,Valores_Reales=spam.test$type)
#lda.class nonspam spam
#   nonspam    650 91
#   spam        36 373
#Tasa de eficiencia 88.9%

Se tienen que el porcentaje de eficiencia en la clasificación de SVM 93.91% es mayor de casi 1% mejor que los métodos lineales, en este caso la regresión logística resulta ser la mejor entre las técnicas lineales.

Sin explicar detalles elijo un modelo de redes neuronales para clasificar, la librería es nnet y la función tienen el mismo nombre.

#Redes Neuronales
red=nnet(type~.,data=spam.train,size=2)
Pred=predict(red,spam.test,type="class")

table(Predicción=Pred,Valores_Reales=spam.test$type)
# Valores_Reales
#Predicción nonspam spam
# nonspam 650 29
# spam 36 435
#Tasa de eficiencia 94.34%

Resulta que el modelo de redes neuronales resulta ser mejor casi 0.5% mejor que el modelo de SVM. Lo cual faltaría hacer una revisión para definir el mejor modelo de redes neuronales y quizás la mejora es mejor o nula pero tendrías los valores óptimos de los parámetros.

#Dos técnicas de arboles
# La primera es por árboles y se hace cross-validation para elegir el mejor modelo
 
tree.spam=tree(type~.,spam.train)
set.seed (3)
cv =cv.tree(tree.spam,FUN=prune.misclass )

#Gráfica de Cross-validation para elegir el mejor valor de del parámetro best
par(mfrow =c(1,2))
plot(cv$size,cv$dev,type="b",col="2")
plot(cv$k,cv$dev,type="b",col="3")

prune.tree=prune.misclass(tree.spam,best=8)
tree.pred=predict(prune.tree,spam.test,type="class")
table(tree.pred,Valores_Reales=spam.test$type)

#         Valores_Reales
#tree.pred nonspam spam
#  nonspam 644 58
#  spam     42 406
#Tasa de clasificación 91.3%
#################################################
#Modelo de Random Forest

set.seed (1)
rf.spam =randomForest(type~.,data=spam.train,mtry=7, importance =TRUE)
rf.pred_spam=predict(rf.spam,newdata=spam.test,type="class")

table(rf.pred_spam,Valores_Reales=spam.test$type)
#            Valores_Reales
#rf.pred_spam nonspam spam
#     nonspam 663 29
#     spam     23 435
#Tasa de clasificación 95.4%

Revisando la tasa de clasificaciones correcta por medio de las técnicas de árboles de decisión , resulta ser la mejor técnica para clasificar los datos de los correos. Se obtienen que la técnica de Random Forest resulta clasificar correctamente el 95.4%, comparado con la red neuronal y SVM es mejor casi en un 2%.

La única intensión de este ejemplo es hacer una comparación entre diversas técnicas de clasificación, de cierto modo hacer un ejemplo sencillo y que se puede replicar. Ha esto le faltaría agregar algunos gráficos que muestran los resultados o hacer algunas variaciones de los parámetros requeridos en cada algoritmos para determinar con mejor eficiencia el mejor modelo.

Espero ilustre de manera breve la variedad de técnicas y permita visualizar que al final se requiere probar con muchos modelos para elegir uno entre todos los realizados.

Algoritmos de Machine Learning en R project

“The best thing about R is  that it was developed by statisticians. The worst thing about R is that….it was developed by statisticians.”  Bo Cowgill, Google, Inc.

Todas las entradas de esta categoría muestran algoritmos y técnicas clasificadas como algoritmos de Machine Learning o Aprendizaje Estadístico. En cada entrada comparto el código  y algunos repositorios donde pueden bajar datos y notas sobre los temas.

Las ventajas de usar R project para dar estos ejemplos son las siguientes:

1.-R project es software libre u open source, por lo cual cualquier persona pude descargarlo.

2.-El código que comparto solo debe de ser copiado y pegado en un Script o en la consola  de R project para reproducir los resultados.

Otra ventaja de aprender estas técnicas con ejemplos en R, es que muchos de los algoritmos son acompañados de alguna técnica gráfica para visualizar el resultado. R project por default cuenta con una gran cantidad de herramientas gráficas o se puede hacer uso de librerías como ggplot2, lattice, ggvis para hacer gráficas aún más elaboradas y visualmente más agradables.

El entendimiento de la técnica después de realizar un ejemplo desde mi perspectiva genera interés por saber como aplicarla a otros datos y nos invita a conocer más sobre el algoritmo desde un punto de vista no solo práctico, sino también teórico.

Evité en la medida de lo posible hacer uso de ecuaciones y en cada ocasión que se menciona algún concepto de matemáticas trato de explicarlo de un modo sencillo. Si este concepto requiere a mi parecer una mejor lectura, agregue algunas ligas para consultar en la red.

La única meta que me propuse fue explicar las cosas de modo tal que cualquier persona con un poco de curiosidad pueda entender algo de cada entrada.

Teniendo en mente lo anterior, espero que  el objetivo de trasmitir el conocimiento desde un ejemplo concreto haya sido logrado. En caso de que no sea así, sería grato saber qué parte no es clara y como mejorar este material.

Resumen de las entradas.

El orden siguiente es el que considero apropiado para revisar cada entrada.

1.-¿Qué se necesita conocer de R project para aprender algunas técnicas de Machine Learning?

Es una breve explicación sobre R project y sobre algunas de las funciones con las que cuenta por default. También menciono las librería que se requieren para replicar los ejemplos, aunque en cada entrada menciono algo breve sobre la librerías requeridas.

2.-Análisis Confirmatorio vs Exploratorio

Explico la diferencia entre los dos tipos de análisis y cómo están relacionados. Clasifico el tipo de medidas que uno puede analizar en los datos y pongo algunos ejemplos del tipo de gráficas que pueden ayudarnos a visualizar las medidas al momento de explorar la información.

3.-Regresión Lineal, un ejemplo sencillo.

Esta técnica es muy sencilla de implementar y los datos que nos regresa el comando en R debe ser entendidos para poder evaluar la calidad del resultado obtenido. Omito detalles formales del tema y comparto algunos ejemplos de regresiones simples y múltiples. Es un tema con muchos detalles importantes, por ejemplo la revisión de las posibles fallas al construir un modelo lineal o multilineal. No toco mucho  esos temas, pero en las referencias pueden encontrar observaciones y técnicas para conocer más al respecto.

4.-Regresión no lineal, Cross-Validation y Regularization. 

Los tres temas pueden haber requerido una entrada para cada uno, pero funciona bien mostrar como se relacionan. El objetivo es mostrar la implementación de la regresión no lineal y validar que el modelo elegido es el adecuado, para evitar la sobre estimación de los datos. En esto último las cross-validation y la regularización ayudan a tener un método para hacer una elección del modelo. Se dan algunos ejemplos del uso de los tres conceptos y técnicas.

5.-Clasificación binaria….Naive Bayes. 

Es uno de los algoritmos más usados por sencillo y por que puede ser modificado para tener una clasificación rápida en varias categorías. Doy el ejemplo clásico sobre detección de Spam y trato de complementarlo con el uso de una librería en la cual se cuenta con una función para estimar el modelo. Agregué una pequeña explicación sobre la probabilidad condicional y sobre el uso de la librería tm para analizar textos en R project. Espero ser claro y que permita comprenderse el ejemplo.

6.-Análisis de Cluster, un ejemplo sencillo. 

Se explica de modo breve como funciona la detección de Clusters. Como el tema requiere hacer uso de la noción de distancia o métrica, trato de explicar el concepto. Omití mucho material, sobre todo en el uso de distintas métricas para distintos tipos de variables.

7.-Un ejemplo sencillo sobre Análisis de Componentes Principales (PCA, Principal Component Analysis).

La técnica es muy usada y conocida para hacer reducción de dimensiones, explico qué es reducir dimensiones y como interpretar los resultado de la técnica. Como ejemplo principal hago una reducción de 25 indicadores para formar uno que resuma la información del resto. Replico un ejemplo de análisis de la digitalización de los números escritos a mano, este es un ejemplo clásico y la base de datos fue objeto de estudio para comparar técnicas de clasificación.

8.-Sobre sistemas de recomendación, ejemplo sencillo. 

Hago un breve ejemplos de un sistema de recomendación usando la técnica de k-vecinos cercanos (K-nn), esta técnica es no-paramétrica. Para ejemplificar como funciona pongo un ejemplo inicial y trato de explicar qué realiza el algoritmo y aplicarlo a un caso concreto. Los sistemas de recomendación prácticamente dan la libertad de elegir la técnica de clasificación que uno considere adecuada para el tipo de datos o servicio, por lo cual existen libertad de replicar este ejemplo haciendo uso de otro técnicas de clasificación.

9.-Optimización.

Esta entrada creo que es extensa y puede ser un poco amplia,ya que traté de explicar desde aspectos básicos de mínimo y máximos hasta algoritmo estocásticos y genéticos. Puse un ejemplo de como usar el código y en los casos más sofisticados, la parte estocástica, comparto un ejemplo que permitiera comparar los resultado obtenidos con diversos tipos de algoritmos, no puse los tiempos que tardan en dar la solución pero en caso de replicarlos será notorio el costo del procesamiento.

10.-Máquina de soporte Vectorial (SVM-Sopport Vector Machine).

La técnica sumamente usada y funciona bien para datos no-lineales, se dan varios ejemplos de como usar dos librerías que permiten calcular SVM con distintos kernels. En uno de los ejemplos de hace la comparación de varios modelos para determinar por medio de MSE cual es el mejor. Son ejemplos muy sencillos y trato de agregar explicaciones a cada ejemplo.

Lo que no está aún en estas entradas, pero que estará en inicio de Marzo 2016.

Dejé fuera por el momento varios temas fuera de las entradas, ejemplo Redes neuronales , Arboles de decisión, Random Forests, AdaBoost. Espero posteriormente agregar entradas sobre esos algoritmos.

En general todos las entradas tratan de simplificar y resumir lo que se puede encontrar en el texto  “Machine Learning for Hackers“, libro escrito por Jhon M. White y Drew Conway. Si bien, el libro en si es sencillo y requiere un bajo nivel de matemáticas para estudiarse, traté de hacer más sencillas las explicaciones y de complementarlo con otros ejemplos.

La recomendación es complementar las explicaciones de las entradas con la lectura del texto.  Jhon M White compartió su código  y datos usados en los ejemplos en el repositorio Github y pueden descargar el código presionando estas letras en rojo.

Algunos de los códigos deben de revisarse ya que las librerías que usaron en el año de la edición del texto ya fueron actualizadas, por lo cual es generan algunos errores.

Dos temas que vienen en el texto y decidí omitir son generación de un rango y análisis de redes sociales o de grafos. En otro momento comparto ejemplos respecto al tema.

El segundo es sumamente interesante; análisis de redes sociales, pero el código con el que cuenta el texto ya no es funcional. Así que se requiere rehacer buena parte del código y diseñar nuevos ejemplos, para eso se necesita cierto tiempo. Más aún debido al boom de las redes sociales existen otro tipo de herramientas sobre las cuales se puede escribir.

Para tener una idea de lo nuevo e interesante se pueden consultar las siguientes ligas para echar un vistazo:

Deseo que el material que se encuentra en esta categoría les sea útil y sobre todo les deje con ganas de aprender más al respecto y no solo desde el lado aplicado, sino también desde el lado teórico.

En la siguiente cita aplique “instrumento= algoritmo”.

“Tienes que aprender a tocar tu instrumento. Después debes practicar, practicar y practicar. Y después, cuando finalmente estás en el escenario, olvídalo todo y ulula.” Charlie Parker

 Nota: Todas las críticas y comentarios respecto a las entradas son bienvenidos.

Máquina de soporte Vectorial (SVM-Sopport Vector Machine)

En la entrada explico la técnica de máquina de soporte vectorial (SVM-Sopport Vector Machine),  es un conjunto de algoritmos que pueden aplicarse a problemas de  clasificación o regresión.

La explicación no es exhaustiva ni mucho menos menciono aspectos teóricos de la técnica, tan solo trato de mostrar con varios ejemplos como funciona.

Existe mucho material para revisar los detalles y como menciona el autor de la referencia [2], existen ahora nuevas técnicas para clasificar pero es casi obligado en Machine Learning conocer el algoritmo SVM por la relevancia que han tenido.

Una de esas nuevas  técnicas es Relevance Vector Machine (RVM), la cual es una variación de SVM en su versión Bayesiana y que permite ver la técnicas SVM desde un punto de vista probabilista.

En otras dos entradas comenté sobre los temas  Naive Bayes y Regresión Lineal, la primera técnica es por excelencia una técnica de clasificación y la segunda una de predicción pero se puede desarrollar o modificar para usarla para clasificar.

Las técnicas de SVM cubren ambos aspectos, clasificación y predicción. En la entrada solo comento sobre como usar esta técnica para hacer clasificaciones y muestro un ejemplo sencillo de como usarla para hacer predicción, lo malo de esta familia de técnica es que al construir un modelo uno desea o busca en algunas ocasiones tener una ecuación explicita para analizar el comportamiento de las variables. En este caso eso no es posible, pero para ciertas situaciones no es necesario saber como afectan las variaciones de las variables predictoras, ejemplo para hacer algo de Marketing uno quizás solo busca la clasificación mejor de la muestra o para clasificar textos, quizás no interesa la forma del modelo porque no tenemos una variable respuesta a modelar sino solo entras para clasificar.

Para los ejemplos hago uso de dos librerías en R que calculan los algoritmos de SVM; e1071 y kernlab. También se puede usar para realizar SVM como método de clasificación para dos clases la librería svmpath.

Algunos ejemplos con las librerías

Antes de hacer el ejemplo del texto Machine Learning for Hackers[1] , hago uso de la librería e1071 para mostrar uno de los conceptos claves en SVM, lo que es un hiper-plano. En este caso lo “hiper” se reduce a una recta en el plano. El código es el siguiente:

#Código de muestra de la técnica SVM
#Se carga a librería e1071
library(e1071)
set.seed(1)
#Se generan los datos
x=matrix(rnorm(100*2), ncol=2)
y=c(rep(-1,10), rep(1,10))
x[y==1,]=x[y==1,] + 1
#Se hace la gráfica de los puntos generados en el plano
plot(x, col=(3-y),main="Datos mezclados",xlab="Eje X",ylab="Eje y")
#Se construye un Data.Frame
dat=data.frame(x=x, y=as.factor(y))

#Se calcula SVM para el kernel lineal
svmfit=svm(y~., data=dat, kernel="linear", cost=10)
plot(svmfit, dat)
#Se puede revisar los valores y el resumen de los estimado en R project para el modelo SVM
#svmfit$index
#summary(svmfit)

#Se estima el modelo SVM con kernel polinomial
svmfit=svm(y~., data=dat, kernel="polynomial", cost=10)
plot(svmfit, dat)
#Se  estima el modelo SVM con el kernel sigmoid
svmfit=svm(y~., data=dat, kernel="sigmoid", cost=10)
plot(svmfit, dat)
#Se estima el modelo SVM con el kernel radial
svmfit=svm(y~., data=dat, kernel="radial", cost=10)
plot(svmfit, dat)

Cuando se corre el código se obtienen cuatro gráficas, la primera solo presenta los datos a clasificar en el plano, la segunda, tercera, cuarta y quinta gráfica muestran la clasificación en dos clases por medio de variaciones al modelo SVM.

Datos_meclados_en_el_planoSVM_Lineal

SVM_polinomial

SVM_sigmoid

SVM_Radial

Cada gráfica muestra una separación del conjunto de puntos en dos grupos, pese a que no tienen un título cada gráfica que indique el kernel que se usó, la secuencia es correcta con respecto al código. Entonces lo que hace el tipo de kernel es definir un tipo de línea o frontera entre las clases. Se observa que cuando el kernel es lineal se obtienen como frontera entre las clases como la recta de un ajuste lineal. Esa rectas, para el caso lineal la recta es lo que se llama hiperplano.

Para comparar el funcionamiento del algoritmo como técnica de clasificación, hago una comparación con el método Naive Bayes aplicándolo a una muestra 4601 correos electrónico analizados y con una clasificación de ser spam o no. Estos datos se encuentran en la librería kernlab y hago uso de la función naivebayes de la librería e1071.

Los datos son 4601 correos con 58 variables asignadas, es decir; la tabla de datos tienen 4601 renglones y 58 columnas. La última columna indica si el correo fue clasificado como spam o no, esto permite comparar al final la clasificación de una muestra. En este caso se toman 100 correos para evaluar el método SVM kernel radial y polinomial.

#Comparación con otro método de clasificación
library(kernlab)
data(spam)
index=sample(1:nrow(spam),100)
fm=ksvm(type~.,data=spam[-index,],kernel="rbfdot",kpar="autolibrary(e1071)matic",C=60,cross=5)
pred=predict(fm,spam[index,])
table(pred,spam[index,58])
#Los datos que se obtienen al ejecutar la clasificación son:
#pred nonspam spam
#nonspam  62   2
#spam      2   34

fm=ksvm(type~.,data=spam[-index,],kernel="polydot",kpar="automatic",C=60,cross=5)
pred=predict(fm,spam[index,])
table(pred,spam[index,58])
#Los datos que se obtienen al ejecutar la clasificación son:
#pred nonspam spam
#nonspam 62    4
#spam     2    32
#Comparación con otro método de clasificación
library(e1071)
model=naiveBayes(type~.,data=spam[-index,])
pred=predict(mol,spam[index,])
table(pred,spam[index,58])
#pred nonspam spam
#nonspam 38    1
#spam    26    35

Calculando el porcentaje de eficiencia de los métodos, SVM tienen 96%  y 94% de eficiencia, por otro lado el método Naive Bayes tienen 73% de aciertos. En este ejemplo se observa que el método es eficiente para hacer clasificaciones, en este último ejemplo se tienen 58 variables no todas siguen comportamientos lineales, por lo cual tomar un kernel no lineal para  SVM  resulta apropiado.

Lo que se observa es que SVM puede servir como un método de clasificación para dos clases, pero también es adecuado para construir un modelo para clasificar para más de dos clases.

El  ejemplos del libro Machine Learning for Hackers[1], del cual se pueden descargar los datos en github, inicialmente muestra un comparativo entre un el método de clasificación lineal logit contra SVM.

Hago la regresión logística para los datos que cuentan con una variable indicadora para distinguir entre aceptados y no aceptados y se compara la estimación con el método SVM.

#Cargamos los datos
df <- read.csv(file.path('data', 'df.csv'))

#Calculamos la regresión logística
logit.fit <- glm(Label ~ X + Y,
 family = binomial(link = 'logit'),
 data = df)

#Estimamos la predicción de los aceptados
logit.predictions <- ifelse(predict(logit.fit) > 0, 1, 0)

#Estimamos la media de la predicción

mean(with(df, logit.predictions == Label))
#[1] 0.5156

 El cálculo de SVM se hace mediante la librería e1071.

#Cargamos la librería

library('e1071')

#Estimamos el modelo por medio de SVM

svm.fit <- svm(Label ~ X + Y, data = df)
svm.predictions <- ifelse(predict(svm.fit) > 0, 1, 0)

#Calculamos la media de las predicciones
mean(with(df, svm.predictions == Label))
#[1] 0.7204

 Los resultados son los siguientes, el método lineal estima correctamente el 51%  de los datos y  SVM el 72% .

Para este ejemplo el kernel que se usa para SVM es lineal  y sin embargo arroja mejores resultados que la regresión logística.

La gráfica de los resultados se verían así:

#Graficamos los resultados
library("reshape")
df <- cbind(df,
 data.frame(Logit = ifelse(predict(logit.fit) > 0, 1, 0),
 SVM = ifelse(predict(svm.fit) > 0, 1, 0)))

predictions <- melt(df, id.vars = c('X', 'Y'))

ggplot(predictions, aes(x = X, y = Y, color = factor(value))) +
 geom_point() +
 facet_grid(variable ~ .)+ggtitle("Ejemplo SVM vs Logit") + theme(plot.title = element_text(lineheight=.9, face="bold"))

Ejemplo_1

En las primeras cuatro gráficas de la entrada mostré que cuando se cambia el tipo de kernel para método SVM las líneas de las clases mediante las cuales separa los datos se comportan de modo distinto. Al aplicar varios tipos de kernel para los datos df, se muestra un gráfico comparativo entre el comportamiento de las fronteras entre los datos.

#Comparamos distintos kernel
#Usamos los datos anteriores

df <- df[, c('X', 'Y', 'Label')]

#Calculamos SVM con el Kernel Lineal
linear.svm.fit <- svm(Label ~ X + Y, data = df, kernel = 'linear')
with(df, mean(Label == ifelse(predict(linear.svm.fit) > 0, 1, 0)))

#Calculamos SVM con el Kernel Polinimial
polynomial.svm.fit <- svm(Label ~ X + Y, data = df, kernel = 'polynomial')
with(df, mean(Label == ifelse(predict(polynomial.svm.fit) > 0, 1, 0)))

#Calculamos SVM con el Kernel Radial o RBF 
radial.svm.fit <- svm(Label ~ X + Y, data = df, kernel = 'radial')
with(df, mean(Label == ifelse(predict(radial.svm.fit) > 0, 1, 0)))

#Calculamos SVM con el Kernel Sigmoid
sigmoid.svm.fit <- svm(Label ~ X + Y, data = df, kernel = 'sigmoid')
with(df, mean(Label == ifelse(predict(sigmoid.svm.fit) > 0, 1, 0)))

#Construimos nuestra tabla de predicciones y datos
df <- cbind(df,
 data.frame(LinearSVM = ifelse(predict(linear.svm.fit) > 0, 1, 0),
 PolynomialSVM = ifelse(predict(polynomial.svm.fit) > 0, 1, 0),
 RadialSVM = ifelse(predict(radial.svm.fit) > 0, 1, 0),
 SigmoidSVM = ifelse(predict(sigmoid.svm.fit) > 0, 1, 0)))

#Reordenamos nuestros datos
predictions <- melt(df, id.vars = c('X', 'Y'))

#Graficamos los cálculos
ggplot(predictions, aes(x = X, y = Y, color = factor(value))) +
 geom_point() +
 facet_grid(variable ~ .)

 Ejemplo_2

Anteriormente mostré como al evaluar o clasificar correos el método SVM muestra un buen comportamiento al compararlo con el método clásico  Naive Bayes para analizar spam/no-spam.

Pero Naive Bayes no es el único método de clasificación entre los algoritmos de Machine Learning, así que hago otro comparativo entre diversas técnicas con datos de 3249 correos detectados como spam y se toman para evaluar los métodos 410 palabras contenidas en ellos. En el ejemplo anterior la base de los correos se encontraba ordenada y procesada, en este caso de hace uso de los textos para detectar y clasificar los correos.

Los datos de los correos se encuentran en formato de una matriz, de la cual se toma el 50% de datos para entrenamiento ( training set) y  prueba (test set) el otro 50%.

La cuestión ahora es elegir entre los otros métodos el que resulte mejor como modelo, para eso se hace uso de la validación cruzada y la regularización. Entonces se una el parámetro lambda y el MSE para determinar el lambda apropiado para el modelo logit.

#Cargamos los datos
load(file.path('data', 'dtm.RData'))

set.seed(1)

training.indices <- sort(sample(1:nrow(dtm), round(0.5 * nrow(dtm))))
test.indices <- which(! 1:nrow(dtm) %in% training.indices)

#Definimos los conjuntos de entrenamiento

train.x <- dtm[training.indices, 3:ncol(dtm)]
train.y <- dtm[training.indices, 1]

#Definimos los conjuntos de prueba

test.x <- dtm[test.indices, 3:ncol(dtm)]
test.y <- dtm[test.indices, 1]

#Borramos la matriz original
rm(dtm)

#Cargamos la librería glmnet para calcular logit
library('glmnet')

#Estimamos logit
regularized.logit.fit <- glmnet(train.x, train.y, family = c('binomial'))

lambdas <- regularized.logit.fit$lambda

performance <- data.frame()

#Extraemos los valores de lambda

for (lambda in lambdas)
{
 predictions <- predict(regularized.logit.fit, test.x, s = lambda)
 predictions <- as.numeric(predictions > 0)
 mse <- mean(predictions != test.y)
 performance <- rbind(performance, data.frame(Lambda = lambda, MSE = mse))
}

#Graficamos los resultados

ggplot(performance, aes(x = Lambda, y = MSE)) +
 geom_point() +
 scale_x_log10()

 La gráfica que se  obtiene es la siguiente:

Logit_Lambda

 

Ya que se elige el mejor modelo Logit se estima como se comportó al clasificar los correos.

#Resultados de Logit

best.lambda <- with(performance, max(Lambda[which(MSE == min(MSE))]))

mse <- with(subset(performance, Lambda == best.lambda), MSE)

mse
#[1] 0.06830769

Se observa que aproximadamente solo el 6% de los correos fueron mal clasificados.

Ahora calculo el modelo SVM para el kernel lineal y radial.

#Calculo de SVM
#Cargamos la librería que se usará
library('e1071')

#Estimamos le modelo para el kernel lineal
linear.svm.fit <- svm(train.x, train.y, kernel = 'linear')

predictions <- predict(linear.svm.fit, test.x)
predictions <- as.numeric(predictions > 0)

mse <- mean(predictions != test.y)

#Calculamos el valor del MSE
mse
#0.128

#Estimamos el modelo para el Kernel radial
radial.svm.fit <- svm(train.x, train.y, kernel = 'radial')

predictions <- predict(radial.svm.fit, test.x)
predictions <- as.numeric(predictions > 0)

#Calculamos MSE
mse <- mean(predictions != test.y)

mse
#[1] 0.1421538

Los dos modelos SVM tienen aproximadamente el 12% y 14% respectivamente de correos mal clasificados. Lo cual es mayor al error de clasificación con el modelo Logit.

Como último ejemplo se hace uso del algoritmo k-vecinos cercanos (k-nn), con k=50.

#Calculamos para 50 vecinos cercanos
#Usamos la librería class para la función knn
library('class')

#Estimamos el modelo
knn.fit <- knn(train.x, test.x, train.y, k = 50)

predictions <- as.numeric(as.character(knn.fit))

#Calculamos MSE
mse <- mean(predictions != test.y)

mse
#[1] 0.1396923

Se observa que este último modelo clasifica incorrectamente aproximadamente el 13 % de los correos.

Para mejorar el uso del modelo k-nn se hace la estimación del modelo para k desde 5 hasta 50 y  se elige el mejor modelo, considerando el modelo que tenga el  MSE con menor valor.

#Calculamos el k-nn modelo para varios k

performance <- data.frame()

for (k in seq(5, 50, by = 5))
{
 knn.fit <- knn(train.x, test.x, train.y, k = k)
 predictions <- as.numeric(as.character(knn.fit))
 mse <- mean(predictions != test.y)
 performance <- rbind(performance, data.frame(K = k, MSE = mse))
}

#Elegimos el mejor k
best.k <- with(performance, K[which(MSE == min(MSE))])

best.k
# 5
#Calculamos MSE para el mejor k
best.mse <- with(subset(performance, K == best.k), MSE)

best.mse
#[1] 0.09169231

Se obtiene que el mejor modelo de la familia k-nn resulta ser para k=5 y aproximadamente hace 9% de malas clasificaciones.

Usando como indicador de la calidad de los modelos al MSE se concluye que el modelo Logit tiene el 6% de errores. Así que el comportamiento de esta muestra de correos parece ser mejor modelado por técnicas lineales que no-lineales.

Lo que se observa con el ejemplo es que uno debe de realizar varios modelos para el mismo conjunto de datos y debe de tener algún criterio o indicador para determinar cual es el mejor modelo de entre todos los realizados. No debe uno de creer que una familia de modelos es mejor que la otra, solo alguna funciona mejor para ciertos datos que otra y nada más.

Siempre es conveniente tener más de un modelo y en caso de que dos puedan ser adecuados, es valido hacer ligeras variaciones de esos para determinar cual se adecua mejor al tipo de datos o fenómeno analizado.

Como ejemplo del tipo de gráficas que se obtiene de la librería kernlab hago un ejemplo. Cabe mencionar que esta librería y e1071 cuentan con varias funciones que concentrar algoritmos usuales en Machine Learning. Lo que las distingue es que la primera, kernlab; hace un concentrado los algoritmos dependientes de algún tipo de kernel.

Para el ejemplo se crean 300 puntos y se clasifican en dos grupos, análogo a los primeros ejemplos se busca la curva que separa en dos clases a los datos.

#Generamos los datos

n<-300 #Número de puntos
p<-2 #Dimensiones

sigma<-1 #Varianza de la distribución
meanpos<-0 #Centro de la distribución de los puntos positivos
meanneg<-3 #Centro de la distribución de los putnos negativos

npos<-round(n/2) #Número del ejemplo positivos
nneg<-n-npos #Números negativos del ejemplo

#Generación de los postivos y negativos
xpos<-matrix(rnorm(npos*p, mean=meanpos,sd=sigma),npos,p)
xneg<-matrix(rnorm(nneg*p, mean=meanneg,sd=sigma),nneg,p)
x<-rbind(xpos,xneg)

#Generación de etiquetas

y<-matrix(c(rep(1,npos),rep(-1,nneg)))

#Visualización de datos
plot(x,col=ifelse(y>0,1,2))
legend("topleft",c('Positivos','Negativos'),col=seq(2),pch=1,text.col=seq(2))

Ejemplo_3 La gráfica muestra la mezcla de puntos en color rojo y negro. Primero se estima el modelo lineal de SVM para determinar la clasificación,  en este caso se toma un subconjunto de entrenamiento y otro de prueba.

#Separamos los datos en entrenamiento y prueba

ntrain <- round(n*0.8) #Entrenamiento
tindex <- sample(n,ntrain) #Muestra de índices para entrenamiento
xtrain <- x[tindex,]
xtest <- x[-tindex,]
ytrain <- y[tindex]
ytest <- y[-tindex]
istrain=rep(0,n)
istrain[tindex]=1 

#Visualización

plot(x,col=ifelse(y>0,1,2),pch=ifelse(istrain==1,1,2))
legend("topleft",c('Positive Train','Positive Test','Negative Train','Negative Test'),col=c(1,1,2,2),pch=c(1,2,1,2),text.col=c(1,1,2,2))

library(kernlab)

#Entrenamiento de SVM

svp <- ksvm(xtrain,ytrain,type="C-svc",kernel='vanilladot',C=100,scaled=c())

#Graficación

plot(svp,data=xtrain)

 Ejemplo_3_1

Ejemplo_3_2

 

En la primera gráfica se muestra como se comportan los subconjunto de entrenamiento y prueba. En la segunda gráfica se muestra como el método SVM separa los datos y se queda con los negritos dentro de lo que se conoce como margen. Las clases en las cuales separa los datos les da color rojo y azul. En general cada librería cuenta con algunos datos los cuales se encuentran procesados y son un recurso para probar con las funciones y métodos.

Un ejemplo para usar SVM para regresión

Para este ejemplo uso datos de R Project correspondientes a un indicador financiero Alemán; DAX, estos corresponden a los cierres entre 1991 y 1998.

#Se cargan los datos
data("EuStockMarkets")
#Exploramos los datos
head(EuStockMarkets)

#Se toma solo solo el indicador DAX y se construye 
#una variable para el tiempo, t

t=1:length(EuStockMarkets[,1])
M=data.frame(EuStockMarkets[,1],t)
colnames(M)<-c("DAX","Tiempo")

#Se construye el modelo con SVM
Modelo=ksvm(M$Tiempo,M$DAX)

#Se hace la gráfica del modelo
plot(M$Tiempo,M$DAX,type="l", main="Ejemplo de SVM para regresión", xlab="Tiempo",ylab="DAX")
lines(M$Tiempo,predict(Modelo,M$Tiempo),col="red")


SVM_Regresion_BlogLa imagen muestra el resultado que predice un modelo regresivo con la familia de técnicas de SVM, este modelo no es el mejor, ya que se tendría que explorar el comportamiento del indicador DAX y hacer algunas pruebas para elegir el mejor kernel para este modelo. Pero la intención es mostrar que SVM también puede ser usado para construir modelos regresivos, la desventaja ante modelos de regresión es la expresión o ecuación, pero como mencioné anteriormente esto realmente es relevante dependiendo de los datos  y el origen de la necesidad de modelar estos datos.

Espero sirva de guía lo mostrado en la entrada, en las referencias se encuentran muchos detalles que no menciono.

Referencias:

1.-http://shop.oreilly.com/product/0636920018483.do

2.-http://www.amazon.com/Machine-Learning-Probabilistic-Perspective-Computation/dp/0262018020

3.-http://research.microsoft.com/en-us/um/people/cburges/papers/svmtutorial.pdf 

4.-http://www.springer.com/gp/book/9780387987804

5.-http://www.kernel-machines.org/

6.-http://www.support-vector-machines.org/

7.-http://www.csie.ntu.edu.tw/~cjlin/libsvm/

8.-El capítulo 7, http://www.springer.com/gp/book/9780387310732

9.-El capitulo 12, http://statweb.stanford.edu/~tibs/ElemStatLearn/