Zuvor implementiertes R2D2, aber Mini-Batch-Lernen konnte nicht implementiert werden. Danach habe ich es dieses Mal durch Ausprobieren geschafft.
Es ist lange her seit dem vorherigen Artikel, daher werde ich den Gesamtfluss grob erklären. Wir werden auch alle Fehler in der vorherigen Implementierung korrigieren. .. ..
Darüber hinaus besteht dieser Artikel aus zwei Teilen, einem Kommentarbereich und einem Abschnitt zur Einstellung von Hyperparametern. Siehe unten für Hyperparameter [Lernen stärken] R2D2-Implementierung / Erklärung Rache Hyperparameter Erklärung (Keras-RL)
Nachtrag: R2D3 wurde ebenfalls implementiert. [Lernen stärken] Ich habe R2D3 (Keras-RL) implementiert / erklärt
Der in diesem Artikel erstellte Code ist unten aufgeführt. Diesmal nur Github.
Als Rückblick werde ich das Bild der Implementierung von DQN (Rainbow) noch einmal erläutern. Eine ausführliche Erklärung finden Sie im zuvor veröffentlichten Artikel.
Das Folgende ist eine Zusammenfassung der Bilder des Lernens mit DQN (Rainbow).
DQN speichert Erfahrungsdaten (Erfahrung) wie folgt im Speicher.
Wenn der Schritt des mehrstufigen Lernens 1 ist, ist der nächste Zustand $ t + 1 $, Wenn es 3 Schritte sind, ist es $ t + 3 $.
Formel | Zahl | |
---|---|---|
Vorheriger Status | observation: t(n-6) ~ t(n-3) | |
Nächster Zustand | observation: t(n-3) ~ t(n) | |
Aktion | action: t(n-3) | |
Belohnung | reward: t(n) |
Darüber hinaus ist die Größe in jeder Variablen wie folgt.
Länge zu halten | Länge zum Speichern im Speicher | |
---|---|---|
rewards | multisteps | 0(Wird nur für Berechnungen verwendet) |
Berechnete Belohnungen | 1 | 1(Aktuellen Zustand) |
actions | multisteps + 1 | 1(Vorheriger Status) |
observations | input_sequence + multisteps | input_sequence + multisteps |
Im vorherigen Artikel Mehrstufiges Lernen habe ich mich auf die Aktion mit $ t_n $ bezogen, was falsch ist. Du ... $ T_ {n-Multisteps} $ war die richtige Antwort, da sie sich auf die Aktion im vorherigen Status bezieht.
Der vorherige Artikel ist unten.
Um es einfach auszudrücken: Wichtige Stichproben werden beim Abrufen von Erfahrungen mit Priority Experience Reply priorisiert. Dann wird die Anzahl der gesammelten Erfahrungen verzerrt. Dann wird die Verzerrung das Lernen beeinflussen, daher ist es wichtig, dies zu korrigieren.
Insbesondere hat eine mit hoher Wahrscheinlichkeit ausgewählte Erfahrung eine niedrige Reflexionsrate zum Aktualisieren des Q-Werts, und eine mit niedriger Wahrscheinlichkeit ausgewählte Erfahrung hat eine hohe Reflexionsrate zum Aktualisieren des Q-Werts.
In der Vergangenheit schien die Implementierung etwas seltsam zu sein und sie lernte nicht gut. Zuvor wurde es auf den aktualisierten Q-Wert selbst angewendet, sollte jedoch auf td_error selbst angewendet werden. (Die Benennung der Variablen war nicht gut.) Da dies in der Aktualisierung des Q-Werts berücksichtigt wird, wird es auch nicht auf die Priorität angewendet.
-Vorherige Implementierung (Pseudocode)
IS
def train():
#Holen Sie sich Erfahrung von PER nach Wahrscheinlichkeit
batchs, batch_weight = memory.sample(batch_size)
#Ruft den Q-Wert des vorherigen Status aus dem Modell ab
# state0_qvals enthält den Q-Wert für jede Aktion
state0_qvals = model.predict(state0_batch)
for batch_i in range(batch_size):
reward = batchs[batch_i]Belohnung
action = batchs[batch_i]Aktion
q0 = state0_qvals[batch_i][action] #Q-Wert vor dem Update
#Modell und Ziel_Ermitteln Sie den maximalen Q-Wert des aktuellen Status mithilfe des Modells
# (Die Erfassungsmethode unterscheidet sich zwischen DQN und DDQN)
maxq =Modell und Ziel_Holen Sie sich vom Modell
td_error = reward + (gamma ** reward_multisteps) * maxq
td_error *= batch_weight
priority = abs(td_error - q0)
#Lernen Sie, indem Sie nur den Q-Wert der Zielaktion ändern
state0_qvals[batch_i][action] = td_error
# train
model.train_on_batch(state0_qvals)
-Implementierung nach Änderung (Pseudocode)
IS
def train():
#Holen Sie sich Erfahrung von PER nach Wahrscheinlichkeit
batchs, batch_weight = memory.sample(batch_size)
#Ruft den Q-Wert des vorherigen Status aus dem Modell ab
# state0_qvals enthält den Q-Wert für jede Aktion
state0_qvals = model.predict(state0_batch)
for batch_i in range(batch_size):
reward = batchs[batch_i]Belohnung
action = batchs[batch_i]Aktion
q0 = state0_qvals[batch_i][action] #Q-Wert vor dem Update
#Modell und Ziel_Ermitteln Sie den maximalen Q-Wert des aktuellen Status mithilfe des Modells
# (Die Erfassungsmethode unterscheidet sich zwischen DQN und DDQN)
maxq =Modell und Ziel_Holen Sie sich vom Modell
#※ -Addiere q0 und td richtig_Geben Sie einen Fehler aus
#* Auch Charge_Tragen Sie hier Gewicht auf
td_error = reward + (gamma ** reward_multisteps) * maxq - q0
#※ td_Der absolute Fehlerwert wird so wie er ist Priorität
priority = abs(td_error)
#Lernen Sie, indem Sie nur den Q-Wert der Zielaktion ändern
#※ td_Da der Fehler zu einem Unterschied wurde, wenden Sie Gewicht an und aktualisieren Sie den Q-Wert mit dem Unterschied.
state0_qvals[batch_i][action] += td_error * batch_weight
# train
model.train_on_batch(state0_qvals)
Bisher konnte das Lernen von Mini-Bienen nicht implementiert werden, da Keras 'zustandsbehaftetes LSTM nicht gut verstanden wurde. Die vorherigen Forschungsartikel lauten wie folgt.
Anscheinend gibt es in hidden_states Zustände im Wert von batch_size, und Sie können sie angeben. Jetzt können Sie mehrere Trainings gleichzeitig zwischen Sequenzen durchführen.
DRQN(R2D2) Um die Geschichte verständlicher zu machen, werde ich mit R2D2 erklären, bei dem der Parallelverarbeitungsteil entfernt wurde. Der vorherige Artikel ist unten.
Es ist ein Bilddiagramm wie DQN.
Es ist ziemlich kompliziert ... Ich habe diese Abbildung geschrieben, weil ich bei der Implementierung verwirrt war ...
Die Methode zum Aktualisieren des Q-Werts und zum Ausgeben der Priorität ist dieselbe wie bei DQN, daher wird sie in der Abbildung weggelassen.
Die Punkte sind Eingabesequenz und Eingabelänge. Das letzte Mal war mir das nicht bewusst. (Unter der Annahme, dass die Eingabesequenz = 1 ist, wurde die Eingabelänge als Eingabesequenz ausgedrückt.)
Die Eingabesequenz ist die Länge des Zustands, der in das Modell eingegeben werden soll, und die Anzahl der Eingaben ist die Eingabelänge. Der Q-Wert wird für jede Eingabelänge aktualisiert und die Priorität wird ebenfalls berechnet. (Ich bin mir über diese Interpretation ein wenig unsicher, aber in Abschnitt 2.3 des R2D2-Papiers habe ich einen neuen Weg vorgeschlagen, um die Priorität festzulegen, und es ist sinnvoll zu glauben, dass eine Erfahrung mehrere Prioritäten gibt, wie oben beschrieben.)
Die in jeder Variablen enthaltene Größe ist wie folgt.
Länge zu halten | Länge zum Speichern im Speicher | |
---|---|---|
rewards | multisteps + input_length - 1 | 0(Wird nur für Berechnungen verwendet) |
Berechnete Belohnungen | input_length | input_length |
actions | multisteps + input_length | input_length(Aus dem vorherigen Zustand) |
hidden states | burnin + multisteps + input_length + 1 | 1(Ältester Staat) |
observations | burnin + input_sequence + multisteps + input_length - 1 | 0(Zur Zusammenfassung unten) |
Zusammenfassende Beobachtungen | burnin + multisteps + input_length | Die gleiche Länge |
Die Neuskalierungsfunktion wurde in R2D2 eingeführt und sollte anstelle des Belohnungsbeschneidens (-1 bis 1) verwendet werden. Früher habe ich mir Sorgen um die Umkehrfunktion gemacht, aber ich habe sie gewaltsam unnötig gemacht.
Die Formel zum Ableiten des TD-Fehlers unter Verwendung der Neuskalierungsfunktion lautet wie folgt. ($ y_t $ ist der TD-Fehler)
Erweitern Sie $ h () $ in der obigen Formel.
Durch Anwenden der Umkehrfunktion auf eine Funktion wird der ursprüngliche Wert wiederhergestellt. * $ H (h ^ {-1} (x)) = x $ Die rechte Seite kann also versetzt werden ($ \ gamma $ wird als Fehler ignoriert ...)
Dann wird es wie folgt.
Die Neuskalierungsfunktion wird jetzt nur auf Belohnungen angewendet ($ r_ {t} $). Wenn Sie sich die Grafik ansehen, können Sie sehen, dass die Belohnungen gut gerundet sind. (100 Belohnungen sind ungefähr 10) Es ist eine gute Alternative zum Ausschneiden.
Der vorherige Artikel ist unten.
Referenz: Vollständiges Verständnis von Python-Threading und Multiprocessing
Zuerst habe ich Queue verwendet, aber da die Menge der Gewichtsdaten groß war und es sich um einen Engpass handelte, habe ich die Kommunikation zwischen den einzelnen Prozessen untersucht. Die Umfrageergebnisse sind die folgenden Artikel.
Daraus ergibt sich folgende Kommunikation. (Infolgedessen wird die Warteschlange so verwendet, wie sie ist.)
Der Informationsaustausch zwischen Prozessen wird im gemeinsamen Speicher implementiert. Es gibt keine besondere Sperre, da der Schreiber und der Leser klar voneinander getrennt sind.
Callbacks
Es stellt sich heraus, dass die Kommunikation zwischen Prozessen ziemlich kostspielig ist Ich habe es implementiert, weil es einen Prozess gab, der Schauspieler und Leaner überspannte.
Ich erstelle es hauptsächlich zum Speichern / Laden und für Protokolle. Die Basisklasse des implementierten Rückrufs lautet wie folgt.
R2D2Callback
import rl.callbacks
class R2D2Callback(rl.callbacks.Callback):
def __init__(self):
pass
#--- train ---
def on_r2d2_train_begin(self):
pass
def on_r2d2_train_end(self):
pass
#--- learner ---
def on_r2d2_learner_begin(self, learner):
pass
def on_r2d2_learner_end(self, learner):
pass
def on_r2d2_learner_train_begin(self, learner):
pass
def on_r2d2_learner_train_end(self, learner):
pass
#--- actor ---
#Unten und rl.callbacks.Rückrufvererbungsmethode
def on_r2d2_actor_begin(self, actor_index, runner):
pass
def on_r2d2_actor_end(self, actor_index, runner):
pass
Wie Sie sehen können, erbt es von Keras-rls Rückruf. Es wird unverändert vom Agenten verwendet.
Beachten Sie, dass Zug, Lernender und Schauspieler von einem anderen Prozess angerufen werden sollen. Selbst wenn Sie einen Prozess schreiben, der diese überspannt, wird der Wert daher nicht beibehalten, da der Prozess anders ist.
Das Speichern / Laden und Protokollieren mit diesen wird im Parameterabschnitt erläutert.
GPU
Wenn ich die GPU wie bei Tensorflow 2.1.0 ausführe, wird der folgende Fehler angezeigt.
tensorflow.python.framework.errors_impl.InternalError: Blas GEMM launch failed : a.shape=(32, 12), b.shape=(12, 128), m=32, n=128, k=12
Anscheinend ist es ein Fehler, der auftritt, wenn die GPU in mehreren Prozessen verwendet wird. Lesen Sie die folgenden Informationen und stellen Sie die Verwendung der GPU in mehreren Prozessen ein.
#Ich möchte, dass Sie es für alle Prozesse festlegen, damit es global beschrieben wird
for device in tf.config.experimental.list_physical_devices('GPU'):
tf.config.experimental.set_memory_growth(device, True)
Außerdem schreibe ich in R2D2Manager einen Prozess, um automatisch zu bestimmen, ob es sich um eine CPU oder eine GPU handelt.
import tensorflow as tf
def train(self):
(Kürzung)
if len(tf.config.experimental.list_physical_devices('GPU')) > 0:
self.enable_GPU = True
else:
self.enable_GPU = False
(Kürzung)
Die Bildverarbeitungsschicht in NN (Neural Network) ist gegenüber DQN unverändert. Also habe ich es erweitert, damit ich es hier ändern kann.
Die NN-Schicht in DQN ist wie folgt.
Schicht | Überblick | |
---|---|---|
1 | Eingabeebene | |
2 | Eingabekonvertierungsschicht | Ebene zur Verallgemeinerung des Eingabeformats |
3 | Bildverarbeitungsschicht | Zur Bildverarbeitung |
4 | LSTM-Schicht | Bei Verwendung von LSTM |
5 | Duell Netzwerkschicht | Bei Verwendung eines Duell-Netzwerks |
6 | Dichte Schicht | Bei Verwendung eines Duell-Netzwerks enthalten |
7 | (Ausgabeschicht) | Eigentlich in der Duell-Netzwerkschicht enthalten |
Die Eingabekonvertierungsebene ist eine Ebene, die eine eindimensionale Ausgabe (Abflachen) für das Eingabeformat erstellt. Es wird unter der Annahme der folgenden vier Eingabetypen erstellt.
InputType
import enum
class InputType(enum.Enum):
VALUES = 1 #Kein Bild
GRAY_2ch = 3 # (width, height)
GRAY_3ch = 4 # (width, height, 1)
COLOR = 5 # (width, height, ch)
Einfach abflachen.
input_sequence = 4
input_shape = 3
c = Input(shape=(input_sequence,) + input_shape)
# output_shape == (None, 4, 3)
c = Flatten()(c)
# output_shape == (None, 12)
Flache es einfach so wie es ist. Eingehüllt in TimeDistributed, um Zeitschritte zu halten.
batch_size = 16
input_sequence = 4
input_shape = 3
c = Input(batch_shape=(batch_size, input_sequence,) + input_shape)
# output_shape == (16, 4, 3)
c = TimeDistributed(Flatten())(c)
# output_shape == (16, 4, 3)
Es ist die in DQN verwendete Konvertierung. Ersetzen Sie den Kanal durch input_sequence (Eingangsgröße).
input_sequence = 4
input_shape = (84, 84) #(widht, height)
c = Input(shape=(input_sequence,) + input_shape)
# output_shape == (None, 4, 84, 84)
c = Permute((2, 3, 1))(c) #Ebene zum Ändern der Reihenfolge
# output_shape == (None, 84, 84, 4)
c =Bildverarbeitungsschicht(c)
Wenn LSTM aktiviert ist, können Sequenzinformationen durch Zeitschritte ergänzt werden. Wir erhöhen die Kanalschicht.
batch_size = 16
input_sequence = 4
input_shape = (84, 84) #(widht, height)
c = Input(batch_shape=(batch_size, input_sequence,) + input_shape)
# output_shape == (16, 4, 84, 84)
c = Reshape((input_sequence, ) + input_shape + (1,) )(c) #Kanalebene hinzufügen
# output_shape == (16, 4, 84, 84, 1)
c =Bildverarbeitungsschicht(c)
Übergeben Sie es so wie es ist an die Bildverarbeitungsebene. Die Informationen von input_sequence können jedoch nicht ausgedrückt werden.
input_sequence = 4
input_shape = (84, 84, 3) #(widht, height, channel)
c = Input(shape=input_shape)
# output_shape == (None, 84, 84, 3)
c =Bildverarbeitungsschicht(c)
Es gibt keinen Unterschied.
batch_size = 16
input_sequence = 4
input_shape = (84, 84, 3) #(widht, height, channel)
c = Input(batch_shape=(batch_size, input_sequence,) + input_shape)
# output_shape == (16, 4, 84, 84, 3)
c =Bildverarbeitungsschicht(c)
Die ImageModel-Klasse ist so definiert, dass die Ebene geändert werden kann.
Das Argument c von create_image_model wird im folgenden Format übergeben.
Ohne LSTM: Form(batch_size, width, height, channel)
Mit LSTM: Form(batch_size, timesteps, width, height, channel)
Der Rückgabewert sollte das folgende Format haben:
Ohne LSTM: Form(batch_size, dim)
Mit LSTM: Form(batch_size, timesteps, dim)
Das Folgende ist ein Beispiel für das DQN-Format.
DQNImageModel
class DQNImageModel(ImageModel):
""" native dqn image model
https://arxiv.org/abs/1312.5602
"""
def create_image_model(self, c, enable_lstm):
"""
c shape(batch_size, width, height, channel)
return shape(batch_size, dim)
"""
if enable_lstm:
c = TimeDistributed(Conv2D(32, (8, 8), strides=(4, 4), padding="same"), name="c1")(c)
c = Activation("relu")(c)
c = TimeDistributed(Conv2D(64, (4, 4), strides=(2, 2), padding="same"), name="c2")(c)
c = Activation("relu")(c)
c = TimeDistributed(Conv2D(64, (3, 3), strides=(1, 1), padding="same"), name="c3")(c)
c = Activation("relu")(c)
c = TimeDistributed(Flatten())(c)
else:
c = Conv2D(32, (8, 8), strides=(4, 4), padding="same", name="c1")(c)
c = Activation("relu")(c)
c = Conv2D(64, (4, 4), strides=(2, 2), padding="same", name="c2")(c)
c = Activation("relu")(c)
c = Conv2D(64, (3, 3), strides=(1, 1), padding="same", name="c3")(c)
c = Activation("relu")(c)
c = Flatten()(c)
return c
Der vorherige Kommentarartikel ist unten.
DQN verwendet nur die Suchrichtlinie für die ε-gierige Methode. Im obigen Artikel wurden jedoch einige Richtlinien eingeführt. Ich habe sie implementiert, damit sie verwendet werden können, aber es scheint, dass ε-gierig genug ist. Details werden im Parameterabschnitt erläutert.
Ich habe es vorerst implementiert. Das nächste Mal möchte ich einen Beispielartikel zum Einstellen der einzelnen Parameter erstellen.
Recommended Posts