Deep Q-Network (DQN)

Le Deep Q-Network (DQN) est une extension du Q-learning, une méthode d’apprentissage par renforcement, qui utilise un réseau de neurones profond pour approximer la fonction Q. Dans les environnements complexes où l’espace d’état est vaste, la table Q traditionnelle devient inefficace car elle nécessite une quantité astronomique de mémoire. Le DQN résout ce problème en remplaçant la table Q par un réseau de neurones qui apprend à estimer les valeurs de Q pour chaque état-action. Cela permet de traiter des environnements de grande dimension, comme les jeux vidéo ou la robotique.

Formule de mise à jour de la fonction Q dans DQN :

Comme dans le Q-learning classique, le DQN met à jour la fonction Q en utilisant la règle suivante :

 

Q(st,at)Q(st,at)+α(rt+γmaxaQ(st+1,a)Q(st,at))Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha \left( r_t + \gamma \max_a Q(s_{t+1}, a) – Q(s_t, a_t) \right)

 

La différence ici est que Q(st,at)Q(s_t, a_t) est approximée par un réseau de neurones, plutôt que d’être stockée dans une table.

Fonctions :

  • Deep Q-Network (exemple)

    Voici un exemple simple d'utilisation de DQN pour entraîner un agent dans l'environnement CartPole-v1 de OpenAI Gym avec un réseau de neurones pour approximer la fonction Q.

    Exemple de code :

    import gymnasium as gym
    import numpy as np
    import random
    from collections import deque
    import tensorflow as tf
    from tensorflow import keras
    
    env = gym.make('CartPole-v1')
    state_size = env.observation_space.shape[0]
    action_size = env.action_space.n
    
    model = keras.Sequential([
        keras.layers.Dense(24, input_shape=(state_size,), activation='relu'),
        keras.layers.Dense(24, activation='relu'),
        keras.layers.Dense(action_size, activation='linear')
    ])
    
    model.compile(loss='mse', optimizer=tf.keras.optimizers.Adam(learning_rate=0.001))
    
    memory = deque(maxlen=2000)
    epsilon = 1.0
    epsilon_min = 0.01
    epsilon_decay = 0.995
    batch_size = 32
    episodes = 10  # réduis si tu veux tester rapidement
    
    def replay():
        if len(memory) < batch_size:
            return
        minibatch = random.sample(memory, batch_size)
        for state, action, reward, next_state, done in minibatch:
            target = reward if done else reward + 0.95 * np.amax(model.predict(np.expand_dims(next_state, axis=0), verbose=0)[0])
            target_f = model.predict(np.expand_dims(state, axis=0), verbose=0)
            target_f[0][action] = target
            model.fit(np.expand_dims(state, axis=0), target_f, epochs=1, verbose=0)
    
        global epsilon
        if epsilon > epsilon_min:
            epsilon *= epsilon_decay
    
    for e in range(episodes):
        state, _ = env.reset()
        total_reward = 0
    
        for time_t in range(500):
            action = random.randrange(action_size) if np.random.rand() <= epsilon else np.argmax(model.predict(np.expand_dims(state, axis=0), verbose=0)[0])
            next_state, reward, terminated, truncated, _ = env.step(action)
            done = terminated or truncated
    
            memory.append((state, action, reward, next_state, done))
            state = next_state
            total_reward += reward
            if done:
                print(f"Episode {e+1}/{episodes} - Score: {total_reward}")
                break
        replay()

    Explication du code :

    1. Initialisation de l’environnement CartPole
    - Environnement : `CartPole-v1` de Gymnasium. - Taille de l’état (`state_size`) : 4 (position, vitesse, angle, vitesse angulaire). - Nombre d’actions (`action_size`) : 2 (gauche ou droite).
    2. Modèle DQN avec TensorFlow/Keras
    - Réseau neuronal séquentiel : - Couche dense 1 : 24 neurones, activation `relu`, input `(state_size,)`. - Couche dense 2 : 24 neurones, activation `relu`. - Couche de sortie : `action_size` neurones, activation `linear` (prédiction Q-valeurs). - Compilation : - Perte : `mean squared error (mse)`. - Optimiseur : `Adam`, `learning_rate=0.001`.
    3. Mécanisme d’apprentissage par expérience (replay memory)
    - `deque` circulaire de taille max 2000 pour stocker les transitions `(état, action, récompense, nouvel état, done)`. - Politique epsilon-greedy : - `epsilon = 1.0` initialement (exploration maximale). - `epsilon_decay = 0.995`, `epsilon_min = 0.01`. - `batch_size = 32`.
    4. Fonction de réentraînement par échantillonnage (replay)
    - Si le buffer contient au moins `batch_size` transitions : - Échantillonnage aléatoire d’un minibatch. - Pour chaque transition : - Calcul de la cible : - `target = reward` si terminal, sinon `target = reward + gamma * max(Q(next_state))` avec `gamma = 0.95`. - Mise à jour de la sortie cible pour l’action effectuée. - Entraînement du modèle avec `fit` sur un seul pas (`epochs=1`). - Mise à jour de `epsilon` pour réduire l’exploration.
    5. Boucle d’entraînement principale
    - Pour `episodes = 10` (peut être augmenté pour un vrai entraînement) : - Réinitialisation de l’environnement. - Exécution d’actions pendant maximum 500 pas ou jusqu’à terminaison : - Action aléatoire (exploration) ou action optimisée via `argmax(Q(state))`. - Stockage de la transition dans la mémoire. - Entraînement via `replay()` en fin d’épisode. - Affichage du score total par épisode.