[PYTHON] Défiez DQN (Modoki) avec Chainer ✕ Open AI Gym!

introduction

J'ai créé un programme pour résoudre le contrôle classique d'OpenAI Gym avec Deep Q Network, communément appelé DQN, qui combine apprentissage en profondeur et apprentissage intensif. Cette fois, je voudrais présenter la mise en œuvre.

À propos de DQN lui-même

L'article est très facile à comprendre et je l'ai implémenté en référence aux articles et au code GitHub présentés ici. Si vous souhaitez connaître la théorie de l'apprentissage par renforcement et le DQN, veuillez vous reporter ici.

DQN "Modoki"?

Comme vous pouvez le voir à partir du nom Deep Q Network, DQN se rapproche fonctionnellement de l'apprentissage Q, qui est l'un des apprentissages par renforcement, avec un réseau neuronal multicouche. En plus de cela, il semble qu'il ne puisse être appelé DQN qu'après avoir incorporé les trois méthodes suivantes.

  1. Experience Replay
  2. Fixed Target Q-Network
  3. Coupure de récompense

La méthode que j'ai implémentée cette fois n'était que de 1 et 2, et je n'ai pas coupé la récompense de 3. Donc, pour être précis, ce n'est pas DQN. J'ai donc choisi DQN "Modoki".

À propos d'OpenAI Gym

Une plate-forme open source qui facilite la création d'un environnement pour un apprentissage amélioré. C'est une bibliothèque python

$ pip install gym

Facile à installer avec. Pour plus de détails, veuillez consulter le Site officiel.

la mise en oeuvre

Je voudrais maintenant présenter le code implémenté. Une partie du code affiché ici est omis, veuillez donc vérifier le tout depuis ici.

réseau neuronal

Je l'ai implémenté en utilisant Chainer. 100 unités ont 3 couches et la fonction d'activation est Leaky ReLU.

class Neuralnet(Chain):

    def __init__(self, n_in, n_out):
        super(Neuralnet, self).__init__(
            L1 = L.Linear(n_in, 100),
            L2 = L.Linear(100, 100),
            L3 = L.Linear(100, 100),
            Q_value = L.Linear(100, n_out, initialW=np.zeros((n_out, 100), dtype=np.float32))
        )

    def Q_func(self, x):
        h = F.leaky_relu(self.L1(x))
        h = F.leaky_relu(self.L2(h))
        h = F.leaky_relu(self.L3(h))
        h = self.Q_value(h)
        return h

Agent

Implémentation de la partie agent de l'apprentissage amélioré.

réglages des paramètres

Il définit les paramètres pour effectuer l'apprentissage par renforcement. Le réseau de neurones introduit précédemment est également défini en fonction du nombre d'états de lecture et du nombre d'actions. Et pour Fixed Target Q-Network, faites une copie complète de la fonction Q créée. En d'autres termes, il existe deux fonctions Q. Au début, j'ai eu du mal à comprendre cette partie ...

class Agent():

    def __init__(self, n_st, n_act, seed):
        self.n_act = n_act
        self.model = Neuralnet(n_st, n_act)
        self.target_model = copy.deepcopy(self.model)
        self.optimizer = optimizers.Adam()
        self.optimizer.setup(self.model)
        self.memory = deque()
        self.loss = 0
        self.step = 0
 self.gamma = 0,99 # taux d'actualisation
 self.mem_size = 1000 #Experience Nombre d'expériences à retenir pour la relecture
 self.batch_size = 100 # Taille du mini-lot pour Experience Replay
 self.train_freq = 10 # Intervalle d'entraînement du réseau neuronal
 self.target_update_freq = 20 # Intervalle de synchronisation du réseau cible
        # ε-greedy
 self.epsilon = 1 # valeur initiale de ε
 self.epsilon_decay = 0,005 # Valeur de décroissance de ε
 self.epsilon_min = 0 # valeur minimale de ε
 self.exploration = 1000 # Nombre d'étapes jusqu'à ce que ε commence à décroître (cette fois jusqu'à ce que la mémoire soit accumulée)

Accumulation d'expérience

Pour la relecture d'expérience

  1. État: st
  2. Action: agir
  3. Récompense: r
  4. État suivant: st_dash
  5. Fin de l'épisode: ep_end

Les cinq éléments de sont exploités comme une expérience et stockés en mémoire. S'il dépasse la taille de la mémoire définie au début, il sera supprimé dans un style Tokoroten à partir de celui mis en premier. Au début, la mémoire n'était qu'une liste, mais j'ai entendu dire qu'il existe un deque qui peut lier les appendices et les pop aux deux extrémités, alors j'ai utilisé cela.

def stock_experience(self, st, act, r, st_dash, ep_end):
    self.memory.append((st, act, r, st_dash, ep_end))
    if len(self.memory) > self.mem_size:
        self.memory.popleft()

Experience Replay

Il s'agit de la partie implémentation de Experience Replay, qui est l'une des techniques importantes de DQN. Mélangez la mémoire stockée, découpez-la dans la taille de mini-lot définie et apprenez.

def suffle_memory(self):
    mem = np.array(self.memory)
    return np.random.permutation(mem)

def parse_batch(self, batch):
    st, act, r, st_dash, ep_end = [], [], [], [], []
    for i in xrange(self.batch_size):
        st.append(batch[i][0])
        act.append(batch[i][1])
        r.append(batch[i][2])
        st_dash.append(batch[i][3])
        ep_end.append(batch[i][4])
    st = np.array(st, dtype=np.float32)
    act = np.array(act, dtype=np.int8)
    r = np.array(r, dtype=np.float32)
    st_dash = np.array(st_dash, dtype=np.float32)
    ep_end = np.array(ep_end, dtype=np.bool)
    return st, act, r, st_dash, ep_end

def experience_replay(self):
    mem = self.suffle_memory()
    perm = np.array(xrange(len(mem)))
    for start in perm[::self.batch_size]:
        index = perm[start:start+self.batch_size]
        batch = mem[index]
        st, act, r, st_d, ep_end = self.parse_batch(batch)
        self.model.zerograds()
        loss = self.forward(st, act, r, st_d, ep_end)
        loss.backward()
        self.optimizer.update()

Partie de mise à jour de la fonction Q

Il s'agit de la partie mise à jour de la fonction Q utilisant le réseau neuronal. Il est important d'utiliser la fonction Q copiée (self.target_model.Q_func) dans la partie qui calcule la valeur Q maximale dans l'état suivant (st_dash).

def forward(self, st, act, r, st_dash, ep_end):
    s = Variable(st)
    s_dash = Variable(st_dash)
    Q = self.model.Q_func(s)
    tmp = self.target_model.Q_func(s_dash)
    tmp = list(map(np.max, tmp.data))
    max_Q_dash = np.asanyarray(tmp, dtype=np.float32)
    target = np.asanyarray(copy.deepcopy(Q.data), dtype=np.float32)
    for i in xrange(self.batch_size):
        target[i, act[i]] = r[i] + (self.gamma * max_Q_dash[i]) * (not ep_end[i])
    loss = F.mean_squared_error(Q, Variable(target))
    return loss

Lors du calcul de la perte ici, il semble que l'apprentissage sera plus rapide si la différence entre la valeur Q et la cible est réduite de -1 à 1, mais je n'ai pas pu l'implémenter car je ne pouvais pas comprendre la théorie en raison d'un manque d'étude (je suis désolé ...

Action de retour

C'est la partie qui renvoie l'action à entreprendre lorsqu'elle est saisie selon la fonction Q apprise. La méthode de sélection d'action utilise ε-gourmand.

def get_action(self, st):
    if np.random.rand() < self.epsilon:
        return np.random.randint(0, self.n_act)
    else:
        s = Variable(st)
        Q = self.model.Q_func(s)
        Q = Q.data[0]
        a = np.argmax(Q)
        return np.asarray(a, dtype=np.int8)

Apprentissage avancé

C'est la partie pour procéder à l'apprentissage lorsque suffisamment de mémoire est accumulée. L'étape est cochée à chaque fois et la fonction Q de la cible est synchronisée à intervalles réguliers. De plus, après avoir terminé la recherche dans une certaine mesure, ε diminuera à chaque étape.

def reduce_epsilon(self):
    if self.epsilon > self.epsilon_min and self.exploration < self.step:
        self.epsilon -= self.epsilon_decay

def train(self):
    if len(self.memory) >= self.mem_size:
        if self.step % self.train_freq == 0:
            self.experience_replay()
            self.reduce_epsilon()
        if self.step % self.target_update_freq == 0:
            self.target_model = copy.deepcopy(self.model)
    self.step += 1

Partie exécution

J'ai essayé de faire en sorte que si vous entrez le nom de l'environnement du contrôle classique, il jugera le nombre d'états et le nombre d'actions sans autorisation C'est peut-être devenu un peu brouillon et difficile à comprendre ^^;

def main(env_name):
    env = gym.make(env_name)
    view_path = "./video/" + env_name

    n_st = env.observation_space.shape[0]
    if type(env.action_space) == gym.spaces.discrete.Discrete:
        # CartPole-v0, Acrobot-v0, MountainCar-v0
        n_act = env.action_space.n
        action_list = range(0, n_act)
    elif type(env.action_space) == gym.spaces.box.Box:
        # Pendulum-v0
        action_list = [np.array([a]) for a in [-2.0, 2.0]]
        n_act = len(action_list)

    agent = Agent(n_st, n_act, seed)

    env.monitor.start(view_path, video_callable=None, force=True, seed=seed)
    for i_episode in xrange(1000):
        observation = env.reset()
        for t in xrange(200):
            env.render()
            state = observation.astype(np.float32).reshape((1,n_st))
            act_i = agent.get_action(state)
            action = action_list[act_i]
            observation, reward, ep_end, _ = env.step(action)
            state_dash = observation.astype(np.float32).reshape((1,n_st))
            agent.stock_experience(state, act_i, reward, state_dash, ep_end)
            agent.train()
            if ep_end:
                break
    env.monitor.close()

Résultat d'exécution

J'ai téléchargé les résultats sur OpenAI Gym

Je pense qu'Acrobot et Pendulum sont de très bons résultats, mais Cart Pole est subtil. Il semble que les résultats varieront en fonction de la fréquence de mise à jour de la fonction Q pour la cible, de l'amplitude de l'atténuation ε et de la méthode d'optimisation. intéressant!

en conclusion

Je veux l'essayer avec les jeux Atari à l'avenir. A ce moment, il semble nécessaire d'envisager de couper la récompense. Dois-je envisager la normalisation et DropOut?

Recommended Posts

Défiez DQN (Modoki) avec Chainer ✕ Open AI Gym!
Résolvez OpenAI Gym Copy-v0 avec Sarsa
Ouvrez AI Gym pour apprendre avec le poteau de chariot contrôlé par PD
Seq2Seq (1) avec chainer
Créez un environnement Open AI Gym avec Bash sur Windows 10
Utiliser tensorboard avec Chainer
Apprentissage par renforcement dans les plus brefs délais avec Keras avec OpenAI Gym