paper note
notes on policy gradient method
Deriving the log-derivative trick and connecting the objective to surrogate losses used in code.
The Mathematical Derivation
The transformation relies on a simple identity from calculus: . Rearranging this gives us the key identity, log-derivative trick:
Now, let’s apply this to the policy gradient derivation.
-
Start with the objective function in its integral form (monte carlo estimation of expectation)
-
Apply the gradient operator.
-
Move the gradient inside the integral. (This is valid under mild conditions).
-
Apply the log-derivative trick. We substitute with .
-
Recognize the expression as an expectation. The integral is now an expectation of the term over the distribution .
This completes the derivation.
Why exactly do we need the log-derivative trick?
There are two reasons to use the log-derivative trick:
-
Fundamental reason: not in Monte Carlo form
This is the more fundamental issue from a machine learning perspective. Our goal is to approximate the integral using samples. The standard Monte Carlo estimation principle is used to approximate an expectation of a function f(x) under a distribution p(x):
The recipe is simple: sample from and average the value of .
Now, let’s look at the problematic form from Step 3:
This integral is not in the standard Monte Carlo form . The probability distribution we can easily sample from, , is part of the term being differentiated. We don’t have a clean separation between the “distribution to sample from” and the “function to evaluate”.
monte carlo estimation require a and a ,step3 only has , thus can’t write in MC estimation form to
To estimate this integral, you would need to:
- Sample trajectories from some base distribution (e.g., a uniform distribution over all possible trajectories, which is impossible).
- For each , evaluate the entire integrand . This is not a viable path.
How the log-trick solves this: The trick rearranges the integral into the perfect Monte Carlo form
Now we have a clear recipe that matches the Monte Carlo template:
- Sample a trajectory from the distribution . This is easy: just run the current policy in the environment.
- Evaluate the function for that trajectory.
- Average the results over many trajectories to get an estimate of the gradient.
-
Transform a product gradient into a sum
Let’s expand what actually is for a trajectory of length :
Now, let’s try to compute its gradient, . Since the environment dynamics and initial state do not depend on , we are taking the gradient of a long product of policy terms:
To differentiate a product, you must use the product rule. For just three terms, it’s already messy:
For a typical trajectory with hundreds of steps (), this results in an explosion of terms. It’s computationally very expensive and numerically unstable, as you are multiplying many small probability values, leading to potential underflow (vanishing gradients).
How the log-trick solves this: The logarithm turns products into sums:
$$
\nabla_{\theta} \log p(\tau|\theta) = \sum_{t=0}^{T-1} \nabla_{\theta} \log \pi_{\theta}(a_t|s_t)
$$
The gradient of a sum is the sum of gradients:
$$
\nabla_{\theta} \log p(\tau|\theta) = \sum_{t=0}^{T-1} \nabla_{\theta} \log \pi_{\theta}(a_t|s_t)
$$
This is vastly simpler to compute and more numerically stable.
$$
\nabla_{\theta} \log p(\tau|\theta) = \sum_{t=0}^{T-1} \nabla_{\theta} \log \pi_{\theta}(a_t|s_t)
$$
Surrogate losses in practice
Why does the policy gradient loss look different in code for on-policy and off-policy cases?
When deriving policy gradient methods, we usually start from the objective
where denotes a trajectory and its total return.
If we consider an off-policy setting and apply importance sampling, the objective becomes
However, in practical implementations, we often observe that:
-
in the on-policy case, the loss is implemented as
loss = log_probs * advantage -
in the off-policy case, the loss becomes
loss = (pi_theta / pi_old) * advantage
Why do these implementations look different from the original objective ?
Short answer
The loss used in code is not a direct implementation of the objective function .
Instead, it is a surrogate loss, constructed such that its gradient with respect to is exactly the gradient of the original objective we want to optimize.
Since we usually perform gradient descent in code, maximizing via gradient ascent is equivalent to minimizing . The implemented loss therefore includes a negative sign and is designed purely to produce the correct gradient.
Below, we derive this step by step.
Step 1: Objective and gradient
Our goal is to maximize
To optimize it using gradient-based methods, we need
The main difficulty is that the expectation is taken with respect to , which itself depends on
Step 2: Log-derivative trick
The key mathematical tool behind policy gradient methods is the log-derivative trick, which allows us to move the gradient inside the expectation:
Here,
where we ignore environment transition probabilities since they do not depend on .
Substituting this back, we obtain the standard form of the policy gradient theorem:
This expression is now computable. In code,
log_probs corresponds to .
Step 3: Variance reduction (from return to advantage)
Although the above estimator is unbiased, it has very high variance. Two standard refinements are applied.
1. Causality (reward-to-go)
An action at time should not be influenced by rewards received before . Therefore, the total return can be replaced by the reward-to-go :
2. Baseline
We may subtract a baseline that depends only on the state, without changing the expected gradient. A common choice is the value function .
This leads to the advantage function
and the gradient becomes
Step 4: Constructing the surrogate loss (connection to code)
In PyTorch , we define a scalar loss and call loss.backward() to compute gradients.
However, the reward or advantage is an external scalar with no gradient. Using it directly as a loss would not allow gradients to flow to .
Therefore, we construct a surrogate objective that:
- has the same gradient w.r.t. as the original objective;
- explicitly depends on , enabling backpropagation.
If we define the surrogate loss
then
In practice, the expectation is approximated by a minibatch, and
log_probscorresponds toadvantagecorresponds to
Thus, the implementation
loss = - (log_probs * advantage).mean()
is exactly minimizing , which is equivalent to maximizing .
Note that the advantage is treated as a constant and is usually detached from the computation graph.
On importance sampling and off-policy objectives
In some implementations (e.g. in ROLL), we encounter a loss of the form
In certain cases, and are identical, so the ratio equals 1. Writing it in this form is still valid because is treated as detached, while gradients flow through the numerator .
Many RL frameworks use a unified loss
for both on-policy and off-policy training.
In the on-policy case, although in value,
the denominator is detached, so gradients still flow through
More importantly, this objective is a first-order equivalent surrogate of the standard policy gradient loss around .
Why does PPO-style off-policy loss not multiply by log-probabilities?
Naively extending the on-policy surrogate would suggest a loss like
However, in practice we use
where
The key point is that we are not patching the on-policy loss to make it off-policy.
Instead, we start from first principles and define an off-policy objective:
Here, the importance ratio already contains , so it naturally enables gradient flow.
Thus, there is no need to multiply by again.
In other words, the importance ratio serves a dual role: it corrects the distribution mismatch and simultaneously provides the necessary dependence on for gradient propagation.