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.

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.

Komponenten und Begriffe beim Reinforcement Learning

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.

Beispiel eines MDP Zustandsdiagrams mit drei Zuständen

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:

\[ V(s) = \max_a \left( R(s, a) + \gamma \sum_{s'} P(s'|s, a) V(s') \right) \]

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.

\[ Q(s, a) \leftarrow Q(s, a) + \alpha \left( R(s, a) + \gamma \max_{a'} Q(s', a') - Q(s, a) \right) \]

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.

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

Zustandsraum von Gridworld

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)

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: 0.8142070770263672

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}")
Matplotlib is building the font cache; this may take a moment.
2025-04-08 07:55:24,060	WARNING algorithm_config.py:4355 -- 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-04-08 07:55:24,074	WARNING ppo.py:295 -- 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/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm.py:569: 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/miniconda3/envs/lehre/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/miniconda3/envs/lehre/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/miniconda3/envs/lehre/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-04-08 07:55:27,308	INFO worker.py:1821 -- Started a local Ray instance.
(SingleAgentEnvRunner pid=48135) /opt/miniconda3/envs/lehre/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=48135)   gym.logger.warn(
(SingleAgentEnvRunner pid=48135) /opt/miniconda3/envs/lehre/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=48135)   gym.logger.warn(
(SingleAgentEnvRunner pid=48135) 2025-04-08 07:55:31,359	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!
/opt/miniconda3/envs/lehre/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

/opt/miniconda3/envs/lehre/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

2025-04-08 07:55:31,445	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-04-08 07:55:31,462	WARNING algorithm_config.py:4355 -- 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-04-08 07:55:31,462	WARNING ppo.py:295 -- 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/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:642: UserWarning:

WARN: Overriding environment rllib-single-agent-env-v0 already in registry.

2025-04-08 07:55:34,020	WARNING rl_module.py:427 -- Could not create a Catalog object for your RLModule! If you are not using the new API stack yet, make sure to switch it off in your config: `config.api_stack(enable_rl_module_and_learner=False, enable_env_runner_and_connector_v2=False)`. Some algos already use the new stack by default. Ignore this message, if your RLModule does not use a Catalog to build its sub-components.
2025-04-08 07:55:34,910	INFO trainable.py:161 -- Trainable.setup took 10.709 seconds. If your trainable is slow to initialize, consider setting reuse_actors=True to reduce actor creation overheads.
(SingleAgentEnvRunner pid=48135) /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/utils/passive_env_checker.py:174: UserWarning: WARN: The default seed argument in `Env.reset` should be `None`, otherwise the environment will by default always be deterministic. Actual default: seed=0
(SingleAgentEnvRunner pid=48135)   logger.warn(
(SingleAgentEnvRunner pid=48135) /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/utils/passive_env_checker.py:134: UserWarning: WARN: The obs returned by the `reset()` method was expecting numpy array dtype to be float32, actual type: float64
(SingleAgentEnvRunner pid=48135)   logger.warn(
(SingleAgentEnvRunner pid=48135) /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/utils/passive_env_checker.py:158: UserWarning: WARN: The obs returned by the `reset()` method is not within the observation space.
(SingleAgentEnvRunner pid=48135)   logger.warn(f"{pre} is not within the observation space.")
(SingleAgentEnvRunner pid=48135) /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/utils/passive_env_checker.py:134: UserWarning: WARN: The obs returned by the `step()` method was expecting numpy array dtype to be float32, actual type: float64
(SingleAgentEnvRunner pid=48135)   logger.warn(
(SingleAgentEnvRunner pid=48135) /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/utils/passive_env_checker.py:158: UserWarning: WARN: The obs returned by the `step()` method is not within the observation space.
(SingleAgentEnvRunner pid=48135)   logger.warn(f"{pre} is not within the observation space.")
Execution Time: 44.63499689102173
# 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)
/opt/miniconda3/envs/lehre/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

/opt/miniconda3/envs/lehre/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
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[21], line 10
      8 rewards = []
      9 while not done:
---> 10     action = algo.compute_single_action(env._get_obs(), state)
     11     state, reward, done, _, _ = env.step(action[0])
     12     total_reward += reward

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm.py:2122, in Algorithm.compute_single_action(self, observation, state, prev_action, prev_reward, info, input_dict, policy_id, full_fetch, explore, timestep, episode, unsquash_action, clip_action, **kwargs)
   2118     assert observation is not None, err_msg
   2120 # Get the policy to compute the action for (in the multi-agent case,
   2121 # Algorithm may hold >1 policies).
-> 2122 policy = self.get_policy(policy_id)
   2123 if policy is None:
   2124     raise KeyError(
   2125         f"PolicyID '{policy_id}' not found in PolicyMap of the "
   2126         f"Algorithm's local worker!"
   2127     )

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm.py:1991, in Algorithm.get_policy(self, policy_id)
   1984 @OldAPIStack
   1985 def get_policy(self, policy_id: PolicyID = DEFAULT_POLICY_ID) -> Policy:
   1986     """Return policy for the specified id, or None.
   1987 
   1988     Args:
   1989         policy_id: ID of the policy to return.
   1990     """
-> 1991     return self.env_runner.get_policy(policy_id)

AttributeError: 'SingleAgentEnvRunner' object has no attribute 'get_policy'
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}")
2025-04-08 07:56:08,947	WARNING algorithm_config.py:4355 -- 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-04-08 07:56:08,947	WARNING deprecation.py:50 -- DeprecationWarning: `AlgorithmConfig.render_env` has been deprecated. The `render_env` setting is not supported on the new API stack! In order to log videos to WandB (or other loggers), take a look at this example here: https://github.com/ray-project/ray/blob/master/rllib/examples/envs/env_rendering_and_recording.py This will raise an error in the future!
2025-04-08 07:56:08,948	WARNING ppo.py:295 -- 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/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm.py:569: 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/miniconda3/envs/lehre/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/miniconda3/envs/lehre/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/miniconda3/envs/lehre/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.
/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:642: UserWarning:

WARN: Overriding environment rllib-single-agent-env-v0 already in registry.

/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:517: DeprecationWarning:

WARN: The environment Pusher-v4 is out of date. You should consider upgrading to version `v5`.

2025-04-08 07:56:14,949	WARNING algorithm_config.py:4355 -- 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-04-08 07:56:14,949	WARNING deprecation.py:50 -- DeprecationWarning: `AlgorithmConfig.render_env` has been deprecated. The `render_env` setting is not supported on the new API stack! In order to log videos to WandB (or other loggers), take a look at this example here: https://github.com/ray-project/ray/blob/master/rllib/examples/envs/env_rendering_and_recording.py This will raise an error in the future!
2025-04-08 07:56:14,950	WARNING ppo.py:295 -- 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/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:642: UserWarning:

WARN: Overriding environment rllib-single-agent-env-v0 already in registry.

/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:517: DeprecationWarning:

WARN: The environment Pusher-v4 is out of date. You should consider upgrading to version `v5`.

2025-04-08 07:56:17,580	WARNING rl_module.py:427 -- Could not create a Catalog object for your RLModule! If you are not using the new API stack yet, make sure to switch it off in your config: `config.api_stack(enable_rl_module_and_learner=False, enable_env_runner_and_connector_v2=False)`. Some algos already use the new stack by default. Ignore this message, if your RLModule does not use a Catalog to build its sub-components.
Execution Time: 71.48267698287964

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}")
2025-04-08 07:56:35,548	WARNING algorithm_config.py:4355 -- 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-04-08 07:56:35,548	WARNING ppo.py:295 -- 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/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm.py:569: 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/miniconda3/envs/lehre/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/miniconda3/envs/lehre/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/miniconda3/envs/lehre/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-04-08 07:56:38,962	ERROR actor_manager.py:804 -- Ray error (The actor died because of an error raised in its creation task, ray::SingleAgentEnvRunner.__init__() (pid=48136, ip=127.0.0.1, actor_id=df79e2c24809d4656d3737c501000000, repr=<ray.rllib.env.single_agent_env_runner.SingleAgentEnvRunner object at 0x3b8147dd0>)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 687, in make
    env_spec = _find_spec(id)
               ^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 531, in _find_spec
    _check_version_exists(ns, name, version)
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 397, in _check_version_exists
    _check_name_exists(ns, name)
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 374, in _check_name_exists
    raise error.NameNotFound(
gymnasium.error.NameNotFound: Environment `FetchPickAndPlaceDense` doesn't exist.

During handling of the above exception, another exception occurred:

ray::SingleAgentEnvRunner.__init__() (pid=48136, ip=127.0.0.1, actor_id=df79e2c24809d4656d3737c501000000, repr=<ray.rllib.env.single_agent_env_runner.SingleAgentEnvRunner object at 0x3b8147dd0>)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/single_agent_env_runner.py", line 89, in __init__
    self.make_env()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/single_agent_env_runner.py", line 627, in make_env
    gym.make_vec(
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 918, in make_vec
    env = gym.vector.SyncVectorEnv(
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/vector/sync_vector_env.py", line 86, in __init__
    self.envs = [env_fn() for env_fn in env_fns]
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/vector/sync_vector_env.py", line 86, in <listcomp>
    self.envs = [env_fn() for env_fn in env_fns]
                 ^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 903, in create_single_env
    single_env = make(env_spec, **env_spec_kwargs.copy())
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 740, in make
    env = env_creator(**env_spec_kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/utils/__init__.py", line 122, in _gym_env_creator
    raise EnvError(ERR_MSG_INVALID_ENV_DESCRIPTOR.format(env_descriptor))
ray.rllib.utils.error.EnvError: The env string you provided ('FetchPickAndPlaceDense-v2') is:
a) Not a supported or -installed environment.
b) Not a tune-registered environment creator.
c) Not a valid env class string.

Try one of the following:
a) For Atari support: `pip install gym[atari] autorom[accept-rom-license]`.
   For PyBullet support: `pip install pybullet`.
b) To register your custom env, do `from ray import tune;
   tune.register('[name]', lambda cfg: [return env obj from here using cfg])`.
   Then in your config, do `config['env'] = [name]`.
c) Make sure you provide a fully qualified classpath, e.g.:
   `ray.rllib.examples.envs.classes.repeat_after_me_env.RepeatAfterMeEnv`), taking actor 1 out of service.
2025-04-08 07:56:38,963	ERROR actor_manager.py:804 -- Ray error (The actor died because of an error raised in its creation task, ray::SingleAgentEnvRunner.__init__() (pid=48137, ip=127.0.0.1, actor_id=4b4e389efbedaac8ae189a7401000000, repr=<ray.rllib.env.single_agent_env_runner.SingleAgentEnvRunner object at 0x3b92cbf50>)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 687, in make
    env_spec = _find_spec(id)
               ^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 531, in _find_spec
    _check_version_exists(ns, name, version)
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 397, in _check_version_exists
    _check_name_exists(ns, name)
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 374, in _check_name_exists
    raise error.NameNotFound(
gymnasium.error.NameNotFound: Environment `FetchPickAndPlaceDense` doesn't exist.

During handling of the above exception, another exception occurred:

ray::SingleAgentEnvRunner.__init__() (pid=48137, ip=127.0.0.1, actor_id=4b4e389efbedaac8ae189a7401000000, repr=<ray.rllib.env.single_agent_env_runner.SingleAgentEnvRunner object at 0x3b92cbf50>)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/single_agent_env_runner.py", line 89, in __init__
    self.make_env()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/single_agent_env_runner.py", line 627, in make_env
    gym.make_vec(
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 918, in make_vec
    env = gym.vector.SyncVectorEnv(
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/vector/sync_vector_env.py", line 86, in __init__
    self.envs = [env_fn() for env_fn in env_fns]
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/vector/sync_vector_env.py", line 86, in <listcomp>
    self.envs = [env_fn() for env_fn in env_fns]
                 ^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 903, in create_single_env
    single_env = make(env_spec, **env_spec_kwargs.copy())
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 740, in make
    env = env_creator(**env_spec_kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/utils/__init__.py", line 122, in _gym_env_creator
    raise EnvError(ERR_MSG_INVALID_ENV_DESCRIPTOR.format(env_descriptor))
ray.rllib.utils.error.EnvError: The env string you provided ('FetchPickAndPlaceDense-v2') is:
a) Not a supported or -installed environment.
b) Not a tune-registered environment creator.
c) Not a valid env class string.

Try one of the following:
a) For Atari support: `pip install gym[atari] autorom[accept-rom-license]`.
   For PyBullet support: `pip install pybullet`.
b) To register your custom env, do `from ray import tune;
   tune.register('[name]', lambda cfg: [return env obj from here using cfg])`.
   Then in your config, do `config['env'] = [name]`.
c) Make sure you provide a fully qualified classpath, e.g.:
   `ray.rllib.examples.envs.classes.repeat_after_me_env.RepeatAfterMeEnv`), taking actor 2 out of service.
2025-04-08 07:56:38,963	ERROR env_runner_group.py:805 -- Validation of EnvRunner failed! Error=The actor died because of an error raised in its creation task, ray::SingleAgentEnvRunner.__init__() (pid=48136, ip=127.0.0.1, actor_id=df79e2c24809d4656d3737c501000000, repr=<ray.rllib.env.single_agent_env_runner.SingleAgentEnvRunner object at 0x3b8147dd0>)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 687, in make
    env_spec = _find_spec(id)
               ^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 531, in _find_spec
    _check_version_exists(ns, name, version)
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 397, in _check_version_exists
    _check_name_exists(ns, name)
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 374, in _check_name_exists
    raise error.NameNotFound(
gymnasium.error.NameNotFound: Environment `FetchPickAndPlaceDense` doesn't exist.

During handling of the above exception, another exception occurred:

ray::SingleAgentEnvRunner.__init__() (pid=48136, ip=127.0.0.1, actor_id=df79e2c24809d4656d3737c501000000, repr=<ray.rllib.env.single_agent_env_runner.SingleAgentEnvRunner object at 0x3b8147dd0>)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/single_agent_env_runner.py", line 89, in __init__
    self.make_env()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/single_agent_env_runner.py", line 627, in make_env
    gym.make_vec(
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 918, in make_vec
    env = gym.vector.SyncVectorEnv(
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/vector/sync_vector_env.py", line 86, in __init__
    self.envs = [env_fn() for env_fn in env_fns]
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/vector/sync_vector_env.py", line 86, in <listcomp>
    self.envs = [env_fn() for env_fn in env_fns]
                 ^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 903, in create_single_env
    single_env = make(env_spec, **env_spec_kwargs.copy())
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 740, in make
    env = env_creator(**env_spec_kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/utils/__init__.py", line 122, in _gym_env_creator
    raise EnvError(ERR_MSG_INVALID_ENV_DESCRIPTOR.format(env_descriptor))
ray.rllib.utils.error.EnvError: The env string you provided ('FetchPickAndPlaceDense-v2') is:
a) Not a supported or -installed environment.
b) Not a tune-registered environment creator.
c) Not a valid env class string.

Try one of the following:
a) For Atari support: `pip install gym[atari] autorom[accept-rom-license]`.
   For PyBullet support: `pip install pybullet`.
b) To register your custom env, do `from ray import tune;
   tune.register('[name]', lambda cfg: [return env obj from here using cfg])`.
   Then in your config, do `config['env'] = [name]`.
c) Make sure you provide a fully qualified classpath, e.g.:
   `ray.rllib.examples.envs.classes.repeat_after_me_env.RepeatAfterMeEnv`
2025-04-08 07:56:38,964	ERROR env_runner_group.py:805 -- Validation of EnvRunner failed! Error=The actor died because of an error raised in its creation task, ray::SingleAgentEnvRunner.__init__() (pid=48137, ip=127.0.0.1, actor_id=4b4e389efbedaac8ae189a7401000000, repr=<ray.rllib.env.single_agent_env_runner.SingleAgentEnvRunner object at 0x3b92cbf50>)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 687, in make
    env_spec = _find_spec(id)
               ^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 531, in _find_spec
    _check_version_exists(ns, name, version)
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 397, in _check_version_exists
    _check_name_exists(ns, name)
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 374, in _check_name_exists
    raise error.NameNotFound(
gymnasium.error.NameNotFound: Environment `FetchPickAndPlaceDense` doesn't exist.

During handling of the above exception, another exception occurred:

ray::SingleAgentEnvRunner.__init__() (pid=48137, ip=127.0.0.1, actor_id=4b4e389efbedaac8ae189a7401000000, repr=<ray.rllib.env.single_agent_env_runner.SingleAgentEnvRunner object at 0x3b92cbf50>)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/single_agent_env_runner.py", line 89, in __init__
    self.make_env()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/single_agent_env_runner.py", line 627, in make_env
    gym.make_vec(
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 918, in make_vec
    env = gym.vector.SyncVectorEnv(
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/vector/sync_vector_env.py", line 86, in __init__
    self.envs = [env_fn() for env_fn in env_fns]
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/vector/sync_vector_env.py", line 86, in <listcomp>
    self.envs = [env_fn() for env_fn in env_fns]
                 ^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 903, in create_single_env
    single_env = make(env_spec, **env_spec_kwargs.copy())
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py", line 740, in make
    env = env_creator(**env_spec_kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/utils/__init__.py", line 122, in _gym_env_creator
    raise EnvError(ERR_MSG_INVALID_ENV_DESCRIPTOR.format(env_descriptor))
ray.rllib.utils.error.EnvError: The env string you provided ('FetchPickAndPlaceDense-v2') is:
a) Not a supported or -installed environment.
b) Not a tune-registered environment creator.
c) Not a valid env class string.

Try one of the following:
a) For Atari support: `pip install gym[atari] autorom[accept-rom-license]`.
   For PyBullet support: `pip install pybullet`.
b) To register your custom env, do `from ray import tune;
   tune.register('[name]', lambda cfg: [return env obj from here using cfg])`.
   Then in your config, do `config['env'] = [name]`.
c) Make sure you provide a fully qualified classpath, e.g.:
   `ray.rllib.examples.envs.classes.repeat_after_me_env.RepeatAfterMeEnv`
/opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:642: UserWarning:

WARN: Overriding environment rllib-single-agent-env-v0 already in registry.
---------------------------------------------------------------------------
NameNotFound                              Traceback (most recent call last)
File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/utils/__init__.py:120, in _gym_env_creator(env_context, env_descriptor)
    119     else:
--> 120         env = gym.make(env_descriptor, **env_context)
    121 except gym.error.Error:

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:687, in make(id, max_episode_steps, disable_env_checker, **kwargs)
    686     # The environment name can include an unloaded module in "module:env_name" style
--> 687     env_spec = _find_spec(id)
    689 assert isinstance(env_spec, EnvSpec)

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:531, in _find_spec(env_id)
    530 if env_spec is None:
--> 531     _check_version_exists(ns, name, version)
    532     raise error.Error(
    533         f"No registered env with id: {env_name}. Did you register it, or import the package that registers it? Use `gymnasium.pprint_registry()` to see all of the registered environments."
    534     )

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:397, in _check_version_exists(ns, name, version)
    395     return
--> 397 _check_name_exists(ns, name)
    398 if version is None:

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:374, in _check_name_exists(ns, name)
    372 suggestion_msg = f" Did you mean: `{suggestion[0]}`?" if suggestion else ""
--> 374 raise error.NameNotFound(
    375     f"Environment `{name}` doesn't exist{namespace_msg}.{suggestion_msg}"
    376 )

NameNotFound: Environment `FetchPickAndPlaceDense` doesn't exist.

During handling of the above exception, another exception occurred:

EnvError                                  Traceback (most recent call last)
Cell In[26], line 12
      1 from ray.rllib.algorithms.ppo import PPOConfig
      3 config = (  # 1. Configure the algorithm,
      4     PPOConfig()
      5     .environment('FetchPickAndPlaceDense-v2')
   (...)
      9     .evaluation(evaluation_num_env_runners=1)
     10 )
---> 12 algo = config.build()  # 2. build the algorithm,
     14 for _ in range(5):
     15     algo.train()  # 3. train it,

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm_config.py:949, in AlgorithmConfig.build(self, env, logger_creator, use_copy)
    946 if isinstance(self.algo_class, str):
    947     algo_class = get_trainable_cls(self.algo_class)
--> 949 return algo_class(
    950     config=self if not use_copy else copy.deepcopy(self),
    951     logger_creator=self.logger_creator,
    952 )

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm.py:585, in Algorithm.__init__(self, config, env, logger_creator, **kwargs)
    582 # Evaluation EnvRunnerGroup and metrics last returned by `self.evaluate()`.
    583 self.eval_env_runner_group: Optional[EnvRunnerGroup] = None
--> 585 super().__init__(
    586     config=config,
    587     logger_creator=logger_creator,
    588     **kwargs,
    589 )

File /opt/miniconda3/envs/lehre/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/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/algorithms/algorithm.py:671, in Algorithm.setup(self, config)
    668     self.config.off_policy_estimation_methods = ope_dict
    670 # Create a set of env runner actors via a EnvRunnerGroup.
--> 671 self.env_runner_group = EnvRunnerGroup(
    672     env_creator=self.env_creator,
    673     validate_env=self.validate_env,
    674     default_policy_class=self.get_default_policy_class(self.config),
    675     config=self.config,
    676     num_env_runners=(
    677         0
    678         if (
    679             self.config.input_
    680             and (
    681                 isinstance(self.config.input_, str)
    682                 or (
    683                     isinstance(self.config.input_, list)
    684                     and isinstance(self.config.input_[0], str)
    685                 )
    686             )
    687             and self.config.input_ != "sampler"
    688             and self.config.enable_rl_module_and_learner
    689         )
    690         else self.config.num_env_runners
    691     ),
    692     local_env_runner=True,
    693     logdir=self.logdir,
    694     tune_trial_id=self.trial_id,
    695 )
    697 # If an input path is available and we are on the new API stack generate
    698 # an `OfflineData` instance.
    699 if (
    700     self.config.input_
    701     and (
   (...)
    709     and self.config.enable_rl_module_and_learner
    710 ):

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/env_runner_group.py:194, in EnvRunnerGroup.__init__(self, env_creator, validate_env, default_policy_class, config, num_env_runners, local_env_runner, logdir, _setup, tune_trial_id, num_workers, local_worker)
    192 if _setup:
    193     try:
--> 194         self._setup(
    195             validate_env=validate_env,
    196             config=config,
    197             num_env_runners=num_env_runners,
    198             local_env_runner=local_env_runner,
    199         )
    200     # EnvRunnerGroup creation possibly fails, if some (remote) workers cannot
    201     # be initialized properly (due to some errors in the EnvRunners's
    202     # constructor).
    203     except RayActorError as e:
    204         # In case of an actor (remote worker) init failure, the remote worker
    205         # may still exist and will be accessible, however, e.g. calling
    206         # its `sample.remote()` would result in strange "property not found"
    207         # errors.

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/env_runner_group.py:285, in EnvRunnerGroup._setup(self, validate_env, config, num_env_runners, local_env_runner)
    283 # Create a local worker, if needed.
    284 if local_env_runner:
--> 285     self._local_env_runner = self._make_worker(
    286         cls=self.env_runner_cls,
    287         env_creator=self._env_creator,
    288         validate_env=validate_env,
    289         worker_index=0,
    290         num_workers=num_env_runners,
    291         config=self._local_config,
    292         spaces=spaces,
    293     )

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/env_runner_group.py:1190, in EnvRunnerGroup._make_worker(self, cls, env_creator, validate_env, worker_index, num_workers, recreated_worker, config, spaces)
   1176 def _make_worker(
   1177     self,
   1178     *,
   (...)
   1188     ] = None,
   1189 ) -> Union[EnvRunner, ActorHandle]:
-> 1190     worker = cls(
   1191         env_creator=env_creator,
   1192         validate_env=validate_env,
   1193         default_policy_class=self._policy_class,
   1194         config=config,
   1195         worker_index=worker_index,
   1196         num_workers=num_workers,
   1197         recreated_worker=recreated_worker,
   1198         log_dir=self._logdir,
   1199         spaces=spaces,
   1200         dataset_shards=self._ds_shards,
   1201         tune_trial_id=self._tune_trial_id,
   1202     )
   1204     return worker

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/single_agent_env_runner.py:89, in SingleAgentEnvRunner.__init__(self, config, **kwargs)
     87 self.env: Optional[gym.vector.VectorEnvWrapper] = None
     88 self.num_envs: int = 0
---> 89 self.make_env()
     91 # Create the env-to-module connector pipeline.
     92 self._env_to_module = self.config.build_env_to_module_connector(self.env)

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/single_agent_env_runner.py:627, in SingleAgentEnvRunner.make_env(self)
    619     entry_point = partial(
    620         _gym_env_creator,
    621         env_descriptor=self.config.env,
    622         env_context=env_ctx,
    623     )
    624 gym.register("rllib-single-agent-env-v0", entry_point=entry_point)
    626 self.env = DictInfoToList(
--> 627     gym.make_vec(
    628         "rllib-single-agent-env-v0",
    629         num_envs=self.config.num_envs_per_env_runner,
    630         vectorization_mode=(
    631             VectorizeMode.ASYNC
    632             if self.config.remote_worker_envs
    633             else VectorizeMode.SYNC
    634         ),
    635     )
    636 )
    638 self.num_envs: int = self.env.num_envs
    639 assert self.num_envs == self.config.num_envs_per_env_runner

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:918, in make_vec(id, num_envs, vectorization_mode, vector_kwargs, wrappers, **kwargs)
    913     if env_spec.entry_point is None:
    914         raise error.Error(
    915             f"Cannot create vectorized environment for {env_spec.id} because it doesn't have an entry point defined."
    916         )
--> 918     env = gym.vector.SyncVectorEnv(
    919         env_fns=(create_single_env for _ in range(num_envs)),
    920         **vector_kwargs,
    921     )
    922 elif vectorization_mode == VectorizeMode.ASYNC:
    923     if env_spec.entry_point is None:

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/vector/sync_vector_env.py:86, in SyncVectorEnv.__init__(self, env_fns, copy, observation_mode)
     83 self.observation_mode = observation_mode
     85 # Initialise all sub-environments
---> 86 self.envs = [env_fn() for env_fn in env_fns]
     88 # Define core attributes using the sub-environments
     89 # As we support `make_vec(spec)` then we can't include a `spec = self.envs[0].spec` as this doesn't guarantee we can actual recreate the vector env.
     90 self.num_envs = len(self.envs)

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/vector/sync_vector_env.py:86, in <listcomp>(.0)
     83 self.observation_mode = observation_mode
     85 # Initialise all sub-environments
---> 86 self.envs = [env_fn() for env_fn in env_fns]
     88 # Define core attributes using the sub-environments
     89 # As we support `make_vec(spec)` then we can't include a `spec = self.envs[0].spec` as this doesn't guarantee we can actual recreate the vector env.
     90 self.num_envs = len(self.envs)

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:903, in make_vec.<locals>.create_single_env()
    902 def create_single_env() -> Env:
--> 903     single_env = make(env_spec, **env_spec_kwargs.copy())
    905     if wrappers is None:
    906         return single_env

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/gymnasium/envs/registration.py:740, in make(id, max_episode_steps, disable_env_checker, **kwargs)
    734         logger.warn(
    735             f"The environment is being initialised with render_mode={render_mode!r} "
    736             f"that is not in the possible render_modes ({render_modes})."
    737         )
    739 try:
--> 740     env = env_creator(**env_spec_kwargs)
    741 except TypeError as e:
    742     if (
    743         str(e).find("got an unexpected keyword argument 'render_mode'") >= 0
    744         and apply_human_rendering
    745     ):

File /opt/miniconda3/envs/lehre/lib/python3.11/site-packages/ray/rllib/env/utils/__init__.py:122, in _gym_env_creator(env_context, env_descriptor)
    120         env = gym.make(env_descriptor, **env_context)
    121 except gym.error.Error:
--> 122     raise EnvError(ERR_MSG_INVALID_ENV_DESCRIPTOR.format(env_descriptor))
    124 return env

EnvError: The env string you provided ('FetchPickAndPlaceDense-v2') is:
a) Not a supported or -installed environment.
b) Not a tune-registered environment creator.
c) Not a valid env class string.

Try one of the following:
a) For Atari support: `pip install gym[atari] autorom[accept-rom-license]`.
   For PyBullet support: `pip install pybullet`.
b) To register your custom env, do `from ray import tune;
   tune.register('[name]', lambda cfg: [return env obj from here using cfg])`.
   Then in your config, do `config['env'] = [name]`.
c) Make sure you provide a fully qualified classpath, e.g.:
   `ray.rllib.examples.envs.classes.repeat_after_me_env.RepeatAfterMeEnv`

Das RL-Modell lernt auch hier den Roboter Arm zu kontrolieren und die Kugel aufzunehmen.