Un juego de serpientes impulsado por IA que utiliza Deep Q Learning

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:

  1. El entorno (el juego que tomamos)
  2. El modelo (Modelo reforzado para predecir el movimiento)
  3. 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: 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.

Mis notas personales
flecha_caer_arriba

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Salir de la versión móvil