Reinforcement Learning trainiert Agenten durch Trial-and-Error. Der Agent nimmt Actions, erhält Rewards, und optimiert seine Policy.

Agent in Environment:
  State: [Position des Agenten, Positionen Gegner, ...]
  Action: [Nach rechts gehen, Schießen, Jump]
  Reward: [+1 für Treffer, -1 für Treffer bekommen, -0.01 pro Schritt]
  → Agent lernt optimale Strategy durch Wiederholung

Kernkonzepte

States (S)

Die aktuelle Situation des Agenten.

Game: State = [Player Position, Enemy Position, Health, Mana]
       State = [50, 120, 100, 80]

Robot: State = [Joint Angles, Joint Velocities, Sensor Readings]
       State = [0.5rad, 0.2rad/s, distance_to_obstacle=2m]

Stock Trading: State = [Current Price, Price History (30d), Volume, Volatility]

Actions (A)

Was der Agent tun kann.

Game: Actions = {UP, DOWN, LEFT, RIGHT, ATTACK, DEFEND}

Robot: Actions = Continuous [θ1, θ2, θ3] (Torques auf Joints)

Trading: Actions = {BUY, HOLD, SELL}

Rewards (R)

Feedback für Agenten.

Game:
  +1: Enemy getötet
  -1: Agent genomen Schaden
  -0.01: Pro Schritt (motiviert schnelle Lösung)

Robot:
  +1: Erreiche Goal
  -0.1: Fall hin
  -0.001: Pro Schritt mit hohem Torque (energieeffizient)

Trading:
  +10: Gewinn gemacht
  -10: Verlust gemacht

Policies (π)

Die Strategy des Agenten: "Was soll ich tun in dieser Situation?"

Deterministic Policy:
  π(s) = a  (gegeben State s, immer Action a)
  z.B. IF enemy_near THEN ATTACK

Stochastic Policy:
  π(a|s) = P(action | state)
  z.B. 70% ATTACK, 20% DEFEND, 10% MOVE

Policy Parametrization (Neural Network):
  θ = Gewichte des Netzwerks
  π_θ(a|s) = Network(s) → [0.7, 0.2, 0.1]

Policy Gradient Methods

REINFORCE (Basis)

Einfachster Policy Gradient Algorithmus.

import torch
import torch.nn.functional as F

class PolicyNetwork(torch.nn.Module):
    def __init__(self, state_dim, action_dim):
        super().__init__()
        self.fc1 = torch.nn.Linear(state_dim, 128)
        self.fc2 = torch.nn.Linear(128, action_dim)

    def forward(self, state):
        x = F.relu(self.fc1(state))
        action_probs = F.softmax(self.fc2(x), dim=-1)
        return action_probs

# Training
policy = PolicyNetwork(state_dim=10, action_dim=4)
optimizer = torch.optim.Adam(policy.parameters(), lr=0.01)

# Episode
states, actions, rewards = [], [], []
state = env.reset()

while not done:
    probs = policy(state)
    action = torch.multinomial(probs, 1).item()
    next_state, reward, done = env.step(action)

    states.append(state)
    actions.append(action)
    rewards.append(reward)
    state = next_state

# Policy Gradient Update
returns = []
G = 0
for r in reversed(rewards):
    G = r + 0.99 * G
    returns.insert(0, G)

returns = torch.tensor(returns)
returns = (returns - returns.mean()) / (returns.std() + 1e-8)

loss = 0
for state, action, G in zip(states, actions, returns):
    probs = policy(state)
    log_prob = torch.log(probs[action])
    loss -= log_prob * G  # Policy gradient!

optimizer.zero_grad()
loss.backward()
optimizer.step()

PPO (Proximal Policy Optimization)

Stabilere, bessere Version von Policy Gradient.

class PPOAgent:
    def __init__(self, state_dim, action_dim, lr=0.001):
        self.policy = PolicyNetwork(state_dim, action_dim)
        self.value = ValueNetwork(state_dim)  # Kritiker
        self.optimizer = torch.optim.Adam(
            list(self.policy.parameters()) + list(self.value.parameters()),
            lr=lr
        )

    def update(self, states, actions, rewards, values, epochs=4, batch_size=64):
        states = torch.tensor(states)
        actions = torch.tensor(actions)
        rewards = torch.tensor(rewards)

        # Berechne Advantages
        advantages = rewards - values.detach()
        returns = rewards

        # PPO Update (mehrere Epochs, mit Clipping)
        for _ in range(epochs):
            for i in range(0, len(states), batch_size):
                batch_states = states[i:i+batch_size]
                batch_actions = actions[i:i+batch_size]
                batch_advantages = advantages[i:i+batch_size]
                batch_returns = returns[i:i+batch_size]

                # Berechne neue Probabilities
                new_probs = self.policy(batch_states)
                old_probs = # alte Probs (cachet vor Update)

                # Probability Ratio
                ratio = new_probs[batch_actions] / (old_probs[batch_actions] + 1e-8)

                # PPO Clipping (Kern-Innovation!)
                surr1 = ratio * batch_advantages
                surr2 = torch.clamp(ratio, 1-0.2, 1+0.2) * batch_advantages
                policy_loss = -torch.min(surr1, surr2).mean()

                # Value Loss
                value_loss = F.mse_loss(
                    self.value(batch_states).squeeze(),
                    batch_returns
                )

                # Total Loss
                loss = policy_loss + 0.5 * value_loss

                self.optimizer.zero_grad()
                loss.backward()
                self.optimizer.step()

PPO Key Innovation: Trust Region Clipping

ratio = new_policy / old_policy

Ohne Clipping: Ratio könnte explosion (z.B. 100x)
              → Zu großer Update, Policy bricht kaputt

Mit Clipping:  ratio ∈ [0.8, 1.2]  (20% max change)
              → Stabil, kontrolled Updates

DPO (Direct Preference Optimization)

Moderne Alternative zu RLHF für LLMs.

Problem mit RLHF

RLHF 3 Stages:
1. Sammle Human Preferences (teuer!)
2. Trainiere Reward Model (komplex)
3. Optimiere Policy mit PPO (instabil)

DPO: Nur 1 Stage!

DPO Algorithm

def dpo_loss(model, preferred_text, non_preferred_text, beta=0.5):
    """
    preferred_text: "Was ist Python?"
    non_preferred_text: "Ich kann das nicht beantworten"
    beta: Temperature (höher = mehr Constraining)
    """
    # Berechne Log Probabilities
    log_prob_preferred = model.logprob(preferred_text)
    log_prob_non_preferred = model.logprob(non_preferred_text)

    # DPO Loss
    dpo_loss = -torch.log(
        torch.sigmoid(
            beta * (log_prob_preferred - log_prob_non_preferred)
        )
    )

    return dpo_loss

# Training
preferred_texts = ["Antwort 1", "Antwort 2", ...]
non_preferred_texts = ["Schlechte Antwort 1", ...]

for pref, non_pref in zip(preferred_texts, non_preferred_texts):
    loss = dpo_loss(model, pref, non_pref)
    loss.backward()
    optimizer.step()

RLHF (Reinforcement Learning from Human Feedback)

Verwendet für ChatGPT, Claude Alignment.

Stage 1: Sammle Preferences
  Human vergleicht: "Antwort A vs Antwort B"
  Feedback: "A ist besser"

Stage 2: Trainiere Reward Model
  Input: "Frage + Antwort"
  Output: Reward Score (wie gut ist die Antwort?)

Stage 3: Optimize Policy
  LLM generiert Antworten
  Reward Model bewertet
  RL optimiert Policy um Rewards zu maximieren

Implementation

# Stage 2: Reward Model
class RewardModel(torch.nn.Module):
    def __init__(self, lm_model):
        super().__init__()
        self.lm = lm_model  # Basierend auf LLM
        self.reward_head = torch.nn.Linear(768, 1)  # Vorhersagt Reward

    def forward(self, text):
        logits = self.lm(text).last_hidden_state[:, -1, :]  # Last token
        reward = self.reward_head(logits).squeeze()
        return reward

# Training
reward_model = RewardModel(lm)
optimizer = torch.optim.Adam(reward_model.parameters())

for preferred, non_preferred in preference_pairs:
    reward_preferred = reward_model(preferred)
    reward_non_preferred = reward_model(non_preferred)

    # Loss: Preferred sollte höheres Reward haben
    loss = -torch.log(
        torch.sigmoid(reward_preferred - reward_non_preferred)
    )

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

# Stage 3: Policy Optimization mit PPO
ppo_agent = PPOAgent(lm.hidden_size, vocab_size)

for state in environment:
    # Generiere Response
    response = lm.generate(state)

    # Reward von Reward Model
    reward = reward_model(response)

    # PPO Update
    ppo_agent.update(states, actions, rewards)

Robotik Anwendungen

Locomotion (Gehen)

# Reward für Roboter-Gait
def locomotion_reward(position, velocity, energy):
    """
    position: Wie weit ist der Roboter gegangen?
    velocity: Ist die Bewegung stabil?
    energy: Wie viel Energie wurde verwendet?
    """
    reward = (
        position * 1.0          # +1 pro Meter gegangen
        + velocity ** 2 * 0.5   # Bonus für Stabilität
        - energy * 0.001        # Penalty für Energieuse
    )
    return reward

Grasping (Greifen)

def grasping_reward(obj_position, gripper_position, gripper_force):
    """
    Roboter muss Objekt greifen
    """
    distance = ||obj_position - gripper_position||

    if distance < 0.01:  # Nah genug zum greifen
        if gripper_force > threshold:
            return +1  # Erfolgreich gegriffen!
        else:
            return -0.5  # Zu nah aber nicht gegriffen
    else:
        return -0.1 * distance  # Motivation sich zu nähern

Games Anwendungen

Atari (Breakout)

State: Screenshot der Spielschrift
Actions: [NOOP, FIRE, UP, DOWN]
Reward: +1 pro Block zerstört, -1 Leben verloren

AlphaGo (Go Game):
State: Spielbrett
Actions: [Platziere Stein an 361 mögliche Positionen]
Reward: +1 wenn Spiel gewonnen, -1 wenn verloren

Häufige Fehler

1. Schlechte Reward Design

❌ reward = +1 wenn Ziel erreicht, -1 sonst
  → Agent funktioniert zufällig bis zum Glück

✅ reward = +1 Ziel, -0.01 pro Schritt, -0.1 für Fehler
  → Agent lernt effizient

2. Exploding Gradients

# ❌
loss.backward()
optimizer.step()

# ✅
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
loss.backward()
optimizer.step()

3. Zu greedy Policy

❌ Immer beste Action (exploitation)
  → Agent stuck in local optima

✅ Manchmal zufällige Action (exploration)
  → Findet bessere Solutions