Theory

1. Reinforcement Learning Section Introduction

Introduction to Reinforcement Learning

In this section of the course, we’ll dive into the theory behind reinforcement learning (RL). This lecture is designed to introduce you to RL in general terms, avoiding complex math or specialized terminology. One important thing to note is that reinforcement learning is quite different from supervised and unsupervised learning. If you’ve just completed an introductory machine learning course or have a background in statistics, where you learned about models like Naive Bayes or K-means clustering, you’ll find RL to be a new and distinct way of thinking. So, take your time to absorb these concepts and don’t be intimidated by this different approach.

Supervised Learning: A Static Function

Let’s start by thinking about supervised learning. Consider an image classifier—this can be thought of as a static function. You pass in an image, and the classifier gives you a prediction, telling you what object is in the image. There’s no notion of time involved here. If you pass in another image, you get another prediction. The classifier simply takes an input and produces an output.

Now, when I say “static” and “time,” you might think of recurrent neural networks (RNNs), which can handle sequential data that varies over time. However, this isn’t the kind of time we’re referring to here. For example, if you input stock prices over a period and your model predicts whether the stock will go up or down tomorrow, it’s still a static function in this context.

Reinforcement Learning: Time and Planning

Reinforcement learning, on the other hand, incorporates the concept of time. Imagine you’re developing a self-driving car simulation. At each moment, the neural network takes a snapshot of the screen and decides the next action—whether to steer left or right, accelerate, or brake. This is where RL differs from supervised learning. While supervised learning is about repeatedly calling a function to get a prediction, RL is more like a loop with a goal in mind, such as driving to a specific destination.

In this loop, yes, the RL model still takes an image and produces an output, but it also considers the future. It’s not just about translating an image into an output; it’s about planning a sequence of actions to achieve a goal. Even though the car may only see where it is on the road right now, it understands that certain actions will lead it closer to its destination.

Comparing Supervised Learning and Reinforcement Learning

The major difference between supervised learning and reinforcement learning lies in their goals and how they approach time. In supervised learning, there’s no concept of planning or future goals. You simply take an input and produce an output—it’s a static function. In contrast, reinforcement learning involves planning for the future and working towards a predefined goal.

Understanding Data in Supervised Learning

Let’s look at the data used in supervised learning. For example, in image classification, you need a label for every input in your training set. If you have an image of a dog, there must be a corresponding label that says “dog.” If you have an image of a cat, the label should say “cat.” For every input , there must be a corresponding output .

It’s crucial to remember that these labeled datasets are created by humans. Some students might think that we could automate the creation of labeled datasets, but if we could do that, we would have already solved machine learning. After all, the goal is to build computers that can perfectly label data. If such computers already existed, there would be no need to build them.

Reinforcement Learning: Goals, Not Targets

Now, consider our self-driving car example again. If we used supervised learning, we’d need to provide a target for every image the car sees. But what should the target be? Should the car steer left, steer right, accelerate, or brake? Labeling every single frame the car encounters during a journey would be nearly impossible. For example, if your camera captures 30 frames per second and you have a one-hour drive, you’d need to label 108,000 images from just one trip!

Instead, reinforcement learning uses goals rather than specific targets. Suppose you want to teach an RL algorithm to solve a maze. The goal here is to find the maze exit. You don’t need to tell the algorithm what to do at each step in the maze—that would be supervised learning. Instead, the RL algorithm only needs to know the goal, and it will figure out the actions required to achieve it. This is the power of reinforcement learning, offering a new paradigm in machine learning.

2. Elements of a Reinforcement Learning Problem

Introduction to Reinforcement Learning Concepts

In this lecture, we’ll dive into the core concepts of reinforcement learning (RL) using examples to make the ideas more concrete. Reinforcement learning involves several key elements that interact within an RL problem, and we’ll explore these by discussing various scenarios.

Agent and Environment

The agent is the learner or decision-maker, and the environment is everything the agent interacts with. Think of yourself as the agent, and the world around you as the environment. For instance, if your goal is to ace a math exam, your environment includes everything related to that goal—classes, textbooks, homework, etc. You need to make decisions (study, take notes, etc.) to achieve your goal.

Example 1: Tic Tac Toe

  • Environment: The computer program running the game, including any predefined rules or AI opponents.
  • Agent: Your program that plays the game, using RL algorithms to learn from experience.

Example 2: Breakout (Atari Game)

  • Environment: The game itself, including all elements like the ball, blocks, and paddle.
  • Agent: A computer program that controls the paddle, aiming to keep the ball in play and destroy the blocks.

Episode

An episode refers to one complete round of interaction between the agent and the environment, ending with a win, loss, or other terminal state. For example, a game of tic-tac-toe or Breakout is an episode. After each episode, the agent can start a new one, using what it learned to improve.

  • Episodic Environments: These environments have a clear start and end, like games.
  • Non-Episodic Environments: These environments continue indefinitely, like the stock market or online advertising systems.

States, Actions, and Rewards

These three concepts describe the interactions between the agent and the environment.

  • State: A representation of the current situation within the environment. In tic-tac-toe, the state is the configuration of X’s and O’s on the board.
  • Action: The moves the agent can make. In tic-tac-toe, an action could be placing an X or O on the board.
  • Reward: A numerical value representing the outcome of an action. For example, winning tic-tac-toe might give a reward of +1, losing -1, and a draw 0.

Example: Maze

  • State: The agent’s position in the maze.
  • Action: Moving up, down, left, or right.
  • Reward: +1 for reaching the goal, with the option to penalize each move with -1 to encourage faster solutions.

State and Action Spaces

  • State Space: The set of all possible states in an environment. In tic-tac-toe, this includes every possible board configuration.
  • Action Space: The set of all possible actions. For tic-tac-toe, these are the available moves at any point in the game.

Summary

To summarize, we covered the following RL concepts:

  1. Agent and Environment: The agent interacts with the environment to achieve a goal.
  2. Episode: One round of interaction, ending in a terminal state.
  3. States, Actions, and Rewards:
    • States: Describe the current situation.
    • Actions: The decisions the agent makes.
    • Rewards: Numerical feedback that the agent aims to maximize.
  4. State and Action Spaces: All possible states and actions within the environment.

Understanding these fundamental terms will help you grasp more complex RL topics and ultimately build solutions to RL problems.

3. States, Actions, Rewards, Policies

Understanding States, Actions, and Rewards in Reinforcement Learning

Overview

In this guide, we’ll explore how to encode and implement states, actions, and rewards in reinforcement learning (RL), particularly focusing on their practical applications in code. Understanding these elements is essential, as they form the foundation of RL algorithms used in real-world scenarios.


States and Actions

1. Discrete vs. Continuous States

  • Discrete States:

    • These are finite and countable, similar to the different configurations on a Tic-Tac-Toe board. Each state represents a specific condition or scenario in the environment.
    • Example: The position of a chess piece on a board can be described as a discrete state.
  • Continuous States:

    • These are infinite and uncountable, representing values within a continuous range. A robot with sensors that generate data like temperature, velocity, or visual input has continuous states.
    • Example: The exact position and velocity of a car driving on a road can be described as a continuous state.
Mathematical Representation:
  • Discrete State Space:

    • Here, is a set of discrete states.
  • Continuous State Space:

    • Continuous states lie within an -dimensional real-valued space.

2. Encoding States in Code

  • Categorical (Discrete) States:

    • These can be represented using integers. For example, assigning dog = 0, cat = 1 allows each category to be used as an index or key in data structures like arrays or dictionaries.
    state_mapping = {'dog': 0, 'cat': 1}
    current_state = state_mapping['dog']  # current_state = 0
  • Continuous States:

    • These are usually stored as vectors or arrays. For complex data types, such as images, the state is stored in a multi-dimensional array (tensor).
    import numpy as np
    current_state = np.array([0.5, 1.2, -0.3])  # A vector representing continuous state

Policies

1. What is a Policy?

  • A policy is a rule or function that an agent uses to select an action based on the current state. It defines the agent’s behavior and is crucial for decision-making in RL.
Mathematical Representation:
  • Deterministic Policy:

    • Given a state , the policy returns a specific action .
  • Stochastic Policy:

    • Given a state , the policy returns a probability distribution over actions, from which an action is sampled.

2. Representing Policies in Code

  • Simple Policy (Discrete):

    • For small, discrete state spaces, a policy can be implemented as a dictionary mapping states to actions.
    policy = {0: 'move_right', 1: 'move_left'}
    action = policy[current_state]
  • Optimized Policy Representation:

    • For large or infinite state spaces, policies are often represented using arrays or functions, especially when states can be indexed by integers.
    policy = np.array(['move_right', 'move_left'])
    action = policy[current_state]

Stochastic Policies and Exploration

1. Limitations of Deterministic Policies

  • Deterministic policies always select the same action for a given state, which can limit exploration. Without exploration, the agent may miss out on better strategies or solutions.

2. Introducing Stochastic Policies

  • Stochastic (Probabilistic) Policies:

    • To foster exploration, policies can be made stochastic, meaning they return a probability distribution over actions rather than a single action.
    import random
     
    def stochastic_policy(state):
        actions = ['move_right', 'move_left']
        probabilities = [0.8, 0.2]  # 80% chance to move right
        return random.choices(actions, probabilities)[0]
     
    action = stochastic_policy(current_state)
  • Epsilon-Greedy Method:

    • This method introduces randomness by occasionally choosing a random action with probability , promoting exploration even in deterministic policies.
    def epsilon_greedy_policy(state, epsilon=0.1):
        if random.random() < epsilon:
            return random.choice(['move_right', 'move_left'])
        return deterministic_policy[state]

3. Continuous State Spaces

  • Softmax Function:

    • In continuous or large state spaces, actions can be selected based on a probability distribution generated by a softmax function. This approach is often used in conjunction with models like neural networks.
    def softmax(x):
        e_x = np.exp(x - np.max(x))
        return e_x / e_x.sum(axis=0)
     
    # Assuming some model output for actions
    model_output = np.array([1.2, 0.8, 0.6])
    probabilities = softmax(model_output)
    action = random.choices(['action1', 'action2', 'action3'], probabilities)[0]

Final Thoughts on Policies

1. Probabilistic Approach

  • Stochastic policies allow greater flexibility and adaptability in complex environments, enabling the agent to explore and potentially discover better strategies.

2. Learning Over Time

  • Although a policy uses only the current state to make decisions, the agent improves its performance over time by learning from the rewards received in different states, gradually refining the policy to maximize long-term rewards.

Conclusion

Mastering how to encode states and actions and implement policies is vital in reinforcement learning. As we continue, we will explore how to optimize these policies and how agents can plan for future rewards by learning from experience. Understanding these fundamental concepts will prepare you for more advanced topics in RL, including policy optimization and value functions.

4. Markov Decision Processes (MDPs)

Let’s break down the concepts step by step, highlighting the core ideas of reinforcement learning (RL) and how the Markov assumption fits into the broader framework. We’ll also use some mathematical notation to clarify these ideas.

1. Terminology Recap

  • Agent: The decision-maker in the RL problem.
  • Environment: The external system with which the agent interacts.
  • State (s): A representation of the current situation of the agent within the environment.
  • Action (a): A decision or move made by the agent.
  • Reward (r): Feedback from the environment to the agent’s action, indicating the immediate benefit of that action.
  • Policy (π): The strategy or rule that the agent follows to choose actions based on the current state.

2. Problem Definition with Markov Assumption

The Markov assumption is central to RL and is often discussed in the context of Markov models and sequence modeling. It states that the probability of transitioning to a future state depends only on the current state, not on the sequence of states that preceded it.

Example: Predicting Weather

  • Suppose you want to predict tomorrow’s weather.
  • Markov Assumption: Tomorrow’s weather depends only on today’s weather, not on the weather from the past week.

Example: Predicting the Next Word in a Sentence

  • If the previous word is “lazy,” you might predict “programmer.” However, knowing the whole sentence, “The quick brown fox jumps over the lazy,” helps you correctly predict “dog” as the next word.
  • The Markov assumption here would simplify by considering only the last word “lazy,” not the entire sentence.

3. Markov Decision Process (MDP)

RL problems are often modeled as Markov Decision Processes (MDPs). An MDP is defined by:

  • A set of states
  • A set of actions
  • A state transition probability
  • A reward function

The state transition probability represents the likelihood of moving to state after taking action in state .

Mathematical Representation:

  • State Transition Probability:
  • Reward Function: or simplified

4. Why the Markov Assumption and MDP are Useful

Even though the Markov assumption might seem restrictive, it provides a powerful framework for solving RL problems. The assumption allows us to model the problem mathematically, making it tractable for algorithmic solutions.

Example: Tic-Tac-Toe and Inverted Pendulum

  • Tic-Tac-Toe: Your action (placing an X or O) is deterministic, but the opponent’s move introduces uncertainty.
  • Inverted Pendulum: While the laws of physics (deterministic) govern the system, small errors or uncertainties (chaos theory) make exact predictions difficult.

5. Agent and Environment Interaction

In an MDP, the interaction between the agent and the environment is cyclical:

  1. The agent observes the current state .
  2. The agent takes action based on its policy .
  3. The environment transitions to a new state and provides a reward .
  4. This cycle repeats.

6. Mathematical Framework for Solutions

By representing both the agent’s policy and the environment’s dynamics probabilistically, we can mathematically describe and solve RL problems.

  • State Transition Probability:
  • Agent Policy:

7. Conclusion: The Importance of a Well-Defined Problem

The process of defining the RL problem mathematically using MDPs is crucial. With a well-defined problem, you can apply mathematical tools to find a solution. This structured approach is the foundation upon which practical RL algorithms, like Q-learning, are built.


This framework will allow us to explore specific algorithms and methods to solve RL problems effectively, starting with foundational methods like Q-learning. Understanding these core concepts will enable you to tackle more complex scenarios and ultimately derive solutions to RL problems.

5. The Return

Let’s break down and structure this lecture content for better understanding, focusing on key concepts, mathematical definitions, and real-world analogies.


Goal of an Agent in Reinforcement Learning

The main objective of an agent in reinforcement learning (RL) is to maximize the sum of future rewards. This is a crucial point because it informs the agent’s decision-making process over time.

  1. Immediate vs. Long-term Rewards:
    • Immediate Reward: This is the reward the agent receives right after taking an action.
    • Future Rewards: Rewards the agent expects to receive in the future, based on its current action.

Real-World Example: Preparing for a Math Exam

  • Immediate actions (e.g., studying, doing homework) might not provide immediate gratification, but they contribute to the future reward (e.g., a good grade).
  • Long-term Planning: The agent (you, preparing for the exam) must consider the long-term outcome (the exam grade) and not just the immediate discomfort of studying.

Defining the Return

In RL, we refer to the sum of future rewards as the return. Mathematically, the return at a specific time is defined as:

Where:

  • is the return at time .
  • is the reward received at time step .
  • is the terminal time, marking the end of the episode.

Dealing with Infinite Horizon: Discounting

If the task doesn’t have a natural endpoint (infinite horizon), summing all future rewards might lead to an infinite return. To handle this, we introduce the concept of discounting:

Here, (Gamma) is the discount factor, a value between 0 and 1 that determines how much we care about future rewards compared to immediate rewards.

  • Gamma (): If is close to 1 (e.g., 0.99), future rewards are nearly as important as immediate rewards. If is much less than 1, immediate rewards are prioritized much higher than future ones.

Analogy: Present Value of Money

  • Money Example: Similar to preferring $100 today over $100 ten years from now, the agent prefers immediate rewards over those in the distant future.
  • Discount Factor in Money: Just like interest rates reduce the future value of money, the discount factor reduces the value of future rewards.

Recursive Definition of Return

One powerful feature of the return is that it can be defined recursively:

This means that the return at time depends on the immediate reward plus the discounted return from the next time step.


Key Takeaways

  • Maximizing future rewards is the agent’s goal, not just immediate rewards.
  • Return is the sum of all future rewards, possibly discounted for long-term tasks.
  • Discounting helps handle infinite tasks and models the decreasing importance of distant future rewards.
  • The recursive formula of return is a fundamental concept in RL, enabling more complex decision-making strategies.

By understanding and applying these concepts, you can effectively design and train agents to make decisions that optimize their performance over time.

6. Value Functions and the Bellman Equation

Let’s break down the lecture into key concepts, explain them more systematically, and incorporate some mathematical notation to enhance understanding.

1. Expected Value (Mean)

The expected value (or mean) of a random variable is a fundamental concept in probability theory. It represents the “average” outcome if we were to repeat an experiment infinitely many times.

Mathematical Definition:

For a discrete random variable with possible values and corresponding probabilities , the expected value is given by:

Example 1: Gaussian Distribution

If you measure the heights of 1,000 students and find an average height of 70 inches with a standard deviation of 4 inches, the expected value (mean) is 70 inches.

Example 2: Coin Flip

Consider a coin flip with outcomes 0 (tails) and 1 (heads), each with a probability of 0.5. The expected value for this random variable is:

This means that over many flips, the average result would be 0.5, even though you never actually see 0.5 in a single flip.

2. Expected Value in Reinforcement Learning

In reinforcement learning, rewards and returns are treated as random variables because both the environment and the policy (agent’s decision-making strategy) introduce uncertainty.

Value Function:

The expected return, or the value function , is the expected sum of future rewards starting from a particular state . Mathematically:

Where:

  • is the reward received at time step .
  • is the discount factor (a number between 0 and 1 that reduces the importance of future rewards).

3. Bellman Equation

The Bellman equation is a recursive formula that expresses the value function in terms of the expected immediate reward plus the discounted value of the next state. It forms the foundation for many reinforcement learning algorithms.

Mathematical Formulation:

For a given policy and state , the Bellman equation is:

Where:

  • is the probability of taking action in state under policy .
  • is the probability of transitioning to state from state by taking action .
  • is the reward received after transitioning from state to state by taking action .

4. Physical Interpretation

  • represents the policy, which is the agent’s decision-making strategy. It can be thought of as the “animal” in the environment.
  • represents the state transition probability, which is determined by the environment dynamics. It can be thought of as the “world.”

5. Linear Equations and the Prediction Problem

If the policy and the environment dynamics are known, the Bellman equation becomes a system of linear equations, which can be solved using linear algebra methods.

Example: Simple Grid World

Assume a grid world with three states. The Bellman equation for each state might look like:

Where are constants derived from the known probabilities.

6. Conclusion:

The Bellman equation provides a framework for evaluating policies by defining the value function, which measures the expected return. By solving this equation, we can assess how good a policy is and, ultimately, find optimal policies that maximize rewards in a reinforcement learning problem.

7. What does it mean to “learn”

Introduction to Learning in Reinforcement Learning

In reinforcement learning (RL), the process of learning revolves around two main tasks:

  1. Prediction Problem: Given a policy , find the associated value function .
  2. Control Problem: Find the optimal policy that maximizes the value function .

Value Functions: State Value vs. Action Value

State Value Function

  • Definition: The value of being in state and following policy thereafter. It represents the expected sum of future rewards from state under policy .

Action Value Function

  • Definition: The value of being in state , taking action , and then following policy thereafter. It adds an extra dimension, conditioning on the action as well as the state .

Storage Requirements

  • State Value Function : If there are states, storing requires an array of size .
  • Action Value Function : If there are states and actions, storing requires a 2D array of size .

Optimal Policy and Value Functions

Optimal State Value Function

  • Definition: The maximum value function over all possible policies. This is the best possible value you can get starting from state .

Optimal Action Value Function

  • Definition: The maximum action value over all possible policies. It represents the value of taking action in state and then following the optimal policy thereafter.

Relationship Between and

  • Mathematical Expression:
  • Interpretation: The optimal state value is the maximum value of taking any action in state , assuming the best policy is followed afterward.

Finding the Optimal Policy: The Control Problem

Naive Search Approach

  1. Enumerate All Possible Policies: For a finite state and action space, list all possible policies.
  2. Evaluate Each Policy: Compute the value function for each policy.
  3. Compare and Select the Best Policy: Choose the policy with the highest value function as the optimal policy .

Practical Considerations

  • Evaluate Function: Finding for a given policy can be done by solving a system of linear equations, provided you know the policy distribution and environment dynamics.
  • Enumeration Impracticality: While conceptually simple, enumerating all policies is computationally infeasible for anything but trivial problems due to the exponential growth in the number of possible policies.

Conclusion

The control problem in reinforcement learning aims to find the optimal policy by maximizing the value function. While a naive search method might work in simple cases, more practical approaches are required for complex environments. In the next lecture, we’ll delve deeper into these practical methods and explore how to efficiently evaluate and optimize policies in reinforcement learning.

8. Solving the Bellman Equation with Reinforcement Learning (pt 1)

Reinforcement Learning Overview

In reinforcement learning, we aim to solve two key problems:

  1. Prediction Problem: Given a policy (a strategy or plan), we need to determine the expected return or “value” for each state. This is known as finding the value function.
  2. Control Problem: This involves finding the best policy that maximizes the expected return. Essentially, it’s about discovering the optimal strategy.

Recap of Simple Approaches

Initially, we looked at naive solutions:

  • Prediction: If we have both the policy distribution and state transition probabilities, solving the prediction problem becomes a linear algebra task.
  • Control: We could theoretically enumerate all possible policies and evaluate them to find the best one.

Why These Approaches Are Unrealistic

  1. Prediction Problem:

    • Environment Dynamics Unknown: In many real-world scenarios, such as playing an Atari game, we don’t have access to the environment’s transition probabilities. We can’t measure state transitions feasibly due to the vast state space.
  2. Control Problem:

    • Exponential Growth of Policies: The number of possible policies grows exponentially with the size of the state and action spaces. This makes it impractical to enumerate and evaluate all policies, especially in environments with large or infinite state spaces.

The Solution: Monte Carlo Sampling

Expected Value and Sample Mean

The challenge lies in estimating the expected value without knowing the probability distribution of outcomes. This is where the sample mean comes in. By taking the average of samples (e.g., experimental outcomes), we can estimate the expected value.

In reinforcement learning:

  • The value function is the expected return.
  • We can estimate the value of each state by sampling returns for that state and computing the average.

Monte Carlo Approach

Monte Carlo methods involve running simulations (episodes) to collect samples of returns. Here’s how we can apply it to solve the prediction problem:

  1. Play an Episode: This involves navigating through a sequence of states and rewards (denoted as and ).

  2. Calculate Returns:

    • Return at Terminal State: The return is the sum of future rewards. For a terminal state, there are no future rewards, so the return is zero.
    • Recursive Calculation: For each prior state, the return can be calculated using the recursive formula: where is the discount factor.
  3. Pseudo Code Outline:

    • Initialization:
      • Play an episode to collect states and rewards.
      • Initialize an empty list to store returns.
    • Reverse Loop Through Rewards: Calculate the return using the recursive formula and store it in the list of returns.
  4. Final Value Function Estimation:

    • Repeat the above steps for a large number of episodes.
    • For each state, compute the average return from the collected samples.

Pseudocode Example

Here’s a simplified pseudocode for the Monte Carlo prediction algorithm:

# Given a policy π
value_function = {}
sample_returns = {}
 
# Run for many episodes
for episode in range(num_episodes):
    states, rewards = play_episode(policy)
    G = 0  # Initialize return
 
    # Loop through rewards in reverse to calculate returns
    for t in reversed(range(len(rewards))):
        G = rewards[t] + gamma * G
        state = states[t]
 
        # Store the return for the state
        if state not in sample_returns:
            sample_returns[state] = []
        sample_returns[state].append(G)
 
# Calculate the value function as the average of the sample returns
for state, returns in sample_returns.items():
    value_function[state] = sum(returns) / len(returns)
 
return value_function

Challenges and Moving Forward

While this method is powerful, it has limitations:

  • State Coverage: Not all states may be encountered during the episodes, leading to incomplete value functions.
  • Control Problem: This method alone doesn’t solve the control problem. The next step involves using techniques like policy iteration or value iteration to find the best policy.

In summary, Monte Carlo methods allow us to estimate the value function in environments where we can’t fully observe or model the dynamics. This sets the foundation for more advanced reinforcement learning algorithms to address the control problem.

9. Solving the Bellman Equation with Reinforcement Learning (pt 2)

Overview of Prediction and Control in Reinforcement Learning

In reinforcement learning, two major problems often arise: prediction and control.

  1. Prediction Problem (Value Function Estimation):

    • The goal is to estimate the value function , which predicts the expected sum of future rewards starting from state .
    • This helps in understanding how good it is to be in a particular state.
  2. Control Problem (Optimal Policy):

    • The goal is to find the optimal policy by maximizing the expected sum of future rewards.
    • The focus is on estimating the action-value function , which predicts the expected future rewards given a state and action .
    • From , we can determine the best action to take by selecting the action that maximizes for any given state .

The Loop of Policy Iteration and Improvement

Policy iteration is a fundamental approach to solving the control problem. It consists of two main steps:

  1. Policy Evaluation:

    • Given a policy, evaluate its value function (either state-value or action-value ).
    • This involves calculating expected returns for each state (or state-action pair) under the current policy.
  2. Policy Improvement:

    • Given the evaluated value function , improve the policy by choosing the action that maximizes for each state.
    • This produces a new policy that is at least as good as the previous one.

This process repeats in a loop, with each iteration potentially improving the policy until it converges to the optimal policy.

Applying Monte Carlo Methods to the Control Problem

Monte Carlo methods are used to evaluate and improve the policy by sampling returns over episodes of the environment. Here’s a basic outline of how it works:

  1. Initialize:

    • Start with a random action-value function and a random policy.
  2. Monte Carlo Policy Evaluation:

    • Run episodes to sample states, actions, and rewards.
    • For each state-action pair encountered in the episode, update based on the returns observed.
  3. Policy Improvement:

    • Update the policy by choosing actions that maximize for each state .

Efficiency Challenges and Value Iteration

The naive approach to Monte Carlo control involves a nested loop: one loop to improve the policy and another to evaluate it using multiple episodes. This can be inefficient because:

  • Storage: requires storing more values compared to .
  • Sample Requirements: More samples are needed to estimate accurately.
  • Computation: The number of required episodes grows quickly, leading to inefficiency.

Value Iteration Approach

A more efficient method, value iteration, reduces the need for multiple episodes per policy evaluation step:

  1. Single Episode Evaluation:

    • Instead of playing multiple episodes to estimate , play a single episode and update immediately based on the observed returns.
    • This allows for faster convergence.
  2. Sample Mean Optimization:

    • Instead of recalculating the sample mean every time a new sample is collected, use a running average. The update rule for becomes:

Q(s, a) \leftarrow Q(s, a) + \alpha \times \left[ \text{Return} - Q(s, a) \right]

where $\alpha$ is a constant learning rate. This method gives more weight to recent experiences, ensuring that the policy adapts quickly to new information. 3. **Exponential Decay**: - Use an exponentially decaying average for updating $Q(s, a)$. This ensures that older experiences have less influence on the value function, which is especially important when the policy is continually changing. ### Conclusion The combination of policy iteration, Monte Carlo sampling, and value iteration provides a powerful framework for solving reinforcement learning problems. The efficiency improvements in value iteration allow for faster convergence to the optimal policy, making it practical for large-scale problems. Understanding these concepts is crucial for designing and implementing reinforcement learning algorithms that can effectively learn from experience and optimize decision-making processes. ## 10. Epsilon-Greedy ### Understanding the Explore-Exploit Dilemma in Reinforcement Learning In reinforcement learning (RL), the **explore-exploit dilemma** is a fundamental challenge when an agent is learning to make decisions. The dilemma arises because: 1. **Exploration**: The agent needs to gather information about the environment to make better decisions in the future. This means trying out different actions to discover their outcomes. 2. **Exploitation**: The agent should also use the knowledge it has already acquired to make the best possible decisions at the current moment, maximizing immediate rewards. ### Problem Setup Imagine you're playing a simplified version of slot machines, where each machine has a different probability of winning (rewarding you with \$1). The problem is, you don’t know these probabilities in advance. You have two options: 1. **Explore**: Try each slot machine multiple times to gather data and estimate their probabilities. 2. **Exploit**: Play the slot machine that you currently believe has the highest probability of winning, based on the data you've collected so far. ### Why Exploration is Necessary Consider a situation where you have three slot machines, and you initialize their estimated rewards (Q-values) as follows: - Q(s, A1) = 0 - Q(s, A2) = 0 - Q(s, A3) = 1 If you always choose the action (slot machine) with the highest estimated reward (A3), you may never explore A1 or A2. However, A1 or A2 might actually have higher true rewards that you miss out on because you never try them. This is the crux of the explore-exploit dilemma: by always exploiting, you risk never discovering better options. ### Epsilon-Greedy Solution A common strategy to balance exploration and exploitation is the **epsilon-greedy** algorithm: - **Epsilon (ε)**: A small probability (e.g., 0.1) that determines how often you should explore. - **Greedy Action**: With probability $1 - \epsilon$, you exploit by choosing the action with the highest estimated reward. - **Random Action**: With probability $\epsilon$, you explore by choosing a random action, regardless of the estimated rewards. ### Pseudocode for Epsilon-Greedy Here's how you might implement the epsilon-greedy strategy: ```python import random def epsilon_greedy(Q, state, epsilon): if random.uniform(0, 1) < epsilon: # Explore: choose a random action action = random.choice(ACTIONS) else: # Exploit: choose the action with the highest Q-value action = max(Q[state], key=Q[state].get) return action ``` ### Why Balance is Important - **Too Much Exploration**: If $\epsilon$ is too high, you'll spend too much time exploring, which can be costly and inefficient. - **Too Much Exploitation**: If $\epsilon$ is too low, you might miss out on discovering better actions that could yield higher rewards. The key is to strike a balance, gradually decreasing $\epsilon$ over time as the agent becomes more confident in its Q-value estimates. ### Conclusion The explore-exploit dilemma highlights the trade-off between gathering information and making the best possible decision with the information you have. The epsilon-greedy algorithm provides a simple yet effective way to navigate this dilemma, ensuring that your agent continues to learn and improve over time while also maximizing rewards. ## 11. Q-Learning ### **1. Introduction to Reinforcement Learning Concepts** - **Relevant Terms:** - **Agent:** The learner or decision-maker. - **Environment:** The system the agent interacts with. - **State:** A configuration or situation within the environment. - **Action:** The choice the agent makes at each state. - **Reward:** The feedback the agent receives after taking an action. - **Mathematical Structuring:** - Reinforcement learning (RL) problems are modeled as **Markov Decision Processes (MDPs)**. ### **2. Solving MDPs** - **Prediction:** Finding the **value function** (V) for a given policy π. This involves predicting the expected return from any given state under policy π. - **Control:** Finding the **optimal policy** that maximizes the value function. The problem becomes more complex when we don't know the transition probabilities of the environment. ### **3. Monte Carlo Methods** - **Monte Carlo Approach:** - Used when we don’t know the environment's transition probabilities. - It involves **sampling** to estimate the value function by averaging returns over multiple episodes. - **Limitation:** - Requires waiting until the end of an episode to calculate returns, which is impractical for long or non-terminating episodes. ### **4. Temporal Difference (TD) Learning** - **Recursive Return Definition:** - The return $G_t$ at time $t$ can be defined recursively: $$ G_t = R_{t+1} + \gamma G_{t+1} $$ - Here, $\gamma$ is the discount factor. - **TD Method:** - **Estimate Returns:** Instead of waiting until the end of an episode, estimate returns using the value of the next state: $$ V(S_t) \leftarrow V(S_t) + \alpha \left[ R_{t+1} + \gamma V(S_{t+1}) - V(S_t) \right]
- $\alpha$ is the learning rate.
- This allows for updates after each step rather than waiting for the episode to finish.
  • Gradient Descent Analogy:
    • The TD update rule is akin to gradient descent where we minimize the error between our prediction and the observed reward plus estimated future rewards.

5. Q-Learning Algorithm

  • Control via Q-Learning:

    • Q-learning updates a Q-table instead of a value function. The Q-value represents the expected return of taking action in state and following the optimal policy afterward.
  • Epsilon-Greedy Policy:

    • To balance exploration and exploitation, use the epsilon-greedy policy:
      • With probability , choose a random action.
      • With probability , choose the action with the highest Q-value.
  • Q-Value Update:

    • The Q-learning update rule:

Q(S_t, A_t) \leftarrow Q(S_t, A_t) + \alpha \left[ R_{t+1} + \gamma \max_a Q(S_{t+1}, a) - Q(S_t, A_t) \right]

- Here, $\max_a Q(S_{t+1}, a)$ assumes the agent will act optimally in the next state. - **Off-Policy Learning:** - Q-learning is an off-policy method. The Q-values are updated as if the agent is always acting greedily, even if it explores randomly during learning. ### **6. Summary** - **TD Methods:** Offer a solution when Monte Carlo methods are impractical due to long or infinite episodes. - **Q-Learning:** Provides an efficient way to find the optimal policy by iteratively improving the Q-values based on immediate rewards and estimated future rewards. - **Online Learning:** The agent learns as it collects data, updating its policy step by step, making it a powerful approach for real-time applications. ## 12. How to Learn Reinforcement Learning **1. Complexity of Reinforcement Learning (RL):** - **Different from Supervised/Unsupervised Learning:** RL is fundamentally different from other machine learning paradigms like supervised and unsupervised learning. - **Implementation is Crucial:** Understanding RL requires hands-on implementation, unlike some beginner courses in supervised learning, which often focus only on intuition without teaching how to implement the models. This can lead to serious misunderstandings and "blissful ignorance" where one doesn't realize the gaps in their knowledge. **2. The Problem with Shallow Learning Approaches:** - **Insufficient Depth in Blogs and Tutorials:** Short tutorials and blog posts often lack the depth necessary to fully understand and implement RL. - **Course Structure:** Courses should start with the intuition behind the model, then cover its implementation, including loading data and interpreting results, to avoid misunderstandings. **3. Recommended Learning Path:** - **Take Comprehensive Courses:** It’s advised to take multiple in-depth courses on RL, starting with basic tabular methods before moving on to approximation methods. - **Focus on Core RL Methods:** Begin with dynamic programming, Monte Carlo methods, and temporal difference methods. Implementation is key, so make sure to spend time coding these methods. **4. Challenges in Implementation:** - **Difficulty of Implementation:** Even experienced programmers find RL challenging to implement correctly due to the potential for subtle bugs. It's common for experts to spend weeks or even months getting RL algorithms to work properly. - **Expert Challenges:** Even top researchers like [[Andrej Karpathy]] face significant challenges. For example, it took him six weeks to get policy gradients working, despite having access to advanced technology and support from peers. **5. Key Takeaway:** - **Prepare for a Long Journey:** Learning RL is a demanding process that requires perseverance, especially during the implementation phase. Expect to invest significant time and effort, as the learning curve is steep even for experts. **Code Practice** ## 1. Trend-Following Strategy with Reinforcement Learning API ### Lecture Overview In this lecture, we’ll reimplement the trend-following strategy using the reinforcement learning (RL) paradigm. Our goal is to eventually achieve secure learning, but we'll take it step by step. This initial step will introduce key RL components: environment, states, actions, and rewards. ### Motivation Previously, when implementing trend-following, we had to shift returns--a process that likely felt unnatural. RL, however, operates over discrete time steps, allowing us to approach the problem more naturally. We'll eliminate the need for shifting data, making the process feel more like controlling a robot in a trading environment rather than manipulating a data frame. ### Step-by-Step Walkthrough Let's start by considering a data frame with SPY closing prices, along with fast and slow moving averages (MAs) and daily log returns. Assume the returns are not shifted, meaning the return on each row corresponds to that day. #### Example Scenario Suppose we're at time $t$ and we’re not currently invested. Today, the fast MA crosses above the slow MA, indicating a buy signal. We buy at today’s closing price, but the return we get is for tomorrow (time $t+1$). This action brings us to the next day, and the reward for this action is the return at time $t+1$. This is an example of a Markov Decision Process (MDP) transition: we move from state $S_t$ to $S_{t+1}$ and receive a reward $R_{t+1}$. ### Actions, States, and Rewards - **States**: A vector containing the fast and slow MAs. - **Rewards**: The daily log return. - **Actions**: Buy, sell, or do nothing. Previously, we used a column "is_invested" to track our position (invested or not), but this is more of a state than an action. The real actions are buying, selling, or holding. #### Decision Logic - **Do Nothing**: If we’re already invested and the fast MA is greater than the slow MA, or if we’re not invested and the fast MA is less than the slow MA. - **Sell**: If we’re invested and the fast MA is less than the slow MA. The strategy relies on whether the fast MA crosses the slow MA from above or below. This crossover is the key event, and the time in between is either a continuation of the trend or a reversal. ### Implementation Overview We'll implement two central objects: the **Agent** and the **Environment**. #### Environment Inspired by the OpenAI Gym, the environment will manage the data and handle transitions. It will have two main functions: 1. **reset()**: Initializes the environment and returns the initial state. 2. **step(action)**: Executes an action, returns the next state, the reward, and a flag indicating if the episode is done. #### Agent The agent’s role is to decide which action to take based on the current state. It will have a single function: - **act(state)**: Takes the current state as input and returns an action (buy, sell, or do nothing). ### Interaction between Agent and Environment The interaction follows a simple loop: 1. Initialize the environment with `env.reset()`. 2. Set a flag `done` to `false` and initialize `total_reward` to `0`. 3. Loop until `done` is `true`: - Call `agent.act(state)` to decide the action. - Execute the action with `env.step(action)`, receiving the next state, reward, and `done` flag. - Update the `total_reward`. - Optionally, call `agent.train()` to update the agent’s logic (not needed for trend-following). - Update `state` to the new state. ### Pseudocode Summary - **Environment**: - `reset()` sets `current_index` to 0 and initializes the state. - `step(action)` increments `current_index`, updates `is_invested` based on the action, computes the reward, and returns the next state, reward, and done flag. - **Agent**: - `act(state)` decides to buy, sell, or do nothing based on the state and whether we’re currently invested. ### Conclusion This setup provides a clear structure for a reinforcement learning program. You’ve learned how the agent and environment interact and the logic that drives their behavior. In more advanced RL tasks, the environment might exist outside your program, such as in OpenAI Gym or real-world scenarios like robotics. ## 2. Trend-Following Strategy Revisited In this lecture, we will reimplement the trend-following strategy using a reinforcement learning (RL) framework. This time, we'll work with explicit environment and agent objects that interact with each other. You can refer to the title of the notebook to see which specific notebook we are using. Although much of the initial setup will be similar to what we've done before, I'll review everything in case you need a refresher on reinforcement learning. ### Step 1: Downloading Data We'll start by downloading the SPY CSFI dataset. Since we are focusing on trend following, we only need the daily closing prices. ### Step 2: Importing Libraries Next, we'll import the necessary libraries: `pandas`, `numpy`, and `matplotlib`. ### Step 3: Preparing the Data We'll load and preprocess the CSFI data, calculating the fast and slow moving averages (SMAs). We'll create a variable, `FTS`, to store the column names of these moving averages. We will also calculate the log returns by taking the logarithm of the close prices and then computing the differences. ### Step 4: Splitting Data We'll split our dataset into training and testing sets, reserving the last 1,000 data points for testing. ### Step 5: Implementing the Environment Class Now, we’ll create the `Environment` class. - **Constructor:** The constructor takes a single argument, which is a DataFrame (either the train or test data). Inside, we store this DataFrame in an instance variable, `df`, and set `N` to the length of `df`, which helps us determine when we reach the end of the data. We also initialize `current_index` to 0, which tracks our position in the DataFrame. The action space is defined as integers `0`, `1`, and `2`, representing buy, sell, and hold actions, respectively. We set an `invested` flag to `0` or `1` to indicate whether we are holding a position. The `states` instance variable will hold the columns specified by the feature list, converted to a NumPy array for convenient indexing. We also store the log returns and initialize the `total_buy_and_hold` variable to accumulate returns throughout the episode. - **Reset Function:** This function resets `current_index` to 0, `total_buy_and_hold` to 0, and the `invested` flag to 0. It returns the initial state, which corresponds to the data at `index 0`. - **Step Function:** The `step` function takes one argument, `action`, and returns the next state, reward, and a `done` flag. We increment `current_index`, check if it exceeds `N` (raising an exception if it does), and then update the `invested` flag based on the action. The reward is computed based on whether the agent is invested. We then retrieve the next state and update the `total_buy_and_hold` counter. The `done` flag is set to `True` if `current_index` is equal to `N-1`. Finally, we return the next state, reward, and `done` flag. ### Step 6: Implementing the Agent Class Next, we define the `Agent` class. - **Constructor:** The constructor initializes an `is_invested` flag to `False`. - **Act Function:** The `act` function takes in the `state` and determines the appropriate action. If the fast SMA is greater than the slow SMA and the agent is not invested, the agent buys. If the fast SMA is less than the slow SMA and the agent is invested, the agent sells. Otherwise, the agent holds. We return `0` for buy, `1` for sell, and `2` for hold. - **Play Episode Function:** This function runs an episode, taking in an `agent` and an `env` (environment) as arguments. We start by resetting the environment and initializing the `done` flag to `False` and `total_reward` to 0. Inside a loop, which continues until `done` is `True`, the agent takes an action based on the current state, and the environment returns the next state, reward, and `done` flag. We accumulate the reward and update the state. Finally, we return the total reward. ### Step 7: Running the Strategy Finally, we’ll put everything together: - We create two environments: one for training and one for testing. - We instantiate the agent. - We call `play_one_episode` with the agent on the training environment, and then on the testing environment. - We print out the training reward and the buy-and-hold total for the training set, which should be around `0.43` and `0.597`, matching previous results. - We also print out the test reward and buy-and-hold total for the test set, which should be around `0.08889` and `0.193`, again matching previous outcomes. ### Conclusion This lecture aimed to solidify your understanding of a few key concepts. First, it should have clarified our earlier code by demonstrating a more realistic implementation of an agent and environment. Second, you learned about important reinforcement learning components, including the environment, agent, states, actions, and rewards. With this foundation, transitioning to learning scripts will feel like a natural extension of what we’ve covered so far. ## 3. Q-Learning in an Algorithmic Trading Context **Lecture Overview:** In this lecture, we'll dive into the basics of Q-learning within a financial engineering context. Think of this session as a quick guide to reinforce your learning. If spending hours on pure reinforcement learning theory isn't appealing to you, this lecture can offer a fresh perspective. However, it's still recommended to delve into the theory for a deeper understanding. **Key Concept: The Q-Table** Unlike our previous discussions on trend-following algorithms, in this lecture, our agent will actively learn and use its experience to improve future outcomes. The core concept we’ll introduce is the Q-Table. Imagine a table where the rows represent states and the columns represent actions. Each entry in this table, Q(s, a), represents the expected return (sum of future rewards) for taking action 'a' in state 's'. This Q-Table is a crucial tool for our agent to decide which action to take at any given state. The goal is to find the action that maximizes future rewards by selecting the action with the highest Q-value. **Understanding States and Actions:** Actions are straightforward--they can be represented as integers like 0, 1, or 2, corresponding to different columns in the table. States, however, are more complex and aren’t naturally represented as integers. We'll cover how to encode states in the next lecture, but for now, we'll assume that we can organize them in a table format. **The Meaning of Q-Values:** The Q-value, Q(s, a), represents the expected sum of future rewards when taking action 'a' in state 's'. This "return" should not be confused with financial returns; here, it refers to the total rewards accumulated over time. Given this definition, choosing the best action becomes simple: select the action that maximizes Q(s, a). While this might seem circular, if the Q-table is accurate, it will guide the agent to make optimal decisions by comparing the Q-values across all possible actions. **Updating the Q-Table:** To ensure the Q-table contains accurate values, we use a process called Q-learning. Q-learning is essentially a method of updating the Q-table using an exponentially weighted moving average. This method reflects how learning from experience works. The idea is to update Q(s, a) based on the reward received after taking action 'a' in state 's', plus the expected future rewards (estimated by the maximum Q-value in the next state). This update process is similar to calculating a sample mean, where we adjust our estimate with new information over time. Mathematically, this update process is akin to gradient descent, a concept familiar to those with a background in machine learning. **Implementing Q-Learning:** Our agent needs two key functions: `act` and `train`. - **Act Function:** The `act` function determines which action to take. Instead of always choosing the best-known action, the agent occasionally selects a random action to explore new possibilities. This balance between exploiting known information and exploring new options is managed by a parameter called epsilon (ε). - **Train Function:** The `train` function updates the Q-table based on the agent's experiences. It takes as input the state, action, reward, next state, and a flag indicating if the next state is terminal. If the next state is terminal, the Q-value update is simple: the expected future reward is zero. Otherwise, the Q-value is updated based on the reward received and the maximum expected future reward from the next state. **Conclusion:** This lecture provided a concise introduction to Q-learning, integrating concepts we've previously covered. We've learned how to use the Q-table to guide our agent's actions and how to update the table to improve decision-making over time. These concepts are central to the `act` and `train` functions of our agent class, laying the foundation for more advanced learning techniques. ## 4. Representing States ### Lecture Overview In this lecture, we'll focus on a key topic that was intentionally left out of the previous lecture: **How do we represent states?** ### Recap and Introduction Before diving in, let's clarify the type of data we'll use. Remember, one of the main themes of this course is that your data can be anything. It's up to you to select something useful. For simplicity and comparison, we'll stick with the same data used in our machine learning scripts: **the previous day's returns for various stocks in the S&P 500.** ### The Challenge: Continuous vs. Discrete States However, there's a challenge. Returns are continuous variables, meaning there are infinitely many possible values. For our approach to be manageable, we need to convert these continuous values into discrete states. But how do we do this? ### Solution: Binning One simple solution is **binning**. Here's how it works: 1. **Define Bins:** Suppose we have three bins: - Bin 1: Values from $-\infty$ to 0 - Bin 2: Values from 0 to 1 - Bin 3: Values from 1 to $+\infty$ 2. **Assign Values to Bins:** This process transforms an infinite number of possible returns into a finite number of bins. #### Defining Useful Bins The next step is to define your bins effectively. The scheme mentioned above is overly simplistic since most returns are close to zero. Instead, consider the following: - **Maximum and Minimum Values:** Split the range between these two into, say, 10 equal parts. - **Return Distributions:** Keep in mind that returns follow a distribution, with some values being more likely (usually near zero) and others less likely. You don’t want a single bin handling rare extreme returns, nor do you want 90% of your returns in one bin. **Goal:** Define bins according to frequency. High-frequency areas should have smaller bins, and low-frequency areas should have larger bins, with the outermost bins extending to infinity. ### Implementation: Using NumPy’s `digitize` Now, let's talk about implementation. #### Binning with NumPy If we know the bin boundaries and have a value, we can determine which bin it belongs to using NumPy's `digitize` function. Here’s an example: - **Bins:** Defined as `[0, 1, 2.5, 4, 10]` - **Values (`X`):** `[0.2, 6.4, 2.9, 1.1]` - **Bin Assignments:** `[1, 4, 3, 2]` **Explanation:** - `0.2` falls between 0 and 1, so it goes into bin 1. - `6.4` falls between 4 and 10, so it goes into bin 4. #### Handling Out-of-Bounds Values What happens if a value falls outside the defined bin boundaries? For example: - **Value `-1`:** Below the smallest boundary, assigned to bin 0. - **Value `11`:** Above the largest boundary, assigned to bin 5. This makes sense because the complete set of bins includes 0, 1, 2, 3, 4, and 5. ### Finding Bin Boundaries Next, let's discuss how to determine the boundaries: 1. **Sort the Returns:** Start by sorting the returns from smallest to largest. 2. **Determine Equal Spacing:** If you have 100 elements and want 10 bins, identify equally spaced elements within the sorted array. **Important Detail:** The boundaries should be centered to handle values extending to infinity, not just aligned with the smallest and largest values. For instance, boundaries could be 5, 15, 25, etc., up to 95, ensuring that extreme values also fit within these bins. ### Representing the Q-Table Since we’re dealing with returns from multiple stocks, our state will be a tuple of integers rather than a single integer. Python’s dictionary is perfect for this, as it allows immutable objects like tuples to be used as keys. - **State Representation:** Tuple of multiple integers. - **Action Representation:** A single integer. Thus, our "table" will be an array indexed by these objects, essentially functioning as a dictionary. ### Conclusion and Advice This was a brief overview of how we'll represent states in the upcoming code. It's a complex topic, so don’t worry if it doesn't all make sense right away. I encourage you to revisit this lecture as needed, and feel free to develop your own solutions if my approach doesn't resonate with you. My goal is to teach the concepts and provide an example implementation. It's not necessarily the fastest or most "Pythonic" way, but it's designed to be clear and understandable. Always aim to implement things in a way that makes the most sense to you. ## 5. Q-Learning for Algorithmic Trading In this lecture, we'll finally implement our learning trader. The script will use the same data as our machine learning script, so we'll need to download the full S&P dataset. #### Setup First, we'll import the necessary libraries: Pandas, NumPy, Matplotlib, and some additional tools. You'll see how these tools are used shortly. Next, we'll load our DataFrame using `pd.read_csv`. Since you've already seen how we process this data, we'll go through it quickly: 1. **Drop Rows with Missing Values**: We start by dropping any rows that have all missing values, as these rows are irrelevant. 2. **Drop Columns with Missing Values**: Next, we drop any columns with at least one missing value, to avoid the need for forward or backfilling. 3. **Generate Log Returns**: We create a new DataFrame containing the log returns for each S&P component. 4. **Split Data**: We split the data into training and testing sets using the returns DataFrame. 5. **Select Input Features**: We specify that our input features will be Apple, Microsoft, and Amazon. It's important to note that because we're using binning, the state space will grow exponentially. For instance, if each column has 10 bins and we have three columns, the total number of unique bins is $10 \times 10 \times 10 = 1000$. With more features, the state space grows exponentially, leading to increased time and space requirements. #### The `Environment` Class This class is mostly the same as before, so we'll go through it quickly. The main difference is that now, the rewards are derived from the SPY column of the DataFrame. The features are also different but are specified by a variable called `fts`. #### The `StateMapper` Class This is likely the most complex part of the script and relies heavily on the theory covered earlier. If you're not quite getting it, I recommend revisiting the theory lecture. - **Purpose**: The `StateMapper` object transforms a continuous vector of states into the correct bins. - **Constructor**: The constructor defines the bin boundaries. It takes an object and a `bins` parameter, which specifies the number of bin boundaries and the number of samples. - **Step 1**: Create an empty list called `states` to store the states we collect. - **Step 2**: Set `done` to `False` and initialize the environment. - **Step 3**: Set an instance variable `D` to the length of the state vector, which is the number of sets of bins we need to create. - **Step 4**: Append the initial state to our list of states. - **Step 5**: Enter a loop that iterates `n_samples` times. Inside the loop: - Choose a random action from the environment's action space. - Call `env.step()` with the random action, which gives us the next state. Append this state to our list. - Check if we're done; if so, reset the environment and start again. - **Step 6**: After collecting the states, cast them to a NumPy array for easy indexing. - **Step 7**: Create the bin boundaries for each column of data. Start by initializing an empty list `self.bins`. - **Step 8**: Loop through each dimension: - For each column, initialize an empty list `current_bin` to store the boundaries. - Loop `bins` times to calculate the boundaries using the formula: $$ \text{Boundary} = \frac{n\_samples}{n\_bins} \times (k + 0.5) $$ This ensures centered bin boundaries. Plugging in example numbers can help clarify the logic. - **Transform Function**: This function converts a continuous state vector into the corresponding bin tuple: - **Step 1**: Create an array of zeros called `x`, where we'll store the bin values. - **Step 2**: Loop through each dimension, using `np.digitize` to determine the bin for each state value. - **Step 3**: Cast `x` to a tuple and return it. - **All Possible States Function**: This function is essential for initializing the Q-table. Since Q-values are updated iteratively, each state-action pair must have an initial value. The function uses `itertools.product` to generate all possible combinations of bins for each dimension. #### The `Agent` Class This class is more complex than our trend-following agent, as it incorporates learning. - **Constructor**: Takes in the `action_size` and a `StateMapper` object. - **Instance Variables**: These include `action_size`, `gamma`, `epsilon`, `learning_rate`, and `state_mapper`. - **Q-table Initialization**: We initialize `Q` as an empty dictionary and loop through all possible states and actions, assigning each state-action pair a random initial value drawn from the standard normal distribution. - **Action Selection Function**: This function implements an epsilon-greedy strategy: - Generate a random number. If it's less than `epsilon`, return a random action. - Otherwise, transform the state into its bin representation, look up the Q-values for all possible actions, and return the action with the highest Q-value. - **Train Function**: Implements the Q-learning formula: - Transform the current and next states into their bin representations. - If the next state is terminal, the target is equal to the reward. Otherwise, calculate the target as the reward plus the discounted maximum Q-value for the next state. - Update the Q-value for the current state-action pair using the learning formula. - **Play One Episode Function**: Similar to the earlier implementation, but with an additional `is_train` argument. If `is_train` is `True`, the agent will learn during this episode. #### Running the Model Finally, we put everything together: 1. Set the number of episodes to 500. 2. Create two environments: one for training and one for testing. 3. Set the action size, instantiate the `StateMapper` and `Agent`. 4. Create empty arrays to store the total rewards for each episode in both training and testing. 5. Loop through the episodes: - Train the agent on the training environment. - Test the agent on the test environment, setting `epsilon` to 0 to prevent exploration during testing. 6. Print the episode number and the rewards for both training and test. #### Plotting the Results Finally, plot the rewards for both training and test sets. You'll observe that while the model performs better on the training set (as expected), it generally outperforms a buy-and-hold strategy on the test set as well. Also, as the agent learns, the variability in test rewards decreases, indicating more consistent and intelligent performance rather than random luck.