Home Deep Rl Sac Ppo Artikel 2
Teil 2 Generative AI

Von Q-Learning zu Policy Gradients

Bild: KI-generiert mit Beuys


Von Q-Learning zu Policy Gradients

Q-Learning ist elegant – solange der Aktionsraum diskret und klein ist. Sobald Roboterarme, Drehmomente oder Trading-Volumen ins Spiel kommen, scheitert die argmax-Operation. Dieser Artikel zeigt den begrifflichen Sprung von Value-basierten zu Policy-basierten Methoden, erklärt den Likelihood-Ratio-Trick und implementiert REINFORCE auf einer numpy-Mini-Welt.


1. Das Problem mit Q-Werten bei kontinuierlichen Aktionen

Q-Learning wählt Aktionen über $a^* = \arg\max_a Q(s, a)$. Diese Operation ist trivial, wenn $A$ klein und diskret ist (z. B. „links/rechts/oben/unten"). Sie wird sofort schwierig, sobald $A$ kontinuierlich ist:

Aktionsraum argmax praktikabel? Beispiel
Diskret, klein Ja – einfache Schleife CartPole (2 Aktionen)
Diskret, groß Schon problematisch Atari (18 Aktionen, ok), Schach (>1000)
Kontinuierlich, 1D Nein – Diskretisierung möglich, aber grob Trading-Volumen
Kontinuierlich, hochdim Nein – kombinatorische Explosion 7-DoF Roboterarm

❌ Workaround: Diskretisierung

# Naive: kontinuierliches Drehmoment [-1, 1] in 11 Bins.
actions = np.linspace(-1, 1, 11)
best_q = max(q_net(state, a) for a in actions)

Bei einem 7-DoF-Arm explodiert das auf $11^7 \approx 2 \cdot 10^7$ Aktionen pro Schritt – unbenutzbar.

✅ Idee: Policy direkt parametrisieren

Statt $Q(s, a)$ zu lernen und dann $\arg\max$ zu suchen, lernen wir die Policy selbst: $\pi_\theta(a \mid s)$ ist ein neuronales Netz, das aus $s$ direkt eine Verteilung über Aktionen ausgibt (oder bei kontinuierlichen Aktionen Parameter einer Gauß-Verteilung). Es gibt kein argmax mehr – Aktionen werden gesampelt.

Quick-Check: Warum kann Q-Learning kontinuierliche Aktionsräume nicht direkt behandeln?

2. Das Policy-Gradient-Objektiv

Der Agent maximiert den erwarteten Return unter seiner Policy:

$$J(\theta) = \mathbb{E}{\tau \sim \pi\theta}\left[\sum_{t=0}^{T} \gamma^t r_t\right]$$

Die naive Idee wäre, $J(\theta)$ direkt zu differenzieren. Problem: Die Erwartung ist über Trajektorien $\tau$, deren Verteilung selbst von $\theta$ abhängt. Wir können nicht einfach „Reward nach $\theta$ ableiten" – Rewards sind black-box.

3. Der Likelihood-Ratio-Trick

Der zentrale Kniff der Policy-Gradient-Theorie macht das Problem lösbar:

$$\nabla_\theta J(\theta) = \mathbb{E}{\tau \sim \pi\theta}\left[\sum_{t=0}^{T} \nabla_\theta \log \pi_\theta(a_t \mid s_t) \cdot G_t\right]$$

In Worten: Der Gradient des erwarteten Returns ist gleich dem Erwartungswert des Gradienten der Log-Policy, gewichtet mit dem Return. Beweis-Skizze in zwei Zeilen:

$$\nabla_\theta \mathbb{E}\pi[f] = \int \nabla\theta \pi \cdot f \, da = \int \pi \cdot \nabla_\theta \log \pi \cdot f \, da = \mathbb{E}\pi[\nabla\theta \log \pi \cdot f]$$

Die Substitution $\nabla \pi = \pi \cdot \nabla \log \pi$ ist der Trick. Das Ergebnis: Wir können den Gradienten per Sampling schätzen – wir brauchen die Dynamik der Umwelt nicht zu kennen.

Quick-Check: Wofür dient der Likelihood-Ratio-Trick im Policy-Gradient?

4. REINFORCE – der kleinste Policy-Gradient-Algorithmus

REINFORCE (Williams, 1992) ist die direkte Umsetzung der Formel oben:

  1. Sammle eine vollständige Episode unter $\pi_\theta$.
  2. Berechne für jeden Schritt den Return $G_t$.
  3. Update: $\theta \leftarrow \theta + \alpha \cdot \nabla_\theta \log \pi_\theta(a_t \mid s_t) \cdot G_t$.

Das ist die ganze Komplexität – ein Naturgesetz und ein Update-Schritt.

5. Lauffähig: REINFORCE auf 1D-Gridworld

Wir setzen denselben GridWorld-Aufbau wie in Artikel 01 ein und lernen eine policy, die das Ziel (Feld rechts) bevorzugt. Die Policy ist eine einfache Tabelle: pro Zustand ein Logit, daraus per Softmax Wahrscheinlichkeiten für links/rechts.

import numpy as np

rng = np.random.default_rng(0)
LENGTH = 7
GAMMA = 0.95
LR = 0.1
EPISODES = 2000

# Policy-Parameter: pro Zustand 2 Logits (links, rechts).
theta = np.zeros((LENGTH, 2))

def softmax(x):
    z = x - x.max()
    e = np.exp(z)
    return e / e.sum()

def policy_probs(s):
    return softmax(theta[s])

def sample_action(s):
    return int(rng.choice(2, p=policy_probs(s)))

def step(state, action):
    state += 1 if action == 1 else -1
    state = max(0, min(LENGTH - 1, state))
    done = state in (0, LENGTH - 1)
    if done:
        r = 1.0 if state == LENGTH - 1 else -1.0
    else:
        r = 0.0
    return state, r, done

def run_episode():
    s = LENGTH // 2
    transitions = []
    for _ in range(50):
        a = sample_action(s)
        s_next, r, done = step(s, a)
        transitions.append((s, a, r))
        s = s_next
        if done:
            break
    return transitions

def compute_returns(rewards):
    g, returns = 0.0, []
    for r in reversed(rewards):
        g = r + GAMMA * g
        returns.insert(0, g)
    return np.array(returns)

# Training.
ep_returns = []
for ep in range(EPISODES):
    traj = run_episode()
    states  = [t[0] for t in traj]
    actions = [t[1] for t in traj]
    rewards = [t[2] for t in traj]
    G = compute_returns(rewards)

    # REINFORCE-Update pro Schritt.
    for s, a, g in zip(states, actions, G):
        probs = policy_probs(s)
        grad_log = -probs.copy()
        grad_log[a] += 1.0      # ∂/∂θ_a log π(a|s) = 1 - π(a|s); für andere: -π
        theta[s] += LR * grad_log * g

    ep_returns.append(sum(rewards))

# Auswertung: erste vs. letzte 200 Episoden.
print(f"Mittlerer Return (erste 200) : {np.mean(ep_returns[:200]):+.3f}")
print(f"Mittlerer Return (letzte 200): {np.mean(ep_returns[-200:]):+.3f}")
print("Gelernte P(rechts) pro Zustand:")
for s in range(LENGTH):
    print(f"  s={s}: {policy_probs(s)[1]:.2f}")

Erwartetes Ergebnis: Der Return startet nahe 0 und steigt im Verlauf gegen +1. Die gelernte $P(\text{rechts})$ ist in der Mitte und besonders rechts deutlich erhöht – der Agent hat gelernt, dass „nach rechts" der profitable Default ist.

Quick-Check: Was passiert, wenn der Return $G_t$ negativ ist?

6. Die Schwäche von REINFORCE: Varianz

REINFORCE funktioniert – aber langsam. Der Grund ist hohe Varianz der Gradientenschätzung. Drei Quellen:

  1. Stochastische Umwelt: dieselbe Aktion liefert verschiedene Rewards.
  2. Stochastische Policy: dieselbe Situation führt zu verschiedenen Aktionen.
  3. Lange Episoden: ein einziger schlechter Move ganz am Anfang verändert alle $G_t$ der Episode.

Das Resultat: Der Schätzer ist erwartungstreu, aber sehr verrauscht – Gradient-Steps zeigen oft in die falsche Richtung, und nur die Mittelung über viele Episoden bringt das Lernen voran.

"REINFORCE is unbiased but high-variance. Almost every improvement in modern policy gradient methods – baselines, critics, GAE, trust regions – is a variance-reduction technique." – paraphrasiert nach Schulman et al., High-Dimensional Continuous Control (2015).

7. Diskussion: Value vs. Policy

Aspekt Value-basiert (DQN) Policy-basiert (REINFORCE)
Aktionsraum Diskret Diskret oder kontinuierlich
Output $Q$-Werte Aktions-Wahrscheinlichkeiten
Stochastische Policies Nur via $\epsilon$-Greedy Nativ
Off-Policy fähig Ja (Replay-Buffer) Schwierig (Importance Sampling)
Varianz der Gradienten Niedrig Hoch
Stabilität Mittel (Catastrophic Forgetting) Niedrig (kleine LR nötig)

Die nächsten Artikel (Actor-Critic, PPO, SAC) lösen die Varianzproblematik durch zusätzliche Critics, Trust-Regions und Off-Policy-Replay – und vereinen so die Stärken beider Welten.

8. Abschluss-Quiz

Was ist die Haupt-*Schwäche* von REINFORCE in der Praxis?

Welche Aussage zum Policy-Gradient-Theorem ist korrekt?

Eine Policy $\pi_\theta(a|s)$ wird bei kontinuierlichen Aktionen meist als …

Warum ist REINFORCE on-policy?

Welche Eigenschaft macht Policy-Gradients besonders attraktiv für Robotik?

9. Weiterführend

Quellen

📄 Als PDF