Flappy Bird schulen: Ein Tutorial zum verstärkenden Lernen
Veröffentlicht: 2022-03-11Bei der klassischen Programmierung werden Softwareanweisungen explizit von Programmierern gemacht und es wird überhaupt nichts aus den Daten gelernt. Maschinelles Lernen hingegen ist ein Gebiet der Informatik, das Computer mit statistischen Methoden in die Lage versetzt, zu lernen und Wissen aus den Daten zu extrahieren, ohne explizit programmiert zu werden.
In diesem Tutorial zum Reinforcement Learning zeige ich, wie wir PyTorch verwenden können, um einem neuronalen Netzwerk für Reinforcement Learning beizubringen, wie man Flappy Bird spielt. Aber zuerst müssen wir eine Reihe von Bausteinen abdecken.
Machine-Learning-Algorithmen lassen sich grob in zwei Teile unterteilen: Traditionelle Lernalgorithmen und Deep-Learning-Algorithmen. Herkömmliche Lernalgorithmen haben normalerweise viel weniger lernbare Parameter als Deep-Learning-Algorithmen und haben viel weniger Lernkapazität.
Außerdem sind herkömmliche Lernalgorithmen nicht in der Lage, Merkmale zu extrahieren : Spezialisten für künstliche Intelligenz müssen eine gute Datendarstellung finden, die dann an den Lernalgorithmus gesendet wird. Beispiele für herkömmliche Techniken des maschinellen Lernens sind SVM, Random Forest, Decision Tree und $k$-means, während der zentrale Algorithmus beim Deep Learning das Deep Neural Network ist .
Die Eingabe in ein tiefes neuronales Netzwerk können Rohbilder sein, und ein Spezialist für künstliche Intelligenz muss keine Datenrepräsentation finden – das neuronale Netzwerk findet die beste Repräsentation während des Trainingsprozesses.
Viele Deep-Learning-Techniken sind schon seit sehr langer Zeit bekannt, aber die jüngsten Fortschritte in der Hardware haben die Deep-Learning-Forschung und -Entwicklung schnell vorangetrieben. Nvidia ist für die Erweiterung des Feldes verantwortlich, da seine GPUs schnelle Deep-Learning-Experimente ermöglicht haben.
Lernbare Parameter und Hyperparameter
Algorithmen für maschinelles Lernen bestehen aus lernbaren Parametern, die im Trainingsprozess abgestimmt werden, und nicht lernbaren Parametern, die vor dem Trainingsprozess festgelegt werden. Vor dem Lernen festgelegte Parameter werden als Hyperparameter bezeichnet.
Die Gittersuche ist eine gängige Methode zum Finden der optimalen Hyperparameter. Es ist eine Brute-Force-Methode: Es bedeutet, alle möglichen Kombinationen von Hyperparametern über einen definierten Bereich auszuprobieren und die Kombination auszuwählen, die eine vordefinierte Metrik maximiert.
Beaufsichtigte, unbeaufsichtigte und bestärkende Lernalgorithmen
Eine Möglichkeit, Lernalgorithmen zu klassifizieren, besteht darin, eine Grenze zwischen überwachten und nicht überwachten Algorithmen zu ziehen. (Aber das ist nicht unbedingt so einfach: Reinforcement Learning liegt irgendwo zwischen diesen beiden Typen.)
Wenn wir über überwachtes Lernen sprechen, betrachten wir $ (x_i, y_i) $-Paare. $ x_i $ ist die Eingabe des Algorithmus und $ y_i $ ist die Ausgabe. Unsere Aufgabe ist es, eine Funktion zu finden, die die korrekte Zuordnung von $ x_i $ zu $ y_i $ durchführt.
Um lernbare Parameter so abzustimmen, dass sie eine Funktion definieren, die $ x_i $ auf $ y_i $ abbildet, müssen eine Verlustfunktion und ein Optimierer definiert werden. Ein Optimierer minimiert die Verlustfunktion. Ein Beispiel für eine Verlustfunktion ist der mittlere quadratische Fehler (MSE):
\[MSE = \sum_{i=1}^{n} (y_i - \widehat{y_i} )^2\]Hier ist $ y_i $ ein Ground-Truth-Label und $ \widehat{y_i} $ ein vorhergesagtes Label. Ein beim Deep Learning sehr beliebter Optimierer ist der stochastische Gradientenabstieg . Es gibt viele Variationen, die versuchen, die Methode des stochastischen Gradientenabstiegs zu verbessern: Adam, Adadelta, Adagrad und so weiter.
Unüberwachte Algorithmen versuchen, Strukturen in den Daten zu finden, ohne explizit mit Labels versehen zu werden. $k$-means ist eines der Beispiele für unüberwachte Algorithmen, die versuchen, optimale Cluster in den Daten zu finden. Unten ist ein Bild mit 300 Datenpunkten. $k$-bedeutet, dass Algorithmen die Struktur in den Daten gefunden und jedem Datenpunkt ein Cluster-Label zugewiesen haben. Jeder Cluster hat seine eigene Farbe.
Reinforcement Learning verwendet Belohnungen: Sparse, zeitverzögerte Labels. Ein Agent ergreift eine Aktion, die die Umgebung verändert, aus der er eine neue Beobachtung und Belohnung erhalten kann. Eine Beobachtung ist der Reiz, den ein Agent aus der Umgebung wahrnimmt. Es kann das sein, was der Agent sieht, hört, riecht und so weiter.
Der Agent erhält eine Belohnung, wenn er eine Aktion ausführt. Es sagt dem Agenten, wie gut die Aktion ist. Durch die Wahrnehmung von Beobachtungen und Belohnungen lernt ein Agent, sich in der Umgebung optimal zu verhalten. Darauf gehe ich weiter unten näher ein.
Aktives, passives und inverses Verstärkungslernen
Es gibt ein paar verschiedene Ansätze für diese Technik. Da ist zunächst das aktive Reinforcement Learning, das wir hier verwenden. Im Gegensatz dazu gibt es passives Verstärkungslernen, bei dem Belohnungen lediglich eine andere Art der Beobachtung sind und Entscheidungen stattdessen nach einer festgelegten Richtlinie getroffen werden.
Schließlich versucht das inverse Reinforcement Learning, eine Belohnungsfunktion zu rekonstruieren, die die Geschichte der Aktionen und ihrer Belohnungen in verschiedenen Zuständen berücksichtigt.
Generalisierung, Overfitting und Underfitting
Jede feste Instanz von Parametern und Hyperparametern wird als Modell bezeichnet. Machine-Learning-Experimente bestehen in der Regel aus zwei Teilen: Training und Test.
Während des Trainingsprozesses werden lernbare Parameter unter Verwendung von Trainingsdaten abgestimmt. Im Testprozess werden lernbare Parameter eingefroren und es gilt zu prüfen, wie gut das Modell Vorhersagen zu bisher ungesehenen Daten trifft. Generalisierung ist die Fähigkeit einer lernenden Maschine, ein neues, noch nie gesehenes Beispiel oder eine neue Aufgabe genau auszuführen, nachdem sie einen Lerndatensatz erlebt hat.
Wenn ein Modell in Bezug auf die Daten zu einfach ist, kann es nicht an die Trainingsdaten angepasst werden und sowohl auf dem Trainingsdatensatz als auch auf dem Testdatensatz eine schlechte Leistung erbringen. In diesem Fall sprechen wir von Underfitting des Modells .
Wenn ein maschinelles Lernmodell bei einem Trainings-Dataset gut, aber bei einem Test-Dataset schlecht abschneidet, sprechen wir von Overfitting . Überanpassung ist die Situation, in der ein Modell in Bezug auf die Daten zu komplex ist. Es kann Trainingsdaten perfekt anpassen, ist aber so sehr an den Trainingsdatensatz angepasst, dass es bei Testdaten schlecht abschneidet – dh es lässt sich einfach nicht verallgemeinern.
Unten sehen Sie ein Bild, das Underfitting und Overfitting im Vergleich zu einer ausgewogenen Situation zwischen den Gesamtdaten und der Vorhersagefunktion zeigt.
Skalierbarkeit
Daten sind beim Erstellen von Modellen für maschinelles Lernen von entscheidender Bedeutung. Normalerweise benötigen traditionelle Lernalgorithmen nicht zu viele Daten. Aufgrund ihrer begrenzten Kapazität ist jedoch auch die Leistung begrenzt. Unten sehen Sie ein Diagramm, das zeigt, wie gut Deep-Learning-Methoden im Vergleich zu herkömmlichen maschinellen Lernalgorithmen skalieren.
Neuronale Netze
Neuronale Netze bestehen aus mehreren Schichten. Das Bild unten zeigt ein einfaches neuronales Netzwerk mit vier Schichten. Die erste Schicht ist die Eingabeschicht und die letzte Schicht die Ausgabeschicht. Die beiden Schichten zwischen der Eingabe- und der Ausgabeschicht sind verborgene Schichten.
Wenn ein neuronales Netzwerk mehr als eine verborgene Schicht hat, nennen wir es ein tiefes neuronales Netzwerk. Die Eingabemenge $ X $ wird an das neuronale Netzwerk gegeben und die Ausgabe $ y $ wird erhalten. Das Lernen erfolgt unter Verwendung eines Backpropagation-Algorithmus, der eine Verlustfunktion und einen Optimierer kombiniert.
Backpropagation besteht aus zwei Teilen: einem Vorwärtsdurchlauf und einem Rückwärtsdurchlauf. Beim Vorwärtsdurchlauf werden Eingangsdaten an den Eingang des neuronalen Netzes gelegt und eine Ausgabe erhalten. Der Verlust zwischen der Grundwahrheit und der Vorhersage wird berechnet, und dann werden im Rückwärtsgang die Parameter der neuronalen Netze in Bezug auf den Verlust abgestimmt.
Faltungsneuronales Netzwerk
Eine Variante des neuronalen Netzwerks ist das Convolutional Neural Network . Es wird hauptsächlich für Computer-Vision-Aufgaben verwendet.
Die wichtigste Schicht in Convolutional Neural Networks ist die Convolutional Layer (daher der Name). Seine Parameter bestehen aus lernbaren Filtern, auch Kernel genannt. Faltungsschichten wenden eine Faltungsoperation auf die Eingabe an und übergeben das Ergebnis an die nächste Schicht. Die Faltungsoperation reduziert die Anzahl der lernbaren Parameter, fungiert als eine Art Heuristik und erleichtert das Trainieren des neuronalen Netzes.
Unten sehen Sie, wie ein Faltungskern in einer Faltungsschicht funktioniert. Der Kernel wird auf das Bild angewendet und ein gefaltetes Merkmal wird erhalten.
ReLU-Schichten werden verwendet, um Nichtlinearitäten in das neuronale Netzwerk einzuführen. Nichtlinearitäten sind wichtig, weil wir mit ihnen alle Arten von Funktionen modellieren können, nicht nur lineare, was das neuronale Netzwerk zu einem universellen Funktionsapproximator macht. Dadurch wird eine ReLU-Funktion wie folgt definiert:
\[ReLU = \max(0, x)\]ReLU ist eines der Beispiele für sogenannte Aktivierungsfunktionen, die verwendet werden, um Nichtlinearitäten in neuronale Netze einzuführen. Beispiele für andere Aktivierungsfunktionen umfassen Sigmoid- und Hypertangensfunktionen. ReLU ist die beliebteste Aktivierungsfunktion, da gezeigt wurde, dass sie das Training neuronaler Netzwerke im Vergleich zu anderen Aktivierungsfunktionen effizienter macht.
Unten ist ein Diagramm einer ReLU-Funktion.
Wie Sie sehen können, ändert diese ReLU-Funktion einfach negative Werte in Nullen. Dies hilft, das Problem des verschwindenden Gradienten zu vermeiden. Wenn ein Gradient verschwindet, hat er keinen großen Einfluss auf die Abstimmung der Gewichtung des neuronalen Netzwerks.
Ein Convolutional Neural Network besteht aus mehreren Schichten: Convolutional Layers, ReLU Layers und Fully Connected Layers. Vollständig verbundene Schichten verbinden jedes Neuron in einer Schicht mit jedem Neuron in einer anderen Schicht, wie mit den beiden verborgenen Schichten im Bild am Anfang dieses Abschnitts zu sehen ist. Die letzte vollständig verbundene Ebene ordnet Ausgaben der vorherigen Ebene in diesem Fall number_of_actions
Werten zu.
Anwendungen
Deep Learning ist erfolgreich und übertrifft klassische maschinelle Lernalgorithmen in mehreren Teilbereichen des maschinellen Lernens, darunter Computer Vision, Spracherkennung und Reinforcement Learning. Diese Bereiche des Deep Learning werden in verschiedenen Bereichen der realen Welt angewendet: Finanzen, Medizin, Unterhaltung usw.
Verstärkungslernen
Reinforcement Learning basiert auf einem Agenten . Ein Agent führt Aktionen in einer Umgebung aus und erhält daraus Beobachtungen und Belohnungen. Ein Agent muss geschult werden, um die kumulative Belohnung zu maximieren. Wie in der Einleitung erwähnt, müssen Ingenieure für maschinelles Lernen bei klassischen maschinellen Lernalgorithmen eine Merkmalsextraktion durchführen, dh gute Merkmale erstellen, die die Umgebung gut darstellen und die in einen maschinellen Lernalgorithmus eingespeist werden.
Mithilfe von Deep Learning ist es möglich, ein End-to-End-System zu erstellen, das hochdimensionalen Input – z. B. Video – verarbeitet und daraus die optimale Strategie für einen Agenten lernt, um gute Maßnahmen zu ergreifen.
Im Jahr 2013 gelang dem Londoner KI-Startup DeepMind ein großer Durchbruch beim Erlernen der Steuerung von Agenten direkt aus hochdimensionalen sensorischen Eingaben. Sie veröffentlichten einen Artikel, Playing Atari with Deep Reinforcement Learning , in dem sie zeigten, wie sie einem künstlichen neuronalen Netzwerk beibrachten, Atari-Spiele zu spielen, indem sie einfach auf den Bildschirm schauten. Sie wurden von Google erworben und veröffentlichten dann ein neues Paper in Nature mit einigen Verbesserungen: Human-level control through deep Reinforcement Learning .
Im Gegensatz zu anderen maschinellen Lernparadigmen hat Reinforcement Learning keinen Supervisor, sondern nur ein Belohnungssignal. Das Feedback ist verzögert: Es erfolgt nicht sofort wie bei überwachten Lernalgorithmen. Daten sind sequentiell und die Aktionen eines Agenten wirken sich auf die nachfolgenden Daten aus, die er empfängt.
Nun befindet sich ein Agent in seiner Umgebung, die sich in einem bestimmten Zustand befindet. Um dies detaillierter zu beschreiben, verwenden wir einen Markov-Entscheidungsprozess, der eine formale Methode zur Modellierung dieser Umgebung des bestärkenden Lernens darstellt. Es besteht aus einer Menge von Zuständen, einer Menge möglicher Aktionen und Regeln (z. B. Wahrscheinlichkeiten) für den Übergang von einem Zustand in einen anderen.
Der Agent ist in der Lage, Aktionen auszuführen und die Umgebung zu verändern. Wir nennen die Belohnung $ R_t $. Es ist ein skalares Feedback-Signal, das angibt, wie gut der Agent bei Schritt $t$ ist.
Für eine gute langfristige Leistung müssen nicht nur sofortige Belohnungen, sondern auch zukünftige Belohnungen berücksichtigt werden. Die Gesamtbelohnung für eine Episode aus Zeitschritt $t$ ist $ R_t = r_t + r_{t+1} + r_{t+2} + \ldots + r_n $. Die Zukunft ist ungewiss und je weiter wir in die Zukunft gehen, desto mehr können Zukunftsprognosen voneinander abweichen. Aus diesem Grund wird eine diskontierte zukünftige Belohnung verwendet: $ R_t = r_t + \gamma r_{t+1} + \gamma^2r_{t+2} + \ldots + \gamma^{nt}r_n = r_t + \gamma R_{t+1} $. Ein Agent sollte die Aktion wählen, die die diskontierte zukünftige Belohnung maximiert.
Tiefes Q-Lernen
Die $ Q(s, a) $-Funktion stellt die maximale diskontierte zukünftige Belohnung dar, wenn die Aktion $ a $
Die Schätzung einer zukünftigen Belohnung ergibt sich aus der Bellman-Gleichung: $ Q(s, a) = r + \gamma \max_{a'}Q(s', a') $ . Mit anderen Worten, die maximale zukünftige Belohnung bei einem Zustand $ s $ und einer Aktion $ a $ ist die unmittelbare Belohnung plus die maximale zukünftige Belohnung für den nächsten Zustand.
Die Approximation von Q-Werten durch nichtlineare Funktionen (neuronale Netze) ist nicht sehr stabil. Aus diesem Grund wird die Wiederholung der Erfahrung für die Stabilität verwendet. Erfahrungen während Episoden in einer Trainingseinheit werden in einem Wiederholungsspeicher gespeichert. Es werden zufällige Mini-Batches aus dem Wiedergabespeicher verwendet, anstatt den letzten Übergang zu verwenden. Dies bricht die Ähnlichkeit nachfolgender Trainingsproben, die sonst das neuronale Netz in ein lokales Minimum treiben würden.

Es gibt zwei weitere wichtige Aspekte, die beim Deep Q-Learning zu erwähnen sind: Exploration und Exploitation. Bei der Ausbeutung wird die beste Entscheidung anhand der aktuellen Informationen getroffen. Exploration sammelt weitere Informationen.
Wenn der Algorithmus eine vom neuronalen Netzwerk vorgeschlagene Aktion ausführt, betreibt er eine Ausbeutung: Er nutzt das erlernte Wissen des neuronalen Netzwerks aus. Im Gegensatz dazu kann ein Algorithmus eine zufällige Aktion ausführen, neue Möglichkeiten erkunden und potenzielles neues Wissen in das neuronale Netzwerk einbringen.
Der „Deep Q-learning algorithm with Experience Replay“ aus dem Paper Playing Atari with Deep Reinforcement Learning von DeepMind ist unten dargestellt.
DeepMind bezeichnet mit ihrem Ansatz trainierte Convolutional Networks als Deep Q-Networks (DQN).
Deep Q-Learning-Beispiel mit Flappy Bird
Flappy Bird war ein beliebtes Handyspiel, das ursprünglich vom vietnamesischen Videospielkünstler und Programmierer Dong Nguyen entwickelt wurde. Darin steuert der Spieler einen Vogel und versucht, zwischen grünen Rohren zu fliegen, ohne sie zu treffen.
Unten ist ein Screenshot von einem Flappy Bird-Klon, der mit PyGame codiert wurde:
Der Klon wurde seitdem gegabelt und modifiziert: Der Hintergrund, die Geräusche und verschiedene Vogel- und Pfeifenstile wurden entfernt und der Code wurde so angepasst, dass er problemlos mit einfachen Reinforcement-Learning-Frameworks verwendet werden kann. Die modifizierte Spiel-Engine stammt aus diesem TensorFlow-Projekt:
Aber anstatt TensorFlow zu verwenden, habe ich mit PyTorch ein Deep Reinforcement Learning Framework erstellt. PyTorch ist ein Deep-Learning-Framework für schnelles, flexibles Experimentieren. Es bietet Tensoren und dynamische neuronale Netze in Python mit starker GPU-Beschleunigung.
Die Architektur des neuronalen Netzwerks ist die gleiche wie DeepMind, die in dem Artikel Human-level control through deep Reinforcement Learning verwendet wird.
Schicht | Eingang | Filtergröße | Schreiten | Anzahl Filter | Aktivierung | Ausgabe |
---|---|---|---|---|---|---|
conv1 | 84x84x4 | 8x8 | 4 | 32 | ReLU | 20x20x32 |
conv2 | 20x20x32 | 4x4 | 2 | 64 | ReLU | 9x9x64 |
conv3 | 9x9x64 | 3x3 | 1 | 64 | ReLU | 7x7x64 |
fc4 | 7x7x64 | 512 | ReLU | 512 | ||
fc5 | 512 | 2 | Linear | 2 |
Es gibt drei Faltungsschichten und zwei vollständig verbundene Schichten. Jede Schicht verwendet die ReLU-Aktivierung, mit Ausnahme der letzten, die eine lineare Aktivierung verwendet. Das neuronale Netzwerk gibt zwei Werte aus, die die einzigen möglichen Aktionen des Spielers darstellen: „Flieg hoch“ und „Nichts tun“.
Die Eingabe besteht aus vier aufeinanderfolgenden 84x84-Schwarzweißbildern. Unten ist ein Beispiel von vier Bildern, die dem neuronalen Netzwerk zugeführt werden.
Sie werden feststellen, dass die Bilder gedreht sind. Das liegt daran, dass die Ausgabe der Spiel-Engine des Klons gedreht wird. Aber wenn das neuronale Netzwerk trainiert und dann mit solchen Bildern getestet wird, wird es seine Leistung nicht beeinträchtigen.
Möglicherweise stellen Sie auch fest, dass das Bild so beschnitten ist, dass der Boden weggelassen wird, da er für diese Aufgabe irrelevant ist. Alle Pixel, die Rohre und den Vogel darstellen, sind weiß und alle Pixel, die den Hintergrund darstellen, sind schwarz.
Dies ist Teil des Codes, der das neuronale Netzwerk definiert. Die Gewichte neuronaler Netze werden so initialisiert, dass sie der gleichmäßigen Verteilung $\mathcal{U}(-0.01, 0.01)$ folgen. Der Bias-Teil der Parameter der neuronalen Netze wird auf 0,01 gesetzt. Es wurden mehrere verschiedene Initialisierungen ausprobiert (Xavier uniform, Xavier normal, Kaiming uniform, Kaiming normal, uniform und normal), aber die obige Initialisierung ließ das neuronale Netzwerk am schnellsten konvergieren und trainieren. Die Größe des neuronalen Netzes beträgt 6,8 MB.
class NeuralNetwork(nn.Module): def __init__(self): super(NeuralNetwork, self).__init__() self.number_of_actions = 2 self.gamma = 0.99 self.final_epsilon = 0.0001 self.initial_epsilon = 0.1 self.number_of_iterations = 2000000 self.replay_memory_size = 10000 self.minibatch_size = 32 self.conv1 = nn.Conv2d(4, 32, 8, 4) self.relu1 = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(32, 64, 4, 2) self.relu2 = nn.ReLU(inplace=True) self.conv3 = nn.Conv2d(64, 64, 3, 1) self.relu3 = nn.ReLU(inplace=True) self.fc4 = nn.Linear(3136, 512) self.relu4 = nn.ReLU(inplace=True) self.fc5 = nn.Linear(512, self.number_of_actions) def forward(self, x): out = self.conv1(x) out = self.relu1(out) out = self.conv2(out) out = self.relu2(out) out = self.conv3(out) out = self.relu3(out) out = out.view(out.size()[0], -1) out = self.fc4(out) out = self.relu4(out) out = self.fc5(out) return out
Im Konstruktor werden Sie feststellen, dass Hyperparameter definiert sind. Hyperparameter-Optimierung wird nicht für den Zweck dieses Blogbeitrags durchgeführt. Stattdessen werden meist Hyperparameter aus den Arbeiten von DeepMind verwendet. Hier sind einige der Hyperparameter niedriger skaliert als in DeepMinds Papier, weil Flappy Bird weniger komplex ist als die Atari-Spiele, die sie zum Tunen verwendet haben.
Außerdem wurde Epsilon geändert, um für dieses Spiel viel vernünftiger zu sein. DeepMind verwendet ein Epsilon von eins, aber hier verwenden wir 0,1. Dies liegt daran, dass höhere Epsilons den Vogel dazu zwingen, stark zu flattern, was den Vogel zum oberen Rand des Bildschirms drückt, was schließlich immer dazu führt, dass der Vogel in ein Rohr stürzt.
Der Reinforcement-Learning-Code hat zwei Modi: Trainieren und Testen. Während der Testphase können wir sehen, wie gut der Reinforcement-Learning-Algorithmus gelernt hat, das Spiel zu spielen. Aber zuerst muss das neuronale Netz trainiert werden. Wir müssen die zu minimierende Verlustfunktion und die Optimierer definieren, die die Verlustfunktion minimieren. Wir verwenden die Adam -Optimierungsmethode und den mittleren quadratischen Fehler für die Verlustfunktion:
optimizer = optim.Adam(model.parameters(), lr=1e-6) criterion = nn.MSELoss()
Das Spiel sollte instanziiert werden:
game_state = GameState()
Der Wiedergabespeicher ist als Python-Liste definiert:
replay_memory = []
Jetzt müssen wir den ersten Zustand initialisieren. Eine Aktion ist ein zweidimensionaler Tensor:
- [1, 0] steht für „nichts tun“
- [0, 1] steht für „hochfliegen“
Die frame_step
Methode gibt uns den nächsten Bildschirm, die Belohnung und Informationen darüber, ob der nächste Zustand endgültig ist. Die Belohnung beträgt 0.1
für die Bewegung jedes Vogels, ohne zu sterben, wenn er nicht durch ein Rohr fliegt, 1
, wenn der Vogel erfolgreich durch ein Rohr fliegt, und -1
, wenn der Vogel abstürzt.
Die Funktion resize_and_bgr2gray
den Boden zu, passt die Bildschirmgröße auf ein 84x84-Bild an und ändert den Farbraum von BGR auf Schwarzweiß. Die Funktion image_to_tensor
konvertiert das Bild in einen PyTorch-Tensor und legt es im GPU-Speicher ab, wenn CUDA verfügbar ist. Schließlich werden die letzten vier sequentiellen Bildschirme miteinander verkettet und sind bereit, an das neuronale Netzwerk gesendet zu werden.
action = torch.zeros([model.number_of_actions], dtype=torch.float32) action[0] = 1 image_data, reward, terminal = game_state.frame_step(action) image_data = resize_and_bgr2gray(image_data) image_data = image_to_tensor(image_data) state = torch.cat((image_data, image_data, image_data, image_data)).unsqueeze(0)
Das anfängliche Epsilon wird mit dieser Codezeile festgelegt:
epsilon = model.initial_epsilon
Es folgt die Hauptendlosschleife. Kommentare werden in den Code geschrieben, und Sie können den Code mit dem oben beschriebenen Deep Q-Learning mit Experience Replay-Algorithmus vergleichen.
Der Algorithmus tastet Mini-Batches aus dem Replay-Speicher ab und aktualisiert die Parameter des neuronalen Netzwerks. Aktionen werden mit Epsilon Greedy Exploration ausgeführt. Epsilon wird im Laufe der Zeit getempert. Die zu minimierende Verlustfunktion ist $ L = \frac{1}{2}\left[\max_{a'}Q(s', a') - Q(s, a)\right]^2 $ . $ Q(s, a) $ ist der anhand der Bellman-Gleichung berechnete Ground-Truth-Wert und $ \max_{a'}Q(s', a') $ wird aus dem neuronalen Netz erhalten. Das neuronale Netzwerk gibt zwei Q-Werte für die zwei möglichen Aktionen und der Algorithmus führt die Aktion mit dem höchsten Q-Wert aus.
while iteration < model.number_of_iterations: # get output from the neural network output = model(state)[0] # initialize action action = torch.zeros([model.number_of_actions], dtype=torch.float32) if torch.cuda.is_available(): # put on GPU if CUDA is available action = action.cuda() # epsilon greedy exploration random_action = random.random() <= epsilon if random_action: print("Performed random action!") action_index = [torch.randint(model.number_of_actions, torch.Size([]), dtype=torch.int) if random_action else torch.argmax(output)][0] if torch.cuda.is_available(): # put on GPU if CUDA is available action_index = action_index.cuda() action[action_index] = 1 # get next state and reward image_data_1, reward, terminal = game_state.frame_step(action) image_data_1 = resize_and_bgr2gray(image_data_1) image_data_1 = image_to_tensor(image_data_1) state_1 = torch.cat((state.squeeze(0)[1:, :, :], image_data_1)).unsqueeze(0) action = action.unsqueeze(0) reward = torch.from_numpy(np.array([reward], dtype=np.float32)).unsqueeze(0) # save transition to replay memory replay_memory.append((state, action, reward, state_1, terminal)) # if replay memory is full, remove the oldest transition if len(replay_memory) > model.replay_memory_size: replay_memory.pop(0) # epsilon annealing epsilon = epsilon_decrements[iteration] # sample random minibatch minibatch = random.sample(replay_memory, min(len(replay_memory), model.minibatch_size)) # unpack minibatch state_batch = torch.cat(tuple(d[0] for d in minibatch)) action_batch = torch.cat(tuple(d[1] for d in minibatch)) reward_batch = torch.cat(tuple(d[2] for d in minibatch)) state_1_batch = torch.cat(tuple(d[3] for d in minibatch)) if torch.cuda.is_available(): # put on GPU if CUDA is available state_batch = state_batch.cuda() action_batch = action_batch.cuda() reward_batch = reward_batch.cuda() state_1_batch = state_1_batch.cuda() # get output for the next state output_1_batch = model(state_1_batch) # set y_j to r_j for terminal state, otherwise to r_j + gamma*max(Q) y_batch = torch.cat(tuple(reward_batch[i] if minibatch[i][4] else reward_batch[i] + model.gamma * torch.max(output_1_batch[i]) for i in range(len(minibatch)))) # extract Q-value q_value = torch.sum(model(state_batch) * action_batch, dim=1) # PyTorch accumulates gradients by default, so they need to be reset in each pass optimizer.zero_grad() # returns a new Tensor, detached from the current graph, the result will never require gradient y_batch = y_batch.detach() # calculate loss loss = criterion(q_value, y_batch) # do backward pass loss.backward() optimizer.step() # set state to be state_1 state = state_1
Jetzt, da alle Teile vorhanden sind, finden Sie hier einen allgemeinen Überblick über den Datenfluss mit unserem neuronalen Netzwerk:
Hier ist eine kurze Sequenz mit einem trainierten neuronalen Netzwerk.
Das oben gezeigte neuronale Netzwerk wurde einige Stunden lang mit einer High-End-GPU Nvidia GTX 1080 trainiert; Bei Verwendung einer CPU-basierten Lösung würde diese spezielle Aufgabe mehrere Tage dauern. Die FPS der Game-Engine wurden beim Training sehr hoch eingestellt: 999…999 – also so viele Bilder pro Sekunde wie möglich. In der Testphase wurde die FPS auf 30 gesetzt.
Unten ist ein Diagramm, das zeigt, wie sich der maximale Q-Wert während Iterationen geändert hat. Jede 10.000ste Iteration wird angezeigt. Eine Abwärtsspitze bedeutet, dass das neuronale Netzwerk für einen bestimmten Frame (eine Iteration ist ein Frame) vorhersagt, dass der Vogel in Zukunft eine sehr geringe Belohnung erhalten wird – dh er wird sehr bald abstürzen.
Der vollständige Code und das vortrainierte Modell sind hier verfügbar.
Deep Reinforcement Learning: 2D, 3D und sogar das echte Leben
In diesem PyTorch-Tutorial zum bestärkenden Lernen habe ich gezeigt, wie ein Computer lernen kann, Flappy Bird ohne Vorkenntnisse über das Spiel zu spielen, indem ich nur einen Trial-and-Error-Ansatz verwendet, wie es ein Mensch tun würde, wenn er das Spiel zum ersten Mal begegnet.
Interessant ist, dass der Algorithmus mit dem PyTorch-Framework in wenigen Codezeilen implementiert werden kann. Das Papier, auf dem die Methode in diesem Blog basiert, ist relativ alt, und viele neuere Papiere mit verschiedenen Modifikationen, die eine schnellere Konvergenz ermöglichen, sind verfügbar. Seitdem wird Deep Reinforcement Learning zum Spielen von 3D-Spielen und in realen Robotersystemen verwendet.
Unternehmen wie DeepMind, Maluuba und Vicarious arbeiten intensiv an Deep Reinforcement Learning. Diese Technologie wurde in AlphaGo verwendet, das Lee Sedol, einen der weltbesten Spieler bei Go, besiegte. Damals ging man davon aus, dass es noch mindestens zehn Jahre dauern würde, bis Maschinen die besten Spieler beim Go besiegen könnten.
Die Flut von Interesse und Investitionen in Deep Reinforcement Learning (und in künstliche Intelligenz im Allgemeinen) könnte sogar zu potenzieller künstlicher allgemeiner Intelligenz (AGI) führen – Intelligenz auf menschlicher Ebene (oder sogar darüber hinaus), die in Form eines Algorithmus ausgedrückt werden kann und auf Computern simuliert. Aber AGI muss Gegenstand eines anderen Artikels sein.
Verweise:
- Deep Reinforcement Learning entmystifizieren
- Deep Reinforcement Learning für Flappy Bird
- „Convolutional Neural Network“ bei Wikipedia
- „Bestärkendes Lernen“ bei Wikipedia
- „Markov-Entscheidungsprozess“ bei Wikipedia
- University College London Kurs über RL