Clasificar con K-Nearest-Neighbor ejemplo en Python

K-Nearest-Neighbor es un algoritmo basado en instancia de tipo supervisado de Machine Learning. Puede usarse para clasificar nuevas muestras (valores discretos) o para predecir (regresión, valores continuos). Al ser un método sencillo, es ideal para introducirse en el mundo del  Aprendizaje Automático. Sirve esencialmente para clasificar valores buscando los puntos de datos “más similares” (por cercanía) aprendidos en la etapa de entrenamiento (ver 7 pasos para crear tu ML) y haciendo conjeturas de nuevos puntos basado en esa clasificación.

A diferencia de K-means, que es un algoritmo no supervisado y donde la “K” significa la cantidad de “grupos” (clusters) que deseamos clasificar, en K-Nearest Neighbor la “K” significa la cantidad de “puntos vecinos” que tenemos en cuenta en las cercanías para clasificar los “n” grupos -que ya se conocen de antemano, pues es un algoritmo supervisado-.

¿Qué es el algoritmo k-Nearest Neighbor ?

Es un método que simplemente busca en las observaciones más cercanas a la que se está tratando de predecir y clasifica el punto de interés basado en la mayoría de datos que le rodean. Como dijimos antes, es un algoritmo:

  • Supervisado: esto -brevemente- quiere decir que tenemos etiquetado nuestro conjunto de datos de entrenamiento, con la clase o resultado esperado dada “una fila” de datos.
  • Basado en Instancia: Esto quiere decir que nuestro algoritmo no aprende explícitamente un modelo (como por ejemplo en Regresión Logística o árboles de decisión). En cambio memoriza las instancias de entrenamiento que son usadas como “base de conocimiento” para la fase de predicción.

¿Dónde se aplica k-Nearest Neighbor?

Aunque sencillo, se utiliza en la resolución demultitud de problemas, como en sistemas de recomendación, búsqueda semántica y detección de anomalías.

Pros y contras

Como pros tiene sobre todo que es sencillo de aprender e implementar. Tiene como contras que utiliza todo el dataset para entrenar “cada punto” y por eso requiere de uso de mucha memoria y recursos de procesamiento (CPU). Por estas razones kNN tiende a funcionar mejor en datasets pequeños y sin una cantidad enorme de features (las columnas).

¿Cómo funciona kNN?

  1. Calcular la distancia entre el item a clasificar y el resto de items del dataset de entrenamiento.
  2. Seleccionar los “k” elementos más cercanos (con menor distancia, según la función que se use)
  3. Realizar una “votación de mayoría” entre los k puntos: los de una clase/etiqueta que <<dominen>> decidirán su clasificación final.

Teniendo en cuenta el punto 3, veremos que para decidir la clase de un punto es muy importante el valor de k, pues este terminará casi por definir a qué grupo pertenecerán los puntos, sobre todo en las “fronteras” entre grupos. Por ejemplo -y a priori- yo elegiría valores impares de k para desempatar (si las features que utilizamos son pares). No será lo mismo tomar para decidir 3 valores que 13. Esto no quiere decir que necesariamente tomar más puntos implique mejorar la precisión. Lo que es seguro es que cuantos más “puntos k”, más tardará nuestro algoritmo en procesar y darnos respuesta 😉

Las formas más populares de “medir la cercanía” entre puntos son la distancia Euclidiana (la “de siempre”) o la Cosine Similarity (mide el ángulo de  los vectores, cuanto menores, serán similares). Recordemos que este algoritmo -y prácticamente todos en ML- funcionan mejor con varias características de las que tomemos datos (las columnas de nuestro dataset). Lo que entendemos como “distancia” en la vida real, quedará abstracto a muchas dimensiones que no podemos “visualizar” fácilmente (como por ejemplo en un mapa).

Hagamos un ejemplo k-Nearest Neighbor en Python

Exploremos el algoritmo con Scikit learn

Realizaremos un ejercicio usando Python y su librería scikit-learn que ya tiene implementado el algoritmo para simplificar las cosas. Veamos cómo se hace.

Requerimientos

Para realizar este ejercicio, crearemos una Jupyter notebook con código Python y la librería SkLearn muy utilizada en Data Science. Recomendamos utilizar la suite para python de Anaconda. Puedes leer este artículo donde muestro paso a paso como instalar el ambiente de desarrollo. Podrás descargar los archivos de entrada csv o visualizar la notebook online (al final de este artículo los enlaces).

El Ejercicio y el Código

Para nuestro ejercicio tomaremos 257 registros con Opiniones de usuarios sobre una app (Reviews). Utilizaremos 2 columnas de datos como fuente de alimento del algoritmo. Recuerden que sólo tomaré 2 features para poder graficar en 2 dimensiones, PERO para un problema “en la vida real” conviene tomar más características de lo que sea que queramos resolver. Esto es únicamente con fines de enseñanza. Las columnas que utilizaremos serán: wordcount con la cantidad de palabras utilizadas y sentimentValue con un valor entre -4 y 4 que indica si el comentario fue valorado como positivo o negativo. Nuestras etiquetas, serán las estrellas que dieron los usuarios a la app, que son valores discretos del 1 al 5. Podemos pensar que si el usuario puntúa con más estrellas, tendrá un sentimiento positivo, pero no necesariamente siempre es así.

Comencemos con el código!

Primero hacemos imports de librerías que utilizaremos para manejo de datos, gráficas y nuestro algoritmo.

Cargamos el archivo entrada csv con pandas, usando separador de punto y coma, pues en las reviews hay textos que usan coma. Con head(10) vemos los 10 primeros registros.

Aprovechamos a ver un resumen estadístico de los datos:

Son 257 registros. Las estrellas lógicamente vemos que van del 1 al 5. La cantidad de palabras van de 1 sóla hasta 103. y las valoraciones de sentimiento están entre -2.27 y 3.26 con una media de 0,38 y a partir del desvío estándar podemos ver que la mayoría están entre 0,38-0,89 y 0,38+0,89.

Un poco de Visualización

Veamos unas gráficas simples y qué información nos aportan:

Vemos que la distribución de “estrellas” no está balanceada… esto no es bueno. Convendría tener las mismas cantidades en las salidas, para no tener resultados “tendenciosos”. Para este ejercicio lo dejaremos así, pero en la vida real, debemos equilibrarlos. La gráfica de Valores de Sentimientos parece bastante una campana movida levemente hacia la derecha del cero y la cantidad de palabras se centra sobre todo de 0 a 10.

Veamos realmente cuantas Valoraciones de Estrellas tenemos:

Con eso confirmamos que hay sobre todo de 3 y 5 estrellas.

Y aqui una gráfica más bonita:

Graficamos mejor la cantidad de palabras y confirmamos que la mayoría están entre 1 y 10 palabras.

Preparamos las entradas

Creamos nuestro X e y de entrada y los sets de entrenamiento y test.

 

Usemos k-Nearest Neighbor con Scikit Learn

Definimos el valor de k en 7 (esto realmente lo sabemos más adelante, ya veréis) y creamos nuestro clasificador.

Accuracy of K-NN classifier on training set: 0.90
Accuracy of K-NN classifier on test set: 0.86

Vemos que la precisión que nos da es de 90% en el set de entrenamiento y del 86% para el de test.

NOTA: como verán utilizamos la clase KNeighborsClassifier de SciKit Learn puesto que nuestras etiquetas son valores discretos (estrellas del 1 al 5). Pero deben saber que también existe la clase KneighborsRegressor para etiquetas con valores continuos.

Precisión del modelo

Confirmemos la precisión viendo la Confusión Matrix y el Reporte sobre el conjunto de test, que nos detalla los aciertos y fallos:

Cómo se ve la puntuación F1 es del 87%, bastante buena. NOTA: recuerden que este es sólo un ejercicio para aprender y tenemos MUY pocos registros totales y en nuestro conjunto de test. Por ejemplo de 2 estrellas sólo tiene 1 valoración y esto es evidentemente insuficiente.

Y ahora, la gráfica que queríamos ver!

Ahora realizaremos la grafica con la clasificación obtenida, la que nos ayuda a ver fácilmente en donde caerán las predicciones. NOTA: al ser 2 features, podemos hacer la gráfica 2D y si fueran 3 podría ser en 3D. Pero para usos reales, podríamos tener  más de 3 dimensiones y no importaría  poder visualizarlo sino el resultado del algoritmo.

Vemos las 5 zonas en las que se relacionan cantidad de palabras con el valor de sentimiento de la Review que deja el usuario.

Se distinguen 5 regiones que podríamos dividir así:

Es decir que “a ojo” una review de 20 palabras y Sentimiento 1, nos daría una valoración de 4 (zona celeste).

Con estas zonas podemos intuir ciertas características de los usuarios que usan y valoran la app:

  • Los usuarios que ponen 1 estrella tienen sentimiento negativo y hasta 25 palabras.
  • Los usuarios que ponen 2 estrellas dan muchas explicaciones (hasta 100 palabras) y su sentimiento puede variar entre negativo y algo positivo.
  • Los usuarios que ponen 3 estrellas son bastante neutrales en sentimientos, puesto que están en torno al cero y hasta unas 25 palabras.
  • Los usuarios que dan 5 estrellas son bastante positivos (de 0,5 en adelante, aproximadamente) y ponen pocas palabras (hasta 10).

Elegir el mejor valor de k

(sobre todo importante para desempatar o elegir los puntos frontera!)

Antes vimos que asignamos el valor n_neighbors=7 como valor de “k” y obtuvimos buenos resultados. ¿Pero de donde salió ese valor?. Pues realmente tuve que ejecutar este código que viene a continuación, donde vemos distintos valores k y la precisión obtenida.

En la gráfica vemos que con valores k=7 a k=14 es donde mayor precisión se logra.

Clasificar y/o Predecir nuevas muestras

Ya tenemos nuestro modelo y nuestro valor de k. Ahora, lo lógico será usarlo! Pues supongamos que nos llegan nuevas reviews! veamos como predecir sus estrellas de 2 maneras. La primera:

[5]

Este resultado nos indica que para 5 palabras y sentimiento 1, nos valorarán la app con 5 estrellas.

Pero también podríamos obtener las probabilidades que de nos den 1, 2,3,4 o 5 estrellas con predict_proba():

[[0.00381998 0.02520212 0.97097789 0. 0. ]]

Aquí vemos que para las coordenadas 20, 0.0 hay 97% probabilidades que nos den 3 estrellas. Puedes comprobar en el gráfico anterior, que encajan en las zonas que delimitamos anteriormente.

Conclusión

En este ejercicio creamos un modelo con Python para procesar y clasificar puntos de un conjunto de entrada con el algoritmo k-Nearest Neighbor. Cómo su nombre en inglés lo dice, se evaluán los “k vecinos más cercanos” para poder clasificar nuevos puntos. Al ser un algoritmo supervisado debemos contar con suficientes muestras etiquetadas para poder entrenar el modelo con buenos resultados. Este algoritmo es bastante simple y -como vimos antes- necesitamos muchos recursos de memoria y cpu para mantener el dataset “vivo” y evaluar nuevos puntos. Esto no lo hace recomendable para conjuntos de datos muy grandes. En el ejemplo, sólo utilizamos 2 dimensiones de entrada para poder graficar y ver en dos dimensiones cómo se obtienen y delimitan los grupos. Finalmente pudimos hacer nuevas predicciones y a raíz de los resultados, comprender mejor la problemática planteada.

Suscripción al Blog

Recibe el próximo artículo quincenal sobre Machine Learning y buenas prácticas Python

Puedes hacer más ejercicios Machine Learning en Python  en nuestra categoría d Ejercicios paso a paso por ejemplo de Regresión Logística ó  clustering K-means ó comprender y crear una Sencilla Red Neuronal.

Recursos y enlaces del ejercicio

Más artículos de Interés sobre k-Nearest Neighbor (en Inglés)

GuardarGuardar

GuardarGuardar

5 Replies to “Clasificar con K-Nearest-Neighbor ejemplo en Python”

  1. Gracias, Juan Ignacio, por esta entrada. Me ha resultado muy ilustrativa.

    Tan sólo indicar que he tenido dos dificultades con el código: al leer el fichero csv (hay que indicar “encoding=’latin-1′”) y determinados valores de “sentiment” dentro de este mismo fichero tienen un formato que da problemas al crear los sets de entrenamiento y prueba (los he asimilado a otros valores válidos). No sé si es el caso de otros usuarios, pero lo indico por si sirve de ayuda.

    Tengo una pregunta relativa a cómo se puede indicar que la distancia sea euclídea, gaussiana o de Mahalanobis al aplicar k-NN y por qué deberíamos emplear una u otra.

    Gracias.

    Andrés.

    1. Hola Andrés, muchas gracias por escribir y por tu aporte para resolver esos problemas de encoding, yo uso mac e imagino que tendrá que ver con el cambio de sistema operativo.
      Para podel cambiar a distintas funciones de distancia, de dejo el enlace a la documentación Documentación kNN Métricas de donde comenta poder usar: [‘cityblock’, ‘cosine’, ‘euclidean’, ‘l1’, ‘l2’, ‘manhattan’] y otras 17 más. Si a pesar de eso, quieres crear una función propia “custom”, también puedes (con lambda), como lo muestran en este enlace: create custom distance function . Otro enlace.
      Saludos!

  2. Hola Nacho. gran artículo, como siempre. Tengo una pregunta: he probado a ejecutar el modelo sin escalar los valores de X (o sea, sin usar MinMaxScaler) y el accuracy ha bajado a 0.78 en training y 0.74 en test. ¿Esto por qué sucede? ¿KNN necesita tener los valores de X entre 0 y 1? ¡Muchas gracias!

      1. Muchas gracias, Nacho. Parece un artículo muy interesante. Le echaré un vistazo en cuanto tenga tiempo.

Leave a Reply