diff --git a/notebooks/unit1/test b/notebooks/unit1/test deleted file mode 100644 index 8b13789..0000000 --- a/notebooks/unit1/test +++ /dev/null @@ -1 +0,0 @@ - diff --git a/notebooks/unit1/unit1.ipynb b/notebooks/unit1/unit1.ipynb index 5159949..d6e6d54 100644 --- a/notebooks/unit1/unit1.ipynb +++ b/notebooks/unit1/unit1.ipynb @@ -53,6 +53,15 @@ "id": "x7oR6R-ZIbeS" } }, + { + "cell_type": "markdown", + "source": [ + "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": "OwEcFHe9RRZW" + } + }, { "cell_type": "markdown", "metadata": { @@ -87,7 +96,7 @@ "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, and RLlib.\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", @@ -172,7 +181,7 @@ { "cell_type": "markdown", "source": [ - "## Step 0: Set the GPU ๐Ÿ’ช and install the virtual screen\n", + "## 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" @@ -1086,30 +1095,18 @@ "Naturally, during the course, weโ€™re going to use and deeper explain again these terms but **itโ€™s better to have a good understanding of them now before diving into the next chapters.**\n" ] }, - { - "cell_type": "markdown", - "metadata": { - "id": "feR90OUSEXq9" - }, - "source": [ - "\n", - "\n", - "## This is a course built with you ๐Ÿ‘ท๐Ÿฟโ€โ™€๏ธ\n", - "\n", - "We want to improve and update the course iteratively with your feedback. If you have some, please fill this form ๐Ÿ‘‰ https://forms.gle/3HgA7bEHwAmmLfwh9\n", - "\n", - "If you found some issues in this notebook, please [open an issue on the Github Repo](https://github.com/huggingface/deep-rl-class/issues).\n", - "\n", - "\n" - ] - }, { "cell_type": "markdown", "metadata": { "id": "BjLhT70TEZIn" }, "source": [ - "See you on [Unit 2](https://github.com/huggingface/deep-rl-class/tree/main/unit2#unit-2-introduction-to-q-learning)! ๐Ÿ”ฅ\n", + "See you on [Bonus unit 1](https://github.com/huggingface/deep-rl-class/tree/main/unit2#unit-2-introduction-to-q-learning)! ๐Ÿ”ฅ TODO CHANGE LINK. Where you'll train Huggy the Dog to fetch the stick.\n", + "\n", + "\"Huggy\"/\n", + "\n", + "\n", + "\n", "TODO CHANGE LINK\n", "## Keep learning, stay awesome ๐Ÿค—" ] @@ -1121,7 +1118,14 @@ "private_outputs": true, "provenance": [], "collapsed_sections": [ - "feR90OUSEXq9" + "dFD9RAFjG8aq", + "QAN7B0_HCVZC", + "ClJJk88yoBUi", + "1bQzQ-QcE3zo", + "BY_HuedOoISR", + "BqPKw3jt_pG5", + "IK_kR78NoNb2", + "Avf6gufJBGMw" ] }, "gpuClass": "standard", diff --git a/notebooks/unit2/unit2.ipynb b/notebooks/unit2/unit2.ipynb new file mode 100644 index 0000000..cf97cdc --- /dev/null +++ b/notebooks/unit2/unit2.ipynb @@ -0,0 +1,1743 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "njb_ProuHiOe" + }, + "source": [ + "# Unit 2: Q-Learning with FrozenLake-v1 โ›„ and Taxi-v3 ๐Ÿš•\n", + "\n", + "\"Unit\n", + "\n", + "In this notebook, **you'll code from scratch your first Reinforcement Learning agent** playing FrozenLake โ„๏ธ using Q-Learning, share it to the community, and experiment with different configurations.\n", + "\n", + "\n", + "โฌ‡๏ธ Here is an example of what **you will achieve in just a couple of minutes.** โฌ‡๏ธ\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vRU_vXBrl1Jx" + }, + "source": [ + "\"Environments\"/" + ] + }, + { + "cell_type": "markdown", + "source": [ + "TODO: ADD TEXT LIVE INFO" + ], + "metadata": { + "id": "yaBKcncmYku4" + } + }, + { + "cell_type": "markdown", + "source": [ + "TODO: ADD IF YOU HAVE QUESTIONS\n" + ], + "metadata": { + "id": "hz5KE5HjYlRh" + } + }, + { + "cell_type": "markdown", + "source": [ + "###๐ŸŽฎ Environments: \n", + "- [FrozenLake-v1](https://www.gymlibrary.dev/environments/toy_text/frozen_lake/)\n", + "- [Taxi-v3](https://www.gymlibrary.dev/environments/toy_text/taxi/)\n", + "\n", + "###๐Ÿ“š RL-Library: \n", + "- Python and Numpy" + ], + "metadata": { + "id": "DPTBOv9HYLZ2" + } + }, + { + "cell_type": "markdown", + "source": [ + "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": "3iaIxM_TwklQ" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4i6tjI2tHQ8j" + }, + "source": [ + "## Objectives of this notebook ๐Ÿ†\n", + "At the end of the notebook, you will:\n", + "- Be able to use **Gym**, the environment library.\n", + "- Be able to code from scratch a Q-Learning agent.\n", + "- Be able to **push your trained agent and the code to the Hub** with a nice video replay and an evaluation score ๐Ÿ”ฅ.\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## This notebook is from Deep Reinforcement Learning Course\n", + "\"Deep" + ], + "metadata": { + "id": "viNzVbVaYvY3" + } + }, + { + "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": "Y-mo_6rXIjRi" + }, + "source": [ + "## Prerequisites ๐Ÿ—๏ธ\n", + "Before diving into the notebook, you need to:\n", + "\n", + "๐Ÿ”ฒ ๐Ÿ“š **Study Q-Learning by reading Unit 2** ๐Ÿค— ADD LINK " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f2ONOODsyrMU" + }, + "source": [ + "## A small recap of Q-Learning" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "V68VveLacfxJ" + }, + "source": [ + "- The *Q-Learning* **is the RL algorithm that** \n", + " - Trains *Q-Function*, an **action-value function** that contains, as internal memory, a *Q-table* **that contains all the state-action pair values.**\n", + " \n", + " - Given a state and action, our Q-Function **will search into its Q-table the corresponding value.**\n", + " \n", + "\"Q\n", + "\n", + "- When the training is done,**we have an optimal Q-Function, so an optimal Q-Table.**\n", + " \n", + "- And if we **have an optimal Q-function**, we\n", + "have an optimal policy,since we **know for each state, what is the best action to take.**\n", + "\n", + "\"Link\n", + "\n", + "\n", + "But, in the beginning,ย our **Q-Table is useless since it gives arbitrary value for each state-action pairย (most of the time we initialize the Q-Table to 0 values)**. But, as weโ€™llย explore the environment and update our Q-Table it will give us better and better approximations\n", + "\n", + "\"q-learning.jpeg\"\n", + "\n", + "This is the Q-Learning pseudocode:\n", + "\n", + "\"Q-Learning\"\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Let's code our first Reinforcement Learning algorithm ๐Ÿš€" + ], + "metadata": { + "id": "HEtx8Y8MqKfH" + } + }, + { + "cell_type": "markdown", + "source": [ + "## Install dependencies and 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 ๐Ÿ–ฅ\n", + "\n", + "Weโ€™ll install multiple ones:\n", + "\n", + "- `gym`: Contains the FrozenLake-v1 โ›„ and Taxi-v3 ๐Ÿš• environments. We use `gym==0.24` since it contains a nice Taxi-v3 UI version.\n", + "- `pygame`: Used for the FrozenLake-v1 and Taxi-v3 UI.\n", + "- `numPy`: Used for handling our Q-table.\n", + "\n", + "The Hugging Face 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 can see here all the Deep reinforcement Learning models available ๐Ÿ‘‰ https://huggingface.co/models?other=q-learning\n" + ], + "metadata": { + "id": "4gpxC1_kqUYe" + } + }, + { + "cell_type": "markdown", + "source": [ + "TODO CHANGE LINK OF THE REQUIREMENTS" + ], + "metadata": { + "id": "32e3NPYgH5ET" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "9XaULfDZDvrC" + }, + "outputs": [], + "source": [ + "!pip install -r https://huggingface.co/spaces/ThomasSimonini/temp-space-requirements/raw/main/requirements/requirements-unit2.txt" + ] + }, + { + "cell_type": "code", + "source": [ + "%capture\n", + "!sudo apt-get update\n", + "!apt install python-opengl\n", + "!apt install ffmpeg\n", + "!apt install xvfb\n", + "!pip3 install pyvirtualdisplay" + ], + "metadata": { + "id": "n71uTX7qqzz2" + }, + "execution_count": null, + "outputs": [] + }, + { + "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": "DaY1N4dBrabi" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W-7f-Swax_9x" + }, + "source": [ + "## Import the packages ๐Ÿ“ฆ\n", + "\n", + "In addition to the installed libraries, we also use:\n", + "\n", + "- `random`: To generate random numbers (that will be useful for Epsilon-Greedy Policy).\n", + "- `imageio`: To generate a replay video\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VcNvOAQlysBJ" + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import gym\n", + "import random\n", + "import imageio\n", + "import os\n", + "\n", + "import pickle5 as pickle\n", + "from tqdm.notebook import tqdm" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xp4-bXKIy1mQ" + }, + "source": [ + "We're now ready to code our Q-Learning algorithm ๐Ÿ”ฅ" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xya49aNJWVvv" + }, + "source": [ + "# Part 1: Frozen Lake โ›„ (non slippery version)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NAvihuHdy9tw" + }, + "source": [ + "## Create and understand [FrozenLake environment โ›„]((https://www.gymlibrary.dev/environments/toy_text/frozen_lake/)\n", + "---\n", + "\n", + "๐Ÿ’ก A good habit when you start to use an environment is to check its documentation \n", + "\n", + "๐Ÿ‘‰ https://www.gymlibrary.dev/environments/toy_text/frozen_lake/\n", + "\n", + "---\n", + "\n", + "We're going to train our Q-Learning agent **to navigate from the starting state (S) to the goal state (G) by walking only on frozen tiles (F) and avoid holes (H)**.\n", + "\n", + "We can have two sizes of environment:\n", + "- `map_name=\"4x4\"`: a 4x4 grid version\n", + "- `map_name=\"8x8\"`: a 8x8 grid version\n", + "\n", + "\n", + "The environment has two modes:\n", + "- `is_slippery=False`: The agent always move in the intended direction due to the non-slippery nature of the frozen lake.\n", + "- `is_slippery=True`: The agent may not always move in the intended direction due to the slippery nature of the frozen lake (stochastic)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UaW_LHfS0PY2" + }, + "source": [ + "For now let's keep it simple with the 4x4 map and non-slippery" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IzJnb8O3y8up" + }, + "outputs": [], + "source": [ + "# Create the FrozenLake-v1 environment using 4x4 map and non-slippery version\n", + "env = gym.make() # TODO use the correct parameters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ji_UrI5l2zzn" + }, + "source": [ + "### Solution" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jNxUbPMP0akP" + }, + "outputs": [], + "source": [ + "env = gym.make(\"FrozenLake-v1\", map_name=\"4x4\", is_slippery=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KASNViqL4tZn" + }, + "source": [ + "You can create your own custom grid like this:\n", + "\n", + "```python\n", + "desc=[\"SFFF\", \"FHFH\", \"FFFH\", \"HFFG\"]\n", + "gym.make('FrozenLake-v1', desc=desc, is_slippery=True)\n", + "```\n", + "\n", + "but we'll use the default environment for now." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SXbTfdeJ1Xi9" + }, + "source": [ + "### Let's see what the Environment looks like:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ZNPG0g_UGCfh" + }, + "outputs": [], + "source": [ + "# We create our environment with gym.make(\"\")\n", + "env.reset()\n", + "print(\"_____OBSERVATION SPACE_____ \\n\")\n", + "print(\"Observation Space\", env.observation_space)\n", + "print(\"Sample observation\", env.observation_space.sample()) # Get a random observation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2MXc15qFE0M9" + }, + "source": [ + "We see with `Observation Space Shape Discrete(16)` that the observation is a value representing the **agentโ€™s current position as current_row * nrows + current_col (where both the row and col start at 0)**. \n", + "\n", + "For example, the goal position in the 4x4 map can be calculated as follows: 3 * 4 + 3 = 15. The number of possible observations is dependent on the size of the map. **For example, the 4x4 map has 16 possible observations.**\n", + "\n", + "\n", + "For instance, this is what state = 0 looks like:\n", + "\n", + "\"FrozenLake\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "We5WqOBGLoSm" + }, + "outputs": [], + "source": [ + "print(\"\\n _____ACTION SPACE_____ \\n\")\n", + "print(\"Action Space Shape\", env.action_space.n)\n", + "print(\"Action Space Sample\", env.action_space.sample()) # Take a random action" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MyxXwkI2Magx" + }, + "source": [ + "The action space (the set of possible actions the agent can take) is discrete with 4 actions available ๐ŸŽฎ:\n", + "- 0: GO LEFT\n", + "- 1: GO DOWN\n", + "- 2: GO RIGHT\n", + "- 3: GO UP\n", + "\n", + "Reward function ๐Ÿ’ฐ:\n", + "- Reach goal: +1\n", + "- Reach hole: 0\n", + "- Reach frozen: 0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1pFhWblk3Awr" + }, + "source": [ + "## Create and Initialize the Q-table ๐Ÿ—„๏ธ\n", + "(๐Ÿ‘€ Step 1 of the pseudocode)\n", + "\n", + "\"Q-Learning\"\n", + "\n", + "\n", + "It's time to initialize our Q-table! To know how many rows (states) and columns (actions) to use, we need to know the action and observation space. OpenAI Gym provides us a way to do that: `env.action_space.n` and `env.observation_space.n`\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "y3ZCdluj3k0l" + }, + "outputs": [], + "source": [ + "state_space = \n", + "print(\"There are \", state_space, \" possible states\")\n", + "\n", + "action_space = \n", + "print(\"There are \", action_space, \" possible actions\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "rCddoOXM3UQH" + }, + "outputs": [], + "source": [ + "# Let's create our Qtable of size (state_space, action_space) and initialized each values at 0 using np.zeros\n", + "def initialize_q_table(state_space, action_space):\n", + " Qtable = \n", + " return Qtable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "9YfvrqRt3jdR" + }, + "outputs": [], + "source": [ + "Qtable_frozenlake = initialize_q_table(state_space, action_space)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "67OdoKL63eDD" + }, + "source": [ + "### Solution" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HuTKv3th3ohG" + }, + "outputs": [], + "source": [ + "state_space = env.observation_space.n\n", + "print(\"There are \", state_space, \" possible states\")\n", + "\n", + "action_space = env.action_space.n\n", + "print(\"There are \", action_space, \" possible actions\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lnrb_nX33fJo" + }, + "outputs": [], + "source": [ + "# Let's create our Qtable of size (state_space, action_space) and initialized each values at 0 using np.zeros\n", + "def initialize_q_table(state_space, action_space):\n", + " Qtable = np.zeros((state_space, action_space))\n", + " return Qtable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Y0WlgkVO3Jf9" + }, + "outputs": [], + "source": [ + "Qtable_frozenlake = initialize_q_table(state_space, action_space)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "flILKhBU3yZ7" + }, + "source": [ + "##Define the epsilon-greedy policy ๐Ÿค–\n", + "\n", + "Epsilon-Greedy is the training policy that handles the exploration/exploitation trade-off.\n", + "\n", + "The idea with Epsilon Greedy:\n", + "\n", + "- With *probability 1โ€Š-โ€Šษ›* : **we do exploitation** (aka our agent selects the action with the highest state-action pair value).\n", + "\n", + "- With *probability ษ›*: we do **exploration** (trying random action).\n", + "\n", + "And as the training goes, we progressively **reduce the epsilon value since we will need less and less exploration and more exploitation.**\n", + "\n", + "\"Q-Learning\"\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LjZSvhsD7_52" + }, + "source": [ + "Thanks to Sambit for finding a bug on the epsilon function ๐Ÿค—" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6Bj7x3in3_Pq" + }, + "outputs": [], + "source": [ + "def epsilon_greedy_policy(Qtable, state, epsilon):\n", + " # Randomly generate a number between 0 and 1\n", + " random_num = \n", + " # if random_num > greater than epsilon --> exploitation\n", + " if random_num > epsilon:\n", + " # Take the action with the highest value given a state\n", + " # np.argmax can be useful here\n", + " action = \n", + " # else --> exploration\n", + " else:\n", + " action = # Take a random action\n", + " \n", + " return action" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8R5ej1fS4P2V" + }, + "source": [ + "#### Solution" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cYxHuckr4LiG" + }, + "outputs": [], + "source": [ + "def epsilon_greedy_policy(Qtable, state, epsilon):\n", + " # Randomly generate a number between 0 and 1\n", + " random_int = random.uniform(0,1)\n", + " # if random_int > greater than epsilon --> exploitation\n", + " if random_int > epsilon:\n", + " # Take the action with the highest value given a state\n", + " # np.argmax can be useful here\n", + " action = np.argmax(Qtable[state])\n", + " # else --> exploration\n", + " else:\n", + " action = env.action_space.sample()\n", + " \n", + " return action" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Atll4Z774gri" + }, + "source": [ + "## Define the greedy policy ๐Ÿค–\n", + "Remember we have two policies since Q-Learning is an **off-policy** algorithm. This means we're using a **different policy for acting and updating the value function**.\n", + "\n", + "- Epsilon greedy policy (acting policy)\n", + "- Greedy policy (updating policy)\n", + "\n", + "Greedy policy will also be the final policy we'll have when the Q-learning agent will be trained. The greedy policy is used to select an action from the Q-table.\n", + "\n", + "\"Q-Learning\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "E3SCLmLX5bWG" + }, + "outputs": [], + "source": [ + "def greedy_policy(Qtable, state):\n", + " # Exploitation: take the action with the highest state, action value\n", + " action = \n", + " \n", + " return action" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "B2_-8b8z5k54" + }, + "source": [ + "#### Solution" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "se2OzWGW5kYJ" + }, + "outputs": [], + "source": [ + "def greedy_policy(Qtable, state):\n", + " # Exploitation: take the action with the highest state, action value\n", + " action = np.argmax(Qtable[state])\n", + " \n", + " return action" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hW80DealcRtu" + }, + "source": [ + "## Define the hyperparameters โš™๏ธ\n", + "The exploration related hyperparamters are some of the most important ones. \n", + "\n", + "- We need to make sure that our agent **explores enough the state space** in order to learn a good value approximation, in order to do that we need to have progressive decay of the epsilon.\n", + "- If you decrease too fast epsilon (too high decay_rate), **you take the risk that your agent is stuck**, since your agent didn't explore enough the state space and hence can't solve the problem." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Y1tWn0tycWZ1" + }, + "outputs": [], + "source": [ + "# Training parameters\n", + "n_training_episodes = 10000 # Total training episodes\n", + "learning_rate = 0.7 # Learning rate\n", + "\n", + "# Evaluation parameters\n", + "n_eval_episodes = 100 # Total number of test episodes\n", + "\n", + "# Environment parameters\n", + "env_id = \"FrozenLake-v1\" # Name of the environment\n", + "max_steps = 99 # Max steps per episode\n", + "gamma = 0.95 # Discounting rate\n", + "eval_seed = [] # The evaluation seed of the environment\n", + "\n", + "# Exploration parameters\n", + "max_epsilon = 1.0 # Exploration probability at start\n", + "min_epsilon = 0.05 # Minimum exploration probability \n", + "decay_rate = 0.0005 # Exponential decay rate for exploration prob" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cDb7Tdx8atfL" + }, + "source": [ + "## Step 6: Create the training loop method" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "paOynXy3aoJW" + }, + "outputs": [], + "source": [ + "def train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable):\n", + " for episode in range(n_training_episodes):\n", + " # Reduce epsilon (because we need less and less exploration)\n", + " epsilon = min_epsilon + (max_epsilon - min_epsilon)*np.exp(-decay_rate*episode)\n", + " # Reset the environment\n", + " state = env.reset()\n", + " step = 0\n", + " done = False\n", + "\n", + " # repeat\n", + " for step in range(max_steps):\n", + " # Choose the action At using epsilon greedy policy\n", + " action = \n", + "\n", + " # Take action At and observe Rt+1 and St+1\n", + " # Take the action (a) and observe the outcome state(s') and reward (r)\n", + " new_state, reward, done, info = \n", + "\n", + " # Update Q(s,a):= Q(s,a) + lr [R(s,a) + gamma * max Q(s',a') - Q(s,a)]\n", + " Qtable[state][action] = \n", + "\n", + " # If done, finish the episode\n", + " if done:\n", + " break\n", + " \n", + " # Our state is the new state\n", + " state = new_state\n", + " return Qtable" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Pnpk2ePoem3r" + }, + "source": [ + "#### Solution" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IyZaYbUAeolw" + }, + "outputs": [], + "source": [ + "def train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable):\n", + " for episode in tqdm(range(n_training_episodes)):\n", + " # Reduce epsilon (because we need less and less exploration)\n", + " epsilon = min_epsilon + (max_epsilon - min_epsilon)*np.exp(-decay_rate*episode)\n", + " # Reset the environment\n", + " state = env.reset()\n", + " step = 0\n", + " done = False\n", + "\n", + " # repeat\n", + " for step in range(max_steps):\n", + " # Choose the action At using epsilon greedy policy\n", + " action = epsilon_greedy_policy(Qtable, state, epsilon)\n", + "\n", + " # Take action At and observe Rt+1 and St+1\n", + " # Take the action (a) and observe the outcome state(s') and reward (r)\n", + " new_state, reward, done, info = env.step(action)\n", + "\n", + " # Update Q(s,a):= Q(s,a) + lr [R(s,a) + gamma * max Q(s',a') - Q(s,a)]\n", + " Qtable[state][action] = Qtable[state][action] + learning_rate * (reward + gamma * np.max(Qtable[new_state]) - Qtable[state][action]) \n", + "\n", + " # If done, finish the episode\n", + " if done:\n", + " break\n", + " \n", + " # Our state is the new state\n", + " state = new_state\n", + " return Qtable" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WLwKQ4tUdhGI" + }, + "source": [ + "## Train the Q-Learning agent ๐Ÿƒ" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "DPBxfjJdTCOH" + }, + "outputs": [], + "source": [ + "Qtable_frozenlake = train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable_frozenlake)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yVeEhUCrc30L" + }, + "source": [ + "## Let's see what our Q-Learning table looks like now ๐Ÿ‘€" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nmfchsTITw4q" + }, + "outputs": [], + "source": [ + "Qtable_frozenlake" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pUrWkxsHccXD" + }, + "source": [ + "## Define the evaluation method ๐Ÿ“" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jNl0_JO2cbkm" + }, + "outputs": [], + "source": [ + "def evaluate_agent(env, max_steps, n_eval_episodes, Q, seed):\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 Q: The Q-table\n", + " :param seed: The evaluation seed array (for taxi-v3)\n", + " \"\"\"\n", + " episode_rewards = []\n", + " for episode in tqdm(range(n_eval_episodes)):\n", + " if seed:\n", + " state = env.reset(seed=seed[episode])\n", + " else:\n", + " state = env.reset()\n", + " step = 0\n", + " done = False\n", + " total_rewards_ep = 0\n", + " \n", + " for step in range(max_steps):\n", + " # Take the action (index) that have the maximum expected future reward given that state\n", + " action = np.argmax(Q[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": "0jJqjaoAnxUo" + }, + "source": [ + "## Evaluate our Q-Learning agent ๐Ÿ“ˆ\n", + "- Normally you should have mean reward of 1.0\n", + "- It's relatively easy since the state space is really small (16). What you can try to do is [to replace with the slippery version](https://www.gymlibrary.dev/environments/toy_text/frozen_lake/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fAgB7s0HEFMm" + }, + "outputs": [], + "source": [ + "# Evaluate our Agent\n", + "mean_reward, std_reward = evaluate_agent(env, max_steps, n_eval_episodes, Qtable_frozenlake, eval_seed)\n", + "print(f\"Mean_reward={mean_reward:.2f} +/- {std_reward:.2f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yxaP3bPdg1DV" + }, + "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", + "\"Model\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kv0k1JQjpMq3" + }, + "source": [ + "Under the hood, the Hub uses git-based repositories (don't worry if you don't know what git is), which means you can update the model with new versions as you experiment and improve your agent." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QZ5LrR-joIHD" + }, + "source": [ + "#### Do not modify this code" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Jex3i9lZ8ksX" + }, + "outputs": [], + "source": [ + "%%capture\n", + "from huggingface_hub import HfApi, HfFolder, Repository\n", + "from huggingface_hub.repocard import metadata_eval_result, metadata_save\n", + "\n", + "from pathlib import Path\n", + "import datetime\n", + "import json" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Qo57HBn3W74O" + }, + "outputs": [], + "source": [ + "def record_video(env, Qtable, out_directory, fps=1):\n", + " images = [] \n", + " done = False\n", + " state = env.reset(seed=random.randint(0,500))\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 = np.argmax(Qtable[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", + "execution_count": null, + "metadata": { + "id": "pwsNrzB339aF" + }, + "outputs": [], + "source": [ + "def push_to_hub(repo_id, \n", + " model,\n", + " env,\n", + " video_fps=1,\n", + " local_repo_path=\"hub\",\n", + " commit_message=\"Push Q-Learning agent to Hub\",\n", + " token= None\n", + " ):\n", + " _, repo_name = repo_id.split(\"/\")\n", + "\n", + " eval_env = env\n", + " \n", + " # Step 1: Clone or create the repo\n", + " # Create the repo (or clone its content if it's nonempty)\n", + " api = HfApi()\n", + " \n", + " repo_url = api.create_repo(\n", + " repo_id=repo_id,\n", + " token=token,\n", + " private=False,\n", + " exist_ok=True,)\n", + " \n", + " # Git pull\n", + " repo_local_path = Path(local_repo_path) / repo_name\n", + " repo = Repository(repo_local_path, clone_from=repo_url, use_auth_token=True)\n", + " repo.git_pull()\n", + " \n", + " repo.lfs_track([\"*.mp4\"])\n", + "\n", + " # Step 1: Save the model\n", + " if env.spec.kwargs.get(\"map_name\"):\n", + " model[\"map_name\"] = env.spec.kwargs.get(\"map_name\")\n", + " if env.spec.kwargs.get(\"is_slippery\", \"\") == False:\n", + " model[\"slippery\"] = False\n", + "\n", + " print(model)\n", + " \n", + " \n", + " # Pickle the model\n", + " with open(Path(repo_local_path)/'q-learning.pkl', 'wb') as f:\n", + " pickle.dump(model, f)\n", + " \n", + " # Step 2: Evaluate the model and build JSON\n", + " mean_reward, std_reward = evaluate_agent(eval_env, model[\"max_steps\"], model[\"n_eval_episodes\"], model[\"qtable\"], model[\"eval_seed\"])\n", + "\n", + " # First get datetime\n", + " eval_datetime = datetime.datetime.now()\n", + " eval_form_datetime = eval_datetime.isoformat()\n", + "\n", + " evaluate_data = {\n", + " \"env_id\": model[\"env_id\"], \n", + " \"mean_reward\": mean_reward,\n", + " \"n_eval_episodes\": model[\"n_eval_episodes\"],\n", + " \"eval_datetime\": eval_form_datetime,\n", + " }\n", + " # Write a JSON file\n", + " with open(Path(repo_local_path) / \"results.json\", \"w\") as outfile:\n", + " json.dump(evaluate_data, outfile)\n", + "\n", + " # Step 3: Create the model card\n", + " # Env id\n", + " env_name = model[\"env_id\"]\n", + " if env.spec.kwargs.get(\"map_name\"):\n", + " env_name += \"-\" + env.spec.kwargs.get(\"map_name\")\n", + "\n", + " if env.spec.kwargs.get(\"is_slippery\", \"\") == False:\n", + " env_name += \"-\" + \"no_slippery\"\n", + "\n", + " metadata = {}\n", + " metadata[\"tags\"] = [\n", + " env_name,\n", + " \"q-learning\",\n", + " \"reinforcement-learning\",\n", + " \"custom-implementation\"\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", + " # **Q-Learning** Agent playing **{env_id}**\n", + " This is a trained model of a **Q-Learning** agent playing **{env_id}** .\n", + " \"\"\"\n", + "\n", + " model_card += \"\"\"\n", + " ## Usage\n", + " ```python\n", + " \"\"\"\n", + "\n", + " model_card += f\"\"\"model = load_from_hub(repo_id=\"{repo_id}\", filename=\"q-learning.pkl\")\n", + "\n", + " # Don't forget to check if you need to add additional attributes (is_slippery=False etc)\n", + " env = gym.make(model[\"env_id\"])\n", + "\n", + " evaluate_agent(env, model[\"max_steps\"], model[\"n_eval_episodes\"], model[\"qtable\"], model[\"eval_seed\"])\n", + " \"\"\"\n", + "\n", + " model_card +=\"\"\"\n", + " ```\n", + " \"\"\"\n", + "\n", + " readme_path = repo_local_path / \"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 4: Record a video\n", + " video_path = repo_local_path / \"replay.mp4\"\n", + " record_video(env, model[\"qtable\"], video_path, video_fps)\n", + " \n", + " # Push everything to hub\n", + " print(f\"Pushing repo {repo_name} to the Hugging Face Hub\")\n", + " repo.push_to_hub(commit_message=commit_message)\n", + "\n", + " print(f\"Your model is pushed to the hub. You can view your model here: {repo_url}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "81J6cet_ogSS" + }, + "source": [ + "### .\n", + "By using `package_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": [ + "from huggingface_hub import notebook_login\n", + "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`" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Gc5AfUeFo3xH" + }, + "source": [ + "3๏ธโƒฃ We're now ready to push our trained agent to the ๐Ÿค— Hub ๐Ÿ”ฅ using `package_to_hub()` function\n", + "\n", + "- Let's create **the model dictionary that contains the hyperparameters and the Q_table**." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "FiMqxqVHg0I4" + }, + "outputs": [], + "source": [ + "model = {\n", + " \"env_id\": env_id,\n", + " \"max_steps\": max_steps,\n", + " \"n_training_episodes\": n_training_episodes,\n", + " \"n_eval_episodes\": n_eval_episodes,\n", + " \"eval_seed\": eval_seed,\n", + "\n", + " \"learning_rate\": learning_rate,\n", + " \"gamma\": gamma,\n", + "\n", + " \"max_epsilon\": max_epsilon,\n", + " \"min_epsilon\": min_epsilon,\n", + " \"decay_rate\": decay_rate,\n", + "\n", + " \"qtable\": Qtable_frozenlake\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9kld-AEso3xH" + }, + "source": [ + "Let's fill the `package_to_hub` function:\n", + "- `repo_id`: the name of the Hugging Face Hub Repository that will be created/updated `\n", + "(repo_id = {username}/{repo_name})`\n", + "๐Ÿ’ก **A good name is {username}/q-{env_id}**\n", + "- `model`: our model dictionary containing the hyperparameters and the Qtable.\n", + "- `env`: the environment.\n", + "- `commit_message`: message of the commit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "5sBo2umnXpPd" + }, + "outputs": [], + "source": [ + "model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "RpOTtSt83kPZ" + }, + "outputs": [], + "source": [ + "username = \"\" # FILL THIS\n", + "repo_name = \"q-FrozenLake-v1-4x4-noSlippery\"\n", + "push_to_hub(\n", + " repo_id=f\"{username}/{repo_name}\",\n", + " model=model,\n", + " env=env)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E2875IGsprzq" + }, + "source": [ + "Congrats ๐Ÿฅณ you've just implemented from scratch, trained and uploaded your first Reinforcement Learning agent. \n", + "FrozenLake-v1 no_slippery is very simple environment, let's try an harder one ๐Ÿ”ฅ." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "18lN8Bz7yvLt" + }, + "source": [ + "# Part 2: Taxi-v3 ๐Ÿš–\n", + "\n", + "## Create and understand [Taxi-v3 ๐Ÿš•](https://www.gymlibrary.dev/environments/toy_text/taxi/)\n", + "---\n", + "\n", + "๐Ÿ’ก A good habit when you start to use an environment is to check its documentation \n", + "\n", + "๐Ÿ‘‰ https://www.gymlibrary.dev/environments/toy_text/taxi/\n", + "\n", + "---\n", + "\n", + "In Taxi-v3 ๐Ÿš•, there are four designated locations in the grid world indicated by R(ed), G(reen), Y(ellow), and B(lue). When the episode starts, the taxi starts off at a random square and the passenger is at a random location. The taxi drives to the passengerโ€™s location, picks up the passenger, drives to the passengerโ€™s destination (another one of the four specified locations), and then drops off the passenger. Once the passenger is dropped off, the episode ends.\n", + "\n", + "\n", + "\"Taxi\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gL0wpeO8gpej" + }, + "outputs": [], + "source": [ + "env = gym.make(\"Taxi-v3\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gBOaXgtsrmtT" + }, + "source": [ + "There are **500 discrete states since there are 25 taxi positions, 5 possible locations of the passenger** (including the case when the passenger is in the taxi), and **4 destination locations.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_TPNaGSZrgqA" + }, + "outputs": [], + "source": [ + "state_space = env.observation_space.n\n", + "print(\"There are \", state_space, \" possible states\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "CdeeZuokrhit" + }, + "outputs": [], + "source": [ + "action_space = env.action_space.n\n", + "print(\"There are \", action_space, \" possible actions\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "R1r50Advrh5Q" + }, + "source": [ + "The action space (the set of possible actions the agent can take) is discrete with **6 actions available ๐ŸŽฎ**:\n", + "- 0: move south\n", + "- 1: move north\n", + "- 2: move east\n", + "- 3: move west\n", + "- 4: pickup passenger\n", + "- 5: drop off passenger\n", + "\n", + "Reward function ๐Ÿ’ฐ:\n", + "- -1 per step unless other reward is triggered.\n", + "- +20 delivering passenger.\n", + "- -10 executing โ€œpickupโ€ and โ€œdrop-offโ€ actions illegally." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "US3yDXnEtY9I" + }, + "outputs": [], + "source": [ + "# Create our Q table with state_size rows and action_size columns (500x6)\n", + "Qtable_taxi = initialize_q_table(state_space, action_space)\n", + "print(Qtable_taxi)\n", + "print(\"Q-table shape: \", Qtable_taxi .shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gUMKPH0_LJyH" + }, + "source": [ + "## Define the hyperparameters โš™๏ธ\n", + "โš  DO NOT MODIFY EVAL_SEED: the eval_seed array **allows us to evaluate your agent with the same taxi starting positions for every classmate**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "AB6n__hhg7YS" + }, + "outputs": [], + "source": [ + "# Training parameters\n", + "n_training_episodes = 25000 # Total training episodes\n", + "learning_rate = 0.7 # Learning rate\n", + "\n", + "# Evaluation parameters\n", + "n_eval_episodes = 100 # Total number of test episodes\n", + "\n", + "# DO NOT MODIFY EVAL_SEED\n", + "eval_seed = [16,54,165,177,191,191,120,80,149,178,48,38,6,125,174,73,50,172,100,148,146,6,25,40,68,148,49,167,9,97,164,176,61,7,54,55,\n", + " 161,131,184,51,170,12,120,113,95,126,51,98,36,135,54,82,45,95,89,59,95,124,9,113,58,85,51,134,121,169,105,21,30,11,50,65,12,43,82,145,152,97,106,55,31,85,38,\n", + " 112,102,168,123,97,21,83,158,26,80,63,5,81,32,11,28,148] # Evaluation seed, this ensures that all classmates agents are trained on the same taxi starting position\n", + " # Each seed has a specific starting state\n", + "\n", + "# Environment parameters\n", + "env_id = \"Taxi-v3\" # Name of the environment\n", + "max_steps = 99 # Max steps per episode\n", + "gamma = 0.95 # Discounting rate\n", + "\n", + "# Exploration parameters\n", + "max_epsilon = 1.0 # Exploration probability at start\n", + "min_epsilon = 0.05 # Minimum exploration probability \n", + "decay_rate = 0.005 # Exponential decay rate for exploration prob\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1TMORo1VLTsX" + }, + "source": [ + "## Train our Q-Learning agent ๐Ÿƒ" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MLNwkNDb14h2" + }, + "outputs": [], + "source": [ + "Qtable_taxi = train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable_taxi)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "WwP3Y2z2eS-K" + }, + "outputs": [], + "source": [ + "Qtable_taxi" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wPdu0SueLVl2" + }, + "source": [ + "## Create a model dictionary ๐Ÿ’พ and publish our trained model on the Hub ๐Ÿ”ฅ\n", + "- We create a model dictionary that will contain all the training hyperparameters for reproducibility and the Q-Table.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0a1FpE_3hNYr" + }, + "outputs": [], + "source": [ + "model = {\n", + " \"env_id\": env_id,\n", + " \"max_steps\": max_steps,\n", + " \"n_training_episodes\": n_training_episodes,\n", + " \"n_eval_episodes\": n_eval_episodes,\n", + " \"eval_seed\": eval_seed,\n", + "\n", + " \"learning_rate\": learning_rate,\n", + " \"gamma\": gamma,\n", + "\n", + " \"max_epsilon\": max_epsilon,\n", + " \"min_epsilon\": min_epsilon,\n", + " \"decay_rate\": decay_rate,\n", + "\n", + " \"qtable\": Qtable_taxi\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dhQtiQozhOn1" + }, + "outputs": [], + "source": [ + "username = \"\" # FILL THIS\n", + "repo_name = \"q-Taxi-v3\"\n", + "push_to_hub(\n", + " repo_id=f\"{username}/{repo_name}\",\n", + " model=model,\n", + " env=env)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZgSdjgbIpRti" + }, + "source": [ + "Now that's on the Hub, you can compare the results of your Taxi-v3 with your classmates using the leaderboard ๐Ÿ† ๐Ÿ‘‰ https://huggingface.co/spaces/huggingface-projects/Deep-Reinforcement-Learning-Leaderboard\n", + "\n", + "\"Taxi" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bzgIO70c0bu2" + }, + "source": [ + "# Part 3: Load from Hub ๐Ÿ”ฝ\n", + "\n", + "What's amazing with Hugging Face Hub ๐Ÿค— is that you can easily load powerful models from the community.\n", + "\n", + "Loading a saved model from the Hub is really easy.\n", + "1. You go https://huggingface.co/models?other=q-learning to see the list of all the q-learning saved models.\n", + "2. You select one and copy its repo_id\n", + "\n", + "\"Copy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gTth6thRoC6X" + }, + "source": [ + "3. Then we just need to use `load_from_hub` with:\n", + "- The repo_id\n", + "- The filename: the saved model inside the repo." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EtrfoTaBoNrd" + }, + "source": [ + "#### Do not modify this code" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Eo8qEzNtCaVI" + }, + "outputs": [], + "source": [ + "from urllib.error import HTTPError\n", + "\n", + "from huggingface_hub import hf_hub_download\n", + "\n", + "\n", + "def load_from_hub(repo_id: str, filename: str) -> str:\n", + " \"\"\"\n", + " Download a model from Hugging Face Hub.\n", + " :param repo_id: id of the model repository from the Hugging Face Hub\n", + " :param filename: name of the model zip file from the repository\n", + " \"\"\"\n", + " try:\n", + " from huggingface_hub import cached_download, hf_hub_url\n", + " except ImportError:\n", + " raise ImportError(\n", + " \"You need to install huggingface_hub to use `load_from_hub`. \"\n", + " \"See https://pypi.org/project/huggingface-hub/ for installation.\"\n", + " )\n", + "\n", + " # Get the model from the Hub, download and cache the model on your local disk\n", + " pickle_model = hf_hub_download(\n", + " repo_id=repo_id,\n", + " filename=filename\n", + " )\n", + "\n", + " with open(pickle_model, 'rb') as f:\n", + " downloaded_model_file = pickle.load(f)\n", + " \n", + " return downloaded_model_file" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "b_sM2gNioPZH" + }, + "source": [ + "### ." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JUm9lz2gCQcU" + }, + "outputs": [], + "source": [ + "model = load_from_hub(repo_id=\"ThomasSimonini/q-Taxi-v3\", filename=\"q-learning.pkl\") # Try to use another model\n", + "\n", + "print(model)\n", + "env = gym.make(model[\"env_id\"])\n", + "\n", + "evaluate_agent(env, model[\"max_steps\"], model[\"n_eval_episodes\"], model[\"qtable\"], model[\"eval_seed\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "O7pL8rg1MulN" + }, + "outputs": [], + "source": [ + "model = load_from_hub(repo_id=\"ThomasSimonini/q-FrozenLake-v1-no-slippery\", filename=\"q-learning.pkl\") # Try to use another model\n", + "\n", + "env = gym.make(model[\"env_id\"], is_slippery=False)\n", + "\n", + "evaluate_agent(env, model[\"max_steps\"], model[\"n_eval_episodes\"], model[\"qtable\"], model[\"eval_seed\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BQAwLnYFPk-s" + }, + "source": [ + "## Some additional challenges ๐Ÿ†\n", + "The best way to learn **is to try things by your own**! As you saw, the current agent is not doing great. As a first suggestion, you can train for more steps. With 1,000,000 steps, we saw some great results! \n", + "\n", + "In the [Leaderboard](https://huggingface.co/spaces/chrisjay/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.\n", + "* **Push your new trained model** on the Hub ๐Ÿ”ฅ\n", + "\n", + "Are walking on ice and driving taxis too boring to you? Try to **change the environment**, why not using FrozenLake-v1 slippery version? Check how they work [using the gym documentation](https://www.gymlibrary.dev/) and have fun ๐ŸŽ‰." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p-fW-EU5WejJ" + }, + "source": [ + "_____________________________________________________________________\n", + "Congrats ๐Ÿฅณ, you've just implemented, trained, and uploaded your first Reinforcement Learning agent.\n", + "\n", + "Understanding Q-Learning is an **important step to understanding value-based methods.**\n", + "\n", + "In the next Unit with Deep Q-Learning, we'll see that creating and updating a Q-table was a good strategy โ€” **however, this is not scalable.**\n", + "\n", + "For instance, imagine you create an agent that learns to play Doom. \n", + "\n", + "\"Doom\"/\n", + "\n", + "Doom is a large environment with a huge state space (millions of different states). Creating and updating a Q-table for that environment would not be efficient. \n", + "\n", + "That's why we'll study, in the next unit, Deep Q-Learning, an algorithm **where we use a neural network that approximates, given a state, the different Q-values for each action.**\n", + "\n", + "\"Environments\"/\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BjLhT70TEZIn" + }, + "source": [ + "See you on [Unit 3](https://github.com/huggingface/deep-rl-class/tree/main/unit2#unit-2-introduction-to-q-learning)! ๐Ÿ”ฅ\n", + "TODO CHANGE LINK\n", + "## Keep learning, stay awesome ๐Ÿค—" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [ + "4i6tjI2tHQ8j", + "Y-mo_6rXIjRi", + "EtrfoTaBoNrd", + "BjLhT70TEZIn" + ], + "private_outputs": true, + "provenance": [] + }, + "gpuClass": "standard", + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/notebooks/unit3/unit3.ipynb b/notebooks/unit3/unit3.ipynb new file mode 100644 index 0000000..019653e --- /dev/null +++ b/notebooks/unit3/unit3.ipynb @@ -0,0 +1,794 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "k7xBVPzoXxOg" + }, + "source": [ + "# Unit 3: Deep Q-Learning with Atari Games ๐Ÿ‘พ using RL Baselines3 Zoo\n", + "\n", + "\"Unit\n", + "\n", + "In this notebook, **you'll train a Deep Q-Learning agent** playing Space Invaders using [RL Baselines3 Zoo](https://github.com/DLR-RM/rl-baselines3-zoo), a training framework based on [Stable-Baselines3](https://stable-baselines3.readthedocs.io/en/master/) that provides scripts for training, evaluating agents, tuning hyperparameters, plotting results and recording videos.\n", + "\n", + "We're using the [RL-Baselines-3 Zoo integration, a vanilla version of Deep Q-Learning](https://stable-baselines3.readthedocs.io/en/master/modules/dqn.html) with no extensions such as Double-DQN, Dueling-DQN, and Prioritized Experience Replay.\n", + "\n", + "โฌ‡๏ธ Here is an example of what **you will achieve** โฌ‡๏ธ" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "J9S713biXntc" + }, + "outputs": [], + "source": [ + "%%html\n", + "" + ] + }, + { + "cell_type": "markdown", + "source": [ + "TODO: ADD TEXT LIVE INFO\n", + "\n", + "TODO: ADD IF YOU HAVE QUESTIONS\n", + "\n", + "\n", + "###๐ŸŽฎ Environments: \n", + "- SpacesInvadersNoFrameskip-v4 \n", + "\n", + "###๐Ÿ“š RL-Library: \n", + "- [RL-Baselines3-Zoo](https://github.com/DLR-RM/rl-baselines3-zoo)" + ], + "metadata": { + "id": "ykJiGevCMVc5" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wciHGjrFYz9m" + }, + "source": [ + "## Objectives of this notebook ๐Ÿ†\n", + "At the end of the notebook, you will:\n", + "- Be able to understand deeper **how RL Baselines3 Zoo works**.\n", + "- Be able to **push your trained agent and the code to the Hub** with a nice video replay and an evaluation score ๐Ÿ”ฅ.\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## This notebook is from Deep Reinforcement Learning Course\n", + "\"Deep" + ], + "metadata": { + "id": "TsnP0rjxMn1e" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nw6fJHIAZd-J" + }, + "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": "0vgANIBBZg1p" + }, + "source": [ + "## Prerequisites ๐Ÿ—๏ธ\n", + "Before diving into the notebook, you need to:\n", + "\n", + "๐Ÿ”ฒ ๐Ÿ“š **Study Deep Q-Learning by reading Unit 3** ๐Ÿค— ADD LINK " + ] + }, + { + "cell_type": "markdown", + "source": [ + "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": "7kszpGFaRVhq" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QR0jZtYreSI5" + }, + "source": [ + "# Let's train a Deep Q-Learning agent playing Atari' Space Invaders ๐Ÿ‘พ and upload it to the Hub." + ] + }, + { + "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", + "!pip3 install pyvirtualdisplay" + ] + }, + { + "cell_type": "code", + "source": [ + "# Additional dependencies for RL Baselines3 Zoo\n", + "!apt-get install swig cmake freeglut3-dev " + ], + "metadata": { + "id": "fWyKJCy_NJBX" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "!pip install pyglet==1.5.1" + ], + "metadata": { + "id": "C5LwHrISW7Q5" + }, + "execution_count": null, + "outputs": [] + }, + { + "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": "ww5PQH1gNLI4" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mYIMvl5X9NAu" + }, + "source": [ + "## Clone RL-Baselines3 Zoo Repo ๐Ÿ“š\n", + "You can now directly install from python package `pip install rl_zoo3` but since we want **the full installation with extra environments and dependencies** we're going to clone `RL-Baselines3-Zoo` repository and install from source." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "eu5ZDPZ09VNQ" + }, + "outputs": [], + "source": [ + "!git clone https://github.com/DLR-RM/rl-baselines3-zoo" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HCIoSbvbfAQh" + }, + "source": [ + "## Install dependencies ๐Ÿ”ฝ\n", + "We can now install the dependencies RL-Baselines3 Zoo needs (this can take 5min โฒ)\n", + "\n", + "But we'll also install:\n", + "- `huggingface_sb3`: Additional code for Stable-baselines3 to load and upload models from the Hugging Face ๐Ÿค— Hub." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "s2QsFAk29h-D" + }, + "outputs": [], + "source": [ + "%cd /content/rl-baselines3-zoo/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "3QaOS7Xj9j1s" + }, + "outputs": [], + "source": [ + "!pip install -r requirements.txt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "RLRGKFR39l9s" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install huggingface_sb3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5iPgzluo9z-u" + }, + "source": [ + "## Train our Deep Q-Learning Agent to Play Space Invaders ๐Ÿ‘พ\n", + "\n", + "To train an agent with RL-Baselines3-Zoo, we just need to do two things:\n", + "1. We define the hyperparameters in `rl-baselines3-zoo/hyperparams/dqn.yml`\n", + "\n", + "\"DQN\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_VjblFSVDQOj" + }, + "source": [ + "Here we see that:\n", + "- We use the `Atari Wrapper` that preprocess the input (Frame reduction ,grayscale, stack 4 frames)\n", + "- We use `CnnPolicy`, since we use Convolutional layers to process the frames\n", + "- We train it for 10 million `n_timesteps` \n", + "- Memory (Experience Replay) size is 100000, aka the amount of experience steps you saved to train again your agent with.\n", + "\n", + "๐Ÿ’ก My advice is to **reduce the training timesteps to 1M,** which will take about 90 minutes on a P100. `!nvidia-smi` will tell you what GPU you're using. At 10 million steps, this will take about 9 hours, which could likely result in Colab timing out. I recommend running this on your local computer (or somewhere else). Just click on: `File>Download`. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5qTkbWrkECOJ" + }, + "source": [ + "In terms of hyperparameters optimization, my advice is to focus on these 3 hyperparameters:\n", + "- `learning_rate`\n", + "- `buffer_size (Experience Memory size)`\n", + "- `batch_size`\n", + "\n", + "As a good practice, you need to **check the documentation to understand what each hyperparameters does**: https://stable-baselines3.readthedocs.io/en/master/modules/dqn.html#parameters\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Hn8bRTHvERRL" + }, + "source": [ + "2. We run `train.py` and save the models on `logs` folder ๐Ÿ“" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Xr1TVW4xfbz3" + }, + "outputs": [], + "source": [ + "!python train.py --algo ________ --env SpaceInvadersNoFrameskip-v4 -f _________" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SeChoX-3SZfP" + }, + "source": [ + "#### Solution" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PuocgdokSab9" + }, + "outputs": [], + "source": [ + "!python train.py --algo dqn --env SpaceInvadersNoFrameskip-v4 -f logs/" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_dLomIiMKQaf" + }, + "source": [ + "## Let's evaluate our agent ๐Ÿ‘€\n", + "- RL-Baselines3-Zoo provides `enjoy.py` to evaluate our agent.\n", + "- Let's evaluate it for 5000 timesteps ๐Ÿ”ฅ" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "co5um_KeKbBJ" + }, + "outputs": [], + "source": [ + "!python enjoy.py --algo dqn --env SpaceInvadersNoFrameskip-v4 --no-render --n-timesteps _________ --folder logs/" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Q24K1tyWSj7t" + }, + "source": [ + "#### Solution" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "P_uSmwGRSk0z" + }, + "outputs": [], + "source": [ + "!python enjoy.py --algo dqn --env SpaceInvadersNoFrameskip-v4 --no-render --n-timesteps 5000 --folder logs/" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "liBeTltiHJtr" + }, + "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", + "\"Space" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ezbHS1q3HYVV" + }, + "source": [ + "By using `rl_zoo3.push_to_hub.py` **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/chrisjay/Deep-Reinforcement-Learning-Leaderboard" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XMSeZRBiHk6X" + }, + "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**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9ToyuaYwHmxG" + }, + "source": [ + "![image.png](data:image/png;base64,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T2/HkCE5rF+/juefe565c+fSvn372tPEiedpUxvYuXzlsTyw7xV5ed0rBxr11PCM4Xzv9AngOwMKr/eg6otYg7VeDeMZGTkX15YgzOTxSp6a5D39oKxyT86n/eu1gZ40BQiR6t3cZ3dnf+XlO2tQExF5+2haTeQEubm5AEyZMgVN05gzxxE5zZ49hwkTJrhFRs7dQ38PiqqqamVx+6uqttZYb9/X7ti5S1144YXq6quvVkeOHlNVXuurqmtsVbVavmKlAtR5XbqoJUuXurYpPl2i7ho/XmVlZaut27a59p8+Y4bq3qOHytu0WVVVW9WRo8fUseMnatK1qjVrv1ft27dX99//gCotK1dV1VY1b958Bah58+a7tmto+t5/zvyqA+TnXO60paVl6t5771PJycnqjVmzlMlkVpaqajVjxgwFqBdeeEFVV1vVofx8dfXVV6uRI0eq/PzDqtpqVSaTWT3+xBPq8ssvVwcPHVJ79+5VF110kbrjjjtUUXGxOnWqUI0ZM0bdcsstKis7W/3000+q2mpVL730kmrfvr1av2GDqrbaVLXV6mFPFRaq/MNHXP6ePXvU4MFD1MiRI1XByVOq2mpVs2b9WwHq/gceUKdLSlS11abMFosyW6rU6ZISNX78eJWcnKzmv/2Oo75Wm6o0mVRVtVWdLilV48ePV1nZ2Wrbtu2OvKutasaMV1WPHj3Ups2b1YkTBWrYsOFqwoQJ6nRJqbJabcpktiiTyayqrTb10087VFZ2tnr66adVVVW1slptqtJkVmazRVmtNp+/EwWO9IYNG65OnChQVptj+dJly1RycrK68847VVFRsbLabOrw4SNq5MiRqkuXLiovL09ZrTa1cqXjPMzNzVUlJaXqgQceUF26dFH//e47R1o2m6tN5s2fr6qtVmW12dTGjRtVjx491Guvv66sNpv6accOlZ2drQYPGaI2bNigrDabKiwqUrfddptKTk5Wa9asUVabTdlsdle67r7Nx6/f2gL4tdYewA/izx7Aj4S1+/r2CFp7AD+4PxXAj5BV7lbV66u6rArgB7CRYsqUKQpQU6ZMUXPmzFGAmjNnToRzcWDweTbppcwqQESklMJms2G1WtHp9S6FxGs71x1djf8///M/XHTRxa5lx44dY/u2beTkDCE1NZVTp04BkNElg1/27OHAgf1kZmbSunVr7HY7R48e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vffdx7hbb0Nv0HM4P585c+Zw3fXXc/XVV9OqZStA8dJLL9GmTWuGDMmhQ4f2bNyYxzvvvI3ZbMZmt7N2zVpmzJjOhRcOJDk5mcJThSxevJhOnToxbNhwYgwxAKxatdL1zkjnzucFf0G5lN3Lj4R1UsctaySz959dY1awASUIM9nwI6y6I4boo/Z4OcVp9uzZTJhwN84GueLyy4Fakbr88stdDRXue1BYvGbx1ffnmt3nNuuvtKxcvf322yozM1PV1Ea1atVKvfDCn1VhUbGyuM3imzo1t3aWn3PWn6VKLVm6TA0aNMi1/3lduqi/v/SSKj5doipNJjV9xgzVqlUrBahBgwapjz76WE2cNEkNGzbMNbuvpLRMPfXUH1xpjBw5Up0oOKksVdV1pu89g6/SZG5Qfu5/JTWz+LKystX2n35yze5b4VZv56y/SpNZvf32O6pnz16u8mRm9lPz5s13zZCrrraq1157TQHqoosuUnv37lXV1VZVcPKkGjlypALU73//lDJbLKq62uqYqedmq6qr1cJFi1TPXo48evbqpea//bb6/VNPecwErLba1Pbt29UNN9zgcexeeullVVpWpixV1erDhQtVZr9+rvWDBg1SS5d945qluXTZMjVo0K9c67t06aJeeulldbqkVB09dlzlPvecqy0BddVVV6sffvhBVVtt6pdf9qrfPvigSk5Odq2/+eab1d69+1R1zcy9aq+ZfN+vW6eys7Nd2y9Y8J6yWm2qoOCkevLJJ11ptWrVSv3pT39SxcWnXfu6z+KzWh0z6U6ePKXGjh2runTpotauXausVpvaf+CAuvPOO115JCcnqwkTJqg9v/yirNbaWXz33XefKisvd83Sy83NVYBauXKlx+y9QH82Hz/QLL/6Z/3VP8uvPtuAWYDNNMsvErP+IjPLr5FnAargZv0pj+W1vl+rAvheM+f8zfrbv3+/grpn6zln93333XcBtwmWiH7N3G63U1JSgt1uJzk5mdjY2KD3Ly0pQeEY3zIYDB53HJUmExaLmaSkZGIMBmfA4xPqlpWVU1VlITkpmVij0TVpw26zU1JaimP2WAoGvd5zR2dONb7JVInZbCE5OQmDIcatJGHcorphtVopKysDIDU1FZ2u/vemg73/rK6qorSsjJTkZGJiY+u8gysvL8fibDfnsavZwa7slJwuAc0xW02n13lk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)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9O6FI0F8HnzE" + }, + "source": [ + "- Copy the token \n", + "- Run the cell below and past the token" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ppu9yePwHrZX" + }, + "outputs": [], + "source": [ + "from huggingface_hub import notebook_login # To log to our Hugging Face account to be able to upload models to the Hub.\n", + "notebook_login()\n", + "!git config --global credential.helper store" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2RVEdunPHs8B" + }, + "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`" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dSLwdmvhHvjw" + }, + "source": [ + "3๏ธโƒฃ We're now ready to push our trained agent to the ๐Ÿค— Hub ๐Ÿ”ฅ" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PW436XnhHw1H" + }, + "source": [ + "Let's run push_to_hub.py file to upload our trained agent to the Hub.\n", + "\n", + "`--repo-name `: The name of the repo\n", + "\n", + "`-orga`: Your Hugging Face username\n", + "\n", + "\"Select" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ygk2sEktTDEw" + }, + "outputs": [], + "source": [ + "!python -m rl_zoo3.push_to_hub --algo dqn --env SpaceInvadersNoFrameskip-v4 --repo-name _____________________ -orga _____________________ -f logs/" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "otgpa0rhS9wR" + }, + "source": [ + "#### Solution" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_HQNlAXuEhci" + }, + "outputs": [], + "source": [ + "!python -m rl_zoo3.push_to_hub --algo dqn --env SpaceInvadersNoFrameskip-v4 --repo-name dqn-SpaceInvadersNoFrameskip-v4 -orga ThomasSimonini -f logs/" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0D4F5zsTTJ-L" + }, + "source": [ + "###." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ff89kd2HL1_s" + }, + "source": [ + "Congrats ๐Ÿฅณ you've just trained and uploaded your first Deep Q-Learning agent using RL-Baselines-3 Zoo. The script above should have displayed a link to a model repository such as https://huggingface.co/ThomasSimonini/dqn-SpaceInvadersNoFrameskip-v4. When you go to this link, you can:\n", + "\n", + "- See a **video preview of your agent** at the right. \n", + "- Click \"Files and versions\" to see all the files in the repository.\n", + "- Click \"Use in stable-baselines3\" to get a code snippet that shows how to load the model.\n", + "- A model card (`README.md` file) which gives a description of the model and the hyperparameters you used.\n", + "\n", + "Under the hood, the Hub uses git-based repositories (don't worry if you don't know what git is), which means you can update the model with new versions as you experiment and improve your agent.\n", + "\n", + "**Compare the results of your agents with your classmates** using the [leaderboard](https://huggingface.co/spaces/huggingface-projects/Deep-Reinforcement-Learning-Leaderboard) ๐Ÿ†" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fyRKcCYY-dIo" + }, + "source": [ + "## Load a powerful trained model ๐Ÿ”ฅ\n", + "- The Stable-Baselines3 team uploaded **more than 150 trained Deep Reinforcement Learning agents on the Hub**.\n", + "\n", + "You can find them here: ๐Ÿ‘‰ https://huggingface.co/sb3\n", + "\n", + "Some examples:\n", + "- Asteroids: https://huggingface.co/sb3/dqn-AsteroidsNoFrameskip-v4\n", + "- Beam Rider: https://huggingface.co/sb3/dqn-BeamRiderNoFrameskip-v4\n", + "- Breakout: https://huggingface.co/sb3/dqn-BreakoutNoFrameskip-v4\n", + "- Road Runner: https://huggingface.co/sb3/dqn-RoadRunnerNoFrameskip-v4\n", + "\n", + "Let's load an agent playing Beam Rider: https://huggingface.co/sb3/dqn-BeamRiderNoFrameskip-v4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "B-9QVFIROI5Y" + }, + "outputs": [], + "source": [ + "%%html\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7ZQNY_r6NJtC" + }, + "source": [ + "1. We download the model using `rl_zoo3.load_from_hub`, and place it in a new folder that we can call `rl_trained`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OdBNZHy0NGTR" + }, + "outputs": [], + "source": [ + "# Download model and save it into the logs/ folder\n", + "!python -m rl_zoo3.load_from_hub --algo dqn --env BeamRiderNoFrameskip-v4 -orga sb3 -f rl_trained/" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LFt6hmWsNdBo" + }, + "source": [ + "2. Let's evaluate if for 5000 timesteps" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "aOxs0rNuN0uS" + }, + "outputs": [], + "source": [ + "!python enjoy.py --algo dqn --env BeamRiderNoFrameskip-v4 -n 5000 -f rl_trained/" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kxMDuDfPON57" + }, + "source": [ + "Why not trying to train your own **Deep Q-Learning Agent playing BeamRiderNoFrameskip-v4? ๐Ÿ†.**\n", + "\n", + "If you want to try, check https://huggingface.co/sb3/dqn-BeamRiderNoFrameskip-v4#hyperparameters **in the model card, you have the hyperparameters of the trained agent.**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xL_ZtUgpOuY6" + }, + "source": [ + "But finding hyperparameters can be a daunting task. Fortunately, we'll see in the next Unit, how we can **use Optuna for optimizing the Hyperparameters ๐Ÿ”ฅ.**\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-pqaco8W-huW" + }, + "source": [ + "## Some additional challenges ๐Ÿ†\n", + "The best way to learn **is to try things by your own**!\n", + "\n", + "In the [Leaderboard](https://huggingface.co/spaces/chrisjay/Deep-Reinforcement-Learning-Leaderboard) you will find your agents. Can you get to the top?\n", + "\n", + "Here's a list of environments you can try to train your agent with:\n", + "- BeamRiderNoFrameskip-v4\n", + "- BreakoutNoFrameskip-v4 \n", + "- EnduroNoFrameskip-v4\n", + "- PongNoFrameskip-v4\n", + "\n", + "Also, **if you want to learn to implement Deep Q-Learning by yourself**, you definitely should look at CleanRL implementation: https://github.com/vwxyzjn/cleanrl/blob/master/cleanrl/dqn_atari.py\n", + "\n", + "\"Environments\"/" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "paS-XKo4-kmu" + }, + "source": [ + "________________________________________________________________________\n", + "Congrats on finishing this chapter!\n", + "\n", + "If youโ€™re still feel confused with all these elements...it's totally normal! **This was the same for me and for all people who studied RL.**\n", + "\n", + "Take time to really **grasp the material before continuing and try the additional challenges**. Itโ€™s important to master these elements and having a solid foundations.\n", + "\n", + "In the next unit, **weโ€™re going to learn about [Optuna](https://optuna.org/)**. One of the most critical task in Deep Reinforcement Learning is to find a good set of training hyperparameters. And Optuna is a library that helps you to automate the search.\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5WRx7tO7-mvC" + }, + "source": [ + "\n", + "\n", + "### This is a course built with you ๐Ÿ‘ท๐Ÿฟโ€โ™€๏ธ\n", + "\n", + "Finally, we want to improve and update the course iteratively with your feedback. If you have some, please fill this form ๐Ÿ‘‰ https://forms.gle/3HgA7bEHwAmmLfwh9\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)." + ] + }, + { + "cell_type": "markdown", + "source": [ + "See you on [Bonus unit 2](https://github.com/huggingface/deep-rl-class/tree/main/unit2#unit-2-introduction-to-q-learning)! ๐Ÿ”ฅ TODO CHANGE LINK" + ], + "metadata": { + "id": "Kc3udPT-RcXc" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fS3Xerx0fIMV" + }, + "source": [ + "### Keep Learning, Stay Awesome ๐Ÿค—" + ] + } + ], + "metadata": { + "colab": { + "private_outputs": true, + "provenance": [] + }, + "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.10.6" + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "accelerator": "GPU", + "gpuClass": "standard" + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/units/en/unitbonus2/hands-on.mdx b/units/en/unitbonus2/hands-on.mdx index ef7c709..ac942a5 100644 --- a/units/en/unitbonus2/hands-on.mdx +++ b/units/en/unitbonus2/hands-on.mdx @@ -1 +1,3 @@ # Hands-on [[hands-on]] + +Now that you've learned to use Optuna, **why not going back to our Deep Q-Learning hands-on and implement Optuna to find the best training hyperparameters?** diff --git a/units/en/unitbonus2/introduction.mdx b/units/en/unitbonus2/introduction.mdx index e3bb200..c586d68 100644 --- a/units/en/unitbonus2/introduction.mdx +++ b/units/en/unitbonus2/introduction.mdx @@ -1,7 +1,7 @@ # Introduction [[introduction]] -One of the most critical task in Deep Reinforcement Learning is to find a good set of training hyperparameters. +One of the most critical task in Deep Reinforcement Learning is to **find a good set of training hyperparameters**. Optuna Logo -Optuna is a library that helps you to automate the search. In this Unit, we'll study a little bit of the theory behind automatic hyperparameter tuning. We'll then try to optimize the parameters manually and then see how to automate the search using Optuna. +[Optuna](https://optuna.org/) is a library that helps you to automate the search. In this Unit, we'll study a **little bit of the theory behind automatic hyperparameter tuning**. We'll then try to optimize the last unit DQN's parameters manually and then **see how to automate the search using Optuna**. diff --git a/units/en/unitbonus2/optuna.mdx b/units/en/unitbonus2/optuna.mdx index 1b116b9..d01d8cc 100644 --- a/units/en/unitbonus2/optuna.mdx +++ b/units/en/unitbonus2/optuna.mdx @@ -1,5 +1,12 @@ # Optuna Tutorial [[optuna]] +The content below comes from [Antonin's Raffin ICRA 2022 presentations](https://araffin.github.io/tools-for-robotic-rl-icra2022/), he's one of the founders of Stable-Baselines and RL-Baselines3-Zoo. + + +## The theory behind Hyperparameter tuning -The content below comes from Antonin's Raffin ICRA 2022 presentations, he's one of the founders of Stable-Baselines and RL-Baselines3-Zoo. + +## Optuna Tutorial + +The notebook ๐Ÿ‘‰ https://colab.research.google.com/github/araffin/tools-for-robotic-rl-icra2022/blob/main/notebooks/optuna_lab.ipynb