From 578727d043b203f94caeb6c1b87e129a5f6dc91e Mon Sep 17 00:00:00 2001 From: Si11ium Date: Mon, 1 Feb 2021 09:59:56 +0100 Subject: [PATCH] transition --- dataset.py | 8 +- eval/metrices.py | 6 - networks/adverserial_auto_encoder.py | 15 +- networks/attention_based_auto_enoder.py | 6 - networks/auto_encoder.py | 11 +- networks/modules.py | 82 ++------- .../seperating_adversarial_auto_encoder.py | 14 +- networks/variational_auto_encoder.py | 9 +- .../default/version_0/meta.experiment | 0 ...s.out.tfevents.1569753030.Mainframe.9592.0 | Bin 40 -> 0 bytes .../default/version_0/meta.experiment | 0 ....out.tfevents.1569753078.Mainframe.11012.0 | Bin 40 -> 0 bytes .../default/version_0/meta.experiment | 0 ...s.out.tfevents.1569753212.Mainframe.2136.0 | Bin 40 -> 0 bytes .../default/version_0/meta.experiment | 1 - .../default/version_0/meta_tags.csv | 8 - .../default/version_0/metrics.csv | 2 - ....out.tfevents.1569753327.Mainframe.12412.0 | Bin 366 -> 0 bytes .../default/version_0/meta.experiment | 1 - .../default/version_0/meta_tags.csv | 8 - .../default/version_0/metrics.csv | 1 - ...s.out.tfevents.1569753853.Mainframe.6536.0 | Bin 280 -> 0 bytes .../default/version_0/meta.experiment | 1 - .../default/version_0/meta_tags.csv | 8 - .../default/version_0/metrics.csv | 3 - ...s.out.tfevents.1569753869.Mainframe.3868.0 | Bin 452 -> 0 bytes .../default/version_0/meta.experiment | 1 - .../default/version_0/meta_tags.csv | 8 - .../default/version_0/metrics.csv | 48 ------ ....out.tfevents.1569754458.Mainframe.12296.0 | Bin 4387 -> 0 bytes run_models.py | 22 +-- viz/print_movement_in_map.py | 56 ++---- viz/tum_map_movement.png | Bin 0 -> 99426 bytes viz/utils.py | 160 +++++++++++++----- viz/viz_latent.py | 3 +- 35 files changed, 177 insertions(+), 305 deletions(-) delete mode 100644 eval/metrices.py delete mode 100644 output/AE_Model/Sun_Sep_29_12-30-30_2019/default/version_0/meta.experiment delete mode 100644 output/AE_Model/Sun_Sep_29_12-30-30_2019/default/version_0/tf/events.out.tfevents.1569753030.Mainframe.9592.0 delete mode 100644 output/AE_Model/Sun_Sep_29_12-31-18_2019/default/version_0/meta.experiment delete mode 100644 output/AE_Model/Sun_Sep_29_12-31-18_2019/default/version_0/tf/events.out.tfevents.1569753078.Mainframe.11012.0 delete mode 100644 output/AE_Model/Sun_Sep_29_12-33-32_2019/default/version_0/meta.experiment delete mode 100644 output/AE_Model/Sun_Sep_29_12-33-32_2019/default/version_0/tf/events.out.tfevents.1569753212.Mainframe.2136.0 delete mode 100644 output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/meta.experiment delete mode 100644 output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/meta_tags.csv delete mode 100644 output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/metrics.csv delete mode 100644 output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/tf/events.out.tfevents.1569753327.Mainframe.12412.0 delete mode 100644 output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/meta.experiment delete mode 100644 output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/meta_tags.csv delete mode 100644 output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/metrics.csv delete mode 100644 output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/tf/events.out.tfevents.1569753853.Mainframe.6536.0 delete mode 100644 output/AE_Model/Sun_Sep_29_12-44-29_2019/default/version_0/meta.experiment delete mode 100644 output/AE_Model/Sun_Sep_29_12-44-29_2019/default/version_0/meta_tags.csv delete mode 100644 output/AE_Model/Sun_Sep_29_12-44-29_2019/default/version_0/metrics.csv delete mode 100644 output/AE_Model/Sun_Sep_29_12-44-29_2019/default/version_0/tf/events.out.tfevents.1569753869.Mainframe.3868.0 delete mode 100644 output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/meta.experiment delete mode 100644 output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/meta_tags.csv delete mode 100644 output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/metrics.csv delete mode 100644 output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/tf/events.out.tfevents.1569754458.Mainframe.12296.0 create mode 100644 viz/tum_map_movement.png diff --git a/dataset.py b/dataset.py index 567c119..77aa167 100644 --- a/dataset.py +++ b/dataset.py @@ -5,7 +5,6 @@ from distutils.util import strtobool import os import ast from abc import ABC, abstractmethod -from torch.nn.modules import BatchNorm1d from tqdm import tqdm import numpy as np @@ -108,11 +107,6 @@ class AbstractDataset(ConcatDataset, ABC): class DataContainer(AbstractDataset): - @staticmethod - def calculate_model_shapes(size, step, **kwargs): - - return - @property def raw_filenames(self): return [f'{x}_trajec.csv' for x in self.maps] @@ -209,7 +203,7 @@ class Trajectories(Dataset): def get_both_by_key(self, item): data = self.data[item:item + self.size * self.step or None:self.step] - return data[0] + return data def __len__(self): total_len = self.data.size()[0] diff --git a/eval/metrices.py b/eval/metrices.py deleted file mode 100644 index 7f9e1b8..0000000 --- a/eval/metrices.py +++ /dev/null @@ -1,6 +0,0 @@ -#ToDo: We need a metric that analysis sequences of coordinates of arbitrary length and clusters them based -# on their embedded type of mevement - -# ToDo: we ne a function, that compares the clustering outcome of our movement analysis with the AE output. - -# Do the variants of AE really adjust their latent space regarding the embedded moveement type? diff --git a/networks/adverserial_auto_encoder.py b/networks/adverserial_auto_encoder.py index e2be38f..f7dfa38 100644 --- a/networks/adverserial_auto_encoder.py +++ b/networks/adverserial_auto_encoder.py @@ -11,9 +11,10 @@ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') class AdversarialAE(AutoEncoder): - def __init__(self, *args, **kwargs): + def __init__(self, *args, train_on_predictions=False, use_norm=False, **kwargs): super(AdversarialAE, self).__init__(*args, **kwargs) - self.discriminator = Discriminator(self.latent_dim, self.features) + self.discriminator = Discriminator(self.latent_dim, self.features, use_norm=use_norm) + self.train_on_predictions = train_on_predictions def forward(self, batch): # Encoder @@ -25,13 +26,6 @@ class AdversarialAE(AutoEncoder): x_hat = self.decoder(z_repeatet) return z, x_hat - -class AdversarialAE_LO(LightningModuleOverrides): - - def __init__(self, train_on_predictions=False): - super(AdversarialAE_LO, self).__init__() - self.train_on_predictions = train_on_predictions - def training_step(self, batch, _, optimizer_i): x, y = batch z, x_hat = self.forward(x) @@ -66,8 +60,7 @@ class AdversarialAE_LO(LightningModuleOverrides): else: raise RuntimeError('This should not have happened, catch me if u can.') - - # This is Fucked up, why do i need to put an additional empty list here? + #FIXME: This is Fucked up, why do i need to put an additional empty list here? def configure_optimizers(self): return [Adam(self.network.discriminator.parameters(), lr=0.02), Adam([*self.network.encoder.parameters(), *self.network.decoder.parameters()], lr=0.02), ],\ diff --git a/networks/attention_based_auto_enoder.py b/networks/attention_based_auto_enoder.py index 061b9b6..06bae27 100644 --- a/networks/attention_based_auto_enoder.py +++ b/networks/attention_based_auto_enoder.py @@ -27,12 +27,6 @@ class AE_WithAttention(AbstractNeuralNetwork, ABC): x_hat = self.decoder(z_repeatet) return z, x_hat - -class AE_WithAttention_LO(LightningModuleOverrides): - - def __init__(self): - super(AE_WithAttention_LO, self).__init__() - def training_step(self, x, batch_nb): # ToDo: We need a new loss function, fullfilling all attention needs # z, x_hat diff --git a/networks/auto_encoder.py b/networks/auto_encoder.py index d4e05f9..eb3040c 100644 --- a/networks/auto_encoder.py +++ b/networks/auto_encoder.py @@ -9,9 +9,11 @@ from torch import Tensor # Basic AE-Implementation class AutoEncoder(AbstractNeuralNetwork, ABC): - def __init__(self, latent_dim: int=0, features: int = 0, use_norm=True, **kwargs): + def __init__(self, latent_dim: int=0, features: int = 0, use_norm=True, + train_on_predictions=False, **kwargs): assert latent_dim and features super(AutoEncoder, self).__init__() + self.train_on_predictions = train_on_predictions self.latent_dim = latent_dim self.features = features self.encoder = Encoder(self.latent_dim, use_norm=use_norm) @@ -27,13 +29,6 @@ class AutoEncoder(AbstractNeuralNetwork, ABC): x_hat = self.decoder(z_repeatet) return z, x_hat - -class AutoEncoder_LO(LightningModuleOverrides): - - def __init__(self, train_on_predictions=False): - super(AutoEncoder_LO, self).__init__() - self.train_on_predictions = train_on_predictions - def training_step(self, batch, batch_nb): x, y = batch # z, x_hat diff --git a/networks/modules.py b/networks/modules.py index cd9ace2..5c1ad32 100644 --- a/networks/modules.py +++ b/networks/modules.py @@ -5,9 +5,7 @@ from functools import reduce import torch from torch import randn import pytorch_lightning as pl -from pytorch_lightning import data_loader from torch.nn import Module, Linear, ReLU, Sigmoid, Dropout, GRU, Tanh -from torchvision.transforms import Normalize from abc import ABC, abstractmethod @@ -27,21 +25,12 @@ class LightningModuleOverrides: def name(self): return self.__class__.__name__ - def forward(self, x): - return self.network.forward(x) - - @data_loader + @pl.data_loader def train_dataloader(self): - num_workers = 0 # os.cpu_count() // 2 + num_workers = 0 # os.cpu_count() // 2 return DataLoader(DataContainer(os.path.join('data', 'training'), self.size, self.step), shuffle=True, batch_size=10000, num_workers=num_workers) - """ - @data_loader - def val_dataloader(self): - num_workers = 0 # os.cpu_count() // 2 - return DataLoader(DataContainer(os.path.join('data', 'validation'), self.size, self.step), - shuffle=True, batch_size=100, num_workers=num_workers) - """ + class AbstractNeuralNetwork(Module): @@ -56,53 +45,6 @@ class AbstractNeuralNetwork(Module): def forward(self, batch): pass - -###################### -# Abstract Network class following the Lightning Syntax -class LightningModule(pl.LightningModule, ABC): - - def __init__(self): - super(LightningModule, self).__init__() - - @abstractmethod - def forward(self, x): - raise NotImplementedError - - @abstractmethod - def training_step(self, batch, batch_nb): - # REQUIRED - raise NotImplementedError - - @abstractmethod - def configure_optimizers(self): - # REQUIRED - raise NotImplementedError - - @pl.data_loader - def train_dataloader(self): - # REQUIRED - raise NotImplementedError - - """ - def validation_step(self, batch, batch_nb): - # OPTIONAL - pass - - def validation_end(self, outputs): - # OPTIONAL - pass - - @pl.data_loader - def val_dataloader(self): - # OPTIONAL - pass - - @pl.data_loader - def test_dataloader(self): - # OPTIONAL - pass - """ - ####################### # Utility Modules class TimeDistributed(Module): @@ -167,12 +109,14 @@ class AvgDimPool(Module): # Generators, Decoders, Encoders, Discriminators class Discriminator(Module): - def __init__(self, latent_dim, features, dropout=.0, activation=ReLU): + def __init__(self, latent_dim, features, dropout=.0, activation=ReLU, use_norm=False): super(Discriminator, self).__init__() self.features = features self.latent_dim = latent_dim self.l1 = Linear(self.latent_dim, self.features * 10) + self.norm1 = torch.nn.BatchNorm1d(self.features * 10) if use_norm else False self.l2 = Linear(self.features * 10, self.features * 20) + self.norm2 = torch.nn.BatchNorm1d(self.features * 20) if use_norm else False self.lout = Linear(self.features * 20, 1) self.dropout = Dropout(dropout) self.activation = activation() @@ -180,9 +124,15 @@ class Discriminator(Module): def forward(self, x, **kwargs): tensor = self.l1(x) - tensor = self.dropout(self.activation(tensor)) + tensor = self.dropout(tensor) + if self.norm1: + tensor = self.norm1(tensor) + tensor = self.activation(tensor) tensor = self.l2(tensor) - tensor = self.dropout(self.activation(tensor)) + tensor = self.dropout(tensor) + if self.norm2: + tensor = self.norm2(tensor) + tensor = self.activation(tensor) tensor = self.lout(tensor) tensor = self.sigmoid(tensor) return tensor @@ -296,13 +246,13 @@ class AttentionEncoder(Module): class PoolingEncoder(Module): - def __init__(self, lat_dim, variational=False): + def __init__(self, lat_dim, variational=False, use_norm=True): self.lat_dim = lat_dim self.variational = variational super(PoolingEncoder, self).__init__() self.p = AvgDimPool() - self.l = EncoderLinearStack() + self.l = EncoderLinearStack(use_norm=use_norm) if variational: self.mu = Linear(self.l.shape, self.lat_dim) self.logvar = Linear(self.l.shape, self.lat_dim) diff --git a/networks/seperating_adversarial_auto_encoder.py b/networks/seperating_adversarial_auto_encoder.py index 15f3b6e..213fad9 100644 --- a/networks/seperating_adversarial_auto_encoder.py +++ b/networks/seperating_adversarial_auto_encoder.py @@ -6,12 +6,13 @@ import torch class SeperatingAAE(Module): - def __init__(self, latent_dim, features, use_norm=True): + def __init__(self, latent_dim, features, train_on_predictions=False, use_norm=True): super(SeperatingAAE, self).__init__() self.latent_dim = latent_dim self.features = features - self.spatial_encoder = PoolingEncoder(self.latent_dim) + self.train_on_predictions = train_on_predictions + self.spatial_encoder = PoolingEncoder(self.latent_dim, use_norm=use_norm) self.temporal_encoder = Encoder(self.latent_dim, use_dense=False, use_norm=use_norm) self.decoder = Decoder(self.latent_dim * 2, self.features, use_norm=use_norm) self.spatial_discriminator = Discriminator(self.latent_dim, self.features) @@ -28,13 +29,6 @@ class SeperatingAAE(Module): x_hat = self.decoder(z_repeatet) return z_spatial, z_temporal, x_hat - -class SeparatingAAE_LO(LightningModuleOverrides): - - def __init__(self, train_on_predictions=False): - super(SeparatingAAE_LO, self).__init__() - self.train_on_predictions = train_on_predictions - def training_step(self, batch, _, optimizer_i): x, y = batch spatial_latent_fake, temporal_latent_fake, x_hat = self.network.forward(x) @@ -92,7 +86,7 @@ class SeparatingAAE_LO(LightningModuleOverrides): else: raise RuntimeError('This should not have happened, catch me if u can.') - # This is Fucked up, why do i need to put an additional empty list here? + #FixMe: This is Fucked up, why do i need to put an additional empty list here? def configure_optimizers(self): return [Adam([*self.network.spatial_discriminator.parameters(), *self.network.spatial_encoder.parameters()] , lr=0.02), diff --git a/networks/variational_auto_encoder.py b/networks/variational_auto_encoder.py index 5f8be7f..aa82c6e 100644 --- a/networks/variational_auto_encoder.py +++ b/networks/variational_auto_encoder.py @@ -12,7 +12,7 @@ class VariationalAE(AbstractNeuralNetwork, ABC): def name(self): return self.__class__.__name__ - def __init__(self, latent_dim=0, features=0, use_norm=True, **kwargs): + def __init__(self, latent_dim=0, features=0, use_norm=True, train_on_predictions=False, **kwargs): assert latent_dim and features super(VariationalAE, self).__init__() self.features = features @@ -34,13 +34,6 @@ class VariationalAE(AbstractNeuralNetwork, ABC): x_hat = self.decoder(repeat(z)) return mu, logvar, x_hat - -class VAE_LO(LightningModuleOverrides): - - def __init__(self, train_on_predictions=False): - super(VAE_LO, self).__init__() - self.train_on_predictions=train_on_predictions - def training_step(self, batch, _): x, y = batch mu, logvar, x_hat = self.forward(x) diff --git a/output/AE_Model/Sun_Sep_29_12-30-30_2019/default/version_0/meta.experiment b/output/AE_Model/Sun_Sep_29_12-30-30_2019/default/version_0/meta.experiment deleted file mode 100644 index e69de29..0000000 diff --git a/output/AE_Model/Sun_Sep_29_12-30-30_2019/default/version_0/tf/events.out.tfevents.1569753030.Mainframe.9592.0 b/output/AE_Model/Sun_Sep_29_12-30-30_2019/default/version_0/tf/events.out.tfevents.1569753030.Mainframe.9592.0 deleted file mode 100644 index c15615a9c0156c3440034d7400f99c460761631e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40 rcmb1OfPlsI-b$QvGgp69Ou6nT#hX-=n3<>NT9%quVrAsgpwkNg)jte8 diff --git a/output/AE_Model/Sun_Sep_29_12-31-18_2019/default/version_0/meta.experiment b/output/AE_Model/Sun_Sep_29_12-31-18_2019/default/version_0/meta.experiment deleted file mode 100644 index e69de29..0000000 diff --git a/output/AE_Model/Sun_Sep_29_12-31-18_2019/default/version_0/tf/events.out.tfevents.1569753078.Mainframe.11012.0 b/output/AE_Model/Sun_Sep_29_12-31-18_2019/default/version_0/tf/events.out.tfevents.1569753078.Mainframe.11012.0 deleted file mode 100644 index efc96da59361b69827b9c8fb3ac2d4203f8c6b58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40 rcmb1OfPlsI-b$P&l@|Y1Ou6nT#hX-=n3<>NT9%quVr6uQ+4D01)Fuq6 diff --git a/output/AE_Model/Sun_Sep_29_12-33-32_2019/default/version_0/meta.experiment b/output/AE_Model/Sun_Sep_29_12-33-32_2019/default/version_0/meta.experiment deleted file mode 100644 index e69de29..0000000 diff --git a/output/AE_Model/Sun_Sep_29_12-33-32_2019/default/version_0/tf/events.out.tfevents.1569753212.Mainframe.2136.0 b/output/AE_Model/Sun_Sep_29_12-33-32_2019/default/version_0/tf/events.out.tfevents.1569753212.Mainframe.2136.0 deleted file mode 100644 index ec8e39efb623ed3d5700fa0bba42f1ccc6b6ad0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40 rcmb1OfPlsI-b$R;KXS+`rCfKE;!P?_%*@ksElbTSu`+V|y2}j!$K?!2 diff --git a/output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/meta.experiment b/output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/meta.experiment deleted file mode 100644 index 69f9514..0000000 --- a/output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/meta.experiment +++ /dev/null @@ -1 +0,0 @@ -{"name": "default", "version": 0, "tags_path": "C:\\Users\\steff\\Google Drive\\LMU\\Research\\ae_toolbox_torch\\output\\AE_Model\\Sun_Sep_29_12-35-27_2019\\default\\version_0/meta_tags.csv", "metrics_path": "C:\\Users\\steff\\Google Drive\\LMU\\Research\\ae_toolbox_torch\\output\\AE_Model\\Sun_Sep_29_12-35-27_2019\\default\\version_0/metrics.csv", "autosave": false, "description": null, "created_at": "2019-09-29 10:35:27.965484", "exp_hash": "default_v0"} \ No newline at end of file diff --git a/output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/meta_tags.csv b/output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/meta_tags.csv deleted file mode 100644 index ab2ef3f..0000000 --- a/output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/meta_tags.csv +++ /dev/null @@ -1,8 +0,0 @@ -key,value -step,5 -features,6 -size,9 -latent_dim,2 -model,AE_Model -refresh,False -future_predictions,False diff --git a/output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/metrics.csv b/output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/metrics.csv deleted file mode 100644 index ae78b1d..0000000 --- a/output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/metrics.csv +++ /dev/null @@ -1,2 +0,0 @@ -loss,epoch,created_at -1.454,0.0,2019-09-29 10:41:14.039965 diff --git a/output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/tf/events.out.tfevents.1569753327.Mainframe.12412.0 b/output/AE_Model/Sun_Sep_29_12-35-27_2019/default/version_0/tf/events.out.tfevents.1569753327.Mainframe.12412.0 deleted file mode 100644 index 6e2962078783f89da8a51fd3344e688a72bfebb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 366 zcmb1OfPlsI-b$P+fB&^sO1bVR#hX-=n3<>NT9%quVr5idvPkzKR2}nnkBOYGXY)(~ zsnfd3$aRU4OC+Npu_!UOSidB-q9ne!G&eV~sM2XYBL}+>ix3kBqtg;bWiU`kNli;E z%_-4QC`&CW&dkqKFyPYF)#ZXndQ=vq7J*GlEdfe%!I?E>i8-aIT)H*7T*W1+1vREz zX{m`NrA4X5HD+AJnN_JZmRvcBB|x{vr)1{V7;)w1r=;f8IJ(CBf*4#yscAs988vQ+ zImM}5X{8{o@dZVxDVfP7Kt~iqCA>Je*tl3geh}VKK3@vzVWHVKbUDxTHGw@Wz^KK` w#m&W%lV4mcx<+P~{XI^BgVIoyQoQqokyY|>@o=%G7UU;qh=No^Bpq1+0FF6vt^fc4 diff --git a/output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/meta.experiment b/output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/meta.experiment deleted file mode 100644 index e991626..0000000 --- a/output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/meta.experiment +++ /dev/null @@ -1 +0,0 @@ -{"name": "default", "version": 0, "tags_path": "C:\\Users\\steff\\Google Drive\\LMU\\Research\\ae_toolbox_torch\\output\\AE_Model\\Sun_Sep_29_12-44-13_2019\\default\\version_0/meta_tags.csv", "metrics_path": "C:\\Users\\steff\\Google Drive\\LMU\\Research\\ae_toolbox_torch\\output\\AE_Model\\Sun_Sep_29_12-44-13_2019\\default\\version_0/metrics.csv", "autosave": false, "description": null, "created_at": "2019-09-29 10:44:13.614075", "exp_hash": "default_v0"} \ No newline at end of file diff --git a/output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/meta_tags.csv b/output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/meta_tags.csv deleted file mode 100644 index 655a21b..0000000 --- a/output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/meta_tags.csv +++ /dev/null @@ -1,8 +0,0 @@ -key,value -step,5 -features,6 -size,9 -latent_dim,2 -model,AE_Model -refresh,False -future_predictions,True diff --git a/output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/metrics.csv b/output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/metrics.csv deleted file mode 100644 index 8b13789..0000000 --- a/output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/metrics.csv +++ /dev/null @@ -1 +0,0 @@ - diff --git a/output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/tf/events.out.tfevents.1569753853.Mainframe.6536.0 b/output/AE_Model/Sun_Sep_29_12-44-13_2019/default/version_0/tf/events.out.tfevents.1569753853.Mainframe.6536.0 deleted file mode 100644 index fbbfc697ef5d7a847f7e75a6ef4290c91f912030..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 280 zcmb1OfPlsI-b$Qob?N(+Qm#8n@g@}|X6EU+mZj#ESQ$k=P%U@>Rj2LAx1IBto#_FP zI;|^=To)O+L^28zixP8-^-EGKO5%%4b8{1mDxKCba0(5J9N@i}25m#=0N@`Axqieh`h{08qng&#xQR9}F pQ=H0`RtnM@Ur>~ql9^lrbVPAYNDzAZfl*AX8=H@0ARXVL>#*C{tvnsX5k}D^%1nAcIl+4^3Bd*;1l+>IWN7r~?5QD2IH4UgXqsA>U zr#O`>trVm+zMv>IB{R7M=!oK)kRqV*UL0I(Tr40jv_EJSmV)|MX!Z?VPSb|@VE+m* zYVmS$bFt*)7Z;0en!DcqQlZysX{bsm-g&~vD*3p0xL8vQ@{=<}K`MN_Z)l+?ZMr`x pTp1XM0-KqUOtsw7VfV4K^|=hx)Ds7%%5owrMKQL)J|{gY005P6i0J?T diff --git a/output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/meta.experiment b/output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/meta.experiment deleted file mode 100644 index c04bdc7..0000000 --- a/output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/meta.experiment +++ /dev/null @@ -1 +0,0 @@ -{"name": "default", "version": 0, "tags_path": "C:\\Users\\steff\\Google Drive\\LMU\\Research\\ae_toolbox_torch\\output\\SAAE_Model\\Sun_Sep_29_12-54-18_2019\\default\\version_0/meta_tags.csv", "metrics_path": "C:\\Users\\steff\\Google Drive\\LMU\\Research\\ae_toolbox_torch\\output\\SAAE_Model\\Sun_Sep_29_12-54-18_2019\\default\\version_0/metrics.csv", "autosave": false, "description": null, "created_at": "2019-09-29 10:54:18.863108", "exp_hash": "default_v0"} \ No newline at end of file diff --git a/output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/meta_tags.csv b/output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/meta_tags.csv deleted file mode 100644 index de0d382..0000000 --- a/output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/meta_tags.csv +++ /dev/null @@ -1,8 +0,0 @@ -key,value -step,5 -features,6 -size,9 -latent_dim,2 -model,SAAE_Model -refresh,False -future_predictions,True diff --git a/output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/metrics.csv b/output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/metrics.csv deleted file mode 100644 index 83d13f1..0000000 --- a/output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/metrics.csv +++ /dev/null @@ -1,48 +0,0 @@ -loss,epoch,created_at -0.471,0.0,2019-09-29 10:54:25.127533 -0.076,1.0,2019-09-29 11:04:46.930249 -0.069,2.0,2019-09-29 11:14:02.826272 -0.089,3.0,2019-09-29 11:23:11.776641 -0.068,4.0,2019-09-29 11:32:19.540023 -0.066,5.0,2019-09-29 11:41:27.129607 -0.067,6.0,2019-09-29 11:50:33.679401 -0.071,7.0,2019-09-29 11:59:38.747566 -0.068,8.0,2019-09-29 12:08:46.713434 -0.067,9.0,2019-09-29 12:17:55.462982 -0.07,10.0,2019-09-29 12:27:03.690029 -0.066,11.0,2019-09-29 12:36:10.274328 -0.066,12.0,2019-09-29 12:45:17.844777 -0.064,13.0,2019-09-29 12:54:25.440055 -0.064,14.0,2019-09-29 13:03:32.662178 -0.063,15.0,2019-09-29 13:12:39.334202 -0.063,16.0,2019-09-29 13:21:45.282941 -0.063,17.0,2019-09-29 13:30:50.702369 -0.062,18.0,2019-09-29 13:39:56.479320 -0.062,19.0,2019-09-29 13:49:03.009732 -0.062,20.0,2019-09-29 13:58:09.206604 -0.062,21.0,2019-09-29 14:07:16.674273 -0.062,22.0,2019-09-29 14:16:32.081830 -0.061,23.0,2019-09-29 14:25:47.816996 -0.061,24.0,2019-09-29 14:34:59.053729 -0.061,25.0,2019-09-29 14:44:12.326646 -0.061,26.0,2019-09-29 14:53:20.545392 -0.061,27.0,2019-09-29 15:02:29.076439 -0.061,28.0,2019-09-29 15:11:40.214715 -0.061,29.0,2019-09-29 15:20:47.708415 -0.061,30.0,2019-09-29 15:29:55.151460 -0.061,31.0,2019-09-29 15:39:02.450643 -0.061,32.0,2019-09-29 15:48:13.678387 -0.061,33.0,2019-09-29 15:57:22.619685 -0.061,34.0,2019-09-29 16:06:32.276767 -0.061,35.0,2019-09-29 16:15:39.175331 -0.061,36.0,2019-09-29 16:24:48.090009 -0.061,37.0,2019-09-29 16:33:53.686359 -0.061,38.0,2019-09-29 16:43:01.209447 -0.061,39.0,2019-09-29 16:52:09.086088 -0.061,40.0,2019-09-29 17:01:17.997290 -0.06,41.0,2019-09-29 17:10:24.687865 -0.061,42.0,2019-09-29 17:19:33.252531 -0.061,43.0,2019-09-29 17:28:40.294962 -0.06,44.0,2019-09-29 17:37:50.408505 -0.06,45.0,2019-09-29 17:46:57.046547 -0.06,46.0,2019-09-29 17:56:05.325744 diff --git a/output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/tf/events.out.tfevents.1569754458.Mainframe.12296.0 b/output/SAAE_Model/Sun_Sep_29_12-54-18_2019/default/version_0/tf/events.out.tfevents.1569754458.Mainframe.12296.0 deleted file mode 100644 index eb1f3364f0bc727ba463fb56771bdfd7f48d8fa7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4387 zcmZA4c~BE)0LEcZKv+cqv0mt)B1brj%3+DkuHb>`YQ=G^TC3n#1{58{qiE4i#e?##O#8i?zcP~_Pu~4VGRYXD54Vy{ z3np8Ht*>1%MbXTTvs|N*$FBDX((~(eGrhwfM6ve=x+K0}i-oG?n=sNzZW}W2qAYF4IkZK#~9|wm0F&OP9T3Aq1Nz<*eG5946Pwl z)Bgm)=8PF*LM~!$USs&Ufh)EtY;m{P>@qK$T(On0#F81xm?%|R?Wiut7qjA9M>UNf zD92eI9!ZzOnPH46uU19Hj3UMJKh9=B>XUK4M?g;vrqqtILg}U8X}xv>YE#3$2^Mq; zY}%)XFGalsw)9?2ssq{j(2Qc+7H3Is|9br`uyhKnk!;5Zogc6%H7|xtC2Sr;vBg1W zq;W+qJg{^MtW37w@uTO!*5};cEwGm@DR%$tOsOHl;0P?80-Gl5-6$^tHf2H6SVyw< zjTOZXa@{ViX*gsDES&;-L>6ItXcw?cljDj7w#t@b7y4#M57)l+0hUgIt(P5J9c%}z zeam+eC$hF}G{tITa;3W$e*FSiIt8{}rnp+&25eVF)hU6^9Z#|3Y}fTw9t4(7fi+?a z{F9S`-F&n)z?rOVoJg_c5Mphe)&NVVz*@1cu}@|Jt9E&MQD9$9qF9$zIZ~&N%U6M= zQ(*1cj)k2QfX&O!UF1U6mQSTv@=6FC5qAz)ItA8??GDiY0&I(wzFA=JIE&b-I@6uN z(kZYqc9HcR39xN0t0E<2ZKfN=s%+Awp^rYp&jdOJb_u&v5t0w=B)OzZVCy_ZY=-sE z8(}S-0vpM$No>ymc5l?J4X$Kuw>QO3HQOvrf8QPfES&?*xs|5DU_+`BUd(=n7 z+7~yffTdGl(^#e|y&BlVJ;%4Wk+nDdMQm2|BV_3m*d6Rk)5bbrJ*p-SbtmjLDaD@Y zNs)d#vh7n?OQ*o@W;ewbI036Is^259wK5Ug-JE9zES&OoA@e#4RLZU6WGGJBKEQO`5%F$Q(!Atvy6%9z}5zsdU=wySA#|D z%M}NVfTdGltJ%+IIje!Kod5Ei!1jeu?3BBS(!Gw!?|`LKV9&COv0?aj2vIi%dXcp! zmx@^5@;g1i(kZa@Y|`SAMp(PE+4h>ic7%%9zv9aEz&3UMp90&&W}UlN0PN|`$>AT9 zwfn+F?0kLiSHRLKu-929NkA>IiU)!B1oqNO5t~|iVlc3D3Tz9@nB3?AwzJMM&YP@# zy;{UJ|Mor~SULr^o&CMlXe6-5S3K(x*vd5`w&L+RKVaz;*hlPpW7lk8cbvRzm`2vN z@*?)>g)gQ9OQ*m-XSXh!G#c3O=7Xlw37ZouV&`;Q{|PLe0{ah}vf+3Yu-~tX%MsWH zrHIWQ{^d`=(kZY;T#)jZ9@y@R@-Z{W+8&jNy&iVW30OJ>)`T0q$M6u?tTWcd0$ZjP zv9F?bUjdd*fi>p>XO#D}bH){EGXxe-Oc(kZaRxacP_VZfffU|J)v8A&46 zHFy{P%jgtXD=zr#aXf@Y&*B1n$=dTNB35HK6bWnT6j)oXEHm{ewENeFTol-6Y4k50 z^2xYyt)1tDf!`#No^4YspapmgXS-k!j!4`Rw8)R_E!rxEz5hOW4`65kw&NN@TTa2^ zLpL`y3-FEYV%Q_~bw0q*0&LG6FmSE__nEpy`jf@!nKYbpAwin^Lxuyu&;snhEr08< z3*aP|C0zo1I-7>c8Bd$6E(I7`fStKkX#sw7t8BN?%p{9DbH#AcXqg0HXaRQP3b$on zg2k^K4!jZIf_*ehj>0^xC0rukVY6_MDT-?iT{e&#Ok}rl;7l(%d3@yOZIhV`^ f4ZwXZp_Tyzepe`lg93AJ0Sqm`e%wlr1B!nEdWFyk diff --git a/run_models.py b/run_models.py index e2a1762..b0587aa 100644 --- a/run_models.py +++ b/run_models.py @@ -1,4 +1,5 @@ from torch.distributions import Normal +from torch.cuda import is_available import time import os @@ -22,9 +23,10 @@ args.add_argument('--step', default=5) args.add_argument('--features', default=6) args.add_argument('--size', default=9) args.add_argument('--latent_dim', default=2) -args.add_argument('--model', default='SAAE_Model') +args.add_argument('--model', default='AE_Model') args.add_argument('--refresh', type=strtobool, default=False) -args.add_argument('--future_predictions', type=strtobool, default=True) +args.add_argument('--future_predictions', type=strtobool, default=False) +args.add_argument('--use_norm', type=strtobool, default=True) class AE_Model(AutoEncoder_LO, LightningModule): @@ -36,7 +38,7 @@ class AE_Model(AutoEncoder_LO, LightningModule): self.features = parameters.features self.step = parameters.step super(AE_Model, self).__init__(train_on_predictions=parameters.future_predictions) - self.network = AutoEncoder(self.latent_dim, self.features) + self.network = AutoEncoder(self.latent_dim, self.features, use_norm=parameters.use_norm) class VAE_Model(VAE_LO, LightningModule): @@ -48,7 +50,7 @@ class VAE_Model(VAE_LO, LightningModule): self.features = parameters.features self.step = parameters.step super(VAE_Model, self).__init__(train_on_predictions=parameters.future_predictions) - self.network = VariationalAE(self.latent_dim, self.features) + self.network = VariationalAE(self.latent_dim, self.features, use_norm=parameters.use_norm) class AAE_Model(AdversarialAE_LO, LightningModule): @@ -61,7 +63,7 @@ class AAE_Model(AdversarialAE_LO, LightningModule): self.step = parameters.step super(AAE_Model, self).__init__(train_on_predictions=parameters.future_predictions) self.normal = Normal(0, 1) - self.network = AdversarialAE(self.latent_dim, self.features) + self.network = AdversarialAE(self.latent_dim, self.features, use_norm=parameters.use_norm) pass @@ -75,7 +77,7 @@ class SAAE_Model(SeparatingAAE_LO, LightningModule): self.step = parameters.step super(SAAE_Model, self).__init__(train_on_predictions=parameters.future_predictions) self.normal = Normal(0, 1) - self.network = SeperatingAAE(self.latent_dim, self.features) + self.network = SeperatingAAE(self.latent_dim, self.features, use_norm=parameters.use_norm) pass @@ -93,17 +95,17 @@ if __name__ == '__main__': from pytorch_lightning.callbacks import ModelCheckpoint checkpoint_callback = ModelCheckpoint( - filepath=os.path.join(outpath, 'weights.ckpt'), + filepath=os.path.join(outpath, 'weights'), save_best_only=False, verbose=True, period=4 ) trainer = Trainer(experiment=exp, - max_nb_epochs=250, - gpus=[0], + max_nb_epochs=60, + gpus=[0] if is_available() else None, row_log_interval=1000, - # checkpoint_callback=checkpoint_callback + checkpoint_callback=checkpoint_callback ) trainer.fit(model) diff --git a/viz/print_movement_in_map.py b/viz/print_movement_in_map.py index 5d002a1..995ebe8 100644 --- a/viz/print_movement_in_map.py +++ b/viz/print_movement_in_map.py @@ -1,50 +1,26 @@ from argparse import ArgumentParser import os +from torch import device +from torch.cuda import is_available + from dataset import DataContainer -from viz.utils import MotionAnalyser, Printer, MapContainer, search_for_weights -import torch -from run_models import SAAE_Model, AAE_Model, VAE_Model, AE_Model +from viz.utils import Printer, MapContainer + +available_device = device('cuda' if is_available() else 'cpu') arguments = ArgumentParser() arguments.add_argument('--data', default='output') -device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') - - -def load_and_viz(path_like_element): - # Define Loop to search for models and folder with visualizations - splitpath = path_like_element.split(os.sep) - base_dir = os.path.join(*splitpath[:4]) - model = globals()[splitpath[2]] - print(f'... loading model named: "{model.name}" from timestamp: {splitpath[3]}') - pretrained_model = model.load_from_metrics( - weights_path=path_like_element, - tags_csv=os.path.join(base_dir, 'default', 'version_0', 'meta_tags.csv'), - on_gpu=True if torch.cuda.is_available() else False, - # map_location=None - ) - - # Init model and freeze its weights ( for faster inference) - pretrained_model = pretrained_model.to(device) - pretrained_model.eval() - pretrained_model.freeze() - - dataIndex = 0 - - datasets = DataContainer(os.path.join(os.pardir, 'data', 'validation'), 9, 6).to(device) - dataset = datasets.datasets[dataIndex] - # ToDO: use dataloader for iteration instead! - dataloader = DataLoader(dataset, ) - - maps = MapContainer(os.path.join(os.pardir, 'data', 'validation')) - base_map = maps.datasets[dataIndex] - - p = Printer(pretrained_model) - p.print_trajec_on_basemap(dataset, base_map, save=os.path.join(base_dir, f'{base_map.name}_movement.png'), - color_by_movement=True) - return True - - if __name__ == '__main__': args = arguments.parse_args() - search_for_weights(load_and_viz, args.data, file_type='movement') + + maps = MapContainer(os.path.join(os.pardir, 'data', 'validation')) + base_map = maps.datasets[0] + + datasets = DataContainer(os.path.join(os.pardir, 'data', 'validation'), 9, 6).to(available_device) + dataset = datasets.datasets[0] + + p = Printer(None) + p.print_trajec_on_basemap(dataset, base_map, save=os.path.join(f'{base_map.name}_movement.png'), + color_by_movement=True, n=20, clustering='fastdtw', show=True) \ No newline at end of file diff --git a/viz/tum_map_movement.png b/viz/tum_map_movement.png new file mode 100644 index 0000000000000000000000000000000000000000..2f32691f211b47df18e9d8a7215ebcd2a3518baf GIT binary patch literal 99426 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�siw4+kL@)wj>Eqer6|uF+zcc( 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 50: raise FileNotFoundError(f'The folder "{folder}" could not be found') folder = os.path.join(os.pardir, folder) + if any([file_type in x.name for x in os.scandir(folder)]): return + elif folder == 'weights' and os.path.isdir(folder): + return - if any(['.ckpt' in element.name and element.is_dir() for element in os.scandir(folder)]): + if any(['weights.ckpt' in element.name and element.is_dir() for element in os.scandir(folder)]) and False: _, _, filenames = next(os.walk(os.path.join(folder, 'weights.ckpt'))) filenames.sort(key=lambda f: int(''.join(filter(str.isdigit, f)))) func(os.path.join(folder, 'weights.ckpt', filenames[-1])) @@ -37,7 +39,7 @@ def search_for_weights(func, folder, file_type='latent_space'): if os.path.exists(element): if element.is_dir(): search_for_weights(func, element.path, file_type=file_type) - elif element.is_file() and element.name.endswith('.ckpt'): + elif element.is_file() and element.name.endswith('weights.ckpt'): func(element.path) else: continue @@ -47,16 +49,15 @@ class Printer(object): def __init__(self, model: AbstractNeuralNetwork, ax=None): self.norm = mcolors.Normalize(vmin=0, vmax=1) - self.colormap = cmaps.gist_rainbow + self.colormap = cmaps.tab20 self.network = model self.fig = plt.figure(dpi=300) self.ax = ax if ax else plt.subplot(1, 1, 1) pass - def colorize(self, x, min_val: Union[float, None] = None, max_val: Union[float, None] = None, - colormap=cmaps.rainbow, **kwargs): + def colorize(self, x, min_val: Union[float, None] = None, max_val: Union[float, None] = None, **kwargs): norm = mcolors.Normalize(vmin=min_val, vmax=max_val) - colored = colormap(norm(x)) + colored = self.colormap(norm(x)) return colored @staticmethod @@ -79,20 +80,26 @@ class Printer(object): clusterer.init = np.asarray(centers) else: # clusterer = Birch(n_clusters=None) - clusterer = Birch() + clusterer = KMeans(3) labels = clusterer.fit_predict(data) print('Birch Clustering Sucessfull') return labels - def print_possible_latent_spaces(self, data: Trajectories, n: Union[int, str] = 1000, **kwargs): - predictions, _ = self._gather_predictions(data, n) + def print_possible_latent_spaces(self, data: Trajectories, n: Union[int, str] = 1000, + cluster_by_motion=True, **kwargs): + predictions, motion_sequence = self._gather_predictions(data, n) if len(predictions) >= 2: predictions += (torch.cat(predictions, dim=-1), ) - labels = self.cluster_data(predictions[-1]) + if cluster_by_motion: + motion_analyzer = MotionAnalyser() + labels = motion_analyzer.cluster_motion(motion_sequence) + else: + labels = self.cluster_data(predictions[-1]) + for idx, prediction in enumerate(predictions): - self.print_latent_space(prediction, labels, running_index=idx, **kwargs) + self.print_latent_space(prediction, labels.squeeze(), running_index=idx, **kwargs) def print_latent_space(self, prediction, labels, running_index=0, save=None): @@ -179,12 +186,13 @@ class Printer(object): print("Gathering Predictions") n = n if isinstance(n, int) and n else len(data) - (data.size * data.step) - idxs = np.random.choice(np.arange(len(data) - data.step * data.size), n, replace=False) + idxs = np.random.choice(np.arange(len(data)), n, replace=True) complete_data = torch.stack([data.get_both_by_key(idx) for idx in idxs], dim=0) segment_coords, trajectories = complete_data[:, :, :2], complete_data[:, :, 2:] if color_by_movement: motion_analyser = MotionAnalyser() - predictions = (motion_analyser.cluster_motion(segment_coords), ) + predictions = (motion_analyser.cluster_motion(segment_coords, + clustering=kwargs.get('clustering', 'kmeans')), ) else: with torch.no_grad(): @@ -193,7 +201,7 @@ class Printer(object): return predictions, segment_coords @staticmethod - def colorize_as_hsv(self, x, min_val: Union[float, None] = None, max_val: Union[float, None] = None, + def colorize_as_hsv(x, min_val: Union[float, None] = None, max_val: Union[float, None] = None, colormap=cmaps.rainbow, **kwargs): norm = mcolors.Normalize(vmin=min_val, vmax=max_val) colored = colormap(norm(x)) @@ -248,11 +256,12 @@ class Printer(object): patches = [Polygon(base_map[i], True, color='black') for i in range(len(base_map))] return PatchCollection(patches, color='black') - def print_trajec_on_basemap(self, data, base_map: Map, save=False, color_by_movement=False, **kwargs): + def print_trajec_on_basemap(self, data, base_map: Map, save=False, show=False, color_by_movement=False, **kwargs): """ :rtype: object """ + prediction_segments = self._gather_predictions(data, color_by_movement=color_by_movement, **kwargs) trajectory_shapes = self._build_trajectory_shapes(*prediction_segments, **kwargs) map_shapes = self._build_map_shapes(base_map) @@ -266,7 +275,8 @@ class Printer(object): self.save(save) else: self.save(base_map.name) - pass + if show: + self.show() @staticmethod def show(): @@ -284,15 +294,25 @@ class MotionAnalyser(object): pass def _sequential_pairwise_map(self, func, xy_sequence, on_deltas=False): - zipped_list = [x for x in zip(xy_sequence[:-1], xy_sequence[1:])] + if on_deltas: + zipped_list = [x for x in zip(xy_sequence[:-1], xy_sequence[1:])] zipped_list = [self.delta(*movement) for movement in zipped_list] else: - pass + zipped_list = xy_sequence return [func(*xy) for xy in zipped_list] + @staticmethod + def _rotatePoint(point, center, angle, is_rad=True): + + angle = (angle) * (pi / 180) if not is_rad else angle # Convert to radians if + rotatedX = cos(angle) * (point[0] - center[0]) - sin(angle) * (point[1] - center[1]) + center[0] + rotatedY = sin(angle) * (point[0] - center[0]) + cos(angle) * (point[1] - center[1]) + center[1] + + return rotatedX, rotatedY + @staticmethod def delta(x1y1, x2y2): x1, y1 = x1y1 @@ -306,10 +326,16 @@ class MotionAnalyser(object): return r @staticmethod - def get_theta(deltax, deltay, rad=False): + def get_theta(deltax, deltay, as_radians=True): # https://mathinsight.org/polar_coordinates + try: + deltax = torch.as_tensor(deltax) + deltay = torch.as_tensor(deltay) + except: + pass + theta = torch.atan2(deltay, deltax) - return theta if rad else theta * 180 / pi + return theta if as_radians else theta * 180 / pi def get_theta_for_sequence(self, xy_sequence): ts = self._sequential_pairwise_map(self.get_theta, xy_sequence, on_deltas=True) @@ -319,38 +345,90 @@ class MotionAnalyser(object): rs = self._sequential_pairwise_map(self.get_r, xy_sequence, on_deltas=True) return rs + def move_to_zero(self, xy_sequence): + old_origin = xy_sequence[0] + return xy_sequence - old_origin + def get_unique_seq_identifier(self, xy_sequence): + xy_sequence = xy_sequence.cpu() + + # Move all points so that the first point is always (0, 0) + # moved_sequence = self.move_to_zero(xy_sequence) + moved_sequence = xy_sequence + + # Rotate, so that x is zero for last point + angle = self.get_theta(*self.delta(moved_sequence[0], moved_sequence[1])) + rotated_sequence = torch.as_tensor([self._rotatePoint(point, moved_sequence[0], -angle) + for point in moved_sequence[1:]]) + rotated_sequence = torch.cat((moved_sequence[0].unsqueeze(0), rotated_sequence)) + # rotated_sequence = moved_sequence + std, mean = torch.std_mean(rotated_sequence) + rotated_sequence = (rotated_sequence - mean) / std + + def centroid_for(arr): + try: + arr = torch.as_tensor(arr) + except: + pass + size = arr.shape[0] + sum_x = torch.sum(arr[:, 0]) + sum_y = torch.sum(arr[:, 1]) + return sum_x/size, sum_y/size # Globals - global_delta = self.delta(xy_sequence[0], xy_sequence[-1]) - global_theta = self.get_theta(*global_delta) + global_delta = self.delta(rotated_sequence[0], rotated_sequence[-1]) global_r = self.get_r(*global_delta) + def f(*args): + return args + centroid = centroid_for(self._sequential_pairwise_map(f, rotated_sequence, on_deltas=True)) + + hull_length = sum(self.get_r_for_sequence(torch.cat((rotated_sequence, rotated_sequence[0].unsqueeze(0))))) + # For Each - theta_seq = self.get_theta_for_sequence(xy_sequence) + theta_seq = self.get_theta_for_sequence(rotated_sequence) mean_theta = sum(theta_seq) / len(theta_seq) theta_sum = sum([abs(theta) for theta in theta_seq]) std_theta = stdev(map(float, theta_seq)) - return torch.stack((global_r, torch.as_tensor(std_theta), mean_theta, global_theta)) + return torch.stack((centroid[0], centroid[1], torch.as_tensor(std_theta), mean_theta, theta_sum, hull_length)) - def cluster_motion(self, trajectory_samples, cluster_class=KMeans): - cluster_class = cluster_class(3) + def cluster_motion(self, trajectory_samples, clustering='kmeans'): + if clustering.lower() == 'kmeans': + cluster_class = KMeans(3) + std, mean = torch.std_mean(trajectory_samples, dim=0) + trajectory_samples = (trajectory_samples - mean) / std - std, mean = torch.std_mean(trajectory_samples, dim=0) - trajectory_samples = (trajectory_samples - mean) / std + unique_seq_identifiers = torch.stack([self.get_unique_seq_identifier(trajectory) + for trajectory in trajectory_samples]) - unique_seq_identifiers = torch.stack([self.get_unique_seq_identifier(trajectory) - for trajectory in trajectory_samples]) + clustered_movement = cluster_class.fit_predict(unique_seq_identifiers) + elif clustering.lower() == 'fastdtw': + # Move all points so that the first point is always (0, 0) + moved_sequence = self.move_to_zero(trajectory_samples) + rotated_sequences = [] + for sequence in moved_sequence: + # Rotate, so that x is zero for last point + angle = self.get_theta(*self.delta(sequence[0], sequence[1])) + rotated_sequence = torch.as_tensor([self._rotatePoint(point, sequence[0], -angle) + for point in sequence[1:]]) + rotated_sequence = torch.cat((sequence[0].unsqueeze(0), rotated_sequence)).unsqueeze(0) + rotated_sequences.append(rotated_sequence) + # deltas = [self._sequential_pairwise_map(self.delta, x, on_deltas=False) for x in rotated_sequence] + t = torch.cat(rotated_sequences) + # t = torch.as_tensor(deltas) + z = torch.zeros((t.shape[0], t.shape[0])) - clustered_movement = cluster_class.fit_predict(unique_seq_identifiers) - if False: - from sklearn.decomposition import PCA - p = PCA(2) - t = p.fit_transform(unique_seq_identifiers) - f = plt.figure() - plt.scatter(t[:, 0], t[:,1]) - plt.show() + import fastdtw + for idx, x in tqdm(enumerate(t), total=z.shape[0]): + for idy, y in enumerate(t): + z[idx, idy] = fastdtw.fastdtw(x, y)[0] + + from sklearn.cluster.hierarchical import AgglomerativeClustering + clusterer = KMeans(3) + clustered_movement = clusterer.fit_predict(z) + else: + raise NotImplementedError return clustered_movement.reshape(-1, 1) diff --git a/viz/viz_latent.py b/viz/viz_latent.py index ceaa132..4af008c 100644 --- a/viz/viz_latent.py +++ b/viz/viz_latent.py @@ -18,7 +18,6 @@ def load_and_predict(path_like_element): weights_path=path_like_element, tags_csv=os.path.join(base_dir, 'default', 'version_0', 'meta_tags.csv'), on_gpu=True if torch.cuda.is_available() else False, - # map_location=None ) print(f'... loading model named: "{model.name}" from timestamp: {splitpath[3]}') @@ -44,7 +43,7 @@ def load_and_predict(path_like_element): # Important: # Use all given valdiation samples, even if they relate to differnt maps. This is important since we want to have a # view on the complete latent space, not just in relation to a single basemap, which would be a major bias. - p.print_possible_latent_spaces(dataset, save=os.path.join(base_dir, f'latent_space')) + p.print_possible_latent_spaces(dataset, save=os.path.join(base_dir, f'latent_space'), cluster_by_motion=False) if __name__ == '__main__':