├── README.md ├── Supplementary Files ├── data_loader ├── augmentations.py └── dataset_loader.py ├── data_preprocess ├── data_preprocess_HAR.py ├── data_preprocess_SHAR.py ├── data_preprocess_UEA.py └── data_preprocess_wisdm.py ├── main.py ├── models ├── BYOL │ ├── mlp_head.py │ ├── resnet_base_network.py │ ├── train_BYOL.py │ ├── trainer.py │ └── utils.py ├── CPC │ ├── config.py │ ├── feature_loader.py │ ├── model.py │ ├── train_CPC.py │ └── transformers.py ├── IDFD │ ├── config_files │ │ ├── EigenWorms_Configs.py │ │ ├── FingerMovements_Configs.py │ │ ├── HAR_Configs.py │ │ ├── PenDigits_Configs.py │ │ ├── SHAR_Configs.py │ │ ├── epilepsy_Configs.py │ │ └── wisdm_Configs.py │ └── train_IDFD.py ├── MHCCL │ ├── framework.py │ └── train_MHCCL.py ├── PCL │ ├── builder.py │ └── train_PCL.py ├── SimCLR │ ├── loss.py │ ├── models.py │ ├── train_SimCLR.py │ └── utils.py ├── SwAV │ ├── hubconf.py │ ├── logger.py │ ├── resnet50.py │ ├── train_SwAV.py │ └── utils.py ├── TLoss │ ├── scikit_wrappers.py │ └── train_TLoss.py ├── TS2Vec │ ├── datautils.py │ ├── train_TS2Vec.py │ ├── ts2vec.py │ └── utils.py ├── TST │ ├── loss.py │ ├── optimizers.py │ ├── running.py │ ├── ts_transformer.py │ └── utils.py ├── TSTCC │ ├── TC.py │ ├── loss.py │ ├── model.py │ ├── train_TSTCC.py │ └── utils.py ├── TimeNet │ ├── framework.py │ └── train_TimeNet.py └── notes.txt ├── organization.png └── taxonomy.png /README.md: -------------------------------------------------------------------------------- 1 | # ULTS 2 | A unified and standardized library of unsupervised representation learning approaches for time series 3 | 4 | 5 | ## Description of this library: 6 | ULTS is a unified and standardized library under the PyTorch framework to enable quick and convenient evaluations on unsupervised representation learning approaches for time series. ULTS integrates 17 representative models covering 2 deep clustering, 2 reconstruction-based and 13 self-supervised learning methods including 2 adversarial, 2 predictive and 9 contrastive ones. For more information, please refer to our paper: [Unsupervised Representation Learning for Time Series: A Review](https://arxiv.org/abs/2308.01578). 7 | 8 | 9 | ## Abstract 10 | Unsupervised representation learning approaches aim to learn discriminative feature representations from unlabeled data, without the requirement of annotating every sample. Enabling unsupervised representation learning is extremely crucial for time series data, due to its unique annotation bottleneck caused by its complex characteristics and lack of visual cues compared with other data modalities. In recent years, unsupervised representation learning techniques have advanced rapidly in various domains. However, there is a lack of systematic analysis of unsupervised representation learning approaches for time series. To fill the gap, we conduct a comprehensive literature review of existing rapidly evolving unsupervised representation learning approaches for time series. Moreover, we also develop a unified and standardized library, named ULTS ({i.e., Unsupervised Learning for Time Series), to facilitate fast implementations and unified evaluations on various models. With ULTS, we empirically evaluate state-of-the-art approaches, especially the rapidly evolving contrastive learning methods, on 9 diverse real-world datasets. We further discuss practical considerations as well as open research challenges on unsupervised representation learning for time series to facilitate future research in this field. 11 | 12 | ## Taxonomy: 13 | ![image](https://github.com/mqwfrog/ULTS/blob/main/taxonomy.png) 14 | 15 | 16 | ## Organization: 17 | ![image](https://github.com/mqwfrog/ULTS/blob/main/organization.png) 18 | 19 | ## Models Implemented in ULTS: 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
1st Category2nd Category3rd CategoryModel (Official Implementations)
Deep Clustering Methods--DeepCluster https://github.com/facebookresearch/deepcluster
--IDFD https://github.com/TTN-YKK/Clustering_friendly_representation_learning
Reconstruction-based Methods--TimeNet https://github.com/paudan/TimeNet
--Deconv https://github.com/cauchyturing/Deconv_SAX
Self-supervised Learning MethodsAdversarial-TimeGAN https://github.com/jsyoon0823/TimeGAN
TimeVAE https://github.com/abudesai/timeVAE
Predictive-EEG-SSL https://github.com/mlberkeley/eeg-ssl
TST https://github.com/gzerveas/mvts_transformer
ContrastiveInstance-LevelSimCLR https://github.com/google-research/simclr
BYOL https://github.com/deepmind/deepmind-research/tree/master/byol
CPC https://github.com/facebookresearch/CPC_audio
Prototype-LevelSwAV https://github.com/facebookresearch/swav
PCL https://github.com/salesforce/PCL
MHCCL https://github.com/mqwfrog/MHCCL
Temporal-LevelTS2Vec https://github.com/yuezhihan/ts2vec
TS-TCC https://github.com/emadeldeen24/TS-TCC
T-Loss https://github.com/White-Link/UnsupervisedScalableRepresentationLearningTimeSeries
74 | 75 | ## Requirements for this library: 76 | - Python ≥ 3.6 77 | - PyTorch ≥ 1.4 78 | 79 | ## Required packages for this library: 80 | - numpy 81 | - sklearn 82 | - openpyxl 83 | - torchvision 84 | - random 85 | - copy 86 | - pandas 87 | - matplotlib 88 | - time 89 | - collections 90 | - scipy 91 | - pynndescent 92 | - builtins 93 | - math 94 | - shutil 95 | - os 96 | - sys 97 | - warnings 98 | - tqdm 99 | - argparse 100 | - tensorboard_logger 101 | 102 | 103 | ## Data: 104 | - The [UCI](https://archive.ics.uci.edu/datasets) archive includes 85 multivariate time series datasets for classification tasks. These datasets covers various application fields including audio spectra classification, business, ECG/EEG classification, human activity recognition, gas detection, motion classification, etc. 105 | - The [UEA](http://www.timeseriesclassification.com/dataset.php) archive includes 30 multivariate time series datasets, covers the application fields of audio spectra classification, ECG/EEG/MEG classification, human activity recognition, motion classification, etc. 106 | - The [MTS](http://www.mustafabaydogan.com/multivariate-time-series-discretization-for-classification.html) archive, also known as Baydogan's archive, includes 13 multivariate time series datasets, covers the application fields of audio spectra classification, ECG classification, human activity recognition, motion classification, etc. 107 | 108 | 109 | ## Codes: 110 | The codes in ULTS library are organized as follows: 111 | - The [main.py](https://github.com/mqwfrog/ULTS/blob/main/main.py) includes the training method for all models. 112 | - The [models](https://github.com/mqwfrog/ULTS/tree/main/models) folder contain all 17 unsupervised learning models. 113 | - The [data_preprocess](https://github.com/mqwfrog/ULTS/tree/main/data_preprocess) folder contain the codes to preprocess data from different archives. 114 | - The [data_loader](https://github.com/mqwfrog/ULTS/tree/main/data_loader) folder contains the codes to perform augmentation transformations and to load the dataset. 115 | 116 | 117 | ## Running: 118 |
119 | python main.py \
120 | --dataset_name wisdm \
121 | --uid SimCLR
122 | --lr 0.03 \
123 | --batch_size 128 \
124 | --feature_size 128
125 | 
126 | 127 | 128 | 129 | ## Results: 130 | - The experimental results will be saved in "experiment_{args.model}_{args.dataset}" directory by default. 131 | 132 | 133 | ## Citation: 134 | If you find any of the codes helpful, kindly cite our paper. 135 | 136 |
137 | @misc{meng2023unsupervised, 
138 |       title={Unsupervised Representation Learning for Time Series: A Review}, 
139 |       author={Qianwen Meng and Hangwei Qian and Yong Liu and Yonghui Xu and Zhiqi Shen and Lizhen Cui},   
140 |       year={2023},
141 |       eprint={2308.01578},
142 |       archivePrefix={arXiv},
143 |       primaryClass={cs.LG}
144 | }
145 | 
146 | 147 | ## References: 148 | Part of the codes are referenced from 149 | https://github.com/PatrickHua/SimSiam 150 | https://github.com/facebookresearch/deepcluster 151 | https://github.com/TTN-YKK/Clustering_friendly_representation_learning 152 | https://github.com/paudan/TimeNet 153 | https://github.com/cauchyturing/Deconv_SAX 154 | https://github.com/jsyoon0823/TimeGAN 155 | https://github.com/abudesai/timeVAE 156 | https://github.com/joergsimon/SSL-ECG-Paper-Reimplementaton 157 | https://github.com/mlberkeley/eeg-ssl 158 | https://github.com/gzerveas/mvts_transformer 159 | https://github.com/google-research/simclr 160 | https://github.com/deepmind/deepmind-research/tree/master/byol 161 | https://github.com/lucidrains/byol-pytorch 162 | https://github.com/facebookresearch/CPC_audio 163 | https://github.com/abhinavagarwalla/swav-cifar10 164 | https://github.com/facebookresearch/swav 165 | https://github.com/salesforce/PCL 166 | https://github.com/mqwfrog/MHCCL 167 | https://github.com/yuezhihan/ts2vec 168 | https://github.com/emadeldeen24/TS-TCC 169 | https://github.com/White-Link/UnsupervisedScalableRepresentationLearningTimeSeries 170 | -------------------------------------------------------------------------------- /Supplementary Files: -------------------------------------------------------------------------------- 1 | Some supplementary files 2 | -------------------------------------------------------------------------------- /data_loader/augmentations.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | from scipy.interpolate import CubicSpline 4 | 5 | def DataTransform(sample): 6 | 7 | aug_1 = scaling(sample, sigma=1.1) 8 | aug_2 = jitter(permutation(sample, max_segments=5, seg_mode="random"), sigma=0.8) 9 | 10 | # You can choose any augmentation transformations or combinations listed as follows: 11 | # x_jitter = jitter(sample, sigma=0.8) #sigma: [0.01, 0.5] 12 | # x_scaling = scaling(sample, sigma=1.1) #sigma: [0.1, 2.] 13 | # x_permutation = permutation(sample, max_segments=5, seg_mode="random") #max_segments: [3., 6.] 14 | # x_rotation = rotation(sample) 15 | # x_magnitude_warp = magnitude_warp(sample, sigma=0.2, knot=4) #sigma: [0.1, 2.], knot: [3, 5] 16 | # x_time_warp = time_warp(sample, sigma=0.2, knot=4) #sigma: [0.01, 0.5], knot: [3, 5] 17 | # x_window_slice = window_slice(sample, reduce_ratio=0.9) #reduce_ratio: [0.95, 0.6] 18 | 19 | return aug_1, aug_2 20 | 21 | def jitter(x, sigma=0.8): 22 | x_new = x + np.random.normal(loc=0., scale=sigma, size=x.shape) 23 | return x_new 24 | 25 | 26 | def scaling(x, sigma = 0.1): 27 | scaler = np.random.normal(loc=1.0, scale = sigma, size = (1, x.shape[1])) 28 | noise = np.matmul(np.ones((x.shape[2], 1)), scaler) 29 | x = x.permute(0,2,1) 30 | x_new = x.clone() 31 | for i in range(0, x.shape[0]): 32 | x_new[i] = x[i] * noise 33 | return x_new.permute(0,2,1) 34 | 35 | 36 | def permutation(x, max_segments=5, seg_mode="random"): 37 | orig_steps = np.arange(x.shape[2]) 38 | num_segs = np.random.randint(1, max_segments, size=(x.shape[0])) 39 | x_new = np.zeros_like(x) 40 | for i, pat in enumerate(x): 41 | if num_segs[i] > 1: 42 | if seg_mode == "random": 43 | split_points = np.random.choice(x.shape[2] - 2, num_segs[i] - 1, replace=False) 44 | split_points.sort() 45 | splits = np.split(orig_steps, split_points) 46 | else: 47 | splits = np.array_split(orig_steps, num_segs[i]) 48 | warp = np.concatenate(np.random.permutation(splits)).ravel() 49 | x_new[i] = pat[0,warp] 50 | else: 51 | x_new[i] = pat 52 | return torch.from_numpy(x_new) 53 | 54 | 55 | def rotation(x): 56 | flip = np.random.choice([-1, 1], size=(x.shape[0],x.shape[2])) 57 | rotate_axis = np.arange(x.shape[2]) 58 | np.random.shuffle(rotate_axis) 59 | x_new = torch.from_numpy(flip)[:,np.newaxis,:] * x[:,:,rotate_axis] 60 | return x_new 61 | 62 | 63 | def magnitude_warp(x, sigma=0.2, knot=4): 64 | orig_steps = np.arange(x.shape[1]) 65 | random_warps = np.random.normal(loc=1.0, scale=sigma, size=(x.shape[0], knot + 2, x.shape[2])) 66 | warp_steps = (np.ones((x.shape[2], 1)) * (np.linspace(0, x.shape[1] - 1., num=knot + 2))).T 67 | x_new = np.zeros_like(x) 68 | for i, pat in enumerate(x): 69 | li = [] 70 | for dim in range(x.shape[2]): 71 | li.append(CubicSpline(warp_steps[:, dim], random_warps[i, :, dim])(orig_steps)) 72 | warper = np.array(li).T 73 | x_new[i] = pat * warper 74 | return torch.from_numpy(x_new) 75 | 76 | 77 | def time_warp(x, sigma=0.2, knot=4): 78 | orig_steps = np.arange(x.shape[1]) 79 | random_warps = np.random.normal(loc=1.0, scale=sigma, size=(x.shape[0], knot + 2, x.shape[2])) 80 | warp_steps = (np.ones((x.shape[2], 1)) * (np.linspace(0, x.shape[1] - 1., num=knot + 2))).T 81 | x_new = np.zeros_like(x) 82 | for i, pat in enumerate(x): 83 | for dim in range(x.shape[2]): 84 | time_warp = CubicSpline(warp_steps[:, dim], warp_steps[:, dim] * random_warps[i, :, dim])(orig_steps) 85 | scale = (x.shape[1] - 1) / time_warp[-1] 86 | x_new[i, :, dim] = np.interp(orig_steps, np.clip(scale * time_warp, 0, x.shape[1] - 1), pat[:, dim]).T 87 | return torch.from_numpy(x_new) 88 | 89 | 90 | def window_slice(x, reduce_ratio=0.9): 91 | target_len = np.ceil(reduce_ratio*x.shape[1]).astype(int) 92 | if target_len >= x.shape[1]: 93 | return x 94 | starts = np.random.randint(low=0, high=x.shape[1]-target_len, size=(x.shape[0])).astype(int) 95 | ends = (target_len + starts).astype(int) 96 | x_new = np.zeros_like(x) 97 | for i, pat in enumerate(x): 98 | for dim in range(x.shape[2]): 99 | x_new[i,:,dim] = np.interp(np.linspace(0, target_len, num=x.shape[1]), np.arange(target_len), pat[starts[i]:ends[i],dim]).T 100 | return x_new 101 | 102 | -------------------------------------------------------------------------------- /data_loader/dataset_loader.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.utils.data import DataLoader 3 | from torch.utils.data import Dataset 4 | import os 5 | import numpy as np 6 | from .augmentations import DataTransform 7 | 8 | 9 | class Load_Dataset(Dataset): 10 | # Initialize your data, download, etc. 11 | def __init__(self, dataset, config, training_mode): 12 | super(Load_Dataset, self).__init__() 13 | self.training_mode = training_mode 14 | 15 | X_train = dataset["samples"] 16 | y_train = dataset["labels"] 17 | 18 | if len(X_train.shape) < 3: 19 | X_train = X_train.unsqueeze(2) 20 | 21 | if X_train.shape.index(min(X_train.shape)) != 1: # make sure the Channels in second dim 22 | X_train = X_train.permute(0, 2, 1) 23 | 24 | if isinstance(X_train, np.ndarray): 25 | self.x_data = torch.from_numpy(X_train) 26 | self.y_data = torch.from_numpy(y_train).long() 27 | else: 28 | self.x_data = X_train 29 | self.y_data = y_train 30 | 31 | self.len = X_train.shape[0] 32 | if training_mode == "self_supervised": # no need to apply Augmentations in other modes 33 | self.aug1, self.aug2 = DataTransform(self.x_data, config) 34 | 35 | def __getitem__(self, index): 36 | if self.training_mode == "self_supervised": 37 | return self.x_data[index], self.y_data[index], self.aug1[index], self.aug2[index], index 38 | else: 39 | return self.x_data[index], self.y_data[index], self.x_data[index], self.x_data[index], index 40 | 41 | def __len__(self): 42 | return self.len 43 | 44 | 45 | def data_generator_all(data_path, configs, training_mode): 46 | 47 | train_dataset = torch.load(os.path.join(data_path, "train.pt")) 48 | valid_dataset = torch.load(os.path.join(data_path, "val.pt")) 49 | test_dataset = torch.load(os.path.join(data_path, "test.pt")) 50 | 51 | 52 | train_dataset = Load_Dataset(train_dataset, configs, training_mode) 53 | valid_dataset = Load_Dataset(valid_dataset, configs, training_mode) 54 | test_dataset = Load_Dataset(test_dataset, configs, training_mode) 55 | 56 | # print(len(train_dataset)) # HAR: 7352 , wisdm: 2617 57 | # print(len(valid_dataset)) # HAR: 1471, wisdm: 655 58 | # print(len(test_dataset)) # HAR: 2947, wisdm: 819 59 | 60 | train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=configs.batch_size, 61 | shuffle=True, drop_last=configs.drop_last, 62 | num_workers=0) 63 | 64 | valid_loader = torch.utils.data.DataLoader(dataset=valid_dataset, batch_size=configs.batch_size, 65 | shuffle=False, drop_last=configs.drop_last, 66 | num_workers=0) 67 | 68 | test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=configs.batch_size, 69 | shuffle=False, drop_last=False, 70 | num_workers=0) 71 | 72 | return train_loader, valid_loader, test_loader 73 | 74 | 75 | def data_generator(data_path, configs, training_mode): 76 | 77 | train_dataset = torch.load(os.path.join(data_path, "train.pt")) 78 | test_dataset = torch.load(os.path.join(data_path, "test.pt")) 79 | 80 | 81 | train_dataset = Load_Dataset(train_dataset, configs, training_mode) 82 | test_dataset = Load_Dataset(test_dataset, configs, training_mode) 83 | 84 | train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=configs.batch_size, 85 | shuffle=True, drop_last=configs.drop_last, 86 | num_workers=0) 87 | 88 | test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=configs.batch_size, 89 | shuffle=False, drop_last=False, 90 | num_workers=0) 91 | 92 | return train_loader, test_loader 93 | -------------------------------------------------------------------------------- /data_preprocess/data_preprocess_HAR.py: -------------------------------------------------------------------------------- 1 | import random 2 | import torch 3 | import os 4 | import numpy as np 5 | from sklearn.model_selection import train_test_split 6 | 7 | data_dir = 'UCI HAR Dataset' 8 | output_dir = '../../data/HAR' 9 | 10 | subject_data = np.loadtxt(f'{data_dir}/train/subject_train.txt') 11 | # Samples 12 | train_acc_x = np.loadtxt(f'{data_dir}/train/Inertial Signals/body_acc_x_train.txt') 13 | train_acc_y = np.loadtxt(f'{data_dir}/train/Inertial Signals/body_acc_y_train.txt') 14 | train_acc_z = np.loadtxt(f'{data_dir}/train/Inertial Signals/body_acc_z_train.txt') 15 | train_gyro_x = np.loadtxt(f'{data_dir}/train/Inertial Signals/body_gyro_x_train.txt') 16 | train_gyro_y = np.loadtxt(f'{data_dir}/train/Inertial Signals/body_gyro_y_train.txt') 17 | train_gyro_z = np.loadtxt(f'{data_dir}/train/Inertial Signals/body_gyro_z_train.txt') 18 | train_tot_acc_x = np.loadtxt(f'{data_dir}/train/Inertial Signals/total_acc_x_train.txt') 19 | train_tot_acc_y = np.loadtxt(f'{data_dir}/train/Inertial Signals/total_acc_y_train.txt') 20 | train_tot_acc_z = np.loadtxt(f'{data_dir}/train/Inertial Signals/total_acc_z_train.txt') 21 | 22 | test_acc_x = np.loadtxt(f'{data_dir}/test/Inertial Signals/body_acc_x_test.txt') 23 | test_acc_y = np.loadtxt(f'{data_dir}/test/Inertial Signals/body_acc_y_test.txt') 24 | test_acc_z = np.loadtxt(f'{data_dir}/test/Inertial Signals/body_acc_z_test.txt') 25 | test_gyro_x = np.loadtxt(f'{data_dir}/test/Inertial Signals/body_gyro_x_test.txt') 26 | test_gyro_y = np.loadtxt(f'{data_dir}/test/Inertial Signals/body_gyro_y_test.txt') 27 | test_gyro_z = np.loadtxt(f'{data_dir}/test/Inertial Signals/body_gyro_z_test.txt') 28 | test_tot_acc_x = np.loadtxt(f'{data_dir}/test/Inertial Signals/total_acc_x_test.txt') 29 | test_tot_acc_y = np.loadtxt(f'{data_dir}/test/Inertial Signals/total_acc_y_test.txt') 30 | test_tot_acc_z = np.loadtxt(f'{data_dir}/test/Inertial Signals/total_acc_z_test.txt') 31 | 32 | # Stacking channels together data 33 | train_data = np.stack((train_acc_x, train_acc_y, train_acc_z, 34 | train_gyro_x, train_gyro_y, train_gyro_z, 35 | train_tot_acc_x, train_tot_acc_y, train_tot_acc_z), axis=1) 36 | X_test = np.stack((test_acc_x, test_acc_y, test_acc_z, 37 | test_gyro_x, test_gyro_y, test_gyro_z, 38 | test_tot_acc_x, test_tot_acc_y, test_tot_acc_z), axis=1) 39 | # labels 40 | train_labels = np.loadtxt(f'{data_dir}/train/y_train.txt') 41 | train_labels -= np.min(train_labels) 42 | y_test = np.loadtxt(f'{data_dir}/test/y_test.txt') 43 | y_test -= np.min(y_test) 44 | 45 | 46 | X_train, X_val, y_train, y_val = train_test_split(train_data, train_labels, test_size=0.2, random_state=42) 47 | 48 | dat_dict = dict() 49 | dat_dict["samples"] = torch.from_numpy(X_train) 50 | dat_dict["labels"] = torch.from_numpy(y_train) 51 | torch.save(dat_dict, os.path.join(output_dir, "train.pt")) 52 | 53 | dat_dict = dict() 54 | dat_dict["samples"] = torch.from_numpy(X_val) 55 | dat_dict["labels"] = torch.from_numpy(y_val) 56 | torch.save(dat_dict, os.path.join(output_dir, "val.pt")) 57 | 58 | dat_dict = dict() 59 | dat_dict["samples"] = torch.from_numpy(X_test) 60 | dat_dict["labels"] = torch.from_numpy(y_test) 61 | torch.save(dat_dict, os.path.join(output_dir, "test.pt")) 62 | -------------------------------------------------------------------------------- /data_preprocess/data_preprocess_SHAR.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | import torch 5 | from scipy.io import loadmat 6 | from pathlib import Path 7 | from typing import List, Tuple, Union, Optional 8 | from sklearn.model_selection import train_test_split 9 | from data_preprocessing.base import BaseDataset, check_path 10 | 11 | __all__ = ['UniMib', 'load', 'load_raw'] 12 | 13 | # Meta Info 14 | SUBJECTS = tuple(range(1, 30+1)) 15 | ACTIVITIES = tuple(['StandingUpFS', 'StandingUpFL', 'Walking', 'Running', 'GoingUpS', 'Jumping', 'GoingDownS', 'LyingDownFS', 'SittingDown', 'FallingForw', 'FallingRight', 'FallingBack', 'HittingObstacle', 'FallingWithPS', 'FallingBackSC', 'Syncope', 'FallingLeft']) 16 | GENDER = {'M': 0, 'F': 1} 17 | Sampling_Rate = 50 # Hz 18 | 19 | class UniMib(BaseDataset): 20 | def __init__(self, path:Path): 21 | super().__init__(path) 22 | 23 | def load(self, data_type:str, window_size:Optional[int]=None, stride:Optional[int]=None, ftrim_sec:int=3, btrim_sec:int=3, subjects:Optional[list]=None) -> Tuple[np.ndarray, np.ndarray]: 24 | if data_type != 'raw': 25 | data, meta = load(path=self.path, data_type=data_type) 26 | segments = {'acceleration': data, 'activity': meta['activity'], 'subject': meta['subject']} 27 | x = np.stack(segments['acceleration']).transpose(0, 2, 1) 28 | y = np.stack(segments['activity']) 29 | x_frames = x 30 | y -= np.min(y) 31 | y_frames = y 32 | 33 | # subject filtering 34 | if subjects is not None: 35 | flags = np.zeros(len(x_frames), dtype=bool) 36 | for sub in subjects: 37 | flags = np.logical_or(flags, y_frames[:, 1] == sub) 38 | x_frames = x_frames[flags] 39 | y_frames = y_frames[flags] 40 | return x_frames, y_frames 41 | 42 | 43 | def load(path:Union[Path,str], data_type:str='full') -> Tuple[List[pd.DataFrame], pd.DataFrame]: 44 | path = check_path(path) 45 | raw = load_raw(path, data_type) 46 | data, meta = reformat(raw) 47 | return data, meta 48 | 49 | 50 | def load_raw(path:Path, data_type:str = 'full') -> Union[Tuple[np.ndarray, pd.DataFrame], Tuple[List[pd.DataFrame], pd.DataFrame]]: 51 | if not isinstance(data_type, str): 52 | raise TypeError('expected type of "type" argument is str, but {}'.format(type(data_type))) 53 | if data_type not in ['full', 'adl', 'fall']: 54 | raise ValueError('unknown data type, {}'.format(data_type)) 55 | if data_type == 'full': 56 | prefix = 'acc' 57 | elif data_type == 'adl': 58 | prefix = 'adl' 59 | elif data_type == 'fall': 60 | prefix = 'fall' 61 | if data_type != 'raw': 62 | data = loadmat(str(path / f'{prefix}_data.mat'))[f'{prefix}_data'].reshape([-1, 3, 151]) 63 | labels = loadmat(str(path / f'{prefix}_labels.mat'))[f'{prefix}_labels'] 64 | meta = labels 65 | meta = pd.DataFrame(meta, columns=['activity', 'subject', 'trial_id']) 66 | meta = meta.astype({'activity': np.int8, 'subject': np.int8, 'trial_id': np.int8}) 67 | return data, meta 68 | 69 | 70 | def reformat(raw) -> Tuple[List[pd.DataFrame], pd.DataFrame]: 71 | data, meta = raw 72 | data = list(map(lambda x: pd.DataFrame(x.T, columns=['x', 'y', 'z']), data)) 73 | return data, meta 74 | 75 | 76 | if __name__ == '__main__': 77 | 78 | output_dir = r'../../data/SHAR' 79 | unimib_path = Path('./data') 80 | unimib = UniMib(unimib_path) 81 | x, y = unimib.load(data_type='full') 82 | 83 | X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42) 84 | X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=42) 85 | 86 | dat_dict = dict() 87 | dat_dict["samples"] = torch.from_numpy(X_train) 88 | dat_dict["labels"] = torch.from_numpy(y_train) 89 | torch.save(dat_dict, os.path.join(output_dir, "train.pt")) 90 | 91 | dat_dict = dict() 92 | dat_dict["samples"] = torch.from_numpy(X_val) 93 | dat_dict["labels"] = torch.from_numpy(y_val) 94 | torch.save(dat_dict, os.path.join(output_dir, "val.pt")) 95 | 96 | dat_dict = dict() 97 | dat_dict["samples"] = torch.from_numpy(X_test) 98 | dat_dict["labels"] = torch.from_numpy(y_test) 99 | torch.save(dat_dict, os.path.join(output_dir, "test.pt")) 100 | -------------------------------------------------------------------------------- /data_preprocess/data_preprocess_UEA.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pickle 3 | import numpy as np 4 | import torch 5 | from tqdm import tqdm 6 | from sktime.utils.data_io import load_from_arff_to_dataframe 7 | from sklearn.preprocessing import LabelEncoder 8 | 9 | # original UEA(0,1,2) [instances, length, features/channels] 10 | # UEA(0,1,2) --> later will be permuted in dataloader-->get UEA(0,2,1) [instances, features/channels, length] 11 | 12 | DATA_DIR = '../data' 13 | def mkdir_if_not_exists(loc, file=False): 14 | loc_ = os.path.dirname(loc) if file else loc 15 | if not os.path.exists(loc): 16 | os.makedirs(loc_, exist_ok=True) 17 | 18 | def create_torch_data(train_file, test_file): 19 | # Get arff format 20 | train_data, train_labels = load_from_arff_to_dataframe(train_file) 21 | test_data, test_labels = load_from_arff_to_dataframe(test_file) 22 | 23 | def convert_data(data): 24 | # Expand the series to numpy 25 | data_expand = data.applymap(lambda x: x.values).values 26 | # Single array, then to tensor 27 | data_numpy = np.stack([np.vstack(x).T for x in data_expand]) 28 | tensor_data = torch.Tensor(data_numpy) 29 | return tensor_data 30 | 31 | train_data, test_data = convert_data(train_data), convert_data(test_data) 32 | 33 | # Encode labels as often given as strings 34 | encoder = LabelEncoder().fit(train_labels) 35 | train_labels, test_labels = encoder.transform(train_labels), encoder.transform(test_labels) 36 | train_labels, test_labels = torch.Tensor(train_labels), torch.Tensor(test_labels) 37 | 38 | return train_data, test_data, train_labels, test_labels 39 | def save_pickle(obj, filename, protocol=4, create_folder=True): 40 | if create_folder: 41 | mkdir_if_not_exists(filename, file=True) 42 | 43 | # Save 44 | with open(filename, 'wb') as file: 45 | pickle.dump(obj, file, protocol=protocol) 46 | 47 | def convert_all_files(dataset='uea'): 48 | """ Convert all files from a given /raw/{subfolder} into torch data to be stored in /interim. """ 49 | assert dataset in ['uea', 'ucr'] 50 | folder = 'UEA' 51 | arff_folder = DATA_DIR + '/raw/{}/Multivariate_arff'.format(folder) 52 | 53 | # Time for a big for loop 54 | for ds_name in tqdm([x for x in os.listdir(arff_folder) if os.path.isdir(arff_folder + '/' + x)]): 55 | # File locations 56 | print(f'ds_name:{ds_name}') 57 | train_file = arff_folder + '/{}/{}_TRAIN.arff'.format(ds_name, ds_name) 58 | test_file = arff_folder + '/{}/{}_TEST.arff'.format(ds_name, ds_name) 59 | 60 | # Ready save dir 61 | save_dir = DATA_DIR + '/processed/{}/{}'.format(folder, ds_name) 62 | print(f'save_dir:{save_dir}') 63 | # If files don't exist, skip. 64 | if any([x.split('/')[-1] not in os.listdir(arff_folder + '/{}'.format(ds_name)) for x in (train_file, test_file)]): 65 | if ds_name not in ['Images', 'Descriptions']: 66 | print('No files found for folder: {}'.format(ds_name)) 67 | continue 68 | # elif os.path.isdir(save_dir): 69 | # print('Files already exist for: {}'.format(ds_name)) 70 | # continue 71 | else: 72 | train_data, test_data, train_labels, test_labels = create_torch_data(train_file, test_file) 73 | 74 | dat_dict = dict() 75 | dat_dict["samples"] = train_data 76 | dat_dict["labels"] = train_labels 77 | torch.save(dat_dict, ds_name+"_train.pt") 78 | 79 | dat_dict = dict() 80 | dat_dict["samples"] = test_data 81 | dat_dict["labels"] = test_labels 82 | torch.save(dat_dict, ds_name+"_test.pt") 83 | 84 | # # Compile train and test data together 85 | # data = torch.cat([train_data, test_data]) 86 | # labels = torch.cat([train_labels, test_labels]) 87 | # 88 | # # Save original train test indexes in case we wish to use original splits 89 | # original_idxs = (np.arange(0, train_data.size(0)), np.arange(train_data.size(0), data.size(0))) 90 | 91 | # # Save data 92 | # save_pickle(data, save_dir + '/data.pkl') 93 | # save_pickle(labels, save_dir + '/labels.pkl') 94 | # save_pickle(original_idxs, save_dir + '/original_idxs.pkl') 95 | 96 | 97 | if __name__ == '__main__': 98 | dataset = 'uea' 99 | convert_all_files(dataset) 100 | -------------------------------------------------------------------------------- /data_preprocess/data_preprocess_wisdm.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | import re 4 | import torch 5 | import numpy as np 6 | import pandas as pd 7 | import pickle 8 | import scipy 9 | import scipy.interpolate 10 | import pathlib 11 | import typing 12 | import errno 13 | from pathlib import Path 14 | from typing import Optional, Union, List, Tuple 15 | from sklearn.model_selection import train_test_split 16 | 17 | __all__ = ['WISDM', 'load', 'load_raw'] 18 | 19 | # Meta Info 20 | SUBJECTS = tuple(range(1, 36+1)) 21 | ACTIVITIES = tuple(['Walking', 'Jogging', 'Sitting', 'Standing', 'Upstairs', 'Downstairs']) 22 | Sampling_Rate = 20 # Hz 23 | 24 | class BaseDataset(object): 25 | def __init__(self, path: Path): 26 | self.path = path 27 | def load(self, *args): 28 | raise NotImplementedError 29 | 30 | class WISDM(BaseDataset): 31 | def __init__(self, path:Path): 32 | super().__init__(path) 33 | 34 | def load(self, window_size:int, stride:int, ftrim_sec:int=3, btrim_sec:int=3, subjects:Optional[list]=None) -> Tuple[np.ndarray, np.ndarray]: 35 | segments, meta = load(path=self.path) 36 | segments = [m.join(seg) for seg, m in zip(segments, meta)] 37 | 38 | x_frames, y_frames = [], [] 39 | for seg in segments: 40 | fs = split_using_sliding_window( 41 | np.array(seg), window_size=window_size, stride=stride, 42 | ftrim=Sampling_Rate*ftrim_sec, btrim=Sampling_Rate*btrim_sec, 43 | return_error_value=None) 44 | if fs is not None: 45 | x_frames += [fs[:, :, 3:]] 46 | y_frames += [np.uint8(fs[:, 0, 1:2][..., ::-1])] 47 | else: 48 | pass 49 | x_frames = np.concatenate(x_frames).transpose([0, 2, 1]) 50 | y_frames = np.concatenate(y_frames) 51 | y_frames -= np.min(y_frames) 52 | y_frames = y_frames.squeeze(1) 53 | 54 | # subject filtering 55 | if subjects is not None: 56 | flags = np.zeros(len(x_frames), dtype=bool) 57 | for sub in subjects: 58 | flags = np.logical_or(flags, y_frames[:, 1] == sub) 59 | x_frames = x_frames[flags] 60 | y_frames = y_frames[flags] 61 | 62 | return x_frames, y_frames 63 | 64 | def check_path(path: Union[Path, str]) -> Path: 65 | if isinstance(path, str): 66 | path = Path(path) 67 | elif not isinstance(path, Path): 68 | raise TypeError('expected type of "path" is Path or str, but {}'.format(type(path))) 69 | if not path.exists(): 70 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), str(path)) 71 | return path 72 | 73 | def to_frames_using_reshape(src: np.ndarray, window_size: int) -> np.ndarray: 74 | num_frames = (src.shape[0] - window_size) // window_size + 1 75 | ret = src[:(num_frames * window_size)] 76 | return ret.reshape(-1, window_size, *src.shape[1:]) 77 | 78 | def to_frames(src: np.ndarray, window_size: int, stride: int, stride_mode: str = 'index') -> np.ndarray: 79 | assert stride > 0, 'stride={}'.format(stride) 80 | assert stride_mode in ['index', 'nptrick'], "stride_mode is 'index' or 'nptrick'. stride_mode={}".format( 81 | stride_mode) 82 | if stride == window_size: 83 | return to_frames_using_reshape(src, window_size) 84 | elif stride_mode == 'index': 85 | return to_frames_using_index(src, window_size, stride) 86 | else: 87 | return to_frames_using_nptricks(src, window_size, stride) 88 | 89 | def to_frames_using_index(src: np.ndarray, window_size: int, stride: int) -> np.ndarray: 90 | assert stride > 0, 'stride={}'.format(stride) 91 | num_frames = (len(src) - window_size) // stride + 1 92 | idx = np.arange(window_size).reshape(-1, window_size).repeat(num_frames, axis=0) + np.arange( 93 | num_frames).reshape(num_frames, 1) * stride 94 | return src[idx] 95 | 96 | def to_frames_using_nptricks(src: np.ndarray, window_size: int, stride: int) -> np.ndarray: 97 | 98 | assert stride > 0, 'stride={}'.format(stride) 99 | num_frames = (src.shape[0] - window_size) // stride + 1 100 | ret_shape = (num_frames, window_size, *src.shape[1:]) 101 | strides = (stride * src.strides[0], *src.strides) 102 | return np.lib.stride_tricks.as_strided(src, shape=ret_shape, strides=strides) 103 | 104 | def split_using_sliding_window(segment: np.ndarray, **options) -> np.ndarray: 105 | assert len(segment.shape) == 2, "Segment's shape is (segment_size, ch). This segment shape is {}".format( 106 | segment.shape) 107 | window_size = options.pop('window_size', 512) 108 | stride = options.pop('stride', None) 109 | ftrim = options.pop('ftrim', 5) 110 | btrim = options.pop('btrim', 5) 111 | return_error_value = options.pop('return_error_value', None) 112 | assert not bool(options), "args error: key {} is not exist.".format(list(options.keys())) 113 | assert type(window_size) is int, "type(window_size) is int: {}".format(type(window_size)) 114 | assert ftrim >= 0 and btrim >= 0, "ftrim >= 0 and btrim >= 0: ftrim={}, btrim={}".format(ftrim, btrim) 115 | if type(segment) is not np.ndarray: 116 | return return_error_value 117 | if len(segment) < ftrim + btrim: 118 | return return_error_value 119 | if btrim == 0: 120 | seg = segment[ftrim:].copy() 121 | else: 122 | seg = segment[ftrim: -btrim].copy() 123 | if len(seg) < window_size: 124 | return return_error_value 125 | if stride is None: 126 | stride = window_size 127 | return to_frames(seg, window_size, stride, stride_mode='index') 128 | 129 | def split_using_target(src: np.ndarray, target: np.ndarray) -> typing.Dict[int, typing.List[np.ndarray]]: 130 | from collections import defaultdict 131 | rshifted = np.roll(target, 1) 132 | diff = target - rshifted 133 | diff[0] = 1 134 | idxes = np.where(diff != 0)[0] 135 | 136 | ret = defaultdict(list) 137 | for i in range(1, len(idxes)): 138 | ret[target[idxes[i - 1]]].append(src[idxes[i - 1]:idxes[i]].copy()) 139 | ret[target[idxes[-1]]].append(src[idxes[-1]:].copy()) 140 | return dict(ret) 141 | 142 | def interpolate(src: np.ndarray, rate: int, kind: str = 'linear', axis: int = -1) -> np.ndarray: 143 | N = src.shape[axis] 144 | x_low = np.linspace(0, 1, N) 145 | x_target = np.linspace(0, 1, N + (N - 1) * (rate - 1)) 146 | f = scipy.interpolate.interp1d(x_low, src, kind=kind, axis=axis) 147 | return f(x_target) 148 | 149 | def pickle_dump(obj: typing.Any, path: typing.Union[str, pathlib.Path]) -> None: 150 | with open(path, mode='wb') as f: 151 | pickle.dump(obj, f) 152 | return 153 | 154 | def pickle_load(path: pathlib.Path) -> typing.Any: 155 | with open(path, mode='rb') as f: 156 | data = pickle.load(f) 157 | return data 158 | 159 | def load(path:Union[Path,str]) -> Tuple[List[pd.DataFrame], List[pd.DataFrame]]: 160 | path = check_path(path) 161 | raw = load_raw(path) 162 | data, meta = reformat(raw) 163 | return data, meta 164 | 165 | def load_raw(path:Path) -> pd.DataFrame: 166 | path = path / 'WISDM_ar_v1.1_raw.txt' 167 | with path.open('r') as fp: 168 | whole_str = fp.read() 169 | 170 | whole_str = whole_str.replace(',;', ';') 171 | semi_separated = re.split('[;\n]', whole_str) 172 | semi_separated = list(filter(lambda x: x != '', semi_separated)) 173 | comma_separated = [r.strip().split(',') for r in semi_separated] 174 | 175 | # debug 176 | for s in comma_separated: 177 | if len(s) != 6: 178 | print('[miss format?]: {}'.format(s)) 179 | 180 | raw_data = pd.DataFrame(comma_separated) 181 | raw_data.columns = ['user', 'activity', 'timestamp', 'x-acceleration', 'y-acceleration', 'z-acceleration'] 182 | raw_data['z-acceleration'] = raw_data['z-acceleration'].replace('', np.nan) 183 | 184 | # convert activity name to activity id 185 | raw_data = raw_data.replace(list(ACTIVITIES), list(range(len(ACTIVITIES)))) 186 | 187 | raw_data = raw_data.astype({'user': 'uint8', 'activity': 'uint8', 'timestamp': 'uint64', 'x-acceleration': 'float64', 'y-acceleration': 'float64', 'z-acceleration': 'float64'}) 188 | raw_data[['x-acceleration', 'y-acceleration', 'z-acceleration']] = raw_data[['x-acceleration', 'y-acceleration', 'z-acceleration']].fillna(method='ffill') 189 | 190 | return raw_data 191 | 192 | 193 | def reformat(raw) -> Tuple[List[pd.DataFrame], List[pd.DataFrame]]: 194 | raw_array = raw.to_numpy() 195 | 196 | # segment (by user and activity) 197 | sdata_splited_by_subjects = split_using_target(src=raw_array, target=raw_array[:, 0]) 198 | segments = [] 199 | for sub_id in sdata_splited_by_subjects.keys(): 200 | for src in sdata_splited_by_subjects[sub_id]: 201 | splited = split_using_target(src=src, target=src[:, 1]) 202 | for act_id in splited.keys(): 203 | segments += splited[act_id] 204 | 205 | segments = list(map(lambda seg: pd.DataFrame(seg, columns=raw.columns).astype(raw.dtypes.to_dict()), segments)) 206 | data = list(map(lambda seg: pd.DataFrame(seg.iloc[:, 3:], columns=raw.columns[3:]), segments)) 207 | meta = list(map(lambda seg: pd.DataFrame(seg.iloc[:, :3], columns=raw.columns[:3]), segments)) 208 | 209 | return data, meta 210 | 211 | if __name__ == '__main__': 212 | 213 | output_dir = r'../../data/wisdm' 214 | 215 | wisdm_path = Path('./') 216 | wisdm = WISDM(wisdm_path) 217 | 218 | x, y = wisdm.load(window_size=256, stride=256, ftrim_sec=0, btrim_sec=0) 219 | 220 | X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42) 221 | X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=42) 222 | 223 | train_sample_num = int(1.0 * len(X_train)) # select x% data 224 | train_sample_list = [i for i in range(len(X_train))] 225 | train_sample_list = random.sample(train_sample_list, train_sample_num) 226 | X_train = X_train[train_sample_list, :] 227 | y_train = y_train[train_sample_list] 228 | 229 | test_sample_num = int(1.0 * len(X_test)) # select x% data 230 | test_sample_list = [i for i in range(len(X_test))] 231 | test_sample_list = random.sample(test_sample_list, test_sample_num) 232 | X_test = X_test[test_sample_list, :] 233 | y_test = y_test[test_sample_list] 234 | 235 | dat_dict = dict() 236 | dat_dict["samples"] = torch.from_numpy(X_train) 237 | dat_dict["labels"] = torch.from_numpy(y_train) 238 | torch.save(dat_dict, os.path.join(output_dir, "train.pt")) 239 | 240 | dat_dict = dict() 241 | dat_dict["samples"] = torch.from_numpy(X_val) 242 | dat_dict["labels"] = torch.from_numpy(y_val) 243 | torch.save(dat_dict, os.path.join(output_dir, "val.pt")) 244 | 245 | dat_dict = dict() 246 | dat_dict["samples"] = torch.from_numpy(X_test) 247 | dat_dict["labels"] = torch.from_numpy(y_test) 248 | torch.save(dat_dict, os.path.join(output_dir, "test.pt")) 249 | 250 | 251 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import torch 3 | 4 | from models.SimCLR.train_SimCLR import * 5 | from models.BYOL.train_BYOL import * 6 | from models.CPC.train_CPC import * 7 | from models.SwAV.train_SwAV import * 8 | from models.PCL.train_PCL import * 9 | from models.MHCCL.train_MHCCL import * 10 | from models.TS2Vec.train_TS2Vec import * 11 | from models.TSTCC.train_TSTCC import * 12 | from models.TLoss.train_TLoss import * 13 | from models.TST.train_TST import * 14 | from models.train_DeepCluster import * 15 | from models.train_IDFD import * 16 | 17 | 18 | parser = argparse.ArgumentParser() 19 | 20 | # models 21 | parser.add_argument('--uid', type=str, default='SimCLR', 22 | help='Staging identifier (default: SimCLR)') 23 | 24 | # datasets 25 | parser.add_argument('--dataset_name', default='wisdm', type=str, 26 | help='Choice of Datasets: wisdm, epilepsy, HAR, SHAR, etc.') 27 | parser.add_argument('--feature_size', type=int, default=128, 28 | help='Feature output size (default: 128') 29 | parser.add_argument('--batch_size', type=int, default=64, metavar='N', 30 | help='input training batch-size') 31 | 32 | # hyperparameters 33 | parser.add_argument('--accumulation_steps', type=int, default=4, metavar='N', 34 | help='Gradient accumulation steps (default: 4') 35 | parser.add_argument('--epochs', type=int, default=5, metavar='N', 36 | help='number of training epochs (default: 150)') 37 | parser.add_argument('--seed', default=0, type=int, 38 | help='seed value') 39 | parser.add_argument('--lr', type=float, default=1e-3, 40 | help='learning rate (default: 1e-3') 41 | parser.add_argument("--decay_lr", default=1e-6, action="store", type=float, 42 | help='Learning rate decay (default: 1e-6') 43 | parser.add_argument('--tau', default=0.5, type=float, 44 | help='Tau temperature smoothing (default 0.5)') 45 | 46 | # gpu 47 | parser.add_argument('--no_cuda', action='store_true', default=False, 48 | help='disables cuda (default: False') 49 | parser.add_argument('--device_id', type=int, default=0, 50 | help='GPU device id (default: 0') 51 | 52 | # directions 53 | parser.add_argument('--data_dir', type=str, default='data', 54 | help='Path to dataset (default: data') 55 | parser.add_argument('--exp_dir', default='exp1', type=str, 56 | help='Logs and checkpoints of experiments') 57 | 58 | # modes 59 | parser.add_argument('--training_mode', default='self_supervised', type=str, 60 | help='Modes of choice: random_init, supervised, self_supervised, fine_tune, train_linear') 61 | parser.add_argument('--load_model', type=str, default=None, 62 | help='Load model to resume training for (default None)') 63 | 64 | # SwAV 65 | parser.add_argument("--epsilon", default=0.05, type=float, 66 | help="regularization parameter for Sinkhorn-Knopp algorithm") 67 | parser.add_argument("--sinkhorn_iterations", default=3, type=int, 68 | help="number of iterations in Sinkhorn-Knopp algorithm") 69 | parser.add_argument("--nmb_prototypes", default=1000, type=int, 70 | help="number of prototypes") 71 | parser.add_argument("--queue_length", type=int, default=0, 72 | help="length of the queue (0 for no queue)") 73 | parser.add_argument("--epoch_queue_starts", type=int, default=15, 74 | help="from this epoch, we start using a queue") 75 | parser.add_argument("--final_lr", type=float, default=0.0006, help="final learning rate") 76 | parser.add_argument("--freeze_prototypes_niters", default=900, type=int, 77 | help="freeze the prototypes during this many iterations from the start") 78 | parser.add_argument("--warmup_epochs", default=10, type=int, help="number of warmup epochs to only train with InfoNCE loss") 79 | parser.add_argument("--start_warmup", default=0, type=float, 80 | help="initial warmup learning rate") 81 | parser.add_argument("--dist_url", default="env://", type=str, help="""url used to set up distributed 82 | training; see https://pytorch.org/docs/stable/distributed.html""") 83 | parser.add_argument("--world_size", default=-1, type=int, help=""" 84 | number of processes: it is set automatically and 85 | should not be passed as argument""") 86 | parser.add_argument("--rank", default=0, type=int, help="""rank of this process: 87 | it is set automatically and should not be passed as argument""") 88 | parser.add_argument("--local_rank", default=0, type=int, 89 | help="this argument is not used and should be ignored") 90 | parser.add_argument("--arch", default="resnet50", type=str, help="convnet architecture") 91 | parser.add_argument("--hidden_mlp", default=64, type=int, 92 | help="hidden layer dimension in projection head") #2048->64 93 | parser.add_argument("--workers", default=10, type=int, 94 | help="number of data loading workers") 95 | parser.add_argument("--checkpoint_freq", type=int, default=25, 96 | help="Save the model periodically") 97 | parser.add_argument("--use_fp16", type=bool_flag, default=True, 98 | help="whether to train with mixed precision or not") 99 | parser.add_argument("--sync_bn", type=str, default="pytorch", help="synchronize bn") 100 | 101 | # PCL 102 | parser.add_argument('--master_port', type=str, default='29501', 103 | help='avoid address already in use') 104 | parser.add_argument('--schedule', default=[120, 160], nargs='*', type=int, 105 | help='learning rate schedule (when to drop lr by 10x)') 106 | parser.add_argument('--print_freq', default=100, type=int, 107 | metavar='N', help='print frequency (default: 10)') 108 | parser.add_argument('--multiprocessing_distributed', action='store_true', 109 | help='Use multi-processing distributed training to launch ' 110 | 'N processes per node, which has N GPUs. This is the ' 111 | 'fastest way to use PyTorch for either single node or ' 112 | 'multi node data parallel training') 113 | parser.add_argument('--dist_backend', default='nccl', type=str, 114 | help='distributed backend') 115 | parser.add_argument('--pcl_r', default=4, type=int, 116 | help='queue size; number of negative pairs; needs to be smaller than num_cluster (default: 16384)') 117 | parser.add_argument('--momentum', default=0.9, type=float, metavar='M', 118 | help='momentum of SGD solver') 119 | parser.add_argument('--moco_m', default=0.999, type=float, 120 | help='moco momentum of updating key encoder (default: 0.999)') 121 | parser.add_argument('--mlp', action='store_true', 122 | help='use mlp head') 123 | parser.add_argument('--cos', action='store_true', 124 | help='use cosine lr schedule') 125 | parser.add_argument('--num_cluster', default='6,12,24', type=str, 126 | help='number of clusters') 127 | 128 | 129 | # MHCCL 130 | parser.add_argument('--posi', default=3, type=int, 131 | help='number of positive instance pairs (default: 3)') 132 | parser.add_argument('--negi', default=4, type=int, 133 | help='number of negative instance pairs(default: 4)') 134 | parser.add_argument('--posp', default=3, type=int, 135 | help='number of positive prototype pairs (default: 3)') 136 | parser.add_argument('--negp', default=4, type=int, 137 | help='number of negative prototype pairs(default: 4)') 138 | parser.add_argument('--tempi', default=0.2, type=float, 139 | help='softmax temperature for instances') 140 | parser.add_argument('--tempp', default=0.3, type=float, 141 | help='softmax temperature for prototypes') 142 | parser.add_argument('--layers', default=3, type=int, 143 | help='save the results of bottom # layers (default 3 for wisdm)') 144 | parser.add_argument('--req_clust', default=None, type=int, 145 | help='specify the number of clusters ') 146 | parser.add_argument('--protoNCE_only', action="store_true", 147 | help='use protoNCE loss only ') 148 | parser.add_argument('--mask_layer0', action="store_true", 149 | help='mask points and recompute centroids at the bottom layer 0') 150 | parser.add_argument('--mask_others', action="store_true", 151 | help='mask points and recompute centroids at all top layers') 152 | parser.add_argument('--replace_centroids', action="store_true", 153 | help='replace computed prototypes with raw data') 154 | parser.add_argument('--usetemp', action="store_true", 155 | help='adopt temperature in loss') 156 | parser.add_argument('--mask_mode', default='mask_farthest', type=str, choices=['mask_farthest', 'mask_threshold', 'mask_proportion'], 157 | help='select the mask mode (default: mask_farthest, other values:' 158 | 'mask_threshold(if use, specify the dist_threshold), ' 159 | 'mask_proportion(if use, specify the proportion') 160 | parser.add_argument('--dist_threshold', default=0.3, type=float, 161 | help='specify the distance threshold beyond which points will be masked ' 162 | 'when select the mask_threshold mode') 163 | parser.add_argument('--proportion', default=0.5, type=float, 164 | help='specify the proportion of how much points far from the centroids will be masked ' 165 | 'when select the mask_proportion mode') 166 | 167 | 168 | # TS2Vec 169 | parser.add_argument('--archive', type=str, required=True, help='The archive name that the dataset belongs to. This can be set to UCR, UEA, forecast_csv, or forecast_csv_univar') 170 | parser.add_argument('--max_train_length', type=int, default=3000, help='For sequence with a length greater than , it would be cropped into some sequences, each of which has a length less than (defaults to 3000)') 171 | parser.add_argument('--iters', type=int, default=None, help='The number of iterations') 172 | parser.add_argument('--save_every', type=int, default=None, help='Save the checkpoint every iterations/epochs') 173 | parser.add_argument('--max_threads', type=int, default=None, help='The maximum allowed number of threads used by this process') 174 | parser.add_argument('--eval', action="store_true", help='Whether to perform evaluation after training') 175 | parser.add_argument('--irregular', type=float, default=0, help='The ratio of missing observations (defaults to 0)') 176 | 177 | 178 | # TLoss 179 | parser.add_argument('--hyper', type=str, metavar='FILE', required=True, 180 | help='path of the file of hyperparameters to use ' + 181 | 'for training; must be a JSON file') 182 | parser.add_argument('--load', action='store_true', default=False, 183 | help='activate to load the estimator instead of ' + 184 | 'training it') 185 | parser.add_argument('--fit_classifier', action='store_true', default=False, 186 | help='if not supervised, activate to load the ' + 187 | 'model and retrain the classifier') 188 | 189 | if __name__ == '__main__': 190 | args=parser.parse_args() 191 | 192 | if args.uid == 'SimCLR': 193 | train_SimCLR(parser).excute() 194 | 195 | elif args.uid == 'BYOL': 196 | train_BYOL(parser).excute() 197 | 198 | elif args.uid == 'CPC': 199 | train_CPC(parser).excute() 200 | 201 | elif args.uid == 'SwAV': 202 | train_SwAV(parser).excute() 203 | 204 | elif args.uid == 'PCL': 205 | train_PCL(parser).excute() 206 | 207 | elif args.uid == 'MHCCL': 208 | train_MHCCL(parser).excute() 209 | 210 | elif args.uid == 'TS2Vec': 211 | train_TS2Vec(parser).excute() 212 | 213 | elif args.uid == 'TSTCC': 214 | train_TSTCC(parser).excute() 215 | 216 | elif args.uid == 'TLoss': 217 | train_TLoss(parser).excute() 218 | 219 | elif args.uid == 'TST': 220 | train_TST(parser).excute() 221 | 222 | elif args.uid == 'DeepCluster': 223 | train_DeepCluster(parser).excute() 224 | 225 | elif args.uid == 'IDFD': 226 | train_IDFD(parser).excute() 227 | 228 | 229 | 230 | 231 | -------------------------------------------------------------------------------- /models/BYOL/mlp_head.py: -------------------------------------------------------------------------------- 1 | from torch import nn 2 | 3 | 4 | class MLPHead(nn.Module): 5 | def __init__(self, in_channels, mlp_hidden_size, projection_size): 6 | super(MLPHead, self).__init__() 7 | 8 | self.net = nn.Sequential( 9 | nn.Linear(in_channels, mlp_hidden_size), 10 | nn.BatchNorm1d(mlp_hidden_size), 11 | nn.ReLU(inplace=True), 12 | nn.Linear(mlp_hidden_size, projection_size) 13 | ) 14 | 15 | def forward(self, x): 16 | return self.net(x) 17 | -------------------------------------------------------------------------------- /models/BYOL/resnet_base_network.py: -------------------------------------------------------------------------------- 1 | import torchvision.models as models 2 | import torch 3 | from models.mlp_head import MLPHead 4 | from torch import nn 5 | from config_files.epilepsy_Configs import Config as Configs 6 | 7 | class ResNet18(torch.nn.Module): 8 | def __init__(self, *args, **kwargs): 9 | super(ResNet18, self).__init__() 10 | if kwargs['name'] == 'resnet18': 11 | resnet = models.resnet18(pretrained=False) 12 | # resnet.conv1 = nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3, 13 | # bias=False) 14 | resnet.conv1 = nn.Conv2d(1, 64, kernel_size=8, stride=1, padding=43, 15 | bias=False) #epilepsy 16 | # resnet.conv1 = nn.Conv2d(9, 64, kernel_size=8, stride=1, padding=4, 17 | # bias=False) #HAR 18 | # resnet.conv1 = nn.Conv2d(3, 64, kernel_size=8, stride=1, padding=4, 19 | # bias=False) # SHAR 20 | # resnet.conv1 = nn.Conv2d(3, 64, kernel_size=8, stride=1, padding=4, 21 | # bias=False) # wisdm 22 | print(resnet) 23 | elif kwargs['name'] == 'resnet50': 24 | resnet = models.resnet50(pretrained=False) 25 | 26 | self.encoder = torch.nn.Sequential(*list(resnet.children())[:-1]) 27 | self.projetion = MLPHead(in_channels=resnet.fc.in_features, **kwargs['projection_head']) 28 | 29 | def forward(self, x): 30 | h = self.encoder(x) 31 | h = h.view(h.shape[0], h.shape[1]) 32 | return self.projetion(h) 33 | -------------------------------------------------------------------------------- /models/BYOL/train_BYOL.py: -------------------------------------------------------------------------------- 1 | import os 2 | import torch 3 | import yaml 4 | import argparse 5 | from mlp_head import MLPHead 6 | from resnet_base_network import ResNet18 7 | from trainer import BYOLTrainer 8 | from ..data_loader.dataset_loader import data_generator 9 | 10 | torch.manual_seed(0) 11 | 12 | class train_BYOL(): 13 | 14 | def __init__(self,parser): 15 | self.args = parser.parse_args() 16 | self.dataset_name = self.args.dataset_name 17 | self.method = 'BYOL' 18 | self.training_mode = self.args.training_mode 19 | self.data_dir = self.args.data_dir 20 | 21 | 22 | def excute(self): 23 | config = yaml.load(open("./config/config.yaml", "r"), Loader=yaml.FullLoader) 24 | 25 | device = 'cuda' if torch.cuda.is_available() else 'cpu' 26 | print(f"Training with: {device}") 27 | 28 | from config_files.wisdm_Configs import Config as Configs 29 | configs = Configs() 30 | train_dl, valid_dl, test_dl = data_generator(os.path.join(self.data_dir, self.dataset_name), configs, self.training_mode) # train_linear # change-2 31 | 32 | # online network 33 | online_network = ResNet18(**config['network']).to(device) 34 | pretrained_folder = config['network']['fine_tune_from'] 35 | 36 | # load pre-trained model if defined 37 | if pretrained_folder: 38 | try: 39 | checkpoints_folder = os.path.join('./runs', pretrained_folder, 'checkpoints') 40 | 41 | # load pre-trained parameters 42 | load_params = torch.load(os.path.join(os.path.join(checkpoints_folder, 'model.pth')), 43 | map_location=torch.device(torch.device(device))) 44 | 45 | online_network.load_state_dict(load_params['online_network_state_dict']) 46 | 47 | except FileNotFoundError: 48 | print("Pre-trained weights not found. Training from scratch.") 49 | 50 | # predictor network 51 | predictor = MLPHead(in_channels=online_network.projetion.net[-1].out_features, 52 | **config['network']['projection_head']).to(device) 53 | 54 | # target encoder 55 | target_network = ResNet18(**config['network']).to(device) 56 | # target_network = ResNet18(configs, **config['network']).to(device) 57 | 58 | optimizer = torch.optim.SGD(list(online_network.parameters()) + list(predictor.parameters()), 59 | **config['optimizer']['params']) 60 | 61 | trainer = BYOLTrainer(online_network=online_network, 62 | target_network=target_network, 63 | optimizer=optimizer, 64 | predictor=predictor, 65 | device=device, 66 | **config['trainer']) 67 | 68 | trainer.train(train_dl) 69 | -------------------------------------------------------------------------------- /models/BYOL/trainer.py: -------------------------------------------------------------------------------- 1 | import os 2 | import torch 3 | import torch.nn.functional as F 4 | import torchvision 5 | from torch.utils.data.dataloader import DataLoader 6 | from torch.utils.tensorboard import SummaryWriter 7 | 8 | from utils import _create_model_training_folder 9 | 10 | 11 | class BYOLTrainer: 12 | def __init__(self, online_network, target_network, predictor, optimizer, device, **params): 13 | self.online_network = online_network 14 | self.target_network = target_network 15 | self.optimizer = optimizer 16 | self.device = device 17 | self.predictor = predictor 18 | self.max_epochs = params['max_epochs'] 19 | self.writer = SummaryWriter() 20 | self.m = params['m'] 21 | self.batch_size = params['batch_size'] 22 | self.num_workers = params['num_workers'] 23 | self.checkpoint_interval = params['checkpoint_interval'] 24 | _create_model_training_folder(self.writer, files_to_same=["./config/config.yaml", "main.py", 'trainer.py']) 25 | 26 | @torch.no_grad() 27 | def _update_target_network_parameters(self): 28 | """ 29 | Momentum update of the key encoder 30 | """ 31 | for param_q, param_k in zip(self.online_network.parameters(), self.target_network.parameters()): 32 | param_k.data = param_k.data * self.m + param_q.data * (1. - self.m) 33 | 34 | @staticmethod 35 | def regression_loss(x, y): 36 | x = F.normalize(x, dim=1) 37 | y = F.normalize(y, dim=1) 38 | return 2 - 2 * (x * y).sum(dim=-1) 39 | 40 | def initializes_target_network(self): 41 | # init momentum network as encoder net 42 | for param_q, param_k in zip(self.online_network.parameters(), self.target_network.parameters()): 43 | param_k.data.copy_(param_q.data) # initialize 44 | param_k.requires_grad = False # not update by gradient 45 | 46 | def train(self, train_dataset): 47 | train_loader = train_dataset 48 | 49 | niter = 0 50 | model_checkpoints_folder = os.path.join(self.writer.log_dir, 'checkpoints') 51 | 52 | self.initializes_target_network() 53 | 54 | for batch_idx, (data, target, batch_view_1, batch_view_2) in enumerate(train_loader): 55 | print(f"train_loader_len:{len(train_loader)}") 56 | data, target = data.float().to(self.device), target.long().to(self.device) 57 | batch_view_1, batch_view_2 = batch_view_1.float().to(self.device), batch_view_2.float().to(self.device) 58 | batch_view_1= batch_view_1.unsqueeze(3) 59 | batch_view_2 = batch_view_2.unsqueeze(3) 60 | # if niter == 0: 61 | # grid = torchvision.utils.make_grid(batch_view_1[:32]) 62 | # self.writer.add_image('views_1', grid, global_step=niter) 63 | 64 | # grid = torchvision.utils.make_grid(batch_view_2[:32]) 65 | # self.writer.add_image('views_2', grid, global_step=niter) 66 | print(batch_view_2.shape) #128,1,178,1 67 | print(batch_view_1.shape) # 128,1,178,1 68 | loss = self.update(batch_view_1, batch_view_2) 69 | self.writer.add_scalar('loss', loss, global_step=niter) 70 | 71 | self.optimizer.zero_grad() 72 | loss.backward() 73 | self.optimizer.step() 74 | 75 | self._update_target_network_parameters() # update the key encoder 76 | 77 | # save checkpoints 78 | self.save_model(os.path.join(model_checkpoints_folder, 'model.pth')) 79 | 80 | def update(self, batch_view_1, batch_view_2): 81 | # compute query feature 82 | predictions_from_view_1 = self.predictor(self.online_network(batch_view_1)) 83 | predictions_from_view_2 = self.predictor(self.online_network(batch_view_2)) 84 | 85 | # compute key features 86 | with torch.no_grad(): 87 | targets_to_view_2 = self.target_network(batch_view_1) 88 | targets_to_view_1 = self.target_network(batch_view_2) 89 | 90 | loss = self.regression_loss(predictions_from_view_1, targets_to_view_1) 91 | loss += self.regression_loss(predictions_from_view_2, targets_to_view_2) 92 | return loss.mean() 93 | 94 | def save_model(self, PATH): 95 | 96 | torch.save({ 97 | 'online_network_state_dict': self.online_network.state_dict(), 98 | 'target_network_state_dict': self.target_network.state_dict(), 99 | 'optimizer_state_dict': self.optimizer.state_dict(), 100 | }, PATH) 101 | -------------------------------------------------------------------------------- /models/BYOL/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | from shutil import copyfile 3 | 4 | import torch 5 | import numpy as np 6 | import pandas as pd 7 | from sklearn.metrics import classification_report, cohen_kappa_score, confusion_matrix, accuracy_score 8 | 9 | def _create_model_training_folder(writer, files_to_same): 10 | model_checkpoints_folder = os.path.join(writer.log_dir, 'checkpoints') 11 | if not os.path.exists(model_checkpoints_folder): 12 | os.makedirs(model_checkpoints_folder) 13 | for file in files_to_same: 14 | copyfile(file, os.path.join(model_checkpoints_folder, os.path.basename(file))) 15 | 16 | -------------------------------------------------------------------------------- /models/CPC/config.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | def configs(): 4 | parser = set_default(argparse.ArgumentParser()) 5 | return parser.parse_args([]) 6 | 7 | 8 | def set_default(parser): 9 | group = parser.add_argument_group('Architecture configuration', 10 | description="The arguments defining the " 11 | "model's architecture.") 12 | group.add_argument('--hiddenEncoder', type=int, default=256, 13 | help='Hidden dimension of the encoder network.') 14 | group.add_argument('--hiddenGar', type=int, default=256, 15 | help='Hidden dimension of the auto-regressive network') 16 | group.add_argument('--nPredicts', type=int, default=12, 17 | help='Number of steps to predict.') 18 | group.add_argument('--negativeSamplingExt', type=int, default=128, 19 | help='Number of negative samples to take.') 20 | group.add_argument('--learningRate', type=float, default=2e-4) 21 | group.add_argument('--schedulerStep', type=int, default=-1, 22 | help='Step of the learning rate scheduler: at each ' 23 | 'step the learning rate is divided by 2. Default: ' 24 | 'no scheduler.') 25 | group.add_argument('--schedulerRamp', type=int, default=None, 26 | help='Enable a warm up phase for the learning rate: ' 27 | 'adds a linear ramp of the given size.') 28 | group.add_argument('--beta1', type=float, default=0.9, 29 | help='Value of beta1 for the Adam optimizer') 30 | group.add_argument('--beta2', type=float, default=0.999, 31 | help='Value of beta2 for the Adam optimizer') 32 | group.add_argument('--epsilon', type=float, default=1e-08, 33 | help='Value of epsilon for the Adam optimizer') 34 | group.add_argument('--sizeWindow', type=int, default=20480, 35 | help='Number of frames to consider at each batch.') 36 | group.add_argument('--nEpoch', type=int, default=200, 37 | help='Number of epoch to run') 38 | group.add_argument('--samplingType', type=str, default='samespeaker', 39 | choices=['samespeaker', 'uniform', 40 | 'samesequence', 'sequential'], 41 | help='How to sample the negative examples in the ' 42 | 'CPC loss.') 43 | group.add_argument('--nLevelsPhone', type=int, default=1, 44 | help='(Supervised mode only). Number of layers in ' 45 | 'the phone classification network.') 46 | group.add_argument('--cpc_mode', type=str, default=None, 47 | choices=['reverse', 'none'], 48 | help='Some variations on CPC.') 49 | group.add_argument('--encoder_type', type=str, 50 | choices=['cpc', 'mfcc', 'lfb'], 51 | default='cpc', 52 | help='Replace the encoder network by mfcc features ' 53 | 'or learned filter banks') 54 | group.add_argument('--normMode', type=str, default='layerNorm', 55 | choices=['instanceNorm', 'ID', 'layerNorm', 56 | 'batchNorm'], 57 | help="Type of normalization to use in the encoder " 58 | "network (default is layerNorm).") 59 | group.add_argument('--onEncoder', action='store_true', 60 | help="(Supervised mode only) Perform the " 61 | "classification on the encoder's output.") 62 | group.add_argument('--random_seed', type=int, default=None, 63 | help="Set a specific random seed.") 64 | group.add_argument('--speakerEmbedding', type=int, default=0, 65 | help="(Depreciated) Feed the prediction network with " 66 | "speaker embeddings along with the usual sequence.") 67 | group.add_argument('--arMode', default='LSTM', 68 | choices=['GRU', 'LSTM', 'RNN', 'no_ar', 'transformer'], 69 | help="Architecture to use for the auto-regressive " 70 | "network (default is lstm).") 71 | group.add_argument('--nLevelsGRU', type=int, default=1, 72 | help='Number of layers in the autoregressive network.') 73 | group.add_argument('--rnnMode', type=str, default='transformer', 74 | choices=['transformer', 'RNN', 'LSTM', 'linear', 75 | 'ffd', 'conv4', 'conv8', 'conv12'], 76 | help="Architecture to use for the prediction network") 77 | group.add_argument('--dropout', action='store_true', 78 | help="Add a dropout layer at the output of the " 79 | "prediction network.") 80 | group.add_argument('--abspos', action='store_true', 81 | help='If the prediction network is a transformer, ' 82 | 'active to use absolute coordinates.') 83 | 84 | return parser 85 | -------------------------------------------------------------------------------- /models/CPC/feature_loader.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torchaudio 3 | import os 4 | import json 5 | import argparse 6 | from config import configs 7 | from model import CPCModel, ConcatenatedModel 8 | 9 | 10 | class FeatureModule(torch.nn.Module): 11 | 12 | def __init__(self, featureMaker, get_encoded, collapse=False): 13 | super(FeatureModule, self).__init__() 14 | self.get_encoded = get_encoded 15 | self.featureMaker = featureMaker 16 | self.collapse = collapse 17 | 18 | def getDownsamplingFactor(self): 19 | return self.featureMaker.gEncoder.DOWNSAMPLING 20 | 21 | def forward(self, data): 22 | 23 | batchAudio, label = data 24 | cFeature, encoded, _ = self.featureMaker(batchAudio.cuda(), label) 25 | if self.get_encoded: 26 | cFeature = encoded 27 | if self.collapse: 28 | cFeature = cFeature.contiguous().view(-1, cFeature.size(2)) 29 | return cFeature 30 | 31 | 32 | class ModelPhoneCombined(torch.nn.Module): 33 | 34 | def __init__(self, model, criterion, oneHot): 35 | super(ModelPhoneCombined, self).__init__() 36 | self.model = model 37 | self.criterion = criterion 38 | self.oneHot = oneHot 39 | 40 | def getDownsamplingFactor(self): 41 | return self.model.getDownsamplingFactor() 42 | 43 | def forward(self, data): 44 | c_feature = self.model(data) 45 | pred = self.criterion.getPrediction(c_feature) 46 | P = pred.size(2) 47 | 48 | if self.oneHot: 49 | pred = pred.argmax(dim=2) 50 | pred = toOneHot(pred, P) 51 | else: 52 | pred = torch.nn.functional.softmax(pred, dim=2) 53 | return pred 54 | 55 | 56 | def loadArgs(args, locArgs, forbiddenAttr=None): 57 | for k, v in vars(locArgs).items(): 58 | if forbiddenAttr is not None: 59 | if k not in forbiddenAttr: 60 | setattr(args, k, v) 61 | else: 62 | setattr(args, k, v) 63 | 64 | 65 | def getCheckpointData(pathDir): 66 | if not os.path.isdir(pathDir): 67 | return None 68 | checkpoints = [x for x in os.listdir(pathDir) 69 | if os.path.splitext(x)[1] == '.pt' 70 | and os.path.splitext(x[11:])[0].isdigit()] 71 | if len(checkpoints) == 0: 72 | print("No checkpoints found at " + pathDir) 73 | return None 74 | checkpoints.sort(key=lambda x: int(os.path.splitext(x[11:])[0])) 75 | data = os.path.join(pathDir, checkpoints[-1]) 76 | with open(os.path.join(pathDir, 'checkpoint_logs.json'), 'rb') as file: 77 | logs = json.load(file) 78 | 79 | with open(os.path.join(pathDir, 'checkpoint_args.json'), 'rb') as file: 80 | args = json.load(file) 81 | 82 | args = argparse.Namespace(**args) 83 | defaultArgs = configs() 84 | loadArgs(defaultArgs, args) 85 | 86 | return os.path.abspath(data), logs, defaultArgs 87 | 88 | 89 | def getEncoder(args): 90 | 91 | if args.encoder_type == 'mfcc': 92 | from .model import MFCCEncoder 93 | return MFCCEncoder(args.hiddenEncoder) 94 | elif args.encoder_type == 'lfb': 95 | from .model import LFBEnconder 96 | return LFBEnconder(args.hiddenEncoder) 97 | else: 98 | from .model import CPCEncoder 99 | return CPCEncoder(args.hiddenEncoder, args.normMode) 100 | 101 | 102 | def getAR(args): 103 | if args.arMode == 'transformer': 104 | from .transformers import buildTransformerAR 105 | arNet = buildTransformerAR(args.hiddenEncoder, 1, 106 | args.sizeWindow // 160, args.abspos) 107 | args.hiddenGar = args.hiddenEncoder 108 | elif args.arMode == 'no_ar': 109 | from .model import NoAr 110 | arNet = NoAr() 111 | else: 112 | from .model import CPCAR 113 | arNet = CPCAR(args.hiddenEncoder, args.hiddenGar, 114 | args.samplingType == "sequential", 115 | args.nLevelsGRU, 116 | mode=args.arMode, 117 | reverse=args.cpc_mode == "reverse") 118 | return arNet 119 | 120 | 121 | def loadModel(pathCheckpoints, loadStateDict=True): 122 | models = [] 123 | hiddenGar, hiddenEncoder = 0, 0 124 | for path in pathCheckpoints: 125 | print(f"Loading checkpoint {path}") 126 | _, _, locArgs = getCheckpointData(os.path.dirname(path)) 127 | 128 | doLoad = locArgs.load is not None and \ 129 | (len(locArgs.load) > 1 or 130 | os.path.dirname(locArgs.load[0]) != os.path.dirname(path)) 131 | 132 | if doLoad: 133 | m_, hg, he = loadModel(locArgs.load, loadStateDict=False) 134 | hiddenGar += hg 135 | hiddenEncoder += he 136 | else: 137 | encoderNet = getEncoder(locArgs) 138 | 139 | arNet = getAR(locArgs) 140 | m_ = CPCModel(encoderNet, arNet) 141 | 142 | if loadStateDict: 143 | print(f"Loading the state dict at {path}") 144 | state_dict = torch.load(path, 'cpu') 145 | m_.load_state_dict(state_dict["gEncoder"], strict=False) 146 | if not doLoad: 147 | hiddenGar += locArgs.hiddenGar 148 | hiddenEncoder += locArgs.hiddenEncoder 149 | 150 | models.append(m_) 151 | 152 | if len(models) == 1: 153 | return models[0], hiddenGar, hiddenEncoder 154 | 155 | return ConcatenatedModel(models), hiddenGar, hiddenEncoder 156 | 157 | 158 | def get_module(i_module): 159 | if isinstance(i_module, torch.nn.DataParallel): 160 | return get_module(i_module.module) 161 | if isinstance(i_module, FeatureModule): 162 | return get_module(i_module.module) 163 | return i_module 164 | 165 | 166 | def save_checkpoint(model_state, criterion_state, optimizer_state, best_state, 167 | path_checkpoint): 168 | 169 | state_dict = {"gEncoder": model_state, 170 | "cpcCriterion": criterion_state, 171 | "optimizer": optimizer_state, 172 | "best": best_state} 173 | 174 | torch.save(state_dict, path_checkpoint) 175 | 176 | 177 | def toOneHot(inputVector, nItems): 178 | 179 | batchSize, seqSize = inputVector.size() 180 | out = torch.zeros((batchSize, seqSize, nItems), 181 | device=inputVector.device, dtype=torch.long) 182 | out.scatter_(2, inputVector.view(batchSize, seqSize, 1), 1) 183 | return out 184 | 185 | 186 | def seqNormalization(out): 187 | mean = out.mean(dim=1, keepdim=True) 188 | var = out.var(dim=1, keepdim=True) 189 | return (out - mean) / torch.sqrt(var + 1e-08) 190 | 191 | 192 | def buildFeature(featureMaker, seqPath, strict=False, 193 | maxSizeSeq=64000, seqNorm=False): 194 | 195 | seq = torchaudio.load(seqPath)[0] 196 | sizeSeq = seq.size(1) 197 | start = 0 198 | out = [] 199 | while start < sizeSeq: 200 | if strict and start + maxSizeSeq > sizeSeq: 201 | break 202 | end = min(sizeSeq, start + maxSizeSeq) 203 | subseq = (seq[:, start:end]).view(1, 1, -1).cuda(device=0) 204 | with torch.no_grad(): 205 | features = featureMaker((subseq, None)) 206 | if seqNorm: 207 | features = seqNormalization(features) 208 | out.append(features.detach().cpu()) 209 | start += maxSizeSeq 210 | 211 | if strict and start < sizeSeq: 212 | subseq = (seq[:, -maxSizeSeq:]).view(1, 1, -1).cuda(device=0) 213 | with torch.no_grad(): 214 | features = featureMaker((subseq, None)) 215 | if seqNorm: 216 | features = seqNormalization(features) 217 | delta = (sizeSeq - start) // featureMaker.getDownsamplingFactor() 218 | out.append(features[:, -delta:].detach().cpu()) 219 | 220 | out = torch.cat(out, dim=1) 221 | return out 222 | -------------------------------------------------------------------------------- /models/CPC/model.py: -------------------------------------------------------------------------------- 1 | 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | import torchaudio 5 | import torch 6 | 7 | 8 | class IDModule(nn.Module): 9 | 10 | def __init__(self, *args, **kwargs): 11 | super(IDModule, self).__init__() 12 | 13 | def forward(self, x): 14 | return x 15 | 16 | 17 | class ChannelNorm(nn.Module): 18 | 19 | def __init__(self, 20 | numFeatures, 21 | epsilon=1e-05, 22 | affine=True): 23 | 24 | super(ChannelNorm, self).__init__() 25 | if affine: 26 | self.weight = nn.parameter.Parameter(torch.Tensor(1, 27 | numFeatures, 1)) 28 | self.bias = nn.parameter.Parameter(torch.Tensor(1, numFeatures, 1)) 29 | else: 30 | self.weight = None 31 | self.bias = None 32 | self.epsilon = epsilon 33 | self.p = 0 34 | self.affine = affine 35 | self.reset_parameters() 36 | 37 | def reset_parameters(self): 38 | if self.affine: 39 | torch.nn.init.ones_(self.weight) 40 | torch.nn.init.zeros_(self.bias) 41 | 42 | def forward(self, x): 43 | 44 | cumMean = x.mean(dim=1, keepdim=True) 45 | cumVar = x.var(dim=1, keepdim=True) 46 | x = (x - cumMean)*torch.rsqrt(cumVar + self.epsilon) 47 | 48 | if self.weight is not None: 49 | x = x * self.weight + self.bias 50 | return x 51 | 52 | 53 | class CPCEncoder(nn.Module): 54 | 55 | def __init__(self, 56 | sizeHidden=512, 57 | normMode="layerNorm"): 58 | 59 | super(CPCEncoder, self).__init__() 60 | 61 | validModes = ["batchNorm", "instanceNorm", "ID", "layerNorm"] 62 | if normMode not in validModes: 63 | raise ValueError(f"Norm mode must be in {validModes}") 64 | 65 | if normMode == "instanceNorm": 66 | def normLayer(x): return nn.InstanceNorm1d(x, affine=True) 67 | elif normMode == "ID": 68 | normLayer = IDModule 69 | elif normMode == "layerNorm": 70 | normLayer = ChannelNorm 71 | else: 72 | normLayer = nn.BatchNorm1d 73 | 74 | self.dimEncoded = sizeHidden 75 | self.conv0 = nn.Conv1d(1, sizeHidden, 10, stride=5, padding=3) 76 | self.batchNorm0 = normLayer(sizeHidden) 77 | self.conv1 = nn.Conv1d(sizeHidden, sizeHidden, 8, stride=4, padding=2) 78 | self.batchNorm1 = normLayer(sizeHidden) 79 | self.conv2 = nn.Conv1d(sizeHidden, sizeHidden, 4, 80 | stride=2, padding=1) 81 | self.batchNorm2 = normLayer(sizeHidden) 82 | self.conv3 = nn.Conv1d(sizeHidden, sizeHidden, 4, stride=2, padding=1) 83 | self.batchNorm3 = normLayer(sizeHidden) 84 | self.conv4 = nn.Conv1d(sizeHidden, sizeHidden, 4, stride=2, padding=1) 85 | self.batchNorm4 = normLayer(sizeHidden) 86 | self.DOWNSAMPLING = 160 87 | 88 | def getDimOutput(self): 89 | return self.conv4.out_channels 90 | 91 | def forward(self, x): 92 | x = F.relu(self.batchNorm0(self.conv0(x))) 93 | x = F.relu(self.batchNorm1(self.conv1(x))) 94 | x = F.relu(self.batchNorm2(self.conv2(x))) 95 | x = F.relu(self.batchNorm3(self.conv3(x))) 96 | x = F.relu(self.batchNorm4(self.conv4(x))) 97 | return x 98 | 99 | 100 | class MFCCEncoder(nn.Module): 101 | 102 | def __init__(self, 103 | dimEncoded): 104 | 105 | super(MFCCEncoder, self).__init__() 106 | melkwargs = {"n_mels": max(128, dimEncoded), "n_fft": 321} 107 | self.dimEncoded = dimEncoded 108 | self.MFCC = torchaudio.transforms.MFCC(n_mfcc=dimEncoded, 109 | melkwargs=melkwargs) 110 | 111 | def forward(self, x): 112 | x = x.view(x.size(0), -1) 113 | x = self.MFCC(x) 114 | return x.permute(0, 2, 1) 115 | 116 | 117 | class LFBEnconder(nn.Module): 118 | 119 | def __init__(self, dimEncoded, normalize=True): 120 | 121 | super(LFBEnconder, self).__init__() 122 | self.dimEncoded = dimEncoded 123 | self.conv = nn.Conv1d(1, 2 * dimEncoded, 124 | 400, stride=1) 125 | self.register_buffer('han', torch.hann_window(400).view(1, 1, 400)) 126 | self.instancenorm = nn.InstanceNorm1d(dimEncoded, momentum=1) \ 127 | if normalize else None 128 | 129 | def forward(self, x): 130 | 131 | N, C, L = x.size() 132 | x = self.conv(x) 133 | x = x.view(N, self.dimEncoded, 2, -1) 134 | x = x[:, :, 0, :]**2 + x[:, :, 1, :]**2 135 | x = x.view(N * self.dimEncoded, 1, -1) 136 | x = torch.nn.functional.conv1d(x, self.han, bias=None, 137 | stride=160, padding=350) 138 | x = x.view(N, self.dimEncoded, -1) 139 | x = torch.log(1 + torch.abs(x)) 140 | 141 | # Normalization 142 | if self.instancenorm is not None: 143 | x = self.instancenorm(x) 144 | return x 145 | 146 | 147 | class CPCAR(nn.Module): 148 | 149 | def __init__(self, 150 | dimEncoded, 151 | dimOutput, 152 | keepHidden, 153 | nLevelsGRU, 154 | mode="GRU", 155 | reverse=False): 156 | 157 | super(CPCAR, self).__init__() 158 | self.RESIDUAL_STD = 0.1 159 | 160 | if mode == "LSTM": 161 | self.baseNet = nn.LSTM(dimEncoded, dimOutput, 162 | num_layers=nLevelsGRU, batch_first=True) 163 | elif mode == "RNN": 164 | self.baseNet = nn.RNN(dimEncoded, dimOutput, 165 | num_layers=nLevelsGRU, batch_first=True) 166 | else: 167 | self.baseNet = nn.GRU(dimEncoded, dimOutput, 168 | num_layers=nLevelsGRU, batch_first=True) 169 | 170 | self.hidden = None 171 | self.keepHidden = keepHidden 172 | self.reverse = reverse 173 | 174 | def getDimOutput(self): 175 | return self.baseNet.hidden_size 176 | 177 | def forward(self, x): 178 | 179 | if self.reverse: 180 | x = torch.flip(x, [1]) 181 | try: 182 | self.baseNet.flatten_parameters() 183 | except RuntimeError: 184 | pass 185 | x, h = self.baseNet(x, self.hidden) 186 | if self.keepHidden: 187 | if isinstance(h, tuple): 188 | self.hidden = tuple(x.detach() for x in h) 189 | else: 190 | self.hidden = h.detach() 191 | 192 | if self.reverse: 193 | x = torch.flip(x, [1]) 194 | return x 195 | 196 | 197 | class NoAr(nn.Module): 198 | 199 | def __init__(self, *args): 200 | super(NoAr, self).__init__() 201 | 202 | def forward(self, x): 203 | return x 204 | 205 | 206 | class BiDIRARTangled(nn.Module): 207 | 208 | def __init__(self, 209 | dimEncoded, 210 | dimOutput, 211 | nLevelsGRU): 212 | 213 | super(BiDIRARTangled, self).__init__() 214 | assert(dimOutput % 2 == 0) 215 | 216 | self.ARNet = nn.GRU(dimEncoded, dimOutput // 2, 217 | num_layers=nLevelsGRU, batch_first=True, 218 | bidirectional=True) 219 | 220 | def getDimOutput(self): 221 | return self.ARNet.hidden_size * 2 222 | 223 | def forward(self, x): 224 | 225 | self.ARNet.flatten_parameters() 226 | xf, _ = self.ARNet(x) 227 | return xf 228 | 229 | 230 | class BiDIRAR(nn.Module): 231 | 232 | def __init__(self, 233 | dimEncoded, 234 | dimOutput, 235 | nLevelsGRU): 236 | 237 | super(BiDIRAR, self).__init__() 238 | assert(dimOutput % 2 == 0) 239 | 240 | self.netForward = nn.GRU(dimEncoded, dimOutput // 2, 241 | num_layers=nLevelsGRU, batch_first=True) 242 | self.netBackward = nn.GRU(dimEncoded, dimOutput // 2, 243 | num_layers=nLevelsGRU, batch_first=True) 244 | 245 | def getDimOutput(self): 246 | return self.netForward.hidden_size * 2 247 | 248 | def forward(self, x): 249 | 250 | self.netForward.flatten_parameters() 251 | self.netBackward.flatten_parameters() 252 | xf, _ = self.netForward(x) 253 | xb, _ = self.netBackward(torch.flip(x, [1])) 254 | return torch.cat([xf, torch.flip(xb, [1])], dim=2) 255 | 256 | 257 | class CPCModel(nn.Module): 258 | 259 | def __init__(self, 260 | encoder, 261 | AR): 262 | 263 | super(CPCModel, self).__init__() 264 | self.gEncoder = encoder 265 | self.gAR = AR 266 | 267 | def forward(self, batchData, label): 268 | encodedData = self.gEncoder(batchData).permute(0, 2, 1) 269 | cFeature = self.gAR(encodedData) 270 | return cFeature, encodedData, label 271 | 272 | 273 | class ConcatenatedModel(nn.Module): 274 | 275 | def __init__(self, model_list): 276 | 277 | super(ConcatenatedModel, self).__init__() 278 | self.models = torch.nn.ModuleList(model_list) 279 | 280 | def forward(self, batchData, label): 281 | 282 | outFeatures = [] 283 | outEncoded = [] 284 | for model in self.models: 285 | cFeature, encodedData, label = model(batchData, label) 286 | outFeatures.append(cFeature) 287 | outEncoded.append(encodedData) 288 | return torch.cat(outFeatures, dim=2), \ 289 | torch.cat(outEncoded, dim=2), label 290 | -------------------------------------------------------------------------------- /models/CPC/train_CPC.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import torch 3 | from model import CPCModel as cpcmodel 4 | from config import configs 5 | from feature_loader import getEncoder, getAR, loadArgs 6 | from ..data_loader.dataset_loader import data_generator 7 | 8 | class train_CPC(): 9 | 10 | def __init__(self,parser): 11 | self.args = parser.parse_args() 12 | self.dataset_name = self.args.dataset_name 13 | self.method = 'CPC' 14 | self.training_mode = self.args.training_mode 15 | self.data_dir = self.args.data_dir 16 | 17 | def excute(self): 18 | locArgs = configs() 19 | loadArgs(locArgs, self.args) 20 | encoderNet = getEncoder(locArgs) 21 | arNet = getAR(locArgs) 22 | model = cpcmodel(encoderNet, arNet) 23 | train_dl, valid_dl, test_dl = data_generator(os.path.join(self.data_dir, self.dataset_name), locArgs, self.training_mode) 24 | 25 | model.train(train_dl) 26 | -------------------------------------------------------------------------------- /models/CPC/transformers.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import math 4 | 5 | 6 | class ScaledDotProductAttention(nn.Module): 7 | def __init__(self, 8 | sizeSeq, # Size of the input sequence 9 | dk, # Dimension of the input sequence 10 | dropout, # Dropout parameter 11 | relpos=False): # Do we retrieve positional information ? 12 | super(ScaledDotProductAttention, self).__init__() 13 | 14 | self.drop = nn.Dropout(dropout) 15 | self.softmax = nn.Softmax(dim=2) 16 | self.relpos = relpos 17 | self.sizeSeq = sizeSeq 18 | 19 | if relpos: 20 | self.Krelpos = nn.Parameter(torch.Tensor(dk, sizeSeq)) 21 | self.initmat_(self.Krelpos) 22 | self.register_buffer('z', torch.zeros(1, sizeSeq, 1)) 23 | 24 | # A mask is set so that a node never queries data in the future 25 | mask = torch.tril(torch.ones(sizeSeq, sizeSeq), diagonal=0) 26 | mask = 1 - mask 27 | mask[mask == 1] = -float('inf') 28 | self.register_buffer('mask', mask.unsqueeze(0)) 29 | 30 | def initmat_(self, mat, dim=0): 31 | stdv = 1. / math.sqrt(mat.size(dim)) 32 | mat.data.uniform_(-stdv, stdv) 33 | 34 | def forward(self, Q, K, V): 35 | # Input dim : N x sizeSeq x dk 36 | QK = torch.bmm(Q, K.transpose(-2, -1)) 37 | 38 | if self.relpos: 39 | bsz = Q.size(0) 40 | QP = Q.matmul(self.Krelpos) 41 | # This trick with z fills QP's diagonal with zeros 42 | QP = torch.cat((self.z.expand(bsz, -1, -1), QP), 2) 43 | QK += QP.view(bsz, self.sizeSeq + 1, self.sizeSeq)[:, 1:, :] 44 | A = self.softmax(QK / math.sqrt(K.size(-1)) + self.mask) 45 | return torch.bmm(self.drop(A), V) 46 | 47 | 48 | class MultiHeadAttention(nn.Module): 49 | def __init__(self, 50 | sizeSeq, # Size of a sequence 51 | dropout, # Dropout parameter 52 | dmodel, # Model's dimension 53 | nheads, # Number of heads in the model 54 | abspos): # Is positional information encoded in the input ? 55 | super(MultiHeadAttention, self).__init__() 56 | self.Wo = nn.Linear(dmodel, dmodel, bias=False) 57 | self.Wk = nn.Linear(dmodel, dmodel, bias=False) 58 | self.Wq = nn.Linear(dmodel, dmodel, bias=False) 59 | self.Wv = nn.Linear(dmodel, dmodel, bias=False) 60 | self.nheads = nheads 61 | self.dk = dmodel // nheads 62 | self.Att = ScaledDotProductAttention(sizeSeq, self.dk, 63 | dropout, not abspos) 64 | 65 | def trans_(self, x): 66 | bsz, bptt, h, dk = x.size(0), x.size(1), self.nheads, self.dk 67 | return x.view(bsz, bptt, h, dk).transpose(1, 2).contiguous().view(bsz * h, bptt, dk) 68 | 69 | def reverse_trans_(self, x): 70 | bsz, bptt, h, dk = x.size( 71 | 0) // self.nheads, x.size(1), self.nheads, self.dk 72 | return x.view(bsz, h, bptt, dk).transpose(1, 2).contiguous().view(bsz, bptt, h * dk) 73 | 74 | def forward(self, Q, K, V): 75 | q = self.trans_(self.Wq(Q)) 76 | k = self.trans_(self.Wk(K)) 77 | v = self.trans_(self.Wv(V)) 78 | y = self.reverse_trans_(self.Att(q, k, v)) 79 | return self.Wo(y) 80 | 81 | 82 | class FFNetwork(nn.Module): 83 | def __init__(self, din, dout, dff, dropout): 84 | super(FFNetwork, self).__init__() 85 | self.lin1 = nn.Linear(din, dff, bias=True) 86 | self.lin2 = nn.Linear(dff, dout, bias=True) 87 | self.relu = nn.ReLU() 88 | self.drop = nn.Dropout(dropout) 89 | 90 | def forward(self, x): 91 | return self.lin2(self.drop(self.relu(self.lin1(x)))) 92 | 93 | 94 | class TransformerLayer(nn.Module): 95 | def __init__(self, sizeSeq=32, dmodel=512, dff=2048, 96 | dropout=0.1, nheads=8, 97 | abspos=False): 98 | super(TransformerLayer, self).__init__() 99 | self.multihead = MultiHeadAttention(sizeSeq, dropout, 100 | dmodel, nheads, abspos) 101 | self.ln_multihead = nn.LayerNorm(dmodel) 102 | self.ffnetwork = FFNetwork(dmodel, dmodel, dff, dropout) 103 | self.ln_ffnetwork = nn.LayerNorm(dmodel) 104 | 105 | def forward(self, x): 106 | y = self.ln_multihead(x + self.multihead(Q=x, K=x, V=x)) 107 | return self.ln_ffnetwork(y + self.ffnetwork(y)) 108 | 109 | 110 | class StaticPositionEmbedding(nn.Module): 111 | def __init__(self, seqlen, dmodel): 112 | super(StaticPositionEmbedding, self).__init__() 113 | pos = torch.arange(0., seqlen).unsqueeze(1).repeat(1, dmodel) 114 | dim = torch.arange(0., dmodel).unsqueeze(0).repeat(seqlen, 1) 115 | div = torch.exp(- math.log(10000) * (2*(dim//2)/dmodel)) 116 | pos *= div 117 | pos[:, 0::2] = torch.sin(pos[:, 0::2]) 118 | pos[:, 1::2] = torch.cos(pos[:, 1::2]) 119 | self.register_buffer('pe', pos.unsqueeze(0)) 120 | 121 | def forward(self, x): 122 | return x + self.pe[:, :x.size(1), :] 123 | 124 | 125 | def buildTransformerAR(dimEncoded, # Output dimension of the encoder 126 | nLayers, # Number of transformer layers 127 | sizeSeq, # Expected size of the input sequence 128 | abspos): 129 | layerSequence = [] 130 | if abspos: 131 | layerSequence += [StaticPositionEmbedding(sizeSeq, dimEncoded)] 132 | layerSequence += [TransformerLayer(sizeSeq=sizeSeq, 133 | dmodel=dimEncoded, abspos=abspos) 134 | for i in range(nLayers)] 135 | return nn.Sequential(*layerSequence) 136 | -------------------------------------------------------------------------------- /models/IDFD/config_files/EigenWorms_Configs.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | def __init__(self): 3 | # model configs 4 | self.input_channels = 6 5 | self.kernel_size = 8 6 | self.stride = 1 7 | self.final_out_channels = 128 8 | 9 | self.num_classes = 5 10 | self.dropout = 0.35 11 | self.features_len = 17984 12 | 13 | # training configs 14 | self.num_epoch = 40 15 | 16 | # optimizer parameters 17 | self.beta1 = 0.9 18 | self.beta2 = 0.99 19 | self.lr = 3e-4 20 | 21 | # data parameters 22 | self.drop_last = True 23 | self.batch_size = 32 24 | 25 | self.Context_Cont = Context_Cont_configs() 26 | self.TC = TC() 27 | self.augmentation = augmentations() 28 | 29 | 30 | class augmentations(object): 31 | def __init__(self): 32 | self.jitter_scale_ratio = 1.1 33 | self.jitter_ratio = 0.8 34 | self.max_seg = 8 35 | 36 | 37 | class Context_Cont_configs(object): 38 | def __init__(self): 39 | self.temperature = 0.2 40 | self.use_cosine_similarity = True 41 | 42 | 43 | class TC(object): 44 | def __init__(self): 45 | self.hidden_dim = 100 46 | self.timesteps = 6 47 | -------------------------------------------------------------------------------- /models/IDFD/config_files/FingerMovements_Configs.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | def __init__(self): 3 | # model configs 4 | self.input_channels = 28 5 | self.kernel_size = 8 6 | self.stride = 1 7 | self.final_out_channels = 128 8 | 9 | self.num_classes = 2 10 | self.dropout = 0.35 11 | self.features_len = 50 12 | 13 | # training configs 14 | self.num_epoch = 40 15 | 16 | # optimizer parameters 17 | self.beta1 = 0.9 18 | self.beta2 = 0.99 19 | self.lr = 3e-4 20 | 21 | # data parameters 22 | self.drop_last = True 23 | self.batch_size = 128 24 | 25 | self.Context_Cont = Context_Cont_configs() 26 | self.TC = TC() 27 | self.augmentation = augmentations() 28 | 29 | 30 | class augmentations(object): 31 | def __init__(self): 32 | self.jitter_scale_ratio = 1.1 33 | self.jitter_ratio = 0.8 34 | self.max_seg = 8 35 | 36 | 37 | class Context_Cont_configs(object): 38 | def __init__(self): 39 | self.temperature = 0.2 40 | self.use_cosine_similarity = True 41 | 42 | 43 | class TC(object): 44 | def __init__(self): 45 | self.hidden_dim = 100 46 | self.timesteps = 6 47 | -------------------------------------------------------------------------------- /models/IDFD/config_files/HAR_Configs.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | def __init__(self): 3 | # model configs 4 | self.input_channels = 9 5 | self.kernel_size = 8 6 | self.stride = 1 7 | self.final_out_channels = 128 8 | 9 | self.num_classes = 6 10 | self.dropout = 0.35 11 | self.features_len = 18 12 | 13 | # training configs 14 | self.num_epoch = 40 15 | 16 | # optimizer parameters 17 | self.beta1 = 0.9 18 | self.beta2 = 0.99 19 | self.lr = 3e-4 20 | 21 | # data parameters 22 | self.drop_last = True 23 | self.batch_size = 4 24 | 25 | self.Context_Cont = Context_Cont_configs() 26 | self.TC = TC() 27 | self.augmentation = augmentations() 28 | 29 | 30 | class augmentations(object): 31 | def __init__(self): 32 | self.jitter_scale_ratio = 1.1 33 | self.jitter_ratio = 0.8 34 | self.max_seg = 8 35 | 36 | 37 | class Context_Cont_configs(object): 38 | def __init__(self): 39 | self.temperature = 0.2 40 | self.use_cosine_similarity = True 41 | 42 | 43 | class TC(object): 44 | def __init__(self): 45 | self.hidden_dim = 100 46 | self.timesteps = 6 47 | -------------------------------------------------------------------------------- /models/IDFD/config_files/PenDigits_Configs.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | def __init__(self): 3 | # model configs 4 | self.input_channels = 2 5 | self.kernel_size = 8 6 | self.stride = 1 7 | self.final_out_channels = 128 8 | 9 | self.num_classes = 10 10 | self.dropout = 0.35 11 | self.features_len = 8 12 | 13 | # training configs 14 | self.num_epoch = 40 15 | 16 | # optimizer parameters 17 | self.beta1 = 0.9 18 | self.beta2 = 0.99 19 | self.lr = 3e-4 20 | 21 | # data parameters 22 | self.drop_last = True 23 | self.batch_size = 128 24 | 25 | self.Context_Cont = Context_Cont_configs() 26 | self.TC = TC() 27 | self.augmentation = augmentations() 28 | 29 | 30 | class augmentations(object): 31 | def __init__(self): 32 | self.jitter_scale_ratio = 1.1 33 | self.jitter_ratio = 0.8 34 | self.max_seg = 8 35 | 36 | 37 | class Context_Cont_configs(object): 38 | def __init__(self): 39 | self.temperature = 0.2 40 | self.use_cosine_similarity = True 41 | 42 | 43 | class TC(object): 44 | def __init__(self): 45 | self.hidden_dim = 100 46 | self.timesteps = 6 47 | -------------------------------------------------------------------------------- /models/IDFD/config_files/SHAR_Configs.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | def __init__(self): 3 | # model configs 4 | self.input_channels = 3 5 | self.kernel_size = 8 6 | self.stride = 1 7 | self.final_out_channels = 128 8 | 9 | self.num_classes = 17 10 | self.dropout = 0.35 11 | self.features_len = 21 12 | 13 | # training configs 14 | self.num_epoch = 40 15 | 16 | # optimizer parameters 17 | self.beta1 = 0.9 18 | self.beta2 = 0.99 19 | self.lr = 3e-4 20 | 21 | # data parameters 22 | self.drop_last = True 23 | self.batch_size = 128 24 | 25 | self.Context_Cont = Context_Cont_configs() 26 | self.TC = TC() 27 | self.augmentation = augmentations() 28 | 29 | 30 | class augmentations(object): 31 | def __init__(self): 32 | self.jitter_scale_ratio = 1.1 33 | self.jitter_ratio = 0.8 34 | self.max_seg = 8 35 | 36 | 37 | class Context_Cont_configs(object): 38 | def __init__(self): 39 | self.temperature = 0.2 40 | self.use_cosine_similarity = True 41 | 42 | 43 | class TC(object): 44 | def __init__(self): 45 | self.hidden_dim = 100 46 | self.timesteps = 6 -------------------------------------------------------------------------------- /models/IDFD/config_files/epilepsy_Configs.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | def __init__(self): 3 | # model configs 4 | self.input_channels = 1 5 | self.kernel_size = 8 6 | self.stride = 1 7 | self.final_out_channels = 128 8 | 9 | self.num_classes = 2 10 | self.dropout = 0.35 11 | self.features_len = 24 12 | 13 | # training configs 14 | self.num_epoch = 10 15 | 16 | 17 | # optimizer parameters 18 | self.beta1 = 0.9 19 | self.beta2 = 0.99 20 | self.lr = 3e-4 21 | 22 | # data parameters 23 | self.drop_last = True 24 | self.batch_size = 128 25 | 26 | self.Context_Cont = Context_Cont_configs() 27 | self.TC = TC() 28 | self.augmentation = augmentations() 29 | 30 | 31 | class augmentations(object): 32 | def __init__(self): 33 | self.jitter_scale_ratio = 0.001 34 | self.jitter_ratio = 0.001 35 | self.max_seg = 5 36 | 37 | 38 | class Context_Cont_configs(object): 39 | def __init__(self): 40 | self.temperature = 0.2 41 | self.use_cosine_similarity = True 42 | 43 | 44 | class TC(object): 45 | def __init__(self): 46 | self.hidden_dim = 100 47 | self.timesteps = 10 48 | -------------------------------------------------------------------------------- /models/IDFD/config_files/wisdm_Configs.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | def __init__(self): 3 | # model configs 4 | self.input_channels = 3 5 | self.kernel_size = 8 6 | self.stride = 1 7 | self.final_out_channels = 128 8 | 9 | self.num_classes = 6 10 | self.dropout = 0.35 11 | self.features_len = 34 12 | 13 | # training configs 14 | self.num_epoch = 40 15 | 16 | # optimizer parameters 17 | self.beta1 = 0.9 18 | self.beta2 = 0.99 19 | self.lr = 3e-4 20 | 21 | # data parameters 22 | self.drop_last = True 23 | self.batch_size = 128 24 | 25 | self.Context_Cont = Context_Cont_configs() 26 | self.TC = TC() 27 | self.augmentation = augmentations() 28 | 29 | 30 | class augmentations(object): 31 | def __init__(self): 32 | self.jitter_scale_ratio = 1.1 33 | self.jitter_ratio = 0.8 34 | self.max_seg = 8 35 | 36 | 37 | class Context_Cont_configs(object): 38 | def __init__(self): 39 | self.temperature = 0.2 40 | self.use_cosine_similarity = True 41 | 42 | 43 | class TC(object): 44 | def __init__(self): 45 | self.hidden_dim = 100 46 | self.timesteps = 256 47 | -------------------------------------------------------------------------------- /models/IDFD/train_IDFD.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | 4 | import numpy as np 5 | from sklearn.cluster import KMeans 6 | from sklearn.metrics import normalized_mutual_info_score, adjusted_rand_score 7 | from scipy.optimize import linear_sum_assignment 8 | import tqdm.autonotebook as tqdm 9 | 10 | import torch 11 | import torch.nn as nn 12 | from torch.autograd import Function 13 | import torch.nn.functional as F 14 | from torchvision import datasets, transforms 15 | from torchvision.models import resnet 16 | 17 | from ..data_loader.dataset_loader import data_generator 18 | 19 | 20 | 21 | class train_IDFD(): 22 | 23 | def __init__(self, parser): 24 | self.args = parser.parse_args() 25 | self.method = 'IDFD' 26 | self.dataset_name = self.args.dataset_name 27 | self.epochs = self.args.epochs 28 | self.gpus = self.args.device_id 29 | self.num_workers = self.args.workers 30 | 31 | 32 | def excute(self): 33 | 34 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 35 | os.environ["CUDA_VISIBLE_DEVICES"] = self.gpus 36 | 37 | if self.dataset_name == 'HAR': 38 | from config_files.HAR_Configs import Config as Configs 39 | configs = Configs() 40 | in_channels = 9 41 | train_loader, test_loader, len_train, len_test = data_generator('data/HAR', configs, 'self_supervised') 42 | elif self.dataset_name == 'wisdm': 43 | from config_files.wisdm_Configs import Config as Configs 44 | configs = Configs() 45 | in_channels = 3 46 | train_loader, test_loader, len_train, len_test = data_generator('data/wisdm', configs, 'self_supervised') 47 | elif self.dataset_name == 'epilepsy': 48 | from config_files.epilepsy_Configs import Config as Configs 49 | configs = Configs() 50 | in_channels = 1 51 | train_loader, test_loader, len_train, len_test = data_generator('data/epilepsy', configs, 'self_supervised') 52 | elif self.dataset_name == 'SHAR': 53 | from config_files.SHAR_Configs import Config as Configs 54 | configs = Configs() 55 | in_channels = 3 56 | train_loader, test_loader, len_train, len_test = data_generator('data/SHAR', configs, 'self_supervised') 57 | elif self.dataset_name == 'PenDigits': 58 | from config_files.PenDigits_Configs import Config as Configs 59 | configs = Configs() 60 | in_channels = 2 61 | train_loader, test_loader, len_train, len_test = data_generator('data/PenDigits', configs, 'self_supervised') 62 | elif self.dataset_name == 'EigenWorms': 63 | from config_files.EigenWorms_Configs import Config as Configs 64 | configs = Configs() 65 | in_channels = 6 66 | train_loader, test_loader, len_train, len_test = data_generator('data/EigenWorms', configs, 'self_supervised') 67 | elif self.dataset_name == 'FingerMovements': 68 | from config_files.FingerMovements_Configs import Config as Configs 69 | configs = Configs() 70 | in_channels = 28 71 | train_loader, test_loader, len_train, len_test = data_generator('data/FingerMovements', configs, 'self_supervised') 72 | elif self.dataset_name == 'StandWalkJump': 73 | from config_files.StandWalkJump_Configs import Config as Configs 74 | configs = Configs() 75 | in_channels = 4 76 | train_loader, test_loader, len_train, len_test = data_generator('data/StandWalkJump', configs, 'self_supervised') 77 | elif self.dataset_name == 'PhonemeSpectra': 78 | from config_files.PhonemeSpectra_Configs import Config as Configs 79 | configs = Configs() 80 | in_channels = 11 81 | train_loader, test_loader, len_train, len_test = data_generator('data/PhonemeSpectra', configs, 'self_supervised') 82 | elif self.dataset_name == 'DuckDuckGeese': 83 | from config_files.DuckDuckGeese_Configs import Config as Configs 84 | configs = Configs() 85 | in_channels = 1345 86 | train_loader, test_loader, len_train, len_test = data_generator('data/DuckDuckGeese', configs, 'self_supervised') 87 | elif self.dataset_name == 'InsectWingbeat': 88 | from config_files.InsectWingbeat_Configs import Config as Configs 89 | configs = Configs() 90 | in_channels = 200 91 | train_loader, test_loader, len_train, len_test = data_generator('data/InsectWingbeat', configs, 'self_supervised') 92 | elif self.dataset_name == 'CharacterTrajectories': 93 | from config_files.CharacterTrajectories_Configs import Config as Configs 94 | configs = Configs() 95 | in_channels = 3 96 | train_loader, test_loader, len_train, len_test = data_generator('data/CharacterTrajectories', configs, 'self_supervised') 97 | print(f'len(train_loader):{len(train_loader)}') 98 | print(f'len(test_loader):{len(test_loader)}') 99 | print(f'len_train:{len_train}') 100 | print(f'len_test:{len_test}') 101 | 102 | 103 | low_dim = 128 104 | net = ResNet18(low_dim=low_dim, in_channels=in_channels) 105 | norm = Normalize(2) 106 | npc = NonParametricClassifier(input_dim=low_dim, 107 | output_dim=len_train, 108 | tau=1.0, 109 | momentum=0.9)#0.5 110 | loss = Loss(tau2=2.0) 111 | net, norm = net.to(device), norm.to(device) 112 | npc, loss = npc.to(device), loss.to(device) 113 | optimizer = torch.optim.Adam(net.parameters(), 114 | lr=0.03, 115 | weight_decay=5e-4) 116 | lr_scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, 117 | [20, 80, 120, 180], 118 | gamma=0.1)#[600, 950, 1300, 1650] 119 | 120 | if torch.cuda.is_available(): 121 | net = torch.nn.DataParallel(net, 122 | device_ids=range(len( 123 | self.gpus.split(",")))) 124 | torch.backends.cudnn.benchmark = True 125 | 126 | trackers = {n: AverageTracker() for n in ["loss", "loss_id", "loss_fd"]} 127 | 128 | 129 | with tqdm.trange(self.epochs) as epoch_bar: 130 | max_acc = 0.1 131 | max_epoch = 0 132 | for epoch in epoch_bar: 133 | net.train() 134 | # for batch_idx, (inputs, _, indexes) in enumerate(tqdm.tqdm(train_loader)): 135 | for batch_idx, (inputs, targets, aug1, aug2, indexes) in enumerate(tqdm.tqdm(train_loader)): #mqw 136 | optimizer.zero_grad() 137 | 138 | aug1 = aug1.unsqueeze(3) 139 | aug1 = aug1.to(device, dtype=torch.float32, non_blocking=True) 140 | indexes = indexes.to(device, non_blocking=True) 141 | features = norm(net(aug1)) 142 | 143 | outputs = npc(features, indexes) # 144 | loss_id, loss_fd = loss(outputs, features, indexes) # 145 | # tot_loss = loss_id + loss_fd 146 | tot_loss = loss_id 147 | tot_loss.backward() 148 | optimizer.step() 149 | # track loss 150 | trackers["loss"].add(tot_loss) 151 | trackers["loss_id"].add(loss_id) 152 | trackers["loss_fd"].add(loss_fd) 153 | lr_scheduler.step() 154 | 155 | # logging 156 | postfix = {name: t.avg() for name, t in trackers.items()} 157 | epoch_bar.set_postfix(**postfix) 158 | for t in trackers.values(): 159 | t.reset() 160 | 161 | 162 | acc, nmi, ari = check_clustering_metrics(npc, train_loader) 163 | print("Epoch:{} Kmeans ACC, NMI, ARI = {}, {}, {}".format(epoch+1, acc, nmi, ari)) 164 | 165 | 166 | if acc > max_acc: 167 | max_acc = acc 168 | max_epoch = epoch+1 169 | torch.save({'net_state_dict': net.state_dict(), 'npc_state_dict': npc.state_dict(), 170 | 'optimizer_state_dict': optimizer.state_dict(), 'lr_scheduler_state_dict': lr_scheduler.state_dict(), 171 | 'max_epoch': max_epoch, 'max_acc': max_acc 172 | }, 173 | f'{self.dataset_name}_{max_epoch}_{max_acc}_model.pth') 174 | 175 | 176 | class AverageTracker(): 177 | def __init__(self): 178 | self.step = 0 179 | self.cur_avg = 0 180 | 181 | def add(self, value): 182 | self.cur_avg *= self.step / (self.step + 1) 183 | self.cur_avg += value / (self.step + 1) 184 | self.step += 1 185 | 186 | def reset(self): 187 | self.step = 0 188 | self.cur_avg = 0 189 | 190 | def avg(self): 191 | return self.cur_avg.item() 192 | 193 | 194 | def check_clustering_metrics(npc, train_loader): 195 | # print(npc) 196 | trainFeatures = npc.memory 197 | z = trainFeatures.cpu().numpy() 198 | y = np.array(train_loader.dataset.y_data) 199 | n_clusters = len(np.unique(y)) 200 | kmeans = KMeans(n_clusters=n_clusters, n_init=20) 201 | y_pred = kmeans.fit_predict(z) 202 | return metrics.acc(y, y_pred), metrics.nmi(y, y_pred), metrics.ari(y, y_pred) 203 | 204 | 205 | class metrics: 206 | ari = adjusted_rand_score 207 | nmi = normalized_mutual_info_score 208 | 209 | @staticmethod 210 | def acc(y_true, y_pred): 211 | y_true = y_true.astype(np.int64) 212 | y_pred = y_pred.astype(np.int64) 213 | assert y_pred.size == y_true.size 214 | D = max(y_pred.max(), y_true.max()) + 1 215 | w = np.zeros((D, D), dtype=np.int64) 216 | for i in range(y_pred.size): 217 | w[y_pred[i], y_true[i]] += 1 218 | row, col = linear_sum_assignment(w.max() - w) 219 | return sum([w[i, j] for i, j in zip(row, col)]) * 1.0 / y_pred.size 220 | 221 | 222 | class NonParametricClassifierOP(Function): 223 | @staticmethod 224 | def forward(ctx, x, y, memory, params): 225 | 226 | tau = params[0].item() 227 | out = x.mm(memory.t()) 228 | out.div_(tau) 229 | ctx.save_for_backward(x, memory, y, params) 230 | return out 231 | 232 | @staticmethod 233 | def backward(ctx, grad_output): 234 | x, memory, y, params = ctx.saved_tensors 235 | tau = params[0] 236 | momentum = params[1] 237 | 238 | grad_output.div_(tau) 239 | 240 | grad_input = grad_output.mm(memory) 241 | grad_input.resize_as_(x) 242 | 243 | weight_pos = memory.index_select(0, y.view(-1)).resize_as_(x) 244 | weight_pos.mul_(momentum) 245 | weight_pos.add_(x.mul(1 - momentum)) 246 | w_norm = weight_pos.pow(2).sum(1, keepdim=True).pow(0.5) 247 | updated_weight = weight_pos.div(w_norm) 248 | memory.index_copy_(0, y, updated_weight) 249 | 250 | return grad_input, None, None, None, None 251 | 252 | 253 | class NonParametricClassifier(nn.Module): 254 | def __init__(self, input_dim, output_dim, tau=1.0, momentum=0.5): 255 | super(NonParametricClassifier, self).__init__() 256 | self.register_buffer('params', torch.tensor([tau, momentum])) 257 | stdv = 1. / np.sqrt(input_dim / 3.) 258 | self.register_buffer( 259 | 'memory', 260 | torch.rand(output_dim, input_dim).mul_(2 * stdv).add_(-stdv)) 261 | 262 | def forward(self, x, y): 263 | out = NonParametricClassifierOP.apply(x, y, self.memory, self.params) 264 | return out 265 | 266 | 267 | 268 | class Normalize(nn.Module): 269 | def __init__(self, power=2): 270 | super().__init__() 271 | self.power = power 272 | 273 | def forward(self, x): 274 | norm = x.pow(self.power).sum(1, keepdim=True).pow(1. / self.power) 275 | out = x.div(norm) 276 | return out 277 | 278 | 279 | def ResNet18(low_dim=128, in_channels=3): 280 | net = resnet.ResNet(resnet.BasicBlock, [2, 2, 2, 2], low_dim) 281 | net.conv1 = nn.Conv2d(in_channels, 64, kernel_size=3, 282 | stride=1, padding=1, bias=False) 283 | net.maxpool = nn.Identity() 284 | return net 285 | 286 | 287 | class Loss(nn.Module): 288 | def __init__(self, tau2): 289 | super().__init__() 290 | self.tau2 = tau2 291 | 292 | def forward(self, x, ff, y): 293 | 294 | L_id = F.cross_entropy(x, y) 295 | 296 | norm_ff = ff / (ff**2).sum(0, keepdim=True).sqrt() 297 | coef_mat = torch.mm(norm_ff.t(), norm_ff) 298 | coef_mat.div_(self.tau2) 299 | a = torch.arange(coef_mat.size(0), device=coef_mat.device) 300 | L_fd = F.cross_entropy(coef_mat, a) 301 | return L_id, L_fd 302 | 303 | -------------------------------------------------------------------------------- /models/PCL/builder.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from random import sample 4 | from torchvision.models import resnet 5 | 6 | def ResNet18(low_dim=128, dataset_name='wisdm'): 7 | 8 | if dataset_name == 'DuckDuckGeese': 9 | in_channels = 1345 10 | elif dataset_name == 'FingerMovements': 11 | in_channels = 28 12 | elif dataset_name == 'PenDigits': 13 | in_channels = 2 14 | elif dataset_name == 'PhonemeSpectra': 15 | in_channels = 11 16 | elif dataset_name == 'StandWalkJump': 17 | in_channels = 4 18 | elif dataset_name == 'InsectWingbeat': 19 | in_channels = 200 20 | elif dataset_name == 'EigenWorms': 21 | in_channels = 6 22 | elif dataset_name == 'HAR': 23 | in_channels = 9 24 | elif dataset_name == 'SHAR': 25 | in_channels = 3 26 | elif dataset_name == 'wisdm': 27 | in_channels = 3 28 | elif dataset_name == 'epilepsy': 29 | in_channels = 1 30 | 31 | 32 | net = resnet.ResNet(resnet.BasicBlock, [2, 2, 2, 2], low_dim) 33 | if dataset_name=='wisdm': 34 | net.conv1 = nn.Conv2d(in_channels, 64, kernel_size=8, stride=1, padding=4, bias=False) 35 | elif dataset_name=='HAR': 36 | net.conv1 = nn.Conv2d(9, 64, kernel_size=8, stride=1, padding=4, bias=False) 37 | elif dataset_name=='epilepsy': 38 | net.conv1 = nn.Conv2d(1, 64, kernel_size=8, stride=1, padding=43, bias=False) 39 | elif dataset_name == 'SHAR': 40 | net.conv1 = nn.Conv2d(3, 64, kernel_size=8, stride=1, padding=4, bias=False) 41 | else: 42 | net.conv1 = nn.Conv2d(in_channels, 64, kernel_size=7, stride=2, padding=3, bias=False) 43 | 44 | 45 | net.maxpool = nn.Identity() 46 | return net 47 | 48 | class MoCo(nn.Module): 49 | 50 | def __init__(self, base_encoder, dim=128, r=4, m=0.999, T=0.1, mlp=False, dataset_name='wisdm'): 51 | """ 52 | dim: feature dimension (default: 128) 53 | r: queue size; number of negative samples/prototypes (default: 4) 54 | m: momentum for updating key encoder (default: 0.999) 55 | T: softmax temperature 56 | mlp: whether to use mlp projection 57 | """ 58 | super(MoCo, self).__init__() 59 | 60 | self.r = r 61 | self.m = m 62 | self.T = T 63 | 64 | # create the encoders 65 | # num_classes is the output fc dimension 66 | self.encoder_q = ResNet18(low_dim=dim, dataset_name=dataset_name) 67 | self.encoder_k = ResNet18(low_dim=dim, dataset_name=dataset_name) 68 | 69 | 70 | 71 | if mlp: # hack: brute-force replacement 72 | dim_mlp = self.encoder_q.fc.weight.shape[1] 73 | self.encoder_q.fc = nn.Sequential(nn.Linear(dim_mlp, dim_mlp), nn.ReLU(), self.encoder_q.fc) 74 | self.encoder_k.fc = nn.Sequential(nn.Linear(dim_mlp, dim_mlp), nn.ReLU(), self.encoder_k.fc) 75 | 76 | for param_q, param_k in zip(self.encoder_q.parameters(), self.encoder_k.parameters()): 77 | param_k.data.copy_(param_q.data) # initialize 78 | param_k.requires_grad = False # not update by gradient 79 | 80 | # create the queue 81 | self.register_buffer("queue", torch.randn(dim, r)) 82 | self.queue = nn.functional.normalize(self.queue, dim=0) 83 | 84 | self.register_buffer("queue_ptr", torch.zeros(1, dtype=torch.long)) 85 | 86 | @torch.no_grad() 87 | def _momentum_update_key_encoder(self): 88 | """ 89 | Momentum update of the key encoder 90 | """ 91 | for param_q, param_k in zip(self.encoder_q.parameters(), self.encoder_k.parameters()): 92 | param_k.data = param_k.data * self.m + param_q.data * (1. - self.m) 93 | 94 | @torch.no_grad() 95 | def _dequeue_and_enqueue(self, keys): 96 | # gather keys before updating queue 97 | keys = concat_all_gather(keys) 98 | 99 | batch_size = keys.shape[0] 100 | 101 | ptr = int(self.queue_ptr) 102 | 103 | print(f'batch_size:{batch_size}')#384 104 | print(f'self.r:{self.r}')#896 105 | print(f'keys.T.shape:{keys.T.shape}')#[128, 384] 106 | 107 | 108 | assert self.r % batch_size == 0 # for simplicity 109 | print('&*&*&*&*&*&*1') 110 | # replace the keys at ptr (dequeue and enqueue) 111 | self.queue[:, ptr:ptr + batch_size] = keys.T 112 | print('&*&*&*&*&*&*2') 113 | ptr = (ptr + batch_size) % self.r # move pointer 114 | print('&*&*&*&*&*&*3') 115 | self.queue_ptr[0] = ptr 116 | 117 | @torch.no_grad() 118 | def _batch_shuffle_ddp(self, x): 119 | """ 120 | Batch shuffle, for making use of BatchNorm. 121 | *** Only support DistributedDataParallel (DDP) model. *** 122 | """ 123 | # gather from all gpus 124 | batch_size_this = x.shape[0] 125 | x_gather = concat_all_gather(x) 126 | batch_size_all = x_gather.shape[0] 127 | 128 | num_gpus = batch_size_all // batch_size_this 129 | 130 | # random shuffle index 131 | idx_shuffle = torch.randperm(batch_size_all).cuda() 132 | 133 | # broadcast to all gpus 134 | torch.distributed.broadcast(idx_shuffle, src=0) 135 | 136 | # index for restoring 137 | idx_unshuffle = torch.argsort(idx_shuffle) 138 | 139 | # shuffled index for this gpu 140 | gpu_idx = torch.distributed.get_rank() 141 | idx_this = idx_shuffle.view(num_gpus, -1)[gpu_idx] 142 | 143 | return x_gather[idx_this], idx_unshuffle 144 | 145 | @torch.no_grad() 146 | def _batch_unshuffle_ddp(self, x, idx_unshuffle): 147 | """ 148 | Undo batch shuffle. 149 | *** Only support DistributedDataParallel (DDP) model. *** 150 | """ 151 | # gather from all gpus 152 | batch_size_this = x.shape[0] 153 | x_gather = concat_all_gather(x) 154 | batch_size_all = x_gather.shape[0] 155 | 156 | num_gpus = batch_size_all // batch_size_this 157 | 158 | # restored index for this gpu 159 | gpu_idx = torch.distributed.get_rank() 160 | idx_this = idx_unshuffle.view(num_gpus, -1)[gpu_idx] 161 | 162 | return x_gather[idx_this] 163 | 164 | def forward(self, im_q, im_k=None, is_eval=False, cluster_result=None, index=None): 165 | """ 166 | Input: 167 | im_q: a batch of query images 168 | im_k: a batch of key images 169 | is_eval: return momentum embeddings (used for clustering) 170 | cluster_result: cluster assignments, centroids, and density 171 | index: indices for training samples 172 | Output: 173 | logits, targets, proto_logits, proto_targets 174 | """ 175 | 176 | if is_eval: 177 | # self.encoder_k.conv1 = nn.Conv2d(9, 64, kernel_size=3, stride=1, padding=1, bias=False) 178 | 179 | k = self.encoder_k(im_q) 180 | 181 | k = nn.functional.normalize(k, dim=1) 182 | return k 183 | 184 | # compute key features 185 | with torch.no_grad(): # no gradient to keys 186 | self._momentum_update_key_encoder() # update the key encoder 187 | 188 | # shuffle for making use of BN 189 | im_k, idx_unshuffle = self._batch_shuffle_ddp(im_k) 190 | 191 | k = self.encoder_k(im_k) # keys: NxC 192 | k = nn.functional.normalize(k, dim=1) 193 | 194 | # undo shuffle 195 | k = self._batch_unshuffle_ddp(k, idx_unshuffle) 196 | 197 | # compute query features 198 | q = self.encoder_q(im_q) # queries: NxC 199 | q = nn.functional.normalize(q, dim=1) 200 | 201 | # compute logits 202 | # Einstein sum is more intuitive 203 | # positive logits: Nx1 204 | l_pos = torch.einsum('nc,nc->n', [q, k]).unsqueeze(-1) 205 | # negative logits: Nxr 206 | l_neg = torch.einsum('nc,ck->nk', [q, self.queue.clone().detach()]) 207 | 208 | # logits: Nx(1+r) 209 | logits = torch.cat([l_pos, l_neg], dim=1) 210 | 211 | # apply temperature 212 | logits /= self.T 213 | 214 | # labels: positive key indicators 215 | labels = torch.zeros(logits.shape[0], dtype=torch.long).cuda() 216 | 217 | # dequeue and enqueue 218 | self._dequeue_and_enqueue(k) 219 | 220 | # prototypical contrast 221 | if cluster_result is not None: 222 | proto_labels = [] 223 | proto_logits = [] 224 | for n, (im2cluster,prototypes,density) in enumerate(zip(cluster_result['im2cluster'],cluster_result['centroids'],cluster_result['density'])): 225 | # get positive prototypes 226 | pos_proto_id = im2cluster[index] 227 | pos_prototypes = prototypes[pos_proto_id] print('e') 228 | # sample negative prototypes 229 | all_proto_id = [i for i in range(im2cluster.max())] 230 | neg_proto_id = set(all_proto_id)-set(pos_proto_id.tolist()) 231 | neg_proto_id = sample(neg_proto_id,self.r) #sample r negative prototypes 232 | neg_prototypes = prototypes[neg_proto_id] 233 | proto_selected = torch.cat([pos_prototypes,neg_prototypes],dim=0) 234 | # compute prototypical logits 235 | logits_proto = torch.mm(q,proto_selected.t()) 236 | 237 | # targets for prototype assignment 238 | labels_proto = torch.linspace(0, q.size(0)-1, steps=q.size(0)).long().cuda() 239 | # scaling temperatures for the selected prototypes 240 | temp_proto = density[torch.cat([pos_proto_id,torch.LongTensor(neg_proto_id).cuda()],dim=0)] 241 | logits_proto /= temp_proto 242 | proto_labels.append(labels_proto) 243 | proto_logits.append(logits_proto) 244 | return logits, labels, proto_logits, proto_labels 245 | else: 246 | return logits, labels, None, None 247 | 248 | 249 | # utils 250 | @torch.no_grad() 251 | def concat_all_gather(tensor): 252 | """ 253 | Performs all_gather operation on the provided tensors. 254 | *** Warning ***: torch.distributed.all_gather has no gradient. 255 | """ 256 | tensors_gather = [torch.ones_like(tensor) 257 | for _ in range(torch.distributed.get_world_size())] 258 | torch.distributed.all_gather(tensors_gather, tensor, async_op=False) 259 | 260 | output = torch.cat(tensors_gather, dim=0) 261 | return output 262 | -------------------------------------------------------------------------------- /models/SimCLR/loss.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | 5 | class contrastive_loss(nn.Module): 6 | def __init__(self, tau=1, normalize=False): 7 | super(contrastive_loss, self).__init__() 8 | self.tau = tau 9 | self.normalize = normalize 10 | 11 | def forward(self, xi, xj): 12 | 13 | x = torch.cat((xi, xj), dim=0) 14 | 15 | is_cuda = x.is_cuda 16 | sim_mat = torch.mm(x, x.T) 17 | if self.normalize: 18 | sim_mat_denom = torch.mm(torch.norm(x, dim=1).unsqueeze(1), torch.norm(x, dim=1).unsqueeze(1).T) 19 | sim_mat = sim_mat / sim_mat_denom.clamp(min=1e-16) 20 | 21 | sim_mat = torch.exp(sim_mat / self.tau) 22 | 23 | # no diag because it's not diffrentiable -> sum - exp(1 / tau) 24 | # diag_ind = torch.eye(xi.size(0) * 2).bool() 25 | # diag_ind = diag_ind.cuda() if use_cuda else diag_ind 26 | 27 | # sim_mat = sim_mat.masked_fill_(diag_ind, 0) 28 | 29 | # top 30 | if self.normalize: 31 | sim_mat_denom = torch.norm(xi, dim=1) * torch.norm(xj, dim=1) 32 | sim_match = torch.exp(torch.sum(xi * xj, dim=-1) / sim_mat_denom / self.tau) 33 | else: 34 | sim_match = torch.exp(torch.sum(xi * xj, dim=-1) / self.tau) 35 | 36 | sim_match = torch.cat((sim_match, sim_match), dim=0) 37 | 38 | norm_sum = torch.exp(torch.ones(x.size(0)) / self.tau) 39 | norm_sum = norm_sum.cuda() if is_cuda else norm_sum 40 | loss = torch.mean(-torch.log(sim_match / (torch.sum(sim_mat, dim=-1) - norm_sum))) 41 | 42 | return loss 43 | -------------------------------------------------------------------------------- /models/SimCLR/train_SimCLR.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import torch 3 | import torch.optim as optim 4 | from torch.optim.lr_scheduler import ExponentialLR 5 | import numpy as np 6 | 7 | from tensorboardX import SummaryWriter 8 | from tqdm import tqdm 9 | import os 10 | import time 11 | 12 | from models import * 13 | from utils import * 14 | from loss import * 15 | 16 | from torch.utils.data.dataloader import DataLoader 17 | from ..data_loader.dataset_loader import data_generator 18 | 19 | class train_SimCLR(): 20 | 21 | def __init__(self,parser): 22 | self.args=parser.parse_args() 23 | self.train_loader=None 24 | self.test_loader=None 25 | self.logger=None 26 | # Setup tensorboard 27 | self.use_tb = self.args.exp_dir is not None 28 | self.log_dir = self.args.exp_dir 29 | # Set cuda 30 | self.use_cuda = not self.args.no_cuda and torch.cuda.is_available() 31 | if self.use_cuda: 32 | self.dtype = torch.cuda.FloatTensor 33 | self.device = torch.device("cuda") 34 | torch.cuda.set_device(self.args.device_id) 35 | print('GPU') 36 | else: 37 | self.dtype = torch.FloatTensor 38 | self.device = torch.device("cpu") 39 | print('CPU') 40 | # Setup tensorboard 41 | self.use_tb = self.args.exp_dir is not None 42 | self.log_dir = self.args.exp_dir 43 | 44 | 45 | # train validate 46 | # def train_validate(model, loader, optimizer, is_train, epoch, use_cuda): 47 | def train_validate(self,model, optimizer, is_train, epoch, use_cuda): 48 | loss_func = contrastive_loss(tau=self.args.tau) 49 | # data_loader = loader.train_loader if is_train else loader.test_loader 50 | data_loader = self.train_loader if is_train else self.test_loader 51 | 52 | if is_train: 53 | model.train() 54 | model.zero_grad() 55 | else: 56 | model.eval() 57 | 58 | desc = 'Train' if is_train else 'Validation' 59 | 60 | total_loss = 0.0 61 | 62 | tqdm_bar = tqdm(data_loader) 63 | print(f'len(tqdm_bar):{len(data_loader)}') 64 | for i, (x_i, _, x_j, _) in enumerate(tqdm_bar): batch_idx, (data, target, batch_view_1, batch_view_2) 65 | x_i = x_i.cuda() if self.use_cuda else x_i 66 | x_j = x_j.cuda() if self.use_cuda else x_j 67 | 68 | x_i = x_i.unsqueeze(3) 69 | x_j = x_j.unsqueeze(3) 70 | 71 | x_i = x_i.float().to(self.device) 72 | x_j = x_j.float().to(self.device) 73 | 74 | _, z_i = model(x_i) 75 | _, z_j = model(x_j) 76 | 77 | loss = loss_func(z_i, z_j) 78 | loss /= self.args.accumulation_steps 79 | 80 | if is_train: 81 | loss.backward() 82 | 83 | if (i + 1) % self.args.accumulation_steps == 0 and is_train: 84 | optimizer.step() 85 | model.zero_grad() 86 | 87 | total_loss += loss.item() 88 | 89 | tqdm_bar.set_description('{} Epoch: [{}] Loss: {:.4f}'.format(desc, epoch, loss.item())) 90 | 91 | return total_loss / (len(data_loader.dataset)) 92 | 93 | def execute_graph(self,model, optimizer, scheduler, epoch, use_cuda): 94 | t_loss = self.train_validate(model, optimizer, True, epoch, use_cuda) 95 | v_loss = self.train_validate(model, optimizer, False, epoch, use_cuda) 96 | 97 | scheduler.step(v_loss) 98 | 99 | if self.use_tb: 100 | self.logger.add_scalar(self.log_dir + '/train-loss', t_loss, epoch) 101 | self.logger.add_scalar(self.log_dir + '/valid-loss', v_loss, epoch) 102 | 103 | return v_loss 104 | 105 | 106 | def excute(self): 107 | # Setup asset directories 108 | if not os.path.exists('models'): 109 | os.makedirs('models') 110 | 111 | if not os.path.exists('runs'): 112 | os.makedirs('runs') 113 | 114 | # Logger 115 | if self.use_tb: 116 | self.logger = SummaryWriter(comment='_' + self.args.uid + '_' + self.args.dataset_name) 117 | 118 | if self.args.dataset_name == 'HAR': 119 | from .config_files.HAR_Configs import Config as Configs 120 | configs = Configs() 121 | in_channels = 9 122 | # valid_loader, 123 | self.train_loader, self.test_loader = data_generator('data/HAR', configs, 'self_supervised') 124 | print(f'len(train_loader):{len(self.train_loader)}') 125 | # print(f'len(valid_loader):{len(self.valid_loader)}') 126 | print(f'len(test_loader):{len(self.test_loader)}') 127 | 128 | elif self.args.dataset_name == 'wisdm': 129 | from .config_files.wisdm_Configs import Config as Configs 130 | configs = Configs() 131 | in_channels = 3 132 | train_loader, valid_loader, test_loader = data_generator('data/wisdm', configs, 'self_supervised') 133 | print(f'len(train_loader):{len(self.train_loader)}') 134 | print(f'len(valid_loader):{len(self.valid_loader)}') 135 | print(f'len(test_loader):{len(self.test_loader)}') 136 | 137 | elif self.args.dataset_name == 'epilepsy': 138 | from .config_files.epilepsy_Configs import Config as Configs 139 | configs = Configs() 140 | in_channels = 1 141 | train_loader, valid_loader, test_loader = data_generator('data/epilepsy', configs, 'self_supervised') 142 | print(f'len(train_loader):{len(self.train_loader)}') 143 | print(f'len(valid_loader):{len(self.valid_loader)}') 144 | print(f'len(test_loader):{len(self.test_loader)}') 145 | 146 | elif self.args.dataset_name == 'SHAR': 147 | from .config_files.SHAR_Configs import Config as Configs 148 | configs = Configs() 149 | in_channels = 3 150 | train_loader, valid_loader, test_loader = data_generator('data/SHAR', configs, 'self_supervised') 151 | print(f'len(train_loader):{len(train_loader)}') 152 | print(f'len(valid_loader):{len(valid_loader)}') 153 | print(f'len(test_loader):{len(test_loader)}') 154 | 155 | 156 | model = resnet50_cifar(self.args.feature_size, self.args.dataset_name).type(self.dtype) -add dataset_name 157 | # resnet50 input_dim=2048 because-- Bottleneck:expansion=4 -- 2048=512*4 158 | # model = resnet18_cifar(args.feature_size, args.dataset_name).type(dtype)-add dataset_name # try resnet18_cifar later 159 | # resnet18 input_dim=512 because-- BasicBlock:expansion=1 -- 512=512*1 160 | 161 | optimizer = optim.Adam(model.parameters(), lr=self.args.lr, weight_decay=self.args.decay_lr) 162 | scheduler = ExponentialLR(optimizer, gamma=self.args.decay_lr) 163 | 164 | # Main training loop 165 | best_loss = np.inf 166 | 167 | # Resume training 168 | if self.args.load_model is not None: 169 | if os.path.isfile(self.args.load_model): 170 | checkpoint = torch.load(self.args.load_model) 171 | model.load_state_dict(checkpoint['model']) 172 | optimizer.load_state_dict(checkpoint['optimizer']) 173 | scheduler.load_state_dict(checkpoint['scheduler']) 174 | best_loss = checkpoint['val_loss'] 175 | epoch = checkpoint['epoch'] 176 | print('Loading model: {}. Resuming from epoch: {}'.format(self.args.load_model, epoch)) 177 | else: 178 | print('Model: {} not found'.format(self.args.load_model)) 179 | 180 | for epoch in range(self.args.epochs): 181 | # v_loss = execute_graph(model, loader, optimizer, scheduler, epoch, use_cuda) 182 | v_loss = self.execute_graph(model, optimizer, scheduler, epoch, self.use_cuda) 183 | if v_loss < best_loss: 184 | best_loss = v_loss 185 | print('Writing model checkpoint') 186 | state = { 187 | 'epoch': epoch, 188 | 'model': model.state_dict(), 189 | 'optimizer': optimizer.state_dict(), 190 | 'scheduler': scheduler.state_dict(), 191 | 'val_loss': v_loss 192 | } 193 | t = time.localtime() 194 | timestamp = time.strftime('%b-%d-%Y_%H%M', t) 195 | file_name = 'models/{}_{}_{}_{:04.4f}.pt'.format(timestamp, self.args.uid, epoch, v_loss) 196 | 197 | torch.save(state, file_name) 198 | 199 | # TensorboardX logger 200 | self.logger.close() 201 | 202 | # save model / restart training 203 | 204 | 205 | -------------------------------------------------------------------------------- /models/SimCLR/utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | 5 | def type_tdouble(use_cuda=False): 6 | return torch.cuda.DoubleTensor if use_cuda else torch.DoubleTensor 7 | 8 | 9 | def one_hot(labels, n_class, use_cuda=False): 10 | # Ensure labels are [N x 1] 11 | if len(list(labels.size())) == 1: 12 | labels = labels.unsqueeze(1) 13 | mask = type_tdouble(use_cuda)(labels.size(0), n_class).fill_(0) 14 | # scatter dimension, position indices, fill_value 15 | return mask.scatter_(1, labels, 1) 16 | 17 | 18 | def init_weights(module): 19 | for m in module.modules(): 20 | if isinstance(m, nn.Conv2d): 21 | nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') 22 | elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): 23 | nn.init.constant_(m.weight, 1) 24 | nn.init.constant_(m.bias, 0) 25 | -------------------------------------------------------------------------------- /models/SwAV/hubconf.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torchvision 3 | from torchvision.models.resnet import resnet50 as _resnet50 4 | 5 | dependencies = ["torch", "torchvision"] 6 | 7 | 8 | def resnet50(pretrained=True, **kwargs): 9 | """ 10 | ResNet-50 pre-trained with SwAV. 11 | 12 | Note that `fc.weight` and `fc.bias` are randomly initialized. 13 | 14 | Achieves 75.3% top-1 accuracy on ImageNet when `fc` is trained. 15 | """ 16 | model = _resnet50(pretrained=False, **kwargs) 17 | if pretrained: 18 | state_dict = torch.hub.load_state_dict_from_url( 19 | url="https://dl.fbaipublicfiles.com/deepcluster/swav_800ep_pretrain.pth.tar", 20 | map_location="cpu", 21 | ) 22 | # removes "module." 23 | state_dict = {k.replace("module.", ""): v for k, v in state_dict.items()} 24 | # load weights 25 | model.load_state_dict(state_dict, strict=False) 26 | return model 27 | -------------------------------------------------------------------------------- /models/SwAV/logger.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import time 4 | from datetime import timedelta 5 | import pandas as pd 6 | 7 | 8 | class LogFormatter: 9 | def __init__(self): 10 | self.start_time = time.time() 11 | 12 | def format(self, record): 13 | elapsed_seconds = round(record.created - self.start_time) 14 | 15 | prefix = "%s - %s - %s" % ( 16 | record.levelname, 17 | time.strftime("%x %X"), 18 | timedelta(seconds=elapsed_seconds), 19 | ) 20 | message = record.getMessage() 21 | message = message.replace("\n", "\n" + " " * (len(prefix) + 3)) 22 | return "%s - %s" % (prefix, message) if message else "" 23 | 24 | 25 | def create_logger(filepath, rank): 26 | """ 27 | Create a logger. 28 | Use a different log file for each process. 29 | """ 30 | # create log formatter 31 | log_formatter = LogFormatter() 32 | 33 | # create file handler and set level to debug 34 | if filepath is not None: 35 | if rank > 0: 36 | filepath = "%s-%i" % (filepath, rank) 37 | file_handler = logging.FileHandler(filepath, "a") 38 | file_handler.setLevel(logging.DEBUG) 39 | file_handler.setFormatter(log_formatter) 40 | 41 | # create console handler and set level to info 42 | console_handler = logging.StreamHandler() 43 | console_handler.setLevel(logging.INFO) 44 | console_handler.setFormatter(log_formatter) 45 | 46 | # create logger and set level to debug 47 | logger = logging.getLogger() 48 | logger.handlers = [] 49 | logger.setLevel(logging.DEBUG) 50 | logger.propagate = False 51 | if filepath is not None: 52 | logger.addHandler(file_handler) 53 | logger.addHandler(console_handler) 54 | 55 | # reset logger elapsed time 56 | def reset_time(): 57 | log_formatter.start_time = time.time() 58 | 59 | logger.reset_time = reset_time 60 | 61 | return logger 62 | 63 | 64 | class PD_Stats(object): 65 | """ 66 | Log stuff with pandas library 67 | """ 68 | 69 | def __init__(self, path, columns): 70 | self.path = path 71 | 72 | # reload path stats 73 | if os.path.isfile(self.path): 74 | self.stats = pd.read_pickle(self.path) 75 | print(self.stats) 76 | # print(self.stats.columns) #Index(['epoch', 'loss'], dtype='object') 77 | # print(columns) #('epoch', 'loss', 'prec1', 'prec5', 'loss_val', 'prec1_val', 'prec5_val') 78 | # check that columns are the same 79 | assert list(self.stats.columns) == list(columns) 80 | 81 | else: 82 | self.stats = pd.DataFrame(columns=columns) 83 | 84 | def update(self, row, save=True): 85 | self.stats.loc[len(self.stats.index)] = row 86 | 87 | # save the statistics 88 | if save: 89 | self.stats.to_pickle(self.path) 90 | -------------------------------------------------------------------------------- /models/SwAV/utils.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from logging import getLogger 3 | import pickle 4 | import os 5 | 6 | import numpy as np 7 | import torch 8 | import torch.nn as nn 9 | 10 | from .logger import create_logger, PD_Stats 11 | 12 | import torch.distributed as dist 13 | 14 | FALSY_STRINGS = {"off", "false", "0"} 15 | TRUTHY_STRINGS = {"on", "true", "1"} 16 | 17 | 18 | logger = getLogger() 19 | 20 | 21 | def bool_flag(s): 22 | """ 23 | Parse boolean arguments from the command line. 24 | """ 25 | if s.lower() in FALSY_STRINGS: 26 | return False 27 | elif s.lower() in TRUTHY_STRINGS: 28 | return True 29 | else: 30 | raise argparse.ArgumentTypeError("invalid value for a boolean flag") 31 | 32 | 33 | def init_distributed_mode(args): 34 | """ 35 | Initialize the following variables: 36 | - world_size 37 | - rank 38 | """ 39 | 40 | args.is_slurm_job = "SLURM_JOB_ID" in os.environ 41 | 42 | if args.is_slurm_job: 43 | args.rank = int(os.environ["SLURM_PROCID"]) 44 | args.world_size = int(os.environ["SLURM_NNODES"]) * int( 45 | os.environ["SLURM_TASKS_PER_NODE"][0] 46 | ) 47 | else: 48 | # multi-GPU job (local or multi-node) - jobs started with torch.distributed.launch 49 | # read environment variables 50 | args.rank = int(os.environ["RANK"]) 51 | args.world_size = int(os.environ["WORLD_SIZE"]) 52 | 53 | # prepare distributed 54 | dist.init_process_group( 55 | backend="nccl", 56 | init_method=args.dist_url, 57 | world_size=args.world_size, 58 | rank=args.rank, 59 | ) 60 | 61 | # set cuda device 62 | args.gpu_to_work_on = args.rank % torch.cuda.device_count() 63 | torch.cuda.set_device(args.gpu_to_work_on) 64 | return 65 | 66 | 67 | def initialize_exp(params, *args, dump_params=True): 68 | if dump_params: 69 | # print(params.dump_path) 70 | pickle.dump(params, open(os.path.join(params.dump_path, "params.pkl"), "wb")) 71 | 72 | # create repo to store checkpoints 73 | params.dump_checkpoints = os.path.join(params.dump_path, "checkpoints") 74 | if not params.rank and not os.path.isdir(params.dump_checkpoints): 75 | os.mkdir(params.dump_checkpoints) 76 | print(params.rank) 77 | # create a panda object to log loss and acc 78 | training_stats = PD_Stats( 79 | os.path.join(params.dump_path, "stats" + str(params.rank) + ".pkl"), args 80 | ) 81 | 82 | # create a logger 83 | logger = create_logger( 84 | os.path.join(params.dump_path, "train.log"), rank=params.rank 85 | ) 86 | logger.info("============ Initialized logger ============") 87 | logger.info( 88 | "\n".join("%s: %s" % (k, str(v)) for k, v in sorted(dict(vars(params)).items())) 89 | ) 90 | logger.info("The experiment will be stored in %s\n" % params.dump_path) 91 | logger.info("") 92 | return logger, training_stats 93 | 94 | 95 | def restart_from_checkpoint(ckp_paths, run_variables=None, **kwargs): 96 | """ 97 | Re-start from checkpoint 98 | """ 99 | # look for a checkpoint in exp repository 100 | if isinstance(ckp_paths, list): 101 | for ckp_path in ckp_paths: 102 | if os.path.isfile(ckp_path): 103 | break 104 | else: 105 | ckp_path = ckp_paths 106 | 107 | if not os.path.isfile(ckp_path): 108 | return 109 | 110 | logger.info("Found checkpoint at {}".format(ckp_path)) 111 | 112 | # open checkpoint file 113 | checkpoint = torch.load( 114 | ckp_path, map_location="cuda:" + str(torch.distributed.get_rank() % torch.cuda.device_count()) 115 | ) 116 | 117 | for key, value in kwargs.items(): 118 | if key in checkpoint and value is not None: 119 | try: 120 | msg = value.load_state_dict(checkpoint[key], strict=False) 121 | print(msg) 122 | except TypeError: 123 | msg = value.load_state_dict(checkpoint[key]) 124 | logger.info("=> loaded {} from checkpoint '{}'".format(key, ckp_path)) 125 | else: 126 | logger.warning( 127 | "=> failed to load {} from checkpoint '{}'".format(key, ckp_path) 128 | ) 129 | 130 | # re load variable important for the run 131 | if run_variables is not None: 132 | for var_name in run_variables: 133 | if var_name in checkpoint: 134 | run_variables[var_name] = checkpoint[var_name] 135 | 136 | 137 | def fix_random_seeds(seed=31): 138 | """ 139 | Fix random seeds. 140 | """ 141 | torch.manual_seed(seed) 142 | torch.cuda.manual_seed_all(seed) 143 | np.random.seed(seed) 144 | 145 | 146 | class AverageMeter(object): 147 | """computes and stores the average and current value""" 148 | 149 | def __init__(self): 150 | self.reset() 151 | 152 | def reset(self): 153 | self.val = 0 154 | self.avg = 0 155 | self.sum = 0 156 | self.count = 0 157 | 158 | def update(self, val, n=1): 159 | self.val = val 160 | self.sum += val * n 161 | self.count += n 162 | self.avg = self.sum / self.count 163 | 164 | 165 | def accuracy(output, target, topk=(1,)): 166 | """Computes the accuracy over the k top predictions for the specified values of k""" 167 | with torch.no_grad(): 168 | maxk = max(topk) 169 | batch_size = target.size(0) 170 | 171 | _, pred = output.topk(maxk, 1, True, True) 172 | pred = pred.t() 173 | correct = pred.eq(target.view(1, -1).expand_as(pred)) 174 | 175 | res = [] 176 | for k in topk: 177 | # correct_k = correct[:k].view(-1).float().sum(0, keepdim=True) 178 | correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) #mqw 179 | res.append(correct_k.mul_(100.0 / batch_size)) 180 | return res 181 | -------------------------------------------------------------------------------- /models/TLoss/train_TLoss.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import math 4 | import torch 5 | import numpy 6 | import argparse 7 | import numpy as np 8 | from torch.utils.data import Dataset, DataLoader 9 | 10 | import scikit_wrappers 11 | 12 | 13 | class train_TLoss(): 14 | def __init__(self,parser): 15 | self.args=parser.parse_args() 16 | self.dataset = self.args.dataset_name 17 | self.path = self.args.data_dir 18 | self.save_path = self.args.exp_dir 19 | self.cuda = not self.args.no_cuda 20 | self.gpu = self.args.device_id 21 | self.hyper = self.args.hyper 22 | self.load = self.args.load 23 | self.fit_classifier = self.args.fit_classifier 24 | 25 | def fit_hyperparameters(file, train, train_labels, cuda, gpu, 26 | save_memory=False): 27 | classifier = scikit_wrappers.CausalCNNEncoderClassifier() 28 | 29 | # Loads a given set of hyperparameters and fits a model with those 30 | hf = open(os.path.join(file), 'r') 31 | params = json.load(hf) 32 | hf.close() 33 | # Check the number of input channels 34 | params['in_channels'] = numpy.shape(train)[1] 35 | params['cuda'] = cuda 36 | params['gpu'] = gpu 37 | classifier.set_params(**params) 38 | return classifier.fit( 39 | train, train_labels, save_memory=save_memory, verbose=True 40 | ) 41 | 42 | def excute(self): 43 | train_dataset = torch.load(os.path.join(self.path, "train.pt")) 44 | # valid_dataset = torch.load(os.path.join(self.path, "val.pt")) 45 | test_dataset = torch.load(os.path.join(self.path, "test.pt")) 46 | train = np.array(train_dataset["samples"]) 47 | train = train.astype(float) 48 | train_labels = np.array(train_dataset["labels"]) 49 | test = np.array(test_dataset["samples"]) 50 | test = test.astype(float) 51 | test_labels = np.array(test_dataset["labels"]) 52 | 53 | if self.dataset not in ('HAR','wisdm','SHAR','epilepsy'): 54 | print('UEA-----need permute') 55 | train = np.swapaxes(train, 1, 2) 56 | test = np.swapaxes(test, 1, 2) 57 | 58 | print(f'self.load:{self.load}')#False-training, True-load 59 | print(f'self.fit_classifier:{self.fit_classifier}')#True-load the model and retrain the classifier 60 | if not self.load and not self.fit_classifier: 61 | classifier = fit_hyperparameters( 62 | self.hyper, train, train_labels, self.cuda, self.gpu, 63 | save_memory=True 64 | ) 65 | else: 66 | classifier = scikit_wrappers.CausalCNNEncoderClassifier() 67 | hf = open( 68 | os.path.join( 69 | self.save_path, self.dataset + '_hyperparameters.json' 70 | ), 'r' 71 | ) 72 | hp_dict = json.load(hf) 73 | hf.close() 74 | hp_dict['cuda'] = self.cuda 75 | hp_dict['gpu'] = self.gpu 76 | classifier.set_params(**hp_dict) 77 | classifier.load(os.path.join(self.save_path, self.dataset)) 78 | 79 | if not self.load:#load=false 80 | if self.fit_classifier:#fit_classifier=true 81 | classifier.fit_classifier(classifier.encode(train), train_labels) 82 | classifier.save( 83 | os.path.join(self.save_path, self.dataset) 84 | ) 85 | with open( 86 | os.path.join( 87 | self.save_path, self.dataset + '_hyperparameters.json' 88 | ), 'w' 89 | ) as fp: 90 | json.dump(classifier.get_params(), fp) 91 | print("Test accuracy: " + str(classifier.mqw_score(test, test_labels, self.dataset)))dataset_name 92 | -------------------------------------------------------------------------------- /models/TS2Vec/datautils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import torch 4 | import pandas as pd 5 | import math 6 | import random 7 | from datetime import datetime 8 | import pickle 9 | from scipy.io.arff import loadarff 10 | from sklearn.preprocessing import StandardScaler, MinMaxScaler 11 | from ..data_loader.dataset_loader import data_generator 12 | 13 | def load_UCR(dataset): 14 | train_file = os.path.join('datasets/UCR', dataset, dataset + "_TRAIN.tsv") 15 | test_file = os.path.join('datasets/UCR', dataset, dataset + "_TEST.tsv") 16 | train_df = pd.read_csv(train_file, sep='\t', header=None) 17 | test_df = pd.read_csv(test_file, sep='\t', header=None) 18 | train_array = np.array(train_df) 19 | test_array = np.array(test_df) 20 | 21 | # Move the labels to {0, ..., L-1} 22 | labels = np.unique(train_array[:, 0]) 23 | transform = {} 24 | for i, l in enumerate(labels): 25 | transform[l] = i 26 | 27 | train = train_array[:, 1:].astype(np.float64) 28 | train_labels = np.vectorize(transform.get)(train_array[:, 0]) 29 | test = test_array[:, 1:].astype(np.float64) 30 | test_labels = np.vectorize(transform.get)(test_array[:, 0]) 31 | 32 | # Normalization for non-normalized datasets 33 | # To keep the amplitude information, we do not normalize values over 34 | # individual time series, but on the whole dataset 35 | if dataset not in [ 36 | 'AllGestureWiimoteX', 37 | 'AllGestureWiimoteY', 38 | 'AllGestureWiimoteZ', 39 | 'BME', 40 | 'Chinatown', 41 | 'Crop', 42 | 'EOGHorizontalSignal', 43 | 'EOGVerticalSignal', 44 | 'Fungi', 45 | 'GestureMidAirD1', 46 | 'GestureMidAirD2', 47 | 'GestureMidAirD3', 48 | 'GesturePebbleZ1', 49 | 'GesturePebbleZ2', 50 | 'GunPointAgeSpan', 51 | 'GunPointMaleVersusFemale', 52 | 'GunPointOldVersusYoung', 53 | 'HouseTwenty', 54 | 'InsectEPGRegularTrain', 55 | 'InsectEPGSmallTrain', 56 | 'MelbournePedestrian', 57 | 'PickupGestureWiimoteZ', 58 | 'PigAirwayPressure', 59 | 'PigArtPressure', 60 | 'PigCVP', 61 | 'PLAID', 62 | 'PowerCons', 63 | 'Rock', 64 | 'SemgHandGenderCh2', 65 | 'SemgHandMovementCh2', 66 | 'SemgHandSubjectCh2', 67 | 'ShakeGestureWiimoteZ', 68 | 'SmoothSubspace', 69 | 'UMD' 70 | ]: 71 | return train[..., np.newaxis], train_labels, test[..., np.newaxis], test_labels 72 | 73 | mean = np.nanmean(train) 74 | std = np.nanstd(train) 75 | train = (train - mean) / std 76 | test = (test - mean) / std 77 | return train[..., np.newaxis], train_labels, test[..., np.newaxis], test_labels 78 | 79 | 80 | def load_UEA_origin(dataset): 81 | train_data = loadarff(f'datasets/UEA/{dataset}/{dataset}_TRAIN.arff')[0] 82 | test_data = loadarff(f'datasets/UEA/{dataset}/{dataset}_TEST.arff')[0] 83 | 84 | def extract_data(data): 85 | res_data = [] 86 | res_labels = [] 87 | for t_data, t_label in data: 88 | t_data = np.array([ d.tolist() for d in t_data ]) 89 | t_label = t_label.decode("utf-8") 90 | res_data.append(t_data) 91 | res_labels.append(t_label) 92 | return np.array(res_data).swapaxes(1, 2), np.array(res_labels) 93 | 94 | train_X, train_y = extract_data(train_data) 95 | test_X, test_y = extract_data(test_data) 96 | 97 | scaler = StandardScaler() 98 | scaler.fit(train_X.reshape(-1, train_X.shape[-1])) 99 | train_X = scaler.transform(train_X.reshape(-1, train_X.shape[-1])).reshape(train_X.shape) 100 | test_X = scaler.transform(test_X.reshape(-1, test_X.shape[-1])).reshape(test_X.shape) 101 | 102 | labels = np.unique(train_y) 103 | transform = { k : i for i, k in enumerate(labels)} 104 | train_y = np.vectorize(transform.get)(train_y) 105 | test_y = np.vectorize(transform.get)(test_y) 106 | print(train_X.shape) #[40,100,6] [Train Size, Length, channels/variables] 107 | print(train_X) 108 | print(train_y.shape) #[40,] 109 | print(train_y) #[2 2 2 2 1 1 3 0.......] 110 | return train_X, train_y, test_X, test_y 111 | 112 | 113 | def load_UEA(dataset): 114 | print('************UEA archive************') 115 | # UEA archive 116 | print(f'dataset:{dataset}') 117 | train_data = torch.load(f'datasets/{dataset}/train.pt') 118 | test_data = torch.load(f'datasets/{dataset}/test.pt') 119 | # dat_dict = dict() 120 | train_X = train_data["samples"].numpy() 121 | train_y = train_data["labels"].numpy() 122 | test_X = test_data["samples"].numpy() 123 | test_y = test_data["labels"].numpy() 124 | 125 | print(f'train_X.shape:{train_X.shape}') # BasicMotions[40,100,6] [Train Size, Length, channels/variables] HAR[5881,128,9] 126 | print(f'test_X.shape:{test_X.shape}') 127 | # print(train_X) 128 | print(f'train_y.shape:{train_y.shape}') # BasicMotions[40,] HAR[5881,] 129 | # print(train_y) # BasicMotions[2 2 2 2 1 1 3 0.......] HAR[5. 1. 1. ... 0. 4. 0.] 130 | return train_X, train_y, test_X, test_y 131 | 132 | def load_MTS(dataset): 133 | print('************HAR / SHAR / wisdm************') 134 | # MTS: multivariate time series 135 | # datasets: HAR / SHAR / wisdm 136 | # train_data = torch.load(f'datasets/{dataset}/train.pt') 137 | # test_data = torch.load(f'datasets/{dataset}/test.pt') 138 | # from config_files.HAR_Configs import Config as Configs 139 | # configs = Configs() 140 | # train_data , valid_dl, test_data = data_generator('./datasets/HAR', configs, 'self_supervised') # train_linear 141 | print(f'dataset:{dataset}') 142 | train_data = torch.load(f'datasets/{dataset}/train.pt') 143 | test_data = torch.load(f'datasets/{dataset}/test.pt') 144 | # dat_dict = dict() 145 | train_X = train_data["samples"].numpy() 146 | train_y = train_data["labels"].numpy() 147 | test_X = test_data["samples"].numpy() 148 | test_y = test_data["labels"].numpy() 149 | 150 | 151 | print(f'train_X.shape:{train_X.shape}') # BasicMotions[40,100,6] [Train Size, Length, channels] HAR[5881,128,9] 152 | print(f'test_X.shape:{test_X.shape}') 153 | # print(train_X) 154 | print(f'train_y.shape:{train_y.shape}') # BasicMotions[40,] HAR[5881,] 155 | # print(train_y) # BasicMotions[2 2 2 2 1 1 3 0.......] HAR[5. 1. 1. ... 0. 4. 0.] 156 | return train_X, train_y, test_X, test_y 157 | 158 | def load_UTS(dataset): 159 | # UTS: Univariate time series 160 | # datasets: epilepsy / sleepEDF 161 | print(f'dataset:{dataset}') 162 | train_data = torch.load(f'datasets/{dataset}/train.pt') 163 | test_data = torch.load(f'datasets/{dataset}/test.pt') 164 | # dat_dict = dict() 165 | train_X = train_data["samples"].numpy() 166 | train_y = train_data["labels"].numpy() 167 | test_X = test_data["samples"].numpy() 168 | test_y = test_data["labels"].numpy() 169 | 170 | 171 | train = train_X[:, 1:].astype(np.float64) 172 | train_labels = np.vectorize(train_y[:, 0]) 173 | test = test_X[:, 1:].astype(np.float64) 174 | test_labels = np.vectorize(test_y[:, 0]) 175 | 176 | mean = np.nanmean(train) 177 | std = np.nanstd(train) 178 | train = (train - mean) / std 179 | test = (test - mean) / std 180 | return train[..., np.newaxis], train_labels, test[..., np.newaxis], test_labels 181 | 182 | 183 | def load_forecast_npy(name, univar=False): 184 | data = np.load(f'datasets/{name}.npy') 185 | if univar: 186 | data = data[: -1:] 187 | 188 | train_slice = slice(None, int(0.6 * len(data))) 189 | valid_slice = slice(int(0.6 * len(data)), int(0.8 * len(data))) 190 | test_slice = slice(int(0.8 * len(data)), None) 191 | 192 | scaler = StandardScaler().fit(data[train_slice]) 193 | data = scaler.transform(data) 194 | data = np.expand_dims(data, 0) 195 | 196 | pred_lens = [24, 48, 96, 288, 672] 197 | return data, train_slice, valid_slice, test_slice, scaler, pred_lens, 0 198 | 199 | def _get_time_features(dt): 200 | return np.stack([ 201 | dt.minute.to_numpy(), 202 | dt.hour.to_numpy(), 203 | dt.dayofweek.to_numpy(), 204 | dt.day.to_numpy(), 205 | dt.dayofyear.to_numpy(), 206 | dt.month.to_numpy(), 207 | dt.weekofyear.to_numpy(), 208 | ], axis=1).astype(np.float) 209 | 210 | def load_forecast_csv(name, univar=False): 211 | data = pd.read_csv(f'datasets/{name}.csv', index_col='date', parse_dates=True) 212 | dt_embed = _get_time_features(data.index) 213 | n_covariate_cols = dt_embed.shape[-1] 214 | 215 | if univar: 216 | if name in ('ETTh1', 'ETTh2', 'ETTm1', 'ETTm2'): 217 | data = data[['OT']] 218 | elif name == 'electricity': 219 | data = data[['MT_001']] 220 | else: 221 | data = data.iloc[:, -1:] 222 | 223 | data = data.to_numpy() 224 | if name == 'ETTh1' or name == 'ETTh2': 225 | train_slice = slice(None, 12*30*24) 226 | valid_slice = slice(12*30*24, 16*30*24) 227 | test_slice = slice(16*30*24, 20*30*24) 228 | elif name == 'ETTm1' or name == 'ETTm2': 229 | train_slice = slice(None, 12*30*24*4) 230 | valid_slice = slice(12*30*24*4, 16*30*24*4) 231 | test_slice = slice(16*30*24*4, 20*30*24*4) 232 | else: 233 | train_slice = slice(None, int(0.6 * len(data))) 234 | valid_slice = slice(int(0.6 * len(data)), int(0.8 * len(data))) 235 | test_slice = slice(int(0.8 * len(data)), None) 236 | 237 | scaler = StandardScaler().fit(data[train_slice]) 238 | data = scaler.transform(data) 239 | if name in ('electricity'): 240 | data = np.expand_dims(data.T, -1) # Each variable is an instance rather than a feature 241 | else: 242 | data = np.expand_dims(data, 0) 243 | 244 | if n_covariate_cols > 0: 245 | dt_scaler = StandardScaler().fit(dt_embed[train_slice]) 246 | dt_embed = np.expand_dims(dt_scaler.transform(dt_embed), 0) 247 | data = np.concatenate([np.repeat(dt_embed, data.shape[0], axis=0), data], axis=-1) 248 | 249 | if name in ('ETTh1', 'ETTh2', 'electricity'): 250 | pred_lens = [24, 48, 168, 336, 720] 251 | else: 252 | pred_lens = [24, 48, 96, 288, 672] 253 | 254 | return data, train_slice, valid_slice, test_slice, scaler, pred_lens, n_covariate_cols 255 | -------------------------------------------------------------------------------- /models/TS2Vec/train_TS2Vec.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import time 4 | import datetime 5 | from ts2vec import TS2Vec 6 | import tasks 7 | import datautils 8 | from utils import init_dl_program, name_with_datetime, pkl_save, data_dropout 9 | import matplotlib.pyplot as plt 10 | import numpy as np 11 | 12 | 13 | Class train_TS2Vec(): 14 | 15 | def __init__(self, parser): 16 | self.args = parser.parse_args() 17 | self.method = 'TS2Vec' 18 | self.dataset_name = self.args.dataset_name 19 | self.run_name = self.args.run_description 20 | self.archive = self.args.archive 21 | self.gpu = self.args.device_id 22 | self.batch_size = self.args.batch_size 23 | self.lr = self.args.lr 24 | self.repr_dims = self.args.feature_size 25 | self.max_train_length = self.args.max_train_length 26 | self.iters = self.args.iters 27 | self.epochs = self.args.epochs 28 | self.save_every = self.args.checkpoint_freq 29 | self.seed = self.args.seed 30 | self.max_threads = self.args.max_threads 31 | self.eval = self.args.eval 32 | self.irregular = self.args.irregular 33 | 34 | def excute(self): 35 | device = init_dl_program(self.gpu, seed=self.seed, max_threads=self.max_threads) 36 | 37 | if self.archive == 'UCR': 38 | task_type = 'classification' 39 | train_data, train_labels, test_data, test_labels = datautils.load_UCR(self.dataset) 40 | 41 | elif self.archive == 'UEA': 42 | print('UEA') 43 | task_type = 'classification' 44 | train_data, train_labels, test_data, test_labels = datautils.load_UEA(self.dataset) 45 | elif self.archive == 'HAR' or 'SHAR' or 'wisdm' or 'epilepsy' or 'sleepEDF': 46 | print('HAR/SHAR/wisdm/epilepsy') 47 | task_type = 'classification' 48 | train_data, train_labels, test_data, test_labels = datautils.load_MTS(self.dataset) 49 | 50 | elif self.archive == 'forecast_csv': 51 | task_type = 'forecasting' 52 | data, train_slice, valid_slice, test_slice, scaler, pred_lens, n_covariate_cols = datautils.load_forecast_csv(self.dataset) 53 | train_data = data[:, train_slice] 54 | elif self.archive == 'forecast_csv_univar': 55 | task_type = 'forecasting' 56 | data, train_slice, valid_slice, test_slice, scaler, pred_lens, n_covariate_cols = datautils.load_forecast_csv(self.dataset, univar=True) 57 | train_data = data[:, train_slice] 58 | elif self.archive == 'forecast_npy': 59 | task_type = 'forecasting' 60 | data, train_slice, valid_slice, test_slice, scaler, pred_lens, n_covariate_cols = datautils.load_forecast_npy(self.dataset) 61 | train_data = data[:, train_slice] 62 | elif self.archive == 'forecast_npy_univar': 63 | task_type = 'forecasting' 64 | data, train_slice, valid_slice, test_slice, scaler, pred_lens, n_covariate_cols = datautils.load_forecast_npy(self.dataset, univar=True) 65 | train_data = data[:, train_slice] 66 | else: 67 | raise ValueError(f"Archive type {self.archive} is not supported.") 68 | 69 | if self.irregular > 0: 70 | if task_type == 'classification': 71 | train_data = data_dropout(train_data, self.irregular) 72 | test_data = data_dropout(test_data, self.irregular) 73 | else: 74 | raise ValueError(f"Task type {task_type} is not supported when irregular is positive.") 75 | 76 | config = dict( 77 | batch_size=self.batch_size, 78 | lr=self.lr, 79 | output_dims=self.repr_dims, 80 | max_train_length=self.max_train_length 81 | ) 82 | 83 | if self.save_every is not None: 84 | unit = 'epoch' if self.epochs is not None else 'iter' 85 | config[f'after_{unit}_callback'] = save_checkpoint_callback(self.save_every, unit) 86 | 87 | run_dir = 'training/' + self.dataset + '__' + name_with_datetime(self.run_name) 88 | os.makedirs(run_dir, exist_ok=True) 89 | 90 | t = time.time() 91 | 92 | model = TS2Vec( 93 | input_dims=train_data.shape[-1], 94 | device=device, 95 | **config 96 | ) 97 | loss_log = model.fit( 98 | train_data, 99 | n_epochs=self.epochs, 100 | n_iters=self.iters, 101 | verbose=True 102 | ) 103 | 104 | 105 | train_loss = loss_log 106 | plt.figure() 107 | plt.plot(np.arange(self.epochs), train_loss, label="Train") 108 | plt.title("Loss_%s"%self.dataset) 109 | plt.legend() 110 | plt.savefig(os.path.join("./loss/",'loss_%s.png'%self.dataset)) 111 | plt.show() 112 | 113 | model.save(f'{run_dir}/model.pkl') 114 | 115 | t = time.time() - t 116 | print(f"\nTraining time: {datetime.timedelta(seconds=t)}\n") 117 | 118 | if self.eval: 119 | if task_type == 'classification': #mqw dataset=self.dataset,并更改了change-svm 120 | # out, eval_res = tasks.eval_classification(model, train_data, train_labels, test_data, test_labels, dataset=self.dataset, eval_protocol='svm') 121 | out, eval_res = tasks.eval_classification(model, train_data, train_labels, test_data, test_labels, dataset=self.dataset, eval_protocol='linear') 122 | elif task_type == 'forecasting': 123 | out, eval_res = tasks.eval_forecasting(model, data, train_slice, valid_slice, test_slice, scaler, pred_lens, n_covariate_cols) 124 | else: 125 | assert False 126 | pkl_save(f'{run_dir}/out.pkl', out) 127 | pkl_save(f'{run_dir}/eval_res.pkl', eval_res) 128 | print('Evaluation result:', eval_res) 129 | 130 | print("Finished.") 131 | 132 | def save_checkpoint_callback(save_every=1, unit='epoch'): 133 | assert unit in ('epoch', 'iter') 134 | def callback(model, loss): 135 | n = model.n_epochs if unit == 'epoch' else model.n_iters 136 | if n % save_every == 0: 137 | model.save(f'{run_dir}/model_{n}.pkl') 138 | return callback 139 | -------------------------------------------------------------------------------- /models/TS2Vec/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pickle 4 | import torch 5 | import random 6 | from datetime import datetime 7 | 8 | def pkl_save(name, var): 9 | with open(name, 'wb') as f: 10 | pickle.dump(var, f) 11 | 12 | def pkl_load(name): 13 | with open(name, 'rb') as f: 14 | return pickle.load(f) 15 | 16 | def torch_pad_nan(arr, left=0, right=0, dim=0): 17 | if left > 0: 18 | padshape = list(arr.shape) 19 | padshape[dim] = left 20 | arr = torch.cat((torch.full(padshape, np.nan), arr), dim=dim) 21 | if right > 0: 22 | padshape = list(arr.shape) 23 | padshape[dim] = right 24 | arr = torch.cat((arr, torch.full(padshape, np.nan)), dim=dim) 25 | return arr 26 | 27 | def pad_nan_to_target(array, target_length, axis=0, both_side=False): 28 | assert array.dtype in [np.float16, np.float32, np.float64] 29 | pad_size = target_length - array.shape[axis] 30 | if pad_size <= 0: 31 | return array 32 | npad = [(0, 0)] * array.ndim 33 | if both_side: 34 | npad[axis] = (pad_size // 2, pad_size - pad_size//2) 35 | else: 36 | npad[axis] = (0, pad_size) 37 | return np.pad(array, pad_width=npad, mode='constant', constant_values=np.nan) 38 | 39 | def split_with_nan(x, sections, axis=0): 40 | assert x.dtype in [np.float16, np.float32, np.float64] 41 | arrs = np.array_split(x, sections, axis=axis) 42 | target_length = arrs[0].shape[axis] 43 | for i in range(len(arrs)): 44 | arrs[i] = pad_nan_to_target(arrs[i], target_length, axis=axis) 45 | return arrs 46 | 47 | def take_per_row(A, indx, num_elem): 48 | all_indx = indx[:,None] + np.arange(num_elem) 49 | return A[torch.arange(all_indx.shape[0])[:,None], all_indx] 50 | 51 | def centerize_vary_length_series(x): 52 | prefix_zeros = np.argmax(~np.isnan(x).all(axis=-1), axis=1) 53 | suffix_zeros = np.argmax(~np.isnan(x[:, ::-1]).all(axis=-1), axis=1) 54 | offset = (prefix_zeros + suffix_zeros) // 2 - prefix_zeros 55 | rows, column_indices = np.ogrid[:x.shape[0], :x.shape[1]] 56 | offset[offset < 0] += x.shape[1] 57 | column_indices = column_indices - offset[:, np.newaxis] 58 | return x[rows, column_indices] 59 | 60 | def data_dropout(arr, p): 61 | B, T = arr.shape[0], arr.shape[1] 62 | mask = np.full(B*T, False, dtype=np.bool) 63 | ele_sel = np.random.choice( 64 | B*T, 65 | size=int(B*T*p), 66 | replace=False 67 | ) 68 | mask[ele_sel] = True 69 | res = arr.copy() 70 | res[mask.reshape(B, T)] = np.nan 71 | return res 72 | 73 | def name_with_datetime(prefix='default'): 74 | now = datetime.now() 75 | return prefix + '_' + now.strftime("%Y%m%d_%H%M%S") 76 | 77 | def init_dl_program( 78 | device_name, 79 | seed=None, 80 | use_cudnn=True, 81 | deterministic=False, 82 | benchmark=False, 83 | use_tf32=False, 84 | max_threads=None 85 | ): 86 | import torch 87 | if max_threads is not None: 88 | torch.set_num_threads(max_threads) # intraop 89 | if torch.get_num_interop_threads() != max_threads: 90 | torch.set_num_interop_threads(max_threads) # interop 91 | try: 92 | import mkl 93 | except: 94 | pass 95 | else: 96 | mkl.set_num_threads(max_threads) 97 | 98 | if seed is not None: 99 | random.seed(seed) 100 | seed += 1 101 | np.random.seed(seed) 102 | seed += 1 103 | torch.manual_seed(seed) 104 | 105 | if isinstance(device_name, (str, int)): 106 | device_name = [device_name] 107 | 108 | devices = [] 109 | for t in reversed(device_name): 110 | t_device = torch.device(t) 111 | devices.append(t_device) 112 | if t_device.type == 'cuda': 113 | assert torch.cuda.is_available() 114 | torch.cuda.set_device(t_device) 115 | if seed is not None: 116 | seed += 1 117 | torch.cuda.manual_seed(seed) 118 | devices.reverse() 119 | torch.backends.cudnn.enabled = use_cudnn 120 | torch.backends.cudnn.deterministic = deterministic 121 | torch.backends.cudnn.benchmark = benchmark 122 | 123 | if hasattr(torch.backends.cudnn, 'allow_tf32'): 124 | torch.backends.cudnn.allow_tf32 = use_tf32 125 | torch.backends.cuda.matmul.allow_tf32 = use_tf32 126 | 127 | return devices if len(devices) > 1 else devices[0] 128 | 129 | -------------------------------------------------------------------------------- /models/TST/loss.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from torch.nn import functional as F 4 | 5 | 6 | def get_loss_module(config): 7 | 8 | task = config['task'] 9 | 10 | if (task == "imputation") or (task == "transduction"): 11 | return MaskedMSELoss(reduction='none') # outputs loss for each batch element 12 | 13 | if task == "classification": 14 | return NoFussCrossEntropyLoss(reduction='none') # outputs loss for each batch sample 15 | 16 | if task == "regression": 17 | return nn.MSELoss(reduction='none') # outputs loss for each batch sample 18 | 19 | else: 20 | raise ValueError("Loss module for task '{}' does not exist".format(task)) 21 | 22 | 23 | def l2_reg_loss(model): 24 | """Returns the squared L2 norm of output layer of given model""" 25 | 26 | for name, param in model.named_parameters(): 27 | if name == 'output_layer.weight': 28 | return torch.sum(torch.square(param)) 29 | 30 | 31 | class NoFussCrossEntropyLoss(nn.CrossEntropyLoss): 32 | """ 33 | pytorch's CrossEntropyLoss is fussy: 1) needs Long (int64) targets only, and 2) only 1D. 34 | This function satisfies these requirements 35 | """ 36 | 37 | def forward(self, inp, target): 38 | return F.cross_entropy(inp, target.long().squeeze(), weight=self.weight, 39 | ignore_index=self.ignore_index, reduction=self.reduction) 40 | 41 | 42 | class MaskedMSELoss(nn.Module): 43 | """ Masked MSE Loss 44 | """ 45 | 46 | def __init__(self, reduction: str = 'mean'): 47 | 48 | super().__init__() 49 | 50 | self.reduction = reduction 51 | self.mse_loss = nn.MSELoss(reduction=self.reduction) 52 | 53 | def forward(self, 54 | y_pred: torch.Tensor, y_true: torch.Tensor, mask: torch.BoolTensor) -> torch.Tensor: 55 | """Compute the loss between a target value and a prediction. 56 | 57 | Args: 58 | y_pred: Estimated values 59 | y_true: Target values 60 | mask: boolean tensor with 0s at places where values should be ignored and 1s where they should be considered 61 | 62 | Returns 63 | ------- 64 | if reduction == 'none': 65 | (num_active,) Loss for each active batch element as a tensor with gradient attached. 66 | if reduction == 'mean': 67 | scalar mean loss over batch as a tensor with gradient attached. 68 | """ 69 | 70 | # for this particular loss, one may also elementwise multiply y_pred and y_true with the inverted mask 71 | masked_pred = torch.masked_select(y_pred, mask) 72 | masked_true = torch.masked_select(y_true, mask) 73 | 74 | return self.mse_loss(masked_pred, masked_true) 75 | -------------------------------------------------------------------------------- /models/TST/optimizers.py: -------------------------------------------------------------------------------- 1 | import math 2 | import torch 3 | from torch.optim.optimizer import Optimizer 4 | 5 | 6 | def get_optimizer(name): 7 | 8 | if name == "Adam": 9 | return torch.optim.Adam 10 | elif name == "RAdam": 11 | return RAdam 12 | 13 | 14 | # from https://github.com/LiyuanLucasLiu/RAdam/blob/master/radam/radam.py 15 | class RAdam(Optimizer): 16 | 17 | def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0, degenerated_to_sgd=True): 18 | if not 0.0 <= lr: 19 | raise ValueError("Invalid learning rate: {}".format(lr)) 20 | if not 0.0 <= eps: 21 | raise ValueError("Invalid epsilon value: {}".format(eps)) 22 | if not 0.0 <= betas[0] < 1.0: 23 | raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0])) 24 | if not 0.0 <= betas[1] < 1.0: 25 | raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1])) 26 | 27 | self.degenerated_to_sgd = degenerated_to_sgd 28 | if isinstance(params, (list, tuple)) and len(params) > 0 and isinstance(params[0], dict): 29 | for param in params: 30 | if 'betas' in param and (param['betas'][0] != betas[0] or param['betas'][1] != betas[1]): 31 | param['buffer'] = [[None, None, None] for _ in range(10)] 32 | defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay, 33 | buffer=[[None, None, None] for _ in range(10)]) 34 | super(RAdam, self).__init__(params, defaults) 35 | 36 | def __setstate__(self, state): 37 | super(RAdam, self).__setstate__(state) 38 | 39 | def step(self, closure=None): 40 | 41 | loss = None 42 | if closure is not None: 43 | loss = closure() 44 | 45 | for group in self.param_groups: 46 | 47 | for p in group['params']: 48 | if p.grad is None: 49 | continue 50 | grad = p.grad.data.float() 51 | if grad.is_sparse: 52 | raise RuntimeError('RAdam does not support sparse gradients') 53 | 54 | p_data_fp32 = p.data.float() 55 | 56 | state = self.state[p] 57 | 58 | if len(state) == 0: 59 | state['step'] = 0 60 | state['exp_avg'] = torch.zeros_like(p_data_fp32) 61 | state['exp_avg_sq'] = torch.zeros_like(p_data_fp32) 62 | else: 63 | state['exp_avg'] = state['exp_avg'].type_as(p_data_fp32) 64 | state['exp_avg_sq'] = state['exp_avg_sq'].type_as(p_data_fp32) 65 | 66 | exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq'] 67 | beta1, beta2 = group['betas'] 68 | 69 | exp_avg_sq.mul_(beta2).addcmul_(1 - beta2, grad, grad) 70 | exp_avg.mul_(beta1).add_(1 - beta1, grad) 71 | 72 | state['step'] += 1 73 | buffered = group['buffer'][int(state['step'] % 10)] 74 | if state['step'] == buffered[0]: 75 | N_sma, step_size = buffered[1], buffered[2] 76 | else: 77 | buffered[0] = state['step'] 78 | beta2_t = beta2 ** state['step'] 79 | N_sma_max = 2 / (1 - beta2) - 1 80 | N_sma = N_sma_max - 2 * state['step'] * beta2_t / (1 - beta2_t) 81 | buffered[1] = N_sma 82 | 83 | # more conservative since it's an approximated value 84 | if N_sma >= 5: 85 | step_size = math.sqrt( 86 | (1 - beta2_t) * (N_sma - 4) / (N_sma_max - 4) * (N_sma - 2) / N_sma * N_sma_max / ( 87 | N_sma_max - 2)) / (1 - beta1 ** state['step']) 88 | elif self.degenerated_to_sgd: 89 | step_size = 1.0 / (1 - beta1 ** state['step']) 90 | else: 91 | step_size = -1 92 | buffered[2] = step_size 93 | 94 | # more conservative since it's an approximated value 95 | if N_sma >= 5: 96 | if group['weight_decay'] != 0: 97 | p_data_fp32.add_(-group['weight_decay'] * group['lr'], p_data_fp32) 98 | denom = exp_avg_sq.sqrt().add_(group['eps']) 99 | p_data_fp32.addcdiv_(-step_size * group['lr'], exp_avg, denom) 100 | p.data.copy_(p_data_fp32) 101 | elif step_size > 0: 102 | if group['weight_decay'] != 0: 103 | p_data_fp32.add_(-group['weight_decay'] * group['lr'], p_data_fp32) 104 | p_data_fp32.add_(-step_size * group['lr'], exp_avg) 105 | p.data.copy_(p_data_fp32) 106 | 107 | return loss 108 | 109 | 110 | class PlainRAdam(Optimizer): 111 | 112 | def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0, degenerated_to_sgd=True): 113 | if not 0.0 <= lr: 114 | raise ValueError("Invalid learning rate: {}".format(lr)) 115 | if not 0.0 <= eps: 116 | raise ValueError("Invalid epsilon value: {}".format(eps)) 117 | if not 0.0 <= betas[0] < 1.0: 118 | raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0])) 119 | if not 0.0 <= betas[1] < 1.0: 120 | raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1])) 121 | 122 | self.degenerated_to_sgd = degenerated_to_sgd 123 | defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay) 124 | 125 | super(PlainRAdam, self).__init__(params, defaults) 126 | 127 | def __setstate__(self, state): 128 | super(PlainRAdam, self).__setstate__(state) 129 | 130 | def step(self, closure=None): 131 | 132 | loss = None 133 | if closure is not None: 134 | loss = closure() 135 | 136 | for group in self.param_groups: 137 | 138 | for p in group['params']: 139 | if p.grad is None: 140 | continue 141 | grad = p.grad.data.float() 142 | if grad.is_sparse: 143 | raise RuntimeError('RAdam does not support sparse gradients') 144 | 145 | p_data_fp32 = p.data.float() 146 | 147 | state = self.state[p] 148 | 149 | if len(state) == 0: 150 | state['step'] = 0 151 | state['exp_avg'] = torch.zeros_like(p_data_fp32) 152 | state['exp_avg_sq'] = torch.zeros_like(p_data_fp32) 153 | else: 154 | state['exp_avg'] = state['exp_avg'].type_as(p_data_fp32) 155 | state['exp_avg_sq'] = state['exp_avg_sq'].type_as(p_data_fp32) 156 | 157 | exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq'] 158 | beta1, beta2 = group['betas'] 159 | 160 | exp_avg_sq.mul_(beta2).addcmul_(1 - beta2, grad, grad) 161 | exp_avg.mul_(beta1).add_(1 - beta1, grad) 162 | 163 | state['step'] += 1 164 | beta2_t = beta2 ** state['step'] 165 | N_sma_max = 2 / (1 - beta2) - 1 166 | N_sma = N_sma_max - 2 * state['step'] * beta2_t / (1 - beta2_t) 167 | 168 | # more conservative since it's an approximated value 169 | if N_sma >= 5: 170 | if group['weight_decay'] != 0: 171 | p_data_fp32.add_(-group['weight_decay'] * group['lr'], p_data_fp32) 172 | step_size = group['lr'] * math.sqrt( 173 | (1 - beta2_t) * (N_sma - 4) / (N_sma_max - 4) * (N_sma - 2) / N_sma * N_sma_max / ( 174 | N_sma_max - 2)) / (1 - beta1 ** state['step']) 175 | denom = exp_avg_sq.sqrt().add_(group['eps']) 176 | p_data_fp32.addcdiv_(-step_size, exp_avg, denom) 177 | p.data.copy_(p_data_fp32) 178 | elif self.degenerated_to_sgd: 179 | if group['weight_decay'] != 0: 180 | p_data_fp32.add_(-group['weight_decay'] * group['lr'], p_data_fp32) 181 | step_size = group['lr'] / (1 - beta1 ** state['step']) 182 | p_data_fp32.add_(-step_size, exp_avg) 183 | p.data.copy_(p_data_fp32) 184 | 185 | return loss 186 | 187 | 188 | class AdamW(Optimizer): 189 | 190 | def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0, warmup=0): 191 | if not 0.0 <= lr: 192 | raise ValueError("Invalid learning rate: {}".format(lr)) 193 | if not 0.0 <= eps: 194 | raise ValueError("Invalid epsilon value: {}".format(eps)) 195 | if not 0.0 <= betas[0] < 1.0: 196 | raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0])) 197 | if not 0.0 <= betas[1] < 1.0: 198 | raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1])) 199 | 200 | defaults = dict(lr=lr, betas=betas, eps=eps, 201 | weight_decay=weight_decay, warmup=warmup) 202 | super(AdamW, self).__init__(params, defaults) 203 | 204 | def __setstate__(self, state): 205 | super(AdamW, self).__setstate__(state) 206 | 207 | def step(self, closure=None): 208 | loss = None 209 | if closure is not None: 210 | loss = closure() 211 | 212 | for group in self.param_groups: 213 | 214 | for p in group['params']: 215 | if p.grad is None: 216 | continue 217 | grad = p.grad.data.float() 218 | if grad.is_sparse: 219 | raise RuntimeError('Adam does not support sparse gradients, please consider SparseAdam instead') 220 | 221 | p_data_fp32 = p.data.float() 222 | 223 | state = self.state[p] 224 | 225 | if len(state) == 0: 226 | state['step'] = 0 227 | state['exp_avg'] = torch.zeros_like(p_data_fp32) 228 | state['exp_avg_sq'] = torch.zeros_like(p_data_fp32) 229 | else: 230 | state['exp_avg'] = state['exp_avg'].type_as(p_data_fp32) 231 | state['exp_avg_sq'] = state['exp_avg_sq'].type_as(p_data_fp32) 232 | 233 | exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq'] 234 | beta1, beta2 = group['betas'] 235 | 236 | state['step'] += 1 237 | 238 | exp_avg_sq.mul_(beta2).addcmul_(1 - beta2, grad, grad) 239 | exp_avg.mul_(beta1).add_(1 - beta1, grad) 240 | 241 | denom = exp_avg_sq.sqrt().add_(group['eps']) 242 | bias_correction1 = 1 - beta1 ** state['step'] 243 | bias_correction2 = 1 - beta2 ** state['step'] 244 | 245 | if group['warmup'] > state['step']: 246 | scheduled_lr = 1e-8 + state['step'] * group['lr'] / group['warmup'] 247 | else: 248 | scheduled_lr = group['lr'] 249 | 250 | step_size = scheduled_lr * math.sqrt(bias_correction2) / bias_correction1 251 | 252 | if group['weight_decay'] != 0: 253 | p_data_fp32.add_(-group['weight_decay'] * scheduled_lr, p_data_fp32) 254 | 255 | p_data_fp32.addcdiv_(-step_size, exp_avg, denom) 256 | 257 | p.data.copy_(p_data_fp32) 258 | 259 | return loss 260 | -------------------------------------------------------------------------------- /models/TST/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import sys 4 | import builtins 5 | import functools 6 | import time 7 | import ipdb 8 | from copy import deepcopy 9 | 10 | import numpy as np 11 | import torch 12 | import xlrd 13 | import xlwt 14 | from xlutils.copy import copy 15 | 16 | import logging 17 | logging.basicConfig(format='%(asctime)s | %(levelname)s : %(message)s', level=logging.INFO) 18 | logger = logging.getLogger(__name__) 19 | 20 | 21 | def timer(func): 22 | """Print the runtime of the decorated function""" 23 | @functools.wraps(func) 24 | def wrapper_timer(*args, **kwargs): 25 | start_time = time.perf_counter() # 1 26 | value = func(*args, **kwargs) 27 | end_time = time.perf_counter() # 2 28 | run_time = end_time - start_time # 3 29 | print(f"Finished {func.__name__!r} in {run_time} secs") 30 | return value 31 | return wrapper_timer 32 | 33 | 34 | def save_model(path, epoch, model, optimizer=None): 35 | if isinstance(model, torch.nn.DataParallel): 36 | state_dict = model.module.state_dict() 37 | else: 38 | state_dict = model.state_dict() 39 | data = {'epoch': epoch, 40 | 'state_dict': state_dict} 41 | if not (optimizer is None): 42 | data['optimizer'] = optimizer.state_dict() 43 | torch.save(data, path) 44 | 45 | 46 | def load_model(model, model_path, optimizer=None, resume=False, change_output=False, 47 | lr=None, lr_step=None, lr_factor=None): 48 | start_epoch = 0 49 | checkpoint = torch.load(model_path, map_location=lambda storage, loc: storage) 50 | state_dict = deepcopy(checkpoint['state_dict']) 51 | if change_output: 52 | for key, val in checkpoint['state_dict'].items(): 53 | if key.startswith('output_layer'): 54 | state_dict.pop(key) 55 | model.load_state_dict(state_dict, strict=False) 56 | print('Loaded model from {}. Epoch: {}'.format(model_path, checkpoint['epoch'])) 57 | 58 | # resume optimizer parameters 59 | if optimizer is not None and resume: 60 | if 'optimizer' in checkpoint: 61 | optimizer.load_state_dict(checkpoint['optimizer']) 62 | start_epoch = checkpoint['epoch'] 63 | start_lr = lr 64 | for i in range(len(lr_step)): 65 | if start_epoch >= lr_step[i]: 66 | start_lr *= lr_factor[i] 67 | for param_group in optimizer.param_groups: 68 | param_group['lr'] = start_lr 69 | print('Resumed optimizer with start lr', start_lr) 70 | else: 71 | print('No optimizer parameters in checkpoint.') 72 | if optimizer is not None: 73 | return model, optimizer, start_epoch 74 | else: 75 | return model 76 | 77 | 78 | def load_config(config_filepath): 79 | """ 80 | Using a json file with the master configuration (config file for each part of the pipeline), 81 | return a dictionary containing the entire configuration settings in a hierarchical fashion. 82 | """ 83 | 84 | with open(config_filepath) as cnfg: 85 | config = json.load(cnfg) 86 | 87 | return config 88 | 89 | 90 | def create_dirs(dirs): 91 | """ 92 | Input: 93 | dirs: a list of directories to create, in case these directories are not found 94 | Returns: 95 | exit_code: 0 if success, -1 if failure 96 | """ 97 | try: 98 | for dir_ in dirs: 99 | if not os.path.exists(dir_): 100 | os.makedirs(dir_) 101 | return 0 102 | except Exception as err: 103 | print("Creating directories error: {0}".format(err)) 104 | exit(-1) 105 | 106 | 107 | def export_performance_metrics(filepath, metrics_table, header, book=None, sheet_name="metrics"): 108 | """Exports performance metrics on the validation set for all epochs to an excel file""" 109 | 110 | if book is None: 111 | book = xlwt.Workbook() # new excel work book 112 | 113 | book = write_table_to_sheet([header] + metrics_table, book, sheet_name=sheet_name) 114 | 115 | book.save(filepath) 116 | logger.info("Exported per epoch performance metrics in '{}'".format(filepath)) 117 | 118 | return book 119 | 120 | 121 | def write_row(sheet, row_ind, data_list): 122 | """Write a list to row_ind row of an excel sheet""" 123 | 124 | row = sheet.row(row_ind) 125 | for col_ind, col_value in enumerate(data_list): 126 | row.write(col_ind, col_value) 127 | return 128 | 129 | 130 | def write_table_to_sheet(table, work_book, sheet_name=None): 131 | """Writes a table implemented as a list of lists to an excel sheet in the given work book object""" 132 | 133 | sheet = work_book.add_sheet(sheet_name) 134 | 135 | for row_ind, row_list in enumerate(table): 136 | write_row(sheet, row_ind, row_list) 137 | 138 | return work_book 139 | 140 | 141 | def export_record(filepath, values): 142 | """Adds a list of values as a bottom row of a table in a given excel file""" 143 | 144 | read_book = xlrd.open_workbook(filepath, formatting_info=True) 145 | read_sheet = read_book.sheet_by_index(0) 146 | last_row = read_sheet.nrows 147 | 148 | work_book = copy(read_book) 149 | sheet = work_book.get_sheet(0) 150 | write_row(sheet, last_row, values) 151 | work_book.save(filepath) 152 | 153 | 154 | def register_record(filepath, timestamp, experiment_name, best_metrics, final_metrics=None, comment=''): 155 | """ 156 | Adds the best and final metrics of a given experiment as a record in an excel sheet with other experiment records. 157 | Creates excel sheet if it doesn't exist. 158 | Args: 159 | filepath: path of excel file keeping records 160 | timestamp: string 161 | experiment_name: string 162 | best_metrics: dict of metrics at best epoch {metric_name: metric_value}. Includes "epoch" as first key 163 | final_metrics: dict of metrics at final epoch {metric_name: metric_value}. Includes "epoch" as first key 164 | comment: optional description 165 | """ 166 | metrics_names, metrics_values = zip(*best_metrics.items()) 167 | row_values = [timestamp, experiment_name, comment] + list(metrics_values) 168 | if final_metrics is not None: 169 | final_metrics_names, final_metrics_values = zip(*final_metrics.items()) 170 | row_values += list(final_metrics_values) 171 | 172 | if not os.path.exists(filepath): # Create a records file for the first time 173 | logger.warning("Records file '{}' does not exist! Creating new file ...".format(filepath)) 174 | directory = os.path.dirname(filepath) 175 | if len(directory) and not os.path.exists(directory): 176 | os.makedirs(directory) 177 | header = ["Timestamp", "Name", "Comment"] + ["Best " + m for m in metrics_names] 178 | if final_metrics is not None: 179 | header += ["Final " + m for m in final_metrics_names] 180 | book = xlwt.Workbook() # excel work book 181 | book = write_table_to_sheet([header, row_values], book, sheet_name="records") 182 | book.save(filepath) 183 | else: 184 | try: 185 | export_record(filepath, row_values) 186 | except Exception as x: 187 | alt_path = os.path.join(os.path.dirname(filepath), "record_" + experiment_name) 188 | logger.error("Failed saving in: '{}'! Will save here instead: {}".format(filepath, alt_path)) 189 | export_record(alt_path, row_values) 190 | filepath = alt_path 191 | 192 | logger.info("Exported performance record to '{}'".format(filepath)) 193 | 194 | 195 | class Printer(object): 196 | """Class for printing output by refreshing the same line in the console, e.g. for indicating progress of a process""" 197 | 198 | def __init__(self, console=True): 199 | 200 | if console: 201 | self.print = self.dyn_print 202 | else: 203 | self.print = builtins.print 204 | 205 | @staticmethod 206 | def dyn_print(data): 207 | """Print things to stdout on one line, refreshing it dynamically""" 208 | sys.stdout.write("\r\x1b[K" + data.__str__()) 209 | sys.stdout.flush() 210 | 211 | 212 | def readable_time(time_difference): 213 | """Convert a float measuring time difference in seconds into a tuple of (hours, minutes, seconds)""" 214 | 215 | hours = time_difference // 3600 216 | minutes = (time_difference // 60) % 60 217 | seconds = time_difference % 60 218 | 219 | return hours, minutes, seconds 220 | 221 | 222 | def check_model(model, verbose=False, zero_thresh=1e-8, inf_thresh=1e6, stop_on_error=False): 223 | status_ok = True 224 | for name, param in model.named_parameters(): 225 | param_ok = check_tensor(param, verbose=verbose, zero_thresh=zero_thresh, inf_thresh=inf_thresh) 226 | if not param_ok: 227 | status_ok = False 228 | print("Parameter '{}' PROBLEM".format(name)) 229 | grad_ok = True 230 | if param.grad is not None: 231 | grad_ok = check_tensor(param.grad, verbose=verbose, zero_thresh=zero_thresh, inf_thresh=inf_thresh) 232 | if not grad_ok: 233 | status_ok = False 234 | print("Gradient of parameter '{}' PROBLEM".format(name)) 235 | if stop_on_error and not (param_ok and grad_ok): 236 | ipdb.set_trace() 237 | 238 | if status_ok: 239 | print("Model Check: OK") 240 | else: 241 | print("Model Check: PROBLEM") 242 | 243 | 244 | def check_tensor(X, verbose=True, zero_thresh=1e-8, inf_thresh=1e6): 245 | 246 | is_nan = torch.isnan(X) 247 | if is_nan.any(): 248 | print("{}/{} nan".format(torch.sum(is_nan), X.numel())) 249 | return False 250 | 251 | num_small = torch.sum(torch.abs(X) < zero_thresh) 252 | num_large = torch.sum(torch.abs(X) > inf_thresh) 253 | 254 | if verbose: 255 | print("Shape: {}, {} elements".format(X.shape, X.numel())) 256 | print("No 'nan' values") 257 | print("Min: {}".format(torch.min(X))) 258 | print("Median: {}".format(torch.median(X))) 259 | print("Max: {}".format(torch.max(X))) 260 | 261 | print("Histogram of values:") 262 | values = X.view(-1).detach().numpy() 263 | hist, binedges = np.histogram(values, bins=20) 264 | for b in range(len(binedges) - 1): 265 | print("[{}, {}): {}".format(binedges[b], binedges[b + 1], hist[b])) 266 | 267 | print("{}/{} abs. values < {}".format(num_small, X.numel(), zero_thresh)) 268 | print("{}/{} abs. values > {}".format(num_large, X.numel(), inf_thresh)) 269 | 270 | if num_large: 271 | print("{}/{} abs. values > {}".format(num_large, X.numel(), inf_thresh)) 272 | return False 273 | 274 | return True 275 | 276 | 277 | def count_parameters(model, trainable=False): 278 | if trainable: 279 | return sum(p.numel() for p in model.parameters() if p.requires_grad) 280 | else: 281 | return sum(p.numel() for p in model.parameters()) 282 | 283 | 284 | def recursively_hook(model, hook_fn): 285 | for name, module in model.named_children(): #model._modules.items(): 286 | if len(list(module.children())) > 0: # if not leaf node 287 | for submodule in module.children(): 288 | recursively_hook(submodule, hook_fn) 289 | else: 290 | module.register_forward_hook(hook_fn) 291 | 292 | 293 | def compute_loss(net: torch.nn.Module, 294 | dataloader: torch.utils.data.DataLoader, 295 | loss_function: torch.nn.Module, 296 | device: torch.device = 'cpu') -> torch.Tensor: 297 | """Compute the loss of a network on a given dataset. 298 | 299 | Does not compute gradient. 300 | 301 | Parameters 302 | ---------- 303 | net: 304 | Network to evaluate. 305 | dataloader: 306 | Iterator on the dataset. 307 | loss_function: 308 | Loss function to compute. 309 | device: 310 | Torch device, or :py:class:`str`. 311 | 312 | Returns 313 | ------- 314 | Loss as a tensor with no grad. 315 | """ 316 | running_loss = 0 317 | with torch.no_grad(): 318 | for x, y in dataloader: 319 | netout = net(x.to(device)).cpu() 320 | running_loss += loss_function(y, netout) 321 | 322 | return running_loss / len(dataloader) 323 | -------------------------------------------------------------------------------- /models/TSTCC/TC.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import numpy as np 4 | from .attention import Seq_Transformer 5 | 6 | 7 | 8 | class TC(nn.Module): 9 | def __init__(self, configs, device): 10 | super(TC, self).__init__() 11 | self.num_channels = configs.final_out_channels 12 | self.timestep = configs.TC.timesteps 13 | self.Wk = nn.ModuleList([nn.Linear(configs.TC.hidden_dim, self.num_channels) for i in range(self.timestep)]) 14 | self.lsoftmax = nn.LogSoftmax() 15 | self.device = device 16 | 17 | self.projection_head = nn.Sequential( 18 | nn.Linear(configs.TC.hidden_dim, configs.final_out_channels // 2), 19 | nn.BatchNorm1d(configs.final_out_channels // 2), 20 | nn.ReLU(inplace=True), 21 | nn.Linear(configs.final_out_channels // 2, configs.final_out_channels // 4), 22 | ) 23 | # mqw-begin 24 | self.seq_transformer = Seq_Transformer(seq_size=self.timestep, patch_size=self.num_channels, dim=configs.TC.hidden_dim, depth=4, heads=4, mlp_dim=64, emb_dropout=0.) 25 | # mqw-end 26 | def forward(self, features_aug1, features_aug2): 27 | z_aug1 = features_aug1 # features are (batch_size, #channels, seq_len) 28 | seq_len = z_aug1.shape[2] 29 | z_aug1 = z_aug1.transpose(1, 2) 30 | 31 | z_aug2 = features_aug2 32 | z_aug2 = z_aug2.transpose(1, 2) 33 | 34 | batch = z_aug1.shape[0] 35 | t_samples = torch.randint(seq_len - self.timestep, size=(1,)).long().to(self.device) # randomly pick time stamps 36 | 37 | nce = 0 # average over timestep and batch 38 | encode_samples = torch.empty((self.timestep, batch, self.num_channels)).float().to(self.device) 39 | 40 | for i in np.arange(1, self.timestep + 1): 41 | encode_samples[i - 1] = z_aug2[:, t_samples + i, :].view(batch, self.num_channels) 42 | forward_seq = z_aug1[:, :t_samples + 1, :] 43 | 44 | c_t = self.seq_transformer(forward_seq) 45 | 46 | pred = torch.empty((self.timestep, batch, self.num_channels)).float().to(self.device) 47 | for i in np.arange(0, self.timestep): 48 | linear = self.Wk[i] 49 | pred[i] = linear(c_t) 50 | for i in np.arange(0, self.timestep): 51 | total = torch.mm(encode_samples[i], torch.transpose(pred[i], 0, 1)) 52 | nce += torch.sum(torch.diag(self.lsoftmax(total))) 53 | nce /= -1. * batch * self.timestep 54 | return nce, self.projection_head(c_t) -------------------------------------------------------------------------------- /models/TSTCC/loss.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | 4 | class NTXentLoss(torch.nn.Module): 5 | 6 | def __init__(self, device, batch_size, temperature, use_cosine_similarity): 7 | super(NTXentLoss, self).__init__() 8 | self.batch_size = batch_size 9 | self.temperature = temperature 10 | self.device = device 11 | self.softmax = torch.nn.Softmax(dim=-1) 12 | self.mask_samples_from_same_repr = self._get_correlated_mask().type(torch.bool) 13 | self.similarity_function = self._get_similarity_function(use_cosine_similarity) 14 | self.criterion = torch.nn.CrossEntropyLoss(reduction="sum") 15 | 16 | def _get_similarity_function(self, use_cosine_similarity): 17 | if use_cosine_similarity: 18 | self._cosine_similarity = torch.nn.CosineSimilarity(dim=-1) 19 | return self._cosine_simililarity 20 | else: 21 | return self._dot_simililarity 22 | 23 | def _get_correlated_mask(self): 24 | diag = np.eye(2 * self.batch_size) 25 | l1 = np.eye((2 * self.batch_size), 2 * self.batch_size, k=-self.batch_size) 26 | l2 = np.eye((2 * self.batch_size), 2 * self.batch_size, k=self.batch_size) 27 | mask = torch.from_numpy((diag + l1 + l2)) 28 | mask = (1 - mask).type(torch.bool) 29 | return mask.to(self.device) 30 | 31 | @staticmethod 32 | def _dot_simililarity(x, y): 33 | v = torch.tensordot(x.unsqueeze(1), y.T.unsqueeze(0), dims=2) 34 | # x shape: (N, 1, C) 35 | # y shape: (1, C, 2N) 36 | # v shape: (N, 2N) 37 | return v 38 | 39 | def _cosine_simililarity(self, x, y): 40 | # x shape: (N, 1, C) 41 | # y shape: (1, 2N, C) 42 | # v shape: (N, 2N) 43 | v = self._cosine_similarity(x.unsqueeze(1), y.unsqueeze(0)) 44 | return v 45 | 46 | def forward(self, zis, zjs): 47 | representations = torch.cat([zjs, zis], dim=0) 48 | 49 | similarity_matrix = self.similarity_function(representations, representations) 50 | 51 | # filter out the scores from the positive samples 52 | l_pos = torch.diag(similarity_matrix, self.batch_size) 53 | r_pos = torch.diag(similarity_matrix, -self.batch_size) 54 | positives = torch.cat([l_pos, r_pos]).view(2 * self.batch_size, 1) 55 | 56 | negatives = similarity_matrix[self.mask_samples_from_same_repr].view(2 * self.batch_size, -1) 57 | 58 | logits = torch.cat((positives, negatives), dim=1) 59 | logits /= self.temperature 60 | 61 | labels = torch.zeros(2 * self.batch_size).to(self.device).long() 62 | loss = self.criterion(logits, labels) 63 | 64 | return loss / (2 * self.batch_size) 65 | -------------------------------------------------------------------------------- /models/TSTCC/model.py: -------------------------------------------------------------------------------- 1 | from torch import nn 2 | from config_files.epilepsy_Configs import Config as Configs 3 | import torch 4 | class base_Model(nn.Module): 5 | def __init__(self, configs): 6 | super(base_Model, self).__init__() 7 | # Original-HAR 8 | self.conv_block1 = nn.Sequential( 9 | nn.Conv1d(configs.input_channels, 32, kernel_size=configs.kernel_size, 10 | stride=configs.stride, bias=False, padding=(configs.kernel_size // 2)), 11 | nn.BatchNorm1d(32), 12 | nn.ReLU(), 13 | nn.MaxPool1d(kernel_size=2, stride=2, padding=1), 14 | nn.Dropout(configs.dropout) 15 | ) 16 | 17 | self.conv_block2 = nn.Sequential( 18 | nn.Conv1d(32, 64, kernel_size=8, stride=1, bias=False, padding=4), 19 | nn.BatchNorm1d(64), 20 | nn.ReLU(), 21 | nn.MaxPool1d(kernel_size=2, stride=2, padding=1) 22 | ) 23 | 24 | self.conv_block3 = nn.Sequential( 25 | nn.Conv1d(64, configs.final_out_channels, kernel_size=8, stride=1, bias=False, padding=4), 26 | nn.BatchNorm1d(configs.final_out_channels), 27 | nn.ReLU(), 28 | nn.MaxPool1d(kernel_size=2, stride=2, padding=1), 29 | ) 30 | model_output_dim = configs.features_len 31 | self.logits = nn.Linear(model_output_dim * configs.final_out_channels, configs.num_classes) 32 | 33 | def forward(self, x_in): 34 | print(f'x_in.shape:{x_in.shape}') # har: [128, 9, 128] & shar: [128,3,151] $ wisdm: [128,3,256] 35 | x = self.conv_block1(x_in) 36 | print(x.shape) #[128, 32, 65] & [128, 32, 77] & [128, 32, 129] 37 | x = self.conv_block2(x) 38 | print(x.shape) #[128, 64, 34] & [128, 64, 40]) & [128, 64, 66] 39 | x = self.conv_block3(x) 40 | print(x.shape) #[128, 128, 18] & [128, 128, 21] & [128, 128, 34] 41 | 42 | x_flat = x.reshape(x.shape[0], -1) 43 | print(f'x_flat.shape:{x_flat.shape}') 44 | logits = self.logits(x_flat) 45 | return logits, x 46 | # logits [128-batch_size, 2-num_classes] 47 | # x [128-batch_size, 128-final_out_channels, 24-features_len] 48 | 49 | if __name__ == '__main__': 50 | finalconfigs = Configs() 51 | m = base_Model(finalconfigs) 52 | x = torch.randn(32,1,178) 53 | y = m(x)[1] 54 | print(y.shape) #[128, 128, 24] -------------------------------------------------------------------------------- /models/TSTCC/train_TSTCC.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | import os 4 | import numpy as np 5 | from datetime import datetime 6 | import argparse 7 | 8 | from sklearn.manifold import TSNE 9 | import matplotlib.pyplot as plt 10 | 11 | from config_files.HAR_Configs import Config 12 | from utils import _logger, set_requires_grad 13 | from ..data_loader.dataset_loader import data_generator 14 | from trainer import Trainer, model_evaluate 15 | from TC import TC 16 | from utils import _calc_metrics, copy_Files 17 | from model import base_Model 18 | 19 | class train_TSTCC(): 20 | 21 | def __init__(self, parser): 22 | self.args = parser.parse_args() 23 | # Args selections 24 | self.start_time = datetime.now() 25 | self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 26 | self.experiment_description = self.args.experiment_description 27 | self.dataset_name = self.args.dataset_name 28 | self.method = 'TS-TCC' 29 | self.training_mode = self.args.training_mode 30 | self.run_description = self.args.run_description 31 | self.logs_save_dir = self.args.logs_save_dir 32 | # ##### random seeds for reproducibility ######## 33 | self.SEED = self.args.seed 34 | 35 | def plot_embedding(self,data, label, title): 36 | x_min, x_max = np.min(data, 0), np.max(data, 0) 37 | data = (data - x_min) / (x_max - x_min) 38 | fig = plt.figure(figsize=(15, 10)) 39 | ax = plt.subplot(111) 40 | ### 2D 41 | if self.dataset_name == 'SHAR' or 'CharacterTrajectories': 42 | for i in range(data.shape[0]): 43 | plt.text(data[i, 0], data[i, 1], int(label[i]), color=plt.cm.tab20(label[i] / 20), 44 | fontdict={'weight': 'bold', 'size': 7}) 45 | plt.xticks() 46 | plt.yticks() 47 | plt.title(title, fontsize=14) 48 | elif self.dataset_name == 'PhonemeSpectra': 49 | for i in range(data.shape[0]): 50 | plt.text(data[i, 0], data[i, 1], int(label[i]), color=plt.cm.tab20(label[i] / 20), 51 | fontdict={'weight': 'bold', 'size': 7}) 52 | 53 | plt.xticks() 54 | plt.yticks() 55 | plt.title(title, fontsize=14) 56 | else: 57 | for i in range(data.shape[0]): 58 | plt.text(data[i, 0], data[i, 1], int(label[i]), color=plt.cm.tab10(label[i] / 10), 59 | fontdict={'weight': 'bold', 'size': 7}) 60 | plt.xticks() 61 | plt.yticks() 62 | plt.title(title, fontsize=14) 63 | return fig 64 | 65 | 66 | def excute(self): 67 | os.makedirs(self.logs_save_dir, exist_ok=True) 68 | 69 | exec(f'from config_files.{self.dataset_name}_Configs import Config') 70 | configs = Config() 71 | 72 | print(self.SEED) 73 | torch.manual_seed(self.SEED) 74 | torch.backends.cudnn.deterministic = False 75 | torch.backends.cudnn.benchmark = False 76 | np.random.seed(self.SEED) 77 | 78 | experiment_log_dir = os.path.join(self.logs_save_dir, self.experiment_description, self.run_description, 79 | self.training_mode + f"_seed_{self.SEED}") 80 | os.makedirs(experiment_log_dir, exist_ok=True) 81 | 82 | # loop through domains 83 | counter = 0 84 | src_counter = 0 85 | 86 | # Logging 87 | log_file_name = os.path.join(experiment_log_dir, f"logs_{datetime.now().strftime('%d_%m_%Y_%H_%M_%S')}.log") 88 | logger = _logger(log_file_name) 89 | logger.debug("=" * 45) 90 | logger.debug(f'Dataset: {self.dataset_name}') 91 | logger.debug(f'Method: {self.method}') 92 | logger.debug(f'Mode: {self.training_mode}') 93 | logger.debug("=" * 45) 94 | 95 | # Load datasets 96 | data_path = f"./data/{self.dataset_name}" 97 | # train_dl, valid_dl, test_dl = data_generator(data_path, configs, training_mode) 98 | train_dl, test_dl = data_generator(data_path, configs, self.training_mode) 99 | # print(train_dl) 100 | logger.debug("Data loaded ...") 101 | 102 | # Load Model 103 | model = base_Model(configs).to(self.device) 104 | temporal_contr_model = TC(configs, self.device).to(self.device) 105 | 106 | if self.training_mode == "fine_tune": 107 | # load saved model of this experiment 108 | load_from = os.path.join( 109 | os.path.join(self.logs_save_dir, self.experiment_description, self.run_description, f"self_supervised_seed_{self.SEED}", 110 | "saved_models")) 111 | chkpoint = torch.load(os.path.join(load_from, "ckp_last.pt"), map_location=self.device) 112 | pretrained_dict = chkpoint["model_state_dict"] 113 | model_dict = model.state_dict() 114 | del_list = ['logits'] 115 | pretrained_dict_copy = pretrained_dict.copy() 116 | for i in pretrained_dict_copy.keys(): 117 | for j in del_list: 118 | if j in i: 119 | del pretrained_dict[i] 120 | model_dict.update(pretrained_dict) 121 | model.load_state_dict(model_dict) 122 | 123 | if self.training_mode == "train_linear" or "tl" in self.training_mode: 124 | load_from = os.path.join( 125 | os.path.join(self.logs_save_dir, self.experiment_description, self.run_description, f"self_supervised_seed_{self.SEED}", 126 | "saved_models")) 127 | chkpoint = torch.load(os.path.join(load_from, "ckp_last.pt"), map_location=self.device) 128 | pretrained_dict = chkpoint["model_state_dict"] 129 | model_dict = model.state_dict() 130 | 131 | # 1. filter out unnecessary keys 132 | pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict} 133 | 134 | # delete these parameters (Ex: the linear layer at the end) 135 | del_list = ['logits'] 136 | pretrained_dict_copy = pretrained_dict.copy() 137 | for i in pretrained_dict_copy.keys(): 138 | for j in del_list: 139 | if j in i: 140 | del pretrained_dict[i] 141 | model_dict.update(pretrained_dict) 142 | model.load_state_dict(model_dict) 143 | set_requires_grad(model, pretrained_dict, requires_grad=False) # Freeze everything except last layer. 144 | 145 | if self.training_mode == "random_init": 146 | model_dict = model.state_dict() 147 | 148 | # delete all the parameters except for logits 149 | del_list = ['logits'] 150 | pretrained_dict_copy = model_dict.copy() 151 | for i in pretrained_dict_copy.keys(): 152 | for j in del_list: 153 | if j in i: 154 | del model_dict[i] 155 | set_requires_grad(model, model_dict, requires_grad=False) # Freeze everything except last layer. 156 | 157 | model_optimizer = torch.optim.Adam(model.parameters(), lr=configs.lr, betas=(configs.beta1, configs.beta2), 158 | weight_decay=3e-4) 159 | temporal_contr_optimizer = torch.optim.Adam(temporal_contr_model.parameters(), lr=configs.lr, 160 | betas=(configs.beta1, configs.beta2), weight_decay=3e-4) 161 | 162 | if self.training_mode == "self_supervised": # to do it only once 163 | copy_Files(os.path.join(self.logs_save_dir, self.experiment_description, self.run_description), self.dataset_name) 164 | 165 | # Trainer 166 | # Trainer(model, temporal_contr_model, model_optimizer, temporal_contr_optimizer, train_dl, valid_dl, test_dl, device, logger, configs, experiment_log_dir, training_mode) 167 | Trainer(model, temporal_contr_model, model_optimizer, temporal_contr_optimizer, train_dl, test_dl, self.device, logger, 168 | configs, experiment_log_dir, self.training_mode) 169 | 170 | if self.training_mode != "self_supervised": 171 | # Testing 172 | outs = model_evaluate(model, temporal_contr_model, test_dl, self.device, self.training_mode) 173 | total_loss, total_acc, pred_labels, true_labels, representations = outs # mqw 174 | _calc_metrics(pred_labels, true_labels, experiment_log_dir, self.args.home_path, self.dataset_name) 175 | 176 | ### TSNE Embeddings of representations 177 | print('Starting to compute t-SNE Embeddings...') 178 | ts = TSNE(n_components=2, init='pca', 179 | random_state=0) 180 | print(f'x.shape{representations.shape}') 181 | result = ts.fit_transform(representations) 182 | fig = self.plot_embedding(result, true_labels, 183 | f't-SNE Embeddings of Time Series Representations---Dataset: {self.dataset_name}') # 显示图像 184 | if not os.path.exists(f'./eb/eb_{self.dataset_name}'): 185 | os.makedirs(f'./eb/eb_{self.dataset_name}') 186 | plt.savefig(f'./eb/eb_{self.dataset_name}/{self.dataset_name}_pca_2d.png', format='png', 187 | bbox_inches='tight') # change-16 命名 init='pca'//'random' 以及2d还是3d 188 | plt.show() 189 | 190 | logger.debug(f"Training time is : {datetime.now() - self.start_time}") 191 | 192 | -------------------------------------------------------------------------------- /models/TSTCC/utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import random 3 | import numpy as np 4 | import pandas as pd 5 | import os 6 | import sys 7 | import logging 8 | from sklearn.metrics import classification_report, cohen_kappa_score, confusion_matrix, accuracy_score 9 | from shutil import copy 10 | import matplotlib.pyplot as plt 11 | 12 | def plot_confusion_matrix(cm, labels_name, title): 13 | plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues) 14 | plt.title(title) 15 | plt.colorbar() 16 | num_local = np.array(range(len(labels_name))) 17 | plt.xticks(num_local, labels_name, rotation=90) 18 | plt.yticks(num_local, labels_name) 19 | plt.ylabel('True label') 20 | plt.xlabel('Predicted label') 21 | for first_index in range(len(cm)): 22 | for second_index in range(len(cm[first_index])): 23 | plt.text(first_index, second_index, cm[first_index][second_index]) 24 | 25 | def set_requires_grad(model, dict_, requires_grad=True): 26 | for param in model.named_parameters(): 27 | if param[0] in dict_: 28 | param[1].requires_grad = requires_grad 29 | 30 | 31 | def fix_randomness(SEED): 32 | random.seed(SEED) 33 | np.random.seed(SEED) 34 | torch.manual_seed(SEED) 35 | torch.cuda.manual_seed(SEED) 36 | torch.backends.cudnn.deterministic = True 37 | 38 | 39 | def epoch_time(start_time, end_time): 40 | elapsed_time = end_time - start_time 41 | elapsed_mins = int(elapsed_time / 60) 42 | elapsed_secs = int(elapsed_time - (elapsed_mins * 60)) 43 | return elapsed_mins, elapsed_secs 44 | 45 | 46 | def _calc_metrics(pred_labels, true_labels, log_dir, home_path, data_type): 47 | pred_labels = np.array(pred_labels).astype(int) 48 | true_labels = np.array(true_labels).astype(int) 49 | 50 | # save targets 51 | labels_save_path = os.path.join(log_dir, "labels") 52 | os.makedirs(labels_save_path, exist_ok=True) 53 | np.save(os.path.join(labels_save_path, "predicted_labels.npy"), pred_labels) 54 | np.save(os.path.join(labels_save_path, "true_labels.npy"), true_labels) 55 | 56 | r = classification_report(true_labels, pred_labels, digits=6, output_dict=True) 57 | cm = confusion_matrix(true_labels, pred_labels) 58 | df = pd.DataFrame(r) 59 | df["cohen"] = cohen_kappa_score(true_labels, pred_labels) 60 | df["accuracy"] = accuracy_score(true_labels, pred_labels) 61 | df = df * 100 62 | 63 | # save classification report 64 | exp_name = os.path.split(os.path.dirname(log_dir))[-1] 65 | training_mode = os.path.basename(log_dir) 66 | file_name = f"{exp_name}_{training_mode}_classification_report.xlsx" 67 | report_Save_path = os.path.join(home_path, log_dir, file_name) 68 | df.to_excel(report_Save_path) 69 | 70 | # save confusion matrix 71 | cm_file_name = f"{exp_name}_{training_mode}_confusion_matrix.torch" 72 | cm_Save_path = os.path.join(home_path, log_dir, cm_file_name) 73 | torch.save(cm, cm_Save_path) 74 | 75 | ### plot confusion matrix 76 | print('Starting to plot confusion matrix...') 77 | dataset_name = data_type 78 | if dataset_name == 'sleepEDF': 79 | labels_name = ['Wake (W)', 'Non-rapid eye movement (N1)', 'Non-rapid eye movement (N2)', 80 | 'Non-rapid eye movement (N3)', 'Rapid Eye Movement (REM)'] # sleepEDF 81 | elif dataset_name == 'HAR': 82 | labels_name = ['WALKING', 'WALKING_UPSTAIRS', 'WALKING_DOWNSTAIRS', 'SITTING', 'STANDING', 'LAYING'] # HAR 83 | elif dataset_name == 'epilepsy': 84 | labels_name = ['epilepsy', 'not epilepsy'] # epilepsy 85 | elif dataset_name == 'SHAR': 86 | labels_name = ['StandingUpFS', 'StandingUpFL', 'Walking', 'Running', 'GoingUpS', 'Jumping', 'GoingDownS', 'LyingDownFS', 'SittingDown', 'FallingForw', 'FallingRight', 'FallingBack', 'HittingObstacle', 'FallingWithPS', 'FallingBackSC', 'Syncope', 'FallingLeft'] # SHAR 87 | elif dataset_name == 'wisdm': 88 | labels_name = ['Walking', 'Jogging', 'Sitting', 'Standing', 'Upstairs', 'Downstairs'] # wisdm 89 | elif dataset_name == 'DuckDuckGeese': 90 | labels_name = ['Black-bellied Whistling Duck', 'Canadian Goose', 'Greylag Goose', 'Pink Footed Goose', 91 | 'White-faced Whistling Duck'] 92 | elif dataset_name == 'FingerMovements': 93 | labels_name = ['Left', 'Right'] 94 | elif dataset_name == 'PenDigits': 95 | labels_name = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] 96 | elif dataset_name == 'PhonemeSpectra': 97 | labels_name = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', 98 | '18', '19', 99 | '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', 100 | '36', '37', '38'] 101 | elif dataset_name == 'StandWalkJump': 102 | labels_name = ['Standing', 'Walking', 'Jumping'] 103 | 104 | 105 | 106 | plot_confusion_matrix(cm, labels_name, f"{dataset_name}--- Confusion Matrix") 107 | plt.subplots_adjust(bottom=0.15) 108 | if not os.path.exists(f'./cm/cm_{dataset_name}'): 109 | os.makedirs(f'./cm/cm_{dataset_name}') 110 | plt.savefig(f'./cm/cm_{dataset_name}/{dataset_name}_cm.png', format='png', bbox_inches='tight') 111 | plt.show() 112 | 113 | 114 | 115 | def _logger(logger_name, level=logging.DEBUG): 116 | """ 117 | Method to return a custom logger with the given name and level 118 | """ 119 | logger = logging.getLogger(logger_name) 120 | logger.setLevel(level) 121 | # format_string = ("%(asctime)s — %(name)s — %(levelname)s — %(funcName)s:" 122 | # "%(lineno)d — %(message)s") 123 | format_string = "%(message)s" 124 | log_format = logging.Formatter(format_string) 125 | # Creating and adding the console handler 126 | console_handler = logging.StreamHandler(sys.stdout) 127 | console_handler.setFormatter(log_format) 128 | logger.addHandler(console_handler) 129 | # Creating and adding the file handler 130 | file_handler = logging.FileHandler(logger_name, mode='a') 131 | file_handler.setFormatter(log_format) 132 | logger.addHandler(file_handler) 133 | return logger 134 | 135 | def copy_Files(destination, data_type): 136 | destination_dir = os.path.join(destination, "model_files") 137 | os.makedirs(destination_dir, exist_ok=True) 138 | copy("train_TSTCC.py", os.path.join(destination_dir, "train_TSTCC.py")) 139 | copy("trainer/trainer.py", os.path.join(destination_dir, "trainer.py")) 140 | copy(f"config_files/{data_type}_Configs.py", os.path.join(destination_dir, f"{data_type}_Configs.py")) 141 | copy("dataloader/augmentations.py", os.path.join(destination_dir, "augmentations.py")) 142 | copy("dataloader/dataloader.py", os.path.join(destination_dir, "dataloader.py")) 143 | copy(f"models/model.py", os.path.join(destination_dir, f"model.py")) 144 | copy("models/loss.py", os.path.join(destination_dir, "loss.py")) 145 | copy("models/TC.py", os.path.join(destination_dir, "TC.py")) 146 | -------------------------------------------------------------------------------- /models/TimeNet/framework.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset 7 | from sklearn.preprocessing import StandardScaler 8 | 9 | 10 | OUTPUT_DIR = "models" 11 | 12 | 13 | class SimpleSeriesDataset(Dataset): 14 | 15 | def __init__(self, series, maxlen=None, normalize=None): 16 | self.series = series 17 | self.normalize = normalize 18 | self.maxlen = maxlen 19 | self.scaler = StandardScaler() if normalize == 'zscore' else None 20 | 21 | def __len__(self): 22 | return len(self.series) 23 | 24 | def __getitem__(self, idx): 25 | series = self.series.iloc[idx] 26 | if self.maxlen is not None and len(series) > self.maxlen: 27 | series = series[:self.maxlen] 28 | if self.normalize: 29 | series = self.scaler.transform(series.values.reshape(-1, 1)) 30 | return torch.Tensor(series) 31 | 32 | 33 | class TimeNet(nn.Module): 34 | 35 | def __init__(self, size, num_layers, dropout=0.0): 36 | super(TimeNet, self).__init__() 37 | self.size = size 38 | self.num_layers = num_layers 39 | self.dropout = dropout 40 | self.encoder = self.build_encoder() 41 | self.decoder = self.build_decoder() 42 | 43 | def build_encoder(self): 44 | encoder_layers = [] 45 | encoder_layers.append(nn.GRU(1, self.size, num_layers=1, batch_first=True)) 46 | for i in range(2, self.num_layers + 1): 47 | encoder_layers.append(nn.GRU(self.size, self.size, num_layers=1, batch_first=True)) 48 | return nn.Sequential(*encoder_layers) 49 | 50 | def build_decoder(self): 51 | decoder_layers = [] 52 | decoder_layers.append(nn.GRU(self.size, self.size, num_layers=1, batch_first=True)) 53 | for i in range(2, self.num_layers + 1): 54 | decoder_layers.append(nn.GRU(self.size, self.size, num_layers=1, batch_first=True)) 55 | decoder_layers.append(nn.Linear(self.size, 1)) 56 | return nn.Sequential(*decoder_layers) 57 | 58 | def forward(self, x): 59 | # Encoder 60 | encode, _ = self.encoder(x) 61 | 62 | # Decoder 63 | decode, _ = self.decoder(torch.flip(encode, [1])) # Reverse the encoded sequence 64 | return decode 65 | 66 | 67 | def train(model, train_loader, optimizer, criterion, lr_scheduler=None, epochs=10, early_stop=5): 68 | run = model.get_run_id() 69 | log_dir = os.path.join(OUTPUT_DIR, run) 70 | weights_path = os.path.join(log_dir, 'weights.pt') 71 | 72 | loaded = False 73 | if os.path.exists(weights_path): 74 | print("Loading {}...".format(weights_path)) 75 | model.load_state_dict(torch.load(weights_path)) 76 | loaded = True 77 | 78 | if (not loaded): 79 | shutil.rmtree(log_dir, ignore_errors=True) 80 | os.makedirs(log_dir) 81 | 82 | weights_path = os.path.join(log_dir, 'weights.pt') 83 | 84 | if lr_scheduler is not None: 85 | optimizer = optim.lr_scheduler(optimizer, mode='min', patience=early_stop, verbose=True, threshold=0.0001, 86 | threshold_mode='rel') 87 | 88 | model.train() 89 | for epoch in range(epochs): 90 | for batch in train_loader: 91 | optimizer.zero_grad() 92 | outputs = model(batch.unsqueeze(2)) 93 | loss = criterion(outputs, batch.unsqueeze(2)) 94 | loss.backward() 95 | optimizer.step() 96 | 97 | # Save model weights 98 | torch.save(model.state_dict(), weights_path) 99 | 100 | return log_dir 101 | 102 | -------------------------------------------------------------------------------- /models/TimeNet/train_TimeNet.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.optim as optim 4 | from torch.optim.lr_scheduler import ReduceLROnPlateau 5 | from framework import TimeNet 6 | from ..data_loader.dataset_loader import data_generator 7 | 8 | class train_TimeNet(): 9 | 10 | def __init__(self, parser): 11 | self.args = parser.parse_args() 12 | self.log_dir = self.args.exp_dir 13 | self.dataset_name = self.args.dataset_name 14 | self.embeddings_dim = self.args.feature_size 15 | self.num_layers = self.args.layers 16 | self.batch_size = self.args.batch_size 17 | self.learning_rate = self.args.lr 18 | self.epochs = self.args.epochs 19 | 20 | def excute(self): 21 | if self.dataset_name == 'HAR': 22 | from config_files.HAR_Configs import Config as Configs 23 | configs = Configs() 24 | train_loader, eval_loader = data_generator('data/HAR', configs, 'self_supervised') 25 | elif self.dataset_name == 'wisdm': 26 | from config_files.wisdm_Configs import Config as Configs 27 | configs = Configs() 28 | train_loader, eval_loader = data_generator('data/wisdm', configs, 'self_supervised') 29 | elif self.dataset_name == 'epilepsy': 30 | from config_files.epilepsy_Configs import Config as Configs 31 | configs = Configs() 32 | train_loader, eval_loader = data_generator('data/epilepsy', configs, 'self_supervised') 33 | elif self.dataset_name == 'SHAR': 34 | from config_files.SHAR_Configs import Config as Configs 35 | configs = Configs() 36 | train_loader, eval_loader = data_generator('data/SHAR', configs, 'self_supervised') 37 | elif self.dataset_name == 'PenDigits': 38 | from config_files.PenDigits_Configs import Config as Configs 39 | configs = Configs() 40 | train_loader, eval_loader = data_generator('data/PenDigits', configs, 'self_supervised') 41 | elif self.dataset_name == 'EigenWorms': 42 | from config_files.EigenWorms_Configs import Config as Configs 43 | configs = Configs() 44 | train_loader, eval_loader = data_generator('data/EigenWorms', configs, 'self_supervised') 45 | elif self.dataset_name == 'FingerMovements': 46 | from config_files.FingerMovements_Configs import Config as Configs 47 | configs = Configs() 48 | train_loader, eval_loader = data_generator('data/FingerMovements', configs, 'self_supervised') 49 | 50 | enc = TimeNet(self.embeddings_dim, num_layers=self.num_layers, dropout=self.dropout) 51 | 52 | criterion = nn.MSELoss() 53 | optimizer = optim.Adam(enc.parameters(), lr=self.learning_rate) 54 | lr_scheduler = ReduceLROnPlateau(optimizer, patience=10) 55 | 56 | for epoch in range(self.epochs): 57 | enc.train() 58 | for data in train_loader: 59 | optimizer.zero_grad() 60 | outputs = enc(data.unsqueeze(2)) # Add a dimension for time steps 61 | loss = criterion(outputs, data.unsqueeze(2)) 62 | loss.backward() 63 | optimizer.step() 64 | 65 | torch.save(enc.state_dict(), 'timenet_model.pth') 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /models/notes.txt: -------------------------------------------------------------------------------- 1 | More models will be uploaded soon 2 | -------------------------------------------------------------------------------- /organization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mqwfrog/ULTS/79f34003a9408e4db0b5a911ab0a27e1bcf6e6e8/organization.png -------------------------------------------------------------------------------- /taxonomy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mqwfrog/ULTS/79f34003a9408e4db0b5a911ab0a27e1bcf6e6e8/taxonomy.png --------------------------------------------------------------------------------