Un enfoque MARL-LP generalizable para la programación en logística

Introducción

que a menudo opera con sorprendente ineficiencia: procesos manuales, montones de papeleo, complejidades legales. Muchas empresas todavía trabajan en papel o Excel y ni siquiera recopilan datos sobre sus envíos.

Pero ¿qué pasa si una empresa es lo suficientemente grande como para ahorrar millones (o incluso cientos de millones) de dólares mediante la optimización (por no hablar del impacto ambiental)? ¿O qué pasa si una empresa es pequeña pero está preparada para un rápido crecimiento?

Movimientos de envío en simulación de redes logísticas

La optimización suele ser inexistente o rudimentaria y está diseñada para la conveniencia operativa en lugar de maximizar los ahorros. La industria está claramente rezagada, pero hay MUCHO dinero sobre la mesa. Las redes de envío se extienden por todo el mundo, desde Alaska hasta Sydney. No los aburriré aquí con estadísticas sobre el tamaño del mercado. Los de dentro ya conocen la escala, y los de fuera pueden hacer una suposición fundamentada (o no tan fundamentada).

Y ahí es donde entré yo. Como especialista en ciencia de datos y aprendizaje automático, me encontré en una gran empresa de logística de rápido crecimiento. Fundamentalmente, el equipo allí no se limitaba a seguir las formalidades; realmente querían optimizar. Esto llevó a la creación de un proyecto de optimización de transporte de línea que dirigí durante dos años y esa es la historia que estoy aquí para contar.

Este proyecto siempre guardará un lugar cálido en mi corazón, aunque nunca llegó a producirse por completo. Creo que tiene un enorme potencial, específicamente en la combinación de logística y la capacidad única de RL para generalizar la toma de decisiones.

Si bien los proyectos de optimización tradicionales generalmente se centran en maximizar la función objetivo o la velocidad de ejecución, la métrica más interesante aquí es cuántos casos invisibles podemos resolver con el mismo modelo (cero disparos o pocos disparos).

En otras palabras, aspiramos a una política generalizable de tiro cero.

Idealmente, capacitamos a un agente, lo colocamos en nuevas condiciones (que nunca ha visto) y simplemente funciona, sin ningún reentrenamiento o con sólo un mínimo ajuste. No necesitamos la perfección; sólo necesitamos que funcione "lo suficientemente bien" como para no violar el SLA.

Entonces podemos decir: 'Genial, el agente también generalizó este caso'.

Estoy seguro de que este enfoque puede producir modelos capaces de una generalización cada vez mayor a lo largo del tiempo. Creo que este es el futuro de la industria.

Y como dijo una vez uno de mis comediantes favoritos:

Al final alguien lo hará de todos modos. Que seamos nosotros.

Contexto empresarial

La empresa había escalado rápidamente, creciendo hasta convertirse en una red de más de 100 terminales de transporte de línea. A esta magnitud, la programación manual alcanzó su límite operativo. Una vez establecido, un cronograma (junto con sus contratos y acuerdos comerciales subyacentes) a menudo permanecería estático durante meses sin un solo cambio.

Observamos una ineficiencia constante: los camiones frecuentemente eran enviados con cargas subóptimas, ya sea subutilizadas (lo que aumentaba los costos unitarios) o congestionadas por desbordamientos de último momento.

El impacto financiero de esta ineficiencia fue significativo. En una red de este tamaño, incluso un aumento del 1% en la utilización de vehículos se traduce en millones de dólares en ahorros anuales. Por lo tanto, maximizar la utilización de los vehículos se convirtió en la principal palanca para reducir costos.

Problema general

Tuvimos acceso a datos históricos de envíos. Si bien el formato de almacenamiento distaba mucho de ser conveniente, el volumen era suficiente para modelar. Gracias a los esfuerzos de mis colegas de ingeniería y ciencia de datos, estos datos sin procesar se transformaron en un estado limpio y utilizable (cubriré los desafíos específicos de la ingeniería de datos en un artículo separado).

Mi objetivo inicial era generar un "buen" cronograma. Un cronograma se define aquí como un conjunto de datos tabulares donde cada fila representa un movimiento físico (envío):

Marca de tiempo: Precisión horaria. Origen y destino: el borde específico en el gráfico. Tipo de vehículo: la clase de activo discreta (por ejemplo, semirremolque de 20 toneladas, furgoneta de 5 toneladas, etc.). Manifiesto de carga: el conjunto particular de 'palets' agregados empaquetados en su interior.

Por lo tanto, elaborar un cronograma requiere cuatro decisiones distintas:

Elige qué paquetes enviar. Qué puede salir mal: si los paquetes de baja prioridad se envían primero, la carga valiosa o urgente podría quedar varada en el almacén. No queremos eso, porque la penalización es mayor para los paquetes más valiosos. Elija el siguiente almacén (dónde enviar). Básicamente, se trata de un problema de enrutamiento: seleccionar el 'siguiente borde' óptimo en el gráfico para cada paquete. Elija los tipos de vehículos y su cantidad. Este es un acto de equilibrio. Qué puede salir mal: enviar varios vehículos pequeños en lugar de uno grande genera ineficiencia en la flota, mientras que enviar camiones grandes que viajan casi vacíos significa pagar por el avión. Por el contrario, el aprovisionamiento insuficiente de la flota provoca retrasos, lo que nos cuesta tanto las penalizaciones de SLA como la reputación. Finalmente, la inacción también es una acción. Para cualquier período de tiempo determinado, la medida óptima podría ser no enviar ningún camión. Para crear un cronograma optimizado, el sistema debe equilibrar perfectamente los envíos activos con el "no hacer nada".

Sin embargo, la realidad introduce complejidades y limitaciones adicionales en el espacio del problema:

Ritmo del cambio: las reglas comerciales son numerosas, complejas y evolucionan rápidamente. El mundo real puede ser mucho más complejo y confuso que una simulación básica. Y los cambios en el mundo real generan actualizaciones de código costosas y que requieren mucho tiempo. Demanda estocástica: la demanda no es determinista, se desconoce de antemano y es dinámica (por ejemplo, visitas múltiples a un cliente dentro de una ventana). Optimización multiobjetivo: no solo estamos minimizando costos; Estamos equilibrando los costos con las penalizaciones de SLA (retrasos) y los gastos de la flota.

Ahora entendemos que no solo necesitamos crear un buen cronograma, sino también crear un sistema que respete la demanda dinámica, la capacidad de los camiones y numerosas reglas comerciales personalizadas, que también pueden cambiar con frecuencia. Esto cristalizó en lo siguiente.

Lista de deseos

Reutilizabilidad de bajo costo. Necesitamos la capacidad de reutilizar el mecanismo para nuevas tareas y contextos de forma económica. Dado que los problemas del mundo real cambian rápidamente, la solución debe ser versátil: adaptable a nuevas configuraciones sin necesidad de volver a entrenar el modelo desde cero cada vez. Inferencia rápida. Si bien el entrenamiento lento es aceptable si produce una generalización más fuerte, la inferencia (toma de decisiones) debe ser rápida. Eficacia "suficientemente buena". No es necesario que el sistema sea perfecto, pero debe cumplir estrictamente con los niveles básicos del SLA. Optimización Global. Necesitamos optimizar el sistema en su conjunto, en lugar de optimizar sus componentes individuales de forma aislada.

Especificaciones del sistema

Topología: gráfico personalizado que contiene de 2 a 100 nodos Frecuencia de decisión: intervalos de 1 hora, 480 pasos/episodio (que representa 20 días) Agentes: centros descentralizados que actúan como tomadores de decisiones independientes Restricciones: límites físicos estrictos en el volumen del vehículo (m³) y peso (kg). Límite estricto en la cantidad de vehículos despachados desde una terminal por hora. Objetivo: Minimizar el costo global respetando las ventanas dinámicas de SLA. Métricas primarias: costo de envío, porcentaje de paquetes retrasados ​​(infracciones de SLA), recuento de vehículos despachados por tipo Métricas secundarias "a largo plazo": tiempo de tránsito promedio y utilización de la capacidad del vehículo.

¿Por qué no los solucionadores estándar?

Spoiler: No pueden lograrlo y no son lo suficientemente buenos.

Naturalmente, comenzamos explorando solucionadores estándar y herramientas disponibles en el mercado como Google OR-Tools. Sin embargo, el consenso fue desalentador: estas herramientas resolverían mal nuestro problema real o resolverían perfectamente una versión diferente e imaginaria del problema. Al final, llegué a la conclusión de que este enfoque era un callejón sin salida.

Optimización lineal

Este es el enfoque más simple y económico, pero tiene un defecto fatal: una formulación lineal no tiene en cuenta la dinámica temporal (todos los demás pasos dependen del anterior).

Esencialmente, LP supone que todo el problema de optimización cabe en una única instantánea estática. Ignora el hecho de que cada paso depende del anterior. Esto es fundamentalmente incorrecto y alejado de la realidad, donde cada movimiento en la red crea efectos en cadena en otros lugares.

Además, el gran volumen de reglas de negocio hace que sea prácticamente imposible agruparlas todas en un solucionador “plano”. En resumen, si bien la programación lineal es una gran herramienta, simplemente es demasiado rígida para un problema de esta magnitud.

Algoritmos genéticos

Los algoritmos genéticos (GA) estaban más cerca en filosofía de lo que necesitábamos. Si bien funcionan, tienen sus propios inconvenientes importantes.

Primero, inferencia lenta. Para obtener un resultado, básicamente debes ejecutar la optimización desde cero cada vez (evolucionando la población). No se puede simplemente “entrenar” un modelo y congelar los pesos, porque no hay pesos para congelar. En consecuencia, el tiempo de reacción del sistema se mide en segundos o incluso minutos (no en milisegundos), típico de una red neuronal o una heurística. En un entorno de producción que trata con cientos de centros en tiempo real, esto se convierte en un cuello de botella importante.

En segundo lugar, la falta de determinismo. Si ejecuta el programador dos veces en el mismo conjunto de datos, un GA puede generar dos programas completamente diferentes. A los clientes empresariales normalmente esto no les gusta mucho, lo que puede generar problemas de confianza.

¿Por qué no Pure RL?

En teoría, se podría intentar resolver todo el problema de un extremo a otro utilizando el aprendizaje por refuerzo puro. Pero ese es definitivamente el camino más difícil.

Una posible solución RL pura tomaría una de dos formas: un único agente en “Modo Dios” que ve todo y asigna cada paquete a cada camión en cada ruta y en cada paso. O un equipo de Agentes Secuenciales actuando uno tras otro.

Agente en modo Dios

En el primer caso, el espacio de acción se vuelve inmanejable. No solo está seleccionando una ruta: debe elegir cada camión (de N tipos) K ​​veces para cada dirección. Con los paquetes, la cosa se pone aún peor: no sólo necesita seleccionar un subconjunto de carga: debe asignar paquetes específicos a camiones específicos. Además, conservas la opción de dejar un paquete en el almacén.

Incluso con una flota pequeña, la cantidad de formas de asignar paquetes específicos a camiones específicos es astronómica. Pedirle a una red neuronal que explore todo este espacio desde cero es ineficiente. Se necesitarían eones para intentar descubrir qué paquete cabe en cada contenedor.

Agentes secuenciales

Una cadena de agentes pasando paquetes por la línea crearía una pesadilla no estacionaria.

Mientras el Agente 1 aprende, su comportamiento es esencialmente aleatorio. El Agente 2 intenta adaptarse al Agente 1, pero como el Agente 1 sigue cambiando su estrategia, el Agente 2 nunca podrá estabilizarse. En lugar de resolver la logística, cada agente se ve obligado a adaptarse infinitamente a la inestabilidad de su vecino. Se convierte en un caso de ciegos guiando a otros ciegos, y es poco probable que converjan en un tiempo razonable.

Además, la RL pura tiene dificultades para aprender restricciones estrictas (como límites de peso máximo) sin incurrir en sanciones masivas. Tiende a “alucinar” soluciones: resultados que parecen eficientes pero que son físicamente imposibles.

Por otro lado, tenemos la Programación Lineal (LP): un solucionador rápido y sencillo que maneja restricciones estrictas de forma nativa. La tentación de crear un subproblema y descargárselo a LP fue demasiado grande para resistirla.

Y por eso elegí un enfoque híbrido.

Solución implementada

Arquitectura híbrida MARL + LP

Creemos un agente de RL que observe el estado de la red logística y organice el flujo de paquetes, decidiendo exactamente qué volumen de carga se mueve entre almacenes en un momento dado. Idealmente, este agente toma decisiones estratégicamente, teniendo en cuenta el estado global del sistema en lugar de simplemente optimizar almacenes individuales de forma aislada.

Luego, un Agente representa un almacén específico responsable de enviar paquetes a sus vecinos. Luego conectamos estos agentes a una red de múltiples agentes. Dado que cada acción realizada por un agente corresponde a un envío a uno o más destinos, la secuencia agregada de estas acciones constituye el cronograma final.

Técnicamente, implementamos un marco de aprendizaje por refuerzo de múltiples agentes (MARL). El entorno RL entrena los algoritmos para generar cronogramas de transporte viables para envíos del mundo real. Fundamentalmente, este proyecto incluye tanto la creación del entorno como los canales de capacitación de los agentes, lo que garantiza que la solución pueda adaptarse (mediante el aprendizaje continuo) a escenarios cada vez más complejos con una mínima intervención humana.

Lo que ven los agentes

A continuación se muestran las observaciones clave (entradas del modelo) ingresadas al agente (cubriré más detalles de la implementación en la Parte 2).

Inventario Local: La cantidad de paquetes en cada almacén. Volumen en tránsito: la cantidad de paquetes que actualmente viajan en los límites entre almacenes. Valor de la carga: el valor financiero total del inventario (crucial para la gestión de riesgos) en cada almacén. SLA Heatmap: Los plazos más cercanos para el stock actual (identificando carga urgente). Pronóstico de entrada: el volumen de paquetes que se espera que lleguen en las próximas 24 horas. Consejos heurísticos: se utilizan exclusivamente durante la etapa de aprendizaje por imitación para iniciar el entrenamiento.

Versión 1. Agentes que dividen una PriorityQueue

En esta versión, los paquetes se alinean en una cola de prioridad, ordenados en orden descendente según una fórmula simple: Prioridad = Valor x Urgencia (cercanía a la fecha límite). El agente de RL “corta” una parte de esta cola seleccionando una fracción de los paquetes principales y decidiendo a qué almacén enviarlos.

Usamos heurísticas para filtrar previamente las opciones: descartar paquetes que definitivamente no queremos enviar todavía o descartar destinos sin sentido (por ejemplo, enviar un paquete en la dirección opuesta a su destino).

Una vez que el RL selecciona qué y dónde, el solucionador de programación lineal interviene para seleccionar la cantidad y el tipo de vehículos. El LP impone estrictas restricciones de peso, volumen y disponibilidad de la flota para garantizar que la simulación no viole las leyes de la física.

En la Versión 1, una única acción consiste en enviar paquetes a un solo vecino. El volumen está determinado por la “fracción” (0,0 a 1,0) seleccionada por el agente. “No hacer nada” es simplemente elegir una fracción de 0.

Figura 1: Arquitectura V1: el agente intenta microgestionar la cola

¡Pero luego me di cuenta!

Versión 2. Agentes que envían camiones

TL;DR: En lugar de seleccionar paquetes, creamos un agente que selecciona cuántos camiones enviar a cada destino. Luego, el solucionador de programación lineal (LP) decide exactamente qué paquetes empaquetar en esos camiones.

¿Qué pasaría si el agente controlara directamente la capacidad de la flota? Esto permite que el solucionador LP maneje el trabajo de "empaquetado de contenedores" de bajo nivel, mientras que el agente RL se centra exclusivamente en la gestión del flujo de alto nivel. ¡Esto es exactamente lo que necesitábamos!

Aquí está la nueva división del trabajo:

Agente RL – Gerente de flota. Decide la cantidad de vehículos y sus destinos.

Intuición: Mira el mapa, consulta el calendario y grita: “¡Envía 5 camiones al Eje Norte!” Se encarga de la gestión del flujo. Habilidad: Estrategia, previsión y equilibrio.

LP Solver – Trabajador portuario. Selecciona los tipos de vehículos específicos (optimizando la combinación de flota) y elige los paquetes específicos para empacar.

Intuición: toma el pedido de los “5 camiones” y la pila de cajas, luego las empaqueta perfectamente para maximizar la densidad del valor. Habilidad: Tetris, álgebra y validez física.

Anteriormente, el agente controlaba una “fracción de la cola”, que determinaba el recuento de paquetes, que determinaba el recuento de camiones y que finalmente determinaba la recompensa. Ahora, el agente controla directamente el recuento de camiones. El vínculo entre acción y recompensa se volvió mucho más corto y predecible, lo que hizo que el entrenamiento fuera más rápido y estable. En términos técnicos, reducimos significativamente el ruido estocástico en la señal de recompensa. El LP ahora optimiza sólo la combinación de embalaje y flota después de que ya se haya tomado la decisión estratégica sobre la capacidad.

Pero los beneficios de ingeniería no terminaron ahí. Dado que LP ahora selecciona los paquetes, ya no necesitamos mantener una cola de prioridad ordenada. Esto simplificó la arquitectura de tres maneras críticas. El primero es la concurrencia: eliminamos los dolores de cabeza técnicos del multiprocesamiento asociados con el intercambio de objetos complejos PriorityQueue entre procesos. En segundo lugar está la vectorización: ya no tenemos que iterar a través de una cola elemento por elemento (un bucle lento de Python). Ahora podemos reescribir todo usando operaciones matriciales. Esto liberó un enorme potencial para la optimización de la velocidad. Además, el código se volvió significativamente más corto y limpio. Y finalmente, acciones multidestino: el agente ahora puede enviar X camiones a N almacenes diferentes en un solo paso (a diferencia de V1, que estaba limitado a un destino por paso). Inmediatamente quedó claro que ésta era la arquitectura ganadora.

Figura 2: Arquitectura V2: el enfoque del "Administrador de flota"

Espacio de observación invariante de escala y generalización

TL;DR: Utilizo representaciones de estado de histograma normalizadas a 0–1 en lugar de valores absolutos para que los agentes sean transferibles a nuevos casos.

Un pilar central de la filosofía de este proyecto es la universalidad: la capacidad de reutilizar la solución en diferentes tareas y nuevas condiciones sin necesidad de volver a capacitarse. Sin embargo, el RL estándar requiere un espacio de acción y observación rígidamente fijo.

Para conciliar esto, normalizamos el espacio de observación para hacerlo invariante en escala. En lugar de realizar un seguimiento de los recuentos brutos (p. ej., “cuántos paquetes se enviaron”), realizamos un seguimiento de los índices (p. ej., “qué porcentaje del total del trabajo pendiente se envió”). Esto permite al agente operar en un nivel más alto de abstracción donde los números absolutos son irrelevantes.

El resultado es un modelo capaz de generalizarse en diferentes escenarios, lo que permite una transferencia cero entre nodos con capacidades muy diferentes.

Un vistazo a la actuación

Los agentes aprendieron el comportamiento de “consolidación LTL”

TL;DR: El aumento del coste de envío provocó más acciones inactivas y menos vehículos.

Uno de los comportamientos emergentes más impresionantes fue la capacidad de los agentes para realizar la consolidación LTL (Less-Than-Truckload). Al comienzo del entrenamiento, los agentes tenían el gatillo fácil y en cada paso despachaban muchos camiones parcialmente llenos. Con el tiempo, su comportamiento cambió.

El costo de envío se calcula como el producto del costo del vehículo y el multiplicador del costo de envío. Cuando el multiplicador del costo de envío aumenta, un envío cuesta más en relación al valor de los paquetes. Eso nos brinda una forma sencilla de ajustar manualmente la parte del costo de envío de la recompensa.

Figura 3: Número total de vehículos enviados por un agente. Un punto: un episodio de “20 días”

A medida que aumentamos el multiplicador del costo de envío (encareciendo la logística en relación con el valor del paquete), los agentes aprendieron a ser pacientes. Comenzaron a elegir acciones más “inactivas”, acumulando efectivamente inventario para enviar menos camiones y más llenos.

Figura 4: Recompensa total del agente. Un punto: un episodio de “20 días”

Debido a que es costoso enviar un camión medio vacío (o medio lleno, dependiendo de su visión del mundo), los agentes comenzaron a esperar para llenar los camiones más cerca del 100% de su capacidad. En otras palabras, los agentes aprendieron a optimizar la utilización del vehículo indirectamente, puramente como un subproducto de la función costo/recompensa.

Por otro lado, enviar menos automóviles generó un mayor número de paquetes vencidos. Creo que cada empresa debe decidir este tipo de compensación (coste versus velocidad) de forma independiente, en función de su estrategia y SLA específicos. En nuestro caso específico, teníamos un límite estricto en el porcentaje de retrasos permitidos, por lo que podíamos optimizar manteniéndonos por debajo de ese límite.

Se mostrarán más resultados y experimentos en la próxima Parte 3.

Restricciones y beneficios

Como mencioné anteriormente, los datos de alta calidad son cruciales para este motor. Si no tiene datos, no tiene simulación, cronogramas ni pronósticos de flujo de paquetes: la base misma de todo el sistema.

También necesita la voluntad de adaptar sus procesos comerciales. En la práctica, esto suele encontrar resistencia. Y, por supuesto, necesita la potencia informática bruta (RAM + CPU sustancial) para ejecutar las simulaciones.

Pero si puede superar estos obstáculos, es posible que descubra que su red logística se ha transformado en algo mucho más poderoso: una red que:

Puede soportar sobrecargas, temporadas altas y eventos repentinos. Esto se debe a que tiene una manera rápida y confiable de generar un nuevo cronograma al instante simplemente aplicando sus agentes previamente capacitados a los nuevos datos. Es más eficiente que la competencia. MARL tiene el potencial de lograr no sólo la optimización local, sino también la optimización global de toda la red durante un horizonte temporal continuo. Puede expandirse o contraerse rápidamente según sea necesario. Esta flexibilidad se logra precisamente a través de las capacidades de generalización del modelo.

¡Todo lo mejor para todos y que sus envíos sean siempre rápidos y confiables!

¡Consulte la próxima Parte 2 para conocer los detalles de implementación y los trucos que utilicé para que esto funcione!

LinkedIn | Correo electrónico