Definition
Monte Carlo Advantage Estimation
Unbiased, but high-variance
Because
The cost is variance.
Implementation
class Advantage(NamedTuple):
ret: Annotated[Tensor, "n_steps n_envs"]
adv: Annotated[Tensor, "n_steps n_envs"]
def monte_carlo(
rew: Annotated[Tensor, "n_steps n_envs"],
dones: Annotated[Tensor, "n_steps n_envs"],
val: Annotated[Tensor, "n_steps n_envs"],
next_val: Annotated[Tensor, "n_envs"],
next_done: Annotated[Tensor, "n_envs"],
discount_factor: float,
) -> Advantage:
returns: Annotated[Tensor, "n_steps n_envs"] = zeros_like(rew)
for step in reversed(range(rew.shape[0])):
if step == rew.shape[0] - 1:
next_nonterminal = 1.0 - next_done
next_return = next_val
else:
next_nonterminal = 1.0 - dones[step + 1]
next_return = returns[step + 1]
returns[step] = rew[step] + discount_factor * next_nonterminal * next_return
advantages: Annotated[Tensor, "n_steps n_envs"] = returns - val
return Advantage(ret=returns, adv=advantages)Each iteration fills one entry of returns from the return recurrence
so the loop runs in reversed order — each if/else only chooses where
- Every step but the last reads the already-computed
returns[step + 1]anddones[step + 1]. - The last step has no
returns[step + 1], so it bootstraps fromnext_val— the critic’s value of the observation after the rollout — gated bynext_done.
Both branches use the same done mask 1 - d_{t+1}: it zeroes
Transition 2 is terminal, so
Finally, advantages = returns - val is the literal adv as the policy-gradient signal, ret as the critic’s regression target.