Prediciendo tu pizza favorita

Este proyecto se vuelve uno de los más interesantes en mi portafolio, y más aún porque hace uso del machine learning; particularmente, utiliza el modelo KNN, programado en Java, para predecir la pizza favorita de la persona.

El verdadero interés y reto para desarrollar correctamente este proyecto, fue el tiempo límite, que fue de tan solo un fin de semana para obtener los datos del sistema, diseñarlo, y programarlo.

Como todos los proyectos, se encuentra disponible el código fuente completo en un repositorio de Github, bajo licencia GNU para software libre. En esta entrada, debido a la extensión del código en el repositorio, sólo me dedicaré a hacer mención del modelo KNN, disponible en la clase PersonaDAO, en el método Leer.

¿Qué es KNN?

Para comprender lo que sucede aquí, empiezo citando a Onel Harrison (2018), que nos dice que “el algoritmo de los k-vecinos más cercanos (KNN) es un algoritmo de machine learning supervisado, simple y fácil de implementar. Que puede ser usado para resolver problemas de clasificación y de regresión”.

En términos simples, el algoritmo se resume en obtener la distancia que hay entre los datos “vecinos” de un nuevo dato, y calcular su distancia. Los k vecinos que tengan una menor distancia, son los datos resultantes del algoritmo.

Se refiere a un algoritmo lento, dado que hay que computar la distancia entre cada uno de los datos en el sistema y el nuevo dato, y sin embargo, no deja de ser eficiente para obtener el resultado que esperamos.

Para calcular la distancia, utilizamos la fórmula:

d(p,q) = \sqrt{(q_1-p_1)^2+(q_2-p_2)^2 + … + (q_n-p_n)^2}

Donde p y q son los conjuntos ordenados de datos del vecino p (el nuevo vecino), el vecino q (el vecino contra el que comparamos la distancia).

¿Cómo lo usamos en este problema?

Para resolver el problema de predicción de la pizza favorita de una persona, se decidió obtener exclusivamente dos datos: sexo (denotado como 0 si es hombre, o 1 si es mujer) y edad.

Por tanto, todo se resume a un problema en un plano bidimensional de clasificación, donde la distancia se calculará entre el sexo y la edad.

Por lo tanto, creamos un par de vectores que almacenen las distancias y las preferencias de las pizzas.

Vector distancias = new Vector(1, 1);
Vector preferencias_pizzas = new Vector(1, 1);

Se lee el archivo denominado “gustospizza.txt”, que almacena todos los datos de nuestro registro, generado a partir de encuestas en redes sociales y personales.

FileReader fileLeePersona = new FileReader("gustospizza.txt");
BufferedReader bufReadPersona = new BufferedReader(fileLeePersona);

Y unas líneas más adelante, podemos visualizar la operación realizada para obtener la distancia:

distancias.addElement(Math.sqrt(Math.pow(sexo-Integer.parseInt(lineaPersona.elementAt(1).toString()),2)+Math.pow(edad-Integer.parseInt(lineaPersona.elementAt(0).toString()),2)));

Y como cierre del código, podemos visualizar cómo se almacena todo en un array clásico, para el tratamiento más rápido de los datos almacenados.

String lectura[][]=new String[distancias.size()][2];
for(int i=0;i<distancias.size();i++){
    lectura[i][0]=String.valueOf(distancias.elementAt(i));
    lectura[i][1]=String.valueOf(preferencias_pizzas.elementAt(i));
}

Problemas a los que nos enfrentamos

En primer lugar, el tiempo, que fue un verdadero limitante para obtener la información y para generar el diseño y la programación del sistema. Por este motivo, puede observarse que parte del código en el repositorio, puede mejorarse para hacerlo más eficiente o más entendible para otros programadores.

En segundo lugar, la variedad de datos, y es que, inclusive entre personas del mismo sexo y de la misma edad, los gustos de pizza fueron realmente variados en muchos casos, hasta el punto de tener que clasificar a todos aquellos que tuvieran distancia menor a 0, en grupos de la misma preferencia, para contabilizar aquellos que tuvieran una mayor probabilidad de aparecer, y ser éstos los que se mostraran como más posibles.

Y finalmente, un grave problema que se presentó fue el overfitting, o sobreajuste, que se refiere a un modelo que está extremamente ajustado a los datos que tiene, que no permite un mayor rango de opciones. Este problema lo descubrí al prestar mi algoritmo ya funcional para que se expusiera en una presentación de proyectos para captación de mi universidad. El problema fue encontrado cuando una chica probó el modelo, y más tarde, ya fuera de la presentación, rebeló que la información fue errónea. Revisando los datos, horas después, su gusto de dicha chica se encontraba entre los 5 más probables, pero el algoritmo sólo mostraba a los 3 con mayor probabilidad de éxito.