Introducción: Este proyecto se basa en el aprendizaje por refuerzo que entrena a la serpiente para que coma la comida del entorno.
A continuación se muestra un gif de ejemplo para darle una idea de lo que vamos a construir.
Serpiente impulsada por IA
Los requisitos previos para este proyecto son:
- Aprendizaje reforzado
- Aprendizaje profundo (red neuronal densa)
- juego de gaitero
Para entender cómo podemos construir esta simulación de animación de serpientes en 2D manualmente usando pygame, siga el enlace: https://www.geeksforgeeks.org/snake-game-in-python-using-pygame-module /
Ahora que hemos creado el juego básico de serpientes, nos centraremos en aprender a aplicarle refuerzos.
Necesitamos crear tres Módulos para este proyecto:
- El entorno (el juego que tomamos)
- El modelo (Modelo reforzado para predecir el movimiento)
- El agente (Mediador entre Entorno y Modelo)
Módulos de enlace
Algoritmo:
Tenemos una serpiente y comida en el tablero que se coloca al azar.
- Calcule el estado de la serpiente utilizando los 11 valores y, si alguna de las condiciones es verdadera, establezca ese valor en cero; de lo contrario, establezca uno.


¿Cómo se definen 11 estados?
Según la posición actual del líder, el agente calculará los 11 valores de estado como se describe anteriormente.
- Después de recibir estos estados, el agente pasaría esto al modelo y ejecutaría la siguiente acción.
- Después de ejecutar el siguiente estado, calcula la recompensa. La remuneración se define de la siguiente manera:
- Comer comida: +10
- Juego terminado: -10
- Otro (0
- Actualice el valor Q (discutido más adelante) y entrene el modelo.
- Después de analizar el algoritmo ahora tenemos que construir la idea para proceder con la codificación de este algoritmo.
El modelo:


Un modelo de red neuronal
El modelo está diseñado con Pytorch, pero también puede usar TensorFlow según su comodidad.
Estamos usando una red neuronal densa con capa de entrada tamaño 11 y uno capa densa con 256 neuronas y el Salida de 3 neuronas. Puede modificar estos hiperparámetros para obtener el mejor resultado.
¿Cómo funcionan los modelos?
- El juego comienza y el valor Q se inicia aleatoriamente.
- El sistema obtiene el estado actual s.
- En función de s, realiza una acción, ya sea aleatoriamente o en función de su red neuronal. Durante la primera fase de entrenamiento, el sistema a menudo elige acciones aleatorias para maximizar la exploración. Posteriormente, el sistema depende cada vez más de su red neuronal.
- Cuando la IA elige y realiza la acción, el entorno proporciona una recompensa. Luego, el agente alcanza el nuevo estado y actualiza su valor Q de acuerdo con la ecuación de Bellman. Seguramente ha cubierto esta ecuación en el curso de aprendizaje por refuerzo. Si no, puede consultar Q-learning Mathematics.


Ecuación de Bellman
- Además, para cada movimiento, almacena el estado original, la acción, el estado logrado después de realizar esa acción, la recompensa recibida y si el juego terminó o no. Estos datos se muestrean posteriormente para entrenar la red neuronal. Esta operación se llama memoria de repetición.
- Estas dos últimas operaciones se repiten hasta que se cumple una determinada condición (por ejemplo: el juego termina).
El núcleo de este proyecto es el modelo en el que vas a entrenar porque la precisión de la transición que reproducirá la serpiente dependerá de la calidad del modelo que hayas construido. Así que quiero explicar esto usando el código en partes.
Parte-I
1. Creating a class named Linear_Qnet for initializing the linear neural network. 2. The function forward is used to take the input(11 state vector) and pass it through the Neural network and apply relu activation function and give the output back i.e the next move of 1 x 3 vector size. In short, this is the prediction function that would be called by the agent. 3. The save function is used to save the trained model for future use.
Python3
class Linear_QNet(nn.Module): def __init__( self , input_size, hidden_size, output_size): super ().__init__() self .linear1 = nn.Linear(input_size, hidden_size) self .linear2 = nn.Linear(hidden_size, output_size) def forward( self , x): x = F.relu( self .linear1(x)) x = self .linear2(x) return x def save( self , file_name = 'model_name.pth' ): model_folder_path = 'Path' file_name = os.path.join(model_folder_path, file_name) torch.save( self .state_dict(), file_name) |
Parte II
1. Initialising QTrainer class ∗ setting the learning rate for the optimizer. * Gamma value that is the discount rate used in Bellman equation. * initialising the Adam optimizer for updation of weight and biases. * criterion is the Mean squared loss function. 2. Train_step function * As you know that PyTorch work only on tensors, so we are converting all the input to tensors. * As discussed above we had a short memory training then we would only pass one value of state, action, reward, move so we need to convert them into a vector, so we had used unsqueezed function . * Get the state from the model and calculate the new Q value using the below formula: Q_new = reward + gamma * max(next_predicted Qvalue) * calculate the mean squared error between the new Q value and previous Q value and backpropagate that loss for weight updation.
Python3
class QTrainer: def __init__( self ,model,lr,gamma): #Learning Rate for Optimizer self .lr = lr #Discount Rate self .gamma = gamma #Linear NN defined above. self .model = model #optimizer for weight and biases updation self .optimer = optim.Adam(model.parameters(),lr = self .lr) #Mean Squared error loss function self .criterion = nn.MSELoss() def train_step( self ,state,action,reward,next_state,done): state = torch.tensor(state,dtype = torch. float ) next_state = torch.tensor(next_state,dtype = torch. float ) action = torch.tensor(action,dtype = torch. long ) reward = torch.tensor(reward,dtype = torch. float ) # if only one parameter to train , then convert to tuple of shape (1, x) if ( len (state.shape) = = 1 ): #(1, x) state = torch.unsqueeze(state, 0 ) next_state = torch.unsqueeze(next_state, 0 ) action = torch.unsqueeze(action, 0 ) reward = torch.unsqueeze(reward, 0 ) done = (done, ) # 1. Predicted Q value with current state pred = self .model(state) target = pred.clone() for idx in range ( len (done)): Q_new = reward[idx] if not done[idx]: Q_new = reward[idx] + self .gamma * torch. max ( self .model(next_state[idx])) target[idx][torch.argmax(action).item()] = Q_new # 2. Q_new = reward + gamma * max(next_predicted Qvalue) #pred.clone() #preds[argmax(action)] = Q_new self .optimer.zero_grad() loss = self .criterion(target,pred) loss.backward() # backward propagation of loss self .optimer.step() |
El agente
- Encuentre el estado actual de la serpiente en el entorno.
Python3
def get_state( self , game): head = juego.serpiente[ 0 ] point_l = Point(head.x - BLOCK_SIZE, head.y) point_r = Point(head.x + BLOCK_SIZE, head.y) point_u = Point(head.x, head.y - BLOCK_SIZE) point_d = Point(head.x, head.y + BLOCK_SIZE) dir_l = game.direction = = Direction.LEFT dir_r = game.direction = = Direction.RIGHT dir_u = game.direction = = Direction.UP dir_d = game.direction = = Direction.DOWN state = [ # Danger Straight (dir_u and game.is_collision(point_u)) or (dir_d and game.is_collision(point_d)) or (dir_l and game.is_collision(point_l)) or (dir_r and game.is_collision(point_r)), # Danger right (dir_u and game.is_collision(point_r)) or (dir_d and game.is_collision(point_l)) or (dir_u and game.is_collision(point_u)) or (dir_d and game.is_collision(point_d)), # Danger Left (dir_u and game.is_collision(point_r)) or (dir_d and game.is_collision(point_l)) or (dir_r and game.is_collision(point_u)) or (dir_l and game.is_collision(point_d)), # Move Direction dir_l, dir_r, dir_u, dir_d, # Food Location game.food.x < game.head.x, # food is in left game.food.x > game.head.x, # food is in right game.food.y < game.head.y, # food is up game.food.y > game.head.y # food is down ] return np.array(state, dtype = int ) |
- Llamada modelo para obtener el siguiente estado de la serpiente.
Python3
def get_action( self , state): # random moves: tradeoff explotation / exploitation self .epsilon = 80 - self .n_game final_move = [ 0 , 0 , 0 ] if (random.randint( 0 , 200 ) < self .epsilon): move = random.randint( 0 , 2 ) final_move[move] = 1 else : state0 = torch.tensor(state, dtype = torch. float ).cuda() prediction = self .model(state0).cuda() # prediction by model move = torch.argmax(prediction).item() final_move[move] = 1 return final_move |
Nota: Sí tiempo de intercambio tanto de explotación como de exploración. En cuanto a la explotación se refiere a la decisión considerada óptima para los datos observados hasta el momento. Y la exploración es tomar decisiones al azar sin pensar en las acciones previas y un par de recompensas. Entonces, ambos son necesarios porque el actor puede no explorar todo el entorno cuando la explotación y la exploración no siempre pueden proporcionar a mejor recompensa.
- Juega el paso predicho por el modelo en el entorno.
- Guarda el estado actual, el movimiento realizado y la recompensa.
- Entrena al modelo en base a la transición realizada y la recompensa recibida por el Entorno. (entrenando la memoria corta)
Python3
def train_short_memory( self , state, action, reward, next_state, done): self .trainer.train_step(state, action, reward, next_state, done) |
- Si el juego termina debido a que golpea una pared o un cuerpo, entrene al modelo en función de todos los movimientos realizados hasta el momento y reinicie el entorno. (Entrenamiento de memoria larga). Entrenamiento en un tamaño de lote de 1000.
Python3
def train_long_memory( self ): if ( len ( self .memory) > BATCH_SIZE): mini_sample = random.sample( self .memory, BATCH_SIZE) else : mini_sample = self .memory states, actions, rewards, next_states, dones = zip ( * mini_sample) self .trainer.train_step(states, actions, rewards, next_states, dones) |
Entrenar el modelo tomaría alrededor de 100 épocas para adoptar un mejor rendimiento. Ver el progreso de mi entrenamiento.
Producción:
- Para ejecutar este juego, primero cree un entorno en las indicaciones de anaconda o (cualquier plataforma). Luego instale los módulos necesarios, como Pytorch (para el modelo de aprendizaje DQ), Pygame (para las imágenes del juego) y otros módulos básicos.
- Luego ejecute el archivo agent.py en el entorno recién creado y luego comenzará el entrenamiento, y verá las siguientes dos GUI, una para el progreso del entrenamiento y la otra para el juego de serpientes impulsado por IA.
- Después de alcanzar una determinada puntuación, puede salir del juego y el modelo que entrenó se almacenará en la ruta que definió en la función de guardar models.py.
En el futuro, puede usar este modelo entrenado cambiando el código en el archivo agent.py como se muestra a continuación:
Python3
self .model.load_state_dict(torch.load( 'PATH' )) |
Nota: Comenta sobre la función de entrenamiento de todas las llamadas.


Progreso de entrenamiento


Entrenamiento inicial


Después de 100 veces
Código fuente: https://github.com/vedantgoswami/SnakeGameAI
Solicitud:
El objetivo de este proyecto es dar una idea de cómo se puede implementar el aprendizaje por refuerzo y cómo se puede usar en aplicaciones del mundo real, como automóviles autónomos (por ejemplo, AWS DeepRacer), robots de entrenamiento en la línea de ensamblaje, y muchos más…
Puntas:
- Utilice un entorno separado e instale todos los módulos necesarios. (Puedes usar el entorno anaconda)
- Para entrenar el modelo, puede usar GPU para un entrenamiento más rápido.