├── .gitignore
├── dataloaders
├── .DS_Store
├── __init__.py
├── dataloader_MOTION_Sense_har.py
├── dataloader_USCHAD_har.py
├── utils.py
└── dataloader_uci_har.py
├── trainers
├── __init__.py
└── maskcae_trainer.py
├── models
├── __init__.py
├── MaskedConvNet.py
├── ConvNet.py
└── Baseline_CNN.py
├── train.py
├── configs
├── yaml
│ ├── maskcaebase.yaml
│ ├── maskcaeConv4Net.yaml
│ ├── maskcaeConv4Net_LP.yaml
│ ├── data.yaml
│ └── data_sup.yaml
├── parser.py
└── base_configs.py
├── requirements.txt
├── pretrain
├── script
│ └── run.py
├── simpletest.py
├── models
│ └── __init__.py
├── utils
│ ├── lr_control.py
│ ├── arg_util.py
│ └── lamb.py
├── sampler.py
├── launch.py
├── dist.py
├── decoder.py
├── encoder.py
└── main.py
├── scripts
└── MaskCAE
│ ├── Base.py
│ └── supevisedlearning.py
├── common.py
├── utils
├── multi_crop.py
├── logger.py
├── metric.py
├── lamb.py
├── setup.py
└── augmentations.py
├── README.md
├── HAR_sklearn.py
├── main.py
├── create_dataset.py
└── MaskCAE
└── pretrain
└── utils
├── lamb.py
└── misc.py
/.gitignore:
--------------------------------------------------------------------------------
1 | /save
2 | **.pyc**
--------------------------------------------------------------------------------
/dataloaders/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cheng-haha/MaskCAE/HEAD/dataloaders/.DS_Store
--------------------------------------------------------------------------------
/trainers/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-03-18 14:25:30
4 | LastEditTime: 2024-08-13 19:27:46
5 | FilePath: /chengdongzhou/action/MaskCAE/trainers/__init__.py
6 | '''
7 | from .maskcae_trainer import maskcae_train
--------------------------------------------------------------------------------
/models/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-02-06 09:50:31
4 | LastEditTime: 2024-08-13 16:04:12
5 | FilePath: /chengdongzhou/action/MaskCAE/models/__init__.py
6 | '''
7 | from .ConvNet import Conv4Net,Conv4NetSup
8 | from .Baseline_CNN import BaseCNN,BaseCNNSup
9 |
--------------------------------------------------------------------------------
/train.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-03-18 14:25:30
4 | LastEditTime: 2024-08-13 19:28:11
5 | FilePath: /chengdongzhou/action/MaskCAE/train.py
6 | '''
7 | from trainers import *
8 |
9 | def MetaTrain(args):
10 | '''
11 | This is also an index.
12 | '''
13 | if args.mode in [ 'maskcae','maskcae_diffmodel'] :
14 | return maskcae_train
15 | else:
16 | raise NotImplementedError
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/configs/yaml/maskcaebase.yaml:
--------------------------------------------------------------------------------
1 |
2 | # parameters
3 | epochs: 100
4 | learning_rate: 0.0001
5 | batch_size: 512
6 | device: 1
7 | times: 1
8 |
9 | model: 'BaseCNN'
10 | pre_trained_path: ''
11 | lr_scale: 0
12 | warm_up_epochs: 0
13 | train_portion: 1.0
14 | mask: -1
15 |
16 | # optim
17 | opt_type: 'adamw'
18 | decay_epochs: 20
19 | gamma: 0.1
20 | momentum: 0.9
21 | weight_decay: 0.0
22 |
23 | use_wandb: False
24 | multicrop: False
25 |
26 | chhander: True
--------------------------------------------------------------------------------
/configs/yaml/maskcaeConv4Net.yaml:
--------------------------------------------------------------------------------
1 |
2 | # parameters
3 | epochs: 100
4 | learning_rate: 0.0001
5 | batch_size: 512
6 | device: 2
7 | times: 1
8 |
9 | model: 'Conv4Net'
10 | pre_trained_path: './MaskCAE/MaskCAE_EXP/'
11 | lr_scale: 0
12 | warm_up_epochs: 0
13 | train_portion: 1.0
14 | mask: 0.3
15 | # optim
16 | opt_type: 'adamw'
17 | decay_epochs: 20
18 | gamma: 0.1
19 | momentum: 0.9
20 | weight_decay: 0.0
21 |
22 | use_wandb: False
23 | multicrop: False
24 | linear_evaluation: False
25 | chhander: True
--------------------------------------------------------------------------------
/configs/yaml/maskcaeConv4Net_LP.yaml:
--------------------------------------------------------------------------------
1 |
2 | # parameters
3 | epochs: 100
4 | learning_rate: 0.0001
5 | batch_size: 512
6 | device: 2
7 | times: 1
8 |
9 | model: 'Conv4Net'
10 | pre_trained_path: './MaskCAE/MaskCAE_EXP/'
11 | lr_scale: 0
12 | warm_up_epochs: 0
13 | train_portion: 1.0
14 | mask: 0.3
15 | # optim
16 | opt_type: 'adamw'
17 | decay_epochs: 20
18 | gamma: 0.1
19 | momentum: 0.9
20 | weight_decay: 0.0
21 |
22 | use_wandb: False
23 | multicrop: False
24 | linear_evaluation: True
25 | chhander: True
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | dataset==1.6.2
2 | einops==0.7.0
3 | h5py==3.10.0
4 | matplotlib==3.7.3
5 | numpy==1.24.4
6 | pandas==2.0.0
7 | pickle5==0.0.11
8 | Pillow
9 | Pillow==10.1.0
10 | plotly==5.18.0
11 | pyfiglet==0.8.post1
12 | pytz==2023.3.post1
13 | PyWavelets==1.4.1
14 | scikit_learn==1.3.2
15 | scipy
16 | seaborn==0.13.0
17 | scikit-image
18 | torch==1.10.0+cu111
19 | torchvision==0.11.0+cu111
20 | tqdm==4.66.1
21 | typed_argument_parser==1.8.1
22 | umap==0.1.1
23 | umap_learn==0.5.4
24 | tap
25 | timm==0.5.4
26 | thop
27 | tensorboard
28 | wandb
29 | tabulate
30 |
--------------------------------------------------------------------------------
/pretrain/script/run.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-03-22 11:34:07
4 | LastEditTime: 2024-08-13 19:33:58
5 | FilePath: /chengdongzhou/action/MaskCAE/pretrain/script/run.py
6 | '''
7 | import os
8 | import argparse
9 | parser = argparse.ArgumentParser()
10 | parser.add_argument('--dataset',type=str,default=None)
11 | parser.add_argument('--device',type=int,default=1)
12 | args = parser.parse_args()
13 | mode = 'Base'
14 | mask_radio = [0.3]
15 | ep = 1000
16 | for radio in mask_radio:
17 | os.system(
18 | f'python pretrain/main.py --dataset_name {args.dataset} --mask {radio} --ep {ep} --device {args.device}'
19 | )
20 |
--------------------------------------------------------------------------------
/configs/yaml/data.yaml:
--------------------------------------------------------------------------------
1 |
2 | # exp_mode: LOCV or Given
3 |
4 | uschad:
5 | filename: "USC-HAD"
6 | overlap_rate: 0.5
7 | down_sample: True
8 | sampling_freq: 30
9 | num_classes: 12
10 | num_channels: 6
11 | window_seconds: 1.0
12 | exp_mode: 'Given'
13 | valid_rate: 0.2
14 |
15 | motion:
16 | filename: "MotionSense"
17 | overlap_rate: 0.5
18 | down_sample: True
19 | sampling_freq: 30
20 | num_classes: 6
21 | num_channels: 12
22 | window_seconds: 1.0
23 | exp_mode: 'LOCV'
24 | valid_rate: 0.2
25 |
26 | ucihar:
27 | filename: "UCI HAR Dataset"
28 | overlap_rate: 0.5
29 | down_sample: True
30 | sampling_freq: 30
31 | num_classes: 6
32 | num_channels: 9
33 | window_seconds: 1.0
34 | exp_mode: 'LOCV'
35 | valid_rate: 0.2
36 |
--------------------------------------------------------------------------------
/configs/yaml/data_sup.yaml:
--------------------------------------------------------------------------------
1 |
2 | # exp_mode: LOCV or Given
3 |
4 | uschad:
5 | filename: "USC-HAD"
6 | overlap_rate: 0.5
7 | down_sample: True
8 | sampling_freq: 30
9 | num_classes: 12
10 | num_channels: 6
11 | window_seconds: 1.0
12 | exp_mode: 'Given'
13 | valid_rate: 0.2
14 |
15 | motion:
16 | filename: "MotionSense"
17 | overlap_rate: 0.5
18 | down_sample: False
19 | sampling_freq: 50
20 | num_classes: 6
21 | num_channels: 12
22 | window_seconds: 2.56
23 | exp_mode: 'Given'
24 | valid_rate: 0.2
25 |
26 | ucihar:
27 | filename: "UCI HAR Dataset"
28 | overlap_rate: 0.5
29 | down_sample: False
30 | sampling_freq: 50
31 | num_classes: 6
32 | num_channels: 9
33 | window_seconds: 2.56
34 | exp_mode: 'Given'
35 | valid_rate: 0.2
36 |
--------------------------------------------------------------------------------
/scripts/MaskCAE/Base.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-03-22 11:34:07
4 | LastEditTime: 2024-08-13 17:49:14
5 | FilePath: /chengdongzhou/action/MaskCAE/scripts/MaskCAE/Base.py
6 | '''
7 | import os
8 | import argparse
9 | parser = argparse.ArgumentParser()
10 | parser.add_argument('--dataset',type=str,default=None)
11 | parser.add_argument('--config',type=str,default=None)
12 | parser.add_argument('--device',type=int,default=1)
13 | parser.add_argument('--times',type=int,default=5)
14 | parser.add_argument('--dataset_pre',type=str,default='self')
15 |
16 | args = parser.parse_args()
17 | mode = 'maskcae'
18 | datasets = ['motion','ucihar','uschad']
19 | if args.dataset :
20 | assert args.dataset in datasets , 'not support this dataset'
21 | datasets = [ dataset.strip() for dataset in args.dataset.split(',')]
22 |
23 | for dataset in datasets:
24 | os.system(
25 | f'python main.py --dataset {dataset} --mode {mode} --config {args.config} --dataset_pre {args.dataset_pre} --device {args.device} --times {args.times}'
26 | )
27 |
--------------------------------------------------------------------------------
/configs/parser.py:
--------------------------------------------------------------------------------
1 | USABLE_TYPES = set([float, int])
2 |
3 |
4 | def trim_preceding_hyphens(st):
5 | i = 0
6 | while st[i] == "-":
7 | i += 1
8 |
9 | return st[i:]
10 |
11 |
12 | def arg_to_varname(st: str):
13 | st = trim_preceding_hyphens(st)
14 | st = st.replace("-", "_")
15 |
16 | return st.split("=")[0]
17 |
18 |
19 | def argv_to_vars(argv):
20 | var_names = []
21 | for arg in argv:
22 | if arg.startswith("-") and arg_to_varname(arg) != "config":
23 | var_names.append(arg_to_varname(arg))
24 |
25 | return var_names
26 |
27 |
28 | def produce_override_string(args, override_args):
29 | lines = []
30 | for v in override_args:
31 | if v != "multigpu":
32 | v_arg = getattr(args, v)
33 | if type(v_arg) in USABLE_TYPES:
34 | lines.append(v + ": " + str(v_arg))
35 | else:
36 | lines.append(v + ": " + f'"{str(v_arg)}"')
37 | else:
38 | lines.append("multigpu: " + str(args.multigpu))
39 |
40 | return "\n# ===== Overrided ===== #\n" + "\n".join(lines)
41 |
--------------------------------------------------------------------------------
/common.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2024-08-13 13:08:07
4 | LastEditTime: 2024-08-13 19:09:44
5 | FilePath: /chengdongzhou/action/MaskCAE/common.py
6 | '''
7 |
8 | channel_list = {
9 | 'ucihar': [ 16, 32, 64, 128, 6 ],
10 | 'motion': [ 16, 32, 64, 128, 6 ],
11 | 'uschad': [ 16, 32, 64, 128, 12 ],
12 | }
13 |
14 | conv_list = {
15 | 'ucihar': [ (5,1), (1,1), (2,0) ],
16 | 'motion': [ (5,1), (1,1), (2,0) ],
17 | 'uschad': [ (5,1), (1,1), (2,0) ],
18 |
19 | }
20 |
21 | maxp_list = {
22 | 'ucihar': [ (5,1), (2,1) , (2,0) ],
23 | 'motion': [ (5,1), (2,1) , (2,0) ],
24 | 'uschad': [ (5,1), (2,1) , (2,0) ],
25 | }
26 |
27 | first_maxp_list = {
28 | 'ucihar': [ (5,1), (4,1), (2,0) ],
29 | 'uschad': [ (5,1), (4,1), (2,0) ],
30 | 'motion': [ (5,1), (4,1), (2,0) ],
31 | }
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/pretrain/simpletest.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2024-08-13 13:08:14
4 | LastEditTime: 2024-08-13 16:09:47
5 | FilePath: /chengdongzhou/action/MaskCAE/pretrain/simpletest.py
6 | '''
7 |
8 | import turtle
9 | import torch
10 | import encoder
11 | from decoder import LightDecoder
12 | from models import build_sparse_encoder
13 | from MaskCAE import MaskCAE
14 | class dotdict(dict):
15 | """dot.notation access to dictionary attributes"""
16 | __getattr__ = dict.get
17 | __setattr__ = dict.__setitem__
18 | __delattr__ = dict.__delitem__
19 | args = dotdict()
20 | args.model = 'Conv4Net'
21 | args.input_size: turtle = (33,12)
22 | args.sbn = False
23 | args.dp: float = 0.0
24 | args.mask = 0.6
25 | args.densify_norm: str = ''
26 | enc: encoder.SparseEncoder = build_sparse_encoder(args.model, input_size=args.input_size, sbn=args.sbn, drop_path_rate=args.dp, verbose=False)
27 | dec = LightDecoder(enc.downsample_raito, sbn=args.sbn)
28 | model = MaskCAE(
29 | sparse_encoder = enc,
30 | dense_decoder = dec,
31 | mask_ratio = args.mask,
32 | densify_norm = args.densify_norm,
33 | sbn = args.sbn,
34 | ).to(args.device)
35 | inp = torch.randn(2,1,128,12)
36 | loss = model(inp, active_b1fs=None, vis=False)
37 | print(loss)
--------------------------------------------------------------------------------
/scripts/MaskCAE/supevisedlearning.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-03-22 11:34:07
4 | LastEditTime: 2024-08-13 19:11:15
5 | FilePath: /chengdongzhou/action/MaskCAE/scripts/MaskCAE/supevisedlearning.py
6 | '''
7 | import os
8 | import argparse
9 | parser = argparse.ArgumentParser()
10 | parser.add_argument('--dataset',type=str,default=None)
11 | parser.add_argument('--config',type=str,default=None)
12 | parser.add_argument('--device',type=int,default=2)
13 | parser.add_argument('--times',type=int,default=5)
14 | parser.add_argument('--dataset_pre',type=str,default='Sup')
15 |
16 | args = parser.parse_args()
17 | mode = 'maskcae_diffmodel'
18 | datasets = ['motion','ucihar','uschad']
19 | models = ['Transformer_HAR','DeepConvLstmAttn','SA_HAR']
20 | # models = ['BaseCNNSup','Conv4NetSup']
21 | if args.dataset :
22 | assert args.dataset in datasets , 'not support this dataset'
23 | datasets = [ dataset.strip() for dataset in args.dataset.split(',')]
24 |
25 | for dataset in datasets:
26 | for model in models:
27 | if model == 'Conv4NetSup':
28 | os.system(
29 | f'python main.py --dataset {dataset} --mode {mode} --config {args.config} --dataset_pre {args.dataset_pre} --model {model} --times {args.times} --mask {0.3}'
30 | )
31 |
32 | else:
33 | os.system(
34 | f'python main.py --dataset {dataset} --mode {mode} --config {args.config} --dataset_pre {args.dataset_pre} --model {model} --times {args.times}'
35 | )
36 |
--------------------------------------------------------------------------------
/pretrain/models/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-06-04 12:00:53
4 | LastEditTime: 2024-08-13 19:42:18
5 | FilePath: /chengdongzhou/action/MaskCAE/pretrain/models/__init__.py
6 | '''
7 | # Copyright (c) ByteDance, Inc. and its affiliates.
8 | # All rights reserved.
9 | #
10 | # This source code is licensed under the license found in the
11 | # LICENSE file in the root directory of this source tree.
12 |
13 | import torch
14 | from timm import create_model
15 | from timm.loss import SoftTargetCrossEntropy
16 | from timm.models.layers import DropPath
17 |
18 |
19 | from models.custom import ConvNet4, ConvNet3,ConvNetSup
20 |
21 |
22 | # log more
23 | def _ex_repr(self):
24 | return ', '.join(
25 | f'{k}=' + (f'{v:g}' if isinstance(v, float) else str(v))
26 | for k, v in vars(self).items()
27 | if not k.startswith('_') and k != 'training'
28 | and not isinstance(v, (torch.nn.Module, torch.Tensor))
29 | )
30 | for clz in (torch.nn.CrossEntropyLoss, SoftTargetCrossEntropy, DropPath):
31 | if hasattr(clz, 'extra_repr'):
32 | clz.extra_repr = _ex_repr
33 | else:
34 | clz.__repr__ = lambda self: f'{type(self).__name__}({_ex_repr(self)})'
35 |
36 |
37 | pretrain_default_model_kwargs = {
38 | 'Conv4Net': dict(),
39 | 'Conv4NetSup': dict(),
40 | 'Conv3Net': dict(),
41 | }
42 | for kw in pretrain_default_model_kwargs.values():
43 | kw['pretrained'] = False
44 | kw['num_classes'] = 0
45 | # kw['global_pool'] = ''
46 |
47 |
48 | def build_sparse_encoder(name: str, dataset_name:str ,input_size: tuple, sbn=False, drop_path_rate=0.0, verbose=False,mode=''):
49 | from encoder import SparseEncoder
50 |
51 | kwargs = pretrain_default_model_kwargs[name]
52 | kwargs['data_name'] = dataset_name
53 | if drop_path_rate != 0:
54 | kwargs['drop_path_rate'] = drop_path_rate
55 | print(f'[build_sparse_encoder] model kwargs={kwargs}')
56 | cnn = create_model(name, **kwargs)
57 | return SparseEncoder(cnn, input_size=input_size, sbn=sbn, verbose=verbose,mode=mode)
58 |
59 |
--------------------------------------------------------------------------------
/pretrain/utils/lr_control.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) ByteDance, Inc. and its affiliates.
2 | # All rights reserved.
3 | #
4 | # This source code is licensed under the license found in the
5 | # LICENSE file in the root directory of this source tree.
6 |
7 | import math
8 | from pprint import pformat
9 |
10 |
11 | def lr_wd_annealing(optimizer, peak_lr, wd, wd_end, cur_it, wp_it, max_it):
12 | wp_it = round(wp_it)
13 | if cur_it < wp_it:
14 | cur_lr = 0.005 * peak_lr + 0.995 * peak_lr * cur_it / wp_it
15 | else:
16 | ratio = (cur_it - wp_it) / (max_it - 1 - wp_it)
17 | cur_lr = 0.001 * peak_lr + 0.999 * peak_lr * (0.5 + 0.5 * math.cos(math.pi * ratio))
18 |
19 | ratio = cur_it / (max_it - 1)
20 | cur_wd = wd_end + (wd - wd_end) * (0.5 + 0.5 * math.cos(math.pi * ratio))
21 |
22 | min_lr, max_lr = cur_lr, cur_lr
23 | min_wd, max_wd = cur_wd, cur_wd
24 | for param_group in optimizer.param_groups:
25 | scaled_lr = param_group['lr'] = cur_lr * param_group.get('lr_scale', 1) # 'lr_scale' could be assigned
26 | min_lr, max_lr = min(min_lr, scaled_lr), max(max_lr, scaled_lr)
27 | scaled_wd = param_group['weight_decay'] = cur_wd * param_group.get('weight_decay_scale', 1) # 'weight_decay_scale' could be assigned
28 | min_wd, max_wd = min(min_wd, scaled_wd), max(max_wd, scaled_wd)
29 | return min_lr, max_lr, min_wd, max_wd
30 |
31 |
32 | def get_param_groups(model, nowd_keys=()):
33 | para_groups, para_groups_dbg = {}, {}
34 |
35 | for name, para in model.named_parameters():
36 | if not para.requires_grad:
37 | continue # frozen weights
38 | if len(para.shape) == 1 or name.endswith('.bias') or any(k in name for k in nowd_keys):
39 | wd_scale, group_name = 0., 'no_decay'
40 | else:
41 | wd_scale, group_name = 1., 'decay'
42 |
43 | if group_name not in para_groups:
44 | para_groups[group_name] = {'params': [], 'weight_decay_scale': wd_scale, 'lr_scale': 1.}
45 | para_groups_dbg[group_name] = {'params': [], 'weight_decay_scale': wd_scale, 'lr_scale': 1.}
46 | para_groups[group_name]['params'].append(para)
47 | para_groups_dbg[group_name]['params'].append(name)
48 |
49 | for g in para_groups_dbg.values():
50 | g['params'] = pformat(', '.join(g['params']), width=200)
51 |
52 | print(f'[get_ft_param_groups] param groups = \n{pformat(para_groups_dbg, indent=2, width=250)}\n')
53 | return list(para_groups.values())
54 |
--------------------------------------------------------------------------------
/utils/multi_crop.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-05-23 16:02:49
4 | LastEditTime: 2023-05-30 13:34:05
5 | FilePath: /chengdongzhou/action/tRexHAR/utils/multi_crop.py
6 | '''
7 | ##################################################
8 | # Multi-crop related code re-used from DINO
9 | # https://github.com/facebookresearch/dino
10 | ##################################################
11 |
12 | import random
13 |
14 | import torch
15 | import torch.nn as nn
16 | import torchvision.transforms as transforms
17 | import numpy as np
18 | from configs.base_configs import args
19 | from utils.augmentations import gen_aug
20 |
21 |
22 | class MultiCropWrapper(nn.Module):
23 | """
24 | Perform forward pass separately on each resolution input.
25 | The inputs corresponding to a single resolution are clubbed and single
26 | forward is run on the same resolution inputs. Hence we do several
27 | forward passes = number of different resolutions used. We then
28 | concatenate all the output features and run the head forward on these
29 | concatenated features.
30 | """
31 |
32 | def __init__(self, encoder, head):
33 | super().__init__()
34 | # disable layers dedicated to ImageNet labels classification
35 | self.encoder = encoder
36 | self.head = head
37 |
38 | def forward(self, x ):
39 | # convert to list
40 | if self.training:
41 | if not isinstance(x, list):
42 | x = [x]
43 | idx_crops = torch.cumsum(
44 | torch.unique_consecutive(
45 | torch.tensor([inp.shape[2] for inp in x]),
46 | return_counts=True,
47 | )[1],
48 | 0,
49 | )
50 |
51 | start_idx, output_list,_cls_out = 0, [torch.empty(0).to(x[0].device) for i in range(3) ] , torch.empty(0).to(x[0].device)
52 | for end_idx in idx_crops:
53 | if start_idx < args.mc_global_number:
54 | _out = self.encoder(torch.cat(x[start_idx:end_idx]))
55 | else:
56 | _out = self.encoder( gen_aug( torch.cat(x[start_idx:end_idx]).cpu().squeeze(1), ssh_type=args.data_aug ).unsqueeze(1).cuda().float() )
57 | # accumulate outputs
58 | _rep_list, _cls = _out['rep_list'] , _out['cls']
59 | _cls_out = torch.cat((_cls_out,_cls))
60 | output_list = [ torch.cat((output_list[x], rep)) for x , rep in enumerate( _rep_list ) ]
61 | start_idx = end_idx
62 | # Run the head forward on the concatenated features
63 | return [self.head(output) for output in output_list ] , _cls_out
64 | else:
65 | return None , self.encoder(x)['cls']
66 |
67 |
68 |
69 |
70 | if __name__ == '__main__':
71 | x = torch.randn(1,3,224,224)
--------------------------------------------------------------------------------
/pretrain/sampler.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) ByteDance, Inc. and its affiliates.
2 | # All rights reserved.
3 | #
4 | # This source code is licensed under the license found in the
5 | # LICENSE file in the root directory of this source tree.
6 |
7 | import random
8 |
9 | import numpy as np
10 | import torch
11 | from torch.utils.data.sampler import Sampler
12 |
13 |
14 | def worker_init_fn(worker_id):
15 | # https://pytorch.org/docs/stable/notes/randomness.html#dataloader
16 | worker_seed = torch.initial_seed() % 2 ** 32
17 | np.random.seed(worker_seed)
18 | random.seed(worker_seed)
19 |
20 |
21 | class DistInfiniteBatchSampler(Sampler):
22 | def __init__(self, world_size, rank, dataset_len, glb_batch_size, seed=1, filling=False, shuffle=True):
23 | assert glb_batch_size % world_size == 0
24 | self.world_size, self.rank = world_size, rank
25 | self.dataset_len = dataset_len
26 | self.glb_batch_size = glb_batch_size
27 | self.batch_size = glb_batch_size // world_size
28 |
29 | self.iters_per_ep = (dataset_len + glb_batch_size - 1) // glb_batch_size
30 | self.filling = filling
31 | self.shuffle = shuffle
32 | self.epoch = 0
33 | self.seed = seed
34 | self.indices = self.gener_indices()
35 |
36 | def gener_indices(self):
37 | global_max_p = self.iters_per_ep * self.glb_batch_size # global_max_p % world_size must be 0 cuz glb_batch_size % world_size == 0
38 | if self.shuffle:
39 | g = torch.Generator()
40 | g.manual_seed(self.epoch + self.seed)
41 | global_indices = torch.randperm(self.dataset_len, generator=g)
42 | else:
43 | global_indices = torch.arange(self.dataset_len)
44 | filling = global_max_p - global_indices.shape[0]
45 | if filling > 0 and self.filling:
46 | global_indices = torch.cat((global_indices, global_indices[:filling]))
47 | global_indices = tuple(global_indices.numpy().tolist())
48 |
49 | seps = torch.linspace(0, len(global_indices), self.world_size + 1, dtype=torch.int)
50 | local_indices = global_indices[seps[self.rank]:seps[self.rank + 1]]
51 | self.max_p = len(local_indices)
52 | return local_indices
53 |
54 | def __iter__(self):
55 | self.epoch = 0
56 | while True:
57 | self.epoch += 1
58 | p, q = 0, 0
59 | while p < self.max_p:
60 | q = p + self.batch_size
61 | yield self.indices[p:q]
62 | p = q
63 | if self.shuffle:
64 | self.indices = self.gener_indices()
65 |
66 | def __len__(self):
67 | return self.iters_per_ep
68 |
69 |
70 | if __name__ == '__main__':
71 | W = 16
72 | for rk in range(W):
73 | ind = DistInfiniteBatchSampler(W, rk, 5024, 5024).gener_indices()
74 | print(rk, len(ind))
75 |
--------------------------------------------------------------------------------
/utils/logger.py:
--------------------------------------------------------------------------------
1 |
2 | import logging
3 | import numpy as np
4 | import os
5 | import pathlib
6 | import pandas as pd
7 | import time
8 | from configs.base_configs import args
9 | import h5py
10 | from utils.setup import GetModel
11 | from utils.metric import GetInferredSpeed,GetFlopsAndParams
12 |
13 | import re
14 |
15 | def Statistics(stat, Test_losses, Acc_tests, mF1_tests, wF1_tests , Recall_tests, Precision_tests, sum_time = 0):
16 | stat['time'] = sum_time
17 | stat['Test_losses'] = Test_losses
18 | stat['Acc_tests'] = Acc_tests
19 | stat['mF1_tests'] = mF1_tests
20 | stat['wF1_tests'] = wF1_tests
21 | stat['Recall_tests'] = Recall_tests
22 | stat['Precision_test'] = Precision_tests
23 | return stat
24 |
25 | class AverageMeter(object):
26 | """Computes and stores the average and current value"""
27 | def __init__(self, name, fmt=':f'):
28 | self.name = name
29 | self.fmt = fmt
30 | self.reset()
31 |
32 | def reset(self):
33 | self.val = 0
34 | self.avg = 0
35 | self.sum = 0
36 | self.count = 0
37 |
38 | def update(self, val, n=1):
39 | self.val = val
40 | self.sum += val * n
41 | self.count += n
42 | self.avg = self.sum / self.count
43 |
44 | def __str__(self):
45 | fmtstr = '{name} {val' + self.fmt + '} ({avg' + self.fmt + '})'
46 | return fmtstr.format(**self.__dict__)
47 |
48 |
49 |
50 | def initialize_logger(file_dir):
51 | """Print the results in the log file."""
52 | logger = logging.getLogger()
53 | fhandler = logging.FileHandler(filename=file_dir, mode='a')
54 | chhander = logging.StreamHandler() if args.chhander else None
55 | formatter = logging.Formatter(fmt='[%(asctime)s] %(message)s',
56 | datefmt ='%m-%d %H:%M')
57 | fhandler.setFormatter(formatter)
58 | chhander.setFormatter(formatter) if args.chhander else None
59 | logger.addHandler(fhandler)
60 | logger.addHandler(chhander) if args.chhander else None
61 | logger.setLevel(logging.INFO)
62 | return logger
63 |
64 |
65 | def record_result(result, epoch, acc, mf1,wf1 ,recall , precision, c_mat , record_flag = 0):
66 | """ Record evaluation results."""
67 | if record_flag == 0:
68 | result.write('Best validation epoch | accuracy: {:.4f}, mF1: {:.4f}, wF1: {:.4f}, Rec: {:.4f}, Pre: {:.4f} (at epoch {})\n'.format(acc, mf1, wf1 ,recall,precision, epoch))
69 | elif record_flag == -1:
70 | result.write('\n\nTest (Best) | accuracy: {:.4f}, mF1: {:.4f}, wF1: {:.4f}, Rec: {:.4f}, Pre: {:.4f}\n'.format(acc, mf1,wf1,recall , precision, epoch))
71 | result.write(np.array2string(c_mat))
72 | result.flush()
73 | result.close
74 | elif record_flag == -2:
75 | result.write('\n\nTest (Final) | accuracy: {:.4f}, mF1: {:.4f}, wF1: {:.4f}, Rec: {:.4f}, Pre: {:.4f}\n'.format(acc, mf1,wf1,recall , precision, epoch))
76 | elif record_flag == -3:
77 | result.write('\n\nFinal validation epoch | accuracy: {:.4f}, mF1: {:.4f}, wF1: {:.4f}, Rec: {:.4f}, Pre: {:.4f}\n'.format(acc, mf1,wf1,recall , precision, epoch))
78 |
79 |
80 |
--------------------------------------------------------------------------------
/pretrain/launch.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2024-08-13 13:08:14
4 | LastEditTime: 2024-08-13 17:38:42
5 | FilePath: /chengdongzhou/action/MaskCAE/pretrain/launch.py
6 | '''
7 | import argparse
8 | import functools
9 | import os
10 | import socket
11 | import subprocess
12 | import sys
13 | from typing import List
14 |
15 | os_system = functools.partial(subprocess.call, shell=True)
16 | echo = lambda info: os_system(f'echo "[$(date "+%m-%d-%H:%M:%S")] ({os.path.basename(sys._getframe().f_back.f_code.co_filename)}, line{sys._getframe().f_back.f_lineno})=> {info}"')
17 |
18 |
19 | def __find_free_port():
20 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
21 | sock.bind(("", 0))
22 | port = sock.getsockname()[1]
23 | sock.close()
24 | return port
25 |
26 |
27 | if __name__ == '__main__':
28 | parser = argparse.ArgumentParser(description='PyTorch Distributed Launcher')
29 | parser.add_argument('--main_py_relpath', type=str, default='main.py',
30 | help='specify launcher script.')
31 |
32 | # distributed environment
33 | parser.add_argument('--num_nodes', type=int, default=1)
34 | parser.add_argument('--ngpu_per_node', type=int, default=1)
35 | parser.add_argument('--node_rank', type=int, default=0,
36 | help='node rank, ranged from 0 to [dist_num_nodes]-1')
37 | parser.add_argument('--master_address', type=str, default='128.0.0.0',
38 | help='master address for distributed communication')
39 | parser.add_argument('--master_port', type=int, default=30001,
40 | help='master port for distributed communication')
41 |
42 | args_for_this, args_for_python = parser.parse_known_args()
43 | args_for_python: List[str]
44 |
45 | echo(f'[initial args_for_python]: {args_for_python}')
46 | # auto-complete: update args like `--sbn` to `--sbn=1`
47 | kwargs = args_for_python[-1]
48 | kwargs = '='.join(map(str.strip, kwargs.split('=')))
49 | kwargs = kwargs.split(' ')
50 | for i, a in enumerate(kwargs):
51 | if len(a) and '=' not in a:
52 | kwargs[i] = f'{a}=1'
53 | args_for_python[-1] = ' '.join(kwargs)
54 | echo(f'[final args_for_python]: {args_for_python}')
55 |
56 | if args_for_this.num_nodes > 1: # distributed
57 | os.environ['NPROC_PER_NODE'] = str(args_for_this.ngpu_per_node)
58 | cmd = (
59 | f'python3 -m torch.distributed.launch'
60 | f' --nnodes={args_for_this.num_nodes}'
61 | f' --nproc_per_node={args_for_this.ngpu_per_node}'
62 | f' --node_rank={args_for_this.node_rank}'
63 | f' --master_addr={args_for_this.master_address}'
64 | f' --master_port={args_for_this.master_port}'
65 | f' {args_for_this.main_py_relpath}'
66 | f' {" ".join(args_for_python)}'
67 | )
68 | else: # single machine with multiple GPUs
69 | cmd = (
70 | f'python3 -m torch.distributed.launch'
71 | f' --nproc_per_node={args_for_this.ngpu_per_node}'
72 | f' --master_port={__find_free_port()}'
73 | f' {args_for_this.main_py_relpath}'
74 | f' {" ".join(args_for_python)}'
75 | )
76 |
77 | exit_code = subprocess.call(cmd, shell=True)
78 | sys.exit(exit_code)
79 |
--------------------------------------------------------------------------------
/pretrain/dist.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) ByteDance, Inc. and its affiliates.
2 | # All rights reserved.
3 | #
4 | # This source code is licensed under the license found in the
5 | # LICENSE file in the root directory of this source tree.
6 |
7 | import os
8 | from typing import List
9 | from typing import Union
10 |
11 | import sys
12 | import torch
13 | import torch.distributed as tdist
14 | import torch.multiprocessing as mp
15 |
16 | __rank, __local_rank, __world_size, __device = 0, 0, 1, 'cpu'
17 | __initialized = False
18 |
19 |
20 | def initialized():
21 | return __initialized
22 |
23 |
24 | def initialize(backend='nccl'):
25 | global __device
26 | if not torch.cuda.is_available():
27 | print(f'[dist initialize] cuda is not available, use cpu instead', file=sys.stderr)
28 | return
29 | elif 'RANK' not in os.environ:
30 | __device = torch.empty(1).cuda().device
31 | print(f'[dist initialize] RANK is not set, use 1 GPU instead', file=sys.stderr)
32 | return
33 |
34 | # ref: https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/dist_utils.py#L29
35 | if mp.get_start_method(allow_none=True) is None:
36 | mp.set_start_method('spawn')
37 | global_rank, num_gpus = int(os.environ['RANK']), torch.cuda.device_count()
38 | local_rank = global_rank % num_gpus
39 | torch.cuda.set_device(local_rank)
40 | tdist.init_process_group(backend=backend)
41 |
42 | global __rank, __local_rank, __world_size, __initialized
43 | __local_rank = local_rank
44 | __rank, __world_size = tdist.get_rank(), tdist.get_world_size()
45 | __device = torch.empty(1).cuda().device
46 | __initialized = True
47 |
48 | assert tdist.is_initialized(), 'torch.distributed is not initialized!'
49 |
50 |
51 | def get_rank():
52 | return __rank
53 |
54 |
55 | def get_local_rank():
56 | return __local_rank
57 |
58 |
59 | def get_world_size():
60 | return __world_size
61 |
62 |
63 | def get_device():
64 | return __device
65 |
66 |
67 | def is_master():
68 | return __rank == 0
69 |
70 |
71 | def is_local_master():
72 | return __local_rank == 0
73 |
74 |
75 | def barrier():
76 | if __initialized:
77 | tdist.barrier()
78 |
79 |
80 | def parallelize(net, syncbn=False):
81 | if syncbn:
82 | net = torch.nn.SyncBatchNorm.convert_sync_batchnorm(net)
83 | net = net.cuda()
84 | net = torch.nn.parallel.DistributedDataParallel(net, device_ids=[get_local_rank()], find_unused_parameters=False, broadcast_buffers=False)
85 | return net
86 |
87 |
88 | def allreduce(t: torch.Tensor) -> None:
89 | if __initialized:
90 | if not t.is_cuda:
91 | cu = t.detach().cuda()
92 | tdist.all_reduce(cu)
93 | t.copy_(cu.cpu())
94 | else:
95 | tdist.all_reduce(t)
96 |
97 |
98 | def allgather(t: torch.Tensor, cat=True) -> Union[List[torch.Tensor], torch.Tensor]:
99 | if __initialized:
100 | if not t.is_cuda:
101 | t = t.cuda()
102 | ls = [torch.empty_like(t) for _ in range(__world_size)]
103 | tdist.all_gather(ls, t)
104 | else:
105 | ls = [t]
106 | if cat:
107 | ls = torch.cat(ls, dim=0)
108 | return ls
109 |
110 |
111 | def broadcast(t: torch.Tensor, src_rank) -> None:
112 | if __initialized:
113 | if not t.is_cuda:
114 | cu = t.detach().cuda()
115 | tdist.broadcast(cu, src=src_rank)
116 | t.copy_(cu.cpu())
117 | else:
118 | tdist.broadcast(t, src=src_rank)
119 |
--------------------------------------------------------------------------------
/models/MaskedConvNet.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2024-08-13 13:08:13
4 | LastEditTime: 2024-08-13 13:14:10
5 | FilePath: /chengdongzhou/action/MaskCAE/models/MaskedConvNet.py
6 | '''
7 | import os
8 | import sys
9 | sys.path.append(os.path.dirname(os.path.dirname(__file__)))
10 |
11 | import torch
12 | import torch.nn as nn
13 | from typing import List
14 | from common import channel_list,conv_list,maxp_list,first_maxp_list
15 | from utils.metric import GetFeatureMapSize
16 | from configs.base_configs import args
17 |
18 | class ConvNet(nn.Module):
19 | def __init__(self, data_name='ucihar' ,use_adativeavg=False, sub_number = 4 ):
20 | super(ConvNet, self).__init__()
21 | self.channel= channel = channel_list[data_name]
22 | conv_params = conv_list[data_name]
23 | max_params = maxp_list[data_name]
24 | first_max = first_maxp_list[data_name]
25 |
26 | self.layer1 = self.maker_layers( 1 , channel[0], conv_params=conv_params, pooling_params=first_max )
27 | self.layer2 = self.maker_layers( channel[0], channel[1], conv_params=conv_params, pooling_params=max_params )
28 | self.layer3 = self.maker_layers( channel[1], channel[2], conv_params=conv_params, pooling_params=max_params )
29 | self.layer4 = self.maker_layers( channel[2], channel[3], conv_params=conv_params, pooling_params=max_params )
30 | h,w = GetFeatureMapSize(data_name,sub_number)
31 | self.adaptiveAvg = nn.AdaptiveAvgPool2d( ( 1, w ) ) if use_adativeavg else nn.Identity()
32 | self.classifier = nn.Linear( channel[2]*w if use_adativeavg else channel[2]* h * w , channel[-1] )
33 |
34 | def maker_layers(self,inp,oup,conv_params=None,pooling_params=None):
35 | assert isinstance(conv_params,list),print('the format of kernel params is error')
36 | assert isinstance(pooling_params,list) or pooling_params == None ,print('the format of pooling params is error')
37 | return nn.Sequential(
38 | nn.Conv2d( inp, oup, *(conv_params) ),
39 | nn.BatchNorm2d( oup ),
40 | nn.ReLU( True ),
41 | nn.MaxPool2d( *(pooling_params) ) if pooling_params else nn.Identity()
42 | )
43 |
44 | def get_downsample_ratio(self) -> int:
45 | return 32
46 |
47 | def get_feature_map_channels(self) -> List[int]:
48 | return self.channel[:-1]
49 |
50 | def forward(self, x: torch.Tensor, hierarchical=False):
51 | B,_,_,_ = x.size()
52 | if hierarchical:
53 | ls = []
54 | x = self.layer1(x); ls.append(x)
55 | x = self.layer2(x); ls.append(x)
56 | x = self.layer3(x); ls.append(x)
57 | x = self.layer4(x); ls.append(x)
58 | return ls
59 | else:
60 | x = self.adaptiveAvg(x)
61 | x = x.view(B,-1)
62 | x = self.classifier( x )
63 | return x
64 |
65 |
66 | @torch.no_grad()
67 | def convnet_test():
68 | cnn = ConvNet()
69 | print('get_downsample_ratio:', cnn.get_downsample_ratio())
70 | print('get_feature_map_channels:', cnn.get_feature_map_channels())
71 |
72 | downsample_ratio = cnn.get_downsample_ratio()
73 | feature_map_channels = cnn.get_feature_map_channels()
74 |
75 | # check the forward function
76 | B, C, H, W = 4, 1, 128, 9
77 | inp = torch.rand(B, C, H, W)
78 | feats = cnn(inp, hierarchical=True)
79 | assert isinstance(feats, list)
80 | assert len(feats) == len(feature_map_channels)
81 | print([tuple(t.shape) for t in feats])
82 |
83 | # check the downsample ratio
84 | feats = cnn(inp, hierarchical=True)
85 | assert feats[-1].shape[-2] == H // downsample_ratio
86 | assert feats[-1].shape[-1] == W // 1
87 |
88 | # check the channel number
89 | for feat, ch in zip(feats, feature_map_channels):
90 | assert feat.ndim == 4
91 | assert feat.shape[1] == ch
92 |
93 |
94 | if __name__ == '__main__':
95 | convnet_test()
96 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
🌟MaskCAE: Masked Convolutional AutoEncoder for HAR
2 | Paper
3 | Dongzhou Cheng
4 |
5 | ## 🔆🚀If you use this project, please cite our papers.
6 | ```
7 | @article{cheng2024maskcae,
8 | title={MaskCAE: Masked Convolutional AutoEncoder via Sensor Data Reconstruction for Self-Supervised Human Activity Recognition},
9 | author={Cheng, Dongzhou and Zhang, Lei and Qin, Lutong and Wang, Shuoyuan and Wu, Hao and Song, Aiguo},
10 | journal={IEEE Journal of Biomedical and Health Informatics},
11 | year={2024},
12 | publisher={IEEE}
13 | }
14 | ```
15 | ```
16 | @article{cheng2023protohar,
17 | title={Protohar: Prototype guided personalized federated learning for human activity recognition},
18 | author={Cheng, Dongzhou and Zhang, Lei and Bu, Can and Wang, Xing and Wu, Hao and Song, Aiguo},
19 | journal={IEEE Journal of Biomedical and Health Informatics},
20 | volume={27},
21 | number={8},
22 | pages={3900--3911},
23 | year={2023},
24 | publisher={IEEE}
25 | }
26 | ```
27 | ```
28 | @article{cheng2023learning,
29 | title={Learning hierarchical time series data augmentation invariances via contrastive supervision for human activity recognition},
30 | author={Cheng, Dongzhou and Zhang, Lei and Bu, Can and Wu, Hao and Song, Aiguo},
31 | journal={Knowledge-Based Systems},
32 | volume={276},
33 | pages={110789},
34 | year={2023},
35 | publisher={Elsevier}
36 | }
37 |
38 | ```
39 |
40 |
41 | # Pretrain environment
42 | ```
43 | conda activate -n maskcae python=3.8
44 |
45 | python -m pip install torch==1.10.0+cu111 -f https://download.pytorch.org/whl/cu111/torch_stable.html
46 |
47 | python -m pip install torchvision==0.11.0+cu111 -f https://download.pytorch.org/whl/cu111/torch_stable.html
48 |
49 | python -m pip uninstall setuptools
50 |
51 | python -m pip install setuptools==59.5.0
52 |
53 | python -m pip install -r requirements.txt
54 | ```
55 | Activate your environment
56 | ```
57 | conda activate maskcae
58 | ```
59 |
60 | ## Settings
61 | 1. enter the folder
62 | ```
63 | cd /MaskCAE
64 | ```
65 | 2. check the yaml configs: `pre_trained_path`, this is the path to save the pre-trained weights
66 | 3. check the `WINDOWS_SAVE_PATH` and `ROOT_PATH`
67 | * `WINDOWS_SAVE_PATH` is the path to save the pre-processed data
68 | * `ROOT_PATH` is the path to save the raw dataset
69 |
70 | ### Get all pretrained weights
71 | ```
72 | python pretrain/script/run.py --dataset ucihar
73 |
74 | python pretrain/script/run.py --dataset uschad
75 |
76 | python pretrain/script/run.py --dataset motion
77 | ```
78 |
79 |
80 | # Self-Supervised Learning
81 | *FCN Base (without MaskCAE)*
82 | ```
83 | # ucihar LOCV mode
84 | python scripts/MaskCAE/Base.py --config configs/yaml/maskcaebase.yaml --dataset ucihar --device 0 --times 1
85 |
86 | # uschad Given mode
87 | python scripts/MaskCAE/Base.py --config configs/yaml/maskcaebase.yaml --dataset uschad --device 2
88 |
89 | # motion LOCV mode
90 | python scripts/MaskCAE/Base.py --config configs/yaml/maskcaebase.yaml --dataset motion --device 1 --times 1
91 | ```
92 | *FCN fully finetune*
93 | ```
94 | # ucihar LOCV mode
95 | python scripts/MaskCAE/Base.py --config configs/yaml/maskcaeConv4Net.yaml --dataset ucihar --device 2 --times 1
96 |
97 | # uschad Given mode
98 | python scripts/MaskCAE/Base.py --config configs/yaml/maskcaeConv4Net.yaml --dataset uschad --device 1
99 |
100 | # motion LOCV mode
101 | python scripts/MaskCAE/Base.py --config configs/yaml/maskcaeConv4Net.yaml --dataset motion --device 2 --times 1
102 | ```
103 | *FCN Linear Evalution*
104 | ```
105 | # ucihar LOCV mode
106 | python scripts/MaskCAE/Base.py --config configs/yaml/maskcaeConv4Net_LP.yaml --dataset ucihar --device 0 --times 1
107 |
108 | # uschad Given mode
109 | python scripts/MaskCAE/Base.py --config configs/yaml/maskcaeConv4Net_LP.yaml --dataset uschad --device 1
110 |
111 | # motion LOCV mode
112 | python scripts/MaskCAE/Base.py --config configs/yaml/maskcaeConv4Net_LP.yaml --dataset motion --device 2 --times 1
113 |
114 | ```
115 |
--------------------------------------------------------------------------------
/HAR_sklearn.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pickle
3 | import numpy as np
4 | import pandas as pd
5 | import matplotlib.pyplot as plt
6 | import sklearn
7 | from sklearn import metrics
8 | from sklearn import svm
9 | from sklearn.neural_network import MLPClassifier
10 | from sklearn.naive_bayes import GaussianNB
11 | from sklearn.neighbors import KNeighborsClassifier
12 | from sklearn.ensemble import BaggingClassifier, RandomForestClassifier, AdaBoostClassifier
13 | from sklearn.metrics import f1_score
14 | from sklearn.manifold import TSNE
15 | import matplotlib.pyplot as plt
16 | import seaborn as sns
17 | import pandas as pd
18 |
19 | from dataset.HAR_dataset import HARDataset
20 |
21 | def read_data(file):
22 | data = pd.read_csv(file)
23 |
24 | # suffle data
25 | data = sklearn.utils.shuffle(data)
26 |
27 | X_data = data.drop(['subject', 'Activity', 'ActivityName'], axis=1)
28 | y_data = data.ActivityName
29 |
30 | return np.array(X_data), np.array(y_data)
31 |
32 | def train_model(train_x, train_y, model_name='NaiveBayes', validation=None):
33 | """
34 | Possible model names: ['NaiveBayes', 'SVM', 'MLP', 'AdaBoost', 'BAG', 'RandomForest']
35 | default = 'NB'
36 |
37 | validation: (val_x, val_y) tupple for validation accuracy score.
38 |
39 | return: trained model
40 | """
41 | model = None
42 | if model_name == 'SVM':
43 | model = svm.SVC(gamma='scale', probability=True)
44 | elif model_name == 'MLP':
45 | model = MLPClassifier(hidden_layer_sizes=(100,100), max_iter=200, alpha=0.0001,
46 | solver='sgd', verbose=10, tol=0.000000001)
47 | elif model_name == 'AdaBoost':
48 | model = AdaBoostClassifier(n_estimators=50)
49 | elif model_name == 'BAG':
50 | model = BaggingClassifier(n_jobs=2, n_estimators=50)
51 | elif model_name == 'RandomForest':
52 | model = RandomForestClassifier(n_estimators=200, max_depth=10)
53 | elif model_name == 'KNN':
54 | model = KNeighborsClassifier(n_neighbors=5, weights='distance', algorithm='auto', leaf_size=30, p=2, metric='minkowski', metric_params=None, n_jobs=None)
55 | elif model_name == 'NaiveBayes':
56 | model = GaussianNB()
57 | else:
58 | raise RuntimeError(f"{model_name} not supported")
59 | model.fit(train_x, train_y)
60 |
61 | if validation is not None:
62 | y_hat = model.predict(validation[0])
63 | acc = metrics.accuracy_score(validation[1], y_hat)
64 | print(f"Validation Accuracy in '{model_name}' = {acc}")
65 | cm = metrics.confusion_matrix(validation[1], y_hat)
66 | print(cm)
67 | f1 = f1_score(validation[1], y_hat, average='weighted')
68 | print(f"F1 Score in '{model_name}' = {f1}")
69 |
70 | return model
71 |
72 | def visualize(data, label):
73 | bs, nc, ws = data.shape
74 |
75 | accel = data[:, :nc // 2, :].reshape(bs, -1)
76 | gyro = data[:, nc//2:, :].reshape(bs, -1)
77 |
78 | tsne = TSNE(n_components=2)
79 | tsne_feat = tsne.fit_transform(np.concatenate((accel, gyro)))
80 | df = pd.DataFrame()
81 | df["y"] = label
82 | df["accel_comp-1"] = tsne_feat[:len(label), 0]
83 | df["accel_comp-2"] = tsne_feat[:len(label), 1]
84 | df["gyro_comp-1"] = tsne_feat[len(label):, 0]
85 | df["gyro_comp-2"] = tsne_feat[len(label):, 1]
86 |
87 | fig, ax = plt.subplots(ncols=2, sharex=True, sharey=True, figsize=(16, 8))
88 | sns.scatterplot(x="accel_comp-1", y="accel_comp-2", hue=df.y.tolist(),
89 | palette=sns.color_palette("hls", 6),
90 | data=df, ax=ax[0]).set(title="Accel feature T-SNE projection")
91 | sns.scatterplot(x="gyro_comp-1", y="gyro_comp-2", hue=df.y.tolist(),
92 | palette=sns.color_palette("hls", 6),
93 | data=df, ax=ax[1]).set(title="Gyro feature T-SNE projection")
94 | plt.savefig("tsne_visualize/UCI_HAR_raw.png")
95 |
96 | if __name__ == "__main__":
97 | train = HARDataset('UCI_HAR', split='train', window_width=128)
98 | test = HARDataset('UCI_HAR', split='test', window_width=128)
99 | print(len(train))
100 | print(len(test))
101 | # train_model(train.data.reshape(train.data.shape[0], -1), train.label, model_name='RandomForest', validation=(test.data.reshape(test.data.shape[0], -1), test.label))
102 | visualize(test.data, test.label)
--------------------------------------------------------------------------------
/pretrain/decoder.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-06-04 12:00:53
4 | LastEditTime: 2024-08-13 13:17:13
5 | FilePath: /chengdongzhou/action/MaskCAE/pretrain/decoder.py
6 | '''
7 | import math
8 | from typing import List
9 |
10 | import torch
11 | import torch.nn as nn
12 | from timm.models.layers import trunc_normal_
13 |
14 | from utils.misc import is_pow2n
15 |
16 |
17 | class UNetBlock(nn.Module):
18 | def __init__(self, cin, cout, bn2d,mode):
19 | """
20 | a UNet block with 2x up sampling
21 | """
22 | super().__init__()
23 | self.up_sample = nn.ConvTranspose2d(cin, cin, kernel_size=(4,1), stride=(2,1), padding=(1,0), bias=True)
24 | if mode == 'PlainDecoder':
25 | self.conv = nn.Sequential(
26 | nn.Conv2d(cin, cin, kernel_size=(5,1), stride=(1,1), padding=(2,0), bias=False), bn2d(cin),
27 | nn.ReLU6(inplace=True),
28 | nn.Conv2d(cin, cout, kernel_size=(5,1), stride=(1,1), padding=(2,0), bias=False), bn2d(cout),
29 | )
30 | elif mode == '9x9IFD':
31 | self.conv = nn.Sequential(
32 | nn.Conv2d(cin, cin, kernel_size=9, stride=1, padding=4, bias=False), bn2d(cin),
33 | nn.ReLU6(inplace=True),
34 | nn.Conv2d(cin, cout, kernel_size=9, stride=1, padding=4, bias=False), bn2d(cout),
35 | )
36 | elif mode == '7x7IFD':
37 | self.conv = nn.Sequential(
38 | nn.Conv2d(cin, cin, kernel_size=7, stride=1, padding=3, bias=False), bn2d(cin),
39 | nn.ReLU6(inplace=True),
40 | nn.Conv2d(cin, cout, kernel_size=7, stride=1, padding=3, bias=False), bn2d(cout),
41 | )
42 | elif mode == '3x3IFD':
43 | self.conv = nn.Sequential(
44 | nn.Conv2d(cin, cin, kernel_size=3, stride=1, padding=1, bias=False), bn2d(cin),
45 | nn.ReLU6(inplace=True),
46 | nn.Conv2d(cin, cout, kernel_size=3, stride=1, padding=1, bias=False), bn2d(cout),
47 | )
48 | else:
49 | self.conv = nn.Sequential(
50 | nn.Conv2d(cin, cin, kernel_size=5, stride=1, padding=2, bias=False), bn2d(cin),
51 | nn.ReLU6(inplace=True),
52 | nn.Conv2d(cin, cout, kernel_size=5, stride=1, padding=2, bias=False), bn2d(cout),
53 | )
54 | def forward(self, x):
55 | x = self.up_sample(x)
56 | return self.conv(x)
57 |
58 |
59 | class LightDecoder(nn.Module):
60 | def __init__(self, up_sample_ratio, width=256, sbn=False,mode=''):
61 | super().__init__()
62 | self.width = width
63 | self.mode = mode
64 | assert is_pow2n(up_sample_ratio)
65 | n = round(math.log2(up_sample_ratio))
66 | channels = [self.width // 2 ** i for i in range(n + 1)]
67 | bn2d = nn.SyncBatchNorm if sbn else nn.BatchNorm2d
68 | self.dec = nn.ModuleList([UNetBlock(cin, cout, bn2d, mode) for (cin, cout) in zip(channels[:-1], channels[1:])])
69 | self.proj = nn.Conv2d(channels[-1], 1, kernel_size=1, stride=1, bias=True)
70 |
71 | self.initialize()
72 |
73 | def forward(self, to_dec: List[torch.Tensor]):
74 | x = 0
75 | for i, d in enumerate(self.dec):
76 | if i < len(to_dec) and to_dec[i] is not None:
77 | x = x + to_dec[i]
78 | x = self.dec[i](x)
79 | return self.proj(x)
80 |
81 | def extra_repr(self) -> str:
82 | return f'width={self.width}'
83 |
84 | def initialize(self):
85 | for m in self.modules():
86 | if isinstance(m, nn.Linear):
87 | trunc_normal_(m.weight, std=.02)
88 | if m.bias is not None:
89 | nn.init.constant_(m.bias, 0)
90 | elif isinstance(m, nn.Conv2d):
91 | trunc_normal_(m.weight, std=.02)
92 | if m.bias is not None:
93 | nn.init.constant_(m.bias, 0)
94 | elif isinstance(m, (nn.Conv2d, nn.ConvTranspose2d)):
95 | nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
96 | if m.bias is not None:
97 | nn.init.constant_(m.bias, 0.)
98 | elif isinstance(m, (nn.LayerNorm, nn.BatchNorm1d, nn.BatchNorm2d, nn.SyncBatchNorm)):
99 | nn.init.constant_(m.bias, 0)
100 | nn.init.constant_(m.weight, 1.0)
101 |
--------------------------------------------------------------------------------
/pretrain/utils/arg_util.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) ByteDance, Inc. and its affiliates.
2 | # All rights reserved.
3 | #
4 | # This source code is licensed under the license found in the
5 | # LICENSE file in the root directory of this source tree.
6 |
7 | import json
8 | import os
9 | import sys
10 |
11 | from tap import Tap
12 | import dist
13 |
14 |
15 | class Args(Tap):
16 | # environment
17 | exp_name: str = 'MaskCAE'
18 | exp_dir: str = 'MaskCAE_EXP' # will be created if not exists
19 | dataset_name: str = ''
20 | resume_from: str = '' # resume from some checkpoint.pth
21 | DATASET_PRE:str = 'self'
22 | # MaskCAE hyperparameters
23 | mask: float = None # mask ratio, should be in (0, 1)
24 | is_sup: bool = False
25 | # encoder hyperparameters
26 | model: str = 'Conv4Net'
27 | sbn: bool = False
28 | device: str = '1'
29 | # data hyperparameters
30 | bs: int = 512
31 | dataloader_workers: int = 8
32 | ablation_mode:str = ''
33 | # pre-training hyperparameters
34 | dp: float = 0.0
35 | base_lr: float = 2e-3
36 | wd: float = 0.04
37 | wde: float = 0.2
38 | ep: int = 1000
39 | wp_ep: int = 40
40 | clip: int = 5.
41 | opt: str = 'lamb'
42 | ada: float = 0.
43 |
44 | # NO NEED TO SPECIFIED; each of these args would be updated in runtime automatically
45 | lr: float = None
46 | batch_size_per_gpu: int = 0
47 | glb_batch_size: int = 0
48 | densify_norm: str = ''
49 |
50 | local_rank: int = 0
51 | cmd: str = ' '.join(sys.argv[1:])
52 | commit_id: str = os.popen(f'git rev-parse HEAD').read().strip() or '[unknown]'
53 | commit_msg: str = (os.popen(f'git log -1').read().strip().splitlines() or ['[unknown]'])[-1].strip()
54 | last_loss: float = 0.
55 | cur_ep: str = ''
56 | remain_time: str = ''
57 | finish_time: str = ''
58 | first_logging: bool = True
59 | log_txt_name: str = '{args.exp_dir}/pretrain_log.txt'
60 | tb_lg_dir: str = '' # tensorboard log directory
61 |
62 | @property
63 | def is_convnet(self):
64 | return 'Conv4Net' in self.model
65 |
66 | def log_epoch(self):
67 | if not dist.is_local_master():
68 | return
69 |
70 | if self.first_logging:
71 | self.first_logging = False
72 | with open(self.log_txt_name, 'w') as fp:
73 | json.dump({
74 | 'name': self.exp_name, 'cmd': self.cmd, 'git_commit_id': self.commit_id, 'git_commit_msg': self.commit_msg,
75 | 'model': self.model,
76 | }, fp)
77 | fp.write('\n\n')
78 |
79 | with open(self.log_txt_name, 'a') as fp:
80 | json.dump({
81 | 'cur_ep': self.cur_ep,
82 | 'last_L': self.last_loss,
83 | 'rema': self.remain_time, 'fini': self.finish_time,
84 | }, fp)
85 | fp.write('\n')
86 |
87 |
88 | def init_dist_and_get_args():
89 | from utils import misc
90 |
91 | # initialize
92 | args = Args(explicit_bool=True).parse_args()
93 | e = os.path.abspath(args.exp_dir)
94 | d, e = os.path.dirname(e), os.path.basename(e)
95 | e = ''.join(ch if (ch.isalnum() or ch == '-') else '_' for ch in e)
96 | args.exp_dir = os.path.join(d, e)
97 |
98 | os.makedirs(args.exp_dir, exist_ok=True)
99 | args.log_txt_name = os.path.join(args.exp_dir, 'pretrain_log.txt')
100 | args.tb_lg_dir = args.tb_lg_dir or os.path.join(args.exp_dir, 'tensorboard_log')
101 | try:
102 | os.makedirs(args.tb_lg_dir, exist_ok=True)
103 | except:
104 | pass
105 |
106 | misc.init_distributed_environ(exp_dir=args.exp_dir)
107 |
108 | # update args
109 | if not dist.initialized():
110 | args.sbn = False
111 | args.first_logging = True
112 | args.device = dist.get_device()
113 | args.batch_size_per_gpu = args.bs // dist.get_world_size()
114 | args.glb_batch_size = args.batch_size_per_gpu * dist.get_world_size()
115 |
116 | if args.is_convnet:
117 | args.ada = args.ada or 0.999
118 | args.densify_norm = 'bn'
119 |
120 | args.opt = args.opt.lower()
121 | args.lr = args.base_lr * args.glb_batch_size / 256
122 | args.wde = args.wde or args.wd
123 |
124 | return args
125 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-05-07 18:23:46
4 | LastEditTime: 2024-08-13 20:06:35
5 | FilePath: /chengdongzhou/action/MaskCAE/main.py
6 | '''
7 |
8 | import os
9 | import torch
10 | from configs.base_configs import args, dict_to_markdown
11 | from torch.utils.data import DataLoader
12 |
13 | os.environ["CUDA_VISIBLE_DEVICES"] = str(args.device)
14 |
15 | import numpy as np
16 |
17 | from create_dataset import GetHarDataset, dotdict
18 | from dataloaders import data_set
19 | from utils.logger import initialize_logger
20 | from utils.setup import set_seed,GetModel
21 | from train import MetaTrain
22 | from trainers.evalution import evaluate
23 | import warnings
24 | import wandb
25 | warnings.filterwarnings("ignore")
26 | import pyfiglet
27 | print('------------------------------------------------------------------')
28 | result = pyfiglet.figlet_format(text="MaskCAE", font="slant")
29 | print(result)
30 | print('------------------------------------------------------------------')
31 | use_cuda = torch.cuda.is_available()
32 |
33 | DATASET_PRE = args.dataset_pre
34 |
35 |
36 |
37 | if __name__ == "__main__":
38 | print(dict_to_markdown(vars(args)))
39 | args.save_folder = os.path.join(args.model_path, args.dataset, args.model, args.mode , args.trial)
40 | if not os.path.isdir(args.save_folder):
41 | os.makedirs(args.save_folder)
42 | # logging
43 | log_dir = os.path.join(args.save_folder, 'train.log')
44 | logger = initialize_logger(log_dir)
45 | # writer = SummaryWriter(args.save_folder + '/run')
46 | total_losses, acc_tests, mf1_tests, wf1_tests, recall_tests, precision_tests = [ [ [] for i in range(args.times)] for i in range(6) ]
47 | for i in range(args.times):
48 | logger.info(f'/n-------run time {i}--------\n')
49 | args.time = i
50 | set_seed(i+1)
51 |
52 | data_args = dotdict()
53 |
54 | data_args.yaml_path = 'configs/yaml/data.yaml'
55 | args.is_sup = False
56 |
57 | data_args, dataset\
58 | = GetHarDataset(dataset_name=args.dataset,data_args=data_args)
59 | for currenct_cv in range(dataset.num_of_cv):
60 | print("================ {} Mode ====================".format(dataset.exp_mode))
61 | print("================ {} CV ====================".format(dataset.num_of_cv))
62 | dataset.update_train_val_test_keys()
63 | args.Part = dataset.index_of_cv
64 | args.num_of_cv = dataset.num_of_cv
65 |
66 | train_data = data_set(data_args,dataset,"train")
67 | test_data = data_set(data_args,dataset,"test")
68 | vali_data = data_set(data_args,dataset,"vali")
69 |
70 | wandb.init(
71 | project=f'{args.mode}',
72 | reinit=True,
73 | config=vars(args),
74 | # entity='NanjingNormalUniversity',
75 | # settings=wandb.Settings(start_method="fork")
76 | # mode='offline'
77 | # name=recorder.save_alg+f'_{i}seed',
78 | # id=recorder.save_alg+f'_{i}seed',
79 | # resume="allow"
80 | ) if args.use_wandb else None
81 | #train
82 |
83 | train = MetaTrain(args)
84 | print(f'The training method is {train.__name__}')
85 | stat = train(train_data,vali_data,logger)
86 | wandb.join() if args.use_wandb else None
87 | model = GetModel()
88 | total_loss, acc_test, mf1_test, wf1_test, recall_test, precision_test , stat = evaluate(model,logger=logger,epoch = stat['best_epoch'],eval_loader=test_data,stat=stat)
89 | for res ,save_lsit in zip( [total_loss, acc_test, mf1_test, wf1_test, recall_test, precision_test], [total_losses, acc_tests, mf1_tests, wf1_tests, recall_tests, precision_tests]):
90 | save_lsit[i].append(res)
91 |
92 | logger.info(f'==>LOCV Acc List is {acc_tests}\n')
93 | logger.info(f'==>LOCV mF1 List is {mf1_tests}\n')
94 | logger.info(f'==>LOCV wF1 List is {wf1_tests}\n')
95 | print('====================== Averaging Results ======================')
96 | average_times = lambda lst : list(np.mean(lst,axis=0)) if args.dataset != 'uschad' else list(np.squeeze(lst))
97 | total_losses = average_times(total_losses)
98 | acc_tests = average_times(acc_tests)
99 | mf1_tests = average_times(mf1_tests)
100 | wf1_tests = average_times(wf1_tests)
101 | recall_tests = average_times(recall_tests)
102 | precision_tests = average_times(precision_tests)
103 |
104 |
105 | logger.info(f'==>LOCV Averaged Acc is {np.around( np.mean( acc_tests ) * 100 , 3 )}\n')
106 | logger.info(f'==>LOCV Averaged mF1 is {np.around( np.mean( mf1_tests ) * 100 , 3 )}\n')
107 | logger.info(f'==>LOCV Averaged wF1 is {np.around( np.mean( wf1_tests ) * 100 , 3 )}\n')
108 |
109 |
--------------------------------------------------------------------------------
/pretrain/encoder.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 |
4 | _cur_active: torch.Tensor = None # B1fs
5 | def _get_active_ex_or_ii(H, returning_active_ex=True):
6 | downsample_raito = H // _cur_active.shape[2]
7 | # B1ff -> B,1,f*downsample_raito,f*downsample_raito,对mask进行重复扩张,也就是说每个层级的特征图所乘的mask网格是不一致的,_cur_active为布尔张量,
8 | # 但仍然需要注意的是每个层级的特征图的mask radio永远都是一致的
9 | active_ex = _cur_active.repeat_interleave(downsample_raito, 2)
10 | return active_ex if returning_active_ex else active_ex.squeeze(1).nonzero(as_tuple=True) # ii: bi, hi, wi
11 |
12 |
13 | def sp_conv_forward(self, x: torch.Tensor):
14 | x = super(type(self), self).forward(x)
15 | x *= _get_active_ex_or_ii(H=x.shape[2], returning_active_ex=True) # (BCHW) *= (B1HW), mask the output of conv
16 | return x
17 |
18 |
19 | def sp_bn_forward(self, x: torch.Tensor):
20 | ii = _get_active_ex_or_ii(H=x.shape[2], returning_active_ex=False)
21 |
22 | bhwc = x.permute(0, 2, 3, 1)
23 | nc = bhwc[ii] # select the features on non-masked positions to form a flatten feature `nc`
24 | nc = super(type(self), self).forward(nc) # use BN1d to normalize this flatten feature `nc`
25 |
26 | bchw = torch.zeros_like(bhwc)
27 | bchw[ii] = nc
28 | bchw = bchw.permute(0, 3, 1, 2)
29 | return bchw
30 |
31 |
32 | class SparseConv2d(nn.Conv2d):
33 | forward = sp_conv_forward # hack: override the forward function; see `sp_conv_forward` above for more details
34 |
35 |
36 | class SparseMaxPooling(nn.MaxPool2d):
37 | forward = sp_conv_forward # hack: override the forward function; see `sp_conv_forward` above for more details
38 |
39 |
40 | class SparseAvgPooling(nn.AvgPool2d):
41 | forward = sp_conv_forward # hack: override the forward function; see `sp_conv_forward` above for more details
42 |
43 |
44 | class SparseBatchNorm2d(nn.BatchNorm1d):
45 | forward = sp_bn_forward # hack: override the forward function; see `sp_bn_forward` above for more details
46 |
47 |
48 | class SparseSyncBatchNorm2d(nn.SyncBatchNorm):
49 | forward = sp_bn_forward # hack: override the forward function; see `sp_bn_forward` above for more details
50 |
51 |
52 |
53 | class SparseEncoder(nn.Module):
54 | def __init__(self, cnn, input_size, sbn=False, verbose=False, mode='' ):
55 | super(SparseEncoder, self).__init__()
56 | if mode != 'plain_conv':
57 | print('==>sparse convolution')
58 | self.sp_cnn = SparseEncoder.dense_model_to_sparse(m=cnn, verbose=verbose, sbn=sbn)
59 | else:# plain convolution, do not
60 | self.sp_cnn = cnn
61 | self.dataset_name= cnn.dataset_name
62 | self.input_size, self.downsample_raito, self.enc_feat_map_chs = input_size, cnn.get_downsample_ratio(), cnn.get_feature_map_channels()
63 |
64 | @staticmethod
65 | def dense_model_to_sparse(m: nn.Module, verbose=False, sbn=False):
66 | oup = m
67 | if isinstance(m, nn.Conv2d):
68 | m: nn.Conv2d
69 | bias = m.bias is not None
70 | oup = SparseConv2d(
71 | m.in_channels, m.out_channels,
72 | kernel_size=m.kernel_size, stride=m.stride, padding=m.padding,
73 | dilation=m.dilation, groups=m.groups, bias=bias, padding_mode=m.padding_mode,
74 | )
75 | oup.weight.data.copy_(m.weight.data)
76 | if bias:
77 | oup.bias.data.copy_(m.bias.data)
78 | elif isinstance(m, nn.MaxPool2d):
79 | m: nn.MaxPool2d
80 | oup = SparseMaxPooling(m.kernel_size, stride=m.stride, padding=m.padding, dilation=m.dilation, return_indices=m.return_indices, ceil_mode=m.ceil_mode)
81 | elif isinstance(m, nn.AvgPool2d):
82 | m: nn.AvgPool2d
83 | oup = SparseAvgPooling(m.kernel_size, m.stride, m.padding, ceil_mode=m.ceil_mode, count_include_pad=m.count_include_pad, divisor_override=m.divisor_override)
84 | elif isinstance(m, (nn.BatchNorm2d, nn.SyncBatchNorm)):
85 | m: nn.BatchNorm2d
86 | oup = (SparseSyncBatchNorm2d if sbn else SparseBatchNorm2d)(m.weight.shape[0], eps=m.eps, momentum=m.momentum, affine=m.affine, track_running_stats=m.track_running_stats)
87 | oup.weight.data.copy_(m.weight.data)
88 | oup.bias.data.copy_(m.bias.data)
89 | oup.running_mean.data.copy_(m.running_mean.data)
90 | oup.running_var.data.copy_(m.running_var.data)
91 | oup.num_batches_tracked.data.copy_(m.num_batches_tracked.data)
92 | if hasattr(m, "qconfig"):
93 | oup.qconfig = m.qconfig
94 | elif isinstance(m, (nn.Conv1d,)):
95 | raise NotImplementedError
96 |
97 | for name, child in m.named_children():
98 | oup.add_module(name, SparseEncoder.dense_model_to_sparse(child, verbose=verbose, sbn=sbn))
99 | del m
100 | return oup
101 |
102 | def forward(self, x):
103 | return self.sp_cnn(x, hierarchical=True)
104 |
--------------------------------------------------------------------------------
/trainers/maskcae_trainer.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-03-18 16:59:36
4 | LastEditTime: 2024-08-13 17:54:05
5 | FilePath: /chengdongzhou/action/MaskCAE/trainers/maskcae_trainer.py
6 | '''
7 | from torch.utils.data import DataLoader
8 | from sklearn import metrics
9 | from configs.base_configs import args
10 | from utils.setup import create_model_opt , lr_wd_annealing,load_checkpoint ,LrSchedulerSet
11 | import torch.nn as nn
12 | from utils.metric import Timer
13 | from utils.logger import Statistics
14 | from tqdm import tqdm, trange
15 | import torch
16 | from torch.nn import functional as F
17 | import wandb
18 | from trainers.evalution import evaluate
19 | import os
20 |
21 | def freeze_modules_by_name(model, module_names):
22 | for name, module in model.named_modules():
23 | if name in module_names:
24 | for param in module.parameters():
25 | param.requires_grad = False
26 | return model
27 |
28 | def LinearEavlution(model):
29 | return freeze_modules_by_name(model,['layer1','layer2','layer3','layer4'])
30 |
31 | def Partial_fine_tuning(model,layer_numbers):
32 | freeze_layer_numbers = 4 - layer_numbers
33 | freeze_layers = [f'layer{ln+1}' for ln in range(freeze_layer_numbers)]
34 | return freeze_modules_by_name(model,freeze_layers)
35 |
36 | def GetEvaMode(model,logger):
37 | if args.linear_evaluation:
38 | logger.info( '==> Linear Probing....')
39 | model = LinearEavlution(model)
40 | eva_flag = 'lp'
41 | else:
42 | if args.partial_ft:
43 | logger.info( '==> Partial Fine Tuning....')
44 | model = Partial_fine_tuning(model,args.ft_layernumbers)
45 | eva_flag = 'pft'
46 | else:
47 | logger.info( '==> Fine Tuning....')
48 | eva_flag = 'ft'
49 | return model,eva_flag
50 |
51 | def maskcae_train(train_set,valid_set,logger):
52 | train_loader = DataLoader( train_set, batch_size=args.batch_size, shuffle=True, drop_last=False,
53 | num_workers=args.num_workers, pin_memory=True )
54 | val_loader = DataLoader( valid_set, batch_size=args.batch_size, shuffle=False, drop_last=False,
55 | num_workers=args.num_workers, pin_memory=True )
56 |
57 | model, optimizer, criterion_cls = create_model_opt()
58 |
59 | stat = {}
60 | stat['best_mf1'] = 0.0
61 | # wandb.watch(model,log_freq=50,log="all")
62 | # logging
63 | logger.info( args )
64 | print( f"==> load pre-trained model..." )
65 | load_checkpoint(args,resume_from=args.pre_trained_path,model=model,optimizer=optimizer)
66 | model,eva_flag = GetEvaMode(model=model,logger=logger)
67 | timer = Timer()
68 | iters_train = len(train_loader)
69 | Test_losses ,Acc_tests, mF1_tests,wF1_tests, Recall_tests, Precision_tests = [],[],[],[],[],[]
70 | for epoch in trange( args.epochs, desc='Training_epoch' ):
71 | model.train()
72 | total_num, total_loss = 0, 0
73 | label_list, predicted_list = [], []
74 | for idx, (data, label) in enumerate(train_loader):
75 | # adjust lr and wd
76 | cur_it = idx + epoch * iters_train
77 | min_lr, max_lr, min_wd, max_wd = lr_wd_annealing(optimizer, args.learning_rate, args.weight_decay, cur_it, args.warm_up_epochs * iters_train, args.epochs * iters_train)
78 | timer.start()
79 | inputs, label = data.cuda().float(), label.cuda().long()
80 | output = model(inputs)['output']
81 | loss = criterion_cls(output, label)
82 | optimizer.zero_grad()
83 | loss.backward()
84 | # orig_norm = nn.utils.clip_grad_norm_( model.parameters(), 100 )
85 | optimizer.step()
86 |
87 | timer.stop()
88 | total_loss += loss.detach().item()
89 | with torch.no_grad():
90 | _, predicted = torch.max( output, 1 )
91 | label_list.append(label)
92 | predicted_list.append( predicted )
93 | total_num = total_num + len( label )
94 |
95 | label = torch.cat(label_list).cpu().detach().numpy()
96 | predicted = torch.cat(predicted_list).cpu().detach().numpy()
97 | acc_train = ( predicted == label ).sum() / total_num
98 | f1_train = metrics.f1_score( label, predicted, average='macro' )
99 | loss_train = total_loss/len(train_loader)
100 |
101 | logger.info( f'Epoch:[{epoch}/{args.epochs}] - {eva_flag}_loss:{loss_train:.7f}, {eva_flag}_train@Acc: {acc_train:.5f}, {eva_flag}_train@F1: {f1_train:.5f}')
102 |
103 | wandb.log({f'{eva_flag} train loss' :loss_train ,
104 | f'{eva_flag} train Acc' :acc_train ,
105 | f'{eva_flag} train F1' :f1_train ,
106 | 'min_lr':min_lr,
107 | 'max_lr':max_lr,
108 | 'min_wd':min_wd,
109 | 'max_wd':max_wd
110 | } ,
111 | commit=False) if args.use_wandb else None
112 |
113 | Test_loss , Acc_test, mF1_test,wF1_test, Recall_test, Precision_test, stat = evaluate(model, logger=logger, eval_loader = val_loader , epoch = epoch , is_test=False, stat=stat)
114 |
115 | for elem , save_res in zip((Test_loss,Acc_test, mF1_test, wF1_test, Recall_test, Precision_test),(Test_losses,Acc_tests, mF1_tests,wF1_tests, Recall_tests, Precision_tests)):
116 | save_res.append(elem)
117 |
118 | stat = Statistics(stat,Test_losses, Acc_tests, mF1_tests, wF1_tests , Recall_tests, Precision_tests,sum_time= timer.sum() )
119 | # wandb.save('model.h5')
120 |
121 | return stat
--------------------------------------------------------------------------------
/models/ConvNet.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-05-22 16:58:22
4 | LastEditTime: 2024-08-13 19:31:13
5 | FilePath: /chengdongzhou/action/MaskCAE/models/ConvNet.py
6 | '''
7 | import torch.nn as nn
8 | import torch.nn.functional as F
9 | import os
10 | import sys
11 | sys.path.append(os.path.dirname(os.path.dirname(__file__)))
12 | import torch
13 | from common import channel_list,conv_list,maxp_list,first_maxp_list
14 | from utils.metric import get_data_size,GetFlopsAndParams,GetFeatureMapSize
15 |
16 |
17 |
18 | class Conv4Net(nn.Module):
19 | def __init__(self, data_name='ucihar' ,use_adativeavg=False, sub_number = 4 ):
20 | super(Conv4Net, self).__init__()
21 | self.dataset = data_name
22 | self.channel= channel = channel_list[data_name]
23 | conv_params = conv_list[data_name]
24 | max_params = maxp_list[data_name]
25 | first_max = first_maxp_list[data_name]
26 |
27 | self.layer1 = self.maker_layers( 1 , channel[0], conv_params=conv_params, pooling_params=max_params )
28 | self.layer2 = self.maker_layers( channel[0], channel[1], conv_params=conv_params, pooling_params=max_params )
29 | self.layer3 = self.maker_layers( channel[1], channel[2], conv_params=conv_params, pooling_params=max_params )
30 | self.layer4 = self.maker_layers( channel[2], channel[3], conv_params=conv_params, pooling_params=max_params )
31 | h,w = GetFeatureMapSize(data_name,sub_number,padding = True)
32 | self.adaptiveAvg = nn.AdaptiveAvgPool2d( ( 1, w ) ) if use_adativeavg else nn.Identity()
33 | self.classifier = nn.Linear( channel[3]*w if use_adativeavg else channel[3]* h * w , channel[-1] )
34 |
35 | def maker_layers(self,inp,oup,conv_params=None,pooling_params=None):
36 | assert isinstance(conv_params,list),print('the format of kernel params is error')
37 | assert isinstance(pooling_params,list) or pooling_params == None ,print('the format of pooling params is error')
38 | return nn.Sequential(
39 | nn.Conv2d( inp, oup, *(conv_params) ),
40 | nn.BatchNorm2d( oup ),
41 | nn.ReLU( True ),
42 | nn.MaxPool2d( *(pooling_params) ) if pooling_params else nn.Identity()
43 | )
44 | def forward(self, x: torch.Tensor):
45 | B,_,_,_ = x.size()
46 | x = F.pad(x, pad=(0,0,1,1), mode='replicate')
47 | x = self.layer1(x)
48 | x = self.layer2(x)
49 | x = self.layer3(x)
50 | x = self.layer4(x)
51 | x = self.adaptiveAvg(x)
52 | x = x.view(B,-1)
53 | x = self.classifier( x )
54 | res = {}
55 | res['output'] = x
56 | return res
57 |
58 |
59 | class Conv4NetSup(nn.Module):
60 | def __init__(self, data_name='ucihar' ,use_adativeavg=False, sub_number = 4 ):
61 | super(Conv4NetSup, self).__init__()
62 | self.dataset = data_name
63 | self.channel= channel = channel_list[data_name]
64 | conv_params = conv_list[data_name]
65 | max_params = maxp_list[data_name]
66 | first_max_params = first_maxp_list[data_name]
67 |
68 | self.layer1 = self.maker_layers( 1 , channel[0], conv_params=conv_params, pooling_params=first_max_params )
69 | self.layer2 = self.maker_layers( channel[0], channel[1], conv_params=conv_params, pooling_params=max_params )
70 | self.layer3 = self.maker_layers( channel[1], channel[2], conv_params=conv_params, pooling_params=max_params )
71 | self.layer4 = self.maker_layers( channel[2], channel[3], conv_params=conv_params, pooling_params=max_params )
72 | h,w = GetFeatureMapSize(data_name,sub_number,padding = True, larger_max= True , is_sup= True )
73 | self.adaptiveAvg = nn.AdaptiveAvgPool2d( ( 1, w ) ) if use_adativeavg else nn.Identity()
74 | self.classifier = nn.Linear( channel[3]*w if use_adativeavg else channel[3]* h * w , channel[-1] )
75 |
76 | def maker_layers(self,inp,oup,conv_params=None,pooling_params=None):
77 | assert isinstance(conv_params,list),print('the format of kernel params is error')
78 | assert isinstance(pooling_params,list) or pooling_params == None ,print('the format of pooling params is error')
79 | return nn.Sequential(
80 | nn.Conv2d( inp, oup, *(conv_params) ),
81 | nn.BatchNorm2d( oup ),
82 | nn.ReLU( True ),
83 | nn.MaxPool2d( *(pooling_params) ) if pooling_params else nn.Identity(),
84 | )
85 |
86 | def forward(self, x: torch.Tensor):
87 | B,_,_,_ = x.size()
88 | if self.dataset in ['uschad']:
89 | x = F.pad(x, pad=(0,0,1,1), mode='replicate')
90 | x = self.layer1(x)
91 | x = self.layer2(x)
92 | x = self.layer3(x)
93 | x = self.layer4(x)
94 | x = self.adaptiveAvg(x)
95 | x = x.view(B,-1)
96 | x = self.classifier( x )
97 | res = {}
98 | res['output'] = x
99 | return res
100 |
101 |
102 | if __name__ == "__main__":
103 | import torch
104 | f_size = {
105 | 'ucihar': (30 , 9 ),
106 | 'uschad': (30 , 6 ),
107 | 'motion': (30 , 12 )
108 | }
109 | dataset = 'motion'
110 | x = torch.randn(64,1,*f_size[dataset])
111 | model = Conv4Net(dataset)
112 | # print(model,model.layer2[1])
113 | GetFlopsAndParams(dataset,model)
114 | print(model(x)['output'].shape)
115 | n_all = sum(p.numel() for p in model.parameters())
116 | n_trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
117 | print("Parameter Count: all {:,d}; trainable {:,d}".format(n_all, n_trainable))
--------------------------------------------------------------------------------
/utils/metric.py:
--------------------------------------------------------------------------------
1 | from thop.profile import profile
2 | from thop import clever_format
3 | import torch
4 | import numpy as np
5 | import time
6 | from common import maxp_list,channel_list,conv_list,first_maxp_list
7 |
8 | def Reture_F_SIZE(dataset , is_sup=False):
9 | F_SIZE = {
10 | 'ucihar': (30 , 9 ) ,
11 | 'uschad': (30 , 6 ) ,
12 | 'motion': (30 , 12 )
13 | }
14 | SUP_F_SIZE = {
15 | 'ucihar': (128 , 9 ) ,
16 | 'uschad': (30 , 6 ) ,
17 | 'motion': (128 , 12 )
18 | }
19 | if not is_sup:
20 | return F_SIZE[dataset]
21 | else:
22 | return SUP_F_SIZE[dataset]
23 |
24 | class Timer:
25 | """Record multiple running times."""
26 | def __init__(self):
27 | self.times = []
28 | self.start()
29 |
30 | def start(self):
31 | """Start the timer."""
32 | self.tik = time.time()
33 |
34 | def stop(self):
35 | """Stop the timer and record the time in a list."""
36 | self.times.append(time.time() - self.tik)
37 | return self.times[-1]
38 |
39 | def avg(self):
40 | """Return the average time."""
41 | return sum(self.times) / len(self.times)
42 |
43 | def sum(self):
44 | """Return the sum of time."""
45 | return sum(self.times)
46 |
47 | def cumsum(self):
48 | """Return the accumulated time."""
49 | return np.array(self.times).cumsum().tolist()
50 |
51 | def get_data_size(data_name, is_sup = False ):
52 | Model_Seen_SSL_F_Size = {
53 | 'ucihar': ( 1, 1, 32, 9 ) ,
54 | 'motion': ( 1, 1, 32, 12 ) ,
55 | 'uschad': ( 1, 1, 32, 6 ) ,
56 | }
57 | Model_Seen_Sup_F_Size = {
58 | 'ucihar': ( 1, 1, 128, 9 ) ,
59 | 'motion': ( 1, 1, 128, 12 ) ,
60 | 'uschad': ( 1, 1, 32, 6 ) ,
61 | }
62 | if is_sup:
63 | size_dict = Model_Seen_Sup_F_Size
64 | else:
65 | size_dict = Model_Seen_SSL_F_Size
66 | if data_name in size_dict:
67 | pass
68 | else:
69 | raise Exception( 'please input correct data name')
70 | return size_dict[data_name]
71 |
72 | def get_classes(data_name):
73 | if data_name == 'ucihar':
74 | classes = 6
75 | elif data_name == 'motion':
76 | classes = 6
77 | elif data_name == 'uschad':
78 | classes = 12
79 | else:
80 | raise Exception( 'please input correct data name')
81 | return classes
82 |
83 |
84 | def GetFeatureMapSize(data_name,idex_layer,padding=False, larger_max=False , is_sup = False ):
85 | size = get_data_size(data_name, is_sup)[2:]
86 | maxpooling_size = maxp_list[data_name]
87 | first_maxp_size = first_maxp_list[data_name]
88 | h,w = size
89 | if idex_layer > 0:
90 | for i in range(idex_layer):
91 | if padding:
92 | if larger_max and i == 0:
93 | h = (( h - first_maxp_size[0][0] + first_maxp_size[2][0] * 2 + first_maxp_size[1][0] ) // first_maxp_size[1][0])
94 | w = (( w - first_maxp_size[0][1] + first_maxp_size[2][1] * 2 + first_maxp_size[1][1] ) // first_maxp_size[1][1])
95 | else:
96 | h = (( h - maxpooling_size[0][0] + maxpooling_size[2][0] * 2 + maxpooling_size[1][0] ) // maxpooling_size[1][0])
97 | w = (( w - maxpooling_size[0][1] + maxpooling_size[2][1] * 2 + maxpooling_size[1][1] ) // maxpooling_size[1][1])
98 | else:
99 | h //= maxpooling_size[0][0]
100 | w //= maxpooling_size[0][1]
101 | return ( h , w )
102 | elif idex_layer == 0:
103 | return ( h , w )
104 | else:
105 | raise ValueError(f'check your idex_layer')
106 |
107 | def GetFeatureMapSizeByConv(data_name,idex_layer):
108 | size = get_data_size(data_name)[2:]
109 | conv_size = conv_list[data_name]
110 | h,w = size
111 | if idex_layer > 0:
112 | for i in range(idex_layer):
113 | h = h - conv_size[0][0] + 1
114 | w = w - conv_size[0][1] + 1
115 | return ( h , w )
116 | else:
117 | raise ValueError(f'check your idex_layer')
118 |
119 | def GetFeatureMapSizeBySelfMax(data_name , max_size, idex_layer):
120 | size = get_data_size(data_name)[2:]
121 | h,w = size
122 | if idex_layer > 0:
123 | for i in range(idex_layer):
124 | h //= max_size[0]
125 | w //= max_size[1]
126 | return ( h , w )
127 | else:
128 | raise ValueError(f'check your idex_layer')
129 |
130 | def GetFlopsAndParams(dataset,net,logger = None , is_sup = False ):
131 | X = torch.randn(1,1,*Reture_F_SIZE(dataset,is_sup))
132 | total_ops, total_params= profile(net, (X,), verbose=False)
133 | flops, params = clever_format([total_ops*2, total_params], "%.3f")
134 | if logger:
135 | logger.info(f'==>FLOPs of model that runs the {dataset} is {flops}')
136 | logger.info(f'==>Params of model that runs the {dataset} is {params}' )
137 | else:
138 | print(f'==>FLOPs of model that runs the {dataset} is {flops}')
139 | print(f'==>Params of model that runs the {dataset} is {params}' )
140 | return flops,params
141 |
142 | def DeConvOrthDist(kernel, stride = 2, padding = 1):
143 | [o_c, i_c, w, h] = kernel.shape
144 | output = torch.conv2d(kernel, kernel, stride=stride, padding=padding)
145 | target = torch.zeros((o_c, o_c, output.shape[-2], output.shape[-1])).cuda()
146 | ct = int(np.floor(output.shape[-1]/2))
147 | target[:,:,ct,ct] = torch.eye(o_c).cuda()
148 | # print(target.shape)
149 | return torch.norm( output - target )
150 |
151 | def GetInferredSpeed(dataset,model,times, is_sup = False ):
152 | x = torch.randn(1,1,*Reture_F_SIZE(dataset,is_sup))
153 | each_times = []
154 | model.eval()
155 | for i in range(times):
156 | start_time = time.time()
157 | model(x)
158 | stop_time = time.time()
159 | each_times.append((stop_time-start_time)*1000)
160 | return np.mean(each_times)
--------------------------------------------------------------------------------
/configs/base_configs.py:
--------------------------------------------------------------------------------
1 | import os
2 | import argparse
3 | import pandas as pd
4 | from configs import parser as _parser
5 | import sys
6 | import yaml
7 |
8 | args = None
9 |
10 | def parse_arguments():
11 | parser = argparse.ArgumentParser()
12 | parser.add_argument('--seed', type=int, default=0, metavar='S',
13 | help='random seed (default: 0). if seed=-1, seed is not fixed.')
14 | parser.add_argument('--batch_size', type=int, default=128, help='batch_size')
15 | parser.add_argument(
16 | "--config", help="Config file to use (see configs dir)", default=None
17 | )
18 | parser.add_argument('--times', type=int, default=1, help='num of different seed')
19 | parser.add_argument('--num_workers', type=int, default=4, help='num of workers to use')
20 | parser.add_argument('--device', type=int, default=2, choices=[0,1,2])
21 | parser.add_argument('--epochs', type=int, default=200, help='number of training epochs')
22 | parser.add_argument('--window_width', type=int, default=0, help='window width')
23 | parser.add_argument('--normalize', action='store_true', default=False, help='normalize signal based on mean/std of training samples')
24 | parser.add_argument('--pretrain', action='store_true', default=False)
25 | parser.add_argument('--not_save_res','--nsr', default=False, action='store_true',
26 | help='The default value is saved.')
27 | parser.add_argument('--mode', type=str, default='ce', help='mode')
28 | parser.add_argument('--dataset_pre', type=str, default='self')
29 | parser.add_argument('--trial', type=str, default='default', help='trial id')
30 | parser.add_argument('--not_avg_exp', action='store_false', default=True)
31 | parser.add_argument('--opt_type', type=str, default='sgd')
32 |
33 | # optimization
34 | parser.add_argument('--learning_rate','--lr', type=float, default=0.01, help='learning rate')
35 | parser.add_argument('--weight_decay', type=float, default = 0.0005 , help='weight decay')
36 | parser.add_argument('--weight_decay_la', type=float, default = 0.0005 , help='weight decay for layer attention')
37 | parser.add_argument('--momentum', type=float, default=0.9, help='momentum')
38 | parser.add_argument('--lr_scheduler', type=str, default='S', help='lr_scheduler')
39 | parser.add_argument('--warm_up_epochs', type=int, default=5, help='linear warm up epochs')
40 | parser.add_argument('--handcrafted_lr', action='store_true', default=False, help='learning rate with warm up')
41 | parser.add_argument('--chhander', type=bool, default=False)
42 |
43 |
44 | # MultiStepLR or ExponentialLR
45 | parser.add_argument('--gamma',type=float,default=0.1,help='gamma')##
46 | parser.add_argument('--milestones',type=list,default=[40,80,120,160],help='optimizer milestones')
47 | parser.add_argument('--decay_epochs',type=float,default=40,help='n_epochs for CosineAnnealingLR or StepLR')
48 |
49 | # CosineAnnealingLR
50 | parser.add_argument('--n_epochs',type=float,default=20,help='n_epochs for CosineAnnealingLR')
51 |
52 | # dataset and model
53 | parser.add_argument('--model', type=str, default='EarlyFusion')
54 | parser.add_argument('--dataset', type=str, default='ucihar')
55 | parser.add_argument('--no_clean', action='store_false', default=False)
56 | parser.add_argument('--no_null', action='store_false', default=True)
57 | parser.add_argument('--train_portion', type=float, default=1.0, help='use portion of trainset')
58 | parser.add_argument('--model_path', type=str, default='save', help='path to save model')
59 | parser.add_argument('--load_model', type=str, default='', help='load the pretrained model')
60 |
61 | # coefficients
62 | parser.add_argument('--lam', type=float, default=0.0, help='An alternate measure coefficient, which defaults to 0')
63 | parser.add_argument('--p', type=float, default=0.0, help='An alternate measure coefficient, which defaults to 0')
64 | parser.add_argument('--beta', type=float, default=0.0, help='An alternate measure coefficient, which defaults to 0')
65 |
66 |
67 |
68 | # for wandb
69 | parser.add_argument('--use_wandb', default=False, action='store_true')
70 |
71 | # for maskcae
72 | parser.add_argument('--partial_ft', type=bool, default=False)
73 | parser.add_argument('--ft_layernumbers', type=int, default=0)
74 | parser.add_argument('--semi_number', type=int, default=0)
75 | parser.add_argument('--mask', type=float, default=-1)
76 | parser.add_argument('--ablationMode', type=str,default='')
77 | parser.add_argument('--pre_epochs', type=int)
78 | parser.add_argument('--decoder_type', type=str)
79 | parser.add_argument('--IP_type', type=str)
80 | parser.add_argument('--loss_type', type=str)
81 | parser.add_argument('--linear_evaluation', type=bool, default=False)
82 | parser.add_argument('--vis_feature', action='store_true', default=False)
83 | args = parser.parse_args()
84 |
85 | get_config(args)
86 |
87 | return args
88 |
89 |
90 | def get_config(args):
91 | # get commands from command line
92 | override_args = _parser.argv_to_vars(sys.argv)
93 | yaml_path = os.path.join(os.path.dirname(os.path.dirname(__file__)) , args.config )
94 | print(f'==> local yaml path:{yaml_path}')
95 | # load yaml file
96 | yaml_txt = open( yaml_path).read()
97 |
98 | # override args
99 | loaded_yaml = yaml.load(yaml_txt, Loader=yaml.FullLoader)
100 | for v in override_args:
101 | loaded_yaml[v] = getattr(args, v)
102 |
103 | print(f"==> Reading YAML config from {args.config}")
104 | args.__dict__.update(loaded_yaml)
105 |
106 |
107 | def run_args():
108 | global args
109 | if args is None:
110 | args = parse_arguments()
111 |
112 |
113 | run_args()
114 |
115 | if args.pretrain:
116 | args.lambda_cls = 0.0
117 | args.lambda_ssl = 1.0
118 |
119 | def dict_to_markdown(d, max_str_len=120):
120 | # convert list into its str representation
121 | d = {k: v.__repr__() if isinstance(v, list) else v for k, v in d.items()}
122 | # truncate string that is longer than max_str_len
123 | if max_str_len is not None:
124 | d = {k: v[-max_str_len:] if isinstance(v, str) else v for k, v in d.items()}
125 | return pd.DataFrame(d, index=[0]).transpose().to_markdown()
126 |
127 | #Display settings
128 | #print(dict_to_markdown(vars(args), max_str_len=120))
--------------------------------------------------------------------------------
/dataloaders/__init__.py:
--------------------------------------------------------------------------------
1 | from torch.utils.data import Dataset
2 | import torch
3 | import numpy as np
4 | import os
5 | import pickle
6 | from tqdm import tqdm
7 |
8 | from .dataloader_uci_har import UCI_HAR_DATA
9 | from .dataloader_MOTION_Sense_har import MotionSense_HAR_DATA
10 | from .dataloader_USCHAD_har import USC_HAD_HAR_DATA
11 | data_dict = {
12 | "ucihar" : UCI_HAR_DATA,
13 | "motion" : MotionSense_HAR_DATA,
14 | "uschad" : USC_HAD_HAR_DATA,
15 | }
16 |
17 | class data_set(Dataset):
18 | def __init__(self, args, dataset, flag):
19 | """
20 | args : a dict , In addition to the parameters for building the model, the parameters for reading the data are also in here
21 | dataset : It should be implmented dataset object, it contarins train_x, train_y, vali_x,vali_y,test_x,test_y
22 | flag : (str) "train","test","vali"
23 | """
24 | self.args = args
25 | self.flag = flag
26 | self.load_all = args.load_all
27 | self.data_x = dataset.normalized_data_x
28 | self.data_y = dataset.data_y
29 | self.slidingwindows = dataset.slidingwindows
30 | self.act_weights = dataset.act_weights
31 |
32 | if self.args.representation_type in ["freq","time_freq"]:
33 | if flag in ["train","vali"]:
34 | self.freq_path = dataset.train_freq_path
35 | self.freq_file_name = dataset.train_freq_file_name
36 | if self.load_all :
37 | self.data_freq = dataset.data_freq
38 | else:
39 | self.freq_path = dataset.test_freq_path
40 | self.freq_file_name = dataset.test_freq_file_name
41 | self.load_all = False
42 |
43 | if self.flag == "train":
44 | # load train
45 | self.window_index = dataset.train_window_index
46 | print(f"================ Train data number: {len(self.window_index)} =================")
47 |
48 |
49 | elif self.flag == "vali":
50 | # load vali
51 |
52 | self.window_index = dataset.valid_window_index
53 | print(f"================ Validation data number: {len(self.window_index)} ================")
54 |
55 |
56 | else:
57 | # load test
58 | self.window_index = dataset.test_window_index
59 | print(f"================ Test data number: {len(self.window_index)} ================")
60 |
61 |
62 | all_labels = list(np.unique(dataset.data_y))
63 | to_drop = list(dataset.drop_activities)
64 | label = [item for item in all_labels if item not in to_drop]
65 | self.nb_classes = len(label)
66 | assert self.nb_classes==len(dataset.no_drop_activites)
67 |
68 | classes = dataset.no_drop_activites
69 | self.class_transform = {x: i for i, x in enumerate(classes)}
70 | self.class_back_transform = {i: x for i, x in enumerate(classes)}
71 | self.input_length = self.slidingwindows[0][2]-self.slidingwindows[0][1]
72 | self.channel_in = self.data_x.shape[1]-2
73 |
74 |
75 | #if self.args.wavelet_filtering:
76 | # SelectedWavelet = PrepareWavelets(K=self.args.number_wavelet_filtering, length=self.args.windowsize)
77 | # self.ScaledFilter = FiltersExtention(SelectedWavelet)
78 | # if self.args.windowsize%2==1:
79 | # self.Filter_ReplicationPad1d = torch.nn.ReplicationPad1d(int((self.args.windowsize-1)/2))
80 | # else:
81 | # self.Filter_ReplicationPad1d = torch.nn.ReplicationPad1d(int(self.args.windowsize/2))
82 |
83 | if self.flag == "train":
84 | print("==>The number of classes is : ", self.nb_classes)
85 | print("==>The input_length is : ", self.input_length)
86 | print("==>The channel_in is : ", self.channel_in)
87 |
88 |
89 | def __getitem__(self, index):
90 | #print(index)
91 | index = self.window_index[index]
92 | start_index = self.slidingwindows[index][1]
93 | end_index = self.slidingwindows[index][2]
94 | # print(index,start_index,end_index)
95 | if self.args.representation_type == "time":
96 |
97 | if self.args.sample_wise ==True:
98 | sample_x = np.array(self.data_x.iloc[start_index:end_index, 1:-1].apply(lambda x: (x - np.mean(x)) / (np.max(x) - np.min(x))))
99 | else:
100 | sample_x = self.data_x.iloc[start_index:end_index, 1:-1].values
101 |
102 | sample_y = self.class_transform[self.data_y.iloc[start_index:end_index].mode().loc[0]]
103 |
104 | sample_x = np.expand_dims(sample_x,0)
105 |
106 | if self.args.multicrop and self.flag == "train":
107 | if self.args.mc_local_number > 0:
108 | sample_x = [sample_x] + np.array_split(sample_x,self.args.mc_local_number,1)
109 | else:
110 | pass
111 | return sample_x , sample_y
112 |
113 | elif self.args.representation_type == "freq":
114 |
115 | if self.load_all:
116 | sample_x = self.data_freq[self.freq_file_name[index]]
117 | else:
118 | with open(os.path.join(self.freq_path,"{}.pickle".format(self.freq_file_name[index])), 'rb') as handle:
119 | sample_x = pickle.load(handle)
120 |
121 | sample_y = self.class_transform[self.data_y.iloc[start_index:end_index].mode().loc[0]]
122 |
123 | return sample_x, sample_y,sample_y
124 |
125 | else:
126 |
127 | if self.args.sample_wise ==True:
128 | sample_ts_x = np.array(self.data_x.iloc[start_index:end_index, 1:-1].apply(lambda x: (x - np.mean(x)) / (np.max(x) - np.min(x))))
129 | else:
130 | sample_ts_x = self.data_x.iloc[start_index:end_index, 1:-1].values
131 |
132 |
133 | if self.load_all:
134 | sample_fq_x = self.data_freq[self.freq_file_name[index]]
135 | else:
136 | with open(os.path.join(self.freq_path,"{}.pickle".format(self.freq_file_name[index])), 'rb') as handle:
137 | sample_fq_x = pickle.load(handle)
138 |
139 | sample_y = self.class_transform[self.data_y.iloc[start_index:end_index].mode().loc[0]]
140 |
141 |
142 | return sample_ts_x, sample_fq_x , sample_y
143 |
144 | def __len__(self):
145 | return len(self.window_index)
146 |
147 |
--------------------------------------------------------------------------------
/create_dataset.py:
--------------------------------------------------------------------------------
1 |
2 | import warnings
3 | warnings.filterwarnings("ignore")
4 |
5 | import os
6 | import sys
7 | sys.path.append(os.path.dirname(__file__))
8 |
9 | from torch.utils.data import DataLoader
10 |
11 | from dataloaders import data_set,data_dict
12 | import torch
13 | import yaml
14 | import os
15 | import pandas as pd
16 | import matplotlib.pyplot as plt
17 | import numpy as np
18 | import argparse
19 |
20 | WINDOWS_SAVE_PATH = r"./MaskCAE/datasets/Sliding_window"
21 | ROOT_PATH = r"./data/raw"
22 |
23 | class dotdict(dict):
24 | """dot.notation access to dictionary attributes"""
25 | __getattr__ = dict.get
26 | __setattr__ = dict.__setitem__
27 | __delattr__ = dict.__delitem__
28 |
29 |
30 |
31 | def GetHarDataset(dataset_name , data_args):
32 |
33 | data_args.freq_save_path = None
34 | data_args.window_save_path = WINDOWS_SAVE_PATH
35 | data_args.root_path = ROOT_PATH
36 |
37 | data_args.drop_transition = False
38 |
39 | data_args.batch_size = 128
40 | data_args.shuffle = True
41 | data_args.drop_last = False
42 | data_args.train_vali_quote = 0.8
43 | data_args.down_sample = True
44 |
45 | data_args.data_name = dataset_name # wisdm
46 |
47 |
48 | # 是否作augmentation difference
49 | data_args.difference = False
50 |
51 | # 是否作augmentation filtering
52 | data_args.filtering = False
53 |
54 | # 是否作augmentation magnitude
55 | data_args.magnitude = False
56 | # 是否按照权重抽取批次
57 | data_args.weighted_sampler = False
58 |
59 | data_args.load_all = None
60 | # 是在load数据的时候 wavelet_filtering
61 | data_args.wavelet_filtering = False
62 | data_args.number_wavelet_filtering = 10
63 |
64 | data_args.datanorm_type = "standardization" # None ,"standardization", "minmax"
65 |
66 | data_args.pos_select = None
67 | data_args.sensor_select = None
68 | data_args.sample_wise = False
69 | data_args.multicrop = False
70 |
71 | data_args.representation_type = "time"
72 |
73 |
74 | config_file = open(data_args.yaml_path, mode='r')
75 | data_config = yaml.load(config_file, Loader=yaml.FullLoader)
76 | config = data_config[data_args.data_name]
77 |
78 | data_args.root_path = os.path.join(data_args.root_path,config["filename"])
79 | data_args.sampling_freq = config["sampling_freq"]
80 | data_args.valid_rate = config["valid_rate"]
81 | data_args.overlap_rate = config["overlap_rate"]
82 | data_args.down_sample = config["down_sample"]
83 | window_seconds = config["window_seconds"]
84 | data_args.windowsize = int(window_seconds * data_args.sampling_freq)
85 | data_args.c_in = config["num_channels"]
86 | data_args.exp_mode = config["exp_mode"]
87 |
88 | data_args.input_length = data_args.windowsize
89 | # input information
90 | if data_args.wavelet_filtering :
91 |
92 | if data_args.windowsize%2==1:
93 | N_ds = int(torch.log2(torch.tensor(data_args.windowsize-1)).floor()) - 2
94 | else:
95 | N_ds = int(torch.log2(torch.tensor(data_args.windowsize)).floor()) - 2
96 |
97 | data_args.f_in = data_args.number_wavelet_filtering*N_ds+1
98 | else:
99 | data_args.f_in = 1
100 |
101 | return data_args , data_dict[data_args.data_name](data_args)
102 |
103 | def CheckDataloader(data_args , dataset):
104 | print("================ {} Mode ====================".format(dataset.exp_mode))
105 | print("================ {} CV ======================".format(dataset.num_of_cv))
106 | for i in range(dataset.num_of_cv):
107 | dataset.update_train_val_test_keys()
108 | train_data = data_set(data_args,dataset,"train")
109 | test_data = data_set(data_args,dataset,"test")
110 | vali_data = data_set(data_args,dataset,"vali")
111 |
112 |
113 | # form the dataloader
114 | train_data_loader = DataLoader(train_data,
115 | batch_size = data_args.batch_size,
116 | shuffle = data_args.shuffle,
117 | num_workers = 4,
118 | drop_last = data_args.drop_last)
119 |
120 | vali_data_loader = DataLoader(vali_data,
121 | batch_size = data_args.batch_size,
122 | shuffle = data_args.shuffle,
123 | num_workers = 4,
124 | drop_last = data_args.drop_last)
125 |
126 | test_data_loader = DataLoader(test_data,
127 | batch_size = data_args.batch_size,
128 | shuffle = data_args.shuffle,
129 | num_workers = 4,
130 | drop_last = data_args.drop_last)
131 |
132 | for datas,label in train_data_loader:
133 | if data_args.multicrop:
134 | [print(data.shape) for data in datas ]
135 | else:
136 | print(datas.shape)
137 | break
138 |
139 |
140 |
141 |
142 |
143 |
144 | def test(dataset):
145 | data_args = dotdict()
146 | data_args.all = False
147 | data_args.name = dataset
148 | if data_args.all:
149 | assert data_args.name == None, f'can not choice the dataset {data_args.name}'
150 | dataset_list = ['hapt','oppo','wisdm','dg','uschad','pamap2','rw','skoda','dsads','motion','ucihar','mhealth']
151 | for dataset_name in dataset_list:
152 | print(f'==>generate {dataset_name} datset-----------------------')
153 | data_args , dataset = GetHarDataset(dataset_name,data_args)
154 | CheckDataloader(data_args , dataset)
155 | else:
156 | assert data_args.name , 'please give a dataset name'
157 | print(f'==>generate {data_args.name} datset-----------------------')
158 | data_args , dataset = GetHarDataset(data_args.name,data_args)
159 | CheckDataloader(data_args , dataset)
160 |
161 |
162 | if __name__ == '__main__':
163 | np.random.seed(1)
164 | parser = argparse.ArgumentParser()
165 | parser.add_argument('--dataset',type=str,default='motion')
166 | args = parser.parse_args()
167 | test(args.dataset)
--------------------------------------------------------------------------------
/models/Baseline_CNN.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-05-22 16:58:22
4 | LastEditTime: 2024-08-13 18:48:44
5 | FilePath: /chengdongzhou/action/MaskCAE/models/Baseline_CNN.py
6 | '''
7 | import torch.nn as nn
8 | import torch.nn.functional as F
9 | import os
10 | import sys
11 | sys.path.append(os.path.dirname(os.path.dirname(__file__)))
12 | import torch
13 | from common import channel_list,conv_list,maxp_list,first_maxp_list
14 | from utils.metric import get_data_size,GetFlopsAndParams,GetInferredSpeed
15 |
16 | def GetFeatureMapSize(data_name,idex_layer,padding=False, larger_max=False , is_sup = False ):
17 | size = get_data_size(data_name, is_sup)[2:]
18 | maxpooling_size = maxp_list[data_name]
19 | first_maxp_size = first_maxp_list[data_name]
20 | h,w = size
21 | if idex_layer > 0:
22 | for i in range(idex_layer):
23 | if padding:
24 | if larger_max and i == 0:
25 | h = (( h - first_maxp_size[0][0] + first_maxp_size[2][0] * 2 + first_maxp_size[1][0] ) // first_maxp_size[1][0])
26 | w = (( w - first_maxp_size[0][1] + first_maxp_size[2][1] * 2 + first_maxp_size[1][1] ) // first_maxp_size[1][1])
27 | else:
28 | h = (( h - maxpooling_size[0][0] + maxpooling_size[2][0] * 2 + maxpooling_size[1][0] ) // maxpooling_size[1][0])
29 | w = (( w - maxpooling_size[0][1] + maxpooling_size[2][1] * 2 + maxpooling_size[1][1] ) // maxpooling_size[1][1])
30 | else:
31 | h //= maxpooling_size[0][0]
32 | w //= maxpooling_size[0][1]
33 | return ( h , w )
34 | elif idex_layer == 0:
35 | return ( h , w )
36 | else:
37 | raise ValueError(f'check your idex_layer')
38 |
39 | class BaseCNN(nn.Module):
40 | def __init__(self, data_name='ucihar' ,use_adativeavg=False, sub_number = 4 ):
41 | super(BaseCNN, self).__init__()
42 | self.dataset = data_name
43 | self.channel= channel = channel_list[data_name]
44 | conv_params = conv_list[data_name]
45 | max_params = maxp_list[data_name]
46 |
47 | self.layer1 = self.maker_layers( 1 , channel[0], conv_params=conv_params, pooling_params=max_params )
48 | self.layer2 = self.maker_layers( channel[0], channel[1], conv_params=conv_params, pooling_params=max_params )
49 | self.layer3 = self.maker_layers( channel[1], channel[2], conv_params=conv_params, pooling_params=max_params )
50 | self.layer4 = self.maker_layers( channel[2], channel[3], conv_params=conv_params, pooling_params=max_params )
51 | h,w = GetFeatureMapSize(data_name,sub_number,padding = True)
52 | self.adaptiveAvg = nn.AdaptiveAvgPool2d( ( 1, w ) ) if use_adativeavg else nn.Identity()
53 | self.classifier = nn.Linear( channel[3]*w if use_adativeavg else channel[3]* h * w , channel[-1] )
54 |
55 | def maker_layers(self,inp,oup,conv_params=None,pooling_params=None):
56 | assert isinstance(conv_params,list),print('the format of kernel params is error')
57 | assert isinstance(pooling_params,list) or pooling_params == None ,print('the format of pooling params is error')
58 | return nn.Sequential(
59 | nn.Conv2d( inp, oup, *(conv_params) ),
60 | nn.BatchNorm2d( oup ),
61 | nn.ReLU( True ),
62 | nn.MaxPool2d( *(pooling_params) ) if pooling_params else nn.Identity(),
63 | )
64 |
65 | def forward(self, x: torch.Tensor):
66 | B,_,_,_ = x.size()
67 | if self.dataset in ['uschad','motion','ucihar']:
68 | x = F.pad(x, pad=(0,0,1,1), mode='replicate')
69 | x = self.layer1(x)
70 | x = self.layer2(x)
71 | x = self.layer3(x)
72 | x = self.layer4(x)
73 | x = self.adaptiveAvg(x)
74 | x = x.view(B,-1)
75 | x = self.classifier( x )
76 | res = {}
77 | res['output'] = x
78 | return res
79 |
80 |
81 |
82 | class BaseCNNSup(nn.Module):
83 | def __init__(self, data_name='ucihar' ,use_adativeavg=False, sub_number = 4 ):
84 | super(BaseCNNSup, self).__init__()
85 | self.dataset = data_name
86 | self.channel= channel = channel_list[data_name]
87 | conv_params = conv_list[data_name]
88 | max_params = maxp_list[data_name]
89 | first_max_params = first_maxp_list[data_name]
90 |
91 | self.layer1 = self.maker_layers( 1 , channel[0], conv_params=conv_params, pooling_params=first_max_params )
92 | self.layer2 = self.maker_layers( channel[0], channel[1], conv_params=conv_params, pooling_params=max_params )
93 | self.layer3 = self.maker_layers( channel[1], channel[2], conv_params=conv_params, pooling_params=max_params )
94 | self.layer4 = self.maker_layers( channel[2], channel[3], conv_params=conv_params, pooling_params=max_params )
95 | h,w = GetFeatureMapSize(data_name,sub_number,padding = True, larger_max= True , is_sup= True )
96 | self.adaptiveAvg = nn.AdaptiveAvgPool2d( ( 1, w ) ) if use_adativeavg else nn.Identity()
97 | self.classifier = nn.Linear( channel[3]*w if use_adativeavg else channel[3]* h * w , channel[-1] )
98 |
99 | def maker_layers(self,inp,oup,conv_params=None,pooling_params=None):
100 | assert isinstance(conv_params,list),print('the format of kernel params is error')
101 | assert isinstance(pooling_params,list) or pooling_params == None ,print('the format of pooling params is error')
102 | return nn.Sequential(
103 | nn.Conv2d( inp, oup, *(conv_params) ),
104 | nn.BatchNorm2d( oup ),
105 | nn.ReLU( True ),
106 | nn.MaxPool2d( *(pooling_params) ) if pooling_params else nn.Identity(),
107 | )
108 |
109 | def forward(self, x: torch.Tensor):
110 | B,_,_,_ = x.size()
111 | if self.dataset in ['uschad']:
112 | x = F.pad(x, pad=(0,0,1,1), mode='replicate')
113 | x = self.layer1(x)
114 | x = self.layer2(x)
115 | x = self.layer3(x)
116 | x = self.layer4(x)
117 | x = self.adaptiveAvg(x)
118 | x = x.view(B,-1)
119 | x = self.classifier( x )
120 | res = {}
121 | res['output'] = x
122 | return res
123 |
124 | if __name__ == "__main__":
125 | import torch
126 | f_size = {
127 | 'ucihar': (30 , 9 ),
128 | 'uschad': (30 , 6 ),
129 | 'motion': (30 , 6 )
130 | }
131 | dataset = 'ucihar'
132 | x = torch.randn(64,1,*f_size[dataset])
133 | model = BaseCNN(dataset)
134 | # print(model,model.layer2[1])
135 | GetFlopsAndParams(dataset,model)
136 | time = GetInferredSpeed(dataset,model,500)
137 | print(f'==>inferred time is {time}')
138 | print(model(x)['output'].shape)
139 | n_all = sum(p.numel() for p in model.parameters())
140 | n_trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
141 | print("Parameter Count: all {:,d}; trainable {:,d}".format(n_all, n_trainable))
--------------------------------------------------------------------------------
/dataloaders/dataloader_MOTION_Sense_har.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | import os
4 |
5 | from dataloaders.dataloader_base import BASE_DATA
6 |
7 | # ======================================== MotionSense_HAR_DATA =============================
8 | class MotionSense_HAR_DATA(BASE_DATA):
9 | """
10 | MotionSense Dataset
11 | https://github.com/mmalekzadeh/motion-sense
12 |
13 | Brief Description of the Dataset:
14 | ---------------------------------
15 |
16 | This dataset includes time-series data generated by accelerometer and gyroscope sensors
17 | (attitude, gravity, userAcceleration, and rotationRate).
18 | It is collected with an iPhone 6s kept in the participant's front pocket using SensingKit
19 | which collects information from Core Motion framework on iOS devices.
20 | All data collected in 50Hz sample rate.
21 |
22 | A total of 24 participants in a range of gender, age, weight, and height
23 | performed 6 activities in 15 trials in the same environment and conditions:
24 |
25 | downstairs, upstairs, walking, jogging, sitting, and standing.
26 |
27 | With this dataset, we aim to look for personal attributes fingerprints in time-series of sensor data,
28 | i.e. attribute-specific patterns that can be used to infer gender or personality of the data subjects in addition to their activities.
29 |
30 |
31 | There three different folders. Usually, you just need the folder (A) (DeviceMotion),
32 | because this folder includes everything that can be captured by both Accelerometer and Gyroscope.
33 |
34 | DeviceMotion_data
35 | This folder contains time-series collected by both Accelerometer and Gyroscope for all 15 trials.
36 |
37 | Thus, we have time-series with 12 features:
38 |
39 | attitude.roll
40 | attitude.pitch
41 | attitude.yaw
42 | gravity.x
43 | gravity.y
44 | gravity.z
45 | rotationRate.x
46 | rotationRate.y
47 | rotationRate.z
48 | userAcceleration.x
49 | userAcceleration.y
50 | userAcceleration.z
51 |
52 |
53 | Labels
54 | There are 6 different labels:
55 |
56 | dws: downstairs
57 | ups: upstairs
58 | sit: sitting
59 | std: standing
60 | wlk: walking
61 | jog: jogging
62 | """
63 |
64 | def __init__(self, args):
65 |
66 | """
67 | root_path : Root directory of the data set
68 | difference (bool) : Whether to calculate the first order derivative of the original data
69 | datanorm_type (str) : Methods of data normalization: "standardization", "minmax" , "per_sample_std", "per_sample_minmax"
70 |
71 | spectrogram (bool): Whether to convert raw data into frequency representations
72 | scales : Depends on the sampling frequency of the data ( UCI 数据的采样频率??)
73 | wavelet : Methods of wavelet transformation
74 |
75 | """
76 |
77 | self.used_cols = [] # no use , use the all columns,
78 | self.col_names = [] # the original files have column name
79 |
80 | self.label_map = [(0, 'dws'), # downstairs
81 | (1, "ups"), # upstairs
82 | (2, "sit"), # sitting
83 | (3, "std"), # standing
84 | (4, "wlk"), # walking
85 | (5, "jog"), # jogging
86 | ]
87 | self.drop_activities = []
88 |
89 | # TODO , There all in total 25 subjects !
90 | # The original train test is ## We consider long trials as training dataset and short trials as test dataset
91 | # The use trial to do the train test split
92 |
93 | self.train_keys = [1,2,3,4,5,7,8,9,10,11,13,14,15,16,17,19,20,21,22,23] # 5,11,17,23
94 | self.vali_keys = []
95 | self.test_keys = [6,12,18,24]
96 |
97 | self.exp_mode = args.exp_mode
98 | self.down_sample:bool \
99 | = args.down_sample
100 |
101 | if self.exp_mode == "LOCV":
102 | self.split_tag = "sub"
103 | else:
104 | self.split_tag = "sub"
105 |
106 | self.all_keys = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]
107 | self.LOCV_keys = [list(elem) for elem in np.array_split(self.all_keys,5)]
108 | self.sub_ids_of_each_sub = {}
109 |
110 | self.file_encoding = {} # no use
111 |
112 |
113 | self.labelToId = {int(x[0]): i for i, x in enumerate(self.label_map)}
114 | self.all_labels = list(range(len(self.label_map)))
115 |
116 | self.drop_activities = [self.labelToId[i] for i in self.drop_activities]
117 | self.no_drop_activites = [item for item in self.all_labels if item not in self.drop_activities]
118 |
119 | super(MotionSense_HAR_DATA, self).__init__(args)
120 |
121 | def load_all_the_data(self, root_path):
122 | print(" ----------------------- load all the data -------------------")
123 |
124 | trial_codes = {"dws":[1,2,11], "ups":[3,4,12], "wlk":[7,8,15], "jog":[9,16], "sit":[5,13], "std":[6,14]}
125 |
126 | df_dict = {}
127 |
128 | for sub in range(1,25):
129 |
130 | for act in ('dws', 'ups', 'wlk', 'jog', 'sit', 'std'):
131 |
132 | for trial in trial_codes[act]:
133 |
134 | fname = os.path.join(root_path,'A_DeviceMotion_data/'+act+'_'+str(trial)+'/sub_'+str(int(sub))+'.csv')
135 | sub_data = pd.read_csv(fname)
136 | sub_data = sub_data.drop(['Unnamed: 0'], axis=1)
137 |
138 | sub_data["activity_id"] = act
139 |
140 | sub_id = "{}_{}_{}".format(sub,act,trial)
141 | sub_data["sub_id"] = sub_id
142 | sub_data["sub"] = sub
143 |
144 | df_dict[sub_id] = sub_data
145 |
146 | if self.split_tag != 'sub':
147 | if trial > 10:
148 | self.test_keys.append(sub_id)
149 | else:
150 | self.train_keys.append(sub_id)
151 |
152 | if sub not in self.sub_ids_of_each_sub.keys():
153 | self.sub_ids_of_each_sub[sub] = []
154 | self.sub_ids_of_each_sub[sub].append(sub_id)
155 |
156 | df_all = pd.concat(df_dict)
157 |
158 | if self.down_sample:
159 | # Downsampling! form 50hz to 30hz
160 | df_all.reset_index(drop=True,inplace=True)
161 | index_list = list( np.arange(0,df_all.shape[0],5/3).astype(int) )
162 | df_all = df_all.iloc[index_list]
163 |
164 | df_all = df_all.set_index('sub_id')
165 |
166 | label_mapping = {item[1]:item[0] for item in self.label_map}
167 | # because the activity label in the df is not encoded, thet are 'dws', 'ups', 'wlk', 'jog', 'sit', 'std'
168 | # first, map them in to nummeric number
169 | df_all["activity_id"] = df_all["activity_id"].map(label_mapping)
170 | df_all["activity_id"] = df_all["activity_id"].map(self.labelToId)
171 |
172 | # reorder the columns as sensor1, sensor2... sensorn, sub, activity_id
173 | df_all = df_all[list(df_all.columns[:-2])+["sub"]+["activity_id"]]
174 |
175 | data_y = df_all.iloc[:,-1]
176 | data_x = df_all.iloc[:,:-1]
177 | # drop partial sensors for special task.
178 | # data_x = df_all.drop(columns=['attitude.roll', 'attitude.pitch', 'attitude.yaw', 'rotationRate.x', 'rotationRate.y', 'rotationRate.z'])
179 | # data_x = data_x.loc[:, ['userAcceleration.x', 'userAcceleration.y' ,'userAcceleration.z', 'gravity.x' , 'gravity.y' , 'gravity.z' , 'sub' ]]
180 | # acc, gra
181 | data_x = data_x.reset_index()
182 | # sub_id, sensor1, sensor2... sensorn, sub,
183 | return data_x, data_y
184 |
--------------------------------------------------------------------------------
/MaskCAE/pretrain/utils/lamb.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | """ PyTorch Lamb optimizer w/ behaviour similar to NVIDIA FusedLamb
4 | This optimizer code was adapted from the following (starting with latest)
5 | * https://github.com/HabanaAI/Model-References/blob/2b435114fe8e31f159b1d3063b8280ae37af7423/PyTorch/nlp/bert/pretraining/lamb.py
6 | * https://github.com/NVIDIA/DeepLearningExamples/blob/master/PyTorch/LanguageModeling/Transformer-XL/pytorch/lamb.py
7 | * https://github.com/cybertronai/pytorch-lamb
8 | Use FusedLamb if you can (GPU). The reason for including this variant of Lamb is to have a version that is
9 | similar in behaviour to APEX FusedLamb if you aren't using NVIDIA GPUs or cannot install/use APEX.
10 | In addition to some cleanup, this Lamb impl has been modified to support PyTorch XLA and has been tested on TPU.
11 | Original copyrights for above sources are below.
12 | Modifications Copyright 2021 Ross Wightman
13 | """
14 | import math
15 |
16 | import torch
17 | from torch.optim.optimizer import Optimizer
18 |
19 |
20 | class TheSameAsTimmLAMB(Optimizer):
21 | """Implements a pure pytorch variant of FuseLAMB (NvLamb variant) optimizer from apex.optimizers.FusedLAMB
22 | reference: https://github.com/NVIDIA/DeepLearningExamples/blob/master/PyTorch/LanguageModeling/Transformer-XL/pytorch/lamb.py
23 |
24 | LAMB was proposed in `Large Batch Optimization for Deep Learning: Training BERT in 76 minutes`_.
25 |
26 | Arguments:
27 | params (iterable): iterable of parameters to optimize or dicts defining parameter groups.
28 | lr (float, optional): learning rate. (default: 1e-3)
29 | betas (Tuple[float, float], optional): coefficients used for computing
30 | running averages of gradient and its norm. (default: (0.9, 0.999))
31 | eps (float, optional): term added to the denominator to improve
32 | numerical stability. (default: 1e-8)
33 | weight_decay (float, optional): weight decay (L2 penalty) (default: 0)
34 | grad_averaging (bool, optional): whether apply (1-beta2) to grad when
35 | calculating running averages of gradient. (default: True)
36 | max_grad_norm (float, optional): value used to clip global grad norm (default: 1.0)
37 | trust_clip (bool): enable LAMBC trust ratio clipping (default: False)
38 | always_adapt (boolean, optional): Apply adaptive learning rate to 0.0
39 | weight decay parameter (default: False)
40 |
41 | .. _Large Batch Optimization for Deep Learning - Training BERT in 76 minutes:
42 | https://arxiv.org/abs/1904.00962
43 | .. _On the Convergence of Adam and Beyond:
44 | https://openreview.net/forum?id=ryQu7f-RZ
45 | """
46 |
47 | def __init__(
48 | self, params, lr=1e-3, bias_correction=True, betas=(0.9, 0.999), eps=1e-6,
49 | weight_decay=0.01, grad_averaging=True, max_grad_norm=2.0, trust_clip=False, always_adapt=False):
50 | defaults = dict(
51 | lr=lr, bias_correction=bias_correction, betas=betas, eps=eps, weight_decay=weight_decay,
52 | grad_averaging=grad_averaging, max_grad_norm=max_grad_norm,
53 | trust_clip=trust_clip, always_adapt=always_adapt)
54 | super().__init__(params, defaults)
55 | print(f'[lamb1] max_grad_norm={max_grad_norm}')
56 | self.global_grad_norm = 0
57 |
58 | @torch.no_grad()
59 | def step(self, closure=None):
60 | """Performs a single optimization step.
61 | Arguments:
62 | closure (callable, optional): A closure that reevaluates the model
63 | and returns the loss.
64 | """
65 | loss = None
66 | if closure is not None:
67 | with torch.enable_grad():
68 | loss = closure()
69 |
70 | device = self.param_groups[0]['params'][0].device
71 | one_tensor = torch.tensor(1.0, device=device) # because torch.where doesn't handle scalars correctly
72 | global_grad_norm = torch.zeros(1, device=device)
73 | for group in self.param_groups:
74 | for p in group['params']:
75 | if p.grad is None:
76 | continue
77 | grad = p.grad
78 | if grad.is_sparse:
79 | raise RuntimeError('Lamb does not support sparse gradients, consider SparseAdam instad.')
80 | global_grad_norm.add_(grad.pow(2).sum())
81 |
82 | global_grad_norm = torch.sqrt(global_grad_norm)
83 | self.global_grad_norm = global_grad_norm.item()
84 | max_grad_norm = torch.tensor(self.defaults['max_grad_norm'], device=device)
85 | clip_global_grad_norm = 1 / torch.where(
86 | global_grad_norm > max_grad_norm,
87 | global_grad_norm / max_grad_norm,
88 | one_tensor)
89 |
90 | for group in self.param_groups:
91 | bias_correction = 1 if group['bias_correction'] else 0
92 | beta1, beta2 = group['betas']
93 | grad_averaging = 1 if group['grad_averaging'] else 0
94 | beta3 = 1 - beta1 if grad_averaging else 1.0
95 |
96 | # assume same step across group now to simplify things
97 | # per parameter step can be easily support by making it tensor, or pass list into kernel
98 | if 'step' in group:
99 | group['step'] += 1
100 | else:
101 | group['step'] = 1
102 |
103 | if bias_correction:
104 | bias_correction1 = 1 - beta1 ** group['step']
105 | bias_correction2 = 1 - beta2 ** group['step']
106 | else:
107 | bias_correction1, bias_correction2 = 1.0, 1.0
108 |
109 | for p in group['params']:
110 | if p.grad is None:
111 | continue
112 | grad = p.grad.mul_(clip_global_grad_norm)
113 | state = self.state[p]
114 |
115 | # State initialization
116 | if len(state) == 0:
117 | # Exponential moving average of gradient valuesa
118 | state['exp_avg'] = torch.zeros_like(p)
119 | # Exponential moving average of squared gradient values
120 | state['exp_avg_sq'] = torch.zeros_like(p)
121 |
122 | exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
123 |
124 | # Decay the first and second moment running average coefficient
125 | exp_avg.mul_(beta1).add_(grad, alpha=beta3) # m_t
126 | exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2) # v_t
127 |
128 | denom = (exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(group['eps'])
129 | update = (exp_avg / bias_correction1).div_(denom)
130 |
131 | weight_decay = group['weight_decay']
132 | if weight_decay != 0:
133 | update.add_(p, alpha=weight_decay)
134 |
135 | if weight_decay != 0 or group['always_adapt']:
136 | # Layer-wise LR adaptation. By default, skip adaptation on parameters that are
137 | # excluded from weight decay, unless always_adapt == True, then always enabled.
138 | w_norm = p.norm(2.0)
139 | g_norm = update.norm(2.0)
140 | # FIXME nested where required since logical and/or not working in PT XLA
141 | trust_ratio = torch.where(
142 | w_norm > 0,
143 | torch.where(g_norm > 0, w_norm / g_norm, one_tensor),
144 | one_tensor,
145 | )
146 | if group['trust_clip']:
147 | # LAMBC trust clipping, upper bound fixed at one
148 | trust_ratio = torch.minimum(trust_ratio, one_tensor)
149 | update.mul_(trust_ratio)
150 |
151 | p.add_(update, alpha=-group['lr'])
152 |
153 | return loss
154 |
--------------------------------------------------------------------------------
/dataloaders/dataloader_USCHAD_har.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | import os
4 |
5 | import scipy.io as sio
6 | from dataloaders.dataloader_base import BASE_DATA
7 |
8 | # ================================= USC_HAD_HAR_DATA ============================================
9 | class USC_HAD_HAR_DATA(BASE_DATA):
10 | """
11 |
12 | **********************************************
13 | Section 1: Device Configuration
14 |
15 |
16 | 2. Sampling rate: 100Hz
17 | 3. Accelerometer range: +-6g
18 | 4. Gyroscope range: +-500dps
19 |
20 |
21 | **********************************************
22 | Section 2: Data Format
23 | Each activity trial is stored in an .mat file.
24 |
25 | The naming convention of each .mat file is defined as:
26 | a"m"t"n".mat, where
27 | "a" stands for activity
28 | "m" stands for activity number
29 | "t" stands for trial
30 | "n" stands for trial number
31 |
32 | Each .mat file contains 13 fields:
33 | 1. title: USC Human Motion Database
34 | 2. version: it is version 1.0 for this first round data collection
35 | 3. date
36 | 4. subject number
37 | 5. age
38 | 6. height
39 | 7. weight
40 | 8. activity name
41 | 9. activity number
42 | 10. trial number
43 | 11. sensor_location
44 | 12. sensor_orientation
45 | 13. sensor_readings
46 |
47 | For sensor_readings field, it consists of 6 readings:
48 | From left to right:
49 | 1. acc_x, w/ unit g (gravity)
50 | 2. acc_y, w/ unit g
51 | 3. acc_z, w/ unit g
52 | 4. gyro_x, w/ unit dps (degrees per second)
53 | 5. gyro_y, w/ unit dps
54 | 6. gyro_z, w/ unit dps
55 |
56 | **********************************************
57 | Section 3: Activities
58 | 1. Walking Forward
59 | 2. Walking Left
60 | 3. Walking Right
61 | 4. Walking Upstairs
62 | 5. Walking Downstairs
63 | 6. Running Forward
64 | 7. Jumping Up
65 | 8. Sitting
66 | 9. Standing
67 | 10. Sleeping
68 | 11. Elevator Up
69 | 12. Elevator Down
70 |
71 | """
72 |
73 | def __init__(self, args):
74 |
75 | """
76 | root_path : Root directory of the data set
77 | difference (bool) : Whether to calculate the first order derivative of the original data
78 | datanorm_type (str) : Methods of data normalization: "standardization", "minmax" , "per_sample_std", "per_sample_minmax"
79 |
80 | spectrogram (bool): Whether to convert raw data into frequency representations
81 | scales : Depends on the sampling frequency of the data ( UCI 数据的采样频率??)
82 | wavelet : Methods of wavelet transformation
83 |
84 | """
85 |
86 | # !!!!!! Depending on the setting of each data set!!!!!!
87 | # because this dataset only has 6 columns, the label is saved in the file name, so this used cols will not be used
88 | self.used_cols = [0,1,2,3,4,5]
89 | # This dataset only has this 6 channels
90 | self.col_names = [ 'acc_x', 'acc_y', 'acc_z', 'gyro_x', 'gyro_y', 'gyro_z' ]
91 |
92 |
93 | # These two variables represent whether all sensors can be filtered according to position and sensor type
94 | # pos_filter ------- > filter according to position
95 | # sensor_filter -----> filter according to the sensor type
96 | self.pos_filter = None
97 | self.sensor_filter = ["acc","gyro"]
98 |
99 | # selected_cols will be updated according to user settings. User have to set -- args.pos_select, args.sensor_select---
100 | self.selected_cols = None
101 | # Filtering channels according to the Position
102 | self.selected_cols = self.Sensor_filter_acoording_to_pos_and_type(args.pos_select, self.pos_filter, self.col_names, "position")
103 | # Filtering channels according to the Sensor Type
104 | if self.selected_cols is None:
105 | self.selected_cols = self.Sensor_filter_acoording_to_pos_and_type(args.sensor_select, self.sensor_filter, self.col_names, "Sensor Type")
106 | else:
107 | self.selected_cols = self.Sensor_filter_acoording_to_pos_and_type(args.sensor_select, self.sensor_filter, self.selected_cols, "Sensor Type")
108 |
109 |
110 | # The original labels are from 1 to 12
111 | self.label_map = [(1, "Walking Forward"),
112 | (2, "Walking Left"),
113 | (3, "Walking Right"),
114 | (4, "Walking Upstairs"),
115 | (5, "Walking Downstairs"),
116 | (6, "Running Forward"),
117 | (7, "Jumping Up"),
118 | (8, "Sitting"),
119 | (9, "Standing"),
120 | (10, "Sleeping"),
121 | (11, "Elevator Up"),
122 | (12, "Elevator Down")]
123 |
124 | # As can be seen from the readme
125 | self.drop_activities = []
126 |
127 |
128 |
129 | self.train_keys = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]
130 | self.vali_keys = [ 11, 12 ]
131 | self.test_keys = [ 13, 14 ]
132 |
133 | self.exp_mode = args.exp_mode
134 | self.down_sample:bool \
135 | = args.down_sample
136 |
137 | self.split_tag = "sub"
138 |
139 | self.LOCV_keys = [[1,2],[3,4],[5,6],[7,8],[9,10],[11,12],[13,14]]
140 | self.all_keys = [1,2,3,4,5,6,7,8,9,10,11,12,13,14]
141 | self.sub_ids_of_each_sub = {}
142 |
143 | self.file_encoding = {} # no use
144 |
145 | self.labelToId = {int(x[0]): i for i, x in enumerate(self.label_map)}
146 | self.all_labels = list(range(len(self.label_map)))
147 |
148 | self.drop_activities = [self.labelToId[i] for i in self.drop_activities]
149 | self.no_drop_activites = [item for item in self.all_labels if item not in self.drop_activities]
150 |
151 | super(USC_HAD_HAR_DATA, self).__init__(args)
152 |
153 | def load_all_the_data(self, root_path):
154 | print(" ----------------------- load all the data -------------------")
155 |
156 | activities = range(1, 13)
157 |
158 | df_dict = {}
159 | for sub in range(1, 15):
160 |
161 | for activity in activities:
162 |
163 | for trial in range(1, 6):
164 |
165 | sub_data = sio.loadmat("%s/Subject%d%sa%dt%d.mat" % (root_path, sub, os.sep, activity, trial))
166 | sub_data = pd.DataFrame(np.array(sub_data['sensor_readings']))
167 |
168 | sub_data =sub_data.iloc[:,self.used_cols]
169 | sub_data.columns = self.col_names
170 |
171 | sub_id = "{}_{}_{}".format(sub,activity,trial)
172 | sub_data["sub_id"] = sub_id
173 | sub_data["sub"] = sub
174 | sub_data["activity_id"] = activity
175 |
176 | df_dict[sub_id] = sub_data
177 |
178 | if sub not in self.sub_ids_of_each_sub.keys():
179 | self.sub_ids_of_each_sub[sub] = []
180 | self.sub_ids_of_each_sub[sub].append(sub_id)
181 |
182 |
183 | df_all = pd.concat(df_dict)
184 |
185 | if self.down_sample:
186 | # Downsampling! form 100hz to 30hz
187 | df_all.reset_index(drop=True,inplace=True)
188 | index_list = list( np.arange(0,df_all.shape[0],10/3).astype(int) )
189 | df_all = df_all.iloc[index_list]
190 |
191 | df_all = df_all.set_index('sub_id')
192 |
193 |
194 | # reorder the columns as sensor1, sensor2... sensorn, sub, activity_id
195 | if self.selected_cols:
196 | df_all = df_all[self.selected_cols+["sub"]+["activity_id"]]
197 | else:
198 | df_all = df_all[self.col_names+["sub"]+["activity_id"]]
199 |
200 | # Label Transformation
201 | df_all["activity_id"] = df_all["activity_id"].map(self.labelToId)
202 |
203 | data_y = df_all.iloc[:,-1]
204 | data_x = df_all.iloc[:,:-1]
205 |
206 | data_x = data_x.reset_index()
207 | # sub_id, sensor1, sensor2... sensorn, sub,
208 | return data_x, data_y
209 |
--------------------------------------------------------------------------------
/utils/lamb.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) ByteDance, Inc. and its affiliates.
2 | # All rights reserved.
3 | #
4 | # This source code is licensed under the license found in the
5 | # LICENSE file in the root directory of this source tree.
6 | #
7 | # This file is basically a copy to: https://github.com/rwightman/pytorch-image-models/blob/v0.5.4/timm/optim/lamb.py
8 | # **The only modification** is adding the `global_grad_norm` member for debugging
9 |
10 |
11 | """ PyTorch Lamb optimizer w/ behaviour similar to NVIDIA FusedLamb
12 | This optimizer code was adapted from the following (starting with latest)
13 | * https://github.com/HabanaAI/Model-References/blob/2b435114fe8e31f159b1d3063b8280ae37af7423/PyTorch/nlp/bert/pretraining/lamb.py
14 | * https://github.com/NVIDIA/DeepLearningExamples/blob/master/PyTorch/LanguageModeling/Transformer-XL/pytorch/lamb.py
15 | * https://github.com/cybertronai/pytorch-lamb
16 | Use FusedLamb if you can (GPU). The reason for including this variant of Lamb is to have a version that is
17 | similar in behaviour to APEX FusedLamb if you aren't using NVIDIA GPUs or cannot install/use APEX.
18 | In addition to some cleanup, this Lamb impl has been modified to support PyTorch XLA and has been tested on TPU.
19 | Original copyrights for above sources are below.
20 | Modifications Copyright 2021 Ross Wightman
21 | """
22 | import math
23 |
24 | import torch
25 | from torch.optim.optimizer import Optimizer
26 |
27 |
28 | class TheSameAsTimmLAMB(Optimizer):
29 | """Implements a pure pytorch variant of FuseLAMB (NvLamb variant) optimizer from apex.optimizers.FusedLAMB
30 | reference: https://github.com/NVIDIA/DeepLearningExamples/blob/master/PyTorch/LanguageModeling/Transformer-XL/pytorch/lamb.py
31 |
32 | LAMB was proposed in `Large Batch Optimization for Deep Learning: Training BERT in 76 minutes`_.
33 |
34 | Arguments:
35 | params (iterable): iterable of parameters to optimize or dicts defining parameter groups.
36 | lr (float, optional): learning rate. (default: 1e-3)
37 | betas (Tuple[float, float], optional): coefficients used for computing
38 | running averages of gradient and its norm. (default: (0.9, 0.999))
39 | eps (float, optional): term added to the denominator to improve
40 | numerical stability. (default: 1e-8)
41 | weight_decay (float, optional): weight decay (L2 penalty) (default: 0)
42 | grad_averaging (bool, optional): whether apply (1-beta2) to grad when
43 | calculating running averages of gradient. (default: True)
44 | max_grad_norm (float, optional): value used to clip global grad norm (default: 1.0)
45 | trust_clip (bool): enable LAMBC trust ratio clipping (default: False)
46 | always_adapt (boolean, optional): Apply adaptive learning rate to 0.0
47 | weight decay parameter (default: False)
48 |
49 | .. _Large Batch Optimization for Deep Learning - Training BERT in 76 minutes:
50 | https://arxiv.org/abs/1904.00962
51 | .. _On the Convergence of Adam and Beyond:
52 | https://openreview.net/forum?id=ryQu7f-RZ
53 | """
54 |
55 | def __init__(
56 | self, params, lr=1e-3, bias_correction=True, betas=(0.9, 0.999), eps=1e-6,
57 | weight_decay=0.01, grad_averaging=True, max_grad_norm=2.0, trust_clip=False, always_adapt=False):
58 | defaults = dict(
59 | lr=lr, bias_correction=bias_correction, betas=betas, eps=eps, weight_decay=weight_decay,
60 | grad_averaging=grad_averaging, max_grad_norm=max_grad_norm,
61 | trust_clip=trust_clip, always_adapt=always_adapt)
62 | super().__init__(params, defaults)
63 | print(f'[lamb1] max_grad_norm={max_grad_norm}')
64 | self.global_grad_norm = 0
65 |
66 | @torch.no_grad()
67 | def step(self, closure=None):
68 | """Performs a single optimization step.
69 | Arguments:
70 | closure (callable, optional): A closure that reevaluates the model
71 | and returns the loss.
72 | """
73 | loss = None
74 | if closure is not None:
75 | with torch.enable_grad():
76 | loss = closure()
77 |
78 | device = self.param_groups[0]['params'][0].device
79 | one_tensor = torch.tensor(1.0, device=device) # because torch.where doesn't handle scalars correctly
80 | global_grad_norm = torch.zeros(1, device=device)
81 | for group in self.param_groups:
82 | for p in group['params']:
83 | if p.grad is None:
84 | continue
85 | grad = p.grad
86 | if grad.is_sparse:
87 | raise RuntimeError('Lamb does not support sparse gradients, consider SparseAdam instad.')
88 | global_grad_norm.add_(grad.pow(2).sum())
89 |
90 | global_grad_norm = torch.sqrt(global_grad_norm)
91 | self.global_grad_norm = global_grad_norm.item()
92 | max_grad_norm = torch.tensor(self.defaults['max_grad_norm'], device=device)
93 | clip_global_grad_norm = 1 / torch.where(
94 | global_grad_norm > max_grad_norm,
95 | global_grad_norm / max_grad_norm,
96 | one_tensor)
97 |
98 | for group in self.param_groups:
99 | bias_correction = 1 if group['bias_correction'] else 0
100 | beta1, beta2 = group['betas']
101 | grad_averaging = 1 if group['grad_averaging'] else 0
102 | beta3 = 1 - beta1 if grad_averaging else 1.0
103 |
104 | # assume same step across group now to simplify things
105 | # per parameter step can be easily support by making it tensor, or pass list into kernel
106 | if 'step' in group:
107 | group['step'] += 1
108 | else:
109 | group['step'] = 1
110 |
111 | if bias_correction:
112 | bias_correction1 = 1 - beta1 ** group['step']
113 | bias_correction2 = 1 - beta2 ** group['step']
114 | else:
115 | bias_correction1, bias_correction2 = 1.0, 1.0
116 |
117 | for p in group['params']:
118 | if p.grad is None:
119 | continue
120 | grad = p.grad.mul_(clip_global_grad_norm)
121 | state = self.state[p]
122 |
123 | # State initialization
124 | if len(state) == 0:
125 | # Exponential moving average of gradient valuesa
126 | state['exp_avg'] = torch.zeros_like(p)
127 | # Exponential moving average of squared gradient values
128 | state['exp_avg_sq'] = torch.zeros_like(p)
129 |
130 | exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
131 |
132 | # Decay the first and second moment running average coefficient
133 | exp_avg.mul_(beta1).add_(grad, alpha=beta3) # m_t
134 | exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2) # v_t
135 |
136 | denom = (exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(group['eps'])
137 | update = (exp_avg / bias_correction1).div_(denom)
138 |
139 | weight_decay = group['weight_decay']
140 | if weight_decay != 0:
141 | update.add_(p, alpha=weight_decay)
142 |
143 | if weight_decay != 0 or group['always_adapt']:
144 | # Layer-wise LR adaptation. By default, skip adaptation on parameters that are
145 | # excluded from weight decay, unless always_adapt == True, then always enabled.
146 | w_norm = p.norm(2.0)
147 | g_norm = update.norm(2.0)
148 | # FIXME nested where required since logical and/or not working in PT XLA
149 | trust_ratio = torch.where(
150 | w_norm > 0,
151 | torch.where(g_norm > 0, w_norm / g_norm, one_tensor),
152 | one_tensor,
153 | )
154 | if group['trust_clip']:
155 | # LAMBC trust clipping, upper bound fixed at one
156 | trust_ratio = torch.minimum(trust_ratio, one_tensor)
157 | update.mul_(trust_ratio)
158 |
159 | p.add_(update, alpha=-group['lr'])
160 |
161 | return loss
162 |
--------------------------------------------------------------------------------
/pretrain/utils/lamb.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) ByteDance, Inc. and its affiliates.
2 | # All rights reserved.
3 | #
4 | # This source code is licensed under the license found in the
5 | # LICENSE file in the root directory of this source tree.
6 | #
7 | # This file is basically a copy to: https://github.com/rwightman/pytorch-image-models/blob/v0.5.4/timm/optim/lamb.py
8 | # **The only modification** is adding the `global_grad_norm` member for debugging
9 |
10 |
11 | """ PyTorch Lamb optimizer w/ behaviour similar to NVIDIA FusedLamb
12 | This optimizer code was adapted from the following (starting with latest)
13 | * https://github.com/HabanaAI/Model-References/blob/2b435114fe8e31f159b1d3063b8280ae37af7423/PyTorch/nlp/bert/pretraining/lamb.py
14 | * https://github.com/NVIDIA/DeepLearningExamples/blob/master/PyTorch/LanguageModeling/Transformer-XL/pytorch/lamb.py
15 | * https://github.com/cybertronai/pytorch-lamb
16 | Use FusedLamb if you can (GPU). The reason for including this variant of Lamb is to have a version that is
17 | similar in behaviour to APEX FusedLamb if you aren't using NVIDIA GPUs or cannot install/use APEX.
18 | In addition to some cleanup, this Lamb impl has been modified to support PyTorch XLA and has been tested on TPU.
19 | Original copyrights for above sources are below.
20 | Modifications Copyright 2021 Ross Wightman
21 | """
22 | import math
23 |
24 | import torch
25 | from torch.optim.optimizer import Optimizer
26 |
27 |
28 | class TheSameAsTimmLAMB(Optimizer):
29 | """Implements a pure pytorch variant of FuseLAMB (NvLamb variant) optimizer from apex.optimizers.FusedLAMB
30 | reference: https://github.com/NVIDIA/DeepLearningExamples/blob/master/PyTorch/LanguageModeling/Transformer-XL/pytorch/lamb.py
31 |
32 | LAMB was proposed in `Large Batch Optimization for Deep Learning: Training BERT in 76 minutes`_.
33 |
34 | Arguments:
35 | params (iterable): iterable of parameters to optimize or dicts defining parameter groups.
36 | lr (float, optional): learning rate. (default: 1e-3)
37 | betas (Tuple[float, float], optional): coefficients used for computing
38 | running averages of gradient and its norm. (default: (0.9, 0.999))
39 | eps (float, optional): term added to the denominator to improve
40 | numerical stability. (default: 1e-8)
41 | weight_decay (float, optional): weight decay (L2 penalty) (default: 0)
42 | grad_averaging (bool, optional): whether apply (1-beta2) to grad when
43 | calculating running averages of gradient. (default: True)
44 | max_grad_norm (float, optional): value used to clip global grad norm (default: 1.0)
45 | trust_clip (bool): enable LAMBC trust ratio clipping (default: False)
46 | always_adapt (boolean, optional): Apply adaptive learning rate to 0.0
47 | weight decay parameter (default: False)
48 |
49 | .. _Large Batch Optimization for Deep Learning - Training BERT in 76 minutes:
50 | https://arxiv.org/abs/1904.00962
51 | .. _On the Convergence of Adam and Beyond:
52 | https://openreview.net/forum?id=ryQu7f-RZ
53 | """
54 |
55 | def __init__(
56 | self, params, lr=1e-3, bias_correction=True, betas=(0.9, 0.999), eps=1e-6,
57 | weight_decay=0.01, grad_averaging=True, max_grad_norm=2.0, trust_clip=False, always_adapt=False):
58 | defaults = dict(
59 | lr=lr, bias_correction=bias_correction, betas=betas, eps=eps, weight_decay=weight_decay,
60 | grad_averaging=grad_averaging, max_grad_norm=max_grad_norm,
61 | trust_clip=trust_clip, always_adapt=always_adapt)
62 | super().__init__(params, defaults)
63 | print(f'[lamb1] max_grad_norm={max_grad_norm}')
64 | self.global_grad_norm = 0
65 |
66 | @torch.no_grad()
67 | def step(self, closure=None):
68 | """Performs a single optimization step.
69 | Arguments:
70 | closure (callable, optional): A closure that reevaluates the model
71 | and returns the loss.
72 | """
73 | loss = None
74 | if closure is not None:
75 | with torch.enable_grad():
76 | loss = closure()
77 |
78 | device = self.param_groups[0]['params'][0].device
79 | one_tensor = torch.tensor(1.0, device=device) # because torch.where doesn't handle scalars correctly
80 | global_grad_norm = torch.zeros(1, device=device)
81 | for group in self.param_groups:
82 | for p in group['params']:
83 | if p.grad is None:
84 | continue
85 | grad = p.grad
86 | if grad.is_sparse:
87 | raise RuntimeError('Lamb does not support sparse gradients, consider SparseAdam instad.')
88 | global_grad_norm.add_(grad.pow(2).sum())
89 |
90 | global_grad_norm = torch.sqrt(global_grad_norm)
91 | self.global_grad_norm = global_grad_norm.item()
92 | max_grad_norm = torch.tensor(self.defaults['max_grad_norm'], device=device)
93 | clip_global_grad_norm = 1 / torch.where(
94 | global_grad_norm > max_grad_norm,
95 | global_grad_norm / max_grad_norm,
96 | one_tensor)
97 |
98 | for group in self.param_groups:
99 | bias_correction = 1 if group['bias_correction'] else 0
100 | beta1, beta2 = group['betas']
101 | grad_averaging = 1 if group['grad_averaging'] else 0
102 | beta3 = 1 - beta1 if grad_averaging else 1.0
103 |
104 | # assume same step across group now to simplify things
105 | # per parameter step can be easily support by making it tensor, or pass list into kernel
106 | if 'step' in group:
107 | group['step'] += 1
108 | else:
109 | group['step'] = 1
110 |
111 | if bias_correction:
112 | bias_correction1 = 1 - beta1 ** group['step']
113 | bias_correction2 = 1 - beta2 ** group['step']
114 | else:
115 | bias_correction1, bias_correction2 = 1.0, 1.0
116 |
117 | for p in group['params']:
118 | if p.grad is None:
119 | continue
120 | grad = p.grad.mul_(clip_global_grad_norm)
121 | state = self.state[p]
122 |
123 | # State initialization
124 | if len(state) == 0:
125 | # Exponential moving average of gradient valuesa
126 | state['exp_avg'] = torch.zeros_like(p)
127 | # Exponential moving average of squared gradient values
128 | state['exp_avg_sq'] = torch.zeros_like(p)
129 |
130 | exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
131 |
132 | # Decay the first and second moment running average coefficient
133 | exp_avg.mul_(beta1).add_(grad, alpha=beta3) # m_t
134 | exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2) # v_t
135 |
136 | denom = (exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(group['eps'])
137 | update = (exp_avg / bias_correction1).div_(denom)
138 |
139 | weight_decay = group['weight_decay']
140 | if weight_decay != 0:
141 | update.add_(p, alpha=weight_decay)
142 |
143 | if weight_decay != 0 or group['always_adapt']:
144 | # Layer-wise LR adaptation. By default, skip adaptation on parameters that are
145 | # excluded from weight decay, unless always_adapt == True, then always enabled.
146 | w_norm = p.norm(2.0)
147 | g_norm = update.norm(2.0)
148 | # FIXME nested where required since logical and/or not working in PT XLA
149 | trust_ratio = torch.where(
150 | w_norm > 0,
151 | torch.where(g_norm > 0, w_norm / g_norm, one_tensor),
152 | one_tensor,
153 | )
154 | if group['trust_clip']:
155 | # LAMBC trust clipping, upper bound fixed at one
156 | trust_ratio = torch.minimum(trust_ratio, one_tensor)
157 | update.mul_(trust_ratio)
158 |
159 | p.add_(update, alpha=-group['lr'])
160 |
161 | return loss
162 |
--------------------------------------------------------------------------------
/dataloaders/utils.py:
--------------------------------------------------------------------------------
1 | import math
2 | import numpy as np
3 |
4 | # necessary functions
5 | from scipy.fftpack import fft,fftfreq,ifft
6 | # Importing Scipy
7 | import scipy as sp
8 | import pywt
9 | import scipy.fft as F
10 | from sklearn.cluster import KMeans
11 | import torch
12 |
13 |
14 | def mag_3_signals(x,y,z):# magnitude function redefintion
15 | return np.array([math.sqrt((x[i]**2+y[i]**2+z[i]**2)) for i in range(len(x))])
16 |
17 | def components_selection_one_signal(t_signal,freq1,freq2,sampling_freq):
18 | """
19 | DC_component: f_signal values having freq between [-0.3 hz to 0 hz] and from [0 hz to 0.3hz]
20 | (-0.3 and 0.3 are included)
21 |
22 | noise components: f_signal values having freq between [-25 hz to 20 hz[ and from ] 20 hz to 25 hz]
23 | (-25 and 25 hz inculded 20hz and -20hz not included)
24 |
25 | selecting body_component: f_signal values having freq between [-20 hz to -0.3 hz] and from [0.3 hz to 20 hz]
26 | (-0.3 and 0.3 not included , -20hz and 20 hz included)
27 | """
28 |
29 | t_signal=np.array(t_signal)
30 | t_signal_length=len(t_signal) # number of points in a t_signal
31 |
32 | # the t_signal in frequency domain after applying fft
33 | f_signal=fft(t_signal) # 1D numpy array contains complex values (in C)
34 |
35 | # generate frequencies associated to f_signal complex values
36 | freqs=np.array(sp.fftpack.fftfreq(t_signal_length, d=1/float(sampling_freq))) # frequency values between [-25hz:+25hz]
37 |
38 |
39 |
40 |
41 | f_DC_signal=[] # DC_component in freq domain
42 | f_body_signal=[] # body component in freq domain numpy.append(a, a[0])
43 | f_noise_signal=[] # noise in freq domain
44 |
45 | for i in range(len(freqs)):# iterate over all available frequencies
46 |
47 | # selecting the frequency value
48 | freq=freqs[i]
49 |
50 | # selecting the f_signal value associated to freq
51 | value= f_signal[i]
52 |
53 | # Selecting DC_component values
54 | if abs(freq)>freq1:# testing if freq is outside DC_component frequency ranges
55 | f_DC_signal.append(float(0)) # add 0 to the list if it was the case (the value should not be added)
56 | else: # if freq is inside DC_component frequency ranges
57 | f_DC_signal.append(value) # add f_signal value to f_DC_signal list
58 |
59 | # Selecting noise component values
60 | if (abs(freq)<=freq2):# testing if freq is outside noise frequency ranges
61 | f_noise_signal.append(float(0)) # # add 0 to f_noise_signal list if it was the case
62 | else:# if freq is inside noise frequency ranges
63 | f_noise_signal.append(value) # add f_signal value to f_noise_signal
64 |
65 | # Selecting body_component values
66 | if (abs(freq)<=freq1 or abs(freq)>freq2):# testing if freq is outside Body_component frequency ranges
67 | f_body_signal.append(float(0))# add 0 to f_body_signal list
68 | else:# if freq is inside Body_component frequency ranges
69 | f_body_signal.append(value) # add f_signal value to f_body_signal list
70 |
71 | ################### Inverse the transformation of signals in freq domain ########################
72 | # applying the inverse fft(ifft) to signals in freq domain and put them in float format
73 | t_DC_component= ifft(np.array(f_DC_signal)).real
74 | t_body_component= ifft(np.array(f_body_signal)).real
75 | #t_noise=ifft(np.array(f_noise_signal)).real
76 |
77 | #total_component=t_signal-t_noise # extracting the total component(filtered from noise)
78 | # # by substracting noise from t_signal (the original signal).
79 |
80 |
81 | #return (total_component,t_DC_component,t_body_component,t_noise)
82 | return (t_DC_component,t_body_component)
83 |
84 |
85 |
86 | class Normalizer(object):
87 | """
88 | Normalizes dataframe across ALL contained rows (time steps). Different from per-sample normalization.
89 | """
90 |
91 | def __init__(self, norm_type):
92 | """
93 | Args:
94 | norm_type: choose from:
95 | "standardization", "minmax": normalizes dataframe across ALL contained rows (time steps)
96 | "per_sample_std", "per_sample_minmax": normalizes each sample separately (i.e. across only its own rows)
97 | mean, std, min_val, max_val: optional (num_feat,) Series of pre-computed values
98 | """
99 |
100 | self.norm_type = norm_type
101 |
102 | def fit(self, df):
103 | if self.norm_type == "standardization":
104 | self.mean = df.mean(0)
105 | self.std = df.std(0)
106 | elif self.norm_type == "minmax":
107 | self.max_val = df.max()
108 | self.min_val = df.min()
109 | elif self.norm_type == "per_sample_std":
110 | self.max_val = None
111 | self.min_val = None
112 | elif self.norm_type == "per_sample_minmax":
113 | self.max_val = None
114 | self.min_val = None
115 | else:
116 | raise (NameError(f'Normalize method "{self.norm_type}" not implemented'))
117 |
118 | def normalize(self, df):
119 | """
120 | Args:
121 | df: input dataframe
122 | Returns:
123 | df: normalized dataframe
124 | """
125 | if self.norm_type == "standardization":
126 | return (df - self.mean) / (self.std + np.finfo(float).eps)
127 |
128 | elif self.norm_type == "minmax":
129 | return (df - self.min_val) / (self.max_val - self.min_val + np.finfo(float).eps)
130 | elif self.norm_type == "per_sample_std":
131 | grouped = df.groupby(by=df.index)
132 | return (df - grouped.transform('mean')) / grouped.transform('std')
133 | elif self.norm_type == "per_sample_minmax":
134 | grouped = df.groupby(by=df.index)
135 | min_vals = grouped.transform('min')
136 | return (df - min_vals) / (grouped.transform('max') - min_vals + np.finfo(float).eps)
137 |
138 | else:
139 | raise (NameError(f'Normalize method "{self.norm_type}" not implemented'))
140 |
141 |
142 |
143 | def PrepareWavelets(K, length=20, seed= 1):
144 | motherwavelets = []
145 | for family in pywt.families():
146 | for mother in pywt.wavelist(family):
147 | motherwavelets.append(mother)
148 |
149 | X = np.zeros([1,length])
150 | PSI = np.zeros([1,length])
151 | for mw_temp in motherwavelets:
152 | if mw_temp.startswith('gaus') or mw_temp.startswith('mexh') or mw_temp.startswith('morl') or mw_temp.startswith('cmor') or mw_temp.startswith('fbsp') or mw_temp.startswith('shan') or mw_temp.startswith('cgau'):
153 | pass
154 | else:
155 | param = pywt.Wavelet(mw_temp).wavefun(level=7)
156 | psi, x = param[1], param[-1]
157 |
158 | # normalization
159 | psi_sum = np.sum(psi)
160 | if np.abs(psi_sum) > 1:
161 | psi = psi / np.abs(psi_sum)
162 | x = x / max(x)
163 |
164 | # down sampling
165 | idx_ds = np.round(np.linspace(0, x.shape[0]-1, length)).astype(int)
166 | x = x[idx_ds]
167 | psi = psi[idx_ds]
168 |
169 | X = np.vstack((X, x.reshape(1,-1)))
170 | PSI = np.vstack((PSI, psi.reshape(1,-1)))
171 |
172 | X = X[1:,:]
173 | PSI = PSI[1:,:]
174 |
175 | # clustering
176 | FRE = np.zeros([1,length])
177 | for i in range(PSI.shape[0]):
178 | FRE = np.vstack((FRE, np.real(F.fft(PSI[i,:])).reshape(1,-1)))
179 | FRE = FRE[1:,:]
180 |
181 | PSI_extended = np.hstack((PSI, FRE))
182 | kmeans = KMeans(n_clusters=K, random_state=seed).fit(PSI_extended)
183 | label = kmeans.labels_
184 |
185 | SelectedWavelet = np.zeros([1,length])
186 | for k in range(K):
187 | wavesidx = np.where(label==k)[0][0]
188 | SelectedWavelet = np.vstack((SelectedWavelet, PSI[wavesidx,:]))
189 |
190 | return torch.tensor(SelectedWavelet[1:,:])
191 |
192 |
193 | def FiltersExtention(Filters):
194 | K, WS = Filters.shape
195 |
196 |
197 | if WS%2==1:
198 | N_padding = int((WS-1)/2)
199 | N_ds = int(torch.log2(torch.tensor(WS-1)).floor()) - 2
200 | else:
201 | N_ds = int(torch.log2(torch.tensor(WS)).floor()) - 2
202 | N_padding = int(WS/2)
203 |
204 | Filter_temp = Filters.repeat(N_ds,1,1)
205 | m = torch.nn.ConstantPad1d(N_padding, 0)
206 |
207 | for n_ds in range(N_ds-1):
208 | filter_temp = Filter_temp[n_ds,:,:]
209 | filter_temp_pad = m(filter_temp)
210 | filter_ds = filter_temp_pad[:,::2] * 2.
211 | Filter_temp[n_ds+1,:,:] = filter_ds
212 |
213 | # formualte dimensionality
214 | Filter_temp = Filter_temp.view(K*N_ds,WS)
215 | Filter_temp = Filter_temp.repeat(1,1,1,1)
216 | Filter_temp = Filter_temp.permute(2,0,1,3)
217 |
218 | # normalization
219 | energy = torch.sum(Filter_temp, dim=3, keepdims=True)
220 | energy[torch.abs(energy)<=1] = 1.
221 | Filter_temp = Filter_temp / energy
222 |
223 | return Filter_temp
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
--------------------------------------------------------------------------------
/dataloaders/dataloader_uci_har.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | import os
4 |
5 | from dataloaders.dataloader_base import BASE_DATA
6 | # ============================== UCI_HAR_DATA ======================================
7 | class UCI_HAR_DATA(BASE_DATA):
8 | """
9 | The experiments have been carried out with a group of 30 volunteers within an age bracket of 19-48 years.
10 | Each person performed six activities
11 | (WALKING, WALKING_UPSTAIRS, WALKING_DOWNSTAIRS, SITTING, STANDING, LAYING)
12 | wearing a smartphone (Samsung Galaxy S II) on the waist. Using its embedded accelerometer and gyroscope,
13 | we captured 3-axial linear acceleration and 3-axial angular velocity at a constant rate of 50Hz.
14 | The experiments have been video-recorded to label the data manually.
15 | The obtained dataset has been randomly partitioned into two sets,
16 | where 70% of the volunteers was selected for generating the training data and 30% the test data.
17 |
18 | The sensor signals (accelerometer and gyroscope) were pre-processed by applying noise filters
19 | and then sampled in fixed-width sliding windows of 2.56 sec and 50% overlap (128 readings/window).
20 | The sensor acceleration signal, which has gravitational and body motion components,
21 | was separated using a Butterworth low-pass filter into body acceleration and gravity.
22 | The gravitational force is assumed to have only low frequency components,
23 | therefore a filter with 0.3 Hz cutoff frequency was used.
24 | From each window, a vector of features was obtained by calculating variables from the time and frequency domain.
25 | See 'features_info.txt' for more details.
26 |
27 | 1 WALKING
28 | 2 WALKING_UPSTAIRS
29 | 3 WALKING_DOWNSTAIRS
30 | 4 SITTING
31 | 5 STANDING
32 | 6 LAYING
33 | """
34 | def __init__(self, args):
35 |
36 | """
37 | root_path : Root directory of the data set
38 | difference (bool) : Whether to calculate the first order derivative of the original data
39 | datanorm_type (str) : Methods of data normalization: "standardization", "minmax" , "per_sample_std", "per_sample_minmax"
40 |
41 | spectrogram (bool): Whether to convert raw data into frequency representations
42 | scales : Depends on the sampling frequency of the data ( UCI 数据的采样频率??)
43 | wavelet : Methods of wavelet transformation
44 |
45 | """
46 | self.used_cols = [] #no use , because this data format has save each sensor in one file
47 | self.col_names = ['body_acc_x_', 'body_acc_y_', 'body_acc_z_',
48 | 'body_gyro_x_', 'body_gyro_y_', 'body_gyro_z_',
49 | 'total_acc_x_', 'total_acc_y_', 'total_acc_z_']
50 |
51 | self.label_map = [
52 | (1, 'WALKING'),
53 | (2, 'WALKING_UPSTAIRS'),
54 | (3, 'WALKING_DOWNSTAIRS'),
55 | (4, 'SITTING'),
56 | (5, 'STANDING'),
57 | (6, 'LAYING'),
58 | ]
59 |
60 | self.drop_activities = []
61 |
62 | # All ID used for training [ 1, 3, 5, 6, 7, 8, 11, 14, 15, 16, 17, 19, 21, 22, 23, 25, 26, 27, 28, 29, 30]
63 | # All ID used for Test [ 2, 4, 9, 10, 12, 13, 18, 20, 24]
64 | # this is given train test split
65 | self.train_keys = [ 1, 3, 5, 7, 8, 14, 15, 16, 17, 21, 22, 23, 26, 27, 28, 29, 6, 11, 19, 25, 30]
66 | self.vali_keys = [ ]
67 | self.test_keys = [ 2, 4, 9, 10, 12, 13, 18, 20, 24]
68 |
69 | self.exp_mode = args.exp_mode
70 | self.down_sample:bool \
71 | = args.down_sample
72 | self.split_tag = "sub"
73 |
74 | self.all_keys = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30]
75 | all_keys = np.array_split(self.all_keys,5)
76 | self.LOCV_keys = [list(elem) for elem in all_keys]
77 | self.sub_ids_of_each_sub = {}
78 |
79 | self.file_encoding = {}
80 |
81 |
82 | self.labelToId = {int(x[0]): i for i, x in enumerate(self.label_map)}
83 | self.all_labels = list(range(len(self.label_map)))
84 |
85 | self.drop_activities = [self.labelToId[i] for i in self.drop_activities]
86 | self.no_drop_activites = [item for item in self.all_labels if item not in self.drop_activities]
87 |
88 |
89 | self.leave_one_out = True
90 | self.full_None_Overlapping = False
91 | self.Semi_None_Overlapping = True
92 |
93 | super(UCI_HAR_DATA, self).__init__(args)
94 |
95 |
96 |
97 |
98 |
99 | def load_all_the_data(self, root_path):
100 |
101 | print(" ----------------------- load all the data -------------------")
102 | # ==================== Load the sensor values ====================
103 | train_vali_path = os.path.join(root_path, "train/Inertial Signals/")
104 | test_path = os.path.join(root_path, "test/Inertial Signals/")
105 |
106 | train_vali_dict = {}
107 | test_dict = {}
108 |
109 | file_list = os.listdir(train_vali_path)
110 | for file in file_list:
111 |
112 | train_vali = pd.read_csv(train_vali_path + file,header=None, delim_whitespace=True)
113 | test = pd.read_csv(test_path+file[:-9]+"test.txt",header=None, delim_whitespace=True)
114 |
115 | train_vali_dict[file[:-9]] = train_vali
116 | test_dict[file[:-9]] = test
117 |
118 |
119 | # =================== Define the sub id and the label for each segments FOR TRAIN VALI ================
120 | train_vali = pd.DataFrame(np.stack([train_vali_dict[col].values.reshape(-1) for col in self.col_names], axis=1), columns = self.col_names)
121 |
122 | train_vali_subjects = pd.read_csv(os.path.join(root_path,"train/subject_train.txt"), header=None)
123 | train_vali_subjects.columns = ["subjects"]
124 |
125 | train_vali_label = pd.read_csv(os.path.join(root_path,"train/y_train.txt"),header=None)
126 | train_vali_label.columns = ["labels"]
127 |
128 | index = []
129 | labels = []
130 | sub_list = []
131 |
132 | assert train_vali_dict["body_acc_x_"].shape[0] == train_vali_subjects.shape[0]
133 |
134 | # repeat the id and the label for each segs 128 tims
135 | for i in range(train_vali_dict["body_acc_x_"].shape[0]):
136 | sub = train_vali_subjects.loc[i,"subjects"]
137 | sub_id = "{}_{}".format(sub,i)
138 |
139 | ac_id = train_vali_label.loc[i,"labels"]
140 | if sub not in self.sub_ids_of_each_sub.keys():
141 | self.sub_ids_of_each_sub[sub] = []
142 | self.sub_ids_of_each_sub[sub].append(sub_id)
143 |
144 | index.extend(128*[sub_id])
145 | labels.extend(128*[ac_id])
146 | sub_list.extend(128*[sub])
147 |
148 | train_vali["sub_id"] = index
149 | train_vali["sub"] = sub_list
150 | train_vali["activity_id"] = labels
151 |
152 | # =================== Define the sub id and the label for each segments FOR TEST ================
153 | test = pd.DataFrame(np.stack([test_dict[col].values.reshape(-1) for col in self.col_names], axis=1), columns = self.col_names)
154 |
155 | test_subjects = pd.read_csv(os.path.join(root_path,"test/subject_test.txt"), header=None)
156 | test_subjects.columns = ["subjects"]
157 |
158 | test_label = pd.read_csv(os.path.join(root_path,"test/y_test.txt"),header=None)
159 | test_label.columns = ["labels"]
160 |
161 | index = []
162 | labels = []
163 | sub_list = []
164 | assert test_dict["body_acc_x_"].shape[0] == test_subjects.shape[0]
165 |
166 | for i in range(test_dict["body_acc_x_"].shape[0]):
167 | sub = test_subjects.loc[i,"subjects"]
168 | sub_id = "{}_{}".format(sub,i)
169 |
170 | ac_id = test_label.loc[i,"labels"]
171 |
172 | if sub not in self.sub_ids_of_each_sub.keys():
173 | self.sub_ids_of_each_sub[sub] = []
174 | self.sub_ids_of_each_sub[sub].append(sub_id)
175 |
176 | index.extend(128*[sub_id])
177 | labels.extend(128*[ac_id])
178 | sub_list.extend(128*[sub])
179 |
180 | test["sub_id"] = index
181 | test["sub"] = sub_list
182 | test["activity_id"] = labels
183 |
184 |
185 |
186 | # The split may be different as the default setting, so we concat all segs together
187 | df_all = pd.concat([train_vali,test])
188 |
189 |
190 | df_dict = {}
191 | for i in df_all.groupby("sub_id"):
192 | df_dict[i[0]] = i[1]
193 | df_all = pd.concat(df_dict)
194 |
195 | if self.down_sample:
196 | # Downsampling! form 50hz to 30hz
197 | df_all.reset_index(drop=True,inplace=True)
198 | index_list = list( np.arange(0,df_all.shape[0],5/3).astype(int) )
199 | df_all = df_all.iloc[index_list]
200 |
201 | # ================= Label Transformation ===================
202 | df_all["activity_id"] = df_all["activity_id"].map(self.labelToId)
203 | df_all = df_all.set_index('sub_id')
204 | data_y = df_all.iloc[:,-1]
205 | data_x = df_all.iloc[:,:-1]
206 |
207 |
208 | data_x = data_x.reset_index()
209 | # drop partial sensors for special task.
210 | # data_x = data_x.drop(columns=['total_acc_x_', 'total_acc_y_', 'total_acc_z_'])
211 |
212 | return data_x, data_y
--------------------------------------------------------------------------------
/utils/setup.py:
--------------------------------------------------------------------------------
1 | from torch.optim.lr_scheduler import MultiStepLR,ExponentialLR,CosineAnnealingLR,StepLR,LambdaLR
2 | import torch
3 | from configs.base_configs import args
4 | import numpy as np
5 | import torch.optim as optim
6 | import torch.nn as nn
7 | from timm.models.layers import trunc_normal_
8 | import models
9 | import random
10 | import math
11 | from bisect import bisect_right
12 | import numpy as np
13 | import os
14 | from pprint import pformat
15 | from typing import List, Tuple, Callable
16 | from timm.optim import Lamb
17 | from torch.optim import SGD,AdamW,Adam
18 | from functools import partial
19 | from timm.loss import SoftTargetCrossEntropy, BinaryCrossEntropy
20 | import sys
21 |
22 | def LrSchedulerSet(optimizer,args):
23 | '''
24 | default: StepLR , gamma is 0.1 , step_size is 40
25 | '''
26 | if args.lr_scheduler == 'C':
27 | print('==>LrScheduler Set CosineAnnealingLR')
28 | return CosineAnnealingLR( optimizer, T_max=args.n_epochs )
29 | elif args.lr_scheduler == 'S':
30 | print(f'==>LrScheduler Set StepLR , decay epoch is {args.decay_epochs} , gamma is {args.gamma}')
31 | return StepLR( optimizer, step_size=args.decay_epochs, gamma=args.gamma )
32 | elif args.lr_scheduler == 'E':
33 | print('==>LrScheduler Set ExponentialLR')
34 | return ExponentialLR( optimizer, gamma=args.gamma )
35 | elif args.lr_scheduler == 'M':
36 | print(f'==>LrScheduler Set MultiStepLR , scale is {args.milestones} , gamma is {args.gamma} ')
37 | return MultiStepLR( optimizer , args.milestones , gamma=args.gamma )
38 | elif args.lr_scheduler == 'W':
39 | print(f'==>LrScheduler Set MultiStepLR with Warm up {args.warm_up_epochs}, scale is {args.milestones} , gamma is {args.gamma} ')
40 | # args.linear_gap = args.learning_rate / args.warm_up_epochs
41 | warm_up_with_multistep_lr = lambda epoch: epoch / args.warm_up_epochs \
42 | if epoch <= args.warm_up_epochs else args.gamma**len([m for m in args.milestones if m <= epoch])
43 | return LambdaLR(optimizer, lr_lambda=warm_up_with_multistep_lr)
44 | elif args.lr_scheduler == None and args.handcrafted_lr:
45 | print(f'==>LrScheduler is set to manually adjust')
46 | else:
47 | raise NotImplementedError('Not Implement this Lr_Scheduler!')
48 |
49 | def adjust_lr(optimizer, epoch, args, step=0, all_iters_per_epoch=0):
50 | '''this version is MultiStepLR with warm up'''
51 | cur_lr = 0.
52 | if epoch < args.warm_up_epochs:
53 | cur_lr = args.learning_rate * float(1 + step + epoch*all_iters_per_epoch)/(args.warm_up_epochs *all_iters_per_epoch)
54 | else:
55 | epoch = epoch - args.warm_up_epochs
56 | # multistep:
57 | cur_lr = args.learning_rate * 0.1 ** bisect_right(args.milestones, epoch)
58 |
59 | for param_group in optimizer.param_groups:
60 | param_group['lr'] = cur_lr
61 |
62 | return cur_lr
63 |
64 |
65 |
66 | def set_seed(seed):
67 | # fix seed
68 | torch.manual_seed(seed)
69 | torch.cuda.manual_seed_all(seed)
70 | np.random.seed(seed)
71 | random.seed(seed)
72 | np.random.seed(seed)
73 | torch.backends.cudnn.deterministic = True
74 |
75 | def GetModel():
76 | try:
77 | model = getattr(models, args.model)(args.dataset).cuda()
78 | except Exception as e:
79 | raise NotImplementedError("{} is not implemented".format(args.model))
80 | return model
81 |
82 |
83 | def GetMOS(opt_type='sgd'):
84 | '''
85 | M: Model
86 | O: Optimier
87 | S: Scheduler
88 | '''
89 | # print(args.model)
90 | try:
91 | model = getattr(models, args.model)(args.dataset).cuda()
92 | except Exception as e:
93 | raise NotImplementedError("{} is not implemented".format(args.model))
94 |
95 | if opt_type == 'sgd':
96 | optimizer = optim.SGD( params = model.parameters(),
97 | lr = args.learning_rate,
98 | momentum = args.momentum ,
99 | weight_decay = args.weight_decay,
100 | # nesterov = True
101 | )
102 | elif opt_type == 'adam':
103 | optimizer = optim.Adam( params = model.parameters() ,
104 | lr = args.learning_rate ,
105 | # betas = [0.9,0.999] ,
106 | weight_decay = args.weight_decay
107 | )
108 | elif opt_type == 'adamw':
109 | optimizer = optim.AdamW( params = model.parameters() ,
110 | lr = args.learning_rate ,
111 | # betas = [0.9,0.999] ,
112 | weight_decay = args.weight_decay
113 | )
114 | else:
115 | raise NotImplementedError
116 | scheduler = LrSchedulerSet( optimizer , args )
117 | return model, optimizer, scheduler
118 |
119 |
120 | def create_model_opt() :
121 | model: torch.nn.Module = getattr(models, args.model)(args.dataset).cuda()
122 | model_para = f'{sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e6:.1f}M'
123 | print(f'[model={args.model}] [#para={model_para}] {model}\n')
124 | model.train()
125 | opt_cls = {
126 | 'sgd': SGD,
127 | 'adam': Adam, 'adamw': AdamW,
128 | 'lamb': partial(Lamb, max_grad_norm=1e7, always_adapt=True, bias_correction=False),
129 | }
130 | param_groups: List[dict] = get_param_groups(model, nowd_keys={'cls_token', 'pos_embed', 'mask_token', 'gamma'}, lr_scale=args.lr_scale)
131 | optimizer = opt_cls[args.opt_type](param_groups, lr=args.learning_rate, weight_decay=args.weight_decay) if args.opt_type !='sgd' else opt_cls[args.opt_type](param_groups, lr=args.learning_rate, weight_decay=args.weight_decay,momentum=args.momentum)
132 | print(f'[optimizer={type(optimizer)}]')
133 |
134 | if 'lamb' in args.opt_type:
135 | # label smoothing is solved in AdaptiveMixup with `label_smoothing`, so here smoothing=0
136 | criterion = BinaryCrossEntropy(smoothing=0, target_threshold=None)
137 | else:
138 | criterion = nn.CrossEntropyLoss()
139 | print(f'[loss_fn] {criterion}')
140 | return model, optimizer, criterion
141 |
142 | def lr_wd_annealing(optimizer, peak_lr, wd, cur_it, wp_it, max_it):
143 | wp_it = round(wp_it)
144 | if cur_it < wp_it:
145 | cur_lr = 0.005 * peak_lr + 0.995 * peak_lr * cur_it / wp_it
146 | else:
147 | ratio = (cur_it - wp_it) / (max_it - 1 - wp_it)
148 | cur_lr = 0.001 * peak_lr + 0.999 * peak_lr * (0.5 + 0.5 * math.cos(math.pi * ratio))
149 |
150 | min_lr, max_lr = cur_lr, cur_lr
151 | min_wd, max_wd = wd, wd
152 | for param_group in optimizer.param_groups:
153 | scaled_lr = param_group['lr'] = cur_lr * param_group.get('lr_scale', 1) # 'lr_scale' could be assigned
154 | min_lr, max_lr = min(min_lr, scaled_lr), max(max_lr, scaled_lr)
155 | scaled_wd = param_group['weight_decay'] = wd * param_group.get('weight_decay_scale', 1) # 'weight_decay_scale' could be assigned
156 | min_wd, max_wd = min(min_wd, scaled_wd), max(max_wd, scaled_wd)
157 | return min_lr, max_lr, min_wd, max_wd
158 |
159 | def get_param_groups(model, nowd_keys=(), lr_scale=0.0):
160 | using_lr_scale = hasattr(model, 'get_layer_id_and_scale_exp') and 0.0 < lr_scale < 1.0
161 | print(f'[get_ft_param_groups][lr decay] using_lr_scale={using_lr_scale}, ft_lr_scale={lr_scale}')
162 | para_groups, para_groups_dbg = {}, {}
163 |
164 | for name, para in model.named_parameters():
165 | if not para.requires_grad:
166 | continue # frozen weights
167 | if len(para.shape) == 1 or name.endswith('.bias') or any(k in name for k in nowd_keys):
168 | wd_scale, group_name = 0., 'no_decay'
169 | else:
170 | wd_scale, group_name = 1., 'decay'
171 |
172 | if using_lr_scale:
173 | layer_id, scale_exp = model.get_layer_id_and_scale_exp(name)
174 | group_name = f'layer{layer_id}_' + group_name
175 | this_lr_scale = lr_scale ** scale_exp
176 | dbg = f'[layer {layer_id}][sc = {lr_scale} ** {scale_exp}]'
177 | else:
178 | this_lr_scale = 1
179 | dbg = f'[no scale]'
180 |
181 | if group_name not in para_groups:
182 | para_groups[group_name] = {'params': [], 'weight_decay_scale': wd_scale, 'lr_scale': this_lr_scale}
183 | para_groups_dbg[group_name] = {'params': [], 'weight_decay_scale': wd_scale, 'lr_scale': dbg}
184 | para_groups[group_name]['params'].append(para)
185 | para_groups_dbg[group_name]['params'].append(name)
186 |
187 | for g in para_groups_dbg.values():
188 | g['params'] = pformat(', '.join(g['params']), width=200)
189 |
190 | print(f'[get_ft_param_groups] param groups = \n{pformat(para_groups_dbg, indent=2, width=250)}\n')
191 | return list(para_groups.values())
192 |
193 |
194 | def load_checkpoint(args,resume_from, model, optimizer):
195 | try:
196 | if args.ablationMode != '':
197 | resume_from = resume_from+ f'{args.ablationMode}Mode_{args.pre_epochs}Epochs_{args.dataset}_{args.Part}Part_{args.num_of_cv}NCV_{args.model}_{args.mask}_pretrained.pth'
198 | else:
199 | resume_from = resume_from+ f'{args.dataset}_{args.Part}Part_{args.num_of_cv}NCV_{args.model}_{args.mask}_pretrained.pth'
200 | except AttributeError:
201 | print('some variable is not exist')
202 |
203 | if os.path.exists(resume_from):
204 | print(f'==> ckpt `is {resume_from}`!')
205 | # return 0, '[no performance_desc]'
206 | print(f'[try to resume from file `{resume_from}`]')
207 | checkpoint = torch.load(resume_from, map_location='cpu')
208 | assert checkpoint.get('is_pretrain', False) == False, 'Please do not use `*_still_pretraining.pth`, which is ONLY for resuming the pretraining. Use `*_pretrained.pth` or `*_finetuned*.pth` instead.'
209 |
210 | ep_start, performance_desc = checkpoint.get('epoch', -1) + 1, checkpoint.get('performance_desc', '[no performance_desc]')
211 | missing, unexpected = model.load_state_dict(checkpoint.get('module', checkpoint), strict=False)
212 | print(f'[load_checkpoint] missing_keys={missing}')
213 | print(f'[load_checkpoint] unexpected_keys={unexpected}')
214 | print(f'[load_checkpoint] ep_start={ep_start}, performance_desc={performance_desc}')
215 |
216 | if 'optimizer' in checkpoint:
217 | optimizer.load_state_dict(checkpoint['optimizer'])
218 | else:
219 | print(f'==> ckpt `{resume_from}` not exist')
220 | if args.model in ['BaseCNN','BaseCNNSup','DeepConvLstm','Transformer_HAR','DeepConvLstmAttn','SA_HAR']:
221 | pass
222 | else:
223 | sys.exit(1)
224 |
--------------------------------------------------------------------------------
/pretrain/main.py:
--------------------------------------------------------------------------------
1 | '''
2 | Description:
3 | Date: 2023-06-04 12:00:53
4 | LastEditTime: 2024-08-13 16:08:29
5 | FilePath: /chengdongzhou/action/MaskCAE/pretrain/main.py
6 | '''
7 | import os
8 |
9 | os.environ["CUDA_VISIBLE_DEVICES"] = '1'
10 |
11 |
12 | import datetime
13 | import math
14 | import sys
15 | import time
16 | from functools import partial
17 | from typing import List
18 |
19 | import torch
20 | from torch.nn.parallel import DistributedDataParallel
21 | from torch.utils.data import DataLoader
22 |
23 | import dist
24 | import encoder
25 | from decoder import LightDecoder
26 | from models import build_sparse_encoder
27 | from sampler import DistInfiniteBatchSampler, worker_init_fn
28 | from MaskCAE import MaskCAE
29 | from utils import arg_util, misc, lamb
30 | from utils.lr_control import lr_wd_annealing, get_param_groups
31 | import numpy as np
32 | import random
33 |
34 |
35 | def get_data_size(data_name, is_sup = False ):
36 | Model_Seen_SSL_F_Size = {
37 | 'ucihar': ( 32, 9 ) ,
38 | 'motion': ( 32, 12 ) ,
39 | 'uschad': ( 32, 6 ) ,
40 | }
41 | Model_Seen_Sup_F_Size = {
42 | 'ucihar': ( 128, 9 ) ,
43 | 'motion': ( 128, 12 ) ,
44 | 'uschad': ( 32, 6 ) ,
45 | }
46 | if is_sup:
47 | size_dict = Model_Seen_Sup_F_Size
48 | else:
49 | size_dict = Model_Seen_SSL_F_Size
50 | if data_name in size_dict:
51 | pass
52 | else:
53 | raise Exception( 'please input correct data name')
54 | return size_dict[data_name]
55 |
56 |
57 | def main_pt(args:arg_util.Args,dataset_train):
58 |
59 |
60 | # build data
61 | print(f'[build data for pre-training] ...\n')
62 | data_loader_train = DataLoader(
63 | dataset = dataset_train,
64 | num_workers = args.dataloader_workers,
65 | pin_memory = True,
66 | batch_sampler = DistInfiniteBatchSampler(
67 | dataset_len = len(dataset_train),
68 | glb_batch_size = args.glb_batch_size,
69 | shuffle = True,
70 | filling = True,
71 | rank = dist.get_rank(),
72 | world_size = dist.get_world_size(),
73 | ),
74 | worker_init_fn = worker_init_fn
75 | )
76 | itrt_train, iters_train = iter(data_loader_train), len(data_loader_train)
77 | print(f'[dataloader] gbs={args.glb_batch_size}, lbs={args.batch_size_per_gpu}, iters_train={iters_train}')
78 |
79 | # build encoder and decoder
80 | print(f'==> is sup? {args.is_sup}')
81 | enc: encoder.SparseEncoder = build_sparse_encoder(args.model,dataset_name =args.dataset_name ,input_size=get_data_size(args.dataset_name,args.is_sup), sbn=args.sbn, drop_path_rate=args.dp, verbose=False,mode=args.ablation_mode)
82 | dec = LightDecoder(enc.downsample_raito, sbn=args.sbn,mode=args.ablation_mode)
83 | model = MaskCAE(
84 | sparse_encoder = enc,
85 | dense_decoder = dec,
86 | mask_ratio = args.mask,
87 | densify_norm = args.densify_norm,
88 | sbn = args.sbn,
89 | mode = args.ablation_mode,
90 | sup_window_length = args.is_sup
91 | ).cuda()
92 | print(f'[PT model] model = {model}\n')
93 |
94 |
95 | # build optimizer and lr_scheduler
96 | param_groups: List[dict] = get_param_groups(model, nowd_keys={'cls_token', 'pos_embed', 'mask_token', 'gamma'})
97 | opt_clz = {
98 | 'sgd': partial(torch.optim.SGD, momentum=0.9, nesterov=True ),
99 | 'adamw': partial(torch.optim.AdamW, betas=(0.9, args.ada) ),
100 | 'lamb': partial(lamb.TheSameAsTimmLAMB, betas=(0.9, args.ada), max_grad_norm=5.0 ),
101 | }[args.opt]
102 | optimizer = opt_clz(params=param_groups, lr=args.lr, weight_decay=0.0)
103 | print(f'[optimizer] optimizer({opt_clz}) ={optimizer}\n')
104 |
105 | # try to resume
106 | ep_start, performance_desc = misc.load_checkpoint(args.resume_from, model, optimizer)
107 | if ep_start >= args.ep: # load from a complete checkpoint file
108 | print(f' [*] [PT already done] Min/Last Recon Loss: {performance_desc}')
109 | else: # perform pre-training
110 | tb_lg = misc.TensorboardLogger(args.tb_lg_dir, is_master=dist.is_master(), prefix='pt')
111 | min_loss = 1e9
112 | print(f'[PT start] from ep{ep_start}')
113 |
114 | pt_start_time = time.time()
115 | for ep in range(ep_start, args.ep):
116 | ep_start_time = time.time()
117 | tb_lg.set_step(ep * iters_train)
118 | if hasattr(itrt_train, 'set_epoch'):
119 | itrt_train.set_epoch(ep)
120 |
121 | stats = pre_train_one_ep(ep, args, tb_lg, itrt_train, iters_train, model, optimizer)
122 | last_loss = stats['last_loss']
123 | min_loss = min(min_loss, last_loss)
124 | performance_desc = f'{min_loss:.4f} {last_loss:.4f}'
125 | if args.ablation_mode != '':
126 | misc.save_checkpoint(f'{args.ablation_mode}Mode_{args.ep}Epochs_{args.dataset_name}_{args.Part}Part_{args.num_of_cv}NCV_{args.model}_{args.mask}_still_pretraining.pth', args, ep, performance_desc, model.state_dict(with_config=True), optimizer.state_dict())
127 | misc.save_checkpoint_for_finetune(f'{args.ablation_mode}Mode_{args.ep}Epochs_{args.dataset_name}_{args.Part}Part_{args.num_of_cv}NCV_{args.model}_{args.mask}_pretrained.pth', args, model.sparse_encoder.sp_cnn.state_dict())
128 | else:
129 | misc.save_checkpoint(f'{args.dataset_name}_{args.Part}Part_{args.num_of_cv}NCV_{args.model}_{args.mask}_still_pretraining.pth', args, ep, performance_desc, model.state_dict(with_config=True), optimizer.state_dict())
130 | misc.save_checkpoint_for_finetune(f'{args.dataset_name}_{args.Part}Part_{args.num_of_cv}NCV_{args.model}_{args.mask}_pretrained.pth', args, model.sparse_encoder.sp_cnn.state_dict())
131 |
132 | ep_cost = round(time.time() - ep_start_time, 2) + 1 # +1s: approximate the following logging cost
133 | remain_secs = (args.ep-1 - ep) * ep_cost
134 | remain_time = datetime.timedelta(seconds=round(remain_secs))
135 | finish_time = time.strftime("%m-%d %H:%M", time.localtime(time.time() + remain_secs))
136 | print(f' [*] [ep{ep}/{args.ep}] Min/Last Recon Loss: {performance_desc}, Cost: {ep_cost}s, Remain: {remain_time}, Finish @ {finish_time}')
137 |
138 | args.cur_ep = f'{ep + 1}/{args.ep}'
139 | args.remain_time, args.finish_time = str(remain_time), str(finish_time)
140 | args.last_loss = last_loss
141 | args.log_epoch()
142 |
143 | tb_lg.update(min_loss=min_loss, head='train', step=ep)
144 | tb_lg.update(rest_hours=round(remain_secs/60/60, 2), head='z_burnout', step=ep)
145 | tb_lg.flush()
146 |
147 | # finish pre-training
148 | tb_lg.update(min_loss=min_loss, head='result', step=ep_start)
149 | tb_lg.update(min_loss=min_loss, head='result', step=args.ep)
150 | tb_lg.flush()
151 | print(f'final args:\n{str(args)}')
152 | print('\n\n')
153 | print(f' [*] [PT finished] Min/Last Recon Loss: {performance_desc}, Total Cost: {(time.time() - pt_start_time) / 60 / 60:.1f}h\n')
154 | print('\n\n')
155 | tb_lg.close()
156 | time.sleep(10)
157 |
158 | args.remain_time, args.finish_time = '-', time.strftime("%m-%d %H:%M", time.localtime(time.time()))
159 | args.log_epoch()
160 |
161 |
162 | def pre_train_one_ep(ep, args: arg_util.Args, tb_lg: misc.TensorboardLogger, itrt_train, iters_train, model: DistributedDataParallel, optimizer):
163 | model.train()
164 | me = misc.MetricLogger(delimiter=' ')
165 | me.add_meter('max_lr', misc.SmoothedValue(window_size=1, fmt='{value:.5f}'))
166 | header = f'[PT] Epoch {ep}:'
167 |
168 | optimizer.zero_grad()
169 | early_clipping = args.clip > 0 and not hasattr(optimizer, 'global_grad_norm')
170 | late_clipping = hasattr(optimizer, 'global_grad_norm')
171 | if early_clipping:
172 | params_req_grad = [p for p in model.parameters() if p.requires_grad]
173 |
174 | for it, (inp, _) in enumerate(me.log_every(iters_train, itrt_train, 3, header)):
175 | # adjust lr and wd
176 | min_lr, max_lr, min_wd, max_wd = lr_wd_annealing(optimizer, args.lr, args.wd, args.wde, it + ep * iters_train, args.wp_ep * iters_train, args.ep * iters_train)
177 |
178 | # forward and backward
179 | inp = inp.cuda().float()
180 | # MaskCAE.forward
181 | loss = model(inp, active_b1fs=None, vis=False)
182 | optimizer.zero_grad()
183 | loss.backward()
184 | loss = loss.item()
185 | if not math.isfinite(loss):
186 | print(f'[rk{dist.get_rank():02d}] Loss is {loss}, stopping training!', force=True, flush=True)
187 | sys.exit(-1)
188 |
189 | # optimize
190 | grad_norm = None
191 | if early_clipping: grad_norm = torch.nn.utils.clip_grad_norm_(params_req_grad, args.clip).item()
192 | optimizer.step()
193 | if late_clipping: grad_norm = optimizer.global_grad_norm
194 | torch.cuda.synchronize()
195 |
196 | # log
197 | me.update(last_loss=loss)
198 | me.update(max_lr=max_lr)
199 | tb_lg.update(loss=me.meters['last_loss'].global_avg, head='train_loss')
200 | tb_lg.update(sche_lr=max_lr, head='train_hp/lr_max')
201 | tb_lg.update(sche_lr=min_lr, head='train_hp/lr_min')
202 | tb_lg.update(sche_wd=max_wd, head='train_hp/wd_max')
203 | tb_lg.update(sche_wd=min_wd, head='train_hp/wd_min')
204 |
205 | if grad_norm is not None:
206 | me.update(orig_norm=grad_norm)
207 | tb_lg.update(orig_norm=grad_norm, head='train_hp')
208 | tb_lg.set_step()
209 |
210 | me.synchronize_between_processes()
211 | return {k: meter.global_avg for k, meter in me.meters.items()}
212 |
213 |
214 | def set_seed(seed):
215 | # fix seed
216 | torch.manual_seed(seed)
217 | torch.cuda.manual_seed_all(seed)
218 | np.random.seed(seed)
219 | random.seed(seed)
220 | np.random.seed(seed)
221 | torch.backends.cudnn.deterministic = True
222 |
223 | if __name__ == '__main__':
224 | from create_dataset import GetHarDataset, dotdict
225 | from dataloaders import data_set
226 | import os
227 | args: arg_util.Args = arg_util.init_dist_and_get_args()
228 | os.environ["CUDA_VISIBLE_DEVICES"] = str(args.device)
229 | print(f'initial args:\n{str(args)}')
230 | args.log_epoch()
231 | set_seed(0)
232 |
233 |
234 | if args.DATASET_PRE == 'self' or args.DATASET_PRE == 'Sup':
235 | data_args = dotdict()
236 | if args.DATASET_PRE == 'Sup':
237 | data_args.yaml_path = 'configs/yaml/data_sup.yaml'
238 | else:
239 | data_args.yaml_path = 'configs/yaml/data.yaml'
240 | data_args, dataset\
241 | = GetHarDataset(dataset_name=args.dataset_name,data_args=data_args)
242 | for currenct_cv in range(dataset.num_of_cv):
243 | print("================ {} Mode ====================".format(dataset.exp_mode))
244 | print("================ {} CV ====================".format(dataset.num_of_cv))
245 | dataset.update_train_val_test_keys()
246 | args.Part = dataset.index_of_cv
247 | args.num_of_cv = dataset.num_of_cv
248 | train_data = data_set(data_args,dataset,"train")
249 | main_pt(args,train_data)
250 |
251 |
--------------------------------------------------------------------------------
/MaskCAE/pretrain/utils/misc.py:
--------------------------------------------------------------------------------
1 |
2 | import datetime
3 | import functools
4 | import os
5 | import subprocess
6 | import sys
7 | import time
8 | from collections import defaultdict, deque
9 | from typing import Iterator
10 |
11 | import numpy as np
12 | import pytz
13 | import torch
14 | from torch.utils.tensorboard import SummaryWriter
15 |
16 | import dist
17 |
18 | os_system = functools.partial(subprocess.call, shell=True)
19 | os_system_get_stdout = lambda cmd: subprocess.run(cmd, shell=True, stdout=subprocess.PIPE).stdout.decode('utf-8')
20 | def os_system_get_stdout_stderr(cmd):
21 | sp = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
22 | return sp.stdout.decode('utf-8'), sp.stderr.decode('utf-8')
23 |
24 |
25 | def is_pow2n(x):
26 | return x > 0 and (x & (x - 1) == 0)
27 |
28 |
29 | def time_str(for_dirname=False):
30 | return datetime.datetime.now(tz=pytz.timezone('Asia/Shanghai')).strftime('%m-%d_%H-%M-%S' if for_dirname else '[%m-%d %H:%M:%S]')
31 |
32 |
33 | def init_distributed_environ(exp_dir):
34 | dist.initialize()
35 | dist.barrier()
36 |
37 | import torch.backends.cudnn as cudnn
38 | cudnn.benchmark = True
39 | cudnn.deterministic = False
40 |
41 | _set_print_only_on_master_proc(is_master=dist.is_local_master())
42 | if dist.is_local_master() and len(exp_dir):
43 | sys.stdout, sys.stderr = _SyncPrintToFile(exp_dir, stdout=True), _SyncPrintToFile(exp_dir, stdout=False)
44 |
45 |
46 | def _set_print_only_on_master_proc(is_master):
47 | import builtins as __builtin__
48 |
49 | builtin_print = __builtin__.print
50 |
51 | def prt(msg, *args, **kwargs):
52 | force = kwargs.pop('force', False)
53 | clean = kwargs.pop('clean', False)
54 | deeper = kwargs.pop('deeper', False)
55 | if is_master or force:
56 | if not clean:
57 | f_back = sys._getframe().f_back
58 | if deeper and f_back.f_back is not None:
59 | f_back = f_back.f_back
60 | file_desc = f'{f_back.f_code.co_filename:24s}'[-24:]
61 | msg = f'{time_str()} ({file_desc}, line{f_back.f_lineno:-4d})=> {msg}'
62 | builtin_print(msg, *args, **kwargs)
63 |
64 | __builtin__.print = prt
65 |
66 |
67 | class _SyncPrintToFile(object):
68 | def __init__(self, exp_dir, stdout=True):
69 | self.terminal = sys.stdout if stdout else sys.stderr
70 | fname = os.path.join(exp_dir, 'stdout_backup.txt' if stdout else 'stderr_backup.txt')
71 | self.log = open(fname, 'w')
72 | self.log.flush()
73 |
74 | def write(self, message):
75 | self.terminal.write(message)
76 | self.log.write(message)
77 | self.log.flush()
78 |
79 | def flush(self):
80 | self.terminal.flush()
81 | self.log.flush()
82 |
83 |
84 | class TensorboardLogger(object):
85 | def __init__(self, log_dir, is_master, prefix='pt'):
86 | self.is_master = is_master
87 | self.writer = SummaryWriter(log_dir=log_dir) if self.is_master else None
88 | self.step = 0
89 | self.prefix = prefix
90 | self.log_freq = 300
91 |
92 | def set_step(self, step=None):
93 | if step is not None:
94 | self.step = step
95 | else:
96 | self.step += 1
97 |
98 | def get_loggable(self, step=None):
99 | if step is None: # iter wise
100 | step = self.step
101 | loggable = step % self.log_freq == 0
102 | else: # epoch wise
103 | loggable = True
104 | return step, (loggable and self.is_master)
105 |
106 | def update(self, head='scalar', step=None, **kwargs):
107 | step, loggable = self.get_loggable(step)
108 | if loggable:
109 | head = f'{self.prefix}_{head}'
110 | for k, v in kwargs.items():
111 | if v is None:
112 | continue
113 | if isinstance(v, torch.Tensor):
114 | v = v.item()
115 | assert isinstance(v, (float, int))
116 | self.writer.add_scalar(head + "/" + k, v, step)
117 |
118 | def log_distribution(self, tag, values, step=None):
119 | step, loggable = self.get_loggable(step)
120 | if loggable:
121 | if not isinstance(values, torch.Tensor):
122 | values = torch.tensor(values)
123 | self.writer.add_histogram(tag=tag, values=values, global_step=step)
124 |
125 | def log_image(self, tag, img, step=None, dataformats='NCHW'):
126 | step, loggable = self.get_loggable(step)
127 | if loggable:
128 | # img = img.cpu().numpy()
129 | self.writer.add_image(tag, img, step, dataformats=dataformats)
130 |
131 | def flush(self):
132 | if self.is_master: self.writer.flush()
133 |
134 | def close(self):
135 | if self.is_master: self.writer.close()
136 |
137 |
138 | def save_checkpoint(save_to, args, epoch, performance_desc, model_without_ddp_state, optimizer_state):
139 | checkpoint_path = os.path.join(args.exp_dir, save_to)
140 | if dist.is_local_master():
141 | to_save = {
142 | 'args': str(args),
143 | 'arch': args.model,
144 | 'epoch': epoch,
145 | 'performance_desc': performance_desc,
146 | 'module': model_without_ddp_state,
147 | 'optimizer': optimizer_state,
148 | 'is_pretrain': True,
149 | }
150 | torch.save(to_save, checkpoint_path)
151 |
152 |
153 | def save_checkpoint_for_finetune(save_to, args, sp_cnn_state):
154 | checkpoint_path = os.path.join(args.exp_dir, save_to)
155 | if dist.is_local_master():
156 | to_save = {
157 | 'arch': args.model,
158 | 'module': sp_cnn_state,
159 | 'is_pretrain': False,
160 | }
161 | torch.save(to_save, checkpoint_path)
162 |
163 |
164 | def load_checkpoint(resume_from, model_without_ddp, optimizer):
165 | if len(resume_from) == 0:
166 | return 0, '[no performance_desc]'
167 | print(f'[try to resume from file `{resume_from}`]')
168 | checkpoint = torch.load(resume_from, map_location='cpu')
169 |
170 | ep_start, performance_desc = checkpoint.get('epoch', -1) + 1, checkpoint.get('performance_desc', '[no performance_desc]')
171 | missing, unexpected = model_without_ddp.load_state_dict(checkpoint['module'], strict=False)
172 | print(f'[load_checkpoint] missing_keys={missing}')
173 | print(f'[load_checkpoint] unexpected_keys={unexpected}')
174 | print(f'[load_checkpoint] ep_start={ep_start}, performance_desc={performance_desc}')
175 |
176 | if 'optimizer' in checkpoint:
177 | optimizer.load_state_dict(checkpoint['optimizer'])
178 | return ep_start, performance_desc
179 |
180 |
181 | class SmoothedValue(object):
182 | """Track a series of values and provide access to smoothed values over a
183 | window or the global series average.
184 | """
185 |
186 | def __init__(self, window_size=20, fmt=None):
187 | if fmt is None:
188 | fmt = "{median:.4f} ({global_avg:.4f})"
189 | self.deque = deque(maxlen=window_size)
190 | self.total = 0.0
191 | self.count = 0
192 | self.fmt = fmt
193 |
194 | def update(self, value, n=1):
195 | self.deque.append(value)
196 | self.count += n
197 | self.total += value * n
198 |
199 | def synchronize_between_processes(self):
200 | """
201 | Warning: does not synchronize the deque!
202 | """
203 | t = torch.tensor([self.count, self.total], dtype=torch.float64, device='cuda')
204 | dist.barrier()
205 | dist.allreduce(t)
206 | t = t.tolist()
207 | self.count = int(t[0])
208 | self.total = t[1]
209 |
210 | @property
211 | def median(self):
212 | d = torch.tensor(list(self.deque))
213 | return d.median().item()
214 |
215 | @property
216 | def avg(self):
217 | d = torch.tensor(list(self.deque), dtype=torch.float32)
218 | return d.mean().item()
219 |
220 | @property
221 | def global_avg(self):
222 | return self.total / self.count
223 |
224 | @property
225 | def max(self):
226 | return max(self.deque)
227 |
228 | @property
229 | def value(self):
230 | return self.deque[-1]
231 |
232 | def __str__(self):
233 | return self.fmt.format(
234 | median=self.median,
235 | avg=self.avg,
236 | global_avg=self.global_avg,
237 | max=self.max,
238 | value=self.value)
239 |
240 |
241 | class MetricLogger(object):
242 | def __init__(self, delimiter="\t"):
243 | self.meters = defaultdict(SmoothedValue)
244 | self.delimiter = delimiter
245 |
246 | def update(self, **kwargs):
247 | for k, v in kwargs.items():
248 | if v is None:
249 | continue
250 | if isinstance(v, torch.Tensor):
251 | v = v.item()
252 | assert isinstance(v, (float, int))
253 | self.meters[k].update(v)
254 |
255 | def __getattr__(self, attr):
256 | if attr in self.meters:
257 | return self.meters[attr]
258 | if attr in self.__dict__:
259 | return self.__dict__[attr]
260 | raise AttributeError("'{}' object has no attribute '{}'".format(
261 | type(self).__name__, attr))
262 |
263 | def __str__(self):
264 | loss_str = []
265 | for name, meter in self.meters.items():
266 | loss_str.append(
267 | "{}: {}".format(name, str(meter))
268 | )
269 | return self.delimiter.join(loss_str)
270 |
271 | def synchronize_between_processes(self):
272 | for meter in self.meters.values():
273 | meter.synchronize_between_processes()
274 |
275 | def add_meter(self, name, meter):
276 | self.meters[name] = meter
277 |
278 | def log_every(self, max_iters, itrt, print_freq, header=None):
279 | print_iters = set(np.linspace(0, max_iters - 1, print_freq, dtype=int).tolist())
280 | if not header:
281 | header = ''
282 | start_time = time.time()
283 | end = time.time()
284 | self.iter_time = SmoothedValue(fmt='{avg:.4f}')
285 | self.data_time = SmoothedValue(fmt='{avg:.4f}')
286 | space_fmt = ':' + str(len(str(max_iters))) + 'd'
287 | log_msg = [
288 | header,
289 | '[{0' + space_fmt + '}/{1}]',
290 | 'eta: {eta}',
291 | '{meters}',
292 | 'iter: {time}s',
293 | 'data: {data}s'
294 | ]
295 | log_msg = self.delimiter.join(log_msg)
296 |
297 | if isinstance(itrt, Iterator) and not hasattr(itrt, 'preload') and not hasattr(itrt, 'set_epoch'):
298 | for i in range(max_iters):
299 | obj = next(itrt)
300 | self.data_time.update(time.time() - end)
301 | yield obj
302 | self.iter_time.update(time.time() - end)
303 | if i in print_iters:
304 | eta_seconds = self.iter_time.global_avg * (max_iters - i)
305 | eta_string = str(datetime.timedelta(seconds=int(eta_seconds)))
306 | print(log_msg.format(
307 | i, max_iters, eta=eta_string,
308 | meters=str(self),
309 | time=str(self.iter_time), data=str(self.data_time)))
310 | end = time.time()
311 | else:
312 | for i, obj in enumerate(itrt):
313 | self.data_time.update(time.time() - end)
314 | yield obj
315 | self.iter_time.update(time.time() - end)
316 | if i in print_iters:
317 | eta_seconds = self.iter_time.global_avg * (max_iters - i)
318 | eta_string = str(datetime.timedelta(seconds=int(eta_seconds)))
319 | print(log_msg.format(
320 | i, max_iters, eta=eta_string,
321 | meters=str(self),
322 | time=str(self.iter_time), data=str(self.data_time)))
323 | end = time.time()
324 |
325 | total_time = time.time() - start_time
326 | total_time_str = str(datetime.timedelta(seconds=int(total_time)))
327 | print('{} Total time: {} ({:.3f} s / it)'.format(
328 | header, total_time_str, total_time / max_iters))
329 |
--------------------------------------------------------------------------------
/utils/augmentations.py:
--------------------------------------------------------------------------------
1 | '''
2 | Author: error: git config user.name && git config user.email & please set dead value or install git
3 | Date: 2022-10-12 21:38:44
4 | LastEditors: Please set LastEditors
5 | LastEditTime: 2023-05-30 13:42:15
6 | FilePath: /chengdongzhou/action/tRexHAR/utils/augmentations.py
7 | Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
8 | '''
9 | import numpy as np
10 | import torch
11 | import scipy
12 | from scipy.interpolate import CubicSpline
13 | import random
14 | from configs.base_configs import args
15 |
16 | def get_aug_data( data , RA = None , FA = None):
17 | if args.RandAug:
18 | aug_data = RA( data.squeeze(1) ).unsqueeze(1)
19 | inputs = torch.cat( [data,aug_data] , dim=0 )
20 | elif args.FixAug:
21 | aug_data = FA( data.squeeze(1) , args.data_aug, args.data_aug2 ).unsqueeze(1)
22 | inputs = torch.cat( [data,aug_data] , dim=0 )
23 | else:
24 | if args.data_aug and (not args.data_aug2) :
25 | aug_data = gen_aug( data.squeeze(1) , ssh_type= args.data_aug ).unsqueeze(1)
26 | inputs = torch.cat( [data,aug_data] , dim=0 )
27 | elif args.data_aug and args.data_aug2 :
28 | aug1 = gen_aug( data.squeeze(1) , ssh_type= args.data_aug ).unsqueeze(1)
29 | aug2 = gen_aug( data.squeeze(1) , ssh_type= args.data_aug2 ).unsqueeze(1)
30 | inputs = torch.cat( [aug1,aug2] , dim=0 )
31 | else:
32 | '''fully supervised without data augmentation'''
33 | inputs = data
34 | return inputs
35 |
36 | def gen_aug(sample, ssh_type = 'na'):
37 | if ssh_type == 'na':
38 | return sample
39 | elif ssh_type == 'shuffle':
40 | return shuffle(sample)
41 | elif ssh_type == 'jit_scal':
42 | scale_sample = scaling(sample, sigma=2)
43 | return torch.from_numpy(scale_sample)
44 | elif ssh_type == 'perm_jit':
45 | return jitter(permutation(sample, max_segments=10), sigma=0.8)
46 | elif ssh_type == 'resample':
47 | return torch.from_numpy(resample(sample))
48 | elif ssh_type == 'noise':
49 | return jitter(sample)
50 | elif ssh_type == 'scale':
51 | return torch.from_numpy(scaling(sample))
52 | elif ssh_type == 'negate':
53 | return negated(sample)
54 | elif ssh_type == 't_flip':
55 | return time_flipped(sample)
56 | elif ssh_type == 'rotation':
57 | if isinstance(multi_rotation(sample), np.ndarray):
58 | return torch.from_numpy(multi_rotation(sample))
59 | else:
60 | return multi_rotation(sample)
61 | elif ssh_type == 'perm':
62 | return permutation(sample, max_segments=10)
63 | elif ssh_type == 't_warp':
64 | return torch.from_numpy(time_warp(sample))
65 | elif ssh_type == 'hfc':
66 | fft, fd = generate_high(sample, r=(32,2), high=True)
67 | return fd
68 | elif ssh_type == 'lfc':
69 | fft, fd = generate_high(sample, r=(32,2), high=False)
70 | return fd
71 | elif ssh_type == 'p_shift':
72 | return ifft_phase_shift(sample)
73 | elif ssh_type == 'ap_p':
74 | return ifft_amp_phase_pert(sample)
75 | elif ssh_type == 'ap_f':
76 | return ifft_amp_phase_pert_fully(sample)
77 | else:
78 | print('The task is not available!\n')
79 |
80 |
81 |
82 |
83 |
84 |
85 | def shuffle(x):
86 | sample_ssh = []
87 | for data in x:
88 | p = np.random.RandomState(seed=21).permutation(data.shape[1])
89 | data = data[:, p]
90 | sample_ssh.append(data)
91 | return torch.stack(sample_ssh)
92 |
93 |
94 | def jitter(x, sigma=0.8):
95 | # https://arxiv.org/pdf/1706.00527.pdf
96 | return x + np.random.normal(loc=0., scale=sigma, size=x.shape)
97 |
98 |
99 | def scaling(x, sigma=1.1): # apply same distortion to the signals from each sensor
100 | # https://arxiv.org/pdf/1706.00527.pdf
101 | factor = np.random.normal(loc=2., scale=sigma, size=(x.shape[0], x.shape[1]))
102 | ai = []
103 | for i in range(x.shape[2]):
104 | xi = x[:, :, i]
105 | ai.append(np.multiply(xi, factor[:, :])[:, :, np.newaxis])
106 | return np.concatenate((ai), axis=2)
107 |
108 |
109 | def negated(X):
110 | return X * -1
111 |
112 |
113 | def time_flipped(X):
114 | inv_idx = torch.arange(X.size(1) - 1, -1, -1).long()
115 | return X[:, inv_idx, :]
116 |
117 |
118 | def permutation(x, max_segments=5, seg_mode="random"):
119 | orig_steps = np.arange(x.shape[1])
120 |
121 | num_segs = np.random.randint(1, max_segments, size=(x.shape[0]))
122 | ret = np.zeros_like(x)
123 | for i, pat in enumerate(x):
124 | if num_segs[i] > 1:
125 | if seg_mode == "random":
126 | split_points = np.random.choice(x.shape[1] - 2, num_segs[i] - 1, replace=False)
127 | split_points.sort()
128 | splits = np.split(orig_steps, split_points)
129 | else:
130 | splits = np.array_split(orig_steps, num_segs[i])
131 | np.random.shuffle(splits)
132 | warp = np.concatenate(splits).ravel()
133 | ret[i] = pat[warp, :]
134 | else:
135 | ret[i] = pat
136 | return torch.from_numpy(ret)
137 |
138 |
139 | def resample(x):
140 | from scipy.interpolate import interp1d
141 | orig_steps = np.arange(x.shape[1])
142 | interp_steps = np.arange(0, orig_steps[-1]+0.001, 1/3)
143 | Interp = interp1d(orig_steps, x, axis=1)
144 | InterpVal = Interp(interp_steps)
145 | start = random.choice(orig_steps)
146 | resample_index = np.arange(start, 3 * x.shape[1], 2)[:x.shape[1]]
147 | return InterpVal[:, resample_index, :]
148 |
149 |
150 | def multi_rotation(x):
151 | n_channel = x.shape[2]
152 | n_rot = n_channel // 3
153 | x_rot = np.array([])
154 | for i in range(n_rot):
155 | x_rot = np.concatenate((x_rot, rotation(x[:, :, i * 3:i * 3 + 3])), axis=2) if x_rot.size else rotation(
156 | x[:, :, i * 3:i * 3 + 3])
157 | return x_rot
158 |
159 | def rotation(X):
160 | """
161 | Applying a random 3D rotation
162 | """
163 | axes = np.random.uniform(low=-1, high=1, size=(X.shape[0], X.shape[2]))
164 | angles = np.random.uniform(low=-np.pi, high=np.pi, size=(X.shape[0]))
165 | matrices = axis_angle_to_rotation_matrix_3d_vectorized(axes, angles)
166 | return np.matmul(X, matrices)
167 |
168 | def axis_angle_to_rotation_matrix_3d_vectorized(axes, angles):
169 | """
170 | Get the rotational matrix corresponding to a rotation of (angle) radian around the axes
171 | Reference: the Transforms3d package - transforms3d.axangles.axangle2mat
172 | Formula: http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle
173 | """
174 | axes = axes / np.linalg.norm(axes, ord=2, axis=1, keepdims=True)
175 | x = axes[:, 0]; y = axes[:, 1]; z = axes[:, 2]
176 | c = np.cos(angles)
177 | s = np.sin(angles)
178 | C = 1 - c
179 |
180 | xs = x*s; ys = y*s; zs = z*s
181 | xC = x*C; yC = y*C; zC = z*C
182 | xyC = x*yC; yzC = y*zC; zxC = z*xC
183 |
184 | m = np.array([
185 | [ x*xC+c, xyC-zs, zxC+ys ],
186 | [ xyC+zs, y*yC+c, yzC-xs ],
187 | [ zxC-ys, yzC+xs, z*zC+c ]])
188 | matrix_transposed = np.transpose(m, axes=(2,0,1))
189 | return matrix_transposed
190 |
191 | def get_cubic_spline_interpolation(x_eval, x_data, y_data):
192 | """
193 | Get values for the cubic spline interpolation
194 | """
195 | cubic_spline = CubicSpline(x_data, y_data)
196 | return cubic_spline(x_eval)
197 |
198 |
199 | def time_warp(X, sigma=0.2, num_knots=4):
200 | """
201 | Stretching and warping the time-series
202 | """
203 | time_stamps = np.arange(X.shape[1])
204 | knot_xs = np.arange(0, num_knots + 2, dtype=float) * (X.shape[1] - 1) / (num_knots + 1)
205 | spline_ys = np.random.normal(loc=1.0, scale=sigma, size=(X.shape[0] * X.shape[2], num_knots + 2))
206 |
207 | spline_values = np.array([get_cubic_spline_interpolation(time_stamps, knot_xs, spline_ys_individual) for spline_ys_individual in spline_ys])
208 |
209 | cumulative_sum = np.cumsum(spline_values, axis=1)
210 | distorted_time_stamps_all = cumulative_sum / cumulative_sum[:, -1][:, np.newaxis] * (X.shape[1] - 1)
211 |
212 | X_transformed = np.empty(shape=X.shape)
213 | for i, distorted_time_stamps in enumerate(distorted_time_stamps_all):
214 | X_transformed[i // X.shape[2], :, i % X.shape[2]] = np.interp(time_stamps, distorted_time_stamps, X[i // X.shape[2], :, i % X.shape[2]])
215 | return X_transformed
216 |
217 |
218 | def distance(i, j, imageSize, r):
219 | dis_x = np.sqrt((i - imageSize[0] / 2) ** 2)
220 | dis_y = np.sqrt((j - imageSize[1] / 2) ** 2)
221 | if dis_x < r[0] and dis_y < r[1]:
222 | return 1.0
223 | else:
224 | return 0
225 |
226 |
227 | def mask_radial(img, r):
228 | rows, cols = img.shape
229 | mask = torch.zeros((rows, cols))
230 | for i in range(rows):
231 | for j in range(cols):
232 | mask[i, j] = distance(i, j, imageSize=(rows, cols), r=r)
233 | return mask
234 |
235 |
236 | def generate_high(sample, r, high=True):
237 | # r: int, radius of the mask
238 | images = torch.unsqueeze(sample, 1)
239 | mask = mask_radial(torch.zeros([images.shape[2], images.shape[3]]), r)
240 | bs, c, h, w = images.shape
241 | x = images.reshape([bs * c, h, w])
242 | fd = torch.fft.fftshift(torch.fft.fftn(x, dim=(-2, -1))) # shift: low f in the center
243 | mask = mask.unsqueeze(0).repeat([bs * c, 1, 1])
244 | if high:
245 | fd = fd * (1.-mask)
246 | else:
247 | fd = fd * mask
248 | fft = torch.real(fd)
249 | fd = torch.fft.ifftn(torch.fft.ifftshift(fd), dim=(-2, -1))
250 | fd = torch.real(fd)
251 | fd = torch.squeeze(fd.reshape([bs, c, h, w]))
252 | return fft, fd
253 |
254 |
255 | def ifft_phase_shift(sample):
256 | images = torch.unsqueeze(sample, 1)
257 | bs, c, h, w = images.shape
258 | x = images.reshape([bs * c, h, w])
259 | fd = torch.fft.fftshift(torch.fft.fftn(x, dim=(-2, -1)))
260 |
261 | amp = fd.abs()
262 | phase = fd.angle()
263 |
264 | # phase shift
265 | angles = np.repeat(np.expand_dims(np.random.uniform(low=-np.pi, high=np.pi, size=(sample.shape[0], sample.shape[1])), axis=2), sample.shape[2], axis=2)
266 | phase = phase + angles
267 |
268 | cmp = amp * torch.exp(1j * phase)
269 | ifft = torch.squeeze(torch.real(torch.fft.ifftn(torch.fft.ifftshift(cmp), dim=(-2, -1))).reshape([bs, c, h, w]))
270 |
271 | return ifft
272 |
273 |
274 | def ifft_amp_phase_pert(sample):
275 | images = torch.unsqueeze(sample, 1)
276 | bs, c, h, w = images.shape
277 | x = images.reshape([bs * c, h, w])
278 | fd = torch.fft.fftshift(torch.fft.fftn(x, dim=(-2, -1)))
279 |
280 | amp = fd.abs()
281 | phase = fd.angle()
282 |
283 | # select a segment to conduct perturbations
284 | start = np.random.randint(0, int(0.5 * sample.shape[1]))
285 | end = start + int(0.5 * sample.shape[1])
286 |
287 | # phase shift
288 | angles = np.repeat(np.expand_dims(np.random.uniform(low=-np.pi, high=np.pi, size=(sample.shape[0], sample.shape[1])), axis=2), sample.shape[2], axis=2)
289 | phase[:, start:end, :] = phase[:, start:end, :] + angles[:, start:end, :]
290 |
291 | # amp shift
292 | amp[:, start:end, :] = amp[:, start:end, :] + np.random.normal(loc=0., scale=0.8, size=sample.shape)[:, start:end, :]
293 |
294 | cmp = amp * torch.exp(1j * phase)
295 | ifft = torch.squeeze(torch.real(torch.fft.ifftn(torch.fft.ifftshift(cmp), dim=(-2, -1))).reshape([bs, c, h, w]))
296 |
297 | return ifft
298 |
299 |
300 | def ifft_amp_phase_pert_fully(sample):
301 | images = torch.unsqueeze(sample, 1)
302 | bs, c, h, w = images.shape
303 | x = images.reshape([bs * c, h, w])
304 | fd = torch.fft.fftshift(torch.fft.fftn(x, dim=(-2, -1)))
305 |
306 | amp = fd.abs()
307 | phase = fd.angle()
308 |
309 | # phase shift
310 | angles = np.repeat(np.expand_dims(np.random.uniform(low=-np.pi, high=np.pi, size=(sample.shape[0], sample.shape[1])), axis=2), sample.shape[2], axis=2)
311 | phase = phase + angles
312 |
313 | # amp shift
314 | amp = amp + np.random.normal(loc=0., scale=0.8, size=sample.shape)
315 |
316 | cmp = amp * torch.exp(1j * phase)
317 | ifft = torch.squeeze(torch.real(torch.fft.ifftn(torch.fft.ifftshift(cmp), dim=(-2, -1))).reshape([bs, c, h, w]))
318 |
319 | return ifft
320 |
321 | if __name__ =='__main__':
322 | x = torch.randn(2,171,10)
323 | aug_x = gen_aug(x,'rotation')
324 | print( aug_x.shape )
--------------------------------------------------------------------------------