diff --git a/Dockerfile b/Dockerfile index fe5977f15..158e80631 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,67 +1,46 @@ -FROM andrewosh/binder-base -MAINTAINER Alexander Panin -USER root +FROM python:3.7-slim +# install the notebook package +RUN pip install --no-cache --upgrade pip && \ + pip install --no-cache notebook -RUN echo "deb http://archive.ubuntu.com/ubuntu trusty-backports main restricted universe multiverse" >> /etc/apt/sources.list RUN apt-get -qq update - -RUN apt-get install -y gcc-4.9 g++-4.9 libstdc++6 wget unzip +# RUN apt-get install -y gcc-4.9 g++-4.9 libstdc++6 wget unzip +RUN apt-get install -y gcc g++ libstdc++6 wget curl unzip git RUN apt-get install -y libopenblas-dev liblapack-dev libsdl2-dev libboost-all-dev graphviz RUN apt-get install -y cmake zlib1g-dev libjpeg-dev RUN apt-get install -y xvfb libav-tools xorg-dev python-opengl python3-opengl RUN apt-get -y install swig3.0 RUN ln -s /usr/bin/swig3.0 /usr/bin/swig - -USER main RUN pip install --upgrade pip==9.0.3 RUN pip install --upgrade --ignore-installed setuptools #fix https://github.com/tensorflow/tensorflow/issues/622 -RUN pip install --upgrade sklearn tqdm nltk editdistance joblib graphviz +RUN pip install --upgrade sklearn tqdm nltk editdistance joblib graphviz pandas matplotlib # install all gym stuff except mujoco - it fails at "import importlib.util" (no module named util) RUN pip install --upgrade gym RUN pip install --upgrade gym[atari] RUN pip install --upgrade gym[box2d] -RUN pip install --upgrade http://download.pytorch.org/whl/cu80/torch-0.3.0.post4-cp27-cp27mu-linux_x86_64.whl +RUN pip install --upgrade https://download.pytorch.org/whl/cpu/torch-1.0.1.post2-cp37-cp37m-linux_x86_64.whl RUN pip install --upgrade torchvision RUN pip install --upgrade keras RUN pip install --upgrade https://github.com/Theano/Theano/archive/master.zip RUN pip install --upgrade https://github.com/Lasagne/Lasagne/archive/master.zip RUN pip install --upgrade https://github.com/yandexdataschool/AgentNet/archive/master.zip RUN pip install gym_pull -RUN pip install ppaquette-gym-doom - - - - -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade pip==9.0.3 - -# fix https://github.com/tensorflow/tensorflow/issues/622 -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade --ignore-installed setuptools - -# python3: fix `GLIBCXX_3.4.20' not found - conda's libgcc blocked system's gcc-4.9 and libstdc++6 -RUN bash -c "conda update -y conda && source activate python3 && conda uninstall -y libgcc && source deactivate" -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade matplotlib numpy scipy pandas graphviz - -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade sklearn tqdm nltk editdistance joblib -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade --ignore-installed setuptools #fix https://github.com/tensorflow/tensorflow/issues/622 - -# install all gym stuff except mujoco - it fails at "mjmodel.h: no such file or directory" -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade gym -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade gym[atari] -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade gym[box2d] - - - -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade http://download.pytorch.org/whl/cu80/torch-0.3.0.post4-cp35-cp35m-linux_x86_64.whl -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade torchvision -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade keras -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade https://github.com/Theano/Theano/archive/master.zip -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade https://github.com/Lasagne/Lasagne/archive/master.zip -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade https://github.com/yandexdataschool/AgentNet/archive/master.zip - -#install TF after everything else not to break python3's pyglet with python2's tensorflow -RUN pip install --upgrade tensorflow==1.4.0 -RUN /home/main/anaconda/envs/python3/bin/pip install --upgrade tensorflow==1.4.0 -#TODO py3 doom once it's no longer broken +# RUN pip install ppaquette-gym-doom + +# create user with a home directory +ARG NB_USER +ARG NB_UID +ENV USER ${NB_USER} +ENV HOME /home/${NB_USER} + +RUN adduser --disabled-password \ + --gecos "Default user" \ + --uid ${NB_UID} \ + ${NB_USER} +WORKDIR ${HOME} +USER ${USER} + +RUN cd ${HOME} && git clone https://github.com/yandexdataschool/Practical_RL diff --git a/README.md b/README.md index 8f8ed278f..5ba841f28 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,33 @@ -# Practical_RL -** Announce - new HSE track will start in late january, YSDA soon after. Tons of changes incoming. We'll also fix all the issues :) ** -A course on reinforcement learning in the wild. +# Practical_RL [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/yandexdataschool/practical_rl/spring19) +An open course on reinforcement learning in the wild. Taught on-campus at [HSE](https://cs.hse.ru) and [YSDA](https://yandexdataschool.com/) and maintained to be friendly to online students (both english and russian). +__Note:__ this branch is an on-campus version of the for __spring 2019 YSDA and HSE students__. For full course materials, switch to the [master branch](https://github.com/yandexdataschool/Practical_RL/tree/master). + #### Manifesto: * __Optimize for the curious.__ For all the materials that aren’t covered in detail there are links to more information and related materials (D.Silver/Sutton/blogs/whatever). Assignments will have bonus sections if you want to dig deeper. * __Practicality first.__ Everything essential to solving reinforcement learning problems is worth mentioning. We won't shun away from covering tricks and heuristics. For every major idea there should be a lab that makes you to “feel” it on a practical problem. * __Git-course.__ Know a way to make the course better? Noticed a typo in a formula? Found a useful link? Made the code more readable? Made a version for alternative framework? You're awesome! [Pull-request](https://help.github.com/articles/about-pull-requests/) it! +[![Github contributors](https://img.shields.io/github/contributors/yandexdataschool/Practical_RL.svg?logo=github&logoColor=white)](https://github.com/yandexdataschool/Practical_RL/graphs/contributors) + # Course info -* Lecture slides are [here](https://yadi.sk/d/loPpY45J3EAYfU). -* Telegram chat room for YSDA & HSE students is [here](https://t.me/rlspring18) -* Grading rules for YSDA & HSE students is [here](https://github.com/yandexdataschool/Practical_RL/wiki/Homeworks-and-grading) -* Online student __[survival guide](https://github.com/yandexdataschool/Practical_RL/wiki/Online-student's-survival-guide)__ -* Installing the libraries - [guide and issues thread](https://github.com/yandexdataschool/Practical_RL/issues/1) -* Magical button that launches you into course environment: - * [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/yandexdataschool/Practical_RL/master) - comes with all libraries pre-installed. May be down time to time. - * If it's down, try [__google colab__](https://colab.research.google.com/) or [__azure notebooks__](http://notebooks.azure.com/). Those last longer, but they will require you to run installer commands (see ./Dockerfile). -* Anonymous [feedback form](https://docs.google.com/forms/d/e/1FAIpQLSdurWw97Sm9xCyYwC8g3iB5EibITnoPJW2IkOVQYE_kcXPh6Q/viewform) for everything that didn't go through e-mail. -* [About the course](https://github.com/yandexdataschool/Practical_RL/wiki/Practical-RL) +* __Chat room__ for YSDA & HSE students is [here](https://t.me/joinchat/CDFcMVcoAQvEiI9WAo1pEQ) +* __Grading__ rules for YSDA & HSE students is [here](https://github.com/yandexdataschool/Practical_RL/wiki/Homeworks-and-grading) + +* __FAQ:__ [About the course](https://github.com/yandexdataschool/Practical_RL/wiki/Practical-RL), [Technical issues thread](https://github.com/yandexdataschool/Practical_RL/issues/1), [Lecture Slides](https://yadi.sk/d/loPpY45J3EAYfU), [Online Student Survival Guide](https://github.com/yandexdataschool/Practical_RL/wiki/Online-student's-survival-guide) + +* Anonymous [feedback form](https://docs.google.com/forms/d/e/1FAIpQLSdurWw97Sm9xCyYwC8g3iB5EibITnoPJW2IkOVQYE_kcXPh6Q/viewform). + +* Virtual course environment: + * [Installing dependencies](https://github.com/yandexdataschool/Practical_RL/issues/1) on your local machine (recommended). + * [__google colab__](https://colab.research.google.com/) - set open -> github -> yandexdataschool/pracical_rl -> {branch name} and select any notebook you want. + * Alternatives: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/yandexdataschool/practical_rl/spring19) and [Azure Notebooks](https://notebooks.azure.com/). + # Additional materials -* A large list of RL materials - [awesome rl](https://github.com/aikorea/awesome-rl) * [RL reading group](https://github.com/yandexdataschool/Practical_RL/wiki/RL-reading-group) @@ -31,75 +35,43 @@ Taught on-campus at [HSE](https://cs.hse.ru) and [YSDA](https://yandexdataschool The syllabus is approximate: the lectures may occur in a slightly different order and some topics may end up taking two weeks. -* [__week1__](https://github.com/yandexdataschool/Practical_RL/tree/master/week1_intro) RL as blackbox optimization +* [__week01_intro__](./week01_intro) Introduction * Lecture: RL problems around us. Decision processes. Stochastic optimization, Crossentropy method. Parameter space search vs action space search. * Seminar: Welcome into openai gym. Tabular CEM for Taxi-v0, deep CEM for box2d environments. * Homework description - see week1/README.md. - * **YSDA Deadline: 2018.02.26 23.59** - * **HSE Deadline: 2018.01.28 23:59** - -* [__week2__](https://github.com/yandexdataschool/Practical_RL/tree/master/week2_value_based) Value-based methods + +* [__week02_value_based__](./week02_value_based) Value-based methods * Lecture: Discounted reward MDP. Value-based approach. Value iteration. Policy iteration. Discounted reward fails. * Seminar: Value iteration. * Homework description - see week2/README.md. - * **HSE Deadline: 2018.02.11 23:59** - * **YSDA Deadline: part1 2018.03.05 23.59, part2 2018.03.12 23.59** - -* [__week3__](https://github.com/yandexdataschool/Practical_RL/tree/master/week3_model_free) Model-free reinforcement learning +* [__week03_model_free__](./week03_model_free) Model-free reinforcement learning * Lecture: Q-learning. SARSA. Off-policy Vs on-policy algorithms. N-step algorithms. TD(Lambda). * Seminar: Qlearning Vs SARSA Vs Expected Value SARSA * Homework description - see week3/README.md. - * **HSE Deadline: 2018.02.15 23:59** - * **YSDA Deadline: 2018.03.12 23.59** - -* [__week4_recap__](https://github.com/yandexdataschool/Practical_RL/tree/master/week4_%5Brecap%5D_deep_learning) - deep learning recap - * Lecture: Deep learning 101 - * Seminar: Simple image classification with convnets - -* [__week4__](https://github.com/yandexdataschool/Practical_RL/tree/master/week4_approx_rl) Approximate reinforcement learning - * Lecture: Infinite/continuous state space. Value function approximation. Convergence conditions. Multiple agents trick; experience replay, target networks, double/dueling/bootstrap DQN, etc. - * Seminar: Approximate Q-learning with experience replay. (CartPole, Atari) - * **HSE Deadline: 2018.03.04 23:30** - * **YSDA Deadline: 2018.03.20 23.30** - -* [__week5__](https://github.com/yandexdataschool/Practical_RL/tree/master/week5_explore) Exploration in reinforcement learning - * Lecture: Contextual bandits. Thompson Sampling, UCB, bayesian UCB. Exploration in model-based RL, MCTS. "Deep" heuristics for exploration. - * Seminar: bayesian exploration for contextual bandits. UCB for MCTS. - - * **YSDA Deadline: 2018.03.30 23.30** - -* [__week6__](https://github.com/yandexdataschool/Practical_RL/tree/master/week6_policy_based) Policy gradient methods I - * Lecture: Motivation for policy-based, policy gradient, logderivative trick, REINFORCE/crossentropy method, variance reduction(baseline), advantage actor-critic (incl. GAE) - * Seminar: REINFORCE, advantage actor-critic - -* [__week7_recap__](https://github.com/yandexdataschool/Practical_RL/tree/master/week7_%5Brecap%5D_rnn) Recurrent neural networks recap - * Lecture: Problems with sequential data. Recurrent neural netowks. Backprop through time. Vanishing & exploding gradients. LSTM, GRU. Gradient clipping - * Seminar: character-level RNN language model -* [__week7__](https://github.com/yandexdataschool/Practical_RL/tree/master/week7_pomdp) Partially observable MDPs - * Lecture: POMDP intro. POMDP learning (agents with memory). POMDP planning (POMCP, etc) - * Seminar: Deep kung-fu & doom with recurrent A3C and DRQN - -* [__week8__](https://github.com/yandexdataschool/Practical_RL/tree/master/week8_scst) Applications II - * Lecture: Reinforcement Learning as a general way to optimize non-differentiable loss. G2P, machine translation, conversation models, image captioning, discrete GANs. Self-critical sequence training. - * Seminar: Simple neural machine translation with self-critical sequence training +* __week04__ Approximate (deep) RL +* __week05__ Exploration +* __week06__ Policy Gradient methods +* __week07__ Applications I +* __week{++i}__ Partially Observed MDP +* __week{++i}__ Advanced policy-based methods +* __week{++i}__ Applications II +* __week{++i}__ Distributional reinforcement learning +* __week{++i}__ Inverse RL and Imitation Learning -* [__week9__](https://github.com/yandexdataschool/Practical_RL/tree/master/week9_policy_II) Policy gradient methods II - * Lecture: Trust region policy optimization. NPO/PPO. Deterministic policy gradient. DDPG. Bonus: DPG for discrete action spaces. - * Seminar: Approximate TRPO for simple robotic tasks. - -* [Some after-course bonus materials](https://github.com/yandexdataschool/Practical_RL/tree/master/yet_another_week) - # Course staff Course materials and teaching by: _[unordered]_ - [Pavel Shvechikov](https://github.com/bestxolodec) - lectures, seminars, hw checkups, reading group -- [Oleg Vasilev](https://github.com/Omrigan) - seminars, hw checkups, technical support -- [Alexander Fritsler](https://github.com/Fritz449) - lectures, seminars, hw checkups - [Nikita Putintsev](https://github.com/qwasser) - seminars, hw checkups, organizing our hot mess -- [Fedor Ratnikov](https://github.com/justheuristic/) - lectures, seminars, hw checkups -- [Alexey Umnov](https://github.com/alexeyum) - seminars, hw checkups +- [Alexander Fritsler](https://github.com/Fritz449) - lectures, seminars, hw checkups +- [Oleg Vasilev](https://github.com/Omrigan) - seminars, hw checkups, technical support +- [Dmitry Nikulin](https://github.com/pastafarianist) - tons of fixes, far and wide +- [Mikhail Konobeev](https://github.com/MichaelKonobeev) - seminars, hw checkups +- [Ivan Kharitonov](https://github.com/neer201) - seminars, hw checkups +- [Ravil Khisamov](https://github.com/zshrav) - seminars, hw checkups +- [Fedor Ratnikov](https://github.com/justheuristic) - admin stuff # Contributions * Using pictures from [Berkeley AI course](http://ai.berkeley.edu/home.html) @@ -107,4 +79,5 @@ Course materials and teaching by: _[unordered]_ * Several tensorflow assignments by [Scitator](https://github.com/Scitator) * A lot of fixes from [arogozhnikov](https://github.com/arogozhnikov) * Other awesome people: see github [contributors](https://github.com/yandexdataschool/Practical_RL/graphs/contributors) +* [Alexey Umnov](https://github.com/alexeyum) helped us a lot during spring2018 diff --git a/docker/Dockerfile b/docker/Dockerfile index ebed46e56..ee883dd6e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -41,7 +41,7 @@ RUN pip install --upgrade pip==9.0.3 && \ https://github.com/Lasagne/Lasagne/archive/master.zip \ https://github.com/yandexdataschool/AgentNet/archive/master.zip \ tensorflow \ - http://download.pytorch.org/whl/cpu/torch-0.4.1-cp27-cp27mu-linux_x86_64.whl \ + https://download.pytorch.org/whl/cpu/torch-1.0.1.post2-cp27-cp27mu-linux_x86_64.whl \ torchvision \ keras @@ -60,7 +60,7 @@ RUN pip3 install --upgrade pip==9.0.3 && \ pip3 install --upgrade https://github.com/Theano/Theano/archive/master.zip \ https://github.com/Lasagne/Lasagne/archive/master.zip \ https://github.com/yandexdataschool/AgentNet/archive/master.zip \ - http://download.pytorch.org/whl/cpu/torch-0.4.1-cp35-cp35m-linux_x86_64.whl \ + https://download.pytorch.org/whl/cpu/torch-1.0.1.post2-cp35-cp35m-linux_x86_64.whl \ torchvision \ tensorflow \ keras && \ diff --git a/setup_colab.sh b/setup_colab.sh new file mode 100644 index 000000000..01340b7ea --- /dev/null +++ b/setup_colab.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# a setup script for google colab. Will be updated +pip install gym +apt-get install -y xvfb +wget https://raw.githubusercontent.com/yandexdataschool/Practical_DL/fall18/xvfb -O ../xvfb +apt-get install -y python-opengl ffmpeg +pip install pyglet==1.2.4 + diff --git a/week1_intro/README.md b/week01_intro/README.md similarity index 75% rename from week1_intro/README.md rename to week01_intro/README.md index 423e6e70c..054ad0b99 100644 --- a/week1_intro/README.md +++ b/week01_intro/README.md @@ -1,5 +1,5 @@ ## Materials: -* [__Lecture slides__](https://yadi.sk/i/sbc0ZCKx3RRGbW) +* [__Lecture slides__](https://yadi.sk/i/-EUHXUXOTC5t9Q) * __Russian:__ * Intro to RL - [video](https://yadi.sk/i/bMo0qa-x3DoqkS) * Blackbox optimization - [video](https://yadi.sk/i/5yf_4oGI3EDJhJ) @@ -13,6 +13,7 @@ ## More materials: * __[recommended]__ - awesome openai post about evolution strategies - [blog post](https://blog.openai.com/evolution-strategies/), [article](https://arxiv.org/abs/1703.03864) +* __[recommended]__ - formal explanation of crossentropy method in [general](https://people.smp.uq.edu.au/DirkKroese/ps/CEEncycl.pdf) and for [optimization](https://people.smp.uq.edu.au/DirkKroese/ps/CEopt.pdf) * Deep learning course (if you want to learn in parallel) - https://github.com/yandexdataschool/HSE_deeplearning * Video on genetic algorithms (english) - [video](https://www.youtube.com/watch?v=ejxfTy4lI6I) * Another guide to genetic algorithm (english) - [video](https://www.youtube.com/watch?v=zwYV11a__HQ) @@ -21,9 +22,10 @@ * Longer video on Ant Colony Algorithm (english) - [video](https://www.youtube.com/watch?v=xpyKmjJuqhk) -## Homework description +## Practice assignment +Instant dive in: [__seminar_gym_interface__](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week01_intro/seminar_gym_interface.ipynb), [__crossentropy_method__](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week01_intro/crossentropy_method.ipynb) + * Open `gym_interface.ipynb` and follow instructions from there - * If you haven't installed everything yet, try [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/yandexdataschool/Practical_RL/master) * After you're done there, proceed to `crossentropy_method.ipynb` * You can find homework and bonus assignment descriptions at the end of that notebook. * Note: so far it's enough to say `pip install gym` on top of any data-science-stuffed python, but we'd appreciate if you gradually switch to [full installation](https://github.com/openai/gym#installing-everything). diff --git a/week1_intro/crossentropy_method.ipynb b/week01_intro/crossentropy_method.ipynb similarity index 85% rename from week1_intro/crossentropy_method.ipynb rename to week01_intro/crossentropy_method.ipynb index bb26b2be0..031b7e6e7 100644 --- a/week1_intro/crossentropy_method.ipynb +++ b/week01_intro/crossentropy_method.ipynb @@ -12,11 +12,12 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ + "# In Google Colab, uncomment this:\n", + "# !wget https://bit.ly/2FMJP5K -O setup.py && bash setup.py\n", + "\n", "# XVFB will be launched if you run on a server\n", "import os\n", "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", @@ -27,9 +28,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "import gym\n", @@ -44,9 +43,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "n_states = env.observation_space.n\n", @@ -73,20 +70,16 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ - "policy = " + "policy = " ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "assert type(policy) in (np.ndarray, np.matrix)\n", @@ -106,9 +99,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "def generate_session(policy, t_max=10**4):\n", @@ -142,9 +133,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "s, a, r = generate_session(policy)\n", @@ -156,9 +145,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# let's see the initial reward distribution\n", @@ -168,10 +155,8 @@ "sample_rewards = [generate_session(policy, t_max=1000)[-1] for _ in range(200)]\n", "\n", "plt.hist(sample_rewards, bins=20)\n", - "plt.vlines([np.percentile(sample_rewards, 50)], [0], [\n", - " 100], label=\"50'th percentile\", color='green')\n", - "plt.vlines([np.percentile(sample_rewards, 90)], [0], [\n", - " 100], label=\"90'th percentile\", color='red')\n", + "plt.vlines([np.percentile(sample_rewards, 50)], [0], [100], label=\"50'th percentile\", color='green')\n", + "plt.vlines([np.percentile(sample_rewards, 90)], [0], [100], label=\"90'th percentile\", color='red')\n", "plt.legend()" ] }, @@ -185,9 +170,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "def select_elites(states_batch, actions_batch, rewards_batch, percentile=50):\n", @@ -195,14 +178,15 @@ " Select states and actions from games that have rewards >= percentile\n", " :param states_batch: list of lists of states, states_batch[session_i][t]\n", " :param actions_batch: list of lists of actions, actions_batch[session_i][t]\n", - " :param rewards_batch: list of rewards, rewards_batch[session_i][t]\n", + " :param rewards_batch: list of rewards, rewards_batch[session_i]\n", "\n", " :returns: elite_states,elite_actions, both 1D lists of states and respective actions from elite sessions\n", "\n", " Please return elite states and actions in their original order \n", " [i.e. sorted by session number and timestep within session]\n", "\n", - " If you're confused, see examples below. Please don't assume that states are integers (they'll get different later).\n", + " If you are confused, see examples below. Please don't assume that states are integers\n", + " (they will become different later).\n", " \"\"\"\n", "\n", " reward_threshold = \n", @@ -216,21 +200,19 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "states_batch = [\n", - " [1, 2, 3], # game1\n", + " [1, 2, 3], # game1\n", " [4, 2, 0, 2], # game2\n", - " [3, 1] # game3\n", + " [3, 1], # game3\n", "]\n", "\n", "actions_batch = [\n", - " [0, 2, 4], # game1\n", + " [0, 2, 4], # game1\n", " [3, 2, 0, 1], # game2\n", - " [3, 3] # game3\n", + " [3, 3], # game3\n", "]\n", "rewards_batch = [\n", " 3, # game1\n", @@ -265,9 +247,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "def update_policy(elite_states, elite_actions):\n", @@ -296,15 +276,11 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ - "\n", - "elite_states, elite_actions = ([1, 2, 3, 4, 2, 0, 2, 3, 1], [\n", - " 0, 2, 4, 3, 2, 0, 1, 3, 3])\n", - "\n", + "elite_states = [1, 2, 3, 4, 2, 0, 2, 3, 1]\n", + "elite_actions = [0, 2, 4, 3, 2, 0, 1, 3, 3]\n", "\n", "new_policy = update_policy(elite_states, elite_actions)\n", "\n", @@ -334,15 +310,12 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from IPython.display import clear_output\n", "\n", - "\n", - "def show_progress(rewards_batch, log, reward_range=[-990, +10]):\n", + "def show_progress(rewards_batch, log, percentile, reward_range=[-990, +10]):\n", " \"\"\"\n", " A convenience function that displays training progress. \n", " No cool math here, just charts.\n", @@ -374,21 +347,17 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# reset policy just in case\n", - "policy = np.ones([n_states, n_actions])/n_actions" + "policy = np.ones([n_states, n_actions]) / n_actions" ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "n_sessions = 250 # sample this many sessions\n", @@ -410,7 +379,7 @@ " policy = learning_rate*new_policy + (1-learning_rate)*policy\n", "\n", " # display results on chart\n", - " show_progress(rewards_batch, log)" + " show_progress(rewards_batch, log, percentile)" ] }, { @@ -419,7 +388,7 @@ "source": [ "# Digging deeper: approximate crossentropy with neural nets\n", "\n", - "![img](https://casd35.wikispaces.com/file/view/digging_deeper_final.jpg/359658499/503x260/digging_deeper_final.jpg)\n", + "![img](https://tip.duke.edu/independent_learning/greek/lesson/digging_deeper_final.jpg)\n", "\n", "In this section we will train a neural network policy for continuous state space game" ] @@ -427,9 +396,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# if you see \" has no attribute .env\", remove .env or update gym\n", @@ -444,18 +411,17 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# create agent\n", "from sklearn.neural_network import MLPClassifier\n", - "agent = MLPClassifier(hidden_layer_sizes=(20, 20),\n", - " activation='tanh',\n", - " warm_start=True, # keep progress between .fit(...) calls\n", - " max_iter=1 # make only 1 iteration on each .fit(...)\n", - " )\n", + "agent = MLPClassifier(\n", + " hidden_layer_sizes=(20, 20),\n", + " activation='tanh',\n", + " warm_start=True, # keep progress between .fit(...) calls\n", + " max_iter=1, # make only 1 iteration on each .fit(...)\n", + ")\n", "# initialize agent to the dimension of state an amount of actions\n", "agent.fit([env.reset()]*n_actions, range(n_actions))" ] @@ -463,9 +429,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "def generate_session(t_max=1000):\n", @@ -498,9 +462,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "n_sessions = 100\n", @@ -513,11 +475,11 @@ "\n", " states_batch, actions_batch, rewards_batch = map(np.array, zip(*sessions))\n", "\n", - " elite_states, elite_actions = \n", "\n", - " \n", + " \n", "\n", - " show_progress(rewards_batch, log, reward_range=[0, np.max(rewards_batch)])\n", + " show_progress(rewards_batch, log, percentile, reward_range=[0, np.max(rewards_batch)])\n", "\n", " if np.mean(rewards_batch) > 190:\n", " print(\"You Win! You may stop training now via KeyboardInterrupt.\")" @@ -533,9 +495,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# record sessions\n", @@ -543,17 +503,13 @@ "env = gym.wrappers.Monitor(gym.make(\"CartPole-v0\"),\n", " directory=\"videos\", force=True)\n", "sessions = [generate_session() for _ in range(100)]\n", - "env.close()\n", - "# upload to gym\n", - "# gym.upload(\"./videos/\",api_key=\"\") #you'll need me later" + "env.close()" ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# show video\n", @@ -581,7 +537,7 @@ "You may have noticed that the taxi problem quickly converges from -100 to a near-optimal score and then descends back into -50/-100. This is in part because the environment has some innate randomness. Namely, the starting points of passenger/driver change from episode to episode.\n", "\n", "### Tasks\n", - "- __1.1__ (1 pts) Find out how the algorithm performance changes if you change different percentile and different n_samples.\n", + "- __1.1__ (1 pts) Find out how the algorithm performance changes if you use a different `percentile` and/or `n_sessions`.\n", "- __1.2__ (2 pts) Tune the algorithm to end up with positive average score.\n", "\n", "It's okay to modify the existing code.\n" @@ -602,7 +558,7 @@ "\n", "### Deep crossentropy method\n", "\n", - "By this moment you should have got enough score on [CartPole-v0](https://gym.openai.com/envs/CartPole-v0) to consider it solved (see the link). It's time to upload the result and get to something harder.\n", + "By this moment you should have got enough score on [CartPole-v0](https://gym.openai.com/envs/CartPole-v0) to consider it solved (see the link). It's time to try something harder.\n", "\n", "* if you have any trouble with CartPole-v0 and feel stuck, feel free to ask us or your peers for help.\n", "\n", @@ -611,7 +567,6 @@ "* __2.1__ (3 pts) Pick one of environments: MountainCar-v0 or LunarLander-v2.\n", " * For MountainCar, get average reward of __at least -150__\n", " * For LunarLander, get average reward of __at least +50__\n", - " * For any environment, upload it to gym and post url in your anytask form.\n", "\n", "See the tips section below, it's kinda important.\n", "__Note:__ If your agent is below the target score, you'll still get most of the points depending on the result, so don't be afraid to submit it.\n", @@ -625,15 +580,38 @@ " \n", " \n", "### Tips\n", - "* Gym page: [mountaincar](https://gym.openai.com/envs/MountainCar-v0), [lunarlander](https://gym.openai.com/envs/LunarLander-v2)\n", + "* Gym page: [MountainCar](https://gym.openai.com/envs/MountainCar-v0), [LunarLander](https://gym.openai.com/envs/LunarLander-v2)\n", "* Sessions for MountainCar may last for 10k+ ticks. Make sure ```t_max``` param is at least 10k.\n", " * Also it may be a good idea to cut rewards via \">\" and not \">=\". If 90% of your sessions get reward of -10k and 20% are better, than if you use percentile 20% as threshold, R >= threshold __fails cut off bad sessions__ whule R > threshold works alright.\n", "* _issue with gym_: Some versions of gym limit game time by 200 ticks. This will prevent cem training in most cases. Make sure your agent is able to play for the specified __t_max__, and if it isn't, try `env = gym.make(\"MountainCar-v0\").env` or otherwise get rid of TimeLimit wrapper.\n", "* If you use old _swig_ lib for LunarLander-v2, you may get an error. See this [issue](https://github.com/openai/gym/issues/100) for solution.\n", "* If it won't train it's a good idea to plot reward distribution and record sessions: they may give you some clue. If they don't, call course staff :)\n", "* 20-neuron network is probably not enough, feel free to experiment.\n", - "* __Please upload the results to openai gym and send links to all submissions in the e-mail__\n", "\n", + "You may find the following snippet useful:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def visualize_mountain_car(env, agent):\n", + " xs = np.linspace(env.min_position, env.max_position, 100)\n", + " vs = np.linspace(-env.max_speed, env.max_speed, 100)\n", + " grid = np.dstack(np.meshgrid(xs, vs)).transpose(1, 0, 2)\n", + " grid_flat = grid.reshape(len(xs) * len(vs), 2)\n", + " probs = agent.predict_proba(grid_flat).reshape(len(xs), len(vs), 3)\n", + " return probs\n", + "\n", + "plt.imshow(visualize_mountain_car(env, agent))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "### Bonus tasks\n", "\n", "* __2.3 bonus__ Try to find a network architecture and training params that solve __both__ environments above (_Points depend on implementation. If you attempted this task, please mention it in anytask submission._)\n", @@ -650,22 +628,9 @@ } ], "metadata": { - "kernelspec": { - "display_name": "Python 3", - "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.7.0" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/week1_intro/pong.py b/week01_intro/pong.py similarity index 100% rename from week1_intro/pong.py rename to week01_intro/pong.py diff --git a/week1_intro/primer_python_for_ml/recap_ml.ipynb b/week01_intro/primer_python_for_ml/recap_ml.ipynb similarity index 99% rename from week1_intro/primer_python_for_ml/recap_ml.ipynb rename to week01_intro/primer_python_for_ml/recap_ml.ipynb index b8b904f89..3726593e0 100644 --- a/week1_intro/primer_python_for_ml/recap_ml.ipynb +++ b/week01_intro/primer_python_for_ml/recap_ml.ipynb @@ -849,7 +849,7 @@ }, "outputs": [], "source": [ - "% % time", + "%%time", "\n", "# ^-- this \"magic\" measures and prints cell computation time", "\n", @@ -888,7 +888,7 @@ }, "outputs": [], "source": [ - "% % time", + "%% time", "\n", "\n", "# Option II: start from python, convert to numpy", @@ -919,7 +919,7 @@ }, "outputs": [], "source": [ - "% % time", + "%% time", "\n", "\n", "# Option III: pure numpy", @@ -2653,4 +2653,4 @@ }, "nbformat": 4, "nbformat_minor": 1 -} \ No newline at end of file +} diff --git a/week1_intro/primer_python_for_ml/train.csv b/week01_intro/primer_python_for_ml/train.csv similarity index 100% rename from week1_intro/primer_python_for_ml/train.csv rename to week01_intro/primer_python_for_ml/train.csv diff --git a/week01_intro/project_starter_evolution_strategies.ipynb b/week01_intro/project_starter_evolution_strategies.ipynb new file mode 100644 index 000000000..9b1bce4df --- /dev/null +++ b/week01_intro/project_starter_evolution_strategies.ipynb @@ -0,0 +1,165 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Project :: Evolution Strategies\n", + "\n", + "![img](https://t4.ftcdn.net/jpg/00/17/46/81/240_F_17468143_wY3hsHyfNYoMdG9BlC56HI4JA7pNu63h.jpg)\n", + "\n", + "Remember the idea behind Evolution Strategies? Here's a neat [blog post](https://blog.openai.com/evolution-strategies/) about 'em.\n", + "\n", + "Can you reproduce their success? You will have to implement evolutionary strategies and see how they work.\n", + "\n", + "This project is optional; has several milestones each worth a number of points [and swag].\n", + "\n", + "__Milestones:__\n", + "* [10pts] Basic prototype of evolutionary strategies that works in one thread on CartPole\n", + "* [+5pts] Modify the code to make them work in parallel\n", + "* [+5pts] if you can run ES distributedly on at least two PCs\n", + "* [+10pts] Apply ES to play Atari Pong at least better than random\n", + "* [++] Additional points for all kinds of cool stuff besides milestones\n", + "\n", + "__Rules:__\n", + "\n", + "* This is __not a mandatory assignment__, but it's a way to learn some cool things if you're getting bored with default assignments.\n", + "* Once you decided to take on this project, please tell any of course staff members so that we can help ypu if you get stuck.\n", + "* There's a default implementation of ES in this [openai repo](https://github.com/openai/evolution-strategies-starter). It's okay to look there if you get stuck or want to compare your solutions, but each copy-pasted chunk of code should be understood thoroughly. We'll test that with questions." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Tips on implementation\n", + "\n", + "* It would be very convenient later if you implemented a function that takes policy weights, generates a session and returns policy changes -- so that you could then run a bunch of them in parallel.\n", + "\n", + "* The simplest way you can do multiprocessing is to use [joblib](https://www.google.com/search?client=ubuntu&channel=fs&q=joblib&ie=utf-8&oe=utf-8)\n", + "\n", + "* For joblib, make sure random variables are independent in each job. Simply add `np.random.seed()` at the beginning of your \"job\" function.\n", + "\n", + "Later once you got distributed, you may need a storage that gathers gradients from all workers. In such case we recommend [Redis](https://redis.io/) due to it's simplicity.\n", + "\n", + "Here's a speed-optimized saver/loader to store numpy arrays in Redis as strings.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import joblib\n", + "from six import BytesIO\n", + "\n", + "\n", + "def dumps(data):\n", + " \"\"\"converts whatever to string\"\"\"\n", + " s = BytesIO()\n", + " joblib.dump(data, s)\n", + " return s.getvalue()\n", + "\n", + "\n", + "def loads(self, string):\n", + " \"\"\"converts string to whatever was dumps'ed in it\"\"\"\n", + " return joblib.load(BytesIO(string))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Tips on atari games\n", + "* There's all the pre-processing and tuning done for you in the code below\n", + " * Images rescaled to 42x42 to speed up computation\n", + " * We use last 4 frames as observations to account for ball velocity\n", + " * The code below requires ```pip install Image``` and ```pip install gym[atari]``` \n", + " * You may also need some dependencies for gym[atari] - google \"gym install all\" dependencies or use our pre-built environment.\n", + "* The recommended agent architecture is a convolutional neural network. Dense network will also do.\n", + "\n", + "\n", + "May the force be with you!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pong import make_pong\n", + "import numpy as np\n", + "\n", + "env = make_pong()\n", + "print(env.action_space)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# get the initial state\n", + "s = env.reset()\n", + "print(s.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "# plot first observation. Only one frame\n", + "plt.imshow(s.swapaxes(1, 2).reshape(-1, s.shape[-1]).T)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# next frame\n", + "new_s, r, done, _ = env.step(env.action_space.sample())\n", + "plt.imshow(new_s.swapaxes(1, 2).reshape(-1, s.shape[-1]).T)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# after 10 frames\n", + "for _ in range(10):\n", + " new_s, r, done, _ = env.step(env.action_space.sample())\n", + "\n", + "plt.imshow(new_s.swapaxes(1, 2).reshape(-1, s.shape[-1]).T, vmin=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "< tons of your code here or elsewhere >" + ] + } + ], + "metadata": { + "language_info": { + "name": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week01_intro/seminar_gym_interface.ipynb b/week01_intro/seminar_gym_interface.ipynb new file mode 100644 index 000000000..1ba5d6df2 --- /dev/null +++ b/week01_intro/seminar_gym_interface.ipynb @@ -0,0 +1,181 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "# In Google Colab, uncomment this:\n", + "# !wget https://bit.ly/2FMJP5K -O setup.py && bash setup.py\n", + "\n", + "# This code creates a virtual display to draw game images on.\n", + "# If you are running locally, just ignore it\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### OpenAI Gym\n", + "\n", + "We're gonna spend several next weeks learning algorithms that solve decision processes. We are then in need of some interesting decision problems to test our algorithms.\n", + "\n", + "That's where OpenAI gym comes into play. It's a python library that wraps many classical decision problems including robot control, videogames and board games.\n", + "\n", + "So here's how it works:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import gym\n", + "\n", + "env = gym.make(\"MountainCar-v0\")\n", + "env.reset()\n", + "\n", + "plt.imshow(env.render('rgb_array'))\n", + "print(\"Observation space:\", env.observation_space)\n", + "print(\"Action space:\", env.action_space)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note: if you're running this on your local machine, you'll see a window pop up with the image above. Don't close it, just alt-tab away." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Gym interface\n", + "\n", + "The three main methods of an environment are\n", + "* __reset()__ - reset environment to initial state, _return first observation_\n", + "* __render()__ - show current environment state (a more colorful version :) )\n", + "* __step(a)__ - commit action __a__ and return (new observation, reward, is done, info)\n", + " * _new observation_ - an observation right after commiting the action __a__\n", + " * _reward_ - a number representing your reward for commiting action __a__\n", + " * _is done_ - True if the MDP has just finished, False if still in progress\n", + " * _info_ - some auxilary stuff about what just happened. Ignore it ~~for now~~." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs0 = env.reset()\n", + "print(\"initial observation code:\", obs0)\n", + "\n", + "# Note: in MountainCar, observation is just two numbers: car position and velocity" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"taking action 2 (right)\")\n", + "new_obs, reward, is_done, _ = env.step(2)\n", + "\n", + "print(\"new observation code:\", new_obs)\n", + "print(\"reward:\", reward)\n", + "print(\"is game over?:\", is_done)\n", + "\n", + "# Note: as you can see, the car has moved to the right slightly (around 0.0005)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Play with it\n", + "\n", + "Below is the code that drives the car to the right. \n", + "\n", + "However, it doesn't reach the flag at the far right due to gravity. \n", + "\n", + "__Your task__ is to fix it. Find a strategy that reaches the flag. \n", + "\n", + "You're not required to build any sophisticated algorithms for now, feel free to hard-code :)\n", + "\n", + "__Hint__: your action at each step should depend either on `t` or on `s`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython import display\n", + "\n", + "# create env manually to set time limit. Please don't change this.\n", + "TIME_LIMIT = 250\n", + "env = gym.wrappers.TimeLimit(\n", + " gym.envs.classic_control.MountainCarEnv(),\n", + " max_episode_steps=TIME_LIMIT + 1,\n", + ")\n", + "s = env.reset()\n", + "actions = {'left': 0, 'stop': 1, 'right': 2}\n", + "\n", + "plt.figure(figsize=(4, 3))\n", + "display.clear_output(wait=True)\n", + "\n", + "for t in range(TIME_LIMIT):\n", + " plt.gca().clear()\n", + " \n", + " # change the line below to reach the flag\n", + " s, r, done, _ = env.step(actions['right'])\n", + "\n", + " # draw game image on display\n", + " plt.imshow(env.render('rgb_array'))\n", + " \n", + " display.clear_output(wait=True)\n", + " display.display(plt.gcf())\n", + "\n", + " if done:\n", + " print(\"Well done!\")\n", + " break\n", + "else:\n", + " print(\"Time limit exceeded. Try again.\")\n", + "\n", + "display.clear_output(wait=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert s[0] > 0.47\n", + "print(\"You solved it!\")" + ] + } + ], + "metadata": { + "language_info": { + "name": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/week02_value_based/README.md b/week02_value_based/README.md new file mode 100644 index 000000000..aca7e7a4d --- /dev/null +++ b/week02_value_based/README.md @@ -0,0 +1,14 @@ +## Materials +* [__Lecture slides__](https://docs.google.com/presentation/d/1lz2oIUTvd2MHWKEQSH8hquS66oe4MZ_eRvVViZs2uuE) +* Our videos: [lecture](https://yadi.sk/i/iAkDaxVftlAdnA) [seminar](https://yadi.sk/i/hrnHB9DK3SeZRC) (russian) +* __[main]__ lecture by David Silver - [url](https://www.youtube.com/watch?v=Nd1-UUMVfz4) +* Alternative lecture by Pieter Abbeel (english): [part 1](https://www.youtube.com/watch?v=i0o-ui1N35U), [part 2](https://www.youtube.com/watch?v=Csiiv6WGzKM) +* Alternative lecture by John Schulmann (english): [video](https://www.youtube.com/watch?v=IL3gVyJMmhg) +* Definitive guide in policy/value iteration from Sutton: start from page 81 [here](http://incompleteideas.net/book/the-book-2nd.html). + + +## Homework description: + +The main assignment is `seminar_vi.ipynb`[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week02_value_based/seminar_vi.ipynb) notebook in this week's folder. It has no requirements besides the most basic data science libraries (e.g. numpy) so you should be able to run it locally. + +__Note:__ if you have any difficulty using graphviz, just set `has_graphviz=False`. diff --git a/week2_value_based/mdp.py b/week02_value_based/mdp.py similarity index 96% rename from week2_value_based/mdp.py rename to week02_value_based/mdp.py index b14798672..2536e31b3 100644 --- a/week2_value_based/mdp.py +++ b/week02_value_based/mdp.py @@ -1,11 +1,10 @@ # most of this code was politely stolen from https://github.com/berkeleydeeprlcourse/homework/ -# all creadit goes to https://github.com/abhishekunique (if i got the author right) +# all credit goes to https://github.com/abhishekunique (if i got the author right) import sys import random import numpy as np try: - from IPython.display import display from graphviz import Digraph import graphviz has_graphviz = True @@ -13,17 +12,6 @@ has_graphviz = False -def weighted_choice(v, p): - total = sum(p) - r = random.uniform(0, total) - upto = 0 - for c, w in zip(v, p): - if upto + w >= r: - return c - upto += w - assert False, "Shouldn't get here" - - class MDP: def __init__(self, transition_probs, rewards, initial_state=None): """ @@ -114,7 +102,7 @@ def step(self, action): """ take action, return next_state, reward, is_done, empty_info """ possible_states, probs = zip( *self.get_next_states(self._current_state, action).items()) - next_state = weighted_choice(possible_states, p=probs) + next_state = possible_states[np.random.choice(np.arange(len(possible_states)), p=probs)] reward = self.get_reward(self._current_state, action, next_state) is_done = self.is_terminal(next_state) self._current_state = next_state @@ -290,7 +278,7 @@ def plot_graph(mdp, graph_size='10,10', s_node_size='1,5', :return: dot object """ s_node_attrs = {'shape': 'doublecircle', - 'color': 'lightgreen', + 'color': '#85ff75', 'style': 'filled', 'width': str(s_node_size), 'height': str(s_node_size), @@ -353,20 +341,24 @@ def plot_graph_with_state_values(mdp, state_values): value = state_values[state_node] graph.node(state_node, label=str(state_node) + '\n' + 'V =' + str(value)[:4]) - return display(graph) + return graph def get_optimal_action_for_plot(mdp, state_values, state, gamma=0.9): """ Finds optimal action using formula above. """ if mdp.is_terminal(state): return None next_actions = mdp.get_possible_actions(state) + try: + from mdp_get_action_value import get_action_value + except ImportError: + raise ImportError("Implement get_action_value(mdp, state_values, state, action, gamma) in the file \"mdp_get_action_value.py\".") q_values = [get_action_value(mdp, state_values, state, action, gamma) for action in next_actions] optimal_action = next_actions[np.argmax(q_values)] return optimal_action -def plot_graph_optimal_strategy_and_state_values(mdp, state_values): +def plot_graph_optimal_strategy_and_state_values(mdp, state_values, gamma=0.9): """ Plot graph with state values and """ graph = plot_graph(mdp) opt_s_a_edge_attrs = {'style': 'bold', @@ -385,4 +377,4 @@ def plot_graph_optimal_strategy_and_state_values(mdp, state_values): gamma): graph.edge(state_node, state_node + "-" + action, **opt_s_a_edge_attrs) - return display(graph) + return graph diff --git a/week02_value_based/seminar_vi.ipynb b/week02_value_based/seminar_vi.ipynb new file mode 100644 index 000000000..c4efc0851 --- /dev/null +++ b/week02_value_based/seminar_vi.ipynb @@ -0,0 +1,1367 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Markov decision process\n", + "\n", + "This week's methods are all built to solve __M__arkov __D__ecision __P__rocesses. In the broadest sense, an MDP is defined by how it changes states and how rewards are computed.\n", + "\n", + "State transition is defined by $P(s' |s,a)$ - how likely are you to end at state $s'$ if you take action $a$ from state $s$. Now there's more than one way to define rewards, but we'll use $r(s,a,s')$ function for convenience.\n", + "\n", + "_This notebook is inspired by the awesome_ [CS294](https://github.com/berkeleydeeprlcourse/homework/blob/36a0b58261acde756abd55306fbe63df226bf62b/hw2/HW2.ipynb) _by Berkeley_" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For starters, let's define a simple MDP from this picture:\n", + "\n", + "_img by MistWiz (Own work) [Public domain], via Wikimedia Commons_" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:44:32.642838Z", + "start_time": "2018-04-02T13:44:32.545142Z" + } + }, + "outputs": [], + "source": [ + "# If you Colab, uncomment this please\n", + "# !wget -q https://raw.githubusercontent.com/yandexdataschool/Practical_RL/spring19/week02_value_based/mdp.py\n", + "\n", + "transition_probs = {\n", + " 's0': {\n", + " 'a0': {'s0': 0.5, 's2': 0.5},\n", + " 'a1': {'s2': 1}\n", + " },\n", + " 's1': {\n", + " 'a0': {'s0': 0.7, 's1': 0.1, 's2': 0.2},\n", + " 'a1': {'s1': 0.95, 's2': 0.05}\n", + " },\n", + " 's2': {\n", + " 'a0': {'s0': 0.4, 's1': 0.6},\n", + " 'a1': {'s0': 0.3, 's1': 0.3, 's2': 0.4}\n", + " }\n", + "}\n", + "rewards = {\n", + " 's1': {'a0': {'s0': +5}},\n", + " 's2': {'a1': {'s0': -1}}\n", + "}\n", + "\n", + "from mdp import MDP\n", + "mdp = MDP(transition_probs, rewards, initial_state='s0')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now use MDP just as any other gym environment:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:44:34.203384Z", + "start_time": "2018-04-02T13:44:34.199297Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "initial state = s0\n", + "next_state = s2, reward = 0.0, done = False\n" + ] + } + ], + "source": [ + "print('initial state =', mdp.reset())\n", + "next_state, reward, done, info = mdp.step('a1')\n", + "print('next_state = %s, reward = %s, done = %s' % (next_state, reward, done))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "but it also has other methods that you'll need for Value Iteration" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:44:34.956122Z", + "start_time": "2018-04-02T13:44:34.949856Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mdp.get_all_states = ('s0', 's1', 's2')\n", + "mdp.get_possible_actions('s1') = ('a0', 'a1')\n", + "mdp.get_next_states('s1', 'a0') = {'s0': 0.7, 's1': 0.1, 's2': 0.2}\n", + "mdp.get_reward('s1', 'a0', 's0') = 5\n", + "mdp.get_transition_prob('s1', 'a0', 's0') = 0.7\n" + ] + } + ], + "source": [ + "print(\"mdp.get_all_states =\", mdp.get_all_states())\n", + "print(\"mdp.get_possible_actions('s1') = \", mdp.get_possible_actions('s1'))\n", + "print(\"mdp.get_next_states('s1', 'a0') = \", mdp.get_next_states('s1', 'a0'))\n", + "print(\"mdp.get_reward('s1', 'a0', 's0') = \", mdp.get_reward('s1', 'a0', 's0'))\n", + "print(\"mdp.get_transition_prob('s1', 'a0', 's0') = \",\n", + " mdp.get_transition_prob('s1', 'a0', 's0'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Optional: Visualizing MDPs\n", + "\n", + "You can also visualize any MDP with the drawing fuction donated by [neer201](https://github.com/neer201).\n", + "\n", + "You have to install graphviz for system and for python. For ubuntu just run:\n", + "\n", + "1. `sudo apt-get install graphviz`\n", + "2. `pip install graphviz`\n", + "3. restart the notebook\n", + "\n", + "__Note:__ Installing graphviz on some OS (esp. Windows) may be tricky. However, you can ignore this part alltogether and use the standart vizualization." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:44:37.797182Z", + "start_time": "2018-04-02T13:44:37.794073Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Graphviz available: True\n" + ] + } + ], + "source": [ + "from mdp import has_graphviz\n", + "from IPython.display import display\n", + "print(\"Graphviz available:\", has_graphviz)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:44:38.715883Z", + "start_time": "2018-04-02T13:44:38.648684Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "MDP\n", + "\n", + "\n", + "s0\n", + "\n", + "\n", + "s0\n", + "\n", + "\n", + "s0-a0\n", + "\n", + "a0\n", + "\n", + "\n", + "s0->s0-a0\n", + "\n", + "\n", + "\n", + "\n", + "s0-a1\n", + "\n", + "a1\n", + "\n", + "\n", + "s0->s0-a1\n", + "\n", + "\n", + "\n", + "\n", + "s0-a0->s0\n", + "\n", + "\n", + "p = 0.5\n", + "\n", + "\n", + "s2\n", + "\n", + "\n", + "s2\n", + "\n", + "\n", + "s0-a0->s2\n", + "\n", + "\n", + "p = 0.5\n", + "\n", + "\n", + "s2-a0\n", + "\n", + "a0\n", + "\n", + "\n", + "s2->s2-a0\n", + "\n", + "\n", + "\n", + "\n", + "s2-a1\n", + "\n", + "a1\n", + "\n", + "\n", + "s2->s2-a1\n", + "\n", + "\n", + "\n", + "\n", + "s0-a1->s2\n", + "\n", + "\n", + "p = 1\n", + "\n", + "\n", + "s1\n", + "\n", + "\n", + "s1\n", + "\n", + "\n", + "s1-a0\n", + "\n", + "a0\n", + "\n", + "\n", + "s1->s1-a0\n", + "\n", + "\n", + "\n", + "\n", + "s1-a1\n", + "\n", + "a1\n", + "\n", + "\n", + "s1->s1-a1\n", + "\n", + "\n", + "\n", + "\n", + "s1-a0->s0\n", + "\n", + "\n", + "p = 0.7  reward =5\n", + "\n", + "\n", + "s1-a0->s2\n", + "\n", + "\n", + "p = 0.2\n", + "\n", + "\n", + "s1-a0->s1\n", + "\n", + "\n", + "p = 0.1\n", + "\n", + "\n", + "s1-a1->s2\n", + "\n", + "\n", + "p = 0.05\n", + "\n", + "\n", + "s1-a1->s1\n", + "\n", + "\n", + "p = 0.95\n", + "\n", + "\n", + "s2-a0->s0\n", + "\n", + "\n", + "p = 0.4\n", + "\n", + "\n", + "s2-a0->s1\n", + "\n", + "\n", + "p = 0.6\n", + "\n", + "\n", + "s2-a1->s0\n", + "\n", + "\n", + "p = 0.3  reward =-1\n", + "\n", + "\n", + "s2-a1->s2\n", + "\n", + "\n", + "p = 0.4\n", + "\n", + "\n", + "s2-a1->s1\n", + "\n", + "\n", + "p = 0.3\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "if has_graphviz:\n", + " from mdp import plot_graph, plot_graph_with_state_values, \\\n", + " plot_graph_optimal_strategy_and_state_values\n", + "\n", + " display(plot_graph(mdp))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Value Iteration\n", + "\n", + "Now let's build something to solve this MDP. The simplest algorithm so far is __V__alue __I__teration\n", + "\n", + "Here's the pseudo-code for VI:\n", + "\n", + "---\n", + "\n", + "`1.` Initialize $V^{(0)}(s)=0$, for all $s$\n", + "\n", + "`2.` For $i=0, 1, 2, \\dots$\n", + " \n", + "`3.` $ \\quad V_{(i+1)}(s) = \\max_a \\sum_{s'} P(s' | s,a) \\cdot [ r(s,a,s') + \\gamma V_{i}(s')]$, for all $s$\n", + "\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, let's write a function to compute the state-action value function $Q^{\\pi}$, defined as follows\n", + "\n", + "$$Q_i(s, a) = \\sum_{s'} P(s' | s,a) \\cdot [ r(s,a,s') + \\gamma V_{i}(s')]$$\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.101416Z", + "start_time": "2018-04-02T13:43:17.095468Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "%%writefile mdp_get_action_value.py\n", + "\n", + "def get_action_value(mdp, state_values, state, action, gamma):\n", + " \"\"\" Computes Q(s,a) as in formula above \"\"\"\n", + "\n", + " # YOUR CODE HERE\n", + "\n", + " return < YOUR CODE >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mdp_get_action_value import get_action_value" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.102247Z", + "start_time": "2018-04-02T13:43:05.502Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "test_Vs = {s: i for i, s in enumerate(sorted(mdp.get_all_states()))}\n", + "assert np.allclose(get_action_value(mdp, test_Vs, 's2', 'a1', 0.9), 0.69)\n", + "assert np.allclose(get_action_value(mdp, test_Vs, 's1', 'a0', 0.9), 3.95)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using $Q(s,a)$ we can now define the \"next\" V(s) for value iteration.\n", + " $$V_{(i+1)}(s) = \\max_a \\sum_{s'} P(s' | s,a) \\cdot [ r(s,a,s') + \\gamma V_{i}(s')] = \\max_a Q_i(s,a)$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.103358Z", + "start_time": "2018-04-02T13:43:05.506Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "def get_new_state_value(mdp, state_values, state, gamma):\n", + " \"\"\" Computes next V(s) as in formula above. Please do not change state_values in process. \"\"\"\n", + " if mdp.is_terminal(state):\n", + " return 0\n", + "\n", + " # \n", + " return" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.104340Z", + "start_time": "2018-04-02T13:43:05.510Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "test_Vs_copy = dict(test_Vs)\n", + "assert np.allclose(get_new_state_value(mdp, test_Vs, 's0', 0.9), 1.8)\n", + "assert np.allclose(get_new_state_value(mdp, test_Vs, 's2', 0.9), 0.69)\n", + "assert test_Vs == test_Vs_copy, \"please do not change state_values in get_new_state_value\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, let's combine everything we wrote into a working value iteration algo." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:44:09.793405Z", + "start_time": "2018-04-02T13:44:09.770623Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "# parameters\n", + "gamma = 0.9 # discount for MDP\n", + "num_iter = 100 # maximum iterations, excluding initialization\n", + "# stop VI if new values are this close to old values (or closer)\n", + "min_difference = 0.001\n", + "\n", + "# initialize V(s)\n", + "state_values = {s: 0 for s in mdp.get_all_states()}\n", + "\n", + "if has_graphviz:\n", + " display(plot_graph_with_state_values(mdp, state_values))\n", + "\n", + "for i in range(num_iter):\n", + "\n", + " # Compute new state values using the functions you defined above.\n", + " # It must be a dict {state : float V_new(state)}\n", + " new_state_values = \n", + "\n", + " assert isinstance(new_state_values, dict)\n", + "\n", + " # Compute difference\n", + " diff = max(abs(new_state_values[s] - state_values[s])\n", + " for s in mdp.get_all_states())\n", + " print(\"iter %4i | diff: %6.5f | \" % (i, diff), end=\"\")\n", + " print(' '.join(\"V(%s) = %.3f\" % (s, v)\n", + " for s, v in state_values.items()), end='\\n\\n')\n", + " state_values = new_state_values\n", + "\n", + " if diff < min_difference:\n", + " print(\"Terminated\")\n", + " break" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "if has_graphviz:\n", + " display(plot_graph_with_state_values(mdp, state_values))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.106395Z", + "start_time": "2018-04-02T13:43:05.522Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "print(\"Final state values:\", state_values)\n", + "\n", + "assert abs(state_values['s0'] - 8.032) < 0.01\n", + "assert abs(state_values['s1'] - 11.169) < 0.01\n", + "assert abs(state_values['s2'] - 8.921) < 0.01" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's use those $V^{*}(s)$ to find optimal actions in each state\n", + "\n", + " $$\\pi^*(s) = argmax_a \\sum_{s'} P(s' | s,a) \\cdot [ r(s,a,s') + \\gamma V_{i}(s')] = argmax_a Q_i(s,a)$$\n", + " \n", + "The only difference vs V(s) is that here we take not max but argmax: find action such with maximum Q(s,a)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.107338Z", + "start_time": "2018-04-02T13:43:05.525Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "def get_optimal_action(mdp, state_values, state, gamma=0.9):\n", + " \"\"\" Finds optimal action using formula above. \"\"\"\n", + " if mdp.is_terminal(state):\n", + " return None\n", + "\n", + " # \n", + "\n", + " return < YOUR CODE >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.108149Z", + "start_time": "2018-04-02T13:43:05.530Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "assert get_optimal_action(mdp, state_values, 's0', gamma) == 'a1'\n", + "assert get_optimal_action(mdp, state_values, 's1', gamma) == 'a0'\n", + "assert get_optimal_action(mdp, state_values, 's2', gamma) == 'a0'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:44:05.017823Z", + "start_time": "2018-04-02T13:44:04.962755Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "if has_graphviz:\n", + " try:\n", + " display(plot_graph_optimal_strategy_and_state_values(mdp, state_values))\n", + " except ImportError:\n", + " raise ImportError(\"Run the cell that starts with \\\"%%writefile mdp_get_action_value.py\\\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.110002Z", + "start_time": "2018-04-02T13:43:05.538Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "# Measure agent's average reward\n", + "\n", + "s = mdp.reset()\n", + "rewards = []\n", + "for _ in range(10000):\n", + " s, r, done, _ = mdp.step(get_optimal_action(mdp, state_values, s, gamma))\n", + " rewards.append(r)\n", + "\n", + "print(\"average reward: \", np.mean(rewards))\n", + "\n", + "assert(0.85 < np.mean(rewards) < 1.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Frozen lake" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.110991Z", + "start_time": "2018-04-02T13:43:05.541Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "from mdp import FrozenLakeEnv\n", + "mdp = FrozenLakeEnv(slip_chance=0)\n", + "\n", + "mdp.render()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.111919Z", + "start_time": "2018-04-02T13:43:05.545Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "def value_iteration(mdp, state_values=None, gamma=0.9, num_iter=1000, min_difference=1e-5):\n", + " \"\"\" performs num_iter value iteration steps starting from state_values. Same as before but in a function \"\"\"\n", + " state_values = state_values or {s: 0 for s in mdp.get_all_states()}\n", + " for i in range(num_iter):\n", + "\n", + " # Compute new state values using the functions you defined above. It must be a dict {state : new_V(state)}\n", + " new_state_values = \n", + "\n", + " assert isinstance(new_state_values, dict)\n", + "\n", + " # Compute difference\n", + " diff = max(abs(new_state_values[s] - state_values[s])\n", + " for s in mdp.get_all_states())\n", + "\n", + " print(\"iter %4i | diff: %6.5f | V(start): %.3f \" %\n", + " (i, diff, new_state_values[mdp._initial_state]))\n", + "\n", + " state_values = new_state_values\n", + " if diff < min_difference:\n", + " break\n", + "\n", + " return state_values" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.112871Z", + "start_time": "2018-04-02T13:43:05.548Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "state_values = value_iteration(mdp)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.114062Z", + "start_time": "2018-04-02T13:43:05.552Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "s = mdp.reset()\n", + "mdp.render()\n", + "for t in range(100):\n", + " a = get_optimal_action(mdp, state_values, s, gamma)\n", + " print(a, end='\\n\\n')\n", + " s, r, done, _ = mdp.step(a)\n", + " mdp.render()\n", + " if done:\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's visualize!\n", + "\n", + "It's usually interesting to see what your algorithm actually learned under the hood. To do so, we'll plot state value functions and optimal actions at each VI step." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.115092Z", + "start_time": "2018-04-02T13:43:05.556Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "\n", + "def draw_policy(mdp, state_values):\n", + " plt.figure(figsize=(3, 3))\n", + " h, w = mdp.desc.shape\n", + " states = sorted(mdp.get_all_states())\n", + " V = np.array([state_values[s] for s in states])\n", + " Pi = {s: get_optimal_action(mdp, state_values, s, gamma) for s in states}\n", + " plt.imshow(V.reshape(w, h), cmap='gray', interpolation='none', clim=(0, 1))\n", + " ax = plt.gca()\n", + " ax.set_xticks(np.arange(h)-.5)\n", + " ax.set_yticks(np.arange(w)-.5)\n", + " ax.set_xticklabels([])\n", + " ax.set_yticklabels([])\n", + " Y, X = np.mgrid[0:4, 0:4]\n", + " a2uv = {'left': (-1, 0), 'down': (0, -1), 'right': (1, 0), 'up': (-1, 0)}\n", + " for y in range(h):\n", + " for x in range(w):\n", + " plt.text(x, y, str(mdp.desc[y, x].item()),\n", + " color='g', size=12, verticalalignment='center',\n", + " horizontalalignment='center', fontweight='bold')\n", + " a = Pi[y, x]\n", + " if a is None:\n", + " continue\n", + " u, v = a2uv[a]\n", + " plt.arrow(x, y, u*.3, -v*.3, color='m',\n", + " head_width=0.1, head_length=0.1)\n", + " plt.grid(color='b', lw=2, ls='-')\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.116164Z", + "start_time": "2018-04-02T13:43:05.560Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "state_values = {s: 0 for s in mdp.get_all_states()}\n", + "\n", + "for i in range(10):\n", + " print(\"after iteration %i\" % i)\n", + " state_values = value_iteration(mdp, state_values, num_iter=1)\n", + " draw_policy(mdp, state_values)\n", + "# please ignore iter 0 at each step" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.117143Z", + "start_time": "2018-04-02T13:43:05.563Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "from IPython.display import clear_output\n", + "from time import sleep\n", + "mdp = FrozenLakeEnv(map_name='8x8', slip_chance=0.1)\n", + "state_values = {s: 0 for s in mdp.get_all_states()}\n", + "\n", + "for i in range(30):\n", + " clear_output(True)\n", + " print(\"after iteration %i\" % i)\n", + " state_values = value_iteration(mdp, state_values, num_iter=1)\n", + " draw_policy(mdp, state_values)\n", + " sleep(0.5)\n", + "# please ignore iter 0 at each step" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Massive tests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.118218Z", + "start_time": "2018-04-02T13:43:05.568Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "mdp = FrozenLakeEnv(slip_chance=0)\n", + "state_values = value_iteration(mdp)\n", + "\n", + "total_rewards = []\n", + "for game_i in range(1000):\n", + " s = mdp.reset()\n", + " rewards = []\n", + " for t in range(100):\n", + " s, r, done, _ = mdp.step(\n", + " get_optimal_action(mdp, state_values, s, gamma))\n", + " rewards.append(r)\n", + " if done:\n", + " break\n", + " total_rewards.append(np.sum(rewards))\n", + "\n", + "print(\"average reward: \", np.mean(total_rewards))\n", + "assert(1.0 <= np.mean(total_rewards) <= 1.0)\n", + "print(\"Well done!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.119075Z", + "start_time": "2018-04-02T13:43:05.571Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "# Measure agent's average reward\n", + "mdp = FrozenLakeEnv(slip_chance=0.1)\n", + "state_values = value_iteration(mdp)\n", + "\n", + "total_rewards = []\n", + "for game_i in range(1000):\n", + " s = mdp.reset()\n", + " rewards = []\n", + " for t in range(100):\n", + " s, r, done, _ = mdp.step(\n", + " get_optimal_action(mdp, state_values, s, gamma))\n", + " rewards.append(r)\n", + " if done:\n", + " break\n", + " total_rewards.append(np.sum(rewards))\n", + "\n", + "print(\"average reward: \", np.mean(total_rewards))\n", + "assert(0.8 <= np.mean(total_rewards) <= 0.95)\n", + "print(\"Well done!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.120316Z", + "start_time": "2018-04-02T13:43:05.574Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "# Measure agent's average reward\n", + "mdp = FrozenLakeEnv(slip_chance=0.25)\n", + "state_values = value_iteration(mdp)\n", + "\n", + "total_rewards = []\n", + "for game_i in range(1000):\n", + " s = mdp.reset()\n", + " rewards = []\n", + " for t in range(100):\n", + " s, r, done, _ = mdp.step(\n", + " get_optimal_action(mdp, state_values, s, gamma))\n", + " rewards.append(r)\n", + " if done:\n", + " break\n", + " total_rewards.append(np.sum(rewards))\n", + "\n", + "print(\"average reward: \", np.mean(total_rewards))\n", + "assert(0.6 <= np.mean(total_rewards) <= 0.7)\n", + "print(\"Well done!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.121544Z", + "start_time": "2018-04-02T13:43:05.578Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "# Measure agent's average reward\n", + "mdp = FrozenLakeEnv(slip_chance=0.2, map_name='8x8')\n", + "state_values = value_iteration(mdp)\n", + "\n", + "total_rewards = []\n", + "for game_i in range(1000):\n", + " s = mdp.reset()\n", + " rewards = []\n", + " for t in range(100):\n", + " s, r, done, _ = mdp.step(\n", + " get_optimal_action(mdp, state_values, s, gamma))\n", + " rewards.append(r)\n", + " if done:\n", + " break\n", + " total_rewards.append(np.sum(rewards))\n", + "\n", + "print(\"average reward: \", np.mean(total_rewards))\n", + "assert(0.6 <= np.mean(total_rewards) <= 0.8)\n", + "print(\"Well done!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bonus area" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Bonus 1 - find an MDP for which value iteration takes long to converge (2+ pts)\n", + "\n", + "When we ran value iteration on the small frozen lake problem, the last iteration where an action changed was iteration 6--i.e., value iteration computed the optimal policy at iteration 6. Are there any guarantees regarding how many iterations it'll take value iteration to compute the optimal policy? There are no such guarantees without additional assumptions--we can construct the MDP in such a way that the greedy policy will change after arbitrarily many iterations.\n", + "\n", + "Your task: define an MDP with at most 3 states and 2 actions, such that when you run value iteration, the optimal action changes at iteration >= 50. Use discount=0.95. (However, note that the discount doesn't matter here--you can construct an appropriate MDP with any discount.)\n", + "\n", + "Note: value function must change at least once after iteration >=50, not necessarily change on every iteration till >=50." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.122424Z", + "start_time": "2018-04-02T13:43:05.582Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "transition_probs = {\n", + " < YOUR CODE >\n", + "}\n", + "rewards = {\n", + " < YOUR CODE >\n", + "}\n", + "\n", + "from mdp import MDP\n", + "from numpy import random\n", + "mdp = MDP(transition_probs, rewards, initial_state=random.choice(tuple(transition_probs.keys())))\n", + "# Feel free to change the initial_state" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.123825Z", + "start_time": "2018-04-02T13:43:05.586Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "state_values = {s: 0 for s in mdp.get_all_states()}\n", + "policy = np.array([get_optimal_action(mdp, state_values, state, gamma)\n", + " for state in sorted(mdp.get_all_states())])\n", + "\n", + "for i in range(100):\n", + " print(\"after iteration %i\" % i)\n", + " state_values = value_iteration(mdp, state_values, num_iter=1)\n", + "\n", + " new_policy = np.array([get_optimal_action(mdp, state_values, state, gamma)\n", + " for state in sorted(mdp.get_all_states())])\n", + "\n", + " n_changes = (policy != new_policy).sum()\n", + " print(\"N actions changed = %i \\n\" % n_changes)\n", + " policy = new_policy\n", + "\n", + "# please ignore iter 0 at each step" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Bonus 2 - Policy Iteration (3+ points)\n", + "\n", + "Let's implement exact policy iteration (PI), which has the following pseudocode:\n", + "\n", + "---\n", + "Initialize $\\pi_0$ `// random or fixed action`\n", + "\n", + "For $n=0, 1, 2, \\dots$\n", + "- Compute the state-value function $V^{\\pi_{n}}$\n", + "- Using $V^{\\pi_{n}}$, compute the state-action-value function $Q^{\\pi_{n}}$\n", + "- Compute new policy $\\pi_{n+1}(s) = \\operatorname*{argmax}_a Q^{\\pi_{n}}(s,a)$\n", + "---\n", + "\n", + "Unlike VI, policy iteration has to maintain a policy - chosen actions from all states - and estimate $V^{\\pi_{n}}$ based on this policy. It only changes policy once values converged.\n", + "\n", + "\n", + "Below are a few helpers that you may or may not use in your implementation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.125320Z", + "start_time": "2018-04-02T13:43:05.590Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "transition_probs = {\n", + " 's0': {\n", + " 'a0': {'s0': 0.5, 's2': 0.5},\n", + " 'a1': {'s2': 1}\n", + " },\n", + " 's1': {\n", + " 'a0': {'s0': 0.7, 's1': 0.1, 's2': 0.2},\n", + " 'a1': {'s1': 0.95, 's2': 0.05}\n", + " },\n", + " 's2': {\n", + " 'a0': {'s0': 0.4, 's1': 0.6},\n", + " 'a1': {'s0': 0.3, 's1': 0.3, 's2': 0.4}\n", + " }\n", + "}\n", + "rewards = {\n", + " 's1': {'a0': {'s0': +5}},\n", + " 's2': {'a1': {'s0': -1}}\n", + "}\n", + "\n", + "from mdp import MDP\n", + "mdp = MDP(transition_probs, rewards, initial_state='s0')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's write a function called `compute_vpi` that computes the state-value function $V^{\\pi}$ for an arbitrary policy $\\pi$.\n", + "\n", + "Unlike VI, this time you must find the exact solution, not just a single iteration.\n", + "\n", + "Recall that $V^{\\pi}$ satisfies the following linear equation:\n", + "$$V^{\\pi}(s) = \\sum_{s'} P(s,\\pi(s),s')[ R(s,\\pi(s),s') + \\gamma V^{\\pi}(s')]$$\n", + "\n", + "You'll have to solve a linear system in your code. (Find an exact solution, e.g., with `np.linalg.solve`.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.126518Z", + "start_time": "2018-04-02T13:43:05.593Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "def compute_vpi(mdp, policy, gamma):\n", + " \"\"\"\n", + " Computes V^pi(s) FOR ALL STATES under given policy.\n", + " :param policy: a dict of currently chosen actions {s : a}\n", + " :returns: a dict {state : V^pi(state) for all states}\n", + " \"\"\"\n", + " # YOUR CODE HERE\n", + " return < YOUR CODE >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.127349Z", + "start_time": "2018-04-02T13:43:05.597Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "test_policy = {s: np.random.choice(\n", + " mdp.get_possible_actions(s)) for s in mdp.get_all_states()}\n", + "new_vpi = compute_vpi(mdp, test_policy, gamma)\n", + "\n", + "print(new_vpi)\n", + "\n", + "assert type(\n", + " new_vpi) is dict, \"compute_vpi must return a dict {state : V^pi(state) for all states}\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once we've got new state values, it's time to update our policy." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.128415Z", + "start_time": "2018-04-02T13:43:05.601Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "def compute_new_policy(mdp, vpi, gamma):\n", + " \"\"\"\n", + " Computes new policy as argmax of state values\n", + " :param vpi: a dict {state : V^pi(state) for all states}\n", + " :returns: a dict {state : optimal action for all states}\n", + " \"\"\"\n", + " \n", + " return < YOUR CODE >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.129416Z", + "start_time": "2018-04-02T13:43:05.604Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "new_policy = compute_new_policy(mdp, new_vpi, gamma)\n", + "\n", + "print(new_policy)\n", + "\n", + "assert type(\n", + " new_policy) is dict, \"compute_new_policy must return a dict {state : optimal action for all states}\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Main loop__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.130183Z", + "start_time": "2018-04-02T13:43:05.608Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "def policy_iteration(mdp, policy=None, gamma=0.9, num_iter=1000, min_difference=1e-5):\n", + " \"\"\" \n", + " Run the policy iteration loop for num_iter iterations or till difference between V(s) is below min_difference.\n", + " If policy is not given, initialize it at random.\n", + " \"\"\"\n", + " < A WHOLE LOT OF YOUR CODE >\n", + "\n", + " return state_values, policy" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Your PI Results__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2018-04-02T13:43:17.130926Z", + "start_time": "2018-04-02T13:43:05.612Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "< Compare PI and VI on the MDP from bonus 1, then on small & large FrozenLake >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.6.8" + }, + "toc": { + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "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 + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week3_model_free/README.md b/week03_model_free/README.md similarity index 74% rename from week3_model_free/README.md rename to week03_model_free/README.md index 920f87c3a..c197e9532 100644 --- a/week3_model_free/README.md +++ b/week03_model_free/README.md @@ -11,15 +11,24 @@ - Blog post on q-learning Vs SARSA - [url](https://studywolf.wordpress.com/2013/07/01/reinforcement-learning-sarsa-vs-q-learning/) ### More materials -* N-step temporal difference from Sutton's book - [suttonbook](http://incompleteideas.net/book/bookdraft2018jan1.pdf) __chapter 7__ -* Eligibility traces from Sutton's book - [suttonbook](http://incompleteideas.net/book/bookdraft2018jan1.pdf) __chapter 12__ +* N-step temporal difference from Sutton's book - [suttonbook](http://incompleteideas.net/book/RLbook2018.pdf) __chapter 7__ +* Eligibility traces from Sutton's book - [suttonbook](http://incompleteideas.net/book/RLbook2018.pdf) __chapter 12__ * Blog post on eligibility traces - [url](http://pierrelucbacon.com/traces/) ### Assignments -Just as usual, start with `seminar_qlearning.ipynb` and then proceed to `homework.ipynb`. +Just as usual, start with +- `seminar_qlearning.ipynb` +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week03_model_free/seminar_qlearning.ipynb) +and then proceed to +- `homework.ipynb` +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week03_model_free/homework.ipynb) + +Please pay attention for uncommenting first lines in code if you use Colab. + +--- (optional) If you're running on a local machine (e.g. your pc) with python2, you can also try `seminar_py2`. It has some neat RL problems with cool visualizations. ### ./seminar_py2 @@ -29,7 +38,7 @@ This homework assignment works on __python2 only__. If you stick to py3, conside This homework also requires some physical display (e.g. laptop monitor). It won't work on binder VM / headless server. Please run it on laptop or consider ./seminar_alternative -* You need to implement **QLearining** algorithm. If you're running go to ```seminar_main/``` folder and open file ```qlearningAgent.py```. +* You need to implement **QLearining** algorithm. Once you're done, run use those commands: ``` @@ -40,7 +49,7 @@ python pacman.py -p PacmanQAgent -x 5000 -n 5010 -l smallGrid # example * Make sure you can tune agent to beat ./run_crawler.sh * on windows, just run `python crawler.py` from cmd in the project directory * other ./run* files are mostly for your amusement. - * ./run_pacman.sh will need more epochs to converge, see [comments](https://github.com/yandexdataschool/Practical_RL/blob/master/week3_model_free/seminar_py2/run_pacman.sh) + * ./run_pacman.sh will need more epochs to converge, see [comments](https://github.com/yandexdataschool/Practical_RL/blob/spring19/week03_model_free/seminar_py2/run_pacman.sh) * on windows, just copy the type `python pacman.py -p PacmanQAgent -x 2000 -n 2010 -l smallGrid` in cmd from assignemnt dir (YSDA/HSE) Please submit only qlearningAgents.py file and include a brief text report as comments in it. diff --git a/week03_model_free/homework.ipynb b/week03_model_free/homework.ipynb new file mode 100644 index 000000000..aaaeb0cd7 --- /dev/null +++ b/week03_model_free/homework.ipynb @@ -0,0 +1,819 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Part I: On-policy learning and SARSA (3 points)](#Part-I:-On-policy-learning-and-SARSA-(3-points))\n", + "\n", + "[Part II: Experience replay (4 points)](#Part-II:-experience-replay-(4-points))\n", + "\n", + "[Bonus I: TD($ \\lambda $) (5+ points)](#Bonus-I:-TD($\\lambda$)-(5+-points))\n", + "\n", + "[Bonus II: More pacman (5+ points)](#Bonus-II:-More-pacman-(5+-points))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part I: On-policy learning and SARSA (3 points)\n", + "\n", + "_This notebook builds upon `qlearning.ipynb`, or to be exact, generating qlearning.py._\n", + "\n", + "The policy we're gonna use is epsilon-greedy policy, where agent takes optimal action with probability $(1-\\epsilon)$, otherwise samples action at random. Note that agent __can__ occasionally sample optimal action during random sampling by pure chance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# In google collab, uncomment this:\n", + "# !wget https://bit.ly/2FMJP5K -q -O setup.py\n", + "# !bash setup.py 2>&1 1>stdout.log | tee stderr.log\n", + "\n", + "# This code creates a virtual display to draw game images on.\n", + "# If you are running locally, just ignore it\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1\n", + "\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now you can use code, generated from seminar `seminar_qlearning.ipynb`. Or just copy&paste it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%writefile qlearning.py\n", + "from collections import defaultdict\n", + "import random\n", + "import math\n", + "import numpy as np\n", + "\n", + "\n", + "class QLearningAgent:\n", + " def __init__(self, alpha, epsilon, discount, get_legal_actions):\n", + " \"\"\"\n", + " Q-Learning Agent\n", + " based on https://inst.eecs.berkeley.edu/~cs188/sp19/projects.html\n", + " Instance variables you have access to\n", + " - self.epsilon (exploration prob)\n", + " - self.alpha (learning rate)\n", + " - self.discount (discount rate aka gamma)\n", + "\n", + " Functions you should use\n", + " - self.get_legal_actions(state) {state, hashable -> list of actions, each is hashable}\n", + " which returns legal actions for a state\n", + " - self.get_qvalue(state,action)\n", + " which returns Q(state,action)\n", + " - self.set_qvalue(state,action,value)\n", + " which sets Q(state,action) := value\n", + "\n", + " !!!Important!!!\n", + " Note: please avoid using self._qValues directly. \n", + " There's a special self.get_qvalue/set_qvalue for that.\n", + " \"\"\"\n", + "\n", + " self.get_legal_actions = get_legal_actions\n", + " self._qvalues = defaultdict(lambda: defaultdict(lambda: 0))\n", + " self.alpha = alpha\n", + " self.epsilon = epsilon\n", + " self.discount = discount\n", + "\n", + " def get_qvalue(self, state, action):\n", + " \"\"\" Returns Q(state,action) \"\"\"\n", + " return self._qvalues[state][action]\n", + "\n", + " def set_qvalue(self, state, action, value):\n", + " \"\"\" Sets the Qvalue for [state,action] to the given value \"\"\"\n", + " self._qvalues[state][action] = value\n", + "\n", + " #---------------------START OF YOUR CODE---------------------#\n", + "\n", + " def get_value(self, state):\n", + " \"\"\"\n", + " Compute your agent's estimate of V(s) using current q-values\n", + " V(s) = max_over_action Q(state,action) over possible actions.\n", + " Note: please take into account that q-values can be negative.\n", + " \"\"\"\n", + " possible_actions = self.get_legal_actions(state)\n", + "\n", + " # If there are no legal actions, return 0.0\n", + " if len(possible_actions) == 0:\n", + " return 0.0\n", + "\n", + " \n", + "\n", + " return value\n", + "\n", + " def update(self, state, action, reward, next_state):\n", + " \"\"\"\n", + " You should do your Q-Value update here:\n", + " Q(s,a) := (1 - alpha) * Q(s,a) + alpha * (r + gamma * V(s'))\n", + " \"\"\"\n", + "\n", + " # agent parameters\n", + " gamma = self.discount\n", + " learning_rate = self.alpha\n", + "\n", + " \n", + "\n", + " self.set_qvalue(state, action, < YOUR_QVALUE > )\n", + "\n", + " def get_best_action(self, state):\n", + " \"\"\"\n", + " Compute the best action to take in a state (using current q-values). \n", + " \"\"\"\n", + " possible_actions = self.get_legal_actions(state)\n", + "\n", + " # If there are no legal actions, return None\n", + " if len(possible_actions) == 0:\n", + " return None\n", + "\n", + " \n", + "\n", + " return best_action\n", + "\n", + " def get_action(self, state):\n", + " \"\"\"\n", + " Compute the action to take in the current state, including exploration. \n", + " With probability self.epsilon, we should take a random action.\n", + " otherwise - the best policy action (self.getPolicy).\n", + "\n", + " Note: To pick randomly from a list, use random.choice(list). \n", + " To pick True or False with a given probablity, generate uniform number in [0, 1]\n", + " and compare it with your probability\n", + " \"\"\"\n", + "\n", + " # Pick Action\n", + " possible_actions = self.get_legal_actions(state)\n", + " action = None\n", + "\n", + " # If there are no legal actions, return None\n", + " if len(possible_actions) == 0:\n", + " return None\n", + "\n", + " # agent parameters:\n", + " epsilon = self.epsilon\n", + "\n", + " \n", + "\n", + " return chosen_action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from qlearning import QLearningAgent\n", + "\n", + "\n", + "class EVSarsaAgent(QLearningAgent):\n", + " \"\"\" \n", + " An agent that changes some of q-learning functions to implement Expected Value SARSA. \n", + " Note: this demo assumes that your implementation of QLearningAgent.update uses get_value(next_state).\n", + " If it doesn't, please add\n", + " def update(self, state, action, reward, next_state):\n", + " and implement it for Expected Value SARSA's V(s')\n", + " \"\"\"\n", + "\n", + " def get_value(self, state):\n", + " \"\"\" \n", + " Returns Vpi for current state under epsilon-greedy policy:\n", + " V_{pi}(s) = sum _{over a_i} {pi(a_i | s) * Q(s, a_i)}\n", + "\n", + " Hint: all other methods from QLearningAgent are still accessible.\n", + " \"\"\"\n", + " epsilon = self.epsilon\n", + " possible_actions = self.get_legal_actions(state)\n", + "\n", + " # If there are no legal actions, return 0.0\n", + " if len(possible_actions) == 0:\n", + " return 0.0\n", + "\n", + " \n", + "\n", + " return state_value" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Cliff World\n", + "\n", + "Let's now see how our algorithm compares against q-learning in case where we force agent to explore all the time.\n", + "\n", + "\n", + "
image by cs188
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import gym\n", + "import gym.envs.toy_text\n", + "env = gym.envs.toy_text.CliffWalkingEnv()\n", + "n_actions = env.action_space.n\n", + "\n", + "print(env.__doc__)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Our cliffworld has one difference from what's on the image: there is no wall.\n", + "# Agent can choose to go as close to the cliff as it wishes. x:start, T:exit, C:cliff, o: flat ground\n", + "env.render()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def play_and_train(env, agent, t_max=10**4):\n", + " \"\"\"This function should \n", + " - run a full game, actions given by agent.getAction(s)\n", + " - train agent using agent.update(...) whenever possible\n", + " - return total reward\"\"\"\n", + " total_reward = 0.0\n", + " s = env.reset()\n", + "\n", + " for t in range(t_max):\n", + " a = agent.get_action(s)\n", + "\n", + " next_s, r, done, _ = env.step(a)\n", + " agent.update(s, a, r, next_s)\n", + "\n", + " s = next_s\n", + " total_reward += r\n", + " if done:\n", + " break\n", + "\n", + " return total_reward" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from qlearning import QLearningAgent\n", + "\n", + "agent_sarsa = EVSarsaAgent(alpha=0.25, epsilon=0.2, discount=0.99,\n", + " get_legal_actions=lambda s: range(n_actions))\n", + "\n", + "agent_ql = QLearningAgent(alpha=0.25, epsilon=0.2, discount=0.99,\n", + " get_legal_actions=lambda s: range(n_actions))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from IPython.display import clear_output\n", + "from pandas import DataFrame\n", + "\n", + "\n", + "def moving_average(x, span=100): return DataFrame(\n", + " {'x': np.asarray(x)}).x.ewm(span=span).mean().values\n", + "\n", + "\n", + "rewards_sarsa, rewards_ql = [], []\n", + "\n", + "for i in range(5000):\n", + " rewards_sarsa.append(play_and_train(env, agent_sarsa))\n", + " rewards_ql.append(play_and_train(env, agent_ql))\n", + " # Note: agent.epsilon stays constant\n", + "\n", + " if i % 100 == 0:\n", + " clear_output(True)\n", + " print('EVSARSA mean reward =', np.mean(rewards_sarsa[-100:]))\n", + " print('QLEARNING mean reward =', np.mean(rewards_ql[-100:]))\n", + " plt.title(\"epsilon = %s\" % agent_ql.epsilon)\n", + " plt.plot(moving_average(rewards_sarsa), label='ev_sarsa')\n", + " plt.plot(moving_average(rewards_ql), label='qlearning')\n", + " plt.grid()\n", + " plt.legend()\n", + " plt.ylim(-500, 0)\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's now see what did the algorithms learn by visualizing their actions at every state." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def draw_policy(env, agent):\n", + " \"\"\" Prints CliffWalkingEnv policy with arrows. Hard-coded. \"\"\"\n", + " n_rows, n_cols = env._cliff.shape\n", + "\n", + " actions = '^>v<'\n", + "\n", + " for yi in range(n_rows):\n", + " for xi in range(n_cols):\n", + " if env._cliff[yi, xi]:\n", + " print(\" C \", end='')\n", + " elif (yi * n_cols + xi) == env.start_state_index:\n", + " print(\" X \", end='')\n", + " elif (yi * n_cols + xi) == n_rows * n_cols - 1:\n", + " print(\" T \", end='')\n", + " else:\n", + " print(\" %s \" %\n", + " actions[agent.get_best_action(yi * n_cols + xi)], end='')\n", + " print()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "print(\"Q-Learning\")\n", + "draw_policy(env, agent_ql)\n", + "\n", + "print(\"SARSA\")\n", + "draw_policy(env, agent_sarsa)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "### More on SARSA\n", + "\n", + "Here are some of the things you can do if you feel like it:\n", + "\n", + "* Play with epsilon. See learned how policies change if you set epsilon to higher/lower values (e.g. 0.75).\n", + "* Expected Value SASRSA for softmax policy __(2pts)__:\n", + "$$ \\pi(a_i|s) = softmax({Q(s,a_i) \\over \\tau}) = {e ^ {Q(s,a_i)/ \\tau} \\over {\\sum_{a_j} e ^{Q(s,a_j) / \\tau }}} $$\n", + "* Implement N-step algorithms and TD($\\lambda$): see [Sutton's book](http://incompleteideas.net/book/bookdraft2018jan1.pdf) chapter 7 and chapter 12.\n", + "* Use those algorithms to train on CartPole in previous / next assignment for this week." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part II: experience replay (4 points)\n", + "\n", + "There's a powerful technique that you can use to improve sample efficiency for off-policy algorithms: [spoiler] Experience replay :)\n", + "\n", + "The catch is that you can train Q-learning and EV-SARSA on `` tuples even if they aren't sampled under current agent's policy. So here's what we're gonna do:\n", + "\n", + "\n", + "\n", + "#### Training with experience replay\n", + "1. Play game, sample ``.\n", + "2. Update q-values based on ``.\n", + "3. Store `` transition in a buffer. \n", + " 3. If buffer is full, delete earliest data.\n", + "4. Sample K such transitions from that buffer and update q-values based on them.\n", + "\n", + "\n", + "To enable such training, first we must implement a memory structure that would act like such a buffer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# In google collab, uncomment this:\n", + "# !wget https://bit.ly/2FMJP5K -q -O setup.py\n", + "# !bash setup.py 2>&1 1>stdout.log | tee stderr.log\n", + "\n", + "# This code creates a virtual display to draw game images on.\n", + "# If you are running locally, just ignore it\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "from IPython.display import clear_output" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import random\n", + "\n", + "\n", + "class ReplayBuffer(object):\n", + " def __init__(self, size):\n", + " \"\"\"\n", + " Create Replay buffer.\n", + " Parameters\n", + " ----------\n", + " size: int\n", + " Max number of transitions to store in the buffer. When the buffer\n", + " overflows the old memories are dropped.\n", + "\n", + " Note: for this assignment you can pick any data structure you want.\n", + " If you want to keep it simple, you can store a list of tuples of (s, a, r, s') in self._storage\n", + " However you may find out there are faster and/or more memory-efficient ways to do so.\n", + " \"\"\"\n", + " self._storage = []\n", + " self._maxsize = size\n", + "\n", + " # OPTIONAL: YOUR CODE\n", + "\n", + " def __len__(self):\n", + " return len(self._storage)\n", + "\n", + " def add(self, obs_t, action, reward, obs_tp1, done):\n", + " '''\n", + " Make sure, _storage will not exceed _maxsize. \n", + " Make sure, FIFO rule is being followed: the oldest examples has to be removed earlier\n", + " '''\n", + " data = (obs_t, action, reward, obs_tp1, done)\n", + "\n", + " # add data to storage\n", + " \n", + "\n", + " def sample(self, batch_size):\n", + " \"\"\"Sample a batch of experiences.\n", + " Parameters\n", + " ----------\n", + " batch_size: int\n", + " How many transitions to sample.\n", + " Returns\n", + " -------\n", + " obs_batch: np.array\n", + " batch of observations\n", + " act_batch: np.array\n", + " batch of actions executed given obs_batch\n", + " rew_batch: np.array\n", + " rewards received as results of executing act_batch\n", + " next_obs_batch: np.array\n", + " next set of observations seen after executing act_batch\n", + " done_mask: np.array\n", + " done_mask[i] = 1 if executing act_batch[i] resulted in\n", + " the end of an episode and 0 otherwise.\n", + " \"\"\"\n", + " idxes = \n", + "\n", + " # collect for each index\n", + " \n", + "\n", + " return np.array( < states > ), np.array( < actions > ), np.array( < rewards > ), np.array( < next_states > ), np.array( < is_done > )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Some tests to make sure your buffer works right" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def obj2arrays(obj):\n", + " for x in obj:\n", + " yield np.array([x])\n", + "\n", + "def obj2sampled(obj):\n", + " return tuple(obj2arrays(obj))\n", + "\n", + "replay = ReplayBuffer(2)\n", + "obj1 = (0, 1, 2, 3, True)\n", + "obj2 = (4, 5, 6, 7, False)\n", + "replay.add(*obj1)\n", + "assert replay.sample(\n", + " 1) == obj2sampled(obj1), \"If there's just one object in buffer, it must be retrieved by buf.sample(1)\"\n", + "replay.add(*obj2)\n", + "assert len(replay) == 2, \"Please make sure __len__ methods works as intended.\"\n", + "replay.add(*obj2)\n", + "assert len(replay) == 2, \"When buffer is at max capacity, replace objects instead of adding new ones.\"\n", + "assert tuple(np.unique(a) for a in replay.sample(100)) == obj2sampled(obj2)\n", + "replay.add(*obj1)\n", + "assert max(len(np.unique(a)) for a in replay.sample(100)) == 2\n", + "replay.add(*obj1)\n", + "assert tuple(np.unique(a) for a in replay.sample(100)) == obj2sampled(obj1)\n", + "print(\"Success!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's use this buffer to improve training:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import gym\n", + "from qlearning import QLearningAgent\n", + "\n", + "env = gym.make(\"Taxi-v2\")\n", + "n_actions = env.action_space.n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def play_and_train_with_replay(env, agent, replay=None,\n", + " t_max=10**4, replay_batch_size=32):\n", + " \"\"\"\n", + " This function should \n", + " - run a full game, actions given by agent.getAction(s)\n", + " - train agent using agent.update(...) whenever possible\n", + " - return total reward\n", + " :param replay: ReplayBuffer where agent can store and sample (s,a,r,s',done) tuples.\n", + " If None, do not use experience replay\n", + " \"\"\"\n", + " total_reward = 0.0\n", + " s = env.reset()\n", + "\n", + " for t in range(t_max):\n", + " # get agent to pick action given state s\n", + " a = \n", + "\n", + " next_s, r, done, _ = env.step(a)\n", + "\n", + " # update agent on current transition. Use agent.update\n", + " \n", + "\n", + " if replay is not None:\n", + " # store current transition in buffer\n", + " \n", + "\n", + " # sample replay_batch_size random transitions from replay,\n", + " # then update agent on each of them in a loop\n", + " s_, a_, r_, next_s_, done_ = replay.sample(replay_batch_size)\n", + " for i in range(replay_batch_size):\n", + " \n", + "\n", + " s = next_s\n", + " total_reward += r\n", + " if done:\n", + " break\n", + "\n", + " return total_reward" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Create two agents: first will use experience replay, second will not.\n", + "\n", + "agent_baseline = QLearningAgent(alpha=0.5, epsilon=0.25, discount=0.99,\n", + " get_legal_actions=lambda s: range(n_actions))\n", + "\n", + "agent_replay = QLearningAgent(alpha=0.5, epsilon=0.25, discount=0.99,\n", + " get_legal_actions=lambda s: range(n_actions))\n", + "\n", + "replay = ReplayBuffer(1000)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from IPython.display import clear_output\n", + "\n", + "rewards_replay, rewards_baseline = [], []\n", + "\n", + "for i in range(1000):\n", + " rewards_replay.append(\n", + " play_and_train_with_replay(env, agent_replay, replay))\n", + " rewards_baseline.append(play_and_train_with_replay(\n", + " env, agent_baseline, replay=None))\n", + "\n", + " agent_replay.epsilon *= 0.99\n", + " agent_baseline.epsilon *= 0.99\n", + "\n", + " if i % 100 == 0:\n", + " clear_output(True)\n", + " print('Baseline : eps =', agent_replay.epsilon,\n", + " 'mean reward =', np.mean(rewards_baseline[-10:]))\n", + " print('ExpReplay: eps =', agent_baseline.epsilon,\n", + " 'mean reward =', np.mean(rewards_replay[-10:]))\n", + " plt.plot(moving_average(rewards_replay), label='exp. replay')\n", + " plt.plot(moving_average(rewards_baseline), label='baseline')\n", + " plt.grid()\n", + " plt.legend()\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "#### What to expect:\n", + "\n", + "Experience replay, if implemented correctly, will improve algorithm's initial convergence a lot, but it shouldn't affect the final performance.\n", + "\n", + "### Outro\n", + "\n", + "We will use the code you just wrote extensively in the next week of our course. If you're feeling that you need more examples to understand how experience replay works, try using it for binarized state spaces (CartPole or other __[classic control envs](https://gym.openai.com/envs/#classic_control)__).\n", + "\n", + "__Next week__ we're gonna explore how q-learning and similar algorithms can be applied for large state spaces, with deep learning models to approximate the Q function.\n", + "\n", + "However, __the code you've written__ for this week is already capable of solving many RL problems, and as an added benifit - it is very easy to detach. You can use Q-learning, SARSA and Experience Replay for any RL problems you want to solve - just thow 'em into a file and import the stuff you need." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Bonus I: TD($\\lambda$) (5+ points)\n", + "\n", + "There's a number of advanced algorithms you can find in week 3 materials (Silver lecture II and/or reading about eligibility traces). One such algorithm is TD(lambda), which is based on the idea of eligibility traces. You can also view it as a combination of N-step updates for alll N.\n", + "* N-step temporal difference from Sutton's book - [url](http://incompleteideas.net/book/the-book-2nd.html), page 142 / chapter 7 \n", + "* Eligibility traces from Sutton's book - same url, chapter 12 / page 278\n", + "* Blog post on eligibility traces - [url](http://pierrelucbacon.com/traces/)\n", + "\n", + "Here's a practical algorithm you can start with: [url](https://stackoverflow.com/questions/40862578/how-to-understand-watkinss-q%CE%BB-learning-algorithm-in-suttonbartos-rl-book/40892302)\n", + "\n", + "\n", + "Implementing this algorithm will prove more challenging than q-learning or sarsa, but doing so will earn you a deeper understanding of how value-based methods work [in addition to some bonus points].\n", + "\n", + "More kudos for comparing and analyzing TD($\\lambda$) against Q-learning and EV-SARSA in different setups (taxi vs cartpole, constant epsilon vs decreasing epsilon)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Bonus II: More pacman (5+ points)\n", + "\n", + "__see README.md for software requirements of seminar_py2__\n", + "\n", + "Remember seminar_py2 where your vanilla q-learning had hard time solving Pacman even on a small grid? Now's the time to fix that issue.\n", + "\n", + "We'll focus on those grids for pacman setup.\n", + "* python pacman.py -p PacmanQAgent -x N_TRAIN_GAMES -n N_TOTAL_GAMES -l __mediumGrid__\n", + "* python pacman.py -p PacmanQAgent -x N_TRAIN_GAMES -n N_TOTAL_GAMES -l __mediumClassic__\n", + "\n", + "Even if you adjust N_TRAIN_GAMES to 10^5 and N_TOTAL_GAMES to 10^5+100 (100 last games are for test), pacman won't solve those environments\n", + "\n", + "The problem with those environments is that they have a large amount of unique states. However, you can devise a smaller environment state by choosing different observation parameters, e.g.:\n", + " * distance and direction to nearest ghost\n", + " * where is nearest food\n", + " * 'center of mass' of all food points (and variance, and whatever)\n", + " * is there a wall in each direction\n", + " * and anything else you see fit \n", + " \n", + "Here's how to get this information from [state](https://github.com/yandexdataschool/Practical_RL/blob/master/week3_model_free/seminar_py2/pacman.py#L49),\n", + " * Get pacman position: [state.getPacmanPosition()](https://github.com/yandexdataschool/Practical_RL/blob/master/week3_model_free/seminar_py2/pacman.py#L128)\n", + " * Is there a wall at (x,y)?: [state.hasWall(x,y)](https://github.com/yandexdataschool/Practical_RL/blob/master/week3_model_free/seminar_py2/pacman.py#L189)\n", + " * Get ghost positions: [state.getGhostPositions()](https://github.com/yandexdataschool/Practical_RL/blob/master/week3_model_free/seminar_py2/pacman.py#L144)\n", + " * Get all food positions: [state.getCapsules()](https://github.com/yandexdataschool/Practical_RL/blob/master/week3_model_free/seminar_py2/pacman.py#L153)\n", + " \n", + "You can call those methods anywhere you see state.\n", + " * e.g. in [agent.getValue(state)](https://github.com/yandexdataschool/Practical_RL/blob/master/week3_model_free/seminar_py2/qlearningAgents.py#L52)\n", + " * Defining a function that extracts all features and calling it in [getQValue](https://github.com/yandexdataschool/Practical_RL/blob/master/week3_model_free/seminar_py2/qlearningAgents.py#L38) and [setQValue](https://github.com/yandexdataschool/Practical_RL/blob/master/week3_model_free/seminar_py2/qlearningAgents.py#L44) is probably enough.\n", + " * You can also change agent parameters. The simplest way is to hard-code them in [PacmanQAgent](https://github.com/yandexdataschool/Practical_RL/blob/master/week3_model_free/seminar_py2/qlearningAgents.py#L140)\n", + "\n", + "Also, don't forget to optimize ```learning_rate```, ```discount``` and ```epsilon``` params of model, this may also help to solve this env." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.6.8" + }, + "latex_envs": { + "LaTeX_envs_menu_present": true, + "autoclose": false, + "autocomplete": true, + "bibliofile": "biblio.bib", + "cite_by": "apalike", + "current_citInitial": 1, + "eqLabelWithNumbers": true, + "eqNumInitial": 1, + "hotkeys": { + "equation": "Ctrl-E", + "itemize": "Ctrl-I" + }, + "labels_anchors": false, + "latex_user_defs": false, + "report_style_numbering": false, + "user_envs_cfg": false + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/week3_model_free/seminar_py2/analysis.py b/week03_model_free/seminar_py2/analysis.py similarity index 100% rename from week3_model_free/seminar_py2/analysis.py rename to week03_model_free/seminar_py2/analysis.py diff --git a/week3_model_free/seminar_py2/crawler.py b/week03_model_free/seminar_py2/crawler.py similarity index 100% rename from week3_model_free/seminar_py2/crawler.py rename to week03_model_free/seminar_py2/crawler.py diff --git a/week3_model_free/seminar_py2/environment.py b/week03_model_free/seminar_py2/environment.py similarity index 100% rename from week3_model_free/seminar_py2/environment.py rename to week03_model_free/seminar_py2/environment.py diff --git a/week3_model_free/seminar_py2/featureExtractors.py b/week03_model_free/seminar_py2/featureExtractors.py similarity index 100% rename from week3_model_free/seminar_py2/featureExtractors.py rename to week03_model_free/seminar_py2/featureExtractors.py diff --git a/week3_model_free/seminar_py2/game.py b/week03_model_free/seminar_py2/game.py similarity index 100% rename from week3_model_free/seminar_py2/game.py rename to week03_model_free/seminar_py2/game.py diff --git a/week3_model_free/seminar_py2/ghostAgents.py b/week03_model_free/seminar_py2/ghostAgents.py similarity index 100% rename from week3_model_free/seminar_py2/ghostAgents.py rename to week03_model_free/seminar_py2/ghostAgents.py diff --git a/week3_model_free/seminar_py2/graphicsCrawlerDisplay.py b/week03_model_free/seminar_py2/graphicsCrawlerDisplay.py similarity index 100% rename from week3_model_free/seminar_py2/graphicsCrawlerDisplay.py rename to week03_model_free/seminar_py2/graphicsCrawlerDisplay.py diff --git a/week3_model_free/seminar_py2/graphicsDisplay.py b/week03_model_free/seminar_py2/graphicsDisplay.py similarity index 100% rename from week3_model_free/seminar_py2/graphicsDisplay.py rename to week03_model_free/seminar_py2/graphicsDisplay.py diff --git a/week3_model_free/seminar_py2/graphicsGridworldDisplay.py b/week03_model_free/seminar_py2/graphicsGridworldDisplay.py similarity index 100% rename from week3_model_free/seminar_py2/graphicsGridworldDisplay.py rename to week03_model_free/seminar_py2/graphicsGridworldDisplay.py diff --git a/week3_model_free/seminar_py2/graphicsUtils.py b/week03_model_free/seminar_py2/graphicsUtils.py similarity index 100% rename from week3_model_free/seminar_py2/graphicsUtils.py rename to week03_model_free/seminar_py2/graphicsUtils.py diff --git a/week3_model_free/seminar_py2/gridworld.py b/week03_model_free/seminar_py2/gridworld.py similarity index 100% rename from week3_model_free/seminar_py2/gridworld.py rename to week03_model_free/seminar_py2/gridworld.py diff --git a/week3_model_free/seminar_py2/how2run b/week03_model_free/seminar_py2/how2run similarity index 100% rename from week3_model_free/seminar_py2/how2run rename to week03_model_free/seminar_py2/how2run diff --git a/week3_model_free/seminar_py2/keyboardAgents.py b/week03_model_free/seminar_py2/keyboardAgents.py similarity index 100% rename from week3_model_free/seminar_py2/keyboardAgents.py rename to week03_model_free/seminar_py2/keyboardAgents.py diff --git a/week3_model_free/seminar_py2/layout.py b/week03_model_free/seminar_py2/layout.py similarity index 100% rename from week3_model_free/seminar_py2/layout.py rename to week03_model_free/seminar_py2/layout.py diff --git a/week3_model_free/seminar_py2/layouts/capsuleClassic.lay b/week03_model_free/seminar_py2/layouts/capsuleClassic.lay similarity index 100% rename from week3_model_free/seminar_py2/layouts/capsuleClassic.lay rename to week03_model_free/seminar_py2/layouts/capsuleClassic.lay diff --git a/week3_model_free/seminar_py2/layouts/contestClassic.lay b/week03_model_free/seminar_py2/layouts/contestClassic.lay similarity index 100% rename from week3_model_free/seminar_py2/layouts/contestClassic.lay rename to week03_model_free/seminar_py2/layouts/contestClassic.lay diff --git a/week3_model_free/seminar_py2/layouts/mediumClassic.lay b/week03_model_free/seminar_py2/layouts/mediumClassic.lay similarity index 100% rename from week3_model_free/seminar_py2/layouts/mediumClassic.lay rename to week03_model_free/seminar_py2/layouts/mediumClassic.lay diff --git a/week3_model_free/seminar_py2/layouts/mediumGrid.lay b/week03_model_free/seminar_py2/layouts/mediumGrid.lay similarity index 100% rename from week3_model_free/seminar_py2/layouts/mediumGrid.lay rename to week03_model_free/seminar_py2/layouts/mediumGrid.lay diff --git a/week3_model_free/seminar_py2/layouts/minimaxClassic.lay b/week03_model_free/seminar_py2/layouts/minimaxClassic.lay similarity index 100% rename from week3_model_free/seminar_py2/layouts/minimaxClassic.lay rename to week03_model_free/seminar_py2/layouts/minimaxClassic.lay diff --git a/week3_model_free/seminar_py2/layouts/openClassic.lay b/week03_model_free/seminar_py2/layouts/openClassic.lay similarity index 100% rename from week3_model_free/seminar_py2/layouts/openClassic.lay rename to week03_model_free/seminar_py2/layouts/openClassic.lay diff --git a/week3_model_free/seminar_py2/layouts/originalClassic.lay b/week03_model_free/seminar_py2/layouts/originalClassic.lay similarity index 100% rename from week3_model_free/seminar_py2/layouts/originalClassic.lay rename to week03_model_free/seminar_py2/layouts/originalClassic.lay diff --git a/week3_model_free/seminar_py2/layouts/smallClassic.lay b/week03_model_free/seminar_py2/layouts/smallClassic.lay similarity index 100% rename from week3_model_free/seminar_py2/layouts/smallClassic.lay rename to week03_model_free/seminar_py2/layouts/smallClassic.lay diff --git a/week3_model_free/seminar_py2/layouts/smallGrid.lay b/week03_model_free/seminar_py2/layouts/smallGrid.lay similarity index 100% rename from week3_model_free/seminar_py2/layouts/smallGrid.lay rename to week03_model_free/seminar_py2/layouts/smallGrid.lay diff --git a/week3_model_free/seminar_py2/layouts/testClassic.lay b/week03_model_free/seminar_py2/layouts/testClassic.lay similarity index 100% rename from week3_model_free/seminar_py2/layouts/testClassic.lay rename to week03_model_free/seminar_py2/layouts/testClassic.lay diff --git a/week3_model_free/seminar_py2/layouts/trappedClassic.lay b/week03_model_free/seminar_py2/layouts/trappedClassic.lay similarity index 100% rename from week3_model_free/seminar_py2/layouts/trappedClassic.lay rename to week03_model_free/seminar_py2/layouts/trappedClassic.lay diff --git a/week3_model_free/seminar_py2/layouts/trickyClassic.lay b/week03_model_free/seminar_py2/layouts/trickyClassic.lay similarity index 100% rename from week3_model_free/seminar_py2/layouts/trickyClassic.lay rename to week03_model_free/seminar_py2/layouts/trickyClassic.lay diff --git a/week3_model_free/seminar_py2/learningAgents.py b/week03_model_free/seminar_py2/learningAgents.py similarity index 100% rename from week3_model_free/seminar_py2/learningAgents.py rename to week03_model_free/seminar_py2/learningAgents.py diff --git a/week3_model_free/seminar_py2/mdp.py b/week03_model_free/seminar_py2/mdp.py similarity index 100% rename from week3_model_free/seminar_py2/mdp.py rename to week03_model_free/seminar_py2/mdp.py diff --git a/week3_model_free/seminar_py2/pacman.py b/week03_model_free/seminar_py2/pacman.py similarity index 100% rename from week3_model_free/seminar_py2/pacman.py rename to week03_model_free/seminar_py2/pacman.py diff --git a/week3_model_free/seminar_py2/pacmanAgents.py b/week03_model_free/seminar_py2/pacmanAgents.py similarity index 100% rename from week3_model_free/seminar_py2/pacmanAgents.py rename to week03_model_free/seminar_py2/pacmanAgents.py diff --git a/week3_model_free/seminar_py2/qlearningAgents.py b/week03_model_free/seminar_py2/qlearningAgents.py similarity index 100% rename from week3_model_free/seminar_py2/qlearningAgents.py rename to week03_model_free/seminar_py2/qlearningAgents.py diff --git a/week3_model_free/seminar_py2/run_crawler.sh b/week03_model_free/seminar_py2/run_crawler.sh similarity index 100% rename from week3_model_free/seminar_py2/run_crawler.sh rename to week03_model_free/seminar_py2/run_crawler.sh diff --git a/week3_model_free/seminar_py2/run_grid.sh b/week03_model_free/seminar_py2/run_grid.sh similarity index 100% rename from week3_model_free/seminar_py2/run_grid.sh rename to week03_model_free/seminar_py2/run_grid.sh diff --git a/week3_model_free/seminar_py2/run_pacman.sh b/week03_model_free/seminar_py2/run_pacman.sh similarity index 100% rename from week3_model_free/seminar_py2/run_pacman.sh rename to week03_model_free/seminar_py2/run_pacman.sh diff --git a/week3_model_free/seminar_py2/textDisplay.py b/week03_model_free/seminar_py2/textDisplay.py similarity index 100% rename from week3_model_free/seminar_py2/textDisplay.py rename to week03_model_free/seminar_py2/textDisplay.py diff --git a/week3_model_free/seminar_py2/textGridworldDisplay.py b/week03_model_free/seminar_py2/textGridworldDisplay.py similarity index 100% rename from week3_model_free/seminar_py2/textGridworldDisplay.py rename to week03_model_free/seminar_py2/textGridworldDisplay.py diff --git a/week3_model_free/seminar_py2/util.py b/week03_model_free/seminar_py2/util.py similarity index 100% rename from week3_model_free/seminar_py2/util.py rename to week03_model_free/seminar_py2/util.py diff --git a/week3_model_free/seminar_qlearning.ipynb b/week03_model_free/seminar_qlearning.ipynb similarity index 98% rename from week3_model_free/seminar_qlearning.ipynb rename to week03_model_free/seminar_qlearning.ipynb index 2aa4be084..a908ff5e2 100644 --- a/week3_model_free/seminar_qlearning.ipynb +++ b/week03_model_free/seminar_qlearning.ipynb @@ -4,8 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Q-learning \n", - "(3 points)\n", + "## Q-learning (3 points)\n", "\n", "This notebook will guide you through implementation of vanilla Q-learning algorithm.\n", "\n", @@ -20,7 +19,12 @@ }, "outputs": [], "source": [ - "# XVFB will be launched if you run on a server\n", + "# In google collab, uncomment this:\n", + "# !wget https://bit.ly/2FMJP5K -q -O setup.py\n", + "# !bash setup.py 2>&1 1>stdout.log | tee stderr.log\n", + "\n", + "# This code creates a virtual display to draw game images on.\n", + "# If you are running locally, just ignore it\n", "import os\n", "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", " !bash ../xvfb start\n", @@ -58,7 +62,7 @@ " def __init__(self, alpha, epsilon, discount, get_legal_actions):\n", " \"\"\"\n", " Q-Learning Agent\n", - " based on http://inst.eecs.berkeley.edu/~cs188/sp09/pacman.html\n", + " based on https://inst.eecs.berkeley.edu/~cs188/sp19/projects.html\n", " Instance variables you have access to\n", " - self.epsilon (exploration prob)\n", " - self.alpha (learning rate)\n", @@ -71,7 +75,6 @@ " which returns Q(state,action)\n", " - self.set_qvalue(state,action,value)\n", " which sets Q(state,action) := value\n", - "\n", " !!!Important!!!\n", " Note: please avoid using self._qValues directly. \n", " There's a special self.get_qvalue/set_qvalue for that.\n", @@ -141,7 +144,7 @@ " \"\"\"\n", " Compute the action to take in the current state, including exploration. \n", " With probability self.epsilon, we should take a random action.\n", - " otherwise - the best policy action (self.getPolicy).\n", + " otherwise - the best policy action (self.get_best_action).\n", "\n", " Note: To pick randomly from a list, use random.choice(list). \n", " To pick True or False with a given probablity, generate uniform number in [0, 1]\n", @@ -362,7 +365,7 @@ "\n", "class Binarizer(ObservationWrapper):\n", "\n", - " def _observation(self, state):\n", + " def observation(self, state):\n", "\n", " # state = \n", " # hint: you can do that with round(x,n_digits)\n", @@ -472,7 +475,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.2" + "version": "3.6.8" } }, "nbformat": 4, diff --git a/week4_[recap]_deep_learning/README.md b/week04_[recap]_deep_learning/README.md similarity index 95% rename from week4_[recap]_deep_learning/README.md rename to week04_[recap]_deep_learning/README.md index 0fac1ceb4..ad6e8542e 100644 --- a/week4_[recap]_deep_learning/README.md +++ b/week04_[recap]_deep_learning/README.md @@ -30,6 +30,7 @@ __Note:__ This week's materials cover the basics of neural nets and deep learnin ### Practice +__[Colab url (pytorch)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week04_%5Brecap%5D_deep_learning/seminar_pytorch.ipynb)__ From now on, we'll have two tracks: theano and tensorflow. We'll also add pytorch seminars as soon as they're ready. Please pick seminar_theano.ipynb, seminar_tensorflow.ipynb or seminar_pytorch.ipynb. diff --git a/week4_[recap]_deep_learning/fix_my_nn.ipynb b/week04_[recap]_deep_learning/fix_my_nn.ipynb similarity index 100% rename from week4_[recap]_deep_learning/fix_my_nn.ipynb rename to week04_[recap]_deep_learning/fix_my_nn.ipynb diff --git a/week4_[recap]_deep_learning/mnist.py b/week04_[recap]_deep_learning/mnist.py similarity index 100% rename from week4_[recap]_deep_learning/mnist.py rename to week04_[recap]_deep_learning/mnist.py diff --git a/week4_[recap]_deep_learning/notmnist.py b/week04_[recap]_deep_learning/notmnist.py similarity index 92% rename from week4_[recap]_deep_learning/notmnist.py rename to week04_[recap]_deep_learning/notmnist.py index fa733b771..5f7c98ac5 100644 --- a/week4_[recap]_deep_learning/notmnist.py +++ b/week04_[recap]_deep_learning/notmnist.py @@ -10,7 +10,7 @@ def load_notmnist(path='./notMNIST_small',letters='ABCDEFGHIJ', # download data if it's missing. If you have any problems, go to the urls and load it manually. if not os.path.exists(path): print("Downloading data...") - assert os.system('curl http://yaroslavvb.com/upload/notMNIST/notMNIST_small.tar.gz > notMNIST_small.tar.gz') == 0 + assert os.system('wget http://yaroslavvb.com/upload/notMNIST/notMNIST_small.tar.gz') == 0 print("Extracting ...") assert os.system('tar -zxvf notMNIST_small.tar.gz > untar_notmnist.log') == 0 diff --git a/week4_[recap]_deep_learning/practice_lasagne.ipynb b/week04_[recap]_deep_learning/practice_lasagne.ipynb similarity index 100% rename from week4_[recap]_deep_learning/practice_lasagne.ipynb rename to week04_[recap]_deep_learning/practice_lasagne.ipynb diff --git a/week4_[recap]_deep_learning/practice_tensorflow.ipynb b/week04_[recap]_deep_learning/practice_tensorflow.ipynb similarity index 100% rename from week4_[recap]_deep_learning/practice_tensorflow.ipynb rename to week04_[recap]_deep_learning/practice_tensorflow.ipynb diff --git a/week04_[recap]_deep_learning/seminar_pytorch.ipynb b/week04_[recap]_deep_learning/seminar_pytorch.ipynb new file mode 100644 index 000000000..8597565df --- /dev/null +++ b/week04_[recap]_deep_learning/seminar_pytorch.ipynb @@ -0,0 +1,1327 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Hello, pytorch\n", + "\n", + "![img](https://pytorch.org/tutorials/_static/pytorch-logo-dark.svg)\n", + "\n", + "__This notebook__ will teach you to use pytorch low-level core. You can install it [here](http://pytorch.org/). For high-level interface see the next notebook.\n", + "\n", + "__Pytorch feels__ differently than tensorflow/theano on almost every level. TensorFlow makes your code live in two \"worlds\" simultaneously: symbolic graphs and actual tensors. First you declare a symbolic \"recipe\" of how to get from inputs to outputs, then feed it with actual minibatches of data. In pytorch, __there's only one world__: all tensors have a numeric value.\n", + "\n", + "You compute outputs on the fly without pre-declaring anything. The code looks exactly as in pure numpy with one exception: pytorch computes gradients for you. And can run stuff on GPU. And has a number of pre-implemented building blocks for your neural nets. [And a few more things.](https://medium.com/towards-data-science/pytorch-vs-tensorflow-spotting-the-difference-25c75777377b)\n", + "\n", + "And now we finally shut up and let pytorch do the talking." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.0.0\n" + ] + } + ], + "source": [ + "# if running in colab, execute this:\n", + "# !wget https://raw.githubusercontent.com/yandexdataschool/Practical_RL/bc1d4d4e8ca9cdf2b088bab0b4fb583f03acd530/week04_%5Brecap%5D_deep_learning/notmnist.py -O notmnist.py\n", + "# !pip3 install torch==1.0.0 torchvision\n", + "# !pip3 uninstall -y Pillow\n", + "# !pip3 install Pillow==5.3.0\n", + "\n", + "from __future__ import print_function\n", + "import numpy as np\n", + "import torch\n", + "print(torch.__version__)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X :\n", + "[[ 0 1 2 3]\n", + " [ 4 5 6 7]\n", + " [ 8 9 10 11]\n", + " [12 13 14 15]]\n", + "\n", + "X.shape : (4, 4)\n", + "\n", + "add 5 :\n", + "[[ 5 6 7 8]\n", + " [ 9 10 11 12]\n", + " [13 14 15 16]\n", + " [17 18 19 20]]\n", + "\n", + "X*X^T :\n", + "[[ 14 38 62 86]\n", + " [ 38 126 214 302]\n", + " [ 62 214 366 518]\n", + " [ 86 302 518 734]]\n", + "\n", + "mean over cols :\n", + "[ 1.5 5.5 9.5 13.5]\n", + "\n", + "cumsum of cols :\n", + "[[ 0 1 2 3]\n", + " [ 4 6 8 10]\n", + " [12 15 18 21]\n", + " [24 28 32 36]]\n", + "\n" + ] + } + ], + "source": [ + "# numpy world\n", + "\n", + "x = np.arange(16).reshape(4, 4)\n", + "\n", + "print(\"X :\\n%s\\n\" % x)\n", + "print(\"X.shape : %s\\n\" % (x.shape,))\n", + "print(\"add 5 :\\n%s\\n\" % (x + 5))\n", + "print(\"X*X^T :\\n%s\\n\" % np.dot(x, x.T))\n", + "print(\"mean over cols :\\n%s\\n\" % (x.mean(axis=-1)))\n", + "print(\"cumsum of cols :\\n%s\\n\" % (np.cumsum(x, axis=0)))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X :\n", + "tensor([[ 0., 1., 2., 3.],\n", + " [ 4., 5., 6., 7.],\n", + " [ 8., 9., 10., 11.],\n", + " [12., 13., 14., 15.]])\n", + "X.shape : torch.Size([4, 4])\n", + "\n", + "add 5 :\n", + "tensor([[ 5., 6., 7., 8.],\n", + " [ 9., 10., 11., 12.],\n", + " [13., 14., 15., 16.],\n", + " [17., 18., 19., 20.]])\n", + "X*X^T :\n", + "tensor([[ 14., 38., 62., 86.],\n", + " [ 38., 126., 214., 302.],\n", + " [ 62., 214., 366., 518.],\n", + " [ 86., 302., 518., 734.]])\n", + "mean over cols :\n", + "tensor([ 1.5000, 5.5000, 9.5000, 13.5000])\n", + "cumsum of cols :\n", + "tensor([[ 0., 1., 2., 3.],\n", + " [ 4., 6., 8., 10.],\n", + " [12., 15., 18., 21.],\n", + " [24., 28., 32., 36.]])\n" + ] + } + ], + "source": [ + "# pytorch world\n", + "\n", + "x = np.arange(16).reshape(4, 4)\n", + "\n", + "x = torch.tensor(x, dtype=torch.float32) # or torch.arange(0,16).view(4,4)\n", + "\n", + "print(\"X :\\n%s\" % x)\n", + "print(\"X.shape : %s\\n\" % (x.shape,))\n", + "print(\"add 5 :\\n%s\" % (x + 5))\n", + "print(\"X*X^T :\\n%s\" % torch.matmul(x, x.transpose(1, 0))) # short: x.mm(x.t())\n", + "print(\"mean over cols :\\n%s\" % torch.mean(x, dim=-1))\n", + "print(\"cumsum of cols :\\n%s\" % torch.cumsum(x, dim=0))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## NumPy and Pytorch\n", + "\n", + "As you can notice, pytorch allows you to hack stuff much the same way you did with numpy. No graph declaration, no placeholders, no sessions. This means that you can _see the numeric value of any tensor at any moment of time_. Debugging such code can be done with by printing tensors or using any debug tool you want (e.g. [gdb](https://wiki.python.org/moin/DebuggingWithGdb)).\n", + "\n", + "You could also notice the a few new method names and a different API. So no, there's no compatibility with numpy [yet](https://github.com/pytorch/pytorch/issues/2228) and yes, you'll have to memorize all the names again. Get excited!\n", + "\n", + "![img](http://i0.kym-cdn.com/entries/icons/original/000/017/886/download.jpg)\n", + "\n", + "For example, \n", + "* If something takes a list/tuple of axes in numpy, you can expect it to take *args in pytorch\n", + " * `x.reshape([1,2,8]) -> x.view(1,2,8)`\n", + "* You should swap _axis_ for _dim_ in operations like mean or cumsum\n", + " * `x.sum(axis=-1) -> x.sum(dim=-1)`\n", + "* most mathematical operations are the same, but types an shaping is different\n", + " * `x.astype('int64') -> x.type(torch.LongTensor)`\n", + "\n", + "To help you acclimatize, there's a [table](https://github.com/torch/torch7/wiki/Torch-for-Numpy-users) covering most new things. There's also a neat [documentation page](http://pytorch.org/docs/master/).\n", + "\n", + "Finally, if you're stuck with a technical problem, we recommend searching [pytorch forumns](https://discuss.pytorch.org/). Or just googling, which usually works just as efficiently. \n", + "\n", + "If you feel like you almost give up, remember two things: __GPU__ an __free gradients__. Besides you can always jump back to numpy with x.numpy()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Warmup: trigonometric knotwork\n", + "_inspired by [this post](https://www.quora.com/What-are-the-most-interesting-equation-plots)_\n", + "\n", + "There are some simple mathematical functions with cool plots. For one, consider this:\n", + "\n", + "$$ x(t) = t - 1.5 * cos( 15 t) $$\n", + "$$ y(t) = t - 1.5 * sin( 16 t) $$\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "t = torch.linspace(-10, 10, steps=10000)\n", + "\n", + "# compute x(t) and y(t) as defined above\n", + "x = # YOUR CODE\n", + "y = # YOUR CODE\n", + "\n", + "plt.plot(x.numpy(), y.numpy())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "if you're done early, try adjusting the formula and seing how it affects the function" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Automatic gradients\n", + "\n", + "Any self-respecting DL framework must do your backprop for you. Torch handles this with the `autograd` module.\n", + "\n", + "The general pipeline looks like this:\n", + "* When creating a tensor, you mark it as `requires_grad`:\n", + " * __```torch.zeros(5, requires_grad=True)```__\n", + " * torch.tensor(np.arange(5), dtype=torch.float32, requires_grad=True)\n", + "* Define some differentiable `loss = arbitrary_function(a)`\n", + "* Call `loss.backward()`\n", + "* Gradients are now available as ```a.grads```\n", + "\n", + "__Here's an example:__ let's fit a linear regression on Boston house prices" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from sklearn.datasets import load_boston\n", + "boston = load_boston()\n", + "plt.scatter(boston.data[:, -1], boston.target)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "from torch.autograd import Variable\n", + "w = torch.zeros(1, requires_grad=True)\n", + "b = torch.zeros(1, requires_grad=True)\n", + "\n", + "x = torch.tensor(boston.data[:, -1] / 10, dtype=torch.float32)\n", + "y = torch.tensor(boston.target, dtype=torch.float32)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "y_pred = w * x + b\n", + "loss = torch.mean((y_pred - y)**2)\n", + "\n", + "# propagete gradients\n", + "loss.backward()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The gradients are now stored in `.grad` of those variables that require them." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dL/dw = \n", + " tensor([-47.3514])\n", + "dL/db = \n", + " tensor([-45.0656])\n" + ] + } + ], + "source": [ + "print(\"dL/dw = \\n\", w.grad)\n", + "print(\"dL/db = \\n\", b.grad)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you compute gradient from multiple losses, the gradients will add up at variables, therefore it's useful to __zero the gradients__ between iteratons." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "loss = 44.59417\n" + ] + } + ], + "source": [ + "from IPython.display import clear_output\n", + "\n", + "for i in range(100):\n", + "\n", + " y_pred = w * x + b\n", + " loss = torch.mean((y_pred - y)**2)\n", + " loss.backward()\n", + "\n", + " w.data -= 0.05 * w.grad.data\n", + " b.data -= 0.05 * b.grad.data\n", + "\n", + " # zero gradients\n", + " w.grad.data.zero_()\n", + " b.grad.data.zero_()\n", + "\n", + " # the rest of code is just bells and whistles\n", + " if (i+1) % 5 == 0:\n", + " clear_output(True)\n", + " plt.scatter(x.data.numpy(), y.data.numpy())\n", + " plt.scatter(x.data.numpy(), y_pred.data.numpy(),\n", + " color='orange', linewidth=5)\n", + " plt.show()\n", + "\n", + " print(\"loss = \", loss.data.numpy())\n", + " if loss.data.numpy() < 0.5:\n", + " print(\"Done!\")\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Bonus quest__: try implementing and writing some nonlinear regression. You can try quadratic features or some trigonometry, or a simple neural network. The only difference is that now you have more variables and a more complicated `y_pred`. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# High-level pytorch\n", + "\n", + "So far we've been dealing with low-level torch API. While it's absolutely vital for any custom losses or layers, building large neura nets in it is a bit clumsy.\n", + "\n", + "Luckily, there's also a high-level torch interface with a pre-defined layers, activations and training algorithms. \n", + "\n", + "We'll cover them as we go through a simple image recognition problem: classifying letters into __\"A\"__ vs __\"B\"__.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Parsing...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/jheuristic/anaconda3/lib/python3.6/site-packages/scipy/misc/pilutil.py:482: FutureWarning: Conversion of the second argument of issubdtype from `int` to `np.signedinteger` is deprecated. In future, it will be treated as `np.int64 == np.dtype(int).type`.\n", + " if issubdtype(ts, int):\n", + "/home/jheuristic/anaconda3/lib/python3.6/site-packages/scipy/misc/pilutil.py:485: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n", + " elif issubdtype(type(size), float):\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "found broken img: ./notMNIST_small/A/RGVtb2NyYXRpY2FCb2xkT2xkc3R5bGUgQm9sZC50dGY=.png [it's ok if <10 images are broken]\n", + "Done\n", + "Train size = 2808, test_size = 937\n" + ] + } + ], + "source": [ + "from notmnist import load_notmnist\n", + "X_train, y_train, X_test, y_test = load_notmnist(letters='AB')\n", + "X_train, X_test = X_train.reshape([-1, 784]), X_test.reshape([-1, 784])\n", + "\n", + "print(\"Train size = %i, test_size = %i\" % (len(X_train), len(X_test)))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for i in [0, 1]:\n", + " plt.subplot(1, 2, i + 1)\n", + " plt.imshow(X_train[i].reshape([28, 28]))\n", + " plt.title(str(y_train[i]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's start with layers. The main abstraction here is __`torch.nn.Module`__" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Base class for all neural network modules.\n", + "\n", + " Your models should also subclass this class.\n", + "\n", + " Modules can also contain other Modules, allowing to nest them in\n", + " a tree structure. You can assign the submodules as regular attributes::\n", + "\n", + " import torch.nn as nn\n", + " import torch.nn.functional as F\n", + "\n", + " class Model(nn.Module):\n", + " def __init__(self):\n", + " super(Model, self).__init__()\n", + " self.conv1 = nn.Conv2d(1, 20, 5)\n", + " self.conv2 = nn.Conv2d(20, 20, 5)\n", + "\n", + " def forward(self, x):\n", + " x = F.relu(self.conv1(x))\n", + " return F.relu(self.conv2(x))\n", + "\n", + " Submodules assigned in this way will be registered, and will have their\n", + " parameters converted too when you call `.cuda()`, etc.\n", + " \n" + ] + } + ], + "source": [ + "from torch import nn\n", + "import torch.nn.functional as F\n", + "\n", + "print(nn.Module.__doc__)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There's a vast library of popular layers and architectures already built for ya'.\n", + "\n", + "This is a binary classification problem, so we'll train a __Logistic Regression with sigmoid__.\n", + "$$P(y_i | X_i) = \\sigma(W \\cdot X_i + b) ={ 1 \\over {1+e^{- [W \\cdot X_i + b]}} }$$\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# create a network that stacks layers on top of each other\n", + "model = nn.Sequential()\n", + "\n", + "# add first \"dense\" layer with 784 input units and 1 output unit.\n", + "model.add_module('l1', nn.Linear(784, 1))\n", + "\n", + "# add softmax activation for probabilities. Normalize over axis 1\n", + "# note: layer names must be unique\n", + "model.add_module('l2', nn.Sigmoid())" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Weight shapes: [torch.Size([1, 784]), torch.Size([1])]\n" + ] + } + ], + "source": [ + "print(\"Weight shapes:\", [w.shape for w in model.parameters()])" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([ 0.4526, 0.4411, 0.5917])" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# create dummy data with 3 samples and 784 features\n", + "x = torch.tensor(X_train[:3], dtype=torch.float32)\n", + "y = torch.tensor(y_train[:3], dtype=torch.float32)\n", + "\n", + "# compute outputs given inputs, both are variables\n", + "y_predicted = model(x)[:, 0]\n", + "\n", + "y_predicted # display what we've got" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's now define a loss function for our model.\n", + "\n", + "The natural choice is to use binary crossentropy (aka logloss, negative llh):\n", + "$$ L = {1 \\over N} \\underset{X_i,y_i} \\sum - [ y_i \\cdot log P(y_i | X_i) + (1-y_i) \\cdot log (1-P(y_i | X_i)) ]$$\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "crossentropy = # YOUR CODE\n", + "\n", + "loss = # YOUR CODE\n", + "\n", + "assert tuple(crossentropy.size()) == (\n", + " 3,), \"Crossentropy must be a vector with element per sample\"\n", + "assert tuple(loss.size()) == tuple(\n", + "), \"Loss must be scalar. Did you forget the mean/sum?\"\n", + "assert loss.data.numpy() > 0, \"Crossentropy must non-negative, zero only for perfect prediction\"\n", + "assert loss.data.numpy() <= np.log(\n", + " 3), \"Loss is too large even for untrained model. Please double-check it.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Note:__ you can also find many such functions in `torch.nn.functional`, just type __`F.`__." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Torch optimizers__\n", + "\n", + "When we trained Linear Regression above, we had to manually .zero_() gradients on both our variables. Imagine that code for a 50-layer network.\n", + "\n", + "Again, to keep it from getting dirty, there's `torch.optim` module with pre-implemented algorithms:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "opt = torch.optim.RMSprop(model.parameters(), lr=0.01)\n", + "\n", + "# here's how it's used:\n", + "loss.backward() # add new gradients\n", + "opt.step() # change weights\n", + "opt.zero_grad() # clear gradients" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# dispose of old variables to avoid bugs later\n", + "del x, y, y_predicted, loss, y_pred" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Putting it all together" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# create network again just in case\n", + "model = nn.Sequential()\n", + "model.add_module('first', nn.Linear(784, 1))\n", + "model.add_module('second', nn.Sigmoid())\n", + "\n", + "opt = torch.optim.Adam(model.parameters(), lr=1e-3)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step #0 | mean loss = 0.573\n", + "step #10 | mean loss = 0.371\n", + "step #20 | mean loss = 0.218\n", + "step #30 | mean loss = 0.159\n", + "step #40 | mean loss = 0.141\n", + "step #50 | mean loss = 0.127\n", + "step #60 | mean loss = 0.131\n", + "step #70 | mean loss = 0.107\n", + "step #80 | mean loss = 0.116\n", + "step #90 | mean loss = 0.101\n" + ] + } + ], + "source": [ + "history = []\n", + "\n", + "for i in range(100):\n", + "\n", + " # sample 256 random images\n", + " ix = np.random.randint(0, len(X_train), 256)\n", + " x_batch = torch.tensor(X_train[ix], dtype=torch.float32)\n", + " y_batch = torch.tensor(y_train[ix], dtype=torch.float32)\n", + "\n", + " # predict probabilities\n", + " y_predicted = # YOUR CODE\n", + "\n", + " assert y_predicted.dim(\n", + " ) == 1, \"did you forget to select first column with [:, 0]\"\n", + "\n", + " # compute loss, just like before\n", + " loss = # YOUR CODE\n", + "\n", + " # compute gradients\n", + " \n", + "\n", + " # Adam step\n", + " \n", + "\n", + " # clear gradients\n", + " \n", + "\n", + " history.append(loss.data.numpy())\n", + "\n", + " if i % 10 == 0:\n", + " print(\"step #%i | mean loss = %.3f\" % (i, np.mean(history[-10:])))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Debugging tips:__\n", + "* make sure your model predicts probabilities correctly. Just print them and see what's inside.\n", + "* don't forget _minus_ sign in the loss function! It's a mistake 99% ppl do at some point.\n", + "* make sure you zero-out gradients after each step. Srsly:)\n", + "* In general, pytorch's error messages are quite helpful, read 'em before you google 'em.\n", + "* if you see nan/inf, print what happens at each iteration to find our where exactly it occurs.\n", + " * If loss goes down and then turns nan midway through, try smaller learning rate. (Our current loss formula is unstable).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Evaluation\n", + "\n", + "Let's see how our model performs on test data" + ] + }, + { + "cell_type": "code", + "execution_count": 254, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test accuracy: 0.96585\n" + ] + } + ], + "source": [ + "# use your model to predict classes (0 or 1) for all test samples\n", + "predicted_y_test = # YOUR CODE\n", + "\n", + "assert isinstance(predicted_y_test, np.ndarray), \"please return np array, not %s\" % type(\n", + " predicted_y_test)\n", + "assert predicted_y_test.shape == y_test.shape, \"please predict one class for each test sample\"\n", + "assert np.in1d(predicted_y_test, y_test).all(), \"please predict class indexes\"\n", + "\n", + "accuracy = np.mean(predicted_y_test == y_test)\n", + "\n", + "print(\"Test accuracy: %.5f\" % accuracy)\n", + "assert accuracy > 0.95, \"try training longer\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## More about pytorch:\n", + "* Using torch on GPU and multi-GPU - [link](http://pytorch.org/docs/master/notes/cuda.html)\n", + "* More tutorials on pytorch - [link](http://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)\n", + "* Pytorch examples - a repo that implements many cool DL models in pytorch - [link](https://github.com/pytorch/examples)\n", + "* Practical pytorch - a repo that implements some... other cool DL models... yes, in pytorch - [link](https://github.com/spro/practical-pytorch)\n", + "* And some more - [link](https://www.reddit.com/r/pytorch/comments/6z0yeo/pytorch_and_pytorch_tricks_for_kaggle/)\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Homework tasks\n", + "\n", + "There will be three tasks worth 2, 3 and 5 points respectively. \n", + "If you get stuck with no progress, try switching to the next task and returning later." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task I (2 points) - tensormancy\n", + "\n", + "![img](https://media.giphy.com/media/3o751UMCYtSrRAFRFC/giphy.gif)\n", + "\n", + "When dealing with more complex stuff like neural network, it's best if you use tensors the way samurai uses his sword. \n", + "\n", + "\n", + "__1.1 the cannabola__ \n", + "[_disclaimer_](https://gist.githubusercontent.com/justheuristic/e2c1fa28ca02670cabc42cacf3902796/raw/fd3d935cef63a01b85ed2790b5c11c370245cbd7/stddisclaimer.h)\n", + "\n", + "Let's write another function, this time in polar coordinates:\n", + "$$\\rho(\\theta) = (1 + 0.9 \\cdot cos (8 \\cdot \\theta) ) \\cdot (1 + 0.1 \\cdot cos(24 \\cdot \\theta)) \\cdot (0.9 + 0.05 \\cdot cos(200 \\cdot \\theta)) \\cdot (1 + sin(\\theta))$$\n", + "\n", + "\n", + "Then convert it into cartesian coordinates ([howto](http://www.mathsisfun.com/polar-cartesian-coordinates.html)) and plot the results.\n", + "\n", + "Use torch tensors only: no lists, loops, numpy arrays, etc." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "theta = torch.linspace(- np.pi, np.pi, steps=1000)\n", + "\n", + "# compute rho(theta) as per formula above\n", + "rho = # YOUR CODE\n", + "\n", + "# Now convert polar (rho, theta) pairs into cartesian (x,y) to plot them.\n", + "x = # YOUR CODE\n", + "y = # YOUR CODE\n", + "\n", + "\n", + "plt.figure(figsize=[6, 6])\n", + "plt.fill(x.numpy(), y.numpy(), color='green')\n", + "plt.grid()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task II: the game of life (3 points)\n", + "\n", + "Now it's time for you to make something more challenging. We'll implement Conway's [Game of Life](http://web.stanford.edu/~cdebs/GameOfLife/) in _pure pytorch_. \n", + "\n", + "While this is still a toy task, implementing game of life this way has one cool benefit: __you'll be able to run it on GPU! __ Indeed, what could be a better use of your gpu than simulating game of life on 1M/1M grids?\n", + "\n", + "![img](https://cdn.tutsplus.com/gamedev/authors/legacy/Stephane%20Beniak/2012/09/11/Preview_Image.png)\n", + "If you've skipped the url above out of sloth, here's the game of life:\n", + "* You have a 2D grid of cells, where each cell is \"alive\"(1) or \"dead\"(0)\n", + "* Any living cell that has 2 or 3 neighbors survives, else it dies [0,1 or 4+ neighbors]\n", + "* Any cell with exactly 3 neighbors becomes alive (if it was dead)\n", + "\n", + "For this task, you are given a reference numpy implementation that you must convert to pytorch.\n", + "_[numpy code inspired by: https://github.com/rougier/numpy-100]_\n", + "\n", + "\n", + "__Note:__ You can find convolution in `torch.nn.functional.conv2d(Z,filters)`. Note that it has a different input format.\n", + "\n", + "__Note 2:__ From the mathematical standpoint, pytorch convolution is actually cross-correlation. Those two are very similar operations. More info: [video tutorial](https://www.youtube.com/watch?v=C3EEy8adxvc), [scipy functions review](http://programmerz.ru/questions/26903/2d-convolution-in-python-similar-to-matlabs-conv2-question), [stack overflow source](https://stackoverflow.com/questions/31139977/comparing-matlabs-conv2-with-scipys-convolve2d)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from scipy.signal import correlate2d\n", + "\n", + "def np_update(Z):\n", + " # Count neighbours with convolution\n", + " filters = np.array([[1, 1, 1],\n", + " [1, 0, 1],\n", + " [1, 1, 1]])\n", + "\n", + " N = correlate2d(Z, filters, mode='same')\n", + "\n", + " # Apply rules\n", + " birth = (N == 3) & (Z == 0)\n", + " survive = ((N == 2) | (N == 3)) & (Z == 1)\n", + "\n", + " Z[:] = birth | survive\n", + " return Z" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def torch_update(Z):\n", + " \"\"\"\n", + " Implement an update function that does to Z exactly the same as np_update.\n", + " :param Z: torch.FloatTensor of shape [height,width] containing 0s(dead) an 1s(alive)\n", + " :returns: torch.FloatTensor Z after updates.\n", + "\n", + " You can opt to create new tensor or change Z inplace.\n", + " \"\"\"\n", + "\n", + " # \n", + "\n", + " return Z" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# initial frame\n", + "Z_numpy = np.random.choice([0, 1], p=(0.5, 0.5), size=(100, 100))\n", + "Z = torch.from_numpy(Z_numpy).type(torch.FloatTensor)\n", + "\n", + "# your debug polygon :)\n", + "Z_new = torch_update(Z.clone())\n", + "\n", + "# tests\n", + "Z_reference = np_update(Z_numpy.copy())\n", + "assert np.all(Z_new.numpy(\n", + ") == Z_reference), \"your pytorch implementation doesn't match np_update. Look into Z and np_update(ZZ) to investigate.\"\n", + "print(\"Well done!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%matplotlib notebook\n", + "plt.ion()\n", + "\n", + "# initialize game field\n", + "Z = np.random.choice([0, 1], size=(100, 100))\n", + "Z = torch.from_numpy(Z).type(torch.FloatTensor)\n", + "\n", + "fig = plt.figure()\n", + "ax = fig.add_subplot(111)\n", + "fig.show()\n", + "\n", + "for _ in range(100):\n", + "\n", + " # update\n", + " Z = torch_update(Z)\n", + "\n", + " # re-draw image\n", + " ax.clear()\n", + " ax.imshow(Z.numpy(), cmap='gray')\n", + " fig.canvas.draw()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Some fun setups for your amusement\n", + "\n", + "# parallel stripes\n", + "Z = np.arange(100) % 2 + np.zeros([100, 100])\n", + "# with a small imperfection\n", + "Z[48:52, 50] = 1\n", + "\n", + "Z = torch.from_numpy(Z).type(torch.FloatTensor)\n", + "\n", + "fig = plt.figure()\n", + "ax = fig.add_subplot(111)\n", + "fig.show()\n", + "\n", + "for _ in range(100):\n", + " Z = torch_update(Z)\n", + " ax.clear()\n", + " ax.imshow(Z.numpy(), cmap='gray')\n", + " fig.canvas.draw()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "More fun with Game of Life: [video](https://www.youtube.com/watch?v=C2vgICfQawE)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "\n", + "\n", + "### Task III: Going deeper (5 points)\n", + "\n", + "Your ultimate task for this week is to build your first neural network [almost] from scratch and pure torch.\n", + "\n", + "This time you will solve the same digit recognition problem, but at a greater scale\n", + "* 10 different letters\n", + "* 20k samples\n", + "\n", + "We want you to build a network that reaches at least 80% accuracy and has at least 2 linear layers in it. Naturally, it should be nonlinear to beat logistic regression. You can implement it with either \n", + "\n", + "\n", + "With 10 classes you will need to use __Softmax__ at the top instead of sigmoid and train for __categorical crossentropy__ (see [here](http://wiki.fast.ai/index.php/Log_Loss)). Write your own loss or use `torch.nn.functional.nll_loss`. Just make sure you understand what it accepts as an input.\n", + "\n", + "Note that you are not required to build 152-layer monsters here. A 2-layer (one hidden, one output) neural network should already give you an edge over logistic regression.\n", + "\n", + "\n", + "__[bonus kudos]__\n", + "If you've already beaten logistic regression with a two-layer net, but enthusiasm still ain't gone, you can try improving the test accuracy even further! It should be possible to reach 90% without convnets.\n", + "\n", + "__SPOILERS!__\n", + "At the end of the notebook you will find a few tips and frequent errors. \n", + "If you feel confident enogh, just start coding right away and get there ~~if~~ once you need to untangle yourself. \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Parsing...\n", + "found broken img: ./notMNIST_small/F/Q3Jvc3NvdmVyIEJvbGRPYmxpcXVlLnR0Zg==.png [it's ok if <10 images are broken]\n", + "found broken img: ./notMNIST_small/A/RGVtb2NyYXRpY2FCb2xkT2xkc3R5bGUgQm9sZC50dGY=.png [it's ok if <10 images are broken]\n" + ] + } + ], + "source": [ + "from notmnist import load_notmnist\n", + "X_train, y_train, X_test, y_test = load_notmnist(letters='ABCDEFGHIJ')\n", + "X_train, X_test = X_train.reshape([-1, 784]), X_test.reshape([-1, 784])" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib inline\n", + "plt.figure(figsize=[12, 4])\n", + "for i in range(20):\n", + " plt.subplot(2, 10, i+1)\n", + " plt.imshow(X_train[i].reshape([28, 28]))\n", + " plt.title(str(y_train[i]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# < a whole lot of your code >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "\n", + "# SPOILERS!\n", + "\n", + "Recommended pipeline\n", + "\n", + "* Adapt logistic regression from previous assignment to classify one letter against others (e.g. A vs the rest)\n", + "* Generalize it to multiclass logistic regression.\n", + " - Either try to remember lecture 0 or google it.\n", + " - Instead of weight vector you'll have to use matrix (feature_id x class_id)\n", + " - softmax (exp over sum of exps) can implemented manually or as nn.Softmax (layer) F.softmax (function)\n", + " - probably better to use STOCHASTIC gradient descent (minibatch) for greater speed\n", + " - you can also try momentum/rmsprop/adawhatever\n", + " - in which case sample should probably be shuffled (or use random subsamples on each iteration)\n", + "* Add a hidden layer. Now your logistic regression uses hidden neurons instead of inputs.\n", + " - Hidden layer uses the same math as output layer (ex-logistic regression), but uses some nonlinearity (e.g. sigmoid) instead of softmax\n", + " - You need to train both layers, not just output layer :)\n", + " - 50 hidden neurons and a sigmoid nonlinearity will do for a start. Many ways to improve. \n", + " - In ideal case this totals to 2 torch.matmul's, 1 softmax and 1 relu/sigmoid\n", + " - __make sure this neural network works better than logistic regression__\n", + " \n", + "* Now's the time to try improving the network. Consider layers (size, neuron count), nonlinearities, optimization methods, initialization - whatever you want, but please avoid convolutions for now.\n", + " \n", + "* If anything seems wrong, try going through one step of training and printing everything you compute.\n", + "* If you see NaNs midway through optimization, you can estimate log P(y|x) as via F.log_softmax(layer_before_softmax)\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.6.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week4_[recap]_deep_learning/seminar_tensorflow.ipynb b/week04_[recap]_deep_learning/seminar_tensorflow.ipynb similarity index 100% rename from week4_[recap]_deep_learning/seminar_tensorflow.ipynb rename to week04_[recap]_deep_learning/seminar_tensorflow.ipynb diff --git a/week4_approx_rl/README.md b/week04_approx_rl/README.md similarity index 69% rename from week4_approx_rl/README.md rename to week04_approx_rl/README.md index 94c043c4e..d75903fc9 100644 --- a/week4_approx_rl/README.md +++ b/week04_approx_rl/README.md @@ -1,6 +1,6 @@ ## Materials -* [__lecture slides I__](https://yadi.sk/i/kGPiXpse3NR3n8), [__slides II__](https://yadi.sk/i/H07O_XEh3NR3oV) -* Our [lecture](https://yadi.sk/i/AHDU2p_j3FT3nr), [second lecture](https://yadi.sk/i/yBO0q4mI3GAxYd), [seminar](https://yadi.sk/i/EeUeheri3FT3ra) (russian) +* [__slides__](https://docs.google.com/presentation/d/1HEfIyKT0rIuUQCGAsR1PIVGirccDXu5LQvxhVUjuIqM) +* Our [lecture](https://yadi.sk/i/Gd9yWV1dpuB7BQ), [seminar](https://yadi.sk/i/mvtKAIRN2yKU2g) (russian) * David Silver lecture - [video](https://www.youtube.com/watch?v=UoPei5o4fps) @@ -18,6 +18,7 @@ * Article on dueling DQN - [arxiv](https://arxiv.org/pdf/1511.06581.pdf) * Article on double DQN - [arxiv](https://arxiv.org/abs/1509.06461) * Article on prioritized experience replay - [arxiv](https://arxiv.org/abs/1511.05952) +* Article on Rainbow: Combining Improvements in Deep Reinforcement Learning - [arxiv](https://arxiv.org/abs/1710.02298) * Article on bootstrap DQN - [pdf](https://papers.nips.cc/paper/6501-deep-exploration-via-bootstrapped-dqn.pdf), [summary](http://pemami4911.github.io/paper-summaries/2016/08/16/Deep-exploration.html) * Article on asynchronuous methods in deep RL - [arxiv](https://arxiv.org/abs/1602.01783) * Successor representations for reinforcement learning - [article](https://arxiv.org/abs/1606.02396), [video](https://www.youtube.com/watch?v=kNqXCn7K-BM&feature=youtu.be) @@ -33,10 +34,17 @@ ## Practice -From now on, we have two tracks, theano and tensorflow. We'll also add pytorch support soon. +* Seminar: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week04_approx_rl/seminar_pytorch.ipynb) +* Homework (main): [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week04_approx_rl/homework_pytorch_main.ipynb#scrollTo=KVvvo7k_ap8w) +* Homework (debug): [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week04_approx_rl/homework_pytorch_debug.ipynb#scrollTo=KVvvo7k_ap8w) -You can choose whichever track you want, but unless you're expertly familiar with your framework, we recommend you to start by completing the task in lasagne and only then reproduce your solution in your chosen framework. + + +From now on, we have two tracks, for pytorch and tensorflow. However, pytorch track is somewhat better supported by the course team. You can choose whichever track you want, but unless you're expertly familiar with your framework, we recommend you to start by completing the task in pytorch and only then reproduce your solution in your chosen framework. Begin with `seminar_.ipynb` and then proceed with `homework_.ipynb`. + __Note: you're not required to submit assignments in all three frameworks. Pick one and go with it. Maybe switch it occasionally if you want more challenge. __ + + diff --git a/week04_approx_rl/atari_wrappers.py b/week04_approx_rl/atari_wrappers.py new file mode 100644 index 000000000..e89e34ee5 --- /dev/null +++ b/week04_approx_rl/atari_wrappers.py @@ -0,0 +1,115 @@ +# taken from OpenAI baselines. + +import numpy as np +import gym + +class MaxAndSkipEnv(gym.Wrapper): + def __init__(self, env, skip=4): + """Return only every `skip`-th frame""" + gym.Wrapper.__init__(self, env) + # most recent raw observations (for max pooling across time steps) + self._obs_buffer = np.zeros((2,)+env.observation_space.shape, dtype=np.uint8) + self._skip = skip + + def step(self, action): + """Repeat action, sum reward, and max over last observations.""" + total_reward = 0.0 + done = None + for i in range(self._skip): + obs, reward, done, info = self.env.step(action) + if i == self._skip - 2: self._obs_buffer[0] = obs + if i == self._skip - 1: self._obs_buffer[1] = obs + total_reward += reward + if done: + break + # Note that the observation on the done=True frame + # doesn't matter + max_frame = self._obs_buffer.max(axis=0) + + return max_frame, total_reward, done, info + + def reset(self, **kwargs): + return self.env.reset(**kwargs) + + +class ClipRewardEnv(gym.RewardWrapper): + def __init__(self, env): + gym.RewardWrapper.__init__(self, env) + + def reward(self, reward): + """Bin reward to {+1, 0, -1} by its sign.""" + return np.sign(reward) + + +class FireResetEnv(gym.Wrapper): + def __init__(self, env): + """Take action on reset for environments that are fixed until firing.""" + gym.Wrapper.__init__(self, env) + assert env.unwrapped.get_action_meanings()[1] == 'FIRE' + assert len(env.unwrapped.get_action_meanings()) >= 3 + + def reset(self, **kwargs): + self.env.reset(**kwargs) + obs, _, done, _ = self.env.step(1) + if done: + self.env.reset(**kwargs) + obs, _, done, _ = self.env.step(2) + if done: + self.env.reset(**kwargs) + return obs + + def step(self, ac): + return self.env.step(ac) + + +class EpisodicLifeEnv(gym.Wrapper): + def __init__(self, env): + """Make end-of-life == end-of-episode, but only reset on true game over. + Done by DeepMind for the DQN and co. since it helps value estimation. + """ + gym.Wrapper.__init__(self, env) + self.lives = 0 + self.was_real_done = True + + def step(self, action): + obs, reward, done, info = self.env.step(action) + self.was_real_done = done + # check current lives, make loss of life terminal, + # then update lives to handle bonus lives + lives = self.env.unwrapped.ale.lives() + if lives < self.lives and lives > 0: + # for Qbert sometimes we stay in lives == 0 condition for a few frames + # so it's important to keep lives > 0, so that we only reset once + # the environment advertises done. + done = True + self.lives = lives + return obs, reward, done, info + + def reset(self, **kwargs): + """Reset only when lives are exhausted. + This way all states are still reachable even though lives are episodic, + and the learner need not know about any of this behind-the-scenes. + """ + if self.was_real_done: + obs = self.env.reset(**kwargs) + else: + # no-op step to advance from terminal/lost life state + obs, _, _, _ = self.env.step(0) + self.lives = self.env.unwrapped.ale.lives() + return obs + + +# in torch imgs have shape [c, h, w] instead of common [h, w, c] +class AntiTorchWrapper(gym.ObservationWrapper): + def __init__(self, env): + gym.ObservationWrapper.__init__(self, env) + + self.img_size = [env.observation_space.shape[i] + for i in [1, 2, 0] + ] + self.observation_space = gym.spaces.Box(0.0, 1.0, self.img_size) + + def _observation(self, img): + """what happens to each observation""" + img = img.transpose(1, 2, 0) + return img \ No newline at end of file diff --git a/week4_approx_rl/framebuffer.py b/week04_approx_rl/framebuffer.py similarity index 100% rename from week4_approx_rl/framebuffer.py rename to week04_approx_rl/framebuffer.py diff --git a/week4_approx_rl/homework_lasagne.ipynb b/week04_approx_rl/homework_lasagne.ipynb similarity index 100% rename from week4_approx_rl/homework_lasagne.ipynb rename to week04_approx_rl/homework_lasagne.ipynb diff --git a/week04_approx_rl/homework_pytorch_debug.ipynb b/week04_approx_rl/homework_pytorch_debug.ipynb new file mode 100644 index 000000000..5c456b432 --- /dev/null +++ b/week04_approx_rl/homework_pytorch_debug.ipynb @@ -0,0 +1,738 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Deep Q-Network implementation.\n", + "\n", + "This homework shamelessly demands you to implement a DQN - an approximate q-learning algorithm with experience replay and target networks - and see if it works any better this way.\n", + "\n", + "Original paper:\n", + "https://arxiv.org/pdf/1312.5602.pdf" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**This notebook is given for debug.** The main task is in the other notebook (**homework_pytorch_main**). The tasks are similar and share most of the code. The main difference is in environments. In main notebook it can take some 2 hours for the agent to start improving so it seems reasonable to launch the algorithm on a simpler env first. Here it is CartPole and it will train in several minutes.\n", + "\n", + "**We suggest the following pipeline:** First implement debug notebook then implement the main one.\n", + "\n", + "**About evaluation:** All points are given for the main notebook with one exception: if agent fails to beat the threshold in main notebook you can get 1 pt (instead of 3 pts) for beating the threshold in debug notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# # in google colab uncomment this\n", + "\n", + "# import os\n", + "\n", + "# os.system('apt-get install -y xvfb')\n", + "# os.system('wget https://raw.githubusercontent.com/yandexdataschool/Practical_DL/fall18/xvfb -O ../xvfb')\n", + "# os.system('apt-get install -y python-opengl ffmpeg')\n", + "# os.system('pip install pyglet==1.2.4')\n", + "\n", + "# os.system('python -m pip install -U pygame --user')\n", + "\n", + "# prefix = 'https://raw.githubusercontent.com/yandexdataschool/Practical_RL/spring19/week04_approx_rl/'\n", + "\n", + "# os.system('wget ' + prefix + 'atari_wrappers.py')\n", + "# os.system('wget ' + prefix + 'utils.py')\n", + "# os.system('wget ' + prefix + 'replay_buffer.py')\n", + "# os.system('wget ' + prefix + 'framebuffer.py')\n", + "\n", + "# print('setup complete')\n", + "\n", + "# XVFB will be launched if you run on a server\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Frameworks__ - we'll accept this homework in any deep learning framework. This particular notebook was designed for pytoch, but you find it easy to adapt it to almost any python-based deep learning framework." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "import numpy as np\n", + "import torch\n", + "import utils" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import gym\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### CartPole again" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ENV_NAME = 'CartPole-v1'\n", + "\n", + "def make_env(seed=None):\n", + " # CartPole is wrapped with a time limit wrapper by default\n", + " env = gym.make(ENV_NAME).unwrapped\n", + " if seed is not None:\n", + " env.seed(seed)\n", + " return env" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env = make_env()\n", + "env.reset()\n", + "state_shape, n_actions = env.observation_space.shape, env.action_space.n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Building a network" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We now need to build a neural network that can map observations to state q-values.\n", + "The model does not have to be huge yet. 1-2 hidden layers with < 200 neurons and ReLU activation will probably be enough. Batch normalization and dropout can spoil everything here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n", + "# those who have a GPU but feel unfair to use it can uncomment:\n", + "# device = torch.device('cpu')\n", + "device" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class DQNAgent(nn.Module):\n", + " def __init__(self, state_shape, n_actions, epsilon=0):\n", + "\n", + " super().__init__()\n", + " self.epsilon = epsilon\n", + " self.n_actions = n_actions\n", + " self.state_shape = state_shape\n", + " # Define your network body here. Please make sure agent is fully contained here\n", + " assert len(state_shape) == 1\n", + " state_dim = state_shape[0]\n", + " \n", + "\n", + " \n", + " def forward(self, state_t):\n", + " \"\"\"\n", + " takes agent's observation (tensor), returns qvalues (tensor)\n", + " :param state_t: a batch states, shape = [batch_size, *state_dim=4]\n", + " \"\"\"\n", + " # Use your network to compute qvalues for given state\n", + " qvalues = \n", + "\n", + " assert qvalues.requires_grad, \"qvalues must be a torch tensor with grad\"\n", + " assert len(\n", + " qvalues.shape) == 2 and qvalues.shape[0] == state_t.shape[0] and qvalues.shape[1] == n_actions\n", + "\n", + " return qvalues\n", + "\n", + " def get_qvalues(self, states):\n", + " \"\"\"\n", + " like forward, but works on numpy arrays, not tensors\n", + " \"\"\"\n", + " model_device = next(self.parameters()).device\n", + " states = torch.tensor(states, device=model_device, dtype=torch.float32)\n", + " qvalues = self.forward(states)\n", + " return qvalues.data.cpu().numpy()\n", + "\n", + " def sample_actions(self, qvalues):\n", + " \"\"\"pick actions given qvalues. Uses epsilon-greedy exploration strategy. \"\"\"\n", + " epsilon = self.epsilon\n", + " batch_size, n_actions = qvalues.shape\n", + "\n", + " random_actions = np.random.choice(n_actions, size=batch_size)\n", + " best_actions = qvalues.argmax(axis=-1)\n", + "\n", + " should_explore = np.random.choice(\n", + " [0, 1], batch_size, p=[1-epsilon, epsilon])\n", + " return np.where(should_explore, random_actions, best_actions)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "agent = DQNAgent(state_shape, n_actions, epsilon=0.5).to(device)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's try out our agent to see if it raises any errors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def evaluate(env, agent, n_games=1, greedy=False, t_max=10000):\n", + " \"\"\" Plays n_games full games. If greedy, picks actions as argmax(qvalues). Returns mean reward. \"\"\"\n", + " rewards = []\n", + " for _ in range(n_games):\n", + " s = env.reset()\n", + " reward = 0\n", + " for _ in range(t_max):\n", + " qvalues = agent.get_qvalues([s])\n", + " action = qvalues.argmax(axis=-1)[0] if greedy else agent.sample_actions(qvalues)[0]\n", + " s, r, done, _ = env.step(action)\n", + " reward += r\n", + " if done:\n", + " break\n", + "\n", + " rewards.append(reward)\n", + " return np.mean(rewards)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "evaluate(env, agent, n_games=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Experience replay\n", + "For this assignment, we provide you with experience replay buffer. If you implemented experience replay buffer in last week's assignment, you can copy-paste it here in main notebook **to get 2 bonus points**.\n", + "\n", + "![img](https://github.com/yandexdataschool/Practical_RL/raw/master/yet_another_week/_resource/exp_replay.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### The interface is fairly simple:\n", + "* `exp_replay.add(obs, act, rw, next_obs, done)` - saves (s,a,r,s',done) tuple into the buffer\n", + "* `exp_replay.sample(batch_size)` - returns observations, actions, rewards, next_observations and is_done for `batch_size` random samples.\n", + "* `len(exp_replay)` - returns number of elements stored in replay buffer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from replay_buffer import ReplayBuffer\n", + "exp_replay = ReplayBuffer(10)\n", + "\n", + "for _ in range(30):\n", + " exp_replay.add(env.reset(), env.action_space.sample(),\n", + " 1.0, env.reset(), done=False)\n", + "\n", + "obs_batch, act_batch, reward_batch, next_obs_batch, is_done_batch = exp_replay.sample(\n", + " 5)\n", + "\n", + "assert len(exp_replay) == 10, \"experience replay size should be 10 because that's what maximum capacity is\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def play_and_record(initial_state, agent, env, exp_replay, n_steps=1):\n", + " \"\"\"\n", + " Play the game for exactly n steps, record every (s,a,r,s', done) to replay buffer. \n", + " Whenever game ends, add record with done=True and reset the game.\n", + " It is guaranteed that env has done=False when passed to this function.\n", + "\n", + " PLEASE DO NOT RESET ENV UNLESS IT IS \"DONE\"\n", + "\n", + " :returns: return sum of rewards over time and the state in which the env stays\n", + " \"\"\"\n", + " s = initial_state\n", + " sum_rewards = 0\n", + "\n", + " # Play the game for n_steps as per instructions above\n", + " \n", + "\n", + " return sum_rewards, s" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# testing your code.\n", + "exp_replay = ReplayBuffer(2000)\n", + "\n", + "state = env.reset()\n", + "play_and_record(state, agent, env, exp_replay, n_steps=1000)\n", + "\n", + "# if you're using your own experience replay buffer, some of those tests may need correction.\n", + "# just make sure you know what your code does\n", + "assert len(exp_replay) == 1000, \"play_and_record should have added exactly 1000 steps, \"\\\n", + " \"but instead added %i\" % len(exp_replay)\n", + "is_dones = list(zip(*exp_replay._storage))[-1]\n", + "\n", + "assert 0 < np.mean(is_dones) < 0.1, \"Please make sure you restart the game whenever it is 'done' and record the is_done correctly into the buffer.\"\\\n", + " \"Got %f is_done rate over %i steps. [If you think it's your tough luck, just re-run the test]\" % (\n", + " np.mean(is_dones), len(exp_replay))\n", + "\n", + "for _ in range(100):\n", + " obs_batch, act_batch, reward_batch, next_obs_batch, is_done_batch = exp_replay.sample(\n", + " 10)\n", + " assert obs_batch.shape == next_obs_batch.shape == (10,) + state_shape\n", + " assert act_batch.shape == (\n", + " 10,), \"actions batch should have shape (10,) but is instead %s\" % str(act_batch.shape)\n", + " assert reward_batch.shape == (\n", + " 10,), \"rewards batch should have shape (10,) but is instead %s\" % str(reward_batch.shape)\n", + " assert is_done_batch.shape == (\n", + " 10,), \"is_done batch should have shape (10,) but is instead %s\" % str(is_done_batch.shape)\n", + " assert [int(i) in (0, 1)\n", + " for i in is_dones], \"is_done should be strictly True or False\"\n", + " assert [\n", + " 0 <= a < n_actions for a in act_batch], \"actions should be within [0, n_actions]\"\n", + "\n", + "print(\"Well done!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Target networks\n", + "\n", + "We also employ the so called \"target network\" - a copy of neural network weights to be used for reference Q-values:\n", + "\n", + "The network itself is an exact copy of agent network, but it's parameters are not trained. Instead, they are moved here from agent's actual network every so often.\n", + "\n", + "$$ Q_{reference}(s,a) = r + \\gamma \\cdot \\max _{a'} Q_{target}(s',a') $$\n", + "\n", + "![img](https://github.com/yandexdataschool/Practical_RL/raw/master/yet_another_week/_resource/target_net.png)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "target_network = DQNAgent(agent.state_shape, agent.n_actions, epsilon=0.5).to(device)\n", + "# This is how you can load weights from agent into target network\n", + "target_network.load_state_dict(agent.state_dict())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Learning with... Q-learning\n", + "Here we write a function similar to `agent.update` from tabular q-learning." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Compute Q-learning TD error:\n", + "\n", + "$$ L = { 1 \\over N} \\sum_i [ Q_{\\theta}(s,a) - Q_{reference}(s,a) ] ^2 $$\n", + "\n", + "With Q-reference defined as\n", + "\n", + "$$ Q_{reference}(s,a) = r(s,a) + \\gamma \\cdot max_{a'} Q_{target}(s', a') $$\n", + "\n", + "Where\n", + "* $Q_{target}(s',a')$ denotes q-value of next state and next action predicted by __target_network__\n", + "* $s, a, r, s'$ are current state, action, reward and next state respectively\n", + "* $\\gamma$ is a discount factor defined two cells above.\n", + "\n", + "\n", + "__Note 1:__ there's an example input below. Feel free to experiment with it before you write the function.\n", + "\n", + "__Note 2:__ compute_td_loss is a source of 99% of bugs in this homework. If reward doesn't improve, it often helps to go through it line by line [with a rubber duck](https://rubberduckdebugging.com/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_td_loss(states, actions, rewards, next_states, is_done,\n", + " agent, target_network,\n", + " gamma=0.99,\n", + " check_shapes=False,\n", + " device=device):\n", + " \"\"\" Compute td loss using torch operations only. Use the formulae above. \"\"\"\n", + " states = torch.tensor(states, device=device, dtype=torch.float) # shape: [batch_size, *state_shape]\n", + "\n", + " # for some torch reason should not make actions a tensor\n", + " actions = torch.tensor(actions, device=device, dtype=torch.long) # shape: [batch_size]\n", + " rewards = torch.tensor(rewards, device=device, dtype=torch.float) # shape: [batch_size]\n", + " # shape: [batch_size, *state_shape]\n", + " next_states = torch.tensor(next_states, device=device, dtype=torch.float)\n", + " is_done = torch.tensor(\n", + " is_done.astype('float32'),\n", + " device=device,\n", + " dtype=torch.float\n", + " ) # shape: [batch_size]\n", + " is_not_done = 1 - is_done\n", + "\n", + " # get q-values for all actions in current states\n", + " predicted_qvalues = agent(states)\n", + "\n", + " # compute q-values for all actions in next states\n", + " predicted_next_qvalues = target_network(next_states)\n", + " \n", + " # select q-values for chosen actions\n", + " predicted_qvalues_for_actions = predicted_qvalues[range(\n", + " len(actions)), actions]\n", + "\n", + " # compute V*(next_states) using predicted next q-values\n", + " next_state_values = \n", + "\n", + " assert next_state_values.dim(\n", + " ) == 1 and next_state_values.shape[0] == states.shape[0], \"must predict one value per state\"\n", + "\n", + " # compute \"target q-values\" for loss - it's what's inside square parentheses in the above formula.\n", + " # at the last state use the simplified formula: Q(s,a) = r(s,a) since s' doesn't exist\n", + " # you can multiply next state values by is_not_done to achieve this.\n", + " target_qvalues_for_actions = \n", + "\n", + " # mean squared error loss to minimize\n", + " loss = torch.mean((predicted_qvalues_for_actions -\n", + " target_qvalues_for_actions.detach()) ** 2)\n", + "\n", + " if check_shapes:\n", + " assert predicted_next_qvalues.data.dim(\n", + " ) == 2, \"make sure you predicted q-values for all actions in next state\"\n", + " assert next_state_values.data.dim(\n", + " ) == 1, \"make sure you computed V(s') as maximum over just the actions axis and not all axes\"\n", + " assert target_qvalues_for_actions.data.dim(\n", + " ) == 1, \"there's something wrong with target q-values, they must be a vector\"\n", + "\n", + " return loss" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sanity checks" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs_batch, act_batch, reward_batch, next_obs_batch, is_done_batch = exp_replay.sample(\n", + " 10)\n", + "\n", + "loss = compute_td_loss(obs_batch, act_batch, reward_batch, next_obs_batch, is_done_batch,\n", + " agent, target_network,\n", + " gamma=0.99, check_shapes=True)\n", + "loss.backward()\n", + "\n", + "assert loss.requires_grad and tuple(loss.data.size()) == (\n", + " ), \"you must return scalar loss - mean over batch\"\n", + "assert np.any(next(agent.parameters()).grad.data.cpu().numpy() !=\n", + " 0), \"loss must be differentiable w.r.t. network weights\"\n", + "assert np.all(next(target_network.parameters()).grad is None), \"target network should not have grads\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Main loop\n", + "\n", + "It's time to put everything together and see if it learns anything." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tqdm import trange\n", + "from IPython.display import clear_output\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "seed = \n", + "random.seed(seed)\n", + "np.random.seed(seed)\n", + "torch.manual_seed(seed)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env = make_env(seed)\n", + "state_dim = env.observation_space.shape\n", + "n_actions = env.action_space.n\n", + "state = env.reset()\n", + "\n", + "agent = DQNAgent(state_dim, n_actions, epsilon=1).to(device)\n", + "target_network = DQNAgent(state_dim, n_actions, epsilon=1).to(device)\n", + "target_network.load_state_dict(agent.state_dict())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exp_replay = ReplayBuffer(10**4)\n", + "for i in range(100):\n", + " if not utils.is_enough_ram(min_available_gb=0.1):\n", + " print(\"\"\"\n", + " Less than 100 Mb RAM available. \n", + " Make sure the buffer size in not too huge.\n", + " Also check, maybe other processes consume RAM heavily.\n", + " \"\"\"\n", + " )\n", + " break\n", + " play_and_record(state, agent, env, exp_replay, n_steps=10**2)\n", + " if len(exp_replay) == 10**4:\n", + " break\n", + "print(len(exp_replay))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "timesteps_per_epoch = 1\n", + "batch_size = 32\n", + "total_steps = 4 * 10**4\n", + "decay_steps = 1 * 10**4\n", + "\n", + "opt = torch.optim.Adam(agent.parameters(), lr=1e-4)\n", + "\n", + "init_epsilon = 1\n", + "final_epsilon = 0.1\n", + "\n", + "loss_freq = 20\n", + "refresh_target_network_freq = 100\n", + "eval_freq = 1000\n", + "\n", + "max_grad_norm = 5000" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mean_rw_history = []\n", + "td_loss_history = []\n", + "grad_norm_history = []\n", + "initial_state_v_history = []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "state = env.reset()\n", + "for step in trange(total_steps + 1):\n", + " if not utils.is_enough_ram():\n", + " print('less that 100 Mb RAM available, freezing')\n", + " print('make sure everything is ok and make KeyboardInterrupt to continue')\n", + " try:\n", + " while True:\n", + " pass\n", + " except KeyboardInterrupt:\n", + " pass\n", + "\n", + " agent.epsilon = utils.linear_decay(init_epsilon, final_epsilon, step, decay_steps)\n", + "\n", + " # play\n", + " _, state = play_and_record(state, agent, env, exp_replay, timesteps_per_epoch)\n", + "\n", + " # train\n", + " < sample batch_size of data from experience replay >\n", + "\n", + " loss = < compute TD loss >\n", + "\n", + " loss.backward()\n", + " grad_norm = nn.utils.clip_grad_norm_(agent.parameters(), max_grad_norm)\n", + " opt.step()\n", + " opt.zero_grad()\n", + "\n", + " if step % loss_freq == 0:\n", + " td_loss_history.append(loss.data.cpu().item())\n", + " grad_norm_history.append(grad_norm)\n", + "\n", + " if step % refresh_target_network_freq == 0:\n", + " # Load agent weights into target_network\n", + " \n", + "\n", + " if step % eval_freq == 0:\n", + " # eval the agent\n", + " mean_rw_history.append(evaluate(\n", + " make_env(seed=step), agent, n_games=3, greedy=True, t_max=1000)\n", + " )\n", + " initial_state_q_values = agent.get_qvalues(\n", + " [make_env(seed=step).reset()]\n", + " )\n", + " initial_state_v_history.append(np.max(initial_state_q_values))\n", + "\n", + " clear_output(True)\n", + " print(\"buffer size = %i, epsilon = %.5f\" %\n", + " (len(exp_replay), agent.epsilon))\n", + "\n", + " plt.figure(figsize=[16, 9])\n", + " plt.subplot(2, 2, 1)\n", + " plt.title(\"Mean reward per episode\")\n", + " plt.plot(mean_rw_history)\n", + " plt.grid()\n", + "\n", + " assert not np.isnan(td_loss_history[-1])\n", + " plt.subplot(2, 2, 2)\n", + " plt.title(\"TD loss history (smoothened)\")\n", + " plt.plot(utils.smoothen(td_loss_history))\n", + " plt.grid()\n", + "\n", + " plt.subplot(2, 2, 3)\n", + " plt.title(\"Initial state V\")\n", + " plt.plot(initial_state_v_history)\n", + " plt.grid()\n", + "\n", + " plt.subplot(2, 2, 4)\n", + " plt.title(\"Grad norm history (smoothened)\")\n", + " plt.plot(utils.smoothen(grad_norm_history))\n", + " plt.grid()\n", + "\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "final_score = evaluate(\n", + " make_env(),\n", + " agent, n_games=30, greedy=True, t_max=1000\n", + ")\n", + "print('final score:', final_score)\n", + "assert final_score > 300, 'not good enough for DQN'\n", + "print('Well done')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "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.6.8" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/week04_approx_rl/homework_pytorch_main.ipynb b/week04_approx_rl/homework_pytorch_main.ipynb new file mode 100644 index 000000000..4c0d16637 --- /dev/null +++ b/week04_approx_rl/homework_pytorch_main.ipynb @@ -0,0 +1,1302 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Deep Q-Network implementation.\n", + "\n", + "This homework shamelessly demands you to implement a DQN - an approximate q-learning algorithm with experience replay and target networks - and see if it works any better this way.\n", + "\n", + "Original paper:\n", + "https://arxiv.org/pdf/1312.5602.pdf" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**This notebook is the main notebook.** Another notebook is given for debug. (**homework_pytorch_main**). The tasks are similar and share most of the code. The main difference is in environments. In main notebook it can take some 2 hours for the agent to start improving so it seems reasonable to launch the algorithm on a simpler env first. Here it is CartPole and it will train in several minutes.\n", + "\n", + "**We suggest the following pipeline:** First implement debug notebook then implement the main one.\n", + "\n", + "**About evaluation:** All points are given for the main notebook with one exception: if agent fails to beat the threshold in main notebook you can get 1 pt (instead of 3 pts) for beating the threshold in debug notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# # in google colab uncomment this\n", + "\n", + "# import os\n", + "\n", + "# os.system('apt-get install -y xvfb')\n", + "# os.system('wget https://raw.githubusercontent.com/yandexdataschool/Practical_DL/fall18/xvfb -O ../xvfb')\n", + "# os.system('apt-get install -y python-opengl ffmpeg')\n", + "# os.system('pip install pyglet==1.2.4')\n", + "\n", + "# os.system('python -m pip install -U pygame --user')\n", + "\n", + "# prefix = 'https://raw.githubusercontent.com/yandexdataschool/Practical_RL/spring19/week04_approx_rl/'\n", + "\n", + "# os.system('wget ' + prefix + 'atari_wrappers.py')\n", + "# os.system('wget ' + prefix + 'utils.py')\n", + "# os.system('wget ' + prefix + 'replay_buffer.py')\n", + "# os.system('wget ' + prefix + 'framebuffer.py')\n", + "\n", + "# print('setup complete')\n", + "\n", + "# XVFB will be launched if you run on a server\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Frameworks__ - we'll accept this homework in any deep learning framework. This particular notebook was designed for pytoch, but you find it easy to adapt it to almost any python-based deep learning framework." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "import numpy as np\n", + "import torch\n", + "import utils" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import gym\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's play some old videogames\n", + "![img](https://github.com/yandexdataschool/Practical_RL/raw/master/yet_another_week/_resource/nerd.png)\n", + "\n", + "This time we're gonna apply approximate q-learning to an atari game called Breakout. It's not the hardest thing out there, but it's definitely way more complex than anything we tried before.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ENV_NAME = \"BreakoutNoFrameskip-v4\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Preprocessing (3 pts)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's see what observations look like." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env = gym.make(ENV_NAME)\n", + "env.reset()\n", + "\n", + "n_cols = 5\n", + "n_rows = 2\n", + "fig = plt.figure(figsize=(16, 9))\n", + "\n", + "for row in range(n_rows):\n", + " for col in range(n_cols):\n", + " ax = fig.add_subplot(n_rows, n_cols, row * n_cols + col + 1)\n", + " ax.imshow(env.render('rgb_array'))\n", + " env.step(env.action_space.sample())\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Let's play a little.**\n", + "\n", + "Pay attention to zoom and fps args of play function. Control: A, D, space." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# does not work in colab.\n", + "# make keyboard interrupt to continue\n", + "\n", + "from gym.utils.play import play\n", + "\n", + "play(env=gym.make(ENV_NAME), zoom=5, fps=30)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Processing game image \n", + "\n", + "Raw atari images are large, 210x160x3 by default. However, we don't need that level of detail in order to learn them.\n", + "\n", + "We can thus save a lot of time by preprocessing game image, including\n", + "* Resizing to a smaller shape, 64 x 64\n", + "* Converting to grayscale\n", + "* Cropping irrelevant image parts (top, bottom and edges)\n", + "\n", + "Also please keep one dimension for channel so that final shape would be 1 x 64 x 64.\n", + "\n", + "Tip: You can implement your own grayscale converter and assign a huge weight to the red channel. This dirty trick is not necessary but it will speed up learning." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from gym.core import ObservationWrapper\n", + "from gym.spaces import Box\n", + "\n", + "\n", + "class PreprocessAtariObs(ObservationWrapper):\n", + " def __init__(self, env):\n", + " \"\"\"A gym wrapper that crops, scales image into the desired shapes and grayscales it.\"\"\"\n", + " ObservationWrapper.__init__(self, env)\n", + "\n", + " self.img_size = (1, 64, 64)\n", + " self.observation_space = Box(0.0, 1.0, self.img_size)\n", + "\n", + "\n", + " def _to_gray_scale(self, rgb, channel_weights=[0.8, 0.1, 0.1]):\n", + " \n", + "\n", + "\n", + " def _observation(self, img):\n", + " \"\"\"what happens to each observation\"\"\"\n", + "\n", + " # Here's what you need to do:\n", + " # * crop image, remove irrelevant parts\n", + " # * resize image to self.img_size\n", + " # (use imresize from any library you want,\n", + " # e.g. opencv, skimage, PIL, keras)\n", + " # * cast image to grayscale\n", + " # * convert image pixels to (0,1) range, float32 type\n", + " \n", + " return < ... >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import gym\n", + "# spawn game instance for tests\n", + "env = gym.make(ENV_NAME) # create raw env\n", + "env = PreprocessAtariObs(env)\n", + "observation_shape = env.observation_space.shape\n", + "n_actions = env.action_space.n\n", + "env.reset()\n", + "obs, _, _, _ = env.step(env.action_space.sample())\n", + "\n", + "# test observation\n", + "assert obs.ndim == 3, \"observation must be [channel, h, w] even if there's just one channel\"\n", + "assert obs.shape == observation_shape\n", + "assert obs.dtype == 'float32'\n", + "assert len(np.unique(obs)) > 2, \"your image must not be binary\"\n", + "assert 0 <= np.min(obs) and np.max(\n", + " obs) <= 1, \"convert image pixels to [0,1] range\"\n", + "\n", + "print(\"Formal tests seem fine. Here's an example of what you'll get.\")\n", + "\n", + "n_cols = 5\n", + "n_rows = 2\n", + "fig = plt.figure(figsize=(16, 9))\n", + "obs = env.reset()\n", + "for row in range(n_rows):\n", + " for col in range(n_cols):\n", + " ax = fig.add_subplot(n_rows, n_cols, row * n_cols + col + 1)\n", + " ax.imshow(obs[0, :, :], interpolation='none', cmap='gray')\n", + " obs, _, _, _ = env.step(env.action_space.sample())\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Wrapping." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**About the game:** You have 5 lives and get points for breaking the wall. Higher bricks cost more than the lower ones. There are 4 actions: start game (should be called at the beginning and after each life is lost), move left, move right and do nothing. There are some common wrappers used for Atari environments." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "import atari_wrappers\n", + "\n", + "def PrimaryAtariWrap(env, clip_rewards=True):\n", + " assert 'NoFrameskip' in env.spec.id\n", + "\n", + " # This wrapper holds the same action for frames and outputs\n", + " # the maximal pixel value of 2 last frames (to handle blinking\n", + " # in some envs)\n", + " env = atari_wrappers.MaxAndSkipEnv(env, skip=4)\n", + "\n", + " # This wrapper sends done=True when each life is lost\n", + " # (not all the 5 lives that are givern by the game rules).\n", + " # It should make easier for the agent to understand that losing is bad.\n", + " env = atari_wrappers.EpisodicLifeEnv(env)\n", + "\n", + " # This wrapper laucnhes the ball when an episode starts.\n", + " # Without it the agent has to learn this action, too.\n", + " # Actually it can but learning would take longer.\n", + " env = atari_wrappers.FireResetEnv(env)\n", + "\n", + " # This wrapper transforms rewards to {-1, 0, 1} according to their sign\n", + " if clip_rewards:\n", + " env = atari_wrappers.ClipRewardEnv(env)\n", + "\n", + " # This wrapper is yours :)\n", + " env = PreprocessAtariObs(env)\n", + " return env" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Let's see if the game is still playable after applying the wrappers.**\n", + "At playing the EpisodicLifeEnv wrapper seems not to work but actually it does (because after when life finishes a new ball is dropped automatically - it means that FireResetEnv wrapper understands that a new episode began)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# does not work in colab.\n", + "# make keyboard interrupt to continue\n", + "\n", + "from gym.utils.play import play\n", + "\n", + "def make_play_env():\n", + " env = gym.make(ENV_NAME)\n", + " env = PrimaryAtariWrap(env)\n", + "# in torch imgs have shape [c, h, w] instead of common [h, w, c]\n", + " env = atari_wrappers.AntiTorchWrapper(env)\n", + " return env\n", + "\n", + "play(make_play_env(), zoom=10, fps=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Frame buffer\n", + "\n", + "Our agent can only process one observation at a time, so we gotta make sure it contains enough information to find optimal actions. For instance, agent has to react to moving objects so he must be able to measure object's velocity.\n", + "\n", + "To do so, we introduce a buffer that stores 4 last images. This time everything is pre-implemented for you, not really by the staff of the course :)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "from framebuffer import FrameBuffer\n", + "\n", + "def make_env(clip_rewards=True, seed=None):\n", + " env = gym.make(ENV_NAME) # create raw env\n", + " if seed is not None:\n", + " env.seed(seed)\n", + " env = PrimaryAtariWrap(env, clip_rewards)\n", + " env = FrameBuffer(env, n_frames=4, dim_order='pytorch')\n", + " return env\n", + "\n", + "env = make_env()\n", + "env.reset()\n", + "n_actions = env.action_space.n\n", + "state_shape = env.observation_space.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for _ in range(12):\n", + " obs, _, _, _ = env.step(env.action_space.sample())\n", + "\n", + "plt.figure(figsize=[12,10])\n", + "plt.title(\"Game image\")\n", + "plt.imshow(env.render(\"rgb_array\"))\n", + "plt.show()\n", + "\n", + "plt.figure(figsize=[15,15])\n", + "plt.title(\"Agent observation (4 frames top to bottom)\")\n", + "plt.imshow(utils.img_by_obs(obs, state_shape), cmap='gray')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## DQN as it is (4 pts)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Building a network\n", + "\n", + "We now need to build a neural network that can map images to state q-values. This network will be called on every agent's step so it better not be resnet-152 unless you have an array of GPUs. Instead, you can use strided convolutions with a small number of features to save time and memory.\n", + "\n", + "You can build any architecture you want, but for reference, here's something that will more or less work:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![img](https://github.com/yandexdataschool/Practical_RL/raw/master/yet_another_week/_resource/dqn_arch.png)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n", + "# those who have a GPU but feel unfair to use it can uncomment:\n", + "# device = torch.device('cpu')\n", + "device" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def conv2d_size_out(size, kernel_size, stride):\n", + " \"\"\"\n", + " common use case:\n", + " cur_layer_img_w = conv2d_size_out(cur_layer_img_w, kernel_size, stride)\n", + " cur_layer_img_h = conv2d_size_out(cur_layer_img_h, kernel_size, stride)\n", + " to understand the shape for dense layer's input\n", + " \"\"\"\n", + " return (size - (kernel_size - 1) - 1) // stride + 1\n", + "\n", + "\n", + "class Flatten(nn.Module):\n", + " def __init__(self):\n", + " super().__init__()\n", + " \n", + " def forward(self, x):\n", + " return x.view(x.size(0), -1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class DQNAgent(nn.Module):\n", + " def __init__(self, state_shape, n_actions, epsilon=0):\n", + "\n", + " super().__init__()\n", + " self.epsilon = epsilon\n", + " self.n_actions = n_actions\n", + " self.state_shape = state_shape\n", + "\n", + " # Define your network body here. Please make sure agent is fully contained here\n", + " \n", + "\n", + " def forward(self, state_t):\n", + " \"\"\"\n", + " takes agent's observation (tensor), returns qvalues (tensor)\n", + " :param state_t: a batch of 4-frame buffers, shape = [batch_size, 4, h, w]\n", + " \"\"\"\n", + " # Use your network to compute qvalues for given state\n", + " qvalues = \n", + "\n", + " assert qvalues.requires_grad, \"qvalues must be a torch tensor with grad\"\n", + " assert len(\n", + " qvalues.shape) == 2 and qvalues.shape[0] == state_t.shape[0] and qvalues.shape[1] == n_actions\n", + "\n", + " return qvalues\n", + "\n", + " def get_qvalues(self, states):\n", + " \"\"\"\n", + " like forward, but works on numpy arrays, not tensors\n", + " \"\"\"\n", + " model_device = next(self.parameters()).device\n", + " states = torch.tensor(states, device=model_device, dtype=torch.float)\n", + " qvalues = self.forward(states)\n", + " return qvalues.data.cpu().numpy()\n", + "\n", + " def sample_actions(self, qvalues):\n", + " \"\"\"pick actions given qvalues. Uses epsilon-greedy exploration strategy. \"\"\"\n", + " epsilon = self.epsilon\n", + " batch_size, n_actions = qvalues.shape\n", + "\n", + " random_actions = np.random.choice(n_actions, size=batch_size)\n", + " best_actions = qvalues.argmax(axis=-1)\n", + "\n", + " should_explore = np.random.choice(\n", + " [0, 1], batch_size, p=[1-epsilon, epsilon])\n", + " return np.where(should_explore, random_actions, best_actions)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "agent = DQNAgent(state_shape, n_actions, epsilon=0.5).to(device)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's try out our agent to see if it raises any errors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def evaluate(env, agent, n_games=1, greedy=False, t_max=10000):\n", + " \"\"\" Plays n_games full games. If greedy, picks actions as argmax(qvalues). Returns mean reward. \"\"\"\n", + " rewards = []\n", + " for _ in range(n_games):\n", + " s = env.reset()\n", + " reward = 0\n", + " for _ in range(t_max):\n", + " qvalues = agent.get_qvalues([s])\n", + " action = qvalues.argmax(axis=-1)[0] if greedy else agent.sample_actions(qvalues)[0]\n", + " s, r, done, _ = env.step(action)\n", + " reward += r\n", + " if done:\n", + " break\n", + "\n", + " rewards.append(reward)\n", + " return np.mean(rewards)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "evaluate(env, agent, n_games=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Experience replay\n", + "For this assignment, we provide you with experience replay buffer. If you implemented experience replay buffer in last week's assignment, you can copy-paste it here **to get 2 bonus points**.\n", + "\n", + "![img](https://github.com/yandexdataschool/Practical_RL/raw/master/yet_another_week/_resource/exp_replay.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### The interface is fairly simple:\n", + "* `exp_replay.add(obs, act, rw, next_obs, done)` - saves (s,a,r,s',done) tuple into the buffer\n", + "* `exp_replay.sample(batch_size)` - returns observations, actions, rewards, next_observations and is_done for `batch_size` random samples.\n", + "* `len(exp_replay)` - returns number of elements stored in replay buffer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from replay_buffer import ReplayBuffer\n", + "exp_replay = ReplayBuffer(10)\n", + "\n", + "for _ in range(30):\n", + " exp_replay.add(env.reset(), env.action_space.sample(),\n", + " 1.0, env.reset(), done=False)\n", + "\n", + "obs_batch, act_batch, reward_batch, next_obs_batch, is_done_batch = exp_replay.sample(\n", + " 5)\n", + "\n", + "assert len(exp_replay) == 10, \"experience replay size should be 10 because that's what maximum capacity is\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def play_and_record(initial_state, agent, env, exp_replay, n_steps=1):\n", + " \"\"\"\n", + " Play the game for exactly n steps, record every (s,a,r,s', done) to replay buffer. \n", + " Whenever game ends, add record with done=True and reset the game.\n", + " It is guaranteed that env has done=False when passed to this function.\n", + "\n", + " PLEASE DO NOT RESET ENV UNLESS IT IS \"DONE\"\n", + "\n", + " :returns: return sum of rewards over time and the state in which the env stays\n", + " \"\"\"\n", + " s = initial_state\n", + " sum_rewards = 0\n", + "\n", + " # Play the game for n_steps as per instructions above\n", + " \n", + "\n", + " return sum_rewards, s" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# testing your code.\n", + "exp_replay = ReplayBuffer(2000)\n", + "\n", + "state = env.reset()\n", + "play_and_record(state, agent, env, exp_replay, n_steps=1000)\n", + "\n", + "# if you're using your own experience replay buffer, some of those tests may need correction.\n", + "# just make sure you know what your code does\n", + "assert len(exp_replay) == 1000, \"play_and_record should have added exactly 1000 steps, \"\\\n", + " \"but instead added %i\" % len(exp_replay)\n", + "is_dones = list(zip(*exp_replay._storage))[-1]\n", + "\n", + "assert 0 < np.mean(is_dones) < 0.1, \"Please make sure you restart the game whenever it is 'done' and record the is_done correctly into the buffer.\"\\\n", + " \"Got %f is_done rate over %i steps. [If you think it's your tough luck, just re-run the test]\" % (\n", + " np.mean(is_dones), len(exp_replay))\n", + "\n", + "for _ in range(100):\n", + " obs_batch, act_batch, reward_batch, next_obs_batch, is_done_batch = exp_replay.sample(\n", + " 10)\n", + " assert obs_batch.shape == next_obs_batch.shape == (10,) + state_shape\n", + " assert act_batch.shape == (\n", + " 10,), \"actions batch should have shape (10,) but is instead %s\" % str(act_batch.shape)\n", + " assert reward_batch.shape == (\n", + " 10,), \"rewards batch should have shape (10,) but is instead %s\" % str(reward_batch.shape)\n", + " assert is_done_batch.shape == (\n", + " 10,), \"is_done batch should have shape (10,) but is instead %s\" % str(is_done_batch.shape)\n", + " assert [int(i) in (0, 1)\n", + " for i in is_dones], \"is_done should be strictly True or False\"\n", + " assert [\n", + " 0 <= a < n_actions for a in act_batch], \"actions should be within [0, n_actions)\"\n", + "\n", + "print(\"Well done!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Target networks\n", + "\n", + "We also employ the so called \"target network\" - a copy of neural network weights to be used for reference Q-values:\n", + "\n", + "The network itself is an exact copy of agent network, but it's parameters are not trained. Instead, they are moved here from agent's actual network every so often.\n", + "\n", + "$$ Q_{reference}(s,a) = r + \\gamma \\cdot \\max _{a'} Q_{target}(s',a') $$\n", + "\n", + "![img](https://github.com/yandexdataschool/Practical_RL/raw/master/yet_another_week/_resource/target_net.png)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "target_network = DQNAgent(agent.state_shape, agent.n_actions, epsilon=0.5).to(device)\n", + "# This is how you can load weights from agent into target network\n", + "target_network.load_state_dict(agent.state_dict())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Learning with... Q-learning\n", + "Here we write a function similar to `agent.update` from tabular q-learning." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Compute Q-learning TD error:\n", + "\n", + "$$ L = { 1 \\over N} \\sum_i [ Q_{\\theta}(s,a) - Q_{reference}(s,a) ] ^2 $$\n", + "\n", + "With Q-reference defined as\n", + "\n", + "$$ Q_{reference}(s,a) = r(s,a) + \\gamma \\cdot max_{a'} Q_{target}(s', a') $$\n", + "\n", + "Where\n", + "* $Q_{target}(s',a')$ denotes q-value of next state and next action predicted by __target_network__\n", + "* $s, a, r, s'$ are current state, action, reward and next state respectively\n", + "* $\\gamma$ is a discount factor defined two cells above.\n", + "\n", + "\n", + "__Note 1:__ there's an example input below. Feel free to experiment with it before you write the function.\n", + "\n", + "__Note 2:__ compute_td_loss is a source of 99% of bugs in this homework. If reward doesn't improve, it often helps to go through it line by line [with a rubber duck](https://rubberduckdebugging.com/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_td_loss(states, actions, rewards, next_states, is_done,\n", + " agent, target_network,\n", + " gamma=0.99,\n", + " check_shapes=False,\n", + " device=device):\n", + " \"\"\" Compute td loss using torch operations only. Use the formulae above. \"\"\"\n", + " states = torch.tensor(states, device=device, dtype=torch.float) # shape: [batch_size, *state_shape]\n", + "\n", + " # for some torch reason should not make actions a tensor\n", + " actions = torch.tensor(actions, device=device, dtype=torch.long) # shape: [batch_size]\n", + " rewards = torch.tensor(rewards, device=device, dtype=torch.float) # shape: [batch_size]\n", + " # shape: [batch_size, *state_shape]\n", + " next_states = torch.tensor(next_states, device=device, dtype=torch.float)\n", + " is_done = torch.tensor(\n", + " is_done.astype('float32'),\n", + " device=device,\n", + " dtype=torch.float\n", + " ) # shape: [batch_size]\n", + " is_not_done = 1 - is_done\n", + "\n", + " # get q-values for all actions in current states\n", + " predicted_qvalues = agent(states)\n", + "\n", + " # compute q-values for all actions in next states\n", + " predicted_next_qvalues = target_network(next_states)\n", + " \n", + " # select q-values for chosen actions\n", + " predicted_qvalues_for_actions = predicted_qvalues[range(\n", + " len(actions)), actions]\n", + "\n", + " # compute V*(next_states) using predicted next q-values\n", + " next_state_values = \n", + "\n", + " assert next_state_values.dim(\n", + " ) == 1 and next_state_values.shape[0] == states.shape[0], \"must predict one value per state\"\n", + "\n", + " # compute \"target q-values\" for loss - it's what's inside square parentheses in the above formula.\n", + " # at the last state use the simplified formula: Q(s,a) = r(s,a) since s' doesn't exist\n", + " # you can multiply next state values by is_not_done to achieve this.\n", + " target_qvalues_for_actions = \n", + "\n", + " # mean squared error loss to minimize\n", + " loss = torch.mean((predicted_qvalues_for_actions -\n", + " target_qvalues_for_actions.detach()) ** 2)\n", + "\n", + " if check_shapes:\n", + " assert predicted_next_qvalues.data.dim(\n", + " ) == 2, \"make sure you predicted q-values for all actions in next state\"\n", + " assert next_state_values.data.dim(\n", + " ) == 1, \"make sure you computed V(s') as maximum over just the actions axis and not all axes\"\n", + " assert target_qvalues_for_actions.data.dim(\n", + " ) == 1, \"there's something wrong with target q-values, they must be a vector\"\n", + "\n", + " return loss" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sanity checks" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs_batch, act_batch, reward_batch, next_obs_batch, is_done_batch = exp_replay.sample(\n", + " 10)\n", + "\n", + "loss = compute_td_loss(obs_batch, act_batch, reward_batch, next_obs_batch, is_done_batch,\n", + " agent, target_network,\n", + " gamma=0.99, check_shapes=True)\n", + "loss.backward()\n", + "\n", + "assert loss.requires_grad and tuple(loss.data.size()) == (\n", + " ), \"you must return scalar loss - mean over batch\"\n", + "assert np.any(next(agent.parameters()).grad.data.cpu().numpy() !=\n", + " 0), \"loss must be differentiable w.r.t. network weights\"\n", + "assert np.all(next(target_network.parameters()).grad is None), \"target network should not have grads\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Main loop (3 pts)\n", + "\n", + "**If deadline is tonight and it has not converged:** It is ok. Send the notebook today and when it converges send it again.\n", + "If the code is exactly the same points will not be discounted.\n", + "\n", + "It's time to put everything together and see if it learns anything." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tqdm import trange\n", + "from IPython.display import clear_output\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "seed = \n", + "random.seed(seed)\n", + "np.random.seed(seed)\n", + "torch.manual_seed(seed)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env = make_env(seed)\n", + "state_shape = env.observation_space.shape\n", + "n_actions = env.action_space.n\n", + "state = env.reset()\n", + "\n", + "agent = DQNAgent(state_shape, n_actions, epsilon=1).to(device)\n", + "target_network = DQNAgent(state_shape, n_actions).to(device)\n", + "target_network.load_state_dict(agent.state_dict())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Buffer of size $10^4$ fits into 5 Gb RAM.\n", + "\n", + "Larger sizes ($10^5$ and $10^6$ are common) can be used. It can improve the learning, but $10^4$ is quiet enough. $10^2$ will probably fail learning." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exp_replay = ReplayBuffer(10**4)\n", + "for i in range(100):\n", + " if not utils.is_enough_ram(min_available_gb=0.1):\n", + " print(\"\"\"\n", + " Less than 100 Mb RAM available. \n", + " Make sure the buffer size in not too huge.\n", + " Also check, maybe other processes consume RAM heavily.\n", + " \"\"\"\n", + " )\n", + " break\n", + " play_and_record(state, agent, env, exp_replay, n_steps=10**2)\n", + " if len(exp_replay) == 10**4:\n", + " break\n", + "print(len(exp_replay))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "timesteps_per_epoch = 1\n", + "batch_size = 16\n", + "total_steps = 3 * 10**6\n", + "decay_steps = 10**6\n", + "\n", + "opt = torch.optim.Adam(agent.parameters(), lr=1e-4)\n", + "\n", + "init_epsilon = 1\n", + "final_epsilon = 0.1\n", + "\n", + "loss_freq = 50\n", + "refresh_target_network_freq = 5000\n", + "eval_freq = 5000\n", + "\n", + "max_grad_norm = 50\n", + "\n", + "n_lives = 5" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mean_rw_history = []\n", + "td_loss_history = []\n", + "grad_norm_history = []\n", + "initial_state_v_history = []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "state = env.reset()\n", + "for step in trange(total_steps + 1):\n", + " if not utils.is_enough_ram():\n", + " print('less that 100 Mb RAM available, freezing')\n", + " print('make sure everythin is ok and make KeyboardInterrupt to continue')\n", + " try:\n", + " while True:\n", + " pass\n", + " except KeyboardInterrupt:\n", + " pass\n", + "\n", + " agent.epsilon = utils.linear_decay(init_epsilon, final_epsilon, step, decay_steps)\n", + "\n", + " # play\n", + " _, state = play_and_record(state, agent, env, exp_replay, timesteps_per_epoch)\n", + "\n", + " # train\n", + " < sample batch_size of data from experience replay >\n", + "\n", + " loss = < compute TD loss >\n", + "\n", + " loss.backward()\n", + " grad_norm = nn.utils.clip_grad_norm_(agent.parameters(), max_grad_norm)\n", + " opt.step()\n", + " opt.zero_grad()\n", + "\n", + " if step % loss_freq == 0:\n", + " td_loss_history.append(loss.data.cpu().item())\n", + " grad_norm_history.append(grad_norm)\n", + "\n", + " if step % refresh_target_network_freq == 0:\n", + " # Load agent weights into target_network\n", + " \n", + "\n", + " if step % eval_freq == 0:\n", + " mean_rw_history.append(evaluate(\n", + " make_env(clip_rewards=True, seed=step), agent, n_games=3 * n_lives, greedy=True)\n", + " )\n", + " initial_state_q_values = agent.get_qvalues(\n", + " [make_env(seed=step).reset()]\n", + " )\n", + " initial_state_v_history.append(np.max(initial_state_q_values))\n", + "\n", + " clear_output(True)\n", + " print(\"buffer size = %i, epsilon = %.5f\" %\n", + " (len(exp_replay), agent.epsilon))\n", + "\n", + " plt.figure(figsize=[16, 9])\n", + "\n", + " plt.subplot(2, 2, 1)\n", + " plt.title(\"Mean reward per life\")\n", + " plt.plot(mean_rw_history)\n", + " plt.grid()\n", + "\n", + " assert not np.isnan(td_loss_history[-1])\n", + " plt.subplot(2, 2, 2)\n", + " plt.title(\"TD loss history (smoothened)\")\n", + " plt.plot(utils.smoothen(td_loss_history))\n", + " plt.grid()\n", + "\n", + " plt.subplot(2, 2, 3)\n", + " plt.title(\"Initial state V\")\n", + " plt.plot(initial_state_v_history)\n", + " plt.grid()\n", + "\n", + " plt.subplot(2, 2, 4)\n", + " plt.title(\"Grad norm history (smoothened)\")\n", + " plt.plot(utils.smoothen(grad_norm_history))\n", + " plt.grid()\n", + "\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Agent is evaluated for 1 life, not for a whole episode of 5 lives. Rewards in evaluation are also truncated. Cuz this is what environment the agent is learning in and in this way mean rewards per life can be compared with initial state value\n", + "\n", + "The goal is to get 10 points in the real env. So 2 or better 3 points in the preprocessed one will probably be enough. You can interrupt learning then." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Final scoring is done on a whole episode with all 5 lives." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "final_score = evaluate(\n", + " make_env(clip_rewards=False, seed=9),\n", + " agent, n_games=30, greedy=True, t_max=10 * 1000\n", + ") * n_lives\n", + "print('final score:', final_score)\n", + "assert final_score > 10, 'not as cool as DQN can'\n", + "print('Cool!')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How to interpret plots:\n", + "\n", + "This aint no supervised learning so don't expect anything to improve monotonously. \n", + "* **TD loss** is the MSE between agent's current Q-values and target Q-values. It may slowly increase or decrease, it's ok. The \"not ok\" behavior includes going NaN or stayng at exactly zero before agent has perfect performance.\n", + "* **grad norm** just shows the intensivity of training. Not ok is growing to values of about 100 (or maybe even 50) though it depends on network architecture.\n", + "* **mean reward** is the expected sum of r(s,a) agent gets over the full game session. It will oscillate, but on average it should get higher over time (after a few thousand iterations...). \n", + " * In basic q-learning implementation it takes about 40k steps to \"warm up\" agent before it starts to get better.\n", + "* **Initial state V** is the expected discounted reward for episode in the oppinion of the agent. It should behave more smoothly than **mean reward**. It should get higher over time but sometimes can experience drawdowns because of the agaent's overestimates.\n", + "* **buffer size** - this one is simple. It should go up and cap at max size.\n", + "* **epsilon** - agent's willingness to explore. If you see that agent's already at 0.01 epsilon before it's average reward is above 0 - it means you need to increase epsilon. Set it back to some 0.2 - 0.5 and decrease the pace at which it goes down.\n", + "* Smoothing of plots is done with a gaussian kernel\n", + "\n", + "At first your agent will lose quickly. Then it will learn to suck less and at least hit the ball a few times before it loses. Finally it will learn to actually score points.\n", + "\n", + "**Training will take time.** A lot of it actually. Probably you will not see any improvment during first **150k** time steps (note that by default in this notebook agent is evaluated every 5000 time steps).\n", + "\n", + "But hey, long training time isn't _that_ bad:\n", + "![img](https://github.com/yandexdataschool/Practical_RL/raw/master/yet_another_week/_resource/training.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## About hyperparameters:\n", + "\n", + "The task has something in common with supervised learning: loss is optimized through the buffer (instead of Train dataset). But the distribution of states and actions in the buffer **is not stationary** and depends on the policy that generated it. It can even happen that the mean TD error across the buffer is very low but the performance is extremely poor (imagine the agent collecting data to the buffer always manages to avoid the ball).\n", + "\n", + "* Total timesteps and training time: It seems to be so huge, but actually it is normal for RL.\n", + "\n", + "* $\\epsilon$ decay shedule was taken from the original paper and is like traditional for epsilon-greedy policies. At the beginning of the training the agent's greedy policy is poor so many random actions should be taken.\n", + "\n", + "* Optimizer: In the original paper RMSProp was used (they did not have Adam in 2013) and it can work not worse than Adam. For us Adam was default and it worked.\n", + "\n", + "* lr: $10^{-3}$ would probably be too huge\n", + "\n", + "* batch size: This one can be very important: if it is too small the agent can fail to learn. Huge batch takes more time to process. If batch of size 8 can not be processed on the hardware you use take 2 (or even 4) batches of size 4, divide the loss on them by 2 (or 4) and make optimization step after both backward() calls in torch.\n", + "\n", + "* target network update frequency: has something in common with learning rate. Too frequent updates can lead to divergence. Too rare can lead to slow leraning. For millions of total timesteps thousands of inner steps seem ok. One iteration of target network updating is an iteration of the (this time approximate) $\\gamma$-compression that stands behind Q-learning. The more inner steps it makes the more accurate is the compression.\n", + "* max_grad_norm - just huge enough. In torch clip_grad_norm also evaluates the norm before clipping and it can be convenient for logging." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Video" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "# record sessions\n", + "import gym.wrappers\n", + "env_monitor = gym.wrappers.Monitor(make_env(), directory=\"videos\", force=True)\n", + "sessions = [evaluate(env_monitor, agent, n_games=n_lives, greedy=True) for _ in range(10)]\n", + "env_monitor.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# show video\n", + "from IPython.display import HTML\n", + "import os\n", + "\n", + "video_names = list(\n", + " filter(lambda s: s.endswith(\".mp4\"), os.listdir(\"./videos/\")))\n", + "\n", + "HTML(\"\"\"\n", + "\n", + "\"\"\".format(\"./videos/\"+video_names[-1])) # this may or may not be _last_ video. Try other indices" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Let's have a closer look at this.\n", + "\n", + "If average episode score is below 200 using all 5 lives, then probably DQN has not converged fully. But anyway let's make a more complete record of an episode." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "eval_env = make_env(clip_rewards=False)\n", + "record = utils.play_and_log_episode(eval_env, agent)\n", + "print('total reward for life:', np.sum(record['rewards']))\n", + "for key in record:\n", + " print(key)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure(figsize=(5, 5))\n", + "ax = fig.add_subplot(1, 1, 1)\n", + "\n", + "ax.scatter(record['v_mc'], record['v_agent'])\n", + "ax.plot(sorted(record['v_mc']), sorted(record['v_mc']),\n", + " 'black', linestyle='--', label='x=y')\n", + "\n", + "ax.grid()\n", + "ax.legend()\n", + "ax.set_title('State Value Estimates')\n", + "ax.set_xlabel('Monte-Carlo')\n", + "ax.set_ylabel('Agent')\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$\\hat V_{Monte-Carlo}(s_t) = \\sum_{\\tau=0}^{episode~end} \\gamma^{\\tau-t}r_t$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Is there a big bias? It's ok, anyway it works." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bonus I (2 pts)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**1.** Plot several (say 3) states with high and low spreads of Q estimate by actions i.e.\n", + "$$\\max_a \\hat Q(s,a) - \\min_a \\hat Q(s,a)\\$$\n", + "Please take those states from different episodes to make sure that the states are really different.\n", + "\n", + "What should high and low spread mean at least in the world of perfect Q-fucntions?\n", + "\n", + "Comment the states you like most.\n", + "\n", + "**2.** Plot several (say 3) states with high td-error and several states with high values of\n", + "$$| \\hat V_{Monte-Carlo}(s) - \\hat V_{agent}(s)|,$$ \n", + "$$\\hat V_{agent}(s)=\\max_a \\hat Q(s,a).$$ Please take those states from different episodes to make sure that the states are really different. From what part (i.e. beginning, middle, end) of an episode did these states come from?\n", + "\n", + "Comment the states you like most." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from utils import play_and_log_episode, img_by_obs\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bonus II (1-5 pts). Get High Score!\n", + "\n", + "1 point to you for each 50 points of your agent. Truncated by 5 points.\n", + "\n", + "One way is to train for several days and use a big GPU (why not actually).\n", + "\n", + "Another way is to apply modifications (see **Bonus III**)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bonus III (2+ pts). Apply modifications to DQN.\n", + "\n", + "For inspiration see [Rainbow](https://arxiv.org/abs/1710.02298) - a version of q-learning that combines lots of them.\n", + "\n", + "Points for Bonus II and Bonus III fully stack. So if modified agent gets score 250+ you get 5 pts for Bonus II + points for modifications. If the final score is 40 then you get the points for modifications.\n", + "\n", + "\n", + "Some modifications:\n", + "* [Prioritized experience replay](https://arxiv.org/abs/1511.05952) (5 pts for your own implementation, 2 pts for using a ready one)\n", + "* [double q-learning](https://arxiv.org/abs/1509.06461) (2 pts)\n", + "* [dueling q-learning](https://arxiv.org/abs/1511.06581) (2 pts)\n", + "* multi-step heuristics (see [Rainbow](https://arxiv.org/abs/1710.02298)) (2 pts)\n", + "* [distributional RL](https://arxiv.org/abs/1707.06887)(distributional and distributed stand for different things here) (5 pts)\n", + "* Other modifications (2+ pts depending on complexity)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bonus IV (4+ pts). Distributed RL.\n", + "\n", + "Solve the task in a distributed way. It can strongly speed up learning. See [article](https://arxiv.org/pdf/1602.01783.pdf) or some guides." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**As usual bonus points for all the tasks fully stack.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "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.7.1" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/week4_approx_rl/homework_tf.ipynb b/week04_approx_rl/homework_tf.ipynb similarity index 100% rename from week4_approx_rl/homework_tf.ipynb rename to week04_approx_rl/homework_tf.ipynb diff --git a/week4_approx_rl/replay_buffer.py b/week04_approx_rl/replay_buffer.py similarity index 100% rename from week4_approx_rl/replay_buffer.py rename to week04_approx_rl/replay_buffer.py diff --git a/week4_approx_rl/seminar_lasagne.ipynb b/week04_approx_rl/seminar_lasagne.ipynb similarity index 100% rename from week4_approx_rl/seminar_lasagne.ipynb rename to week04_approx_rl/seminar_lasagne.ipynb diff --git a/week04_approx_rl/seminar_pytorch.ipynb b/week04_approx_rl/seminar_pytorch.ipynb new file mode 100644 index 000000000..0223454dc --- /dev/null +++ b/week04_approx_rl/seminar_pytorch.ipynb @@ -0,0 +1,454 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Approximate q-learning\n", + "\n", + "In this notebook you will teach a __pytorch__ neural network to do Q-learning." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# # in google colab uncomment this\n", + "\n", + "# import os\n", + "\n", + "# os.system('apt-get install -y xvfb')\n", + "# os.system('wget https://raw.githubusercontent.com/yandexdataschool/Practical_DL/fall18/xvfb -O ../xvfb')\n", + "# os.system('apt-get install -y python-opengl ffmpeg')\n", + "# os.system('pip install pyglet==1.2.4')\n", + "\n", + "# XVFB will be launched if you run on a server\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import gym\n", + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "scrolled": false + }, + "outputs": [], + "source": [ + "env = gym.make(\"CartPole-v0\").env\n", + "env.reset()\n", + "n_actions = env.action_space.n\n", + "state_dim = env.observation_space.shape\n", + "\n", + "plt.imshow(env.render(\"rgb_array\"))\n", + "env.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Approximate Q-learning: building the network\n", + "\n", + "To train a neural network policy one must have a neural network policy. Let's build it.\n", + "\n", + "\n", + "Since we're working with a pre-extracted features (cart positions, angles and velocities), we don't need a complicated network yet. In fact, let's build something like this for starters:\n", + "\n", + "![img](https://raw.githubusercontent.com/yandexdataschool/Practical_RL/spring19/yet_another_week/_resource/qlearning_scheme.png)\n", + "\n", + "For your first run, please only use linear layers (nn.Linear) and activations. Stuff like batch normalization or dropout may ruin everything if used haphazardly. \n", + "\n", + "Also please avoid using nonlinearities like sigmoid & tanh: agent's observations are not normalized so sigmoids may become saturated from init.\n", + "\n", + "Ideally you should start small with maybe 1-2 hidden layers with < 200 neurons and then increase network size if agent doesn't beat the target score." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "network = nn.Sequential()\n", + "\n", + "network.add_module('layer1', < ... >)\n", + "\n", + "\n", + "\n", + "# hint: use state_dim[0] as input size" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def get_action(state, epsilon=0):\n", + " \"\"\"\n", + " sample actions with epsilon-greedy policy\n", + " recap: with p = epsilon pick random action, else pick action with highest Q(s,a)\n", + " \"\"\"\n", + " state = torch.tensor(state[None], dtype=torch.float32)\n", + " q_values = network(state).detach().numpy()\n", + "\n", + " # YOUR CODE\n", + "\n", + " return int( < epsilon-greedily selected action > )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "s = env.reset()\n", + "assert tuple(network(torch.tensor([s]*3, dtype=torch.float32)).size()) == (\n", + " 3, n_actions), \"please make sure your model maps state s -> [Q(s,a0), ..., Q(s, a_last)]\"\n", + "assert isinstance(list(network.modules(\n", + "))[-1], nn.Linear), \"please make sure you predict q-values without nonlinearity (ignore if you know what you're doing)\"\n", + "assert isinstance(get_action(\n", + " s), int), \"get_action(s) must return int, not %s. try int(action)\" % (type(get_action(s)))\n", + "\n", + "# test epsilon-greedy exploration\n", + "for eps in [0., 0.1, 0.5, 1.0]:\n", + " state_frequencies = np.bincount(\n", + " [get_action(s, epsilon=eps) for i in range(10000)], minlength=n_actions)\n", + " best_action = state_frequencies.argmax()\n", + " assert abs(state_frequencies[best_action] -\n", + " 10000 * (1 - eps + eps / n_actions)) < 200\n", + " for other_action in range(n_actions):\n", + " if other_action != best_action:\n", + " assert abs(state_frequencies[other_action] -\n", + " 10000 * (eps / n_actions)) < 200\n", + " print('e=%.1f tests passed' % eps)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Q-learning via gradient descent\n", + "\n", + "We shall now train our agent's Q-function by minimizing the TD loss:\n", + "$$ L = { 1 \\over N} \\sum_i (Q_{\\theta}(s,a) - [r(s,a) + \\gamma \\cdot max_{a'} Q_{-}(s', a')]) ^2 $$\n", + "\n", + "\n", + "Where\n", + "* $s, a, r, s'$ are current state, action, reward and next state respectively\n", + "* $\\gamma$ is a discount factor defined two cells above.\n", + "\n", + "The tricky part is with $Q_{-}(s',a')$. From an engineering standpoint, it's the same as $Q_{\\theta}$ - the output of your neural network policy. However, when doing gradient descent, __we won't propagate gradients through it__ to make training more stable (see lectures).\n", + "\n", + "To do so, we shall use `x.detach()` function which basically says \"consider this thing constant when doingbackprop\"." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def to_one_hot(y_tensor, n_dims=None):\n", + " \"\"\" helper: take an integer vector and convert it to 1-hot matrix. \"\"\"\n", + " y_tensor = y_tensor.type(torch.LongTensor).view(-1, 1)\n", + " n_dims = n_dims if n_dims is not None else int(torch.max(y_tensor)) + 1\n", + " y_one_hot = torch.zeros(\n", + " y_tensor.size()[0], n_dims).scatter_(1, y_tensor, 1)\n", + " return y_one_hot\n", + "\n", + "\n", + "def where(cond, x_1, x_2):\n", + " \"\"\" helper: like np.where but in pytorch. \"\"\"\n", + " return (cond * x_1) + ((1-cond) * x_2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def compute_td_loss(states, actions, rewards, next_states, is_done, gamma=0.99, check_shapes=False):\n", + " \"\"\" Compute td loss using torch operations only. Use the formula above. \"\"\"\n", + " states = torch.tensor(\n", + " states, dtype=torch.float32) # shape: [batch_size, state_size]\n", + " actions = torch.tensor(actions, dtype=torch.int32) # shape: [batch_size]\n", + " rewards = torch.tensor(rewards, dtype=torch.float32) # shape: [batch_size]\n", + " # shape: [batch_size, state_size]\n", + " next_states = torch.tensor(next_states, dtype=torch.float32)\n", + " is_done = torch.tensor(is_done, dtype=torch.float32) # shape: [batch_size]\n", + "\n", + " # get q-values for all actions in current states\n", + " predicted_qvalues = network(states)\n", + "\n", + " # select q-values for chosen actions\n", + " predicted_qvalues_for_actions = torch.sum(\n", + " predicted_qvalues * to_one_hot(actions, n_actions), dim=1)\n", + "\n", + " # compute q-values for all actions in next states\n", + " predicted_next_qvalues = # YOUR CODE\n", + "\n", + " # compute V*(next_states) using predicted next q-values\n", + " next_state_values = # YOUR CODE\n", + " assert next_state_values.dtype == torch.float32\n", + "\n", + " # compute \"target q-values\" for loss - it's what's inside square parentheses in the above formula.\n", + " target_qvalues_for_actions = # YOUR CODE\n", + "\n", + " # at the last state we shall use simplified formula: Q(s,a) = r(s,a) since s' doesn't exist\n", + " target_qvalues_for_actions = where(\n", + " is_done, rewards, target_qvalues_for_actions)\n", + "\n", + " # mean squared error loss to minimize\n", + " loss = torch.mean((predicted_qvalues_for_actions -\n", + " target_qvalues_for_actions.detach()) ** 2)\n", + "\n", + " if check_shapes:\n", + " assert predicted_next_qvalues.data.dim(\n", + " ) == 2, \"make sure you predicted q-values for all actions in next state\"\n", + " assert next_state_values.data.dim(\n", + " ) == 1, \"make sure you computed V(s') as maximum over just the actions axis and not all axes\"\n", + " assert target_qvalues_for_actions.data.dim(\n", + " ) == 1, \"there's something wrong with target q-values, they must be a vector\"\n", + "\n", + " return loss" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# sanity checks\n", + "s = env.reset()\n", + "a = env.action_space.sample()\n", + "next_s, r, done, _ = env.step(a)\n", + "loss = compute_td_loss([s], [a], [r], [next_s], [done], check_shapes=True)\n", + "loss.backward()\n", + "\n", + "assert len(loss.size()) == 0, \"you must return scalar loss - mean over batch\"\n", + "assert np.any(next(network.parameters()).grad.detach().numpy() !=\n", + " 0), \"loss must be differentiable w.r.t. network weights\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Playing the game" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "opt = torch.optim.Adam(network.parameters(), lr=1e-4)\n", + "epsilon = 0.5" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def generate_session(t_max=1000, epsilon=0, train=False):\n", + " \"\"\"play env with approximate q-learning agent and train it at the same time\"\"\"\n", + " total_reward = 0\n", + " s = env.reset()\n", + "\n", + " for t in range(t_max):\n", + " a = get_action(s, epsilon=epsilon)\n", + " next_s, r, done, _ = env.step(a)\n", + "\n", + " if train:\n", + " opt.zero_grad()\n", + " compute_td_loss([s], [a], [r], [next_s], [done]).backward()\n", + " opt.step()\n", + "\n", + " total_reward += r\n", + " s = next_s\n", + " if done:\n", + " break\n", + "\n", + " return total_reward" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "scrolled": true + }, + "outputs": [], + "source": [ + "for i in range(1000):\n", + " session_rewards = [generate_session(\n", + " epsilon=epsilon, train=True) for _ in range(100)]\n", + " print(\"epoch #{}\\tmean reward = {:.3f}\\tepsilon = {:.3f}\".format(\n", + " i, np.mean(session_rewards), epsilon))\n", + "\n", + " epsilon *= 0.99\n", + " assert epsilon >= 1e-4, \"Make sure epsilon is always nonzero during training\"\n", + "\n", + " if np.mean(session_rewards) > 300:\n", + " print(\"You Win!\")\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### How to interpret results\n", + "\n", + "\n", + "Welcome to the f.. world of deep f...n reinforcement learning. Don't expect agent's reward to smoothly go up. Hope for it to go increase eventually. If it deems you worthy.\n", + "\n", + "Seriously though,\n", + "* __ mean reward__ is the average reward per game. For a correct implementation it may stay low for some 10 epochs, then start growing while oscilating insanely and converges by ~50-100 steps depending on the network architecture. \n", + "* If it never reaches target score by the end of for loop, try increasing the number of hidden neurons or look at the epsilon.\n", + "* __ epsilon__ - agent's willingness to explore. If you see that agent's already at < 0.01 epsilon before it's is at least 200, just reset it back to 0.1 - 0.5." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Record videos\n", + "\n", + "As usual, we now use `gym.wrappers.Monitor` to record a video of our agent playing the game. Unlike our previous attempts with state binarization, this time we expect our agent to act ~~(or fail)~~ more smoothly since there's no more binarization error at play.\n", + "\n", + "As you already did with tabular q-learning, we set epsilon=0 for final evaluation to prevent agent from exploring himself to death." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "scrolled": true + }, + "outputs": [], + "source": [ + "# record sessions\n", + "import gym.wrappers\n", + "env = gym.wrappers.Monitor(gym.make(\"CartPole-v0\"),\n", + " directory=\"videos\", force=True)\n", + "sessions = [generate_session(epsilon=0, train=False) for _ in range(100)]\n", + "env.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# show video\n", + "from IPython.display import HTML\n", + "import os\n", + "\n", + "video_names = list(\n", + " filter(lambda s: s.endswith(\".mp4\"), os.listdir(\"./videos/\")))\n", + "\n", + "HTML(\"\"\"\n", + "\n", + "\"\"\".format(\"./videos/\"+video_names[-1])) # this may or may not be _last_ video. Try other indices" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "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.6.8" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/week4_approx_rl/seminar_tf.ipynb b/week04_approx_rl/seminar_tf.ipynb similarity index 96% rename from week4_approx_rl/seminar_tf.ipynb rename to week04_approx_rl/seminar_tf.ipynb index c7047146a..06e2fdd2c 100644 --- a/week4_approx_rl/seminar_tf.ipynb +++ b/week04_approx_rl/seminar_tf.ipynb @@ -74,7 +74,7 @@ "\n", "Since we're working with a pre-extracted features (cart positions, angles and velocities), we don't need a complicated network yet. In fact, let's build something like this for starters:\n", "\n", - "![img](https://s14.postimg.org/uzay2q5rl/qlearning_scheme.png)\n", + "![img](https://raw.githubusercontent.com/yandexdataschool/Practical_RL/master/yet_another_week/_resource/qlearning_scheme.png)\n", "\n", "For your first run, please only use linear layers (L.Dense) and activations. Stuff like batch normalization or dropout may ruin everything if used haphazardly. \n", "\n", @@ -186,11 +186,11 @@ "outputs": [], "source": [ "# Create placeholders for the tuple and a special indicator for game end (is_done = True)\n", - "states_ph = tf.placeholder('float32', shape=(None,) + state_dim)\n", - "actions_ph = tf.placeholder('int32', shape=[None])\n", - "rewards_ph = tf.placeholder('float32', shape=[None])\n", - "next_states_ph = tf.placeholder('float32', shape=(None,) + state_dim)\n", - "is_done_ph = tf.placeholder('bool', shape=[None])" + "states_ph = keras.backend.placeholder(dtype='float32', shape=(None,) + state_dim)\n", + "actions_ph = keras.backend.placeholder(dtype='int32', shape=[None])\n", + "rewards_ph = keras.backend.placeholder(dtype='float32', shape=[None])\n", + "next_states_ph = keras.backend.placeholder(dtype='float32', shape=(None,) + state_dim)\n", + "is_done_ph = keras.backend.placeholder(dtype='bool', shape=[None])" ] }, { diff --git a/week04_approx_rl/utils.py b/week04_approx_rl/utils.py new file mode 100644 index 000000000..cea654848 --- /dev/null +++ b/week04_approx_rl/utils.py @@ -0,0 +1,87 @@ +import numpy as np +import psutil +from scipy.signal import convolve, gaussian +import torch +from torch import nn +import os + +def get_cum_discounted_rewards(rewards, gamma): + """ + evaluates cumulative discounted rewards: + r_t + gamma * r_{t+1} + gamma^2 * r_{t_2} + ... + """ + cum_rewards = [] + cum_rewards.append(rewards[-1]) + for r in reversed(rewards[:-1]): + cum_rewards.insert(0, r + gamma * cum_rewards[0]) + return cum_rewards + + +def play_and_log_episode(env, agent, gamma=0.99, t_max=10000): + """ + always greedy + """ + states = [] + v_mc = [] + v_agent = [] + q_spreads = [] + td_errors = [] + rewards = [] + + s = env.reset() + for step in range(t_max): + states.append(s) + qvalues = agent.get_qvalues([s]) + max_q_value, min_q_value = np.max(qvalues), np.min(qvalues) + v_agent.append(max_q_value) + q_spreads.append(max_q_value - min_q_value) + if step > 0: + td_errors.append(np.abs(rewards[-1] + gamma * v_agent[-1] - v_agent[-2])) + + action = qvalues.argmax(axis=-1)[0] + + s, r, done, _ = env.step(action) + rewards.append(r) + if done: + break + td_errors.append(np.abs(rewards[-1] + gamma * v_agent[-1] - v_agent[-2])) + + v_mc = get_cum_discounted_rewards(rewards, gamma) + + return_pack = { + 'states': np.array(states), + 'v_mc': np.array(v_mc), + 'v_agent': np.array(v_agent), + 'q_spreads': np.array(q_spreads), + 'td_errors': np.array(td_errors), + 'rewards': np.array(rewards), + 'episode_finished': np.array(done) + } + + return return_pack + + +def img_by_obs(obs, state_dim): + """ + Unwraps obs by channels. + observation is of shape [c, h=w, w=h] + """ + return obs.reshape([-1, state_dim[2]]) + + +def is_enough_ram(min_available_gb = 0.1): + mem = psutil.virtual_memory() + return mem.available >= min_available_gb * (1024 ** 3) + + +def linear_decay(init_val, final_val, cur_step, total_steps): + if cur_step >= total_steps: + return final_val + return (init_val * (total_steps - cur_step) + final_val * cur_step) / total_steps + + +def smoothen(values): + kernel = gaussian(100, std=100) + # kernel = np.concatenate([np.arange(100), np.arange(99, -1, -1)]) + kernel = kernel / np.sum(kernel) + return convolve(values, kernel, 'valid') diff --git a/week5_explore/README.md b/week05_explore/README.md similarity index 85% rename from week5_explore/README.md rename to week05_explore/README.md index 8cc723409..f4fe9b595 100644 --- a/week5_explore/README.md +++ b/week05_explore/README.md @@ -1,3 +1,5 @@ +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week05_explore/week5.ipynb) + ### Slides - [here](https://yadi.sk/i/H0zVBROe3TWWHz) ## Exploration and exploitation @@ -6,7 +8,8 @@ * Alternative lecture by N. de Freitas (with bayesian opt) - [video](https://www.youtube.com/watch?v=vz3D36VXefI) * Our lectures (russian) - "mathematical" lecture (by Alexander Vorobev) '17 - [slides](https://yadi.sk/i/JAeItALT3JmvCL), [video](https://yadi.sk/i/bVHmu9gt3Hi9Ym) - - "engineering" lecture '18 - [video](https://yadi.sk/i/_myWJ13O3TdzXo) + - "practical" lecture '18 - [video](https://yadi.sk/i/_myWJ13O3TdzXo) + - Seminar - [video](https://yadi.sk/i/du7FLXs13TdzZS) diff --git a/week5_explore/action_rewards.npy b/week05_explore/action_rewards.npy similarity index 99% rename from week5_explore/action_rewards.npy rename to week05_explore/action_rewards.npy index 848a26938..231bcb18b 100644 Binary files a/week5_explore/action_rewards.npy and b/week05_explore/action_rewards.npy differ diff --git a/week5_explore/all_states.npy b/week05_explore/all_states.npy similarity index 99% rename from week5_explore/all_states.npy rename to week05_explore/all_states.npy index 16539f012..43940d9ba 100644 Binary files a/week5_explore/all_states.npy and b/week05_explore/all_states.npy differ diff --git a/week5_explore/bayes.py b/week05_explore/bayes.py similarity index 100% rename from week5_explore/bayes.py rename to week05_explore/bayes.py diff --git a/week5_explore/bnn.png b/week05_explore/bnn.png similarity index 100% rename from week5_explore/bnn.png rename to week05_explore/bnn.png diff --git a/week5_explore/river_swim.png b/week05_explore/river_swim.png similarity index 100% rename from week5_explore/river_swim.png rename to week05_explore/river_swim.png diff --git a/week05_explore/week5.ipynb b/week05_explore/week5.ipynb new file mode 100644 index 000000000..bd2e1149a --- /dev/null +++ b/week05_explore/week5.ipynb @@ -0,0 +1,1483 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run this if in COLAB\n", + "!pip install --upgrade https://github.com/Theano/Theano/archive/master.zip\n", + "!pip install --upgrade https://github.com/Lasagne/Lasagne/archive/master.zip\n", + " \n", + "!wget -q https://raw.githubusercontent.com/yandexdataschool/Practical_RL/master/week5_explore/bayes.py\n", + "!wget -q https://raw.githubusercontent.com/yandexdataschool/Practical_RL/master/week5_explore/action_rewards.npy\n", + "!wget -q https://raw.githubusercontent.com/yandexdataschool/Practical_RL/master/week5_explore/all_states.npy" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from abc import ABCMeta, abstractmethod, abstractproperty\n", + "import enum\n", + "\n", + "import numpy as np\n", + "np.set_printoptions(precision=3)\n", + "np.set_printoptions(suppress=True)\n", + "\n", + "import pandas\n", + "\n", + "from matplotlib import pyplot as plt\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Contents\n", + "* [1. Bernoulli Bandit](#Part-1.-Bernoulli-Bandit)\n", + " * [Bonus 1.1. Gittins index (5 points)](#Bonus-1.1.-Gittins-index-%285-points%29.)\n", + " * [HW 1.1. Nonstationary Bernoulli bandit](#HW-1.1.-Nonstationary-Bernoulli-bandit)\n", + "* [2. Contextual bandit](#Part-2.-Contextual-bandit)\n", + " * [2.1 Bulding a BNN agent](#2.1-Bulding-a-BNN-agent)\n", + " * [2.2 Training the agent](#2.2-Training-the-agent)\n", + " * [HW 2.1 Better exploration](#HW-2.1-Better-exploration)\n", + "* [3. Exploration in MDP](#Part-3.-Exploration-in-MDP)\n", + " * [Bonus 3.1 Posterior sampling RL (3 points)](#Bonus-3.1-Posterior-sampling-RL-%283-points%29)\n", + " * [Bonus 3.2 Bootstrapped DQN (10 points)](#Bonus-3.2-Bootstrapped-DQN-%2810-points%29)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 1. Bernoulli Bandit\n", + "\n", + "We are going to implement several exploration strategies for simplest problem - bernoulli bandit.\n", + "\n", + "The bandit has $K$ actions. Action produce 1.0 reward $r$ with probability $0 \\le \\theta_k \\le 1$ which is unknown to agent, but fixed over time. Agent's objective is to minimize regret over fixed number $T$ of action selections:\n", + "\n", + "$$\\rho = T\\theta^* - \\sum_{t=1}^T r_t$$\n", + "\n", + "Where $\\theta^* = \\max_k\\{\\theta_k\\}$\n", + "\n", + "**Real-world analogy:**\n", + "\n", + "Clinical trials - we have $K$ pills and $T$ ill patient. After taking pill, patient is cured with probability $\\theta_k$. Task is to find most efficient pill.\n", + "\n", + "A research on clinical trials - https://arxiv.org/pdf/1507.08025.pdf" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class BernoulliBandit:\n", + " def __init__(self, n_actions=5):\n", + " self._probs = np.random.random(n_actions)\n", + "\n", + " @property\n", + " def action_count(self):\n", + " return len(self._probs)\n", + "\n", + " def pull(self, action):\n", + " if np.any(np.random.random() > self._probs[action]):\n", + " return 0.0\n", + " return 1.0\n", + "\n", + " def optimal_reward(self):\n", + " \"\"\" Used for regret calculation\n", + " \"\"\"\n", + " return np.max(self._probs)\n", + "\n", + " def step(self):\n", + " \"\"\" Used in nonstationary version\n", + " \"\"\"\n", + " pass\n", + "\n", + " def reset(self):\n", + " \"\"\" Used in nonstationary version\n", + " \"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class AbstractAgent(metaclass=ABCMeta):\n", + " def init_actions(self, n_actions):\n", + " self._successes = np.zeros(n_actions)\n", + " self._failures = np.zeros(n_actions)\n", + " self._total_pulls = 0\n", + "\n", + " @abstractmethod\n", + " def get_action(self):\n", + " \"\"\"\n", + " Get current best action\n", + " :rtype: int\n", + " \"\"\"\n", + " pass\n", + "\n", + " def update(self, action, reward):\n", + " \"\"\"\n", + " Observe reward from action and update agent's internal parameters\n", + " :type action: int\n", + " :type reward: int\n", + " \"\"\"\n", + " self._total_pulls += 1\n", + " if reward == 1:\n", + " self._successes[action] += 1\n", + " else:\n", + " self._failures[action] += 1\n", + "\n", + " @property\n", + " def name(self):\n", + " return self.__class__.__name__\n", + "\n", + "\n", + "class RandomAgent(AbstractAgent):\n", + " def get_action(self):\n", + " return np.random.randint(0, len(self._successes))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Epsilon-greedy agent\n", + "\n", + "**for** $t = 1,2,...$ **do**\n", + "\n", + "   **for** $k = 1,...,K$ **do**\n", + "\n", + "       $\\hat\\theta_k \\leftarrow \\alpha_k / (\\alpha_k + \\beta_k)$\n", + "\n", + "   **end for** \n", + "\n", + "   $x_t \\leftarrow argmax_{k}\\hat\\theta$ with probability $1 - \\epsilon$ or random action with probability $\\epsilon$\n", + "\n", + "   Apply $x_t$ and observe $r_t$\n", + "\n", + "   $(\\alpha_{x_t}, \\beta_{x_t}) \\leftarrow (\\alpha_{x_t}, \\beta_{x_t}) + (r_t, 1-r_t)$\n", + "\n", + "**end for**\n", + "\n", + "Implement the algorithm above in the cell below:" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class EpsilonGreedyAgent(AbstractAgent):\n", + " def __init__(self, epsilon=0.01):\n", + " self._epsilon = epsilon\n", + "\n", + " def get_action(self):\n", + " # YOUR CODE HERE\n", + "\n", + " @property\n", + " def name(self):\n", + " return self.__class__.__name__ + \"(epsilon={})\".format(self._epsilon)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UCB Agent\n", + "Epsilon-greedy strategy heve no preference for actions. It would be better to select among actions that are uncertain or have potential to be optimal. One can come up with idea of index for each action that represents otimality and uncertainty at the same time. One efficient way to do it is to use UCB1 algorithm:\n", + "\n", + "**for** $t = 1,2,...$ **do**\n", + "\n", + "   **for** $k = 1,...,K$ **do**\n", + "\n", + "       $w_k \\leftarrow \\alpha_k / (\\alpha_k + \\beta_k) + \\sqrt{2log\\ t \\ / \\ (\\alpha_k + \\beta_k)}$\n", + "\n", + "   **end for** \n", + "\n", + "   **end for** \n", + " $x_t \\leftarrow argmax_{k}w$\n", + "\n", + "   Apply $x_t$ and observe $r_t$\n", + "\n", + "   $(\\alpha_{x_t}, \\beta_{x_t}) \\leftarrow (\\alpha_{x_t}, \\beta_{x_t}) + (r_t, 1-r_t)$\n", + "\n", + "**end for**\n", + "\n", + "More versions and optimality analysis - https://homes.di.unimi.it/~cesabian/Pubblicazioni/ml-02.pdf" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class UCBAgent(AbstractAgent):\n", + " def get_action(self):\n", + " # YOUR CODE HERE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Thompson sampling\n", + "\n", + "UCB1 algorithm does not take into account actual distribution of rewards. If we know the distribution - we can do much better by using Thompson sampling:\n", + "\n", + "**for** $t = 1,2,...$ **do**\n", + "\n", + "   **for** $k = 1,...,K$ **do**\n", + "\n", + "       Sample $\\hat\\theta_k \\sim beta(\\alpha_k, \\beta_k)$\n", + "\n", + "   **end for** \n", + "\n", + "   $x_t \\leftarrow argmax_{k}\\hat\\theta$\n", + "\n", + "   Apply $x_t$ and observe $r_t$\n", + "\n", + "   $(\\alpha_{x_t}, \\beta_{x_t}) \\leftarrow (\\alpha_{x_t}, \\beta_{x_t}) + (r_t, 1-r_t)$\n", + "\n", + "**end for**\n", + " \n", + "\n", + "More on Thompson Sampling:\n", + "https://web.stanford.edu/~bvr/pubs/TS_Tutorial.pdf" + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class ThompsonSamplingAgent(AbstractAgent):\n", + " def get_action(self):\n", + " # YOUR CODE HERE" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def plot_regret(env, agents, n_steps=5000, n_trials=50):\n", + " scores = {\n", + " agent.name: [0.0 for step in range(n_steps)] for agent in agents\n", + " }\n", + "\n", + " for trial in range(n_trials):\n", + " env.reset()\n", + "\n", + " for a in agents:\n", + " a.init_actions(env.action_count)\n", + "\n", + " for i in range(n_steps):\n", + " optimal_reward = env.optimal_reward()\n", + "\n", + " for agent in agents:\n", + " action = agent.get_action()\n", + " reward = env.pull(action)\n", + " agent.update(action, reward)\n", + " scores[agent.name][i] += optimal_reward - reward\n", + "\n", + " env.step() # change bandit's state if it is unstationary\n", + "\n", + " plt.figure(figsize=(17, 8))\n", + " for agent in agents:\n", + " plt.plot(np.cumsum(scores[agent.name]) / n_trials)\n", + "\n", + " plt.legend([agent.name for agent in agents])\n", + "\n", + " plt.ylabel(\"regret\")\n", + " plt.xlabel(\"steps\")\n", + "\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA+4AAAHjCAYAAAC95UVJAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4xLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvAOZPmwAAIABJREFUeJzs3Xd0VVXexvHvSe+FNEoIJHREWgLSRTojCowoIirYKGLDCo7DgMqIwviiOOjgoKJiAJGiOBQZDIrUhE4oIRAgIQmQ3ts97x+BqxlCJySE57MWy3vP2Wef373JdeW5e599DNM0EREREREREZGqyaayCxARERERERGRC1NwFxEREREREanCFNxFREREREREqjAFdxEREREREZEqTMFdREREREREpApTcBcRERERERGpwhTcRURERERERKowBXcRERERERGRKkzBXURERERERKQKs6vsAq6Fr6+vWb9+/couQ0REREREROSKRUVFnTFN0+9S7W7q4F6/fn0iIyMruwwRERERERGRK2YYxrHLaaep8iIiIiIiIiJVmIK7iIiIiIiISBWm4C4iIiIiIiJShd3U17iXp6ioiPj4ePLz8yu7FJGbmpOTE4GBgdjb21d2KSIiIiIit7RqF9zj4+Nxd3enfv36GIZR2eWI3JRM0yQlJYX4+HiCg4MruxwRERERkVtatZsqn5+fj4+Pj0K7yDUwDAMfHx/NXBERERERqQKqXXAHFNpFrgN9jkREREREqoZqGdxFREREREREqgsF9wpga2tL69atrf+mTZt2xX1ERkby3HPPAfDFF1/wzDPPXHU9MTExDBgwgAYNGhAaGspdd93FL7/8ctX9XczkyZOZMWPGJdudOXMGe3t7PvnkkwqpIz09ndmzZ5fZlpiYyIABA67reSZNmsTatWsB6N69O5GRkde1/z+aN28ejRo1olGjRsybN6/cNqmpqfTu3ZtGjRrRu3dv0tLSADhw4AAdO3bE0dGxzM+nsLCQbt26UVxcXGF1i4iIiIjItVFwrwDOzs7s3LnT+m/ChAlX3EdYWBgffvjhNdeSn5/P3XffzahRo4iNjSUqKopZs2Zx5MiR89reyPD27bff0qFDB8LDwyuk//KC+/vvv89TTz11Xc/z5ptv0qtXr+vaZ3lSU1OZMmUKW7ZsYevWrUyZMsUayv9o2rRp9OzZk5iYGHr27Gn90qhGjRp8+OGHvPzyy2XaOzg40LNnTxYuXFjhr0FERERERK5OtVtV/o+m/LCP6JOZ17XP5rU9+Ns9t13VsfXr1+eBBx5g5cqVODs7880339CwYUO+/fZbpkyZgq2tLZ6envzyyy9EREQwY8YMVqxYUaaPuLg4Hn/8cc6cOYOfnx+ff/45QUFBjBw5Eg8PDyIjI0lKSuK9995jyJAhzJ8/n44dO3Lvvfda+2jRogUtWrQASkfIY2NjOXLkCEFBQXz99ddMmDCBiIgICgoKGDduHKNHjwZg+vTpLFq0iIKCAgYPHsyUKVMAmDp1KvPmzcPf35+6desSGhpKbGws999/P9u3bwdKR/2HDh1qfR4eHs4//vEPHnroIeLj4wkMDARg7ty5vPvuu3h5edGqVSscHR356KOPOH36NGPGjOH48eMAzJw5k86dOzN58mSOHz/OkSNHOH78OC+88ALPPfccEyZMIDY2ltatW9O7d2+mT5/Od999x9tvvw1ASUlJua8zIiKCSZMm4e7uzuHDh7nrrruYPXs2pmnyxBNPEBkZiWEYPP7444wfP56RI0cyYMAAhgwZUubnFB4ezt///ndM0+Tuu+/m3XffBcDNzY3nn3+eFStW4OzszPLlywkICLjk787q1avp3bs3NWrUAKB3796sWrWKYcOGlWm3fPlyIiIiABgxYgTdu3fn3Xffxd/fH39/f3788cfz+h40aBATJ05k+PDhl6xDRERERERuvGod3CtLXl4erVu3tj6fOHEiQ4cOBcDT05M9e/bw5Zdf8sILL7BixQrefPNNVq9eTZ06dUhPT79o388++ywjRoxgxIgRfPbZZzz33HMsW7YMKJ0KvmHDBg4cOMC9997LkCFD2LdvH23btr1on9HR0WzYsAFnZ2fmzJmDp6cn27Zto6CggM6dO9OnTx9iYmKIiYlh69atmKbJvffeyy+//IKrqysLFixg586dFBcX07ZtW0JDQ2nQoAGenp7s3LmT1q1b8/nnn/PYY48BcOLECRITE2nfvj0PPPAACxcu5KWXXuLkyZO89dZbbN++HXd3d3r06EGrVq0AeP755xk/fjxdunTh+PHj9O3bl/379wOl08B//vlnsrKyaNKkCWPHjmXatGns3buXnTt3AnD06FG8vb1xdHQESr8gKO91AmzdupXo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IiIiIiIiInWM3wTuQE8gzVqbYa0tA/4FXFXLeaoWm/KWAjAk5Xe1nBMRERERERGpa/wpcE8EdlV4v9u7rRJjzK3GmBXGmBXZ2dk1lrkzYWJa084dQ/OY1rWdFREREREREalj6tzidNbaN4E3Abp3725rOTun5KH/N6W2syAiIiIiIiJ1lD/1uO8Bkiu8T/JuExEREREREam3/Clw/wloZ4xpZYwJAa4HPq3lPImIiIiIiIjUKr8ZKm+tLTfG3AXMAwKBd6y1G2s5WyIiIiIiIiK1ym8CdwBr7Vxgbm3nQ0RERERERMRf+NNQeRERERERERE5igJ3ERERERERET+mwF1ERERERETEjylwFxEREREREfFjCtxFRERERERE/JgCdxERERERERE/psBdRERERERExI8pcBcRERERERHxYwrcRURERERERPyYAncRERERERERP6bAXURERERERMSPKXAXERERERER8WMK3EVERERERET8mAJ3ERERERERET9mrLW1nYfTZozJBjJrOx+nKA44WNuZEPkVVGalLlF5lbpGZVbqEpVXqWvqUpltYa1tfLJEdTpwr0uMMSustd1rOx8ip0plVuoSlVepa1RmpS5ReZW65lwssxoqLyIiIiIiIuLHFLiLiIiIiIiI+DEF7jXnzdrOgMivpDIrdYnKq9Q1KrNSl6i8Sl1zzpVZzXEXERERERER8WPqcRcRERERERHxYwrcRURERERERPyYAvcaYIy5whizxRiTZoy5v7bzI/WTMSbZGLPIGPOzMWajMeaP3u0xxpj5xpht3p+NvNuNMeYVb7ldZ4zpWuFcY73ptxljxtbWNcm5zxgTaIxZbYyZ433fyhizzFsuPzTGhHi3h3rfp3n3t6xwjge827cYY4bWzpVIfWCMaWiM+dgYs9kYs8kYk6o6VvyVMWai935ggzFmhjEmTHWs+BNjzDvGmCxjzIYK26qtTjXGdDPGrPce84oxxtTsFf46CtzPMmNMIDAVGAZ0BEYbYzrWbq6knioH/tda2xHoDdzpLYv3Awutte2Ahd734Cmz7bz/bgXeAE+FCTwK9AJ6Ao8eqTRFzoI/ApsqvH8OeMla2xbIA272br8ZyPNuf8mbDm8Zvx7oBFwBvO6tl0XOhr8CX1prOwApeMqu6ljxO8aYROBuoLu1tjMQiKeuVB0r/uRdPOWqouqsU98Abqlw3NG/y68ocD/7egJp1toMa20Z8C/gqlrOk9RD1tp91tpV3tdFeG4oE/GUx394k/0DGOV9fRXwnvX4EWhojGkGDAXmW2tzrbV5wHz8vKKTuskYkwSMAN72vjfAQOBjb5Kjy+uRcvwxMMib/irgX9Zah7V2O5CGp14WqVbGmAZAP2AagLW2zFqbj+pY8V9BQLgxJgiIAPahOlb8iLX2OyD3qM3VUqd6951nrf3RelZrf6/CufySAvezLxHYVeH9bu82kVrjHeLWBVgGNLHW7vPu2g808b4+XtlVmZaa8jJwL+D2vo8F8q215d73Fcuer1x69xd406u8Sk1pBWQD073TO942xkSiOlb8kLV2D/ACsBNPwF4ArER1rPi/6qpTE72vj97utxS4i9Qzxpgo4N/ABGttYcV93hZHPSNSap0xZiSQZa1dWdt5ETlFQUBX4A1rbRfgML8M4QRUx4r/8A4VvgpPg1MCEIlGdkgdU9/qVAXuZ98eILnC+yTvNpEaZ4wJxhO0/9NaO8u7+YB3uBDen1ne7ccruyrTUhP6AFcaY3bgmWI0EM/84YbeYZ1Quez5yqV3fwMgB5VXqTm7gd3W2mXe9x/jCeRVx4o/Ggxst9ZmW2udwCw89a7qWPF31VWn7vG+Pnq731Lgfvb9BLTzrtIZgmcBj09rOU9SD3nnok0DNllrX6yw61PgyAqbY4HZFbbf6F2lszdQ4B2aNA+43BjTyNtif7l3m0i1sdY+YK1Nsta2xFNvfm2tHQMsAq7xJju6vB4px9d401vv9uu9KyK3wrP4zPIaugypR6y1+4Fdxpj23k2DgJ9RHSv+aSfQ2xgT4b0/OFJeVceKv6uWOtW7r9AY09v7Gbixwrn8UtDJk8iZsNaWG2PuwlNoAoF3rLUbazlbUj/1AW4A1htj1ni3PQg8C8w0xtwMZALXeffNBYbjWWimGBgHYK3NNcY8gadRCuBxa+3RC4eInC33Af8yxjwJrMa7EJj35/vGmDQ8C9lcD2Ct3WiMmYnnhrQcuNNa66r5bEs98Qfgn96G+gw89WYAqmPFz1hrlxljPgZW4akbVwNvAp+jOlb8hDFmBnAZEGeM2Y1ndfjqvG+9A8/K9eHAF95/fst4GstERERERERExB9pqLyIiIiIiIiIH1PgLiIiIiIiIuLHFLiLiIiIiIiI+DEF7iIiIiIiIiJ+TIG7iIiIiIiIiB9T4C4iIlKPGWMmGGMiajsfIiIicnx6HJyIiEg9ZozZAXS31h6s7byIiIhI1dTjLiIiUk8YYyKNMZ8bY9YaYzYYYx4FEoBFxphF3jSXG2OWGmNWGWM+MsZEebfvMMY8b4xZb4xZboxp691+rfdca40x39Xe1YmIiJy7FLiLiIjUH1cAe621KdbazsDLwF5ggLV2gDEmDngIGGyt7QqsAO6pcHyBtfZC4DXvsQCPAEOttSnAlTV1ISIiIvWJAncREZH6Yz0wxBjznDGmr7W24Kj9vYGOwBJjzBpgLNCiwv4ZFX6mel8vAd41xtwCBJ69rIuIiNRfQbWdAREREakZ1tqtxpiuwHDgSWPMwqOSGGC+tXb08U5x9Gtr7W3GmF7ACGClMaabtTanuvMuIiJSn6nHXUREpJ4wxiQAxdbaD4C/AF2BIiDam+RHoE+F+euRxpjzK5zitxV+LvWmaWOtXWatfQTIBpLP/pWIiIjUL+pxFxERqT8uBP5ijHEDTuB2PEPevzTG7PXOc/9vYIYxJtR7zEPAVu/rRsaYdYADONIr/xdjTDs8vfULgbU1cykiIiL1hx4HJyIiIielx8aJiIjUHg2VFxEREREREfFj6nEXERERERER8WPqcRcRERERERHxYwrcRURERERERPyYAncRERERERERP6bAXURERERERMSPKXAXERERERER8WP/H26slP2kNJxQAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Uncomment agents\n", + "agents = [\n", + " # EpsilonGreedyAgent(),\n", + " # UCBAgent(),\n", + " # ThompsonSamplingAgent()\n", + "]\n", + "\n", + "plot_regret(BernoulliBandit(), agents, n_steps=10000, n_trials=10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Bonus 1.1. Gittins index (5 points).\n", + "\n", + "Bernoulli bandit problem has an optimal solution - Gittins index algorithm. Implement finite horizon version of the algorithm and demonstrate it's performance with experiments. some articles:\n", + "- Wikipedia article - https://en.wikipedia.org/wiki/Gittins_index\n", + "- Different algorithms for index computation - http://www.ece.mcgill.ca/~amahaj1/projects/bandits/book/2013-bandit-computations.pdf (see \"Bernoulli\" section)\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# HW 1.1. Nonstationary Bernoulli bandit\n", + "\n", + "What if success probabilities change over time? Here is an example of such bandit:" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class DriftingBandit(BernoulliBandit):\n", + " def __init__(self, n_actions=5, gamma=0.01):\n", + " \"\"\"\n", + " Idea from https://github.com/iosband/ts_tutorial\n", + " \"\"\"\n", + " super().__init__(n_actions)\n", + "\n", + " self._gamma = gamma\n", + "\n", + " self._successes = None\n", + " self._failures = None\n", + " self._steps = 0\n", + "\n", + " self.reset()\n", + "\n", + " def reset(self):\n", + " self._successes = np.zeros(self.action_count) + 1.0\n", + " self._failures = np.zeros(self.action_count) + 1.0\n", + " self._steps = 0\n", + "\n", + " def step(self):\n", + " action = np.random.randint(self.action_count)\n", + " reward = self.pull(action)\n", + " self._step(action, reward)\n", + "\n", + " def _step(self, action, reward):\n", + " self._successes = self._successes * (1 - self._gamma) + self._gamma\n", + " self._failures = self._failures * (1 - self._gamma) + self._gamma\n", + " self._steps += 1\n", + "\n", + " self._successes[action] += reward\n", + " self._failures[action] += 1.0 - reward\n", + "\n", + " self._probs = np.random.beta(self._successes, self._failures)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And a picture how it's reward probabilities change over time" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "drifting_env = DriftingBandit(n_actions=5)\n", + "\n", + "drifting_probs = []\n", + "for i in range(20000):\n", + " drifting_env.step()\n", + " drifting_probs.append(drifting_env._probs)\n", + "\n", + "plt.figure(figsize=(17, 8))\n", + "plt.plot(pandas.DataFrame(drifting_probs).rolling(window=20).mean())\n", + "\n", + "plt.xlabel(\"steps\")\n", + "plt.ylabel(\"Success probability\")\n", + "plt.title(\"Reward probabilities over time\")\n", + "plt.legend([\"Action {}\".format(i) for i in range(drifting_env.action_count)])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Your task is to invent an agent that will have better regret than stationary agents from above." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# YOUR AGENT HERE SECTION" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "drifting_agents = [\n", + " ThompsonSamplingAgent(),\n", + " EpsilonGreedyAgent(),\n", + " UCBAgent(),\n", + " YourAgent()\n", + "]\n", + "\n", + "plot_regret(DriftingBandit(), drifting_agents, n_steps=20000, n_trials=10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 2. Contextual bandit\n", + "\n", + "Now we will solve much more complex problem - reward will depend on bandit's state.\n", + "\n", + "**Real-word analogy:**\n", + "\n", + "> Contextual advertising. We have a lot of banners and a lot of different users. Users can have different features: age, gender, search requests. We want to show banner with highest click probability.\n", + "\n", + "If we want use strategies from above, we need some how store reward distributions conditioned both on actions and bandit's state. \n", + "One way to do this - use bayesian neural networks. Instead of giving pointwise estimates of target, they maintain probability distributions\n", + "\n", + "\n", + "Picture from https://arxiv.org/pdf/1505.05424.pdf\n", + "\n", + "\n", + "More material:\n", + " * A post on the matter - [url](http://twiecki.github.io/blog/2016/07/05/bayesian-deep-learning/)\n", + " * Theano+PyMC3 for more serious stuff - [url](http://pymc-devs.github.io/pymc3/notebooks/bayesian_neural_network_advi.html)\n", + " * Same stuff in tensorflow - [url](http://edwardlib.org/tutorials/bayesian-neural-network)\n", + " \n", + "Let's load our dataset:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "State size: 60, actions: 10\n" + ] + } + ], + "source": [ + "all_states = np.load(\"all_states.npy\")\n", + "action_rewards = np.load(\"action_rewards.npy\")\n", + "\n", + "state_size = all_states.shape[1]\n", + "n_actions = action_rewards.shape[1]\n", + "\n", + "print(\"State size: %i, actions: %i\" % (state_size, n_actions))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import theano\n", + "import theano.tensor as T\n", + "import lasagne\n", + "from lasagne import init\n", + "from lasagne.layers import *\n", + "import bayes\n", + "\n", + "as_bayesian = bayes.bbpwrap(bayes.NormalApproximation(std=0.1))\n", + "BayesDenseLayer = as_bayesian(DenseLayer)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2.1 Bulding a BNN agent\n", + "\n", + "Let's implement epsilon-greedy BNN agent" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class BNNAgent:\n", + " \"\"\"a bandit with bayesian neural net\"\"\"\n", + "\n", + " def __init__(self, state_size, n_actions):\n", + " input_states = T.matrix(\"states\")\n", + " target_actions = T.ivector(\"actions taken\")\n", + " target_rewards = T.vector(\"rewards\")\n", + "\n", + " self.total_samples_seen = theano.shared(\n", + " np.int32(0), \"number of training samples seen so far\")\n", + " batch_size = target_actions.shape[0] # por que?\n", + "\n", + " # Network\n", + " inp = InputLayer((None, state_size), name='input')\n", + " # YOUR NETWORK HERE\n", + " out = \n", + "\n", + " # Prediction\n", + " prediction_all_actions = get_output(out, inputs=input_states)\n", + " self.predict_sample_rewards = theano.function(\n", + " [input_states], prediction_all_actions)\n", + "\n", + " # Training\n", + "\n", + " # select prediction for target action\n", + " prediction_target_actions = prediction_all_actions[T.arange(\n", + " batch_size), target_actions]\n", + "\n", + " # loss = negative log-likelihood (mse) + KL\n", + " negative_llh = T.sum((prediction_target_actions - target_rewards)**2)\n", + "\n", + " kl = bayes.get_var_cost(out) / (self.total_samples_seen+batch_size)\n", + "\n", + " loss = (negative_llh + kl)/batch_size\n", + "\n", + " self.weights = get_all_params(out, trainable=True)\n", + " self.out = out\n", + "\n", + " # gradient descent\n", + " updates = lasagne.updates.adam(loss, self.weights)\n", + " # update counts\n", + " updates[self.total_samples_seen] = self.total_samples_seen + \\\n", + " batch_size.astype('int32')\n", + "\n", + " self.train_step = theano.function([input_states, target_actions, target_rewards],\n", + " [negative_llh, kl],\n", + " updates=updates,\n", + " allow_input_downcast=True)\n", + "\n", + " def sample_prediction(self, states, n_samples=1):\n", + " \"\"\"Samples n_samples predictions for rewards,\n", + "\n", + " :returns: tensor [n_samples, state_i, action_i]\n", + " \"\"\"\n", + " assert states.ndim == 2, \"states must be 2-dimensional\"\n", + "\n", + " return np.stack([self.predict_sample_rewards(states) for _ in range(n_samples)])\n", + "\n", + " epsilon = 0.25\n", + "\n", + " def get_action(self, states):\n", + " \"\"\"\n", + " Picks action by \n", + " - with p=1-epsilon, taking argmax of average rewards\n", + " - with p=epsilon, taking random action\n", + " This is exactly e-greedy policy.\n", + " \"\"\"\n", + "\n", + " reward_samples = self.sample_prediction(states, n_samples=100)\n", + " # ^-- samples for rewards, shape = [n_samples,n_states,n_actions]\n", + "\n", + " best_actions = reward_samples.mean(axis=0).argmax(axis=-1)\n", + " # ^-- we take mean over samples to compute expectation, then pick best action with argmax\n", + "\n", + " # YOUR CODE HERE\n", + " chosen_actions = <-- implement epsilon-greedy strategy - ->\n", + "\n", + " return chosen_actions\n", + "\n", + " def train(self, states, actions, rewards, n_iters=10):\n", + " \"\"\"\n", + " trains to predict rewards for chosen actions in given states\n", + " \"\"\"\n", + " loss_sum = kl_sum = 0\n", + " for _ in range(n_iters):\n", + " loss, kl = self.train_step(states, actions, rewards)\n", + " loss_sum += loss\n", + " kl_sum += kl\n", + "\n", + " return loss_sum / n_iters, kl_sum / n_iters\n", + "\n", + " @property\n", + " def name(self):\n", + " return self.__class__.__name__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2.2 Training the agent" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "N_ITERS = 100" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def get_new_samples(states, action_rewards, batch_size=10):\n", + " \"\"\"samples random minibatch, emulating new users\"\"\"\n", + " batch_ix = np.random.randint(0, len(states), batch_size)\n", + " return states[batch_ix], action_rewards[batch_ix]" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from IPython.display import clear_output\n", + "\n", + "from pandas import DataFrame\n", + "moving_average = lambda x, **kw: DataFrame(\n", + " {'x': np.asarray(x)}).x.ewm(**kw).mean().values\n", + "\n", + "def train_contextual_agent(agent, batch_size=10, n_iters=100):\n", + " rewards_history = []\n", + "\n", + " for i in range(n_iters):\n", + " b_states, b_action_rewards = get_new_samples(\n", + " all_states, action_rewards, batch_size)\n", + " b_actions = agent.get_action(b_states)\n", + " b_rewards = b_action_rewards[\n", + " np.arange(batch_size), b_actions\n", + " ]\n", + "\n", + " mse, kl = agent.train(b_states, b_actions, b_rewards, n_iters=100)\n", + "\n", + " rewards_history.append(b_rewards.mean())\n", + "\n", + " if i % 10 == 0:\n", + " clear_output(True)\n", + " print(\"iteration #%i\\tmean reward=%.3f\\tmse=%.3f\\tkl=%.3f\" %\n", + " (i, np.mean(rewards_history[-10:]), mse, kl))\n", + " plt.plot(rewards_history)\n", + " plt.plot(moving_average(np.array(rewards_history), alpha=0.1))\n", + " plt.title(\"Reward per epesode\")\n", + " plt.xlabel(\"Episode\")\n", + " plt.ylabel(\"Reward\")\n", + " plt.show()\n", + "\n", + " samples = agent.sample_prediction(\n", + " b_states[:1], n_samples=100).T[:, 0, :]\n", + " for i in range(len(samples)):\n", + " plt.hist(samples[i], alpha=0.25, label=str(i))\n", + " plt.legend(loc='best')\n", + " print('Q(s,a) std:', ';'.join(\n", + " list(map('{:.3f}'.format, np.std(samples, axis=1)))))\n", + " print('correct', b_action_rewards[0].argmax())\n", + " plt.title(\"p(Q(s, a))\")\n", + " plt.show()\n", + "\n", + " return moving_average(np.array(rewards_history), alpha=0.1)" + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "iteration #90\tmean reward=0.560\tmse=0.457\tkl=0.044\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Q(s,a) std: 0.178;0.011;0.000;0.000;0.195;0.000;0.000;0.124;0.023;0.000\n", + "correct 4\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:37: FutureWarning: pd.ewm_mean is deprecated for ndarrays and will be removed in a future version\n" + ] + } + ], + "source": [ + "bnn_agent = BNNAgent(state_size=state_size, n_actions=n_actions)\n", + "greedy_agent_rewards = train_contextual_agent(\n", + " bnn_agent, batch_size=10, n_iters=N_ITERS)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## HW 2.1 Better exploration\n", + "\n", + "Use strategies from first part to gain more reward in contextual setting" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class ThompsonBNNAgent(BNNAgent):\n", + " def get_action(self, states):\n", + " \"\"\"\n", + " picks action based by taking _one_ sample from BNN and taking action with highest sampled reward (yes, that simple)\n", + " This is exactly thompson sampling.\n", + " \"\"\"\n", + "\n", + " # YOUR CODE HERE" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "iteration #90\tmean reward=0.360\tmse=0.590\tkl=0.038\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Q(s,a) std: 0.000;0.028;0.277;0.000;0.044;0.059;0.063;0.093;0.000;0.018\n", + "correct 2\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:32: FutureWarning: pd.ewm_mean is deprecated for ndarrays and will be removed in a future version\n" + ] + } + ], + "source": [ + "thompson_agent_rewards = train_contextual_agent(ThompsonBNNAgent(state_size=state_size, n_actions=n_actions),\n", + " batch_size=10, n_iters=N_ITERS)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class BayesUCBBNNAgent(BNNAgent):\n", + " q = 90\n", + "\n", + " def get_action(self, states):\n", + " \"\"\"\n", + " Compute q-th percentile of rewards P(r|s,a) for all actions\n", + " Take actions that have highest percentiles.\n", + "\n", + " This implements bayesian UCB strategy\n", + " \"\"\"\n", + "\n", + " # YOUR CODE HERE" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "iteration #90\tmean reward=0.630\tmse=0.354\tkl=0.047\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Q(s,a) std: 0.067;0.027;0.093;0.069;0.014;0.148;0.173;0.026;0.043;0.101\n", + "correct 5\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:32: FutureWarning: pd.ewm_mean is deprecated for ndarrays and will be removed in a future version\n" + ] + } + ], + "source": [ + "ucb_agent_rewards = train_contextual_agent(BayesUCBBNNAgent(state_size=state_size, n_actions=n_actions),\n", + " batch_size=10, n_iters=N_ITERS)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAHVCAYAAADsApjCAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4xLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvAOZPmwAAIABJREFUeJzs3Xt8z3X/x/HHZ+exzWEHcz4OwxhmM+eIVJSihIRUSCpJCFEJoVwl6UqJHEJS5BRRTmNsM+Qwhxmb02ZjBztvn98frsuPy2nYfIfn/Xa7btv3+30fnt9tuvba+/N5vw3TNBERERERERGR/Gdl6QAiIiIiIiIiDyoV3SIiIiIiIiIFREW3iIiIiIiISAFR0S0iIiIiIiJSQFR0i4iIiIiIiBQQFd0iIiIiIiIiBURFt4iIiIiIiEgBUdEtIiIiIiIiUkBUdIuIiIiIiIgUEBtLTezm5mZWqlTJUtOLiIiIiIiI3LHQ0NBzpmm636qdxYruSpUqERISYqnpRURERERERO6YYRjH89JOl5eLiIiIiIiIFBAV3SIiIiIiIiIFREW3iIiIiIiISAGx2D3d15OVlUVMTAzp6emWjiIPMAcHB8qVK4etra2lo4iIiIiIyAOuUBXdMTExODs7U6lSJQzDsHQceQCZpkl8fDwxMTFUrlzZ0nFEREREROQBV6guL09PT8fV1VUFtxQYwzBwdXXV1RQiIiIiInJPFKqiG1DBLQVOP2MiIiIiInKvFLqiW0RERERERORBoaL7f5w9e5bu3btTpUoVGjZsSGBgIL/++muBzDV79mzeeOONPLcfO3YsZcuWxdfXl5o1azJgwAByc3MB6N27N2XLliUjIwOAc+fOUalSJQCioqIwDINp06ZdHuuNN95g9uzZ+fZeRERERERE5Foquq9gmiadOnWiRYsWREZGEhoaysKFC4mJibmmbXZ2tgUSwuDBgwkPD2f//v3s3buXjRs3Xn7N2tqaWbNmXbefh4cHX3zxBZmZmfcqqoiIiIiIyEOvUO1efqUPf9/H/lNJ+TpmrTIujOlY+4avb9iwATs7O/r373/5uYoVKzJo0CDg0sr00qVLSUlJIScnh40bNzJ58mQWL15MRkYGzzzzDB9++CEA8+bN48svvyQzM5OAgAC+/vprrK2t+eGHH5gwYQLFixenXr162Nvbk5ycTN26dTl06BC2trYkJSVRr169y4+vJzMzk/T0dEqUKHH5ubfffpupU6fy6quvXtPe3d2dpk2bMmfOnOu+LiIiIiIiIvlPK91X2LdvHw0aNLhpm7CwMJYsWcLGjRtZu3Ythw8fZseOHYSHhxMaGsqmTZs4cOAAixYtYuvWrYSHh2Ntbc38+fM5ffo0Y8aMYevWrWzZsoX9+/cD4OzsTKtWrVi5ciUACxcu5Nlnn71uwT116lR8fX0pXbo01atXx9fX9/JrFSpUoFmzZsydO/e62YcNG8aUKVPIycm50y+RiIiIiIiI3IZCu9J9sxXpe2XgwIFs2bIFOzs7du7cCUDbtm0pWbIkAGvXrmXt2rXUr18fgJSUFA4fPsyePXsIDQ2lUaNGAKSlpeHh4UFwcDCtWrXC3d0dgK5du3Lo0CEAXnnlFSZNmkSnTp344YcfmDlz5nUzDR48mHfffZesrCy6dOnCwoULeeGFFy6/PmLECJ5++mmefPLJa/pWqVKFgIAAFixYkE9fIREREREREbkZrXRfoXbt2oSFhV1+PH36dNavX09cXNzl54oWLXr5c9M0GTFiBOHh4YSHh3PkyBH69u2LaZr06tXr8vMRERGMHTv2pnM3bdqUqKgo/v77b3JycqhTp85N29va2tK+fXs2bdp01fNeXl74+vqyePHi6/Z7//33+fTTTzFN86bji4iIiIiIyN1T0X2F1q1bk56ezowZMy4/l5qaesP2jz32GLNmzSIlJQWAkydPEhsbS5s2bViyZAmxsbEAJCQkcPz4cQICAti4cSPx8fFkZWXx888/XzXeSy+9RPfu3enTp88ts5qmydatW6lateo1r40cOZIpU6Zct1/NmjWpVasWv//++y3nEBERERERkbujovsKhmHw22+/sXHjRipXroy/vz+9evXi008/vW77du3a0b17dwIDA/Hx8aFLly4kJydTq1Ytxo0bR7t27ahbty5t27bl9OnTlC5dmrFjxxIYGEjTpk3x9va+arwePXpw/vx5unXrdsOM/72nu06dOuTk5PD6669f06Z27do3vTd95MiR192RXURERERERPKXYanLjP38/MyQkJCrnjtw4MA1hejDZMmSJSxbtuyGG6FJ/nnYf9ZERERERAqb+LR4nO2csbO2s3SUPDEMI9Q0Tb9btSu0G6k9bAYNGsTq1atZ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o1GjRgAMGDAAHx8fjhw5kucx6tSpQ8uWLYmPj+fDDz+kVKlSzJo1i88//xxbW1ucnZ2ZO3cu5cqV4+OPP6ZVq1aYpknHjh158skn87xSPGXKFDZv3oyVlRV169alXbt2+Pv7X74v2t/fHy8vr9v+Glwvf3Jy8m2NERgYSPv27fHx8aFUqVL4+PhQrFix67b97z3dWVlZ1K9fn379+l3Tpl69etSrV4/9+/dfd4xRo0bRsGHD28ooIiIiIiJ5k56dzpsb3iQxI5E57edQzrmcpSMVSoalLr/18/MzQ0JCrnruwIEDeHt7WyRPQRo1ahRubm433aW7MMvP/CkpKTg5OXHx4kWaNWvGnDlzqFu3bj6kvD0P6s+aiIiIiMi9kGvmMnTjUNYdX8fUR6bSpsK1ey096AzDCDVN85aXAGulW+6pvn37EhERQXp6Oi+//LJFCm4REREREbk708Ons/b4Wt5p+M5DWXDfDhXd98C4ceMsHeGu5Gd+7SguIiIiInJ/+/3o73y751ue9XqW3rV7WzpOoaeN1ERERERERCRPws6GMSZoDP6e/owKGHXLU41ERbeIiIiIiIjkQXRyNG//9TZlnMrweavPsbW2tXSk+4KKbhEREREREbmppMwk3lj/BjlmDl+1/opi9tc/hUiupXu6RURERERE5Iayc7N59+93OZF0gn+3/TeVilWydKT7ila6rxAVFUWdOnWuem7s2LFMmTLl8uMpU6ZQs2ZNfH19adSoET/++CMArVq1okaNGvj6+uLt7c2333573Tlu1q5SpUp07tz58uMlS5bQu3dvAGbPno2VlRV79uy5/HqdOnWIioq627ctIiIiIiJyXaZpMnHHRLad3sbowNH4l/a3dKT7joru2/DNN9+wbt06duzYQXh4OOvXr+fKc87nz59PeHg4W7duZdiwYWRmZl53nJu1Cw0NZf/+/dftV65cOT755JP8fVMiIiIiIiI38POhn1kUsYg+tfvwrNezlo5zXyq0l5d/uuNTDiYczNcxa5asyTD/YXfcf/z48fz999+4uLgA4OLiQq9eva5pl5KSQtGiRbG2tr7peNdrN2TIED755BPmz59/TfsOHTqwadMmIiIiqFGjxh2/DxERERERkVvZH7+fiTsm0rRMU95q8Jal49y3tNKdR0lJSSQnJ1OlSpUbtunRowd169alRo0ajB49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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.figure(figsize=(17, 8))\n", + "\n", + "plt.plot(greedy_agent_rewards)\n", + "plt.plot(thompson_agent_rewards)\n", + "plt.plot(ucb_agent_rewards)\n", + "\n", + "plt.legend([\n", + " \"Greedy BNN\",\n", + " \"Thompson sampling BNN\",\n", + " \"UCB BNN\"\n", + "])\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 3. Exploration in MDP\n", + "\n", + "The following problem, called \"river swim\", illustrates importance of exploration in context of mdp's." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "Picture from https://arxiv.org/abs/1306.0940" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Rewards and transition probabilities are unknown to an agent. Optimal policy is to swim against current, while easiest way to gain reward is to go left." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class RiverSwimEnv:\n", + " LEFT_REWARD = 5.0 / 1000\n", + " RIGHT_REWARD = 1.0\n", + "\n", + " def __init__(self, intermediate_states_count=4, max_steps=16):\n", + " self._max_steps = max_steps\n", + " self._current_state = None\n", + " self._steps = None\n", + " self._interm_states = intermediate_states_count\n", + " self.reset()\n", + "\n", + " def reset(self):\n", + " self._steps = 0\n", + " self._current_state = 1\n", + " return self._current_state, 0.0, False\n", + "\n", + " @property\n", + " def n_actions(self):\n", + " return 2\n", + "\n", + " @property\n", + " def n_states(self):\n", + " return 2 + self._interm_states\n", + "\n", + " def _get_transition_probs(self, action):\n", + " if action == 0:\n", + " if self._current_state == 0:\n", + " return [0, 1.0, 0]\n", + " else:\n", + " return [1.0, 0, 0]\n", + "\n", + " elif action == 1:\n", + " if self._current_state == 0:\n", + " return [0, .4, .6]\n", + " if self._current_state == self.n_states - 1:\n", + " return [.4, .6, 0]\n", + " else:\n", + " return [.05, .6, .35]\n", + " else:\n", + " raise RuntumeError(\n", + " \"Unknown action {}. Max action is {}\".format(action, self.n_actions))\n", + "\n", + " def step(self, action):\n", + " \"\"\"\n", + " :param action:\n", + " :type action: int\n", + " :return: observation, reward, is_done\n", + " :rtype: (int, float, bool)\n", + " \"\"\"\n", + " reward = 0.0\n", + "\n", + " if self._steps >= self._max_steps:\n", + " return self._current_state, reward, True\n", + "\n", + " transition = np.random.choice(\n", + " range(3), p=self._get_transition_probs(action))\n", + " if transition == 0:\n", + " self._current_state -= 1\n", + " elif transition == 1:\n", + " pass\n", + " else:\n", + " self._current_state += 1\n", + "\n", + " if self._current_state == 0:\n", + " reward = self.LEFT_REWARD\n", + " elif self._current_state == self.n_states - 1:\n", + " reward = self.RIGHT_REWARD\n", + "\n", + " self._steps += 1\n", + " return self._current_state, reward, False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's implement q-learning agent with epsilon-greedy exploration strategy and see how it performs." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class QLearningAgent:\n", + " def __init__(self, n_states, n_actions, lr=0.2, gamma=0.95, epsilon=0.1):\n", + " self._gamma = gamma\n", + " self._epsilon = epsilon\n", + " self._q_matrix = np.zeros((n_states, n_actions))\n", + " self._lr = lr\n", + "\n", + " def get_action(self, state):\n", + " if np.random.random() < self._epsilon:\n", + " return np.random.randint(0, self._q_matrix.shape[1])\n", + " else:\n", + " return np.argmax(self._q_matrix[state])\n", + "\n", + " def get_q_matrix(self):\n", + " \"\"\" Used for policy visualization\n", + " \"\"\"\n", + "\n", + " return self._q_matrix\n", + "\n", + " def start_episode(self):\n", + " \"\"\" Used in PSRL agent\n", + " \"\"\"\n", + " pass\n", + "\n", + " def update(self, state, action, reward, next_state):\n", + " # YOUR CODE HERE\n", + " # Finish implementation of q-learnig agent" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def train_mdp_agent(agent, env, n_episodes):\n", + " episode_rewards = []\n", + "\n", + " for ep in range(n_episodes):\n", + " state, ep_reward, is_done = env.reset()\n", + " agent.start_episode()\n", + " while not is_done:\n", + " action = agent.get_action(state)\n", + "\n", + " next_state, reward, is_done = env.step(action)\n", + " agent.update(state, action, reward, next_state)\n", + "\n", + " state = next_state\n", + " ep_reward += reward\n", + "\n", + " episode_rewards.append(ep_reward)\n", + " return episode_rewards" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:6: FutureWarning: pd.ewm_mean is deprecated for ndarrays and will be removed in a future version\n", + " \n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "env = RiverSwimEnv()\n", + "agent = QLearningAgent(env.n_states, env.n_actions)\n", + "rews = train_mdp_agent(agent, env, 1000)\n", + "plt.figure(figsize=(15, 8))\n", + "\n", + "plt.plot(moving_average(np.array(rews), alpha=.1))\n", + "plt.xlabel(\"Episode count\")\n", + "plt.ylabel(\"Reward\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's visualize our policy:" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def plot_policy(agent):\n", + " fig = plt.figure(figsize=(15, 8))\n", + " ax = fig.add_subplot(111)\n", + " ax.matshow(agent.get_q_matrix().T)\n", + " ax.set_yticklabels(['', 'left', 'right'])\n", + " plt.xlabel(\"State\")\n", + " plt.ylabel(\"Action\")\n", + " plt.title(\"Values of state-action pairs\")\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_policy(agent)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As your see, agent uses suboptimal policy of going left and does not explore the right state." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bonus 3.1 Posterior sampling RL (3 points)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we will implement Thompson Sampling for MDP!\n", + "\n", + "General algorithm:\n", + "\n", + ">**for** episode $k = 1,2,...$ **do**\n", + ">> sample $M_k \\sim f(\\bullet\\ |\\ H_k)$\n", + "\n", + ">> compute policy $\\mu_k$ for $M_k$\n", + "\n", + ">> **for** time $t = 1, 2,...$ **do**\n", + "\n", + ">>> take action $a_t$ from $\\mu_k$ \n", + "\n", + ">>> observe $r_t$ and $s_{t+1}$\n", + ">>> update $H_k$\n", + "\n", + ">> **end for**\n", + "\n", + ">**end for**\n", + "\n", + "In our case we will model $M_k$ with two matricies: transition and reward. Transition matrix is sampled from dirichlet distribution. Reward matrix is sampled from normal-gamma distribution.\n", + "\n", + "Distributions are updated with bayes rule - see continious distribution section at https://en.wikipedia.org/wiki/Conjugate_prior\n", + "\n", + "Article on PSRL - https://arxiv.org/abs/1306.0940" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def sample_normal_gamma(mu, lmbd, alpha, beta):\n", + " \"\"\" https://en.wikipedia.org/wiki/Normal-gamma_distribution\n", + " \"\"\"\n", + " tau = np.random.gamma(alpha, beta)\n", + " mu = np.random.normal(mu, 1.0 / np.sqrt(lmbd * tau))\n", + " return mu, tau\n", + "\n", + "\n", + "class PsrlAgent:\n", + " def __init__(self, n_states, n_actions, horizon=10):\n", + " self._n_states = n_states\n", + " self._n_actions = n_actions\n", + " self._horizon = horizon\n", + "\n", + " # params for transition sampling - Dirichlet distribution\n", + " self._transition_counts = np.zeros(\n", + " (n_states, n_states, n_actions)) + 1.0\n", + "\n", + " # params for reward sampling - Normal-gamma distribution\n", + " self._mu_matrix = np.zeros((n_states, n_actions)) + 1.0\n", + " self._state_action_counts = np.zeros(\n", + " (n_states, n_actions)) + 1.0 # lambda\n", + "\n", + " self._alpha_matrix = np.zeros((n_states, n_actions)) + 1.0\n", + " self._beta_matrix = np.zeros((n_states, n_actions)) + 1.0\n", + "\n", + " def _value_iteration(self, transitions, rewards):\n", + " # YOU CODE HERE\n", + " state_values = < Find action values with value iteration >\n", + " return state_values\n", + "\n", + " def start_episode(self):\n", + " # sample new mdp\n", + " self._sampled_transitions = np.apply_along_axis(\n", + " np.random.dirichlet, 1, self._transition_counts)\n", + "\n", + " sampled_reward_mus, sampled_reward_stds = sample_normal_gamma(\n", + " self._mu_matrix,\n", + " self._state_action_counts,\n", + " self._alpha_matrix,\n", + " self._beta_matrix\n", + " )\n", + "\n", + " self._sampled_rewards = sampled_reward_mus\n", + " self._current_value_function = self._value_iteration(\n", + " self._sampled_transitions, self._sampled_rewards)\n", + "\n", + " def get_action(self, state):\n", + " return np.argmax(self._sampled_rewards[state] +\n", + " self._current_value_function.dot(self._sampled_transitions[state]))\n", + "\n", + " def update(self, state, action, reward, next_state):\n", + " # YOUR CODE HERE\n", + " # update rules - https://en.wikipedia.org/wiki/Conjugate_prior\n", + "\n", + " def get_q_matrix(self):\n", + " return self._sampled_rewards + self._current_value_function.dot(self._sampled_transitions)" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:7: FutureWarning: pd.ewm_mean is deprecated for ndarrays and will be removed in a future version\n", + " import sys\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pandas import DataFrame\n", + "moving_average = lambda x, **kw: DataFrame(\n", + " {'x': np.asarray(x)}).x.ewm(**kw).mean().values\n", + "\n", + "horizon = 20\n", + "env = RiverSwimEnv(max_steps=horizon)\n", + "agent = PsrlAgent(env.n_states, env.n_actions, horizon=horizon)\n", + "rews = train_mdp_agent(agent, env, 1000)\n", + "\n", + "plt.figure(figsize=(15, 8))\n", + "plt.plot(moving_average(np.array(rews), alpha=0.1))\n", + "\n", + "plt.xlabel(\"Episode count\")\n", + "plt.ylabel(\"Reward\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_policy(agent)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bonus 3.2 Bootstrapped DQN (10 points)\n", + "\n", + "Implement Bootstrapped DQN algorithm and compare it's performance with ordinary DQN on BeamRider Atari game. Links:\n", + "- https://arxiv.org/abs/1602.04621" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week6_policy_based/README.md b/week06_policy_based/README.md similarity index 53% rename from week6_policy_based/README.md rename to week06_policy_based/README.md index af35de0df..b48b49c9e 100644 --- a/week6_policy_based/README.md +++ b/week06_policy_based/README.md @@ -1,37 +1,24 @@ ## Materials -* [Slides](https://docviewer.yandex.ru/?url=ya-disk-public%3A%2F%2FG3IXcG62RwNUGSSos%2BuGhtgXNfsBjP9RxUtUfgCffIk%3D%3A%2Flecture6.pdf&name=lecture6.pdf&c=58c876c4863a) +* [Slides](https://yadi.sk/i/keSzKSgA2oYuwQ) * Video lecture by D. Silver - [video](https://www.youtube.com/watch?v=KHZVXao4qXs) * Our [lecture](https://yadi.sk/i/yPIPkO_f3TPsNK), [seminar(pytorch)](https://yadi.sk/i/flW8ezGk3TPsQ5), [seminar(theano)](https://yadi.sk/i/8f9NX_E73GKBkT) * Alternative lecture by J. Schulman part 1 - [video](https://www.youtube.com/watch?v=BB-BhTn6DCM) * Alternative lecture by J. Schulman part 2 - [video](https://www.youtube.com/watch?v=Wnl-Qh2UHGg) +* Andrej Karpathy's [post](http://karpathy.github.io/2016/05/31/rl/) on policy gradients ## More materials * Actually proving the policy gradient for discounted rewards - [article](https://papers.nips.cc/paper/1713-policy-gradient-methods-for-reinforcement-learning-with-function-approximation.pdf) * On variance of policy gradient and optimal baselines: [article](https://papers.nips.cc/paper/4264-analysis-and-improvement-of-policy-gradient-estimation.pdf), another [article](https://arxiv.org/pdf/1301.2315.pdf) -* Generalized Advantage Estimation - a way you can speed up training for homework_*.ipynb - [article](https://arxiv.org/abs/1506.02438) +* Learn Advatangeg Actor Critic with a [comic](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) * Generalizing log-derivative trick - [url](http://blog.shakirm.com/2015/11/machine-learning-trick-of-the-day-5-log-derivative-trick/) * Combining policy gradient and q-learning - [arxiv](https://arxiv.org/abs/1611.01626) -* Bayesian perspective on why reparameterization & logderivative tricks matter (Vetrov's take) - [pdf](https://www.sdsj.ru/slides/Vetrov.pdf) +* Variational perspective on reinforcement learning (from DeepBayes) - [pdf](http://incompleteideas.net/book/the-book-2nd.html) * Adversarial review of policy gradient - [blog](http://www.argmin.net/2018/02/20/reinforce/) -## Homework +Run seminar notebook in colab: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week06_policy_based/reinforce_pytorch.ipynb) -As usual, pick reinfoce_.ipynb for starters and then proceed with homework_.ipynb. - -To run seminar notebook in colab -* run it [here](https://colab.research.google.com/github/yandexdataschool/Practical_DL/blob/fall18/week10_rl/reinforce_pytorch.ipynb#scrollTo=y9nfDwJY3sGI) -* paste this to install libraries -``` -!pip install gym -!apt-get install -y xvfb -!wget https://raw.githubusercontent.com/yandexdataschool/Practical_DL/fall18/xvfb -!apt-get install -y python-opengl ffmpeg -!pip install pyglet==1.2.4 - -!bash ./xvfb start -%env DISPLAY=:1 -``` +Run optional homework notebook in colab: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week06_policy_based/a2c-optional.ipynb) diff --git a/week06_policy_based/a2c-optional.ipynb b/week06_policy_based/a2c-optional.ipynb new file mode 100644 index 000000000..5139ae867 --- /dev/null +++ b/week06_policy_based/a2c-optional.ipynb @@ -0,0 +1,328 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# # in google colab uncomment this\n", + "\n", + "# import os\n", + "\n", + "# os.system('apt-get install -y xvfb')\n", + "# os.system('wget https://raw.githubusercontent.com/yandexdataschool/Practical_DL/fall18/xvfb -O ../xvfb')\n", + "# os.system('apt-get install -y python-opengl ffmpeg')\n", + "# os.system('pip install pyglet==1.2.4')\n", + "\n", + "# os.system('python -m pip install -U pygame --user')\n", + "\n", + "# print('setup complete')\n", + "\n", + "# XVFB will be launched if you run on a server\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Implementing Advantage-Actor Critic (A2C)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this notebook you will implement Advantage Actor Critic algorithm that trains on a batch of Atari 2600 environments running in parallel. \n", + "\n", + "Firstly, we will use environment wrappers implemented in file `atari_wrappers.py`. These wrappers preprocess observations (resize, grayscal, take max between frames, skip frames and stack them together) and rewards. Some of the wrappers help to reset the environment and pass `done` flag equal to `True` when agent dies.\n", + "File `env_batch.py` includes implementation of `ParallelEnvBatch` class that allows to run multiple environments in parallel. To create an environment we can use `nature_dqn_env` function. Note that if you are using \n", + "PyTorch and not using `tensorboardX` you will need to implement a wrapper that will log **raw** total rewards that the *unwrapped* environment returns and redefine the implemention of `nature_dqn_env` function here. \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from atari_wrappers import nature_dqn_env\n", + "\n", + "\n", + "env = nature_dqn_env(\"SpaceInvadersNoFrameskip-v4\", nenvs=8)\n", + "obs = env.reset()\n", + "assert obs.shape == (8, 84, 84, 4)\n", + "assert obs.dtype == np.uint8" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we will need to implement a model that predicts logits and values. It is suggested that you use the same model as in [Nature DQN paper](https://web.stanford.edu/class/psych209/Readings/MnihEtAlHassibis15NatureControlDeepRL.pdf) with a modification that instead of having a single output layer, it will have two output layers taking as input the output of the last hidden layer. **Note** that this model is different from the model you used in homework where you implemented DQN. You can use your favorite deep learning framework here. We suggest that you use orthogonal initialization with parameter $\\sqrt{2}$ for kernels and initialize biases with zeros. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# import tensorflow as torch\n", + "# import torch as tf\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You will also need to define and use a policy that wraps the model. While the model computes logits for all actions, the policy will sample actions and also compute their log probabilities. `policy.act` should return a dictionary of all the arrays that are needed to interact with an environment and train the model.\n", + " Note that actions must be an `np.ndarray` while the other\n", + "tensors need to have the type determined by your deep learning framework. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class Policy:\n", + " def __init__(self, model):\n", + " self.model = model\n", + " \n", + " def act(self, inputs):\n", + " \n", + " # Should return a dict containing keys ['actions', 'logits', 'log_probs', 'values']." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next will pass the environment and policy to a runner that collects partial trajectories from the environment. \n", + "The class that does is is already implemented for you." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from runners import EnvRunner" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This runner interacts with the environment for a given number of steps and returns a dictionary containing\n", + "keys \n", + "\n", + "* 'observations' \n", + "* 'rewards' \n", + "* 'resets'\n", + "* 'actions'\n", + "* all other keys that you defined in `Policy`\n", + "\n", + "under each of these keys there is a python `list` of interactions with the environment of specified length $T$ — the size of partial trajectory. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To train the part of the model that predicts state values you will need to compute the value targets. \n", + "Any callable could be passed to `EnvRunner` to be applied to each partial trajectory after it is collected. \n", + "Thus, we can implement and use `ComputeValueTargets` callable. \n", + "The formula for the value targets is simple:\n", + "\n", + "$$\n", + "\\hat v(s_t) = \\sum_{t'=0}^{T - 1}\\gamma^{t'}r_{t+t'} + \\gamma^T \\hat{v}(s_{t+T}),\n", + "$$\n", + "\n", + "In implementation, however, do not forget to use \n", + "`trajectory['resets']` flags to check if you need to add the value targets at the next step when \n", + "computing value targets for the current step. You can access `trajectory['state']['latest_observation']`\n", + "to get last observations in partial trajectory — $s_{t+T}$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class ComputeValueTargets:\n", + " def __init__(self, policy, gamma=0.99):\n", + " self.policy = policy\n", + " \n", + " def __call__(self, trajectory):\n", + " # This method should modify trajectory inplace by adding \n", + " # an item with key 'value_targets' to it. \n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After computing value targets we will transform lists of interactions into tensors\n", + "with the first dimension `batch_size` which is equal to `T * nenvs`, i.e. you essentially need\n", + "to flatten the first two dimensions. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class MergeTimeBatch:\n", + " \"\"\" Merges first two axes typically representing time and env batch. \"\"\"\n", + " def __call__(self, trajectory):\n", + " # Modify trajectory inplace. \n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "model = \n", + "policy = Policy(model)\n", + "runner = EnvRunner(env, policy, nsteps=5,\n", + " transforms=[ComputeValueTargets(),\n", + " MergeTimeBatch()])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now is the time to implement the advantage actor critic algorithm itself. You can look into your lecture,\n", + "[Mnih et al. 2016](https://arxiv.org/abs/1602.01783) paper, and [lecture](https://www.youtube.com/watch?v=Tol_jw5hWnI&list=PLkFD6_40KJIxJMR-j5A1mkxK26gh_qg37&index=20) by Sergey Levine." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class A2C:\n", + " def __init__(self,\n", + " policy,\n", + " optimizer,\n", + " value_loss_coef=0.25,\n", + " entropy_coef=0.01,\n", + " max_grad_norm=0.5):\n", + " self.policy = policy\n", + " self.optimizer = optimizer\n", + " self.value_loss_coef = value_loss_coef\n", + " self.entropy_coef = entropy_coef\n", + " self.max_grad_norm = max_grad_norm\n", + " \n", + " def policy_loss(self, trajectory):\n", + " # You will need to compute advantages here. \n", + " \n", + " \n", + " def value_loss(self, trajectory):\n", + " \n", + " \n", + " def loss(self, trajectory):\n", + " \n", + " \n", + " def step(self, trajectory):\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now you can train your model. With reasonable hyperparameters training on a single GTX1080 for 10 million steps across all batched environments (which translates to about 5 hours of wall clock time)\n", + "it should be possible to achieve *average raw reward over last 100 episodes* (the average is taken over 100 last \n", + "episodes in each environment in the batch) of about 600. You should plot this quantity with respect to \n", + "`runner.step_var` — the number of interactions with all environments. It is highly \n", + "encouraged to also provide plots of the following quantities (these are useful for debugging as well):\n", + "\n", + "* [Coefficient of Determination](https://en.wikipedia.org/wiki/Coefficient_of_determination) between \n", + "value targets and value predictions\n", + "* Entropy of the policy $\\pi$\n", + "* Value loss\n", + "* Policy loss\n", + "* Value targets\n", + "* Value predictions\n", + "* Gradient norm\n", + "* Advantages\n", + "* A2C loss\n", + "\n", + "For optimization we suggest you use RMSProp with learning rate starting from 7e-4 and linearly decayed to 0, smoothing constant (alpha in PyTorch and decay in TensorFlow) equal to 0.99 and epsilon equal to 1e-5." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "a2c = \n", + "\n", + "" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "rl", + "language": "python", + "name": "rl" + }, + "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.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week06_policy_based/atari_wrappers.py b/week06_policy_based/atari_wrappers.py new file mode 100644 index 000000000..778ac99c9 --- /dev/null +++ b/week06_policy_based/atari_wrappers.py @@ -0,0 +1,307 @@ +""" Environment wrappers. """ +from collections import deque + +import cv2 +import gym +import gym.spaces as spaces +from gym.envs import atari +import numpy as np +import tensorflow as tf + +from env_batch import ParallelEnvBatch +cv2.ocl.setUseOpenCL(False) + + +class EpisodicLife(gym.Wrapper): + """ Sets done flag to true when agent dies. """ + def __init__(self, env): + super(EpisodicLife, self).__init__(env) + self.lives = 0 + self.real_done = True + + def step(self, action): + obs, rew, done, info = self.env.step(action) + self.real_done = done + info["real_done"] = done + lives = self.env.unwrapped.ale.lives() + if 0 < lives < self.lives: + done = True + self.lives = lives + return obs, rew, done, info + + def reset(self, **kwargs): + if self.real_done: + obs = self.env.reset(**kwargs) + else: + obs, _, _, _ = self.env.step(0) + self.lives = self.env.unwrapped.ale.lives() + return obs + + +class FireReset(gym.Wrapper): + """ Makes fire action when reseting environment. + + Some environments are fixed until the agent makes the fire action, + this wrapper makes this action so that the epsiode starts automatically. + """ + def __init__(self, env): + super(FireReset, self).__init__(env) + action_meanings = env.unwrapped.get_action_meanings() + if len(action_meanings) < 3: + raise ValueError( + "env.unwrapped.get_action_meanings() must be of length >= 3" + f"but is of length {len(action_meanings)}") + if env.unwrapped.get_action_meanings()[1] != "FIRE": + raise ValueError( + "env.unwrapped.get_action_meanings() must have 'FIRE' " + f"under index 1, but is {action_meanings}") + + def step(self, action): + return self.env.step(action) + + def reset(self, **kwargs): + self.env.reset(**kwargs) + obs, _, done, _ = self.env.step(1) + if done: + self.env.reset(**kwargs) + obs, _, done, _ = self.env.step(2) + if done: + self.env.reset(**kwargs) + return obs + + +class StartWithRandomActions(gym.Wrapper): + """ Makes random number of random actions at the beginning of each + episode. """ + def __init__(self, env, max_random_actions=30): + super(StartWithRandomActions, self).__init__(env) + self.max_random_actions = max_random_actions + self.real_done = True + + def step(self, action): + obs, rew, done, info = self.env.step(action) + self.real_done = info.get("real_done", True) + return obs, rew, done, info + + def reset(self, **kwargs): + obs = self.env.reset() + if self.real_done: + num_random_actions = np.random.randint(self.max_random_actions + 1) + for _ in range(num_random_actions): + obs, _, _, _ = self.env.step(self.env.action_space.sample()) + self.real_done = False + return obs + + +class ImagePreprocessing(gym.ObservationWrapper): + """ Preprocesses image-observations by possibly grayscaling and resizing. """ + def __init__(self, env, width=84, height=84, grayscale=True): + super(ImagePreprocessing, self).__init__(env) + self.width = width + self.height = height + self.grayscale = grayscale + ospace = self.env.observation_space + low, high, dtype = ospace.low.min(), ospace.high.max(), ospace.dtype + if self.grayscale: + self.observation_space = spaces.Box(low=low, high=high, + shape=(width, height), dtype=dtype) + else: + obs_shape = (width, height) + self.observation_space.shape[2:] + self.observation_space = spaces.Box(low=low, high=high, + shape=obs_shape, dtype=dtype) + + def observation(self, observation): + """ Performs image preprocessing. """ + if self.grayscale: + observation = cv2.cvtColor(observation, cv2.COLOR_RGB2GRAY) + observation = cv2.resize(observation, (self.width, self.height), + cv2.INTER_AREA) + return observation + + +class MaxBetweenFrames(gym.ObservationWrapper): + """ Takes maximum between two subsequent frames. """ + def __init__(self, env): + if (isinstance(env.unwrapped, atari.AtariEnv) and + "NoFrameskip" not in env.spec.id): + raise ValueError("MaxBetweenFrames requires NoFrameskip in atari env id") + super(MaxBetweenFrames, self).__init__(env) + self.last_obs = None + + def observation(self, observation): + obs = np.maximum(observation, self.last_obs) + self.last_obs = observation + return obs + + def reset(self, **kwargs): + self.last_obs = self.env.reset() + return self.last_obs + + +class QueueFrames(gym.ObservationWrapper): + """ Queues specified number of frames together along new dimension. """ + def __init__(self, env, nframes, concat=False): + super(QueueFrames, self).__init__(env) + self.obs_queue = deque([], maxlen=nframes) + self.concat = concat + ospace = self.observation_space + if self.concat: + oshape = ospace.shape[:-1] + (ospace.shape[-1] * nframes,) + else: + oshape = ospace.shape + (nframes,) + self.observation_space = spaces.Box(ospace.low.min(), ospace.high.max(), + oshape, ospace.dtype) + + def observation(self, observation): + self.obs_queue.append(observation) + return (np.concatenate(self.obs_queue, -1) if self.concat + else np.dstack(self.obs_queue)) + + def reset(self, **kwargs): + obs = self.env.reset() + for _ in range(self.obs_queue.maxlen - 1): + self.obs_queue.append(obs) + return self.observation(obs) + + +class SkipFrames(gym.Wrapper): + """ Performs the same action for several steps and returns the final result. + """ + def __init__(self, env, nskip=4): + super(SkipFrames, self).__init__(env) + if (isinstance(env.unwrapped, atari.AtariEnv) and + "NoFrameskip" not in env.spec.id): + raise ValueError("SkipFrames requires NoFrameskip in atari env id") + self.nskip = nskip + + def step(self, action): + total_reward = 0.0 + for _ in range(self.nskip): + obs, rew, done, info = self.env.step(action) + total_reward += rew + if done: + break + return obs, total_reward, done, info + + def reset(self, **kwargs): + return self.env.reset(**kwargs) + + +class ClipReward(gym.RewardWrapper): + """ Modifes reward to be in {-1, 0, 1} by taking sign of it. """ + def reward(self, reward): + return np.sign(reward) + + +class TFSummaries(gym.Wrapper): + """ Writes env summaries.""" + def __init__(self, env, prefix=None, running_mean_size=100, step_var=None): + super(TFSummaries, self).__init__(env) + self.episode_counter = 0 + self.prefix = prefix or self.env.spec.id + self.step_var = (step_var if step_var is not None + else tf.train.get_global_step()) + + nenvs = getattr(self.env.unwrapped, "nenvs", 1) + self.rewards = np.zeros(nenvs) + self.had_ended_episodes = np.zeros(nenvs, dtype=np.bool) + self.episode_lengths = np.zeros(nenvs) + self.reward_queues = [deque([], maxlen=running_mean_size) + for _ in range(nenvs)] + + def should_write_summaries(self): + """ Returns true if it's time to write summaries. """ + return np.all(self.had_ended_episodes) + + def add_summaries(self): + """ Writes summaries. """ + tf.contrib.summary.scalar( + f"{self.prefix}/total_reward", + tf.reduce_mean([q[-1] for q in self.reward_queues]), + step=self.step_var) + tf.contrib.summary.scalar( + f"{self.prefix}/reward_mean_{self.reward_queues[0].maxlen}", + tf.reduce_mean([np.mean(q) for q in self.reward_queues]), + step=self.step_var) + tf.contrib.summary.scalar( + f"{self.prefix}/episode_length", + tf.reduce_mean(self.episode_lengths), + step=self.step_var) + if self.had_ended_episodes.size > 1: + tf.contrib.summary.scalar( + f"{self.prefix}/min_reward", + min(q[-1] for q in self.reward_queues), + step=self.step_var) + tf.contrib.summary.scalar( + f"{self.prefix}/max_reward", + max(q[-1] for q in self.reward_queues), + step=self.step_var) + self.episode_lengths.fill(0) + self.had_ended_episodes.fill(False) + + def step(self, action): + obs, rew, done, info = self.env.step(action) + self.rewards += rew + self.episode_lengths[~self.had_ended_episodes] += 1 + + info_collection = [info] if isinstance(info, dict) else info + done_collection = [done] if isinstance(done, bool) else done + done_indices = [i for i, info in enumerate(info_collection) + if info.get("real_done", done_collection[i])] + for i in done_indices: + if not self.had_ended_episodes[i]: + self.had_ended_episodes[i] = True + self.reward_queues[i].append(self.rewards[i]) + self.rewards[i] = 0 + + if self.should_write_summaries(): + self.add_summaries() + return obs, rew, done, info + + def reset(self, **kwargs): + self.rewards.fill(0) + self.episode_lengths.fill(0) + self.had_ended_episodes.fill(False) + return self.env.reset(**kwargs) + + +def nature_dqn_env(env_id, nenvs=None, seed=None, + summaries=True, clip_reward=True): + """ Wraps env as in Nature DQN paper. """ + if "NoFrameskip" not in env_id: + raise ValueError(f"env_id must have 'NoFrameskip' but is {env_id}") + if nenvs is not None: + if seed is None: + seed = list(range(nenvs)) + if isinstance(seed, int): + seed = [seed] * nenvs + if len(seed) != nenvs: + raise ValueError(f"seed has length {len(seed)} but must have " + f"length equal to nenvs which is {nenvs}") + + env = ParallelEnvBatch([ + lambda i=i, env_seed=env_seed: nature_dqn_env( + env_id, seed=env_seed, summaries=False, clip_reward=False) + for i, env_seed in enumerate(seed) + ]) + if summaries: + env = TFSummaries(env, prefix=env_id) + if clip_reward: + env = ClipReward(env) + return env + + env = gym.make(env_id) + env.seed(seed) + if summaries: + env = TFSummaries(env) + env = EpisodicLife(env) + if "FIRE" in env.unwrapped.get_action_meanings(): + env = FireReset(env) + env = StartWithRandomActions(env, max_random_actions=30) + env = MaxBetweenFrames(env) + env = SkipFrames(env, 4) + env = ImagePreprocessing(env, width=84, height=84, grayscale=True) + env = QueueFrames(env, 4) + if clip_reward: + env = ClipReward(env) + return env diff --git a/week06_policy_based/env_batch.py b/week06_policy_based/env_batch.py new file mode 100644 index 000000000..208d413e8 --- /dev/null +++ b/week06_policy_based/env_batch.py @@ -0,0 +1,201 @@ +# pylint: skip-file +from multiprocessing import Process, Pipe + +from gym import Env, Wrapper, Space +import numpy as np + + +class SpaceBatch(Space): + def __init__(self, spaces): + first_type = type(spaces[0]) + first_shape = spaces[0].shape + first_dtype = spaces[0].dtype + for space in spaces: + if not isinstance(space, first_type): + raise TypeError("spaces have different types: {}, {}" + .format(first_type, type(space))) + if first_shape != space.shape: + raise ValueError("spaces have different shapes: {}, {}" + .format(first_shape, space.shape)) + if first_dtype != space.dtype: + raise ValueError("spaces have different data types: {}, {}" + .format(first_dtype, space.dtype)) + + self.spaces = spaces + super(SpaceBatch, self).__init__(shape=self.spaces[0].shape, + dtype=self.spaces[0].dtype) + + def sample(self): + return np.stack([space.sample() for space in self.spaces]) + + def __getattr__(self, attr): + return getattr(self.spaces[0], attr) + + +class EnvBatch(Env): + def __init__(self, make_env, nenvs=None): + make_env_functions = self._get_make_env_functions(make_env, nenvs) + self._envs = [make_env() for make_env in make_env_functions] + self._nenvs = len(self.envs) + # self.observation_space = SpaceBatch([env.observation_space + # for env in self._envs]) + self.action_space = SpaceBatch([env.action_space + for env in self._envs]) + + def _get_make_env_functions(self, make_env, nenvs): + if nenvs is None and not isinstance(make_env, list): + raise ValueError("When nenvs is None make_env" + " must be a list of callables") + if nenvs is not None and not callable(make_env): + raise ValueError("When nenvs is not None make_env must be callable") + + if nenvs is not None: + make_env = [make_env for _ in range(nenvs)] + return make_env + + @property + def nenvs(self): + return self._nenvs + + @property + def envs(self): + return self._envs + + def _check_actions(self, actions): + if not len(actions) == self.nenvs: + raise ValueError( + "number of actions is not equal to number of envs: " + "len(actions) = {}, nenvs = {}" + .format(len(actions), self.nenvs)) + + def step(self, actions): + self._check_actions(actions) + obs, rews, resets, infos = [], [], [], [] + for env, action in zip(self._envs, actions): + ob, rew, done, info = env.step(action) + if done: + ob = env.reset() + obs.append(ob) + rews.append(rew) + resets.append(done) + infos.append(info) + return np.stack(obs), np.stack(rews), np.stack(resets), infos + + def reset(self): + return np.stack([env.reset() for env in self.envs]) + + +class SingleEnvBatch(Wrapper, EnvBatch): + def __init__(self, env): + super(SingleEnvBatch, self).__init__(env) + self.observation_space = SpaceBatch([self.env.observation_space]) + self.action_space = SpaceBatch([self.env.action_space]) + + @property + def nenvs(self): + return 1 + + @property + def envs(self): + return [self.env] + + def step(self, actions): + self._check_actions(actions) + ob, rew, done, info = self.env.step(actions[0]) + if done: + ob = self.env.reset() + return ob[None], np.expand_dims(rew, 0), np.expand_dims(done, 0), [info] + + def reset(self): + return self.env.reset()[None] + + +def worker(parent_connection, worker_connection, make_env_function, + send_spaces=True): + # Adapted from SubprocVecEnv github.com/openai/baselines + parent_connection.close() + env = make_env_function() + if send_spaces: + worker_connection.send((env.observation_space, env.action_space)) + while True: + cmd, action = worker_connection.recv() + if cmd == "step": + ob, rew, done, info = env.step(action) + if done: + ob = env.reset() + worker_connection.send((ob, rew, done, info)) + elif cmd == "reset": + ob = env.reset() + worker_connection.send(ob) + elif cmd == "close": + env.close() + worker_connection.close() + break + else: + raise NotImplementedError("Unknown command %s" % cmd) + + +class ParallelEnvBatch(EnvBatch): + """ + An abstract batch of environments. + """ + def __init__(self, make_env, nenvs=None): + make_env_functions = self._get_make_env_functions(make_env, nenvs) + self._nenvs = len(make_env_functions) + self._parent_connections, self._worker_connections = zip(*[ + Pipe() for _ in range(self._nenvs) + ]) + self._processes = [ + Process( + target=worker, + args=(parent_connection, worker_connection, make_env), + daemon=True + ) + for i, (parent_connection, worker_connection, make_env) + in enumerate(zip(self._parent_connections, + self._worker_connections, + make_env_functions)) + ] + for p in self._processes: + p.start() + self._closed = False + + for conn in self._worker_connections: + conn.close() + + observation_spaces, action_spaces = [], [] + for conn in self._parent_connections: + ob_space, ac_space = conn.recv() + observation_spaces.append(ob_space) + action_spaces.append(ac_space) + self.observation_space = SpaceBatch(observation_spaces) + self.action_space = SpaceBatch(action_spaces) + + @property + def nenvs(self): + return self._nenvs + + def step(self, actions): + self._check_actions(actions) + for conn, a in zip(self._parent_connections, actions): + conn.send(("step", a)) + results = [conn.recv() for conn in self._parent_connections] + obs, rews, dones, infos = zip(*results) + return np.stack(obs), np.stack(rews), np.stack(dones), infos + + def reset(self): + for conn in self._parent_connections: + conn.send(("reset", None)) + return np.stack([conn.recv() for conn in self._parent_connections]) + + def close(self): + if self._closed: + return + for conn in self._parent_connections: + conn.send(("close", None)) + for p in self._processes: + p.join() + self._closed = True + + def render(self): + raise ValueError("render not defined for %s" % self) diff --git a/week6_policy_based/reinforce_lasagne.ipynb b/week06_policy_based/reinforce_lasagne.ipynb similarity index 100% rename from week6_policy_based/reinforce_lasagne.ipynb rename to week06_policy_based/reinforce_lasagne.ipynb diff --git a/week06_policy_based/reinforce_pytorch.ipynb b/week06_policy_based/reinforce_pytorch.ipynb new file mode 100644 index 000000000..90a409d67 --- /dev/null +++ b/week06_policy_based/reinforce_pytorch.ipynb @@ -0,0 +1,426 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# REINFORCE in pytorch\n", + "\n", + "Just like we did before for q-learning, this time we'll design a pytorch network to learn `CartPole-v0` via policy gradient (REINFORCE).\n", + "\n", + "Most of the code in this notebook is taken from approximate qlearning, so you'll find it more or less familiar and even simpler." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# # in google colab uncomment this\n", + "\n", + "# import os\n", + "\n", + "# os.system('apt-get install -y xvfb')\n", + "# os.system('wget https://raw.githubusercontent.com/yandexdataschool/Practical_DL/fall18/xvfb -O ../xvfb')\n", + "# os.system('apt-get install -y python-opengl ffmpeg')\n", + "# os.system('pip install pyglet==1.2.4')\n", + "\n", + "# os.system('python -m pip install -U pygame --user')\n", + "\n", + "# print('setup complete')\n", + "\n", + "# XVFB will be launched if you run on a server\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import gym\n", + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "env = gym.make(\"CartPole-v0\").env\n", + "env.reset()\n", + "\n", + "plt.imshow(env.render(\"rgb_array\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Building the network for REINFORCE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For REINFORCE algorithm, we'll need a model that predicts action probabilities given states. Let's define such a model below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Build a simple neural network that predicts policy logits. \n", + "# Keep it simple: CartPole isn't worth deep architectures.\n", + "model = nn.Sequential(\n", + " < YOUR CODE HERE: define a neural network that predicts policy logits >\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Predict function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def predict_probs(states):\n", + " \"\"\" \n", + " Predict action probabilities given states.\n", + " :param states: numpy array of shape [batch, state_shape]\n", + " :returns: numpy array of shape [batch, n_actions]\n", + " \"\"\"\n", + " # convert states, compute logits, use softmax to get probability\n", + " \n", + " return < your code >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test_states = np.array([env.reset() for _ in range(5)])\n", + "test_probas = predict_probs(test_states)\n", + "assert isinstance(\n", + " test_probas, np.ndarray), \"you must return np array and not %s\" % type(test_probas)\n", + "assert tuple(test_probas.shape) == (\n", + " test_states.shape[0], env.action_space.n), \"wrong output shape: %s\" % np.shape(test_probas)\n", + "assert np.allclose(np.sum(test_probas, axis=1),\n", + " 1), \"probabilities do not sum to 1\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Play the game\n", + "\n", + "We can now use our newly built agent to play the game." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def generate_session(t_max=1000):\n", + " \"\"\" \n", + " play a full session with REINFORCE agent and train at the session end.\n", + " returns sequences of states, actions andrewards\n", + " \"\"\"\n", + " # arrays to record session\n", + " states, actions, rewards = [], [], []\n", + " s = env.reset()\n", + "\n", + " for t in range(t_max):\n", + " # action probabilities array aka pi(a|s)\n", + " action_probs = predict_probs(np.array([s]))[0]\n", + "\n", + " # Sample action with given probabilities.\n", + " a = < your code >\n", + " new_s, r, done, info = env.step(a)\n", + "\n", + " # record session history to train later\n", + " states.append(s)\n", + " actions.append(a)\n", + " rewards.append(r)\n", + "\n", + " s = new_s\n", + " if done:\n", + " break\n", + "\n", + " return states, actions, rewards" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# test it\n", + "states, actions, rewards = generate_session()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Computing cumulative rewards" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_cumulative_rewards(rewards, # rewards at each step\n", + " gamma=0.99 # discount for reward\n", + " ):\n", + " \"\"\"\n", + " take a list of immediate rewards r(s,a) for the whole session \n", + " compute cumulative returns (a.k.a. G(s,a) in Sutton '16)\n", + " G_t = r_t + gamma*r_{t+1} + gamma^2*r_{t+2} + ...\n", + "\n", + " The simple way to compute cumulative rewards is to iterate from last to first time tick\n", + " and compute G_t = r_t + gamma*G_{t+1} recurrently\n", + "\n", + " You must return an array/list of cumulative rewards with as many elements as in the initial rewards.\n", + " \"\"\"\n", + " \n", + " return < array of cumulative rewards >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "get_cumulative_rewards(rewards)\n", + "assert len(get_cumulative_rewards(list(range(100)))) == 100\n", + "assert np.allclose(get_cumulative_rewards([0, 0, 1, 0, 0, 1, 0], gamma=0.9), [\n", + " 1.40049, 1.5561, 1.729, 0.81, 0.9, 1.0, 0.0])\n", + "assert np.allclose(get_cumulative_rewards(\n", + " [0, 0, 1, -2, 3, -4, 0], gamma=0.5), [0.0625, 0.125, 0.25, -1.5, 1.0, -4.0, 0.0])\n", + "assert np.allclose(get_cumulative_rewards(\n", + " [0, 0, 1, 2, 3, 4, 0], gamma=0), [0, 0, 1, 2, 3, 4, 0])\n", + "print(\"looks good!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Loss function and updates\n", + "\n", + "We now need to define objective and update over policy gradient.\n", + "\n", + "Our objective function is\n", + "\n", + "$$ J \\approx { 1 \\over N } \\sum _{s_i,a_i} \\pi_\\theta (a_i | s_i) \\cdot G(s_i,a_i) $$\n", + "\n", + "\n", + "Following the REINFORCE algorithm, we can define our objective as follows: \n", + "\n", + "$$ \\hat J \\approx { 1 \\over N } \\sum _{s_i,a_i} log \\pi_\\theta (a_i | s_i) \\cdot G(s_i,a_i) $$\n", + "\n", + "When you compute gradient of that function over network weights $ \\theta $, it will become exactly the policy gradient.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def to_one_hot(y_tensor, ndims):\n", + " \"\"\" helper: take an integer vector and convert it to 1-hot matrix. \"\"\"\n", + " y_tensor = y_tensor.type(torch.LongTensor).view(-1, 1)\n", + " y_one_hot = torch.zeros(\n", + " y_tensor.size()[0], ndims).scatter_(1, y_tensor, 1)\n", + " return y_one_hot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Your code: define optimizers\n", + "optimizer = torch.optim.Adam(model.parameters(), 1e-3)\n", + "\n", + "\n", + "def train_on_session(states, actions, rewards, gamma=0.99, entropy_coef=1e-2):\n", + " \"\"\"\n", + " Takes a sequence of states, actions and rewards produced by generate_session.\n", + " Updates agent's weights by following the policy gradient above.\n", + " Please use Adam optimizer with default parameters.\n", + " \"\"\"\n", + "\n", + " # cast everything into torch tensors\n", + " states = torch.tensor(states, dtype=torch.float32)\n", + " actions = torch.tensor(actions, dtype=torch.int32)\n", + " cumulative_returns = np.array(get_cumulative_rewards(rewards, gamma))\n", + " cumulative_returns = torch.tensor(cumulative_returns, dtype=torch.float32)\n", + "\n", + " # predict logits, probas and log-probas using an agent.\n", + " logits = model(states)\n", + " probs = nn.functional.softmax(logits, -1)\n", + " log_probs = nn.functional.log_softmax(logits, -1)\n", + "\n", + " assert all(isinstance(v, torch.Tensor) for v in [logits, probs, log_probs]), \\\n", + " \"please use compute using torch tensors and don't use predict_probs function\"\n", + "\n", + " # select log-probabilities for chosen actions, log pi(a_i|s_i)\n", + " log_probs_for_actions = torch.sum(\n", + " log_probs * to_one_hot(actions, env.action_space.n), dim=1)\n", + " \n", + " # Compute loss here. Don't forgen entropy regularization with `entropy_coef` \n", + " entropy = < your code >\n", + " loss = < your code\n", + "\n", + " # Gradient descent step\n", + " < your code >\n", + "\n", + " # technical: return session rewards to print them later\n", + " return np.sum(rewards)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The actual training" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "for i in range(100):\n", + " rewards = [train_on_session(*generate_session())\n", + " for _ in range(100)] # generate new sessions\n", + " print(\"mean reward:%.3f\" % (np.mean(rewards)))\n", + " if np.mean(rewards) > 500:\n", + " print(\"You Win!\") # but you can train even further\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Video" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# record sessions\n", + "import gym.wrappers\n", + "env = gym.wrappers.Monitor(gym.make(\"CartPole-v0\"),\n", + " directory=\"videos\", force=True)\n", + "sessions = [generate_session() for _ in range(100)]\n", + "env.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# show video\n", + "from IPython.display import HTML\n", + "import os\n", + "\n", + "video_names = list(\n", + " filter(lambda s: s.endswith(\".mp4\"), os.listdir(\"./videos/\")))\n", + "\n", + "HTML(\"\"\"\n", + "\n", + "\"\"\".format(\"./videos/\"+video_names[-1])) # this may or may not be the _last_ video. Try other indices" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.6.0" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/week06_policy_based/reinforce_tensorflow.ipynb b/week06_policy_based/reinforce_tensorflow.ipynb new file mode 100644 index 000000000..39c21b74f --- /dev/null +++ b/week06_policy_based/reinforce_tensorflow.ipynb @@ -0,0 +1,387 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# REINFORCE in TensorFlow (3 pts)¶\n", + "\n", + "This notebook implements a basic reinforce algorithm a.k.a. policy gradient for CartPole env.\n", + "\n", + "It has been deliberately written to be as simple and human-readable.\n", + "\n", + "Authors: [Practical_RL](https://github.com/yandexdataschool/Practical_RL) course team" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%env THEANO_FLAGS = 'floatX=float32'\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The notebook assumes that you have [openai gym](https://github.com/openai/gym) installed.\n", + "\n", + "In case you're running on a server, [use xvfb](https://github.com/openai/gym#rendering-on-a-server)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import gym\n", + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "env = gym.make(\"CartPole-v0\")\n", + "\n", + "# gym compatibility: unwrap TimeLimit\n", + "if hasattr(env, 'env'):\n", + " env = env.env\n", + "\n", + "env.reset()\n", + "n_actions = env.action_space.n\n", + "state_dim = env.observation_space.shape\n", + "\n", + "plt.imshow(env.render(\"rgb_array\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Building the network for REINFORCE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For REINFORCE algorithm, we'll need a model that predicts action probabilities given states.\n", + "\n", + "For numerical stability, please __do not include the softmax layer into your network architecture__.\n", + "We'll use softmax or log-softmax where appropriate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import tensorflow as tf\n", + "\n", + "# create input variables. We only need for REINFORCE\n", + "states = tf.placeholder('float32', (None,)+state_dim, name=\"states\")\n", + "actions = tf.placeholder('int32', name=\"action_ids\")\n", + "cumulative_rewards = tf.placeholder('float32', name=\"cumulative_returns\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "\n", + "\n", + "logits = \n", + "\n", + "policy = tf.nn.softmax(logits)\n", + "log_policy = tf.nn.log_softmax(logits)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# utility function to pick action in one given state\n", + "def get_action_proba(s): return policy.eval({states: [s]})[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Loss function and updates\n", + "\n", + "We now need to define objective and update over policy gradient.\n", + "\n", + "Our objective function is\n", + "\n", + "$$ J \\approx { 1 \\over N } \\sum _{s_i,a_i} \\pi_\\theta (a_i | s_i) \\cdot G(s_i,a_i) $$\n", + "\n", + "\n", + "Following the REINFORCE algorithm, we can define our objective as follows: \n", + "\n", + "$$ \\hat J \\approx { 1 \\over N } \\sum _{s_i,a_i} log \\pi_\\theta (a_i | s_i) \\cdot G(s_i,a_i) $$\n", + "\n", + "When you compute gradient of that function over network weights $ \\theta $, it will become exactly the policy gradient.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# get probabilities for parti\n", + "indices = tf.stack([tf.range(tf.shape(log_policy)[0]), actions], axis=-1)\n", + "log_policy_for_actions = tf.gather_nd(log_policy, indices)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# policy objective as in the last formula. please use mean, not sum.\n", + "# note: you need to use log_policy_for_actions to get log probabilities for actions taken.\n", + "\n", + "J = " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# regularize with entropy\n", + "entropy = " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# all network weights\n", + "all_weights = \n", + "\n", + "# weight updates. maximizing J is same as minimizing -J. Adding negative entropy.\n", + "loss = -J - 0.1*entropy\n", + "\n", + "update = tf.train.AdamOptimizer().minimize(loss, var_list=all_weights)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Computing cumulative rewards" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "def get_cumulative_rewards(rewards, # rewards at each step\n", + " gamma=0.99 # discount for reward\n", + " ):\n", + " \"\"\"\n", + " take a list of immediate rewards r(s,a) for the whole session \n", + " compute cumulative rewards R(s,a) (a.k.a. G(s,a) in Sutton '16)\n", + " R_t = r_t + gamma*r_{t+1} + gamma^2*r_{t+2} + ...\n", + "\n", + " The simple way to compute cumulative rewards is to iterate from last to first time tick\n", + " and compute R_t = r_t + gamma*R_{t+1} recurrently\n", + "\n", + " You must return an array/list of cumulative rewards with as many elements as in the initial rewards.\n", + " \"\"\"\n", + "\n", + " \n", + "\n", + " return < array of cumulative rewards >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert len(get_cumulative_rewards(range(100))) == 100\n", + "assert np.allclose(get_cumulative_rewards([0, 0, 1, 0, 0, 1, 0], gamma=0.9), [\n", + " 1.40049, 1.5561, 1.729, 0.81, 0.9, 1.0, 0.0])\n", + "assert np.allclose(get_cumulative_rewards(\n", + " [0, 0, 1, -2, 3, -4, 0], gamma=0.5), [0.0625, 0.125, 0.25, -1.5, 1.0, -4.0, 0.0])\n", + "assert np.allclose(get_cumulative_rewards(\n", + " [0, 0, 1, 2, 3, 4, 0], gamma=0), [0, 0, 1, 2, 3, 4, 0])\n", + "print(\"looks good!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def train_step(_states, _actions, _rewards):\n", + " \"\"\"given full session, trains agent with policy gradient\"\"\"\n", + " _cumulative_rewards = get_cumulative_rewards(_rewards)\n", + " update.run({states: _states, actions: _actions,\n", + " cumulative_rewards: _cumulative_rewards})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Playing the game" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def generate_session(t_max=1000):\n", + " \"\"\"play env with REINFORCE agent and train at the session end\"\"\"\n", + "\n", + " # arrays to record session\n", + " states, actions, rewards = [], [], []\n", + "\n", + " s = env.reset()\n", + "\n", + " for t in range(t_max):\n", + "\n", + " # action probabilities array aka pi(a|s)\n", + " action_probas = get_action_proba(s)\n", + "\n", + " a = \n", + "\n", + " new_s, r, done, info = env.step(a)\n", + "\n", + " # record session history to train later\n", + " states.append(s)\n", + " actions.append(a)\n", + " rewards.append(r)\n", + "\n", + " s = new_s\n", + " if done:\n", + " break\n", + "\n", + " train_step(states, actions, rewards)\n", + "\n", + " return sum(rewards)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s = tf.InteractiveSession()\n", + "s.run(tf.global_variables_initializer())\n", + "\n", + "for i in range(100):\n", + "\n", + " rewards = [generate_session() for _ in range(100)] # generate new sessions\n", + "\n", + " print(\"mean reward:%.3f\" % (np.mean(rewards)))\n", + "\n", + " if np.mean(rewards) > 300:\n", + " print(\"You Win!\")\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Results & video" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# record sessions\n", + "import gym.wrappers\n", + "env = gym.wrappers.Monitor(gym.make(\"CartPole-v0\"),\n", + " directory=\"videos\", force=True)\n", + "sessions = [generate_session() for _ in range(100)]\n", + "env.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# show video\n", + "from IPython.display import HTML\n", + "import os\n", + "\n", + "video_names = list(\n", + " filter(lambda s: s.endswith(\".mp4\"), os.listdir(\"./videos/\")))\n", + "\n", + "HTML(\"\"\"\n", + "\n", + "\"\"\".format(\"./videos/\"+video_names[-1])) # this may or may not be _last_ video. Try other indices" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# That's all, thank you for your attention!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/week06_policy_based/runners.py b/week06_policy_based/runners.py new file mode 100644 index 000000000..83394afb2 --- /dev/null +++ b/week06_policy_based/runners.py @@ -0,0 +1,60 @@ +""" RL env runner """ +from collections import defaultdict +import numpy as np + + +class EnvRunner: + """ Reinforcement learning runner in an environment with given policy """ + def __init__(self, env, policy, nsteps, + transforms=None, step_var=None): + self.env = env + self.policy = policy + self.nsteps = nsteps + self.transforms = transforms or [] + self.step_var = step_var if step_var is not None else 0 + self.state = {"latest_observation": self.env.reset()} + + @property + def nenvs(self): + """ Returns number of batched envs or `None` if env is not batched """ + return getattr(self.env.unwrapped, "nenvs", None) + + def reset(self): + """ Resets env and runner states. """ + self.state["latest_observation"] = self.env.reset() + self.policy.reset() + + def get_next(self): + """ Runs the agent in the environment. """ + trajectory = defaultdict(list, {"actions": []}) + observations = [] + rewards = [] + resets = [] + self.state["env_steps"] = self.nsteps + + for i in range(self.nsteps): + observations.append(self.state["latest_observation"]) + act = self.policy.act(self.state["latest_observation"]) + if "actions" not in act: + raise ValueError("result of policy.act must contain 'actions' " + f"but has keys {list(act.keys())}") + for key, val in act.items(): + trajectory[key].append(val) + + obs, rew, done, _ = self.env.step(trajectory["actions"][-1]) + self.state["latest_observation"] = obs + rewards.append(rew) + resets.append(done) + self.step_var += self.nenvs or 1 + + # Only reset if the env is not batched. Batched envs should auto-reset. + if not self.nenvs and np.all(done): + self.state["env_steps"] = i + 1 + self.state["latest_observation"] = self.env.reset() + + trajectory.update(observations=observations, rewards=rewards, resets=resets) + trajectory["state"] = self.state + + for transform in self.transforms: + transform(trajectory) + return trajectory diff --git a/week7_[recap]_rnn/README.md b/week07_[recap]_rnn/README.md similarity index 88% rename from week7_[recap]_rnn/README.md rename to week07_[recap]_rnn/README.md index 99f24b360..61f8428cf 100644 --- a/week7_[recap]_rnn/README.md +++ b/week07_[recap]_rnn/README.md @@ -13,6 +13,7 @@ * OpenAI research on sentiment analysis that sheds some light on what's inside LSTM language model. # Homework description +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week07_%5Brecap%5D_rnn/seminar_pytorch.ipynb) This week's practice gets you acquainted with basics of recurrent neural networks. For simplicity, we'll train them on character language modelling task. Pick any one of `seminar_lasagne`, `seminar_lasagne_ingraph` or `seminar_tf`. diff --git a/week7_[recap]_rnn/arxiv_data.csv b/week07_[recap]_rnn/arxiv_data.csv similarity index 100% rename from week7_[recap]_rnn/arxiv_data.csv rename to week07_[recap]_rnn/arxiv_data.csv diff --git a/week7_[recap]_rnn/mtg_card_names.txt b/week07_[recap]_rnn/mtg_card_names.txt similarity index 100% rename from week7_[recap]_rnn/mtg_card_names.txt rename to week07_[recap]_rnn/mtg_card_names.txt diff --git a/week7_[recap]_rnn/names b/week07_[recap]_rnn/names similarity index 100% rename from week7_[recap]_rnn/names rename to week07_[recap]_rnn/names diff --git a/week7_[recap]_rnn/rnn.png b/week07_[recap]_rnn/rnn.png similarity index 100% rename from week7_[recap]_rnn/rnn.png rename to week07_[recap]_rnn/rnn.png diff --git a/week7_[recap]_rnn/seminar_lasagne.ipynb b/week07_[recap]_rnn/seminar_lasagne.ipynb similarity index 100% rename from week7_[recap]_rnn/seminar_lasagne.ipynb rename to week07_[recap]_rnn/seminar_lasagne.ipynb diff --git a/week7_[recap]_rnn/seminar_lasagne_ingraph.ipynb b/week07_[recap]_rnn/seminar_lasagne_ingraph.ipynb similarity index 100% rename from week7_[recap]_rnn/seminar_lasagne_ingraph.ipynb rename to week07_[recap]_rnn/seminar_lasagne_ingraph.ipynb diff --git a/week07_[recap]_rnn/seminar_pytorch.ipynb b/week07_[recap]_rnn/seminar_pytorch.ipynb new file mode 100644 index 000000000..93a6b86c9 --- /dev/null +++ b/week07_[recap]_rnn/seminar_pytorch.ipynb @@ -0,0 +1,765 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "seminar_pytorch.ipynb", + "version": "0.3.2", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + } + }, + "cells": [ + { + "metadata": { + "id": "BBnWIbH4xNou", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "### Generating names with recurrent neural networks\n", + "\n", + "This time you'll find yourself delving into the heart (and other intestines) of recurrent neural networks on a class of toy problems.\n", + "\n", + "Struggle to find a name for the variable? Let's see how you'll come up with a name for your son/daughter. Surely no human has expertize over what is a good child name, so let us train RNN instead;\n", + "\n", + "It's dangerous to go alone, take these:" + ] + }, + { + "metadata": { + "id": "LtOprx8exNo4", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "# if you're in in colab, uncomment\n", + "# !wget https://raw.githubusercontent.com/yandexdataschool/Practical_RL/99ae2a3dae648428edbfc41fd10ed688e5365161/week07_%5Brecap%5D_rnn/names -O names" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "dO7UJFP-xNpM", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "# Our data\n", + "The dataset contains ~8k earthling names from different cultures, all in latin transcript.\n", + "\n", + "This notebook has been designed so as to allow you to quickly swap names for something similar: deep learning article titles, IKEA furniture, pokemon names, etc." + ] + }, + { + "metadata": { + "id": "OkxTfJKBxNpT", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "import os\n", + "start_token = \" \"\n", + "\n", + "with open(\"names\") as f:\n", + " lines = f.read()[:-1].split('\\n')\n", + " lines = [start_token + line for line in lines]" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "CSUSTDKWxNph", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "print ('n samples = ',len(lines))\n", + "for x in lines[::1000]:\n", + " print (x)\n", + " \n" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "n-ztLh0wxNp2", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "MAX_LENGTH = max(map(len, lines))\n", + "print(\"max length =\", MAX_LENGTH)\n", + "\n", + "plt.title('Sequence length distribution')\n", + "plt.hist(list(map(len, lines)),bins=25);" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "-Ab7zEqJxNp-", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "# Text processing\n", + "\n", + "First we need next to collect a \"vocabulary\" of all unique tokens i.e. unique characters. We can then encode inputs as a sequence of character ids." + ] + }, + { + "metadata": { + "id": "RpppLCJ8xNqC", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "#all unique characters go here\n", + "tokens = \n", + "\n", + "tokens = list(tokens)\n", + "\n", + "num_tokens = len(tokens)\n", + "print ('num_tokens = ', num_tokens)\n", + "\n", + "assert 50 < num_tokens < 60, \"Names should contain within 50 and 60 unique tokens depending on encoding\"" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "3rObMYUnxNqL", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "### Convert characters to integers\n", + "\n", + "Torch is built for crunching numbers, not strings. \n", + "To train our neural network, we'll need to replace characters with their indices in tokens list.\n", + "\n", + "Let's compose a dictionary that does this mapping." + ] + }, + { + "metadata": { + "id": "HFUPS7SZxNqP", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "token_to_id = its identifier (index in tokens list)>" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "wBD3JmwLxNqd", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "assert len(tokens) == len(token_to_id), \"dictionaries must have same size\"\n", + "\n", + "for i in range(num_tokens):\n", + " assert token_to_id[tokens[i]] == i, \"token identifier must be it's position in tokens list\"\n", + "\n", + "print(\"Seems alright!\")" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "AATQIEw-xNqw", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "def to_matrix(lines, max_len=None, pad=token_to_id[' '], dtype='int32', batch_first = True):\n", + " \"\"\"Casts a list of names into rnn-digestable matrix\"\"\"\n", + " \n", + " max_len = max_len or max(map(len, lines))\n", + " lines_ix = np.zeros([len(lines), max_len], dtype) + pad\n", + "\n", + " for i in range(len(lines)):\n", + " line_ix = [token_to_id[c] for c in lines[i]]\n", + " lines_ix[i, :len(line_ix)] = line_ix\n", + " \n", + " if not batch_first: # convert [batch, time] into [time, batch]\n", + " lines_ix = np.transpose(lines_ix)\n", + "\n", + " return lines_ix" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "e6LjFvFYxNrA", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "#Example: cast 4 random names to matrices, pad with zeros\n", + "print('\\n'.join(lines[::2000]))\n", + "print(to_matrix(lines[::2000]))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "eRsgx9TXxNrJ", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "# Recurrent neural network\n", + "\n", + "We can rewrite recurrent neural network as a consecutive application of dense layer to input $x_t$ and previous rnn state $h_t$. This is exactly what we're gonna do now.\n", + "\n", + "\n", + "Since we're training a language model, there should also be:\n", + "* An embedding layer that converts character id x_t to a vector.\n", + "* An output layer that predicts probabilities of next phoneme" + ] + }, + { + "metadata": { + "id": "LyPI4I1exNrO", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "import torch, torch.nn as nn\n", + "import torch.nn.functional as F\n", + "\n", + "class CharRNNCell(nn.Module):\n", + " \"\"\"\n", + " Implement the scheme above as torch module\n", + " \"\"\"\n", + " def __init__(self, num_tokens=len(tokens), embedding_size=16, rnn_num_units=64):\n", + " super(self.__class__,self).__init__()\n", + " self.num_units = rnn_num_units\n", + " \n", + " self.embedding = nn.Embedding(num_tokens, embedding_size)\n", + " self.rnn_update = nn.Linear(embedding_size + rnn_num_units, rnn_num_units)\n", + " self.rnn_to_logits = nn.Linear(rnn_num_units, num_tokens)\n", + " \n", + " def forward(self, x, h_prev):\n", + " \"\"\"\n", + " This method computes h_next(x, h_prev) and log P(x_next | h_next)\n", + " We'll call it repeatedly to produce the whole sequence.\n", + " \n", + " :param x: batch of character ids, int64[batch_size]\n", + " :param h_prev: previous rnn hidden states, float32 matrix [batch, rnn_num_units]\n", + " \"\"\"\n", + " # get vector embedding of x\n", + " x_emb = self.embedding(x)\n", + " \n", + " # compute next hidden state using self.rnn_update\n", + " # hint: use torch.cat(..., dim=...) for concatenation\n", + " h_next = ###YOUR CODE HERE\n", + " \n", + " h_next = torch.tanh(h_next)\n", + " \n", + " assert h_next.size() == h_prev.size()\n", + " \n", + " #compute logits for next character probs\n", + " logits = ###YOUR CODE\n", + " \n", + " return h_next, F.log_softmax(logits, -1)\n", + " \n", + " def initial_state(self, batch_size):\n", + " \"\"\" return rnn state before it processes first input (aka h0) \"\"\"\n", + " return torch.zeros(batch_size, self.num_units)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "TFhNjoG5xNrV", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "char_rnn = CharRNNCell()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "DRG9DwU4xNrk", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "### RNN loop\n", + "\n", + "Once we've defined a single RNN step, we can apply it in a loop to get predictions on each step." + ] + }, + { + "metadata": { + "id": "1wZV18EMxNro", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "def rnn_loop(char_rnn, batch_ix):\n", + " \"\"\"\n", + " Computes log P(next_character) for all time-steps in lines_ix\n", + " :param lines_ix: an int32 matrix of shape [batch, time], output of to_matrix(lines)\n", + " \"\"\"\n", + " batch_size, max_length = batch_ix.size()\n", + " hid_state = char_rnn.initial_state(batch_size)\n", + " logprobs = []\n", + "\n", + " for x_t in batch_ix.transpose(0,1):\n", + " hid_state, logp_next = char_rnn(x_t, hid_state) # <-- here we call your one-step code\n", + " logprobs.append(logp_next)\n", + " \n", + " return torch.stack(logprobs, dim=1)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "cyeW8FGTxNry", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "batch_ix = to_matrix(lines[:5])\n", + "batch_ix = torch.tensor(batch_ix, dtype=torch.int64)\n", + "\n", + "logp_seq = rnn_loop(char_rnn, batch_ix)\n", + "\n", + "assert torch.max(logp_seq).data.numpy() <= 0\n", + "assert tuple(logp_seq.size()) == batch_ix.shape + (num_tokens,)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "s4-dWabbxNr6", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "### Likelihood and gradients\n", + "\n", + "We can now train our neural network to minimize crossentropy (maximize log-likelihood) with the actual next tokens.\n", + "\n", + "To do so in a vectorized manner, we take `batch_ix[:, 1:]` - a matrix of token ids shifted i step to the left so i-th element is acutally the \"next token\" for i-th prediction" + ] + }, + { + "metadata": { + "id": "GLHf-Aq8xNr9", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "predictions_logp = logp_seq[:, :-1]\n", + "actual_next_tokens = batch_ix[:, 1:]\n", + "\n", + "logp_next = torch.gather(predictions_logp, dim=2, index=actual_next_tokens[:,:,None])\n", + "\n", + "loss = -logp_next.mean()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "UaCMB9JPxNsK", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "loss.backward()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "4vQT0t-qxNsa", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "for w in char_rnn.parameters():\n", + " assert w.grad is not None and torch.max(torch.abs(w.grad)).data.numpy() != 0, \\\n", + " \"Loss is not differentiable w.r.t. a weight with shape %s. Check forward method.\" % (w.size(),)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "_Gxi9BhJxNsn", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "### The training loop\n", + "\n", + "We train our char-rnn exactly the same way we train any deep learning model: by minibatch sgd.\n", + "\n", + "The only difference is that this time we sample strings, not images or sound." + ] + }, + { + "metadata": { + "id": "kjcJR_1cxNsq", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "from IPython.display import clear_output\n", + "from random import sample\n", + "\n", + "char_rnn = CharRNNCell()\n", + "opt = torch.optim.Adam(char_rnn.parameters())\n", + "history = []" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "6STFsVgsxNsy", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "\n", + "for i in range(1000):\n", + " batch_ix = to_matrix(sample(lines, 32), max_len=MAX_LENGTH)\n", + " batch_ix = torch.tensor(batch_ix, dtype=torch.int64)\n", + " \n", + " logp_seq = rnn_loop(char_rnn, batch_ix)\n", + " \n", + " # compute loss\n", + " \n", + " \n", + " loss = ###YOUR CODE\n", + " \n", + " # train with backprop\n", + " \n", + " \n", + " history.append(loss.data.numpy())\n", + " if (i+1)%100==0:\n", + " clear_output(True)\n", + " plt.plot(history,label='loss')\n", + " plt.legend()\n", + " plt.show()\n", + "\n", + "assert np.mean(history[:10]) > np.mean(history[-10:]), \"RNN didn't converge.\"" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "Kae9COy8xNtB", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "### RNN: sampling\n", + "Once we've trained our network a bit, let's get to actually generating stuff. \n", + "All we need is the single rnn step function you have defined in `char_rnn.forward`." + ] + }, + { + "metadata": { + "id": "piuEtqYexNtG", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "def generate_sample(char_rnn, seed_phrase=' ', max_length=MAX_LENGTH, temperature=1.0):\n", + " '''\n", + " The function generates text given a phrase of length at least SEQ_LENGTH.\n", + " :param seed_phrase: prefix characters. The RNN is asked to continue the phrase\n", + " :param max_length: maximum output length, including seed_phrase\n", + " :param temperature: coefficient for sampling. higher temperature produces more chaotic outputs,\n", + " smaller temperature converges to the single most likely output\n", + " '''\n", + " \n", + " x_sequence = [token_to_id[token] for token in seed_phrase]\n", + " x_sequence = torch.tensor([x_sequence], dtype=torch.int64)\n", + " hid_state = char_rnn.initial_state(batch_size=1)\n", + " \n", + " #feed the seed phrase, if any\n", + " for i in range(len(seed_phrase) - 1):\n", + " hid_state, _ = char_rnn(x_sequence[:, i], hid_state)\n", + " \n", + " #start generating\n", + " for _ in range(max_length - len(seed_phrase)):\n", + " hid_state, logp_next = char_rnn(x_sequence[:, -1], hid_state)\n", + " p_next = F.softmax(logp_next / temperature, dim=-1).data.numpy()[0]\n", + " \n", + " # sample next token and push it back into x_sequence\n", + " next_ix = np.random.choice(num_tokens,p=p_next)\n", + " next_ix = torch.tensor([[next_ix]], dtype=torch.int64)\n", + " x_sequence = torch.cat([x_sequence, next_ix], dim=1)\n", + " \n", + " return ''.join([tokens[ix] for ix in x_sequence.data.numpy()[0]])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "wLx2EBLJxNtY", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "for _ in range(10):\n", + " print(generate_sample(char_rnn))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "QzbIA1WPxNtg", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "for _ in range(50):\n", + " print(generate_sample(char_rnn, seed_phrase=' Trump'))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "QwqUmy_pxNtp", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "### Try it out!\n", + "You've just implemented a recurrent language model that can be tasked with generating any kind of sequence, so there's plenty of data you can try it on:\n", + "\n", + "* Novels/poems/songs of your favorite author\n", + "* News titles/clickbait titles\n", + "* Source code of Linux or Tensorflow\n", + "* Molecules in [smiles](https://en.wikipedia.org/wiki/Simplified_molecular-input_line-entry_system) format\n", + "* Melody in notes/chords format\n", + "* Ikea catalog titles\n", + "* Pokemon names\n", + "* Cards from Magic, the Gathering / Hearthstone\n", + "\n", + "If you're willing to give it a try, here's what you wanna look at:\n", + "* Current data format is a sequence of lines, so a novel can be formatted as a list of sentences. Alternatively, you can change data preprocessing altogether.\n", + "* While some datasets are readily available, others can only be scraped from the web. Try `Selenium` or `Scrapy` for that.\n", + "* Make sure MAX_LENGTH is adjusted for longer datasets. There's also a bonus section about dynamic RNNs at the bottom.\n", + "* More complex tasks require larger RNN architecture, try more neurons or several layers. It would also require more training iterations.\n", + "* Long-term dependencies in music, novels or molecules are better handled with LSTM or GRU\n", + "\n", + "__Good hunting!__" + ] + }, + { + "metadata": { + "collapsed": true, + "id": "BgbahGE0xNtw", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "### More seriously\n", + "\n", + "What we just did is a manual low-level implementation of RNN. While it's cool, i guess you won't like the idea of re-writing it from scratch on every occasion. \n", + "\n", + "As you might have guessed, torch has a solution for this. To be more specific, there are two options:\n", + "* `nn.RNNCell(emb_size, rnn_num_units)` - implements a single step of RNN just like you did. Basically concat-linear-tanh\n", + "* `nn.RNN(emb_size, rnn_num_units` - implements the whole rnn_loop for you.\n", + "\n", + "There's also `nn.LSTMCell` vs `nn.LSTM`, `nn.GRUCell` vs `nn.GRU`, etc. etc.\n", + "\n", + "In this example we'll rewrite the char_rnn and rnn_loop using high-level rnn API." + ] + }, + { + "metadata": { + "id": "7uewn3r3xNtz", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "class CharRNNLoop(nn.Module):\n", + " def __init__(self, num_tokens=num_tokens, emb_size=16, rnn_num_units=64):\n", + " super(self.__class__, self).__init__()\n", + " self.emb = nn.Embedding(num_tokens, emb_size)\n", + " self.rnn = nn.RNN(emb_size, rnn_num_units, batch_first=True)\n", + " self.hid_to_logits = nn.Linear(rnn_num_units, num_tokens)\n", + " \n", + " def forward(self, x):\n", + " h_seq, _ = self.rnn(self.emb(x))\n", + " next_logits = self.hid_to_logits(h_seq)\n", + " next_logp = F.log_softmax(next_logits, dim=-1)\n", + " return next_logp\n", + " \n", + "model = CharRNNLoop()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "ls-H-9u6xNt9", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "# the model applies over the whole sequence\n", + "batch_ix = to_matrix(sample(lines, 32), max_len=MAX_LENGTH)\n", + "batch_ix = torch.tensor(batch_ix, dtype=torch.int64)\n", + "\n", + "logp_seq = model(batch_ix)\n", + "\n", + "# compute loss. This time we use nll_loss with some duct tape\n", + "loss = F.nll_loss(logp_seq[:, 1:].contiguous().view(-1, num_tokens), \n", + " batch_ix[:, :-1].contiguous().view(-1))\n", + "\n", + "loss.backward()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "collapsed": true, + "id": "oG-50QGhxNuG", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "Here's another example" + ] + }, + { + "metadata": { + "id": "5LTWutHKxNuL", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "import torch, torch.nn as nn\n", + "import torch.nn.functional as F\n", + "\n", + "class CharLSTMCell(nn.Module):\n", + " \"\"\"\n", + " Implements something like CharRNNCell, but with LSTM\n", + " \"\"\"\n", + " def __init__(self, num_tokens=len(tokens), embedding_size=16, rnn_num_units=64):\n", + " super(self.__class__,self).__init__()\n", + " self.num_units = rnn_num_units\n", + " self.emb = nn.Embedding(num_tokens, embedding_size)\n", + " self.lstm = nn.LSTMCell(embedding_size, rnn_num_units)\n", + " self.rnn_to_logits = nn.Linear(rnn_num_units, num_tokens)\n", + " \n", + " def forward(self, x, prev_state):\n", + " (prev_h, prev_c) = prev_state\n", + " (next_h, next_c) = self.lstm(self.emb(x), (prev_h, prev_c))\n", + " logits = self.rnn_to_logits(next_h)\n", + " \n", + " return (next_h, next_c), F.log_softmax(logits, -1)\n", + " \n", + " def initial_state(self, batch_size):\n", + " \"\"\" LSTM has two state variables, cell and hid \"\"\"\n", + " return torch.zeros(batch_size, self.num_units), torch.zeros(batch_size, self.num_units)\n", + " \n", + "char_lstm = CharLSTMCell()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "KR02vVMDxNuS", + "colab_type": "code", + "colab": {} + }, + "cell_type": "code", + "source": [ + "# the model applies over the whole sequence\n", + "batch_ix = to_matrix(sample(lines, 32), max_len=MAX_LENGTH)\n", + "batch_ix = torh.tensor(batch_ix, dtype=torch.int64)\n", + "\n", + "logp_seq = rnn_loop(char_lstm, batch_ix)\n", + "\n", + "# compute loss. This time we use nll_loss with some duct tape\n", + "loss = F.nll_loss(logp_seq[:, 1:].contiguous().view(-1, num_tokens), \n", + " batch_ix[:, :-1].contiguous().view(-1))\n", + "\n", + "loss.backward()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "metadata": { + "id": "5eVtel6_xNuX", + "colab_type": "text" + }, + "cell_type": "markdown", + "source": [ + "__Bonus quest: __ implement a model that uses 2 LSTM layers (the second lstm uses the first as input) and train it on your data." + ] + } + ] +} diff --git a/week7_[recap]_rnn/seminar_tf.ipynb b/week07_[recap]_rnn/seminar_tf.ipynb similarity index 100% rename from week7_[recap]_rnn/seminar_tf.ipynb rename to week07_[recap]_rnn/seminar_tf.ipynb diff --git a/week8_scst/README.md b/week07_seq2seq/README.md similarity index 73% rename from week8_scst/README.md rename to week07_seq2seq/README.md index 8c2da06d1..c2fcd1340 100644 --- a/week8_scst/README.md +++ b/week07_seq2seq/README.md @@ -9,16 +9,18 @@ * Self-critical sequence traning [original article](https://arxiv.org/abs/1612.00563) ## Practice -As usual, go to practice_{your framework}.ipynb above and follow instructions from there. [pytorch](https://github.com/yandexdataschool/Practical_RL/blob/master/week8_scst/practice_torch.ipynb), [tensorflow](https://github.com/yandexdataschool/Practical_RL/blob/master/week8_scst/practice_tf.ipynb), [theano](https://github.com/yandexdataschool/Practical_RL/blob/master/week8_scst/practice_theano.ipynb) -Binder quickstart (lasts 1 hour): [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/yandexdataschool/Practical_RL/master) +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week07_seq2seq/practice_torch.ipynb) + + +As usual, go to practice_{your framework}.ipynb above and follow instructions from there. [pytorch](./practice_torch.ipynb), [tensorflow](./practice_tf.ipynb), [theano](./practice_theano.ipynb) ## More materials * An [awesome post](http://distill.pub/2016/augmented-rnns/) explaining attention and long-term memory models. * [BLEU](http://www.aclweb.org/anthology/P02-1040.pdf) and [CIDEr](https://arxiv.org/pdf/1411.5726.pdf) articles. * Image captioning * MSCOCO captioning [challenge](http://mscoco.org/dataset/#captions-challenge2015) - * Captioning baseline [notebook](https://github.com/yandexdataschool/HSE_deeplearning/blob/master/week7/captioning_solution_ars.ipynb) + * Captioning tutorial [notebook](https://github.com/yandexdataschool/Practical_DL/tree/980121c7b3147ed28a7c1360df5038d3432b8cc3/week07_seq2seq) * Other articles on reinforcement learning for natural language: * [task-oriented conversation system](https://arxiv.org/abs/1703.07055) * [generating dialogues](https://arxiv.org/abs/1606.01541) diff --git a/week8_scst/basic_model_tf.py b/week07_seq2seq/basic_model_tf.py similarity index 100% rename from week8_scst/basic_model_tf.py rename to week07_seq2seq/basic_model_tf.py diff --git a/week8_scst/basic_model_theano.py b/week07_seq2seq/basic_model_theano.py similarity index 100% rename from week8_scst/basic_model_theano.py rename to week07_seq2seq/basic_model_theano.py diff --git a/week8_scst/basic_model_torch.py b/week07_seq2seq/basic_model_torch.py similarity index 89% rename from week8_scst/basic_model_torch.py rename to week07_seq2seq/basic_model_torch.py index fc68ae971..329432bd2 100644 --- a/week8_scst/basic_model_torch.py +++ b/week07_seq2seq/basic_model_torch.py @@ -1,7 +1,6 @@ import torch import torch.nn as nn import torch.nn.functional as F -from torch.autograd import Variable # Note: unlike official pytorch tutorial, this model doesn't process one sample at a time # because it's slow on GPU. instead it uses masks just like ye olde theano/tensorflow. @@ -65,8 +64,9 @@ def forward(self, inp, out, eps=1e-30, **flags): In other words, logp are probabilities of __current__ output at each tick, not the next one therefore you can get likelihood as logprobas * tf.one_hot(out,n_tokens) """ + device = next(self.parameters()).device batch_size = inp.shape[0] - bos = Variable(torch.LongTensor([self.out_voc.bos_ix] * batch_size)) + bos = torch.tensor([self.out_voc.bos_ix] * batch_size, dtype=torch.long, device=device) logits_seq = [torch.log(to_one_hot(bos, len(self.out_voc)) + eps)] hid_state = self.encode(inp, **flags) @@ -87,9 +87,10 @@ def translate(self, inp, greedy=False, max_len = None, eps = 1e-30, **flags): :return: output tokens int32[batch,time] and log-probabilities of all tokens at each tick, [batch,time,n_tokens] """ + device = next(self.parameters()).device batch_size = inp.shape[0] - bos = Variable(torch.LongTensor([self.out_voc.bos_ix] * batch_size)) - mask = Variable(torch.ones(batch_size).type(torch.ByteTensor)) + bos = torch.tensor([self.out_voc.bos_ix] * batch_size, dtype=torch.long, device=device) + mask = torch.ones(batch_size, dtype=torch.uint8, device=device) logits_seq = [torch.log(to_one_hot(bos, len(self.out_voc)) + eps)] out_seq = [bos] @@ -115,7 +116,7 @@ def translate(self, inp, greedy=False, max_len = None, eps = 1e-30, **flags): ### Utility functions ### -def infer_mask(seq, eos_ix, batch_first=True, include_eos=True, type=torch.FloatTensor): +def infer_mask(seq, eos_ix, batch_first=True, include_eos=True, dtype=torch.float): """ compute length given output indices and eos code :param seq: tf matrix [time,batch] if batch_first else [batch,time] @@ -124,7 +125,7 @@ def infer_mask(seq, eos_ix, batch_first=True, include_eos=True, type=torch.Float :returns: lengths, int32 vector of shape [batch] """ assert seq.dim() == 2 - is_eos = (seq == eos_ix).type(torch.FloatTensor) + is_eos = (seq == eos_ix).to(dtype=torch.float) if include_eos: if batch_first: is_eos = torch.cat((is_eos[:,:1]*0, is_eos[:, :-1]), dim=1) @@ -132,9 +133,9 @@ def infer_mask(seq, eos_ix, batch_first=True, include_eos=True, type=torch.Float is_eos = torch.cat((is_eos[:1,:]*0, is_eos[:-1, :]), dim=0) count_eos = torch.cumsum(is_eos, dim=1 if batch_first else 0) mask = count_eos == 0 - return mask.type(type) + return mask.to(dtype=dtype) -def infer_length(seq, eos_ix, batch_first=True, include_eos=True, type=torch.LongTensor): +def infer_length(seq, eos_ix, batch_first=True, include_eos=True, dtype=torch.long): """ compute mask given output indices and eos code :param seq: tf matrix [time,batch] if time_major else [batch,time] @@ -142,16 +143,15 @@ def infer_length(seq, eos_ix, batch_first=True, include_eos=True, type=torch.Lon :param include_eos: if True, the time-step where eos first occurs is has mask = 1 :returns: mask, float32 matrix with '0's and '1's of same shape as seq """ - mask = infer_mask(seq, eos_ix, batch_first, include_eos, type) + mask = infer_mask(seq, eos_ix, batch_first, include_eos, dtype) return torch.sum(mask, dim=1 if batch_first else 0) def to_one_hot(y, n_dims=None): """ Take integer y (tensor or variable) with n dims and convert it to 1-hot representation with n+1 dims. """ - y_tensor = y.data if isinstance(y, Variable) else y - y_tensor = y_tensor.type(torch.LongTensor).view(-1, 1) + y_tensor = y.data + y_tensor = y_tensor.to(dtype=torch.long).view(-1, 1) n_dims = n_dims if n_dims is not None else int(torch.max(y_tensor)) + 1 - y_one_hot = torch.zeros(y_tensor.size()[0], n_dims).scatter_(1, y_tensor, 1) + y_one_hot = torch.zeros(y_tensor.size()[0], n_dims, device=y.device).scatter_(1, y_tensor, 1) y_one_hot = y_one_hot.view(*y.shape, -1) - return Variable(y_one_hot) if isinstance(y, Variable) else y_one_hot - + return y_one_hot diff --git a/week8_scst/bonus.ipynb b/week07_seq2seq/bonus.ipynb similarity index 99% rename from week8_scst/bonus.ipynb rename to week07_seq2seq/bonus.ipynb index 2c7778acb..11ecd3c58 100644 --- a/week8_scst/bonus.ipynb +++ b/week07_seq2seq/bonus.ipynb @@ -48,7 +48,7 @@ "\n", "Some seq2seq tasks can benefit from the attention mechanism. In addition to taking the _last_ time-step of encoder hidden state, we can allow decoder to peek on any time-step of his choice.\n", "\n", - "![img](https://s30.postimg.org/f8um3kt5d/google_seq2seq_attention.gif)\n", + "![img](https://xiandong79.github.io/downloads/nmt-model-fast.gif)\n", "\n", "\n", "#### Recommended steps:\n", @@ -344,4 +344,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/week8_scst/he-pron-wiktionary.txt b/week07_seq2seq/he-pron-wiktionary.txt similarity index 100% rename from week8_scst/he-pron-wiktionary.txt rename to week07_seq2seq/he-pron-wiktionary.txt diff --git a/week8_scst/main_dataset.txt b/week07_seq2seq/main_dataset.txt similarity index 100% rename from week8_scst/main_dataset.txt rename to week07_seq2seq/main_dataset.txt diff --git a/week07_seq2seq/practice_tf.ipynb b/week07_seq2seq/practice_tf.ipynb new file mode 100644 index 000000000..da535d868 --- /dev/null +++ b/week07_seq2seq/practice_tf.ipynb @@ -0,0 +1,1040 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reinforcement Learning for seq2seq\n", + "\n", + "This time we'll solve a problem of transribing hebrew words in english, also known as g2p (grapheme2phoneme)\n", + "\n", + " * word (sequence of letters in source language) -> translation (sequence of letters in target language)\n", + "\n", + "Unlike what most deep learning practicioners do, we won't only train it to maximize likelihood of correct translation, but also employ reinforcement learning to actually teach it to translate with as few errors as possible.\n", + "\n", + "\n", + "### About the task\n", + "\n", + "One notable property of Hebrew is that it's consonant language. That is, there are no wovels in the written language. One could represent wovels with diacritics above consonants, but you don't expect people to do that in everyay life.\n", + "\n", + "Therefore, some hebrew characters will correspond to several english letters and others - to none, so we should use encoder-decoder architecture to figure that out.\n", + "\n", + "![img](https://esciencegroup.files.wordpress.com/2016/03/seq2seq.jpg)\n", + "_(img: esciencegroup.files.wordpress.com)_\n", + "\n", + "Encoder-decoder architectures are about converting anything to anything, including\n", + " * Machine translation and spoken dialogue systems\n", + " * [Image captioning](http://mscoco.org/dataset/#captions-challenge2015) and [image2latex](https://openai.com/requests-for-research/#im2latex) (convolutional encoder, recurrent decoder)\n", + " * Generating [images by captions](https://arxiv.org/abs/1511.02793) (recurrent encoder, convolutional decoder)\n", + " * Grapheme2phoneme - convert words to transcripts\n", + " \n", + "We chose simplified __Hebrew->English__ machine translation for words and short phrases (character-level), as it is relatively quick to train even without a gpu cluster." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# If True, only translates phrases shorter than 20 characters (way easier).\n", + "EASY_MODE = True\n", + "# Useful for initial coding.\n", + "# If false, works with all phrases (please switch to this mode for homework assignment)\n", + "\n", + "MODE = \"he-to-en\" # way we translate. Either \"he-to-en\" or \"en-to-he\"\n", + "# maximal length of _generated_ output, does not affect training\n", + "MAX_OUTPUT_LENGTH = 50 if not EASY_MODE else 20\n", + "REPORT_FREQ = 100 # how often to evaluate validation score" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 1: preprocessing\n", + "\n", + "We shall store dataset as a dictionary\n", + "`{ word1:[translation1,translation2,...], word2:[...],...}`.\n", + "\n", + "This is mostly due to the fact that many words have several correct translations.\n", + "\n", + "We have implemented this thing for you so that you can focus on more interesting parts.\n", + "\n", + "\n", + "__Attention python2 users!__ You may want to cast everything to unicode later during homework phase, just make sure you do it _everywhere_." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from collections import defaultdict\n", + "word_to_translation = defaultdict(list) # our dictionary\n", + "\n", + "bos = '_'\n", + "eos = ';'\n", + "\n", + "with open(\"main_dataset.txt\") as fin:\n", + " for line in fin:\n", + "\n", + " en, he = line[:-1].lower().replace(bos, ' ').replace(eos,\n", + " ' ').split('\\t')\n", + " word, trans = (he, en) if MODE == 'he-to-en' else (en, he)\n", + "\n", + " if len(word) < 3:\n", + " continue\n", + " if EASY_MODE:\n", + " if max(len(word), len(trans)) > 20:\n", + " continue\n", + "\n", + " word_to_translation[word].append(trans)\n", + "\n", + "print(\"size = \", len(word_to_translation))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# get all unique lines in source language\n", + "all_words = np.array(list(word_to_translation.keys()))\n", + "# get all unique lines in translation language\n", + "all_translations = np.array(\n", + " [ts for all_ts in word_to_translation.values() for ts in all_ts])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### split the dataset\n", + "\n", + "We hold out 10% of all words to be used for validation.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "scrolled": true + }, + "outputs": [], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "train_words, test_words = train_test_split(\n", + " all_words, test_size=0.1, random_state=42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Building vocabularies\n", + "\n", + "We now need to build vocabularies that map strings to token ids and vice versa. We're gonna need these fellas when we feed training data into model or convert output matrices into english words." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from voc import Vocab\n", + "inp_voc = Vocab.from_lines(''.join(all_words), bos=bos, eos=eos, sep='')\n", + "out_voc = Vocab.from_lines(''.join(all_translations), bos=bos, eos=eos, sep='')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Here's how you cast lines into ids and backwards.\n", + "batch_lines = all_words[:5]\n", + "batch_ids = inp_voc.to_matrix(batch_lines)\n", + "batch_lines_restored = inp_voc.to_lines(batch_ids)\n", + "\n", + "print(\"lines\")\n", + "print(batch_lines)\n", + "print(\"\\nwords to ids (0 = bos, 1 = eos):\")\n", + "print(batch_ids)\n", + "print(\"\\nback to words\")\n", + "print(batch_lines_restored)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Draw word/translation length distributions to estimate the scope of the task." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "plt.figure(figsize=[8, 4])\n", + "plt.subplot(1, 2, 1)\n", + "plt.title(\"words\")\n", + "plt.hist(list(map(len, all_words)), bins=20)\n", + "\n", + "plt.subplot(1, 2, 2)\n", + "plt.title('translations')\n", + "plt.hist(list(map(len, all_translations)), bins=20)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 3: deploy encoder-decoder (1 point)\n", + "\n", + "__assignment starts here__\n", + "\n", + "Our architecture consists of two main blocks:\n", + "* Encoder reads words character by character and outputs code vector (usually a function of last RNN state)\n", + "* Decoder takes that code vector and produces translations character by character\n", + "\n", + "Than it gets fed into a model that follows this simple interface:\n", + "* __`model.symbolic_translate(inp, **flags) -> out, logp`__ - takes symbolic int32 matrix of hebrew words, produces output tokens sampled from the model and output log-probabilities for all possible tokens at each tick.\n", + " * if given flag __`greedy=True`__, takes most likely next token at each iteration. Otherwise samples with next token probabilities predicted by model.\n", + "* __`model.symbolic_score(inp, out, **flags) -> logp`__ - takes symbolic int32 matrices of hebrew words and their english translations. Computes the log-probabilities of all possible english characters given english prefices and hebrew word.\n", + "* __`model.weights`__ - weights from all model layers [a list of variables]\n", + "\n", + "That's all! It's as hard as it gets. With those two methods alone you can implement all kinds of prediction and training." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import tensorflow as tf\n", + "tf.reset_default_graph()\n", + "s = tf.InteractiveSession()\n", + "\n", + "# ^^^ if you get \"variable *** already exists\": re-run this cell again" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from basic_model_tf import BasicTranslationModel\n", + "model = BasicTranslationModel('model', inp_voc, out_voc,\n", + " emb_size=64, hid_size=128)\n", + "\n", + "s.run(tf.global_variables_initializer())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "scrolled": true + }, + "outputs": [], + "source": [ + "# Play around with symbolic_translate and symbolic_score\n", + "inp = tf.placeholder_with_default(np.random.randint(\n", + " 0, 10, [3, 5], dtype='int32'), [None, None])\n", + "out = tf.placeholder_with_default(np.random.randint(\n", + " 0, 10, [3, 5], dtype='int32'), [None, None])\n", + "\n", + "# translate inp (with untrained model)\n", + "sampled_out, logp = model.symbolic_translate(inp, greedy=False)\n", + "print(\"\\nSymbolic_translate output:\\n\", sampled_out, logp)\n", + "print(\"\\nSample translations:\\n\", s.run(sampled_out))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# score logp(out | inp) with untrained input\n", + "logp = model.symbolic_score(inp, out)\n", + "print(\"\\nSymbolic_score output:\\n\", logp)\n", + "print(\"\\nLog-probabilities (clipped):\\n\", s.run(logp)[:, :2, :5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Prepare any operations you want here\n", + "input_sequence = tf.placeholder('int32', [None, None])\n", + "greedy_translations, logp = \n", + "\n", + "\n", + "def translate(lines):\n", + " \"\"\"\n", + " You are given a list of input lines. \n", + " Make your neural network translate them.\n", + " :return: a list of output lines\n", + " \"\"\"\n", + " # Convert lines to a matrix of indices\n", + " lines_ix = \n", + "\n", + " # Compute translations in form of indices\n", + " trans_ix = s.run(greedy_translations, { < YOUR CODE - feed dict > })\n", + "\n", + " # Convert translations back into strings\n", + " return out_voc.to_lines(trans_ix)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "print(\"Sample inputs:\", all_words[:3])\n", + "print(\"Dummy translations:\", translate(all_words[:3]))\n", + "\n", + "assert isinstance(greedy_translations,\n", + " tf.Tensor) and greedy_translations.dtype.is_integer, \"trans must be a tensor of integers (token ids)\"\n", + "assert translate(all_words[:3]) == translate(\n", + " all_words[:3]), \"make sure translation is deterministic (use greedy=True and disable any noise layers)\"\n", + "assert type(translate(all_words[:3])) is list and (type(translate(all_words[:1])[0]) is str or type(\n", + " translate(all_words[:1])[0]) is unicode), \"translate(lines) must return a sequence of strings!\"\n", + "print(\"Tests passed!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Scoring function\n", + "\n", + "LogLikelihood is a poor estimator of model performance.\n", + "* If we predict zero probability once, it shouldn't ruin entire model.\n", + "* It is enough to learn just one translation if there are several correct ones.\n", + "* What matters is how many mistakes model's gonna make when it translates!\n", + "\n", + "Therefore, we will use minimal Levenshtein distance. It measures how many characters do we need to add/remove/replace from model translation to make it perfect. Alternatively, one could use character-level BLEU/RougeL or other similar metrics.\n", + "\n", + "The catch here is that Levenshtein distance is not differentiable: it isn't even continuous. We can't train our neural network to maximize it by gradient descent." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import editdistance # !pip install editdistance\n", + "\n", + "\n", + "def get_distance(word, trans):\n", + " \"\"\"\n", + " A function that takes word and predicted translation\n", + " and evaluates (Levenshtein's) edit distance to closest correct translation\n", + " \"\"\"\n", + " references = word_to_translation[word]\n", + " assert len(references) != 0, \"wrong/unknown word\"\n", + " return min(editdistance.eval(trans, ref) for ref in references)\n", + "\n", + "\n", + "def score(words, bsize=100):\n", + " \"\"\"a function that computes levenshtein distance for bsize random samples\"\"\"\n", + " assert isinstance(words, np.ndarray)\n", + "\n", + " batch_words = np.random.choice(words, size=bsize, replace=False)\n", + " batch_trans = translate(batch_words)\n", + "\n", + " distances = list(map(get_distance, batch_words, batch_trans))\n", + "\n", + " return np.array(distances, dtype='float32')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# should be around 5-50 and decrease rapidly after training :)\n", + "[score(test_words, 10).mean() for _ in range(5)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 2: Supervised pre-training\n", + "\n", + "Here we define a function that trains our model through maximizing log-likelihood a.k.a. minimizing crossentropy." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# import utility functions\n", + "from basic_model_tf import initialize_uninitialized, infer_length, infer_mask, select_values_over_last_axis\n", + "\n", + "\n", + "class supervised_training:\n", + "\n", + " # variable for inputs and correct answers\n", + " input_sequence = tf.placeholder('int32', [None, None])\n", + " reference_answers = tf.placeholder('int32', [None, None])\n", + "\n", + " # Compute log-probabilities of all possible tokens at each step. Use model interface.\n", + " logprobs_seq = \n", + "\n", + " # compute mean crossentropy\n", + " crossentropy = - select_values_over_last_axis(logprobs_seq, reference_answers)\n", + "\n", + " mask = infer_mask(reference_answers, out_voc.eos_ix)\n", + "\n", + " loss = tf.reduce_sum(crossentropy * mask)/tf.reduce_sum(mask)\n", + "\n", + " # Build weights optimizer. Use model.weights to get all trainable params.\n", + " train_step = \n", + "\n", + "\n", + "# intialize optimizer params while keeping model intact\n", + "initialize_uninitialized(s)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Actually run training on minibatches" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import random\n", + "\n", + "\n", + "def sample_batch(words, word_to_translation, batch_size):\n", + " \"\"\"\n", + " sample random batch of words and random correct translation for each word\n", + " example usage:\n", + " batch_x,batch_y = sample_batch(train_words, word_to_translations,10)\n", + " \"\"\"\n", + " # choose words\n", + " batch_words = np.random.choice(words, size=batch_size)\n", + "\n", + " # choose translations\n", + " batch_trans_candidates = list(map(word_to_translation.get, batch_words))\n", + " batch_trans = list(map(random.choice, batch_trans_candidates))\n", + "\n", + " return inp_voc.to_matrix(batch_words), out_voc.to_matrix(batch_trans)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "bx, by = sample_batch(train_words, word_to_translation, batch_size=3)\n", + "print(\"Source:\")\n", + "print(bx)\n", + "print(\"Target:\")\n", + "print(by)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "scrolled": false + }, + "outputs": [], + "source": [ + "from IPython.display import clear_output\n", + "from tqdm import tqdm, trange # or use tqdm_notebook,tnrange\n", + "\n", + "loss_history = []\n", + "editdist_history = []\n", + "\n", + "for i in trange(25000):\n", + " bx, by = sample_batch(train_words, word_to_translation, 32)\n", + "\n", + " feed_dict = {\n", + " supervised_training.input_sequence: bx,\n", + " supervised_training.reference_answers: by\n", + " }\n", + "\n", + " loss, _ = s.run([supervised_training.loss,\n", + " supervised_training.train_step], feed_dict)\n", + " loss_history.append(loss)\n", + "\n", + " if (i+1) % REPORT_FREQ == 0:\n", + " clear_output(True)\n", + " current_scores = score(test_words)\n", + " editdist_history.append(current_scores.mean())\n", + " plt.figure(figsize=(12, 4))\n", + " plt.subplot(131)\n", + " plt.title('train loss / traning time')\n", + " plt.plot(loss_history)\n", + " plt.grid()\n", + " plt.subplot(132)\n", + " plt.title('val score distribution')\n", + " plt.hist(current_scores, bins=20)\n", + " plt.subplot(133)\n", + " plt.title('val score / traning time')\n", + " plt.plot(editdist_history)\n", + " plt.grid()\n", + " plt.show()\n", + " print(\"llh=%.3f, mean score=%.3f\" %\n", + " (np.mean(loss_history[-10:]), np.mean(editdist_history[-10:])))\n", + "\n", + "# Note: it's okay if loss oscillates up and down as long as it gets better on average over long term (e.g. 5k batches)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "for word in train_words[:10]:\n", + " print(\"%s -> %s\" % (word, translate([word])[0]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "test_scores = []\n", + "for start_i in trange(0, len(test_words), 32):\n", + " batch_words = test_words[start_i:start_i+32]\n", + " batch_trans = translate(batch_words)\n", + " distances = list(map(get_distance, batch_words, batch_trans))\n", + " test_scores.extend(distances)\n", + "\n", + "print(\"Supervised test score:\", np.mean(test_scores))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Preparing for reinforcement learning (2 points)\n", + "\n", + "First we need to define loss function as a custom tf operation.\n", + "\n", + "The simple way to do so is through `tensorflow.py_func` wrapper.\n", + "```\n", + "def my_func(x):\n", + " # x will be a numpy array with the contents of the placeholder below\n", + " return np.sinh(x)\n", + "inp = tf.placeholder(tf.float32)\n", + "y = tf.py_func(my_func, [inp], tf.float32)\n", + "```\n", + "\n", + "\n", + "__Your task__ is to implement `_compute_levenshtein` function that takes matrices of words and translations, along with input masks, then converts those to actual words and phonemes and computes min-levenshtein via __get_distance__ function above.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def _compute_levenshtein(words_ix, trans_ix):\n", + " \"\"\"\n", + " A custom tensorflow operation that computes levenshtein loss for predicted trans.\n", + "\n", + " Params:\n", + " - words_ix - a matrix of letter indices, shape=[batch_size,word_length]\n", + " - words_mask - a matrix of zeros/ones, \n", + " 1 means \"word is still not finished\"\n", + " 0 means \"word has already finished and this is padding\"\n", + "\n", + " - trans_mask - a matrix of output letter indices, shape=[batch_size,translation_length]\n", + " - trans_mask - a matrix of zeros/ones, similar to words_mask but for trans_ix\n", + "\n", + "\n", + " Please implement the function and make sure it passes tests from the next cell.\n", + "\n", + " \"\"\"\n", + "\n", + " # convert words to strings\n", + " words = \n", + "\n", + " assert type(words) is list and type(\n", + " words[0]) is str and len(words) == len(words_ix)\n", + "\n", + " # convert translations to lists\n", + " translations = \n", + "\n", + " assert type(distances) in (list, tuple, np.ndarray) and len(\n", + " distances) == len(words_ix)\n", + "\n", + " distances = np.array(list(distances), dtype='float32')\n", + " return distances\n", + "\n", + "\n", + "def compute_levenshtein(words_ix, trans_ix):\n", + " out = tf.py_func(_compute_levenshtein, [words_ix, trans_ix, ], tf.float32)\n", + " out.set_shape([None])\n", + "\n", + " return tf.stop_gradient(out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Simple test suite to make sure your implementation is correct. Hint: if you run into any bugs, feel free to use print from inside _compute_levenshtein." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# test suite\n", + "# sample random batch of (words, correct trans, wrong trans)\n", + "batch_words = np.random.choice(train_words, size=100)\n", + "batch_trans = list(map(random.choice, map(\n", + " word_to_translation.get, batch_words)))\n", + "batch_trans_wrong = np.random.choice(all_translations, size=100)\n", + "\n", + "batch_words_ix = tf.constant(inp_voc.to_matrix(batch_words))\n", + "batch_trans_ix = tf.constant(out_voc.to_matrix(batch_trans))\n", + "batch_trans_wrong_ix = tf.constant(out_voc.to_matrix(batch_trans_wrong))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# assert compute_levenshtein is zero for ideal translations\n", + "correct_answers_score = compute_levenshtein(\n", + " batch_words_ix, batch_trans_ix).eval()\n", + "\n", + "assert np.all(correct_answers_score ==\n", + " 0), \"a perfect translation got nonzero levenshtein score!\"\n", + "\n", + "print(\"Everything seems alright!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# assert compute_levenshtein matches actual scoring function\n", + "wrong_answers_score = compute_levenshtein(\n", + " batch_words_ix, batch_trans_wrong_ix).eval()\n", + "\n", + "true_wrong_answers_score = np.array(\n", + " list(map(get_distance, batch_words, batch_trans_wrong)))\n", + "\n", + "assert np.all(wrong_answers_score ==\n", + " true_wrong_answers_score), \"for some word symbolic levenshtein is different from actual levenshtein distance\"\n", + "\n", + "print(\"Everything seems alright!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once you got it working...\n", + "\n", + "\n", + "* You may now want to __remove/comment asserts__ from function code for a slight speed-up.\n", + "\n", + "* There's a more detailed tutorial on custom tensorflow ops: [`py_func`](https://www.tensorflow.org/api_docs/python/tf/py_func), [`low-level`](https://www.tensorflow.org/api_docs/python/tf/py_func)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Self-critical policy gradient (2 points)\n", + "\n", + "In this section you'll implement algorithm called self-critical sequence training (here's an [article](https://arxiv.org/abs/1612.00563)).\n", + "\n", + "The algorithm is a vanilla policy gradient with a special baseline. \n", + "\n", + "$$ \\nabla J = E_{x \\sim p(s)} E_{y \\sim \\pi(y|x)} \\nabla log \\pi(y|x) \\cdot (R(x,y) - b(x)) $$\n", + "\n", + "Here reward R(x,y) is a __negative levenshtein distance__ (since we minimize it). The baseline __b(x)__ represents how well model fares on word __x__.\n", + "\n", + "In practice, this means that we compute baseline as a score of greedy translation, $b(x) = R(x,y_{greedy}(x)) $.\n", + "\n", + "![img](https://github.com/yandexdataschool/Practical_RL/raw/master/yet_another_week/_resource/scheme.png)\n", + "Luckily, we already obtained the required outputs: `model.greedy_translations, model.greedy_mask` and we only need to compute levenshtein using `compute_levenshtein` function.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class trainer:\n", + "\n", + " input_sequence = tf.placeholder('int32', [None, None])\n", + "\n", + " # use model to __sample__ symbolic translations given input_sequence\n", + " sample_translations, sample_logp = \n", + " # use model to __greedy__ symbolic translations given input_sequence\n", + " greedy_translations, greedy_logp = \n", + "\n", + " rewards = - compute_levenshtein(input_sequence, sample_translations)\n", + "\n", + " # compute __negative__ levenshtein for greedy mode\n", + " baseline = \n", + "\n", + " # compute advantage using rewards and baseline\n", + " advantage = \n", + " assert advantage.shape.ndims == 1, \"advantage must be of shape [batch_size]\"\n", + "\n", + " # compute log_pi(a_t|s_t), shape = [batch, seq_length]\n", + " logprobs_phoneme = # YOUR CODE\n", + " # ^-- hint: look at how crossentropy is implemented in supervised learning loss above\n", + " # mind the sign - this one should not be multiplied by -1 :)\n", + "\n", + "\n", + " # Compute policy gradient\n", + " # or rather surrogate function who's gradient is policy gradient\n", + " J = logprobs_phoneme*advantage[:, None]\n", + "\n", + " mask = infer_mask(sample_translations, out_voc.eos_ix)\n", + " loss = - tf.reduce_sum(J*mask) / tf.reduce_sum(mask)\n", + "\n", + " # regularize with negative entropy. Don't forget the sign!\n", + " # note: for entropy you need probabilities for all tokens (sample_logp), not just phoneme_logprobs\n", + " entropy = \n", + " # hint: you can get sample probabilities from sample_logp using math :)\n", + "\n", + "\n", + " assert entropy.shape.ndims == 2, \"please make sure elementwise entropy is of shape [batch,time]\"\n", + "\n", + " loss -= 0.01*tf.reduce_sum(entropy*mask) / tf.reduce_sum(mask)\n", + "\n", + " # compute weight updates, clip by norm\n", + " grads = tf.gradients(loss, model.weights)\n", + " grads = tf.clip_by_global_norm(grads, 50)[0]\n", + "\n", + " train_step = tf.train.AdamOptimizer(\n", + " learning_rate=1e-5).apply_gradients(zip(grads, model.weights,))\n", + "\n", + "\n", + "initialize_uninitialized()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Policy gradient training\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "scrolled": false + }, + "outputs": [], + "source": [ + "for i in trange(100000):\n", + " bx = sample_batch(train_words, word_to_translation, 32)[0]\n", + " pseudo_loss, _ = s.run([trainer.loss, trainer.train_step], {\n", + " trainer.input_sequence: bx})\n", + "\n", + " loss_history.append(\n", + " pseudo_loss\n", + " )\n", + "\n", + " if (i+1) % REPORT_FREQ == 0:\n", + " clear_output(True)\n", + " current_scores = score(test_words)\n", + " editdist_history.append(current_scores.mean())\n", + " plt.figure(figsize=(8, 4))\n", + " plt.subplot(121)\n", + " plt.title('val score distribution')\n", + " plt.hist(current_scores, bins=20)\n", + " plt.subplot(122)\n", + " plt.title('val score / traning time')\n", + " plt.plot(editdist_history)\n", + " plt.grid()\n", + " plt.show()\n", + " print(\"J=%.3f, mean score=%.3f\" %\n", + " (np.mean(loss_history[-10:]), np.mean(editdist_history[-10:])))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "model.translate(\"EXAMPLE;\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "for word in train_words[:10]:\n", + " print(\"%s -> %s\" % (word, translate([word])[0]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "scrolled": true + }, + "outputs": [], + "source": [ + "test_scores = []\n", + "for start_i in trange(0, len(test_words), 32):\n", + " batch_words = test_words[start_i:start_i+32]\n", + " batch_trans = translate(batch_words)\n", + " distances = list(map(get_distance, batch_words, batch_trans))\n", + " test_scores.extend(distances)\n", + "print(\"Supervised test score:\", np.mean(test_scores))\n", + "\n", + "# ^^ If you get Out Of Memory, please replace this with batched computation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 6: Make it actually work (5++ pts)\n", + "\n", + "\n", + "In this section we want you to finally __restart with EASY_MODE=False__ and experiment to find a good model/curriculum for that task.\n", + "\n", + "We recommend you to start with the following architecture\n", + "\n", + "```\n", + "encoder---decoder\n", + "\n", + " P(y|h)\n", + " ^\n", + " LSTM -> LSTM\n", + " ^ ^\n", + " biLSTM -> LSTM\n", + " ^ ^\n", + "input y_prev\n", + "```\n", + "\n", + "__Note:__ you can fit all 4 state tensors of both LSTMs into a in a single state - just assume that it contains, for example, [h0, c0, h1, c1] - pack it in encode and update in decode.\n", + "\n", + "\n", + "Here are some cool ideas on what you can do then.\n", + "\n", + "__General tips & tricks:__\n", + "* In some tensorflow versions and for some layers, it is required that each rnn/gru/lstm cell gets it's own `tf.variable_scope(unique_name, reuse=False)`.\n", + " * Otherwise it will complain about wrong tensor sizes because it tries to reuse weights from one rnn to the other.\n", + "* You will likely need to adjust pre-training time for such a network.\n", + "* Supervised pre-training may benefit from clipping gradients somehow.\n", + "* SCST may indulge a higher learning rate in some cases and changing entropy regularizer over time.\n", + "* It's often useful to save pre-trained model parameters to not re-train it every time you want new policy gradient parameters. \n", + "* When leaving training for nighttime, try setting REPORT_FREQ to a larger value (e.g. 500) not to waste time on it.\n", + "\n", + "__Formal criteria:__\n", + "To get 5 points we want you to build an architecture that:\n", + "* _doesn't consist of single GRU_\n", + "* _works better_ than single GRU baseline. \n", + "* We also want you to provide either learning curve or trained model, preferably both\n", + "* ... and write a brief report or experiment log describing what you did and how it fared.\n", + "\n", + "### Attention\n", + "There's more than one way to connect decoder to encoder\n", + " * __Vanilla:__ layer_i of encoder last state goes to layer_i of decoder initial state\n", + " * __Every tick:__ feed encoder last state _on every iteration_ of decoder.\n", + " * __Attention:__ allow decoder to \"peek\" at one (or several) positions of encoded sequence on every tick.\n", + " \n", + "The most effective (and cool) of those is, of course, attention.\n", + "You can read more about attention [in this nice blog post](https://distill.pub/2016/augmented-rnns/). The easiest way to begin is to use \"soft\" attention with \"additive\" or \"dot-product\" intermediate layers.\n", + "\n", + "__Tips__\n", + "* Model usually generalizes better if you no longer allow decoder to see final encoder state\n", + "* Once your model made it through several epochs, it is a good idea to visualize attention maps to understand what your model has actually learned\n", + "\n", + "* There's more stuff [here](https://github.com/yandexdataschool/Practical_RL/blob/master/week8_scst/bonus.ipynb)\n", + "* If you opted for hard attention, we recommend [gumbel-softmax](https://blog.evjang.com/2016/11/tutorial-categorical-variational.html) instead of sampling. Also please make sure soft attention works fine before you switch to hard.\n", + "\n", + "### UREX\n", + "* This is a way to improve exploration in policy-based settings. The main idea is that you find and upweight under-appreciated actions.\n", + "* Here's [video](https://www.youtube.com/watch?v=fZNyHoXgV7M&feature=youtu.be&t=3444)\n", + " and an [article](https://arxiv.org/abs/1611.09321).\n", + "* You may want to reduce batch size 'cuz UREX requires you to sample multiple times per source sentence.\n", + "* Once you got it working, try using experience replay with importance sampling instead of (in addition to) basic UREX.\n", + "\n", + "### Some additional ideas:\n", + "* (advanced deep learning) It may be a good idea to first train on small phrases and then adapt to larger ones (a.k.a. training curriculum).\n", + "* (advanced nlp) You may want to switch from raw utf8 to something like unicode or even syllables to make task easier.\n", + "* (advanced nlp) Since hebrew words are written __with vowels omitted__, you may want to use a small Hebrew vowel markup dataset at `he-pron-wiktionary.txt`.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Bonus hints: [here](https://github.com/yandexdataschool/Practical_RL/blob/master/week8_scst/bonus.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "assert not EASY_MODE, \"make sure you set EASY_MODE = False at the top of the notebook.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "`[your report/log here or anywhere you please]`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Contributions:__ This notebook is brought to you by\n", + "* Yandex [MT team](https://tech.yandex.com/translate/)\n", + "* Denis Mazur ([DeniskaMazur](https://github.com/DeniskaMazur)), Oleg Vasilev ([Omrigan](https://github.com/Omrigan/)), Dmitry Emelyanenko ([TixFeniks](https://github.com/tixfeniks)) and Fedor Ratnikov ([justheuristic](https://github.com/justheuristic/))\n", + "* Dataset is parsed from [Wiktionary](https://en.wiktionary.org), which is under CC-BY-SA and GFDL licenses.\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.6.2" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/week8_scst/practice_theano.ipynb b/week07_seq2seq/practice_theano.ipynb similarity index 99% rename from week8_scst/practice_theano.ipynb rename to week07_seq2seq/practice_theano.ipynb index 4efce5992..dc8a372b1 100644 --- a/week8_scst/practice_theano.ipynb +++ b/week07_seq2seq/practice_theano.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Week 8: Reinforcement Learning for seq2seq\n", + "## Reinforcement Learning for seq2seq\n", "\n", "This time we'll solve a problem of transribing hebrew words in english, also known as g2p (grapheme2phoneme)\n", "\n", @@ -1293,4 +1293,4 @@ }, "nbformat": 4, "nbformat_minor": 1 -} \ No newline at end of file +} diff --git a/week07_seq2seq/practice_torch.ipynb b/week07_seq2seq/practice_torch.ipynb new file mode 100644 index 000000000..cd6919cba --- /dev/null +++ b/week07_seq2seq/practice_torch.ipynb @@ -0,0 +1,877 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reinforcement Learning for seq2seq\n", + "\n", + "This time we'll solve a problem of transribing hebrew words in english, also known as g2p (grapheme2phoneme)\n", + "\n", + " * word (sequence of letters in source language) -> translation (sequence of letters in target language)\n", + "\n", + "Unlike what most deep learning practicioners do, we won't only train it to maximize likelihood of correct translation, but also employ reinforcement learning to actually teach it to translate with as few errors as possible.\n", + "\n", + "\n", + "### About the task\n", + "\n", + "One notable property of Hebrew is that it's consonant language. That is, there are no wovels in the written language. One could represent wovels with diacritics above consonants, but you don't expect people to do that in everyay life.\n", + "\n", + "Therefore, some hebrew characters will correspond to several english letters and others - to none, so we should use encoder-decoder architecture to figure that out.\n", + "\n", + "![img](https://esciencegroup.files.wordpress.com/2016/03/seq2seq.jpg)\n", + "_(img: esciencegroup.files.wordpress.com)_\n", + "\n", + "Encoder-decoder architectures are about converting anything to anything, including\n", + " * Machine translation and spoken dialogue systems\n", + " * [Image captioning](http://mscoco.org/dataset/#captions-challenge2015) and [image2latex](https://htmlpreview.github.io/?https://github.com/openai/requests-for-research/blob/master/_requests_for_research/im2latex.html) (convolutional encoder, recurrent decoder)\n", + " * Generating [images by captions](https://arxiv.org/abs/1511.02793) (recurrent encoder, convolutional decoder)\n", + " * Grapheme2phoneme - convert words to transcripts\n", + " \n", + "We chose simplified __Hebrew->English__ machine translation for words and short phrases (character-level), as it is relatively quick to train even without a gpu cluster." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# if running in colab, uncomment\n", + "# !wget https://github.com/yandexdataschool/Practical_RL/raw/c54b56049df85315490d1cec42b2fda0c96ad9b2/week07_scst/basic_model_torch.py -O basic_model_torch.py\n", + "# !wget https://github.com/yandexdataschool/Practical_RL/raw/c54b56049df85315490d1cec42b2fda0c96ad9b2/week07_scst/main_dataset.txt -O main_dataset.txt\n", + "# !wget https://github.com/yandexdataschool/Practical_RL/raw/c54b56049df85315490d1cec42b2fda0c96ad9b2/week07_scst/voc.py -O voc.py\n", + "# !pip3 install torch==1.0.0 nltk editdistance" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# If True, only translates phrases shorter than 20 characters (way easier).\n", + "EASY_MODE = True\n", + "# Useful for initial coding.\n", + "# If false, works with all phrases (please switch to this mode for homework assignment)\n", + "\n", + "# way we translate. Either \"he-to-en\" or \"en-to-he\"\n", + "MODE = \"he-to-en\"\n", + "# maximal length of _generated_ output, does not affect training\n", + "MAX_OUTPUT_LENGTH = 50 if not EASY_MODE else 20\n", + "REPORT_FREQ = 100 # how often to evaluate validation score" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 1: preprocessing\n", + "\n", + "We shall store dataset as a dictionary\n", + "`{ word1:[translation1,translation2,...], word2:[...],...}`.\n", + "\n", + "This is mostly due to the fact that many words have several correct translations.\n", + "\n", + "We have implemented this thing for you so that you can focus on more interesting parts.\n", + "\n", + "\n", + "__Attention python2 users!__ You may want to cast everything to unicode later during homework phase, just make sure you do it _everywhere_." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from collections import defaultdict\n", + "word_to_translation = defaultdict(list) # our dictionary\n", + "\n", + "bos = '_'\n", + "eos = ';'\n", + "\n", + "with open(\"main_dataset.txt\", encoding=\"utf-8\") as fin:\n", + " for line in fin:\n", + "\n", + " en, he = line[:-1].lower().replace(bos, ' ').replace(eos,\n", + " ' ').split('\\t')\n", + " word, trans = (he, en) if MODE == 'he-to-en' else (en, he)\n", + "\n", + " if len(word) < 3:\n", + " continue\n", + " if EASY_MODE:\n", + " if max(len(word), len(trans)) > 20:\n", + " continue\n", + "\n", + " word_to_translation[word].append(trans)\n", + "\n", + "print(\"size = \", len(word_to_translation))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# get all unique lines in source language\n", + "all_words = np.array(list(word_to_translation.keys()))\n", + "# get all unique lines in translation language\n", + "all_translations = np.array(\n", + " [ts for all_ts in word_to_translation.values() for ts in all_ts])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### split the dataset\n", + "\n", + "We hold out 10% of all words to be used for validation.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "train_words, test_words = train_test_split(\n", + " all_words, test_size=0.1, random_state=42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Building vocabularies\n", + "\n", + "We now need to build vocabularies that map strings to token ids and vice versa. We're gonna need these fellas when we feed training data into model or convert output matrices into english words." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from voc import Vocab\n", + "inp_voc = Vocab.from_lines(''.join(all_words), bos=bos, eos=eos, sep='')\n", + "out_voc = Vocab.from_lines(''.join(all_translations), bos=bos, eos=eos, sep='')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Here's how you cast lines into ids and backwards.\n", + "batch_lines = all_words[:5]\n", + "batch_ids = inp_voc.to_matrix(batch_lines)\n", + "batch_lines_restored = inp_voc.to_lines(batch_ids)\n", + "\n", + "print(\"lines\")\n", + "print(batch_lines)\n", + "print(\"\\nwords to ids (0 = bos, 1 = eos):\")\n", + "print(batch_ids)\n", + "print(\"\\nback to words\")\n", + "print(batch_lines_restored)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Draw word/translation length distributions to estimate the scope of the task." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "plt.figure(figsize=[8, 4])\n", + "plt.subplot(1, 2, 1)\n", + "plt.title(\"words\")\n", + "plt.hist(list(map(len, all_words)), bins=20)\n", + "\n", + "plt.subplot(1, 2, 2)\n", + "plt.title('translations')\n", + "plt.hist(list(map(len, all_translations)), bins=20)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 3: deploy encoder-decoder (1 point)\n", + "\n", + "__assignment starts here__\n", + "\n", + "Our architecture consists of two main blocks:\n", + "* Encoder reads words character by character and outputs code vector (usually a function of last RNN state)\n", + "* Decoder takes that code vector and produces translations character by character\n", + "\n", + "Than it gets fed into a model that follows this simple interface:\n", + "* __`model(inp, out, **flags) -> logp`__ - takes symbolic int32 matrices of hebrew words and their english translations. Computes the log-probabilities of all possible english characters given english prefices and hebrew word.\n", + "* __`model.translate(inp, **flags) -> out, logp`__ - takes symbolic int32 matrix of hebrew words, produces output tokens sampled from the model and output log-probabilities for all possible tokens at each tick.\n", + " * if given flag __`greedy=True`__, takes most likely next token at each iteration. Otherwise samples with next token probabilities predicted by model.\n", + "\n", + "That's all! It's as hard as it gets. With those two methods alone you can implement all kinds of prediction and training." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from basic_model_torch import BasicTranslationModel\n", + "model = BasicTranslationModel(inp_voc, out_voc,\n", + " emb_size=64, hid_size=256)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Play around with symbolic_translate and symbolic_score\n", + "inp = torch.tensor(np.random.randint(0, 10, [3, 5]), dtype=torch.int64)\n", + "out = torch.tensor(np.random.randint(0, 10, [3, 5]), dtype=torch.int64)\n", + "\n", + "# translate inp (with untrained model)\n", + "sampled_out, logp = model.translate(inp, greedy=False)\n", + "\n", + "print(\"Sample translations:\\n\", sampled_out)\n", + "print(\"Log-probabilities at each step:\\n\", logp)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# score logp(out | inp) with untrained input\n", + "logp = model(inp, out)\n", + "print(\"Symbolic_score output:\\n\", logp)\n", + "\n", + "print(\"Log-probabilities of output tokens:\\n\",\n", + " torch.gather(logp, dim=2, index=out[:, :, None]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def translate(lines, max_len=MAX_OUTPUT_LENGTH):\n", + " \"\"\"\n", + " You are given a list of input lines. \n", + " Make your neural network translate them.\n", + " :return: a list of output lines\n", + " \"\"\"\n", + " # Convert lines to a matrix of indices\n", + " lines_ix = inp_voc.to_matrix(lines)\n", + " lines_ix = torch.tensor(lines_ix, dtype=torch.int64)\n", + "\n", + " # Compute translations in form of indices\n", + " trans_ix = \n", + "\n", + " # Convert translations back into strings\n", + " return out_voc.to_lines(trans_ix.data.numpy())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Sample inputs:\", all_words[:3])\n", + "print(\"Dummy translations:\", translate(all_words[:3]))\n", + "trans = translate(all_words[:3])\n", + "\n", + "assert translate(all_words[:3]) == translate(\n", + " all_words[:3]), \"make sure translation is deterministic (use greedy=True and disable any noise layers)\"\n", + "assert type(translate(all_words[:3])) is list and (type(translate(all_words[:1])[0]) is str or type(\n", + " translate(all_words[:1])[0]) is unicode), \"translate(lines) must return a sequence of strings!\"\n", + "# note: if translation freezes, make sure you used max_len parameter\n", + "print(\"Tests passed!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Scoring function\n", + "\n", + "LogLikelihood is a poor estimator of model performance.\n", + "* If we predict zero probability once, it shouldn't ruin entire model.\n", + "* It is enough to learn just one translation if there are several correct ones.\n", + "* What matters is how many mistakes model's gonna make when it translates!\n", + "\n", + "Therefore, we will use minimal Levenshtein distance. It measures how many characters do we need to add/remove/replace from model translation to make it perfect. Alternatively, one could use character-level BLEU/RougeL or other similar metrics.\n", + "\n", + "The catch here is that Levenshtein distance is not differentiable: it isn't even continuous. We can't train our neural network to maximize it by gradient descent." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import editdistance # !pip install editdistance\n", + "\n", + "\n", + "def get_distance(word, trans):\n", + " \"\"\"\n", + " A function that takes word and predicted translation\n", + " and evaluates (Levenshtein's) edit distance to closest correct translation\n", + " \"\"\"\n", + " references = word_to_translation[word]\n", + " assert len(references) != 0, \"wrong/unknown word\"\n", + " return min(editdistance.eval(trans, ref) for ref in references)\n", + "\n", + "\n", + "def score(words, bsize=100):\n", + " \"\"\"a function that computes levenshtein distance for bsize random samples\"\"\"\n", + " assert isinstance(words, np.ndarray)\n", + "\n", + " batch_words = np.random.choice(words, size=bsize, replace=False)\n", + " batch_trans = translate(batch_words)\n", + "\n", + " distances = list(map(get_distance, batch_words, batch_trans))\n", + "\n", + " return np.array(distances, dtype='float32')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# should be around 5-50 and decrease rapidly after training :)\n", + "[score(test_words, 10).mean() for _ in range(5)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 2: Supervised pre-training (2 points)\n", + "\n", + "Here we define a function that trains our model through maximizing log-likelihood a.k.a. minimizing crossentropy." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "\n", + "def sample_batch(words, word_to_translation, batch_size):\n", + " \"\"\"\n", + " sample random batch of words and random correct translation for each word\n", + " example usage:\n", + " batch_x,batch_y = sample_batch(train_words, word_to_translations,10)\n", + " \"\"\"\n", + " # choose words\n", + " batch_words = np.random.choice(words, size=batch_size)\n", + "\n", + " # choose translations\n", + " batch_trans_candidates = list(map(word_to_translation.get, batch_words))\n", + " batch_trans = list(map(random.choice, batch_trans_candidates))\n", + " return batch_words, batch_trans" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bx, by = sample_batch(train_words, word_to_translation, batch_size=3)\n", + "print(\"Source:\")\n", + "print(bx)\n", + "print(\"Target:\")\n", + "print(by)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from basic_model_torch import infer_length, infer_mask, to_one_hot\n", + "\n", + "\n", + "def compute_loss_on_batch(input_sequence, reference_answers):\n", + " \"\"\" Compute crossentropy loss given a batch of sources and translations \"\"\"\n", + " input_sequence = torch.tensor(inp_voc.to_matrix(input_sequence), dtype=torch.int64)\n", + " reference_answers = torch.tensor(out_voc.to_matrix(reference_answers), dtype=torch.int64)\n", + "\n", + " # Compute log-probabilities of all possible tokens at each step. Use model interface.\n", + " logprobs_seq = # YOUR CODE\n", + "\n", + " # compute elementwise crossentropy as negative log-probabilities of reference_answers.\n", + " crossentropy = - \\\n", + " torch.sum(logprobs_seq *\n", + " to_one_hot(reference_answers, len(out_voc)), dim=-1)\n", + " assert crossentropy.dim(\n", + " ) == 2, \"please return elementwise crossentropy, don't compute mean just yet\"\n", + "\n", + " # average with mask\n", + " mask = infer_mask(reference_answers, out_voc.eos_ix)\n", + " loss = torch.sum(crossentropy * mask) / torch.sum(mask)\n", + "\n", + " return loss" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# test it\n", + "loss = compute_loss_on_batch(*sample_batch(train_words, word_to_translation, 3))\n", + "print('loss = ', loss)\n", + "\n", + "assert loss.item() > 0.0\n", + "loss.backward()\n", + "for w in model.parameters():\n", + " assert w.grad is not None and torch.max(torch.abs(w.grad)).item() != 0, \\\n", + " \"Loss is not differentiable w.r.t. a weight with shape %s. Check comput_loss_on_batch.\" % (\n", + " w.size(),)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Actually train the model\n", + "\n", + "Minibatches and stuff..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import clear_output\n", + "from tqdm import tqdm, trange # or use tqdm_notebook,tnrange\n", + "\n", + "loss_history = []\n", + "editdist_history = []\n", + "entropy_history = []\n", + "opt = torch.optim.Adam(model.parameters())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "for i in trange(25000):\n", + " loss = compute_loss_on_batch(*sample_batch(train_words, word_to_translation, 32))\n", + "\n", + " # train with backprop\n", + " loss.backward()\n", + " opt.step()\n", + " opt.zero_grad()\n", + "\n", + " loss_history.append(loss.item())\n", + "\n", + " if (i+1) % REPORT_FREQ == 0:\n", + " clear_output(True)\n", + " current_scores = score(test_words)\n", + " editdist_history.append(current_scores.mean())\n", + " print(\"llh=%.3f, mean score=%.3f\" %\n", + " (np.mean(loss_history[-10:]), np.mean(editdist_history[-10:])))\n", + " plt.figure(figsize=(12, 4))\n", + " plt.subplot(131)\n", + " plt.title('train loss / traning time')\n", + " plt.plot(loss_history)\n", + " plt.grid()\n", + " plt.subplot(132)\n", + " plt.title('val score distribution')\n", + " plt.hist(current_scores, bins=20)\n", + " plt.subplot(133)\n", + " plt.title('val score / traning time (lower is better)')\n", + " plt.plot(editdist_history)\n", + " plt.grid()\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__How to interpret the plots:__\n", + "\n", + "* __Train loss__ - that's your model's crossentropy over minibatches. It should go down steadily. Most importantly, it shouldn't be NaN :)\n", + "* __Val score distribution__ - distribution of translation edit distance (score) within batch. It should move to the left over time.\n", + "* __Val score / training time__ - it's your current mean edit distance. This plot is much whimsier than loss, but make sure it goes below 8 by 2500 steps. \n", + "\n", + "If it doesn't, first try to re-create both model and opt. You may have changed it's weight too much while debugging. If that doesn't help, it's debugging time." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for word in train_words[:10]:\n", + " print(\"%s -> %s\" % (word, translate([word])[0]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test_scores = []\n", + "for start_i in trange(0, len(test_words), 32):\n", + " batch_words = test_words[start_i:start_i+32]\n", + " batch_trans = translate(batch_words)\n", + " distances = list(map(get_distance, batch_words, batch_trans))\n", + " test_scores.extend(distances)\n", + "\n", + "print(\"Supervised test score:\", np.mean(test_scores))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Self-critical policy gradient (2 points)\n", + "\n", + "In this section you'll implement algorithm called self-critical sequence training (here's an [article](https://arxiv.org/abs/1612.00563)).\n", + "\n", + "The algorithm is a vanilla policy gradient with a special baseline. \n", + "\n", + "$$ \\nabla J = E_{x \\sim p(s)} E_{y \\sim \\pi(y|x)} \\nabla log \\pi(y|x) \\cdot (R(x,y) - b(x)) $$\n", + "\n", + "Here reward R(x,y) is a __negative levenshtein distance__ (since we minimize it). The baseline __b(x)__ represents how well model fares on word __x__.\n", + "\n", + "In practice, this means that we compute baseline as a score of greedy translation, $b(x) = R(x,y_{greedy}(x)) $.\n", + "\n", + "![img](https://github.com/yandexdataschool/Practical_RL/raw/master/yet_another_week/_resource/scheme.png)\n", + "\n", + "\n", + "Luckily, we already obtained the required outputs: `model.greedy_translations, model.greedy_mask` and we only need to compute levenshtein using `compute_levenshtein` function.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_reward(input_sequence, translations):\n", + " \"\"\" computes sample-wise reward given token ids for inputs and translations \"\"\"\n", + " distances = list(map(get_distance,\n", + " inp_voc.to_lines(input_sequence.data.numpy()),\n", + " out_voc.to_lines(translations.data.numpy())))\n", + " # use negative levenshtein distance so that larger reward means better policy\n", + " return - torch.tensor(distances, dtype=torch.int64)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def scst_objective_on_batch(input_sequence, max_len=MAX_OUTPUT_LENGTH):\n", + " \"\"\" Compute pseudo-loss for policy gradient given a batch of sources \"\"\"\n", + " input_sequence = torch.tensor(inp_voc.to_matrix(input_sequence), dtype=torch.int64)\n", + "\n", + " # use model to __sample__ symbolic translations given input_sequence\n", + " sample_translations, sample_logp = # YOUR CODE\n", + " # use model to __greedy__ symbolic translations given input_sequence\n", + " greedy_translations, greedy_logp = # YOUR CODE\n", + "\n", + " # compute rewards and advantage\n", + " rewards = compute_reward(input_sequence, sample_translations)\n", + " baseline = \n", + "\n", + " # compute advantage using rewards and baseline\n", + " advantage = # YOUR CODE\n", + "\n", + " # compute log_pi(a_t|s_t), shape = [batch, seq_length]\n", + " logp_sample = # YOUR CODE\n", + " \n", + " # ^-- hint: look at how crossentropy is implemented in supervised learning loss above\n", + " # mind the sign - this one should not be multiplied by -1 :)\n", + "\n", + " # policy gradient pseudo-loss. Gradient of J is exactly policy gradient.\n", + " J = logp_sample * advantage[:, None]\n", + "\n", + " assert J.dim() == 2, \"please return elementwise objective, don't compute mean just yet\"\n", + "\n", + " # average with mask\n", + " mask = infer_mask(sample_translations, out_voc.eos_ix)\n", + " loss = - torch.sum(J * mask) / torch.sum(mask)\n", + "\n", + " # regularize with negative entropy. Don't forget the sign!\n", + " # note: for entropy you need probabilities for all tokens (sample_logp), not just logp_sample\n", + " entropy = \n", + " # hint: you can get sample probabilities from sample_logp using math :)\n", + "\n", + " assert entropy.dim(\n", + " ) == 2, \"please make sure elementwise entropy is of shape [batch,time]\"\n", + "\n", + " reg = - 0.01 * torch.sum(entropy * mask) / torch.sum(mask)\n", + "\n", + " return loss + reg, torch.sum(entropy * mask) / torch.sum(mask)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Policy gradient training\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "entropy_history = [np.nan] * len(loss_history)\n", + "opt = torch.optim.Adam(model.parameters(), lr=1e-5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for i in trange(100000):\n", + " loss, ent = scst_objective_on_batch(\n", + " sample_batch(train_words, word_to_translation, 32)[0]) # [0] = only source sentence\n", + "\n", + " # train with backprop\n", + " loss.backward()\n", + " opt.step()\n", + " opt.zero_grad()\n", + "\n", + " loss_history.append(loss.item())\n", + " entropy_history.append(ent.item())\n", + "\n", + " if (i+1) % REPORT_FREQ == 0:\n", + " clear_output(True)\n", + " current_scores = score(test_words)\n", + " editdist_history.append(current_scores.mean())\n", + " plt.figure(figsize=(12, 4))\n", + " plt.subplot(131)\n", + " plt.title('val score distribution')\n", + " plt.hist(current_scores, bins=20)\n", + " plt.subplot(132)\n", + " plt.title('val score / traning time')\n", + " plt.plot(editdist_history)\n", + " plt.grid()\n", + " plt.subplot(133)\n", + " plt.title('policy entropy / traning time')\n", + " plt.plot(entropy_history)\n", + " plt.grid()\n", + " plt.show()\n", + " print(\"J=%.3f, mean score=%.3f\" %\n", + " (np.mean(loss_history[-10:]), np.mean(editdist_history[-10:])))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Debugging tips:__\n", + "\n", + "\n", + " * As usual, don't expect improvements right away, but in general the model should be able to show some positive changes by 5k steps.\n", + " * Entropy is a good indicator of many problems. \n", + " * If it reaches zero, you may need greater entropy regularizer.\n", + " * If it has rapid changes time to time, you may need gradient clipping.\n", + " * If it oscillates up and down in an erratic manner... it's perfectly okay for entropy to do so. But it should decrease at the end.\n", + " \n", + " * We don't show loss_history cuz it's uninformative for pseudo-losses in policy gradient. However, if something goes wrong you can check it to see if everything isn't a constant zero." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for word in train_words[:10]:\n", + " print(\"%s -> %s\" % (word, translate([word])[0]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test_scores = []\n", + "for start_i in trange(0, len(test_words), 32):\n", + " batch_words = test_words[start_i:start_i+32]\n", + " batch_trans = translate(batch_words)\n", + " distances = list(map(get_distance, batch_words, batch_trans))\n", + " test_scores.extend(distances)\n", + "print(\"Supervised test score:\", np.mean(test_scores))\n", + "\n", + "# ^^ If you get Out Of MemoryError, please replace this with batched computation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 6: Make it actually work (5++ pts)\n", + "\n", + "In this section we want you to finally __restart with EASY_MODE=False__ and experiment to find a good model/curriculum for that task.\n", + "\n", + "We recommend you to start with the following architecture\n", + "\n", + "```\n", + "encoder---decoder\n", + "\n", + " P(y|h)\n", + " ^\n", + " LSTM -> LSTM\n", + " ^ ^\n", + " biLSTM -> LSTM\n", + " ^ ^\n", + "input y_prev\n", + "```\n", + "\n", + "__Note:__ you can fit all 4 state tensors of both LSTMs into a in a single state - just assume that it contains, for example, [h0, c0, h1, c1] - pack it in encode and update in decode.\n", + "\n", + "\n", + "Here are some cool ideas on what you can do then.\n", + "\n", + "__General tips & tricks:__\n", + "* You will likely need to adjust pre-training time for such a network.\n", + "* Supervised pre-training may benefit from clipping gradients somehow.\n", + "* SCST may indulge a higher learning rate in some cases and changing entropy regularizer over time.\n", + "* It's often useful to save pre-trained model parameters to not re-train it every time you want new policy gradient parameters. \n", + "* When leaving training for nighttime, try setting REPORT_FREQ to a larger value (e.g. 500) not to waste time on it.\n", + "\n", + "__Formal criteria:__\n", + "To get 5 points we want you to build an architecture that:\n", + "* _doesn't consist of single GRU_\n", + "* _works better_ than single GRU baseline. \n", + "* We also want you to provide either learning curve or trained model, preferably both\n", + "* ... and write a brief report or experiment log describing what you did and how it fared.\n", + "\n", + "### Attention\n", + "There's more than one way to connect decoder to encoder\n", + " * __Vanilla:__ layer_i of encoder last state goes to layer_i of decoder initial state\n", + " * __Every tick:__ feed encoder last state _on every iteration_ of decoder.\n", + " * __Attention:__ allow decoder to \"peek\" at one (or several) positions of encoded sequence on every tick.\n", + " \n", + "The most effective (and cool) of those is, of course, attention.\n", + "You can read more about attention [in this nice blog post](https://distill.pub/2016/augmented-rnns/). The easiest way to begin is to use \"soft\" attention with \"additive\" or \"dot-product\" intermediate layers.\n", + "\n", + "__Tips__\n", + "* Model usually generalizes better if you no longer allow decoder to see final encoder state\n", + "* Once your model made it through several epochs, it is a good idea to visualize attention maps to understand what your model has actually learned\n", + "\n", + "* There's more stuff [here](https://github.com/yandexdataschool/Practical_RL/blob/master/week8_scst/bonus.ipynb)\n", + "* If you opted for hard attention, we recommend [gumbel-softmax](https://blog.evjang.com/2016/11/tutorial-categorical-variational.html) instead of sampling. Also please make sure soft attention works fine before you switch to hard.\n", + "\n", + "### UREX\n", + "* This is a way to improve exploration in policy-based settings. The main idea is that you find and upweight under-appreciated actions.\n", + "* Here's [video](https://www.youtube.com/watch?v=fZNyHoXgV7M&feature=youtu.be&t=3444)\n", + " and an [article](https://arxiv.org/abs/1611.09321).\n", + "* You may want to reduce batch size 'cuz UREX requires you to sample multiple times per source sentence.\n", + "* Once you got it working, try using experience replay with importance sampling instead of (in addition to) basic UREX.\n", + "\n", + "### Some additional ideas:\n", + "* (advanced deep learning) It may be a good idea to first train on small phrases and then adapt to larger ones (a.k.a. training curriculum).\n", + "* (advanced nlp) You may want to switch from raw utf8 to something like unicode or even syllables to make task easier.\n", + "* (advanced nlp) Since hebrew words are written __with vowels omitted__, you may want to use a small Hebrew vowel markup dataset at `he-pron-wiktionary.txt`.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert not EASY_MODE, \"make sure you set EASY_MODE = False at the top of the notebook.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`[your report/log here or anywhere you please]`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Contributions:__ This notebook is brought to you by\n", + "* Yandex [MT team](https://tech.yandex.com/translate/)\n", + "* Denis Mazur ([DeniskaMazur](https://github.com/DeniskaMazur)), Oleg Vasilev ([Omrigan](https://github.com/Omrigan/)), Dmitry Emelyanenko ([TixFeniks](https://github.com/tixfeniks)) and Fedor Ratnikov ([justheuristic](https://github.com/justheuristic/))\n", + "* Dataset is parsed from [Wiktionary](https://en.wiktionary.org), which is under CC-BY-SA and GFDL licenses.\n" + ] + } + ], + "metadata": { + "language_info": { + "name": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/week8_scst/scheme.svg b/week07_seq2seq/scheme.svg similarity index 100% rename from week8_scst/scheme.svg rename to week07_seq2seq/scheme.svg diff --git a/week8_scst/voc.py b/week07_seq2seq/voc.py similarity index 100% rename from week8_scst/voc.py rename to week07_seq2seq/voc.py diff --git a/week7_pomdp/README.md b/week08_pomdp/README.md similarity index 85% rename from week7_pomdp/README.md rename to week08_pomdp/README.md index 0b045c0c0..e0013ca20 100644 --- a/week7_pomdp/README.md +++ b/week08_pomdp/README.md @@ -1,5 +1,5 @@ # Materials -[lecture slides](https://yadi.sk/d/RGx8BUCr3Gq6DC) +[lecture slides](https://yadi.sk/i/3-4q71hhwtFxuw) _Links on all articles mentioned during the lecture could be found in "References" at the very end of the presentation slides. All other interesing links which contribute to the topic of POMDP are presented below_ @@ -23,5 +23,6 @@ _Links on all articles mentioned during the lecture could be found in "Reference # Practice +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week08_pomdp/practice_pytorch.ipynb) The assignment is platform and framewerk independent, so choose the framework that suits you best, but pay attention on how many you will need to implement youself in case of nonstandart ones. diff --git a/week7_pomdp/atari_util.py b/week08_pomdp/atari_util.py similarity index 100% rename from week7_pomdp/atari_util.py rename to week08_pomdp/atari_util.py diff --git a/week7_pomdp/env_pool.py b/week08_pomdp/env_pool.py similarity index 100% rename from week7_pomdp/env_pool.py rename to week08_pomdp/env_pool.py diff --git a/week7_pomdp/homework_common_part2.ipynb b/week08_pomdp/homework_common_part2.ipynb similarity index 100% rename from week7_pomdp/homework_common_part2.ipynb rename to week08_pomdp/homework_common_part2.ipynb diff --git a/week7_pomdp/img1.jpg b/week08_pomdp/img1.jpg similarity index 100% rename from week7_pomdp/img1.jpg rename to week08_pomdp/img1.jpg diff --git a/week7_pomdp/img2.jpg b/week08_pomdp/img2.jpg similarity index 100% rename from week7_pomdp/img2.jpg rename to week08_pomdp/img2.jpg diff --git a/week7_pomdp/img3.jpg b/week08_pomdp/img3.jpg similarity index 100% rename from week7_pomdp/img3.jpg rename to week08_pomdp/img3.jpg diff --git a/week08_pomdp/practice_pytorch.ipynb b/week08_pomdp/practice_pytorch.ipynb new file mode 100644 index 000000000..bb74cf8da --- /dev/null +++ b/week08_pomdp/practice_pytorch.ipynb @@ -0,0 +1,699 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from __future__ import print_function, division\n", + "from IPython.core import display\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "import numpy as np\n", + "\n", + "# if you're running in colab\n", + "# !wget https://raw.githubusercontent.com/yandexdataschool/Practical_RL/0ccb0673965dd650d9b284e1ec90c2bfd82c8a94/week08_pomdp/atari_util.py\n", + "# !wget https://raw.githubusercontent.com/yandexdataschool/Practical_RL/0ccb0673965dd650d9b284e1ec90c2bfd82c8a94/week08_pomdp/env_pool.py\n", + "\n", + "# If you are running on a server, launch xvfb to record game videos\n", + "# Please make sure you have xvfb installed\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Kung-Fu, recurrent style\n", + "\n", + "In this notebook we'll once again train RL agent for for atari [KungFuMaster](https://gym.openai.com/envs/KungFuMaster-v0/), this time using recurrent neural networks.\n", + "\n", + "![http://www.retroland.com/wp-content/uploads/2011/07/King-Fu-Master.jpg](http://www.retroland.com/wp-content/uploads/2011/07/King-Fu-Master.jpg)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mWARN: gym.spaces.Box autodetected dtype as . Please provide explicit dtype.\u001b[0m\n", + "Observation shape: (1, 42, 42)\n", + "Num actions: 14\n", + "Action names: ['NOOP', 'UP', 'RIGHT', 'LEFT', 'DOWN', 'DOWNRIGHT', 'DOWNLEFT', 'RIGHTFIRE', 'LEFTFIRE', 'DOWNFIRE', 'UPRIGHTFIRE', 'UPLEFTFIRE', 'DOWNRIGHTFIRE', 'DOWNLEFTFIRE']\n" + ] + } + ], + "source": [ + "import gym\n", + "from atari_util import PreprocessAtari\n", + "\n", + "\n", + "def make_env():\n", + " env = gym.make(\"KungFuMasterDeterministic-v0\")\n", + " env = PreprocessAtari(env, height=42, width=42,\n", + " crop=lambda img: img[60:-30, 15:],\n", + " color=False, n_frames=1)\n", + " return env\n", + "\n", + "\n", + "env = make_env()\n", + "\n", + "obs_shape = env.observation_space.shape\n", + "n_actions = env.action_space.n\n", + "\n", + "print(\"Observation shape:\", obs_shape)\n", + "print(\"Num actions:\", n_actions)\n", + "print(\"Action names:\", env.env.env.get_action_meanings())" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/jheuristic/anaconda3/lib/python3.6/site-packages/scipy/misc/pilutil.py:482: FutureWarning: Conversion of the second argument of issubdtype from `int` to `np.signedinteger` is deprecated. In future, it will be treated as `np.int64 == np.dtype(int).type`.\n", + " if issubdtype(ts, int):\n", + "/home/jheuristic/anaconda3/lib/python3.6/site-packages/scipy/misc/pilutil.py:485: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n", + " elif issubdtype(type(size), float):\n" + ] + }, + { + "data": { + "image/png": "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\nRQAAAABJRU5ErkJggg==\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAP8AAAEICAYAAACQ6CLfAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAFz5JREFUeJzt3X20HHV9x/H35z5xQxIeEkIMJAUfooItpi1G6sORolhE\nFGzViihpy7HtsfRYH9qqfcJWrZ6K2HP06EFFUquAjzVVasmJIIVaHsSIQagBBBMTEhACuXm6T9/+\nMXPL3jtzc/fe3Z3dze/zOmfP3f3N7M539u53Z+a3M7+vIgIzS09PuwMws/Zw8pslyslvlignv1mi\nnPxmiXLymyXKyZ8wSSdKCkl97Y5lNiRdIOm6dsfR7Zz8TSTpBkmPSTqswmWGpGdUtbyqlX1BRcQX\nIuLl7YzrUODkbxJJJwIvBgJ4dVuD6SDK+HPWgfxPaZ4Lgf8BrgTW1E6QtFjSv0t6QtJtkt4v6aaa\n6c+WtF7So5L+V9Lra6ZdKekTkr4labekWyQ9PZ92Yz7bDyUNSfrdqUFJ6pH015IelLRT0r9IOnLK\nbH8gaZuk7ZLeWfPc1ZJuz+PeIemjNdNOk/TfknZJ+qGk02um3SDpA5JuBvYC75V0+5S43i5pXX7/\nlZJ+kC9ni6RLamadWMdd+Tr+hqTfm/L+vSB/Xx/P/75gSiz/IOnm/P27TtIxU9+nJEWEb024AfcC\nbwV+HRgBltZMuzq/HQ6cDGwBbsqnzc8f/z7QB/wa8AjwnHz6lcCjwOp8+heAq2teO4BnHCSuP8hj\nexqwAPga8Pl82on586/K4/gV4GHgZfn07wFvzu8vAE7L7x8P/AI4m2wDcmb+eEk+/QbgZ8Bz8piP\nBHYDK2viug14Q37/9HzZPcApwA7gvCkx9tU89/dq3r9FwGPAm/NlnZ8/XlwTy33AM4F5+eMPtfvz\n0gk3b/mbQNKLgBOAL0XE98k+bG/Mp/UCvwP8XUTsjYgfA2trnn4O8EBEfC4iRiPiDuCrwGtr5vla\nRNwaEaNkyb9qFuFdAHw0Iu6PiCHgPcAbpnTyvS8i9kTEj4DPkSUQZF9iz5B0TEQMRcT/5O1vAq6N\niGsjYjwi1gO3k30ZTLgyIu7K1+lx4BsTrytpJfBsYB1ARNwQET/KX+tOsi+jl9S5fq8ENkfE5/Nl\nXQXcA7yqZp7PRcRPImIf8CVm9/4dspz8zbEGuC4iHskff5End/2XkG2RttTMX3v/BOD5+e7zLkm7\nyBL2KTXzPFRzfy/ZVrhexwEP1jx+MI9n6TTxPJg/B+Aisi3mPfnu9Dk1Mb9uSswvApZN85qQvScT\nXypvBP4tIvYCSHq+pOslPSzpceCPgXp3zaeu38Q6HF/zuJH375DVVT/xdCJJ84DXA72SJj5khwFH\nSXousAkYBZYDP8mnr6h5iS3AdyPizBaFuI0sWSf8Uh7PjjymiXjuqZm+DSAiNgPn5x12vw18RdLi\nPObPR8RbDrLcqZeLXgccI2kV2ZfA22umfRH4OPCKiNgv6WM8mfwzXXY6df0m1uHbMzwved7yN+48\nYIzsWH5VfjsJ+C/gwogYIzvOvkTS4ZKeTdY5OOGbwDMlvVlSf357nqST6lz+DrLj+elcBbxd0lMl\nLQA+CFyTH0JM+Js8tueQ9T1cAyDpTZKWRMQ4sCufdwz4V+BVkn5LUq+kQUmnS1rONPLlfQX4J7Lj\n9PU1kxcCj+aJv5r8kCn3MDB+kHW8luz9e6OkvrzT82Sy99UOwsnfuDVkx5Q/i4iHJm5kW7IL8mPr\ni8k6vR4CPk+WkAcAImI38HLgDWRbsYeAD5PtPdTjEmBtvvv9+pLpV+TLvBH4KbAf+NMp83yXrFNw\nA/CRiJg4geYs4C5JQ8A/k3XQ7Y+ILcC5wHvJknML8OfM/Hn6IvAy4MtTvnzeCvy9pN3A35IdlwOQ\nHxp8ALg5X8fTal8wIn5B1m/yTrJOx78Azqk5BLNpKO8RtQpJ+jDwlIhYM+PMZi3iLX8F8t/xT8nO\nd9Fqso60r7c7LkubO/yqsZBsV/84YCdwKdlPX2Zt491+s0R5t98sUQ3t9ks6i6wXuBf4TER86GDz\n9w/Mj8HBoxtZpJkdxP79jzEyvEf1zDvn5M9PW/0E2XndW4HbJK3LT18tNTh4NKeuvniuizSzGdx+\n68frnreR3f7VwL35OePDZBeunNvA65lZhRpJ/uOZfP72ViafTw2ApD/MLwu9fWRkTwOLM7NmaiT5\ny44rCj8dRMTlEXFqRJza3z+/gcWZWTM10uG3lckXqCwnvyBkOhraR//NmxpYpJkdjA7sq3veRrb8\ntwEr8wtGBsjOTV/XwOuZWYXmvOWPiFFJFwP/SfZT3xURcVfTIjOzlmrod/6IuJbskkoz6zI+w88s\nUZVe2BNHzGP/i0+pcpFmSYn/uqHueb3lN0uUk98sUU5+s0Q5+c0S5eQ3S1Slvf3Di4It549Oaovx\n4iUCkkcXMoio/7Mxm3k7SbPjHr6r/ud6y2+WKCe/WaKc/GaJcvKbJaracftDxNjkDo4YKX7/lHUC\ntpsGxgttU9cFgLK2duor7wBST7E9hjtsW1ASI/3F/wNADPcW2zqsv6/0MzRa/Lw09PmfxXM77L9t\nZlVx8pslyslvlignv1miGq3Y8wCwGxgDRiPi1IPOPyL6fj657LzGGomgQmVfk2UdSh3WyTStsvUp\n70vrLNNtrro19ibHrZH6O/ya0dv/mxHxSBNex8wq5N1+s0Q1mvwBXCfp+5L+sGyG2oo9Y3tcsces\nUzS62//CiNgm6VhgvaR7IuLG2hki4nLgcoDB5Su65YjY7JDX6NDd2/K/OyV9nax4543Tzg/ElBOx\nVNbh0YFfEVPjhmk6Kzss9rK4gdJ9vtL/RTuV9F2NT7M+PZ0We4my2MtOYqzqMzTn3X5J8yUtnLgP\nvBxwLS6zLtHIln8p8HVJE6/zxYj4dlOiMrOWa6Rc1/3Ac5sYi5lVyD/1mSWq0kt6RUknWYd1kE2n\nGzr3ykx7BmUXdJCVvb893XJGaInS2Nv4GfKW3yxRTn6zRDn5zRLl5DdLlJPfLFGV9vYHEFO+bnx6\nb2tNe3pvyamzGi22tdUhdnpv6WeoG0/vNbPu5uQ3S5ST3yxRTn6zRFVbsacvGF1cR69Sh3WaAaWd\nTx0Z51SzKf7SaevTaPGjblifJscY/S7RbWYzcPKbJcrJb5YoJ79Zombs8JN0BXAOsDMifjlvWwRc\nA5wIPAC8PiIem3Fp40J7p47g2Wm9MmZdrMkluq8EzprS9m5gQ0SsBDbkj82si8yY/Pk4/I9OaT4X\nWJvfXwuc1+S4zKzF5nrMvzQitgPkf4+dbsZJFXuGXLHHrFO0vMMvIi6PiFMj4tTeBfNbvTgzq9Nc\nz/DbIWlZRGyXtAzYWdezAnpGpjY2ehqXmf2/WfSfz3XLvw5Yk99fA3xjjq9jZm0yY/JLugr4HvAs\nSVslXQR8CDhT0mbgzPyxmXWRGXf7I+L8aSa9tMmxmFmFfIafWaIqv6R3bHGhx8/MmqXPl/Sa2Qyc\n/GaJcvKbJcrJb5aoakt0D4uBrQNVLtIsKRpu7iW9ZnYIcvKbJcrJb5YoJ79Zopz8Zoly8pslyslv\nlignv1minPxmiapnJJ8rJO2UtKmm7RJJP5e0Mb+d3dowzazZ5lq0A+CyiFiV365tblhm1mpzLdph\nZl2ukWP+iyXdmR8WHN20iMysEnNN/k8CTwdWAduBS6ebcVLFnj2u2GPWKeaU/BGxIyLGImIc+DSw\n+iDzPlmxZ74r9ph1ijklf16lZ8JrgE3TzWtmnWnGwTzyoh2nA8dI2gr8HXC6pFVkxYEeAP6ohTGa\nWQvMtWjHZ1sQi5lVyGf4mSXKyW+WKCe/WaKc/GaJcvKbJcrJb5YoJ79Zopz8Zoly8pslyslvlign\nv1minPxmiXLymyXKyW+WKCe/WaKc/GaJcvKbJaqeij0rJF0v6W5Jd0l6W96+SNJ6SZvzvx6+26yL\n1LPlHwXeGREnAacBfyLpZODdwIaIWAlsyB+bWZeop2LP9oi4I7+/G7gbOB44F1ibz7YWOK9VQZpZ\n883qmF/SicCvArcASyNiO2RfEMCx0zzHRTvMOlDdyS9pAfBV4M8i4ol6n+eiHWadqa7kl9RPlvhf\niIiv5c07Jop35H93tiZEM2uFenr7RTZO/90R8dGaSeuANfn9NcA3mh+embXKjEU7gBcCbwZ+JGlj\n3vZe4EPAlyRdBPwMeF1rQjSzVqinYs9NgKaZ/NLmhmNmVfEZfmaJcvKbJaqeY/6m0Tj07Z18BDE2\nLwrzxXQHGW3Ut68Y1PhAcb7x3uL6dIve4fre+LGB7l1He5K3/GaJcvKbJcrJb5YoJ79Zoirt8Ot/\naA/L//G/J7Vte9cLCvMNH9neDqWBJ4odX8ddekuhbdebVhfbVrYkpKZTyVu8Yv1Qoa1vZ/Eyjvsv\nPK7Q1s0dnanylt8sUU5+s0Q5+c0S5eQ3S5ST3yxR1Z7ee9hh9J749Mlt41VGUJ+ekWJb39Ilhbbx\n3gqCaRGNF3/RGD7qsEJb7xNl5zCXvGAXvxep8pbfLFFOfrNEOfnNEtVIxZ5LJP1c0sb8dnbrwzWz\nZqmnw2+iYs8dkhYC35e0Pp92WUR8pN6F7V/Sx0/eMnl4/94DJaeFtvlM0QOLigFsfttTC20aK3ly\nGzswy8ZGmHZwhP3FeR84r2TeviMKTQMPdd7/zGavnjH8tgMTxTl2S5qo2GNmXayRij0AF0u6U9IV\n0xXqdMUes87USMWeTwJPB1aR7RlcWvY8V+wx60xzrtgTETsiYiwixoFPA8XrW82sY814zD9dxR5J\nyyYKdQKvATbN9Fo9IzBv5+ROpZGFnTeAp8aKAQw+XJxvZGGxrZ3XtZ+y+r5C29ED+0rn/e59xYEH\nLlv95ULbkt7i9fwXrntroa1vdweOumoH1UjFnvMlrSLr530A+KOWRGhmLdFIxZ5rmx+OmVXFZ/iZ\nJcrJb5aoSi/pBegZnfy47NLSaPNgkBqts63sDL82Xtq6cePTCm1/dWZ55fS3/MZ3C233DC8rtG3a\nt7zQ1lNnZR/rbN7ymyXKyW+WKCe/WaKc/GaJqrbDTzDeP7mp3Wfz1Wtq3NB5sfc/Xvwu/8Tml5TO\ne8ep1xTaHhrbW2j74I/PKrT1HJhDcNZxvOU3S5ST3yxRTn6zRDn5zRLl5DdLVKW9/dELw0dOOXW3\nAyv2jA0WTy8em1cyYxcMWrnnzkWl7c/ce2GhbXS4+HHof7BYxccODd7ymyXKyW+WKCe/WaLqqdgz\nKOlWST/MK/a8L29/qqRbJG2WdI2kknKuZtap6unwOwCcERFD+Si+N0n6D+AdZBV7rpb0KeAisuG8\np6XBMfpPmjwg5MjdxYow7e4EHDmi2JO38ITHC2177j2y0Na7r7PO+e0ZLY/nWct2FNru2XFsoS1w\nh9+hasYtf2SG8of9+S2AM4Cv5O1rgfNaEqGZtUS94/b35iP37gTWA/cBuyJiYnybrUxTwqu2Ys/o\nE8ULR8ysPepK/rw4xypgOVlxjpPKZpvmuf9fsafviMPnHqmZNdWsevsjYhdwA3AacJSkiT6D5cC2\n5oZmZq1UT8WeJcBIROySNA94GfBh4HrgtcDVwBqgfKTIGhFibKzzf10s67QbGy+Ju9Mu6C8x3l9+\nGuJLj7mn0HbvL44ptI00PSLrFPX09i8D1krqJdtT+FJEfFPSj4GrJb0f+AFZSS8z6xL1VOy5k6ws\n99T2+3FxTrOu1fn74GbWEk5+s0RVeknvQN8oJyx+dFLb/b0LCvOpAy/zXXrE7kLbTw8rxt67v7O+\nT0fnl3f4/crglkLbnkeKP8X6nO1DV2d9Us2sMk5+s0Q5+c0S5eQ3S1SlHX6j4z3sHJrSSdaBnXtj\nC4tB9ajYcdYz0gVn+C0oqS0O3HWgeB2W9rWxvrhVzlt+s0Q5+c0S5eQ3S5ST3yxR1RbtGOpj+ObF\nk9oGy/uj2mrgiWLH17atKwpthw9XEU1j5u0sP0fvW5eeVmg74szitqCsNLl1Lo3VP6+3/GaJcvKb\nJcrJb5YoJ79Zohqp2HOlpJ9K2pjfVrU+XDNrlkYq9gD8eUR85SDPnaRnBOZv74K61ocQjZe/30PP\nOrrQNvho8bTm6On8U5jtST2z+PWsnjH8Aiir2GNmXWxOFXsi4pZ80gck3SnpMkmlRd0mVezZv6dJ\nYZtZo+ZUsUfSLwPvAZ4NPA9YBPzlNM99smLP4PwmhW1mjZprxZ6zImJ7XsTzAPA5PIy3WVeZc8Ue\nScsiYrskkVXo3TTTa4VgzCNCVqy8w2500NfuH4pmU0SqkYo938m/GARsBP54DrGaWZs0UrHnjJZE\nZGaV8Bl+Zoly8pslqtLr+cf7Ye9SnzFm1iqzGX/BW36zRDn5zRLl5DdLlJPfLFGVdvj1jMDhO3xB\noFmr9IzMYt7WhWFmnczJb5YoJ79Zopz8ZomqtMNPMbuKImY2OyWV5KflLb9Zopz8Zoly8pslyslv\nlqi6kz8fvvsHkr6ZP36qpFskbZZ0jSSPzmfWRWbT2/824G7giPzxh4HLIuJqSZ8CLgI+ebAX6BkO\nFm6ZUtS+pCLMeG/5Nf8Du4YLbRorVplphfGB4oCXIwuLF0+X9bb2DRXPuewZruZnj+gt/34fPqr4\nXd0zVgxeo8W2vqHi/6Eqw0eXlodAJbFHX3HdB3YdKD55mqpGzTY2v/h5GRsoxjhdlaR6Pv+9++vP\nh3qLdiwHXgl8Jn8s4AxgolTXWrIRfM2sS9S72/8x4C+Aia+VxcCuiJioDLYVOL7sibUVe0ZGXLHH\nrFPUU6X3HGBnRHy/trlk1tJ9p9qKPf39rthj1inqOeZ/IfBqSWcDg2TH/B8DjpLUl2/9lwPbWhem\nmTVbPeP2v4esLh+STgfeFREXSPoy8FrgamAN8I2ZXmt0vtixenKHzciC4g7D+EB5B8xxNxU7e/qf\nqKbjbOj4YgfZL1aVxFnStPjO4tu8YGs1nWYjC8sr82x7cXGnr/dAcYdu4PFi27G3Nx7XXP38JeUj\nVPbtK8ZZ9tlasaG+Ts1WePTk4ud3aEVJR2VveTxP+d5goe2wxybX5J5NSfVGfuf/S+Adku4l6wP4\nbAOvZWYVm9WFPRFxA1mhTiLiflyc06xr+Qw/s0Q5+c0SpYjqBtSU9DDwYP7wGOCRyhbeWofSuoDX\np9MdbH1OiIgl9bxIpck/acHS7RFxalsW3mSH0rqA16fTNWt9vNtvlignv1mi2pn8l7dx2c12KK0L\neH06XVPWp23H/GbWXt7tN0uUk98sUZUnv6SzJP2vpHslvbvq5TdK0hWSdkraVNO2SNL6fEiz9ZKO\nbmeMsyFphaTrJd0t6S5Jb8vbu26dJA1KulXSD/N1eV/e3tVDzrVqCL1Kk19SL/AJ4BXAycD5kk6u\nMoYmuBI4a0rbu4ENEbES2JA/7hajwDsj4iTgNOBP8v9JN67TAeCMiHgusAo4S9JpPDnk3ErgMbIh\n57rJxBB6E5qyPlVv+VcD90bE/RExTHY58LkVx9CQiLgReHRK87lkQ5lBlw1pFhHbI+KO/P5usg/Z\n8XThOkVmKH/Yn9+CLh5yrpVD6FWd/McDW2oeTzv8V5dZGhHbIUsm4Ng2xzMnkk4EfhW4hS5dp3wX\neSOwE1gP3EedQ851qDkPoTeTqpO/7uG/rFqSFgBfBf4sIp5odzxzFRFjEbGKbHSp1cBJZbNVG9Xc\nNDqE3kwqLdRJ9i21oubxoTL81w5JyyJiu6RlZFudriGpnyzxvxARX8ubu3qdImKXpBvI+jG6dci5\nlg6hV/WW/zZgZd5bOQC8AVhXcQytsI5sKDOoc0izTpEfQ34WuDsiPlozqevWSdISSUfl9+cBLyPr\nw7iebMg56JJ1gWwIvYhYHhEnkuXKdyLiApq1PhFR6Q04G/gJ2bHYX1W9/CbEfxWwHRgh25O5iOw4\nbAOwOf+7qN1xzmJ9XkS223gnsDG/nd2N6wScAvwgX5dNwN/m7U8DbgXuBb4MHNbuWOewbqcD32zm\n+vj0XrNE+Qw/s0Q5+c0S5eQ3S5ST3yxRTn6zRDn5zRLl5DdL1P8B8FPBd33wU/8AAAAASUVORK5C\nYII=\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "s = env.reset()\n", + "for _ in range(100):\n", + " s, _, _, _ = env.step(env.action_space.sample())\n", + "\n", + "plt.title('Game image')\n", + "plt.imshow(env.render('rgb_array'))\n", + "plt.show()\n", + "\n", + "plt.title('Agent observation')\n", + "plt.imshow(s.reshape([42, 42]))\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### POMDP setting\n", + "\n", + "The atari game we're working with is actually a POMDP: your agent needs to know timing at which enemies spawn and move, but cannot do so unless it has some memory. \n", + "\n", + "Let's design another agent that has a recurrent neural net memory to solve this. Here's a sketch.\n", + "\n", + "![img](img1.jpg)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "\n", + "# a special module that converts [batch, channel, w, h] to [batch, units]\n", + "\n", + "\n", + "class Flatten(nn.Module):\n", + " def forward(self, input):\n", + " return input.view(input.size(0), -1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class SimpleRecurrentAgent(nn.Module):\n", + " def __init__(self, obs_shape, n_actions, reuse=False):\n", + " \"\"\"A simple actor-critic agent\"\"\"\n", + " super(self.__class__, self).__init__()\n", + "\n", + " self.conv0 = nn.Conv2d(1, 32, kernel_size=(3, 3), stride=(2, 2))\n", + " self.conv1 = nn.Conv2d(32, 32, kernel_size=(3, 3), stride=(2, 2))\n", + " self.conv2 = nn.Conv2d(32, 32, kernel_size=(3, 3), stride=(2, 2))\n", + " self.flatten = Flatten()\n", + "\n", + " self.hid = nn.Linear(512, 128)\n", + " self.rnn = nn.LSTMCell(128, 128)\n", + "\n", + " self.logits = nn.Linear(128, n_actions)\n", + " self.state_value = nn.Linear(128, 1)\n", + "\n", + " def forward(self, prev_state, obs_t):\n", + " \"\"\"\n", + " Takes agent's previous step and observation, \n", + " returns next state and whatever it needs to learn (tf tensors)\n", + " \"\"\"\n", + "\n", + " # YOUR CODE: apply the whole neural net for one step here.\n", + " # See docs on self.rnn(...)\n", + " # the recurrent cell should take the last feedforward dense layer as input\n", + " \n", + "\n", + " new_state = \n", + " logits = \n", + " state_value = \n", + "\n", + " return new_state, (logits, state_value)\n", + "\n", + " def get_initial_state(self, batch_size):\n", + " \"\"\"Return a list of agent memory states at game start. Each state is a np array of shape [batch_size, ...]\"\"\"\n", + " return torch.zeros((batch_size, 128)), torch.zeros((batch_size, 128))\n", + "\n", + " def sample_actions(self, agent_outputs):\n", + " \"\"\"pick actions given numeric agent outputs (np arrays)\"\"\"\n", + " logits, state_values = agent_outputs\n", + " probs = F.softmax(logits)\n", + " return torch.multinomial(probs, 1)[:, 0].data.numpy()\n", + "\n", + " def step(self, prev_state, obs_t):\n", + " \"\"\" like forward, but obs_t is a numpy array \"\"\"\n", + " obs_t = torch.tensor(np.asarray(obs_t), dtype=torch.float32)\n", + " (h, c), (l, s) = self.forward(prev_state, obs_t)\n", + " return (h.detach(), c.detach()), (l.detach(), s.detach())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "n_parallel_games = 5\n", + "gamma = 0.99\n", + "\n", + "agent = SimpleRecurrentAgent(obs_shape, n_actions)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "state = [env.reset()]\n", + "_, (logits, value) = agent.step(agent.get_initial_state(1), state)\n", + "print(\"action logits:\\n\", logits)\n", + "print(\"state values:\\n\", value)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's play!\n", + "Let's build a function that measures agent's average reward." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def evaluate(agent, env, n_games=1):\n", + " \"\"\"Plays an entire game start to end, returns session rewards.\"\"\"\n", + "\n", + " game_rewards = []\n", + " for _ in range(n_games):\n", + " # initial observation and memory\n", + " observation = env.reset()\n", + " prev_memories = agent.get_initial_state(1)\n", + "\n", + " total_reward = 0\n", + " while True:\n", + " new_memories, readouts = agent.step(\n", + " prev_memories, observation[None, ...])\n", + " action = agent.sample_actions(readouts)\n", + "\n", + " observation, reward, done, info = env.step(action[0])\n", + "\n", + " total_reward += reward\n", + " prev_memories = new_memories\n", + " if done:\n", + " break\n", + "\n", + " game_rewards.append(total_reward)\n", + " return game_rewards" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "env_monitor = gym.wrappers.Monitor(env, directory=\"kungfu_videos\", force=True)\n", + "rw = evaluate(agent, env_monitor, n_games=3,)\n", + "env_monitor.close()\n", + "print(rw)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# show video\n", + "from IPython.display import HTML\n", + "import os\n", + "\n", + "video_names = list(filter(lambda s: s.endswith(\n", + " \".mp4\"), os.listdir(\"./kungfu_videos/\")))\n", + "\n", + "HTML(\"\"\"\n", + "\n", + "\"\"\".format(\"./kungfu_videos/\"+video_names[-1])) # this may or may not be _last_ video. Try other indices" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Training on parallel games\n", + "\n", + "We introduce a class called EnvPool - it's a tool that handles multiple environments for you. Here's how it works:\n", + "![img](img2.jpg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from env_pool import EnvPool\n", + "pool = EnvPool(agent, make_env, n_parallel_games)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We gonna train our agent on a thing called __rollouts:__\n", + "![img](img3.jpg)\n", + "\n", + "A rollout is just a sequence of T observations, actions and rewards that agent took consequently.\n", + "* First __s0__ is not necessarily initial state for the environment\n", + "* Final state is not necessarily terminal\n", + "* We sample several parallel rollouts for efficiency" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# for each of n_parallel_games, take 10 steps\n", + "rollout_obs, rollout_actions, rollout_rewards, rollout_mask = pool.interact(10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "print(\"Actions shape:\", rollout_actions.shape)\n", + "print(\"Rewards shape:\", rollout_rewards.shape)\n", + "print(\"Mask shape:\", rollout_mask.shape)\n", + "print(\"Observations shape: \", rollout_obs.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Actor-critic objective\n", + "\n", + "Here we define a loss function that uses rollout above to train advantage actor-critic agent.\n", + "\n", + "\n", + "Our loss consists of three components:\n", + "\n", + "* __The policy \"loss\"__\n", + " $$ \\hat J = {1 \\over T} \\cdot \\sum_t { \\log \\pi(a_t | s_t) } \\cdot A_{const}(s,a) $$\n", + " * This function has no meaning in and of itself, but it was built such that\n", + " * $ \\nabla \\hat J = {1 \\over N} \\cdot \\sum_t { \\nabla \\log \\pi(a_t | s_t) } \\cdot A(s,a) \\approx \\nabla E_{s, a \\sim \\pi} R(s,a) $\n", + " * Therefore if we __maximize__ J_hat with gradient descent we will maximize expected reward\n", + " \n", + " \n", + "* __The value \"loss\"__\n", + " $$ L_{td} = {1 \\over T} \\cdot \\sum_t { [r + \\gamma \\cdot V_{const}(s_{t+1}) - V(s_t)] ^ 2 }$$\n", + " * Ye Olde TD_loss from q-learning and alike\n", + " * If we minimize this loss, V(s) will converge to $V_\\pi(s) = E_{a \\sim \\pi(a | s)} R(s,a) $\n", + "\n", + "\n", + "* __Entropy Regularizer__\n", + " $$ H = - {1 \\over T} \\sum_t \\sum_a {\\pi(a|s_t) \\cdot \\log \\pi (a|s_t)}$$\n", + " * If we __maximize__ entropy we discourage agent from predicting zero probability to actions\n", + " prematurely (a.k.a. exploration)\n", + " \n", + " \n", + "So we optimize a linear combination of $L_{td}$ $- \\hat J$, $-H$\n", + " \n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "```\n", + "\n", + "\n", + "__One more thing:__ since we train on T-step rollouts, we can use N-step formula for advantage for free:\n", + " * At the last step, $A(s_t,a_t) = r(s_t, a_t) + \\gamma \\cdot V(s_{t+1}) - V(s) $\n", + " * One step earlier, $A(s_t,a_t) = r(s_t, a_t) + \\gamma \\cdot r(s_{t+1}, a_{t+1}) + \\gamma ^ 2 \\cdot V(s_{t+2}) - V(s) $\n", + " * Et cetera, et cetera. This way agent starts training much faster since it's estimate of A(s,a) depends less on his (imperfect) value function and more on actual rewards. There's also a [nice generalization](https://arxiv.org/abs/1506.02438) of this.\n", + "\n", + "\n", + "__Note:__ it's also a good idea to scale rollout_len up to learn longer sequences. You may wish set it to >=20 or to start at 10 and then scale up as time passes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def to_one_hot(y, n_dims=None):\n", + " \"\"\" Take an integer tensor and convert it to 1-hot matrix. \"\"\"\n", + " y_tensor = y.to(dtype=torch.int64).view(-1, 1)\n", + " n_dims = n_dims if n_dims is not None else int(torch.max(y_tensor)) + 1\n", + " y_one_hot = torch.zeros(y_tensor.size()[0], n_dims).scatter_(1, y_tensor, 1)\n", + " return y_one_hot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "opt = torch.optim.Adam(agent.parameters(), lr=1e-5)\n", + "\n", + "\n", + "def train_on_rollout(states, actions, rewards, is_not_done, prev_memory_states, gamma=0.99):\n", + " \"\"\"\n", + " Takes a sequence of states, actions and rewards produced by generate_session.\n", + " Updates agent's weights by following the policy gradient above.\n", + " Please use Adam optimizer with default parameters.\n", + " \"\"\"\n", + "\n", + " # shape: [batch_size, time, c, h, w]\n", + " states = torch.tensor(np.asarray(states), dtype=torch.float32)\n", + " actions = torch.tensor(np.array(actions), dtype=torch.int64) # shape: [batch_size, time]\n", + " rewards = torch.tensor(np.array(rewards), dtype=torch.float32) # shape: [batch_size, time]\n", + " is_not_done = torch.tensor(np.array(is_not_done), dtype=torch.float32) # shape: [batch_size, time]\n", + " rollout_length = rewards.shape[1] - 1\n", + "\n", + " # predict logits, probas and log-probas using an agent.\n", + " memory = [m.detach() for m in prev_memory_states]\n", + "\n", + " logits = [] # append logit sequence here\n", + " state_values = [] # append state values here\n", + " for t in range(rewards.shape[1]):\n", + " obs_t = states[:, t]\n", + "\n", + " # use agent to comute logits_t and state values_t.\n", + " # append them to logits and state_values array\n", + "\n", + " memory, (logits_t, values_t) = \n", + "\n", + " logits.append(logits_t)\n", + " state_values.append(values_t)\n", + "\n", + " logits = torch.stack(logits, dim=1)\n", + " state_values = torch.stack(state_values, dim=1)\n", + " probas = F.softmax(logits, dim=2)\n", + " logprobas = F.log_softmax(logits, dim=2)\n", + "\n", + " # select log-probabilities for chosen actions, log pi(a_i|s_i)\n", + " actions_one_hot = to_one_hot(actions, n_actions).view(\n", + " actions.shape[0], actions.shape[1], n_actions)\n", + " logprobas_for_actions = torch.sum(logprobas * actions_one_hot, dim=-1)\n", + "\n", + " # Now let's compute two loss components:\n", + " # 1) Policy gradient objective.\n", + " # Notes: Please don't forget to call .detach() on advantage term. Also please use mean, not sum.\n", + " # it's okay to use loops if you want\n", + " J_hat = 0 # policy objective as in the formula for J_hat\n", + "\n", + " # 2) Temporal difference MSE for state values\n", + " # Notes: Please don't forget to call on V(s') term. Also please use mean, not sum.\n", + " # it's okay to use loops if you want\n", + " value_loss = 0\n", + "\n", + " cumulative_returns = state_values[:, -1].detach()\n", + "\n", + " for t in reversed(range(rollout_length)):\n", + " r_t = rewards[:, t] # current rewards\n", + " # current state values\n", + " V_t = state_values[:, t]\n", + " V_next = state_values[:, t + 1].detach() # next state values\n", + " # log-probability of a_t in s_t\n", + " logpi_a_s_t = logprobas_for_actions[:, t]\n", + "\n", + " # update G_t = r_t + gamma * G_{t+1} as we did in week6 reinforce\n", + " cumulative_returns = G_t = r_t + gamma * cumulative_returns\n", + "\n", + " # Compute temporal difference error (MSE for V(s))\n", + " value_loss += \n", + "\n", + " # compute advantage A(s_t, a_t) using cumulative returns and V(s_t) as baseline\n", + " advantage = \n", + " advantage = advantage.detach()\n", + "\n", + " # compute policy pseudo-loss aka -J_hat.\n", + " J_hat += \n", + "\n", + " # regularize with entropy\n", + " entropy_reg = \n", + "\n", + " # add-up three loss components and average over time\n", + " loss = -J_hat / rollout_length +\\\n", + " value_loss / rollout_length +\\\n", + " -0.01 * entropy_reg\n", + "\n", + " # Gradient descent step\n", + " < your code >\n", + "\n", + " return loss.data.numpy()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# let's test it\n", + "memory = list(pool.prev_memory_states)\n", + "rollout_obs, rollout_actions, rollout_rewards, rollout_mask = pool.interact(10)\n", + "\n", + "train_on_rollout(rollout_obs, rollout_actions,\n", + " rollout_rewards, rollout_mask, memory)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Train \n", + "\n", + "just run train step and see if agent learns any better" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from IPython.display import clear_output\n", + "from tqdm import trange\n", + "from pandas import DataFrame\n", + "moving_average = lambda x, **kw: DataFrame(\n", + " {'x': np.asarray(x)}).x.ewm(**kw).mean().values\n", + "\n", + "rewards_history = []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "for i in trange(15000):\n", + "\n", + " memory = list(pool.prev_memory_states)\n", + " rollout_obs, rollout_actions, rollout_rewards, rollout_mask = pool.interact(\n", + " 10)\n", + " train_on_rollout(rollout_obs, rollout_actions,\n", + " rollout_rewards, rollout_mask, memory)\n", + "\n", + " if i % 100 == 0:\n", + " rewards_history.append(np.mean(evaluate(agent, env, n_games=1)))\n", + " clear_output(True)\n", + " plt.plot(rewards_history, label='rewards')\n", + " plt.plot(moving_average(np.array(rewards_history),\n", + " span=10), label='rewards ewma@10')\n", + " plt.legend()\n", + " plt.show()\n", + " if rewards_history[-1] >= 10000:\n", + " print(\"Your agent has just passed the minimum homework threshold\")\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Relax and grab some refreshments while your agent is locked in an infinite loop of violence and death.\n", + "\n", + "__How to interpret plots:__\n", + "\n", + "The session reward is the easy thing: it should in general go up over time, but it's okay if it fluctuates ~~like crazy~~. It's also OK if it reward doesn't increase substantially before some 10k initial steps. However, if reward reaches zero and doesn't seem to get up over 2-3 evaluations, there's something wrong happening.\n", + "\n", + "\n", + "Since we use a policy-based method, we also keep track of __policy entropy__ - the same one you used as a regularizer. The only important thing about it is that your entropy shouldn't drop too low (`< 0.1`) before your agent gets the yellow belt. Or at least it can drop there, but _it shouldn't stay there for long_.\n", + "\n", + "If it does, the culprit is likely:\n", + "* Some bug in entropy computation. Remember that it is $ - \\sum p(a_i) \\cdot log p(a_i) $\n", + "* Your agent architecture converges too fast. Increase entropy coefficient in actor loss. \n", + "* Gradient explosion - just [clip gradients](https://stackoverflow.com/a/43486487) and maybe use a smaller network\n", + "* Us. Or TF developers. Or aliens. Or lizardfolk. Contact us on forums before it's too late!\n", + "\n", + "If you're debugging, just run `logits, values = agent.step(batch_states)` and manually look into logits and values. This will reveal the problem 9 times out of 10: you'll likely see some NaNs or insanely large numbers or zeros. Try to catch the moment when this happens for the first time and investigate from there." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### \"Final\" evaluation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "env_monitor = gym.wrappers.Monitor(env, directory=\"kungfu_videos\", force=True)\n", + "final_rewards = evaluate(agent, env_monitor, n_games=20,)\n", + "env_monitor.close()\n", + "print(\"Final mean reward\", np.mean(final_rewards))\n", + "\n", + "video_names = list(filter(lambda s: s.endswith(\n", + " \".mp4\"), os.listdir(\"./kungfu_videos/\")))\n", + "HTML(\"\"\"\n", + "\n", + "\"\"\".format(\"./kungfu_videos/\"+video_names[-1])) # this may or may not be _last_ video. Try other indices" + ] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "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.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/week7_pomdp/practice_tensorflow.ipynb b/week08_pomdp/practice_tensorflow.ipynb similarity index 100% rename from week7_pomdp/practice_tensorflow.ipynb rename to week08_pomdp/practice_tensorflow.ipynb diff --git a/week7_pomdp/practice_theano.ipynb b/week08_pomdp/practice_theano.ipynb similarity index 100% rename from week7_pomdp/practice_theano.ipynb rename to week08_pomdp/practice_theano.ipynb diff --git a/week7_pomdp/theano_optional_recurrence_tutorial.ipynb b/week08_pomdp/theano_optional_recurrence_tutorial.ipynb similarity index 100% rename from week7_pomdp/theano_optional_recurrence_tutorial.ipynb rename to week08_pomdp/theano_optional_recurrence_tutorial.ipynb diff --git a/week9_policy_II/README.md b/week09_policy_II/README.md similarity index 75% rename from week9_policy_II/README.md rename to week09_policy_II/README.md index 1ed36866e..0350aac9a 100644 --- a/week9_policy_II/README.md +++ b/week09_policy_II/README.md @@ -6,12 +6,13 @@ This section covers some steroids for policy gradient methods, along with a cool * Lecture on NPG and TRPO by J. Schulman - [video](https://www.youtube.com/watch?v=_t5fpZuuf-4) * Alternative lecture on TRPO and open problems by... J. Schulman - [video](https://www.youtube.com/watch?v=gb5Q2XL5c8A) -* Our videos: [lecture](https://yadi.sk/i/OP0B1BEj3UcmW9), [seminar(pytorch)](https://yadi.sk/i/D8mHrKM63UcmWh) [seminar(theano)](https://yadi.sk/i/b0ol2gUV3HiKKJ) (russian) +* Our videos: [lecture](https://yadi.sk/i/c7GR1kAAJc00Og), [seminar(pytorch)](https://yadi.sk/i/OGZJJjkQH_7h5g) [seminar(theano)](https://yadi.sk/i/b0ol2gUV3HiKKJ) (russian) * Original articles - [TRPO](https://arxiv.org/abs/1502.05477), [NPG](https://papers.nips.cc/paper/2073-a-natural-policy-gradient.pdf) ## Practice -Go to `seminar_TRPO_.ipynb` and follow instructions in the notebook. +* Seminar: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week09_policy_II/seminar_TRPO_pytorch.ipynb) +* Homework: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week09_policy_II/ppo.ipynb) ## More: Reinforcement learning in large/continuous action spaces While you already know algorithms that will work with continuously many actions, it can't hurt to learn something more specialized. diff --git a/week09_policy_II/mujoco_wrappers.py b/week09_policy_II/mujoco_wrappers.py new file mode 100644 index 000000000..ea4c61c03 --- /dev/null +++ b/week09_policy_II/mujoco_wrappers.py @@ -0,0 +1,97 @@ +""" MuJoCo env wrappers. """ +# Adapted from https://github.com/openai/baselines +import gym +import numpy as np + + +class RunningMeanVar: + """ Computes running mean and variance. + + Args: + eps (float): a small constant used to initialize mean to zero and + variance to 1. + shape tuple(int): shape of the statistics. + """ + # https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Parallel_algorithm + def __init__(self, eps=1e-4, shape=()): + self.mean = np.zeros(shape) + self.var = np.ones(shape) + self.count = eps + + def update(self, batch): + """ Updates the running statistics given a batch of samples. """ + if not batch.shape[1:] == self.mean.shape: + raise ValueError(f"batch has invalid shape: {batch.shape}, " + f"expected shape {(None,) + self.mean.shape}") + batch_mean = np.mean(batch, axis=0) + batch_var = np.var(batch, axis=0) + batch_count = batch.shape[0] + self.update_from_moments(batch_mean, batch_var, batch_count) + + def update_from_moments(self, batch_mean, batch_var, batch_count): + """ Updates the running statistics given their new values on new data. """ + self.mean, self.var, self.count = update_mean_var_count_from_moments( + self.mean, self.var, self.count, batch_mean, batch_var, batch_count) + + +def update_mean_var_count_from_moments(mean, var, count, + batch_mean, batch_var, batch_count): + """ Updates running mean statistics given a new batch. """ + delta = batch_mean - mean + tot_count = count + batch_count + + new_mean = mean + delta * batch_count / tot_count + new_var = ( + var * (count / tot_count) + + batch_var * (batch_count / tot_count) + + np.square(delta) * (count * batch_count / tot_count ** 2)) + new_count = tot_count + + return new_mean, new_var, new_count + + +class Normalize(gym.Wrapper): + """ + A vectorized wrapper that normalizes the observations + and returns from an environment. + """ + # pylint: disable=too-many-arguments + def __init__(self, env, obs=True, ret=True, + clipobs=10., cliprew=10., gamma=0.99, eps=1e-8): + super().__init__(env) + self.obs_rmv = (RunningMeanVar(shape=self.observation_space.shape) + if obs else None) + self.ret_rmv = RunningMeanVar(shape=()) if ret else None + self.clipob = clipobs + self.cliprew = cliprew + self.ret = np.zeros(getattr(self.env.unwrapped, "nenvs", 1)) + self.gamma = gamma + self.eps = eps + + def observation(self, obs): + """ Preprocesses a given observation. """ + if not self.obs_rmv: + return obs + rmv_batch = (np.expand_dims(obs, 0) + if not hasattr(self.env.unwrapped, "nenvs") + else obs) + self.obs_rmv.update(rmv_batch) + obs = (obs - self.obs_rmv.mean) / np.sqrt(self.obs_rmv.var + self.eps) + obs = np.clip(obs, -self.clipob, self.clipob) + return obs + + def step(self, action): + obs, rews, resets, info = self.env.step(action) + self.ret = self.ret * self.gamma + rews + obs = self.observation(obs) + if self.ret_rmv: + self.ret_rmv.update(self.ret) + rews = np.clip(rews / np.sqrt(self.ret_rmv.var + self.eps), + -self.cliprew, self.cliprew) + self.ret[resets] = 0. + return obs, rews, resets, info + + def reset(self, **kwargs): + self.ret = np.zeros(getattr(self.env.unwrapped, "nenvs", 1)) + obs = self.env.reset(**kwargs) + return self.observation(obs) diff --git a/week09_policy_II/ppo.ipynb b/week09_policy_II/ppo.ipynb new file mode 100644 index 000000000..3eb0312d9 --- /dev/null +++ b/week09_policy_II/ppo.ipynb @@ -0,0 +1,516 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# # in google colab uncomment this\n", + "\n", + "# import os\n", + "\n", + "# os.system('apt-get install -y xvfb')\n", + "# os.system('wget https://raw.githubusercontent.com/yandexdataschool/Practical_DL/fall18/xvfb -O ../xvfb')\n", + "# os.system('apt-get install -y python-opengl ffmpeg')\n", + "# os.system('pip install pyglet==1.2.4')\n", + "# os.system('pip install gym')\n", + "\n", + "# prefix = 'https://raw.githubusercontent.com/yandexdataschool/Practical_RL/spring19/week06_policy_II/'\n", + "\n", + "# os.system('wget ' + prefix + 'runners.py')\n", + "# os.system('wget ' + prefix + 'mujoco_wrappers.py')\n", + "\n", + "# print('setup complete')\n", + "\n", + "# XVFB will be launched if you run on a server\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Implementing Proximal Policy Optimization \n", + "\n", + "\n", + "In this notebook you will be implementing Proximal Policy Optimization algorithm, \n", + "scaled up version of which was used to train [OpenAI Five](https://openai.com/blog/openai-five/) \n", + "to [win](https://openai.com/blog/how-to-train-your-openai-five/) against the\n", + "world champions in Dota 2.\n", + "You will be solving a continuous control environment on which it may be easier and faster \n", + "to train an agent, however note that PPO here may not be the best algorithm as, for example,\n", + "Deep Deterministic Policy Gradient and Soft Actor Critic may be more suited \n", + "for continuous control environments. To run the environment you will need to install \n", + "[pybullet-gym](https://github.com/benelot/pybullet-gym) which unlike MuJoCo \n", + "does not require you to have a license.\n", + "\n", + "To install the library:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!git clone https://github.com/benelot/pybullet-gym lib/pybullet-gym\n", + "!pip install -e lib/pybullet-gym" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The overall structure of the code is similar to the one in the A2C optional homework, but don't worry if you haven't done it, it should be relatively easy to figure it out. \n", + "First, we will create an instance of the environment. \n", + "We will normalize the observations and rewards, but before that you will need a wrapper that will \n", + "write summaries, mainly, the total reward during an episode. You can either use one for `TensorFlow` \n", + "implemented in `atari_wrappers.py` file from the optional A2C homework, or implement your own. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import gym \n", + "import pybulletgym\n", + "\n", + "env = gym.make(\"HalfCheetahMuJoCoEnv-v0\")\n", + "print(\"observation space: \", env.observation_space,\n", + " \"\\nobservations:\", env.reset())\n", + "print(\"action space: \", env.action_space, \n", + " \"\\naction_sample: \", env.action_space.sample())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Summaries(gym.Wrapper):\n", + " \"\"\" Wrapper to write summaries. \"\"\"\n", + " def step(self, action):\n", + " # TODO: implement writing summaries\n", + " return self.env.step(action)\n", + " \n", + " def reset(self, **kwargs):\n", + " # TODO: implement writing summaries\n", + " return self.env.reset(**kwargs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The normalization wrapper will subtract running mean from observations and rewards and divide \n", + "the resulting quantities by the running variances." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mujoco_wrappers import Normalize\n", + "\n", + "env = Normalize(Summaries(gym.make(\"HalfCheetahMuJoCoEnv-v0\")));\n", + "env.unwrapped.seed(0);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, you will need to define a model for training. We suggest that you use two separate networks: one for policy\n", + "and another for value function. Each network should be a 3-layer MLP with 64 hidden units, $\\mathrm{tanh}$ \n", + "activation function, kernel matrices initialized with orthogonal initializer with parameter $\\sqrt{2}$\n", + "and biases initialized with zeros. \n", + "\n", + "Our policy distribution is going to be multivariate normal with diagonal covariance. \n", + "The network from above will predict the mean, and the covariance should be represented by a single \n", + "(learned) vector of size 6 (corresponding to the dimensionality of the action space from above). \n", + "You should initialize this vector to zero and take the exponent of it to always\n", + "have a non-negative quantity. \n", + "\n", + "Overall the model should return three things: predicted mean of the distribution, variance vector, \n", + "value function. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# import tensorflow as tf\n", + "# import torch\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This model will be wrapped by a `Policy`. The policy can work in two modes, but in either case \n", + "it is going to return dictionary with string-type keys. The first mode is when the policy is \n", + "used to sample actions for a trajectory which will later be used for training. In this case \n", + "the flag `training` passed to `act` method is `False` and the method should return \n", + "a `dict` with the following keys: \n", + "\n", + "* `\"actions\"`: actions to pass to the environment\n", + "* `\"log_probs\"`: log-probabilities of sampled actions\n", + "* `\"values\"`: value function $V^\\pi(s)$ predictions.\n", + "\n", + "We don't need to use the values under these keys for training, so all of them should be of type `np.ndarray`.\n", + "\n", + "When `training` is `True`, the model is training on a given batch of observations. In this\n", + "case it should return a `dict` with the following keys\n", + "\n", + "* `\"distribution\"`: an instance of multivariate normal distribution (`torch.distributions.MultivariateNormal` or `tf.distributions.MultivariateNormalDiag`)\n", + "* `\"values\"`: value function $V^\\pi(s)$ prediction.\n", + "\n", + "The distinction about the modes comes into play depending on where the policy is used: if it is called from `EnvRunner`, \n", + "the `training` flag is `False`, if it is called from `PPO`, the `training` flag is `True`. These classed \n", + "will be described below. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Policy:\n", + " def __init__(self, model):\n", + " self.model = model\n", + " \n", + " def act(self, inputs, training=False):\n", + " \n", + " # Should return a dict." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will use `EnvRunner` to perform interactions with an environment with a policy for a fixed number of timesteps. Calling `.get_next()` on a runner will return a trajectory — dictionary \n", + "containing keys\n", + "\n", + "* `\"observations\"`\n", + "* `\"rewards\"` \n", + "* `\"resets\"`\n", + "* `\"actions\"`\n", + "* all other keys that you defined in `Policy`,\n", + "\n", + "under each of these keys there is a `np.ndarray` of specified length $T$ — the size of partial trajectory. \n", + "\n", + "Additionally, before returning a trajectory this runner can apply a list of transformations. \n", + "Each transformation is simply a callable that should modify passed trajectory in-place." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class AsArray:\n", + " \"\"\" \n", + " Converts lists of interactions to ndarray.\n", + " \"\"\"\n", + " def __call__(self, trajectory):\n", + " # Modify trajectory inplace. \n", + " for k, v in filter(lambda kv: kv[0] != \"state\",\n", + " trajectory.items()):\n", + " trajectory[k] = np.asarray(v)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from runners import EnvRunner\n", + "\n", + "class DummyPolicy:\n", + " def act(self, inputs, training=False):\n", + " assert not training\n", + " return {\"actions\": np.random.randn(6), \"values\": np.nan}\n", + " \n", + "runner = EnvRunner(env, DummyPolicy(), 3,\n", + " transforms=[AsArray()])\n", + "trajectory = runner.get_next()\n", + "\n", + "{k: v.shape for k, v in trajectory.items() if k != \"state\"}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You will need to implement the following two transformations. \n", + "\n", + "The first is `GAE` that implements [Generalized Advantage Estimator](https://arxiv.org/abs/1506.02438).\n", + "In it you should add two keys to the trajectory: `\"advantages\"` and `\"value_targets\"`. In GAE the advantages\n", + "$A_t^{\\mathrm{GAE}(\\gamma,\\lambda)}$ are essentially defined as the exponential \n", + "moving average with parameter $\\lambda$ of the regular advantages \n", + "$\\hat{A}^{(n)}(s_t) = \\sum_{l=0}^{T-1} \\gamma^l r_{t+l} + \\gamma^{T} V^\\pi(s_{t+l}) - V^\\pi(s_t)$. \n", + "The exact formula for the computation is the following\n", + "\n", + "$$\n", + "A_t^{\\mathrm{GAE}(\\gamma,\\lambda)} = \\sum_{l=0}^{T-1} (\\gamma\\lambda)^l\\delta_{t + l}^V,\n", + "$$\n", + "where $\\delta_{t+l}^V = r_{t+l} + \\gamma V^\\pi(s_{t+l+1}) - V^\\pi(s_{t+l})$. You can look at the \n", + "derivation (formulas 11-16) in the paper. Don't forget to reset the summation on terminal\n", + "states as determined by the flags `trajectory[\"resets\"]`. You can use `trajectory[\"values\"]`\n", + "to get values of all observations except the most recent which is stored under \n", + " `trajectory[\"state\"][\"latest_observation\"]`. For this observation you will need to call the policy \n", + " to get the value prediction.\n", + "\n", + "Once you computed the advantages, you can get the targets for training the value function by adding \n", + "back values:\n", + "$$\n", + "\\hat{V}(s_{t+l}) = A_{t+l}^{\\mathrm{GAE}(\\gamma,\\lambda)} + V(s_{t + l}),\n", + "$$\n", + "where $\\hat{V}$ is a tensor of value targets that are used to train the value function. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class GAE:\n", + " \"\"\" Generalized Advantage Estimator. \"\"\"\n", + " def __init__(self, policy, gamma=0.99, lambda_=0.95):\n", + " self.policy = policy\n", + " self.gamma = gamma\n", + " self.lambda_ = lambda_\n", + " \n", + " def __call__(self, trajectory):\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The main advantage of PPO over simpler policy based methods like A2C is that it is possible\n", + "to train on the same trajectory for multiple gradient steps. The following class wraps \n", + "an `EnvRunner`. It should call the runner to get a trajectory, then return minibatches \n", + "from it for a number of epochs, shuffling the data before each epoch." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class TrajectorySampler:\n", + " \"\"\" Samples minibatches from trajectory for a number of epochs. \"\"\"\n", + " def __init__(self, runner, num_epochs, num_minibatches, transforms=None):\n", + " self.runner = runner\n", + " self.num_epochs = num_epochs\n", + " self.num_minibatches = num_minibatches\n", + " self.transforms = transforms or []\n", + " self.minibatch_count = 0\n", + " self.epoch_count = 0\n", + " self.trajectory = None\n", + " \n", + " def shuffle_trajectory(self):\n", + " \"\"\" Shuffles all elements in trajectory.\n", + " \n", + " Should be called at the beginning of each epoch.\n", + " \"\"\"\n", + " \n", + " \n", + " def get_next(self):\n", + " \"\"\" Returns next minibatch. \"\"\"\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A common trick to use with GAE is to normalize advantages, the following transformation does that. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class NormalizeAdvantages:\n", + " \"\"\" Normalizes advantages to have zero mean and variance 1. \"\"\"\n", + " def __call__(self, trajectory):\n", + " adv = trajectory[\"advantages\"]\n", + " adv = (adv - adv.mean()) / (adv.std() + 1e-8)\n", + " trajectory[\"advantages\"] = adv" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, we can create our PPO runner. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def make_ppo_runner(env, policy, num_runner_steps=2048,\n", + " gamma=0.99, lambda_=0.95, \n", + " num_epochs=10, num_minibatches=32):\n", + " \"\"\" Creates runner for PPO algorithm. \"\"\"\n", + " runner_transforms = [AsArray(),\n", + " GAE(policy, gamma=gamma, lambda_=lambda_)]\n", + " runner = EnvRunner(env, policy, num_runner_steps, \n", + " transforms=runner_transforms)\n", + " \n", + " sampler_transforms = [NormalizeAdvantages()]\n", + " sampler = TrajectorySampler(runner, num_epochs=num_epochs, \n", + " num_minibatches=num_minibatches,\n", + " transforms=sampler_transforms)\n", + " return sampler" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the next cell you will need to implement Proximal Policy Optimization algorithm itself. The algorithm\n", + "modifies the typical policy gradient loss in the following way:\n", + "\n", + "$$\n", + "L_{\\pi} = \\frac{1}{T-1}\\sum_{l=0}^{T-1}\n", + "\\frac{\\pi_\\theta(a_{t+l}|s_{t+l})}{\\pi_\\theta^{\\text{old}}(a_{t+l}|s_{t+l})}\n", + "A^{\\mathrm{GAE}(\\gamma,\\lambda)}_{t+l}\\\\\n", + "L_{\\pi}^{\\text{clipped}} = \\frac{1}{T-1}\\sum_{l=0}^{T-1}\\mathrm{clip}\\left(\n", + "\\frac{\\pi_\\theta(a_{t+l}|s_{t+l})}{\\pi_{\\theta^{\\text{old}}}(a_{t+l}|s_{t+l})}\n", + "\\cdot A^{\\mathrm{GAE(\\gamma, \\lambda)}}_{t+l},\n", + "1 - \\text{cliprange}, 1 + \\text{cliprange}\\right)\\\\\n", + "L_{\\text{policy}} = \\max\\left(L_\\pi, L_{\\pi}^{\\text{clipped}}\\right).\n", + "$$\n", + "\n", + "Additionally, the value loss is modified in the following way:\n", + "\n", + "$$\n", + "L_V = \\frac{1}{T-1}\\sum_{l=0}^{T-1}(V_\\theta(s_{t+l}) - \\hat{V}(s_{t+l}))^2\\\\\n", + "L_{V}^{\\text{clipped}} = \\frac{1}{T-1}\\sum_{l=0}^{T-1}\n", + "V_{\\theta^{\\text{old}}}(s_{t+l}) +\n", + "\\text{clip}\\left(\n", + "V_\\theta(s_{t+l}) - V_{\\theta^\\text{old}}(s_{t+l}),\n", + "-\\text{cliprange}, \\text{cliprange}\n", + "\\right)\\\\\n", + "L_{\\text{value}} = \\max\\left(L_V, L_V^{\\text{clipped}}\\right).\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class PPO:\n", + " def __init__(self, policy, optimizer,\n", + " cliprange=0.2,\n", + " value_loss_coef=0.25,\n", + " max_grad_norm=0.5):\n", + " self.policy = policy\n", + " self.optimizer = optimizer\n", + " self.cliprange = cliprange\n", + " self.value_loss_coef = value_loss_coef\n", + " # Note that we don't need entropy regularization for this env.\n", + " self.max_grad_norm = max_grad_norm\n", + " \n", + " def policy_loss(self, trajectory, act):\n", + " \"\"\" Computes and returns policy loss on a given trajectory. \"\"\"\n", + " \n", + " \n", + " def value_loss(self, trajectory, act):\n", + " \"\"\" Computes and returns value loss on a given trajectory. \"\"\"\n", + " \n", + " \n", + " def loss(self, trajectory):\n", + " act = self.policy.act(trajectory[\"observations\"], training=True)\n", + " policy_loss = self.policy_loss(trajectory, act)\n", + " value_loss = self.value_loss(trajectory, act)\n", + " return policy_loss + self.value_loss_coef * value_loss\n", + " \n", + " def step(self, trajectory):\n", + " \"\"\" Computes the loss function and performs a single gradient step. \"\"\"\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now everything is ready to do training. In one million of interactions it should be possible to \n", + "achieve the total raw reward of about 1500. You should plot this quantity with respect to \n", + "`runner.step_var` — the number of interactions with the environment. It is highly \n", + "encouraged to also provide plots of the following quantities (these are useful for debugging as well):\n", + "\n", + "* [Coefficient of Determination](https://en.wikipedia.org/wiki/Coefficient_of_determination) between \n", + "value targets and value predictions\n", + "* Entropy of the policy $\\pi$\n", + "* Value loss\n", + "* Policy loss\n", + "* Value targets\n", + "* Value predictions\n", + "* Gradient norm\n", + "* Advantages\n", + "\n", + "For optimization it is suggested to use Adam optimizer with linearly annealing learning rate \n", + "from 3e-4 to 0 and epsilon 1e-5." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "rl", + "language": "python", + "name": "rl" + }, + "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.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week09_policy_II/runners.py b/week09_policy_II/runners.py new file mode 120000 index 000000000..4c11d0d4f --- /dev/null +++ b/week09_policy_II/runners.py @@ -0,0 +1 @@ +../week06_policy_based/runners.py \ No newline at end of file diff --git a/week09_policy_II/seminar_TRPO_pytorch.ipynb b/week09_policy_II/seminar_TRPO_pytorch.ipynb new file mode 100644 index 000000000..4fa31fe2a --- /dev/null +++ b/week09_policy_II/seminar_TRPO_pytorch.ipynb @@ -0,0 +1,839 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# # in google colab uncomment this\n", + "\n", + "# import os\n", + "\n", + "# os.system('apt-get install -y xvfb')\n", + "# os.system('wget https://raw.githubusercontent.com/yandexdataschool/Practical_DL/fall18/xvfb -O ../xvfb')\n", + "# os.system('apt-get install -y python-opengl ffmpeg')\n", + "# os.system('pip install pyglet==1.2.4')\n", + "\n", + "# launch XVFB if you run on a server\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's make a TRPO!\n", + "\n", + "In this notebook we will write the code of the one Trust Region Policy Optimization.\n", + "As usually, it contains a few different parts which we are going to reproduce.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "from torch.autograd import Variable" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mWARN: gym.spaces.Box autodetected dtype as . Please provide explicit dtype.\u001b[0m\n", + "Observation Space Box(6,)\n", + "Action Space Discrete(3)\n" + ] + } + ], + "source": [ + "import gym\n", + "\n", + "env = gym.make(\"Acrobot-v1\")\n", + "env.reset()\n", + "observation_shape = env.observation_space.shape\n", + "n_actions = env.action_space.n\n", + "print(\"Observation Space\", env.observation_space)\n", + "print(\"Action Space\", env.action_space)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "plt.imshow(env.render('rgb_array'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 1: Defining a network\n", + "\n", + "With all it's complexity, at it's core TRPO is yet another policy gradient method. \n", + "\n", + "This essentially means we're actually training a stochastic policy $ \\pi_\\theta(a|s) $. \n", + "\n", + "And yes, it's gonna be a neural network. So let's start by defining one." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class TRPOAgent(nn.Module):\n", + " def __init__(self, state_shape, n_actions, hidden_size=32):\n", + " '''\n", + " Here you should define your model\n", + " You should have LOG-PROBABILITIES as output because you will need it to compute loss\n", + " We recommend that you start simple: \n", + " use 1-2 hidden layers with 100-500 units and relu for the first try\n", + " '''\n", + " nn.Module.__init__(self)\n", + "\n", + " \n", + " self.model = None\n", + "\n", + " def forward(self, states):\n", + " \"\"\"\n", + " takes agent's observation (Variable), returns log-probabilities (Variable)\n", + " :param state_t: a batch of states, shape = [batch_size, state_shape]\n", + " \"\"\"\n", + "\n", + " # Use your network to compute log_probs for given state\n", + " log_probs = self.model(states)\n", + " return log_probs\n", + "\n", + " def get_log_probs(self, states):\n", + " '''\n", + " Log-probs for training\n", + " '''\n", + "\n", + " return self.forward(states)\n", + "\n", + " def get_probs(self, states):\n", + " '''\n", + " Probs for interaction\n", + " '''\n", + "\n", + " return torch.exp(self.forward(states))\n", + "\n", + " def act(self, obs, sample=True):\n", + " '''\n", + " Samples action from policy distribution (sample = True) or takes most likely action (sample = False)\n", + " :param: obs - single observation vector\n", + " :param sample: if True, samples from \\pi, otherwise takes most likely action\n", + " :returns: action (single integer) and probabilities for all actions\n", + " '''\n", + "\n", + " probs = self.get_probs(Variable(torch.FloatTensor([obs]))).data.numpy()\n", + "\n", + " if sample:\n", + " action = int(np.random.choice(n_actions, p=probs[0]))\n", + " else:\n", + " action = int(np.argmax(probs))\n", + "\n", + " return action, probs[0]\n", + "\n", + "\n", + "agent = TRPOAgent(observation_shape, n_actions)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled: [(2, array([0.35253003, 0.37892205, 0.26854792], dtype=float32)), (2, array([0.35269254, 0.37673423, 0.27057323], dtype=float32)), (0, array([0.35406563, 0.37682924, 0.26910514], dtype=float32)), (0, array([0.3560282 , 0.37561142, 0.2683604 ], dtype=float32)), (1, array([0.35539204, 0.37685862, 0.26774937], dtype=float32))]\n", + "greedy: [(1, array([0.3518883 , 0.37830737, 0.2698043 ], dtype=float32)), (1, array([0.3544095 , 0.37609497, 0.26949552], dtype=float32)), (1, array([0.35528135, 0.37493262, 0.269786 ], dtype=float32)), (1, array([0.3589018 , 0.37457928, 0.26651892], dtype=float32)), (1, array([0.35414994, 0.3769723 , 0.26887777], dtype=float32))]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.5/dist-packages/torch/nn/modules/container.py:67: UserWarning: Implicit dimension choice for log_softmax has been deprecated. Change the call to include dim=X as an argument.\n", + " input = module(input)\n" + ] + } + ], + "source": [ + "# Check if log-probabilities satisfies all the requirements\n", + "log_probs = agent.get_log_probs(Variable(torch.FloatTensor([env.reset()])))\n", + "assert isinstance(\n", + " log_probs, Variable) and log_probs.requires_grad, \"qvalues must be a torch variable with grad\"\n", + "assert len(\n", + " log_probs.shape) == 2 and log_probs.shape[0] == 1 and log_probs.shape[1] == n_actions\n", + "sums = torch.sum(torch.exp(log_probs), dim=1)\n", + "assert (0.999 < sums).all() and (1.001 > sums).all()\n", + "\n", + "# Demo use\n", + "print(\"sampled:\", [agent.act(env.reset()) for _ in range(5)])\n", + "print(\"greedy:\", [agent.act(env.reset(), sample=False) for _ in range(5)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Flat parameters operations\n", + "\n", + "We are going to use it" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def get_flat_params_from(model):\n", + " params = []\n", + " for param in model.parameters():\n", + " params.append(param.data.view(-1))\n", + "\n", + " flat_params = torch.cat(params)\n", + " return flat_params\n", + "\n", + "\n", + "def set_flat_params_to(model, flat_params):\n", + " prev_ind = 0\n", + " for param in model.parameters():\n", + " flat_size = int(np.prod(list(param.size())))\n", + " param.data.copy_(\n", + " flat_params[prev_ind:prev_ind + flat_size].view(param.size()))\n", + " prev_ind += flat_size" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Compute cummulative reward just like you did in vanilla REINFORCE" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import scipy.signal\n", + "\n", + "\n", + "def get_cummulative_returns(r, gamma=1):\n", + " \"\"\"\n", + " Computes cummulative discounted rewards given immediate rewards\n", + " G_i = r_i + gamma*r_{i+1} + gamma^2*r_{i+2} + ...\n", + " Also known as R(s,a).\n", + " \"\"\"\n", + " r = np.array(r)\n", + " assert r.ndim >= 1\n", + " return scipy.signal.lfilter([1], [1, -gamma], r[::-1], axis=0)[::-1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# simple demo on rewards [0,0,1,0,0,1]\n", + "get_cummulative_returns([0, 0, 1, 0, 0, 1], gamma=0.9)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Rollout**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def rollout(env, agent, max_pathlength=2500, n_timesteps=50000):\n", + " \"\"\"\n", + " Generate rollouts for training.\n", + " :param: env - environment in which we will make actions to generate rollouts.\n", + " :param: act - the function that can return policy and action given observation.\n", + " :param: max_pathlength - maximum size of one path that we generate.\n", + " :param: n_timesteps - total sum of sizes of all pathes we generate.\n", + " \"\"\"\n", + " paths = []\n", + "\n", + " total_timesteps = 0\n", + " while total_timesteps < n_timesteps:\n", + " obervations, actions, rewards, action_probs = [], [], [], []\n", + " obervation = env.reset()\n", + " for _ in range(max_pathlength):\n", + " action, policy = agent.act(obervation)\n", + " obervations.append(obervation)\n", + " actions.append(action)\n", + " action_probs.append(policy)\n", + " obervation, reward, done, _ = env.step(action)\n", + " rewards.append(reward)\n", + " total_timesteps += 1\n", + " if done or total_timesteps == n_timesteps:\n", + " path = {\"observations\": np.array(obervations),\n", + " \"policy\": np.array(action_probs),\n", + " \"actions\": np.array(actions),\n", + " \"rewards\": np.array(rewards),\n", + " \"cumulative_returns\": get_cummulative_returns(rewards),\n", + " }\n", + " paths.append(path)\n", + " break\n", + " return paths" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "paths = rollout(env, agent, max_pathlength=5, n_timesteps=100)\n", + "print(paths[-1])\n", + "assert (paths[0]['policy'].shape == (5, n_actions))\n", + "assert (paths[0]['cumulative_returns'].shape == (5,))\n", + "assert (paths[0]['rewards'].shape == (5,))\n", + "assert (paths[0]['observations'].shape == (5,)+observation_shape)\n", + "assert (paths[0]['actions'].shape == (5,))\n", + "print('It\\'s ok')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 3: Auxiliary functions\n", + "\n", + "Now let's define the loss functions and something else for actual TRPO training." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The surrogate reward should be\n", + "$$J_{surr}= {1 \\over N} \\sum\\limits_{i=0}^N \\frac{\\pi_{\\theta}(s_i, a_i)}{\\pi_{\\theta_{old}}(s_i, a_i)}A_{\\theta_{old}(s_i, a_i)}$$\n", + "\n", + "For simplicity, let's use cummulative returns instead of advantage for now:\n", + "$$J'_{surr}= {1 \\over N} \\sum\\limits_{i=0}^N \\frac{\\pi_{\\theta}(s_i, a_i)}{\\pi_{\\theta_{old}}(s_i, a_i)}G_{\\theta_{old}(s_i, a_i)}$$\n", + "\n", + "Or alternatively, minimize the surrogate loss:\n", + "$$ L_{surr} = - J'_{surr} $$ \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def get_loss(agent, observations, actions, cummulative_returns, old_probs):\n", + " \"\"\"\n", + " Computes TRPO objective\n", + " :param: observations - batch of observations\n", + " :param: actions - batch of actions\n", + " :param: cummulative_returns - batch of cummulative returns\n", + " :param: old_probs - batch of probabilities computed by old network\n", + " :returns: scalar value of the objective function\n", + " \"\"\"\n", + " batch_size = observations.shape[0]\n", + " log_probs_all = agent.get_log_probs(observations)\n", + " probs_all = torch.exp(log_probs_all)\n", + "\n", + " probs_for_actions = probs_all[torch.arange(\n", + " 0, batch_size, out=torch.LongTensor()), actions]\n", + " old_probs_for_actions = old_probs[torch.arange(\n", + " 0, batch_size, out=torch.LongTensor()), actions]\n", + "\n", + " # Compute surrogate loss, aka importance-sampled policy gradient\n", + " Loss = \n", + "\n", + " assert Loss.shape == torch.Size([])\n", + " return Loss" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can ascend these gradients as long as our $pi_\\theta(a|s)$ satisfies the constraint\n", + "$$E_{s,\\pi_{\\Theta_{t}}}\\Big[KL(\\pi(\\Theta_{t}, s) \\:||\\:\\pi(\\Theta_{t+1}, s))\\Big]< \\alpha$$\n", + "\n", + "\n", + "where\n", + "\n", + "$$KL(p||q) = E _p log({p \\over q})$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def get_kl(agent, observations, actions, cummulative_returns, old_probs):\n", + " \"\"\"\n", + " Computes KL-divergence between network policy and old policy\n", + " :param: observations - batch of observations\n", + " :param: actions - batch of actions\n", + " :param: cummulative_returns - batch of cummulative returns (we don't need it actually)\n", + " :param: old_probs - batch of probabilities computed by old network\n", + " :returns: scalar value of the KL-divergence\n", + " \"\"\"\n", + " batch_size = observations.shape[0]\n", + " log_probs_all = agent.get_log_probs(observations)\n", + " probs_all = torch.exp(log_probs_all)\n", + "\n", + " # Compute Kullback-Leibler divergence (see formula above)\n", + " # Note: you need to sum KL and entropy over all actions, not just the ones agent took\n", + " old_log_probs = torch.log(old_probs+1e-10)\n", + "\n", + " kl = \n", + "\n", + " assert kl.shape == torch.Size([])\n", + " assert (kl > -0.0001).all() and (kl < 10000).all()\n", + " return kl" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def get_entropy(agent, observations):\n", + " \"\"\"\n", + " Computes entropy of the network policy \n", + " :param: observations - batch of observations\n", + " :returns: scalar value of the entropy\n", + " \"\"\"\n", + "\n", + " observations = Variable(torch.FloatTensor(observations))\n", + "\n", + " batch_size = observations.shape[0]\n", + " log_probs_all = agent.get_log_probs(observations)\n", + " probs_all = torch.exp(log_probs_all)\n", + "\n", + " entropy = torch.sum(-probs_all * log_probs_all) / batch_size\n", + "\n", + " assert entropy.shape == torch.Size([])\n", + " return entropy" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Linear search**\n", + "\n", + "TRPO in its core involves ascending surrogate policy gradient constrained by KL divergence. \n", + "\n", + "In order to enforce this constraint, we're gonna use linesearch. You can find out more about it [here](https://en.wikipedia.org/wiki/Linear_search)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def linesearch(f, x, fullstep, max_kl):\n", + " \"\"\"\n", + " Linesearch finds the best parameters of neural networks in the direction of fullstep contrainted by KL divergence.\n", + " :param: f - function that returns loss, kl and arbitrary third component.\n", + " :param: x - old parameters of neural network.\n", + " :param: fullstep - direction in which we make search.\n", + " :param: max_kl - constraint of KL divergence.\n", + " :returns:\n", + " \"\"\"\n", + " max_backtracks = 10\n", + " loss, _, = f(x)\n", + " for stepfrac in .5**np.arange(max_backtracks):\n", + " xnew = x + stepfrac * fullstep\n", + " new_loss, kl = f(xnew)\n", + " actual_improve = new_loss - loss\n", + " if kl.data.numpy() <= max_kl and actual_improve.data.numpy() < 0:\n", + " x = xnew\n", + " loss = new_loss\n", + " return x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Conjugate gradients**\n", + "\n", + "Since TRPO includes contrainted optimization, we will need to solve Ax=b using conjugate gradients.\n", + "\n", + "In general, CG is an algorithm that solves Ax=b where A is positive-defined. A is Hessian matrix so A is positive-defined. You can find out more about them [here](https://en.wikipedia.org/wiki/Conjugate_gradient_method)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from numpy.linalg import inv\n", + "\n", + "\n", + "def conjugate_gradient(f_Ax, b, cg_iters=10, residual_tol=1e-10):\n", + " \"\"\"\n", + " This method solves system of equation Ax=b using iterative method called conjugate gradients\n", + " :f_Ax: function that returns Ax\n", + " :b: targets for Ax\n", + " :cg_iters: how many iterations this method should do\n", + " :residual_tol: epsilon for stability\n", + " \"\"\"\n", + " p = b.clone()\n", + " r = b.clone()\n", + " x = torch.zeros(b.size())\n", + " rdotr = torch.sum(r*r)\n", + " for i in range(cg_iters):\n", + " z = f_Ax(p)\n", + " v = rdotr / (torch.sum(p*z) + 1e-8)\n", + " x += v * p\n", + " r -= v * z\n", + " newrdotr = torch.sum(r*r)\n", + " mu = newrdotr / (rdotr + 1e-8)\n", + " p = r + mu * p\n", + " rdotr = newrdotr\n", + " if rdotr < residual_tol:\n", + " break\n", + " return x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# This code validates conjugate gradients\n", + "A = np.random.rand(8, 8)\n", + "A = np.matmul(np.transpose(A), A)\n", + "\n", + "\n", + "def f_Ax(x):\n", + " return torch.matmul(torch.FloatTensor(A), x.view((-1, 1))).view(-1)\n", + "\n", + "\n", + "b = np.random.rand(8)\n", + "\n", + "w = np.matmul(np.matmul(inv(np.matmul(np.transpose(A), A)),\n", + " np.transpose(A)), b.reshape((-1, 1))).reshape(-1)\n", + "print(w)\n", + "print(conjugate_gradient(f_Ax, torch.FloatTensor(b)).numpy())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 4: training\n", + "In this section we construct the whole update step function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def update_step(agent, observations, actions, cummulative_returns, old_probs, max_kl):\n", + " \"\"\"\n", + " This function does the TRPO update step\n", + " :param: observations - batch of observations\n", + " :param: actions - batch of actions\n", + " :param: cummulative_returns - batch of cummulative returns\n", + " :param: old_probs - batch of probabilities computed by old network\n", + " :param: max_kl - controls how big KL divergence may be between old and new policy every step.\n", + " :returns: KL between new and old policies and the value of the loss function.\n", + " \"\"\"\n", + "\n", + " # Here we prepare the information\n", + " observations = Variable(torch.FloatTensor(observations))\n", + " actions = torch.LongTensor(actions)\n", + " cummulative_returns = Variable(torch.FloatTensor(cummulative_returns))\n", + " old_probs = Variable(torch.FloatTensor(old_probs))\n", + "\n", + " # Here we compute gradient of the loss function\n", + " loss = get_loss(agent, observations, actions,\n", + " cummulative_returns, old_probs)\n", + " grads = torch.autograd.grad(loss, agent.parameters())\n", + " loss_grad = torch.cat([grad.view(-1) for grad in grads]).data\n", + "\n", + " def Fvp(v):\n", + " # Here we compute Fx to do solve Fx = g using conjugate gradients\n", + " # We actually do here a couple of tricks to compute it efficiently\n", + "\n", + " kl = get_kl(agent, observations, actions,\n", + " cummulative_returns, old_probs)\n", + "\n", + " grads = torch.autograd.grad(kl, agent.parameters(), create_graph=True)\n", + " flat_grad_kl = torch.cat([grad.view(-1) for grad in grads])\n", + "\n", + " kl_v = (flat_grad_kl * Variable(v)).sum()\n", + " grads = torch.autograd.grad(kl_v, agent.parameters())\n", + " flat_grad_grad_kl = torch.cat(\n", + " [grad.contiguous().view(-1) for grad in grads]).data\n", + "\n", + " return flat_grad_grad_kl + v * 0.1\n", + "\n", + " # Here we solveolve Fx = g system using conjugate gradients\n", + " stepdir = conjugate_gradient(Fvp, -loss_grad, 10)\n", + "\n", + " # Here we compute the initial vector to do linear search\n", + " shs = 0.5 * (stepdir * Fvp(stepdir)).sum(0, keepdim=True)\n", + "\n", + " lm = torch.sqrt(shs / max_kl)\n", + " fullstep = stepdir / lm[0]\n", + "\n", + " neggdotstepdir = (-loss_grad * stepdir).sum(0, keepdim=True)\n", + "\n", + " # Here we get the start point\n", + " prev_params = get_flat_params_from(agent)\n", + "\n", + " def get_loss_kl(params):\n", + " # Helper for linear search\n", + " set_flat_params_to(agent, params)\n", + " return [get_loss(agent, observations, actions, cummulative_returns, old_probs),\n", + " get_kl(agent, observations, actions, cummulative_returns, old_probs)]\n", + "\n", + " # Here we find our new parameters\n", + " new_params = linesearch(get_loss_kl, prev_params, fullstep, max_kl)\n", + "\n", + " # And we set it to our network\n", + " set_flat_params_to(agent, new_params)\n", + "\n", + " return get_loss_kl(new_params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Step 5: Main TRPO loop\n", + "\n", + "Here we will train our network!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import time\n", + "from itertools import count\n", + "from collections import OrderedDict\n", + "\n", + "# this is hyperparameter of TRPO. It controls how big KL divergence may be between old and new policy every step.\n", + "max_kl = 0.01\n", + "numeptotal = 0 # this is number of episodes that we played.\n", + "\n", + "start_time = time.time()\n", + "\n", + "for i in count(1):\n", + "\n", + " print(\"\\n********** Iteration %i ************\" % i)\n", + "\n", + " # Generating paths.\n", + " print(\"Rollout\")\n", + " paths = rollout(env, agent)\n", + " print(\"Made rollout\")\n", + "\n", + " # Updating policy.\n", + " observations = np.concatenate([path[\"observations\"] for path in paths])\n", + " actions = np.concatenate([path[\"actions\"] for path in paths])\n", + " returns = np.concatenate([path[\"cumulative_returns\"] for path in paths])\n", + " old_probs = np.concatenate([path[\"policy\"] for path in paths])\n", + "\n", + " loss, kl = update_step(agent, observations, actions,\n", + " returns, old_probs, max_kl)\n", + "\n", + " # Report current progress\n", + " episode_rewards = np.array([path[\"rewards\"].sum() for path in paths])\n", + "\n", + " stats = OrderedDict()\n", + " numeptotal += len(episode_rewards)\n", + " stats[\"Total number of episodes\"] = numeptotal\n", + " stats[\"Average sum of rewards per episode\"] = episode_rewards.mean()\n", + " stats[\"Std of rewards per episode\"] = episode_rewards.std()\n", + " stats[\"Time elapsed\"] = \"%.2f mins\" % ((time.time() - start_time)/60.)\n", + " stats[\"KL between old and new distribution\"] = kl.data.numpy()\n", + " stats[\"Entropy\"] = get_entropy(agent, observations).data.numpy()\n", + " stats[\"Surrogate loss\"] = loss.data.numpy()\n", + " for k, v in stats.items():\n", + " print(k + \": \" + \" \" * (40 - len(k)) + str(v))\n", + " i += 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Homework option I: better sampling (10+pts)\n", + "\n", + "In this section, you're invited to implement a better rollout strategy called _vine_.\n", + "\n", + "![img](https://s17.postimg.org/i90chxgvj/vine.png)\n", + "\n", + "In most gym environments, you can actually backtrack by using states. You can find a wrapper that saves/loads states in [the mcts seminar](https://github.com/yandexdataschool/Practical_RL/blob/spring19/week10_planning/seminar_MCTS.ipynb).\n", + "\n", + "You can read more about in the [TRPO article](https://arxiv.org/abs/1502.05477) in section 5.2.\n", + "\n", + "The goal here is to implement such rollout policy (we recommend using tree data structure like in the seminar above).\n", + "Then you can assign cummulative rewards similar to `get_cummulative_rewards`, but for a tree.\n", + "\n", + "__bonus task__ - parallelize samples using multiple cores" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "# Homework option II (10+pts)\n", + "\n", + "Let's use TRPO to train evil robots! (pick any of two)\n", + "* [MuJoCo robots](https://gym.openai.com/envs#mujoco)\n", + "* [Box2d robot](https://gym.openai.com/envs/BipedalWalker-v2)\n", + "\n", + "The catch here is that those environments have continuous action spaces. \n", + "\n", + "Luckily, TRPO is a policy gradient method, so it's gonna work for any parametric $\\pi_\\theta(a|s)$. We recommend starting with gaussian policy:\n", + "\n", + "$$\\pi_\\theta(a|s) = N(\\mu_\\theta(s),\\sigma^2_\\theta(s)) = {1 \\over \\sqrt { 2 \\pi {\\sigma^2}_\\theta(s) } } e^{ (a - \n", + "\\mu_\\theta(s))^2 \\over 2 {\\sigma^2}_\\theta(s) } $$\n", + "\n", + "In the $\\sqrt { 2 \\pi {\\sigma^2}_\\theta(s) }$ clause, $\\pi$ means ~3.1415926, not agent's policy.\n", + "\n", + "This essentially means that you will need two output layers:\n", + "* $\\mu_\\theta(s)$, a dense layer with linear activation\n", + "* ${\\sigma^2}_\\theta(s)$, a dense layer with activation tf.exp (to make it positive; like rho from bandits)\n", + "\n", + "For multidimensional actions, you can use fully factorized gaussian (basically a vector of gaussians).\n", + "\n", + "__bonus task__: compare performance of continuous action space method to action space discretization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.6.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week09_policy_II/seminar_TRPO_tensorflow.ipynb b/week09_policy_II/seminar_TRPO_tensorflow.ipynb new file mode 100644 index 000000000..f4ac1569b --- /dev/null +++ b/week09_policy_II/seminar_TRPO_tensorflow.ipynb @@ -0,0 +1,792 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# launch XVFB if you run on a server\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's make a TRPO!\n", + "\n", + "In this notebook we will write the code of the one Trust Region Policy Optimization.\n", + "As usually, it contains a few different parts which we are going to reproduce.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import tensorflow as tf" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import gym\n", + "\n", + "env = gym.make(\"Acrobot-v1\")\n", + "env.reset()\n", + "observation_shape = env.observation_space.shape\n", + "n_actions = env.action_space.n\n", + "print(\"Observation Space\", env.observation_space)\n", + "print(\"Action Space\", env.action_space)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "plt.imshow(env.render('rgb_array'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 1: Defining a network\n", + "\n", + "With all it's complexity, at it's core TRPO is yet another policy gradient method. \n", + "\n", + "This essentially means we're actually training a stochastic policy $ \\pi_\\theta(a|s) $. \n", + "\n", + "And yes, it's gonna be a neural network. So let's start by defining one." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# input tensors\n", + "observations_ph = tf.placeholder(\n", + " shape=(None, observation_shape[0]), dtype=tf.float32)\n", + "# Actions that we made\n", + "actions_ph = tf.placeholder(shape=(None,), dtype=tf.int32)\n", + "# \"G = r + gamma*r' + gamma^2*r'' + ...\"\n", + "cummulative_returns_ph = tf.placeholder(shape=(None,), dtype=tf.float32)\n", + "# Action probabilities from previous iteration\n", + "old_probs_ph = tf.placeholder(shape=(None, n_actions), dtype=tf.float32)\n", + "\n", + "all_inputs = [observations_ph, actions_ph,\n", + " cummulative_returns_ph, old_probs_ph]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def denselayer(name, x, out_dim, nonlinearity=None):\n", + " with tf.variable_scope(name):\n", + " if nonlinearity is None:\n", + " nonlinearity = tf.identity\n", + "\n", + " x_shape = x.get_shape().as_list()\n", + "\n", + " w = tf.get_variable('w', shape=[x_shape[1], out_dim])\n", + " b = tf.get_variable(\n", + " 'b', shape=[out_dim], initializer=tf.constant_initializer(0))\n", + " o = nonlinearity(tf.matmul(x, w) + b)\n", + "\n", + " return o\n", + "\n", + "\n", + "sess = tf.InteractiveSession()\n", + "\n", + "nn = observations_ph\n", + "\n", + "\n", + "\n", + "policy_out = \n", + "\n", + "probs_out = tf.exp(policy_out)\n", + "\n", + "weights = tf.trainable_variables()\n", + "sess.run(tf.global_variables_initializer())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 2: Actions and rollouts\n", + "\n", + "In this section, we'll define functions that take actions $ a \\sim \\pi_\\theta(a|s) $ and rollouts $ $." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# compile function\n", + "\n", + "\n", + "def act(obs, sample=True):\n", + " \"\"\"\n", + " Samples action from policy distribution (sample = True) or takes most likely action (sample = False)\n", + " :param: obs - single observation vector\n", + " :param sample: if True, samples from \\pi, otherwise takes most likely action\n", + " :returns: action (single integer) and probabilities for all actions\n", + " \"\"\"\n", + "\n", + " probs = sess.run(probs_out, feed_dict={\n", + " observations_ph: obs.reshape((1, -1))})[0]\n", + "\n", + " if sample:\n", + " action = int(np.random.choice(n_actions, p=probs))\n", + " else:\n", + " action = int(np.argmax(probs))\n", + "\n", + " return action, probs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# demo\n", + "print(\"sampled:\", [act(env.reset()) for _ in range(5)])\n", + "print(\"greedy:\", [act(env.reset(), sample=False) for _ in range(5)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Compute cummulative reward just like you did in vanilla REINFORCE" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import scipy.signal\n", + "\n", + "\n", + "def get_cummulative_returns(r, gamma=1):\n", + " \"\"\"\n", + " Computes cummulative discounted rewards given immediate rewards\n", + " G_i = r_i + gamma*r_{i+1} + gamma^2*r_{i+2} + ...\n", + " Also known as R(s,a).\n", + " \"\"\"\n", + " r = np.array(r)\n", + " assert r.ndim >= 1\n", + " return scipy.signal.lfilter([1], [1, -gamma], r[::-1], axis=0)[::-1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# simple demo on rewards [0,0,1,0,0,1]\n", + "get_cummulative_returns([0, 0, 1, 0, 0, 1], gamma=0.9)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Rollout**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def rollout(env, act, max_pathlength=2500, n_timesteps=50000):\n", + " \"\"\"\n", + " Generate rollouts for training.\n", + " :param: env - environment in which we will make actions to generate rollouts.\n", + " :param: act - the function that can return policy and action given observation.\n", + " :param: max_pathlength - maximum size of one path that we generate.\n", + " :param: n_timesteps - total sum of sizes of all pathes we generate.\n", + " \"\"\"\n", + " paths = []\n", + "\n", + " total_timesteps = 0\n", + " while total_timesteps < n_timesteps:\n", + " obervations, actions, rewards, action_probs = [], [], [], []\n", + " obervation = env.reset()\n", + " for _ in range(max_pathlength):\n", + " action, policy = act(obervation)\n", + " obervations.append(obervation)\n", + " actions.append(action)\n", + " action_probs.append(policy)\n", + " obervation, reward, done, _ = env.step(action)\n", + " rewards.append(reward)\n", + " total_timesteps += 1\n", + " if done or total_timesteps == n_timesteps:\n", + " path = {\"observations\": np.array(obervations),\n", + " \"policy\": np.array(action_probs),\n", + " \"actions\": np.array(actions),\n", + " \"rewards\": np.array(rewards),\n", + " \"cumulative_returns\": get_cummulative_returns(rewards),\n", + " }\n", + " paths.append(path)\n", + " break\n", + " return paths" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "paths = rollout(env, act, max_pathlength=5, n_timesteps=100)\n", + "print(paths[-1])\n", + "assert (paths[0]['policy'].shape == (5, n_actions))\n", + "assert (paths[0]['cumulative_returns'].shape == (5,))\n", + "assert (paths[0]['rewards'].shape == (5,))\n", + "assert (paths[0]['observations'].shape == (5,)+observation_shape)\n", + "assert (paths[0]['actions'].shape == (5,))\n", + "print('It\\'s ok')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 3: loss functions\n", + "\n", + "Now let's define the loss functions and constraints for actual TRPO training." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The surrogate reward should be\n", + "$$J_{surr}= {1 \\over N} \\sum\\limits_{i=0}^N \\frac{\\pi_{\\theta}(s_i, a_i)}{\\pi_{\\theta_{old}}(s_i, a_i)}A_{\\theta_{old}(s_i, a_i)}$$\n", + "\n", + "For simplicity, let's use cummulative returns instead of advantage for now:\n", + "$$J'_{surr}= {1 \\over N} \\sum\\limits_{i=0}^N \\frac{\\pi_{\\theta}(s_i, a_i)}{\\pi_{\\theta_{old}}(s_i, a_i)}G_{\\theta_{old}(s_i, a_i)}$$\n", + "\n", + "Or alternatively, minimize the surrogate loss:\n", + "$$ L_{surr} = - J'_{surr} $$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# select probabilities of chosen actions\n", + "batch_size = tf.shape(observations_ph)[0]\n", + "probs_all = tf.reshape(probs_out, [-1])\n", + "probs_for_actions = tf.gather(probs_all, tf.range(\n", + " 0, batch_size) * n_actions + actions_ph)\n", + "old_probs_all = tf.reshape(old_probs_ph, [-1])\n", + "old_probs_for_actions = tf.gather(\n", + " old_probs_all, tf.range(0, batch_size) * n_actions + actions_ph)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Compute surrogate loss: negative importance-sampled policy gradient\n", + "\n", + "L_surr = " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# compute and return surrogate policy gradient\n", + "def var_shape(x):\n", + " return [k.value for k in x.get_shape()]\n", + "\n", + "\n", + "def numel(x):\n", + " return np.prod(var_shape(x))\n", + "\n", + "\n", + "def flatgrad(loss, var_list):\n", + " grads = tf.gradients(loss, var_list)\n", + " return tf.concat([tf.reshape(grad, [numel(v)])\n", + " for (v, grad) in zip(var_list, grads)], 0)\n", + "\n", + "\n", + "flat_grad_surr = flatgrad(L_surr, weights)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can ascend these gradients as long as our $pi_\\theta(a|s)$ satisfies the constraint\n", + "$$E_{s,\\pi_{\\Theta_{t}}}\\Big[KL(\\pi(\\Theta_{t}, s) \\:||\\:\\pi(\\Theta_{t+1}, s))\\Big]< \\alpha$$\n", + "\n", + "\n", + "where\n", + "\n", + "$$KL(p||q) = E _p log({p \\over q})$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Compute Kullback-Leibler divergence (see formula above)\n", + "# Note: you need to sum KL and entropy over all actions, not just the ones agent took\n", + "old_log_probs = tf.log(old_probs_ph+1e-10)\n", + "\n", + "kl = \n", + "\n", + "# Compute policy entropy\n", + "entropy = \n", + "\n", + "losses = [L_surr, kl, entropy]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Linear search**\n", + "\n", + "TRPO in its core involves ascending surrogate policy gradient constrained by KL divergence. \n", + "\n", + "In order to enforce this constraint, we're gonna use linesearch. You can find out more about it [here](https://en.wikipedia.org/wiki/Linear_search)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def linesearch(f, x, fullstep, max_kl):\n", + " \"\"\"\n", + " Linesearch finds the best parameters of neural networks in the direction of fullstep contrainted by KL divergence.\n", + " :param: f - function that returns loss, kl and arbitrary third component.\n", + " :param: x - old parameters of neural network.\n", + " :param: fullstep - direction in which we make search.\n", + " :param: max_kl - constraint of KL divergence.\n", + " :returns:\n", + " \"\"\"\n", + " max_backtracks = 10\n", + " loss, _, _ = f(x)\n", + " for stepfrac in .5**np.arange(max_backtracks):\n", + " xnew = x + stepfrac * fullstep\n", + " new_loss, kl, _ = f(xnew)\n", + " actual_improve = new_loss - loss\n", + " if kl <= max_kl and actual_improve < 0:\n", + " x = xnew\n", + " loss = new_loss\n", + " return x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 4: training\n", + "In this section we construct rest parts of our computational graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def slice_vector(vector, shapes):\n", + " \"\"\"\n", + " Slices symbolic vector into several symbolic tensors of given shapes.\n", + " Auxilary function used to un-flatten gradients, tangents etc.\n", + " :param vector: 1-dimensional symbolic vector\n", + " :param shapes: list or tuple of shapes (list, tuple or symbolic)\n", + " :returns: list of symbolic tensors of given shapes\n", + " \"\"\"\n", + " assert len(vector.get_shape()) == 1, \"vector must be 1-dimensional\"\n", + " start = 0\n", + " tensors = []\n", + " for shape in shapes:\n", + " size = np.prod(shape)\n", + " tensor = tf.reshape(vector[start:(start + size)], shape)\n", + " tensors.append(tensor)\n", + " start += size\n", + " return tensors" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# intermediate grad in conjugate_gradient\n", + "conjugate_grad_intermediate_vector = tf.placeholder(\n", + " dtype=tf.float32, shape=(None,))\n", + "\n", + "# slice flat_tangent into chunks for each weight\n", + "weight_shapes = [sess.run(var).shape for var in weights]\n", + "tangents = slice_vector(conjugate_grad_intermediate_vector, weight_shapes)\n", + "\n", + "# KL divergence where first arg is fixed\n", + "kl_firstfixed = tf.reduce_sum((tf.stop_gradient(probs_out) * (tf.stop_gradient(\n", + " tf.log(probs_out)) - tf.log(probs_out)))) / tf.cast(batch_size, tf.float32)\n", + "\n", + "# compute fisher information matrix (used for conjugate gradients and to estimate KL)\n", + "gradients = tf.gradients(kl_firstfixed, weights)\n", + "gradient_vector_product = [tf.reduce_sum(\n", + " g[0] * t) for (g, t) in zip(gradients, tangents)]\n", + "\n", + "fisher_vec_prod = flatgrad(gradient_vector_product, weights)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### TRPO helpers\n", + "\n", + "Here we define a few helper functions used in the main TRPO loop" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Conjugate gradients**\n", + "\n", + "Since TRPO includes contrainted optimization, we will need to solve Ax=b using conjugate gradients.\n", + "\n", + "In general, CG is an algorithm that solves Ax=b where A is positive-defined. A is Hessian matrix so A is positive-defined. You can find out more about them [here](https://en.wikipedia.org/wiki/Conjugate_gradient_method)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from numpy.linalg import inv\n", + "\n", + "\n", + "def conjugate_gradient(f_Ax, b, cg_iters=10, residual_tol=1e-10):\n", + " \"\"\"\n", + " This method solves system of equation Ax=b using iterative method called conjugate gradients\n", + " :f_Ax: function that returns Ax\n", + " :b: targets for Ax\n", + " :cg_iters: how many iterations this method should do\n", + " :residual_tol: epsilon for stability\n", + " \"\"\"\n", + " p = b.copy()\n", + " r = b.copy()\n", + " x = np.zeros_like(b)\n", + " rdotr = r.dot(r)\n", + " for i in range(cg_iters):\n", + " z = f_Ax(p)\n", + " v = rdotr / (p.dot(z) + 1e-8)\n", + " x += v * p\n", + " r -= v * z\n", + " newrdotr = r.dot(r)\n", + " mu = newrdotr / (rdotr + 1e-8)\n", + " p = r + mu * p\n", + " rdotr = newrdotr\n", + " if rdotr < residual_tol:\n", + " break\n", + " return x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# This code validates conjugate gradients\n", + "A = np.random.rand(8, 8)\n", + "A = np.matmul(np.transpose(A), A)\n", + "\n", + "\n", + "def f_Ax(x):\n", + " return np.matmul(A, x.reshape(-1, 1)).reshape(-1)\n", + "\n", + "\n", + "b = np.random.rand(8)\n", + "\n", + "w = np.matmul(np.matmul(inv(np.matmul(np.transpose(A), A)),\n", + " np.transpose(A)), b.reshape((-1, 1))).reshape(-1)\n", + "print(w)\n", + "print(conjugate_gradient(f_Ax, b))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Compile a function that exports network weights as a vector\n", + "flat_weights = tf.concat([tf.reshape(var, [-1]) for var in weights], axis=0)\n", + "\n", + "# ... and another function that imports vector back into network weights\n", + "flat_weights_placeholder = tf.placeholder(tf.float32, shape=(None,))\n", + "assigns = slice_vector(flat_weights_placeholder, weight_shapes)\n", + "\n", + "load_flat_weights = [w.assign(ph) for w, ph in zip(weights, assigns)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Step 5: Main TRPO loop\n", + "\n", + "Here we will train our network!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import time\n", + "from itertools import count\n", + "from collections import OrderedDict\n", + "\n", + "# this is hyperparameter of TRPO. It controls how big KL divergence may be between old and new policy every step.\n", + "max_kl = 0.01\n", + "cg_damping = 0.1 # This parameters regularize addition to\n", + "numeptotal = 0 # this is number of episodes that we played.\n", + "\n", + "start_time = time.time()\n", + "\n", + "for i in count(1):\n", + "\n", + " print(\"\\n********** Iteration %i ************\" % i)\n", + "\n", + " # Generating paths.\n", + " print(\"Rollout\")\n", + " paths = rollout(env, act)\n", + " print(\"Made rollout\")\n", + "\n", + " # Updating policy.\n", + " observations = np.concatenate([path[\"observations\"] for path in paths])\n", + " actions = np.concatenate([path[\"actions\"] for path in paths])\n", + " returns = np.concatenate([path[\"cumulative_returns\"] for path in paths])\n", + " old_probs = np.concatenate([path[\"policy\"] for path in paths])\n", + " inputs_batch = [observations, actions, returns, old_probs]\n", + " feed_dict = {observations_ph: observations,\n", + " actions_ph: actions,\n", + " old_probs_ph: old_probs,\n", + " cummulative_returns_ph: returns,\n", + " }\n", + " old_weights = sess.run(flat_weights)\n", + "\n", + " def fisher_vector_product(p):\n", + " \"\"\"gets intermediate grads (p) and computes fisher*vector \"\"\"\n", + " feed_dict[conjugate_grad_intermediate_vector] = p\n", + " return sess.run(fisher_vec_prod, feed_dict) + cg_damping * p\n", + "\n", + " flat_grad = sess.run(flat_grad_surr, feed_dict)\n", + "\n", + " stepdir = conjugate_gradient(fisher_vector_product, -flat_grad)\n", + " shs = .5 * stepdir.dot(fisher_vector_product(stepdir))\n", + " lm = np.sqrt(shs / max_kl)\n", + " fullstep = stepdir / lm\n", + "\n", + " # Compute new weights with linesearch in the direction we found with CG\n", + "\n", + " def losses_f(flat_weights):\n", + " feed_dict[flat_weights_placeholder] = flat_weights\n", + " sess.run(load_flat_weights, feed_dict)\n", + " return sess.run(losses, feed_dict)\n", + "\n", + " new_weights = linesearch(losses_f, old_weights, fullstep, max_kl)\n", + " feed_dict[flat_weights_placeholder] = new_weights\n", + " sess.run(load_flat_weights, feed_dict)\n", + "\n", + " # Report current progress\n", + " L_surr, kl, entropy = sess.run(losses, feed_dict)\n", + " episode_rewards = np.array([path[\"rewards\"].sum() for path in paths])\n", + "\n", + " stats = OrderedDict()\n", + " numeptotal += len(episode_rewards)\n", + " stats[\"Total number of episodes\"] = numeptotal\n", + " stats[\"Average sum of rewards per episode\"] = episode_rewards.mean()\n", + " stats[\"Std of rewards per episode\"] = episode_rewards.std()\n", + " stats[\"Entropy\"] = entropy\n", + " stats[\"Time elapsed\"] = \"%.2f mins\" % ((time.time() - start_time)/60.)\n", + " stats[\"KL between old and new distribution\"] = kl\n", + " stats[\"Surrogate loss\"] = L_surr\n", + " for k, v in stats.items():\n", + " print(k + \": \" + \" \" * (40 - len(k)) + str(v))\n", + " i += 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Homework option I: better sampling (10+pts)\n", + "\n", + "In this section, you're invited to implement a better rollout strategy called _vine_.\n", + "\n", + "![img](https://s17.postimg.org/i90chxgvj/vine.png)\n", + "\n", + "In most gym environments, you can actually backtrack by using states. You can find a wrapper that saves/loads states in [the mcts seminar](https://github.com/yandexdataschool/Practical_RL/blob/spring19/week10_planning/seminar_MCTS.ipynb).\n", + "\n", + "You can read more about in the [TRPO article](https://arxiv.org/abs/1502.05477) in section 5.2.\n", + "\n", + "The goal here is to implement such rollout policy (we recommend using tree data structure like in the seminar above).\n", + "Then you can assign cummulative rewards similar to `get_cummulative_rewards`, but for a tree.\n", + "\n", + "__bonus task__ - parallelize samples using multiple cores" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "# Homework option II (10+pts)\n", + "\n", + "Let's use TRPO to train evil robots! (pick any of two)\n", + "* [MuJoCo robots](https://gym.openai.com/envs#mujoco)\n", + "* [Box2d robot](https://gym.openai.com/envs/BipedalWalker-v2)\n", + "\n", + "The catch here is that those environments have continuous action spaces. \n", + "\n", + "Luckily, TRPO is a policy gradient method, so it's gonna work for any parametric $\\pi_\\theta(a|s)$. We recommend starting with gaussian policy:\n", + "\n", + "$$\\pi_\\theta(a|s) = N(\\mu_\\theta(s),\\sigma^2_\\theta(s)) = {1 \\over \\sqrt { 2 \\pi {\\sigma^2}_\\theta(s) } } e^{ (a - \n", + "\\mu_\\theta(s))^2 \\over 2 {\\sigma^2}_\\theta(s) } $$\n", + "\n", + "In the $\\sqrt { 2 \\pi {\\sigma^2}_\\theta(s) }$ clause, $\\pi$ means ~3.1415926, not agent's policy.\n", + "\n", + "This essentially means that you will need two output layers:\n", + "* $\\mu_\\theta(s)$, a dense layer with linear activation\n", + "* ${\\sigma^2}_\\theta(s)$, a dense layer with activation tf.exp (to make it positive; like rho from bandits)\n", + "\n", + "For multidimensional actions, you can use fully factorized gaussian (basically a vector of gaussians).\n", + "\n", + "__bonus task__: compare performance of continuous action space method to action space discretization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.6.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week09_policy_II/seminar_TRPO_theano.ipynb b/week09_policy_II/seminar_TRPO_theano.ipynb new file mode 100644 index 000000000..2585a2b33 --- /dev/null +++ b/week09_policy_II/seminar_TRPO_theano.ipynb @@ -0,0 +1,792 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# launch XVFB if you run on a server\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's make a TRPO!\n", + "\n", + "In this notebook we will write the code of the one Trust Region Policy Optimization.\n", + "As usually, it contains a few different parts which we are going to reproduce.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import theano\n", + "import theano.tensor as T" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Observation Space Box(6,)\n", + "Action Space Discrete(3)\n" + ] + } + ], + "source": [ + "import gym\n", + "\n", + "env = gym.make(\"Acrobot-v1\")\n", + "env.reset()\n", + "\n", + "observation_shape = env.observation_space.shape\n", + "n_actions = env.action_space.n\n", + "print(\"Observation Space\", env.observation_space)\n", + "print(\"Action Space\", env.action_space)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQsAAAD8CAYAAABgtYFHAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAADktJREFUeJzt3W2opGd9x/Hvr5sHbRXXJMew7K5sxKXoizZmD3ElpdhE\nS0zFzYsEIlKXsLDQWlAs2E0LLUJfaF+YIBR1aaRrUZPUB7KEtDZsEkpfGHPWPLuNOUpqDhvclTzY\nItpG/30x19Fx9yTn2j0zZ2Z2vx8Y5rqv+7pn/hNmf7nue64zk6pCklbzG5MuQNJsMCwkdTEsJHUx\nLCR1MSwkdTEsJHUZS1gkuTrJk0kWk+wbx3NIWl8Z9TqLJBuA7wLvBpaAB4H3V9V3RvpEktbVOGYW\nlwOLVfX9qvpf4DZg1xieR9I6OmcMj7kZeGZoewl4+ysdcNFFF9W2bdvGUIqkZYcPH/5RVc2d7vHj\nCIus0HfSuU6SvcBegDe+8Y0sLCyMoRRJy5L811qOH8dpyBKwdWh7C3D0xEFVtb+q5qtqfm7utMNO\n0joZR1g8CGxPckmS84AbgINjeB5J62jkpyFV9VKSPwO+AWwAPl9VT4z6eSStr3Fcs6Cq7gbuHsdj\nS5oMV3BK6mJYSOpiWEjqYlhI6mJYSOpiWEjqYlhI6mJYSOpiWEjqYlhI6mJYSOpiWEjqYlhI6mJY\nSOpiWEjqYlhI6mJYSOpiWEjqYlhI6mJYSOpiWEjqYlhI6mJYSOpiWEjqYlhI6mJYSOpiWEjqYlhI\n6mJYSOpiWEjqYlhI6mJYSOpiWEjqYlhI6mJYSOqyalgk+XySY0keH+q7IMk9SZ5q969v/Uny6SSL\nSR5Nctk4i5e0fnpmFv8IXH1C3z7gUFVtBw61bYD3ANvbbS/wmdGUKWnSVg2Lqvp34LkTuncBB1r7\nAHDtUP8XauCbwMYkm0ZVrKTJOd1rFhdX1bMA7f4NrX8z8MzQuKXWd5Ike5MsJFk4fvz4aZYhab2M\n+gJnVuirlQZW1f6qmq+q+bm5uRGXIWnUTjcsfrh8etHuj7X+JWDr0LgtwNHTL0/StDjdsDgI7G7t\n3cCdQ/0fbJ+K7AReXD5dkTTbzlltQJIvA+8ELkqyBPwN8AngjiR7gB8A17fhdwPXAIvAT4Abx1Cz\npAlYNSyq6v0vs+uqFcYW8KG1FiVp+riCU1IXw0JSF8NCUhfDQlIXw0JSF8NCUhfDQlIXw0JSF8NC\nUpcMFl1OuIhk8kVIZ77DVTV/ugevutx7PezYsYOFhYVJlyGd0ZKVvkGin6chkroYFpK6GBaSuhgW\nkroYFpK6GBaSuhgWkroYFpK6GBaSuhgWkroYFpK6GBaSuhgWkroYFpK6GBaSuhgWkroYFpK6GBaS\nuhgWkroYFpK6GBaSuhgWkroYFpK6rBoWSbYmuS/JkSRPJPlw678gyT1Jnmr3r2/9SfLpJItJHk1y\n2bhfhKTx65lZvAT8eVW9BdgJfCjJW4F9wKGq2g4catsA7wG2t9te4DMjr1rSuls1LKrq2ar6dmv/\nN3AE2AzsAg60YQeAa1t7F/CFGvgmsDHJppFXLmldndI1iyTbgLcBDwAXV9WzMAgU4A1t2GbgmaHD\nllqfpBnWHRZJXgN8FfhIVf34lYau0HfSDx8n2ZtkIcnC8ePHe8uQNCFdYZHkXAZB8cWq+lrr/uHy\n6UW7P9b6l4CtQ4dvAY6e+JhVtb+q5qtqfm5u7nTrl7ROej4NCXArcKSqPjW06yCwu7V3A3cO9X+w\nfSqyE3hx+XRF0uw6p2PMFcAfA48lebj1/SXwCeCOJHuAHwDXt313A9cAi8BPgBtHWrGkiVg1LKrq\nP1j5OgTAVSuML+BDa6xL0pRxBaekLoaFpC6GhaQuhoWkLoaFpC6GhaQuhoWkLoaFpC6GhaQuhoWk\nLoaFpC49f0gm/dLhw7/+Z0I7dpz0VSU6QzmzULcTg+Ll+nRmMizU5ZVCwcA4OxgWWlVPGBgYZz7D\nQlIXw0JSF8NCUhfDQquaZ2EkYzTbDAt1eaUwMCjODoaFuq0UCgbF2cMVnDolhsPZy5mFpC6GhaQu\nhoWkLoaFpC6GhaQuhoWkLoaFpC6GhaQuhoWkLoaFpC6GhaQuhoWkLoaFpC6rhkWSVyX5VpJHkjyR\n5OOt/5IkDyR5KsntSc5r/ee37cW2f9t4X4Kk9dAzs/gZcGVV/S5wKXB1kp3AJ4Gbq2o78Dywp43f\nAzxfVW8Gbm7jJM24VcOiBv6nbZ7bbgVcCXyl9R8Arm3tXW2btv+qJH5PvDTjuq5ZJNmQ5GHgGHAP\n8D3ghap6qQ1ZAja39mbgGYC2/0XgwhUec2+ShSQLx48fX9urkDR2XWFRVT+vqkuBLcDlwFtWGtbu\nV5pFnPSDmFW1v6rmq2p+bm6ut15JE3JKn4ZU1QvA/cBOYGOS5a/l2wIcbe0lYCtA2/864LlRFCtp\n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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "plt.imshow(env.render('rgb_array'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 1: Defining a network\n", + "\n", + "With all it's complexity, at it's core TRPO is yet another policy gradient method. \n", + "\n", + "This essentially means we're actually training a stochastic policy $ \\pi_\\theta(a|s) $. \n", + "\n", + "And yes, it's gonna be a neural network. So let's start by defining one." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# input tensors\n", + "observations = T.matrix(name=\"obs\")\n", + "actions = T.ivector(name=\"action\")\n", + "cummulative_returns = T.vector(name=\"G = r + gamma*r' + gamma^2*r'' + ...\")\n", + "old_probs = T.matrix(name=\"action probabilities from previous iteration\")\n", + "\n", + "all_inputs = [observations, actions, cummulative_returns, old_probs]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Create neural network.\n", + "from lasagne.layers import *\n", + "\n", + "nn = InputLayer((None,)+observation_shape, input_var=observations)\n", + "\n", + "\n", + "\n", + "policy = \n", + "\n", + "probs = get_output(policy)\n", + "\n", + "weights = get_all_params(policy, trainable=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 2: Actions and rollouts\n", + "\n", + "In this section, we'll define functions that take actions $ a \\sim \\pi_\\theta(a|s) $ and rollouts $ $." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# compile function\n", + "get_policy = theano.function([observations], probs, allow_input_downcast=True)\n", + "\n", + "\n", + "def act(obs, sample=True):\n", + " \"\"\"\n", + " Samples action from policy distribution (sample = True) or takes most likely action (sample = False)\n", + " :param: obs - single observation vector\n", + " :param sample: if True, samples from \\pi, otherwise takes most likely action\n", + " :returns: action (single integer) and probabilities for all actions\n", + " \"\"\"\n", + "\n", + " policy = get_policy([obs])[0]\n", + "\n", + " if sample:\n", + " action = int(np.random.choice(n_actions, p=policy))\n", + " else:\n", + " action = int(np.argmax(policy))\n", + "\n", + " return action, policy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# demo\n", + "print(\"sampled:\", [act(env.reset()) for _ in range(100)])\n", + "print(\"greedy:\", [act(env.reset(), sample=False) for _ in range(100)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Compute cummulative reward just like you did in vanilla REINFORCE" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import scipy.signal\n", + "\n", + "\n", + "def get_cummulative_returns(r, gamma=1):\n", + " \"\"\"\n", + " Computes cummulative discounted rewards given immediate rewards\n", + " G_i = r_i + gamma*r_{i+1} + gamma^2*r_{i+2} + ...\n", + " Also known as R(s,a).\n", + " \"\"\"\n", + " r = np.array(r)\n", + " assert r.ndim >= 1\n", + " return scipy.signal.lfilter([1], [1, -gamma], r[::-1], axis=0)[::-1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# simple demo on rewards [0,0,1,0,0,1]\n", + "get_cummulative_returns([0, 0, 1, 0, 0, 1], gamma=0.9)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Rollout**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def rollout(env, act, max_pathlength=2500, n_timesteps=50000):\n", + " \"\"\"\n", + " Generate rollouts for training.\n", + " :param: env - environment in which we will make actions to generate rollouts.\n", + " :param: act - the function that can return policy and action given observation.\n", + " :param: max_pathlength - maximum size of one path that we generate.\n", + " :param: n_timesteps - total sum of sizes of all pathes we generate.\n", + " \"\"\"\n", + " paths = []\n", + "\n", + " total_timesteps = 0\n", + " while total_timesteps < n_timesteps:\n", + " obervations, actions, rewards, action_probs = [], [], [], []\n", + " obervation = env.reset()\n", + " for _ in range(max_pathlength):\n", + " action, policy = act(obervation)\n", + " obervations.append(obervation)\n", + " actions.append(action)\n", + " action_probs.append(policy)\n", + " obervation, reward, done, _ = env.step(action)\n", + " rewards.append(reward)\n", + " total_timesteps += 1\n", + " if done or total_timesteps == n_timesteps:\n", + " path = {\"observations\": np.array(obervations),\n", + " \"policy\": np.array(action_probs),\n", + " \"actions\": np.array(actions),\n", + " \"rewards\": np.array(rewards),\n", + " \"cumulative_returns\": get_cummulative_returns(rewards),\n", + " }\n", + " paths.append(path)\n", + " break\n", + " return paths" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "paths = rollout(env, act, max_pathlength=5, n_timesteps=100)\n", + "print(paths[-1])\n", + "assert (paths[0]['policy'].shape == (5, n_actions))\n", + "assert (paths[0]['cumulative_returns'].shape == (5,))\n", + "assert (paths[0]['rewards'].shape == (5,))\n", + "assert (paths[0]['observations'].shape == (5,)+observation_shape)\n", + "assert (paths[0]['actions'].shape == (5,))\n", + "print('It\\'s ok')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 3: loss functions\n", + "\n", + "Now let's define the loss functions and constraints for actual TRPO training." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The surrogate reward should be\n", + "$$J_{surr}= {1 \\over N} \\sum\\limits_{i=0}^N \\frac{\\pi_{\\theta}(s_i, a_i)}{\\pi_{\\theta_{old}}(s_i, a_i)}A_{\\theta_{old}(s_i, a_i)}$$\n", + "\n", + "For simplicity, let's use cummulative returns instead of advantage for now:\n", + "$$J'_{surr}= {1 \\over N} \\sum\\limits_{i=0}^N \\frac{\\pi_{\\theta}(s_i, a_i)}{\\pi_{\\theta_{old}}(s_i, a_i)}G_{\\theta_{old}(s_i, a_i)}$$\n", + "\n", + "Or alternatively, minimize the surrogate loss:\n", + "$$ L_{surr} = - J'_{surr} $$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# select probabilities of chosen actions\n", + "batch_size = actions.shape[0]\n", + "\n", + "probs_for_actions = probs[T.arange(batch_size), actions]\n", + "old_probs_for_actions = old_probs[T.arange(batch_size), actions]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Compute surrogate loss: negative importance-sampled policy gradient\n", + "\n", + "L_surr = " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# compute and return surrogate policy gradient\n", + "\n", + "\n", + "def get_flat_gradient(loss, var_list):\n", + " \"\"\"gradient of loss wrt var_list flattened into a large vector\"\"\"\n", + " grads = T.grad(loss, var_list)\n", + " return T.concatenate([grad.ravel() for grad in grads])\n", + "\n", + "\n", + "get_surrogate_gradients = theano.function(all_inputs, get_flat_gradient(L_surr, weights),\n", + " allow_input_downcast=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can ascend these gradients as long as our $pi_\\theta(a|s)$ satisfies the constraint\n", + "$$E_{s,\\pi_{\\Theta_{t}}}\\Big[KL(\\pi(\\Theta_{t}, s) \\:||\\:\\pi(\\Theta_{t+1}, s))\\Big]< \\alpha$$\n", + "\n", + "\n", + "where\n", + "\n", + "$$KL(p||q) = E _p log({p \\over q})$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Compute Kullback-Leibler divergence (see formula above)\n", + "# Note: you need to sum KL and entropy over all actions, not just the ones agent took\n", + "old_log_probs = T.log(old_probs + 1e-10)\n", + "\n", + "kl = \n", + "\n", + "# Compute policy entropy\n", + "entropy = \n", + "\n", + "compute_losses = theano.function(all_inputs, [L_surr, kl, entropy],\n", + " allow_input_downcast=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Linear search**\n", + "\n", + "TRPO in its core involves ascending surrogate policy gradient constrained by KL divergence. \n", + "\n", + "In order to enforce this constraint, we're gonna use linesearch. You can find out more about it [here](https://en.wikipedia.org/wiki/Linear_search)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def linesearch(f, x, fullstep, max_kl):\n", + " \"\"\"\n", + " Linesearch finds the best parameters of neural networks in the direction of fullstep contrainted by KL divergence.\n", + " :param: f - function that returns loss, kl and arbitrary third component.\n", + " :param: x - old parameters of neural network.\n", + " :param: fullstep - direction in which we make search.\n", + " :param: max_kl - constraint of KL divergence.\n", + " :returns:\n", + " \"\"\"\n", + " max_backtracks = 10\n", + " loss, _, _ = f(x)\n", + " for stepfrac in .5 ** np.arange(max_backtracks):\n", + " xnew = x + stepfrac * fullstep\n", + " new_loss, kl, _ = f(xnew)\n", + " actual_improve = new_loss - loss\n", + " if kl <= max_kl and actual_improve < 0:\n", + " x = xnew\n", + " loss = new_loss\n", + " return x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 4: training\n", + "In this section we construct rest parts of our computational graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def slice_vector(vector, shapes):\n", + " \"\"\"\n", + " Slices symbolic vector into several symbolic tensors of given shapes.\n", + " Auxilary function used to un-flatten gradients, tangents etc.\n", + " :param vector: 1-dimensional symbolic vector\n", + " :param shapes: list or tuple of shapes (list, tuple or symbolic)\n", + " :returns: list of symbolic tensors of given shapes\n", + " \"\"\"\n", + " assert vector.ndim == 1, \"vector must be 1-dimensional\"\n", + " start = 0\n", + " tensors = []\n", + " for shape in shapes:\n", + " size = T.prod(shape)\n", + " tensor = vector[start:(start + size)].reshape(shape)\n", + " tensors.append(tensor)\n", + " start += size\n", + " return tensors" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "conjugate_grad_intermediate_vector = T.vector(\n", + " \"intermediate grad in conjugate_gradient\")\n", + "\n", + "# slice flat_tangent into chunks for each weight\n", + "weight_shapes = [var.get_value().shape for var in weights]\n", + "tangents = slice_vector(conjugate_grad_intermediate_vector, weight_shapes)\n", + "\n", + "# KL divergence where first arg is fixed\n", + "from theano.gradient import disconnected_grad as const\n", + "kl_firstfixed = (const(probs) * (const(T.log(probs)) -\n", + " T.log(probs))).sum(axis=-1).mean()\n", + "\n", + "# compute fisher information matrix (used for conjugate gradients and to estimate KL)\n", + "gradients = T.grad(kl_firstfixed, weights)\n", + "gradient_vector_product = [T.sum(g * t) for (g, t) in zip(gradients, tangents)]\n", + "\n", + "fisher_vector_product = get_flat_gradient(\n", + " sum(gradient_vector_product), weights)\n", + "\n", + "compute_fisher_vector_product = theano.function(\n", + " [observations, conjugate_grad_intermediate_vector], fisher_vector_product)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### TRPO helpers\n", + "\n", + "Here we define a few helper functions used in the main TRPO loop" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Conjugate gradients**\n", + "\n", + "Since TRPO includes contrainted optimization, we will need to solve Ax=b using conjugate gradients.\n", + "\n", + "In general, CG is an algorithm that solves Ax=b where A is positive-defined. A is Hessian matrix so A is positive-defined. You can find out more about them [here](https://en.wikipedia.org/wiki/Conjugate_gradient_method)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from numpy.linalg import inv\n", + "\n", + "\n", + "def conjugate_gradient(f_Ax, b, cg_iters=10, residual_tol=1e-10):\n", + " \"\"\"\n", + " This method solves system of equation Ax=b using iterative method called conjugate gradients\n", + " :f_Ax: function that returns Ax\n", + " :b: targets for Ax\n", + " :cg_iters: how many iterations this method should do\n", + " :residual_tol: epsilon for stability\n", + " \"\"\"\n", + " p = b.copy()\n", + " r = b.copy()\n", + " x = np.zeros_like(b)\n", + " rdotr = r.dot(r)\n", + " for i in range(cg_iters):\n", + " z = f_Ax(p)\n", + " v = rdotr / (p.dot(z) + 1e-8)\n", + " x += v * p\n", + " r -= v * z\n", + " newrdotr = r.dot(r)\n", + " mu = newrdotr / (rdotr + 1e-8)\n", + " p = r + mu * p\n", + " rdotr = newrdotr\n", + " if rdotr < residual_tol:\n", + " break\n", + " return x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# This code validates conjugate gradients\n", + "A = np.random.rand(8, 8)\n", + "A = np.matmul(np.transpose(A), A)\n", + "\n", + "\n", + "def f_Ax(x):\n", + " return np.matmul(A, x.reshape(-1, 1)).reshape(-1)\n", + "\n", + "\n", + "b = np.random.rand(8)\n", + "\n", + "w = np.matmul(np.matmul(inv(np.matmul(np.transpose(A), A)),\n", + " np.transpose(A)), b.reshape((-1, 1))).reshape(-1)\n", + "print(w)\n", + "print(conjugate_gradient(f_Ax, b))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Compile a function that exports network weights as a vector\n", + "flat_weights = T.concatenate([var.ravel() for var in weights])\n", + "get_flat_weights = theano.function([], flat_weights)\n", + "\n", + "# ... and another function that imports vector back into network weights\n", + "flat_weights_placeholder = T.vector(\"flattened weights\")\n", + "assigns = slice_vector(flat_weights_placeholder, weight_shapes)\n", + "\n", + "load_flat_weights = theano.function(\n", + " [flat_weights_placeholder], updates=dict(zip(weights, assigns)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Step 5: Main TRPO loop\n", + "\n", + "Here we will train our network!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import time\n", + "from itertools import count\n", + "from collections import OrderedDict\n", + "\n", + "# this is hyperparameter of TRPO. It controls how big KL divergence may be between old and new policy every step.\n", + "max_kl = 0.01\n", + "cg_damping = 0.1 # This parameters regularize addition to\n", + "numeptotal = 0 # this is number of episodes that we played.\n", + "\n", + "start_time = time.time()\n", + "\n", + "for i in count(1):\n", + "\n", + " print(\"\\n********** Iteration %i ************\" % i)\n", + "\n", + " # Generating paths.\n", + " print(\"Rollout\")\n", + " paths = rollout(env, act)\n", + " print(\"Made rollout\")\n", + "\n", + " # Updating policy.\n", + " observations = np.concatenate([path[\"observations\"] for path in paths])\n", + " actions = np.concatenate([path[\"actions\"] for path in paths])\n", + " returns = np.concatenate([path[\"cumulative_returns\"] for path in paths])\n", + " old_probs = np.concatenate([path[\"policy\"] for path in paths])\n", + " inputs_batch = [observations, actions, returns, old_probs]\n", + "\n", + " old_weights = get_flat_weights()\n", + "\n", + " def fisher_vector_product(p):\n", + " \"\"\"gets intermediate grads (p) and computes fisher*vector \"\"\"\n", + " return compute_fisher_vector_product(observations, p) + cg_damping * p\n", + "\n", + " flat_grad = get_surrogate_gradients(*inputs_batch)\n", + "\n", + " stepdir = conjugate_gradient(fisher_vector_product, -flat_grad)\n", + " shs = .5 * stepdir.dot(fisher_vector_product(stepdir))\n", + " lm = np.sqrt(shs / max_kl)\n", + " fullstep = stepdir / lm\n", + "\n", + " # Compute new weights with linesearch in the direction we found with CG\n", + "\n", + " def losses_f(flat_weights):\n", + " load_flat_weights(flat_weights)\n", + " return compute_losses(*inputs_batch)\n", + "\n", + " new_weights = linesearch(losses_f, old_weights, fullstep, max_kl)\n", + "\n", + " load_flat_weights(new_weights)\n", + "\n", + " # Report current progress\n", + " L_surr, kl, entropy = compute_losses(*inputs_batch)\n", + " episode_rewards = np.array([path[\"rewards\"].sum() for path in paths])\n", + "\n", + " stats = OrderedDict()\n", + " numeptotal += len(episode_rewards)\n", + " stats[\"Total number of episodes\"] = numeptotal\n", + " stats[\"Average sum of rewards per episode\"] = episode_rewards.mean()\n", + " stats[\"Std of rewards per episode\"] = episode_rewards.std()\n", + " stats[\"Entropy\"] = entropy\n", + " stats[\"Time elapsed\"] = \"%.2f mins\" % ((time.time() - start_time)/60.)\n", + " stats[\"KL between old and new distribution\"] = kl\n", + " stats[\"Surrogate loss\"] = L_surr\n", + " for k, v in stats.items():\n", + " print(k + \": \" + \" \" * (40 - len(k)) + str(v))\n", + " i += 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Homework option I: better sampling (10+pts)\n", + "\n", + "In this section, you're invited to implement a better rollout strategy called _vine_.\n", + "\n", + "![img](https://s17.postimg.org/i90chxgvj/vine.png)\n", + "\n", + "In most gym environments, you can actually backtrack by using states. You can find a wrapper that saves/loads states in [the mcts seminar](https://github.com/yandexdataschool/Practical_RL/blob/spring19/week10_planning/seminar_MCTS.ipynb).\n", + "\n", + "You can read more about in the [TRPO article](https://arxiv.org/abs/1502.05477) in section 5.2.\n", + "\n", + "The goal here is to implement such rollout policy (we recommend using tree data structure like in the seminar above).\n", + "Then you can assign cummulative rewards similar to `get_cummulative_rewards`, but for a tree.\n", + "\n", + "__bonus task__ - parallelize samples using multiple cores" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "# Homework option II (10+pts)\n", + "\n", + "Let's use TRPO to train evil robots! (pick any of two)\n", + "* [MuJoCo robots](https://gym.openai.com/envs#mujoco)\n", + "* [Box2d robot](https://gym.openai.com/envs/BipedalWalker-v2)\n", + "\n", + "The catch here is that those environments have continuous action spaces. \n", + "\n", + "Luckily, TRPO is a policy gradient method, so it's gonna work for any parametric $\\pi_\\theta(a|s)$. We recommend starting with gaussian policy:\n", + "\n", + "$$\\pi_\\theta(a|s) = N(\\mu_\\theta(s),\\sigma^2_\\theta(s)) = {1 \\over \\sqrt { 2 \\pi {\\sigma^2}_\\theta(s) } } e^{ (a - \n", + "\\mu_\\theta(s))^2 \\over 2 {\\sigma^2}_\\theta(s) } $$\n", + "\n", + "In the $\\sqrt { 2 \\pi {\\sigma^2}_\\theta(s) }$ clause, $\\pi$ means ~3.1415926, not agent's policy.\n", + "\n", + "This essentially means that you will need two output layers:\n", + "* $\\mu_\\theta(s)$, a dense layer with linear activation\n", + "* ${\\sigma^2}_\\theta(s)$, a dense layer with activation T.exp (to make it positive; like rho from bandits)\n", + "\n", + "For multidimensional actions, you can use fully factorized gaussian (basically a vector of gaussians).\n", + "\n", + "__bonus task__: compare performance of continuous action space method to action space discretization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.6.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week10_planning/README.md b/week10_planning/README.md new file mode 100644 index 000000000..ca7689341 --- /dev/null +++ b/week10_planning/README.md @@ -0,0 +1,26 @@ +## Assignments + +Just as usual, start with `seminar_MCTS.ipynb` +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/yandexdataschool/Practical_RL/blob/spring19/week10_planning/seminar_MCTS.ipynb) + +## Materials: planning + +* Planning by dynamic programming (D. Silver) - [video](https://www.youtube.com/watch?v=Nd1-UUMVfz4) +* Planning via tree search [videos 2-6 from CS188](https://www.youtube.com/channel/UCHBzJsIcRIVuzzHVYabikTQ) +* Our lecture: + * [Lecture(planning, MCTS, planning in POMDP)](https://yadi.sk/i/lOAUu7o13JBHFz) & [Seminar(MCTS)](https://yadi.sk/i/bkmjEZrk3JBHGF) + * Slides [part1](https://yadi.sk/i/3PM9zCP33J3ub3) (intro), [part2](https://yadi.sk/i/M03xvZ2y3JMQre) (pomdp) +* Monte-carlo tree search + * Monte-carlo tree search step-by-step by J.Levine (very intuitive) - [video](https://www.youtube.com/watch?v=UXW2yZndl7U) + * Udacity video on monte-carlo tree search (first part of a chain) - [video](https://www.youtube.com/watch?v=onBYsen2_eA) + * A Survey of Monte Carlo Tree Search Methods (2011-2012) [pdf](http://mcts.ai/pubs/mcts-survey-master.pdf) + * Reminder: UCB-1 - [slides](https://www.cs.bham.ac.uk/internal/courses/robotics/lectures/ucb1.pdf) + * Guide to MCTS - [url](https://jeffbradberry.com/posts/2015/09/intro-to-monte-carlo-tree-search/) + or [cached](https://webcache.googleusercontent.com/search?q=cache:jeffbradberry.com/posts/2015/09/intro-to-monte-carlo-tree-search/) + +## Supplementary materials + +* Integrating learning and planning (D. Silver) - [video](https://www.youtube.com/watch?v=ItMutbeOHtc) +* Approximating the MCTS optimal actions - 5vision solution for deephack.RL, code by Mikhail Pavlov - [repo](https://github.com/5vision/uct_atari) +* Alpha Go - [explanation](https://medium.com/@jonathan_hui/alphago-how-it-works-technically-26ddcc085319) +* Monte Carlo Planning in POMDP [pdf](https://papers.nips.cc/paper/4031-monte-carlo-planning-in-large-pomdps.pdf) diff --git a/week10_planning/seminar_MCTS.ipynb b/week10_planning/seminar_MCTS.ipynb new file mode 100644 index 000000000..a6571e6c3 --- /dev/null +++ b/week10_planning/seminar_MCTS.ipynb @@ -0,0 +1,686 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# In google collab, uncomment this:\n", + "# !wget https://bit.ly/2FMJP5K -q -O setup.py\n", + "# !bash setup.py 2>&1 1>stdout.log | tee stderr.log\n", + "\n", + "# This code creates a virtual display to draw game images on.\n", + "# If you are running locally, just ignore it\n", + "import os\n", + "if type(os.environ.get(\"DISPLAY\")) is not str or len(os.environ.get(\"DISPLAY\")) == 0:\n", + " !bash ../xvfb start\n", + " %env DISPLAY = : 1\n", + "\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Seminar: Monte-carlo tree search (5 pts)\n", + "\n", + "In this seminar, we'll implement a vanilla MCTS planning and use it to solve some Gym envs.\n", + "\n", + "But before we do that, we first need to modify gym env to allow saving and loading game states to facilitate backtracking." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import gym\n", + "from gym.core import Wrapper\n", + "from pickle import dumps, loads\n", + "from collections import namedtuple\n", + "\n", + "# a container for get_result function below. Works just like tuple, but prettier\n", + "ActionResult = namedtuple(\n", + " \"action_result\", (\"snapshot\", \"observation\", \"reward\", \"is_done\", \"info\"))\n", + "\n", + "\n", + "class WithSnapshots(Wrapper):\n", + " \"\"\"\n", + " Creates a wrapper that supports saving and loading environemnt states.\n", + " Required for planning algorithms.\n", + "\n", + " This class will have access to the core environment as self.env, e.g.:\n", + " - self.env.reset() #reset original env\n", + " - self.env.ale.cloneState() #make snapshot for atari. load with .restoreState()\n", + " - ...\n", + "\n", + " You can also use reset, step and render directly for convenience.\n", + " - s, r, done, _ = self.step(action) #step, same as self.env.step(action)\n", + " - self.render(close=True) #close window, same as self.env.render(close=True)\n", + " \"\"\"\n", + "\n", + " def get_snapshot(self, render=False):\n", + " \"\"\"\n", + " :returns: environment state that can be loaded with load_snapshot \n", + " Snapshots guarantee same env behaviour each time they are loaded.\n", + "\n", + " Warning! Snapshots can be arbitrary things (strings, integers, json, tuples)\n", + " Don't count on them being pickle strings when implementing MCTS.\n", + "\n", + " Developer Note: Make sure the object you return will not be affected by \n", + " anything that happens to the environment after it's saved.\n", + " You shouldn't, for example, return self.env. \n", + " In case of doubt, use pickle.dumps or deepcopy.\n", + "\n", + " \"\"\"\n", + " if render:\n", + " self.render() # close popup windows since we can't pickle them\n", + " self.close()\n", + " \n", + " if self.unwrapped.viewer is not None:\n", + " self.unwrapped.viewer.close()\n", + " self.unwrapped.viewer = None\n", + " return dumps(self.env)\n", + "\n", + " def load_snapshot(self, snapshot, render=False):\n", + " \"\"\"\n", + " Loads snapshot as current env state.\n", + " Should not change snapshot inplace (in case of doubt, deepcopy).\n", + " \"\"\"\n", + "\n", + " assert not hasattr(self, \"_monitor\") or hasattr(\n", + " self.env, \"_monitor\"), \"can't backtrack while recording\"\n", + "\n", + " if render:\n", + " self.render() # close popup windows since we can't load into them\n", + " self.close()\n", + " self.env = loads(snapshot)\n", + "\n", + " def get_result(self, snapshot, action):\n", + " \"\"\"\n", + " A convenience function that \n", + " - loads snapshot, \n", + " - commits action via self.step,\n", + " - and takes snapshot again :)\n", + "\n", + " :returns: next snapshot, next_observation, reward, is_done, info\n", + "\n", + " Basically it returns next snapshot and everything that env.step would have returned.\n", + " \"\"\"\n", + "\n", + " \n", + "\n", + " return ActionResult(< next_snapshot > , #fill in the variables\n", + " < next_observation > ,\n", + " < reward > , < is_done > , < info > )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Try out snapshots:\n", + "Let`s check our wrapper. At first, reset environment and save it, further randomly play some actions and restore our environment from the snapshot. It should be the same as our previous initial state." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# make env\n", + "env = WithSnapshots(gym.make(\"CartPole-v0\"))\n", + "env.reset()\n", + "\n", + "n_actions = env.action_space.n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"initial_state:\")\n", + "plt.imshow(env.render('rgb_array'))\n", + "env.close()\n", + "\n", + "# create first snapshot\n", + "snap0 = env.get_snapshot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# play without making snapshots (faster)\n", + "while True:\n", + " is_done = env.step(env.action_space.sample())[2]\n", + " if is_done:\n", + " print(\"Whoops! We died!\")\n", + " break\n", + "\n", + "print(\"final state:\")\n", + "plt.imshow(env.render('rgb_array'))\n", + "env.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# reload initial state\n", + "env.load_snapshot(snap0)\n", + "\n", + "print(\"\\n\\nAfter loading snapshot\")\n", + "plt.imshow(env.render('rgb_array'))\n", + "env.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# get outcome (snapshot, observation, reward, is_done, info)\n", + "res = env.get_result(snap0, env.action_space.sample())\n", + "\n", + "snap1, observation, reward = res[:3]\n", + "\n", + "# second step\n", + "res2 = env.get_result(snap1, env.action_space.sample())" + ] + }, + { + "attachments": { + "MCTS.png": { + "image/png": "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" + } + }, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# MCTS: Monte-Carlo tree search\n", + "\n", + "![MCTS.png](attachment:MCTS.png)\n", + "\n", + "In this section, we'll implement the vanilla MCTS algorithm with UCB1-based node selection.\n", + "\n", + "$$\n", + "\\dot{v_a} = v_a + C_n \\sqrt{\\frac{2 \\log {N}}{n_a}}\n", + "$$\n", + "\n", + "where: \n", + "- $N$ - number of time-steps so far,\n", + "- $n_a$ - times action a is taken\n", + "- $C_n$ - balance between exploration-exploitation, for $R \\in [0,1]$ $ C_p = \\frac{1}{\\sqrt{2}}$ \n", + "[paper](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.374.1202&rep=rep1&type=pdf)\n", + "\n", + "We will start by implementing the `Node` class - a simple class that acts like MCTS node and supports some of the MCTS algorithm steps.\n", + "\n", + "This MCTS implementation makes some assumptions about the environment, you can find those _in the notes section at the end of the notebook_." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "assert isinstance(env, WithSnapshots)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Node:\n", + " \"\"\" a tree node for MCTS \"\"\"\n", + "\n", + " # metadata:\n", + " parent = None # parent Node\n", + " value_sum = 0. # sum of state values from all visits (numerator)\n", + " times_visited = 0 # counter of visits (denominator)\n", + "\n", + " def __init__(self, parent, action,):\n", + " \"\"\"\n", + " Creates and empty node with no children.\n", + " Does so by commiting an action and recording outcome.\n", + "\n", + " :param parent: parent Node\n", + " :param action: action to commit from parent Node\n", + "\n", + " \"\"\"\n", + "\n", + " self.parent = parent\n", + " self.action = action\n", + " self.children = set() # set of child nodes\n", + "\n", + " # get action outcome and save it\n", + " res = env.get_result(parent.snapshot, action)\n", + " self.snapshot, self.observation, self.immediate_reward, self.is_done, _ = res\n", + "\n", + " def is_leaf(self):\n", + " return len(self.children) == 0\n", + "\n", + " def is_root(self):\n", + " return self.parent is None\n", + "\n", + " def get_mean_value(self):\n", + " return self.value_sum / self.times_visited if self.times_visited != 0 else 0\n", + "\n", + " def ucb_score(self, scale=10, max_value=1e100):\n", + " \"\"\"\n", + " Computes ucb1 upper bound using current value and visit counts for node and it's parent.\n", + "\n", + " :param scale: Multiplies upper bound by that. From hoeffding inequality, assumes reward range to be [0,scale].\n", + " :param max_value: a value that represents infinity (for unvisited nodes)\n", + "\n", + " \"\"\"\n", + "\n", + " if self.times_visited == 0:\n", + " return max_value\n", + "\n", + " # compute ucb-1 additive component (to be added to mean value)\n", + " # hint: you can use self.parent.times_visited for N times node was considered,\n", + " # and self.times_visited for n times it was visited\n", + "\n", + " U = \n", + "\n", + " return self.get_mean_value() + scale*U\n", + "\n", + " # MCTS steps\n", + "\n", + " def select_best_leaf(self):\n", + " \"\"\"\n", + " Picks the leaf with highest priority to expand\n", + " Does so by recursively picking nodes with best UCB-1 score until it reaches the leaf.\n", + "\n", + " \"\"\"\n", + " if self.is_leaf():\n", + " return self\n", + "\n", + " children = self.children\n", + "\n", + " best_child = \n", + "\n", + " if node.is_done:\n", + " node.propagate(0)\n", + "\n", + " else: # node is not terminal\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plan and execute\n", + "In this section, we use the MCTS implementation to find optimal policy." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env = WithSnapshots(gym.make(\"CartPole-v0\"))\n", + "root_observation = env.reset()\n", + "root_snapshot = env.get_snapshot()\n", + "root = Root(root_snapshot, root_observation)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# plan from root:\n", + "plan_mcts(root, n_iters=1000)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# import copy\n", + "# saved_root = copy.deepcopy(root)\n", + "# root = saved_root" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import clear_output\n", + "from itertools import count\n", + "from gym.wrappers import Monitor\n", + "\n", + "total_reward = 0 # sum of rewards\n", + "test_env = loads(root_snapshot) # env used to show progress\n", + "\n", + "for i in count():\n", + "\n", + " # get best child\n", + " best_child = ');\n", - " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", - " fmt_picker_span.append(fmt_picker);\n", - " nav_element.append(fmt_picker_span);\n", - " this.format_dropdown = fmt_picker[0];\n", - "\n", - " for (var ind in mpl.extensions) {\n", - " var fmt = mpl.extensions[ind];\n", - " var option = $(\n", - " '