Es scheint, dass die nächste Methode von R2D2 namens R2D3 angekündigt wurde. Ich war neugierig und habe es umgesetzt.
Der in diesem Artikel erstellte Code ist unten aufgeführt.
R2D3 ist eine sogenannte Verstärkungslernmethode der DQN-Serie. Die technischen Erklärungen bis zu diesem Punkt werden in der folgenden Reihe erläutert. Nehmen Sie Kontakt mit uns auf.
Mit der von Google DeepMind im September 2019 angekündigten erweiterten Lernmethode Grob gesagt ist es eine Methode, die R2D2 und DQfD kombiniert.
Grob gesagt lernt DQfD besser (DQN-Basis), indem es sich auf das Spiel (Demonstration) eines guten Menschen bezieht, und Letztendlich ist es eine Möglichkeit, eine bessere Leistung als eine Demonstration zu erlernen.
Übrigens scheint der nicht abgekürzte Name Recurrent Replay Distributed DQN from Demonstrations (R2D3) zu sein.
·Referenz
Das Obige ist die Gesamtansicht von R2D3. (* Zitiert aus dem Papier) Die rechte Seite der Figur (lila und blauer Teil) entspricht R2D2, der Unterschied ist der rote Teil auf der linken Seite.
Als Voraussetzung wird die Demo-Wiedergabe zunächst mit Referenzspieldaten im Voraus geliefert.
Bis R2D2 wurden die für das Training verwendeten Stapeldaten für die Stapelgröße basierend auf den Agentenwiedergabedaten erstellt. R2D3 erstellt diese Batchdaten aus der Demo-Wiedergabe und der Agentenwiedergabe gemäß dem Demo-Verhältnis.
In der Arbeit haben wir 1/16, 1/32, 1/64 1/128, 1/256 mit festen Werten verglichen. Es wurde festgestellt, dass 1/256 der genaueste Teil der Aufgabe ist.
Von hier an ist es meine Meinung, aber im eigentlichen menschlichen Sinne ist das Demo-Spiel zunächst hilfreich, aber wenn man sich daran gewöhnt, sieht man es nicht. In meiner Implementierung habe ich hier das Demo-Verhältnis implementiert, damit es geglüht werden kann. (Das Tempern entspricht dem festen Fall, wenn Sie die Einstellung ändern.)
Zunächst müssen wir die Daten für die Demonstration vorbereiten. Ich habe es unter Bezugnahme auf den von OpenAI unten bereitgestellten Code für das manuelle Spielen erstellt.
Die zu speichernde Datenstruktur ist zweigeteilt, eine zum Lernen und die andere zur Wiedergabe. Sie hat die folgende Form.
・ Zum Lernen (für jeden Frame speichern)
Name | Inhalt |
---|---|
action | Aktion |
observation | Status |
reward | Belohnung |
done | Ob es fertig ist |
・ Zur Wiedergabe (allgemeine Informationen)
Name | Inhalt |
---|---|
episode | Episodennummer |
rgb_size | Bildgröße |
states | Array jeder Frame-Information(Enthält die folgenden Informationen) |
・ Für die Wiedergabe (für jedes Bild speichern)
Name | Inhalt |
---|---|
step | Rahmennummer |
reward_total | Aktuelle Gesamtbelohnung |
info | Frame-Info-Informationen(gym) |
rgb | Bild |
(Der Code ist die Funktion add_memory in env_play.py.)
Wenn Sie Demo-Wiedergabedaten zum Speicher hinzufügen, müssen Sie die gleichen Schritte wie der eigentliche Agent ausführen, um sie zu speichern. (Referenz: [Erklärung zur Implementierung von [DQN (Rainbow)]](https://qiita.com/pocokhc/items/408f0f818140924ad4c4#dqnrainbow%E3%81%AE%E5%AE%9F%E8%A3%85%E8%A7 % A3% E8% AA% AC))
Es ist ein bisschen redundant, aber wir werden den gleichen Mechanismus separat erstellen und ihn dem Speicher hinzufügen. Unten ist der Ablauf mit Pseudocode. (Da es kompliziert ist, wird es im Fall von Stateful LSTM nicht beschrieben.)
add_memory
def add_memory(episode_file, memory, agent):
・ Folge_Holen Sie sich Demo-Play-Informationen aus der Datei
#Erstellen Sie Variablen für die empirische Datenerstellung
recent_actions =Array mit der Anzahl der zu speichernden Aktionen
recent_rewards =Eine Reihe von Belohnungen zum Speichern
recent_rewards_multistep =Zur mehrstufigen Berechnung
recent_observations =Eine Reihe von Situationen, die gespeichert werden sollen
für Schritt in Folge:
observation =Rahmeninformationen[step]["observation"]
action =Rahmeninformationen[step]["action"]
reward =Rahmeninformationen[step]["reward"]
#Status hinzufügen
recent_observations.pop(0)
recent_observations.append(observation)
#Erstellen Sie eine Erfahrung
exp = (
recent_observations[:agent.input_sequence], #Vorheriger Status
recent_actions[0], #Aktion im vorherigen Zustand
recent_rewards_multistep, #Belohnung
recent_observations[-agent.input_sequence:]) #Nächster Zustand
)
#Erfahrung in Erinnerung behalten
memory.add(exp)
#Fügen Sie Aktion und Belohnung hinzu
recent_actions.pop(0)
recent_actions.append(action)
recent_rewards.pop(0)
recent_rewards.append(reward)
recent_rewards_multistep =Mehrstufige Lernberechnung
(Der Code ist die EpisodeSave-Klasse in env_play.py)
Dies ist die Klasse, die Sie tatsächlich spielen. Es hat die folgenden Funktionen.
Der Bildschirm ist wie folgt.
Der auszuführende Code sieht wie folgt aus.
import gym
from src.env_play import EpisodeSave
def run_play():
env = gym.make("MountainCar-v0")
processor = None #Wenn ja, geben Sie es beliebig an
es = EpisodeSave(
env,
episode_save_dir="tmp",
processor=processor
)
es.play()
env.close()
run_play()
Die Schlüsselbindung des Spiels kann vom Prozessor festgelegt werden. Wenn der Prozessor über eine Methode get_keys_to_action verfügt, wird diese geladen.
get_keys_to_action
import rl
class MyProcessor(rl.core.Processor):
def get_keys_to_action(self):
return {
():0, #0, wenn nicht gedrückt
(ord('d'),):1, #d Taste ist 1
(ord('a'),):2, #Ein Schlüssel ist 2
}
(Der Code ist die EpisodeReplay-Klasse in env_play.py)
Ich habe auch einen Mechanismus zum Abspielen der von EpisodeSave gespeicherten Episode erstellt. Hauptsächlich zur Bestätigung.
from src.env_play import EpisodeReplay
def replay():
r = EpisodeReplay(episode_save_dir="tmp")
r.play()
replay()
Wie üblich werden wir es aus der Rainbow-Version implementieren, die ohne parallele Verarbeitung leicht zu verstehen ist.
Name | Inhalt |
---|---|
demo_memory | Speichertyp(Ähnlich wie beim Wiedergabespeicher) |
demo_episode_dir | Verzeichnispfad gespeichert durch Episode Speichern oben |
demo_ratio_initial | Anfangsrate der Demo |
demo_ratio_final | Demo-Endstatusrate |
demo_ratio_steps | Anzahl der Schritte, um die endgültige Rate zu erreichen |
demo_memory kann aus ReplayMemory, PERGreedyMemory, PERProportionalMemory, PERRankBaseMemory sowie [ReplayMemory] ausgewählt werden Ich kann es schaffen
rainbow
def __init__(self):
(Kürzung)
# add_Demo mit Speicherfunktion_Demo-Wiedergabe zum Speicher hinzugefügt
add_memory(demo_episode_dir, self.demo_memory, self)
# demo_Stellen Sie Variablen für das Verhältnisglühen ein
self.demo_ratio_initial = demo_ratio_initial
if demo_ratio_final is None:
self.demo_ratio_final = self.demo_ratio_initial
else:
self.demo_ratio_final = demo_ratio_final
self.demo_ratio_step = (self.demo_ratio_initial - self.demo_ratio_final) / demo_ratio_steps
(Kürzung)
rainbow
import random
def forward(self, observation):
#Es ist der Zeitpunkt zum Zeitpunkt des Lernens
(Kürzung)
#Berechnen Sie das Verhältnis von Demo-Verhältnis
ratio_demo = self.demo_ratio_initial - self.local_step * self.demo_ratio_step
if ratio_demo < self.demo_ratio_final:
ratio_demo = self.demo_ratio_final
#Berechnen Sie die Anzahl der Chargen anhand des Verhältnisses
batch_replay = 0
batch_demo = 0
for _ in range(self.batch_size):
r = random.random()
if r < ratio_demo:
batch_demo += 1
continue
batch_replay += 1
#Erstellen Sie einen Stapel basierend auf dem Verhältnis
indexes = []
batchs = []
weights = []
memory_types = [] #Speichern Sie den erfassten Speichertyp
if batch_replay > 0:
(i, b, w) = self.memory.sample(batch_replay, self.local_step)
indexes.extend(i)
batchs.extend(b)
weights.extend(w)
#0 ist Wiedergabe_memory
memory_types.extend([0 for _ in range(batch_replay)])
if batch_demo > 0:
(i, b, w) = self.demo_memory.sample(batch_demo, self.local_step)
indexes.extend(i)
batchs.extend(b)
weights.extend(w)
#1 ist Demo_memory
memory_types.extend([1 for _ in range(batch_demo)])
(Kürzung)
for i in range(self.batch_size):
(Lernen)
#Priorität aktualisieren
if memory_types[i] == 0:
# replay_Speicher aktualisieren
self.memory.update(indexes[i], batchs[i], priority)
elif memory_types[i] == 1:
# demo_Speicher aktualisieren
self.demo_memory.update(indexes[i], batchs[i], priority)
else:
assert False
(Kürzung)
Es kann genauso implementiert werden wie Rainbow. Implementieren Sie es einfach auf der Lernerseite.
EpisodeMemory
Dies ist meine eigene Implementierung. Ich kam auf die Idee, nachdem ich das Demo-Spiel gesehen hatte, aber wenn das Demo-Spiel sinnvoll ist, warum nicht beim Lernen? Ich dachte.
Insbesondere wird das Spiel mit der höchsten Gesamtbelohnung der Episode separat im Speicher gespeichert. Die Idee ist, dies auf die gleiche Weise wie diesmal zu einer Charge zu mischen.
Als Bild fühlt es sich an, als würde ich mich an das Stück erinnern, das zufällig erfolgreich war, und es viele Male wiederholen.
Unten ist die Implementierung.
Erstellen Sie ein EpisodeMemory, das ReplayMemory enthält. Dies ist eine Wrapper-Klasse, die ReplayMemory pro Episode mehr Erfahrung bietet.
EpisodeMemory
class EpisodeMemory():
def __init__(self, memory):
self.max_reward = None
self.memory = memory
def add_episode(self, episode, total_reward):
# max_Füge eine Episode zum Speicher hinzu, wenn die Belohnung aktualisiert wird
if self.max_reward is None:
self.max_reward = total_reward
elif self.max_reward <= total_reward: #Zum Speicher hinzufügen, auch wenn es sich in derselben Zeile befindet
self.max_reward = total_reward
else:
return
#Tatsächliche Speicher zusätzliche Verarbeitung
for e in episode_recent:
if len(e) == 5: #Verarbeitung bei Priorität
self.memory.add(e, e[4])
else:
self.memory.add(e)
Name | Inhalt |
---|---|
episode_memory | Speichertyp(Ähnlich wie beim Wiedergabespeicher) |
episode_ratio | Episodenerinnerungsrate |
rainbow
from src.memory.EpisodeMemory import EpisodeMemory
def __init__(self):
(Kürzung)
#Wrap in EpisodeMemory-Klasse
self.episode_memory = EpisodeMemory(episode_memory)
self.episode_ratio = episode_ratio
(Kürzung)
reset_states
#Wird zu Beginn der Episode aufgerufen
def reset_states(self):
(Kürzung)
#Zum Speichern von Episodenerfahrung
self.episode_exp = []
self.total_reward = 0
#Zur Überprüfung des Endstatus
self.recent_terminal = False
forward
#Wird vor der Ausführung der Aktion in jedem Schritt aufgerufen
def forward(self, observation):
(Kürzung)
#Wenn es fertig ist, Folge_Zum Speicher hinzufügen
if self.recent_terminal:
self.episode_memory.add_episode(self.episode_exp, self.total_reward)
(Kürzung)
exp =(Erfahrungsdaten erstellen)
self.memory.add(exp) # replay_Speicher hinzufügen
self.episode_exp.append(exp) # episode_Erfahrung für das Gedächtnis hinzufügen
(Kürzung)
backward
#Wird aufgerufen, nachdem in jedem Schritt eine Aktion ausgeführt wurde
def backward(self, reward, terminal):
(Kürzung)
#Berechnen Sie die Gesamterfahrung
self.total_reward += reward
#Exit-Status speichern
self.recent_terminal = terminal
(Kürzung)
rainbow
import random
def forward(self, observation):
#Es ist der Zeitpunkt zum Zeitpunkt des Lernens
(Kürzung)
ratio_demo =(Berechnung des Demoverhältnisses)
# episode_Wenn Speicher Speicher hat, mischen Sie ihn in einem Stapel
if len(self.episode_memory) < self.batch_size:
ratio_epi = 0
else:
ratio_epi = self.episode_ratio
#Berechnen Sie die Anzahl der Chargen anhand des Verhältnisses
batch_replay = 0
batch_demo = 0
batch_episode = 0
for _ in range(self.batch_size):
r = random.random()
if r < ratio_demo:
batch_demo += 1
continue
r -= ratio_demo
if r < ratio_epi:
batch_episode += 1
continue
batch_replay += 1
#Erstellen Sie einen Stapel basierend auf dem Verhältnis
indexes = []
batchs = []
weights = []
memory_types = [] #Speichern Sie den erfassten Speichertyp
if batch_replay > 0:
(replay_Stapelerstellung des Speichers)
if batch_demo > 0:
(demo_Stapelerstellung des Speichers)
if batch_episode > 0:
(i, b, w) = self.episode_memory.sample(batch_episode, self.local_step)
indexes.extend(i)
batchs.extend(b)
weights.extend(w)
# episode_Speicher ist 2
memory_types.extend([2 for _ in range(batch_episode)])
(Kürzung)
for i in range(self.batch_size):
(Lernen)
#Priorität aktualisieren
if memory_types[i] == 0:
(replay_Speicher aktualisieren)
elif memory_types[i] == 1:
(demo_Speicher aktualisieren)
elif memory_types[i] == 2:
# episode_Speicher aktualisieren
self.episode_memory.update(indexes[i], batchs[i], priority)
else:
assert False
(Kürzung)
Es ist fast das gleiche wie die Implementierung auf Rainbow. Es werden jedoch Episodendaten für jeden Schauspieler erstellt. Es wird auf der Lernerseite verwaltet, um den Kommunikationsaufwand zwischen Prozessen zu verringern.
Learner
class Learner():
def __init__():
(Kürzung)
#Erstellen Sie Variablen für das Episodenmanagement für jeden Schauspieler, indem Sie Learner initialisieren
self.episode_exp = [ [] for _ in range(self.actors_num)]
self.total_reward = [ 0 for _ in range(self.actors_num)]
def train(self):
(Kürzung)
#Schauspieler → Erfahrung zum Lernenden hinzufügen
for _ in range(self.exp_q.qsize()):
exp = self.exp_q.get(timeout=1)
# add memory
self.memory.add(exp[0], exp[0][4])
# add episode_exp
self.total_reward[exp[1]] += exp[0][2]
self.episode_exp[exp[1]].append(exp[0])
if exp[2]: # terminal
self.episode_memory.add_episode(
self.episode_exp[exp[1]],
self.total_reward[exp[1]]
)
self.episode_exp[exp[1]] = []
self.total_reward[exp[1]] = 0
(Kürzung)
Actor
class Actor():
def forward(self, observation):
(Kürzung)
#An den Lernenden senden
# actor_Übergeben Sie auch Index- und Terminalinformationen
self.exp_q.put((exp, self.actor_index, self.recent_terminal))
(Kürzung)
MountainCar
Dieses Mal werde ich es mit MountainCar versuchen.
Mountain Car ist ein Spiel, bei dem Sie das Auto nach links und rechts bewegen, um auf die Flagge oben rechts zu zielen.
Die Belohnung ist immer -1. Kurz gesagt, je früher Sie die Flagge erreichen, desto höher ist Ihre Punktzahl.
Wenn Sie es als Q-Lernen betrachten, ist es eine Aufgabe, die Sie erst belohnen können, wenn Sie das Ziel erreicht haben (es ist nicht bekannt, ob es gut oder schlecht ist), und es ist eine ziemlich schwierige Aufgabe.
Processor ist ein reines MountainCar-Training, das vom Fitnessstudio ohne Definition angeboten wird. Das Protokoll wird alle 2000 Schritte erfasst. (Dies ist die Operation in Rainbow)
Die ersten 50.000 Schritte werden aufgewärmt und anschließend 100.000 Mal trainiert. ·Ergebnis
Es ist schwer zu lernen, bis das Spiel, das das Ziel erreicht hat, zu einem gewissen Grad im Speicher gespeichert ist. Die Ergebnisse beginnen mit rund 130.000 Schritten.
Die anderen Parameter als der DemoReplay-Speicher sind dieselben wie die vorherigen Ergebnisse. Die Demo hat nur eine Episode unten vorbereitet.
Die Parameter des DemoReplay-Speichers sind wie folgt.
demo_memory = PERProportionalMemory(100_000, alpha=0.8)
demo_episode_dir = episode_save_dir
demo_ratio_initial = 1.0
demo_ratio_final = 1.0/512.0
demo_ratio_steps = warmup + 50_000
·Ergebnis
Die Ergebnisse liegen bereits bei rund 70.000.
Andere Parameter als EpisodeMemory sind dieselben wie die vorherigen Ergebnisse. Die Parameter von EpisodeMemory sind wie folgt.
episode_memory = PERProportionalMemory(2_000, alpha=0.8),
episode_ratio = 1.0/8.0,
·Ergebnis
Die Ergebnisse beginnen bei rund 80.000.
Sowohl DemoReplay-Speicher als auch EpisodeMemory sind gültig. (Die x-Achse enthält nicht die Anzahl der Aufwärmübungen.)
Es war einfacher zu implementieren als ich erwartet hatte. Ich denke, dass es eine sehr effektive Methode ist, da es beim Lernen nicht viele Aufgaben ohne Demo-Spiel gibt. Die Entwicklung des verstärkenden Lernens hat noch nicht aufgehört. Ich freue mich darauf zu sehen, was als nächstes kommt.
Recommended Posts