Compute PPO's clipped surrogate objective over a minibatch, along with the fraction of samples the clip range actually touched.
Vanilla policy gradients are only valid for data collected by the current policy, so a step that is too large destroys the policy and there is no gradient signal left to recover with. TRPO solved this with a hard KL trust region and a conjugate-gradient solve. PPO gets most of the benefit with a first-order objective you can write in four lines.
Start from the importance-sampling ratio between the new and old policy on the same action:
rt(θ)=πθold(at∣st)πθ(at∣st)=exp(logπθ−logπθold)
Computing it as exp of a log-difference rather than a quotient of probabilities is what keeps it numerically sane. The objective is then
LCLIP=Et[min(rtAt, clip(rt,1−ε,1+ε)At)]
The min makes this a pessimistic lower bound, and its asymmetry is the whole trick:
The commonly logged diagnostic clipfrac is the fraction of samples with ∣rt−1∣>ε; a value that climbs toward 1 means the policy is moving too far per update.
Implement:
def ppo_clip_objective(logp_old, logp_new, advantages, clip_eps):
...
Return a **tuple **(objective, clip_fraction)****:
Both are floats.
Three lists of floats and a float in; a tuple of two floats out, rounded to 4 decimals by the grader.
obj, frac = ppo_clip_objective([0.0, 0.0], [0.0, 0.0], [2.0, -3.0], 0.2)
print(round(obj, 4), round(frac, 4))
Output:
-0.5 0.0
Both ratios are exp(0) = 1, nothing is clipped, and the objective is the mean advantage (2.0 - 3.0)/2 = -0.5.
ppo_clip_objective([0.0, 0.0], [0.0, 0.0], [2.0, -3.0], 0.2)
-0.5 0.0
Identical log-probabilities give ratio = exp(0) = 1 for both samples, so nothing falls outside [0.8, 1.2] and clip_fraction is 0. With ratio 1 the clipped and unclipped terms are both just the advantage, so the objective is the mean advantage (2.0 + (-3.0))/2 = -0.5.
1 <= batch_size <= 1000000.0 < clip_eps < 1.0exp(logp_new - logp_old), not as a quotient of probabilities.objective is a mean, not a sum.clip_fraction counts samples with abs(ratio - 1) > clip_eps, regardless of the sign of the advantage.(objective, clip_fraction) in that order.