Chapter 2: Fundamental Theory of Reinforcement Learning

📖 Reading Time: 20-25 min 📊 Difficulty: Beginner 💻 Code Examples: 8 📝 Exercises: 3

Chapter 2: Fundamental Theory of Reinforcement Learning

This chapter organizes the intuition behind representative methods such as Q-learning, DQN, and PPO, along with their differences. You will develop a sense of which method to try for which type of problem.

💡 Supplement: Continuous control pairs well with policy-gradient methods such as PPO. For discrete choices, start with Q-based methods.

Learning Objectives

In this chapter, you will master the following:


2.1 Policy Gradient Methods

Limitations of Q-Learning

The Q-learning and DQN methods from Chapter 1 were value-based methods. These have the following limitations:

  1. Discrete actions only: $\arg\max_a Q(s,a)$ is difficult in continuous action spaces
  2. Deterministic policy: Always selects the same action (cannot learn a stochastic policy)
  3. Fragile to small changes: A tiny change in the Q-value can drastically change the policy

In materials science, continuous control (raising the temperature by 0.5 degrees, changing the composition ratio by 2%) is important.

Basic Idea of Policy Gradient Methods

Policy gradient methods optimize the policy directly:

$$ \pi_\theta(a|s) = P(a|s; \theta) $$

Objective: Maximize the expected cumulative reward $J(\theta)$

$$ J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta} \left[ \sum_{t=0}^T r_t \right] $$

Policy Gradient Theorem

The REINFORCE algorithm (Williams, 1992) computes the gradient as follows:

$$ \nabla_\theta J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta} \left[ \sum_{t=0}^T \nabla_\theta \log \pi_\theta(a_t|s_t) \cdot R_t \right] $$

Intuitive meaning: - Increase the probability of actions that received high rewards - Decrease the probability of actions that received low rewards

Implementation of REINFORCE

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt

class PolicyNetwork(nn.Module):
    """Policy network

    Takes a state as input and outputs the probability of each action
    """
    def __init__(self, state_dim, action_dim, hidden_dim=64):
        super(PolicyNetwork, self).__init__()
        self.fc1 = nn.Linear(state_dim, hidden_dim)
        self.fc2 = nn.Linear(hidden_dim, hidden_dim)
        self.fc3 = nn.Linear(hidden_dim, action_dim)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return torch.softmax(self.fc3(x), dim=-1)  # Probability distribution


class REINFORCEAgent:
    """REINFORCE algorithm"""
    def __init__(self, state_dim, action_dim, lr=1e-3, gamma=0.99):
        self.gamma = gamma
        self.policy = PolicyNetwork(state_dim, action_dim)
        self.optimizer = optim.Adam(self.policy.parameters(), lr=lr)

        # Store the log within the episode
        self.log_probs = []
        self.rewards = []

    def select_action(self, state):
        """Sample an action according to the policy"""
        state_tensor = torch.FloatTensor(state).unsqueeze(0)
        probs = self.policy(state_tensor)

        # Sample from the probability distribution
        action_dist = torch.distributions.Categorical(probs)
        action = action_dist.sample()

        # Store the log probability (for gradient computation)
        self.log_probs.append(action_dist.log_prob(action))

        return action.item()

    def store_reward(self, reward):
        """Store the reward"""
        self.rewards.append(reward)

    def update(self):
        """Update the policy after the episode ends"""
        # Compute the returns (cumulative rewards)
        returns = []
        R = 0
        for r in reversed(self.rewards):
            R = r + self.gamma * R
            returns.insert(0, R)

        returns = torch.FloatTensor(returns)

        # Normalize (stabilize learning)
        returns = (returns - returns.mean()) / (returns.std() + 1e-8)

        # Policy gradient
        policy_loss = []
        for log_prob, R in zip(self.log_probs, returns):
            policy_loss.append(-log_prob * R)

        # Gradient descent
        self.optimizer.zero_grad()
        loss = torch.stack(policy_loss).sum()
        loss.backward()
        self.optimizer.step()

        # Reset the log
        self.log_probs = []
        self.rewards = []


# Simple materials exploration environment (discrete action version)
class DiscreteMaterialsEnv:
    """Discrete-action materials exploration environment"""
    def __init__(self, state_dim=4):
        self.state_dim = state_dim
        self.target = np.array([3.0, 5.0, 2.5, 4.0])
        self.state = None

    def reset(self):
        self.state = np.random.uniform(0, 10, self.state_dim)
        return self.state

    def step(self, action):
        # Actions: 0=increase dim 0, 1=decrease dim 0, 2=increase dim 1, 3=decrease dim 1
        dim = action // 2
        delta = 0.5 if action % 2 == 0 else -0.5

        self.state[dim] = np.clip(self.state[dim] + delta, 0, 10)

        # Reward: distance to the target
        distance = np.linalg.norm(self.state - self.target)
        reward = -distance

        done = distance < 0.5

        return self.state, reward, done


# Training REINFORCE
env = DiscreteMaterialsEnv()
agent = REINFORCEAgent(state_dim=4, action_dim=4)

episodes = 1000
rewards_history = []

for episode in range(episodes):
    state = env.reset()
    total_reward = 0
    done = False

    while not done:
        action = agent.select_action(state)
        next_state, reward, done = env.step(action)

        agent.store_reward(reward)
        state = next_state
        total_reward += reward

    # Update after the episode ends
    agent.update()
    rewards_history.append(total_reward)

    if (episode + 1) % 100 == 0:
        avg_reward = np.mean(rewards_history[-100:])
        print(f"Episode {episode+1}: Avg Reward = {avg_reward:.2f}")

# Learning curve
plt.figure(figsize=(10, 6))
plt.plot(np.convolve(rewards_history, np.ones(20)/20, mode='valid'))
plt.xlabel('Episode')
plt.ylabel('Average Reward (20 episodes)')
plt.title('REINFORCE: Materials Exploration with Policy Gradient')
plt.grid(True)
plt.show()

Example output:

Episode 100: Avg Reward = -38.24
Episode 200: Avg Reward = -28.15
Episode 500: Avg Reward = -15.32
Episode 1000: Avg Reward = -7.89

2.2 Baselines and Variance Reduction

Problems with REINFORCE

REINFORCE has high variance. Even with the same policy, the return $R_t$ fluctuates greatly depending on whether luck is good or bad.

Introducing a Baseline

By subtracting a baseline $b(s)$, the variance is reduced:

$$ \nabla_\theta J(\theta) = \mathbb{E} \left[ \sum_{t=0}^T \nabla_\theta \log \pi_\theta(a_t|s_t) \cdot (R_t - b(s_t)) \right] $$

Optimal baseline: the state-value function $V(s)$

$$ b(s_t) = V(s_t) = \mathbb{E}_{\pi} \left[ \sum_{k=t}^T \gamma^{k-t} r_k \mid s_t \right] $$

Advantage function $A(s, a)$: $$ A(s, a) = Q(s, a) - V(s) = R_t - V(s_t) $$

This represents "how much better this action is than average."

REINFORCE with a Baseline

class ValueNetwork(nn.Module):
    """Value network (baseline)"""
    def __init__(self, state_dim, hidden_dim=64):
        super(ValueNetwork, self).__init__()
        self.fc1 = nn.Linear(state_dim, hidden_dim)
        self.fc2 = nn.Linear(hidden_dim, hidden_dim)
        self.fc3 = nn.Linear(hidden_dim, 1)  # Output the state value

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return self.fc3(x)


class REINFORCEWithBaseline:
    """REINFORCE with a baseline"""
    def __init__(self, state_dim, action_dim, lr=1e-3, gamma=0.99):
        self.gamma = gamma
        self.policy = PolicyNetwork(state_dim, action_dim)
        self.value = ValueNetwork(state_dim)

        self.policy_optimizer = optim.Adam(self.policy.parameters(), lr=lr)
        self.value_optimizer = optim.Adam(self.value.parameters(), lr=lr)

        self.log_probs = []
        self.rewards = []
        self.states = []

    def select_action(self, state):
        state_tensor = torch.FloatTensor(state).unsqueeze(0)
        probs = self.policy(state_tensor)

        action_dist = torch.distributions.Categorical(probs)
        action = action_dist.sample()

        self.log_probs.append(action_dist.log_prob(action))
        self.states.append(state)

        return action.item()

    def store_reward(self, reward):
        self.rewards.append(reward)

    def update(self):
        # Compute the returns
        returns = []
        R = 0
        for r in reversed(self.rewards):
            R = r + self.gamma * R
            returns.insert(0, R)

        returns = torch.FloatTensor(returns)
        states = torch.FloatTensor(self.states)

        # Output of the value network (baseline)
        values = self.value(states).squeeze()

        # Advantage = return - baseline
        advantages = returns - values.detach()

        # Policy gradient loss
        policy_loss = []
        for log_prob, adv in zip(self.log_probs, advantages):
            policy_loss.append(-log_prob * adv)

        # Value network loss (MSE)
        value_loss = nn.MSELoss()(values, returns)

        # Optimization
        self.policy_optimizer.zero_grad()
        torch.stack(policy_loss).sum().backward()
        self.policy_optimizer.step()

        self.value_optimizer.zero_grad()
        value_loss.backward()
        self.value_optimizer.step()

        # Reset
        self.log_probs = []
        self.rewards = []
        self.states = []


# Training (with a baseline)
agent_baseline = REINFORCEWithBaseline(state_dim=4, action_dim=4)

rewards_baseline = []
for episode in range(1000):
    state = env.reset()
    total_reward = 0
    done = False

    while not done:
        action = agent_baseline.select_action(state)
        next_state, reward, done = env.step(action)

        agent_baseline.store_reward(reward)
        state = next_state
        total_reward += reward

    agent_baseline.update()
    rewards_baseline.append(total_reward)

# Comparison
plt.figure(figsize=(10, 6))
plt.plot(np.convolve(rewards_history, np.ones(20)/20, mode='valid'), label='REINFORCE')
plt.plot(np.convolve(rewards_baseline, np.ones(20)/20, mode='valid'), label='REINFORCE + Baseline')
plt.xlabel('Episode')
plt.ylabel('Average Reward')
plt.title('Learning Stabilization via Baseline')
plt.legend()
plt.grid(True)
plt.show()

Result: With the baseline, learning becomes more stable and convergence becomes faster.


2.3 Actor-Critic Architecture

Concept of Actor-Critic

Learn the Actor (policy) and the Critic (value function) simultaneously:

flowchart LR S[State s] --> A[Actor: πθ] S --> C[Critic: Vϕ] A -->|Action a| E[Environment] E -->|Reward r| C C -->|TD error| A C -->|Value estimate| C style A fill:#e1f5ff style C fill:#ffe1cc

TD Error and Advantage

TD error (Temporal Difference Error): $$ \delta_t = r_t + \gamma V_\phi(s_{t+1}) - V_\phi(s_t) $$

This can be used as a one-step advantage estimate.

A2C (Advantage Actor-Critic)

class A2CAgent:
    """Advantage Actor-Critic"""
    def __init__(self, state_dim, action_dim, lr_actor=1e-3, lr_critic=1e-3, gamma=0.99):
        self.gamma = gamma

        self.actor = PolicyNetwork(state_dim, action_dim)
        self.critic = ValueNetwork(state_dim)

        self.actor_optimizer = optim.Adam(self.actor.parameters(), lr=lr_actor)
        self.critic_optimizer = optim.Adam(self.critic.parameters(), lr=lr_critic)

    def select_action(self, state):
        state_tensor = torch.FloatTensor(state).unsqueeze(0)
        probs = self.actor(state_tensor)

        action_dist = torch.distributions.Categorical(probs)
        action = action_dist.sample()

        return action.item(), action_dist.log_prob(action)

    def update(self, state, action_log_prob, reward, next_state, done):
        """Update at every step"""
        state_tensor = torch.FloatTensor(state).unsqueeze(0)
        next_state_tensor = torch.FloatTensor(next_state).unsqueeze(0)

        # Current and next state values
        value = self.critic(state_tensor)
        next_value = self.critic(next_state_tensor)

        # TD target and TD error
        td_target = reward + (1 - done) * self.gamma * next_value.item()
        td_error = td_target - value.item()

        # Critic loss (MSE)
        critic_loss = (torch.FloatTensor([td_target]) - value).pow(2)

        # Actor loss (policy gradient x advantage)
        actor_loss = -action_log_prob * td_error

        # Optimization
        self.actor_optimizer.zero_grad()
        actor_loss.backward()
        self.actor_optimizer.step()

        self.critic_optimizer.zero_grad()
        critic_loss.backward()
        self.critic_optimizer.step()


# A2C training
agent_a2c = A2CAgent(state_dim=4, action_dim=4)

rewards_a2c = []
for episode in range(1000):
    state = env.reset()
    total_reward = 0
    done = False

    while not done:
        action, log_prob = agent_a2c.select_action(state)
        next_state, reward, done = env.step(action)

        # Update at every step
        agent_a2c.update(state, log_prob, reward, next_state, done)

        state = next_state
        total_reward += reward

    rewards_a2c.append(total_reward)

    if (episode + 1) % 100 == 0:
        avg_reward = np.mean(rewards_a2c[-100:])
        print(f"Episode {episode+1}: Avg Reward = {avg_reward:.2f}")

Advantages: - Online learning without waiting for the episode to end - Low variance thanks to the TD error


2.4 Proximal Policy Optimization (PPO)

Trust Region Methods

In policy gradient methods, if the update is too large, the policy can collapse.

Trust Region Policy Optimization (TRPO) constrains the change in the policy:

$$ \max_\theta \mathbb{E} \left[ \frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)} A(s, a) \right] \quad \text{s.t.} \quad D_{\text{KL}}(\pi_{\theta_{\text{old}}} | \pi_\theta) \leq \delta $$

However, optimizing with a KL divergence constraint is complex.

Simplification by PPO

PPO (Schulman et al., 2017) realizes the constraint through clipping within the loss function:

$$ L^{\text{CLIP}}(\theta) = \mathbb{E} \left[ \min \left( r_t(\theta) A_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) A_t \right) \right] $$

Intuition: - Advantage is positive (good action) -> increase $r_t$, but cap it at $1+\epsilon$ - Advantage is negative (bad action) -> decrease $r_t$, but floor it at $1-\epsilon$ - Prevent abrupt policy changes

Entropy Bonus

To encourage exploration, add entropy to the loss:

$$ L^{\text{PPO}}(\theta) = L^{\text{CLIP}}(\theta) + c_1 L^{\text{VF}}(\theta) - c_2 H[\pi_\theta] $$

Implementation of PPO (using Stable Baselines3)

from stable_baselines3 import PPO
from stable_baselines3.common.vec_env import DummyVecEnv
import gym

# Gym environment wrapper
class GymMaterialsEnv(gym.Env):
    """OpenAI Gym-compatible materials exploration environment"""
    def __init__(self):
        super(GymMaterialsEnv, self).__init__()
        self.state_dim = 4
        self.target = np.array([3.0, 5.0, 2.5, 4.0])

        # Define the action and state spaces
        self.action_space = gym.spaces.Discrete(4)
        self.observation_space = gym.spaces.Box(
            low=0, high=10, shape=(self.state_dim,), dtype=np.float32
        )

        self.state = None

    def reset(self):
        self.state = np.random.uniform(0, 10, self.state_dim).astype(np.float32)
        return self.state

    def step(self, action):
        dim = action // 2
        delta = 0.5 if action % 2 == 0 else -0.5

        self.state[dim] = np.clip(self.state[dim] + delta, 0, 10)

        distance = np.linalg.norm(self.state - self.target)
        reward = -distance
        done = distance < 0.5

        return self.state, reward, done, {}

    def render(self, mode='human'):
        pass


# Create the environment
env = DummyVecEnv([lambda: GymMaterialsEnv()])

# PPO model
model = PPO(
    "MlpPolicy",                # Multilayer perceptron policy
    env,
    learning_rate=3e-4,
    n_steps=2048,               # Number of steps before an update
    batch_size=64,
    n_epochs=10,                # Number of optimization epochs per update
    gamma=0.99,
    gae_lambda=0.95,            # GAE (Generalized Advantage Estimation)
    clip_range=0.2,             # PPO clipping range
    ent_coef=0.01,              # Entropy coefficient
    verbose=1,
    tensorboard_log="./ppo_materials_tensorboard/"
)

# Training
model.learn(total_timesteps=100000)

# Save
model.save("ppo_materials_agent")

# Evaluation
eval_env = GymMaterialsEnv()
state = eval_env.reset()
total_reward = 0

for _ in range(100):
    action, _ = model.predict(state, deterministic=True)
    state, reward, done, _ = eval_env.step(action)
    total_reward += reward

    if done:
        break

print(f"Evaluation result: Total Reward = {total_reward:.2f}")
print(f"Final state: {state}")
print(f"Target: {eval_env.target}")

Example output:

---------------------------------
| rollout/           |          |
|    ep_len_mean     | 45.2     |
|    ep_rew_mean     | -15.3    |
| time/              |          |
|    fps             | 1024     |
|    iterations      | 50       |
|    time_elapsed    | 97       |
|    total_timesteps | 102400   |
---------------------------------

Evaluation result: Total Reward = -5.23
Final state: [3.02 4.98 2.47 3.95]
Target: [3.  5.  2.5 4. ]

Explanation: - With Stable Baselines3, PPO is implemented in just a few lines - Learning progress can be visualized with TensorBoard - A material very close to the target is discovered


2.5 Extension to Continuous Action Spaces

Gaussian Policy

In materials science, continuous control of temperature, composition ratios, and so on is required.

For continuous actions, use a Gaussian distribution policy:

$$ \pi_\theta(a|s) = \mathcal{N}(\mu_\theta(s), \sigma_\theta(s)) $$

Continuous-Action PPO

# Continuous-action environment
class ContinuousGymMaterialsEnv(gym.Env):
    """Continuous-action materials exploration environment"""
    def __init__(self):
        super(ContinuousGymMaterialsEnv, self).__init__()
        self.state_dim = 4
        self.target = np.array([3.0, 5.0, 2.5, 4.0])

        # Continuous action space (4-dimensional vector, range [-1, 1])
        self.action_space = gym.spaces.Box(
            low=-1, high=1, shape=(self.state_dim,), dtype=np.float32
        )
        self.observation_space = gym.spaces.Box(
            low=0, high=10, shape=(self.state_dim,), dtype=np.float32
        )

        self.state = None

    def reset(self):
        self.state = np.random.uniform(0, 10, self.state_dim).astype(np.float32)
        return self.state

    def step(self, action):
        # Map the action to a state change (-1 to 1 -> -0.5 to 0.5)
        delta = action * 0.5
        self.state = np.clip(self.state + delta, 0, 10)

        distance = np.linalg.norm(self.state - self.target)
        reward = -distance
        done = distance < 0.3

        return self.state, reward, done, {}

    def render(self, mode='human'):
        pass


# Continuous-action PPO
env_continuous = DummyVecEnv([lambda: ContinuousGymMaterialsEnv()])

model_continuous = PPO(
    "MlpPolicy",
    env_continuous,
    learning_rate=3e-4,
    n_steps=2048,
    batch_size=64,
    n_epochs=10,
    gamma=0.99,
    clip_range=0.2,
    verbose=1
)

model_continuous.learn(total_timesteps=100000)

# Evaluation
eval_env_cont = ContinuousGymMaterialsEnv()
state = eval_env_cont.reset()

for _ in range(50):
    action, _ = model_continuous.predict(state, deterministic=True)
    state, reward, done, _ = eval_env_cont.step(action)

    if done:
        break

print(f"Final state: {state}")
print(f"Target: {eval_env_cont.target}")
print(f"Distance: {np.linalg.norm(state - eval_env_cont.target):.4f}")

Example output:

Final state: [3.001 5.003 2.498 3.997]
Target: [3.  5.  2.5 4. ]
Distance: 0.0054

Explanation: Continuous actions enable precise control toward the target.


Exercises

Problem 1 (Difficulty: easy)

Explain why using a baseline reduces the variance, using the following expression.

$$ \text{Var}[R_t] \quad \text{vs.} \quad \text{Var}[R_t - b(s_t)] $$

Hint Property of variance: $\text{Var}[X - c] = \text{Var}[X]$ (subtracting a constant $c$ does not change the variance). However, $b(s\_t)$ is state-dependent, so it is not a constant.
Sample Solution When the baseline $b(s\_t)$ is close to the state value $V(s\_t)$: - The **return** $R\_t$ fluctuates greatly depending on the state (large influence of luck) - The **advantage** $R\_t - V(s\_t)$ is the "deviation from the average," so its fluctuation is small Mathematically: $$ \text{Var}[R\_t - V(s\_t)] \leq \text{Var}[R\_t] $$ This is because $V(s\_t)$ is the "expected cumulative reward from state $s\_t$," and thus cancels out the influence of luck. **Concrete example**: - Returns from state A: 100, 105, 95 -> variance = 25 - Value of state A: 100 - Advantage: 0, 5, -5 -> variance = 25 (same) However, when considering multiple states: - Return from state A: 100±5 - Return from state B: 50±5 - Overall variance: large By subtracting the per-state average with the baseline, the differences between states disappear and the variance is reduced.

Problem 2 (Difficulty: medium)

Explain what happens when the PPO clipping range $\epsilon$ is increased, and what happens in the extreme case of $\epsilon=0$.

Hint Review the clipping expression and think about how the change in $r\_t(\theta)$ is restricted.
Sample Solution **When $\epsilon$ is increased**: - The clipping range widens, and the policy change becomes larger - Learning is fast but tends to be unstable - In the extreme case, the policy may collapse **When $\epsilon=0$**: $$ \text{clip}(r\_t, 1, 1) = 1 $$ - The importance ratio is always clipped to 1 - The policy is not updated at all (forces $\pi\_\theta = \pi\_{\theta\_{\text{old}}}$) **Practical value**: $\epsilon = 0.1 \sim 0.2$ is common **Experiment code**:
# ε=0.05 (tight constraint)
model_tight = PPO("MlpPolicy", env, clip_range=0.05)

# ε=0.5 (loose constraint)
model_loose = PPO("MlpPolicy", env, clip_range=0.5)

# Compare the learning curves
# -> model_tight is stable but slow
# -> model_loose is fast but oscillates

Problem 3 (Difficulty: hard)

In materials exploration, compare the following two reward designs and describe the strengths and weaknesses of each. Also, actually experiment with them in code.

Reward A (sparse reward): reward 1 only when the target is reached, 0 otherwise Reward B (dense reward): a continuous reward based on the distance to the target

Hint Sparse rewards make exploration difficult, whereas dense rewards are prone to falling into local optima. Consider the influence of the entropy bonus as well.
Sample Solution **Strengths and weaknesses of Reward A (sparse reward)**: **Strengths**: - Clear objective (no ambiguity) - Less prone to falling into local optima (not misled by intermediate rewards) **Weaknesses**: - Exploration is extremely difficult (weak learning signal) - Learning takes a long time **Strengths and weaknesses of Reward B (dense reward)**: **Strengths**: - Exploration is easy (feedback at every step) - Learning is fast **Weaknesses**: - Reward design is difficult (distance alone may be insufficient) - Prone to falling into local optima **Experiment code**:
# Reward A (sparse reward)
class SparseRewardEnv(gym.Env):
    def step(self, action):
        # ... (state update) ...
        distance = np.linalg.norm(self.state - self.target)

        if distance < 0.5:
            reward = 1.0  # Reached
            done = True
        else:
            reward = 0.0  # Otherwise
            done = False

        return self.state, reward, done, {}

# Reward B (dense reward)
class DenseRewardEnv(gym.Env):
    def step(self, action):
        # ... (state update) ...
        distance = np.linalg.norm(self.state - self.target)
        reward = -distance  # Continuous reward
        done = distance < 0.5

        return self.state, reward, done, {}

# Comparison experiment
model_sparse = PPO("MlpPolicy", DummyVecEnv([lambda: SparseRewardEnv()]))
model_dense = PPO("MlpPolicy", DummyVecEnv([lambda: DenseRewardEnv()]))

model_sparse.learn(total_timesteps=100000)
model_dense.learn(total_timesteps=100000)

# Result: model_dense learns faster, but
# in complex environments model_sparse may find a better solution
**Best practice**: Start with a dense reward, and depending on the problem, consider a sparse reward or **reward shaping** (adding intermediate rewards).

Summary of This Section

In the next chapter, you will learn how to build custom environments specialized for materials exploration and how to design rewards.


Quality Checklist: Verifying Your Policy Gradient Implementation

Theoretical Understanding Skills

Implementation Skills

Application to Materials Exploration

Debugging Skills


References

  1. Williams "Simple statistical gradient-following algorithms for connectionist reinforcement learning" Machine Learning (1992) - REINFORCE
  2. Mnih et al. "Asynchronous methods for deep reinforcement learning" ICML (2016) - A3C/A2C
  3. Schulman et al. "Proximal policy optimization algorithms" arXiv (2017) - PPO
  4. Schulman et al. "Trust region policy optimization" ICML (2015) - TRPO
  5. Raffin et al. "Stable-Baselines3: Reliable reinforcement learning implementations" JMLR (2021)

Next Chapter: Chapter 3: Building Materials Exploration Environments

Disclaimer