Created
March 1, 2022 22:25
-
-
Save fgolemo/6144f7b7ddd970d19f6d9804573c5d48 to your computer and use it in GitHub Desktop.
Toy example for Autobots: Latent Variable Sequential Set Transformers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"nbformat": 4, | |
"nbformat_minor": 0, | |
"metadata": { | |
"accelerator": "GPU", | |
"colab": { | |
"name": "autobot_toy.ipynb", | |
"provenance": [], | |
"collapsed_sections": [] | |
}, | |
"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" | |
} | |
}, | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "EB9MwlqV98w-" | |
}, | |
"source": [ | |
"# AutoBot Toy Dataset Modelling" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"collapsed": true, | |
"id": "6lMLqmTN9i8G" | |
}, | |
"source": [ | |
"## Generate Small Synthetic Non-linear Particle Accelerator Dataset\n", | |
"In this section, we generate our tiny toy dataset that showcases AutoBot's ability to model multimodal trajectories. We generate these trajectories by adopting a simple bicycle model that turns with a constant steering angle at a constant speed. This toy dataset not only demonstrates the multimodal trajectories AutoBot generates, but also shows the importance of the entropy regularization term." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"colab": { | |
"base_uri": "https://localhost:8080/", | |
"height": 596 | |
}, | |
"id": "cBIkHktQ9i8K", | |
"outputId": "a4a79aad-763c-4b8a-dee5-4b9c67856ce1" | |
}, | |
"source": [ | |
"import numpy as np\n", | |
"import matplotlib.pyplot as plt\n", | |
"%matplotlib inline\n", | |
"\n", | |
"\n", | |
"l = 2 # length of bycicle\n", | |
"dt = 0.5\n", | |
"start_pos = np.array([-0.0, -10.0])\n", | |
"data = []\n", | |
"\n", | |
"# we'll generate a total of 6 trajectories\n", | |
"\n", | |
"# we will have 2 trajectories go left, 2 go straight, 2 go right\n", | |
"phis = [-0.2, 0.0, 0.2]\n", | |
"\n", | |
"# the trjeactories will go left/straight/right at one of 2 possible speeds\n", | |
"speeds = [1.5, 3.0]\n", | |
"\n", | |
"configs = np.array(np.meshgrid(phis, speeds)).T.reshape(-1, 2)\n", | |
"\n", | |
"fig, ax = plt.subplots(nrows=2, ncols=3, figsize=(15, 10))\n", | |
"row = 0\n", | |
"for i, config in enumerate(configs):\n", | |
" wheel_pos = []\n", | |
" speed = 3.0\n", | |
" heading = np.pi / 2\n", | |
" phi = 0.0\n", | |
" positions = {\n", | |
" \"rear\": np.array([0.0, 0.0]) + start_pos,\n", | |
" \"front\": np.array([l * np.cos(heading), l * np.sin(heading)]) + start_pos\n", | |
" }\n", | |
"\n", | |
" # we are generating trajectories of total length 18, \n", | |
" # 6 of which will be used as input trajectory and\n", | |
" # the remaining 12 as output trajectory\n", | |
" for t in range(18):\n", | |
" \n", | |
" # the first 6 steps are fixed to be straight upwards-facing at the given velocity\n", | |
" if t > 6:\n", | |
" phi, speed = config\n", | |
"\n", | |
" # for the remaining 12 steps, we apply different headings and velocities\n", | |
" x_v = speed*np.cos(heading)\n", | |
" y_v = speed*np.sin(heading)\n", | |
" omega = speed*np.tan(phi)/l\n", | |
" heading += omega * dt\n", | |
" positions[\"rear\"] += np.array([x_v * dt, y_v * dt])\n", | |
" positions[\"front\"] = positions[\"rear\"] + np.array([l * np.cos(heading), l * np.sin(heading)])\n", | |
" wheel_pos.append([positions[\"rear\"][0], positions[\"rear\"][1], positions[\"front\"][0], positions[\"front\"][1]])\n", | |
" data.append(np.array(wheel_pos))\n", | |
" \n", | |
" # plotting the data\n", | |
" col = i % 3\n", | |
" if i > 0 and i % 3 == 0:\n", | |
" row += 1\n", | |
" ax[row, col].scatter(np.array(wheel_pos)[:6, 0], np.array(wheel_pos)[:6, 1], color='#94D0FF', label='past', s=40)\n", | |
" ax[row, col].scatter(np.array(wheel_pos)[6:, 0], np.array(wheel_pos)[6:, 1], color='#FF6AD5', label='future', s=40)\n", | |
" ax[row, col].axis(xmin=-15, xmax=15, ymin=-15, ymax=20)\n", | |
"\n", | |
"ax[1, 2].legend()\n", | |
"plt.show()\n", | |
"data = np.array(data)[:, :, :2]" | |
], | |
"execution_count": 2, | |
"outputs": [ | |
{ | |
"output_type": "display_data", | |
"data": { | |
"image/png": "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kV53eXk58/Pt7rngipr2Lc4l+/Yo6AAA2NpyTT74aPL2zyQ/94Xm8YOPNse36U1velM+//nP5/u+7/ty8cUX5+d//ufPPHfttdfmkUceydvf/vZ87nOfy3XXXZef/MmfzB//8R/nB37gB85831ve8pbcfvvtSZrtEd72trflJS95Sf7zf/7P+ehHP5obbrghL3nJS/La1742X/3qV7c91mG4qqZ9J1aSx0/axgAAgK2t3+v45MrZLbHueGzbL/me97wn3/AN35CPf/zjeetb3zrwe2699dZ80zd9U+677778h//wH7Z8zec973m5995788pXvjLveMc7cuedd+bee+/NwYMH84u/+IvbHuswTL+kPcv17P8p50rT9XJtTd38JtsdAACwc2211/FN+3oz++uf/tN/miT57//9v+fBBx/My1/+8iTJqVOncsMNN0z13Io62rP+LktW/4951/Hm8ZYDnQ0LAICeOrPX8YCplmt7HS/uGesUCwsLWVk5O4PsxIkT2/q+Zz3rWUmSWmu++7u/Ox/4wAfGGtco+lHWcuHb6i6LqZgAAGzUwl7HV155Ze69994kyb333psvfOELSZJnP/vZ+cpXvnLm+/7e3/t7efDBB3Py5MkcP348H/vYxwa+3vXXX58//dM/zcMPP5wk+drXvpa//Mu/HHuc5zORoq6U8t5SyhOllPvXHfuZUsrRUsp9qx/fP4lzMaPO3GUZYO0uC0yYbAL6Sj7BkFrY6/jmm2/OsWPH8s3f/M355V/+5bzgBS9I0qyRe/nLX55rr702P/mTP5nLL788t9xyS6699trccsst+dZv/daBr7d3797cfvvtef3rX58Xv/jFueGGG/IXf/EXY4/zfCY1/fL2JL+c5P/ZcPxdtdaff+a3s+O0cJcFBrg9sgnop9sjn2A4a3saD+rLMIZHHnnkzOcf/ehHB37P+9///nO+fuc735l3vvOd532tJPnO7/zOfOpTnxprfKOYSFFXa/1EKeXKSbwWF6i1uywbp2BO8C4LbCSbgL6STzACex1vadq/jbeUUj69OsXguVM+F3138/6zb5/vmTtb0I15lwW2QTYBfSWfYDP2Ot7UNH8jv5rkm5Jcl+TRJL8w6JtKKYdKKXeXUu5+8sknpzgcOrd2l+XWFyZvu6p5vOWA7Qxo21DZlMgnoHWunYBtmVpRV2t9vNa6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\n", | |
"text/plain": [ | |
"<Figure size 1080x720 with 6 Axes>" | |
] | |
}, | |
"metadata": { | |
"tags": [], | |
"needs_background": "light" | |
} | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "9JaFXTes9i8M" | |
}, | |
"source": [ | |
"## Creating a pytorch dataloader" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"colab": { | |
"base_uri": "https://localhost:8080/", | |
"height": 0 | |
}, | |
"id": "SlVSU4jFSyC-", | |
"outputId": "50d4c9c3-2b1a-471d-934f-02fb05a50fe3" | |
}, | |
"source": [ | |
"# Code tested with pytorch 1.6.0\n", | |
"!pip install -q torch==1.6.0 torchvision==0.7.0" | |
], | |
"execution_count": 3, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"\u001b[K |████████████████████████████████| 748.8MB 20kB/s \n", | |
"\u001b[K |████████████████████████████████| 5.9MB 21.5MB/s \n", | |
"\u001b[31mERROR: torchtext 0.9.1 has requirement torch==1.8.1, but you'll have torch 1.6.0 which is incompatible.\u001b[0m\n", | |
"\u001b[?25h" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "P0eRpxAs9i8M" | |
}, | |
"source": [ | |
"from torch.utils.data import Dataset\n", | |
"\n", | |
"\n", | |
"class NPYFakeDataset(Dataset):\n", | |
" def __init__(self):\n", | |
" self.ego_dataset = data\n", | |
"\n", | |
" def get_input_output_seqs(self, ego_data):\n", | |
" # 6 input timesteps, (cyan-colored in the plot above), \n", | |
" # which are identical across all 6 examples.\n", | |
" ego_in = ego_data[:6] \n", | |
"\n", | |
" # 12 output (to be predicted by the model) timesteps,\n", | |
" # (pink in the plot above)\n", | |
" ego_out = ego_data[6:]\n", | |
" \n", | |
" return ego_in, ego_out\n", | |
"\n", | |
" def __getitem__(self, idx: int):\n", | |
" ego_data = self.ego_dataset[idx]\n", | |
" in_ego, out_ego = self.get_input_output_seqs(ego_data)\n", | |
" return in_ego, out_ego\n", | |
"\n", | |
" def __len__(self):\n", | |
" return len(self.ego_dataset)\n" | |
], | |
"execution_count": 4, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "7sg9ohVJ9i8N" | |
}, | |
"source": [ | |
"## Model Code" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "9On9HNKh9i8N" | |
}, | |
"source": [ | |
"import math\n", | |
"import numpy as np\n", | |
"import torch\n", | |
"import torch.nn as nn\n", | |
"import torch.nn.functional as F\n", | |
"\n", | |
"\n", | |
"def init(module, weight_init, bias_init, gain=1):\n", | |
" weight_init(module.weight.data, gain=gain)\n", | |
" bias_init(module.bias.data)\n", | |
" return module\n", | |
"\n", | |
"\n", | |
"class PositionalEncoding(nn.Module):\n", | |
" '''\n", | |
" Sine/cosine positional encoding (standard procedure for transformer sequential inputs)\n", | |
" '''\n", | |
"\n", | |
" def __init__(self, d_model, dropout=0.1, max_len=20):\n", | |
" super(PositionalEncoding, self).__init__()\n", | |
" self.dropout = nn.Dropout(p=dropout)\n", | |
" pe = torch.zeros(max_len, d_model)\n", | |
" position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)\n", | |
" div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))\n", | |
" pe[:, 0::2] = torch.sin(position * div_term)\n", | |
" pe[:, 1::2] = torch.cos(position * div_term)\n", | |
" pe = pe.unsqueeze(0).transpose(0, 1)\n", | |
" self.register_buffer('pe', pe)\n", | |
"\n", | |
" def forward(self, x):\n", | |
" '''\n", | |
" :param x: must be (T, B, H)\n", | |
" :return:\n", | |
" '''\n", | |
" x = x + self.pe[:x.size(0), :]\n", | |
" return self.dropout(x)\n", | |
"\n", | |
"\n", | |
"class AutoBotEgo(nn.Module):\n", | |
" '''\n", | |
" Sequential Set Transformer model for Small Synthetic Non-linear Particle Accelerator Dataset.\n", | |
" '''\n", | |
" def __init__(self, d_k=64, num_modes=3):\n", | |
" super(AutoBotEgo, self).__init__()\n", | |
"\n", | |
" init_ = lambda m: init(m, nn.init.orthogonal_, lambda x: nn.init.constant_(x, 0), np.sqrt(2))\n", | |
"\n", | |
" self.d_k = d_k\n", | |
" self.num_modes = num_modes\n", | |
" self.num_heads = 8\n", | |
"\n", | |
" self.output_model = OutputModelBVG(d_k=d_k)\n", | |
"\n", | |
" tx_encoder_layer = nn.TransformerEncoderLayer(d_model=d_k, nhead=self.num_heads)\n", | |
" self.tx_encoder = nn.TransformerEncoder(tx_encoder_layer, num_layers=1)\n", | |
"\n", | |
" self.emb_pos = init_(nn.Linear(2, d_k))\n", | |
" \n", | |
" tx_decoder_layer = nn.TransformerDecoderLayer(d_model=d_k, nhead=self.num_heads)\n", | |
" self.tx_decoder = nn.TransformerDecoder(tx_decoder_layer, num_layers=1)\n", | |
"\n", | |
" self.pos_encoder = PositionalEncoding(d_k, dropout=0.0)\n", | |
"\n", | |
" self.emb_intention = nn.Sequential(\n", | |
" init_(nn.Linear(num_modes, d_k))\n", | |
" )\n", | |
" self.emb_posint = nn.Sequential(\n", | |
" init_(nn.Linear(2*d_k, d_k)), nn.ReLU(),\n", | |
" init_(nn.Linear(d_k, d_k))\n", | |
" )\n", | |
"\n", | |
" self.mode_parameters = nn.Parameter(torch.Tensor(1, num_modes, d_k))\n", | |
" nn.init.xavier_uniform_(self.mode_parameters)\n", | |
" self.prob_decoder = nn.TransformerDecoderLayer(d_model=d_k, nhead=8)\n", | |
" self.prob_predictor = init_(nn.Linear(d_k, 1))\n", | |
"\n", | |
" self.train()\n", | |
"\n", | |
" def generate_decoder_mask(self, seq_len, device):\n", | |
" ''' For masking out the subsequent info. '''\n", | |
" subsequent_mask = (torch.triu(torch.ones((seq_len, seq_len), device=device), diagonal=1)).bool()\n", | |
" return subsequent_mask\n", | |
"\n", | |
" def forward(self, ego_input_positions, ego_output_positions):\n", | |
" B = ego_input_positions.size(0)\n", | |
" horizon = ego_output_positions.size(1)\n", | |
" \n", | |
" # Encode all observations\n", | |
" encoded_obs = self.emb_pos(ego_input_positions).transpose(0, 1)\n", | |
"\n", | |
" # Add positional encoding\n", | |
" encoded_obs = self.pos_encoder(encoded_obs)\n", | |
"\n", | |
" # TX on input seqs\n", | |
" in_memory = self.tx_encoder(encoded_obs)\n", | |
" mode_probs = self.prob_decoder(self.mode_parameters.repeat(B, 1, 1).transpose(0, 1), in_memory).transpose(0,1)\n", | |
" mode_probs = F.softmax(self.prob_predictor(mode_probs).squeeze(-1), dim=1)\n", | |
"\n", | |
" intentions = torch.eye(self.num_modes).to(device=ego_input_positions.device).unsqueeze(0).repeat(B, 1, 1)\n", | |
" enc_intentions = self.emb_intention(intentions).view(B*self.num_modes, self.d_k).unsqueeze(0)\n", | |
" in_memory = in_memory.unsqueeze(2).repeat(1, 1, self.num_modes, 1).view(-1, B * self.num_modes, self.d_k)\n", | |
"\n", | |
" pred_obs = [ego_input_positions[:, -1].unsqueeze(1).repeat(1, self.num_modes, 1).view(B * self.num_modes, -1)]\n", | |
" dec_start_emb = self.emb_pos(torch.stack(pred_obs, dim=0))\n", | |
" dec_input_emb = dec_start_emb\n", | |
" for ts in range(horizon): # autoregressive rollout\n", | |
" T = len(dec_input_emb)\n", | |
" curr_intentions = enc_intentions.repeat(T, 1, 1)\n", | |
" out_emb = torch.cat((curr_intentions, dec_input_emb), dim=-1)\n", | |
" out_emb = self.emb_posint(out_emb)\n", | |
"\n", | |
" out_emb = self.pos_encoder(out_emb)\n", | |
" time_masks = self.generate_decoder_mask(seq_len=T, device=ego_input_positions.device)\n", | |
" out_seq = self.tx_decoder(out_emb, in_memory, tgt_mask=time_masks)\n", | |
" dec_input_emb = torch.cat((dec_start_emb, out_seq), dim=0)\n", | |
"\n", | |
" out_dists = self.output_model(out_seq).view(horizon, B, self.num_modes, -1).permute(2, 0, 1, 3)\n", | |
" return out_dists, mode_probs\n", | |
"\n", | |
"\n", | |
"class OutputModelBVG(nn.Module):\n", | |
" def __init__(self, d_k=64):\n", | |
" super(OutputModelBVG, self).__init__()\n", | |
" self.d_k = d_k\n", | |
" init_ = lambda m: init(m, nn.init.orthogonal_, lambda x: nn.init.constant_(x, 0), np.sqrt(2))\n", | |
" self.observation_model = nn.Sequential(\n", | |
" init_(nn.Linear(d_k, d_k)), nn.ReLU(),\n", | |
" init_(nn.Linear(d_k, d_k)), nn.ReLU(),\n", | |
" init_(nn.Linear(d_k, 5))\n", | |
" )\n", | |
" self.min_stdev = 0.1 # for stability.\n", | |
"\n", | |
" def forward(self, agent_latent_state):\n", | |
" '''\n", | |
" :param agent_latent_state: the hidden-state of the ego-agent (B, T, H).\n", | |
" :return: A tensor with dimension (B, T, 5) where the 5-D vectors correspond \n", | |
" to the parameters of a bivariate Gaussian.\n", | |
" '''\n", | |
" pred_obs = self.observation_model(agent_latent_state)\n", | |
" x_mean = pred_obs[:, :, 0]\n", | |
" y_mean = pred_obs[:, :, 1]\n", | |
" x_sigma = F.softplus(pred_obs[:, :, 2]) + self.min_stdev\n", | |
" y_sigma = F.softplus(pred_obs[:, :, 3]) + self.min_stdev\n", | |
" rho = torch.tanh(pred_obs[:, :, 4]) * 0.9 # for stability\n", | |
" return torch.stack([x_mean, y_mean, x_sigma, y_sigma, rho], dim=2)\n", | |
"\n" | |
], | |
"execution_count": 5, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "hvQG71Au9i8Q" | |
}, | |
"source": [ | |
"## Utility Functions\n", | |
"We define some utility functions for plotting circles for the output distributions (mean and variance at each timestep) and for calculating the multimodal loss.\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "Iwz5gzTE9i8S" | |
}, | |
"source": [ | |
"import numpy as np\n", | |
"import torch\n", | |
"from scipy import special\n", | |
"import torch.distributions as D\n", | |
"from torch.distributions import MultivariateNormal\n", | |
"from matplotlib.patches import Ellipse\n", | |
"\n", | |
"\n", | |
"def _plot_gaussian(dist, ax, color, zorder=0):\n", | |
" \"\"\"Plots the mean and 2-std ellipse of a given Gaussian\"\"\"\n", | |
" cov_val = dist[4] * dist[2] * dist[3]\n", | |
" mean = [dist[0], dist[1]]\n", | |
" covariance = np.array([[dist[2] ** 2, cov_val], [cov_val, dist[3] ** 2]])\n", | |
"\n", | |
" if covariance.ndim == 1:\n", | |
" covariance = np.diag(covariance)\n", | |
"\n", | |
" radius = np.sqrt(5.991) # for 95% confidence interval.\n", | |
" eigvals, eigvecs = np.linalg.eig(covariance)\n", | |
" axis = np.sqrt(eigvals) * radius\n", | |
" slope = eigvecs[1][0] / eigvecs[1][1]\n", | |
" angle = 180.0 * np.arctan(slope) / np.pi\n", | |
"\n", | |
" e = Ellipse(mean, 2 * axis[0], 2 * axis[1], angle=angle, fill=False, color=color, linewidth=1, zorder=zorder, alpha=1.0)\n", | |
" ax.add_artist(e)\n", | |
" e.set_clip_box(ax.bbox)\n", | |
" return ax\n", | |
"\n", | |
"\n", | |
"def get_BVG_distributions(pred):\n", | |
" '''\n", | |
" Transform the prediction tensor of dim (B, T, 5) to torch Multivariate Gaussians distributions.\n", | |
" '''\n", | |
" B = pred.size(0)\n", | |
" T = pred.size(1)\n", | |
" mu_x = pred[:, :, 0].unsqueeze(2)\n", | |
" mu_y = pred[:, :, 1].unsqueeze(2)\n", | |
" sigma_x = pred[:, :, 2]\n", | |
" sigma_y = pred[:, :, 3]\n", | |
" rho = pred[:, :, 4]\n", | |
"\n", | |
" cov = torch.zeros((B, T, 2, 2)).to(pred.device)\n", | |
" cov[:, :, 0, 0] = sigma_x ** 2\n", | |
" cov[:, :, 1, 1] = sigma_y ** 2\n", | |
" cov_val = rho * sigma_x * sigma_y\n", | |
" cov[:, :, 0, 1] = cov_val\n", | |
" cov[:, :, 1, 0] = cov_val\n", | |
"\n", | |
" biv_gauss_dist = MultivariateNormal(loc=torch.cat((mu_x, mu_y), dim=-1), covariance_matrix=cov)\n", | |
" return biv_gauss_dist\n", | |
"\n", | |
"\n", | |
"def nll_pytorch_dist(pred, data):\n", | |
" '''\n", | |
" Args:\n", | |
" pred: [B, T, 5]\n", | |
" data: [B, T, 2]\n", | |
" This function computes the negative log-likelihood of the data given the predicted distributions.\n", | |
" Returns the nll vector for all elements in the batch.\n", | |
" '''\n", | |
" biv_gauss_dist = get_BVG_distributions(pred)\n", | |
" loss = -biv_gauss_dist.log_prob(data).sum(1) # sum over all timesteps\n", | |
" return loss # [B]\n", | |
"\n", | |
"\n", | |
"def nll_loss_multimodes(pred, data, modes_pred, entropy_weight=1.0, val_nll=False, kl_weight=1.0):\n", | |
" \"\"\"NLL loss multimodes for training. MFP Loss function\n", | |
" Args:\n", | |
" pred: [K, T, B, 5]\n", | |
" data: [B, T, 2]\n", | |
" modes_pred: [B, K], prior prob over modes\n", | |
" \"\"\"\n", | |
" K = len(pred)\n", | |
" T, B, dim = pred[0].shape\n", | |
"\n", | |
" # Here, we compute the log-likelihood of the data given the predicted distributions, p(y|z,x). \n", | |
" # This part is used in combination with the predicted prior distribution p(z|x) to compute the posterior p(z|y,x).\n", | |
" log_lik = np.zeros((B, K))\n", | |
" with torch.no_grad():\n", | |
" for kk in range(K):\n", | |
" nll = nll_pytorch_dist(pred[kk].transpose(0, 1), data)\n", | |
" log_lik[:, kk] = -nll.cpu().numpy()\n", | |
"\n", | |
" # The following is an application of Bayes Rule.\n", | |
" priors = modes_pred.detach().cpu().numpy()\n", | |
" log_post_unnorm = log_lik + np.log(priors)\n", | |
" log_post = log_post_unnorm - special.logsumexp(log_post_unnorm, axis=1).reshape((B, 1))\n", | |
" post_prob = np.exp(log_post)\n", | |
" post_prob = torch.tensor(post_prob).float().to(data.device)\n", | |
"\n", | |
" # Using the computed posterior, we now can compute the data negative loglikelihood exactly.\n", | |
" loss = 0.0\n", | |
" for kk in range(K):\n", | |
" nll_k = nll_pytorch_dist(pred[kk].transpose(0, 1), data) * post_prob[:, kk]\n", | |
" loss += nll_k.sum() / float(B)\n", | |
"\n", | |
" # Compute the KL divergence between p(z|x) and p(z|x,y).\n", | |
" kl_loss = torch.nn.KLDivLoss(reduction='batchmean')\n", | |
" loss += kl_weight*kl_loss(torch.log(modes_pred), post_prob)\n", | |
"\n", | |
" # The entropy regularization term.\n", | |
" if not val_nll:\n", | |
" entropy_vals = []\n", | |
" for kk in range(K):\n", | |
" entropy_vals.append(get_BVG_distributions(pred[kk]).entropy())\n", | |
" entropy_loss = torch.mean(torch.stack(entropy_vals).permute(2, 0, 1).sum(2).max(1)[0])\n", | |
" loss += entropy_weight*entropy_loss\n", | |
"\n", | |
" return loss\n" | |
], | |
"execution_count": 6, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "aXJMdKqU9i8U" | |
}, | |
"source": [ | |
"## Training Loop\n", | |
"The training loop takes about 10 minutes on a single-GPU machine." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"colab": { | |
"base_uri": "https://localhost:8080/", | |
"height": 0 | |
}, | |
"id": "6mxWEJKd9i8V", | |
"outputId": "b36fbbc0-6d61-4615-f40f-ea8ba5f07e14" | |
}, | |
"source": [ | |
"import torch\n", | |
"from torch import optim\n", | |
"import torch.distributions as D\n", | |
"print(torch.__version__)\n", | |
"\n", | |
"\n", | |
"num_modes = 10\n", | |
"d_k = 64\n", | |
"learning_rate = 0.0001\n", | |
"entropy_weight = 5.0 # turn this up/down to see the effect on the variance.\n", | |
"seed = 0\n", | |
"np.random.seed(seed)\n", | |
"\n", | |
"if torch.cuda.is_available():\n", | |
" device = torch.device(\"cuda\")\n", | |
" torch.cuda.manual_seed(seed)\n", | |
"else:\n", | |
" device = torch.device(\"cpu\")\n", | |
"\n", | |
"# Initialize model\n", | |
"autobot_model = AutoBotEgo(d_k=d_k, num_modes=num_modes).to(device)\n", | |
"optimiser = optim.Adam(autobot_model.parameters(), lr=learning_rate, eps=1e-4)\n", | |
"\n", | |
"# Initialize dataloader\n", | |
"train_nuscenes = NPYFakeDataset()\n", | |
"train_loader = torch.utils.data.DataLoader(train_nuscenes, batch_size=6, shuffle=True, num_workers=3, drop_last=True, pin_memory=True)\n", | |
"\n", | |
"total_steps = 0\n", | |
"losses = []\n", | |
"for train_iter in range(0, 3000):\n", | |
" for i, data in enumerate(train_loader):\n", | |
" ego_in, ego_out = data\n", | |
" ego_in = ego_in.float().to(device)\n", | |
" ego_out = ego_out.float().to(device)\n", | |
"\n", | |
" # encode observations\n", | |
" pred_obs, modes_pred = autobot_model(ego_in, ego_out)\n", | |
"\n", | |
" # Compute the loss.\n", | |
" loss = nll_loss_multimodes(pred_obs, ego_out[:, :, :2], modes_pred, entropy_weight=entropy_weight)\n", | |
"\n", | |
" # A measure of the entropy of the output distributions.\n", | |
" sigmas = pred_obs[:, :, :, 2:4]\n", | |
" sigma_magnitude = torch.mean(torch.norm(sigmas, dim=-1))\n", | |
"\n", | |
" optimiser.zero_grad()\n", | |
" loss.backward()\n", | |
" torch.nn.utils.clip_grad_norm_(autobot_model.parameters(), 0.5)\n", | |
" optimiser.step()\n", | |
"\n", | |
" # Store (0) observation loss (1) reward loss (2) KL loss\n", | |
" losses.append(loss.item())\n", | |
"\n", | |
" if train_iter % 50 == 0:\n", | |
" print(train_iter, \"Obs_Loss\", losses[-1], \"Prior Entropy\", torch.mean(D.Categorical(modes_pred).entropy()).item(), \"Sigma Magnitude\", sigma_magnitude.item())\n" | |
], | |
"execution_count": 7, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"1.6.0\n", | |
"0 Obs_Loss 2369.8955078125 Prior Entropy 1.4875106811523438 Sigma Magnitude 0.8969748616218567\n", | |
"50 Obs_Loss 210.09817504882812 Prior Entropy 2.1081435680389404 Sigma Magnitude 1.4822051525115967\n", | |
"100 Obs_Loss 157.78390502929688 Prior Entropy 2.1547019481658936 Sigma Magnitude 1.066394567489624\n", | |
"150 Obs_Loss 130.46844482421875 Prior Entropy 2.1374456882476807 Sigma Magnitude 0.9238507747650146\n", | |
"200 Obs_Loss 93.3446044921875 Prior Entropy 2.1341917514801025 Sigma Magnitude 0.7530378699302673\n", | |
"250 Obs_Loss 72.35761260986328 Prior Entropy 2.1071135997772217 Sigma Magnitude 0.6062721014022827\n", | |
"300 Obs_Loss 80.39238739013672 Prior Entropy 2.0682501792907715 Sigma Magnitude 0.5384072065353394\n", | |
"350 Obs_Loss 51.74512481689453 Prior Entropy 2.0305049419403076 Sigma Magnitude 0.47288694977760315\n", | |
"400 Obs_Loss 32.685508728027344 Prior Entropy 2.011359214782715 Sigma Magnitude 0.3868240714073181\n", | |
"450 Obs_Loss 11.433774948120117 Prior Entropy 2.0145623683929443 Sigma Magnitude 0.3831108808517456\n", | |
"500 Obs_Loss 9.873607635498047 Prior Entropy 1.97100031375885 Sigma Magnitude 0.33936724066734314\n", | |
"550 Obs_Loss 8.771446228027344 Prior Entropy 2.0121748447418213 Sigma Magnitude 0.31136900186538696\n", | |
"600 Obs_Loss -19.142765045166016 Prior Entropy 1.9727678298950195 Sigma Magnitude 0.28895100951194763\n", | |
"650 Obs_Loss -13.80767822265625 Prior Entropy 1.9760602712631226 Sigma Magnitude 0.2773299515247345\n", | |
"700 Obs_Loss -34.384971618652344 Prior Entropy 1.9884058237075806 Sigma Magnitude 0.2564449906349182\n", | |
"750 Obs_Loss -39.28346252441406 Prior Entropy 1.9636551141738892 Sigma Magnitude 0.24932558834552765\n", | |
"800 Obs_Loss -41.30181121826172 Prior Entropy 1.987878441810608 Sigma Magnitude 0.23153577744960785\n", | |
"850 Obs_Loss -48.81230926513672 Prior Entropy 1.9718965291976929 Sigma Magnitude 0.22708381712436676\n", | |
"900 Obs_Loss -61.86613845825195 Prior Entropy 1.983053207397461 Sigma Magnitude 0.2147447019815445\n", | |
"950 Obs_Loss -59.352760314941406 Prior Entropy 1.9651894569396973 Sigma Magnitude 0.2024281769990921\n", | |
"1000 Obs_Loss -57.75294876098633 Prior Entropy 1.949167251586914 Sigma Magnitude 0.20529471337795258\n", | |
"1050 Obs_Loss -62.86778259277344 Prior Entropy 1.961978793144226 Sigma Magnitude 0.20152588188648224\n", | |
"1100 Obs_Loss -73.48599243164062 Prior Entropy 1.9676356315612793 Sigma Magnitude 0.19365176558494568\n", | |
"1150 Obs_Loss -69.45549774169922 Prior Entropy 1.951179027557373 Sigma Magnitude 0.18875083327293396\n", | |
"1200 Obs_Loss -65.29815673828125 Prior Entropy 1.9962520599365234 Sigma Magnitude 0.18455862998962402\n", | |
"1250 Obs_Loss -74.91232299804688 Prior Entropy 1.9666643142700195 Sigma Magnitude 0.1774861216545105\n", | |
"1300 Obs_Loss -82.59066772460938 Prior Entropy 1.9747203588485718 Sigma Magnitude 0.17883709073066711\n", | |
"1350 Obs_Loss -83.88932037353516 Prior Entropy 1.95893132686615 Sigma Magnitude 0.17508520185947418\n", | |
"1400 Obs_Loss -94.74249267578125 Prior Entropy 1.9619406461715698 Sigma Magnitude 0.17152521014213562\n", | |
"1450 Obs_Loss -83.56954956054688 Prior Entropy 1.9574122428894043 Sigma Magnitude 0.1688963770866394\n", | |
"1500 Obs_Loss -95.97639465332031 Prior Entropy 1.934739589691162 Sigma Magnitude 0.16633732616901398\n", | |
"1550 Obs_Loss -84.9005126953125 Prior Entropy 1.952906608581543 Sigma Magnitude 0.167943075299263\n", | |
"1600 Obs_Loss -93.68824768066406 Prior Entropy 1.9422146081924438 Sigma Magnitude 0.16340167820453644\n", | |
"1650 Obs_Loss -93.48336029052734 Prior Entropy 1.9700676202774048 Sigma Magnitude 0.1643861085176468\n", | |
"1700 Obs_Loss -93.68887329101562 Prior Entropy 1.947595238685608 Sigma Magnitude 0.16277040541172028\n", | |
"1750 Obs_Loss -106.33206176757812 Prior Entropy 1.9439034461975098 Sigma Magnitude 0.1631186455488205\n", | |
"1800 Obs_Loss -114.87834167480469 Prior Entropy 1.9303315877914429 Sigma Magnitude 0.16260327398777008\n", | |
"1850 Obs_Loss -111.52286529541016 Prior Entropy 1.9322032928466797 Sigma Magnitude 0.16073709726333618\n", | |
"1900 Obs_Loss -99.67212677001953 Prior Entropy 1.954646110534668 Sigma Magnitude 0.1583443582057953\n", | |
"1950 Obs_Loss -112.62327575683594 Prior Entropy 1.9359534978866577 Sigma Magnitude 0.15894180536270142\n", | |
"2000 Obs_Loss -112.17411041259766 Prior Entropy 1.9476476907730103 Sigma Magnitude 0.15647853910923004\n", | |
"2050 Obs_Loss -117.08065032958984 Prior Entropy 1.9370139837265015 Sigma Magnitude 0.15652796626091003\n", | |
"2100 Obs_Loss -116.01622772216797 Prior Entropy 1.943585753440857 Sigma Magnitude 0.15343520045280457\n", | |
"2150 Obs_Loss -104.03912353515625 Prior Entropy 1.920638918876648 Sigma Magnitude 0.15452148020267487\n", | |
"2200 Obs_Loss -118.58104705810547 Prior Entropy 1.945374846458435 Sigma Magnitude 0.15166401863098145\n", | |
"2250 Obs_Loss -127.10646057128906 Prior Entropy 1.934796929359436 Sigma Magnitude 0.15318652987480164\n", | |
"2300 Obs_Loss -129.23353576660156 Prior Entropy 1.9234099388122559 Sigma Magnitude 0.15286634862422943\n", | |
"2350 Obs_Loss -118.142333984375 Prior Entropy 1.9349241256713867 Sigma Magnitude 0.15083317458629608\n", | |
"2400 Obs_Loss -122.60208892822266 Prior Entropy 1.9366976022720337 Sigma Magnitude 0.1516687273979187\n", | |
"2450 Obs_Loss -132.3092498779297 Prior Entropy 1.9179943799972534 Sigma Magnitude 0.1510191559791565\n", | |
"2500 Obs_Loss -119.2080078125 Prior Entropy 1.937208652496338 Sigma Magnitude 0.15157970786094666\n", | |
"2550 Obs_Loss -126.15424346923828 Prior Entropy 1.913926601409912 Sigma Magnitude 0.1499032974243164\n", | |
"2600 Obs_Loss -127.50374603271484 Prior Entropy 1.9175949096679688 Sigma Magnitude 0.14963364601135254\n", | |
"2650 Obs_Loss -127.7978744506836 Prior Entropy 1.911878228187561 Sigma Magnitude 0.1486077606678009\n", | |
"2700 Obs_Loss -130.70504760742188 Prior Entropy 1.90842866897583 Sigma Magnitude 0.15001989901065826\n", | |
"2750 Obs_Loss -134.3357696533203 Prior Entropy 1.9386142492294312 Sigma Magnitude 0.1491604447364807\n", | |
"2800 Obs_Loss -137.72341918945312 Prior Entropy 1.9272490739822388 Sigma Magnitude 0.14933128654956818\n", | |
"2850 Obs_Loss -140.33743286132812 Prior Entropy 1.933469295501709 Sigma Magnitude 0.14862534403800964\n", | |
"2900 Obs_Loss -133.574951171875 Prior Entropy 1.9343080520629883 Sigma Magnitude 0.14803217351436615\n", | |
"2950 Obs_Loss -135.62042236328125 Prior Entropy 1.9297490119934082 Sigma Magnitude 0.14848901331424713\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "Tw0HaOtk9i8W" | |
}, | |
"source": [ | |
"## Testing Mode Learning on Toy Dataset\n", | |
"\n", | |
"This consistutes the results shown in Figure 2 (bottom row) of the paper. To get the results corresponding to the middle row, reduce the `entropy_weight` in the training block above and repeat training.\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"colab": { | |
"base_uri": "https://localhost:8080/", | |
"height": 596 | |
}, | |
"id": "9o6cRo6y9i8W", | |
"outputId": "d1b20cec-7307-4486-ac96-36f6f0464730" | |
}, | |
"source": [ | |
"autobot_model.eval()\n", | |
"with torch.no_grad():\n", | |
" for i, data in enumerate(train_loader):\n", | |
" ego_in, ego_out = data\n", | |
" ego_in = ego_in.float().to(device)\n", | |
" ego_out = ego_out.float().to(device)\n", | |
"\n", | |
" pred_obs, mode_preds = autobot_model(ego_in, ego_out)\n", | |
" pred_positions = pred_obs[:, :, 0, :2].squeeze().cpu().numpy()\n", | |
" mode_probs_np = mode_preds[0].squeeze().cpu().numpy()\n", | |
" pred_distributions = pred_obs[:, :, 0].squeeze().cpu().numpy()\n", | |
"\n", | |
" top_6_modes = mode_probs_np.argsort()[-6:][::-1]\n", | |
" fig, ax = plt.subplots(nrows=2, ncols=5, figsize=(15, 10))\n", | |
" row = 0\n", | |
" for k_idx in range(num_modes):\n", | |
" col = k_idx % 5\n", | |
" if k_idx > 0 and k_idx % 5 == 0:\n", | |
" row += 1\n", | |
" k = k_idx\n", | |
" ax[row, col].scatter(pred_positions[k, :, 0], pred_positions[k, :, 1], s=10, color='k')\n", | |
"\n", | |
" for t in range(12):\n", | |
" ax[row, col] = _plot_gaussian(pred_distributions[k, t], ax[row, col], color='#966BFF')\n", | |
" ax[row, col].axis(xmin=-15, xmax=15, ymin=-15, ymax=20)\n", | |
"\n", | |
" plt.show()\n" | |
], | |
"execution_count": 8, | |
"outputs": [ | |
{ | |
"output_type": "display_data", | |
"data": { | |
"image/png": "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\n", | |
"text/plain": [ | |
"<Figure size 1080x720 with 10 Axes>" | |
] | |
}, | |
"metadata": { | |
"tags": [], | |
"needs_background": "light" | |
} | |
} | |
] | |
} | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment