Autor: Taylor Foust

Este trabajo es el resultado de un esfuerzo colaborativo de muchos ingenieros de Nubank (en orden alfabético): Austin McEver, Brayan Garzon, Daniel Braithwait, Fábio Souza, Gabriel Gandour, Gustavo Vieira, Helder Dias, Hiroto Udagawa, José Mora, Lucas Costa, Marcelo Buga, Matheus Ramos, Neriton Tolentino y Stelios Karvanis. También agradecemos a Rohan Ramanath, Daniel Silva y Guilherme Tanure por su apoyo.


Introducción

En publicaciones anteriores de nuestro blog, presentamos el concepto de cómo Nubank utiliza modelos fundacionales para aprender representaciones óptimas de características en tareas predictivas. En particular, demostramos cómo los datos de transacciones bancarias pueden representarse como una secuencia de tokens construidos a partir de atributos de la transacción (por ejemplo: fecha, monto, descripción). Las transacciones se combinan con otros eventos e información de contexto para formar una representación del usuario (o narrativa), que luego es tokenizada y enviada a nuestros modelos fundacionales. Estos modelos convierten la secuencia de tokens en representaciones óptimas para sus tareas de aprendizaje. Estas narrativas de usuario no son fijas: es necesario decidir qué información incluir y cómo representarla de la manera más útil para los modelos.

En el resto de esta publicación, mostramos que construir estas narrativas puede considerarse como una búsqueda de hiperparámetros en dos dimensiones principales: qué fuentes de información incluir y cómo representar los eventos que componen cada fuente. Presentamos nuestro enfoque experimental para explorar este espacio de hiperparámetros, y mostramos cómo este proceso reduce significativamente el esfuerzo necesario para incorporar nuevas fuentes de datos, elimina la necesidad de suposiciones y mejora el rendimiento de los modelos.

Descubre las oportunidades

Incluir una transacción en una narrativa de usuario: ¿cómo representamos una transacción?

Presentemos el proceso con un ejemplo sencillo: una persona realiza una compra con tarjeta de crédito. En su forma más simple, podríamos representar este evento con un único token, como <compra-tarjeta-credito>, pero la historia completa incluye mucho más.

Figura 1

¿Cuándo se realizó la compra? ¿Con qué comercio? ¿Fue una compra presencial o en línea? ¿Cuánto dinero se gastó? ¿La compra fue aprobada? Si fue rechazada, ¿cuál fue la razón? ¿Cuál era el saldo de la cuenta después de la compra?

Intuitivamente, podríamos pensar que usar la mayor cantidad posible de información en la representación de una transacción llevaría al mejor desempeño del modelo – el modelo podría simplemente aprender a ignorar lo irrelevante. Sin embargo, nuestros experimentos muestran que agregar más tokens (incluso aquellos que parecen informativos) no siempre mejora el rendimiento del modelo.

Nuestros modelos, como todas las arquitecturas basadas en transformers, tienen una longitud de contexto limitada, debido al escalamiento cuadrático de la atención – incluso cuando usamos algoritmos eficientes como FlashAttention. Esto significa que, cuando la ventana de contexto está llena, cualquier información adicional hará que otra información sea desplazada. Por eso, cada token debe justificar su presencia: debe aportar información útil para la tarea de modelado y no repetir lo que já está representado en otros tokens. En cierto sentido, podemos pensar en este proceso como editar un documento de usuario: asegurándonos de que el lenguaje sea conciso y que solo conservemos lo esencial.

Figura 2

Optimizando Representaciones de Transacciones

Nuestro objetivo al crear documentos de usuario es doble: incluir la mayor cantidad de información útil posible y representar esa información con la menor cantidad de tokens posible. Entonces, ¿cómo sabemos si vale la pena incluir un token determinado? Determinamos la efectividad de la selección de tokens mediante experimentación y pruebas empíricas con métricas de evaluación offline.

Para ilustrar este proceso, consideremos el siguiente ejemplo de usuario: abc-123. Esta persona tiene cuatro transacciones: una transferencia recibida y tres compras con tarjeta de crédito. Estas transacciones se muestran en la Figura 3 a continuación:

Figura 3: transacciones de ejemplo para el usuario abc-123

Para cada una de estas transacciones, hay varios atributos que pueden ser útiles para nuestros modelos: el monto, la fecha (año, día, día de la semana, mes, etc.), el estado de la transacción, la fuente, la descripción, el motivo de rechazo (si corresponde) y el modo de entrada (cómo se realizó la transacción). Para cada uno de estos atributos, creamos módulos de preprocesamiento que agregan los tokens necesarios a la representación de la transacción. Muchos de estos módulos agregan tokens especiales (rangos de monto, si fue pago o recepción, atributos de fecha, estado de la transacción), mientras que otros (como la descripción) se envían como texto sin procesar al tokenizador y se tokenizan con el algoritmo BPE.

En este experimento, generamos varios módulos de preprocesamiento: amount, date, description, source, status, denial_reason y entry_mode, que, al seleccionarse, alteran la representación de las transacciones que se envían al modelo. Luego podemos seleccionar cualquier combinación de estos módulos al inicializar el modelo, entrenarlo y generar métricas de evaluación en un conjunto de prueba.

En la prueba 1, inicializamos el modelo con pre_processors = [amount, date, description]. Una versión tokenizada de la transacción 4 aparece en la Figura 4, con el texto representado por cada token entre corchetes angulares.

Figura 4

En la prueba 2,  pre_processors = [amount, date, description, source].  La representación cambia a:

Figura 5

En la prueba  3,  pre_processors = [amount, date, description, status]:

Figura 6

En la prueba  4,  pre_processors = [amount, date, description, entry_mode]:

Figura 7

En la prueba 5,   pre_processors = [amount, date, description, denial_reason]:

Figura 8

En la prueba  6,   pre_processors = [amount, date, description, status, denial_reason]:

Figura 9

Entrenamos los modelos y evaluamos el rendimiento en nuestro conjunto de validación. Existen muchas más combinaciones posibles además de las que mostramos aquí. Resultados de ejemplo se ilustran en la Figura 10 a continuación:

Figura 10

Aunque limitado en alcance con fines ilustrativos, este experimento nos permitió encontrar una forma optimizada de representar transacciones — un componente clave de la narrativa de usuario. Primero, creamos preprocesadores reutilizables que convierten atributos de transacciones en tokens, y luego realizamos una búsqueda de hiperparámetros para determinar la combinación óptima de estos preprocesadores. En este caso, descubrimos que incluir más información no siempre mejora el rendimiento: en algunos casos, la inclusión de ciertos preprocesadores (o sus combinaciones) puede degradar el desempeño, a menudo debido a la ventana de contexto limitada del modelo.

¿Qué fuentes de información incluir?

En los ejemplos anteriores, solo hablamos de fuentes de datos de transferencias y compras con tarjeta de crédito, pero Nubank cuenta con muchas más fuentes de transacciones, además de otras que no son transaccionales. Al evaluar nuevas fuentes de datos, aplicamos el mismo enfoque experimental para asegurarnos de que la información adicional enriquece las representaciones de usuario y mejora nuestros indicadores de desempeño. Sin embargo, en algunas fuentes, ciertos tokens pueden no ser útiles, y otras pueden requerir la incorporación de nuevos tokens. Por eso, diseñamos nuestro framework para que cada fuente pueda utilizar una lógica de preprocesamiento independiente.

Las Caixinhas de Nubank son cuentas de inversión donde las personas pueden asignar fondos a productos de inversión según sus preferencias — normalmente con un objetivo de ahorro específico. La estructura de estas transacciones es similar a la que ya exploramos con transferencias y tarjetas de crédito: vemos montos entrando y saliendo, y usualmente la descripción corresponde a la meta de ahorro indicada por el usuario. Un ejemplo de estos datos se muestra en la Figura 11 a continuación.

Figura 11

Usar los mismos módulos de preprocesamiento que aplicamos a las fuentes de transacciones anteriores permite capturar una buena cantidad de información (como monto, fecha, descripción, origen y estado). Sin embargo, hay algunos campos adicionales que son únicos de las transacciones de Caixinhas y que también pueden ser útiles: transaction_type, investment_type y account_balance. Podemos crear nuevos módulos que procesen esta información en tokens especiales y probar la inclusión de la fuente de Caixinhas tanto con los preprocesadores estándar como con los nuevos módulos.

Realizar estos experimentos es prácticamente igual a las pruebas que mostramos anteriormente — con la diferencia de que ahora se especifica un conjunto de datos que contiene transacciones de Caixinhas y se activan los nuevos preprocesadores diseñados para estas transacciones. Así, podemos definir las siguientes pruebas adicionales:

Prueba 7: pre_processors = [amount, date, description, status, denial_reason], dataset=”transactions_with_money_box”

Prueba 8: pre_processors = [amount, date, description, status, denial_reason], dataset=”transactions_with_money_box”, pre_processor_overrides={“MONEY BOX”: [amount, date, description, status, transaction_type, investment_type, account_balance]}

En la prueba 8, agregaríamos al documento de usuario una entrada como la siguiente:

Figura 12

De manera similar, en las transacciones relacionadas con préstamos existen varios atributos adicionales que pasamos al modelo y que no son relevantes para las otras fuentes que mencionamos hasta ahora. Los préstamos tienen tasas de interés asociadas, saldos pendientes, número de cuotas pagadas, entre otros. Para aprovechar esta información valiosa sin llenar la ventana de contexto con tokens irrelevantes para otras fuentes, permitimos que cada fuente extraiga solo los tokens que necesita.

Para representar un pago de préstamo incluyendo los preprocesadores interest_rate, number_of_payments_made y remaining_balance, la transacción podría verse de la siguiente manera:

Figura 13

También incorporamos una variedad de otras fuentes de datos que son aún más diferentes de las transacciones tradicionales. Por ejemplo, en nuestros modelos fundacionales de Brasil, integramos datos del Sistema de Información Crediticia (SCR). Estos datos son completamente distintos de las transacciones y toman la forma de actualizaciones mensuales de métricas relacionadas con el crédito de los usuarios. Esta información puede aportar visibilidad sobre el historial financiero de los usuarios antes de unirse a Nubank, y ofrecer una visión de su vida financiera fuera de Nubank. Esto ayuda a garantizar que nuestros modelos funcionen de manera robusta tanto para nuevos clientes como para aquellos que utilizan los servicios de Nubank con menor frecuencia. Después del preprocesamiento, estos datos pueden tener un formato similar al de la Figura 14.

Figura 14

Tener la flexibilidad de incorporar diferentes formatos de datos fácilmente es crucial para acelerar la experimentación y nos permite cubrir vacíos en la comprensión de nuestros usuarios. Al igual que antes, agregar estas nuevas fuentes al modelo y probar su impacto en el desempeño consiste simplemente en crear el módulo de preprocesamiento, seleccionar el conjunto de datos con la nueva fuente disponible y activar el módulo de preprocesamiento. Al optimizar estos hiperparámetros, optimizamos la representación de los usuarios y logramos mejoras en el rendimiento del modelo. En la Figura 15 a continuación se representa el desempeño observado.

Figura 15

Escribiendo Documentos de Usuario

Con las fuentes de datos que mencionamos, el documento para nuestro usuario de ejemplo toma una forma similar a la siguiente:

Figura 16

Aunque no sea una lectura agradable para una persona, hemos validado a través de experimentación que nuestros modelos prefieren esta estructura. Esta representación permite que nuestros modelos comprendan el recorrido de este usuario, desde antes de que se convirtiera en cliente de Nubank hasta la adopción de varios productos del banco. El modelo puede entender la situación financiera del usuario, cómo ha cambiado a lo largo del tiempo, sus hábitos de gasto y ahorro, sus objetivos financieros y dificultades, y los productos de Nubank que utiliza para mejorar su vida. Lo más importante es que nuestro rol no es crear la representación final de los datos para el modelo, sino presentarlos de manera que el modelo pueda extraer de forma óptima la información que necesita y construir las características que lo ayuden a cumplir sus objetivos.

La selección y representación de los datos son extremadamente importantes en todas las aplicaciones de aprendizaje automático, y son una de las partes más laboriosas del proceso. Puede tomar semanas para los profesionales de aprendizaje automático evaluar una nueva fuente de datos, descubrir cómo derivar características tabulares, limpiar y preprocesar estas características y finalmente entrenar un nuevo modelo con esos datos y ejecutar la evaluación.

Con los modelos fundacionales en Nubank, hemos simplificado este proceso, transformándolo en una cuestión de mejorar las maneras de escribir documentos de usuario. Como hemos demostrado, aún hay matices en este proceso y es necesario pensar, pero el proceso es mucho más simple, rápido, flexible y proporciona retroalimentación cuantificable en términos de rendimiento de la evaluación del modelo. Podemos pasar de una fuente de datos sin procesar a obtener resultados de prueba en solo unos pocos días. Y las representaciones de los datos no tienen que ajustarse a las restricciones típicas del aprendizaje automático tabular. Como investigadores en Nubank, solo tenemos que considerar: ¿qué información podría enriquecer la narrativa que tenemos para este usuario y cómo puedo presentar esta información a nuestros modelos de manera eficiente y útil? Luego, presentamos una variedad de opciones y dejamos que el modelo seleccione la representación que más le guste.

Descubre las oportunidades