Skip to content

Instantly share code, notes, and snippets.

@tomtung
Last active November 22, 2020 22:31
Show Gist options
  • Save tomtung/e90e9e046fd158d42bd225abd45e8890 to your computer and use it in GitHub Desktop.
Save tomtung/e90e9e046fd158d42bd225abd45e8890 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Deep Q-Learning for Solving Unity's \"Banana Collectors\" Problem\n",
"\n",
"In this notebook we report how we experimented using deep Q-learning to solve a modified version of Unitfy's \"Banana Collectors\" environment, where the agent needs to navigate a 3D space to collect as many yellow bananas as possible while trying to avoid blue bananas.\n",
"\n",
"This notebook contains all the code for training and running the agent.\n",
"\n",
"A demo of a trained agent is shown in the gif below:\n",
"\n",
"![demo](https://user-images.githubusercontent.com/513210/99918973-558ba680-2ccf-11eb-8503-a78689a3a029.gif)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Environment Setup\n",
"\n",
"The dependencies can be set up by following the instructions from the [DRLND repo](https://github.com/udacity/deep-reinforcement-learning#dependencies). Once it's done, the following imports should work:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"from unityagents import UnityEnvironment\n",
"import numpy as np\n",
"import torch\n",
"import matplotlib.pyplot as plt\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that to make GPU training work on our machine, the following version of PyTorch is used:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'1.7.0+cu110'"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"torch.__version__"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"device(type='cuda', index=0)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
"device"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Additionally, we also need to download the pre-built Unity environment, which is available for different platforms:\n",
"\n",
"- [Linux](https://s3-us-west-1.amazonaws.com/udacity-drlnd/P1/Banana/Banana_Linux.zip)\n",
"- [Mac OSX](https://s3-us-west-1.amazonaws.com/udacity-drlnd/P1/Banana/Banana.app.zip)\n",
"- [Windows (32-bit)](https://s3-us-west-1.amazonaws.com/udacity-drlnd/P1/Banana/Banana_Windows_x86.zip)\n",
"- [Windows (64-bit)](https://s3-us-west-1.amazonaws.com/udacity-drlnd/P1/Banana/Banana_Windows_x86_64.zip)\n",
"\n",
"Once downloaded and extracted, please set the path beolow according, e.g.\n",
"\n",
"- **Mac**: `\"path/to/Banana.app\"`\n",
"- **Windows** (x86): `\"path/to/Banana_Windows_x86/Banana.exe\"`\n",
"- **Windows** (x86_64): `\"path/to/Banana_Windows_x86_64/Banana.exe\"`\n",
"- **Linux** (x86): `\"path/to/Banana_Linux/Banana.x86\"`\n",
"- **Linux** (x86_64): `\"path/to/Banana_Linux/Banana.x86_64\"`\n",
"- **Linux** (x86, headless): `\"path/to/Banana_Linux_NoVis/Banana.x86\"`\n",
"- **Linux** (x86_64, headless): `\"path/to/Banana_Linux_NoVis/Banana.x86_64\"`"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"ENV_PATH = \"../Banana_Linux/Banana.x86_64\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If set up correctly, we should be able to initialize the environment:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:unityagents:\n",
"'Academy' started successfully!\n",
"Unity Academy name: Academy\n",
" Number of Brains: 1\n",
" Number of External Brains : 1\n",
" Lesson number : 0\n",
" Reset Parameters :\n",
"\t\t\n",
"Unity brain name: BananaBrain\n",
" Number of Visual Observations (per agent): 0\n",
" Vector Observation space type: continuous\n",
" Vector Observation space size (per agent): 37\n",
" Number of stacked Vector Observation: 1\n",
" Vector Action space type: discrete\n",
" Vector Action space size (per agent): 4\n",
" Vector Action descriptions: , , , \n"
]
}
],
"source": [
"env = UnityEnvironment(file_name=ENV_PATH)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"# get the default brain\n",
"brain_name = env.brain_names[0]\n",
"brain = env.brains[brain_name]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Environment description\n",
"\n",
"The simulation contains a single agent that navigates a large environment. At each time step, it has four actions at its disposal:\n",
"- `0` - walk forward \n",
"- `1` - walk backward\n",
"- `2` - turn left\n",
"- `3` - turn right\n",
"\n",
"The state space has `37` dimensions and contains the agent's velocity, along with ray-based perception of objects around agent's forward direction. A reward of `+1` is provided for collecting a yellow banana, and a reward of `-1` is provided for collecting a blue banana."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of actions: 4\n",
"States have length: 37\n"
]
}
],
"source": [
"# reset the environment\n",
"env_info = env.reset(train_mode=True)[brain_name]\n",
"\n",
"# number of actions\n",
"action_size = brain.vector_action_space_size\n",
"print('Number of actions:', action_size)\n",
"\n",
"# examine the state space \n",
"state = env_info.vector_observations[0]\n",
"state_size = len(state)\n",
"print('States have length:', state_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Methodology & Implementation\n",
"\n",
"To solve this toy problem, we experimented with deep Q-learning as described in Mnih et al. (2015). Additionally, we also added the following extensions:\n",
"\n",
"- Prioritized experience replay\n",
"- Noisy network\n",
"- Double Q-learning\n",
"- Dueling network\n",
"\n",
"The details and code are presented in the following sub-sections."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Hyper-parameters\n",
"\n",
"The hyper-parameters we used is shown as the default values in the following data class:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"AgentConfig(batch_size=256, learning_rate=0.0003, replay_buffer_size=100000, target_params_update_every=4, target_params_update_ratio=0.005, reward_discount_factor=0.99, reward_unroll_steps=5)"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"!pip install -q dataclasses\n",
"\n",
"from dataclasses import dataclass\n",
"\n",
"\n",
"@dataclass\n",
"class AgentConfig:\n",
" batch_size: int = 256\n",
" learning_rate: float = 3e-4\n",
" replay_buffer_size: int = 100_000\n",
" target_params_update_every: int = 4\n",
" target_params_update_ratio: float = 0.005\n",
" reward_discount_factor: float = 0.99\n",
" reward_unroll_steps: int = 5\n",
"\n",
"\n",
"AgentConfig()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Prioritized experience replay\n",
"\n",
"Here we use prioritized experience replay as described in Schaul et al. (2015). Specifically, we implemented the proportional priorization variant with the sum-tree data structure.\n",
"\n",
"The code for the replay buffer is as follows:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"from typing import Any, Generator, List, Tuple, Optional\n",
"\n",
"\n",
"class ProportionallyPrioritizedReplayBuffer:\n",
" \"\"\"A proportionally prioritized replay buffer implemented with sum-tree.\"\"\"\n",
"\n",
" _curr_index: int\n",
" _size: int\n",
" _max_priority: float\n",
" _sum_tree: List[float]\n",
" _priorities: List[float]\n",
" _samples: List[Any]\n",
"\n",
" def __init__(self, buffer_size: int):\n",
" assert buffer_size > 1\n",
" self._curr_index = 0\n",
" self._size = 0\n",
" self._max_priority = 1.0\n",
" self._sum_tree = [0] * (2 ** (math.floor(math.log2(buffer_size - 1)) + 1) - 1)\n",
" self._priorities = [0] * buffer_size\n",
" self._samples = [None] * buffer_size\n",
"\n",
" def _ancestor_indices(self, sample_index: int) -> Generator[int, None, None]:\n",
" assert 0 <= sample_index <= len(self._samples)\n",
" index = sample_index + len(self._sum_tree)\n",
" while index > 0:\n",
" index = (index - 1) // 2\n",
" yield index\n",
"\n",
" @staticmethod\n",
" def _children_indices(index: int) -> Tuple[int, int]:\n",
" # Note that it could go out-of-bounds for the sum tree array\n",
" left_index = index * 2 + 1\n",
" right_index = left_index + 1\n",
" return left_index, right_index\n",
"\n",
" def _set_priority(self, sample_index: int, priority: float):\n",
" assert priority > 0, \"Weights must be non-negative\"\n",
" delta = priority - self._priorities[sample_index]\n",
" self._priorities[sample_index] = priority\n",
" for index in self._ancestor_indices(sample_index):\n",
" self._sum_tree[index] += delta\n",
"\n",
" self._max_priority = max(self._max_priority, priority)\n",
"\n",
" def _set_sample(self, sample_index: int, sample: Any, priority: float):\n",
" self._set_priority(sample_index, priority)\n",
" self._samples[sample_index] = sample\n",
"\n",
" class _SampleHandle:\n",
" _parent: \"ProportionallyPrioritizedReplayBuffer\"\n",
" _index: int\n",
"\n",
" def __init__(self, parent: \"ProportionallyPrioritizedReplayBuffer\", index: int):\n",
" assert 0 <= index <= len(parent._samples)\n",
" self._parent = parent\n",
" self._index = index\n",
"\n",
" @property\n",
" def value(self) -> Any:\n",
" return self._parent._samples[self._index]\n",
"\n",
" @property\n",
" def priority(self) -> float:\n",
" return self._parent._priorities[self._index]\n",
"\n",
" @priority.setter\n",
" def priority(self, priority: float):\n",
" self._parent._set_priority(self._index, priority)\n",
"\n",
" def reset(self, value: Any, priority: float):\n",
" self._parent._set_sample(self._index, value, priority)\n",
"\n",
" def add(self, value: Any, priority: Optional[float] = None):\n",
" \"\"\"Add a new sample.\"\"\"\n",
" if priority is None:\n",
" priority = self._max_priority\n",
"\n",
" self._SampleHandle(self, self._curr_index).reset(value, priority)\n",
"\n",
" buffer_size = len(self._samples)\n",
" self._curr_index = (self._curr_index + 1) % buffer_size\n",
" self._size = min(self._size + 1, buffer_size)\n",
"\n",
" @property\n",
" def priority_sum(self):\n",
" return self._sum_tree[0]\n",
"\n",
" @property\n",
" def max_priority(self):\n",
" return self._max_priority\n",
"\n",
" def sample_single(self, query: Optional[float] = None) -> _SampleHandle:\n",
" \"\"\"Draw a sample.\"\"\"\n",
" assert self.priority_sum > 0.0, \"Nothing has been added\"\n",
"\n",
" if query is None:\n",
" query = random.random()\n",
"\n",
" assert 0.0 <= query <= 1.0\n",
" target = self.priority_sum * query\n",
" index = 0\n",
" while True:\n",
" assert 0.0 <= target <= self._sum_tree[index]\n",
" index_l, index_r = self._children_indices(index)\n",
" assert (index_l < len(self._sum_tree)) == (index_r < len(self._sum_tree))\n",
" if index_l >= len(self._sum_tree):\n",
" index_l -= len(self._sum_tree)\n",
" index_r -= len(self._sum_tree)\n",
" break\n",
"\n",
" sum_l = self._sum_tree[index_l]\n",
" if target <= sum_l:\n",
" index = index_l\n",
" else:\n",
" target -= sum_l\n",
" index = index_r\n",
"\n",
" assert index_l < len(self._priorities)\n",
" if target <= self._priorities[index_l]:\n",
" index = index_l\n",
" else:\n",
" assert index_r < len(self._priorities)\n",
" index = index_r\n",
"\n",
" return self._SampleHandle(self, index)\n",
"\n",
" def sample_batch(self, batch_size: int) -> List[_SampleHandle]:\n",
" \"\"\"Draw a stratified batch of samples with the given size.\"\"\"\n",
" end_points = np.linspace(0.0, 1.0, batch_size + 1).tolist()\n",
" return [\n",
" self.sample_single(query=random.uniform(l, r))\n",
" for l, r in zip(end_points[:-1], end_points[1:])\n",
" ]\n",
"\n",
" def __len__(self):\n",
" \"\"\"Return the current size of internal memory.\"\"\"\n",
" return self._size"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Noisy Network\n",
"\n",
"Following Fortunato et al. (2017), here we add factorized Gaussian noise to all layer parameters for more effective exploration.\n",
"\n",
"In addition to better training time exploration, we also found that without the noisy layers, the agent might get stuck at certain states and keep oscillating without making any more progress. The small amount of noise from the trained noisy layers also effectively prevent this from happening.\n",
"\n",
"The custom noisy linear layer is implemented below:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"\n",
"\n",
"class FactorizedNoisyLinear(torch.nn.Module):\n",
" __constants__ = [\"in_features\", \"out_features\"]\n",
" in_features: int\n",
" out_features: int\n",
"\n",
" weight_mean: torch.Tensor\n",
" weight_var: torch.Tensor\n",
" bias_mean: torch.Tensor\n",
" bias_var: torch.Tensor\n",
"\n",
" def __init__(self, in_features: int, out_features: int) -> None:\n",
" super().__init__()\n",
" self.in_features = in_features\n",
" self.out_features = out_features\n",
" self.weight_mean = torch.nn.Parameter(torch.Tensor(out_features, in_features))\n",
" self.weight_var = torch.nn.Parameter(torch.Tensor(out_features, in_features))\n",
" self.bias_mean = torch.nn.Parameter(torch.Tensor(out_features))\n",
" self.bias_var = torch.nn.Parameter(torch.Tensor(out_features))\n",
" self.reset_parameters()\n",
"\n",
" def reset_parameters(self) -> None:\n",
" x = 1 / math.sqrt(self.in_features)\n",
" for mean in [self.weight_mean, self.bias_mean]:\n",
" torch.nn.init.uniform_(mean, -x, x)\n",
"\n",
" for var in [self.weight_var, self.bias_var]:\n",
" torch.nn.init.constant_(var, 0.4 * x)\n",
"\n",
" def forward(self, input: torch.Tensor) -> torch.Tensor:\n",
" def f(x: torch.Tensor):\n",
" return torch.sign(x) * torch.sqrt(torch.abs(x))\n",
"\n",
" epsilon_in = f(torch.randn(1, self.in_features, device=input.device))\n",
" epsilon_out = f(torch.randn(self.out_features, 1, device=input.device))\n",
"\n",
" weight = self.weight_mean + (epsilon_out @ epsilon_in) * self.weight_var\n",
" bias = self.bias_mean + epsilon_out.squeeze() * self.bias_var\n",
" return torch.nn.functional.linear(input, weight, bias)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Dueling network\n",
"\n",
"We also use the dueling network architecture described in Wang et al. (2015). Specifically, the network has one fully-connected layer before forking into value and advantage streams, and each stream has one hidden layer and one output layer. We use 128 hidden units for all hidden layers, and use ReLU as the activation function."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"class DuelingNetwork(torch.nn.Module):\n",
" \"\"\"Actor (Policy) Model.\"\"\"\n",
"\n",
" def __init__(\n",
" self,\n",
" state_size,\n",
" action_size,\n",
" hidden_size_1=128,\n",
" hidden_size_2=128,\n",
" activation_fn=torch.nn.functional.relu,\n",
" ):\n",
" \"\"\"Initialize parameters and build model.\n",
" Params\n",
" ======\n",
" state_size (int): Dimension of each state\n",
" action_size (int): Dimension of each action\n",
" \"\"\"\n",
" super().__init__()\n",
" self.activation_fn = activation_fn\n",
" self.fc1 = FactorizedNoisyLinear(state_size, hidden_size_1)\n",
" self.fc2_v = FactorizedNoisyLinear(hidden_size_1, hidden_size_2)\n",
" self.fc2_a = FactorizedNoisyLinear(hidden_size_1, hidden_size_2)\n",
" self.fc3_v = FactorizedNoisyLinear(hidden_size_2, 1)\n",
" self.fc3_a = FactorizedNoisyLinear(hidden_size_2, action_size)\n",
"\n",
" def forward(self, state):\n",
" \"\"\"Build a network that maps state -> action values.\"\"\"\n",
" x = state\n",
" x = self.activation_fn(self.fc1(x))\n",
"\n",
" v = self.activation_fn(self.fc2_v(x))\n",
" v = self.fc3_v(v)\n",
"\n",
" a = self.activation_fn(self.fc2_a(x))\n",
" a = self.fc3_a(a)\n",
" a = a - a.mean(dim=1, keepdim=True)\n",
"\n",
" x = v + a\n",
" return x"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Double Q-learning\n",
"\n",
"Finally to put it together, we used double Q-learning as described by van Hasselt et al. (2015). The agent is implemented as follows:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"from collections import namedtuple, deque\n",
"import copy\n",
"from typing import List, Any, Generator, Tuple, Optional\n",
"\n",
"import torch\n",
"import torch.nn.functional as F\n",
"import torch.optim as optim\n",
"\n",
"class Agent:\n",
" \"\"\"Interacts with and learns from the environment.\"\"\"\n",
"\n",
" def __init__(self, state_size, action_size, config: Optional[AgentConfig] = None):\n",
" \"\"\"Initialize an Agent object.\n",
"\n",
" Params\n",
" ======\n",
" state_size (int): dimension of each state\n",
" action_size (int): dimension of each action\n",
" \"\"\"\n",
" self.state_size = state_size\n",
" self.action_size = action_size\n",
" self.config = config or AgentConfig()\n",
"\n",
" # Q-Network\n",
" self.qnetwork_local = DuelingNetwork(state_size, action_size).to(device)\n",
" self.qnetwork_target = copy.deepcopy(self.qnetwork_local)\n",
" self.qnetwork_target.eval()\n",
"\n",
" self.optimizer = optim.Adam(\n",
" self.qnetwork_local.parameters(), lr=self.config.learning_rate\n",
" )\n",
"\n",
" # Replay memory\n",
" self.replay_buffer = ProportionallyPrioritizedReplayBuffer(\n",
" self.config.replay_buffer_size\n",
" )\n",
" self.experience_unroll_queue = deque(\n",
" maxlen=self.config.reward_unroll_steps\n",
" )\n",
"\n",
"\n",
" # Initialize time step\n",
" self.t_step = 0\n",
"\n",
" def step(self, state, action, reward, next_state, done):\n",
" self.t_step += 1\n",
"\n",
" # Save experience in replay memory\n",
" self.experience_unroll_queue.append(\n",
" (state, action, next_state, reward, done)\n",
" )\n",
" if len(self.experience_unroll_queue) >= self.config.reward_unroll_steps:\n",
" state, action, _, _, _ = self.experience_unroll_queue[0]\n",
" _, _, next_state, _, done = self.experience_unroll_queue[-1]\n",
" reward = sum(\n",
" (self.config.reward_discount_factor ** i) * r\n",
" for i, (_, _, _, r, _) in enumerate(self.experience_unroll_queue)\n",
" )\n",
" self.replay_buffer.add((state, action, reward, next_state, done))\n",
"\n",
" self.learn()\n",
"\n",
" def act(self, state):\n",
" \"\"\"Returns actions for given state as per current policy.\n",
"\n",
" Params\n",
" ======\n",
" state (array_like): current state\n",
" \"\"\"\n",
" state = torch.from_numpy(state).float().unsqueeze(0).to(device)\n",
" self.qnetwork_local.eval()\n",
" with torch.no_grad():\n",
" action_values = self.qnetwork_local(state)\n",
" self.qnetwork_local.train()\n",
" return np.argmax(action_values.cpu().data.numpy())\n",
"\n",
" def learn(self):\n",
" \"\"\"Update value parameters using given batch of experience tuples.\"\"\"\n",
" if len(self.replay_buffer) < self.config.batch_size:\n",
" return\n",
" \n",
" experiences = self.replay_buffer.sample_batch(self.config.batch_size)\n",
" \n",
" with torch.no_grad():\n",
" states, actions, rewards, next_states, dones = [\n",
" torch.from_numpy(np.vstack(v)).to(dtype=dtype, device=device)\n",
" for v, dtype in zip(\n",
" zip(*[e.value for e in experiences]),\n",
" [torch.float, torch.long, torch.float, torch.float, torch.float],\n",
" )\n",
" ]\n",
" sample_probs = (\n",
" torch.from_numpy(np.vstack([e.priority for e in experiences])).to(\n",
" dtype=torch.float, device=device\n",
" )\n",
" / self.replay_buffer.priority_sum\n",
" )\n",
" weights = 1.0 / (sample_probs * len(self.replay_buffer))\n",
"\n",
" q_curr = torch.gather(self.qnetwork_local(states), dim=-1, index=actions)\n",
"\n",
" with torch.no_grad():\n",
" _, next_actions = torch.max(\n",
" self.qnetwork_local(states), dim=-1, keepdim=True\n",
" )\n",
" q_target = rewards + (\n",
" (1.0 - dones)\n",
" * (self.config.reward_discount_factor ** self.config.reward_unroll_steps)\n",
" * torch.gather(\n",
" self.qnetwork_target(next_states), dim=-1, index=next_actions\n",
" )\n",
" )\n",
"\n",
" losses = F.mse_loss(q_curr, q_target, reduction=\"none\")\n",
"\n",
" # Update sample priorities\n",
" with torch.no_grad():\n",
" new_priorities = (\n",
" (losses.sqrt() + 1e-6)\n",
" .squeeze()\n",
" .cpu()\n",
" .numpy()\n",
" )\n",
" assert len(experiences) == len(new_priorities)\n",
" for experience, priority in zip(experiences, new_priorities):\n",
" experience.priority = priority\n",
"\n",
" loss = torch.mean(losses * weights)\n",
" self.optimizer.zero_grad()\n",
" loss.backward()\n",
" self.optimizer.step()\n",
"\n",
" # Update target network\n",
" if self.t_step % self.config.target_params_update_every == 0:\n",
" self.soft_update(self.qnetwork_local, self.qnetwork_target)\n",
"\n",
" def soft_update(self, local_model, target_model):\n",
" \"\"\"Soft update model parameters.\n",
" θ_target = τ*θ_local + (1 - τ)*θ_target\n",
"\n",
" Params\n",
" ======\n",
" local_model (PyTorch model): weights will be copied from\n",
" target_model (PyTorch model): weights will be copied to\n",
" \"\"\"\n",
" for target_param, local_param in zip(\n",
" target_model.parameters(), local_model.parameters()\n",
" ):\n",
" tau = self.config.target_params_update_ratio\n",
" target_param.data.copy_(\n",
" tau * local_param.data + (1.0 - tau) * target_param.data\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Train & Evaluate\n",
"\n",
"With everything set up, we're now ready to train the agent. Note that since we use noisy networks for exploration, the epsilon-greedy strategy is not needed here.\n",
"\n",
"We train the model until the agent gets an average score of +13 over 100 consecutive episodes."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"agent = Agent(state_size=state_size, action_size=action_size)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"from collections import deque\n",
"\n",
"def dqn(n_episodes=3000, max_t=1000):\n",
" \"\"\"Deep Q-Learning.\n",
"\n",
" Params\n",
" ======\n",
" n_episodes (int): maximum number of training episodes\n",
" max_t (int): maximum number of timesteps per episode\n",
" \"\"\"\n",
" scores = [] # list containing scores from each episode\n",
" scores_window = deque(maxlen=100) # last 100 scores\n",
" for i_episode in range(1, n_episodes + 1):\n",
" env_info = env.reset(train_mode=True)[brain_name]\n",
" state = env_info.vector_observations[0]\n",
" score = 0\n",
" for _ in range(max_t):\n",
" action = agent.act(state)\n",
" env_info = env.step(action)[brain_name]\n",
" next_state = env_info.vector_observations[0]\n",
" reward = env_info.rewards[0]\n",
" done = env_info.local_done[0]\n",
" agent.step(state, action, reward, next_state, done)\n",
" \n",
" score += reward\n",
" state = next_state\n",
" if done:\n",
" break\n",
"\n",
" scores_window.append(score) # save most recent score\n",
" scores.append(score) # save most recent score\n",
" average_score = np.mean(scores_window)\n",
" print(\n",
" f\"\\rEpisode {i_episode}\\tScore: {score:.2f}\\tWindowed average Score: {average_score:.2f}\",\n",
" end=\"\\n\" if i_episode % 100 == 0 else \"\",\n",
" )\n",
" if len(scores) > 100 and average_score > 13.:\n",
" print(\n",
" f\"\\nEnvironment solved between episode {i_episode - 100} and episode {i_episode}!\"\n",
" f\"\\tAverage Score: {average_score:.2f}\"\n",
" )\n",
" torch.save(agent.qnetwork_local.state_dict(), \"model.pt\")\n",
" break\n",
"\n",
" return scores"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 2 µs, sys: 0 ns, total: 2 µs\n",
"Wall time: 4.53 µs\n",
"Episode 100\tScore: 8.00\tWindowed average Score: 10.17\n",
"Episode 135\tScore: 16.00\tWindowed average Score: 13.04\n",
"Environment solved between episode 35 and episode 135!\tAverage Score: 13.04\n"
]
}
],
"source": [
"%time\n",
"scores = dqn()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The following graph shows how the score changes as we train for more episodes:"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 720x432 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"fig = plt.figure(figsize=(10, 6))\n",
"ax = fig.add_subplot(111)\n",
"plt.plot(np.arange(1, len(scores)+1), scores)\n",
"plt.plot(np.arange(1, len(scores)+1), [13.] * len(scores))\n",
"plt.ylabel('Score')\n",
"plt.xlabel('Episode #')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, runing the following cell would simulate one run to demonstrate how the trained agent performs:"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Score: 11.0\n"
]
}
],
"source": [
"env_info = env.reset(train_mode=False)[brain_name]\n",
"state = env_info.vector_observations[0]\n",
"score = 0\n",
"while True:\n",
" action = np.random.randint(action_size)\n",
" action = agent.act(state)\n",
" env_info = env.step(action)[brain_name]\n",
" next_state = env_info.vector_observations[0]\n",
" reward = env_info.rewards[0]\n",
" done = env_info.local_done[0]\n",
" score += reward\n",
" state = next_state\n",
" if done:\n",
" break\n",
"\n",
"print(\"Score: {}\".format(score))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Next steps\n",
"\n",
"During our experiment, we haven't got around to thoroughly tune the hyper-parameters, or analyze how each extension to DQN separately affects the training process and agent performance.\n",
"\n",
"It would also be interesting to experiment with all the other extensions to DQN that are described in Rainbow (Hessel et al. 2017), which effectively serves as the current baseline.\n",
"\n",
"We also observed that the agent sometimes ignores a cluster of yellow bananas near it that it saw a few seconds ago, and instead chases after another banana that is currently visible but far away. This inefficiency is likely caused by the decision process not being fully Markovian, since the the agent can't even observe all directions around it at the same time. One solution would be introduce some kind of recurrent mechanism as described in Hausknecht and Stone (2015), so that the agent can remember what it has seen recently and act more efficiently."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## References\n",
"\n",
"- Mnih, V. et al. (2015) ‘Human-level control through deep reinforcement learning’, Nature, 518(7540), pp. 529–533. doi: 10.1038/nature14236.\n",
"- Schaul, T. et al. (2015) ‘Prioritized Experience Replay’. Available at: http://arxiv.org/abs/1511.05952\n",
"- Wang, Z. et al. (2015) ‘Dueling Network Architectures for Deep Reinforcement Learning’. Available at: http://arxiv.org/abs/1511.06581\n",
"- Fortunato, M. et al. (2017) ‘Noisy Networks for Exploration’. Available at: https://arxiv.org/abs/1706.10295\n",
"- van Hasselt, H., Guez, A. and Silver, D. (2015) ‘Deep Reinforcement Learning with Double Q-learning’. Available at: http://arxiv.org/abs/1509.06461\n",
"- Hessel, M. et al. (2017) ‘Rainbow: Combining Improvements in Deep Reinforcement Learning’. Available at: http://arxiv.org/abs/1710.02298\n",
"- Hausknecht, M. and Stone, P. (2015) ‘Deep Recurrent Q-Learning for Partially Observable MDPs’. Available at: http://arxiv.org/abs/1507.06527"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "drlnd",
"language": "python",
"name": "drlnd"
},
"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.12"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@tomtung
Copy link
Author

tomtung commented Nov 22, 2020

demo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment