Home Deep Rl Sac Ppo Artikel 1
Teil 1 Generative AI

RL-Grundlagen: MDPs, Reward, Policy

Bild: KI-generiert mit Beuys


RL-Grundlagen: MDPs, Reward, Policy

Reinforcement Learning ist weder überwacht noch unüberwacht – es lernt aus Konsequenzen. Dieser Artikel baut das Vokabular für die gesamte Serie: State, Action, Reward, Policy, Markov-Eigenschaft, Diskontierung, Bellman-Gleichung. Eine lauffähige Mini-Env aus reinem numpy macht die Begriffe greifbar.


1. Das Problem: Lernen ohne Lehrer, aber mit Folgen

Klassisches Supervised Learning bekommt zu jedem Input das richtige Label. Unsupervised Learning sucht Struktur ohne Label. Reinforcement Learning bekommt keine Labels – sondern Belohnungen, oft erst lange nach der Aktion.

"Reinforcement learning is learning what to do – how to map situations to actions – so as to maximize a numerical reward signal." – Sutton & Barto, Reinforcement Learning: An Introduction

Die folgende Tabelle trennt die drei Paradigmen:

Paradigma Trainings-Signal Typische Frage
Supervised $(x, y)$-Paare „Welches Label hat dieses Bild?"
Unsupervised nur $x$ „Welche Cluster gibt es?"
Reinforcement $(s, a, r, s')$-Tupel über Zeit „Welche Aktion maximiert langfristigen Reward?"

Der entscheidende Unterschied: In RL beeinflusst die Aktion des Agenten die zukünftigen Daten. Es gibt keine feste i.i.d.-Verteilung – die Verteilung ist die Policy.

2. Das MDP – das formale Gerüst

Ein Markov-Entscheidungsprozess ist das mathematische Standard-Modell für RL. Er ist ein Tupel $(S, A, P, R, \gamma)$:

Eine Policy $\pi(a \mid s)$ ist die Strategie des Agenten – eine Wahrscheinlichkeitsverteilung über Aktionen, gegeben den Zustand.

Quick-Check: Was bedeutet die Markov-Eigenschaft?

Wann ist die Markov-Annahme verletzt?

In der Praxis ständig. Ein Roboter sieht nur ein Kamerabild, nicht die Geschwindigkeit. Ein Trading-Agent sieht den Kurs, nicht die Orderbuch-Tiefe. Die übliche Antwort: Frame-Stacking (letzte $k$ Beobachtungen als Zustand) oder rekurrente Netze (LSTM/GRU als State-Komprimierer). Mathematisch wechselt man dann vom MDP zum POMDP (Partially Observable MDP) – konzeptionell aber bleibt der Rahmen gleich.

3. Rewards, Returns und Diskontierung

Ein einzelner Reward $r_t$ ist nicht das Lernziel. Der Agent maximiert den Return $G_t$ – die diskontierte Summe zukünftiger Rewards:

$$G_t = \sum_{k=0}^{\infty} \gamma^k r_{t+k+1}$$

Drei Spezialfälle machen $\gamma$ greifbar:

$\gamma$ Verhalten Beispiel
$0$ Greedy: nur unmittelbarer Reward zählt Multi-Armed-Bandit
$0.99$ Langer Horizont, „normaler" Default CartPole, Atari
$\to 1$ Quasi-unendlicher Horizont, Konvergenz heikel Lange Robotik-Trajektorien

Quick-Check: Was passiert mit $\gamma = 0$?

4. Value-Functions und die Bellman-Gleichung

Aus dem Return lassen sich zwei zentrale Funktionen ableiten:

Die Bellman-Gleichung zerlegt $V^\pi$ rekursiv:

$$V^\pi(s) = \mathbb{E}_{a \sim \pi, s' \sim P}\left[R(s, a) + \gamma V^\pi(s')\right]$$

In Worten: Der Wert eines Zustands ist der erwartete sofortige Reward plus der diskontierte Wert des nächsten Zustands. Diese Selbstbezüglichkeit ist der Hebel hinter sämtlichen Value-basierten RL-Verfahren (Q-Learning, DQN, SAC-Critic).

5. Eine lauffähige Mini-Welt: 1D-Gridworld

Statt gymnasium zu importieren (das im Pyodide-Playground nicht funktioniert), bauen wir eine winzige Umgebung selbst – als pure-numpy-Variante. Der Agent steht auf einer 1D-Linie der Länge 7. Aktion 0 = links, 1 = rechts. Ziel ist Feld 6 (Reward +1), Feld 0 ist Falle (Reward -1). Episode endet bei beiden Terminal-Zuständen.

import numpy as np

rng = np.random.default_rng(0)

class GridWorld1D:
    """Mini-MDP: 7 Felder, Start in der Mitte, Ziel rechts, Falle links."""
    def __init__(self, length: int = 7):
        self.length = length
        self.reset()

    def reset(self):
        self.state = self.length // 2
        return self.state

    def step(self, action: int):
        # 0 = links, 1 = rechts
        self.state += 1 if action == 1 else -1
        self.state = max(0, min(self.length - 1, self.state))
        done = self.state in (0, self.length - 1)
        if done:
            reward = 1.0 if self.state == self.length - 1 else -1.0
        else:
            reward = 0.0
        return self.state, reward, done


def run_episode(env, policy, max_steps: int = 50):
    s = env.reset()
    total, steps = 0.0, 0
    for _ in range(max_steps):
        a = policy(s)
        s, r, done = env.step(a)
        total += r
        steps += 1
        if done:
            break
    return total, steps


# Random-Policy als Baseline: 50/50 links/rechts.
random_policy = lambda s: int(rng.integers(0, 2))

env = GridWorld1D()
results = [run_episode(env, random_policy) for _ in range(500)]
returns = np.array([r for r, _ in results])
lengths = np.array([n for _, n in results])

print(f"Mittlerer Return : {returns.mean():+.3f}")
print(f"Win-Rate         : {(returns > 0).mean():.1%}")
print(f"Mittlere Länge   : {lengths.mean():.1f} Schritte")

Die Ausgabe sollte einen Return nahe 0 und eine Win-Rate nahe 50 % zeigen – exakt das, was eine Random-Policy in einer symmetrischen Welt liefert. Das ist die Baseline, die jeder gelernte Agent in den folgenden Artikeln schlagen muss.

Quick-Check: Warum ist die Random-Policy in diesem GridWorld nahe Return = 0?

6. RL-Loop als funktionale Idee

❌ Imperativ: alles in einer Schleife

total = 0
state = env.reset()
while True:
    action = pick_action(state)
    next_state, reward, done = env.step(action)
    total += reward
    state = next_state
    if done:
        break

Funktioniert, ist aber schwer zu testen und zu komponieren. Jeder Forschungs-Codebase, der so beginnt, endet in 800-Zeilen-Methoden.

✅ Funktional: Schritte als Datenstrom

from dataclasses import dataclass

@dataclass(frozen=True)
class Transition:
    state: int
    action: int
    reward: float
    next_state: int
    done: bool

def rollout(env, policy, max_steps=200):
    s = env.reset()
    for _ in range(max_steps):
        a = policy(s)
        s_next, r, done = env.step(a)
        yield Transition(s, a, r, s_next, done)
        if done:
            return
        s = s_next

Jede Transition ist ein unveränderliches Datenobjekt. rollout ist ein Generator – damit lassen sich Trajektorien streamen, filtern, batchen, ohne dass die Lern-Logik die Daten-Logik kennt. Genau dieses Muster nutzen wir in den Folgeartikeln für PPO und SAC.

7. Diskussion: Was RL schwer macht

Drei Schwierigkeiten begleiten uns durch die ganze Serie:

  1. Credit-Assignment – Der Reward kommt oft viele Schritte nach der ursächlichen Aktion. Welcher Move war eigentlich entscheidend?
  2. Exploration vs. Exploitation – Folgt der Agent dem aktuell besten Plan oder probiert er neue Aktionen? Falsche Balance → lokale Optima.
  3. Nicht-stationäre Daten – Die Trainings-Verteilung hängt von der aktuellen Policy ab. Was gestern gelernt wurde, kann heute schon veraltet sein.

8. Abschluss-Quiz

Welches Element gehört NICHT zum klassischen MDP-Tupel $(S, A, P, R, \gamma)$?

Was unterscheidet RL grundlegend von Supervised Learning?

Wofür steht das Q in $Q^\pi(s, a)$?

Welche Konsequenz hat ein hoher Diskontfaktor $\gamma$ nahe 1?

Welche der folgenden ist NICHT eine typische RL-Schwierigkeit?

9. Weiterführend

Quellen

📄 Als PDF