From 13e9070f569ee7f7acca54d9b22de90990deead7 Mon Sep 17 00:00:00 2001 From: AngryCracker Date: Wed, 28 Feb 2018 12:50:31 +0530 Subject: [PATCH 1/2] Added pathfinding example --- mdp_apps.ipynb | 193 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 166 insertions(+), 27 deletions(-) diff --git a/mdp_apps.ipynb b/mdp_apps.ipynb index 78542e075..50dce5427 100644 --- a/mdp_apps.ipynb +++ b/mdp_apps.ipynb @@ -31,7 +31,8 @@ " - State dependent reward function\n", " - State and action dependent reward function\n", " - State, action and next state dependent reward function\n", - "\n", + "- Grid MDP\n", + " - Pathfinding problem\n", "\n", "## SIMPLE MDP\n", "---\n", @@ -221,7 +222,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "['study', 'pub', 'sleep', 'facebook', 'quit']\n" + "['quit', 'sleep', 'study', 'pub', 'facebook']\n" ] } ], @@ -294,7 +295,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'class3': 'pub', 'leisure': 'quit', 'class2': 'study', 'class1': 'study', 'end': None}\n" + "{'class2': 'sleep', 'class3': 'pub', 'end': None, 'class1': 'study', 'leisure': 'quit'}\n" ] } ], @@ -318,7 +319,7 @@ "data": { "text/plain": [ "{'class1': 'study',\n", - " 'class2': 'study',\n", + " 'class2': 'sleep',\n", " 'class3': 'pub',\n", " 'end': None,\n", " 'leisure': 'quit'}" @@ -668,7 +669,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "['study', 'pub', 'sleep', 'facebook', 'quit']\n" + "['quit', 'sleep', 'study', 'pub', 'facebook']\n" ] } ], @@ -769,7 +770,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'class3': 'study', 'leisure': 'quit', 'class2': 'sleep', 'class1': 'facebook', 'end': None}\n" + "{'class2': 'sleep', 'class3': 'study', 'end': None, 'class1': 'facebook', 'leisure': 'quit'}\n" ] } ], @@ -832,10 +833,9 @@ "We have the following transition probability matrices:\n", "
\n", "
\n", - "Action 1: Cruising streets\n", - "
\n", + "Action 1: Cruising streets \n", "
\n", - "$$\\\\\n", + "$\\\\\n", " P^{1} = \n", " \\left[ {\\begin{array}{ccc}\n", " \\frac{1}{2} & \\frac{1}{4} & \\frac{1}{4} \\\\\n", @@ -843,13 +843,12 @@ " \\frac{1}{4} & \\frac{1}{4} & \\frac{1}{2} \\\\\n", " \\end{array}}\\right] \\\\\n", " \\\\\n", - "$$\n", + " $\n", "
\n", "
\n", - "Action 2: Waiting at the taxi stand \n", + "Action 2: Waiting at the taxi stand \n", "
\n", - "
\n", - "$$\\\\\n", + "$\\\\\n", " P^{2} = \n", " \\left[ {\\begin{array}{ccc}\n", " \\frac{1}{16} & \\frac{3}{4} & \\frac{3}{16} \\\\\n", @@ -857,13 +856,12 @@ " \\frac{1}{8} & \\frac{3}{4} & \\frac{1}{8} \\\\\n", " \\end{array}}\\right] \\\\\n", " \\\\\n", - "$$\n", + " $\n", "
\n", "
\n", "Action 3: Waiting for dispatch \n", "
\n", - "
\n", - "$$\\\\\n", + "$\\\\\n", " P^{3} =\n", " \\left[ {\\begin{array}{ccc}\n", " \\frac{1}{4} & \\frac{1}{8} & \\frac{5}{8} \\\\\n", @@ -871,7 +869,7 @@ " \\frac{3}{4} & \\frac{1}{16} & \\frac{3}{16} \\\\\n", " \\end{array}}\\right] \\\\\n", " \\\\\n", - "$$\n", + " $\n", "
\n", "
\n", "For the sake of readability, we will call the states A, B and C and the actions 'cruise', 'stand' and 'dispatch'.\n", @@ -914,8 +912,7 @@ "
\n", "Action 1: Cruising streets \n", "
\n", - "
\n", - "$$\\\\\n", + "$\\\\\n", " R^{1} = \n", " \\left[ {\\begin{array}{ccc}\n", " 10 & 4 & 8 \\\\\n", @@ -923,13 +920,12 @@ " 10 & 2 & 8 \\\\\n", " \\end{array}}\\right] \\\\\n", " \\\\\n", - "$$\n", + " $\n", "
\n", "
\n", "Action 2: Waiting at the taxi stand \n", "
\n", - "
\n", - "$$\\\\\n", + "$\\\\\n", " R^{2} = \n", " \\left[ {\\begin{array}{ccc}\n", " 8 & 2 & 4 \\\\\n", @@ -937,13 +933,12 @@ " 6 & 4 & 2\\\\\n", " \\end{array}}\\right] \\\\\n", " \\\\\n", - "$$\n", + " $\n", "
\n", "
\n", "Action 3: Waiting for dispatch \n", "
\n", - "
\n", - "$$\\\\\n", + "$\\\\\n", " R^{3} = \n", " \\left[ {\\begin{array}{ccc}\n", " 4 & 6 & 4 \\\\\n", @@ -951,7 +946,7 @@ " 4 & 0 & 8\\\\\n", " \\end{array}}\\right] \\\\\n", " \\\\\n", - "$$\n", + " $\n", "
\n", "
\n", "We now build the reward model as a dictionary using these matrices." @@ -1194,7 +1189,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "['cruise', 'dispatch', 'stand']\n" + "['stand', 'dispatch', 'cruise']\n" ] } ], @@ -1290,6 +1285,150 @@ "We have successfully adapted the existing code to a different scenario yet again.\n", "The takeaway from this section is that you can convert the vast majority of reinforcement learning problems into MDPs and solve for the best policy using simple yet efficient tools." ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## GRID MDP\n", + "---\n", + "### Pathfinding Problem\n", + "Markov Decision Processes can be used to find the best path through a maze. Let us consider this simple maze.\n", + "![title](images/maze.png)\n", + "\n", + "This environment can be formulated as a GridMDP.\n", + "
\n", + "To make the grid matrix, we will consider the state-reward to be -0.1 for every state.\n", + "
\n", + "State (1, 1) will have a reward of -5 to signify that this state is to be prohibited.\n", + "
\n", + "State (9, 9) will have a reward of +5.\n", + "This will be the terminal state.\n", + "
\n", + "The matrix can be generated using the GridMDP editor or we can write it ourselves." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "grid = [\n", + " [None, None, None, None, None, None, None, None, None, None, None], \n", + " [None, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, None, +5.0, None], \n", + " [None, -0.1, None, None, None, None, None, None, None, -0.1, None], \n", + " [None, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, None], \n", + " [None, -0.1, None, None, None, None, None, None, None, None, None], \n", + " [None, -0.1, None, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, None], \n", + " [None, -0.1, None, None, None, None, None, -0.1, None, -0.1, None], \n", + " [None, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, None, -0.1, None], \n", + " [None, None, None, None, None, -0.1, None, -0.1, None, -0.1, None], \n", + " [None, -5.0, -0.1, -0.1, -0.1, -0.1, None, -0.1, None, -0.1, None], \n", + " [None, None, None, None, None, None, None, None, None, None, None]\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have only one terminal state, (9, 9)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "terminals = [(9, 9)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We define our maze environment below" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [], + "source": [ + "maze = GridMDP(grid, terminals)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To solve the maze, we can use the `best_policy` function along with `value_iteration`." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "pi = best_policy(maze, value_iteration(maze))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is the heatmap generated by the GridMDP editor using `value_iteration` on this environment\n", + "
\n", + "![title](images/mdp-d.png)\n", + "
\n", + "Let's print out the best policy" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None None None None None None None None None None None\n", + "None v < < < < < < None . None\n", + "None v None None None None None None None ^ None\n", + "None > > > > > > > > ^ None\n", + "None ^ None None None None None None None None None\n", + "None ^ None > > > > v < < None\n", + "None ^ None None None None None v None ^ None\n", + "None ^ < < < < < < None ^ None\n", + "None None None None None ^ None ^ None ^ None\n", + "None > > > > ^ None ^ None ^ None\n", + "None None None None None None None None None None None\n" + ] + } + ], + "source": [ + "from utils import print_table\n", + "print_table(maze.to_arrows(pi))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can infer, we can find the path to the terminal state starting from any given state using this policy.\n", + "All maze problems can be solved by formulating it as a MDP." + ] } ], "metadata": { From 6848290a6233b3f58ed32820d6b36cbdfe6b92ef Mon Sep 17 00:00:00 2001 From: AngryCracker Date: Wed, 28 Feb 2018 12:51:37 +0530 Subject: [PATCH 2/2] Added images --- images/maze.png | Bin 0 -> 4576 bytes images/mdp-d.png | Bin 0 -> 21321 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/maze.png create mode 100644 images/mdp-d.png diff --git a/images/maze.png b/images/maze.png new file mode 100644 index 0000000000000000000000000000000000000000..f3fcd19904cfe1ae6e57f38897ee1b924745e6fe GIT binary patch literal 4576 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 literal 0 HcmV?d00001 diff --git a/images/mdp-d.png b/images/mdp-d.png new file mode 100644 index 0000000000000000000000000000000000000000..8ba7cf073988e5487d1a2e2d4b2fb37af08b9f40 GIT binary patch literal 21321 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 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy