Skip to content

Instantly share code, notes, and snippets.

@fgolemo
Created March 1, 2022 22:25
Show Gist options
  • Save fgolemo/6144f7b7ddd970d19f6d9804573c5d48 to your computer and use it in GitHub Desktop.
Save fgolemo/6144f7b7ddd970d19f6d9804573c5d48 to your computer and use it in GitHub Desktop.
Toy example for Autobots: Latent Variable Sequential Set Transformers
Display the source blob
Display the rendered blob
Raw
{
"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