Skip to main content
Create your own

Implementing REINFORCE

Hello! Welcome to the next lesson in our Deep Reinforcement Learning module.

In our previous lessons, we explored value-based methods like DQN and Double DQN. These algorithms learn a value function (Q-values) and then use it to implicitly define a policy, for example, by always choosing the action with the highest Q-value. This approach has been incredibly successful, but it's not the only way to solve RL problems.

Today, we shift our perspective to a different class of algorithms: policy gradient methods. Instead of learning values and inferring a policy, we will directly learn the parameters of the policy itself.

Our learning outcome for this lesson is to implement the REINFORCE algorithm, which is the most fundamental policy gradient method. We will explore its theoretical underpinnings, understand its practical implementation, and identify its core limitations, which will set the stage for our next topic.

1. From Value-Based to Policy-Based RL

In value-based methods, our policy is often deterministic (e.g., ). Policy-based methods, however, directly parameterize the policy , which can be a neural network that outputs a probability distribution over actions given a state.

Our goal is to find the parameters that maximize an objective function, typically the expected total reward. We achieve this using gradient ascent.

The key advantages of this direct approach are:

  • Continuous Action Spaces: It can naturally handle continuous action spaces, where outputting a Q-value for every possible action is intractable.
  • Stochastic Policies: It can learn truly stochastic policies, which can be optimal in environments where a deterministic policy might fail (e.g., in a game of rock-paper-scissors).

Let's begin with a video that contrasts policy gradient methods with the value-based methods we've already covered.

Policy Gradient Methods | Reinforcement Learning Part 6

The 'Mutual Information' channel provides a clear introduction to what policy gradient methods are and why they are a more direct approach to solving the RL problem.

Watch the first 2 minutes and 36 seconds of the video. Focus on the conceptual shift from learning a value function Q(s,a) to directly learning a policy π_θ(a|s).

2. The Policy Gradient Theorem

The core challenge in policy gradient methods is computing the gradient of the objective function with respect to the policy parameters . The objective function depends on the state distribution induced by the policy, and this dependency is complex and usually unknown.

Fortunately, the Policy Gradient Theorem provides a way to compute this gradient without needing to differentiate the state distribution. It gives us an expression for the gradient that we can estimate from experience.

Policy Gradient Algorithms

Lilian Weng's blog post 'Policy Gradient Algorithms' is a classic resource that provides an excellent, mathematically rigorous explanation of the theory. We'll focus on the theorem that makes these algorithms work.

Read the section 'Policy Gradient Theorem'. Don't worry about the full proof for now, but focus on the final expression for the gradient, which is proportional to the sum over states and actions of the Q-value multiplied by the gradient of the policy: $$ \nabla_\theta J(\theta) \propto \sum_{s \in \mathcal{S}} d^\pi(s) \sum_{a \in \mathcal{A}} Q^\pi(s, a) \nabla_\theta \pi_\theta(a \vert s) $$ This theorem is the foundation for what follows.

This expression can be rewritten using a common identity known as the log-derivative trick: . Substituting this into the theorem and rearranging gives a very useful form expressed as an expectation:

This equation tells us that to perform gradient ascent, we should nudge the parameters in the direction of (which increases the probability of action in state ), and the size of this nudge should be proportional to the action-value . In other words: if an action leads to a good outcome, increase its probability.

3. REINFORCE: A Monte Carlo Policy Gradient Algorithm

The REINFORCE algorithm, also known as Monte-Carlo Policy Gradient, applies this theorem in the simplest way possible. It recognizes that the true action-value, , is the expected return from that point onward. A simple, unbiased estimate for this expected return is the actual, observed return from a single trajectory, which we'll call .

So, we can replace with the Monte Carlo return . The gradient update becomes:

This leads to the REINFORCE algorithm:

  1. Generate an episode: Using the current policy , run a full episode from start to finish, collecting states, actions, and rewards: .
  2. Calculate returns: For each time step in the episode, calculate the discounted return . This is the "reward-to-go".
  3. Update the policy: Update the policy parameters using gradient ascent. The update for a single step is proportional to .

Let's watch a video that walks through these steps and explains how to implement them.

REINFORCE: Reinforcement Learning Most Fundamental Algorithm

This video by Andriy Drozdyuk provides a clear, step-by-step walkthrough of the REINFORCE algorithm, from the high-level idea to the implementation details.

Watch from the beginning to 09:53. Pay close attention to: The core idea of using rewards to 'reinforce' good actions (0:00 - 3:57). The steps of the algorithm: generate an episode, then loop through it to update (3:57 - 5:40). How to calculate the discounted return G for each time step (5:40 - 6:12). A crucial implementation detail: how to use a categorical distribution (like torch.distributions.Categorical) to both sample an action and get its log-probability (6:12 - 9:53).

The image below, from the article "REINFORCE: Easy Online RL for LLMs," summarizes the key formulas for policy gradients, including the basic form used by REINFORCE and a variant with a baseline, which we will discuss shortly.

Policy Gradients: The Foundation of RLHF
This image shows the fundamental equations for policy optimization. Top: the gradient ascent update rule. Middle: the basic policy gradient and its empirical estimate. Bottom: variations of the \(\Psi_t\) term, where the "Reward-to-go" form corresponds to the REINFORCE algorithm.
Test your understanding!

An agent is using REINFORCE. It completes a short episode of 3 steps:

  • Step 0: takes action in state , gets reward .
  • Step 1: takes action in state , gets reward .
  • Step 2: takes action in state , gets reward , and the episode ends.

Assume the discount factor and the learning rate .

  1. What is the return for each time step ?
  2. Which action's log-probability will be most strongly increased by the update? Which will be most strongly decreased (or least increased)?
Show answer
  1. Calculating Returns (Reward-to-go):

  2. Update Direction: The update for each action is proportional to its return .

    • Action has a return of -2.35.
    • Action has a return of -1.5.
    • Action has a return of -5.

    Since all returns are negative, the algorithm will try to decrease the probability of all three actions. However, the action with the least negative return () will be decreased the least. The action with the most negative return () will be decreased the most. Therefore, relative to the others, is the "best" action in this trajectory and will be reinforced the most (or punished the least).

4. Implementation in PyTorch

Now, let's translate the algorithm into code. The implementation follows the steps we just outlined.

REINFORCE: Reinforcement Learning Most Fundamental Algorithm

Let's continue with the video from Andriy Drozdyuk, which now provides a concise PyTorch implementation of REINFORCE.

Watch from 09:53 to 12:43. This section covers the full implementation loop: Gathering experience: An episode is run, storing the log probabilities of actions and the rewards. Computing returns: The rewards are processed in reverse to calculate the discounted returns G_t for each step. Policy update: The loss is calculated as -log_prob * return for each step, and the optimizer performs a gradient step.

Here is a conceptual Python/PyTorch implementation that captures the essence of the algorithm for a single training iteration (one episode):

import torch
import torch.nn as nn
from torch.distributions import Categorical

# Assume 'env', 'policy_net', and 'optimizer' are defined
# policy_net is an nn.Module that outputs logits for actions

# --- 1. Generate an episode ---
saved_log_probs = []
rewards = []
state, _ = env.reset()
done = False

while not done:
    # Get action probabilities from the policy network
    state_tensor = torch.from_numpy(state).float().unsqueeze(0)
    action_logits = policy_net(state_tensor)
    action_dist = Categorical(logits=action_logits)
    
    # Sample an action and save its log probability
    action = action_dist.sample()
    saved_log_probs.append(action_dist.log_prob(action))
    
    # Take the action in the environment
    state, reward, terminated, truncated, _ = env.step(action.item())
    rewards.append(reward)
    done = terminated or truncated

# --- 2. Calculate returns and assemble loss ---
returns = []
discounted_return = 0
# Iterate through rewards in reverse order
for r in rewards[::-1]:
    discounted_return = r + gamma * discounted_return
    returns.insert(0, discounted_return) # Prepend to keep order

returns = torch.tensor(returns)
# Standardize returns for better performance (a simple form of baseline)
returns = (returns - returns.mean()) / (returns.std() + 1e-9)

policy_loss = []
for log_prob, R in zip(saved_log_probs, returns):
    # The loss for one step is -log_prob * Return
    # We want to maximize log_prob * R, so we minimize -(log_prob * R)
    policy_loss.append(-log_prob * R)

# --- 3. Update the policy ---
optimizer.zero_grad()
loss = torch.cat(policy_loss).sum() # Sum losses over the episode
loss.backward()
optimizer.step()

5. The Problem with REINFORCE: High Variance

REINFORCE works, but it has a major drawback: high variance. The return from a single trajectory can be very noisy. A good action might be part of an overall unlucky trajectory, receiving a negative return and being discouraged. A bad action might be part of a lucky trajectory and be reinforced. This makes learning unstable and slow.

The solution is to subtract a baseline from the return. The update rule becomes proportional to .

A good baseline should only depend on the state , not the action . This ensures the update remains unbiased. A common and effective baseline is the state-value function, . The term is an estimate of the advantage function , which measures how much better taking action was than the average action from state .

  • If , the action was better than average; increase its probability.
  • If , the action was worse than average; decrease its probability.

This is a much more stable learning signal.

Policy Gradient Methods | Reinforcement Learning Part 6

Let's return to the 'Mutual Information' video to see a clear explanation of REINFORCE's high variance and how introducing a baseline helps stabilize training.

Watch from 16:40 to 21:27. This segment demonstrates how a simple change (adding a constant to all rewards) can hurt REINFORCE's performance and explains intuitively why subtracting a baseline—specifically, the state-value function V(s)—solves the problem.

Notice in the code snippet above, we already included a simple baseline: standardizing the returns for the batch (returns = (returns - returns.mean()) / (returns.std())). This centers the returns around zero, ensuring some actions get positive reinforcement and others get negative, which is a simple but effective way to reduce variance.

Conclusion

In this lesson, we made the leap from value-based to policy-based reinforcement learning. We explored the theoretical foundation of policy gradient methods and implemented REINFORCE, the simplest algorithm in this family.

Key Takeaways:

  • Policy Gradient Methods directly optimize a parameterized policy via gradient ascent on the expected reward.
  • The Policy Gradient Theorem provides a way to compute the policy gradient without differentiating the environment dynamics.
  • REINFORCE is a Monte Carlo algorithm that uses the observed return from an episode as an unbiased estimate for .
  • The update rule for REINFORCE pushes up the log-probability of an action in proportion to the return that followed it.
  • REINFORCE suffers from high variance, which makes training unstable. This can be mitigated by subtracting a baseline, such as the state-value function , from the return.

Preview of the Next Lesson:

The idea of using the value function as a baseline is powerful. But how do we get if we are only learning a policy? The answer is: we learn it! This leads directly to our next topic: Actor-Critic methods. We will build an agent with two components: an Actor (the policy) that decides what to do, and a Critic (the value function) that evaluates how good those decisions are.

Can't find a good explanation? Sign up and we'll make it for you

Sign up