|
|
| import os |
| import time |
| import torch |
| import random |
| import numpy as np |
| import torch.nn as nn |
|
|
| from utils.config import Config |
| from utils.utils import print_log |
|
|
|
|
| from torch.utils.data import DataLoader |
| from data.dataloader_nba import NBADataset, seq_collate |
|
|
|
|
| from models.model_led_initializer import LEDInitializer as InitializationModel |
| from models.model_diffusion import TransformerDenoisingModel as CoreDenoisingModel |
|
|
| import pdb |
| NUM_Tau = 5 |
|
|
| class Trainer: |
| def __init__(self, config): |
| |
| if torch.cuda.is_available(): torch.cuda.set_device(config.gpu) |
| self.device = torch.device('cuda') if config.cuda else torch.device('cpu') |
| self.cfg = Config(config.cfg, config.info) |
| |
| |
| train_dset = NBADataset( |
| obs_len=self.cfg.past_frames, |
| pred_len=self.cfg.future_frames, |
| training=True) |
|
|
| self.train_loader = DataLoader( |
| train_dset, |
| batch_size=self.cfg.train_batch_size, |
| shuffle=True, |
| num_workers=4, |
| collate_fn=seq_collate, |
| pin_memory=True) |
| |
| test_dset = NBADataset( |
| obs_len=self.cfg.past_frames, |
| pred_len=self.cfg.future_frames, |
| training=False) |
|
|
| self.test_loader = DataLoader( |
| test_dset, |
| batch_size=self.cfg.test_batch_size, |
| shuffle=False, |
| num_workers=4, |
| collate_fn=seq_collate, |
| pin_memory=True) |
| |
| |
| self.traj_mean = torch.FloatTensor(self.cfg.traj_mean).cuda().unsqueeze(0).unsqueeze(0).unsqueeze(0) |
| self.traj_scale = self.cfg.traj_scale |
|
|
| |
| self.n_steps = self.cfg.diffusion.steps |
|
|
| |
| self.betas = self.make_beta_schedule( |
| schedule=self.cfg.diffusion.beta_schedule, n_timesteps=self.n_steps, |
| start=self.cfg.diffusion.beta_start, end=self.cfg.diffusion.beta_end).cuda() |
| |
| self.alphas = 1 - self.betas |
| self.alphas_prod = torch.cumprod(self.alphas, 0) |
| self.alphas_bar_sqrt = torch.sqrt(self.alphas_prod) |
| self.one_minus_alphas_bar_sqrt = torch.sqrt(1 - self.alphas_prod) |
|
|
|
|
| |
| self.model = CoreDenoisingModel().cuda() |
| |
| model_cp = torch.load(self.cfg.pretrained_core_denoising_model, map_location='cpu') |
| self.model.load_state_dict(model_cp['model_dict']) |
|
|
| self.model_initializer = InitializationModel(t_h=10, d_h=6, t_f=20, d_f=2, k_pred=20).cuda() |
|
|
| self.opt = torch.optim.AdamW(self.model_initializer.parameters(), lr=config.learning_rate) |
| self.scheduler_model = torch.optim.lr_scheduler.StepLR(self.opt, step_size=self.cfg.decay_step, gamma=self.cfg.decay_gamma) |
|
|
| |
| self.log = open(os.path.join(self.cfg.log_dir, 'log.txt'), 'a+') |
| self.print_model_param(self.model, name='Core Denoising Model') |
| self.print_model_param(self.model_initializer, name='Initialization Model') |
|
|
| |
| self.temporal_reweight = torch.FloatTensor([21 - i for i in range(1, 21)]).cuda().unsqueeze(0).unsqueeze(0) / 10 |
|
|
|
|
| def print_model_param(self, model: nn.Module, name: str = 'Model') -> None: |
| ''' |
| Count the trainable/total parameters in `model`. |
| ''' |
| total_num = sum(p.numel() for p in model.parameters()) |
| trainable_num = sum(p.numel() for p in model.parameters() if p.requires_grad) |
| print_log("[{}] Trainable/Total: {}/{}".format(name, trainable_num, total_num), self.log) |
| return None |
|
|
|
|
| def make_beta_schedule(self, schedule: str = 'linear', |
| n_timesteps: int = 1000, |
| start: float = 1e-5, end: float = 1e-2) -> torch.Tensor: |
| ''' |
| Make beta schedule. |
| |
| Parameters |
| ---- |
| schedule: str, in ['linear', 'quad', 'sigmoid'], |
| n_timesteps: int, diffusion steps, |
| start: float, beta start, `start<end`, |
| end: float, beta end, |
| |
| Returns |
| ---- |
| betas: Tensor with the shape of (n_timesteps) |
| |
| ''' |
| if schedule == 'linear': |
| betas = torch.linspace(start, end, n_timesteps) |
| elif schedule == "quad": |
| betas = torch.linspace(start ** 0.5, end ** 0.5, n_timesteps) ** 2 |
| elif schedule == "sigmoid": |
| betas = torch.linspace(-6, 6, n_timesteps) |
| betas = torch.sigmoid(betas) * (end - start) + start |
| return betas |
|
|
|
|
| def extract(self, input, t, x): |
| shape = x.shape |
| out = torch.gather(input, 0, t.to(input.device)) |
| reshape = [t.shape[0]] + [1] * (len(shape) - 1) |
| return out.reshape(*reshape) |
|
|
| def noise_estimation_loss(self, x, y_0, mask): |
| batch_size = x.shape[0] |
| |
| t = torch.randint(0, self.n_steps, size=(batch_size // 2 + 1,)).to(x.device) |
| t = torch.cat([t, self.n_steps - t - 1], dim=0)[:batch_size] |
| |
| a = self.extract(self.alphas_bar_sqrt, t, y_0) |
| beta = self.extract(self.betas, t, y_0) |
| |
| am1 = self.extract(self.one_minus_alphas_bar_sqrt, t, y_0) |
| e = torch.randn_like(y_0) |
| |
| y = y_0 * a + e * am1 |
| output = self.model(y, beta, x, mask) |
| |
| return (e - output).square().mean() |
|
|
|
|
|
|
| def p_sample(self, x, mask, cur_y, t): |
| if t==0: |
| z = torch.zeros_like(cur_y).to(x.device) |
| else: |
| z = torch.randn_like(cur_y).to(x.device) |
| t = torch.tensor([t]).cuda() |
| |
| eps_factor = ((1 - self.extract(self.alphas, t, cur_y)) / self.extract(self.one_minus_alphas_bar_sqrt, t, cur_y)) |
| |
| beta = self.extract(self.betas, t.repeat(x.shape[0]), cur_y) |
| eps_theta = self.model(cur_y, beta, x, mask) |
| mean = (1 / self.extract(self.alphas, t, cur_y).sqrt()) * (cur_y - (eps_factor * eps_theta)) |
| |
| z = torch.randn_like(cur_y).to(x.device) |
| |
| sigma_t = self.extract(self.betas, t, cur_y).sqrt() |
| sample = mean + sigma_t * z |
| return (sample) |
| |
| def p_sample_accelerate(self, x, mask, cur_y, t): |
| if t==0: |
| z = torch.zeros_like(cur_y).to(x.device) |
| else: |
| z = torch.randn_like(cur_y).to(x.device) |
| t = torch.tensor([t]).cuda() |
| |
| eps_factor = ((1 - self.extract(self.alphas, t, cur_y)) / self.extract(self.one_minus_alphas_bar_sqrt, t, cur_y)) |
| |
| beta = self.extract(self.betas, t.repeat(x.shape[0]), cur_y) |
| eps_theta = self.model.generate_accelerate(cur_y, beta, x, mask) |
| mean = (1 / self.extract(self.alphas, t, cur_y).sqrt()) * (cur_y - (eps_factor * eps_theta)) |
| |
| z = torch.randn_like(cur_y).to(x.device) |
| |
| sigma_t = self.extract(self.betas, t, cur_y).sqrt() |
| sample = mean + sigma_t * z * 0.00001 |
| return (sample) |
|
|
|
|
|
|
| def p_sample_loop(self, x, mask, shape): |
| self.model.eval() |
| prediction_total = torch.Tensor().cuda() |
| for _ in range(20): |
| cur_y = torch.randn(shape).to(x.device) |
| for i in reversed(range(self.n_steps)): |
| cur_y = self.p_sample(x, mask, cur_y, i) |
| prediction_total = torch.cat((prediction_total, cur_y.unsqueeze(1)), dim=1) |
| return prediction_total |
| |
| def p_sample_loop_mean(self, x, mask, loc): |
| prediction_total = torch.Tensor().cuda() |
| for loc_i in range(1): |
| cur_y = loc |
| for i in reversed(range(NUM_Tau)): |
| cur_y = self.p_sample(x, mask, cur_y, i) |
| prediction_total = torch.cat((prediction_total, cur_y.unsqueeze(1)), dim=1) |
| return prediction_total |
|
|
| def p_sample_loop_accelerate(self, x, mask, loc): |
| ''' |
| Batch operation to accelerate the denoising process. |
| |
| x: [11, 10, 6] |
| mask: [11, 11] |
| cur_y: [11, 10, 20, 2] |
| ''' |
| prediction_total = torch.Tensor().cuda() |
| cur_y = loc[:, :10] |
| for i in reversed(range(NUM_Tau)): |
| cur_y = self.p_sample_accelerate(x, mask, cur_y, i) |
| cur_y_ = loc[:, 10:] |
| for i in reversed(range(NUM_Tau)): |
| cur_y_ = self.p_sample_accelerate(x, mask, cur_y_, i) |
| |
| prediction_total = torch.cat((cur_y_, cur_y), dim=1) |
| return prediction_total |
|
|
|
|
|
|
| def fit(self): |
| |
| for epoch in range(0, self.cfg.num_epochs): |
| loss_total, loss_distance, loss_uncertainty = self._train_single_epoch(epoch) |
| print_log('[{}] Epoch: {}\t\tLoss: {:.6f}\tLoss Dist.: {:.6f}\tLoss Uncertainty: {:.6f}'.format( |
| time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), |
| epoch, loss_total, loss_distance, loss_uncertainty), self.log) |
| |
| if (epoch + 1) % self.cfg.test_interval == 0: |
| performance, samples = self._test_single_epoch() |
| for time_i in range(4): |
| print_log('--ADE({}s): {:.4f}\t--FDE({}s): {:.4f}'.format( |
| time_i+1, performance['ADE'][time_i]/samples, |
| time_i+1, performance['FDE'][time_i]/samples), self.log) |
| cp_path = self.cfg.model_path % (epoch + 1) |
| model_cp = {'model_initializer_dict': self.model_initializer.state_dict()} |
| torch.save(model_cp, cp_path) |
| self.scheduler_model.step() |
|
|
|
|
| def data_preprocess(self, data): |
| """ |
| pre_motion_3D: torch.Size([32, 11, 10, 2]), [batch_size, num_agent, past_frame, dimension] |
| fut_motion_3D: torch.Size([32, 11, 20, 2]) |
| fut_motion_mask: torch.Size([32, 11, 20]) |
| pre_motion_mask: torch.Size([32, 11, 10]) |
| traj_scale: 1 |
| pred_mask: None |
| seq: nba |
| """ |
| batch_size = data['pre_motion_3D'].shape[0] |
|
|
| traj_mask = torch.zeros(batch_size*11, batch_size*11).cuda() |
| for i in range(batch_size): |
| traj_mask[i*11:(i+1)*11, i*11:(i+1)*11] = 1. |
|
|
| initial_pos = data['pre_motion_3D'].cuda()[:, :, -1:] |
| |
| past_traj_abs = ((data['pre_motion_3D'].cuda() - self.traj_mean)/self.traj_scale).contiguous().view(-1, 10, 2) |
| past_traj_rel = ((data['pre_motion_3D'].cuda() - initial_pos)/self.traj_scale).contiguous().view(-1, 10, 2) |
| past_traj_vel = torch.cat((past_traj_rel[:, 1:] - past_traj_rel[:, :-1], torch.zeros_like(past_traj_rel[:, -1:])), dim=1) |
| past_traj = torch.cat((past_traj_abs, past_traj_rel, past_traj_vel), dim=-1) |
|
|
| fut_traj = ((data['fut_motion_3D'].cuda() - initial_pos)/self.traj_scale).contiguous().view(-1, 20, 2) |
| return batch_size, traj_mask, past_traj, fut_traj |
|
|
|
|
| def _train_single_epoch(self, epoch): |
| |
| self.model.train() |
| self.model_initializer.train() |
| loss_total, loss_dt, loss_dc, count = 0, 0, 0, 0 |
| |
| for data in self.train_loader: |
| batch_size, traj_mask, past_traj, fut_traj = self.data_preprocess(data) |
|
|
| sample_prediction, mean_estimation, variance_estimation = self.model_initializer(past_traj, traj_mask) |
| sample_prediction = torch.exp(variance_estimation/2)[..., None, None] * sample_prediction / sample_prediction.std(dim=1).mean(dim=(1, 2))[:, None, None, None] |
| loc = sample_prediction + mean_estimation[:, None] |
| |
| generated_y = self.p_sample_loop_accelerate(past_traj, traj_mask, loc) |
| |
| loss_dist = ( (generated_y - fut_traj.unsqueeze(dim=1)).norm(p=2, dim=-1) |
| * |
| self.temporal_reweight |
| ).mean(dim=-1).min(dim=1)[0].mean() |
| loss_uncertainty = (torch.exp(-variance_estimation) |
| * |
| (generated_y - fut_traj.unsqueeze(dim=1)).norm(p=2, dim=-1).mean(dim=(1, 2)) |
| + |
| variance_estimation |
| ).mean() |
| |
| loss = loss_dist*50 + loss_uncertainty |
| loss_total += loss.item() |
| loss_dt += loss_dist.item()*50 |
| loss_dc += loss_uncertainty.item() |
|
|
| self.opt.zero_grad() |
| loss.backward() |
| torch.nn.utils.clip_grad_norm_(self.model_initializer.parameters(), 1.) |
| self.opt.step() |
| count += 1 |
| if self.cfg.debug and count == 2: |
| break |
|
|
| return loss_total/count, loss_dt/count, loss_dc/count |
|
|
|
|
| def _test_single_epoch(self): |
| performance = { 'FDE': [0, 0, 0, 0], |
| 'ADE': [0, 0, 0, 0]} |
| samples = 0 |
| def prepare_seed(rand_seed): |
| np.random.seed(rand_seed) |
| random.seed(rand_seed) |
| torch.manual_seed(rand_seed) |
| torch.cuda.manual_seed_all(rand_seed) |
| prepare_seed(0) |
| count = 0 |
| with torch.no_grad(): |
| for data in self.test_loader: |
| batch_size, traj_mask, past_traj, fut_traj = self.data_preprocess(data) |
|
|
| sample_prediction, mean_estimation, variance_estimation = self.model_initializer(past_traj, traj_mask) |
| sample_prediction = torch.exp(variance_estimation/2)[..., None, None] * sample_prediction / sample_prediction.std(dim=1).mean(dim=(1, 2))[:, None, None, None] |
| loc = sample_prediction + mean_estimation[:, None] |
| |
| pred_traj = self.p_sample_loop_accelerate(past_traj, traj_mask, loc) |
|
|
| fut_traj = fut_traj.unsqueeze(1).repeat(1, 20, 1, 1) |
| |
| distances = torch.norm(fut_traj - pred_traj, dim=-1) * self.traj_scale |
| for time_i in range(1, 5): |
| ade = (distances[:, :, :5*time_i]).mean(dim=-1).min(dim=-1)[0].sum() |
| fde = (distances[:, :, 5*time_i-1]).min(dim=-1)[0].sum() |
| performance['ADE'][time_i-1] += ade.item() |
| performance['FDE'][time_i-1] += fde.item() |
| samples += distances.shape[0] |
| count += 1 |
| |
| |
| return performance, samples |
|
|
|
|
| def save_data(self): |
| ''' |
| Save the visualization data. |
| ''' |
| model_path = './results/checkpoints/led_vis.p' |
| model_dict = torch.load(model_path, map_location=torch.device('cpu'))['model_initializer_dict'] |
| self.model_initializer.load_state_dict(model_dict) |
| def prepare_seed(rand_seed): |
| np.random.seed(rand_seed) |
| random.seed(rand_seed) |
| torch.manual_seed(rand_seed) |
| torch.cuda.manual_seed_all(rand_seed) |
| prepare_seed(0) |
| root_path = './visualization/data/' |
| |
| with torch.no_grad(): |
| for data in self.test_loader: |
| _, traj_mask, past_traj, _ = self.data_preprocess(data) |
|
|
| sample_prediction, mean_estimation, variance_estimation = self.model_initializer(past_traj, traj_mask) |
| torch.save(sample_prediction, root_path+'p_var.pt') |
| torch.save(mean_estimation, root_path+'p_mean.pt') |
| torch.save(variance_estimation, root_path+'p_sigma.pt') |
|
|
| sample_prediction = torch.exp(variance_estimation/2)[..., None, None] * sample_prediction / sample_prediction.std(dim=1).mean(dim=(1, 2))[:, None, None, None] |
| loc = sample_prediction + mean_estimation[:, None] |
|
|
| pred_traj = self.p_sample_loop_accelerate(past_traj, traj_mask, loc) |
| pred_mean = self.p_sample_loop_mean(past_traj, traj_mask, mean_estimation) |
|
|
| torch.save(data['pre_motion_3D'], root_path+'past.pt') |
| torch.save(data['fut_motion_3D'], root_path+'future.pt') |
| torch.save(pred_traj, root_path+'prediction.pt') |
| torch.save(pred_mean, root_path+'p_mean_denoise.pt') |
|
|
| raise ValueError |
|
|
|
|
|
|
| def test_single_model(self): |
| model_path = './results/checkpoints/led_new.p' |
| model_dict = torch.load(model_path, map_location=torch.device('cpu'))['model_initializer_dict'] |
| self.model_initializer.load_state_dict(model_dict) |
| performance = { 'FDE': [0, 0, 0, 0], |
| 'ADE': [0, 0, 0, 0]} |
| samples = 0 |
| print_log(model_path, log=self.log) |
| def prepare_seed(rand_seed): |
| np.random.seed(rand_seed) |
| random.seed(rand_seed) |
| torch.manual_seed(rand_seed) |
| torch.cuda.manual_seed_all(rand_seed) |
| prepare_seed(0) |
| count = 0 |
| with torch.no_grad(): |
| for data in self.test_loader: |
| batch_size, traj_mask, past_traj, fut_traj = self.data_preprocess(data) |
|
|
| sample_prediction, mean_estimation, variance_estimation = self.model_initializer(past_traj, traj_mask) |
| sample_prediction = torch.exp(variance_estimation/2)[..., None, None] * sample_prediction / sample_prediction.std(dim=1).mean(dim=(1, 2))[:, None, None, None] |
| loc = sample_prediction + mean_estimation[:, None] |
| |
| pred_traj = self.p_sample_loop_accelerate(past_traj, traj_mask, loc) |
|
|
| fut_traj = fut_traj.unsqueeze(1).repeat(1, 20, 1, 1) |
| |
| distances = torch.norm(fut_traj - pred_traj, dim=-1) * self.traj_scale |
| for time_i in range(1, 5): |
| ade = (distances[:, :, :5*time_i]).mean(dim=-1).min(dim=-1)[0].sum() |
| fde = (distances[:, :, 5*time_i-1]).min(dim=-1)[0].sum() |
| performance['ADE'][time_i-1] += ade.item() |
| performance['FDE'][time_i-1] += fde.item() |
| samples += distances.shape[0] |
| count += 1 |
| |
| |
| for time_i in range(4): |
| print_log('--ADE({}s): {:.4f}\t--FDE({}s): {:.4f}'.format(time_i+1, performance['ADE'][time_i]/samples, \ |
| time_i+1, performance['FDE'][time_i]/samples), log=self.log) |
| |
| |