Reinforcement Learning#
The way positive reinforcement is carried out is more important than the amount.
— Burrhus Frederic Skinner
Reinforcement Learning (RL) ist ein Bereich des maschinellen Lernens, bei dem ein Agent lernt, in einer Umgebung durch Interaktionen zu handeln, um eine maximale Belohnung zu erzielen. Der Agent nimmt Aktionen basierend auf seinem aktuellen Zustand vor und erhält Belohnungen oder Bestrafungen, die er verwendet, um seine Strategie zu verbessern.
Während Supervised Learning aus Beispielen lernt („Was ist das?“), lernt Reinforcement Learning durch Versuch und Irrtum („Was soll ich tun?“), indem es für gute Entscheidungen belohnt wird. Es benötigt keine gelabelten Daten, sondern eine Umgebung und Rückmeldung (Reward).
RL eignet sich besonders für Aufgaben, bei denen ein Agent eigenständig durch Interaktion mit einer Umgebung lernen muss, optimale Entscheidungen zu treffen. Typische Einsatzgebiete sind dynamische Steuerungs- und Optimierungsprobleme, bei denen klassische regelbasierte Ansätze an ihre Grenzen stoßen. RL wird häufig verwendet, wenn die Umgebung komplex, unsicher oder sich verändernd ist und keine gelabelten Trainingsdaten vorliegen.
Beispiele im Bauwesen sind: Ein Heiz-/Kühlsystem in Gebäuden lernt, abhängig von Wetter und Nutzung, effizient zu regeln. Beim 3D-Druck von Betonelementen kann RL den Materialeinsatz optimieren. Ein autonomer Bagger lernt, wie er Erdbewegungen möglichst ressourcenschonend und schnell ausführt.
Das macht auf der einen Seite RL vom Konzept her einfach zu verstehen und zu implementieren. Die Spezifikation der Belohnungsstrategie ist allerdings meist schwierig, da dies Verständnis des Problems erfordert. Der Ansatz benötigt keine gelabelte Trainingsdaten, ist also kein Überwachter ML-Ansatz. Er ist auch kein unüberwachter Ansatz, da ja durchaus Wissen in Form der Belohnungsstrategie notwendig ist.
RL eignet sich insbesondere für dynamische Entscheidungen und wird häufig in der Robotik verwendet, aber wird auch immer beliebter in traditionellen Gebieten.
Folien#
Grundlegende Konzepte#
Reinforcement Learning basiert auf einer iterativen Lernstrategie, bei der die Qualität der Lösung durch positives oder negatives Feedback bewertet wird. Das RL-Modell wird hierbei als Agent gesehen, der mit der Umgebung interagiert. Die Begriffe in diesem Kontext sind:
Agent (Agent): Der Lernende oder Entscheidungsträger, der in der Umgebung agiert.
Umgebung (Environment): Alles, mit dem der Agent interagiert.
Aktion (Action): Eine Entscheidung oder Bewegung, die der Agent treffen kann.
Belohnung (Reward): Rückmeldung aus der Umgebung, die angibt, wie gut eine Aktion im gegebenen Zustand war.
Wertfunktion (Value Function): Eine Funktion, die angibt, wie gut ein bestimmter Zustand oder eine Aktion ist.
Strategie (Policy): Eine Strategie, die der Agent verwendet, um Aktionen zu wählen basierend auf dem Zustand und der Wertfunktion.
Zustand (State): Eine Repräsentation der aktuellen Situation der Umgebung und ggf. der Strategie.
Zur Modellierung der Umgebung und aktuellen Strategie verwendet man im RL oft ein Markov Decision Processes (MDPs). Das ist ein diskretes Zustandsmodell, bei dem das System sich immer nur in einem einzigen Zustand befinden kann, der die Umgebung (oder Strategie) widerspiegelt und die Aktion bestimmt. Jeder Zustand beschreibt eine bestimmte Bedingung oder Position im Verhalten des Systems. Der Zustand wird auf Basis der Belohnung von der Umgebung gewechselt.
Markov-Modelle sind eine sehr beliebte Modelltyp im Maschinellem Lernen. Sie basieren alle auf der Markov-Bedingung, dass die Übergangswahrscheinlichkeit von einem Zustand in den anderen nur von dem aktuellen Zustand abhängt und nicht vorhergehenden Zuständen (es gibt also keine Autokorrelation). Man spricht dabei auch von der Gedächtnislosigkeit. Dies basiert auf der Idee, dass der Zustand des Systems zu einem bestimmten Zeitpunkt alle Informationen enthält, die notwendig sind, um sein zukünftiges Verhalten vorherzusagen.
Ein MDP besteht aus:
\( S \): Menge aller möglichen Zustände.
\( A \): Menge aller möglichen Aktionen.
\( P(s'|s, a) \): Übergangswahrscheinlichkeit vom Zustand \( s \) zum Zustand \( s' \) bei Aktion \( a \).
\( R(s, a) \): Belohnungsfunktion, die die Belohnung angibt, die der Agent erhält, wenn er im Zustand \( s \) die Aktion \( a \) ausführt.
\( \gamma \): Diskontierungsfaktor, der zukünftige Belohnungen abwertet.
Der MDP modelliert also die Zustände und die bisherige Übergangswahrscheinlichkeit sowie die erhaltenen Belohnungen. Die entscheidende Frage im Reinforcement Learning ist nun, wie man daraus die beste Aktion und somit den nächsten Zustand ableiten kann. Hierbei gibt es das Dilemma, das aufgrund der Markov-Bedingung das Modell zwar einfach ist, aber wir auch gedächtnislos sind, wir also nicht die Historie der Zustände mit in unserer Entscheidung betrachten können. Das ist problematisch, wenn das Ziel nur erreicht werden kann, wenn eine bestimmte Zustandsfolge eintritt, wie in dem Gridworld-Beispiel unten diskutiert.
Eine wichtige Gleichung ist hierfür die Bellman-Gleichung. Sie beschreibt die Beziehung zwischen dem aktuellen Zustands-Aktionspaar \((s,a)\), der beobachteten Belohnung und den möglichen Nachfolge-Zustands-Aktionspaaren \(s',a'\). Diese Beziehung wird verwendet, um die optimale Wertfunktion zu finden.
Die Bellman-Gleichung basiert auf dem Prinzip der Optimalität, das besagt, dass ein optimaler Policy eine rekursive Struktur aufweist. Das bedeutet, dass der Wert eines Zustands unter einer optimalen Policy aus der sofortigen Belohnung und dem diskontierten Wert der zukünftigen Zustände besteht. Die Bellman-Gleichung für die Wertfunktion \(V(s)\) eines Zustands \(s\) definiert den Wert eines Zustands als die erwartete Belohnung für die beste Aktion in diesem Zustand plus den diskontierten Wert des besten nächsten Zustands, unter der Annahme, dass der Agent die optimale Policy verfolgt. Mathematisch formuliert lautet die Bellman-Optimalitätsgleichung:
Diese Gleichung besagt, dass der optimale Wert eines Zustands \(s\) die maximale erwartete Belohnung ist, die der Agent erhalten kann, wenn er im Zustand \(s\) startet, die Aktion \(a\) wählt und danach der optimalen Policy folgt. Dadurch berücksichtigen wir bei der Bewertung des Zustandsüberganges durch die Aktion \(a\) nicht nur den aktuellen Zustand, sondern auch die durch Aktion \(a\) ermöglichten zukünftigen Zustandsübergänge. Damit lösen wir das Dilemma der Gedächtnislosigkeit, das aus der Markov-Bedingung folgt.
Die Bellman-Gleichung wird genutzt, um die optimale Strategie in Form einer Q-Funktion \(Q(s,a)\) im Q-Learning zu erlernen. Das ist ein populärer Off-Policy-Algorithmus, bei dem der Agent eine Q-Funktion \( Q(s, a) \) lernt, welche Belohnungen einer Aktion \( a \) in einem Zustand \( s \) erwartet wird.
hierbei stellt \(\alpha\) die Lernrate dar. Die ist ein sehr wichtiger Parameter, da er entscheidet, wie schnell das Modell auf eine Lösung konvergiert. Ein niedriger Wert sorgt dazu, dass der Algorithmus mehr positive Beispiele zum Lernen benötigt, was also längeres Training erfordert, insbesondere bei seltenen, positiven Feedback. Ein hoher Wert kann dazu sorgen, dass sich das Modell schnell in einer schlechteren Lösung (lokales Optimum) verrennt.
Deshalb kombiniert man den Algorithmus man meist mit einer Erkundungsstrategie. Statt immer nur die geschätzte optimale Strategie \(Q(s,a)\) zu nehmen, wählt man mit der Wahrscheinlichkeit \(\epsilon\) zufällige Strategien aus, um somit alternative Strategien zu entdecken. Ein hoher \(\epsilon\)-Wert sorgt für eine hohe Erkundungsrate (Exploration), während ein niedriger ein verlässlichere Vorhersage erlaubt. Um hier einen Trade-Off zu finden, macht man oft beides und wählt am Anfang des Trainings ein hohes \(\epsilon\), um den Lösungsbereich zu erkunden und am Ende des Trainings ein niedriges \(\epsilon\) um schneller und verlässlicher zu konvergieren.
Beispiel: Gridworld-Umgebung#
Wir werden eine einfache Gridworld-Umgebung verwenden, um die Grundprinzipien von Reinforcement Learning zu veranschaulichen. In dieser Umgebung versucht ein Agent, in einem Grid von einem Startzustand zu einem Zielzustand zu gelangen und dabei den kürzesten Weg zu finden.
Dies kann zum Beispiel zur Steuerung eines Materialtransports auf einer Baustelle dienen. Ein Agent (z.B. ein Bagger) befindet sich auf einer vereinfachten Baustelle und muss Material von einer Quelle zu einem Zielbereich bringen. Wir simulieren dieses Szenario in einer Gitterwelt mit folgenden Regeln:
Der Agent kann sich hoch, runter, links, rechts bewegen.
Er erhält +10 Punkte, wenn er das Ziel (Z) erreicht.
Für jeden Schritt wird er mit -1 Punkt bestraft.
Die Umgebung ist sehr einfach und zufallsbasiert.
Wir erstellen uns als erstes eine Klasse, welche die Umgebung repräsentiert und die Zustände enthält. Die Umgebung initialisiert zuerst unser Raster (Grid) mit der Start- und Endposition. Dieses Raster definiert unseren Zustandsraum, da der Agent, wenn er sich fort bewegt sich in jeder dieser Zellen im Raster aufhalten kann.
Damit wir Experimente beim Lernen wiederholen können gibt es eine reset
-Funktion. Mit der step
-Funktion kann der Agent sich fortbewegen. Wir übergeben der Funktion als action
die Richtung, in der wir uns bewegen wollen. Dadurch wechselt sich die aktuelle Position, also der aktuelle Zustand in unserem Zustandsmodell.
import numpy as np # Import von NumPy
# Definition der Gridworld-Umgebung
class Gridworld:
def __init__(self, size, start, goal):
self.size = size
self.start = start
self.goal = goal
self.state = start
self.actions = ['up', 'down', 'left', 'right']
self.grid=np.zeros((self.size,self.size))
self.grid[start]=1
self.grid[goal]=0
def reset(self):
self.state = self.start
return self.state
def step(self, action):
x, y = self.state
if action == 'up':
x = max(0, x - 1)
elif action == 'down':
x = min(self.size - 1, x + 1)
elif action == 'left':
y = max(0, y - 1)
elif action == 'right':
y = min(self.size - 1, y + 1)
self.state = (x, y)
self.grid[self.state]+=1
reward = 1 if self.state == self.goal else -0.1
done = self.state == self.goal
return self.state, reward, done
Erstellen wir uns als Beispiel ein 3x3 Gridworld und wollen uns vom Punkt \([0,0]\) zum Punkt \([3,3]\) bewegen.
env = Gridworld(size=4, start=(0, 0), goal=(3, 3))
state = env.reset()
print("Startzustand:", state)
env.grid
Startzustand: (0, 0)
array([[1., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
Der Zustandsraum ist hierbei ein 2D Grid
Bewegen wir uns einmal manuell durch das Grid, so sehen wir wie die Zustände (unsere Position) sich ändern und welche Belohnungen wir erhalten. Wichtig hierbei ist, dass wir eine positive Belohnung erst beim letzten Schritt erhalten, vorher immer nur bestraft werden (z.B. Erschöpfung).
# Beispiel für einen Schritt in der Umgebung
steps=['right','down','right','down','right','down']
for step in steps:
next_state, reward, done = env.step(step)
print("Nächster Zustand:", next_state, "Belohnung:", reward, "Ziel erreicht:", done)
Nächster Zustand: (0, 1) Belohnung: -0.1 Ziel erreicht: False
Nächster Zustand: (1, 1) Belohnung: -0.1 Ziel erreicht: False
Nächster Zustand: (1, 2) Belohnung: -0.1 Ziel erreicht: False
Nächster Zustand: (2, 2) Belohnung: -0.1 Ziel erreicht: False
Nächster Zustand: (2, 3) Belohnung: -0.1 Ziel erreicht: False
Nächster Zustand: (3, 3) Belohnung: 1 Ziel erreicht: True
import plotly.express as px
px.imshow(env.grid)
/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/kaleido/__init__.py:14: UserWarning:
Warning: You have Plotly version 5.24.1, which is not compatible with this version of Kaleido (1.0.0).
This means that static image generation (e.g. `fig.write_image()`) will not work.
Please upgrade Plotly to version 6.1.1 or greater, or downgrade Kaleido to version 0.2.1.
Random Walk#
Ein naiver Lösungsansatz ist der so genannte Random Walk (Zufallsweg), bei dem man bei jedem Schritt in eine zufällig gewählte Richtung läuft, was auch bedeutet, dass man ggf. zurück läuft. Untersuchen wir einmal in 500 Experimenten die Erfolgschancen dieser Lösungsstrategie.
env = Gridworld(size=4, start=(0, 0), goal=(3, 3))
trials = 500
rewards = []
retries = []
for trial in range(trials):
env.reset()
n, r_sum = 0, 0
done = False
while not done:
# Wir wählen eine zufällige Aktion
action = np.random.choice(env.actions)
# Führe die Aktion aus und erhalte von der Umgebung den neuen Zustand und die Belohnung
new_state, reward, done = env.step(action)
# Sammel Erfolgsstatistik
r_sum += reward
n += 1
rewards.append(r_sum)
retries.append(n)
In allen Experimenten ist unser Agent zum Ziel gekommen, was zeigt, dass diese zufällige Explorations-Strategie durchaus erfolgreich ist. Schauen wir auf die Anzahl der Versuche bis zum Ziel über unsere Experimente, so sehen wir, dass diese gleichbleibend stark variiert. Wir haben also kein Lerneffekt.
import plotly.express as px
px.line(retries)
Entsprechend niedrig sind auch die Belohnungen, die das Programm pro Experiment sammelt.
px.line(rewards)
Wenn wir uns die Häufigkeit ansehen, der besuchten Rasterpunkte, so zeigt sich, dass der Großteil der Versuche um dem Starpunkt herum hängen bleibt, also oft auch wieder sich zurückbewegt.
px.imshow(env.grid)
Q-Learning#
Wir implementieren als nächstes den Q-Learning Ansatz wie er oben beschrieben worden ist. Hierfür initialisieren wir als erstes die Q-Matrix, in welcher wir die erlernten Q-Werte speichern. Da unser Zustandsraum die Große 3x3 hat und wir 4 Aktionen haben, hat die Q-Matrix eine Größe von 3x3x4.
Der Q-Learning Algorithmus besteht nun zum einen in der Möglichkeit mit der Wahrscheinlichkeit \(\epsilon\) ein explorativen zufälligen Schritt zu machen oder den vielversprechendsten Schritt unseres aktuellen Zustands \(s_1,s_2\) mit dem maximalen Q-Wert (argmax).
env = Gridworld(size=4, start=(0, 0), goal=(3, 3))
# Initialisiere Q-Werte
Q = np.zeros((env.size, env.size, len(env.actions)))
# Hyperparameter
alpha = 0.2 # Lernrate
gamma = 0.9 # Diskontierungsfaktor
epsilon = 0.1 # Epsilon für die Epsilon-Greedy-Strategie
rewardsQ=[]
retriesQ=[]
for trial in range(trials):
state = env.reset()
done = False
n, r_sum=0, 0
while not done:
if np.random.uniform(0,1) < epsilon:
# Wähle Aktion entweder Zufällig aus
action = np.random.randint(0, len(env.actions))
else:
# Wähle Aktion mit maximaler erwarteten Belohnung Q(s)
action = np.argmax(Q[state[0], state[1], :])
# Führe die Aktion aus und erhalte von der Umgebung den neuen Zustand und die Belohnung
new_state, reward, done = env.step(env.actions[action])
# Aktualisiere Q-Werte auf Basis der erhaltenen Belohnung
Q[state[0], state[1], action] += alpha * (reward + gamma * np.max(Q[new_state[0], new_state[1], :]) - Q[state[0], state[1], action])
# Aktualisiere den Zustand
state = new_state
# Sammel Erfolgsstatistik
r_sum+=reward
n += 1
rewardsQ.append(r_sum)
retriesQ.append(n)
Wenn wir nun die Wiederholungsversuche uns ansehen, dann sehen wir, dass diese schnell auf das Minimum von nur 6 Schritte abfallen. Es gibt immer noch Schwankungen, die an dem Zufallsanteil \(\epsilon\) liegen.
px.line(retriesQ)
Betrachten wir die Belohnungen, so sehen wir, dass der Ansatz sehr schnell nur noch positive Belohnungen erzieht.
px.line(rewardsQ)
Der resultierende Weg ist hierbei einer von vielen optimalen Wegen.
px.imshow(env.grid)
Zeitreihe#
Mit Reinforcement Learning kann man auch Modelle auf Zeitreihen trainieren. Wir wollen uns zum Beispiel einen Entscheidungsalgorithmus trainieren, welcher lernt, wann er Aktien oder Shorts auf diese kaufen soll und wann er sie verkaufen soll. Hier ein zufälliger Aktienkurs.
import numpy as np
import pandas as pd
# Simulierte Zeitreihe (z.B. Aktienpreise)
np.random.seed(42)
data = np.cumsum(np.random.randn(200) + 0.5)
# Daten visualisieren
px.line(data)
Wir definieren uns wieder eine Trainingsumgebung. Dieses Mal haben wir allerdings die Schwierigkeit, dass der Wert kontinuierlich sind und nicht diskret, wir also keine natürliche Zustandsraumdarstellung haben. Deshalb müssen wir die kontinuierliche Zeitreihe des Aktienwertes zuerst diskretisieren.
class StockTradingEnv:
def __init__(self, data, num_states=40):
self.data = data
self.current_step = 0
self.state = None
self.states = np.linspace(min(data), max(data), num_states)
self.done = False
self.actions = ['kaufen', 'verkauf']
self.position = 0 # 1 = long, -1 = short, 0 = neutral
self.balance = 0
self.current_price=0
self.old_price = 0
def discretize(self):
return np.digitize(self.data[self.current_step], self.states) - 1
def reset(self):
self.current_step = 0
#self.state = self.data[self.current_step:self.current_step+5]
self.state = self.discretize()
self.done = False
self.position = 0
self.balance = 0
self.old_price = 0
self.current_price=0
return self.state
def step(self, action):
self.current_step += 1
if self.current_step > len(self.data) - 2:
self.done = True
self.current_price = self.data[self.current_step]
reward = 0
if action == 0: # Kaufen
if self.position == 0: # Kaufe Aktie
self.position = 1
self.balance -= self.current_price
self.old_price = self.current_price
elif self.position == -1: # Verkaufe Short
reward = 2 * (self.old_price - self.current_price)
self.position = 0
self.balance += self.old_price
elif action == 1: # Verkaufen
if self.position == 0: # Kaufe Short
self.position = -1
self.balance += self.current_price
self.old_price = self.current_price
elif self.position == 1: # Verkaufe Aktie
reward = 2 * (self.current_price - self.old_price)
self.position = 0
self.balance -= self.old_price
elif action == 2: # Halten
pass
self.state = self.discretize()
return self.state, reward, self.done
Als Beispiel kaufen wir Akten, halten sie für 6 Züge und verkaufen sie. Dann kaufen wir Shorts, um auf fallende Aktien zu wetten, halten sie wieder und verkaufen sie.
num_states=40
# Beispiel für die Erstellung und Verwendung der StockTradingEnv-Umgebung
env = StockTradingEnv(data, num_states)
state = env.reset()
print("Startzustand:", state)
# Beispiel für einen Schritt in der Umgebung
for action in [0,2,2,2,2,2,1,1,2,2,2,2,2,0]:# 0 = Kaufen, 2 = Halten, 1 = Verkaufen
next_state, reward, done = env.step(action)
print("Nächster Zustand:", next_state, "Belohnung:", reward, "Ziel erreicht:", done)
Startzustand: 0
Nächster Zustand: 0 Belohnung: 0 Ziel erreicht: False
Nächster Zustand: 0 Belohnung: 0 Ziel erreicht: False
Nächster Zustand: 1 Belohnung: 0 Ziel erreicht: False
Nächster Zustand: 1 Belohnung: 0 Ziel erreicht: False
Nächster Zustand: 1 Belohnung: 0 Ziel erreicht: False
Nächster Zustand: 2 Belohnung: 0 Ziel erreicht: False
Nächster Zustand: 3 Belohnung: 14.098151214993003 Ziel erreicht: False
Nächster Zustand: 3 Belohnung: 0 Ziel erreicht: False
Nächster Zustand: 3 Belohnung: 0 Ziel erreicht: False
Nächster Zustand: 3 Belohnung: 0 Ziel erreicht: False
Nächster Zustand: 3 Belohnung: 0 Ziel erreicht: False
Nächster Zustand: 3 Belohnung: 0 Ziel erreicht: False
Nächster Zustand: 3 Belohnung: 0 Ziel erreicht: False
Nächster Zustand: 2 Belohnung: 1.565646416803098 Ziel erreicht: False
Als nächstes implementieren wir wieder den Q-Learning Algorithmus. Der ist fast identisch zu dem Gridworld-Beispiel. Unterschiede gibt es nur, da wir nur eine zweidimensionale Q-Matrix haben, statt einer dreidimensionalen, da der Zustandsraum weniger Dimensionen hat.
import time
# Beispiel für die Erstellung und Verwendung der StockTradingEnv-Umgebung
tic=time.time()
env = StockTradingEnv(data, num_states)
# Initialisiere Q-Werte
Q = np.zeros((num_states, 3))
# Hyperparameter
alpha = 0.1 # Lernrate
gamma = 0.9 # Diskontierungsfaktor
epsilon = 0.1 # Epsilon für die Epsilon-Greedy-Strategie
# Training des Q-Learning-Agenten
for episode in range(1000):
state = env.reset()
done = False
actions = []
rewards = []
total_reward = 0
while not done:
if np.random.random() < epsilon:
# Wähle Aktion entweder Zufällig aus
action = np.random.randint(0, len(env.actions))
else:
# Wähle Aktion mit maximaler erwarteten Belohnung Q(s)
action = np.argmax(Q[env.state])
# Führe die Aktion aus und erhalte von der Umgebung den neuen Zustand und die Belohnung
new_state, reward, done = env.step(action)
new_state_idx = env.current_step
# Aktualisiere Q-Werte auf Basis der erhaltenen Belohnung
Q[state, action] += alpha * (reward + gamma * np.max(Q[new_state]) - Q[state, action])
# Aktualisiere den Zustand
state = new_state
# Sammel Erfolgsstatistik
actions.append(action)
total_reward+=-reward
rewards.append(total_reward)
print(f"Execution Time: {time.time()-tic}")
Execution Time: 2.4238998889923096
Auch hier zeigt sich, dass der Q-Learning Algorithmus erfolgreich lernt, wann er Aktien kaufen, verkaufen oder shorten soll, so dass er am Ende Gewinn macht.
px.line(rewards)
Hierbei macht der Algorithmus gar nicht so viele Transaktionen, wie er könnte, um das Ergebnis zu maximieren.
px.line(actions)
RL Frameworks#
RL gehört nicht zu den traditionellen ML-Verfahren und ist deshalb auch nicht in SciKit-Learn oder Statsmodels enthalten. Das liegt unter anderem daran, dass man beim RL nicht einfach ein Modell auf einem Datensatz trainiert, wie es von der Standard-API von SciKit-Learn erwartet wird, sondern manuell ein Umgebungsmodell erstellen muss.
Mit der wachsenden Popularität von RL in den letzten Jahren haben sich aber spezielle Bibliotheken für RL entwickelt, die dies vereinfachen und das Lösen komplexer Probleme vereinfachen. Eine Bibliothek dafür ist Gymnasium
oder kurz Gym
von OpenAI. Die Gym
-Bibliothek ist eine Open-Source-Bibliothek, die speziell für die Entwicklung und das Testen von RL-Algorithmen entwickelt wurde. Sie bietet eine standardisierte API und eine Vielzahl von vordefinierten Umgebungen, die es ermöglichen RL-Algorithmen effizient zu implementieren und zu evaluieren. Hierbei spielt insbesondere die Definition der Umgebung eine wichtige Rolle.
Das Standard-Interface in Gym
für eine Umgebung für den Börsenfall ist mehr oder weniger identisch mit der Klasse, die wir oben definiert haben.
import gymnasium as gym
from gymnasium import spaces
import numpy as np
import pandas as pd
class StockTradingEnvGym(gym.Env):
def __init__(self, data):
super(StockTradingEnvGym, self).__init__()
self.data = data.copy()
self.current_step = 0
self.balance = 10000 # Startguthaben
self.position = 0 # 0: neutral, 1: long, -1: short
self.old_price = 0
self.current_price = self.data[self.current_step]
# Actions: 0 = Kaufen, 1 = Verkaufen, 2 = Halten
self.action_space = spaces.Discrete(3)
# Observation space: [current price, position, balance]
self.observation_space = spaces.Box(
low=np.array([-np.inf, -1, -np.inf]),
high=np.array([np.inf, 1, np.inf]),
dtype=np.float32
)
def reset(self, seed=0, options=None):
self.current_step = 0
self.balance = 10000
self.position = 0
self.current_price = self.data[self.current_step]
return self._get_obs(), {}
def _get_obs(self):
return np.array([self.current_price, self.position, self.balance])
def step(self, action):
prev_price = self.current_price
self.current_step += 1
self.current_price = self.data[self.current_step]
reward = 0
if action == 0: # Kaufen
if self.position == 0: # Kaufe Aktie
self.position = 1
self.balance -= self.current_price
self.old_price = self.current_price
elif self.position == -1: # Verkaufe Short
reward = 2 * (self.old_price - self.current_price)
self.position = 0
self.balance += self.old_price
elif action == 1: # Verkaufen
if self.position == 0: # Kaufe Short
self.position = -1
self.balance += self.current_price
self.old_price = self.current_price
elif self.position == 1: # Verkaufe Aktie
reward = 2 * (self.current_price - self.old_price)
self.position = 0
self.balance -= self.old_price
elif action == 2: # Halten
reward = 0
done = self.current_step >= len(self.data) - 1
return self._get_obs(), reward, done, False, {}
def render(self, mode='human'):
print(f'Step: {self.current_step}, Price: {self.current_price}, Position: {self.position}, Balance: {self.balance}')
Ein Aspekt warum RL in den letzten Jahren so beliebt geworden ist, ist dass sich der Lernvorgang gut parallelisieren lässt. Da wir zum Lernen viele Experimente machen müssen, können wir diese natürlich auch parallel ausführen und dadurch gut in einem Rechenzentrum in der Cloud oder auf Grafikkarten mit ihren tausenden kleinen Prozessoren verteilen.
Eine Bibliothek, die hierbei viel genutzt wird, ist Ray welche auch gerne zur Parallelisierung von ML-Aufgaben genutzt wird, da die Bibliothek das Verteilen der Lernaufgaben im Cluster und das Sammeln der Ergebnisse übernimmt.
Wir nutzen hier den Proximale Policy Optimization (PPO) Algorithmus. Er ist ein iteratives Verfahren, welches zur Optimierung von Richtlinien in Agenten verwendet wird und instabile Updates der Policy vermeidet und ermöglicht so die effiziente Handhabung komplexer Aufgaben mit kontinuierlichen Aktionsräumen. Dies wird durch die Einführung eines Clip-Parameters ε erreicht, der die maximale Änderung der Policy-Parameter begrenzt.
import os
os.environ["PYTHONWARNINGS"]="ignore::DeprecationWarning"
from ray.rllib.algorithms.ppo import PPOConfig
from ray.tune.registry import register_env
tic=time.time()
def env_creator(env_config):
return StockTradingEnvGym(data) # return an env instance
register_env("StockTradingEnvGym", env_creator)
config = (
PPOConfig()
.environment("StockTradingEnvGym")
.env_runners(num_env_runners=2)
.framework("torch")
.training()
.evaluation(evaluation_num_env_runners=1)
)
algo = config.build() # 2. build the algorithm,
for _ in range(10):
algo.train() # 3. train it,
#algo.evaluate() # 4. and evaluate it.
print(f"Execution Time: {time.time()-tic}")
/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning:
IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
2025-08-05 10:56:31,181 INFO util.py:154 -- Missing packages: ['ipywidgets']. Run `pip install -U ipywidgets`, then restart the notebook server for rich notebook output.
2025-08-05 10:56:33,074 INFO util.py:154 -- Missing packages: ['ipywidgets']. Run `pip install -U ipywidgets`, then restart the notebook server for rich notebook output.
2025-08-05 10:56:33,708 WARNING deprecation.py:50 -- DeprecationWarning: `build` has been deprecated. Use `AlgorithmConfig.build_algo` instead. This will raise an error in the future!
2025-08-05 10:56:33,710 WARNING algorithm_config.py:4921 -- You have specified 1 evaluation workers, but your `evaluation_interval` is 0 or None! Therefore, evaluation doesn't occur automatically with each call to `Algorithm.train()`. Instead, you have to call `Algorithm.evaluate()` manually in order to trigger an evaluation run.
2025-08-05 10:56:33,711 WARNING algorithm_config.py:5033 -- You are running PPO on the new API stack! This is the new default behavior for this algorithm. If you don't want to use the new API stack, set `config.api_stack(enable_rl_module_and_learner=False,enable_env_runner_and_connector_v2=False)`. For a detailed migration guide, see here: https://docs.ray.io/en/master/rllib/new-api-stack-migration-guide.html
/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm.py:520: RayDeprecationWarning:
This API is deprecated and may be removed in future Ray releases. You could suppress this warning by setting env variable PYTHONWARNINGS="ignore::DeprecationWarning"
`UnifiedLogger` will be removed in Ray 2.7.
/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/tune/logger/unified.py:53: RayDeprecationWarning:
This API is deprecated and may be removed in future Ray releases. You could suppress this warning by setting env variable PYTHONWARNINGS="ignore::DeprecationWarning"
The `JsonLogger interface is deprecated in favor of the `ray.tune.json.JsonLoggerCallback` interface and will be removed in Ray 2.7.
/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/tune/logger/unified.py:53: RayDeprecationWarning:
This API is deprecated and may be removed in future Ray releases. You could suppress this warning by setting env variable PYTHONWARNINGS="ignore::DeprecationWarning"
The `CSVLogger interface is deprecated in favor of the `ray.tune.csv.CSVLoggerCallback` interface and will be removed in Ray 2.7.
/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/tune/logger/unified.py:53: RayDeprecationWarning:
This API is deprecated and may be removed in future Ray releases. You could suppress this warning by setting env variable PYTHONWARNINGS="ignore::DeprecationWarning"
The `TBXLogger interface is deprecated in favor of the `ray.tune.tensorboardx.TBXLoggerCallback` interface and will be removed in Ray 2.7.
2025-08-05 10:56:36,787 INFO worker.py:1927 -- Started a local Ray instance.
[2025-08-05 10:56:43,850 E 2909 2909] core_worker.cc:2740: Actor with class name: 'SingleAgentEnvRunner' and ID: '2734bb0191246c06db0e09b801000000' has constructor arguments in the object store and max_restarts > 0. If the arguments in the object store go out of scope or are lost, the actor restart will fail. See https://github.com/ray-project/ray/issues/53727 for more details.
[2025-08-05 10:56:43,921 E 2909 2909] core_worker.cc:2740: Actor with class name: 'SingleAgentEnvRunner' and ID: '6856b587ecd5df5b45d22f7501000000' has constructor arguments in the object store and max_restarts > 0. If the arguments in the object store go out of scope or are lost, the actor restart will fail. See https://github.com/ray-project/ray/issues/53727 for more details.
(SingleAgentEnvRunner pid=3055) /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/gymnasium/spaces/box.py:235: UserWarning: WARN: Box low's precision lowered by casting to float32, current low.dtype=float64
(SingleAgentEnvRunner pid=3055) gym.logger.warn(
(SingleAgentEnvRunner pid=3055) /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/gymnasium/spaces/box.py:305: UserWarning: WARN: Box high's precision lowered by casting to float32, current high.dtype=float64
(SingleAgentEnvRunner pid=3055) gym.logger.warn(
(SingleAgentEnvRunner pid=3055) 2025-08-05 10:56:48,201 WARNING deprecation.py:50 -- DeprecationWarning: `RLModule(config=[RLModuleConfig object])` has been deprecated. Use `RLModule(observation_space=.., action_space=.., inference_only=.., model_config=.., catalog_class=..)` instead. This will raise an error in the future!
2025-08-05 10:56:48,416 WARNING deprecation.py:50 -- DeprecationWarning: `RLModule(config=[RLModuleConfig object])` has been deprecated. Use `RLModule(observation_space=.., action_space=.., inference_only=.., model_config=.., catalog_class=..)` instead. This will raise an error in the future!
2025-08-05 10:56:48,446 WARNING algorithm_config.py:4921 -- You have specified 1 evaluation workers, but your `evaluation_interval` is 0 or None! Therefore, evaluation doesn't occur automatically with each call to `Algorithm.train()`. Instead, you have to call `Algorithm.evaluate()` manually in order to trigger an evaluation run.
[2025-08-05 10:56:48,494 E 2909 2909] core_worker.cc:2740: Actor with class name: 'SingleAgentEnvRunner' and ID: 'b72e8dc1a13bae46590f509001000000' has constructor arguments in the object store and max_restarts > 0. If the arguments in the object store go out of scope or are lost, the actor restart will fail. See https://github.com/ray-project/ray/issues/53727 for more details.
(autoscaler +40s) Tip: use `ray status` to view detailed cluster status. To disable these messages, set RAY_SCHEDULER_EVENTS=0.
(autoscaler +40s) Warning: The following resource request cannot be scheduled right now: {'CPU': 1.0}. This is likely due to all cluster resources being claimed by actors. Consider creating fewer actors or adding more nodes to this Ray cluster.
(autoscaler +1m15s) Warning: The following resource request cannot be scheduled right now: {'CPU': 1.0}. This is likely due to all cluster resources being claimed by actors. Consider creating fewer actors or adding more nodes to this Ray cluster.
(autoscaler +1m50s) Warning: The following resource request cannot be scheduled right now: {'CPU': 1.0}. This is likely due to all cluster resources being claimed by actors. Consider creating fewer actors or adding more nodes to this Ray cluster.
(autoscaler +2m25s) Warning: The following resource request cannot be scheduled right now: {'CPU': 1.0}. This is likely due to all cluster resources being claimed by actors. Consider creating fewer actors or adding more nodes to this Ray cluster.
---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
Cell In[20], line 21
10 register_env("StockTradingEnvGym", env_creator)
12 config = (
13 PPOConfig()
14 .environment("StockTradingEnvGym")
(...) 18 .evaluation(evaluation_num_env_runners=1)
19 )
---> 21 algo = config.build() # 2. build the algorithm,
23 for _ in range(10):
24 algo.train() # 3. train it,
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/rllib/utils/deprecation.py:128, in Deprecated.<locals>._inner.<locals>._ctor(*args, **kwargs)
121 deprecation_warning(
122 old=old or obj.__name__,
123 new=new,
124 help=help,
125 error=error,
126 )
127 # Call the deprecated method/function.
--> 128 return obj(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm_config.py:5794, in AlgorithmConfig.build(self, *args, **kwargs)
5792 @Deprecated(new="AlgorithmConfig.build_algo", error=False)
5793 def build(self, *args, **kwargs):
-> 5794 return self.build_algo(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm_config.py:1001, in AlgorithmConfig.build_algo(self, env, logger_creator, use_copy)
998 if isinstance(self.algo_class, str):
999 algo_class = get_trainable_cls(self.algo_class)
-> 1001 return algo_class(
1002 config=self if not use_copy else copy.deepcopy(self),
1003 logger_creator=self.logger_creator,
1004 )
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm.py:536, in Algorithm.__init__(self, config, env, logger_creator, **kwargs)
533 # Evaluation EnvRunnerGroup and metrics last returned by `self.evaluate()`.
534 self.eval_env_runner_group: Optional[EnvRunnerGroup] = None
--> 536 super().__init__(
537 config=config,
538 logger_creator=logger_creator,
539 **kwargs,
540 )
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/tune/trainable/trainable.py:158, in Trainable.__init__(self, config, logger_creator, storage)
154 logger.debug(f"StorageContext on the TRAINABLE:\n{storage}")
156 self._open_logfiles(stdout_file, stderr_file)
--> 158 self.setup(copy.deepcopy(self.config))
159 setup_time = time.time() - self._start_time
160 if setup_time > SETUP_TIME_THRESHOLD:
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm.py:677, in Algorithm.setup(self, config)
669 _, env_creator = self._get_env_id_and_creator(
670 self.evaluation_config.env, self.evaluation_config
671 )
673 # Create a separate evaluation worker set for evaluation.
674 # If evaluation_num_env_runners=0, use the evaluation set's local
675 # worker for evaluation, otherwise, use its remote workers
676 # (parallelized evaluation).
--> 677 self.eval_env_runner_group: EnvRunnerGroup = EnvRunnerGroup(
678 env_creator=env_creator,
679 validate_env=None,
680 default_policy_class=self.get_default_policy_class(self.config),
681 config=self.evaluation_config,
682 logdir=self.logdir,
683 tune_trial_id=self.trial_id,
684 # New API stack: User decides whether to create local env runner.
685 # Old API stack: Always create local EnvRunner.
686 local_env_runner=(
687 True
688 if not self.evaluation_config.enable_env_runner_and_connector_v2
689 else self.evaluation_config.create_local_env_runner
690 ),
691 pg_offset=self.config.num_env_runners,
692 )
694 if self.env_runner_group:
695 self.spaces = self.env_runner_group.get_spaces()
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/rllib/env/env_runner_group.py:198, in EnvRunnerGroup.__init__(self, env_creator, validate_env, default_policy_class, config, local_env_runner, logdir, _setup, tune_trial_id, pg_offset, num_env_runners, num_workers, local_worker)
196 if _setup:
197 try:
--> 198 self._setup(
199 validate_env=validate_env,
200 config=config,
201 num_env_runners=(
202 num_env_runners
203 if num_env_runners is not None
204 else config.num_env_runners
205 ),
206 local_env_runner=local_env_runner,
207 )
208 # EnvRunnerGroup creation possibly fails, if some (remote) workers cannot
209 # be initialized properly (due to some errors in the EnvRunners's
210 # constructor).
211 except RayActorError as e:
212 # In case of an actor (remote worker) init failure, the remote worker
213 # may still exist and will be accessible, however, e.g. calling
214 # its `sample.remote()` would result in strange "property not found"
215 # errors.
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/rllib/env/env_runner_group.py:272, in EnvRunnerGroup._setup(self, validate_env, config, num_env_runners, local_env_runner)
269 self._ds_shards = None
271 # Create a number of @ray.remote workers.
--> 272 self.add_workers(
273 num_env_runners,
274 validate=config.validate_env_runners_after_construction,
275 )
277 # If num_env_runners > 0 and we don't have an env on the local worker,
278 # get the observation- and action spaces for each policy from
279 # the first remote worker (which does have an env).
280 if (
281 local_env_runner
282 and self._worker_manager.num_actors() > 0
283 and not config.create_env_on_local_worker
284 and (not config.observation_space or not config.action_space)
285 ):
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/rllib/env/env_runner_group.py:750, in EnvRunnerGroup.add_workers(self, num_workers, validate)
747 # Validate here, whether all remote workers have been constructed properly
748 # and are "up and running". Establish initial states.
749 if validate:
--> 750 for result in self._worker_manager.foreach_actor(
751 lambda w: w.assert_healthy()
752 ):
753 # Simiply raise the error, which will get handled by the try-except
754 # clause around the _setup().
755 if not result.ok:
756 e = result.get()
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/rllib/utils/actor_manager.py:461, in FaultTolerantActorManager.foreach_actor(self, func, kwargs, healthy_only, remote_actor_ids, timeout_seconds, return_obj_refs, mark_healthy)
454 remote_calls = self._call_actors(
455 func=func,
456 kwargs=kwargs,
457 remote_actor_ids=remote_actor_ids,
458 )
460 # Collect remote request results (if available given timeout and/or errors).
--> 461 _, remote_results = self._fetch_result(
462 remote_actor_ids=remote_actor_ids,
463 remote_calls=remote_calls,
464 tags=[None] * len(remote_calls),
465 timeout_seconds=timeout_seconds,
466 return_obj_refs=return_obj_refs,
467 mark_healthy=mark_healthy,
468 )
470 return remote_results
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/rllib/utils/actor_manager.py:839, in FaultTolerantActorManager._fetch_result(self, remote_actor_ids, remote_calls, tags, timeout_seconds, return_obj_refs, mark_healthy)
836 if not remote_calls:
837 return [], RemoteCallResults()
--> 839 readies, _ = ray.wait(
840 remote_calls,
841 num_returns=len(remote_calls),
842 timeout=timeout,
843 # Make sure remote results are fetched locally in parallel.
844 fetch_local=not return_obj_refs,
845 )
847 # Remote data should already be fetched to local object store at this point.
848 remote_results = RemoteCallResults()
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/_private/auto_init_hook.py:22, in wrap_auto_init.<locals>.auto_init_wrapper(*args, **kwargs)
19 @wraps(fn)
20 def auto_init_wrapper(*args, **kwargs):
21 auto_init_ray()
---> 22 return fn(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/_private/client_mode_hook.py:104, in client_mode_hook.<locals>.wrapper(*args, **kwargs)
102 if func.__name__ != "init" or is_client_mode_enabled_by_default:
103 return getattr(ray, func.__name__)(*args, **kwargs)
--> 104 return func(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/ray/_private/worker.py:3089, in wait(ray_waitables, num_returns, timeout, fetch_local)
3087 timeout = timeout if timeout is not None else 10**6
3088 timeout_milliseconds = int(timeout * 1000)
-> 3089 ready_ids, remaining_ids = worker.core_worker.wait(
3090 ray_waitables,
3091 num_returns,
3092 timeout_milliseconds,
3093 fetch_local,
3094 )
3095 return ready_ids, remaining_ids
File python/ray/_raylet.pyx:3512, in ray._raylet.CoreWorker.wait()
File python/ray/includes/common.pxi:83, in ray._raylet.check_status()
KeyboardInterrupt:
# Evaluierung des Agents
env = StockTradingEnvGym(data)
state = env.reset()
done = False
total_reward = 0
actions = []
rewards = []
while not done:
action = algo.compute_single_action(env._get_obs(), state)
state, reward, done, _, _ = env.step(action[0])
total_reward += reward
actions.append(action)
rewards.append(total_reward)
env.render()
print("Gesamtbelohnung:", total_reward)
Step: 1, Price: 1.358449851840048, Position: 0, Balance: 10000
Step: 2, Price: 2.5061383899407406, Position: 0, Balance: 10000
Step: 3, Price: 4.529168246348766, Position: 0, Balance: 10000
Step: 4, Price: 4.79501487162543, Position: 1, Balance: 9995.204985128374
Step: 5, Price: 5.060877914676249, Position: 1, Balance: 9995.204985128374
Step: 6, Price: 7.1400907301836405, Position: 1, Balance: 9995.204985128374
Step: 7, Price: 8.407525459336549, Position: 1, Balance: 9995.204985128374
Step: 8, Price: 8.438051073401597, Position: 1, Balance: 9995.204985128374
Step: 9, Price: 9.480611116987562, Position: 1, Balance: 9995.204985128374
Step: 10, Price: 9.517193424175101, Position: 1, Balance: 9995.204985128374
Step: 11, Price: 9.551463670604845, Position: 1, Balance: 9995.204985128374
Step: 12, Price: 10.29342594217088, Position: 1, Balance: 9995.204985128374
Step: 13, Price: 8.88014569751308, Position: 1, Balance: 9995.204985128374
Step: 14, Price: 7.6552278650000485, Position: 1, Balance: 9995.204985128374
Step: 15, Price: 7.592940335759076, Position: 1, Balance: 9995.204985128374
Step: 16, Price: 7.0801092154246525, Position: 1, Balance: 9995.204985128374
Step: 17, Price: 7.8943565480199265, Position: 1, Balance: 9995.204985128374
Step: 18, Price: 7.486332472498716, Position: 1, Balance: 9995.204985128374
Step: 19, Price: 6.574028771163424, Position: 1, Balance: 9995.204985128374
Step: 20, Price: 8.539677540084979, Position: 1, Balance: 9995.204985128374
Step: 21, Price: 8.813901239598444, Position: 1, Balance: 9995.204985128374
Step: 22, Price: 9.381429444286368, Position: 1, Balance: 9995.204985128374
Step: 23, Price: 8.456681258072912, Position: 1, Balance: 9995.204985128374
Step: 24, Price: 8.41229853354773, Position: 1, Balance: 9995.204985128374
Step: 25, Price: 9.023221123257596, Position: 1, Balance: 9995.204985128374
Step: 26, Price: 8.372227545835292, Position: 1, Balance: 9995.204985128374
Step: 27, Price: 9.247925564180964, Position: 1, Balance: 9995.204985128374
Step: 28, Price: 9.147286874262159, Position: 1, Balance: 9995.204985128374
Step: 29, Price: 9.355593124468882, Position: 1, Balance: 9995.204985128374
Step: 30, Price: 9.253886512239486, Position: 1, Balance: 9995.204985128374
Step: 31, Price: 11.606164696748424, Position: 1, Balance: 9995.204985128374
Step: 32, Price: 12.09266747201049, Position: 1, Balance: 9995.204985128374
Step: 33, Price: 11.53495654305459, Position: 1, Balance: 9995.204985128374
Step: 34, Price: 12.85750145515778, Position: 1, Balance: 9995.204985128374
Step: 35, Price: 12.136657805186758, Position: 0, Balance: 9990.409970256747
Step: 36, Price: 12.845521400191513, Position: 0, Balance: 9990.409970256747
Step: 37, Price: 11.385851276311739, Position: 1, Balance: 9979.024118980436
Step: 38, Price: 10.557665227413308, Position: 1, Balance: 9979.024118980436
Step: 39, Price: 11.254526463282431, Position: 1, Balance: 9979.024118980436
Step: 40, Price: 12.492993043277842, Position: 1, Balance: 9979.024118980436
Step: 41, Price: 13.164361324467812, Position: 1, Balance: 9979.024118980436
Step: 42, Price: 13.548713042079571, Position: 1, Balance: 9979.024118980436
Step: 43, Price: 13.747609346490282, Position: 1, Balance: 9979.024118980436
Step: 44, Price: 12.769087356122855, Position: 1, Balance: 9979.024118980436
Step: 45, Price: 12.549243147728147, Position: 1, Balance: 9979.024118980436
Step: 46, Price: 12.588604376768359, Position: 1, Balance: 9979.024118980436
Step: 47, Price: 14.145726602987274, Position: 1, Balance: 9979.024118980436
Step: 48, Price: 14.989344892555735, Position: 1, Balance: 9979.024118980436
Step: 49, Price: 13.726304737193, Position: 1, Balance: 9979.024118980436
Step: 50, Price: 14.550388706587796, Position: 1, Balance: 9979.024118980436
Step: 51, Price: 14.66530642617148, Position: 1, Balance: 9979.024118980436
Step: 52, Price: 14.48838442586552, Position: 1, Balance: 9979.024118980436
Step: 53, Price: 15.600060714706387, Position: 1, Balance: 9979.024118980436
Step: 54, Price: 17.131060237202338, Position: 1, Balance: 9979.024118980436
Step: 55, Price: 18.562340356318536, Position: 1, Balance: 9979.024118980436
Step: 56, Price: 18.223122833095896, Position: 1, Balance: 9979.024118980436
Step: 57, Price: 18.413910457244683, Position: 1, Balance: 9979.024118980436
Step: 58, Price: 19.245173888648246, Position: 1, Balance: 9979.024118980436
Step: 59, Price: 20.720719015770605, Position: 1, Balance: 9979.024118980436
Step: 60, Price: 20.741544777925316, Position: 1, Balance: 9979.024118980436
Step: 61, Price: 21.0558858012615, Position: 1, Balance: 9979.024118980436
Step: 62, Price: 20.44955082725547, Position: 1, Balance: 9979.024118980436
Step: 63, Price: 19.7533442031748, Position: 1, Balance: 9979.024118980436
Step: 64, Price: 21.065870025568998, Position: 1, Balance: 9979.024118980436
Step: 65, Price: 22.92211005413982, Position: 1, Balance: 9979.024118980436
Step: 66, Price: 23.350099932559488, Position: 1, Balance: 9979.024118980436
Step: 67, Price: 24.853632830451513, Position: 1, Balance: 9979.024118980436
Step: 68, Price: 25.715268855499147, Position: 1, Balance: 9979.024118980436
Step: 69, Price: 25.570149100894024, Position: 0, Balance: 9967.638267704126
Step: 70, Price: 26.431544706402438, Position: 1, Balance: 9941.206722997724
Step: 71, Price: 28.469581272868407, Position: 1, Balance: 9941.206722997724
Step: 72, Price: 28.933755233758454, Position: 1, Balance: 9941.206722997724
Step: 73, Price: 30.99839888957246, Position: 1, Balance: 9941.206722997724
Step: 74, Price: 28.878653785482715, Position: 1, Balance: 9941.206722997724
Step: 75, Price: 30.20055628985794, Position: 1, Balance: 9941.206722997724
Step: 76, Price: 30.78760335809611, Position: 1, Balance: 9941.206722997724
Step: 77, Price: 30.988596007630242, Position: 0, Balance: 9914.775178291322
Step: 78, Price: 31.580356784165744, Position: 0, Balance: 9914.775178291322
Step: 79, Price: 30.09278786956485, Position: 0, Balance: 9914.775178291322
Step: 80, Price: 30.373115981727338, Position: 1, Balance: 9884.402062309595
Step: 81, Price: 31.230228553239083, Position: 1, Balance: 9884.402062309595
Step: 82, Price: 33.2081225979806, Position: 1, Balance: 9884.402062309595
Step: 83, Price: 33.18985237970695, Position: 1, Balance: 9884.402062309595
Step: 84, Price: 32.88135877681376, Position: 1, Balance: 9884.402062309595
Step: 85, Price: 32.87960173322922, Position: 1, Balance: 9884.402062309595
Step: 86, Price: 34.295003850931295, Position: 1, Balance: 9884.402062309595
Step: 87, Price: 35.12375496059098, Position: 1, Balance: 9884.402062309595
Step: 88, Price: 35.09399475682394, Position: 1, Balance: 9884.402062309595
Step: 89, Price: 36.107262189937295, Position: 1, Balance: 9884.402062309595
Step: 90, Price: 36.70433973928534, Position: 1, Balance: 9884.402062309595
Step: 91, Price: 38.172984729818225, Position: 1, Balance: 9884.402062309595
Step: 92, Price: 37.97093163594087, Position: 1, Balance: 9884.402062309595
Step: 93, Price: 38.143269489343105, Position: 0, Balance: 9854.028946327868
Step: 94, Price: 38.25116133621095, Position: -1, Balance: 9892.280107664079
Step: 95, Price: 37.28764638807883, Position: 0, Balance: 9930.53126900029
Step: 96, Price: 38.0837666651434, Position: 0, Balance: 9930.53126900029
Step: 97, Price: 38.84482193732329, Position: 1, Balance: 9891.686447062966
Step: 98, Price: 39.34993539396575, Position: 1, Balance: 9891.686447062966
Step: 99, Price: 39.6153482605906, Position: 1, Balance: 9891.686447062966
Step: 100, Price: 38.69997751854019, Position: 1, Balance: 9891.686447062966
Step: 101, Price: 38.77933219577483, Position: 0, Balance: 9852.841625125642
Step: 102, Price: 38.93661767924806, Position: 0, Balance: 9852.841625125642
Step: 103, Price: 38.63434041002644, Position: 0, Balance: 9852.841625125642
Step: 104, Price: 38.97305469836043, Position: 0, Balance: 9852.841625125642
Step: 105, Price: 39.877105555174964, Position: 0, Balance: 9852.841625125642
Step: 106, Price: 42.263291456385495, Position: 0, Balance: 9852.841625125642
Step: 107, Price: 42.93786926921734, Position: 0, Balance: 9852.841625125642
Step: 108, Price: 43.6954196599401, Position: 0, Balance: 9852.841625125642
Step: 109, Price: 44.12097374417393, Position: 0, Balance: 9852.841625125642
Step: 110, Price: 42.70220252887489, Position: 1, Balance: 9810.139422596767
Step: 111, Price: 43.175688653425674, Position: 1, Balance: 9810.139422596767
Step: 112, Price: 43.7359188633667, Position: 1, Balance: 9810.139422596767
Step: 113, Price: 46.69916097585198, Position: 1, Balance: 9810.139422596767
Step: 114, Price: 47.00680001107086, Position: 1, Balance: 9810.139422596767
Step: 115, Price: 47.80834735340447, Position: 1, Balance: 9810.139422596767
Step: 116, Price: 48.273635583699225, Position: 1, Balance: 9810.139422596767
Step: 117, Price: 47.604957546079696, Position: 1, Balance: 9810.139422596767
Step: 118, Price: 49.24778036059472, Position: 0, Balance: 9767.437220067892
Step: 119, Price: 50.49971339328149, Position: 1, Balance: 9716.93750667461
Step: 120, Price: 51.790745340324534, Position: 1, Balance: 9716.93750667461
Step: 121, Price: 51.381357885529795, Position: 1, Balance: 9716.93750667461
Step: 122, Price: 53.2841521964659, Position: 1, Balance: 9716.93750667461
Step: 123, Price: 52.382301133673614, Position: 1, Balance: 9716.93750667461
Step: 124, Price: 53.46915822747388, Position: 1, Balance: 9716.93750667461
Step: 125, Price: 56.159613853283865, Position: 1, Balance: 9716.93750667461
Step: 126, Price: 55.669077528153174, Position: 1, Balance: 9716.93750667461
Step: 127, Price: 55.6027797985504, Position: 1, Balance: 9716.93750667461
Step: 128, Price: 56.20243116363804, Position: 1, Balance: 9716.93750667461
Step: 129, Price: 56.19895550952184, Position: 1, Balance: 9716.93750667461
Step: 130, Price: 55.148292078455704, Position: 1, Balance: 9716.93750667461
Step: 131, Price: 55.71685505326173, Position: 1, Balance: 9716.93750667461
Step: 132, Price: 55.15455133953563, Position: 1, Balance: 9716.93750667461
Step: 133, Price: 56.12814377017081, Position: 1, Balance: 9716.93750667461
Step: 134, Price: 55.70871953593701, Position: 1, Balance: 9716.93750667461
Step: 135, Price: 57.758653940954545, Position: 1, Balance: 9716.93750667461
Step: 136, Price: 57.47540064861831, Position: 0, Balance: 9666.43779328133
Step: 137, Price: 57.65333913241263, Position: 0, Balance: 9666.43779328133
Step: 138, Price: 58.966856349782304, Position: 0, Balance: 9666.43779328133
Step: 139, Price: 58.235992033348346, Position: -1, Balance: 9724.673785314679
Step: 140, Price: 58.96345196795247, Position: -1, Balance: 9724.673785314679
Step: 141, Price: 60.770594722234904, Position: -1, Balance: 9724.673785314679
Step: 142, Price: 59.663111487673675, Position: -1, Balance: 9724.673785314679
Step: 143, Price: 60.347745346205976, Position: -1, Balance: 9724.673785314679
Step: 144, Price: 61.1076281404544, Position: -1, Balance: 9724.673785314679
Step: 145, Price: 62.38945101223171, Position: -1, Balance: 9724.673785314679
Step: 146, Price: 61.652500301353626, Position: 0, Balance: 9782.909777348028
Step: 147, Price: 60.83204368826935, Position: 1, Balance: 9722.077733659758
Step: 148, Price: 61.85398525388625, Position: 1, Balance: 9722.077733659758
Step: 149, Price: 62.650969927119434, Position: 1, Balance: 9722.077733659758
Step: 150, Price: 63.40146277746531, Position: 0, Balance: 9661.245689971489
Step: 151, Price: 64.24791098696228, Position: 1, Balance: 9596.997778984527
Step: 152, Price: 64.06788626538379, Position: 1, Balance: 9596.997778984527
Step: 153, Price: 64.80013996254479, Position: 1, Balance: 9596.997778984527
Step: 154, Price: 65.59321243584347, Position: 1, Balance: 9596.997778984527
Step: 155, Price: 65.3788610178171, Position: 1, Balance: 9596.997778984527
Step: 156, Price: 67.74463552896185, Position: 1, Balance: 9596.997778984527
Step: 157, Price: 68.71846844987364, Position: 1, Balance: 9596.997778984527
Step: 158, Price: 68.02716495267099, Position: 1, Balance: 9596.997778984527
Step: 159, Price: 69.18371856130481, Position: 1, Balance: 9596.997778984527
Step: 160, Price: 68.70903689107749, Position: 1, Balance: 9596.997778984527
Step: 161, Price: 69.99612149481995, Position: 1, Balance: 9596.997778984527
Step: 162, Price: 71.65471707382736, Position: 1, Balance: 9596.997778984527
Step: 163, Price: 71.33403475547564, Position: 1, Balance: 9596.997778984527
Step: 164, Price: 72.79741088471997, Position: 1, Balance: 9596.997778984527
Step: 165, Price: 73.71019181165646, Position: 1, Balance: 9596.997778984527
Step: 166, Price: 75.03225197165095, Position: 1, Balance: 9596.997778984527
Step: 167, Price: 77.4290449543049, Position: 1, Balance: 9596.997778984527
Step: 168, Price: 77.68365683830203, Position: 1, Balance: 9596.997778984527
Step: 169, Price: 77.42992067394454, Position: 1, Balance: 9596.997778984527
Step: 170, Price: 77.04040624431902, Position: 1, Balance: 9596.997778984527
Step: 171, Price: 76.72459595935358, Position: 1, Balance: 9596.997778984527
Step: 172, Price: 77.14749424993947, Position: 1, Balance: 9596.997778984527
Step: 173, Price: 77.98864622475611, Position: 1, Balance: 9596.997778984527
Step: 174, Price: 78.76533702408612, Position: 1, Balance: 9596.997778984527
Step: 175, Price: 80.09252027312215, Position: 1, Balance: 9596.997778984527
Step: 176, Price: 80.60552216500005, Position: 1, Balance: 9596.997778984527
Step: 177, Price: 82.55905624215737, Position: 0, Balance: 9532.749867997565
Step: 178, Price: 82.79439940891942, Position: 1, Balance: 9449.955468588645
Step: 179, Price: 86.01456857550903, Position: 1, Balance: 9449.955468588645
Step: 180, Price: 87.14023592327403, Position: 1, Balance: 9449.955468588645
Step: 181, Price: 86.78307836685775, Position: 1, Balance: 9449.955468588645
Step: 182, Price: 86.21218586879664, Position: 1, Balance: 9449.955468588645
Step: 183, Price: 87.19465828403982, Position: 1, Balance: 9449.955468588645
Step: 184, Price: 87.47119549871397, Position: 1, Balance: 9449.955468588645
Step: 185, Price: 88.68519599280606, Position: 1, Balance: 9449.955468588645
Step: 186, Price: 89.6584336173796, Position: 1, Balance: 9449.955468588645
Step: 187, Price: 90.08560470472273, Position: 1, Balance: 9449.955468588645
Step: 188, Price: 89.73881098665431, Position: 1, Balance: 9449.955468588645
Step: 189, Price: 88.72396376196845, Position: 1, Balance: 9449.955468588645
Step: 190, Price: 88.77744880990143, Position: 1, Balance: 9449.955468588645
Step: 191, Price: 90.1338476042249, Position: 1, Balance: 9449.955468588645
Step: 192, Price: 90.8479413483551, Position: 1, Balance: 9449.955468588645
Step: 193, Price: 90.10220256964313, Position: 0, Balance: 9367.161069179725
Step: 194, Price: 90.77538349549431, Position: 0, Balance: 9367.161069179725
Step: 195, Price: 91.66070087522314, Position: 0, Balance: 9367.161069179725
Step: 196, Price: 91.276843439022, Position: 0, Balance: 9367.161069179725
Step: 197, Price: 91.93056854496753, Position: 1, Balance: 9275.230500634758
Step: 198, Price: 92.48877726341352, Position: 1, Balance: 9275.230500634758
Step: 199, Price: 91.8458069655829, Position: 1, Balance: 9275.230500634758
Gesamtbelohnung: 146.08859019547393
px.line([a[0] for a in actions])
px.line(rewards)
Robotik#
DL ist insbesondere in der Robotik ein beliebtes Lernverfahren. Das liegt daran, das zum einen einfache Aufgaben, wie das Greifen von Objekten für Roboter hochkomplex sind und die Regelungen und Steuerungen sehr komplex zu entwickeln sind. Auch wir Menschen brauchen Wochen als Kleinkind um die Grob- und Feinmotorik dafür zu lernen. Trotzdem ist die Aufgabe und die Umgebung eines Roboters einfach zu simulieren. Deshalb setzt man vermehrt auf RL, um solche komplexen Steuerungsprobleme zu erlernen, statt selbstständisch Steuerungen zu entwickeln.
Robot Pusher#
Pusher-v4 ist eine Umgebung aus der gym-Bibliothek, die zur Simulation von Aufgaben im Bereich der Robotersteuerung verwendet wird. In dieser speziellen Umgebung wird ein Roboterarm simuliert, der darauf trainiert wird, ein Objekt zu einer bestimmten Zielposition zu schieben.
Das Ziel des Agenten (Roboterarms) in der Pusher-v4-Umgebung ist es, ein Objekt (in der Regel ein Block) zu einer festgelegten Zielposition zu schieben. Der Agent muss lernen, wie er seine Gelenke bewegen kann, um das Objekt erfolgreich zu schieben.
Der Aktionsraum ist kontinuierlich und repräsentiert die Steuerung des Roboterarms. Typischerweise handelt es sich um einen Vektor von Gelenkbewegungen, die der Agent ausführen kann.
Der Beobachtungsraum umfasst verschiedene Aspekte des Zustands der Umgebung, einschließlich der Position des Endeffektors des Roboterarms; die Position des zu schiebenden Objekts und die Zielposition, zu der das Objekt geschoben werden soll.
Die Belohnung in der Pusher-v4-Umgebung basiert darauf, wie nah das Objekt an der Zielposition ist. Der Agent erhält eine höhere Belohnung, wenn das Objekt näher an der Zielposition ist, und eine geringere Belohnung, wenn es weiter entfernt ist.
Da das Modell in Gym
enthalten ist, ist die Initialisierung einfach.
from ray.rllib.algorithms.ppo import PPOConfig
config = ( # 1. Configure the algorithm,
PPOConfig()
.environment("Pusher-v4", render_env=True)
.env_runners(num_env_runners=2)
.framework("torch")
.training()
.evaluation(evaluation_num_env_runners=1)
)
algo = config.build() # 2. build the algorithm,
for _ in range(5):
algo.train() # 3. train it,
#algo.evaluate() # 4. and evaluate it.
print(f"Execution Time: {time.time()-tic}")
2024-06-24 11:12:43,177 WARNING algorithm_config.py:4078 -- You have specified 1 evaluation workers, but your `evaluation_interval` is 0 or None! Therefore, evaluation will not occur automatically with each call to `Algorithm.train()`. Instead, you will have to call `Algorithm.evaluate()` manually in order to trigger an evaluation run.
/Users/jploennigs/miniconda3/envs/lehre4/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm.py:525: RayDeprecationWarning:
This API is deprecated and may be removed in future Ray releases. You could suppress this warning by setting env variable PYTHONWARNINGS="ignore::DeprecationWarning"
`UnifiedLogger` will be removed in Ray 2.7.
/Users/jploennigs/miniconda3/envs/lehre4/lib/python3.11/site-packages/ray/tune/logger/unified.py:53: RayDeprecationWarning:
This API is deprecated and may be removed in future Ray releases. You could suppress this warning by setting env variable PYTHONWARNINGS="ignore::DeprecationWarning"
The `JsonLogger interface is deprecated in favor of the `ray.tune.json.JsonLoggerCallback` interface and will be removed in Ray 2.7.
/Users/jploennigs/miniconda3/envs/lehre4/lib/python3.11/site-packages/ray/tune/logger/unified.py:53: RayDeprecationWarning:
This API is deprecated and may be removed in future Ray releases. You could suppress this warning by setting env variable PYTHONWARNINGS="ignore::DeprecationWarning"
The `CSVLogger interface is deprecated in favor of the `ray.tune.csv.CSVLoggerCallback` interface and will be removed in Ray 2.7.
/Users/jploennigs/miniconda3/envs/lehre4/lib/python3.11/site-packages/ray/tune/logger/unified.py:53: RayDeprecationWarning:
This API is deprecated and may be removed in future Ray releases. You could suppress this warning by setting env variable PYTHONWARNINGS="ignore::DeprecationWarning"
The `TBXLogger interface is deprecated in favor of the `ray.tune.tensorboardx.TBXLoggerCallback` interface and will be removed in Ray 2.7.
2024-06-24 11:12:47,084 WARNING algorithm_config.py:4078 -- You have specified 1 evaluation workers, but your `evaluation_interval` is 0 or None! Therefore, evaluation will not occur automatically with each call to `Algorithm.train()`. Instead, you will have to call `Algorithm.evaluate()` manually in order to trigger an evaluation run.
2024-06-24 11:12:49,605 WARNING util.py:61 -- Install gputil for GPU system monitoring.
Execution Time: 79.14778399467468
Betrachten wir einmal das Ergebnis einer zufälligen Bewegung, so sehen wir wie der Arm vorerst orientierungslos agiert.
Nach mehreren Trainingsepisoden lernt der Algorithmus allerdings den Arm gut zu benutzen. Hier ein Vergleich unterschiedlicher RL-Ansätze. Zu beobachten ist, dass über die Trainings-Episoden die Modelle durch Zufall die Lösung entdecken und dann wiederholen können. Die finalen Lösungen sind dabei aber auch nach vielen Trainings-Episoden nicht perfekt.
from IPython.display import IFrame
IFrame(width="800", height="413", src="https://www.youtube.com/embed/_QmcH1TyNwg", title="Gymnasium - Pusher-v4, Test with different algorithms",
frameborder="0", allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share", referrerpolicy="strict-origin-when-cross-origin", allowfullscreen=True)
Pick and Place#
“FetchPickAndPlaceDense-v2” ist eine Umgebung in der ein simulierter Roboterarm verwendet, um Objekte aufzuheben (Pick) und an einem Zielort abzulegen (Place).
Das Hauptziel des Agenten (Roboterarms) in der FetchPickAndPlaceDense-v2-Umgebung ist es, ein Objekt zu greifen (Pick) und es an einer festgelegten Zielposition abzulegen (Place). Der Agent muss lernen, wie er seine Gelenke und den Greifer steuern kann, um diese Aufgabe erfolgreich zu erfüllen.
Der Aktionsraum ist kontinuierlich und besteht aus den Koordinaten \(x, y, z\) der Hand des Roboterarms (Endeffektors) und die Öffnung des Greifers \(w\) (öffnen oder schließen). Der Beobachtungsraum enthält die Position und Geschwindigkeit des Endeffektors; die Position des zu bewegenden Objekts, die Zielposition, zu der das Objekt bewegt werden soll und den Zustand des Greifers (geöffnet oder geschlossen).
Die Umgebung bietet eine dichte Belohnungsstruktur, was bedeutet, dass der Agent kontinuierlich Rückmeldungen erhält, die ihn darauf hinweisen, wie gut er sich in Richtung seines Ziels bewegt. Die Belohnung basiert hauptsächlich darauf, wie nah das Objekt an der Zielposition ist und ob das Objekt erfolgreich gegriffen und bewegt wurde.
Auch hier ist die Initialisierung einfach.
from ray.rllib.algorithms.ppo import PPOConfig
config = ( # 1. Configure the algorithm,
PPOConfig()
.environment('FetchPickAndPlaceDense-v2')
.env_runners(num_env_runners=2)
.framework("torch")
.training()
.evaluation(evaluation_num_env_runners=1)
)
algo = config.build() # 2. build the algorithm,
for _ in range(5):
algo.train() # 3. train it,
#algo.evaluate() # 4. and evaluate it.
print(f"Execution Time: {time.time()-tic}")
2024-06-24 11:13:13,573 WARNING algorithm_config.py:4078 -- You have specified 1 evaluation workers, but your `evaluation_interval` is 0 or None! Therefore, evaluation will not occur automatically with each call to `Algorithm.train()`. Instead, you will have to call `Algorithm.evaluate()` manually in order to trigger an evaluation run.
2024-06-24 11:13:17,302 WARNING algorithm_config.py:4078 -- You have specified 1 evaluation workers, but your `evaluation_interval` is 0 or None! Therefore, evaluation will not occur automatically with each call to `Algorithm.train()`. Instead, you will have to call `Algorithm.evaluate()` manually in order to trigger an evaluation run.
2024-06-24 11:13:20,204 WARNING util.py:61 -- Install gputil for GPU system monitoring.
Execution Time: 119.0861268043518
Das RL-Modell lernt auch hier den Roboter Arm zu kontrolieren und die Kugel aufzunehmen.