DDPG, TD3, SAC: Continuous Control Algorithms Compared for Production
Deep dive into DDPG, TD3, and SAC for continuous control.
20+ years shipping production ML systems and the infrastructure behind them. Lessons pulled from things that broke in production.
- ✓Deep production experience
- ✓Understanding of internals and trade-offs
- ✓Experience debugging complex systems
- DDPG is the baseline off-policy actor-critic for continuous actions, but brittle and sample-inefficient.
- TD3 fixes DDPG's overestimation bias with clipped double Q-learning and target policy smoothing.
- SAC adds entropy regularization for better exploration and robustness, often outperforming TD3.
- All three are off-policy, using replay buffers, but SAC's stochastic policy gives it an edge in complex tasks.
- In production, SAC is the default choice for continuous control, but TD3 can be simpler to tune.
DDPG, TD3, and SAC are off-policy actor-critic algorithms for reinforcement learning in continuous action spaces. They learn a policy (actor) and value function (critic) from experience stored in a replay buffer, enabling sample-efficient learning from past data.
Imagine you're teaching a robot to pour water. DDPG is like a student who learns by copying but often overestimates how good his moves are. TD3 is a more cautious student who double-checks his estimates. SAC is the smartest: he not only learns to pour but also keeps trying new ways, balancing skill with curiosity.
Continuous control deals with real-valued action vectors, not discrete buttons. DDPG, TD3, and SAC are the standard off-policy algorithms for robotics, autonomous driving, and simulation-based policy learning. Choosing the right one and debugging it in production is non-trivial. As foundation models for robotics and sim-to-real transfer gain traction, understanding these algorithms at a production level is critical.
DDPG was the first off-policy actor-critic to handle continuous actions, but it suffers from Q-value overestimation and brittleness. TD3 systematically addresses these flaws with clipped double Q-learning, delayed policy updates, and target policy smoothing. SAC goes further by incorporating entropy regularization, making it robust and sample-efficient.
This article goes beyond textbook explanations. We'll dissect the math, compare implementations, and share real production war stories. You'll learn not just how these algorithms work, but how to debug them when they fail in the wild.
Whether you're building a robotic arm controller or a trading agent, mastering DDPG, TD3, and SAC gives you the tools to deploy continuous control systems that actually work.
The Continuous Control Landscape: Why DDPG, TD3, and SAC Matter
By 2026, continuous control has become the foundation of real-world autonomous systems: robotic manipulation, autonomous driving, drone navigation, and industrial process control. The action spaces in these domains are inherently continuous—torques, velocities, steering angles—making discrete-action algorithms like DQN irrelevant. Three algorithms dominate the production landscape: DDPG, TD3, and SAC. DDPG, published in 2016, was the first off-policy actor-critic to handle continuous actions at scale, but its fragility in practice led to TD3 (2018) and SAC (2018). TD3 fixed DDPG's notorious overestimation bias with clipped double Q-learning and target policy smoothing, while SAC introduced entropy regularization for robust exploration and stochastic policies. DDPG is rarely used in production except as a baseline or in low-dimensional, well-tuned environments. TD3 is the standard tool for deterministic control tasks where sample efficiency and stability are critical—think factory robot arms with precise torque commands. SAC is preferred for exploration-heavy tasks like dexterous manipulation or autonomous racing, where the stochastic policy prevents premature convergence. Both TD3 and SAC have been extended with distributed training (e.g., TD3-APG, SAC-Distributed) and combined with model-based planning for sample efficiency. Understanding their core mechanisms—overestimation bias, target smoothing, entropy regularization—is essential for any engineer deploying RL in continuous domains.
import gymnasium as gym import numpy as np # Quick environment check for continuous action spaces env = gym.make('HalfCheetah-v4') print(f"Action space: {env.action_space}") print(f"Observation space: {env.observation_space}") print(f"Action dim: {env.action_space.shape[0]}") print(f"Obs dim: {env.observation_space.shape[0]}") # Dummy policy: random actions obs, _ = env.reset() for _ in range(5): action = env.action_space.sample() obs, reward, terminated, truncated, _ = env.step(action) print(f"Action: {action[:3]}... Reward: {reward:.2f}") if terminated or truncated: break env.close()
DDPG: The Baseline and Its Pitfalls
Deep Deterministic Policy Gradient (DDPG) extends DQN to continuous action spaces by using an actor-critic architecture with a deterministic policy. The actor μ(s) outputs a continuous action, and the critic Q(s,a) estimates its value. DDPG uses experience replay and target networks with soft updates (polyak averaging, τ=0.005) to stabilize training. The core update: Q(s,a) ← r + γ Q'(s', μ'(s')) for the critic, and ∇_θ J ≈ E[∇_a Q(s,a) ∇_θ μ(s)] for the actor. In practice, DDPG is notoriously brittle. The primary failure mode is overestimation bias: the critic's max over actions (via the deterministic policy) leads to systematic overestimation of Q-values, which cascades into poor policy updates. This is exacerbated by the deterministic policy's lack of exploration—DDPG relies on adding Ornstein-Uhlenbeck noise or Gaussian noise to actions, which is inefficient and can destabilize training. Another pitfall is the sensitivity to hyperparameters: learning rates, noise scale, and target update rate require careful tuning per environment. DDPG is rarely used in production; it serves as a baseline for comparing TD3 and SAC. However, understanding DDPG is crucial because TD3 and SAC directly address its flaws. For example, TD3's clipped double Q-learning directly mitigates overestimation, while SAC's stochastic policy inherently explores better. DDPG's simplicity makes it a good starting point for implementing actor-critic algorithms, but never deploy it without the TD3 fixes.
import torch import torch.nn as nn import torch.optim as optim # Simplified DDPG critic update class Critic(nn.Module): def __init__(self, state_dim, action_dim): super().__init__() self.net = nn.Sequential( nn.Linear(state_dim + action_dim, 256), nn.ReLU(), nn.Linear(256, 256), nn.ReLU(), nn.Linear(256, 1) ) def forward(self, state, action): return self.net(torch.cat([state, action], dim=-1)) # Training loop snippet (single step) state_dim, action_dim = 17, 6 critic = Critic(state_dim, action_dim) critic_target = Critic(state_dim, action_dim) critic_target.load_state_dict(critic.state_dict()) optimizer = optim.Adam(critic.parameters(), lr=3e-4) # Dummy batch states = torch.randn(32, state_dim) actions = torch.randn(32, action_dim) rewards = torch.randn(32, 1) next_states = torch.randn(32, state_dim) dones = torch.zeros(32, 1) # Compute target Q (using target actor, not shown) next_actions = torch.randn(32, action_dim) # placeholder for target actor with torch.no_grad(): target_q = rewards + 0.99 * (1 - dones) * critic_target(next_states, next_actions) # Current Q estimate current_q = critic(states, actions) # MSBE loss loss = nn.MSELoss()(current_q, target_q) optimizer.zero_grad() loss.backward() optimizer.step() print(f"Critic loss: {loss.item():.4f}")
TD3: Fixing Overestimation with Clipped Double Q-Learning
Twin Delayed DDPG (TD3) addresses DDPG's overestimation bias with three key modifications: clipped double Q-learning, delayed policy updates, and target policy smoothing. Clipped double Q-learning maintains two Q-networks (Q1, Q2) and uses the minimum of their targets: y = r + γ min_{i=1,2} Q_i'(s', μ'(s')). This prevents overestimation because the minimum of two overestimates is closer to the true value. Delayed policy updates (e.g., update actor every 2 critic steps) reduces variance in the policy gradient by allowing the critic to stabilize first. Target policy smoothing adds Gaussian noise to the target action: a' = μ'(s') + ε, ε ~ clip(N(0,σ), -c, c). This encourages the policy to avoid actions that have narrow, spiky Q-function peaks, improving robustness. In practice, TD3 is significantly more stable than DDPG. For example, on HalfCheetah-v4, TD3 achieves ~12,000 average return in 1M steps vs DDPG's ~8,000. The hyperparameters are more forgiving: learning rate 3e-4, target noise σ=0.2, noise clip c=0.5, policy delay d=2. TD3 is the go-to algorithm for deterministic control tasks where you need reliable, sample-efficient training. However, TD3's deterministic policy still limits exploration; it relies on adding noise during training (e.g., Gaussian noise with std=0.1). For tasks requiring extensive exploration, SAC is preferred. TD3 also struggles with high-dimensional action spaces (e.g., 20+ dimensions) where the Q-function approximation becomes noisy. In production, TD3 is used for robotic arm control, autonomous vehicle lateral control, and any task where actions must be precise and repeatable.
import torch import torch.nn as nn # TD3 target computation with clipped double Q and target smoothing class TD3Target: def __init__(self, actor_target, critic1_target, critic2_target, noise_std=0.2, noise_clip=0.5): self.actor_target = actor_target self.critic1_target = critic1_target self.critic2_target = critic2_target self.noise_std = noise_std self.noise_clip = noise_clip def compute_target(self, rewards, next_states, dones, gamma=0.99): with torch.no_grad(): # Target policy smoothing next_actions = self.actor_target(next_states) noise = torch.randn_like(next_actions) * self.noise_std noise = torch.clamp(noise, -self.noise_clip, self.noise_clip) next_actions = torch.clamp(next_actions + noise, -1.0, 1.0) # Clipped double Q q1_target = self.critic1_target(next_states, next_actions) q2_target = self.critic2_target(next_states, next_actions) q_target = torch.min(q1_target, q2_target) return rewards + gamma * (1 - dones) * q_target # Example usage (dummy networks) class Actor(nn.Module): def __init__(self, state_dim, action_dim): super().__init__() self.net = nn.Sequential(nn.Linear(state_dim, 256), nn.ReLU(), nn.Linear(256, action_dim), nn.Tanh()) def forward(self, state): return self.net(state) class Critic(nn.Module): def __init__(self, state_dim, action_dim): super().__init__() self.net = nn.Sequential(nn.Linear(state_dim+action_dim, 256), nn.ReLU(), nn.Linear(256, 1)) def forward(self, state, action): return self.net(torch.cat([state, action], dim=-1)) state_dim, action_dim = 17, 6 actor_target = Actor(state_dim, action_dim) critic1_target = Critic(state_dim, action_dim) critic2_target = Critic(state_dim, action_dim) td3_target = TD3Target(actor_target, critic1_target, critic2_target) # Dummy batch rewards = torch.randn(32, 1) next_states = torch.randn(32, state_dim) dones = torch.zeros(32, 1) target = td3_target.compute_target(rewards, next_states, dones) print(f"Target Q shape: {target.shape}, mean: {target.mean().item():.3f}")
SAC: Entropy Regularization for Robust Exploration
Soft Actor-Critic (SAC) introduces entropy regularization to the RL objective: the policy maximizes expected return plus expected entropy, π* = argmax_π E[Σ γ^t (r_t + α H(π(·|s_t)))]. This encourages exploration and prevents premature convergence to poor local optima. SAC learns a stochastic policy π(a|s) (typically a diagonal Gaussian with mean and log_std output) and two Q-functions with clipped double Q (like TD3). The critic update uses the entropy-augmented target: y = r + γ (min_i Q_i'(s', a') - α log π(a'|s')), where a' ~ π(·|s'). The actor update maximizes: J_π = E[α log π(a|s) - min_i Q_i(s,a)]. The temperature α controls the trade-off between exploration and exploitation. In the modern variant, α is automatically tuned to maintain a target entropy H_target = -dim(A) (e.g., for 6D action space, H_target = -6). SAC is sample-efficient and robust to hyperparameters. On MuJoCo benchmarks, SAC achieves state-of-the-art performance: ~18,000 on HalfCheetah-v4 in 1M steps. The stochastic policy provides natural exploration, eliminating the need for action noise. SAC handles high-dimensional action spaces better than TD3 because the stochastic policy smooths the Q-function landscape. However, SAC's stochastic policy can be a liability in production: for tasks requiring deterministic, low-variance actions (e.g., precise torque control), the stochasticity must be removed at test time (use mean action). SAC also has higher computational cost per step due to sampling from the policy and computing log-probabilities. SAC is the default choice for exploration-heavy tasks like dexterous manipulation, autonomous racing, and any environment with sparse rewards. For deterministic tasks, TD3 is often preferred for its lower variance and simpler implementation.
import torch import torch.nn as nn import torch.distributions as dist # SAC actor loss computation class GaussianPolicy(nn.Module): def __init__(self, state_dim, action_dim, hidden_dim=256): super().__init__() self.net = nn.Sequential(nn.Linear(state_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU()) self.mean = nn.Linear(hidden_dim, action_dim) self.log_std = nn.Linear(hidden_dim, action_dim) def forward(self, state): x = self.net(state) mean = self.mean(x) log_std = torch.clamp(self.log_std(x), -20, 2) # stabilize std = log_std.exp() return dist.Normal(mean, std) def sample(self, state): normal = self.forward(state) z = normal.rsample() # reparameterization trick action = torch.tanh(z) log_prob = normal.log_prob(z) - torch.log(1 - action.pow(2) + 1e-6) log_prob = log_prob.sum(dim=-1, keepdim=True) return action, log_prob # Actor loss state_dim, action_dim = 17, 6 policy = GaussianPolicy(state_dim, action_dim) critic1 = nn.Sequential(nn.Linear(state_dim+action_dim, 256), nn.ReLU(), nn.Linear(256, 1)) critic2 = nn.Sequential(nn.Linear(state_dim+action_dim, 256), nn.ReLU(), nn.Linear(256, 1)) alpha = torch.tensor(0.2) # fixed or learned states = torch.randn(32, state_dim) actions, log_probs = policy.sample(states) q1 = critic1(torch.cat([states, actions], dim=-1)) q2 = critic2(torch.cat([states, actions], dim=-1)) q_min = torch.min(q1, q2) actor_loss = (alpha * log_probs - q_min).mean() print(f"Actor loss: {actor_loss.item():.4f}") print(f"Log probs mean: {log_probs.mean().item():.3f}")
Mathematical Deep Dive: Bellman Equations and Loss Functions
The Bellman equation underpins off-policy actor-critic methods. For DDPG, the Q-function update minimizes the mean squared Bellman error (MSBE): L = E[(Q(s,a) - (r + γ Q'(s', π'(s'))))²]. The target Q' and target policy π' are slowly copied from the online networks via Polyak averaging. This direct bootstrapping is simple but brittle: Q-function overestimation propagates through the target, leading to divergence in high-dimensional tasks. TD3 fixes this by using clipped double Q-learning: two Q-functions are learned, and the target uses min(Q1', Q2'). The policy loss becomes Lπ = -E[Q1(s, π(s))], but the policy update is delayed (every two Q updates) and target policy smoothing adds noise to actions in the target: a' = π'(s') + clip(ε, -c, c) with ε ~ N(0, σ). This prevents exploitation of sharp peaks in the Q-function. SAC introduces entropy regularization: the policy maximizes expected return plus expected entropy αH(π(·|s)). The soft Bellman equation is Q(s,a) = r + γ E[V(s')] where V(s') = E[Q(s',a')] - α log π(a'|s'). The Q-loss is LQ = E[(Q(s,a) - (r + γ (min(Q1'(s',a'), Q2'(s',a')) - α log π(a'|s'))))²]. The policy loss is Lπ = E[α log π(a|s) - min(Q1(s,a), Q2(s,a))]. The temperature α can be learned by minimizing Lα = E[-α log π(a|s) - α H_target] to enforce a target entropy (typically -dim(A)). These equations reveal a progression: DDPG trusts a single critic, TD3 adds safety through double Q and smoothing, SAC adds stochasticity and entropy to balance exploration and exploitation.
import torch import torch.nn.functional as F def compute_sac_losses(q1, q2, q1_target, q2_target, policy, replay_buffer, gamma=0.99, alpha=0.2, target_entropy=-2): """Compute SAC losses for Q, policy, and alpha (temperature).""" states, actions, rewards, next_states, dones = replay_buffer.sample(256) # Target Q computation with clipped double Q with torch.no_grad(): next_actions, next_log_probs = policy.sample(next_states) next_q1 = q1_target(next_states, next_actions) next_q2 = q2_target(next_states, next_actions) next_q = torch.min(next_q1, next_q2) - alpha * next_log_probs target_q = rewards + gamma * (1 - dones) * next_q # Q loss current_q1 = q1(states, actions) current_q2 = q2(states, actions) q1_loss = F.mse_loss(current_q1, target_q) q2_loss = F.mse_loss(current_q2, target_q) q_loss = q1_loss + q2_loss # Policy loss new_actions, log_probs = policy.sample(states) q1_new = q1(states, new_actions) q2_new = q2(states, new_actions) q_new = torch.min(q1_new, q2_new) policy_loss = (alpha * log_probs - q_new).mean() # Alpha loss (if learned) alpha_loss = -(alpha * (log_probs + target_entropy).detach()).mean() return q_loss, policy_loss, alpha_loss
Implementation Details: Replay Buffers, Target Networks, and Hyperparameters
Replay buffers are the memory of off-policy algorithms. A uniform replay buffer stores transitions (s, a, r, s', done) and samples batches uniformly. For DDPG and TD3, a buffer size of 1e6 is standard; for SAC, 1e6 is also common but can be reduced to 5e5 for faster iteration. Prioritized replay (PER) can accelerate learning but adds complexity and hyperparameters (alpha for prioritization, beta for importance sampling). In production, PER often underperforms uniform sampling unless the reward structure is extremely sparse—stick to uniform first. Target networks are updated via Polyak averaging: θ_target = τ θ_online + (1 - τ) θ_target. For DDPG, τ = 0.001 is typical; for TD3, τ = 0.005; for SAC, τ = 0.005. The update frequency matters: TD3 delays policy updates (every 2 Q updates) to reduce error accumulation. SAC updates policy and Q every step. Hyperparameters: DDPG uses learning rate 1e-4 for actor and critic, TD3 uses 1e-3, SAC uses 3e-4. Batch size is 256 for all. SAC's temperature α: fixed at 0.2 works for many tasks, but learning α with target entropy = -dim(A) is more robust. Network architecture: two hidden layers of 256 (DDPG, TD3) or 256 (SAC) with ReLU. For SAC, the policy outputs mean and log_std, then uses the reparameterization trick: a = tanh(μ + σ * ε) with ε ~ N(0,1). Gradient clipping (max norm 1.0) prevents exploding gradients. Initialization: use orthogonal or Xavier uniform; avoid zero initialization for policy log_std (start at -2 or -5).
import numpy as np from collections import deque import random class ReplayBuffer: def __init__(self, capacity=1000000): self.buffer = deque(maxlen=capacity) def push(self, state, action, reward, next_state, done): self.buffer.append((state, action, reward, next_state, done)) def sample(self, batch_size=256): batch = random.sample(self.buffer, batch_size) states, actions, rewards, next_states, dones = zip(*batch) return (np.array(states, dtype=np.float32), np.array(actions, dtype=np.float32), np.array(rewards, dtype=np.float32).reshape(-1, 1), np.array(next_states, dtype=np.float32), np.array(dones, dtype=np.float32).reshape(-1, 1)) def __len__(self): return len(self.buffer) # Usage buffer = ReplayBuffer(capacity=1000000) for _ in range(1000): buffer.push(np.random.randn(4), np.random.randn(2), 0.0, np.random.randn(4), False) batch = buffer.sample(256) print(f"Sampled batch: states shape {batch[0].shape}, actions shape {batch[1].shape}")
Production Debugging: Real Incidents and How to Fix Them
Incident 1: Q-values explode to infinity. This happened in a robotic arm task using DDPG. The reward was unbounded (distance to target in meters). After 50k steps, Q-values reached 1e8 and policy became erratic. Fix: clip rewards to [-1, 1] and normalize observations. Also, reduce tau from 0.01 to 0.001. Incident 2: Policy collapses to a single action (deterministic) in SAC. The log_std became -20, meaning the policy was essentially deterministic. This occurred because the target entropy was set too low (-1 for a 6-D action space). Fix: set target entropy to -dim(A) = -6. Also, clip log_std to [-20, 2] to prevent collapse. Incident 3: TD3 training oscillates—returns go up then crash. This was in a financial trading environment with sparse rewards. The issue was the target policy smoothing noise σ was too high (0.2) relative to action range ([-1, 1]). Fix: reduce σ to 0.1 and clip noise to [-0.5, 0.5]. Also, increase policy delay from 2 to 4. Incident 4: SAC never explores—entropy drops to zero. The temperature α was fixed at 0.01, too low. Fix: learn α with target entropy = -dim(A). Start with α=1.0. Incident 5: Replay buffer memory blowup. A team stored full images (84x84x3) as float32 in a buffer of 1e6—that's 848434 bytes 1e6 = 84.7 GB. Fix: use uint8 compression or store latent representations. For continuous control, states are small (e.g., 17 dimensions), so 1e6 is ~68 MB. Incident 6: Target network update causes NaN. This happened when using layer normalization and a high learning rate (1e-3). The target Q became NaN after 10k steps. Fix: reduce learning rate to 3e-4, add gradient clipping (max norm 1.0), and use weight decay (1e-5) on Q networks.
import numpy as np def debug_q_values(q_values, threshold=1e6): """Check for Q-value explosion.""" mean_q = np.mean(q_values) max_q = np.max(q_values) if max_q > threshold: print(f"WARNING: Q-values exploding! Mean: {mean_q:.2f}, Max: {max_q:.2f}") print("Possible fixes: clip rewards, reduce tau, normalize observations") return True return False # Simulate Q-values from a training run q_values = np.random.randn(1000) * 1000 # Large variance print(f"Mean Q: {np.mean(q_values):.2f}, Max Q: {np.max(q_values):.2f}") debug_q_values(q_values, threshold=5000) # After fix: clip rewards to [-1, 1] q_values_clipped = np.clip(q_values, -1, 1) print(f"After clipping: Mean Q: {np.mean(q_values_clipped):.2f}, Max Q: {np.max(q_values_clipped):.2f}")
Choosing the Right Algorithm: A Practical Decision Framework
The choice between DDPG, TD3, and SAC depends on the environment, computational budget, and stability requirements. Here's a decision framework: If your action space is low-dimensional (≤6) and you need maximum sample efficiency, start with SAC. SAC's entropy regularization provides built-in exploration and is robust to hyperparameters. It works well in robotics, continuous control benchmarks (MuJoCo, PyBullet), and real-world systems where you can afford 100k-1M steps. If you have limited compute (e.g., embedded systems) and need deterministic inference, use TD3. TD3 is simpler (no entropy term, no log-prob computation) and faster per step. It's ideal for deployment where you can't sample from a distribution. Use DDPG only as a baseline or when you have a very smooth, low-noise environment (e.g., simple pendulum). DDPG is brittle—it often diverges in practice. For high-dimensional action spaces (>10), SAC with learned temperature is preferred because it automatically balances exploration. For sparse reward environments, SAC with Hindsight Experience Replay (HER) can work, but TD3 with HER is also viable. If you need to train in under 10k steps (e.g., real-world robotics with limited data), consider model-based methods instead. For multi-agent settings, MADDPG (based on DDPG) is common, but SAC with centralized critics is emerging. In production, always run a hyperparameter sweep: for SAC, tune α (fixed 0.1-0.5) or learn it; for TD3, tune policy delay (2-4) and noise σ (0.1-0.2). Use the same network architecture (256x256) for fair comparison. Final rule: if you have time, use SAC. If you need speed, use TD3. If you're debugging, use both and compare Q-value distributions.
def select_algorithm(action_dim, compute_budget, reward_sparsity, deterministic_inference): """Practical algorithm selection based on task constraints.""" if deterministic_inference and compute_budget < 100000: return "TD3" elif action_dim > 10 or reward_sparsity: return "SAC (learned alpha)" elif compute_budget > 500000: return "SAC" else: return "TD3" # Example usage print(select_algorithm(action_dim=6, compute_budget=200000, reward_sparsity=False, deterministic_inference=False)) # Output: SAC print(select_algorithm(action_dim=2, compute_budget=50000, reward_sparsity=False, deterministic_inference=True)) # Output: TD3
The Case of the Oscillating Robot Arm: A SAC Production Failure
- Always use automatic entropy tuning in SAC for real-world deployment.
- Sim-to-real gap can manifest as policy instability; test with domain randomization.
- Add action smoothing (e.g., low-pass filter) for safety-critical systems.
python train.py --lr 3e-5python train.py --double-q Truepython train.py --exploration-noise 0.3python train.py --alpha 0.5python train.py --reward-scale 0.1python train.py --normalize-obs True| Feature | DDPG | TD3 | SAC |
|---|---|---|---|
| Policy Type | Deterministic | Deterministic | Stochastic |
| Q-function Count | 1 | 2 (clipped min) | 2 (clipped min) |
| Target Smoothing | None | Gaussian noise on target actions | Inherent via stochastic policy |
| Exploration | Action noise (e.g., OU or Gaussian) | Action noise + target smoothing | Entropy regularization |
| Sample Efficiency | Low | Medium | High |
| File | Command / Code | Purpose |
|---|---|---|
| io | env = gym.make('HalfCheetah-v4') | The Continuous Control Landscape |
| io | class Critic(nn.Module): | DDPG |
| io | class TD3Target: | TD3 |
| io | class GaussianPolicy(nn.Module): | SAC |
| io | def compute_sac_losses(q1, q2, q1_target, q2_target, policy, replay_buffer, gamm... | Mathematical Deep Dive |
| io | from collections import deque | Implementation Details |
| io | def debug_q_values(q_values, threshold=1e6): | Production Debugging |
| io | def select_algorithm(action_dim, compute_budget, reward_sparsity, deterministic_... | Choosing the Right Algorithm |
Key takeaways
Interview Questions on This Topic
Explain how TD3 addresses the overestimation bias in DDPG.
What is entropy regularization and why is it beneficial in SAC?
Compare the exploration strategies of DDPG, TD3, and SAC.
Frequently Asked Questions
TD3 fixes DDPG's overestimation bias by using two Q-functions (clipped double Q-learning), delaying policy updates, and adding target policy smoothing. This makes TD3 more stable and sample-efficient.
Entropy regularization encourages the policy to be stochastic, promoting exploration and preventing premature convergence to poor local optima. It balances exploration and exploitation automatically.
Start with SAC. It's robust, sample-efficient, and works well out of the box. If you need deterministic actions or have limited compute, TD3 is a simpler alternative. Avoid DDPG unless you have a specific reason.
DDPG and TD3 are designed for continuous actions. SAC has a discrete action variant, but the standard version is for continuous spaces. For discrete actions, consider DQN or PPO.
20+ years shipping production ML systems and the infrastructure behind them. Lessons pulled from things that broke in production.
That's Reinforcement Learning. Mark it forged?
8 min read · try the examples if you haven't