Construyendo un Sistema de Recomendaciones con C# y ML.NET
Esta publicación es parte del Segundo Calendario de Adviento de C# en Español, una iniciativa liderada por Benjamín Camacho. Revisa este enlace para conocer más artículos interesantes sobre C# publicados por varios miembros de la comunidad.
Las recomendaciones tienen un fuerte impacto en las elecciones que hacemos al momento de seleccionar un servicio o producto. Los sistemas recomendadores estan en todas partes, por ejemplo al visitar una red social se muestran en primer plano las publicaciones con las que es más probable que interactúes (recuerda que cualquier acción: un like, un retweet -incluso no hacer nada- es registrada por el sistema). Cuando compras en línea, después de hacer una búsqueda te aparecerán productos sugeridos. Esa lista de elemenos no es aleatoria, es una decisión que toma el sistema en base a diferentes factores: ubicación, historial de compras, lo que han comprado tus amigos, artículos relacionados, etc.
Si somos desarrolladores .NET y quisiéramos implementar un sistema recomendador podemos hacerlo gracias a ML.NET, el cual es un poderoso framework de machine learning open-source y cross-platform con el cual podemos crear aplicaciones en C# o F# capaces de resolver problemas de clasificación, detección de anomalías, visión de computadora, sistemas de recomendación y más.
El objetivo de esta publicación es precisamente mostrarte cómo construir un sistema de recomendaciones en C# con ML.NET. Durante el tutorial explicaré algunos conceptos, asi que ¡pongamos manos a la obra!
El código fuente de este proyecto está disponible en mi repositorio de GitHub (lo necesitarás para algunos archivos de datos que hay que agregar en el proyecto).
Paso 1. Vamos a crear una Aplicación de Consola .NET Core

Paso 2. El nombre del proyecto es MovieRecommender:

Paso 3. Una vez creado el proyecto y la solución, agrega los siguientes dos paquetes Nuget:
Microsoft.ML

Microsoft.ML.Recommender

Paso 4. Agrega la carpeta Data, y dentro de ella los dos archivos .csv que vienen en la carpeta con el mismo nombre.

- recommendation-ratings-train.csv será utilizado para entrenar un modelo de recomendaciones.
- recommendation-ratings-test.csv será utilizado para evaluar el desempeño de nuestro modelo entrenado.
En su conjunto, ambos archivos componen un dataset de IMDB, que muestra la puntuación asignada a películas por los usuarios de la plataforma en un formato para análisis de datos.

Normalmente el dataset de entrenamiento significa el 80% de los datos, mientras que el 20% restante sirve para evaluar el modelo generado. Los porcentajes pueden variar, a veces se asigna 70% y 30%, 75% y 25%, etc.
Paso 5. Selecciona ambos archivos y selecciona Copy if newer en la sección Copy to Output Directory.

Como puediste observar con anterioridad, tenemos 4 columnas de datos: userId, movieId, rating y timestamp. La columna que queremos predecir es la de rating, pues deseamos saber si una película se le debe recomendar a un usuario o no. Esta columna de predicción también recibe el nombre de Label. El resto de propiedades serán conocidas como Features en nuestro modelo de Machine Learning, y no es necesario utilizar todas, pues puede ser que alguna característica no sea relevante.
Paso 6. Considerando lo anterior, agrega la carpeta Models y dentro de ella la clase MovieRating, en la que incluiremos las 3 propiedades significativas para nuestro análisis: userId, movieId y rating. Adornamos cada propiedad con el atributo LoadColumnAttribute para indicar el orden en que aparecen en nuestro dataset.
Paso 7. En la misma carpeta, agrega la clase MovieRatingPrediction, con el código mostrado a continuación.
Paso 8. En Program.cs agrega los siguientes espacios de nombre:

Paso 9. Crea una función llamada LoadData, cuyo código se muestra a continuación y que básicamente construye un par de objetos IDataView a partir de la lectura de los archivos .csv de la carpeta Data. Cuando se abren los archivos indicamos que el separador de valores es una coma y que el primer renglón del archivo pertenece a la sección de encabezados (por tanto, no es un dato).
En ML.NET existen 3 elementos importantes:
- Data: representa el formato de datos que lees de un archivo.
- Transformers: representa el formato compatible requerido por los algoritmos de Machine Learning para entrenar los datos.
- Estimators: son utilizados para pasar de Data a Transformers.

Paso 10. En base a lo anterior, vamos a crear el método BuildTrainModel y tiene el código indicado en la sección siguiente:
El código anterior se explica así:
- Primero definimos la transformación de datos (el Estimator) que toma las columnas userId y movieId del Data (los datos originales) y crea un Transformer a donde se mapearán los valores de estas propiedades hacia dos nuevas columnas: userIdEncoded y movieIdEncoded. El tipo de esta columna es Feature.

- Posteriormente, indicamos al Estimator el algoritmo de Machine Learning que utilizaremos para entrenar el sistema de recomendaciones: MatrixFactorizationTrainer. MatrixFactorization es un enfoque de recomendación utilizado cuando se tiene un historial de datos sobre la calificación asignada por los usuarios.
Además, este algoritmo utiliza Filtrado Colaborativo (Collaborative Filtering), hagamos una pausa para explicar que existen varios criterios que podemos considerar para recomendar una película, por ejemplo:
- Population Average: Solo quiero películas cuyo rating (puntuación) promedio (de todas las puntuaciones del público) es superior a 8 puntos.
- Content Based Filtering: Recomiéndame películas en base a mi historial: por ejemplo, que aparezcan películas del género super héroes porque son aquellas que me interesan o con mejor evaluación de mi parte.
- Collaborative Filtering: Sugiéreme una película en base a las opiniones de otros usuarios similares a la mía.


Para nuestro sistema de recomendaciones, utilizaremos el Filtrado Colaborativo, representado en el algoritmo de Matrix Factorization.
- Otros hiperparámetros que se configuraron en el algoritmo son el número de iteraciones (20) y el rango de aproximación (100).
- El método Fit toma los datos iniciales (transformados) y realiza el entrenamiento del modelo, devolviéndolo en formato Transformer.
Paso 11. Genera el método EvaluateModel, cuyo código se muestra a continuación y se explica debajo:
- En este caso, ahora tomamos los datos de prueba (test) y los transformamos en otro formato.
- Con el método Evaluate se hace una comparación entre los valores generados por el modelo entrenado y los valores reales y se devuelve un objeto RegressionMetrics que servirá para evaluar el desempeño del modelo. En pocas palabras, el modelo de predicción sugerirá una puntuación y se comparará este valor contra el real para ver qué tan eficiente fue el algoritmo. Eso se determina con la raíz de la desviación cuadrática media, un estadístico común de evaluación.
Paso 12. El siguiente método a crear se llama UseModelForSinglePrediction e indica si una película debe ser recomendada a un usuario o no en base al modelo recién construido:
- El método CreatePredictionEngine crea un motor de predicción a partir de un modelo suministrado.
- El método Predict realiza la predicción pasando el nuevo valor del que se desea obtener información.
- Se recomienda la película si la puntuación obtenida supera el valor de 3.5.
Paso 13. El último método se llama SaveModel y se utiliza para guardar el modelo para reutilizarlo posteriormente en otras aplicaciones (sin necesidad de entrenar el modelo nuevamente).
Paso 14. El último paso es agregar el código en el método Main para llamar a las funciones que acabamos de crear:
Paso 15. Compila y ejecuta el proyecto. Observa las siguientes pantallas:

- Se entrena el modelo y se muestran las iteraciones como resultado de esta fase
- Se evalúa el modelo entrenado, con un valor de R cuadrada de 0.42 (lo ideal es que el valor sea cercano a 1, pero es un inicio).
- Con el modelo generado, se hace la predicción para el usuario 6 y la película 10: Se recomienda
- Se guarda el modelo de predicción (está disponible en la carpeta Data dentro de bin/Debug o bin/Release, según hayas seleccionado al ejecutar la aplicación)
Como puedes ver, se requieren pocas líneas para comenzar a utilizar ML.NET. La parte matemática ya está codificada en los algoritmos, solo es cosa de entenderlos, revisar la documentación y tutoriales oficiales. Es un inicio, pero puede ser la base para realizar más experimentos de predicción con machine learning.
Por cierto, ¿cómo podemos mejorar el resultado?
- Utilizando un dataset más grande para tener más datos de entrenamiento
- Incrementando el número de características (solo usamos 3 en este ejercicio)
- Probando un algoritmo diferente
- Modificando los hiperparámetros, por ejemplo, utilizando más iteraciones para disminuir el error.
- Revisa más recomendaciones en este enlace.
Una vez que tu modelo haya sido entrenado con una eficiencia satisfactoria, puedes incorporarlo en tu aplicación, por ejemplo construir un sitio web o un API que emita recomendaciones.
Espero que esta publicación te haya sido de utilidad, no olvides compartirla en tus redes sociales, tal vez le sirva a alguien más =)
No olvides seguir el resto de las interesantes publicaciones del Segundo Calendario de Adviento C# en Español. También puedes seguir la conversación en Twitter con el hashtag #advientocsharp.
¡Gracias por la visita y hasta la próxima!
Luis