Optimización de la gestión de inventario con aprendizaje por refuerzo: una guía práctica de Python | de Peyman Kor | octubre de 2024

El estado actual está representado por una tupla (alfa, beta), donde: alfa es el inventario disponible actual (artículos en stock), beta es el inventario en pedido actual (artículos pedidos pero aún no recibidos), init_inv calcula el inventario inicial total sumando alfa y beta.

Luego, necesitamos simular la demanda del cliente utilizando la distribución de Poisson con el valor lambda “self.poisson_lambda”. Aquí, la demanda muestra la aleatoriedad de la demanda del cliente:

alpha, beta = state
init_inv = alpha + beta
demand = np.random.poisson(self.poisson_lambda)

Nota: La distribución de Poisson se utiliza para modelar la demanda, que es una opción común para modelar eventos aleatorios como la llegada de clientes. Sin embargo, podemos entrenar el modelo con datos históricos de demanda o interactuar en vivo con el entorno en tiempo real. En esencia, el aprendizaje por refuerzo consiste en aprender a partir de los datos y no requiere conocimiento previo de un modelo.

Ahora, el “próximo alfa”, que es el inventario disponible, se puede escribir como max(0,init_inv-demand). Lo que eso significa es que si la demanda es mayor que el inventario inicial, entonces el nuevo alfa sería cero; de lo contrario, init_inv-demand.

El costo viene en dos partes. Costo de tenencia: se calcula multiplicando el número de bicicletas en la tienda por el coste de mantenimiento unitario. Entonces, tenemos otro costo, que es costo de desabastecimiento. Es un costo que debemos pagar en los casos de demanda incumplida. Estas dos partes forman la “recompensa” que intentamos maximizar utilizando el método de aprendizaje por refuerzo (una mejor manera de decirlo es que queremos minimizar el costo, por lo que maximizamos la recompensa).

new_alpha = max(0, init_inv - demand)
holding_cost = -new_alpha * self.holding_cost
stockout_cost = 0

if demand > init_inv:

stockout_cost = -(demand - init_inv) * self.stockout_cost

reward = holding_cost + stockout_cost
next_state = (new_alpha, action)

Exploración – Explotación en Q-Learning

Elegir una acción en el método Q-learning implica cierto grado de exploración para obtener una descripción general del valor Q para todos los estados en la tabla Q. Para hacer eso, en cada acción elegida, existe una probabilidad épsilon de que adoptemos un enfoque de exploración y seleccionemos “al azar” una acción, mientras que, con una probabilidad de 1-ϵ, tomemos la mejor acción posible de la tabla Q.

def choose_action(self, state):

# Epsilon-greedy action selection
if np.random.rand() < self.epsilon:

return np.random.choice(self.user_capacity - (state[0] + state[1]) + 1)

else:

return max(self.Q[state], key=self.Q[state].get)

Agente de capacitación RL

El entrenamiento del agente RL se realiza mediante la función “entrenar”, y es el siguiente: Primero, necesitamos inicializar la Q (estructura de diccionario vacía). Luego, se recopilan experiencias en cada lote (self.batch.append((estado, acción, recompensa, next_state))), y la tabla Q se actualiza al final de cada lote (self.update_Q(self.batch)). El número de episodios está limitado a “max_actions_per_episode” en cada lote. El número de episodios es el número de veces que el agente interactúa con el entorno para aprender la política óptima.

Cada episodio comienza con un estado asignado aleatoriamente y, aunque el número de acciones es inferior a max_actions_per_episode, la recopilación de datos para ese lote continúa.

def train(self):

self.Q = self.initialize_Q() # Reinitialize Q-table for each training run

for episode in range(self.episodes):
alpha_0 = random.randint(0, self.user_capacity)
beta_0 = random.randint(0, self.user_capacity - alpha_0)
state = (alpha_0, beta_0)
#total_reward = 0
self.batch = [] # Reset the batch at the start of each episode
action_taken = 0
while action_taken < self.max_actions_per_episode:
action = self.choose_action(state)
next_state, reward = self.simulate_transition_and_reward(state, action)
self.batch.append((state, action, reward, next_state)) # Collect experience
state = next_state
action_taken += 1

self.update_Q(self.batch) # Update Q-table using the batch