Cuando intentamos separa datos en dos o más clases (por ejemplo separar hojas de árboles y hojas de pétalos) con un algoritmo de machine learning, entonces siempre es importante entender qué tipo de datos son los que tenemos. La idea básica de clasificar datos es encontrar una función g() tal qué pueda separar los datos en las regiones requeridas. Esto es bastante sencillo si lo hiciéramos con datos que se pueden separar por una línea (o con un hiperplano). Sin embargo, muchos de los datos en la realidad no se pueden separar de esta manera. Para ejemplificar datos que no son linealmente separables, utilizaremos una distribución de datos aleatorias llamada moons, o lunas, que tiene la ventaja de mostrar el problema en dos dimensiones. 

Para este estudio requeriremos:

  • sklearn
  • python3
  • numpy
  • matplotlib

Vamos a cargar las herramientas necesarias:

from sklearn import datasets, linear_model
import sklearn
import numpy as np
import matplotlib.pyplot as plt

Ahora bien, para comenzar generaremos los datos con los cuáles vamos a trabajar y los mostraremos en pantalla.

np.random.seed(0)
X, y = datasets.make_moons(200, noise=0.20)
plt.scatter(X[:,0], X[:,1], s=40, c=y, cmap=plt.cm.Spectral)

clf = linear_model.LogisticRegressionCV()
clf.fit(X,y)

plot_decision_boundary(lambda x: clf.predict(x))
plt.title("Regresión logística")

plt.show()

 Cómo podemos observar, los datos no podrían ser separados por una línea. Muchos puntos rojos quedarían del lado azul y viceversa. Si intentamos usar un clasificador lineal para un problema cómo este, el error sería muy grande. Para mostrarlo, vamos a usar un clasificador lineal, cómo lo es la regresión logística. 

clf = linear_model.LogisticRegressionCV()
clf.fit(X,y)

plot_decision_boundary(lambda x: clf.predict(x))
plt.title("Regresión logística")

plt.show()

La línea que se logró dibujar entre las dos clases fue la mejor. Pero el problema no es la estimación de la línea, si no que se requiere un clasificador más poderoso. Como punto interesante, una única neurona artificial (el perceptrón) únicamente puede lograr este tipo de separación, mientras qué una red con una capa oculta logrará hacer una separación no lineal y una red con dos capas ocultas logrará incluso separar islas en los datos. 

Para esto, recurriremos a un clasificador no lineal. En este caso una Máquina de Soporte Vectorial. Esta máquina agrega dimensiones para poder hacer una separación lineal de datos no linealmente separables. Para entender más cómo funciona una SVM le recomendamos: https://www.analyticsvidhya.com/blog/2017/09/understaing-support-vector-machine-example-code/

Vamos a llamar a SVM igual que al clasificador de regresión logística con la implementación de SVM de sklearn para hacer la clasificación y posteriormente realizar la graficación. 

from sklearn.svm import SVC
clf = SVC()
clf.fit(X,y)

SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
    max_iter=-1, probability=False, random_state=None, shrinking=True,
    tol=0.001, verbose=False)

plt.scatter(X[:,0], X[:,1], s=40, c=y, cmap=plt.cm.Spectral)

plot_decision_boundary(lambda x: clf.predict(x))
plt.title("Clasificación con SVM")

plt.show()

Si bien la nueva clasificación no es perfecta, los elementos que erroneamente se encuentran en el campo contrario se ha reducido de manera importante.  ¡Ya no tenemos una línea que separa las clases, si no una función no lineal! Si bien usamos una caja negra para hacer la clasificación, la intención es mostrar de manera visual y práctica que se quiere decir con linealmente separable o no. Espero les ha agradado este breve artículo. Dejo el código completo abajo y la referencia de los sitios de donde se ha obtenido información y código. 

from sklearn import datasets, linear_model
import sklearn
import numpy as np
import matplotlib.pyplot as plt


def plot_decision_boundary(pred_func):
    # Set min and max values and give it some padding
    x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
    y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
    h = 0.01
    # Generate a grid of points with distance h between them
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    # Predict the function value for the whole gid
    Z = pred_func(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    # Plot the contour and training examples
    plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral)

np.random.seed(0)
X, y = datasets.make_moons(200, noise=0.20)
plt.scatter(X[:,0], X[:,1], s=40, c=y, cmap=plt.cm.Spectral)

clf = linear_model.LogisticRegressionCV()
clf.fit(X,y)

plot_decision_boundary(lambda x: clf.predict(x))
plt.title("Regresión logística")

plt.show()


from sklearn.svm import SVC
clf = SVC()
clf.fit(X,y)

SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
    max_iter=-1, probability=False, random_state=None, shrinking=True,
    tol=0.001, verbose=False)

plt.scatter(X[:,0], X[:,1], s=40, c=y, cmap=plt.cm.Spectral)

plot_decision_boundary(lambda x: clf.predict(x))
plt.title("Clasificación con SVM")

plt.show()

 

Referencias:

http://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html

http://www.wildml.com/2015/09/implementing-a-neural-network-from-scratch/

https://gist.github.com/dennybritz/ff8e7c2954dd47a4ce5f

 

 

Share This