[PYTHON] Si tu es un homme, tais-toi et dis-lui de se lever avec un bâton

Récemment, je suis accro au renforcement de l'apprentissage. Après tout, c'est un homme qui veut tenir un bâton lors de l'apprentissage par renforcement. Donc, je présenterai le CartPole </ font> d'OpenAI Gym après la dernière fois.

Article précédent Je veux escalader une montagne avec un apprentissage amélioré

Qu'est-ce que la méthode d'apprentissage SARSA?

En ce qui concerne l'apprentissage Q mentionné dans l'article précédent, cette fois, j'aimerais utiliser une méthode appelée SARSA. Revoyons. Mise à jour de la valeur de comportement de l'état Q dans l'apprentissage par renforcement,

\begin{aligned}Q\left( s_{t},a_{t}\right) \\ \leftarrow Q\left( s_{t},a_{t}\right) \\ +\alpha \left( G_{t}-Q\left( s_{t},a_{t}\right) \right) \end{aligned}

Est effectué pour chaque transition d'état. La différence entre l'apprentissage SARSA et Q est de savoir comment déterminer ce $ G_ {t} $.

Pour ** Q learning ** </ font> $G_{t}=r_{t+1}+\gamma\max_{a\in At}[Q(s_{t+1},a)]$

Pour ** SARSA ** </ font> $G_{t}=r_{t+1}+\gamma Q(s_{t+1},a_{t+1}^{\pi})$

Ici, $ a_ {t + 1} ^ {\ pi} $ indique l'action lorsque l'action suivante est sélectionnée selon la politique dans l'état $ s_ {t + 1} $. Ce que nous pouvons voir de ce qui précède, c'est que Q learning utilise max pour mettre à jour la valeur, c'est-à-dire qu'il s'agit d'une méthode d'apprentissage ** optimiste ** qui se met à jour en utilisant la valeur d'état maximale pouvant être obtenue. En revanche, SARSA prend en compte les actions suivantes, ce qui en fait une manière plus ** réaliste ** de déterminer les stratégies. Cette fois, nous les comparerons également.

Règles du panier

スクリーンショット 2020-09-14 18.07.29.png Si vous maintenez ce bâton debout pendant une longue période (200 pas), il sera effacé. Il y a quatre états donnés, ** la position du chariot, la vitesse du chariot, l'angle du pôle et la vitesse angulaire du pôle **. Les actions se limitent à pousser le chariot vers la gauche: 0 et à pousser vers la droite: 1. L'angle du poteau est incliné de 12 degrés ou plus, ou l'endurance est de 200 pas.

la mise en oeuvre

Tout d'abord, importez la bibliothèque.

import gym
from gym import logger as gymlogger
gymlogger.set_level(40) #error only
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
import math
import glob
import io
import base64

Définit une classe SARSA qui implémente l'apprentissage.

class SARSA:
    def __init__(self, env):
        self.env = env
        self.env_low = self.env.observation_space.low #État minimum
        self.env_high = self.env.observation_space.high #État valeur maximale
        
        tmp = [7,7,7,7] #Divisez l'état en 7 états
        self.env_dx = [0,0,0,0]
        self.env_dx[0] = (self.env_high[0] - self.env_low[0]) / tmp[0]
        self.env_dx[1] = (self.env_high[1] - self.env_low[1]) / tmp[1]
        self.env_dx[2] = (self.env_high[2] - self.env_low[2]) / tmp[2]
        self.env_dx[3] = (self.env_high[3] - self.env_low[3]) / tmp[3]
        
        self.q_table = np.zeros((tmp[0],tmp[1],tmp[2],tmp[3],2)) #Initialisation de la fonction de valeur d'état
        
    def get_status(self, _observation): #État discriminatoire
        
        s1 = int((_observation[0] - self.env_low[0])/self.env_dx[0]) #Passez dans l'un des sept états
        
        if _observation[1] < -1.5: #Classer par vous-même
          s2 = 0
        elif -1.5 <= _observation[1] < - 1:
          s2 = 1
        elif -1 <= _observation[1] < -0.5:
          s2 = 2
        elif -0.5 <= _observation[1] < 0.5:
          s2 = 3
        elif 0.5 <= _observation[1] < 1.5:
          s2 = 4
        elif 1.5 <= _observation[1] < 2:
          s2 = 5
        elif 2 <= _observation[1]:
          s2 = 6
        
        s3 = int((_observation[2] - self.env_low[2])/self.env_dx[2]) #Passez dans l'un des sept états
        
        if _observation[3] < -1: #Classer par vous-même
          s4 = 0
        elif -1 <= _observation[3] < -0.7:
          s4 = 1
        elif -0.7 <= _observation[3] < -0.6:
          s4 = 2
        elif -0.6 <= _observation[3] < -0.5:
          s4 = 3
        elif -0.5 <= _observation[3] < -0.4:
          s4 = 4
        elif -0.4 <= _observation[3] < -0.4:
          s4 = 5
        else:
          s4 = 6
          
        return s1, s2, s3, s4
    
    def policy(self, s, epi): #Sélectionnez une action dans les états
        
        epsilon = 0.5 * (1 / (epi + 1))
        
        if np.random.random() <= epsilon:
            return np.random.randint(2) #Choisissez au hasard
        else:
            s1, s2, s3, s4 = self.get_status(s)
            return np.argmax(self.q_table[s1][s2][s3][s4]) #Sélectionnez l'action avec la valeur d'action la plus élevée
    
    def learn(self, time = 200, alpha = 0.5, gamma = 0.99): #Apprenez autant de fois que le temps
        
        log = [] #Enregistrez la récompense totale pour chaque épisode
        t_log = [] #Enregistrez le nombre de pas par épisode
        
        for j in range(time+1):
            t = 0 #Nombre d'étapes
            total = 0 #Récompense totale
            s = self.env.reset()
            done = False
            
            while not done:
                t += 1
                a = self.policy(s, j)
                next_s, reward, done, _ = self.env.step(a)
                
                reward = t/10 #Plus vous endurez longtemps, plus vous obtenez de récompenses
                
                if done:
                  if t < 195:
                    reward -= 1000 #Pénalités pour non-endurance
                  else:
                    reward = 1000 #Donnez plus de récompenses en cas de succès

                total += reward
                
                
                s1, s2, s3, s4 = self.get_status(next_s)
                G = reward + gamma * self.q_table[s1][s2][s3][s4][self.policy(next_s, j)] #Calcul de la récompense cumulée
                
                s1, s2, s3, s4 = self.get_status(s)
                self.q_table[s1][s2][s3][s4][a] += alpha*(G - self.q_table[s1][s2][s3][s4][a]) #Mise à jour Q
                s = next_s

            t_log.append(t)
            log.append(total)
            
            if j %1000 == 0:
              print(str(j) + " ===total reward=== : " + str(total))
            
        return plt.plot(t_log)

    def show(self): #Afficher les résultats d'apprentissage
        s = self.env.reset()
        img = self.env.render()
        done = False
        t = 0
        while not done:
          t += 1
          a = self.policy(s, 10000)
          s, _, done, _ = self.env.step(a)
          self.env.render()
                
        print(t)
        self.env.reset()
        self.env.close()

Point de problème

Voilà où j'ai trébuché. Chez init, je me prépare à disperser chacun des quatre états avec env_dx, mais j'ai un problème ici. Si vous regardez attentivement la référence, スクリーンショット 2020-09-14 18.34.59.png La zone variable de la valeur de vitesse est ** inf **. Oui, ** c'est infini! **

Dans ce cas, la valeur de env_dx devient également infinie et la dispersion des valeurs continues ne fonctionne pas. Donc,

from random import random
env.step(random.randint(2))

Nous avons effectué plusieurs fois et observé des variations de la vitesse du chariot puis de la vitesse angulaire de la perche. Puis,

if _observation[1] < -1.5: #La vitesse du chariot
          s2 = 0
        elif -1.5 <= _observation[1] < - 1:
          s2 = 1
        elif -1 <= _observation[1] < -0.5:
          s2 = 2
        elif -0.5 <= _observation[1] < 0.5:
          s2 = 3
        elif 0.5 <= _observation[1] < 1.5:
          s2 = 4
        elif 1.5 <= _observation[1] < 2:
          s2 = 5
        elif 2 <= _observation[1]:
          s2 = 6

        if _observation[3] < -1: #Vitesse angulaire du pôle
          s4 = 0
        elif -1 <= _observation[3] < -0.7:
          s4 = 1
        elif -0.7 <= _observation[3] < -0.6:
          s4 = 2
        elif -0.6 <= _observation[3] < -0.5:
          s4 = 3
        elif -0.5 <= _observation[3] < -0.4:
          s4 = 4
        elif -0.4 <= _observation[3] < -0.4:
          s4 = 5
        else:
          s4 = 6

J'ai réalisé que je pouvais le classer comme ça.

Apprentissage

Apprenez comme ça. C'est environ 3000 fois et je peux me le permettre.

env = gym.make('CartPole-v0')
agent = SARSA(env)
agent.learn(time = 3000)

Le changement du nombre d'étapes est comme ça. スクリーンショット 2020-09-14 18.44.03.png

Maintenant, vérifions l'animation avec ```agent.show () `` `. 棒を立てる.gif

C'est assez stable et a une grande durabilité. Je suis heureux d'être un ** homme **.

Apprentissage Q vs SARSA

Comparons l'apprentissage Q et SARSA dans cet environnement. Apprentissage G en Q

G = reward + gamma * max(self.q_table[s1][s2][s3][s4])

Faire. Quand j'essaye d'apprendre avec ça, スクリーンショット 2020-09-14 18.48.59.png La stabilité de la convergence semble meilleure avec SARSA. Si vous êtes un homme, regardez la réalité comme SARSA. Oui.

Impressions

Dans cet environnement, je me suis demandé si la dispersion des États était la partie la plus difficile. Il semble que DQN soit né pour résoudre ce problème. La prochaine fois, je pense que je vais essayer de construire un DQN. à plus!