diff --git a/unit4.ipynb b/unit4.ipynb new file mode 100644 index 0000000..0fb17e6 --- /dev/null +++ b/unit4.ipynb @@ -0,0 +1,1623 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CjRWziAVU2lZ" + }, + "source": [ + "# Unit 4: Code your first Deep Reinforcement Learning Algorithm with PyTorch: Reinforce. And test its robustness ๐Ÿ’ช\n", + "\n", + "\"thumbnail\"/\n", + "\n", + "\n", + "In this notebook, you'll code your first Deep Reinforcement Learning algorithm from scratch: Reinforce (also called Monte Carlo Policy Gradient).\n", + "\n", + "Reinforce is a *Policy-based method*: a Deep Reinforcement Learning algorithm that tries **to optimize the policy directly without using an action-value function**.\n", + "\n", + "More precisely, Reinforce is a *Policy-gradient method*, a subclass of *Policy-based methods* that aims **to optimize the policy directly by estimating the weights of the optimal policy using gradient ascent**.\n", + "\n", + "To test its robustness, we're going to train it in 2 different simple environments:\n", + "- Cartpole-v1\n", + "- PixelcopterEnv\n", + "\n", + "โฌ‡๏ธ Here is an example of what **you will achieve at the end of this notebook.** โฌ‡๏ธ" + ] + }, + { + "cell_type": "markdown", + "source": [ + " \"Environments\"/\n" + ], + "metadata": { + "id": "s4rBom2sbo7S" + } + }, + { + "cell_type": "markdown", + "source": [ + "### ๐ŸŽฎ Environments: \n", + "\n", + "- [CartPole-v1](https://www.gymlibrary.dev/environments/classic_control/cart_pole/)\n", + "- [PixelCopter](https://pygame-learning-environment.readthedocs.io/en/latest/user/games/pixelcopter.html)\n", + "\n", + "### ๐Ÿ“š RL-Library: \n", + "\n", + "- Python\n", + "- PyTorch\n", + "\n", + "\n", + "We're constantly trying to improve our tutorials, so **if you find some issues in this notebook**, please [open an issue on the GitHub Repo](https://github.com/huggingface/deep-rl-class/issues)." + ], + "metadata": { + "id": "BPLwsPajb1f8" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "L_WSo0VUV99t" + }, + "source": [ + "## Objectives of this notebook ๐Ÿ†\n", + "At the end of the notebook, you will:\n", + "- Be able to **code from scratch a Reinforce algorithm using PyTorch.**\n", + "- Be able to **test the robustness of your agent using simple environments.**\n", + "- Be able to **push your trained agent to the Hub** with a nice video replay and an evaluation score ๐Ÿ”ฅ." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lEPrZg2eWa4R" + }, + "source": [ + "## This notebook is from the Deep Reinforcement Learning Course\n", + "\"Deep" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6p5HnEefISCB" + }, + "source": [ + "In this free course, you will:\n", + "\n", + "- ๐Ÿ“– Study Deep Reinforcement Learning in **theory and practice**.\n", + "- ๐Ÿง‘โ€๐Ÿ’ป Learn to **use famous Deep RL libraries** such as Stable Baselines3, RL Baselines3 Zoo, CleanRL and Sample Factory 2.0.\n", + "- ๐Ÿค– Train **agents in unique environments** \n", + "\n", + "And more check ๐Ÿ“š the syllabus ๐Ÿ‘‰ https://simoninithomas.github.io/deep-rl-course\n", + "\n", + "Donโ€™t forget to **sign up to the course** (we are collecting your email to be able toย **send you the links when each Unit is published and give you information about the challenges and updates).**\n", + "\n", + "\n", + "The best way to keep in touch is to join our discord server to exchange with the community and with us ๐Ÿ‘‰๐Ÿป https://discord.gg/ydHrjt3WP5" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mjY-eq3eWh9O" + }, + "source": [ + "## Prerequisites ๐Ÿ—๏ธ\n", + "Before diving into the notebook, you need to:\n", + "\n", + "๐Ÿ”ฒ ๐Ÿ“š [Study Policy Gradients by reading Unit 4](https://huggingface.co/deep-rl-course/unit4/introduction)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Let's code Reinforce algorithm from scratch ๐Ÿ”ฅ\n", + "\n", + "\n", + "To validate this hands-on for the certification process, you need to push your trained models to the Hub.\n", + "\n", + "- Get a result of >= 350 for `Cartpole-v1`.\n", + "- Get a result of >= 5 for `PixelCopter`.\n", + "\n", + "To find your result, go to the leaderboard and find your model, **the result = mean_reward - std of reward**. **If you don't see your model on the leaderboard, go at the bottom of the leaderboard page and click on the refresh button**.\n", + "\n", + "For more information about the certification process, check this section ๐Ÿ‘‰ https://huggingface.co/deep-rl-course/en/unit0/introduction#certification-process\n" + ], + "metadata": { + "id": "Bsh4ZAamchSl" + } + }, + { + "cell_type": "markdown", + "source": [ + "## An advice ๐Ÿ’ก\n", + "It's better to run this colab in a copy on your Google Drive, so that **if it timeouts** you still have the saved notebook on your Google Drive and do not need to fill everything from scratch.\n", + "\n", + "To do that you can either do `Ctrl + S` or `File > Save a copy in Google Drive.`" + ], + "metadata": { + "id": "JoTC9o2SczNn" + } + }, + { + "cell_type": "markdown", + "source": [ + "## Set the GPU ๐Ÿ’ช\n", + "- To **accelerate the agent's training, we'll use a GPU**. To do that, go to `Runtime > Change Runtime type`\n", + "\n", + "\"GPU" + ], + "metadata": { + "id": "PU4FVzaoM6fC" + } + }, + { + "cell_type": "markdown", + "source": [ + "- `Hardware Accelerator > GPU`\n", + "\n", + "\"GPU" + ], + "metadata": { + "id": "KV0NyFdQM9ZG" + } + }, + { + "cell_type": "markdown", + "source": [ + "## Create a virtual display ๐Ÿ–ฅ\n", + "\n", + "During the notebook, we'll need to generate a replay video. To do so, with colab, **we need to have a virtual screen to be able to render the environment** (and thus record the frames). \n", + "\n", + "Hence the following cell will install the librairies and create and run a virtual screen ๐Ÿ–ฅ" + ], + "metadata": { + "id": "bTpYcVZVMzUI" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jV6wjQ7Be7p5" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!apt install python-opengl\n", + "!apt install ffmpeg\n", + "!apt install xvfb\n", + "!pip install pyvirtualdisplay\n", + "!pip install pyglet==1.5.1" + ] + }, + { + "cell_type": "code", + "source": [ + "# Virtual display\n", + "from pyvirtualdisplay import Display\n", + "\n", + "virtual_display = Display(visible=0, size=(1400, 900))\n", + "virtual_display.start()" + ], + "metadata": { + "id": "Sr-Nuyb1dBm0" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tjrLfPFIW8XK" + }, + "source": [ + "## Install the dependencies ๐Ÿ”ฝ\n", + "The first step is to install the dependencies. Weโ€™ll install multiple ones:\n", + "\n", + "- `gym`\n", + "- `gym-games`: Extra gym environments made with PyGame.\n", + "- `huggingface_hub`: ๐Ÿค— works as a central place where anyone can share and explore models and datasets. It has versioning, metrics, visualizations, and other features that will allow you to easily collaborate with others.\n", + "\n", + "You may be wondering why we install gym and not gymnasium, a more recent version of gym? **Because the gym-games we are using are not updated yet with gymnasium**. \n", + "\n", + "The differences you'll encounter here:\n", + "- In `gym` we don't have `terminated` and `truncated` but only `done`.\n", + "- In `gym` using `env.step()` returns `state, reward, done, info`\n", + "\n", + "You can learn more about the differences between Gym and Gymnasium here ๐Ÿ‘‰ https://gymnasium.farama.org/content/migration-guide/\n", + "\n", + "\n", + "You can see here all the Reinforce models available ๐Ÿ‘‰ https://huggingface.co/models?other=reinforce\n", + "\n", + "And you can find all the Deep Reinforcement Learning models here ๐Ÿ‘‰ https://huggingface.co/models?pipeline_tag=reinforcement-learning\n" + ] + }, + { + "cell_type": "code", + "source": [ + "!pip install -r https://raw.githubusercontent.com/huggingface/deep-rl-class/main/notebooks/unit4/requirements-unit4.txt" + ], + "metadata": { + "id": "e8ZVi-uydpgL" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AAHAq6RZW3rn" + }, + "source": [ + "## Import the packages ๐Ÿ“ฆ\n", + "In addition to import the installed libraries, we also import:\n", + "\n", + "- `imageio`: A library that will help us to generate a replay video\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "V8oadoJSWp7C" + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "from collections import deque\n", + "\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "# PyTorch\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "import torch.optim as optim\n", + "from torch.distributions import Categorical\n", + "\n", + "# Gym\n", + "import gym\n", + "import gym_pygame\n", + "\n", + "# Hugging Face Hub\n", + "from huggingface_hub import notebook_login # To log to our Hugging Face account to be able to upload models to the Hub.\n", + "import imageio" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Check if we have a GPU\n", + "\n", + "- Let's check if we have a GPU\n", + "- If it's the case you should see `device:cuda0`" + ], + "metadata": { + "id": "RfxJYdMeeVgv" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "kaJu5FeZxXGY" + }, + "outputs": [], + "source": [ + "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "U5TNYa14aRav" + }, + "outputs": [], + "source": [ + "print(device)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PBPecCtBL_pZ" + }, + "source": [ + "We're now ready to implement our Reinforce algorithm ๐Ÿ”ฅ" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8KEyKYo2ZSC-" + }, + "source": [ + "# First agent: Playing CartPole-v1 ๐Ÿค–" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "haLArKURMyuF" + }, + "source": [ + "## Create the CartPole environment and understand how it works\n", + "### [The environment ๐ŸŽฎ](https://www.gymlibrary.dev/environments/classic_control/cart_pole/)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AH_TaLKFXo_8" + }, + "source": [ + "### Why do we use a simple environment like CartPole-v1?\n", + "As explained in [Reinforcement Learning Tips and Tricks](https://stable-baselines3.readthedocs.io/en/master/guide/rl_tips.html), when you implement your agent from scratch you need **to be sure that it works correctly and find bugs with easy environments before going deeper**. Since finding bugs will be much easier in simple environments.\n", + "\n", + "\n", + "> Try to have some โ€œsign of lifeโ€ on toy problems\n", + "\n", + "\n", + "> Validate the implementation by making it run on harder and harder envs (you can compare results against the RL zoo). You usually need to run hyperparameter optimization for that step.\n", + "___\n", + "### The CartPole-v1 environment\n", + "\n", + "> A pole is attached by an un-actuated joint to a cart, which moves along a frictionless track. The pendulum is placed upright on the cart and the goal is to balance the pole by applying forces in the left and right direction on the cart.\n", + "\n", + "\n", + "\n", + "So, we start with CartPole-v1. The goal is to push the cart left or right **so that the pole stays in the equilibrium.**\n", + "\n", + "The episode ends if:\n", + "- The pole Angle is greater than ยฑ12ยฐ\n", + "- Cart Position is greater than ยฑ2.4\n", + "- Episode length is greater than 500\n", + "\n", + "We get a reward ๐Ÿ’ฐ of +1 every timestep the Pole stays in the equilibrium." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "POOOk15_K6KA" + }, + "outputs": [], + "source": [ + "env_id = \"CartPole-v1\"\n", + "# Create the env\n", + "env = gym.make(env_id)\n", + "\n", + "# Create the evaluation env\n", + "eval_env = gym.make(env_id)\n", + "\n", + "# Get the state space and action space\n", + "s_size = env.observation_space.shape[0]\n", + "a_size = env.action_space.n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "FMLFrjiBNLYJ" + }, + "outputs": [], + "source": [ + "print(\"_____OBSERVATION SPACE_____ \\n\")\n", + "print(\"The State Space is: \", s_size)\n", + "print(\"Sample observation\", env.observation_space.sample()) # Get a random observation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Lu6t4sRNNWkN" + }, + "outputs": [], + "source": [ + "print(\"\\n _____ACTION SPACE_____ \\n\")\n", + "print(\"The Action Space is: \", a_size)\n", + "print(\"Action Space Sample\", env.action_space.sample()) # Take a random action" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7SJMJj3WaFOz" + }, + "source": [ + "## Let's build the Reinforce Architecture\n", + "This implementation is based on two implementations:\n", + "- [PyTorch official Reinforcement Learning example](https://github.com/pytorch/examples/blob/main/reinforcement_learning/reinforce.py)\n", + "- [Udacity Reinforce](https://github.com/udacity/deep-reinforcement-learning/blob/master/reinforce/REINFORCE.ipynb)\n", + "- [Improvement of the integration by Chris1nexus](https://github.com/huggingface/deep-rl-class/pull/95)\n", + "\n", + "\"Reinforce\"/" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "49kogtxBODX8" + }, + "source": [ + "So we want:\n", + "- Two fully connected layers (fc1 and fc2).\n", + "- Using ReLU as activation function of fc1\n", + "- Using Softmax to output a probability distribution over actions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "w2LHcHhVZvPZ" + }, + "outputs": [], + "source": [ + "class Policy(nn.Module):\n", + " def __init__(self, s_size, a_size, h_size):\n", + " super(Policy, self).__init__()\n", + " # Create two fully connected layers\n", + "\n", + "\n", + "\n", + " def forward(self, x):\n", + " # Define the forward pass\n", + " # state goes to fc1 then we apply ReLU activation function\n", + "\n", + " # fc1 outputs goes to fc2\n", + "\n", + " # We output the softmax\n", + " \n", + " def act(self, state):\n", + " \"\"\"\n", + " Given a state, take action\n", + " \"\"\"\n", + " state = torch.from_numpy(state).float().unsqueeze(0).to(device)\n", + " probs = self.forward(state).cpu()\n", + " m = Categorical(probs)\n", + " action = np.argmax(m)\n", + " return action.item(), m.log_prob(action)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rOMrdwSYOWSC" + }, + "source": [ + "### Solution" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jGdhRSVrOV4K" + }, + "outputs": [], + "source": [ + "class Policy(nn.Module):\n", + " def __init__(self, s_size, a_size, h_size):\n", + " super(Policy, self).__init__()\n", + " self.fc1 = nn.Linear(s_size, h_size)\n", + " self.fc2 = nn.Linear(h_size, a_size)\n", + "\n", + " def forward(self, x):\n", + " x = F.relu(self.fc1(x))\n", + " x = self.fc2(x)\n", + " return F.softmax(x, dim=1)\n", + " \n", + " def act(self, state):\n", + " state = torch.from_numpy(state).float().unsqueeze(0).to(device)\n", + " probs = self.forward(state).cpu()\n", + " m = Categorical(probs)\n", + " action = np.argmax(m)\n", + " return action.item(), m.log_prob(action)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZTGWL4g2eM5B" + }, + "source": [ + "I make a mistake, can you guess where?\n", + "\n", + "- To find out let's make a forward pass:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lwnqGBCNePor" + }, + "outputs": [], + "source": [ + "debug_policy = Policy(s_size, a_size, 64).to(device)\n", + "debug_policy.act(env.reset())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "14UYkoxCPaor" + }, + "source": [ + "- Here we see that the error says `ValueError: The value argument to log_prob must be a Tensor`\n", + "\n", + "- It means that `action` in `m.log_prob(action)` must be a Tensor **but it's not.**\n", + "\n", + "- Do you know why? Check the act function and try to see why it does not work. \n", + "\n", + "Advice ๐Ÿ’ก: Something is wrong in this implementation. Remember that we act function **we want to sample an action from the probability distribution over actions**.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gfGJNZBUP7Vn" + }, + "source": [ + "### (Real) Solution" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ho_UHf49N9i4" + }, + "outputs": [], + "source": [ + "class Policy(nn.Module):\n", + " def __init__(self, s_size, a_size, h_size):\n", + " super(Policy, self).__init__()\n", + " self.fc1 = nn.Linear(s_size, h_size)\n", + " self.fc2 = nn.Linear(h_size, a_size)\n", + "\n", + " def forward(self, x):\n", + " x = F.relu(self.fc1(x))\n", + " x = self.fc2(x)\n", + " return F.softmax(x, dim=1)\n", + " \n", + " def act(self, state):\n", + " state = torch.from_numpy(state).float().unsqueeze(0).to(device)\n", + " probs = self.forward(state).cpu()\n", + " m = Categorical(probs)\n", + " action = m.sample()\n", + " return action.item(), m.log_prob(action)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rgJWQFU_eUYw" + }, + "source": [ + "By using CartPole, it was easier to debug since **we know that the bug comes from our integration and not from our simple environment**." + ] + }, + { + "cell_type": "markdown", + "source": [ + "- Since **we want to sample an action from the probability distribution over actions**, we can't use `action = np.argmax(m)` since it will always output the action that have the highest probability.\n", + "\n", + "- We need to replace with `action = m.sample()` that will sample an action from the probability distribution P(.|s)" + ], + "metadata": { + "id": "c-20i7Pk0l1T" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4MXoqetzfIoW" + }, + "source": [ + "### Let's build the Reinforce Training Algorithm\n", + "This is the Reinforce algorithm pseudocode:\n", + "\n", + "\"Policy\n", + " " + ] + }, + { + "cell_type": "markdown", + "source": [ + "- When we calculate the return Gt (line 6) we see that we calculate the sum of discounted rewards **starting at timestep t**.\n", + "\n", + "- Why? Because our policy should only **reinforce actions on the basis of the consequences**: so rewards obtained before taking an action are useless (since they were not because of the action), **only the ones that come after the action matters**.\n", + "\n", + "- Before coding this you should read this section [don't let the past distract you](https://spinningup.openai.com/en/latest/spinningup/rl_intro3.html#don-t-let-the-past-distract-you) that explains why we use reward-to-go policy gradient.\n", + "\n", + "We use an interesting technique coded by [Chris1nexus](https://github.com/Chris1nexus) to **compute the return at each timestep efficiently**. The comments explained the procedure. Don't hesitate also [to check the PR explanation](https://github.com/huggingface/deep-rl-class/pull/95)\n", + "But overall the idea is to **compute the return at each timestep efficiently**." + ], + "metadata": { + "id": "QmcXG-9i2Qu2" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "O554nUGPpcoq" + }, + "source": [ + "The second question you may ask is **why do we minimize the loss**? You talked about Gradient Ascent not Gradient Descent?\n", + "\n", + "- We want to maximize our utility function $J(\\theta)$ but in PyTorch like in Tensorflow it's better to **minimize an objective function.**\n", + " - So let's say we want to reinforce action 3 at a certain timestep. Before training this action P is 0.25.\n", + " - So we want to modify $\\theta$ such that $\\pi_\\theta(a_3|s; \\theta) > 0.25$\n", + " - Because all P must sum to 1, max $\\pi_\\theta(a_3|s; \\theta)$ will **minimize other action probability.**\n", + " - So we should tell PyTorch **to min $1 - \\pi_\\theta(a_3|s; \\theta)$.**\n", + " - This loss function approaches 0 as $\\pi_\\theta(a_3|s; \\theta)$ nears 1.\n", + " - So we are encouraging the gradient to max $\\pi_\\theta(a_3|s; \\theta)$\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "iOdv8Q9NfLK7" + }, + "outputs": [], + "source": [ + "def reinforce(policy, optimizer, n_training_episodes, max_t, gamma, print_every):\n", + " # Help us to calculate the score during the training\n", + " scores_deque = deque(maxlen=100)\n", + " scores = []\n", + " # Line 3 of pseudocode\n", + " for i_episode in range(1, n_training_episodes+1):\n", + " saved_log_probs = []\n", + " rewards = []\n", + " state = # TODO: reset the environment\n", + " # Line 4 of pseudocode\n", + " for t in range(max_t):\n", + " action, log_prob = # TODO get the action\n", + " saved_log_probs.append(log_prob)\n", + " state, reward, done, _ = # TODO: take an env step\n", + " rewards.append(reward)\n", + " if done:\n", + " break \n", + " scores_deque.append(sum(rewards))\n", + " scores.append(sum(rewards))\n", + " \n", + " # Line 6 of pseudocode: calculate the return\n", + " returns = deque(maxlen=max_t) \n", + " n_steps = len(rewards) \n", + " # Compute the discounted returns at each timestep,\n", + " # as the sum of the gamma-discounted return at time t (G_t) + the reward at time t\n", + " \n", + " # In O(N) time, where N is the number of time steps\n", + " # (this definition of the discounted return G_t follows the definition of this quantity \n", + " # shown at page 44 of Sutton&Barto 2017 2nd draft)\n", + " # G_t = r_(t+1) + r_(t+2) + ...\n", + " \n", + " # Given this formulation, the returns at each timestep t can be computed \n", + " # by re-using the computed future returns G_(t+1) to compute the current return G_t\n", + " # G_t = r_(t+1) + gamma*G_(t+1)\n", + " # G_(t-1) = r_t + gamma* G_t\n", + " # (this follows a dynamic programming approach, with which we memorize solutions in order \n", + " # to avoid computing them multiple times)\n", + " \n", + " # This is correct since the above is equivalent to (see also page 46 of Sutton&Barto 2017 2nd draft)\n", + " # G_(t-1) = r_t + gamma*r_(t+1) + gamma*gamma*r_(t+2) + ...\n", + " \n", + " \n", + " ## Given the above, we calculate the returns at timestep t as: \n", + " # gamma[t] * return[t] + reward[t]\n", + " #\n", + " ## We compute this starting from the last timestep to the first, in order\n", + " ## to employ the formula presented above and avoid redundant computations that would be needed \n", + " ## if we were to do it from first to last.\n", + " \n", + " ## Hence, the queue \"returns\" will hold the returns in chronological order, from t=0 to t=n_steps\n", + " ## thanks to the appendleft() function which allows to append to the position 0 in constant time O(1)\n", + " ## a normal python list would instead require O(N) to do this.\n", + " for t in range(n_steps)[::-1]:\n", + " disc_return_t = (returns[0] if len(returns)>0 else 0)\n", + " returns.appendleft( ) # TODO: complete here \n", + " \n", + " ## standardization of the returns is employed to make training more stable\n", + " eps = np.finfo(np.float32).eps.item()\n", + " \n", + " ## eps is the smallest representable float, which is \n", + " # added to the standard deviation of the returns to avoid numerical instabilities\n", + " returns = torch.tensor(returns)\n", + " returns = (returns - returns.mean()) / (returns.std() + eps)\n", + " \n", + " # Line 7:\n", + " policy_loss = []\n", + " for log_prob, disc_return in zip(saved_log_probs, returns):\n", + " policy_loss.append(-log_prob * disc_return)\n", + " policy_loss = torch.cat(policy_loss).sum()\n", + " \n", + " # Line 8: PyTorch prefers gradient descent \n", + " optimizer.zero_grad()\n", + " policy_loss.backward()\n", + " optimizer.step()\n", + " \n", + " if i_episode % print_every == 0:\n", + " print('Episode {}\\tAverage Score: {:.2f}'.format(i_episode, np.mean(scores_deque)))\n", + " \n", + " return scores" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YB0Cxrw1StrP" + }, + "source": [ + "#### Solution" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "NCNvyElRStWG" + }, + "outputs": [], + "source": [ + "def reinforce(policy, optimizer, n_training_episodes, max_t, gamma, print_every):\n", + " # Help us to calculate the score during the training\n", + " scores_deque = deque(maxlen=100)\n", + " scores = []\n", + " # Line 3 of pseudocode\n", + " for i_episode in range(1, n_training_episodes+1):\n", + " saved_log_probs = []\n", + " rewards = []\n", + " state = env.reset()\n", + " # Line 4 of pseudocode\n", + " for t in range(max_t):\n", + " action, log_prob = policy.act(state)\n", + " saved_log_probs.append(log_prob)\n", + " state, reward, done, _ = env.step(action)\n", + " rewards.append(reward)\n", + " if done:\n", + " break \n", + " scores_deque.append(sum(rewards))\n", + " scores.append(sum(rewards))\n", + " \n", + " # Line 6 of pseudocode: calculate the return\n", + " returns = deque(maxlen=max_t) \n", + " n_steps = len(rewards) \n", + " # Compute the discounted returns at each timestep,\n", + " # as \n", + " # the sum of the gamma-discounted return at time t (G_t) + the reward at time t\n", + " #\n", + " # In O(N) time, where N is the number of time steps\n", + " # (this definition of the discounted return G_t follows the definition of this quantity \n", + " # shown at page 44 of Sutton&Barto 2017 2nd draft)\n", + " # G_t = r_(t+1) + r_(t+2) + ...\n", + " \n", + " # Given this formulation, the returns at each timestep t can be computed \n", + " # by re-using the computed future returns G_(t+1) to compute the current return G_t\n", + " # G_t = r_(t+1) + gamma*G_(t+1)\n", + " # G_(t-1) = r_t + gamma* G_t\n", + " # (this follows a dynamic programming approach, with which we memorize solutions in order \n", + " # to avoid computing them multiple times)\n", + " \n", + " # This is correct since the above is equivalent to (see also page 46 of Sutton&Barto 2017 2nd draft)\n", + " # G_(t-1) = r_t + gamma*r_(t+1) + gamma*gamma*r_(t+2) + ...\n", + " \n", + " \n", + " ## Given the above, we calculate the returns at timestep t as: \n", + " # gamma[t] * return[t] + reward[t]\n", + " #\n", + " ## We compute this starting from the last timestep to the first, in order\n", + " ## to employ the formula presented above and avoid redundant computations that would be needed \n", + " ## if we were to do it from first to last.\n", + " \n", + " ## Hence, the queue \"returns\" will hold the returns in chronological order, from t=0 to t=n_steps\n", + " ## thanks to the appendleft() function which allows to append to the position 0 in constant time O(1)\n", + " ## a normal python list would instead require O(N) to do this.\n", + " for t in range(n_steps)[::-1]:\n", + " disc_return_t = (returns[0] if len(returns)>0 else 0)\n", + " returns.appendleft( gamma*disc_return_t + rewards[t] ) \n", + " \n", + " ## standardization of the returns is employed to make training more stable\n", + " eps = np.finfo(np.float32).eps.item()\n", + " ## eps is the smallest representable float, which is \n", + " # added to the standard deviation of the returns to avoid numerical instabilities \n", + " returns = torch.tensor(returns)\n", + " returns = (returns - returns.mean()) / (returns.std() + eps)\n", + " \n", + " # Line 7:\n", + " policy_loss = []\n", + " for log_prob, disc_return in zip(saved_log_probs, returns):\n", + " policy_loss.append(-log_prob * disc_return)\n", + " policy_loss = torch.cat(policy_loss).sum()\n", + " \n", + " # Line 8: PyTorch prefers gradient descent \n", + " optimizer.zero_grad()\n", + " policy_loss.backward()\n", + " optimizer.step()\n", + " \n", + " if i_episode % print_every == 0:\n", + " print('Episode {}\\tAverage Score: {:.2f}'.format(i_episode, np.mean(scores_deque)))\n", + " \n", + " return scores" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RIWhQyJjfpEt" + }, + "source": [ + "## Train it\n", + "- We're now ready to train our agent.\n", + "- But first, we define a variable containing all the training hyperparameters.\n", + "- You can change the training parameters (and should ๐Ÿ˜‰)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "utRe1NgtVBYF" + }, + "outputs": [], + "source": [ + "cartpole_hyperparameters = {\n", + " \"h_size\": 16,\n", + " \"n_training_episodes\": 1000,\n", + " \"n_evaluation_episodes\": 10,\n", + " \"max_t\": 1000,\n", + " \"gamma\": 1.0,\n", + " \"lr\": 1e-2,\n", + " \"env_id\": env_id,\n", + " \"state_space\": s_size,\n", + " \"action_space\": a_size,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "D3lWyVXBVfl6" + }, + "outputs": [], + "source": [ + "# Create policy and place it to the device\n", + "cartpole_policy = Policy(cartpole_hyperparameters[\"state_space\"], cartpole_hyperparameters[\"action_space\"], cartpole_hyperparameters[\"h_size\"]).to(device)\n", + "cartpole_optimizer = optim.Adam(cartpole_policy.parameters(), lr=cartpole_hyperparameters[\"lr\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uGf-hQCnfouB" + }, + "outputs": [], + "source": [ + "scores = reinforce(cartpole_policy,\n", + " cartpole_optimizer,\n", + " cartpole_hyperparameters[\"n_training_episodes\"], \n", + " cartpole_hyperparameters[\"max_t\"],\n", + " cartpole_hyperparameters[\"gamma\"], \n", + " 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Qajj2kXqhB3g" + }, + "source": [ + "## Define evaluation method ๐Ÿ“\n", + "- Here we define the evaluation method that we're going to use to test our Reinforce agent." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "3FamHmxyhBEU" + }, + "outputs": [], + "source": [ + "def evaluate_agent(env, max_steps, n_eval_episodes, policy):\n", + " \"\"\"\n", + " Evaluate the agent for ``n_eval_episodes`` episodes and returns average reward and std of reward.\n", + " :param env: The evaluation environment\n", + " :param n_eval_episodes: Number of episode to evaluate the agent\n", + " :param policy: The Reinforce agent\n", + " \"\"\"\n", + " episode_rewards = []\n", + " for episode in range(n_eval_episodes):\n", + " state = env.reset()\n", + " step = 0\n", + " done = False\n", + " total_rewards_ep = 0\n", + " \n", + " for step in range(max_steps):\n", + " action, _ = policy.act(state)\n", + " new_state, reward, done, info = env.step(action)\n", + " total_rewards_ep += reward\n", + " \n", + " if done:\n", + " break\n", + " state = new_state\n", + " episode_rewards.append(total_rewards_ep)\n", + " mean_reward = np.mean(episode_rewards)\n", + " std_reward = np.std(episode_rewards)\n", + "\n", + " return mean_reward, std_reward" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xdH2QCrLTrlT" + }, + "source": [ + "## Evaluate our agent ๐Ÿ“ˆ" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ohGSXDyHh0xx" + }, + "outputs": [], + "source": [ + "evaluate_agent(eval_env, \n", + " cartpole_hyperparameters[\"max_t\"], \n", + " cartpole_hyperparameters[\"n_evaluation_episodes\"],\n", + " cartpole_policy)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7CoeLkQ7TpO8" + }, + "source": [ + "### Publish our trained model on the Hub ๐Ÿ”ฅ\n", + "Now that we saw we got good results after the training, we can publish our trained model on the hub ๐Ÿค— with one line of code.\n", + "\n", + "Here's an example of a Model Card:\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Jmhs1k-cftIq" + }, + "source": [ + "### Push to the Hub\n", + "#### Do not modify this code" + ] + }, + { + "cell_type": "code", + "source": [ + "from huggingface_hub import HfApi, snapshot_download\n", + "from huggingface_hub.repocard import metadata_eval_result, metadata_save\n", + "\n", + "from pathlib import Path\n", + "import datetime\n", + "import json\n", + "import imageio\n", + "\n", + "import tempfile\n", + "\n", + "import os" + ], + "metadata": { + "id": "LIVsvlW_8tcw" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Lo4JH45if81z" + }, + "outputs": [], + "source": [ + "def record_video(env, policy, out_directory, fps=30):\n", + " \"\"\"\n", + " Generate a replay video of the agent\n", + " :param env\n", + " :param Qtable: Qtable of our agent\n", + " :param out_directory\n", + " :param fps: how many frame per seconds (with taxi-v3 and frozenlake-v1 we use 1)\n", + " \"\"\"\n", + " images = [] \n", + " done = False\n", + " state = env.reset()\n", + " img = env.render(mode='rgb_array')\n", + " images.append(img)\n", + " while not done:\n", + " # Take the action (index) that have the maximum expected future reward given that state\n", + " action, _ = policy.act(state)\n", + " state, reward, done, info = env.step(action) # We directly put next_state = state for recording logic\n", + " img = env.render(mode='rgb_array')\n", + " images.append(img)\n", + " imageio.mimsave(out_directory, [np.array(img) for i, img in enumerate(images)], fps=fps)" + ] + }, + { + "cell_type": "code", + "source": [ + "def push_to_hub(repo_id, \n", + " model,\n", + " hyperparameters,\n", + " eval_env,\n", + " video_fps=30\n", + " ):\n", + " \"\"\"\n", + " Evaluate, Generate a video and Upload a model to Hugging Face Hub.\n", + " This method does the complete pipeline:\n", + " - It evaluates the model\n", + " - It generates the model card\n", + " - It generates a replay video of the agent\n", + " - It pushes everything to the Hub\n", + "\n", + " :param repo_id: repo_id: id of the model repository from the Hugging Face Hub\n", + " :param model: the pytorch model we want to save\n", + " :param hyperparameters: training hyperparameters\n", + " :param eval_env: evaluation environment\n", + " :param video_fps: how many frame per seconds to record our video replay \n", + " \"\"\"\n", + "\n", + " _, repo_name = repo_id.split(\"/\")\n", + " api = HfApi()\n", + " \n", + " # Step 1: Create the repo\n", + " repo_url = api.create_repo(\n", + " repo_id=repo_id,\n", + " exist_ok=True,\n", + " )\n", + "\n", + " with tempfile.TemporaryDirectory() as tmpdirname:\n", + " local_directory = Path(tmpdirname)\n", + " \n", + " # Step 2: Save the model\n", + " torch.save(model, local_directory / \"model.pt\")\n", + "\n", + " # Step 3: Save the hyperparameters to JSON\n", + " with open(local_directory / \"hyperparameters.json\", \"w\") as outfile:\n", + " json.dump(hyperparameters, outfile)\n", + " \n", + " # Step 4: Evaluate the model and build JSON\n", + " mean_reward, std_reward = evaluate_agent(eval_env, \n", + " hyperparameters[\"max_t\"],\n", + " hyperparameters[\"n_evaluation_episodes\"], \n", + " model)\n", + " # Get datetime\n", + " eval_datetime = datetime.datetime.now()\n", + " eval_form_datetime = eval_datetime.isoformat()\n", + "\n", + " evaluate_data = {\n", + " \"env_id\": hyperparameters[\"env_id\"], \n", + " \"mean_reward\": mean_reward,\n", + " \"n_evaluation_episodes\": hyperparameters[\"n_evaluation_episodes\"],\n", + " \"eval_datetime\": eval_form_datetime,\n", + " }\n", + "\n", + " # Write a JSON file\n", + " with open(local_directory / \"results.json\", \"w\") as outfile:\n", + " json.dump(evaluate_data, outfile)\n", + "\n", + " # Step 5: Create the model card\n", + " env_name = hyperparameters[\"env_id\"]\n", + " \n", + " metadata = {}\n", + " metadata[\"tags\"] = [\n", + " env_name,\n", + " \"reinforce\",\n", + " \"reinforcement-learning\",\n", + " \"custom-implementation\",\n", + " \"deep-rl-class\"\n", + " ]\n", + "\n", + " # Add metrics\n", + " eval = metadata_eval_result(\n", + " model_pretty_name=repo_name,\n", + " task_pretty_name=\"reinforcement-learning\",\n", + " task_id=\"reinforcement-learning\",\n", + " metrics_pretty_name=\"mean_reward\",\n", + " metrics_id=\"mean_reward\",\n", + " metrics_value=f\"{mean_reward:.2f} +/- {std_reward:.2f}\",\n", + " dataset_pretty_name=env_name,\n", + " dataset_id=env_name,\n", + " )\n", + "\n", + " # Merges both dictionaries\n", + " metadata = {**metadata, **eval}\n", + "\n", + " model_card = f\"\"\"\n", + " # **Reinforce** Agent playing **{env_id}**\n", + " This is a trained model of a **Reinforce** agent playing **{env_id}** .\n", + " To learn to use this model and train yours check Unit 4 of the Deep Reinforcement Learning Course: https://huggingface.co/deep-rl-course/unit4/introduction\n", + " \"\"\"\n", + "\n", + " readme_path = local_directory / \"README.md\"\n", + " readme = \"\"\n", + " if readme_path.exists():\n", + " with readme_path.open(\"r\", encoding=\"utf8\") as f:\n", + " readme = f.read()\n", + " else:\n", + " readme = model_card\n", + "\n", + " with readme_path.open(\"w\", encoding=\"utf-8\") as f:\n", + " f.write(readme)\n", + "\n", + " # Save our metrics to Readme metadata\n", + " metadata_save(readme_path, metadata)\n", + "\n", + " # Step 6: Record a video\n", + " video_path = local_directory / \"replay.mp4\"\n", + " record_video(env, model, video_path, video_fps)\n", + "\n", + " # Step 7. Push everything to the Hub\n", + " api.upload_folder(\n", + " repo_id=repo_id,\n", + " folder_path=local_directory,\n", + " path_in_repo=\".\",\n", + " )\n", + "\n", + " print(f\"Your model is pushed to the Hub. You can view your model here: {repo_url}\")" + ], + "metadata": { + "id": "_TPdq47D7_f_" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "w17w8CxzoURM" + }, + "source": [ + "### .\n", + "\n", + "By using `push_to_hub` **you evaluate, record a replay, generate a model card of your agent and push it to the Hub**.\n", + "\n", + "This way:\n", + "- You can **showcase our work** ๐Ÿ”ฅ\n", + "- You can **visualize your agent playing** ๐Ÿ‘€\n", + "- You can **share with the community an agent that others can use** ๐Ÿ’พ\n", + "- You can **access a leaderboard ๐Ÿ† to see how well your agent is performing compared to your classmates** ๐Ÿ‘‰ https://huggingface.co/spaces/huggingface-projects/Deep-Reinforcement-Learning-Leaderboard\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cWnFC0iZooTw" + }, + "source": [ + "To be able to share your model with the community there are three more steps to follow:\n", + "\n", + "1๏ธโƒฃ (If it's not already done) create an account to HF โžก https://huggingface.co/join\n", + "\n", + "2๏ธโƒฃ Sign in and then, you need to store your authentication token from the Hugging Face website.\n", + "- Create a new token (https://huggingface.co/settings/tokens) **with write role**\n", + "\n", + "\n", + "\"Create\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "QB5nIcxR8paT" + }, + "outputs": [], + "source": [ + "notebook_login()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GyWc1x3-o3xG" + }, + "source": [ + "If you don't want to use a Google Colab or a Jupyter Notebook, you need to use this command instead: `huggingface-cli login` (or `login`)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "F-D-zhbRoeOm" + }, + "source": [ + "3๏ธโƒฃ We're now ready to push our trained agent to the ๐Ÿค— Hub ๐Ÿ”ฅ using `package_to_hub()` function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "UNwkTS65Uq3Q" + }, + "outputs": [], + "source": [ + "repo_id = \"\" #TODO Define your repo id {username/Reinforce-{model-id}}\n", + "push_to_hub(repo_id,\n", + " cartpole_policy, # The model we want to save\n", + " cartpole_hyperparameters, # Hyperparameters\n", + " eval_env, # Evaluation environment\n", + " video_fps=30\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jrnuKH1gYZSz" + }, + "source": [ + "Now that we try the robustness of our implementation, let's try a more complex environment: PixelCopter ๐Ÿš\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Second agent: PixelCopter ๐Ÿš\n", + "\n", + "### Study the PixelCopter environment ๐Ÿ‘€\n", + "- [The Environment documentation](https://pygame-learning-environment.readthedocs.io/en/latest/user/games/pixelcopter.html)\n" + ], + "metadata": { + "id": "JNLVmKKVKA6j" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JBSc8mlfyin3" + }, + "outputs": [], + "source": [ + "env_id = \"Pixelcopter-PLE-v0\"\n", + "env = gym.make(env_id)\n", + "eval_env = gym.make(env_id)\n", + "s_size = env.observation_space.shape[0]\n", + "a_size = env.action_space.n" + ] + }, + { + "cell_type": "code", + "source": [ + "print(\"_____OBSERVATION SPACE_____ \\n\")\n", + "print(\"The State Space is: \", s_size)\n", + "print(\"Sample observation\", env.observation_space.sample()) # Get a random observation" + ], + "metadata": { + "id": "L5u_zAHsKBy7" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "print(\"\\n _____ACTION SPACE_____ \\n\")\n", + "print(\"The Action Space is: \", a_size)\n", + "print(\"Action Space Sample\", env.action_space.sample()) # Take a random action" + ], + "metadata": { + "id": "D7yJM9YXKNbq" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NNWvlyvzalXr" + }, + "source": [ + "The observation space (7) ๐Ÿ‘€:\n", + "- player y position\n", + "- player velocity\n", + "- player distance to floor\n", + "- player distance to ceiling\n", + "- next block x distance to player\n", + "- next blocks top y location\n", + "- next blocks bottom y location\n", + "\n", + "The action space(2) ๐ŸŽฎ:\n", + "- Up\n", + "- Down\n", + "\n", + "The reward function ๐Ÿ’ฐ: \n", + "- For each vertical block it passes through it gains a positive reward of +1. Each time a terminal state reached it receives a negative reward of -1." + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Define the new Policy ๐Ÿง \n", + "- We need to have a deeper neural network since the environment is more complex" + ], + "metadata": { + "id": "aV1466QP8crz" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "I1eBkCiX2X_S" + }, + "outputs": [], + "source": [ + "class Policy(nn.Module):\n", + " def __init__(self, s_size, a_size, h_size):\n", + " super(Policy, self).__init__()\n", + " # Define the three layers here\n", + "\n", + " def forward(self, x):\n", + " # Define the forward process here\n", + " return F.softmax(x, dim=1)\n", + " \n", + " def act(self, state):\n", + " state = torch.from_numpy(state).float().unsqueeze(0).to(device)\n", + " probs = self.forward(state).cpu()\n", + " m = Categorical(probs)\n", + " action = m.sample()\n", + " return action.item(), m.log_prob(action)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "#### Solution" + ], + "metadata": { + "id": "47iuAFqV8Ws-" + } + }, + { + "cell_type": "code", + "source": [ + "class Policy(nn.Module):\n", + " def __init__(self, s_size, a_size, h_size):\n", + " super(Policy, self).__init__()\n", + " self.fc1 = nn.Linear(s_size, h_size)\n", + " self.fc2 = nn.Linear(h_size, h_size*2)\n", + " self.fc3 = nn.Linear(h_size*2, a_size)\n", + "\n", + " def forward(self, x):\n", + " x = F.relu(self.fc1(x))\n", + " x = F.relu(self.fc2(x))\n", + " x = self.fc3(x)\n", + " return F.softmax(x, dim=1)\n", + " \n", + " def act(self, state):\n", + " state = torch.from_numpy(state).float().unsqueeze(0).to(device)\n", + " probs = self.forward(state).cpu()\n", + " m = Categorical(probs)\n", + " action = m.sample()\n", + " return action.item(), m.log_prob(action)" + ], + "metadata": { + "id": "wrNuVcHC8Xu7" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SM1QiGCSbBkM" + }, + "source": [ + "### Define the hyperparameters โš™๏ธ\n", + "- Because this environment is more complex.\n", + "- Especially for the hidden size, we need more neurons." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "y0uujOR_ypB6" + }, + "outputs": [], + "source": [ + "pixelcopter_hyperparameters = {\n", + " \"h_size\": 64,\n", + " \"n_training_episodes\": 50000,\n", + " \"n_evaluation_episodes\": 10,\n", + " \"max_t\": 10000,\n", + " \"gamma\": 0.99,\n", + " \"lr\": 1e-4,\n", + " \"env_id\": env_id,\n", + " \"state_space\": s_size,\n", + " \"action_space\": a_size,\n", + "}" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Train it\n", + "- We're now ready to train our agent ๐Ÿ”ฅ." + ], + "metadata": { + "id": "wyvXTJWm9GJG" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7mM2P_ckysFE" + }, + "outputs": [], + "source": [ + "# Create policy and place it to the device\n", + "# torch.manual_seed(50)\n", + "pixelcopter_policy = Policy(pixelcopter_hyperparameters[\"state_space\"], pixelcopter_hyperparameters[\"action_space\"], pixelcopter_hyperparameters[\"h_size\"]).to(device)\n", + "pixelcopter_optimizer = optim.Adam(pixelcopter_policy.parameters(), lr=pixelcopter_hyperparameters[\"lr\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "v1HEqP-fy-Rf" + }, + "outputs": [], + "source": [ + "scores = reinforce(pixelcopter_policy,\n", + " pixelcopter_optimizer,\n", + " pixelcopter_hyperparameters[\"n_training_episodes\"], \n", + " pixelcopter_hyperparameters[\"max_t\"],\n", + " pixelcopter_hyperparameters[\"gamma\"], \n", + " 1000)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Publish our trained model on the Hub ๐Ÿ”ฅ" + ], + "metadata": { + "id": "8kwFQ-Ip85BE" + } + }, + { + "cell_type": "code", + "source": [ + "repo_id = \"\" #TODO Define your repo id {username/Reinforce-{model-id}}\n", + "push_to_hub(repo_id,\n", + " pixelcopter_policy, # The model we want to save\n", + " pixelcopter_hyperparameters, # Hyperparameters\n", + " eval_env, # Evaluation environment\n", + " video_fps=30\n", + " )" + ], + "metadata": { + "id": "6PtB7LRbTKWK" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7VDcJ29FcOyb" + }, + "source": [ + "## Some additional challenges ๐Ÿ†\n", + "The best way to learn **is to try things on your own**! As you saw, the current agent is not doing great. As a first suggestion, you can train for more steps. But also trying to find better parameters.\n", + "\n", + "In the [Leaderboard](https://huggingface.co/spaces/huggingface-projects/Deep-Reinforcement-Learning-Leaderboard) you will find your agents. Can you get to the top?\n", + "\n", + "Here are some ideas to achieve so:\n", + "* Train more steps\n", + "* Try different hyperparameters by looking at what your classmates have done ๐Ÿ‘‰ https://huggingface.co/models?other=reinforce\n", + "* **Push your new trained model** on the Hub ๐Ÿ”ฅ\n", + "* **Improving the implementation for more complex environments** (for instance, what about changing the network to a Convolutional Neural Network to handle\n", + "frames as observation)?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "x62pP0PHdA-y" + }, + "source": [ + "________________________________________________________________________\n", + "\n", + "**Congrats on finishing this unit**!ย There was a lot of information.\n", + "And congrats on finishing the tutorial. You've just coded your first Deep Reinforcement Learning agent from scratch using PyTorch and shared it on the Hub ๐Ÿฅณ.\n", + "\n", + "Don't hesitate to iterate on this unit **by improving the implementation for more complex environments** (for instance, what about changing the network to a Convolutional Neural Network to handle\n", + "frames as observation)?\n", + "\n", + "In the next unit, **we're going to learn more about Unity MLAgents**, by training agents in Unity environments. This way, you will be ready to participate in the **AI vs AI challenges where you'll train your agents\n", + "to compete against other agents in a snowball fight and a soccer game.**\n", + "\n", + "Sounds fun? See you next time!\n", + "\n", + "Finally, we would love **to hear what you think of the course and how we can improve it**. If you have some feedback then, please ๐Ÿ‘‰ [fill this form](https://forms.gle/BzKXWzLAGZESGNaE9)\n", + "\n", + "See you in Unit 5! ๐Ÿ”ฅ\n", + "\n", + "### Keep Learning, stay awesome ๐Ÿค—\n", + "\n" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "private_outputs": true, + "provenance": [], + "collapsed_sections": [ + "BPLwsPajb1f8", + "L_WSo0VUV99t", + "mjY-eq3eWh9O", + "JoTC9o2SczNn", + "gfGJNZBUP7Vn", + "YB0Cxrw1StrP", + "47iuAFqV8Ws-", + "x62pP0PHdA-y" + ], + "include_colab_link": true + }, + "gpuClass": "standard", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file