CODE_ARCHITECTURE.md — Asynchronous Methods for Deep Reinforcement Learning (A3C)
Overview
The notebook implements a simplified single-process version of the A3C (Asynchronous Advantage Actor-Critic) algorithm from Mnih et al. (2016). The core components are faithfully reproduced: a shared global actor-critic network, n-step return computation, the advantage actor-critic loss with entropy regularization, and periodic gradient updates. We train on CartPole-v1 (4-dim state, 2 actions) using an MLP. The notebook plots policy entropy alongside episode reward — the key diagnostic that shows how exploration decreases as the policy converges. This is CPU-only and self-contained.
Section-by-Section Breakdown
1. Setup & Imports
- Purpose: Import PyTorch, gymnasium, matplotlib, numpy. Set random seeds.
- Key detail: Uses
gymnasium(maintained successor to OpenAI Gym). All packages pip-installable.
2. Hyperparameters
- Purpose: Centralize all configuration.
- Key values:
state_dim = 4— CartPole observation dimensionaction_dim = 2— CartPole actions (left, right)hidden_dim = 128— hidden layer sizelr = 3e-3— learning rate (Adam; paper used RMSProp 7e-4 for Atari, we use a slightly higher LR for faster CartPole convergence)gamma = 0.99— discount factort_max = 5— n-step return horizon (paper value)entropy_coef = 0.01— entropy regularization coefficient (paper: β = 0.01)value_coef = 0.5— value loss coefficient (paper: c_v = 0.5)max_grad_norm = 0.5— gradient clipping (stabilizes training)num_episodes = 500— total training episodesnum_envs = 8— number of parallel environments (simulates A3C’s parallel threads)
- Data flow: These constants flow into network construction, environment setup, and training loop.
3. Actor-Critic Network
- Purpose: A single network that outputs both the policy (actor) and value (critic).
- Architecture: Shared MLP body + two heads
- Shared body:
(B, 4)→(B, 128)ReLU →(B, 128)ReLU - Actor head:
(B, 128)→(B, 2)→ Softmax → action probabilities - Critic head:
(B, 128)→(B, 1)→ state value
- Shared body:
- Key class:
ActorCritic(nn.Module)withforward(state)returning(log_probs, value)or(action_probs, value) - Data flow: state → shared layers → split → actor head (policy) + critic head (value)
- Simplification vs paper: The paper used a CNN for Atari pixel inputs. We use an MLP for the 4-dim CartPole state. The shared-body + two-head architecture is faithful to the paper.
4. Parallel Environments (Multi-Env Wrapper)
- Purpose: Simulate A3C’s parallel threads with multiple environment instances in a single process.
- Key class:
MultiEnv— managesnum_envsgymnasium environments - Methods:
reset_all(),step_all(actions)— vectorized step across all envs - Data flow: Each env produces independent trajectories; gradients from all envs are accumulated and applied to the shared global network.
- Key detail: In the original paper, each thread has its own local model copy and asynchronously applies gradients. Our simplified version processes all envs in a synchronized loop but accumulates gradients from n-step returns computed in each env independently — preserving the diversity-of-experience benefit.
5. n-Step Return Computation
- Purpose: Compute the n-step discounted return for each environment.
- Algorithm:
R = 0 for k in range(t_max): R += gamma^k * r_k if done: break if not done: R += gamma^t_max * V(s_{t_max}) # bootstrap with value - Key detail: If an episode terminates within the n-step window, the return is just accumulated rewards (no bootstrap). The bootstrap term uses the critic’s value estimate of the state at the end of the n-step window.
- Shape: Scalar per environment →
(num_envs,)tensor.
6. Advantage Calculation
- Purpose: Compute the advantage A = R - V(s_t).
- Formula:
A(s_t) = n_step_return - V(s_t)where V(s_t) is the critic’s value estimate at the start of the n-step window. - Key detail: The advantage measures how much better the observed return is compared to what the critic expected. Positive advantage → increase probability of taken actions; negative → decrease.
7. Loss Function
- Purpose: Combined actor-critic loss with entropy regularization.
- Components:
- Policy loss (actor):
-mean(log_prob(a_t) * A.detach())— the policy gradient objective - Value loss (critic):
mean((R - V(s_t))²)— MSE between n-step return and value estimate - Entropy bonus:
mean(H(π(·|s_t)))— Shannon entropy of the policy, encouraging exploration - Total:
L = policy_loss + value_coef * value_loss - entropy_coef * entropy
- Policy loss (actor):
- Key detail: Advantages are detached (
.detach()) so the critic’s gradient doesn’t flow through the actor loss. The entropy term is subtracted (we want to maximize entropy = explore more).
8. Training Loop
- Purpose: Main A3C training loop.
- Per-iteration flow:
for each update step: for each parallel env: 1. Run n steps in the env, collecting (s, a, r, s', done) transitions 2. Compute n-step return R with bootstrap 3. Accumulate: log_prob(a), value V(s), R Combine across envs: 4. Compute advantage A = R - V(s_t) 5. Compute total loss = policy_loss + value_coef * value_loss - entropy_coef * entropy 6. Backprop and clip gradients (max_norm) 7. Optimizer step Log metrics: episode rewards, policy entropy, value loss - Tracking: Episode rewards (per env), mean policy entropy, mean value loss per update step.
9. Visualization: Reward Curve
- Purpose: Plot episode reward over training. Should show an upward trend as the agent learns to balance the pole longer.
- Output: Raw reward curve + smoothed (moving average). CartPole-v1 max reward is 500.
10. Visualization: Policy Entropy
- Purpose: Plot policy entropy over training. This is the key diagnostic from the paper.
- Expected behavior: Entropy starts high (uniform policy ≈ ln(2) ≈ 0.693 for 2 actions) and decreases as the policy becomes more confident/deterministic. If entropy drops too fast, the agent may be converging prematurely. If it stays high, the agent isn’t learning.
- Key detail: The entropy regularization term in the loss prevents entropy from dropping to zero — it maintains a minimum exploration level. The β = 0.01 coefficient controls this balance.
11. Visualization: Value Loss
- Purpose: Plot the critic’s value loss over training. Should decrease as the critic learns to estimate returns accurately.
12. Evaluation
- Purpose: Run the trained policy (stochastic, no epsilon needed) for several episodes and report average reward.
- Key detail: Unlike DQN (which needs ε-greedy), A3C’s policy is stochastic — we sample actions from π(a|s) during evaluation. For deterministic evaluation, we can also take argmax.
Key Functions/Classes Summary
| Component | Type | Input Shape | Output Shape | Notes |
|---|---|---|---|---|
ActorCritic | nn.Module | (B, 4) | (B, 2), (B, 1) | Shared body, actor head (softmax), critic head |
MultiEnv | Class | — | — | Manages N parallel gymnasium envs |
compute_n_step_return | Function | rewards, gamma, V(s_end), done | scalar | n-step return with bootstrap |
compute_loss | Function | log_probs, values, returns | scalar | A2C + entropy loss |
train() | Function | — | — | Main training loop |
Data Flow Diagram
┌─── Env 0 ── n steps ── (s, a, r, s', done) ──┐
├─── Env 1 ── n steps ── (s, a, r, s', done) ──┤
Shared Global Net ──┼─── Env 2 ── n steps ── (s, a, r, s', done) ──┤── Accumulate
(θ, θ_v) │ ... │ gradients
└─── Env N ── n steps ── (s, a, r, s', done) ──┘
│
┌──────────────────────────────────────────┘
│
Compute n-step returns: R_i for each env
Compute advantages: A_i = R_i - V(s_i)
│
┌─────────┴───────────────────┐
│ │
Actor Loss Critic Loss Entropy
-log π(a_i)·A_i (R_i - V_i)² H(π(·|s_i))
│ │ │
└─────── L_total ─────────────┴───────────────┘
│
Backprop + clip
│
Optimizer step (shared θ, θ_v)
│
Sync metrics: reward, entropy, loss
Deliberate Simplifications vs Full Paper
- Single-process synchronous instead of multi-threaded asynchronous — the original uses Python threading with shared memory. Our version runs multiple envs in a synchronized loop within a single process. The gradient accumulation from diverse parallel experience is preserved; only the asynchronicity of gradient application is simplified.
- Environment: CartPole-v1 (4-dim state, 2 actions) instead of Atari 2600 (84×84×4 pixels, 4–18 actions) — CPU tractable
- Network: MLP instead of CNN — appropriate for low-dimensional CartPole state
- Optimizer: Adam instead of RMSProp — Adam is more robust to hyperparameter choices and works well for small-scale RL. The paper used RMSProp with custom decay.
- Thread count: 8 parallel envs instead of 16 threads — sufficient for CartPole
- No frame preprocessing: CartPole provides clean 4-dim state; Atari requires frame stacking, grayscaling, resizing
- Algorithm preserved: n-step returns, advantage actor-critic loss, entropy regularization, gradient clipping, parallel exploration diversity — all core A3C components are faithfully implemented