{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# DQN in pytorch\n", "\n", "The goal of this exercise is to implement DQN using pytorch and to apply it to the cartpole balancing problem. \n", "\n", "The code is adapted from the Pytorch tutorial: ." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "try:\n", " import google.colab\n", " IN_COLAB = True\n", "except:\n", " IN_COLAB = False\n", "\n", "if IN_COLAB:\n", " !pip install -U gymnasium pygame swig\n", " !pip install -U moviepy==1.0.3\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "gym version: 1.0.0\n", "Device: mps\n" ] } ], "source": [ "# Default libraries\n", "import math\n", "import random\n", "import os\n", "import time\n", "import numpy as np\n", "rng = np.random.default_rng()\n", "import matplotlib.pyplot as plt\n", "from collections import namedtuple, deque\n", "\n", "# Gymnasium\n", "import gymnasium as gym\n", "print(\"gym version:\", gym.__version__)\n", "\n", "# pytorch\n", "import torch\n", "import torch.nn.functional as F\n", "\n", "# Select hardware: \n", "if torch.cuda.is_available(): # GPU\n", " device = torch.device(\"cuda\")\n", "elif torch.backends.mps.is_available(): # Metal (Macos)\n", " device = torch.device(\"mps\")\n", "else: # CPU\n", " device = torch.device(\"cpu\")\n", "print(f\"Device: {device}\")" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "from moviepy.editor import ImageSequenceClip, ipython_display\n", "\n", "class GymRecorder(object):\n", " \"\"\"\n", " Simple wrapper over moviepy to generate a .gif with the frames of a gym environment.\n", " \n", " The environment must have the render_mode `rgb_array_list`.\n", " \"\"\"\n", " def __init__(self, env):\n", " self.env = env\n", " self._frames = []\n", "\n", " def record(self, frames):\n", " \"To be called at the end of an episode.\"\n", " for frame in frames:\n", " self._frames.append(np.array(frame))\n", "\n", " def make_video(self, filename):\n", " \"Generates the gif video.\"\n", " directory = os.path.dirname(os.path.abspath(filename))\n", " if not os.path.exists(directory):\n", " os.mkdir(directory)\n", " self.clip = ImageSequenceClip(list(self._frames), fps=self.env.metadata[\"render_fps\"])\n", " self.clip.write_gif(filename, fps=self.env.metadata[\"render_fps\"], loop=0)\n", " del self._frames\n", " self._frames = []" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Cartpole balancing task\n", "\n", "We are going to use the Cartpole balancing problem, which can be loaded with:\n", "\n", "```python\n", "gym.make('CartPole-v0', render_mode=\"rgb_array_list\")\n", "```\n", "\n", "States have 4 continuous values (position and speed of the cart, angle and speed of the pole) and 2 discrete outputs (going left or right). The reward is +1 for each transition where the pole is still standing (angle of less than 30° with the vertical). \n", "\n", "In CartPole-v0, the episode ends when the pole fails or after 200 steps. In CartPole-v1, the maximum episode length is 500 steps, which is too long for us, so we stick to v0 here.\n", "\n", "The maximal (undiscounted) return is therefore 200. Can DQN learn this?" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Return: 40.0\n" ] } ], "source": [ "# Create the environment\n", "env = gym.make('CartPole-v0', render_mode=\"rgb_array_list\")\n", "recorder = GymRecorder(env)\n", "\n", "# Sample the initial state\n", "state, info = env.reset()\n", "\n", "# One episode:\n", "done = False\n", "return_episode = 0\n", "while not done:\n", "\n", " # Select an action randomly\n", " action = env.action_space.sample()\n", " \n", " # Sample a single transition\n", " next_state, reward, terminal, truncated, info = env.step(action)\n", "\n", " # End of the episode\n", " done = terminal or truncated\n", "\n", " # Update undiscounted return\n", " return_episode += reward\n", " \n", " # Go in the next state\n", " state = next_state\n", "\n", "print(\"Return:\", return_episode)\n", "recorder.record(env.render())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "video = \"videos/cartpole_random.gif\"\n", "recorder.make_video(video)\n", "ipython_display(video)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Value network in pytorch\n", "\n", "As the state in Cartpole has only four dimensions, we do not need a CNN for the value network. A simple MLP with a couple of hidden layers will be enough.\n", "\n", "**Q:** Create a MLP class in pytorch taking four inputs and two outputs (one Q-value per action), and two hidden layers of 128 neurons (you can change it later). If possible, make it parameterizable, i.e. have the constructor take in the number of inputs, outputs and hidden neurons. The activation function for the hidden layers should be ReLU." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "class MLP(torch.nn.Module):\n", " \"Value network for DQN on Cartpole.\"\n", "\n", " def __init__(self, nb_observations, nb_hidden1, nb_hidden2, nb_actions):\n", " super(MLP, self).__init__()\n", " \n", " # Layers\n", " self.fc1 = torch.nn.Linear(nb_observations, nb_hidden1)\n", " self.fc2 = torch.nn.Linear(nb_hidden1, nb_hidden2)\n", " self.fc3 = torch.nn.Linear(nb_hidden2, nb_actions)\n", "\n", " def forward(self, x):\n", "\n", " x = F.relu(self.fc1(x))\n", " x = F.relu(self.fc2(x))\n", " x = self.fc3(x)\n", " \n", " return x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q:** Create a network, an environment, get the initial state using `env.reset()` and pass it to the `forward()` method of your NN. What happens?\n", "\n", "Do not forget to send the network to your device, especially if you have a GPU. Create the network using something like:\n", "\n", "```python\n", "net = MLP(...).to(device)\n", "```" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[-0.00195839 -0.04332381 0.0268923 -0.02864526]\n" ] }, { "ename": "TypeError", "evalue": "linear(): argument 'input' (position 1) must be Tensor, not numpy.ndarray", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[8], line 12\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28mprint\u001b[39m(state)\n\u001b[1;32m 11\u001b[0m \u001b[38;5;66;03m# Predict the Q-values from the initial state\u001b[39;00m\n\u001b[0;32m---> 12\u001b[0m Q_values \u001b[38;5;241m=\u001b[39m \u001b[43mnet\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mforward\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstate\u001b[49m\u001b[43m)\u001b[49m\n", "Cell \u001b[0;32mIn[7], line 14\u001b[0m, in \u001b[0;36mMLP.forward\u001b[0;34m(self, x)\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mforward\u001b[39m(\u001b[38;5;28mself\u001b[39m, x):\n\u001b[0;32m---> 14\u001b[0m x \u001b[38;5;241m=\u001b[39m F\u001b[38;5;241m.\u001b[39mrelu(\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfc1\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 15\u001b[0m x \u001b[38;5;241m=\u001b[39m F\u001b[38;5;241m.\u001b[39mrelu(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfc2(x))\n\u001b[1;32m 16\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfc3(x)\n", "File \u001b[0;32m~/Teaching/DeepReinforcementLearning/.venv/lib/python3.12/site-packages/torch/nn/modules/module.py:1736\u001b[0m, in \u001b[0;36mModule._wrapped_call_impl\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1734\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_compiled_call_impl(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs) \u001b[38;5;66;03m# type: ignore[misc]\u001b[39;00m\n\u001b[1;32m 1735\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m-> 1736\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_call_impl\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", "File \u001b[0;32m~/Teaching/DeepReinforcementLearning/.venv/lib/python3.12/site-packages/torch/nn/modules/module.py:1747\u001b[0m, in \u001b[0;36mModule._call_impl\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1742\u001b[0m \u001b[38;5;66;03m# If we don't have any hooks, we want to skip the rest of the logic in\u001b[39;00m\n\u001b[1;32m 1743\u001b[0m \u001b[38;5;66;03m# this function, and just call forward.\u001b[39;00m\n\u001b[1;32m 1744\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_backward_hooks \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_backward_pre_hooks \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_forward_hooks \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_forward_pre_hooks\n\u001b[1;32m 1745\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m _global_backward_pre_hooks \u001b[38;5;129;01mor\u001b[39;00m _global_backward_hooks\n\u001b[1;32m 1746\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m _global_forward_hooks \u001b[38;5;129;01mor\u001b[39;00m _global_forward_pre_hooks):\n\u001b[0;32m-> 1747\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mforward_call\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1749\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 1750\u001b[0m called_always_called_hooks \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mset\u001b[39m()\n", "File \u001b[0;32m~/Teaching/DeepReinforcementLearning/.venv/lib/python3.12/site-packages/torch/nn/modules/linear.py:125\u001b[0m, in \u001b[0;36mLinear.forward\u001b[0;34m(self, input)\u001b[0m\n\u001b[1;32m 124\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mforward\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;28minput\u001b[39m: Tensor) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Tensor:\n\u001b[0;32m--> 125\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mF\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlinear\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mweight\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbias\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: linear(): argument 'input' (position 1) must be Tensor, not numpy.ndarray" ] } ], "source": [ "# Create the environment\n", "env = gym.make('CartPole-v0')\n", "\n", "# Create the value network\n", "net = MLP(env.observation_space.shape[0], 128, 128, env.action_space.n).to(device)\n", "\n", "# Sample the initial state\n", "state, info = env.reset()\n", "print(state)\n", "\n", "# Predict the Q-values from the initial state\n", "Q_values = net.forward(state)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alright, we need to cast the state vector into a pytorch tensor, pytorch does not do it automatically.\n", "\n", "To cast a numpy vector of shape (4,) into a tensor, one simply needs to call:\n", "\n", "```python\n", "state = torch.tensor(state, dtype=torch.float32, device=device)\n", "```\n", "\n", "The dtype must be set to `torch.float32` for floating numbers. Integers should be set to `torch.long`. Do not forget to send the tensor to your device if you plan to pass it to your network.\n", "\n", "**Q:** Pass the new tensor to your network. What is the shape of the output tensor?" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[-0.00917135 0.04095928 -0.0089457 -0.01949579] (4,)\n", "tensor([-0.0092, 0.0410, -0.0089, -0.0195], device='mps:0') torch.Size([4])\n", "tensor([ 0.0196, -0.0394], device='mps:0', grad_fn=) torch.Size([2])\n" ] } ], "source": [ "# Create the environment\n", "env = gym.make('CartPole-v0')\n", "\n", "# Create the value network\n", "net = MLP(env.observation_space.shape[0], 128, 128, env.action_space.n).to(device)\n", "\n", "# Sample the initial state\n", "state, info = env.reset()\n", "print(state, state.shape)\n", "\n", "# Cast the state to a tensor\n", "state = torch.tensor(state, dtype=torch.float32, device=device)\n", "print(state, state.shape)\n", "\n", "# Predict the Q-values from the initial state\n", "Q_values = net.forward(state)\n", "print(Q_values, Q_values.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The value network outputs one Q-value per action, great. Now, let's identify the **greedy** action, i.e. the one with the highest Q-value. The two actions expected by the cartpole environment are 0 and 1, i.e. the index of the element with the highest Q-value as a Python integer. \n", "\n", "Have a look at those two methods of `Tensor`:\n", "\n", "* ``Tensor.argmax``: \n", "* ``Tensor.item``: \n", "\n", "**Q:** Find a way to obtain the index (as a Python integer) of the element with the highest value in the tensor of Q-values. Check that it works. " ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Greedy action: 0\n" ] } ], "source": [ "greedy_action = Q_values.argmax().item()\n", "print(f\"Greedy action: {greedy_action}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q:** Create a dummy agent class (as in the previous exercises) storing a value network and acting using $\\epsilon$-greedy action selection. Add a ``test()``method running a few episodes and possibly recording them. \n", "\n", "The constructor should accept several hyperparameters, such as the `config` dictionary in the following skeleton:\n", "\n", "```python\n", "class RandomDQNAgent:\n", " def __init__(self, env, config):\n", " def act(self, state):\n", " def test(self, nb_episodes, recorder=None):\n", "```\n", "\n", "but feel free to pass the hyperparameters one by one.\n", "\n", "To prepare ourselves, implement a schedule for `epsilon` in the `act()` method: epsilon should start at a high value of 0.9 and decrease exponentially to 0.05 for each action made. The value of epsilon follows this formula:\n", "\n", "$$\n", " \\epsilon = 0.05 + (0.9 - 0.05) * \\exp ( - \\dfrac{t}{1000})\n", "$$\n", "\n", "where t is the number of steps since the start. 0.05, 0.9 and 1000 should be parameters of the class." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "class RandomDQNAgent:\n", " \"\"\"\n", " Random deep Q-learning agent.\n", " \"\"\"\n", " \n", " def __init__(self, env, config):\n", "\n", " self.env = env\n", " self.config = config\n", " self.epsilon = self.config['eps_start']\n", "\n", " # Number of actions\n", " self.n_actions = self.env.action_space.n\n", "\n", " # Number of states\n", " self.state, info = self.env.reset()\n", " self.n_observations = len(self.state)\n", "\n", " # Value network\n", " self.value_net = MLP(self.n_observations, config['nb_hidden'], config['nb_hidden'], self.n_actions).to(device)\n", "\n", " self.steps_done = 0\n", " \n", " \n", " def act(self, state):\n", " \"Returns an action using epsilon-greedy action selection.\"\n", "\n", " # Decay epsilon exponentially\n", " self.epsilon = self.config['eps_end'] + (self.config['eps_start'] - self.config['eps_end']) * math.exp(-1. * self.steps_done / self.config['eps_decay'])\n", "\n", " # Keep track of time\n", " self.steps_done += 1\n", " \n", " # epsilon-greedy action selection\n", " if rng.random() < self.epsilon:\n", " return self.env.action_space.sample()\n", " else:\n", " with torch.no_grad():\n", " return self.value_net(state).argmax().item()\n", "\n", " \n", " def test(self, nb_episodes, recorder=None):\n", " \"Performs a test episode without exploration.\"\n", " previous_epsilon = self.epsilon\n", " self.epsilon = 0.0\n", "\n", " for episode in range(nb_episodes):\n", " \n", " # Reset\n", " state, _ = self.env.reset()\n", " state = torch.tensor(state, dtype=torch.float32, device=device)\n", "\n", " # Sample the episode\n", " done = False\n", " return_episode = 0\n", " while not done: \n", " action = self.act(state)\n", " next_state, reward, terminal, truncated, info = self.env.step(action)\n", " return_episode += reward\n", " done = terminal or truncated\n", " state = torch.tensor(next_state, dtype=torch.float32, device=device)\n", "\n", " print(f\"Episode {episode}: return {return_episode}, epsilon: {self.epsilon:.4f}\")\n", " \n", " self.epsilon = previous_epsilon\n", " \n", " if recorder is not None:\n", " recorder.record(self.env.render())\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Episode 0: return 14.0, epsilon: 0.8890\n", "Episode 1: return 12.0, epsilon: 0.8790\n", "Episode 2: return 19.0, epsilon: 0.8634\n", "Episode 3: return 9.0, epsilon: 0.8561\n", "Episode 4: return 42.0, epsilon: 0.8230\n", "Episode 5: return 31.0, epsilon: 0.7994\n", "Episode 6: return 11.0, epsilon: 0.7912\n", "Episode 7: return 46.0, epsilon: 0.7579\n", "Episode 8: return 9.0, epsilon: 0.7515\n", "Episode 9: return 28.0, epsilon: 0.7321\n" ] } ], "source": [ "# Create the environment\n", "env = gym.make('CartPole-v0', render_mode=\"rgb_array_list\")\n", "recorder = GymRecorder(env)\n", "\n", "# Hyperparameters\n", "config = {}\n", "config['nb_hidden'] = 128 # number of hidden neurons in each layer\n", "config['eps_start'] = 0.9 # starting value of epsilon\n", "config['eps_end'] = 0.05 # final value of epsilon\n", "config['eps_decay'] = 1000 # rate of exponential decay of epsilon, higher means a slower decay\n", "\n", "# Create the agent\n", "agent = RandomDQNAgent(env, config)\n", "\n", "# Make 10 evaluation episodes\n", "agent.test(10, recorder)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "video = \"videos/cartpole-random2.gif\"\n", "recorder.make_video(video)\n", "ipython_display(video)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Target network\n", "\n", "The original DQN algorithm implies two neural networks:\n", "\n", "1. The value network $Q_\\theta(s, a)$, learning to predict the Q-values for the current state.\n", "2. The target network $Q_{\\theta'}(s, a)$, used to predict the Q-values in the next state.\n", "\n", "The target network is a copy of the value network (in terms of structure and parameters), but the update occurs only from time to time.\n", "\n", "**Q:** Create two MLPs of the same size and predict the Q-values of a single state. What happens?" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([ 0.0960, -0.0482], device='mps:0', grad_fn=)\n", "tensor([0.0574, 0.0539], device='mps:0', grad_fn=)\n" ] } ], "source": [ "# Create the environment\n", "env = gym.make('CartPole-v0')\n", "\n", "# Create the value network\n", "value_net = MLP(env.observation_space.shape[0], 128, 128, env.action_space.n).to(device)\n", "\n", "# Create the target network\n", "target_net = MLP(env.observation_space.shape[0], 128, 128, env.action_space.n).to(device)\n", "\n", "# Sample the initial state\n", "state, _ = env.reset()\n", "\n", "# Cast the state to a tensor\n", "state = torch.tensor(state, dtype=torch.float32, device=device)\n", "\n", "# Predict the Q-values for both networks\n", "Q_value = value_net.forward(state)\n", "Q_target = target_net.forward(state)\n", "\n", "print(Q_value)\n", "print(Q_target)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Obviously, the two MLPs are initialized using random parameters, so they are different. We need a method to copy the weights of a network into another one. \n", "\n", "It is fortunately very easy to save/load the parameters of a pytorch network:\n", "\n", "```python\n", "params = net.state_dict()\n", "net.load_state_dict(params)\n", "```\n", "\n", "**Q:** Apply these methods to update the weights of the target network with the value one. Check that they now predict the same thing." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([ 0.0139, -0.0111], device='mps:0', grad_fn=)\n", "tensor([ 0.0139, -0.0111], device='mps:0', grad_fn=)\n" ] } ], "source": [ "# Create the environment\n", "env = gym.make('CartPole-v0')\n", "\n", "# Create the value network\n", "value_net = MLP(env.observation_space.shape[0], 128, 128, env.action_space.n).to(device)\n", "\n", "# Create the target network\n", "target_net = MLP(env.observation_space.shape[0], 128, 128, env.action_space.n).to(device)\n", "\n", "# Update the target network\n", "target_net.load_state_dict(value_net.state_dict())\n", "\n", "# Sample the initial state\n", "state, _ = env.reset()\n", "\n", "# Cast the state to a tensor\n", "state = torch.tensor(state, dtype=torch.float32, device=device)\n", "\n", "# Predict the Q-values for both networks\n", "Q_value = value_net.forward(state)\n", "Q_target = target_net.forward(state)\n", "\n", "print(Q_value)\n", "print(Q_target)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Experience Replay Memory\n", "\n", "The second important component of DQN is the experience replay memory (ERM) or replay buffer. It is a limited size buffer that can store $(s, a, r, s', d)$ transitions, where $d$ is a boolean indicating whether the next state $s'$ is terminal or not (in gymnasium, this is the boolean `done = terminal or truncated`).\n", "\n", "Below is a simple implementation of an ERM. The important data structure here is `deque` (double-ended queue) which behaves like a list when `append()` is called, until its capacity is reached (`maxlen`), in which case new elements overwrite older ones. \n", "\n", "`batch = sample(batch_size)` randomly samples a minibatch from the ERM and returns a structure of $(s, a, r, s', d)$ transitions, nicely casted into pytorch tensors. These tensors are accessed with `batch.state`, `batch.action`, etc." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "# Named tuples are fancy dictionaries\n", "Transition = namedtuple('Transition', ('state', 'action', 'reward', 'next_state', 'done'))\n", "\n", "class ReplayMemory(object):\n", " \"Simple Experience Replay Memory using uniform sampling.\"\n", "\n", " def __init__(self, capacity):\n", " self.memory = deque([], maxlen=capacity)\n", "\n", " def append(self, state, action, reward, next_state, done):\n", " \"Appends a transition (s, a, r, s', done) to the buffer.\"\n", "\n", " # Get numpy arrays even if it is a torch tensor\n", " if isinstance(state, (torch.Tensor,)): state = state.numpy(force=True)\n", " if isinstance(next_state, (torch.Tensor,)): next_state = next_state.numpy(force=True)\n", " \n", " # Append to the buffer\n", " self.memory.append(Transition(state, action, reward, next_state, done))\n", "\n", " def sample(self, batch_size):\n", " \"Returns a minibatch of (s, a, r, s', done)\"\n", "\n", " # Sample the batch\n", " transitions = random.sample(self.memory, batch_size)\n", " \n", " # Transpose the batch.\n", " batch = Transition(*zip(*transitions))\n", " \n", " # Cast to tensors\n", " states = torch.tensor(batch.state, dtype=torch.float32, device=device)\n", " actions = torch.tensor(batch.action, dtype=torch.long, device=device)\n", " rewards = torch.tensor(batch.reward, dtype=torch.float32, device=device)\n", " next_states = torch.tensor(batch.next_state, dtype=torch.float32, device=device)\n", " dones = torch.tensor(batch.done, dtype=torch.bool, device=device)\n", "\n", " return Transition(states, actions, rewards, next_states, dones)\n", "\n", " def __len__(self):\n", " return len(self.memory)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q:** Modify your random DQN agent so that it stores a replay buffer of capacity 10000 and appends all transitions into it. Do a few episodes, sample a small minibatch and have a look at the data you obtain." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "class RandomDQNAgent:\n", " \"\"\"\n", " Random deep Q-learning agent with memory.\n", " \"\"\"\n", " \n", " def __init__(self, env, config):\n", "\n", " self.env = env\n", " self.config = config\n", " self.epsilon = self.config['eps_start']\n", "\n", " # Number of actions\n", " self.n_actions = self.env.action_space.n\n", "\n", " # Number of states\n", " self.state, info = self.env.reset()\n", " self.n_observations = len(self.state)\n", "\n", " # Value network\n", " self.value_net = MLP(self.n_observations, config['nb_hidden'], config['nb_hidden'], self.n_actions).to(device)\n", "\n", " # Replay buffer\n", " self.memory = ReplayMemory(capacity=1000)\n", "\n", " self.steps_done = 0\n", " \n", " \n", " def act(self, state):\n", " \"Returns an action using epsilon-greedy action selection.\"\n", "\n", " # Decay epsilon exponentially\n", " self.epsilon = self.config['eps_end'] + (self.config['eps_start'] - self.config['eps_end']) * math.exp(-1. * self.steps_done / self.config['eps_decay'])\n", "\n", " # Keep track of time\n", " self.steps_done += 1\n", " \n", " # epsilon-greedy action selection\n", " if rng.random() < self.epsilon:\n", " return self.env.action_space.sample()\n", " else:\n", " with torch.no_grad():\n", " return self.value_net(state).argmax().item()\n", "\n", " \n", " def test(self, nb_episodes, recorder=None):\n", " \"Performs a test episode without exploration.\"\n", " previous_epsilon = self.epsilon\n", " self.epsilon = 0.0\n", "\n", " for episode in range(nb_episodes):\n", " \n", " # Reset\n", " state, _ = self.env.reset()\n", " state = torch.tensor(state, dtype=torch.float32, device=device)\n", "\n", " # Sample the episode\n", " done = False\n", " return_episode = 0\n", " while not done: \n", " action = self.act(state)\n", " next_state, reward, terminal, truncated, info = self.env.step(action)\n", " return_episode += reward\n", " done = terminal or truncated\n", " \n", " # Append the transition to the replay buffer\n", " self.memory.append(state, action, reward, next_state, done)\n", "\n", " state = torch.tensor(next_state, dtype=torch.float32, device=device)\n", "\n", " print(f\"Episode {episode}: return {return_episode}, epsilon: {self.epsilon:.4f}\")\n", " \n", " self.epsilon = previous_epsilon\n", " \n", " if recorder is not None:\n", " recorder.record(self.env.render())\n" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Episode 0: return 24.0, epsilon: 0.8807\n", "Episode 1: return 12.0, epsilon: 0.8708\n", "Episode 2: return 19.0, epsilon: 0.8553\n", "Episode 3: return 32.0, epsilon: 0.8300\n", "Episode 4: return 9.0, epsilon: 0.8230\n", "Episode 5: return 17.0, epsilon: 0.8099\n", "Episode 6: return 17.0, epsilon: 0.7971\n", "Episode 7: return 15.0, epsilon: 0.7860\n", "Episode 8: return 33.0, epsilon: 0.7621\n", "Episode 9: return 12.0, epsilon: 0.7536\n" ] } ], "source": [ "# Create the environment\n", "env = gym.make('CartPole-v0')\n", "\n", "# Hyperparameters\n", "config = {}\n", "config['nb_hidden'] = 128 # number of hidden neurons in each layer\n", "config['eps_start'] = 0.9 # starting value of epsilon\n", "config['eps_end'] = 0.05 # final value of epsilon\n", "config['eps_decay'] = 1000 # rate of exponential decay of epsilon, higher means a slower decay\n", "config['buffer_limit'] = 1000 # maximum number of transitions in the replay buffer\n", "\n", "# Create the agent\n", "agent = RandomDQNAgent(env, config)\n", "\n", "# Make 10 evaluation episodes\n", "agent.test(10)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "States: torch.Size([10, 4]) tensor([[-0.1452, -1.4138, 0.1999, 2.2473],\n", " [-0.1104, -0.2476, 0.2082, 0.7865],\n", " [-0.0767, -0.8002, 0.0919, 1.1989],\n", " [-0.0178, -0.6156, 0.0382, 0.8665],\n", " [-0.0724, -0.2315, 0.1183, 0.4170],\n", " [ 0.1283, 0.2030, -0.0597, -0.2070],\n", " [ 0.0689, 0.3966, 0.0116, -0.4653],\n", " [ 0.1142, -0.7662, -0.0327, 1.1176],\n", " [ 0.1016, -0.7656, -0.0328, 1.0440],\n", " [ 0.0760, 1.0158, -0.1276, -1.5086]], device='mps:0')\n", "Actions: torch.Size([10]) tensor([1, 1, 1, 1, 1, 0, 0, 0, 0, 1], device='mps:0')\n", "Rewards: torch.Size([10]) tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], device='mps:0')\n", "Next states: torch.Size([10, 4]) tensor([[-0.1735, -1.2211, 0.2449, 2.0223],\n", " [-0.1154, -0.0559, 0.2240, 0.5658],\n", " [-0.0927, -0.6064, 0.1158, 0.9363],\n", " [-0.0301, -0.4211, 0.0556, 0.5861],\n", " [-0.0770, -0.0382, 0.1266, 0.1638],\n", " [ 0.1324, 0.0088, -0.0638, 0.0662],\n", " [ 0.0768, 0.2013, 0.0023, -0.1690],\n", " [ 0.0989, -0.9609, -0.0104, 1.3998],\n", " [ 0.0863, -0.9603, -0.0120, 1.3262],\n", " [ 0.0963, 1.2122, -0.1577, -1.8382]], device='mps:0')\n", "Dones: torch.Size([10]) tensor([ True, True, False, False, False, False, False, False, False, False],\n", " device='mps:0')\n" ] } ], "source": [ "# Sample the ERM\n", "batch = agent.memory.sample(10)\n", "\n", "print(\"States:\", batch.state.shape, batch.state)\n", "print(\"Actions:\", batch.action.shape, batch.action)\n", "print(\"Rewards:\", batch.reward.shape, batch.reward)\n", "print(\"Next states:\", batch.next_state.shape, batch.next_state)\n", "print(\"Dones:\", batch.done.shape, batch.done)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q:** Use the value network stored into your agent to predict the Q-values of all actions for the states contained in the minibatch. Do NOT use a for loop. Check the size of the resulting tensor. " ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "torch.Size([10, 2])\n", "tensor([[ 0.1905, -0.1435],\n", " [ 0.0781, -0.0912],\n", " [ 0.1190, -0.1079],\n", " [ 0.0915, -0.0930],\n", " [ 0.0475, -0.0733],\n", " [ 0.0260, -0.0398],\n", " [ 0.0280, -0.0336],\n", " [ 0.1112, -0.1073],\n", " [ 0.1075, -0.1021],\n", " [ 0.0572, -0.0072]], device='mps:0', grad_fn=)\n" ] } ], "source": [ "Q_values = agent.value_net.forward(batch.state)\n", "\n", "print(Q_values.shape)\n", "print(Q_values)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q:** The previous tensors returns the value of all actions in those visited states. We now want only the Q-value of action that was taken (whose index is in `batch.action`). The resulting tensor should therefore a vector of length `batch_size`. How do we do that?\n", "\n", "*Hint:* it would take months of practice to master all the indexing methods available in pytorch: . Meanwhile, numpy-style indexing could be useful. Check what the following statements do:\n", "\n", "```python\n", "N = 10\n", "A = torch.randn((N, 2))\n", "B = torch.randint(0, 2, (N,))\n", "C = A[range(N), B]\n", "```" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[ 0.1905, -0.1435],\n", " [ 0.0781, -0.0912],\n", " [ 0.1190, -0.1079],\n", " [ 0.0915, -0.0930],\n", " [ 0.0475, -0.0733],\n", " [ 0.0260, -0.0398],\n", " [ 0.0280, -0.0336],\n", " [ 0.1112, -0.1073],\n", " [ 0.1075, -0.1021],\n", " [ 0.0572, -0.0072]], device='mps:0', grad_fn=)\n", "tensor([1, 1, 1, 1, 1, 0, 0, 0, 0, 1], device='mps:0')\n", "tensor([-0.1435, -0.0912, -0.1079, -0.0930, -0.0733, 0.0260, 0.0280, 0.1112,\n", " 0.1075, -0.0072], device='mps:0', grad_fn=)\n" ] } ], "source": [ "print(Q_values)\n", "print(batch.action)\n", "Q_taken = Q_values[range(10), batch.action]\n", "print(Q_taken)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## DQN agent\n", "\n", "Good, we should now have all the elementary bricks to create our DQN agent. \n", "\n", "Reminder from the lecture:\n", "\n", "---\n", "\n", "* Initialize value network $Q_{\\theta}$ and target network $Q_{\\theta'}$.\n", "\n", "* Initialize experience replay memory $\\mathcal{D}$ of maximal size $N$.\n", "\n", "* for $t \\in [0, T_\\text{total}]$:\n", "\n", " * Select an action $a_t$ based on $Q_\\theta(s_t, a)$ ($\\epsilon$-greedy), observe $s_{t+1}$ and $r_{t+1}$.\n", "\n", " * Store $(s_t, a_t, r_{t+1}, s_{t+1})$ in the experience replay memory.\n", "\n", " * Every $T_\\text{train}$ steps:\n", "\n", " * Sample a minibatch $\\mathcal{D}_s$ randomly from $\\mathcal{D}$.\n", "\n", " * For each transition $(s_k, a_k, r_k, s'_k)$ in the minibatch:\n", "\n", " * Compute the target value $t_k = r_k + \\gamma \\, \\max_{a'} Q_{\\theta'}(s'_k, a')$ using the target network.\n", "\n", " * Update the value network $Q_{\\theta}$ on $\\mathcal{D}_s$ to minimize:\n", "\n", " $$\\mathcal{L}(\\theta) = \\mathbb{E}_{\\mathcal{D}_s}[(t_k - Q_\\theta(s_k, a_k))^2]$$\n", "\n", " * Every $T_\\text{target}$ steps:\n", "\n", " * Update target network: $\\theta' \\leftarrow \\theta$.\n", "---\n", "\n", "Create a DQN agent class inspired from the notebooks on MC or TD. The constructor should create the value and target networks, and make sure that their parameters are the same. It also creates an empty replay buffer. \n", "\n", "The `act()` method implements $\\epsilon$-greedy action selection, with an exponentially decaying schedule for $\\epsilon$. The greedy action is read from the value network.\n", "\n", "The `train()` and `test()` methods run training and test episodes as usual, with optional rendering. The train method should return (or store in the object) the return of each episode (its length) so we can plot it at the end. \n", "\n", "The main difficulty will be the `update()` method, where learning is supposed to happen. It should sample a minibatch from the replay memory, compute a vector of Bellman targets $r_t + \\gamma \\, \\max_a Q(s_{t+1}, a)$ for each transition in the batch, compute the loss function (mse between these targets and the predicted Q-values), backpropagate the gradients and apply the optimizer (Adam, but feel free to pick your preferred optimizer). Refer to the previous notebook on pytorch if you do not know how to do that.\n", "\n", "The main tricky part is when $V(s_{t+1})$ has to be predicted by the **target** network. You do not want the target network to learn from the minibatch, so it should not compute the corresponding gradients to save computational time. You can make sure that the target network is purely in inference mode with the following context:\n", "\n", "```python\n", "with torch.no_grad():\n", " next_Q_values = target_net(batch.next_state)\n", "```\n", "\n", "Of course you want the Q-value of the greedy action in the next state, not the vector of all Q-values, so check the doc of `Tensor.max()`. \n", "\n", "Importantly, when the next state $s'$ is terminal (either the agent failed or the 200 steps are over), the Bellman target should be simply $r_t$ instead of $r_t + \\gamma \\, \\max_a Q(s_{t+1}, a)$, as no action will be taken in the next state. This is why we saved the booleans `done` were saved in the replay buffer. As they are boolean, you can use them for indexing:\n", "\n", "```python\n", "Q = torch.randn((batch_size,))\n", "Q[batch.dones] = 0.0\n", "```\n", "\n", "A minor detail: do not start learning until the replay buffer is full enough, otherwise you will not fill your minibatch. Usually, there is no learning until the buffer contains two or three times the batch size. Use `len(memory)` to know the current number of stored transitions.\n", "\n", "Here is a set of suggested hyperparameters to help you start. Of course, it is strongly advised to modify them and observe their influence, but it depends on the remaining time.\n", "\n", "* $\\gamma = 0.99$.\n", "* MLP with two layers of 128 neurons, Adam optimizer with a fixed learning rate of 0.001.\n", "* Replay buffer of maximum capacity 10000, batch size of 128.\n", "* Target network updated every 120 steps.\n", "* Epsilon-greedy action selection, with the schedule:\n", "\n", "$$\n", " \\epsilon = 0.05 + (0.9 - 0.05) * \\exp ( - \\dfrac{t}{1000})\n", "$$\n", "\n", "where $t$ is the total number of steps.\n", "\n", "\n", "**Q:** Train a DQN on cartpole for 250 episodes. How would you characterize the learning process (speed, stability, etc.). If possible, do several runs. Vary the hyperparameters." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "class DQNAgent:\n", " \"DQN agent.\"\n", " \n", " def __init__(self, env, config):\n", "\n", " # Parameters\n", " self.env = env\n", " self.config = config\n", "\n", " # Number of actions\n", " self.n_actions = self.env.action_space.n\n", "\n", " # Number of states\n", " self.state, info = self.env.reset()\n", " self.n_observations = len(self.state)\n", "\n", " # Value network\n", " self.value_net = MLP(self.n_observations, config['nb_hidden'], config['nb_hidden'], self.n_actions).to(device)\n", "\n", " # Target network\n", " self.target_net = MLP(self.n_observations, config['nb_hidden'], config['nb_hidden'], self.n_actions).to(device)\n", "\n", " # Copy the value weights into the target network\n", " self.target_net.load_state_dict(self.value_net.state_dict())\n", "\n", " # Optimizer\n", " self.optimizer = torch.optim.Adam(self.value_net.parameters(), lr=self.config['learning_rate'])\n", "\n", " # Loss function\n", " self.loss_function = torch.nn.MSELoss()\n", " \n", " # Replay buffer\n", " self.memory = ReplayMemory(self.config['buffer_limit'])\n", "\n", " self.steps_done = 0\n", " self.episode_durations = []\n", "\n", "\n", " def act(self, state):\n", "\n", " # Decay epsilon exponentially\n", " self.epsilon = self.config['eps_end'] + (self.config['eps_start'] - self.config['eps_end']) * math.exp(-1. * self.steps_done / self.config['eps_decay'])\n", "\n", " # Keep track of time\n", " self.steps_done += 1\n", " \n", " # epsilon-greedy action selection\n", " if rng.random() < self.epsilon:\n", " return self.env.action_space.sample()\n", " else:\n", " with torch.no_grad():\n", " return self.value_net(state).argmax(dim=0).item()\n", "\n", " def update(self):\n", "\n", " # Only learn when the replay buffer is full enough\n", " if len(self.memory) < 2 * self.config['batch_size']:\n", " return\n", " \n", " # Sample a batch\n", " batch = self.memory.sample(self.config['batch_size'])\n", "\n", " # Compute Q(s_t, a) with the current value network.\n", " Q_values = self.value_net(batch.state)[range(self.config['batch_size']), batch.action]\n", " \n", " # Compute Q(s_{t+1}, a*) for all next states.\n", " # If the next state is terminal, set the value to zero.\n", " # Do not compute gradients.\n", " with torch.no_grad():\n", " next_Q_values = self.target_net(batch.next_state).max(dim=1).values\n", " next_Q_values[batch.done] = 0.0\n", "\n", " # Compute the target Q values\n", " targets = (next_Q_values * self.config['gamma']) + batch.reward\n", "\n", " # Compute loss\n", " loss = self.loss_function(Q_values, targets)\n", "\n", " # Reinitialize the gradients\n", " self.optimizer.zero_grad()\n", "\n", " # Backpropagation\n", " loss.backward()\n", "\n", " # In-place gradient clipping (optional)\n", " #torch.nn.utils.clip_grad_value_(self.value_net.parameters(), 100)\n", "\n", " # Optimizer step\n", " self.optimizer.step()\n", "\n", " def train(self, num_episodes):\n", " \n", " for i_episode in range(num_episodes):\n", "\n", " tstart = time.time()\n", "\n", " # Initialize the environment and get its state\n", " state, _ = self.env.reset()\n", "\n", " # Transform the state into a tensor\n", " state = torch.tensor(state, dtype=torch.float32, device=device)\n", "\n", " done = False\n", " steps_episode = 0\n", " while not done:\n", " \n", " # Select an action\n", " action = self.act(state)\n", " \n", " # Perform the action\n", " next_state, reward, terminated, truncated, _ = self.env.step(action)\n", " \n", " # Terminal state\n", " done = terminated or truncated\n", "\n", " # Store the transition in memory\n", " self.memory.append(state, action, reward, next_state, done)\n", "\n", " # Move to the next state\n", " state = torch.tensor(next_state, dtype=torch.float32, device=device)\n", "\n", " # Perform one step of the optimization (on the policy network)\n", " self.update()\n", "\n", " # Update of the target network's weights\n", " if self.steps_done % self.config['target_update_period'] == 0:\n", " self.target_net.load_state_dict(self.value_net.state_dict())\n", "\n", " # Finish episode\n", " steps_episode += 1\n", " if done:\n", " self.episode_durations.append(steps_episode)\n", " print(f\"Episode {i_episode+1}, duration {steps_episode}, epsilon {self.epsilon:.4f} done in {time.time() - tstart}\")" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Episode 1, duration 20, epsilon 0.8840 done in 0.010928869247436523\n", "Episode 2, duration 39, epsilon 0.8521 done in 0.02133011817932129\n", "Episode 3, duration 35, epsilon 0.8245 done in 0.021078109741210938\n", "Episode 4, duration 20, epsilon 0.8092 done in 0.010505914688110352\n", "Episode 5, duration 44, epsilon 0.7765 done in 0.026645898818969727\n", "Episode 6, duration 25, epsilon 0.7586 done in 0.016075849533081055\n", "Episode 7, duration 13, epsilon 0.7494 done in 0.006777763366699219\n", "Episode 8, duration 34, epsilon 0.7260 done in 0.021413803100585938\n", "Episode 9, duration 41, epsilon 0.6989 done in 2.526124954223633\n", "Episode 10, duration 21, epsilon 0.6854 done in 0.14540505409240723\n", "Episode 11, duration 17, epsilon 0.6747 done in 0.12012696266174316\n", "Episode 12, duration 14, epsilon 0.6660 done in 0.09074711799621582\n", "Episode 13, duration 13, epsilon 0.6580 done in 0.08664298057556152\n", "Episode 14, duration 28, epsilon 0.6412 done in 0.19265198707580566\n", "Episode 15, duration 22, epsilon 0.6284 done in 0.15033173561096191\n", "Episode 16, duration 22, epsilon 0.6158 done in 0.15267610549926758\n", "Episode 17, duration 23, epsilon 0.6029 done in 0.15503692626953125\n", "Episode 18, duration 15, epsilon 0.5947 done in 0.10272073745727539\n", "Episode 19, duration 17, epsilon 0.5855 done in 0.11703014373779297\n", "Episode 20, duration 15, epsilon 0.5775 done in 0.10234189033508301\n", "Episode 21, duration 14, epsilon 0.5702 done in 0.1012122631072998\n", "Episode 22, duration 21, epsilon 0.5594 done in 0.14426207542419434\n", "Episode 23, duration 32, epsilon 0.5434 done in 0.21706485748291016\n", "Episode 24, duration 27, epsilon 0.5302 done in 0.18805289268493652\n", "Episode 25, duration 27, epsilon 0.5174 done in 0.19099903106689453\n", "Episode 26, duration 22, epsilon 0.5073 done in 0.16488099098205566\n", "Episode 27, duration 33, epsilon 0.4924 done in 0.2926771640777588\n", "Episode 28, duration 63, epsilon 0.4654 done in 0.49425673484802246\n", "Episode 29, duration 108, epsilon 0.4229 done in 0.7813999652862549\n", "Episode 30, duration 84, epsilon 0.3928 done in 0.6466999053955078\n", "Episode 31, duration 66, epsilon 0.3709 done in 0.4575388431549072\n", "Episode 32, duration 45, epsilon 0.3568 done in 0.31013035774230957\n", "Episode 33, duration 177, epsilon 0.3070 done in 1.2206060886383057\n", "Episode 34, duration 123, epsilon 0.2773 done in 0.8495826721191406\n", "Episode 35, duration 156, epsilon 0.2445 done in 1.1377429962158203\n", "Episode 36, duration 151, epsilon 0.2172 done in 1.1700310707092285\n", "Episode 37, duration 166, epsilon 0.1916 done in 1.1666131019592285\n", "Episode 38, duration 173, epsilon 0.1691 done in 1.2102069854736328\n", "Episode 39, duration 200, epsilon 0.1475 done in 1.4016377925872803\n", "Episode 40, duration 190, epsilon 0.1307 done in 1.4297728538513184\n", "Episode 41, duration 168, epsilon 0.1182 done in 1.1653170585632324\n", "Episode 42, duration 200, epsilon 0.1058 done in 1.3941991329193115\n", "Episode 43, duration 157, epsilon 0.0977 done in 1.0960440635681152\n", "Episode 44, duration 174, epsilon 0.0901 done in 1.2252748012542725\n", "Episode 45, duration 200, epsilon 0.0828 done in 1.3784756660461426\n", "Episode 46, duration 173, epsilon 0.0776 done in 1.213353157043457\n", "Episode 47, duration 200, epsilon 0.0726 done in 1.4108171463012695\n", "Episode 48, duration 162, epsilon 0.0692 done in 1.1317131519317627\n", "Episode 49, duration 176, epsilon 0.0661 done in 1.2339060306549072\n", "Episode 50, duration 175, epsilon 0.0635 done in 1.2066349983215332\n", "Episode 51, duration 179, epsilon 0.0613 done in 1.2518279552459717\n", "Episode 52, duration 199, epsilon 0.0593 done in 1.370851993560791\n", "Episode 53, duration 157, epsilon 0.0579 done in 1.0979859828948975\n", "Episode 54, duration 152, epsilon 0.0568 done in 1.072814702987671\n", "Episode 55, duration 157, epsilon 0.0558 done in 1.0916438102722168\n", "Episode 56, duration 165, epsilon 0.0549 done in 1.1536707878112793\n", "Episode 57, duration 155, epsilon 0.0542 done in 1.0730979442596436\n", "Episode 58, duration 164, epsilon 0.0536 done in 1.1358988285064697\n", "Episode 59, duration 173, epsilon 0.0530 done in 1.1930968761444092\n", "Episode 60, duration 200, epsilon 0.0525 done in 1.3894200325012207\n", "Episode 61, duration 162, epsilon 0.0521 done in 1.123974084854126\n", "Episode 62, duration 191, epsilon 0.0517 done in 1.3286561965942383\n", "Episode 63, duration 182, epsilon 0.0514 done in 1.265678882598877\n", "Episode 64, duration 200, epsilon 0.0512 done in 1.3921267986297607\n", "Episode 65, duration 155, epsilon 0.0510 done in 1.0708959102630615\n", "Episode 66, duration 122, epsilon 0.0509 done in 0.8449039459228516\n", "Episode 67, duration 169, epsilon 0.0508 done in 1.171288013458252\n", "Episode 68, duration 155, epsilon 0.0506 done in 1.065803050994873\n", "Episode 69, duration 171, epsilon 0.0505 done in 1.1903810501098633\n", "Episode 70, duration 158, epsilon 0.0505 done in 1.0794060230255127\n", "Episode 71, duration 135, epsilon 0.0504 done in 0.9425179958343506\n", "Episode 72, duration 173, epsilon 0.0503 done in 1.2061538696289062\n", "Episode 73, duration 134, epsilon 0.0503 done in 1.0127298831939697\n", "Episode 74, duration 140, epsilon 0.0503 done in 1.1216182708740234\n", "Episode 75, duration 166, epsilon 0.0502 done in 1.1763079166412354\n", "Episode 76, duration 184, epsilon 0.0502 done in 1.2722370624542236\n", "Episode 77, duration 200, epsilon 0.0502 done in 1.3994901180267334\n", "Episode 78, duration 162, epsilon 0.0501 done in 1.1298630237579346\n", "Episode 79, duration 141, epsilon 0.0501 done in 0.9746568202972412\n", "Episode 80, duration 165, epsilon 0.0501 done in 1.2154066562652588\n", "Episode 81, duration 163, epsilon 0.0501 done in 1.1442301273345947\n", "Episode 82, duration 164, epsilon 0.0501 done in 1.1488800048828125\n", "Episode 83, duration 128, epsilon 0.0501 done in 0.8947670459747314\n", "Episode 84, duration 94, epsilon 0.0501 done in 0.6654331684112549\n", "Episode 85, duration 115, epsilon 0.0500 done in 0.7962470054626465\n", "Episode 86, duration 105, epsilon 0.0500 done in 0.7088890075683594\n", "Episode 87, duration 134, epsilon 0.0500 done in 0.8996789455413818\n", "Episode 88, duration 127, epsilon 0.0500 done in 0.8609750270843506\n", "Episode 89, duration 137, epsilon 0.0500 done in 0.9249551296234131\n", "Episode 90, duration 126, epsilon 0.0500 done in 0.8459548950195312\n", "Episode 91, duration 116, epsilon 0.0500 done in 0.7772917747497559\n", "Episode 92, duration 91, epsilon 0.0500 done in 0.6441352367401123\n", "Episode 93, duration 91, epsilon 0.0500 done in 0.6781599521636963\n", "Episode 94, duration 127, epsilon 0.0500 done in 0.8687968254089355\n", "Episode 95, duration 129, epsilon 0.0500 done in 0.8872959613800049\n", "Episode 96, duration 111, epsilon 0.0500 done in 0.7792479991912842\n", "Episode 97, duration 131, epsilon 0.0500 done in 0.8846781253814697\n", "Episode 98, duration 141, epsilon 0.0500 done in 0.936420202255249\n", "Episode 99, duration 105, epsilon 0.0500 done in 0.6909458637237549\n", "Episode 100, duration 146, epsilon 0.0500 done in 0.9847288131713867\n", "Episode 101, duration 127, epsilon 0.0500 done in 0.8729619979858398\n", "Episode 102, duration 119, epsilon 0.0500 done in 0.7925238609313965\n", "Episode 103, duration 107, epsilon 0.0500 done in 0.7179620265960693\n", "Episode 104, duration 151, epsilon 0.0500 done in 1.0024669170379639\n", "Episode 105, duration 110, epsilon 0.0500 done in 0.7429869174957275\n", "Episode 106, duration 105, epsilon 0.0500 done in 0.7670748233795166\n", "Episode 107, duration 101, epsilon 0.0500 done in 0.7440199851989746\n", "Episode 108, duration 120, epsilon 0.0500 done in 0.864722728729248\n", "Episode 109, duration 106, epsilon 0.0500 done in 0.7149121761322021\n", "Episode 110, duration 112, epsilon 0.0500 done in 0.8089501857757568\n", "Episode 111, duration 93, epsilon 0.0500 done in 0.6416912078857422\n", "Episode 112, duration 103, epsilon 0.0500 done in 0.7595870494842529\n", "Episode 113, duration 99, epsilon 0.0500 done in 0.6787068843841553\n", "Episode 114, duration 90, epsilon 0.0500 done in 0.6839339733123779\n", "Episode 115, duration 99, epsilon 0.0500 done in 0.70619797706604\n", "Episode 116, duration 113, epsilon 0.0500 done in 0.7977390289306641\n", "Episode 117, duration 107, epsilon 0.0500 done in 0.7442677021026611\n", "Episode 118, duration 104, epsilon 0.0500 done in 0.7060117721557617\n", "Episode 119, duration 96, epsilon 0.0500 done in 0.6665740013122559\n", "Episode 120, duration 99, epsilon 0.0500 done in 0.6954801082611084\n", "Episode 121, duration 109, epsilon 0.0500 done in 0.7392399311065674\n", "Episode 122, duration 122, epsilon 0.0500 done in 0.822786808013916\n", "Episode 123, duration 113, epsilon 0.0500 done in 0.7565281391143799\n", "Episode 124, duration 132, epsilon 0.0500 done in 0.8871691226959229\n", "Episode 125, duration 116, epsilon 0.0500 done in 0.7746288776397705\n", "Episode 126, duration 115, epsilon 0.0500 done in 0.7686679363250732\n", "Episode 127, duration 131, epsilon 0.0500 done in 0.8958530426025391\n", "Episode 128, duration 161, epsilon 0.0500 done in 1.087691068649292\n", "Episode 129, duration 160, epsilon 0.0500 done in 1.0682952404022217\n", "Episode 130, duration 169, epsilon 0.0500 done in 1.1190180778503418\n", "Episode 131, duration 171, epsilon 0.0500 done in 1.1476161479949951\n", "Episode 132, duration 195, epsilon 0.0500 done in 1.300534963607788\n", "Episode 133, duration 200, epsilon 0.0500 done in 1.3386471271514893\n", "Episode 134, duration 200, epsilon 0.0500 done in 1.3347461223602295\n", "Episode 135, duration 200, epsilon 0.0500 done in 1.3350942134857178\n", "Episode 136, duration 200, epsilon 0.0500 done in 1.3486897945404053\n", "Episode 137, duration 200, epsilon 0.0500 done in 1.3442697525024414\n", "Episode 138, duration 200, epsilon 0.0500 done in 1.3415100574493408\n", "Episode 139, duration 200, epsilon 0.0500 done in 1.3514528274536133\n", "Episode 140, duration 200, epsilon 0.0500 done in 1.324605941772461\n", "Episode 141, duration 200, epsilon 0.0500 done in 1.3301770687103271\n", "Episode 142, duration 200, epsilon 0.0500 done in 1.3422541618347168\n", "Episode 143, duration 200, epsilon 0.0500 done in 1.3305821418762207\n", "Episode 144, duration 200, epsilon 0.0500 done in 1.346459150314331\n", "Episode 145, duration 200, epsilon 0.0500 done in 1.3368988037109375\n", "Episode 146, duration 200, epsilon 0.0500 done in 1.324411153793335\n", "Episode 147, duration 200, epsilon 0.0500 done in 1.3370959758758545\n", "Episode 148, duration 200, epsilon 0.0500 done in 1.3372869491577148\n", "Episode 149, duration 200, epsilon 0.0500 done in 1.3436431884765625\n", "Episode 150, duration 200, epsilon 0.0500 done in 1.3790981769561768\n", "Episode 151, duration 200, epsilon 0.0500 done in 1.4742920398712158\n", "Episode 152, duration 200, epsilon 0.0500 done in 1.44557523727417\n", "Episode 153, duration 200, epsilon 0.0500 done in 1.4291670322418213\n", "Episode 154, duration 200, epsilon 0.0500 done in 1.4063260555267334\n", "Episode 155, duration 200, epsilon 0.0500 done in 1.405716896057129\n", "Episode 156, duration 188, epsilon 0.0500 done in 1.293168067932129\n", "Episode 157, duration 184, epsilon 0.0500 done in 1.514660120010376\n", "Episode 158, duration 200, epsilon 0.0500 done in 1.5891220569610596\n", "Episode 159, duration 184, epsilon 0.0500 done in 1.4287211894989014\n", "Episode 160, duration 200, epsilon 0.0500 done in 1.6346118450164795\n", "Episode 161, duration 200, epsilon 0.0500 done in 1.6076791286468506\n", "Episode 162, duration 200, epsilon 0.0500 done in 1.5353331565856934\n", "Episode 163, duration 188, epsilon 0.0500 done in 1.4301581382751465\n", "Episode 164, duration 164, epsilon 0.0500 done in 1.2197020053863525\n", "Episode 165, duration 119, epsilon 0.0500 done in 0.8470640182495117\n", "Episode 166, duration 91, epsilon 0.0500 done in 0.705880880355835\n", "Episode 167, duration 112, epsilon 0.0500 done in 0.8511989116668701\n", "Episode 168, duration 121, epsilon 0.0500 done in 0.9285330772399902\n", "Episode 169, duration 165, epsilon 0.0500 done in 1.4010276794433594\n", "Episode 170, duration 200, epsilon 0.0500 done in 1.4931089878082275\n", "Episode 171, duration 158, epsilon 0.0500 done in 1.1297760009765625\n", "Episode 172, duration 184, epsilon 0.0500 done in 1.2905008792877197\n", "Episode 173, duration 161, epsilon 0.0500 done in 1.2188148498535156\n", "Episode 174, duration 169, epsilon 0.0500 done in 1.2725789546966553\n", "Episode 175, duration 173, epsilon 0.0500 done in 1.2186980247497559\n", "Episode 176, duration 193, epsilon 0.0500 done in 1.338731050491333\n", "Episode 177, duration 166, epsilon 0.0500 done in 1.316972017288208\n", "Episode 178, duration 198, epsilon 0.0500 done in 1.4319920539855957\n", "Episode 179, duration 195, epsilon 0.0500 done in 1.3895812034606934\n", "Episode 180, duration 200, epsilon 0.0500 done in 1.4180948734283447\n", "Episode 181, duration 200, epsilon 0.0500 done in 1.3860301971435547\n", "Episode 182, duration 200, epsilon 0.0500 done in 1.544933795928955\n", "Episode 183, duration 200, epsilon 0.0500 done in 1.376188039779663\n", "Episode 184, duration 200, epsilon 0.0500 done in 1.37398099899292\n", "Episode 185, duration 191, epsilon 0.0500 done in 1.3100950717926025\n", "Episode 186, duration 200, epsilon 0.0500 done in 1.3645508289337158\n", "Episode 187, duration 200, epsilon 0.0500 done in 1.3671917915344238\n", "Episode 188, duration 71, epsilon 0.0500 done in 0.4840209484100342\n", "Episode 189, duration 170, epsilon 0.0500 done in 1.1554310321807861\n", "Episode 190, duration 200, epsilon 0.0500 done in 1.3751280307769775\n", "Episode 191, duration 200, epsilon 0.0500 done in 1.3681631088256836\n", "Episode 192, duration 190, epsilon 0.0500 done in 1.296057939529419\n", "Episode 193, duration 200, epsilon 0.0500 done in 1.369154930114746\n", "Episode 194, duration 163, epsilon 0.0500 done in 1.1157150268554688\n", "Episode 195, duration 172, epsilon 0.0500 done in 1.1656968593597412\n", "Episode 196, duration 200, epsilon 0.0500 done in 1.3778979778289795\n", "Episode 197, duration 200, epsilon 0.0500 done in 1.3623039722442627\n", "Episode 198, duration 200, epsilon 0.0500 done in 1.3735718727111816\n", "Episode 199, duration 200, epsilon 0.0500 done in 1.3809599876403809\n", "Episode 200, duration 200, epsilon 0.0500 done in 1.380000114440918\n", "Episode 201, duration 200, epsilon 0.0500 done in 1.3676831722259521\n", "Episode 202, duration 200, epsilon 0.0500 done in 1.3898499011993408\n", "Episode 203, duration 200, epsilon 0.0500 done in 1.3679068088531494\n", "Episode 204, duration 200, epsilon 0.0500 done in 1.3534259796142578\n", "Episode 205, duration 200, epsilon 0.0500 done in 1.36385178565979\n", "Episode 206, duration 186, epsilon 0.0500 done in 1.2612950801849365\n", "Episode 207, duration 177, epsilon 0.0500 done in 1.2120819091796875\n", "Episode 208, duration 80, epsilon 0.0500 done in 0.5482821464538574\n", "Episode 209, duration 170, epsilon 0.0500 done in 1.1629600524902344\n", "Episode 210, duration 180, epsilon 0.0500 done in 1.2311630249023438\n", "Episode 211, duration 200, epsilon 0.0500 done in 1.3580238819122314\n", "Episode 212, duration 200, epsilon 0.0500 done in 1.3839151859283447\n", "Episode 213, duration 200, epsilon 0.0500 done in 1.3652369976043701\n", "Episode 214, duration 200, epsilon 0.0500 done in 1.3564629554748535\n", "Episode 215, duration 200, epsilon 0.0500 done in 1.3642499446868896\n", "Episode 216, duration 200, epsilon 0.0500 done in 1.3575990200042725\n", "Episode 217, duration 200, epsilon 0.0500 done in 1.3559317588806152\n", "Episode 218, duration 200, epsilon 0.0500 done in 1.3548388481140137\n", "Episode 219, duration 200, epsilon 0.0500 done in 1.3770561218261719\n", "Episode 220, duration 200, epsilon 0.0500 done in 1.478581190109253\n", "Episode 221, duration 183, epsilon 0.0500 done in 1.3361449241638184\n", "Episode 222, duration 162, epsilon 0.0500 done in 1.1335511207580566\n", "Episode 223, duration 196, epsilon 0.0500 done in 1.3585941791534424\n", "Episode 224, duration 200, epsilon 0.0500 done in 1.4101898670196533\n", "Episode 225, duration 200, epsilon 0.0500 done in 1.4771928787231445\n", "Episode 226, duration 200, epsilon 0.0500 done in 1.5035011768341064\n", "Episode 227, duration 119, epsilon 0.0500 done in 0.856168270111084\n", "Episode 228, duration 200, epsilon 0.0500 done in 1.439486026763916\n", "Episode 229, duration 200, epsilon 0.0500 done in 1.316842794418335\n", "Episode 230, duration 200, epsilon 0.0500 done in 1.5013949871063232\n", "Episode 231, duration 200, epsilon 0.0500 done in 1.3974170684814453\n", "Episode 232, duration 200, epsilon 0.0500 done in 1.3904950618743896\n", "Episode 233, duration 200, epsilon 0.0500 done in 1.399580955505371\n", "Episode 234, duration 200, epsilon 0.0500 done in 1.395763874053955\n", "Episode 235, duration 200, epsilon 0.0500 done in 1.4145090579986572\n", "Episode 236, duration 200, epsilon 0.0500 done in 1.4335989952087402\n", "Episode 237, duration 200, epsilon 0.0500 done in 1.4076240062713623\n", "Episode 238, duration 146, epsilon 0.0500 done in 1.0287110805511475\n", "Episode 239, duration 192, epsilon 0.0500 done in 1.3662900924682617\n", "Episode 240, duration 28, epsilon 0.0500 done in 0.20001626014709473\n", "Episode 241, duration 148, epsilon 0.0500 done in 1.019770860671997\n", "Episode 242, duration 115, epsilon 0.0500 done in 0.8170418739318848\n", "Episode 243, duration 154, epsilon 0.0500 done in 1.069025993347168\n", "Episode 244, duration 200, epsilon 0.0500 done in 1.3859591484069824\n", "Episode 245, duration 200, epsilon 0.0500 done in 1.4307832717895508\n", "Episode 246, duration 200, epsilon 0.0500 done in 1.416254997253418\n", "Episode 247, duration 200, epsilon 0.0500 done in 1.357828140258789\n", "Episode 248, duration 200, epsilon 0.0500 done in 1.391690969467163\n", "Episode 249, duration 200, epsilon 0.0500 done in 1.431091070175171\n", "Episode 250, duration 200, epsilon 0.0500 done in 1.4289429187774658\n" ] } ], "source": [ "# Hyperparameters\n", "config = {}\n", "config['nb_hidden'] = 128 # number of hidden neurons in each layer\n", "config['batch_size'] = 128 # number of transitions sampled from the replay buffer\n", "config['gamma'] = 0.99 # discount factor\n", "config['eps_start'] = 0.9 # starting value of epsilon\n", "config['eps_end'] = 0.05 # final value of epsilon\n", "config['eps_decay'] = 1000 # rate of exponential decay of epsilon, higher means a slower decay\n", "config['learning_rate'] = 1e-3 # learning rate of the optimizer\n", "config['target_update_period'] = 120 # update period (in steps) of the target network\n", "config['buffer_limit'] = 10000 # maximum number of transitions in the replay buffer\n", "\n", "# Create the environment\n", "env = gym.make('CartPole-v0')\n", "\n", "# Create the agent\n", "agent = DQNAgent(env, config)\n", "\n", "# Train the agent\n", "agent.train(num_episodes=250)\n" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'Returns')" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.figure(figsize=(10, 6))\n", "plt.plot(agent.episode_durations)\n", "plt.xlabel(\"Episodes\")\n", "plt.ylabel(\"Returns\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**A:** Training is very slow and unstable. Different runs lead to very different convergence profiles. There is much to improve..." ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.7" } }, "nbformat": 4, "nbformat_minor": 2 }