Construyendo Sistemas de Recomendación Potentes con Aprendizaje Profundo

'Building Powerful Recommendation Systems with Deep Learning'

Ilustración del autor

Una Implementación Paso a Paso Utilizando la Biblioteca PyTorch TorchRec

Recomendar el producto adecuado a los clientes en el momento adecuado es un desafío frecuente en diversas industrias. Por ejemplo, los banqueros buscan constantemente sugerir servicios altamente relevantes a sus clientes existentes o potenciales. Los minoristas se esfuerzan por recomendar productos atractivos que se ajusten a los gustos de los clientes. De manera similar, las redes sociales buscan construir feeds cautivadores para fomentar la adopción de los usuarios.

A pesar de ser un caso de uso ampliamente explorado, lograr resultados satisfactorios sigue siendo arduo debido a la naturaleza única del problema. Las principales razones incluyen la presencia de abundantes datos categóricos, que a menudo conducen a problemas de escasez, y el aspecto computacional del caso de uso, que plantea problemas de escalabilidad. Solo recientemente los modelos de recomendación han aprovechado las redes neuronales.

En este contexto, Meta ha desarrollado y puesto a disposición de manera abierta un modelo de recomendación de aprendizaje profundo (DRLM, por sus siglas en inglés). El modelo es particularmente notable por combinar los principios de filtrado colaborativo y análisis predictivo, y ser adecuado para la producción a gran escala.

Objetivo

El objetivo de este artículo es guiarlo a través de una implementación paso a paso utilizando la biblioteca PyTorch TorchRec, lo que le permitirá abordar eficazmente su propio caso de uso de recomendación.

Después de leer este artículo, comprenderá:

  1. ¿Cómo funciona el modelo DRLM?
  2. ¿Qué distingue a los modelos DRLM y los hace potentes y escalables?
  3. ¿Cómo puede implementar su propio sistema de recomendación de principio a fin?

El artículo requiere conocimientos generales del problema del sistema de recomendación y familiaridad con la biblioteca PyTorch. Las experimentaciones descritas en el artículo se llevaron a cabo utilizando las bibliotecas TorchRec y PyTorch. Puede encontrar el código aquí en GitHub.

GitHub – linafaik08/recommender_systems_dlrm

Contribuye al desarrollo de linafaik08/recommender_systems_dlrm creando una cuenta en GitHub.

github.co

1. Descifrando el Modelo DRLM

Primero, adentrémonos en las complejidades del modelo DRLM y exploremos sus principios y mecanismos subyacentes.

1.1. Una Visión General del Diseño del Modelo

Para proporcionar una ilustración más tangible, consideremos el escenario de un minorista en línea que busca crear un feed personalizado para cada cliente que visita su sitio web.

Para lograr esto, el minorista puede entrenar un modelo que predice la probabilidad de que un cliente compre un producto en particular. Este modelo asigna una puntuación a cada producto para cada cliente individual, basándose en varios factores. El feed se crea clasificando las puntuaciones.

En este caso, el modelo puede aprender de datos históricos que abarcan una variedad de información para cada cliente y producto. Esto incluye variables numéricas como la edad del cliente y el precio del producto, así como características categóricas como el tipo de producto, el color y más.

Aquí es donde el modelo DRLM sobresale: posee la notable capacidad de aprovechar tanto variables numéricas como categóricas, incluso al tratar con un gran número de categorías únicas. Esto permite que el modelo analice y comprenda de manera exhaustiva las relaciones complejas entre las características. Para entender por qué, echemos un vistazo al modelo de arquitectura en la Figura 1.

Figura 1 — Arquitectura del modelo DRLM, ilustración del autor, inspirada en [5]

Características categóricas

DLRM aprende una tabla de incrustación para cada característica categórica y las utiliza para asignar estas variables a representaciones densas. Por lo tanto, cada característica categórica se representa como un vector de la misma longitud.

Características numéricas

DLRM procesa características numéricas a través de una MLP, llamada MLP inferior. La salida de esta MLP tiene la misma dimensión que los vectores de incrustación anteriores.

Interacción de pares

DLRM calcula el producto escalar entre todos los pares de vectores de incrustación y las características numéricas procesadas. Esto permite que el modelo incluya la interacción de características de segundo orden.

Concatenación y salida final

DLRM concatena estos productos escalares con las características numéricas procesadas y utiliza los resultados para alimentar otra MLP, llamada MLP superior. La probabilidad final se obtiene pasando la salida de esta MLP a una función sigmoide.

1.2. Implementación del modelo

Aunque el potencial del modelo parece prometedor en teoría, su implementación práctica presenta un obstáculo computacional.

Por lo general, los casos de uso de recomendación involucran el manejo de volúmenes masivos de datos. El uso de modelos DLRM, en particular, introduce un número muy grande de parámetros, mayor que los modelos comunes de aprendizaje profundo. En consecuencia, esto amplifica las demandas computacionales asociadas con su implementación.

  1. La mayoría de los parámetros en DLRMs se pueden atribuir a las incrustaciones, ya que consisten en múltiples tablas, cada una de las cuales requiere una gran memoria. Esto hace que los DLRMs sean computacionalmente exigentes, tanto en términos de capacidad de memoria como de ancho de banda.
  2. Aunque la huella de memoria de los parámetros de MLP es más pequeña, aún requieren recursos computacionales sustanciales.

Para mitigar el cuello de botella de memoria, DLRM utiliza una combinación única de paralelismo de modelo para las incrustaciones y paralelismo de datos para las MLP.

2. De concepto a implementación: una guía paso a paso para construir su propio sistema de recomendación personalizado

Esta sección proporciona una guía detallada paso a paso sobre cómo implementar su propio sistema de recomendación desde el principio hasta el final.

2.1. Transformación de datos y construcción de lotes

El primer paso implica convertir los datos en tensores y organizarlos en lotes para ingresar al modelo.

Para ilustrar este proceso, consideremos este dataframe como ejemplo.

Para características dispersas, necesitamos concatenar los valores en un solo vector y calcular las longitudes. Esto se puede lograr utilizando la función KeyedJaggedTensor.from_lengths_sync, que toma ambos elementos como entrada. Aquí hay un ejemplo del script en Python:

values = sample[cols_sparse].sum(axis=0).sum(axis=0)values = torch.tensor(values).to(device)# values = tensor([1, 0, 2, 0, 2, 2, 0, 2, 0, 1, 0, 1, 2, 0], device='cuda:0')lengths = torch.tensor(    pd.concat([sample[feat].apply(lambda x: len(x)) for feat in cols_sparse],              axis=0).values,    dtype=torch.int32).to(self.device)# lengths = tensor([1, 1, 1, 1, 1, 2, 3, 2, 2, 0], device='cuda:0', dtype=torch.int32)sparse_features = KeyedJaggedTensor.from_lengths_sync(  keys=cols_sparse,  values=values,  lengths=lengths)

Para características densas y etiquetas, el proceso es más sencillo. Aquí hay un ejemplo del script en Python:

dense_features = torch.tensor(sample[cols_dense].values, dtype=torch.float32).to(device)labels = torch.tensor(sample[col_label].values, dtype=torch.int32).to(device)

Utilizando las salidas de los pasos anteriores, es posible construir un lote. Aquí hay un ejemplo del script en Python:

batch = Batch(  dense_features=dense_features,  sparse_features=sparse_features,  labels=labels,).to(device)

Para una implementación más completa, puede consultar el archivo batch.py en el repositorio de GitHub correspondiente.

2.2. Inicialización del modelo y configuración de optimización

El siguiente paso implica inicializar el modelo, como se muestra en el siguiente código en Python:

# Inicializar el modelo y configurar la optimización# Definir la dimensionalidad de las incrustaciones utilizadas en el modelodimension_incrustacion = 10# Calcular el número de incrustaciones por característicanum_incrustaciones_por_caracteristica = {c: len(v) for c, v in map_sparse.items()}# Definir los tamaños de capa para la arquitectura densadense_arch_layer_sizes = [512, 256, dimension_incrustacion]# Definir los tamaños de capa para la arquitectura globalover_arch_layer_sizes = [512, 512, 256, 1]# Especificar si se utiliza el optimizador Adagrad o el optimizador SGDadagrad = False# Establecer el valor de épsilon para la optimización de Adagrareps = 1e-8# Establecer la tasa de aprendizaje para la optimizaciónlearning_rate = 0.01# Crear una lista de objetos EmbeddingBagConfig para cada característica dispersaeb_configs = [    EmbeddingBagConfig(        name=f"t_{feature_name}",        embedding_dim=dimension_incrustacion,        num_embeddings=num_incrustaciones_por_caracteristica[feature_name + '_enc'],        feature_names=[feature_name + '_enc'],    )    for feature_idx, feature_name in enumerate(cols_sparse)]# Inicializar el modelo DLRM con la colección de bolsas de incrustación y las especificaciones de la arquitecturadlrm_model = DLRM(    embedding_bag_collection=EmbeddingBagCollection(        tables=eb_configs, device=device    ),    dense_in_features=len(cols_dense),    dense_arch_layer_sizes=dense_arch_layer_sizes,    over_arch_layer_sizes=over_arch_layer_sizes,    dense_device=device,)# Crear una instancia de DLRMTrain para manejar la operación de entrenamientotrain_model = DLRMTrain(dlrm_model).to(device)# Elegir la clase de optimizador adecuada para los parámetros de incrustaciónoptimizador_incrustacion = torch.optim.Adagrad if adagrad else torch.optim.SGD# Establecer los argumentos de palabras clave del optimizadoroptimizer_kwargs = {"lr": learning_rate}if adagrad:    optimizer_kwargs["eps"] = eps# Aplicar el optimizador a los parámetros de la arquitectura dispersa en retrocesoapply_optimizer_in_backward(    optimizer_class=optimizador_incrustacion,    params=train_model.model.sparse_arch.parameters(),    optimizer_kwargs=optimizer_kwargs,)# Inicializar el optimizador denso con los parámetros adecuadosdense_optimizer = KeyedOptimizerWrapper(    dict(in_backward_optimizer_filter(train_model.named_parameters())),    optimizer_with_params(adagrad, learning_rate, eps),)# Crear una instancia de CombinedOptimizer para manejar la optimizaciónoptimizer = CombinedOptimizer([dense_optimizer])

El modelo puede ser entrenado y evaluado utilizando el siguiente código:

pérdida, (pérdida2, logits, etiquetas) = train_model(batch)

Para una implementación más completa, puedes consultar el archivo model.py en el repositorio correspondiente de GitHub.

Puntos clave

✔ El modelo DLRM presenta un enfoque convincente para combinar de manera efectiva características numéricas y categóricas utilizando embeddings, lo que permite capturar patrones y relaciones intrincadas.

✔ Aunque su arquitectura requiere recursos computacionales considerables, su implementación incorpora una combinación única de paralelismo de modelo y paralelismo de datos, lo que hace que el modelo sea escalable para producción.

✔ Sin embargo, debido a la disponibilidad limitada de datos, el rendimiento del modelo no ha sido ampliamente probado en conjuntos de datos del mundo real diversos. Esto genera incertidumbre sobre su efectividad en escenarios prácticos.

✔ Además, el modelo requiere ajustar una cantidad considerable de parámetros, lo que complica aún más el proceso.

✔ Teniendo esto en cuenta, modelos más simples como LGBM pueden ofrecer un rendimiento comparable con una implementación, ajuste y mantenimiento a largo plazo más sencillos, sin la misma carga computacional.

Referencias

[1] M Naumov y otros, Modelo de recomendación de aprendizaje profundo para personalización y sistemas de recomendación, mayo de 2019

[2] Repositorio de GitHub de la implementación inicial del modelo DLRM por parte del equipo de Facebook, disponible como código abierto

[3] DLRM: Un modelo de recomendación de aprendizaje profundo avanzado y de código abierto, Meta AI Blog, julio de 2019

[4] Biblioteca Pytorch para sistemas de recomendación de producción modernos, torchec

[5] Vinh Nguyen, Tomasz Grel y Mengdi Huang, Optimización del modelo de recomendación de aprendizaje profundo en GPUs NVIDIA, junio de 2020