Compute the REINFORCE gradient of a softmax policy with respect to its logits, for one recorded episode, with a baseline subtracted.
Policy gradient methods optimise the policy directly. The policy gradient theorem, via the log-derivative trick, says
∇θJ(θ)=E[Gt∇θlogπθ(At∣St)]
so all you need is the gradient of a log-probability, scaled by how good the outcome was. For a softmax policy over logits z, that gradient has a famously clean closed form:
∂zi∂logπ(a∣s)=1[i=a]−π(i∣s)
One-hot minus the probability vector. Read it as an instruction: push the taken action's logit up by 1 - pi(a), and push every other logit down by its own probability. Multiply by the return and you get "increase the logits of actions that preceded high returns". Note that these components sum to zero — a softmax gradient can only redistribute probability mass.
A baseline b(st) subtracted from the return leaves the gradient unbiased (because E[∇logπ]=0) while cutting its variance, which is the difference between REINFORCE working and not.
g^=∑t=0T−1(Gt−b(st))(1At−πt)
Implement:
def softmax_probs(logits):
...
def reinforce_gradient(logits_seq, actions, rewards, gamma, baseline):
...
softmax_probs returns a list of n_actions floats. reinforce_gradient returns the summed gradient as a list of n_actions floats.
Nested lists of floats in; lists of floats out, rounded to 4 decimals by the grader.
logits = [[0.0, 0.0]]
print([round(p, 4) for p in softmax_probs(logits[0])])
print([round(g, 4) for g in reinforce_gradient(logits, [0], [2.0], 0.9, [0.0])])
Output:
[0.5, 0.5]
[1.0, -1.0]
The policy is uniform, so the log-prob gradient is [1, 0] - [0.5, 0.5] = [0.5, -0.5], scaled by the return 2.0.
softmax_probs([0.0, 0.0]) and reinforce_gradient([[0.0, 0.0]], [0], [2.0], 0.9, [0.0])
[0.5, 0.5] [1.0, -1.0]
Equal logits give a uniform policy [0.5, 0.5]. The log-probability gradient for the taken action 0 is one-hot minus the probabilities, [1, 0] - [0.5, 0.5] = [0.5, -0.5]. The single-step return is 2.0 with a zero baseline, so the gradient is 2.0 * [0.5, -0.5] = [1.0, -1.0].
1 <= T <= 500, 2 <= n_actions <= 50G_t is the discounted return from step t to the end of the episode.0.0 <= gamma <= 1.0