Skip to content

Instantly share code, notes, and snippets.

@fgolemo
Created February 19, 2021 05:48
Show Gist options
  • Save fgolemo/b762ddc59c83ca19cd15f3767e2c3780 to your computer and use it in GitHub Desktop.
Save fgolemo/b762ddc59c83ca19cd15f3767e2c3780 to your computer and use it in GitHub Desktop.
autobot_toy.ipynb
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": [],
"include_colab_link": true
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/fgolemo/b762ddc59c83ca19cd15f3767e2c3780/autobot_toy.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "EB9MwlqV98w-"
},
"source": [
"# AutoBot Toy Dataset Modelling"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"id": "6lMLqmTN9i8G"
},
"source": [
"## Generate Toy 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": "13cebbfc-0bd7-434f-e929-22b7948eec08"
},
"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": null,
"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": {
"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": null,
"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 AutoBot(nn.Module):\n",
" '''\n",
" Nested Set Transformer model specialized for car environment with opponents.\n",
" '''\n",
" def __init__(self, hidden_size=64, num_modes=3):\n",
" super(AutoBot, 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.hidden_size = hidden_size\n",
" self.num_modes = num_modes\n",
" self.num_heads = 8\n",
"\n",
" self.output_model = OutputModelBVG(hidden_size=hidden_size)\n",
"\n",
" tx_encoder_layer = nn.TransformerEncoderLayer(d_model=hidden_size, nhead=self.num_heads)\n",
" self.tx_encoder = nn.TransformerEncoder(tx_encoder_layer, num_layers=2)\n",
"\n",
" self.emb_pos = init_(nn.Linear(2, hidden_size))\n",
" \n",
" tx_decoder_layer = nn.TransformerDecoderLayer(d_model=hidden_size, nhead=self.num_heads)\n",
" self.tx_decoder = nn.TransformerDecoder(tx_decoder_layer, num_layers=2)\n",
"\n",
" self.pos_encoder = PositionalEncoding(hidden_size, dropout=0.0)\n",
"\n",
" self.emb_intention = nn.Sequential(\n",
" init_(nn.Linear(num_modes, hidden_size))\n",
" )\n",
" self.emb_posint = nn.Sequential(\n",
" init_(nn.Linear(2*hidden_size, hidden_size)), nn.ReLU(),\n",
" init_(nn.Linear(hidden_size, hidden_size))\n",
" )\n",
"\n",
" self.mode_parameters = nn.Parameter(torch.Tensor(1, num_modes, hidden_size))\n",
" nn.init.xavier_uniform_(self.mode_parameters)\n",
" self.prob_decoder = nn.TransformerDecoderLayer(d_model=hidden_size, nhead=8)\n",
" self.prob_predictor = init_(nn.Linear(hidden_size, 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.hidden_size).unsqueeze(0)\n",
" in_memory = in_memory.unsqueeze(2).repeat(1, 1, self.num_modes, 1).view(-1, B * self.num_modes, self.hidden_size)\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, hidden_size=64):\n",
" super(OutputModelBVG, self).__init__()\n",
" self.hidden_size = hidden_size\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(hidden_size, hidden_size)), nn.ReLU(),\n",
" init_(nn.Linear(hidden_size, hidden_size)), nn.ReLU(),\n",
" init_(nn.Linear(hidden_size, 5))\n",
" )\n",
" self.min_stdev = 0.1\n",
"\n",
" def forward(self, agent_latent_state):\n",
" '''\n",
" :param agent_latent_state: the social state of the ego-agent (B, H).\n",
" :return: reward for current latent state\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": null,
"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": null,
"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/"
},
"id": "6mxWEJKd9i8V",
"outputId": "700362c2-e517-4d47-e169-6c7700cf8aed"
},
"source": [
"import torch\n",
"from torch import optim\n",
"import torch.distributions as D\n",
"\n",
"\n",
"num_modes = 10\n",
"hidden_size = 64\n",
"learning_rate = 0.000075\n",
"entropy_weight = 10.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 = AutoBot(hidden_size=hidden_size, 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": null,
"outputs": [
{
"output_type": "stream",
"text": [
"0 Obs_Loss 11723.958984375 Prior Entropy 1.857177734375 Sigma Magnitude 1.3646622896194458\n",
"50 Obs_Loss 306.9662170410156 Prior Entropy 2.06771183013916 Sigma Magnitude 0.9929002523422241\n",
"100 Obs_Loss 234.70278930664062 Prior Entropy 2.0464229583740234 Sigma Magnitude 0.8464294075965881\n",
"150 Obs_Loss 219.2841339111328 Prior Entropy 1.9615720510482788 Sigma Magnitude 0.7770584225654602\n",
"200 Obs_Loss 198.8031463623047 Prior Entropy 2.008603811264038 Sigma Magnitude 0.673245906829834\n",
"250 Obs_Loss 138.7711639404297 Prior Entropy 2.072270393371582 Sigma Magnitude 0.6537865400314331\n",
"300 Obs_Loss 123.04424285888672 Prior Entropy 2.0243639945983887 Sigma Magnitude 0.6342011094093323\n",
"350 Obs_Loss 96.79385375976562 Prior Entropy 2.082216501235962 Sigma Magnitude 0.6264477968215942\n",
"400 Obs_Loss 81.11720275878906 Prior Entropy 2.120720863342285 Sigma Magnitude 0.619354784488678\n",
"450 Obs_Loss 67.96978759765625 Prior Entropy 2.129866123199463 Sigma Magnitude 0.5915012359619141\n",
"500 Obs_Loss 98.8485336303711 Prior Entropy 2.142521858215332 Sigma Magnitude 0.5233737826347351\n",
"550 Obs_Loss 142.97808837890625 Prior Entropy 2.0970046520233154 Sigma Magnitude 0.6683558225631714\n",
"600 Obs_Loss 68.43778228759766 Prior Entropy 2.091160297393799 Sigma Magnitude 0.6223364472389221\n",
"650 Obs_Loss 29.45511245727539 Prior Entropy 2.0658717155456543 Sigma Magnitude 0.5888274908065796\n",
"700 Obs_Loss 17.957138061523438 Prior Entropy 2.093405246734619 Sigma Magnitude 0.6362965106964111\n",
"750 Obs_Loss 31.798233032226562 Prior Entropy 2.0856008529663086 Sigma Magnitude 0.5461905002593994\n",
"800 Obs_Loss 10.051948547363281 Prior Entropy 2.090461254119873 Sigma Magnitude 0.6038259267807007\n",
"850 Obs_Loss 59.378623962402344 Prior Entropy 2.0865261554718018 Sigma Magnitude 0.5151104927062988\n",
"900 Obs_Loss -6.811798095703125 Prior Entropy 2.1136908531188965 Sigma Magnitude 0.5225884914398193\n",
"950 Obs_Loss -10.759872436523438 Prior Entropy 2.118497371673584 Sigma Magnitude 0.6135889291763306\n",
"1000 Obs_Loss -33.444671630859375 Prior Entropy 2.0734384059906006 Sigma Magnitude 0.5419284105300903\n",
"1050 Obs_Loss -35.84764862060547 Prior Entropy 2.1095805168151855 Sigma Magnitude 0.5613827109336853\n",
"1100 Obs_Loss -29.234100341796875 Prior Entropy 2.1014890670776367 Sigma Magnitude 0.5027151703834534\n",
"1150 Obs_Loss -34.968345642089844 Prior Entropy 2.1073012351989746 Sigma Magnitude 0.5491200685501099\n",
"1200 Obs_Loss -39.077980041503906 Prior Entropy 2.100574016571045 Sigma Magnitude 0.4987635016441345\n",
"1250 Obs_Loss -48.33007049560547 Prior Entropy 2.1208224296569824 Sigma Magnitude 0.510762631893158\n",
"1300 Obs_Loss -58.208953857421875 Prior Entropy 2.0919651985168457 Sigma Magnitude 0.4930139482021332\n",
"1350 Obs_Loss -87.03924560546875 Prior Entropy 2.118295669555664 Sigma Magnitude 0.4436866343021393\n",
"1400 Obs_Loss -47.048927307128906 Prior Entropy 2.097531795501709 Sigma Magnitude 0.44906753301620483\n",
"1450 Obs_Loss -72.15503692626953 Prior Entropy 2.092167854309082 Sigma Magnitude 0.4120866358280182\n",
"1500 Obs_Loss -80.47696685791016 Prior Entropy 2.103466749191284 Sigma Magnitude 0.4134567379951477\n",
"1550 Obs_Loss -94.25555419921875 Prior Entropy 2.080009698867798 Sigma Magnitude 0.36127781867980957\n",
"1600 Obs_Loss -88.59275817871094 Prior Entropy 2.0875422954559326 Sigma Magnitude 0.3591216206550598\n",
"1650 Obs_Loss -124.26649475097656 Prior Entropy 2.093277931213379 Sigma Magnitude 0.34293627738952637\n",
"1700 Obs_Loss -108.07246398925781 Prior Entropy 2.0913078784942627 Sigma Magnitude 0.29710695147514343\n",
"1750 Obs_Loss -133.356689453125 Prior Entropy 2.0764667987823486 Sigma Magnitude 0.2864120900630951\n",
"1800 Obs_Loss -150.91000366210938 Prior Entropy 2.1066884994506836 Sigma Magnitude 0.27043548226356506\n",
"1850 Obs_Loss -144.8616943359375 Prior Entropy 2.0808260440826416 Sigma Magnitude 0.2537614107131958\n",
"1900 Obs_Loss -142.93882751464844 Prior Entropy 2.0847625732421875 Sigma Magnitude 0.2622417211532593\n",
"1950 Obs_Loss -174.2861328125 Prior Entropy 2.078876495361328 Sigma Magnitude 0.23730503022670746\n",
"2000 Obs_Loss -148.6193389892578 Prior Entropy 2.062580108642578 Sigma Magnitude 0.24768507480621338\n",
"2050 Obs_Loss -131.22848510742188 Prior Entropy 2.0892975330352783 Sigma Magnitude 0.21530592441558838\n",
"2100 Obs_Loss -183.94528198242188 Prior Entropy 2.0834622383117676 Sigma Magnitude 0.2223784178495407\n",
"2150 Obs_Loss -139.89260864257812 Prior Entropy 2.076235055923462 Sigma Magnitude 0.19737689197063446\n",
"2200 Obs_Loss -177.97433471679688 Prior Entropy 2.0840203762054443 Sigma Magnitude 0.20732863247394562\n",
"2250 Obs_Loss -194.98463439941406 Prior Entropy 2.1062493324279785 Sigma Magnitude 0.20598143339157104\n",
"2300 Obs_Loss -177.98443603515625 Prior Entropy 2.083662748336792 Sigma Magnitude 0.19716553390026093\n",
"2350 Obs_Loss -183.91876220703125 Prior Entropy 2.076279640197754 Sigma Magnitude 0.19121479988098145\n",
"2400 Obs_Loss -201.8154296875 Prior Entropy 2.092423677444458 Sigma Magnitude 0.19742748141288757\n",
"2450 Obs_Loss -189.528076171875 Prior Entropy 2.092670440673828 Sigma Magnitude 0.1925940066576004\n",
"2500 Obs_Loss -194.66998291015625 Prior Entropy 2.0779054164886475 Sigma Magnitude 0.17988410592079163\n",
"2550 Obs_Loss -200.75863647460938 Prior Entropy 2.0983500480651855 Sigma Magnitude 0.18578983843326569\n",
"2600 Obs_Loss -199.38670349121094 Prior Entropy 2.093242645263672 Sigma Magnitude 0.18134918808937073\n",
"2650 Obs_Loss -192.5203857421875 Prior Entropy 2.089531421661377 Sigma Magnitude 0.18082661926746368\n",
"2700 Obs_Loss -187.53192138671875 Prior Entropy 2.0818114280700684 Sigma Magnitude 0.17122453451156616\n",
"2750 Obs_Loss -207.5955352783203 Prior Entropy 2.062140941619873 Sigma Magnitude 0.17080239951610565\n",
"2800 Obs_Loss -189.11380004882812 Prior Entropy 2.0947937965393066 Sigma Magnitude 0.17124949395656586\n",
"2850 Obs_Loss -198.44921875 Prior Entropy 2.0777957439422607 Sigma Magnitude 0.16536590456962585\n",
"2900 Obs_Loss -217.40306091308594 Prior Entropy 2.0797410011291504 Sigma Magnitude 0.16628232598304749\n",
"2950 Obs_Loss -228.67001342773438 Prior Entropy 2.0792946815490723 Sigma Magnitude 0.1648883819580078\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 5 (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": "021a3675-9863-4103-b5a8-e41e0534b706"
},
"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": null,
"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