From 13a58ac3869333614b3238e19564ac7193336ff2 Mon Sep 17 00:00:00 2001 From: Huangshuqi <1105374939@qq.com> Date: Sun, 14 Jul 2024 19:05:30 +1000 Subject: [PATCH] Upload code --- README.md | 113 ++++- confusion_matrix.png | Bin 0 -> 72196 bytes datasets/__init__.py | 4 + datasets/mydataset.py | 80 +++ datasets/split_data.py | 97 ++++ datasets/threeaugment.py | 117 +++++ datasets/transforms.py | 72 +++ environment.yml | 23 + estimate_model.py | 338 +++++++++++++ models/__init__.py | 2 + models/build_models.py | 864 +++++++++++++++++++++++++++++++++ models/helpers.py | 62 +++ optim_AUC.py | 67 +++ sample_png/1720946952217.jpg | Bin 0 -> 84433 bytes sample_png/1720947030019.jpg | Bin 0 -> 117881 bytes scheduler/__init__.py | 1 + scheduler/cosine_lr.py | 119 +++++ scheduler/multistep_lr.py | 66 +++ scheduler/plateau_lr.py | 103 ++++ scheduler/poly_lr.py | 116 +++++ scheduler/scheduler_factory.py | 111 +++++ scheduler/scheduler_main.py | 117 +++++ scheduler/step_lr.py | 63 +++ scheduler/tanh_lr.py | 117 +++++ train_gpu.py | 537 ++++++++++++++++++++ util/__init__.py | 7 + util/engine.py | 168 +++++++ util/losses.py | 64 +++ util/lr_decay.py | 77 +++ util/lr_sched.py | 24 + util/optimizer.py | 441 +++++++++++++++++ util/samplers.py | 66 +++ util/utils.py | 279 +++++++++++ 33 files changed, 4314 insertions(+), 1 deletion(-) create mode 100644 confusion_matrix.png create mode 100644 datasets/__init__.py create mode 100644 datasets/mydataset.py create mode 100644 datasets/split_data.py create mode 100644 datasets/threeaugment.py create mode 100644 datasets/transforms.py create mode 100644 environment.yml create mode 100644 estimate_model.py create mode 100644 models/__init__.py create mode 100644 models/build_models.py create mode 100644 models/helpers.py create mode 100644 optim_AUC.py create mode 100644 sample_png/1720946952217.jpg create mode 100644 sample_png/1720947030019.jpg create mode 100644 scheduler/__init__.py create mode 100644 scheduler/cosine_lr.py create mode 100644 scheduler/multistep_lr.py create mode 100644 scheduler/plateau_lr.py create mode 100644 scheduler/poly_lr.py create mode 100644 scheduler/scheduler_factory.py create mode 100644 scheduler/scheduler_main.py create mode 100644 scheduler/step_lr.py create mode 100644 scheduler/tanh_lr.py create mode 100644 train_gpu.py create mode 100644 util/__init__.py create mode 100644 util/engine.py create mode 100644 util/losses.py create mode 100644 util/lr_decay.py create mode 100644 util/lr_sched.py create mode 100644 util/optimizer.py create mode 100644 util/samplers.py create mode 100644 util/utils.py diff --git a/README.md b/README.md index bcaac99..a848b6e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,113 @@ -# MambaVision +

MambaVision

+ This is a warehouse for MambaVision-Pytorch-model, can be used to train your image-datasets for vision tasks. + +### [MambaVision: A Hybrid Mamba-Transformer Vision Backbone](https://arxiv.org/pdf/2407.08083) +### [Large Batch Optimization for Deep Learning: Training BERT in 76 minutes](https://arxiv.org/abs/1904.00962) + +![image](https://github.com/jiaowoguanren0615/VisionTransformer/blob/main/sample_png/KAN-model.jpg) +![image](https://production-media.paperswithcode.com/methods/Screen_Shot_2021-01-26_at_9.43.31_PM_uI4jjMq.png) + + +## Preparation +### Install mamba_ssm & causal_conv1d +[Github Tutorial website](https://github.com/jiaowoguanren0615/Install_Mamba) + +### Download the dataset: +[flower_dataset](https://www.kaggle.com/datasets/alxmamaev/flowers-recognition). + +## Project Structure +``` +├── datasets: Load datasets + ├── my_dataset.py: Customize reading data sets and define transforms data enhancement methods + ├── split_data.py: Define the function to read the image dataset and divide the training-set and test-set + ├── threeaugment.py: Additional data augmentation methods +├── models: MambaVision Model + ├── build_models.py: Construct MambaVision models + ├── helpers.py: Compute scaled dot product attention +├── scheduler: + ├──scheduler_main.py: Fundamental Scheduler module + ├──scheduler_factory.py: Create lr_scheduler methods according to parameters what you set + ├──other_files: Construct lr_schedulers (cosine_lr, poly_lr, multistep_lr, etc) +├── util: + ├── engine.py: Function code for a training/validation process + ├── losses.py: Knowledge distillation loss, combined with teacher model (if any) + ├── lr_decay.py: Define "inverse_sqrt_lr_decay" function for "Adafactor" optimizer + ├── lr_sched.py: Define "adjust_learning_rate" function + ├── optimizer.py: Define Sophia & Adafactor & LAMB optimizer(for mambavision models training) + ├── samplers.py: Define the parameter of "sampler" in DataLoader + ├── utils.py: Record various indicator information and output and distributed environment +├── estimate_model.py: Visualized evaluation indicators ROC curve, confusion matrix, classification report, etc. +└── train_gpu.py: Training model startup file (including infer process) +``` + +## Precautions +Before you use the code to train your own data set, please first enter the ___train_gpu.py___ file and modify the ___data_root___, ___batch_size___, ___data_len___, ___num_workers___ and ___nb_classes___ parameters. If you want to draw the confusion matrix and ROC curve, you only need to set the ___predict___ parameter to __True__. +Moreover, you can set the ___opt_auc___ parameter to True if you want to optimize your model for a better performance(maybe~). + + +## Train this model + +### Parameters Meaning: +``` +1. nproc_per_node: +2. CUDA_VISIBLE_DEVICES: +3. nnodes: +4. node_rank: +5. master_addr: +6. master_port: +``` +### Transfer Learning: +Step 1: Write the ___pre-training weight path___ into the ___args.fintune___ in string format. +Step 2: Modify the ___args.freeze_layers___ according to your own GPU memory. If you don't have enough memory, you can set this to True to freeze the weights of the remaining layers except the last layer of classification-head without updating the parameters. If you have enough memory, you can set this to False and not freeze the model weights. + +#### Here is an example for setting parameters: +![image](https://github.com/jiaowoguanren0615/VisionTransformer/blob/main/sample_png/transfer_learning.jpg) + + +### Note: +If you want to use multiple GPU for training, whether it is a single machine with multiple GPUs or multiple machines with multiple GPUs, each GPU will divide the batch_size equally. For example, batch_size=4 in my train_gpu.py. If I want to use 2 GPUs for training, it means that the batch_size on each GPU is 4. ___Do not let batch_size=1 on each GPU___, otherwise BN layer maybe report an error. + +### train model with single-machine single-GPU: +``` +python train_gpu.py +``` + +### train model with single-machine multi-GPU: +``` +python -m torch.distributed.run --nproc_per_node=8 train_gpu.py +``` + +### train model with single-machine multi-GPU: +(using a specified part of the GPUs: for example, I want to use the second and fourth GPUs) +``` +CUDA_VISIBLE_DEVICES=1,3 python -m torch.distributed.run --nproc_per_node=2 train_gpu.py +``` + +### train model with multi-machine multi-GPU: +(For the specific number of GPUs on each machine, modify the value of --nproc_per_node. If you want to specify a certain GPU, just add CUDA_VISIBLE_DEVICES= to specify the index number of the GPU before each command. The principle is the same as single-machine multi-GPU training) +``` +On the first machine: python -m torch.distributed.run --nproc_per_node=1 --nnodes=2 --node_rank=0 --master_addr= --master_port= train_gpu.py + +On the second machine: python -m torch.distributed.run --nproc_per_node=1 --nnodes=2 --node_rank=1 --master_addr= --master_port= train_gpu.py +``` + + +## Citation +``` +@article{chen2022pali, + title={Pali: A jointly-scaled multilingual language-image model}, + author={Chen, Xi and Wang, Xiao and Changpinyo, Soravit and Piergiovanni, AJ and Padlewski, Piotr and Salz, Daniel and Goodman, Sebastian and Grycner, Adam and Mustafa, Basil and Beyer, Lucas and others}, + journal={arXiv preprint arXiv:2209.06794}, + year={2022} +} +``` + +``` +@article{you2019large, + title={Large batch optimization for deep learning: Training bert in 76 minutes}, + author={You, Yang and Li, Jing and Reddi, Sashank and Hseu, Jonathan and Kumar, Sanjiv and Bhojanapalli, Srinadh and Song, Xiaodan and Demmel, James and Keutzer, Kurt and Hsieh, Cho-Jui}, + journal={arXiv preprint arXiv:1904.00962}, + year={2019} +} +``` diff --git a/confusion_matrix.png b/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..edc59e07bb0f4bd7cfe5c70a3a361090b3327635 GIT binary patch literal 72196 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 z�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 32 + if is_train: + # this should always dispatch to transforms_imagenet_train + transform = create_transform( + input_size=args.input_size, + is_training=True, + color_jitter=args.color_jitter, + auto_augment=args.aa, + interpolation=args.train_interpolation, + re_prob=args.reprob, + re_mode=args.remode, + re_count=args.recount, + ) + if not resize_im: + # replace RandomResizedCropAndInterpolation with + # RandomCrop + transform.transforms[0] = transforms.RandomCrop( + args.input_size, padding=4) + return transform + + t = [] + if resize_im: + # size = int((256 / 224) * args.input_size) + size = int((1.0 / 0.96) * args.input_size) + t.append( + # to maintain same ratio w.r.t. 224 images + transforms.Resize(size, interpolation=3), + ) + t.append(transforms.CenterCrop(args.input_size)) + + t.append(transforms.ToTensor()) + t.append(transforms.Normalize(IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD)) + return transforms.Compose(t) + + +def build_dataset(args): + train_image_path, train_image_label, val_image_path, val_image_label, class_indices = read_split_data(args.data_root) + + train_transform = build_transform(True, args) + valid_transform = build_transform(False, args) + + train_set = MyDataset(train_image_path, train_image_label, train_transform) + valid_set = MyDataset(val_image_path, val_image_label, valid_transform) + + return train_set, valid_set + diff --git a/datasets/split_data.py b/datasets/split_data.py new file mode 100644 index 0000000..a9d6f02 --- /dev/null +++ b/datasets/split_data.py @@ -0,0 +1,97 @@ +import os, cv2, json, random +import pandas as pd +from tqdm import tqdm +from sklearn.model_selection import train_test_split +import matplotlib.pyplot as plt + + +def read_split_data(root, plot_image=False): + filepaths = [] + labels = [] + bad_images = [] + + random.seed(0) + assert os.path.exists(root), 'Your root does not exists!!!' + + classes = [cla for cla in os.listdir(root) if os.path.isdir(os.path.join(root, cla))] + classes.sort() + class_indices = {k: v for v, k in enumerate(classes)} + + json_str = json.dumps({v: k for k, v in class_indices.items()}, indent=4) + + with open('./classes_indices.json', 'w') as json_file: + json_file.write(json_str) + + every_class_num = [] + supported = ['.jpg', '.png', '.jpeg', '.PNG', '.JPG', '.JPEG'] + + for klass in classes: + classpath = os.path.join(root, klass) + images = [os.path.join(root, klass, i) for i in os.listdir(classpath) if os.path.splitext(i)[-1] in supported] + every_class_num.append(len(images)) + flist = sorted(os.listdir(classpath)) + desc = f'{klass:23s}' + for f in tqdm(flist, ncols=110, desc=desc, unit='file', colour='blue'): + fpath = os.path.join(classpath, f) + fl = f.lower() + index = fl.rfind('.') + ext = fl[index:] + if ext in supported: + try: + img = cv2.imread(fpath) + filepaths.append(fpath) + labels.append(klass) + except: + bad_images.append(fpath) + print('defective image file: ', fpath) + else: + bad_images.append(fpath) + + Fseries = pd.Series(filepaths, name='filepaths') + Lseries = pd.Series(labels, name='labels') + df = pd.concat([Fseries, Lseries], axis=1) + + print(f'{len(df.labels.unique())} kind of images were found in the datasets') + train_df, test_df = train_test_split(df, train_size=.8, shuffle=True, random_state=123, stratify=df['labels']) + + train_image_path = train_df['filepaths'].tolist() + val_image_path = test_df['filepaths'].tolist() + + train_image_label = [class_indices[i] for i in train_df['labels'].tolist()] + val_image_label = [class_indices[i] for i in test_df['labels'].tolist()] + + sample_df = train_df.sample(n=50, replace=False) + ht, wt, count = 0, 0, 0 + for i in range(len(sample_df)): + fpath = sample_df['filepaths'].iloc[i] + try: + img = cv2.imread(fpath) + h = img.shape[0] + w = img.shape[1] + ht += h + wt += w + count += 1 + except: + pass + have = int(ht / count) + wave = int(wt / count) + aspect_ratio = have / wave + print('{} images were found in the datasets.\n{} for training, {} for validation'.format( + sum(every_class_num), len(train_image_path), len(val_image_path) + )) + print('average image height= ', have, ' average image width= ', wave, ' aspect ratio h/w= ', aspect_ratio) + + if plot_image: + plt.bar(range(len(classes)), every_class_num, align='center') + plt.xticks(range(len(classes)), classes) + + for i, v in enumerate(every_class_num): + plt.text(x=i, y=v + 5, s=str(v), ha='center') + + plt.xlabel('image class') + plt.ylabel('number of images') + + plt.title('class distribution') + plt.show() + + return train_image_path, train_image_label, val_image_path, val_image_label, class_indices \ No newline at end of file diff --git a/datasets/threeaugment.py b/datasets/threeaugment.py new file mode 100644 index 0000000..bdbe813 --- /dev/null +++ b/datasets/threeaugment.py @@ -0,0 +1,117 @@ +""" +3Augment implementation from (https://github.com/facebookresearch/deit/blob/main/augment.py) +Data-augmentation (DA) based on dino DA (https://github.com/facebookresearch/dino) +and timm DA(https://github.com/rwightman/pytorch-image-models) +Can be called by adding "--ThreeAugment" to the command line +""" +import torch +from timm.data.transforms import str_to_pil_interp, RandomResizedCropAndInterpolation +from torchvision import transforms +import random + +from PIL import ImageFilter, ImageOps + + +class GaussianBlur(object): + """ + Apply Gaussian Blur to the PIL image. + """ + + def __init__(self, p=0.1, radius_min=0.1, radius_max=2.): + self.prob = p + self.radius_min = radius_min + self.radius_max = radius_max + + def __call__(self, img): + do_it = random.random() <= self.prob + if not do_it: + return img + + img = img.filter( + ImageFilter.GaussianBlur( + radius=random.uniform(self.radius_min, self.radius_max) + ) + ) + return img + + +class Solarization(object): + """ + Apply Solarization to the PIL image. + """ + + def __init__(self, p=0.2): + self.p = p + + def __call__(self, img): + if random.random() < self.p: + return ImageOps.solarize(img) + else: + return img + + +class gray_scale(object): + """ + Apply Solarization to the PIL image. + """ + + def __init__(self, p=0.2): + self.p = p + self.transf = transforms.Grayscale(3) + + def __call__(self, img): + if random.random() < self.p: + return self.transf(img) + else: + return img + + +class horizontal_flip(object): + """ + Apply Solarization to the PIL image. + """ + + def __init__(self, p=0.2, activate_pred=False): + self.p = p + self.transf = transforms.RandomHorizontalFlip(p=1.0) + + def __call__(self, img): + if random.random() < self.p: + return self.transf(img) + else: + return img + + +def new_data_aug_generator(args=None): + img_size = args.input_size + remove_random_resized_crop = False + mean, std = [0.485, 0.456, 0.406], [0.229, 0.224, 0.225] + primary_tfl = [] + scale = (0.08, 1.0) + interpolation = 'bicubic' + if remove_random_resized_crop: + primary_tfl = [ + transforms.Resize(img_size, interpolation=3), + transforms.RandomCrop(img_size, padding=4, padding_mode='reflect'), + transforms.RandomHorizontalFlip() + ] + else: + primary_tfl = [ + RandomResizedCropAndInterpolation( + img_size, scale=scale, interpolation=interpolation), + transforms.RandomHorizontalFlip() + ] + + secondary_tfl = [transforms.RandomChoice([gray_scale(p=1.0), + Solarization(p=1.0), + GaussianBlur(p=1.0)])] + + if args.color_jitter is not None and not args.color_jitter == 0: + secondary_tfl.append(transforms.ColorJitter(args.color_jitter, args.color_jitter, args.color_jitter)) + final_tfl = [ + transforms.ToTensor(), + transforms.Normalize( + mean=torch.tensor(mean), + std=torch.tensor(std)) + ] + return transforms.Compose(primary_tfl + secondary_tfl + final_tfl) \ No newline at end of file diff --git a/datasets/transforms.py b/datasets/transforms.py new file mode 100644 index 0000000..d2dc8da --- /dev/null +++ b/datasets/transforms.py @@ -0,0 +1,72 @@ +DEFAULT_CROP_PCT = 0.875 + +IMAGENET_DEFAULT_MEAN = (0.485, 0.456, 0.406) +IMAGENET_DEFAULT_STD = (0.229, 0.224, 0.225) +IMAGENET_INCEPTION_MEAN = (0.5, 0.5, 0.5) +IMAGENET_INCEPTION_STD = (0.5, 0.5, 0.5) +IMAGENET_DPN_MEAN = (124 / 255, 117 / 255, 104 / 255) +IMAGENET_DPN_STD = tuple([1 / (.0167 * 255)] * 3) + + +def resolve_data_config(model, args, default_cfg={}, verbose=True): + new_config = {} + default_cfg = default_cfg + if not default_cfg and model is not None and hasattr(model, 'default_cfg'): + default_cfg = model.default_cfg + + # Resolve input/image size + # FIXME grayscale/chans arg to use different # channels? + in_chans = 3 + input_size = (in_chans, 224, 224) + if args.img_size is not None: + # FIXME support passing img_size as tuple, non-square + assert isinstance(args.img_size, int) + input_size = (in_chans, args.img_size, args.img_size) + elif 'input_size' in default_cfg: + input_size = default_cfg['input_size'] + new_config['input_size'] = input_size + + # resolve interpolation method + new_config['interpolation'] = 'bicubic' + if args.interpolation: + new_config['interpolation'] = args.interpolation + elif 'interpolation' in default_cfg: + new_config['interpolation'] = default_cfg['interpolation'] + + # resolve datasets + model mean for normalization + new_config['mean'] = IMAGENET_DEFAULT_MEAN + if args.mean is not None: + mean = tuple(args.mean) + if len(mean) == 1: + mean = tuple(list(mean) * in_chans) + else: + assert len(mean) == in_chans + new_config['mean'] = mean + elif 'mean' in default_cfg: + new_config['mean'] = default_cfg['mean'] + + # resolve datasets + model std deviation for normalization + new_config['std'] = IMAGENET_DEFAULT_STD + if args.std is not None: + std = tuple(args.std) + if len(std) == 1: + std = tuple(list(std) * in_chans) + else: + assert len(std) == in_chans + new_config['std'] = std + elif 'std' in default_cfg: + new_config['std'] = default_cfg['std'] + + # resolve default crop percentage + new_config['crop_pct'] = DEFAULT_CROP_PCT + if args.crop_pct is not None: + new_config['crop_pct'] = args.crop_pct + elif 'crop_pct' in default_cfg: + new_config['crop_pct'] = default_cfg['crop_pct'] + + if verbose: + print('Data processing configuration for current model + datasets:') + for n, v in new_config.items(): + print('\t%s: %s' % (n, str(v))) + + return new_config \ No newline at end of file diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..369119b --- /dev/null +++ b/environment.yml @@ -0,0 +1,23 @@ +name: CV +dependencies: + - python=3.9 + - pip + - pip: + - onnx==1.13 + - onnxoptimizer==0.3.13 + - onnxruntime==1.18 + - numpy==1.23.0 + - opencv-contrib-python==4.7.0.72 + - opencv-python==4.7.0.72 + - openpyxl==3.1.2 + - pandas==1.5.3 + - pillow==9.3.0 + - plotly==5.14.1 + - scikit-learn==1.3.0 + - tensorboardx==2.6.2.2 + - timm==0.9.2 + - torch==1.13.0+cu118 + - torchaudio==0.13.0+cu118 + - torchinfo==1.7.2 + - torchvision==0.14.0+cu118 + - transformers==4.28.1 \ No newline at end of file diff --git a/estimate_model.py b/estimate_model.py new file mode 100644 index 0000000..965aa45 --- /dev/null +++ b/estimate_model.py @@ -0,0 +1,338 @@ +import torch, json, os +import seaborn as sns +from sklearn.metrics import auc, f1_score, roc_curve, classification_report, confusion_matrix, roc_auc_score +from itertools import cycle +from numpy import interp +import numpy as np +import matplotlib.pyplot as plt +from PIL import Image +from torchvision import transforms +from optim_AUC import OptimizeAUC +from terminaltables import AsciiTable +from typing import Iterable + + +@torch.inference_mode() +def Plot_ROC(net: torch.nn.Module, val_loader: Iterable, save_name: str, device: torch.device): + """ + Plot ROC Curve + + Save the roc curve as an image file in the current directory + + Args: + net (torch.nn.Module): The model to be evaluated. + val_loader (Iterable): The data loader for the valid data. + save_name (str): The file path of your model weights + device (torch.device): The device used for training (CPU or GPU). + + Returns: + None + """ + + try: + json_file = open('./classes_indices.json', 'r') + class_indict = json.load(json_file) + except Exception as e: + print(e) + exit(-1) + + score_list = [] + label_list = [] + + net.load_state_dict(torch.load(save_name)['model']) + + for i, data in enumerate(val_loader): + images, labels = data + images, labels = images.to(device), labels.to(device) + outputs = torch.softmax(net(images), dim=1) + score_tmp = outputs + score_list.extend(score_tmp.detach().cpu().numpy()) + label_list.extend(labels.cpu().numpy()) + + score_array = np.array(score_list) + # convert label to one-hot form + label_tensor = torch.tensor(label_list) + label_tensor = label_tensor.reshape((label_tensor.shape[0], 1)) + label_onehot = torch.zeros(label_tensor.shape[0], len(class_indict.keys())) + label_onehot.scatter_(dim=1, index=label_tensor, value=1) + label_onehot = np.array(label_onehot) + + print("score_array:", score_array.shape) # (batchsize, classnum) + print("label_onehot:", label_onehot.shape) # torch.Size([batchsize, classnum]) + + # compute tpr and fpr for each label by using sklearn lib + fpr_dict = dict() + tpr_dict = dict() + roc_auc_dict = dict() + for i in range(len(class_indict.keys())): + fpr_dict[i], tpr_dict[i], _ = roc_curve(label_onehot[:, i], score_array[:, i]) + roc_auc_dict[i] = auc(fpr_dict[i], tpr_dict[i]) + # micro + fpr_dict["micro"], tpr_dict["micro"], _ = roc_curve(label_onehot.ravel(), score_array.ravel()) + roc_auc_dict["micro"] = auc(fpr_dict["micro"], tpr_dict["micro"]) + + # macro + # First aggregate all false positive rates + all_fpr = np.unique(np.concatenate([fpr_dict[i] for i in range(len(class_indict.keys()))])) + # Then interpolate all ROC curves at this points + mean_tpr = np.zeros_like(all_fpr) + + for i in range(len(set(label_list))): + mean_tpr += interp(all_fpr, fpr_dict[i], tpr_dict[i]) + + # Finally average it and compute AUC + mean_tpr /= len(class_indict.keys()) + fpr_dict["macro"] = all_fpr + tpr_dict["macro"] = mean_tpr + roc_auc_dict["macro"] = auc(fpr_dict["macro"], tpr_dict["macro"]) + + # plot roc curve for each label + plt.figure(figsize=(12, 12)) + lw = 2 + + plt.plot(fpr_dict["micro"], tpr_dict["micro"], + label='micro-average ROC curve (area = {0:0.2f})' + ''.format(roc_auc_dict["micro"]), + color='deeppink', linestyle=':', linewidth=4) + + plt.plot(fpr_dict["macro"], tpr_dict["macro"], + label='macro-average ROC curve (area = {0:0.2f})' + ''.format(roc_auc_dict["macro"]), + color='navy', linestyle=':', linewidth=4) + + colors = cycle(['aqua', 'darkorange', 'cornflowerblue']) + for i, color in zip(range(len(class_indict.keys())), colors): + plt.plot(fpr_dict[i], tpr_dict[i], color=color, lw=lw, + label='ROC curve of class {0} (area = {1:0.2f})' + ''.format(class_indict[str(i)], roc_auc_dict[i])) + + plt.plot([0, 1], [0, 1], 'k--', lw=lw, label='Chance', color='red') + plt.xlim([0.0, 1.0]) + plt.ylim([0.0, 1.05]) + plt.xlabel('False Positive Rate') + plt.ylabel('True Positive Rate') + plt.title('Receiver operating characteristic to multi-class') + plt.legend(loc="lower right") + plt.savefig('./multi_classes_roc.png') + # plt.show() + + +@torch.inference_mode() +def predict_single_image(model: torch.nn.Module, device: torch.device): + """ + Predict Single Image. + + Save the prediction as an image file which including pred label and prob in the current directory + + Args: + model (torch.nn.Module): The model to be evaluated. + device (torch.device): The device used for training (CPU or GPU). + + Returns: + None + """ + + data_transform = { + 'train': transforms.Compose([transforms.RandomResizedCrop(224), transforms.ToTensor(), + transforms.RandomHorizontalFlip(), + transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]), + + 'valid': transforms.Compose([transforms.Resize((224, 224)), transforms.CenterCrop(224), + transforms.ToTensor(), + transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]) + } + + img_transform = data_transform['valid'] + + # load image + img_path = "rose.jpg" + assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path) + img = Image.open(img_path) + plt.imshow(img) + # [N, C, H, W] + img = img_transform(img) + # expand batch dimension + img = torch.unsqueeze(img, dim=0) + + # read class_indict + json_path = './classes_indices.json' + assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path) + + with open(json_path, "r") as f: + class_indict = json.load(f) + + # load model weights + + assert os.path.exists('./save/checkpoint.pth'), "weight file dose not exist." + model.load_state_dict(torch.load('./save/checkpoint.pth', map_location=device)['model']) + + model.eval() + # predict class + output = torch.squeeze(model(img.to(device))).cpu() + predict = torch.softmax(output, dim=0) + predict_cla = torch.argmax(predict).numpy() + + print_res = "class: {} prob: {:.3}".format(class_indict[str(predict_cla)], + predict[predict_cla].numpy()) + + plt.title(print_res) + for i in range(len(predict)): + print("class: {:10} prob: {:.3}".format(class_indict[str(i)], + predict[i].numpy())) + plt.savefig(f'./pred_{img_path}') + # plt.show() + + +@torch.inference_mode() +def Predictor(net: torch.nn.Module, test_loader: Iterable, save_name: str, device: torch.device): + """ + Evaluate the performance of the model on the given dataset. + + 1. This function will print the following metrics: + - F1 score + - Confusion matrix + - Classification report + + 2. Save the confusion matrix as an image file in the current directory. + + Args: + net (torch.nn.Module): The model to be evaluated. + test_loader (Iterable): The data loader for the valid data. + save_name (str): The file path of your model weights + device (torch.device): The device used for training (CPU or GPU). + + Returns: + None + """ + + try: + json_file = open('./classes_indices.json', 'r') + class_indict = json.load(json_file) + except Exception as e: + print(e) + exit(-1) + + errors = 0 + y_pred, y_true = [], [] + net.load_state_dict(torch.load(save_name)['model']) + + net.eval() + + for data in test_loader: + images, labels = data + images, labels = images.to(device), labels.to(device) + preds = torch.argmax(torch.softmax(net(images), dim=1), dim=1) + for i in range(len(preds)): + y_pred.append(preds[i].cpu()) + y_true.append(labels[i].cpu()) + + tests = len(y_pred) + for i in range(tests): + pred_index = y_pred[i] + true_index = y_true[i] + if pred_index != true_index: + errors += 1 + + acc = (1 - errors / tests) * 100 + print(f'there were {errors} errors in {tests} tests for an accuracy of {acc:6.2f}%') + + ypred = np.array(y_pred) + ytrue = np.array(y_true) + + f1score = f1_score(ytrue, ypred, average='weighted') * 100 + + print(f'The F1-score was {f1score:.3f}') + class_count = len(list(class_indict.values())) + classes = list(class_indict.values()) + + cm = confusion_matrix(ytrue, ypred) + plt.figure(figsize=(16, 8)) + plt.subplot(1, 2, 1) + sns.heatmap(cm, annot=True, vmin=0, fmt='g', cmap='Blues', cbar=False) + plt.xticks(np.arange(class_count) + .5, classes, rotation=45, fontsize=14) + plt.yticks(np.arange(class_count) + .5, classes, rotation=0, fontsize=14) + plt.xlabel("Predicted", fontsize=14) + plt.ylabel("True", fontsize=14) + plt.title("Confusion Matrix") + + plt.subplot(1, 2, 2) + sns.heatmap(cm / np.sum(cm), annot=True, fmt='.1%') + plt.xticks(np.arange(class_count) + .5, classes, rotation=45, fontsize=14) + plt.yticks(np.arange(class_count) + .5, classes, rotation=0, fontsize=14) + plt.xlabel('Predicted', fontsize=14) + plt.ylabel('True', fontsize=14) + plt.savefig('./confusion_matrix.png') + # plt.show() + + clr = classification_report(y_true, y_pred, target_names=classes, digits=4) + print("Classification Report:\n----------------------\n", clr) + + + +@torch.inference_mode() +def OptAUC(net: torch.nn.Module, val_loader: Iterable, save_name: str, device: torch.device): + """ + Optimize model for improving AUC + + Print a table of initial and optimized AUC and F1-score. + + This function takes the initial and optimized AUC and F1-score, and generates + an ASCII table to display the results. The table will have the following format: + + Optimize Results + +----------------------+----------------------+----------------------+----------------------+ + | Initial AUC | Initial F1-Score | Optimize AUC | Optimize F1-Score | + +----------------------+----------------------+----------------------+----------------------+ + | 0.654321 | 0.654321 | 0.876543 | 0.876543 | + +----------------------+----------------------+----------------------+----------------------+ + + The optimized AUC and F1-score are obtained by using the `OptimizeAUC` class (in ./optim_AUC.py), which + performs optimization on the initial metrics. + + Args: + net (torch.nn.Module): The model to be evaluated. + test_loader (Iterable): The data loader for the valid data. + save_name (str): The file path of your model weights + device (torch.device): The device used for training (CPU or GPU). + + Returns: + None + """ + + score_list = [] + label_list = [] + + net.load_state_dict(torch.load(save_name)['model']) + + for i, data in enumerate(val_loader): + images, labels = data + images, labels = images.to(device), labels.to(device) + outputs = torch.softmax(net(images), dim=1) + score_tmp = outputs + score_list.extend(score_tmp.detach().cpu().numpy()) + label_list.extend(labels.detach().cpu().numpy()) + + score_array = np.array(score_list) + label_list = np.array(label_list) + y_preds = np.argmax(score_array, axis=1) + f1score = f1_score(label_list, y_preds, average='weighted') * 100 + auc_score = roc_auc_score(label_list, score_array, average='weighted', multi_class='ovo') + + opt_auc = OptimizeAUC() + opt_auc.fit(score_array, label_list) + opt_preds = opt_auc.predict(score_array) + opt_y_preds = np.argmax(opt_preds, axis=1) + opt_f1score = f1_score(label_list, opt_y_preds, average='weighted') * 100 + opt_auc_score = roc_auc_score(label_list, opt_preds, average='weighted', multi_class='ovo') + + TITLE = 'Optimize Results' + TABLE_DATA = ( + ('Initial AUC', 'Initial F1-Score', 'Optimize AUC', 'Optimize F1-Score'), + ('{:.6f}'.format(auc_score), + '{:.6f}'.format(f1score), + '{:.6f}'.format(opt_auc_score), + '{:.6f}'.format(opt_f1score) + ), + ) + table_instance = AsciiTable(TABLE_DATA, TITLE) + print(table_instance.table) \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..745bc14 --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,2 @@ +from .build_models import mamba_vision_T, mamba_vision_T2, mamba_vision_S, \ + mamba_vision_B, mamba_vision_L, mamba_vision_L2 \ No newline at end of file diff --git a/models/build_models.py b/models/build_models.py new file mode 100644 index 0000000..d1b95ae --- /dev/null +++ b/models/build_models.py @@ -0,0 +1,864 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + + +import torch +import torch.nn as nn +from timm.models import register_model +import math +from timm.models.layers import trunc_normal_, DropPath, LayerNorm2d +# from timm.models._builder import resolve_pretrained_cfg, _update_default_kwargs +from timm.models.vision_transformer import Mlp, PatchEmbed +import torch.nn.functional as F +from mamba_ssm.ops.selective_scan_interface import selective_scan_fn +from einops import rearrange, repeat +from pathlib import Path + + +def _cfg(url='', **kwargs): + return {'url': url, + 'num_classes': 1000, + 'input_size': (3, 224, 224), + 'pool_size': None, + 'crop_pct': 0.875, + 'interpolation': 'bicubic', + 'fixed_input_size': True, + 'mean': (0.485, 0.456, 0.406), + 'std': (0.229, 0.224, 0.225), + **kwargs + } + + +default_cfgs = { + 'mamba_vision_T': _cfg(url='https://huggingface.co/ahatamiz/mambavision/resolve/main/mambavision_tiny_1k.pth.tar', + crop_pct=1.0, + input_size=(3, 224, 224), + crop_mode='center'), + 'mamba_vision_T2': _cfg(url='https://huggingface.co/ahatamiz/mambavision/resolve/main/mambavision_tiny2_1k.pth.tar', + crop_pct=0.98, + input_size=(3, 224, 224), + crop_mode='center'), + 'mamba_vision_S': _cfg(url='https://huggingface.co/ahatamiz/mambavision/resolve/main/mambavision_small_1k.pth.tar', + crop_pct=0.93, + input_size=(3, 224, 224), + crop_mode='center'), + 'mamba_vision_B': _cfg(url='https://huggingface.co/ahatamiz/mambavision/resolve/main/mambavision_base_1k.pth.tar', + crop_pct=1.0, + input_size=(3, 224, 224), + crop_mode='center'), + 'mamba_vision_L': _cfg(url='https://huggingface.co/ahatamiz/mambavision/resolve/main/mambavision_large_1k.pth.tar', + crop_pct=1.0, + input_size=(3, 224, 224), + crop_mode='center'), + 'mamba_vision_L2': _cfg( + url='https://huggingface.co/ahatamiz/mambavision/resolve/main/mambavision_large2_1k.pth.tar', + crop_pct=1.0, + input_size=(3, 224, 224), + crop_mode='center') +} + + +def window_partition(x, window_size): + """ + Args: + x: (B, C, H, W) + window_size: window size + h_w: Height of window + w_w: Width of window + Returns: + local window features (num_windows*B, window_size*window_size, C) + """ + B, C, H, W = x.shape + x = x.view(B, C, H // window_size, window_size, W // window_size, window_size) + windows = x.permute(0, 2, 4, 3, 5, 1).reshape(-1, window_size * window_size, C) + return windows + + +def window_reverse(windows, window_size, H, W): + """ + Args: + windows: local window features (num_windows*B, window_size, window_size, C) + window_size: Window size + H: Height of image + W: Width of image + Returns: + x: (B, C, H, W) + """ + B = int(windows.shape[0] / (H * W / window_size / window_size)) + x = windows.reshape(B, H // window_size, W // window_size, window_size, window_size, -1) + x = x.permute(0, 5, 1, 3, 2, 4).reshape(B, windows.shape[2], H, W) + return x + + +def _load_state_dict(module, state_dict, strict=False, logger=None): + """Load state_dict to a module. + + This method is modified from :meth:`torch.nn.Module.load_state_dict`. + Default value for ``strict`` is set to ``False`` and the message for + param mismatch will be shown even if strict is False. + + Args: + module (Module): Module that receives the state_dict. + state_dict (OrderedDict): Weights. + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. + logger (:obj:`logging.Logger`, optional): Logger to log the error + message. If not specified, print function will be used. + """ + unexpected_keys = [] + all_missing_keys = [] + err_msg = [] + + metadata = getattr(state_dict, '_metadata', None) + state_dict = state_dict.copy() + if metadata is not None: + state_dict._metadata = metadata + + def load(module, prefix=''): + local_metadata = {} if metadata is None else metadata.get( + prefix[:-1], {}) + module._load_from_state_dict(state_dict, prefix, local_metadata, True, + all_missing_keys, unexpected_keys, + err_msg) + for name, child in module._modules.items(): + if child is not None: + load(child, prefix + name + '.') + + load(module) + load = None + missing_keys = [ + key for key in all_missing_keys if 'num_batches_tracked' not in key + ] + + if unexpected_keys: + err_msg.append('unexpected key in source ' + f'state_dict: {", ".join(unexpected_keys)}\n') + if missing_keys: + err_msg.append( + f'missing keys in source state_dict: {", ".join(missing_keys)}\n') + + if len(err_msg) > 0: + err_msg.insert( + 0, 'The model and loaded state dict do not match exactly\n') + err_msg = '\n'.join(err_msg) + if strict: + raise RuntimeError(err_msg) + elif logger is not None: + logger.warning(err_msg) + else: + print(err_msg) + + +def _load_checkpoint(model, + filename, + map_location='cpu', + strict=False, + logger=None): + """Load checkpoint from a file or URI. + + Args: + model (Module): Module to load checkpoint. + filename (str): Accept local filepath, URL, ``torchvision://xxx``, + ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for + details. + map_location (str): Same as :func:`torch.load`. + strict (bool): Whether to allow different params for the model and + checkpoint. + logger (:mod:`logging.Logger` or None): The logger for error message. + + Returns: + dict or OrderedDict: The loaded checkpoint. + """ + checkpoint = torch.load(filename, map_location=map_location) + if not isinstance(checkpoint, dict): + raise RuntimeError( + f'No state_dict found in checkpoint file {filename}') + if 'state_dict' in checkpoint: + state_dict = checkpoint['state_dict'] + elif 'model' in checkpoint: + state_dict = checkpoint['model'] + else: + state_dict = checkpoint + if list(state_dict.keys())[0].startswith('module.'): + state_dict = {k[7:]: v for k, v in state_dict.items()} + + if sorted(list(state_dict.keys()))[0].startswith('encoder'): + state_dict = {k.replace('encoder.', ''): v for k, v in state_dict.items() if k.startswith('encoder.')} + + _load_state_dict(model, state_dict, strict, logger) + return checkpoint + + +class Downsample(nn.Module): + """ + Down-sampling block" + """ + + def __init__(self, + dim, + keep_dim=False, + ): + """ + Args: + dim: feature size dimension. + norm_layer: normalization layer. + keep_dim: bool argument for maintaining the resolution. + """ + + super().__init__() + if keep_dim: + dim_out = dim + else: + dim_out = 2 * dim + self.reduction = nn.Sequential( + nn.Conv2d(dim, dim_out, 3, 2, 1, bias=False), + ) + + def forward(self, x): + x = self.reduction(x) + return x + + +class PatchEmbed(nn.Module): + """ + Patch embedding block" + """ + + def __init__(self, in_chans=3, in_dim=64, dim=96): + """ + Args: + in_chans: number of input channels. + dim: feature size dimension. + """ + # in_dim = 1 + super().__init__() + self.proj = nn.Identity() + self.conv_down = nn.Sequential( + nn.Conv2d(in_chans, in_dim, 3, 2, 1, bias=False), + nn.BatchNorm2d(in_dim, eps=1e-4), + nn.ReLU(), + nn.Conv2d(in_dim, dim, 3, 2, 1, bias=False), + nn.BatchNorm2d(dim, eps=1e-4), + nn.ReLU() + ) + + def forward(self, x): + x = self.proj(x) + x = self.conv_down(x) + return x + + +class ConvBlock(nn.Module): + + def __init__(self, dim, + drop_path=0., + layer_scale=None, + kernel_size=3): + super().__init__() + + self.conv1 = nn.Conv2d(dim, dim, kernel_size=kernel_size, stride=1, padding=1) + self.norm1 = nn.BatchNorm2d(dim, eps=1e-5) + self.act1 = nn.GELU(approximate='tanh') + self.conv2 = nn.Conv2d(dim, dim, kernel_size=kernel_size, stride=1, padding=1) + self.norm2 = nn.BatchNorm2d(dim, eps=1e-5) + self.layer_scale = layer_scale + if layer_scale is not None and type(layer_scale) in [int, float]: + self.gamma = nn.Parameter(layer_scale * torch.ones(dim)) + self.layer_scale = True + else: + self.layer_scale = False + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + + def forward(self, x): + input = x + x = self.conv1(x) + x = self.norm1(x) + x = self.act1(x) + x = self.conv2(x) + x = self.norm2(x) + if self.layer_scale: + x = x * self.gamma.view(1, -1, 1, 1) + x = input + self.drop_path(x) + return x + + +class MambaVisionMixer(nn.Module): + def __init__( + self, + d_model, + d_state=16, + d_conv=4, + expand=2, + dt_rank="auto", + dt_min=0.001, + dt_max=0.1, + dt_init="random", + dt_scale=1.0, + dt_init_floor=1e-4, + conv_bias=True, + bias=False, + use_fast_path=True, + layer_idx=None, + device=None, + dtype=None, + ): + factory_kwargs = {"device": device, "dtype": dtype} + super().__init__() + self.d_model = d_model + self.d_state = d_state + self.d_conv = d_conv + self.expand = expand + self.d_inner = int(self.expand * self.d_model) + self.dt_rank = math.ceil(self.d_model / 16) if dt_rank == "auto" else dt_rank + self.use_fast_path = use_fast_path + self.layer_idx = layer_idx + self.in_proj = nn.Linear(self.d_model, self.d_inner, bias=bias, **factory_kwargs) + self.x_proj = nn.Linear( + self.d_inner // 2, self.dt_rank + self.d_state * 2, bias=False, **factory_kwargs + ) + self.dt_proj = nn.Linear(self.dt_rank, self.d_inner // 2, bias=True, **factory_kwargs) + dt_init_std = self.dt_rank ** -0.5 * dt_scale + if dt_init == "constant": + nn.init.constant_(self.dt_proj.weight, dt_init_std) + elif dt_init == "random": + nn.init.uniform_(self.dt_proj.weight, -dt_init_std, dt_init_std) + else: + raise NotImplementedError + dt = torch.exp( + torch.rand(self.d_inner // 2, **factory_kwargs) * (math.log(dt_max) - math.log(dt_min)) + + math.log(dt_min) + ).clamp(min=dt_init_floor) + inv_dt = dt + torch.log(-torch.expm1(-dt)) + with torch.no_grad(): + self.dt_proj.bias.copy_(inv_dt) + self.dt_proj.bias._no_reinit = True + A = repeat( + torch.arange(1, self.d_state + 1, dtype=torch.float32, device=device), + "n -> d n", + d=self.d_inner // 2, + ).contiguous() + A_log = torch.log(A) + self.A_log = nn.Parameter(A_log) + self.A_log._no_weight_decay = True + self.D = nn.Parameter(torch.ones(self.d_inner // 2, device=device)) + self.D._no_weight_decay = True + self.out_proj = nn.Linear(self.d_inner, self.d_model, bias=bias, **factory_kwargs) + self.conv1d_x = nn.Conv1d( + in_channels=self.d_inner // 2, + out_channels=self.d_inner // 2, + bias=conv_bias // 2, + kernel_size=d_conv, + groups=self.d_inner // 2, + **factory_kwargs, + ) + self.conv1d_z = nn.Conv1d( + in_channels=self.d_inner // 2, + out_channels=self.d_inner // 2, + bias=conv_bias // 2, + kernel_size=d_conv, + groups=self.d_inner // 2, + **factory_kwargs, + ) + + def forward(self, hidden_states): + """ + hidden_states: (B, L, D) + Returns: same shape as hidden_states + """ + _, seqlen, _ = hidden_states.shape + xz = self.in_proj(hidden_states) + xz = rearrange(xz, "b l d -> b d l") + x, z = xz.chunk(2, dim=1) + A = -torch.exp(self.A_log.float()) + x = F.silu(F.conv1d(input=x, weight=self.conv1d_x.weight, bias=self.conv1d_x.bias, padding='same', + groups=self.d_inner // 2)) + z = F.silu(F.conv1d(input=z, weight=self.conv1d_z.weight, bias=self.conv1d_z.bias, padding='same', + groups=self.d_inner // 2)) + x_dbl = self.x_proj(rearrange(x, "b d l -> (b l) d")) + dt, B, C = torch.split(x_dbl, [self.dt_rank, self.d_state, self.d_state], dim=-1) + dt = rearrange(self.dt_proj(dt), "(b l) d -> b d l", l=seqlen) + B = rearrange(B, "(b l) dstate -> b dstate l", l=seqlen).contiguous() + C = rearrange(C, "(b l) dstate -> b dstate l", l=seqlen).contiguous() + y = selective_scan_fn(x, + dt, + A, + B, + C, + self.D.float(), + z=None, + delta_bias=self.dt_proj.bias.float(), + delta_softplus=True, + return_last_state=None) + + y = torch.cat([y, z], dim=1) + y = rearrange(y, "b d l -> b l d") + out = self.out_proj(y) + return out + + +class Attention(nn.Module): + + def __init__( + self, + dim, + num_heads=8, + qkv_bias=False, + qk_norm=False, + attn_drop=0., + proj_drop=0., + norm_layer=nn.LayerNorm, + ): + super().__init__() + assert dim % num_heads == 0 + self.num_heads = num_heads + self.head_dim = dim // num_heads + self.scale = self.head_dim ** -0.5 + self.fused_attn = True + + self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) + self.q_norm = norm_layer(self.head_dim) if qk_norm else nn.Identity() + self.k_norm = norm_layer(self.head_dim) if qk_norm else nn.Identity() + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + + def forward(self, x): + B, N, C = x.shape + qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4) + q, k, v = qkv.unbind(0) + q, k = self.q_norm(q), self.k_norm(k) + + if self.fused_attn: + from models.helpers import scaled_dot_product_attention + x = scaled_dot_product_attention( + q, k, v, + dropout_p=self.attn_drop.p + ) + else: + q = q * self.scale + attn = q @ k.transpose(-2, -1) + attn = attn.softmax(dim=-1) + attn = self.attn_drop(attn) + x = attn @ v + + x = x.transpose(1, 2).reshape(B, N, C) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Block(nn.Module): + def __init__(self, + dim, + num_heads, + counter, + transformer_blocks, + mlp_ratio=4., + qkv_bias=False, + qk_scale=False, + drop=0., + attn_drop=0., + drop_path=0., + act_layer=nn.GELU, + norm_layer=nn.LayerNorm, + Mlp_block=Mlp, + layer_scale=None, + window_size=0): + super().__init__() + self.norm1 = norm_layer(dim) + if counter in transformer_blocks: + self.mixer = Attention( + dim, + num_heads=num_heads, + qkv_bias=qkv_bias, + qk_norm=qk_scale, + attn_drop=attn_drop, + proj_drop=drop, + norm_layer=norm_layer, + ) + else: + self.mixer = MambaVisionMixer(d_model=dim, + d_state=8, + d_conv=3, + expand=1 + ) + + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp_block(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) + use_layer_scale = layer_scale is not None and type(layer_scale) in [int, float] + self.gamma_1 = nn.Parameter(layer_scale * torch.ones(dim)) if use_layer_scale else 1 + self.gamma_2 = nn.Parameter(layer_scale * torch.ones(dim)) if use_layer_scale else 1 + + def forward(self, x): + x = x + self.drop_path(self.gamma_1 * self.mixer(self.norm1(x))) + x = x + self.drop_path(self.gamma_2 * self.mlp(self.norm2(x))) + return x + + +class MambaVisionLayer(nn.Module): + """ + MambaVision layer" + """ + + def __init__(self, + dim, + depth, + num_heads, + window_size, + conv=False, + downsample=True, + mlp_ratio=4., + qkv_bias=True, + qk_scale=None, + drop=0., + attn_drop=0., + drop_path=0., + layer_scale=None, + layer_scale_conv=None, + transformer_blocks=[], + ): + """ + Args: + dim: feature size dimension. + depth: number of layers in each stage. + window_size: window size in each stage. + downsample: bool argument for down-sampling. + mlp_ratio: MLP ratio. + num_heads: number of heads in each stage. + qkv_bias: bool argument for query, key, value learnable bias. + qk_scale: bool argument to scaling query, key. + drop: dropout rate. + attn_drop: attention dropout rate. + drop_path: drop path rate. + norm_layer: normalization layer. + layer_scale: layer scaling coefficient. + layer_scale_conv: conv layer scaling coefficient. + transformer_blocks: list of transformer blocks. + """ + + super().__init__() + self.conv = conv + self.transformer_block = False + if conv: + self.blocks = nn.ModuleList([ + ConvBlock(dim=dim, + drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path, + layer_scale=layer_scale_conv) + for i in range(depth)]) + self.transformer_block = False + else: + self.transformer_block = True + self.blocks = nn.ModuleList([ + Block(dim=dim, counter=i, transformer_blocks=transformer_blocks, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop, + attn_drop=attn_drop, + drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path, + window_size=window_size, + layer_scale=layer_scale, + ) + for i in range(depth)]) + self.transformer_block = True + + self.downsample = None if not downsample else Downsample(dim=dim) + self.do_gt = False + self.window_size = window_size + + def forward(self, x): + B, C, H, W = x.shape + if self.transformer_block: + x = window_partition(x, self.window_size) + for _, blk in enumerate(self.blocks): + x = blk(x) + if self.transformer_block: + x = window_reverse(x, self.window_size, H, W) + if self.downsample is None: + return x + return self.downsample(x) + + +class MambaVision(nn.Module): + """ + MambaVision, + """ + + def __init__(self, + dim, + in_dim, + depths, + window_size, + mlp_ratio, + num_heads, + resolution=224, + drop_path_rate=0.2, + in_chans=3, + num_classes=1000, + qkv_bias=True, + qk_scale=None, + drop_rate=0., + attn_drop_rate=0., + layer_scale=None, + layer_scale_conv=None, + **kwargs): + """ + Args: + dim: feature size dimension. + depths: number of layers in each stage. + window_size: window size in each stage. + mlp_ratio: MLP ratio. + num_heads: number of heads in each stage. + resolution: input image resolution. + drop_path_rate: drop path rate. + in_chans: number of input channels. + num_classes: number of classes. + qkv_bias: bool argument for query, key, value learnable bias. + qk_scale: bool argument to scaling query, key. + drop_rate: dropout rate. + attn_drop_rate: attention dropout rate. + norm_layer: normalization layer. + layer_scale: layer scaling coefficient. + layer_scale_conv: conv layer scaling coefficient. + """ + super().__init__() + + num_features = int(dim * 2 ** (len(depths) - 1)) + self.num_classes = num_classes + self.patch_embed = PatchEmbed(in_chans=in_chans, in_dim=in_dim, dim=dim) + dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] + self.levels = nn.ModuleList() + for i in range(len(depths)): + conv = True if (i == 0 or i == 1) else False + level = MambaVisionLayer(dim=int(dim * 2 ** i), + depth=depths[i], + num_heads=num_heads[i], + window_size=window_size[i], + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + conv=conv, + drop=drop_rate, + attn_drop=attn_drop_rate, + drop_path=dpr[sum(depths[:i]):sum(depths[:i + 1])], + downsample=(i < 3), + layer_scale=layer_scale, + layer_scale_conv=layer_scale_conv, + transformer_blocks=list(range(depths[i] // 2 + 1, depths[i])) if depths[ + i] % 2 != 0 else list( + range(depths[i] // 2, depths[i])) + ) + + self.levels.append(level) + + self.norm = nn.BatchNorm2d(num_features) + self.avgpool = nn.AdaptiveAvgPool2d(1) + self.head = nn.Linear(num_features, num_classes) if num_classes > 0 else nn.Identity() + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight, std=.02) + if isinstance(m, nn.Linear) and m.bias is not None: + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.LayerNorm): + nn.init.constant_(m.bias, 0) + nn.init.constant_(m.weight, 1.0) + elif isinstance(m, LayerNorm2d): + nn.init.constant_(m.bias, 0) + nn.init.constant_(m.weight, 1.0) + elif isinstance(m, nn.BatchNorm2d): + nn.init.ones_(m.weight) + nn.init.zeros_(m.bias) + + @torch.jit.ignore + def no_weight_decay_keywords(self): + return {'rpb'} + + def forward_features(self, x): + x = self.patch_embed(x) + for level in self.levels: + x = level(x) + x = self.norm(x) + x = self.avgpool(x) + x = torch.flatten(x, 1) + return x + + def forward(self, x): + x = self.forward_features(x) + x = self.head(x) + return x + + def _load_state_dict(self, + pretrained, + strict: bool = False): + _load_checkpoint(self, + pretrained, + strict=strict) + + + +@register_model +def mamba_vision_T(pretrained=False, pretrained_cfg=None, pretrained_cfg_overlay=None, **kwargs): + model_path = kwargs.pop("model_path", "/tmp/mamba_vision_T.pth.tar") + # pretrained_cfg = resolve_pretrained_cfg('mamba_vision_T').to_dict() + # _update_default_kwargs(pretrained_cfg, kwargs, kwargs_filter=None) + model = MambaVision(depths=[1, 3, 8, 4], + num_heads=[2, 4, 8, 16], + window_size=[8, 8, 14, 7], + dim=80, + in_dim=32, + mlp_ratio=4, + resolution=224, + drop_path_rate=0.2, + **kwargs) + model.pretrained_cfg = pretrained_cfg + model.default_cfg = model.pretrained_cfg + if pretrained: + if not Path(model_path).is_file(): + url = model.default_cfg['url'] + torch.hub.download_url_to_file(url=url, dst=model_path) + model._load_state_dict(model_path) + return model + + + +@register_model +def mamba_vision_T2(pretrained=False, pretrained_cfg=None, pretrained_cfg_overlay=None, **kwargs): + model_path = kwargs.pop("model_path", "/tmp/mamba_vision_T2.pth.tar") + # pretrained_cfg = resolve_pretrained_cfg('mamba_vision_T2').to_dict() + # _update_default_kwargs(pretrained_cfg, kwargs, kwargs_filter=None) + model = MambaVision(depths=[1, 3, 11, 4], + num_heads=[2, 4, 8, 16], + window_size=[8, 8, 14, 7], + dim=80, + in_dim=32, + mlp_ratio=4, + resolution=224, + drop_path_rate=0.2, + **kwargs) + model.pretrained_cfg = pretrained_cfg + model.default_cfg = model.pretrained_cfg + if pretrained: + if not Path(model_path).is_file(): + url = model.default_cfg['url'] + torch.hub.download_url_to_file(url=url, dst=model_path) + model._load_state_dict(model_path) + return model + + + +@register_model +def mamba_vision_S(pretrained=False, pretrained_cfg=None, pretrained_cfg_overlay=None, **kwargs): + model_path = kwargs.pop("model_path", "/tmp/mamba_vision_S.pth.tar") + # pretrained_cfg = resolve_pretrained_cfg('mamba_vision_S').to_dict() + # _update_default_kwargs(pretrained_cfg, kwargs, kwargs_filter=None) + model = MambaVision(depths=[3, 3, 7, 5], + num_heads=[2, 4, 8, 16], + window_size=[8, 8, 14, 7], + dim=96, + in_dim=64, + mlp_ratio=4, + resolution=224, + drop_path_rate=0.2, + **kwargs) + model.pretrained_cfg = pretrained_cfg + model.default_cfg = model.pretrained_cfg + if pretrained: + if not Path(model_path).is_file(): + url = model.default_cfg['url'] + torch.hub.download_url_to_file(url=url, dst=model_path) + model._load_state_dict(model_path) + return model + + + +@register_model +def mamba_vision_B(pretrained=False, pretrained_cfg=None, pretrained_cfg_overlay=None, **kwargs): + model_path = kwargs.pop("model_path", "/tmp/mamba_vision_B.pth.tar") + # pretrained_cfg = resolve_pretrained_cfg('mamba_vision_B').to_dict() + # _update_default_kwargs(pretrained_cfg, kwargs, kwargs_filter=None) + model = MambaVision(depths=[3, 3, 10, 5], + num_heads=[2, 4, 8, 16], + window_size=[8, 8, 14, 7], + dim=128, + in_dim=64, + mlp_ratio=4, + resolution=224, + drop_path_rate=0.3, + layer_scale=1e-5, + layer_scale_conv=None, + **kwargs) + model.pretrained_cfg = pretrained_cfg + model.default_cfg = model.pretrained_cfg + if pretrained: + if not Path(model_path).is_file(): + url = model.default_cfg['url'] + torch.hub.download_url_to_file(url=url, dst=model_path) + model._load_state_dict(model_path) + return model + + + +@register_model +def mamba_vision_L(pretrained=False, pretrained_cfg=None, pretrained_cfg_overlay=None, **kwargs): + model_path = kwargs.pop("model_path", "/tmp/mamba_vision_L.pth.tar") + # pretrained_cfg = resolve_pretrained_cfg('mamba_vision_L').to_dict() + # _update_default_kwargs(pretrained_cfg, kwargs, kwargs_filter=None) + model = MambaVision(depths=[3, 3, 10, 5], + num_heads=[4, 8, 16, 32], + window_size=[8, 8, 14, 7], + dim=196, + in_dim=64, + mlp_ratio=4, + resolution=224, + drop_path_rate=0.3, + layer_scale=1e-5, + layer_scale_conv=None, + **kwargs) + model.pretrained_cfg = pretrained_cfg + model.default_cfg = model.pretrained_cfg + if pretrained: + if not Path(model_path).is_file(): + url = model.default_cfg['url'] + torch.hub.download_url_to_file(url=url, dst=model_path) + model._load_state_dict(model_path) + return model + + + +@register_model +def mamba_vision_L2(pretrained=False, pretrained_cfg=None, pretrained_cfg_overlay=None, **kwargs): + model_path = kwargs.pop("model_path", "/tmp/mamba_vision_L2.pth.tar") + # pretrained_cfg = resolve_pretrained_cfg('mamba_vision_L2').to_dict() + # _update_default_kwargs(pretrained_cfg, kwargs, kwargs_filter=None) + model = MambaVision(depths=[3, 3, 12, 5], + num_heads=[4, 8, 16, 32], + window_size=[8, 8, 14, 7], + dim=196, + in_dim=64, + mlp_ratio=4, + resolution=224, + drop_path_rate=0.3, + layer_scale=1e-5, + layer_scale_conv=None, + **kwargs) + model.pretrained_cfg = pretrained_cfg + model.default_cfg = model.pretrained_cfg + if pretrained: + if not Path(model_path).is_file(): + url = model.default_cfg['url'] + torch.hub.download_url_to_file(url=url, dst=model_path) + model._load_state_dict(model_path) + return model \ No newline at end of file diff --git a/models/helpers.py b/models/helpers.py new file mode 100644 index 0000000..20333a6 --- /dev/null +++ b/models/helpers.py @@ -0,0 +1,62 @@ +""" +This function can be found in torch.nn.Functional.scaled_dot_product_attention if torch version >= 2.0.X. + +Due to my conda virtual environment (torch==1.13.0+cu118), I wrote this function to compute scaled dot product attention \ +for using build attention blocks in build_models.py(line 440). +""" +import torch +import math +from typing import Optional + + +def scaled_dot_product_attention(query: torch.Tensor, + key: torch.Tensor, + value: torch.Tensor, + attn_mask: Optional[torch.Tensor] = None, + dropout_p: float = 0.0, + is_causal=False, + scale: Optional[float] = None, + device: Optional[str] = 'cuda') -> torch.Tensor: + """ + Computes scaled dot product attention on query, key and value tensors, using an optional attention mask if passed, + and applying dropout if a probability greater than 0.0 is specified. + The optional scale argument can only be specified as a keyword argument. + + + Args: + query (torch.Tensor): The query tensor of shape [batch_size, num_heads, sequence_length, d_model//num_heads]. + key (torch.Tensor): The key tensor of shape [batch_size, num_heads, sequence_length, d_model//num_heads]. + value (torch.Tensor): The value tensor of shape [batch_size, num_heads, sequence_length, d_model//num_heads]. + attn_mask (torch.Tensor, optional): The attention mask tensor, used to prevent attention from being computed on + unwanted locations. Can be a boolean tensor or a float tensor. Default is None. + dropout_p (float, optional): Dropout probability; if greater than 0.0, dropout is applied + is_causal (bool, optional): If True, the attention mask is set to be a causal mask, where the attention weights + are masked to prevent attending to future positions. Default is False. + scale (float, optional): The scaling factor for the attention weights. If None, the scaling factor is set to + 1/sqrt(d_model//num_heads). Default is None. + device (str, optional): The device to move the tensors to. Can be 'cpu' or 'cuda'. Default is 'cuda' + + Returns: + torch.Tensor: The output tensor of shape [batch_size, num_heads, sequence_length, d_model//num_heads] + """ + + L, S = query.size(-2), key.size(-2) + scale_factor = 1 / math.sqrt(query.size(-1)) if scale is None else scale + attn_bias = torch.zeros(L, S, dtype=query.dtype).to(device) + + if is_causal: + assert attn_mask is None + temp_mask = torch.ones(L, S, dtype=torch.bool).tril(diagonal=0).to(device) + attn_bias.masked_fill_(temp_mask.logical_not(), float("-inf")) + attn_bias.to(query.dtype) + + if attn_mask is not None: + if attn_mask.dtype == torch.bool: + attn_bias.masked_fill_(attn_mask.logical_not().to(device), float("-inf")) + else: + attn_bias += attn_mask.to(device) + attn_weight = query @ key.transpose(-2, -1).to(device) * scale_factor + attn_weight += attn_bias + attn_weight = torch.softmax(attn_weight, dim=-1) + attn_weight = torch.dropout(attn_weight.to(device), dropout_p, train=True) + return attn_weight @ value diff --git a/optim_AUC.py b/optim_AUC.py new file mode 100644 index 0000000..fc1d4f7 --- /dev/null +++ b/optim_AUC.py @@ -0,0 +1,67 @@ +import numpy as np +from functools import partial +from scipy.optimize import fmin +from sklearn import metrics + + +def max_voting(preds): + """ + Create mean predictions + :param probas: 2-d array of prediction values + :return: max voted predictions + """ + + ''' + preds: np.array([[0, 2, 2, 2], [1, 1, 0, 1]]) + return : [[2] + [1]] + ''' + idxs = np.argmax(preds, axis=1) + return np.take_along_axis(preds, idxs[:, None], axis=1) + + +class OptimizeAUC: + def __init__(self): + self.coef_ = 0. + + def _auc(self, coef, outputs, labels): + """ + This functions calulates and returns AUC. + :param coef: coef list, of the same length as number of models + :param X: predictions, in this case a 2d array + :param y: targets, in our case binary 1d array + """ + # multiply coefficients with every column of the array + # with predictions. + # this means: element 1 of coef is multiplied by column 1 + # of the prediction array, element 2 of coef is multiplied + # by column 2 of the prediction array and so on! + + x_coef = coef * outputs + + # create predictions by taking row wise sum + predictions = x_coef / np.sum(x_coef, axis=1, keepdims=True) + + # calculate auc score + auc_score = metrics.roc_auc_score(labels, predictions, average='weighted', multi_class='ovo') + + # return negative auc + return -1.0 * auc_score + + + def fit(self, X, y): + # remember partial from hyperparameter optimization chapter? + loss_partial = partial(self._auc, outputs=X, labels=y) + + # dirichlet distribution. you can use any distribution you want + # to initialize the coefficients + # we want the coefficients to sum to 1 + initial_coef = np.random.dirichlet(np.ones(X.shape[1]), size=1) + # use scipy fmin to minimize the loss function, in our case auc + self.coef_ = fmin(loss_partial, initial_coef, disp=True) + + def predict(self, X): + # this is similar to _auc function + x_coef = X * self.coef_ + predictions = x_coef / np.sum(x_coef, axis=1, keepdims=True) + return predictions \ No newline at end of file diff --git a/sample_png/1720946952217.jpg b/sample_png/1720946952217.jpg new file mode 100644 index 0000000000000000000000000000000000000000..df6bcbc35be5e7f5a1d42765c737871e3df91da6 GIT binary patch literal 84433 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%0D={4NhAz_+7+!jhpFsWj)!9;;X6Z86^Q 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 WIk{}e{&NNR($zH5sCi%?`9A<@Pgafq literal 0 HcmV?d00001 diff --git a/scheduler/__init__.py b/scheduler/__init__.py new file mode 100644 index 0000000..9917b4a --- /dev/null +++ b/scheduler/__init__.py @@ -0,0 +1 @@ +from .scheduler_factory import * \ No newline at end of file diff --git a/scheduler/cosine_lr.py b/scheduler/cosine_lr.py new file mode 100644 index 0000000..c6aa66c --- /dev/null +++ b/scheduler/cosine_lr.py @@ -0,0 +1,119 @@ +""" Cosine Scheduler + +Cosine LR schedule with warmup, cycle/restarts, noise, k-decay. + +Hacked together by / Copyright 2021 Ross Wightman +""" +import logging +import math +import numpy as np +import torch + +from .scheduler_main import Scheduler + + +_logger = logging.getLogger(__name__) + + +class CosineLRScheduler(Scheduler): + """ + Cosine decay with restarts. + This is described in the paper https://arxiv.org/abs/1608.03983. + + Inspiration from + https://github.com/allenai/allennlp/blob/master/allennlp/training/learning_rate_schedulers/cosine.py + + k-decay option based on `k-decay: A New Method For Learning Rate Schedule` - https://arxiv.org/abs/2004.05909 + """ + + def __init__(self, + optimizer: torch.optim.Optimizer, + t_initial: int, + lr_min: float = 0., + cycle_mul: float = 1., + cycle_decay: float = 1., + cycle_limit: int = 1, + warmup_t=0, + warmup_lr_init=0, + warmup_prefix=False, + t_in_epochs=True, + noise_range_t=None, + noise_pct=0.67, + noise_std=1.0, + noise_seed=42, + k_decay=1.0, + initialize=True) -> None: + super().__init__( + optimizer, param_group_field="lr", + noise_range_t=noise_range_t, noise_pct=noise_pct, noise_std=noise_std, noise_seed=noise_seed, + initialize=initialize) + + assert t_initial > 0 + assert lr_min >= 0 + if t_initial == 1 and cycle_mul == 1 and cycle_decay == 1: + _logger.warning("Cosine annealing scheduler will have no effect on the learning " + "rate since t_initial = t_mul = eta_mul = 1.") + self.t_initial = t_initial + self.lr_min = lr_min + self.cycle_mul = cycle_mul + self.cycle_decay = cycle_decay + self.cycle_limit = cycle_limit + self.warmup_t = warmup_t + self.warmup_lr_init = warmup_lr_init + self.warmup_prefix = warmup_prefix + self.t_in_epochs = t_in_epochs + self.k_decay = k_decay + if self.warmup_t: + self.warmup_steps = [(v - warmup_lr_init) / self.warmup_t for v in self.base_values] + super().update_groups(self.warmup_lr_init) + else: + self.warmup_steps = [1 for _ in self.base_values] + + def _get_lr(self, t): + if t < self.warmup_t: + lrs = [self.warmup_lr_init + t * s for s in self.warmup_steps] + else: + if self.warmup_prefix: + t = t - self.warmup_t + + if self.cycle_mul != 1: + i = math.floor(math.log(1 - t / self.t_initial * (1 - self.cycle_mul), self.cycle_mul)) + t_i = self.cycle_mul ** i * self.t_initial + t_curr = t - (1 - self.cycle_mul ** i) / (1 - self.cycle_mul) * self.t_initial + else: + i = t // self.t_initial + t_i = self.t_initial + t_curr = t - (self.t_initial * i) + + gamma = self.cycle_decay ** i + lr_max_values = [v * gamma for v in self.base_values] + k = self.k_decay + + if i < self.cycle_limit: + lrs = [ + self.lr_min + 0.5 * (lr_max - self.lr_min) * (1 + math.cos(math.pi * t_curr ** k / t_i ** k)) + for lr_max in lr_max_values + ] + else: + lrs = [self.lr_min for _ in self.base_values] + + return lrs + + def get_epoch_values(self, epoch: int): + if self.t_in_epochs: + return self._get_lr(epoch) + else: + return None + + def get_update_values(self, num_updates: int): + if not self.t_in_epochs: + return self._get_lr(num_updates) + else: + return None + + def get_cycle_length(self, cycles=0): + cycles = max(1, cycles or self.cycle_limit) + if self.cycle_mul == 1.0: + return self.t_initial * cycles + else: + return int(math.floor(-self.t_initial * (self.cycle_mul ** cycles - 1) / (1 - self.cycle_mul))) \ No newline at end of file diff --git a/scheduler/multistep_lr.py b/scheduler/multistep_lr.py new file mode 100644 index 0000000..558fa0d --- /dev/null +++ b/scheduler/multistep_lr.py @@ -0,0 +1,66 @@ +""" MultiStep LR Scheduler + +Basic multi step LR schedule with warmup, noise. +""" +import torch +import bisect +from timm.scheduler.scheduler import Scheduler +from typing import List + + +class MultiStepLRScheduler(Scheduler): + """ + """ + + def __init__(self, + optimizer: torch.optim.Optimizer, + decay_t: List[int], + decay_rate: float = 1., + warmup_t=0, + warmup_lr_init=0, + t_in_epochs=True, + noise_range_t=None, + noise_pct=0.67, + noise_std=1.0, + noise_seed=42, + initialize=True, + ) -> None: + super().__init__( + optimizer, param_group_field="lr", + noise_range_t=noise_range_t, noise_pct=noise_pct, noise_std=noise_std, noise_seed=noise_seed, + initialize=initialize) + + self.decay_t = decay_t + self.decay_rate = decay_rate + self.warmup_t = warmup_t + self.warmup_lr_init = warmup_lr_init + self.t_in_epochs = t_in_epochs + if self.warmup_t: + self.warmup_steps = [(v - warmup_lr_init) / self.warmup_t for v in self.base_values] + super().update_groups(self.warmup_lr_init) + else: + self.warmup_steps = [1 for _ in self.base_values] + + def get_curr_decay_steps(self, t): + # find where in the array t goes, + # assumes self.decay_t is sorted + return bisect.bisect_right(self.decay_t, t+1) + + def _get_lr(self, t): + if t < self.warmup_t: + lrs = [self.warmup_lr_init + t * s for s in self.warmup_steps] + else: + lrs = [v * (self.decay_rate ** self.get_curr_decay_steps(t)) for v in self.base_values] + return lrs + + def get_epoch_values(self, epoch: int): + if self.t_in_epochs: + return self._get_lr(epoch) + else: + return None + + def get_update_values(self, num_updates: int): + if not self.t_in_epochs: + return self._get_lr(num_updates) + else: + return None \ No newline at end of file diff --git a/scheduler/plateau_lr.py b/scheduler/plateau_lr.py new file mode 100644 index 0000000..2c31930 --- /dev/null +++ b/scheduler/plateau_lr.py @@ -0,0 +1,103 @@ +""" Plateau Scheduler + +Adapts PyTorch plateau scheduler and allows application of noise, warmup. + +Hacked together by / Copyright 2020 Ross Wightman +""" +import torch + +from .scheduler_main import Scheduler + + +class PlateauLRScheduler(Scheduler): + """Decay the LR by a factor every time the validation loss plateaus.""" + + def __init__(self, + optimizer, + decay_rate=0.1, + patience_t=10, + verbose=True, + threshold=1e-4, + cooldown_t=0, + warmup_t=0, + warmup_lr_init=0, + lr_min=0, + mode='max', + noise_range_t=None, + noise_type='normal', + noise_pct=0.67, + noise_std=1.0, + noise_seed=None, + initialize=True, + ): + super().__init__( + optimizer, + 'lr', + noise_range_t=noise_range_t, + noise_type=noise_type, + noise_pct=noise_pct, + noise_std=noise_std, + noise_seed=noise_seed, + initialize=initialize, + ) + + self.lr_scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( + self.optimizer, + patience=patience_t, + factor=decay_rate, + verbose=verbose, + threshold=threshold, + cooldown=cooldown_t, + mode=mode, + min_lr=lr_min + ) + + self.warmup_t = warmup_t + self.warmup_lr_init = warmup_lr_init + if self.warmup_t: + self.warmup_steps = [(v - warmup_lr_init) / self.warmup_t for v in self.base_values] + super().update_groups(self.warmup_lr_init) + else: + self.warmup_steps = [1 for _ in self.base_values] + self.restore_lr = None + + def state_dict(self): + return { + 'best': self.lr_scheduler.best, + 'last_epoch': self.lr_scheduler.last_epoch, + } + + def load_state_dict(self, state_dict): + self.lr_scheduler.best = state_dict['best'] + if 'last_epoch' in state_dict: + self.lr_scheduler.last_epoch = state_dict['last_epoch'] + + # override the base class step fn completely + def step(self, epoch, metric=None): + if epoch <= self.warmup_t: + lrs = [self.warmup_lr_init + epoch * s for s in self.warmup_steps] + super().update_groups(lrs) + else: + if self.restore_lr is not None: + # restore actual LR from before our last noise perturbation before stepping base + for i, param_group in enumerate(self.optimizer.param_groups): + param_group['lr'] = self.restore_lr[i] + self.restore_lr = None + + self.lr_scheduler.step(metric, epoch) # step the base scheduler + + if self._is_apply_noise(epoch): + self._apply_noise(epoch) + + def _apply_noise(self, epoch): + noise = self._calculate_noise(epoch) + + # apply the noise on top of previous LR, cache the old value so we can restore for normal + # stepping of base scheduler + restore_lr = [] + for i, param_group in enumerate(self.optimizer.param_groups): + old_lr = float(param_group['lr']) + restore_lr.append(old_lr) + new_lr = old_lr + old_lr * noise + param_group['lr'] = new_lr + self.restore_lr = restore_lr \ No newline at end of file diff --git a/scheduler/poly_lr.py b/scheduler/poly_lr.py new file mode 100644 index 0000000..6e8b839 --- /dev/null +++ b/scheduler/poly_lr.py @@ -0,0 +1,116 @@ +""" Polynomial Scheduler + +Polynomial LR schedule with warmup, noise. + +Hacked together by / Copyright 2021 Ross Wightman +""" +import math +import logging + +import torch + +from .scheduler_main import Scheduler + + +_logger = logging.getLogger(__name__) + + +class PolyLRScheduler(Scheduler): + """ Polynomial LR Scheduler w/ warmup, noise, and k-decay + + k-decay option based on `k-decay: A New Method For Learning Rate Schedule` - https://arxiv.org/abs/2004.05909 + """ + + def __init__(self, + optimizer: torch.optim.Optimizer, + t_initial: int, + power: float = 0.5, + lr_min: float = 0., + cycle_mul: float = 1., + cycle_decay: float = 1., + cycle_limit: int = 1, + warmup_t=0, + warmup_lr_init=0, + warmup_prefix=False, + t_in_epochs=True, + noise_range_t=None, + noise_pct=0.67, + noise_std=1.0, + noise_seed=42, + k_decay=1.0, + initialize=True) -> None: + super().__init__( + optimizer, param_group_field="lr", + noise_range_t=noise_range_t, noise_pct=noise_pct, noise_std=noise_std, noise_seed=noise_seed, + initialize=initialize) + + assert t_initial > 0 + assert lr_min >= 0 + if t_initial == 1 and cycle_mul == 1 and cycle_decay == 1: + _logger.warning("Cosine annealing scheduler will have no effect on the learning " + "rate since t_initial = t_mul = eta_mul = 1.") + self.t_initial = t_initial + self.power = power + self.lr_min = lr_min + self.cycle_mul = cycle_mul + self.cycle_decay = cycle_decay + self.cycle_limit = cycle_limit + self.warmup_t = warmup_t + self.warmup_lr_init = warmup_lr_init + self.warmup_prefix = warmup_prefix + self.t_in_epochs = t_in_epochs + self.k_decay = k_decay + if self.warmup_t: + self.warmup_steps = [(v - warmup_lr_init) / self.warmup_t for v in self.base_values] + super().update_groups(self.warmup_lr_init) + else: + self.warmup_steps = [1 for _ in self.base_values] + + def _get_lr(self, t): + if t < self.warmup_t: + lrs = [self.warmup_lr_init + t * s for s in self.warmup_steps] + else: + if self.warmup_prefix: + t = t - self.warmup_t + + if self.cycle_mul != 1: + i = math.floor(math.log(1 - t / self.t_initial * (1 - self.cycle_mul), self.cycle_mul)) + t_i = self.cycle_mul ** i * self.t_initial + t_curr = t - (1 - self.cycle_mul ** i) / (1 - self.cycle_mul) * self.t_initial + else: + i = t // self.t_initial + t_i = self.t_initial + t_curr = t - (self.t_initial * i) + + gamma = self.cycle_decay ** i + lr_max_values = [v * gamma for v in self.base_values] + k = self.k_decay + + if i < self.cycle_limit: + lrs = [ + self.lr_min + (lr_max - self.lr_min) * (1 - t_curr ** k / t_i ** k) ** self.power + for lr_max in lr_max_values + ] + else: + lrs = [self.lr_min for _ in self.base_values] + + return lrs + + def get_epoch_values(self, epoch: int): + if self.t_in_epochs: + return self._get_lr(epoch) + else: + return None + + def get_update_values(self, num_updates: int): + if not self.t_in_epochs: + return self._get_lr(num_updates) + else: + return None + + def get_cycle_length(self, cycles=0): + cycles = max(1, cycles or self.cycle_limit) + if self.cycle_mul == 1.0: + return self.t_initial * cycles + else: + return int(math.floor(-self.t_initial * (self.cycle_mul ** cycles - 1) / (1 - self.cycle_mul))) \ No newline at end of file diff --git a/scheduler/scheduler_factory.py b/scheduler/scheduler_factory.py new file mode 100644 index 0000000..9556f18 --- /dev/null +++ b/scheduler/scheduler_factory.py @@ -0,0 +1,111 @@ +""" Scheduler Factory +Hacked together by / Copyright 2021 Ross Wightman +""" +from .cosine_lr import CosineLRScheduler +from .multistep_lr import MultiStepLRScheduler +from .plateau_lr import PlateauLRScheduler +from .poly_lr import PolyLRScheduler +from .step_lr import StepLRScheduler +from .tanh_lr import TanhLRScheduler + + +def create_scheduler(args, optimizer): + num_epochs = args.epochs + n_iter = args.data_len // (args.batch_size * args.world_size) + tot_iter = num_epochs * n_iter + warmup_iters = args.warmup_epochs * n_iter + if getattr(args, 'lr_noise', None) is not None: + lr_noise = getattr(args, 'lr_noise') + if isinstance(lr_noise, (list, tuple)): + noise_range = [n * num_epochs for n in lr_noise] + if len(noise_range) == 1: + noise_range = noise_range[0] + else: + noise_range = lr_noise * num_epochs + else: + noise_range = None + noise_args = dict( + noise_range_t=noise_range, + noise_pct=getattr(args, 'lr_noise_pct', 0.67), + noise_std=getattr(args, 'lr_noise_std', 1.), + noise_seed=getattr(args, 'seed', 42), + ) + cycle_args = dict( + cycle_mul=getattr(args, 'lr_cycle_mul', 1.), + cycle_decay=getattr(args, 'lr_cycle_decay', 0.1), + cycle_limit=getattr(args, 'lr_cycle_limit', 1), + ) + + lr_scheduler = None + if args.sched == 'cosine': + lr_scheduler = CosineLRScheduler( + optimizer, + t_initial=tot_iter, + lr_min=args.min_lr, + warmup_lr_init=args.warmup_lr, + warmup_t=warmup_iters, + k_decay=getattr(args, 'lr_k_decay', 1.0), + t_in_epochs=args.lr_ep, + **cycle_args, + **noise_args, + ) + cycle_length = lr_scheduler.get_cycle_length() // n_iter + num_epochs = cycle_length + args.cooldown_epochs + elif args.sched == 'tanh': + lr_scheduler = TanhLRScheduler( + optimizer, + t_initial=num_epochs, + lr_min=args.min_lr, + warmup_lr_init=args.warmup_lr, + warmup_t=args.warmup_epochs, + t_in_epochs=True, + **cycle_args, + **noise_args, + ) + num_epochs = lr_scheduler.get_cycle_length() + args.cooldown_epochs + elif args.sched == 'step': + lr_scheduler = StepLRScheduler( + optimizer, + decay_t=args.decay_epochs, + decay_rate=args.decay_rate, + warmup_lr_init=args.warmup_lr, + warmup_t=args.warmup_epochs, + **noise_args, + ) + elif args.sched == 'multistep': + lr_scheduler = MultiStepLRScheduler( + optimizer, + decay_t=args.decay_milestones, + decay_rate=args.decay_rate, + warmup_lr_init=args.warmup_lr, + warmup_t=args.warmup_epochs, + **noise_args, + ) + elif args.sched == 'plateau': + mode = 'min' if 'loss' in getattr(args, 'eval_metric', '') else 'max' + lr_scheduler = PlateauLRScheduler( + optimizer, + decay_rate=args.decay_rate, + patience_t=args.patience_epochs, + lr_min=args.min_lr, + mode=mode, + warmup_lr_init=args.warmup_lr, + warmup_t=args.warmup_epochs, + cooldown_t=0, + **noise_args, + ) + elif args.sched == 'poly': + lr_scheduler = PolyLRScheduler( + optimizer, + power=args.decay_rate, # overloading 'decay_rate' as polynomial power + t_initial=num_epochs, + lr_min=args.min_lr, + warmup_lr_init=args.warmup_lr, + warmup_t=args.warmup_epochs, + k_decay=getattr(args, 'lr_k_decay', 1.0), + **cycle_args, + **noise_args, + ) + num_epochs = lr_scheduler.get_cycle_length() + args.cooldown_epochs + + return lr_scheduler, num_epochs \ No newline at end of file diff --git a/scheduler/scheduler_main.py b/scheduler/scheduler_main.py new file mode 100644 index 0000000..221cddd --- /dev/null +++ b/scheduler/scheduler_main.py @@ -0,0 +1,117 @@ +from typing import Dict, Any + +import torch + + +class Scheduler: + """ Parameter Scheduler Base Class + A scheduler base class that can be used to schedule any optimizer parameter groups. + + Unlike the builtin PyTorch schedulers, this is intended to be consistently called + * At the END of each epoch, before incrementing the epoch count, to calculate next epoch's value + * At the END of each optimizer update, after incrementing the update count, to calculate next update's value + + The schedulers built on this should try to remain as stateless as possible (for simplicity). + + This family of schedulers is attempting to avoid the confusion of the meaning of 'last_epoch' + and -1 values for special behaviour. All epoch and update counts must be tracked in the training + code and explicitly passed in to the schedulers on the corresponding step or step_update call. + + Based on ideas from: + * https://github.com/pytorch/fairseq/tree/master/fairseq/optim/lr_scheduler + * https://github.com/allenai/allennlp/tree/master/allennlp/training/learning_rate_schedulers + """ + + def __init__(self, + optimizer: torch.optim.Optimizer, + param_group_field: str, + noise_range_t=None, + noise_type='normal', + noise_pct=0.67, + noise_std=1.0, + noise_seed=None, + initialize: bool = True) -> None: + self.optimizer = optimizer + self.param_group_field = param_group_field + self._initial_param_group_field = f"initial_{param_group_field}" + if initialize: + for i, group in enumerate(self.optimizer.param_groups): + if param_group_field not in group: + raise KeyError(f"{param_group_field} missing from param_groups[{i}]") + group.setdefault(self._initial_param_group_field, group[param_group_field]) + else: + for i, group in enumerate(self.optimizer.param_groups): + if self._initial_param_group_field not in group: + raise KeyError(f"{self._initial_param_group_field} missing from param_groups[{i}]") + self.base_values = [group[self._initial_param_group_field] for group in self.optimizer.param_groups] + self.metric = None # any point to having this for all? + self.noise_range_t = noise_range_t + self.noise_pct = noise_pct + self.noise_type = noise_type + self.noise_std = noise_std + self.noise_seed = noise_seed if noise_seed is not None else 42 + self.update_groups(self.base_values) + + def state_dict(self) -> Dict[str, Any]: + return {key: value for key, value in self.__dict__.items() if key != 'optimizer'} + + def load_state_dict(self, state_dict: Dict[str, Any]) -> None: + self.__dict__.update(state_dict) + + def get_epoch_values(self, epoch: int): + return None + + def get_update_values(self, num_updates: int): + return None + + def step(self, epoch: int, metric: float = None) -> None: + self.metric = metric + values = self.get_epoch_values(epoch) + if values is not None: + values = self._add_noise(values, epoch) + self.update_groups(values) + + def step_update(self, num_updates: int, metric: float = None): + self.metric = metric + values = self.get_update_values(num_updates) + if values is not None: + values = self._add_noise(values, num_updates) + self.update_groups(values) + + def update_groups(self, values): + if not isinstance(values, (list, tuple)): + values = [values] * len(self.optimizer.param_groups) + for param_group, value in zip(self.optimizer.param_groups, values): + if 'lr_scale' in param_group: + param_group[self.param_group_field] = value * param_group['lr_scale'] + else: + param_group[self.param_group_field] = value + + def _add_noise(self, lrs, t): + if self._is_apply_noise(t): + noise = self._calculate_noise(t) + lrs = [v + v * noise for v in lrs] + return lrs + + def _is_apply_noise(self, t) -> bool: + """Return True if scheduler in noise range.""" + apply_noise = False + if self.noise_range_t is not None: + if isinstance(self.noise_range_t, (list, tuple)): + apply_noise = self.noise_range_t[0] <= t < self.noise_range_t[1] + else: + apply_noise = t >= self.noise_range_t + return apply_noise + + def _calculate_noise(self, t) -> float: + g = torch.Generator() + g.manual_seed(self.noise_seed + t) + if self.noise_type == 'normal': + while True: + # resample if noise out of percent limit, brute force but shouldn't spin much + noise = torch.randn(1, generator=g).item() + if abs(noise) < self.noise_pct: + return noise + else: + noise = 2 * (torch.rand(1, generator=g).item() - 0.5) * self.noise_pct + return noise \ No newline at end of file diff --git a/scheduler/step_lr.py b/scheduler/step_lr.py new file mode 100644 index 0000000..8eb1d88 --- /dev/null +++ b/scheduler/step_lr.py @@ -0,0 +1,63 @@ +""" Step Scheduler + +Basic step LR schedule with warmup, noise. + +Hacked together by / Copyright 2020 Ross Wightman +""" +import math +import torch + +from .scheduler_main import Scheduler + + +class StepLRScheduler(Scheduler): + """ + """ + + def __init__(self, + optimizer: torch.optim.Optimizer, + decay_t: float, + decay_rate: float = 1., + warmup_t=0, + warmup_lr_init=0, + t_in_epochs=True, + noise_range_t=None, + noise_pct=0.67, + noise_std=1.0, + noise_seed=42, + initialize=True, + ) -> None: + super().__init__( + optimizer, param_group_field="lr", + noise_range_t=noise_range_t, noise_pct=noise_pct, noise_std=noise_std, noise_seed=noise_seed, + initialize=initialize) + + self.decay_t = decay_t + self.decay_rate = decay_rate + self.warmup_t = warmup_t + self.warmup_lr_init = warmup_lr_init + self.t_in_epochs = t_in_epochs + if self.warmup_t: + self.warmup_steps = [(v - warmup_lr_init) / self.warmup_t for v in self.base_values] + super().update_groups(self.warmup_lr_init) + else: + self.warmup_steps = [1 for _ in self.base_values] + + def _get_lr(self, t): + if t < self.warmup_t: + lrs = [self.warmup_lr_init + t * s for s in self.warmup_steps] + else: + lrs = [v * (self.decay_rate ** (t // self.decay_t)) for v in self.base_values] + return lrs + + def get_epoch_values(self, epoch: int): + if self.t_in_epochs: + return self._get_lr(epoch) + else: + return None + + def get_update_values(self, num_updates: int): + if not self.t_in_epochs: + return self._get_lr(num_updates) + else: + return None \ No newline at end of file diff --git a/scheduler/tanh_lr.py b/scheduler/tanh_lr.py new file mode 100644 index 0000000..c5801f7 --- /dev/null +++ b/scheduler/tanh_lr.py @@ -0,0 +1,117 @@ +""" TanH Scheduler + +TanH schedule with warmup, cycle/restarts, noise. + +Hacked together by / Copyright 2021 Ross Wightman +""" +import logging +import math +import numpy as np +import torch + +from .scheduler_main import Scheduler + + +_logger = logging.getLogger(__name__) + + +class TanhLRScheduler(Scheduler): + """ + Hyberbolic-Tangent decay with restarts. + This is described in the paper https://arxiv.org/abs/1806.01593 + """ + + def __init__(self, + optimizer: torch.optim.Optimizer, + t_initial: int, + lb: float = -7., + ub: float = 3., + lr_min: float = 0., + cycle_mul: float = 1., + cycle_decay: float = 1., + cycle_limit: int = 1, + warmup_t=0, + warmup_lr_init=0, + warmup_prefix=False, + t_in_epochs=True, + noise_range_t=None, + noise_pct=0.67, + noise_std=1.0, + noise_seed=42, + initialize=True) -> None: + super().__init__( + optimizer, param_group_field="lr", + noise_range_t=noise_range_t, noise_pct=noise_pct, noise_std=noise_std, noise_seed=noise_seed, + initialize=initialize) + + assert t_initial > 0 + assert lr_min >= 0 + assert lb < ub + assert cycle_limit >= 0 + assert warmup_t >= 0 + assert warmup_lr_init >= 0 + self.lb = lb + self.ub = ub + self.t_initial = t_initial + self.lr_min = lr_min + self.cycle_mul = cycle_mul + self.cycle_decay = cycle_decay + self.cycle_limit = cycle_limit + self.warmup_t = warmup_t + self.warmup_lr_init = warmup_lr_init + self.warmup_prefix = warmup_prefix + self.t_in_epochs = t_in_epochs + if self.warmup_t: + t_v = self.base_values if self.warmup_prefix else self._get_lr(self.warmup_t) + self.warmup_steps = [(v - warmup_lr_init) / self.warmup_t for v in t_v] + super().update_groups(self.warmup_lr_init) + else: + self.warmup_steps = [1 for _ in self.base_values] + + def _get_lr(self, t): + if t < self.warmup_t: + lrs = [self.warmup_lr_init + t * s for s in self.warmup_steps] + else: + if self.warmup_prefix: + t = t - self.warmup_t + + if self.cycle_mul != 1: + i = math.floor(math.log(1 - t / self.t_initial * (1 - self.cycle_mul), self.cycle_mul)) + t_i = self.cycle_mul ** i * self.t_initial + t_curr = t - (1 - self.cycle_mul ** i) / (1 - self.cycle_mul) * self.t_initial + else: + i = t // self.t_initial + t_i = self.t_initial + t_curr = t - (self.t_initial * i) + + if i < self.cycle_limit: + gamma = self.cycle_decay ** i + lr_max_values = [v * gamma for v in self.base_values] + + tr = t_curr / t_i + lrs = [ + self.lr_min + 0.5 * (lr_max - self.lr_min) * (1 - math.tanh(self.lb * (1. - tr) + self.ub * tr)) + for lr_max in lr_max_values + ] + else: + lrs = [self.lr_min for _ in self.base_values] + return lrs + + def get_epoch_values(self, epoch: int): + if self.t_in_epochs: + return self._get_lr(epoch) + else: + return None + + def get_update_values(self, num_updates: int): + if not self.t_in_epochs: + return self._get_lr(num_updates) + else: + return None + + def get_cycle_length(self, cycles=0): + cycles = max(1, cycles or self.cycle_limit) + if self.cycle_mul == 1.0: + return self.t_initial * cycles + else: + return int(math.floor(-self.t_initial * (self.cycle_mul ** cycles - 1) / (1 - self.cycle_mul))) \ No newline at end of file diff --git a/train_gpu.py b/train_gpu.py new file mode 100644 index 0000000..ed02f95 --- /dev/null +++ b/train_gpu.py @@ -0,0 +1,537 @@ +""" ImageNet Training Script + +This is intended to be a lean and easily modifiable ImageNet training script that reproduces ImageNet +training results with some of the latest networks and training techniques. It favours canonical PyTorch +and standard Python style over trying to be able to 'do it all.' That said, it offers quite a few speed +and training result improvements over the usual PyTorch example scripts. Repurpose as you see fit. + +This script was started from an early version of the PyTorch ImageNet example +(https://github.com/pytorch/examples/tree/master/imagenet) + +NVIDIA CUDA specific speedups adopted from NVIDIA Apex examples +(https://github.com/NVIDIA/apex/tree/master/examples/imagenet) + +Hacked together by / Copyright 2020 Ross Wightman (https://github.com/rwightman) +""" +import argparse +import datetime +import numpy as np +import time +import torch +import torch.backends.cudnn as cudnn +from tensorboardX import SummaryWriter +import json +import os + +from pathlib import Path + +from timm.data import Mixup +from timm.models import create_model +from timm.loss import LabelSmoothingCrossEntropy, SoftTargetCrossEntropy +from timm.scheduler import create_scheduler +from timm.optim import create_optimizer +from timm.utils import NativeScaler, get_state_dict, ModelEma + + +from util.samplers import RASampler +from util import utils as utils +from util.optimizer import SophiaG, AdaFactor, LAMB +from util.engine import train_one_epoch, evaluate +from util.losses import DistillationLoss +from util.lr_sched import adjust_learning_rate +from util.lr_decay import inverse_sqrt_lr_decay + +from datasets import build_dataset +from datasets.threeaugment import new_data_aug_generator + +from models import * + +from estimate_model import Predictor, Plot_ROC, OptAUC + + +def get_args_parser(): + parser = argparse.ArgumentParser( + 'MambaVision training and evaluation script', add_help=False) + parser.add_argument('--batch-size', default=16, type=int) + parser.add_argument('--epochs', default=5, type=int) + parser.add_argument('--predict', default=True, type=bool, help='plot ROC curve and confusion matrix') + parser.add_argument('--opt_auc', default=False, type=bool, help='Optimize AUC') + + # Model parameters + parser.add_argument('--model', default='mamba_vision_B', type=str, metavar='MODEL', + choices=['mamba_vision_T', 'mamba_vision_T2', 'mamba_vision_S', + 'mamba_vision_B', 'mamba_vision_L', 'mamba_vision_L2' + ], + help='Name of model to train') + parser.add_argument('--input-size', default=224, type=int, help='images input size') + parser.add_argument('--model-ema', action='store_true') + parser.add_argument('--no-model-ema', action='store_false', dest='model_ema') + parser.set_defaults(model_ema=True) + parser.add_argument('--model-ema-decay', type=float, default=0.99996, help='') + parser.add_argument('--model-ema-force-cpu', action='store_true', default=False, help='') + + # Optimizer parameters + parser.add_argument('--opt', default='adamw', type=str, metavar='OPTIMIZER', + help='Optimizer (default: "adamw"') + parser.add_argument('--opt-eps', default=1e-8, type=float, metavar='EPSILON', + help='Optimizer Epsilon (default: 1e-8)') + parser.add_argument('--opt-betas', default=None, type=float, nargs='+', metavar='BETA', + help='Optimizer Betas (default: None, use opt default)') + parser.add_argument('--clip-grad', type=float, default=0.02, metavar='NORM', + help='Clip gradient norm (default: None, no clipping)') + parser.add_argument('--clip-mode', type=str, default='agc', + help='Gradient clipping mode. One of ("norm", "value", "agc")') + parser.add_argument('--momentum', type=float, default=0.9, metavar='M', + help='SGD momentum (default: 0.9)') + parser.add_argument('--weight-decay', type=float, default=0.025, + help='weight decay (default: 0.025)') + + + # Learning rate schedule parameters + parser.add_argument('--sched', default='cosine', type=str, metavar='SCHEDULER', + help='LR scheduler (default: "cosine"') + parser.add_argument('--lr', type=float, default=1e-3, metavar='LR', + help='learning rate (default: 1e-3)') + parser.add_argument('--lr-ep', action='store_true', default=False, + help='using the epoch-based scheduler') + parser.add_argument('--lr-noise', type=float, nargs='+', default=None, metavar='pct, pct', + help='learning rate noise on/off epoch percentages') + parser.add_argument('--lr-noise-pct', type=float, default=0.67, metavar='PERCENT', + help='learning rate noise limit percent (default: 0.67)') + parser.add_argument('--lr-noise-std', type=float, default=1.0, metavar='STDDEV', + help='learning rate noise std-dev (default: 1.0)') + parser.add_argument('--lr-cycle-mul', type=float, default=1.0, metavar='MULT', + help='learning rate cycle len multiplier (default: 1.0)') + parser.add_argument('--lr-cycle-decay', type=float, default=1.0, metavar='MULT', + help='amount to decay each learning rate cycle (default: 0.5)') + parser.add_argument('--lr-cycle-limit', type=int, default=1, metavar='N', + help='learning rate cycle limit, cycles enabled if > 1') + parser.add_argument('--lr-k-decay', type=float, default=1.0, + help='learning rate k-decay for cosine/poly (default: 1.0)') + parser.add_argument('--warmup-lr', type=float, default=2e-4, metavar='LR', + help='warmup learning rate (default: 1e-4)') + parser.add_argument('--min-lr', type=float, default=1e-4, metavar='LR', + help='lower lr bound for cyclic schedulers that hit 0 (1e-5)') + parser.add_argument('--decay-milestones', default=[30, 60], type=int, nargs='+', metavar="MILESTONES", + help='list of decay epoch indices for multistep lr. must be increasing') + parser.add_argument('--decay-epochs', type=float, default=30, metavar='N', + help='epoch interval to decay LR') + parser.add_argument('--warmup-epochs', type=int, default=5, metavar='N', + help='epochs to warmup LR, if scheduler supports') + parser.add_argument('--cooldown-epochs', type=int, default=10, metavar='N', + help='epochs to cooldown LR at min_lr, after cyclic schedule ends') + parser.add_argument('--patience-epochs', type=int, default=10, metavar='N', + help='patience epochs for Plateau LR scheduler (default: 10') + parser.add_argument('--decay-rate', '--dr', type=float, default=0.1, metavar='RATE', + help='LR decay rate (default: 0.1)') + + # Augmentation parameters + parser.add_argument('--ThreeAugment', action='store_true') + parser.add_argument('--color-jitter', type=float, default=0.4, metavar='PCT', + help='Color jitter factor (default: 0.4)') + parser.add_argument('--aa', type=str, default='rand-m9-mstd0.5-inc1', metavar='NAME', + help='Use AutoAugment policy. "v0" or "original". " + \ + "(default: rand-m9-mstd0.5-inc1)'), + parser.add_argument('--smoothing', type=float, default=0.1, + help='Label smoothing (default: 0.1)') + parser.add_argument('--train-interpolation', type=str, default='bicubic', + help='Training interpolation (random, bilinear, bicubic default: "bicubic")') + parser.add_argument('--repeated-aug', action='store_true') + parser.add_argument('--no-repeated-aug', + action='store_false', dest='repeated_aug') + parser.set_defaults(repeated_aug=True) + + # Random Erase params + parser.add_argument('--reprob', type=float, default=0.25, metavar='PCT', + help='Random erase prob (default: 0.25)') + parser.add_argument('--remode', type=str, default='pixel', + help='Random erase mode (default: "pixel")') + parser.add_argument('--recount', type=int, default=1, + help='Random erase count (default: 1)') + parser.add_argument('--resplit', action='store_true', default=False, + help='Do not random erase first (clean) augmentation split') + + # Mixup params + parser.add_argument('--mixup', type=float, default=0.8, + help='mixup alpha, mixup enabled if > 0. (default: 0.8)') + parser.add_argument('--cutmix', type=float, default=1.0, + help='cutmix alpha, cutmix enabled if > 0. (default: 1.0)') + parser.add_argument('--cutmix-minmax', type=float, nargs='+', default=None, + help='cutmix min/max ratio, overrides alpha and enables cutmix if set (default: None)') + parser.add_argument('--mixup-prob', type=float, default=1.0, + help='Probability of performing mixup or cutmix when either/both is enabled') + parser.add_argument('--mixup-switch-prob', type=float, default=0.5, + help='Probability of switching to cutmix when both mixup and cutmix enabled') + parser.add_argument('--mixup-mode', type=str, default='batch', + help='How to apply mixup/cutmix params. Per "batch", "pair", or "elem"') + + # Distillation parameters + parser.add_argument('--teacher-model', default='regnety_160', type=str, metavar='MODEL', + help='Name of teacher model to train (default: "regnety_160"') + parser.add_argument('--teacher-path', type=str, + default='https://dl.fbaipublicfiles.com/deit/regnety_160-a5fe301d.pth') + parser.add_argument('--distillation-type', default='none', + choices=['none', 'soft', 'hard'], type=str, help="") + parser.add_argument('--distillation-alpha', + default=0.5, type=float, help="") + parser.add_argument('--distillation-tau', default=1.0, type=float, help="") + + # Finetuning params + parser.add_argument('--finetune', default='', + help='finetune from checkpoint') + parser.add_argument('--freeze_layers', type=bool, default=False, help='freeze layers') + parser.add_argument('--set_bn_eval', action='store_true', default=False, + help='set BN layers to eval mode during finetuning.') + + # Dataset parameters + parser.add_argument('--data_root', default='/mnt/d/flower_data', type=str, + help='datasets path') + parser.add_argument('--data_len', default=3670, type=int, + help='count of your entire data_set. For example: ImageNet 1281167') + parser.add_argument('--nb_classes', default=5, type=int, + help='number classes of your datasets') + parser.add_argument('--data-set', default='IMNET', choices=['CIFAR', 'IMNET', 'INAT', 'INAT19'], + type=str, help='Image Net datasets path') + parser.add_argument('--inat-category', default='name', + choices=['kingdom', 'phylum', 'class', 'order', + 'supercategory', 'family', 'genus', 'name'], + type=str, help='semantic granularity') + parser.add_argument('--output_dir', default='./output', + help='path where to save, empty for no saving') + parser.add_argument('--writer_output', default='./', + help='path where to save SummaryWriter, empty for no saving') + parser.add_argument('--device', default='cuda', + help='device to use for training / testing') + parser.add_argument('--seed', default=0, type=int) + parser.add_argument('--resume', default='', help='resume from checkpoint') + parser.add_argument('--start_epoch', default=0, type=int, metavar='N', + help='start epoch') + parser.add_argument('--eval', action='store_true', + help='Perform evaluation only') + parser.add_argument('--dist-eval', action='store_true', + default=False, help='Enabling distributed evaluation') + parser.add_argument('--num_workers', default=0, type=int) + parser.add_argument('--pin-mem', action='store_true', + help='Pin CPU memory in DataLoader for more efficient (sometimes) transfer to GPU.') + parser.add_argument('--no-pin-mem', action='store_false', dest='pin_mem', + help='') + parser.set_defaults(pin_mem=True) + + # training parameters + parser.add_argument('--world_size', default=1, type=int, + help='number of distributed processes') + parser.add_argument('--local_rank', default=0, type=int) + parser.add_argument('--dist_url', default='env://', + help='url used to set up distributed training') + parser.add_argument('--save_freq', default=1, type=int, + help='frequency of model saving') + return parser + + + + +def main(args): + print(args) + utils.init_distributed_mode(args) + + if args.local_rank == 0: + writer = SummaryWriter(os.path.join(args.writer_output, 'runs')) + + if args.distillation_type != 'none' and args.finetune and not args.eval: + raise NotImplementedError( + "Finetuning with distillation not yet supported") + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + # random.seed(seed) + + cudnn.benchmark = True + + dataset_train, dataset_val = build_dataset(args=args) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + if args.repeated_aug: + sampler_train = RASampler( + dataset_train, num_replicas=num_tasks, rank=global_rank, shuffle=True + ) + else: + sampler_train = torch.utils.data.DistributedSampler( + dataset_train, num_replicas=num_tasks, rank=global_rank, shuffle=True + ) + if args.dist_eval: + if len(dataset_val) % num_tasks != 0: + print('Warning: Enabling distributed evaluation with an eval datasets not divisible by process number. ' + 'This will slightly alter validation results as extra duplicate entries are added to achieve ' + 'equal num of samples per-process.') + sampler_val = torch.utils.data.DistributedSampler( + dataset_val, num_replicas=num_tasks, rank=global_rank, shuffle=False) + else: + sampler_val = torch.utils.data.SequentialSampler(dataset_val) + else: + sampler_train = torch.utils.data.RandomSampler(dataset_train) + sampler_val = torch.utils.data.SequentialSampler(dataset_val) + + data_loader_train = torch.utils.data.DataLoader( + dataset_train, sampler=sampler_train, + batch_size=args.batch_size, + num_workers=args.num_workers, + pin_memory=args.pin_mem, + drop_last=True, + ) + + if args.ThreeAugment: + data_loader_train.dataset.transform = new_data_aug_generator(args) + + data_loader_val = torch.utils.data.DataLoader( + dataset_val, sampler=sampler_val, + batch_size=int(1.5 * args.batch_size), + num_workers=args.num_workers, + pin_memory=args.pin_mem, + drop_last=False + ) + + mixup_fn = None + mixup_active = args.mixup > 0 or args.cutmix > 0. or args.cutmix_minmax is not None + if mixup_active: + mixup_fn = Mixup( + mixup_alpha=args.mixup, cutmix_alpha=args.cutmix, cutmix_minmax=args.cutmix_minmax, + prob=args.mixup_prob, switch_prob=args.mixup_switch_prob, mode=args.mixup_mode, + label_smoothing=args.smoothing, num_classes=args.nb_classes) + + print(f"Creating model: {args.model}") + + model = create_model( + args.model, + num_classes=args.nb_classes, + args=args + ) + + if args.finetune: + if args.finetune.startswith('https'): + checkpoint = torch.hub.load_state_dict_from_url( + args.finetune, map_location='cpu', check_hash=True) + else: + checkpoint = utils.load_model(args.finetune, model) + + checkpoint_model = checkpoint['model'] + # state_dict = model.state_dict() + for k in list(checkpoint_model.keys()): + if 'head' in k: + print(f"Removing key {k} from pretrained checkpoint") + del checkpoint_model[k] + + msg = model.load_state_dict(checkpoint_model, strict=False) + print(msg) + + if args.freeze_layers: + for name, para in model.named_parameters(): + if 'head' not in name: + para.requires_grad_(False) + else: + print('training {}'.format(name)) + + model.to(device) + + model_ema = None + if args.model_ema: + # Important to create EMA model after cuda(), DP wrapper, and AMP but + # before SyncBN and DDP wrapper + model_ema = ModelEma( + model, + decay=args.model_ema_decay, + device='cpu' if args.model_ema_force_cpu else '', + resume='') + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel( + model, device_ids=[args.gpu]) + model_without_ddp = model.module + n_parameters = sum(p.numel() for p in model.parameters() if p.requires_grad) + print('number of params:', n_parameters) + + linear_scaled_lr = args.lr * args.batch_size * utils.get_world_size() / 512.0 + # args.lr = linear_scaled_lr + # + # print('*****************') + # print('Initial LR is ', linear_scaled_lr) + # print('*****************') + + # optimizer = create_optimizer(args, model_without_ddp) + optimizer = LAMB(model_without_ddp.parameters(), lr=args.lr, weight_decay=0.05) + + # lr_scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=inverse_sqrt_lr_decay) + + + loss_scaler = NativeScaler() + lr_scheduler, _ = create_scheduler(args, optimizer) + + criterion = LabelSmoothingCrossEntropy() + + if args.mixup > 0.: + # smoothing is handled with mixup label transform + criterion = SoftTargetCrossEntropy() + elif args.smoothing: + criterion = LabelSmoothingCrossEntropy(smoothing=args.smoothing) + else: + criterion = torch.nn.CrossEntropyLoss() + + teacher_model = None + if args.distillation_type != 'none': + assert args.teacher_path, 'need to specify teacher-path when using distillation' + print(f"Creating teacher model: {args.teacher_model}") + teacher_model = create_model( + args.teacher_model, + pretrained=False, + num_classes=args.nb_classes, + global_pool='avg', + ) + if args.teacher_path.startswith('https'): + checkpoint = torch.hub.load_state_dict_from_url( + args.teacher_path, map_location='cpu', check_hash=True) + else: + checkpoint = torch.load(args.teacher_path, map_location='cpu') + teacher_model.load_state_dict(checkpoint['model']) + teacher_model.to(device) + teacher_model.eval() + + # wrap the criterion in our custom DistillationLoss, which + # just dispatches to the original criterion if args.distillation_type is + # 'none' + criterion = DistillationLoss( + criterion, teacher_model, args.distillation_type, args.distillation_alpha, args.distillation_tau + ) + + max_accuracy = 0.0 + + output_dir = Path(args.output_dir) + if args.output_dir and utils.is_main_process(): + with (output_dir / "model.txt").open("a") as f: + f.write(str(model)) + if args.output_dir and utils.is_main_process(): + with (output_dir / "args.txt").open("a") as f: + f.write(json.dumps(args.__dict__, indent=2) + "\n") + if args.resume or os.path.exists(f'{args.output_dir}/{args.model}_best_checkpoint.pth'): + args.resume = f'{args.output_dir}/{args.model}_best_checkpoint.pth' + if args.resume.startswith('https'): + checkpoint = torch.hub.load_state_dict_from_url( + args.resume, map_location='cpu', check_hash=True) + else: + print("Loading local checkpoint at {}".format(args.resume)) + checkpoint = torch.load(args.resume, map_location='cpu') + msg = model_without_ddp.load_state_dict(checkpoint['model']) + print(msg) + + if not args.eval and 'optimizer' in checkpoint and 'lr_scheduler' in checkpoint and 'epoch' in checkpoint: + + optimizer.load_state_dict(checkpoint['optimizer']) + for state in optimizer.state.values(): # load parameters to cuda + for k, v in state.items(): + if isinstance(v, torch.Tensor): + state[k] = v.cuda() + + lr_scheduler.load_state_dict(checkpoint['lr_scheduler']) + max_accuracy = checkpoint['best_score'] + print(f'Now max accuracy is {max_accuracy}') + args.start_epoch = checkpoint['epoch'] + 1 + if args.model_ema: + utils._load_checkpoint_for_ema( + model_ema, checkpoint['model_ema']) + if 'scaler' in checkpoint: + loss_scaler.load_state_dict(checkpoint['scaler']) + + if args.eval: + # util.replace_batchnorm(model) # Users may choose whether to merge Conv-BN layers during eval + print(f"Evaluating model: {args.model}") + print(f'No Visualization') + test_stats = evaluate(data_loader_val, model, device, None, None, args, visualization=False) + print( + f"Accuracy of the network on the {len(dataset_val)} test images: {test_stats['acc1']:.1f}%" + ) + + print(f"Start training for {args.epochs} epochs") + start_time = time.time() + + for epoch in range(args.start_epoch, args.epochs): + if args.distributed: + data_loader_train.sampler.set_epoch(epoch) + + train_stats = train_one_epoch( + model, criterion, data_loader_train, + optimizer, device, epoch, loss_scaler, + args.clip_grad, args.clip_mode, model_ema, mixup_fn, + # set_training_mode=args.finetune == '' # keep in eval mode during finetuning + set_training_mode=True, + set_bn_eval=args.set_bn_eval, # set bn to eval if finetune + writer=writer, + args=args + ) + + lr_scheduler.step(epoch) + + test_stats = evaluate(data_loader_val, model, device, epoch, writer, args, visualization=True) + print( + f"Accuracy of the network on the {len(dataset_val)} test images: {test_stats['acc1']:.1f}%") + + if max_accuracy < test_stats["acc1"]: + max_accuracy = test_stats["acc1"] + if args.output_dir: + ckpt_path = os.path.join(output_dir, f'{args.model}_best_checkpoint.pth') + checkpoint_paths = [ckpt_path] + print("Saving checkpoint to {}".format(ckpt_path)) + for checkpoint_path in checkpoint_paths: + utils.save_on_master({ + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'lr_scheduler': lr_scheduler.state_dict(), + 'epoch': epoch, + 'best_score': max_accuracy, + 'model_ema': get_state_dict(model_ema), + 'scaler': loss_scaler.state_dict(), + 'args': args, + }, checkpoint_path) + + print(f'Max accuracy: {max_accuracy:.2f}%') + + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + **{f'test_{k}': v for k, v in test_stats.items()}, + 'epoch': epoch, + 'n_parameters': n_parameters} + + if args.output_dir and utils.is_main_process(): + with (output_dir / "log.txt").open("a") as f: + f.write(json.dumps(log_stats) + "\n") + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + # plot ROC curve and confusion matrix + if args.predict and utils.is_main_process(): + model_predict = create_model( + args.model, + num_classes=args.nb_classes, + args=args + ) + + model_predict.to(device) + print('*******************STARTING PREDICT*******************') + Predictor(model_predict, data_loader_val, f'{args.output_dir}/{args.model}_best_checkpoint.pth', device) + Plot_ROC(model_predict, data_loader_val, f'{args.output_dir}/{args.model}_best_checkpoint.pth', device) + + if args.opt_auc: + OptAUC(model_predict, data_loader_val, f'{args.output_dir}/{args.model}_best_checkpoint.pth', device) + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + 'MambaVision training and evaluation script', parents=[get_args_parser()]) + args = parser.parse_args() + if args.output_dir: + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + main(args) diff --git a/util/__init__.py b/util/__init__.py new file mode 100644 index 0000000..bc9118f --- /dev/null +++ b/util/__init__.py @@ -0,0 +1,7 @@ +from .engine import train_one_epoch, evaluate +from .losses import DistillationLoss +from .samplers import RASampler +from .optimizer import SophiaG, AdaFactor, LAMB +from .utils import * +from .lr_decay import * +from .lr_sched import adjust_learning_rate \ No newline at end of file diff --git a/util/engine.py b/util/engine.py new file mode 100644 index 0000000..e0a40af --- /dev/null +++ b/util/engine.py @@ -0,0 +1,168 @@ +""" +Train and eval functions used in main.py +""" +import math +import sys +from typing import Iterable, Optional + +import torch + +from timm.data import Mixup +from timm.utils import ModelEma, accuracy + +from .losses import DistillationLoss +from util import utils as utils + + +def set_bn_state(model): + for m in model.modules(): + if isinstance(m, torch.nn.modules.batchnorm._BatchNorm): + m.eval() + +def train_one_epoch(model: torch.nn.Module, criterion: DistillationLoss, + data_loader: Iterable, optimizer: torch.optim.Optimizer, + device: torch.device, epoch: int, loss_scaler, + clip_grad: float = 0, + clip_mode: str = 'norm', + model_ema: Optional[ModelEma] = None, mixup_fn: Optional[Mixup] = None, + set_training_mode=True, + set_bn_eval=False, + writer=None, + args=None): + """ + Train the model for one epoch. + + Args: + model (torch.nn.Module): The model to be trained. + criterion (DistillationLoss): The loss function used for training. + data_loader (Iterable): The data loader for the training data. + optimizer (torch.optim.Optimizer): The optimizer used for training. + device (torch.device): The device used for training (CPU or GPU). + epoch (int): The current training epoch. + loss_scaler: The object used for gradient scaling. + clip_grad (float, optional): The maximum value for gradient clipping. Default is 0, which means no gradient clipping. + clip_mode (str, optional): The mode for gradient clipping, can be 'norm' or 'value'. Default is 'norm'. + model_ema (Optional[ModelEma], optional): The EMA (Exponential Moving Average) model for saving model weights. + mixup_fn (Optional[Mixup], optional): The function used for Mixup data augmentation. + set_training_mode (bool, optional): Whether to set the model to training mode. Default is True. + set_bn_eval (bool, optional): Whether to set the batch normalization layers to evaluation mode. Default is False. + writer (Optional[Any], optional): The object used for writing TensorBoard logs. + args (Optional[Any], optional): Additional arguments. + + Returns: + Dict[str, float]: A dictionary containing the average values of the training metrics. + """ + + model.train(set_training_mode) + num_steps = len(data_loader) + + if set_bn_eval: + set_bn_state(model) + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value:.6f}')) + header = 'Epoch: [{}]'.format(epoch) + print_freq = 50 + + for idx, (samples, targets) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + samples = samples.to(device, non_blocking=True) + targets = targets.to(device, non_blocking=True) + + if mixup_fn is not None: + samples, targets = mixup_fn(samples, targets) + + with torch.cuda.amp.autocast(): + outputs = model(samples) + loss = criterion(samples, outputs, targets) + + loss_value = loss.item() + + if not math.isfinite(loss_value): + print("Loss is {}, stopping training".format(loss_value)) + sys.exit(1) + + optimizer.zero_grad() + + # this attribute is added by timm on one optimizer (adahessian) + is_second_order = hasattr(optimizer, 'is_second_order') and optimizer.is_second_order + with torch.cuda.amp.autocast(): + loss_scaler(loss, optimizer, clip_grad=clip_grad, clip_mode=clip_mode, + parameters=model.parameters(), create_graph=is_second_order) + + torch.cuda.synchronize() + if model_ema is not None: + model_ema.update(model) + + learning_rate = optimizer.param_groups[0]["lr"] + metric_logger.update(loss=loss_value) + metric_logger.update(lr=learning_rate) + + + if idx % print_freq == 0: + if args.local_rank == 0: + iter_all_count = epoch * num_steps + idx + writer.add_scalar('loss', loss, iter_all_count) + # writer.add_scalar('grad_norm', grad_norm, iter_all_count) + writer.add_scalar('lr', learning_rate, iter_all_count) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger) + return {k: meter.global_avg for k, meter in metric_logger.meters.items()} + + +@torch.inference_mode() +def evaluate(data_loader: Iterable, model: torch.nn.Module, + device: torch.device, epoch: int, + writer, args, + visualization=True): + """ + Evaluate the model for one epoch. + + Args: + data_loader (Iterable): The data loader for the valid data. + model (torch.nn.Module): The model to be evaluated. + device (torch.device): The device used for training (CPU or GPU). + epoch (int): The current training epoch. + writer (Optional[Any], optional): The object used for writing TensorBoard logs. + args (Optional[Any], optional): Additional arguments. + visualization (bool, optional): Whether to use TensorBoard visualization. Default is True. + + Returns: + Dict[str, float]: A dictionary containing the average values of the training metrics. + """ + + criterion = torch.nn.CrossEntropyLoss() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Test:' + # switch to evaluation mode + model.eval() + + print_freq = 20 + for images, target in metric_logger.log_every(data_loader, print_freq, header): + images = images.to(device, non_blocking=True) + target = target.to(device, non_blocking=True) + + # compute output + with torch.cuda.amp.autocast(): + output = model(images) + loss = criterion(output, target) + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + + batch_size = images.shape[0] + metric_logger.update(loss=loss.item()) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + + + if visualization and args.local_rank == 0: + writer.add_scalar('Acc@1', acc1.item(), epoch) + writer.add_scalar('Acc@5', acc5.item(), epoch) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print('* Acc@1 {top1.global_avg:.3f} Acc@5 {top5.global_avg:.3f} loss {losses.global_avg:.3f}' + .format(top1=metric_logger.acc1, top5=metric_logger.acc5, losses=metric_logger.loss)) + + return {k: meter.global_avg for k, meter in metric_logger.meters.items()} \ No newline at end of file diff --git a/util/losses.py b/util/losses.py new file mode 100644 index 0000000..c9ecb21 --- /dev/null +++ b/util/losses.py @@ -0,0 +1,64 @@ +""" +Implements the knowledge distillation loss, proposed in deit +""" +import torch +from torch.nn import functional as F + + +class DistillationLoss(torch.nn.Module): + """ + This module wraps a standard criterion and adds an extra knowledge distillation loss by + taking a teacher model prediction and using it as additional supervision. + """ + + def __init__(self, base_criterion: torch.nn.Module, teacher_model: torch.nn.Module, + distillation_type: str, alpha: float, tau: float): + super().__init__() + self.base_criterion = base_criterion + self.teacher_model = teacher_model + assert distillation_type in ['none', 'soft', 'hard'] + self.distillation_type = distillation_type + self.alpha = alpha + self.tau = tau + + def forward(self, inputs, outputs, labels): + """ + Args: + inputs: The original inputs that are feed to the teacher model + outputs: the outputs of the model to be trained. It is expected to be + either a Tensor, or a Tuple[Tensor, Tensor], with the original output + in the first position and the distillation predictions as the second output + labels: the labels for the base criterion + """ + outputs_kd = None + if not isinstance(outputs, torch.Tensor): + # assume that the model outputs a tuple of [outputs, outputs_kd] + outputs, outputs_kd = outputs + base_loss = self.base_criterion(outputs, labels) + if self.distillation_type == 'none': + return base_loss + + if outputs_kd is None: + raise ValueError("When knowledge distillation is enabled, the model is " + "expected to return a Tuple[Tensor, Tensor] with the output of the " + "class_token and the dist_token") + # don't backprop throught the teacher + with torch.no_grad(): + teacher_outputs = self.teacher_model(inputs) + + if self.distillation_type == 'soft': + T = self.tau + # taken from https://github.com/peterliht/knowledge-distillation-pytorch/blob/master/model/net.py#L100 + # with slight modifications + distillation_loss = F.kl_div( + F.log_softmax(outputs_kd / T, dim=1), + F.log_softmax(teacher_outputs / T, dim=1), + reduction='sum', + log_target=True + ) * (T * T) / outputs_kd.numel() + elif self.distillation_type == 'hard': + distillation_loss = F.cross_entropy( + outputs_kd, teacher_outputs.argmax(dim=1)) + + loss = base_loss * (1 - self.alpha) + distillation_loss * self.alpha + return loss \ No newline at end of file diff --git a/util/lr_decay.py b/util/lr_decay.py new file mode 100644 index 0000000..771fecb --- /dev/null +++ b/util/lr_decay.py @@ -0,0 +1,77 @@ +# Copyright 2023 The OFA-Sys Team. +# All rights reserved. +# This source code is licensed under the Apache 2.0 license +# found in the LICENSE file in the root directory. +# -------------------------------------------------------- +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. +# -------------------------------------------------------- +# References: +# ELECTRA https://github.com/google-research/electra +# BEiT: https://github.com/microsoft/unilm/tree/master/beit +# -------------------------------------------------------- + +import json +import math + + +def param_groups_lrd(model, weight_decay=0.05, no_weight_decay_list=[], layer_decay=.75): + param_group_names = {} + param_groups = {} + + num_layers = len(model.encoder.layers) + 1 + + layer_scales = list(layer_decay ** (num_layers - i) for i in range(num_layers + 1)) + + for n, p in model.named_parameters(): + if not p.requires_grad: + continue + + # no decay: all 1D parameters and model specific ones + if p.ndim == 1 or n in no_weight_decay_list: + g_decay = "no_decay" + this_decay = 0. + else: + g_decay = "decay" + this_decay = weight_decay + + # different param_groups would make deepspeed slows down + layer_id = get_layer_id_for_vit(n, num_layers) if layer_decay < 1 else 0 + group_name = "layer_%d_%s" % (layer_id, g_decay) + + if group_name not in param_group_names: + this_scale = layer_scales[layer_id] + + param_group_names[group_name] = { + "lr_scale": this_scale, + "weight_decay": this_decay, + "params": [], + } + param_groups[group_name] = { + "lr_scale": this_scale, + "weight_decay": this_decay, + "params": [], + } + + param_group_names[group_name]["params"].append(n) + param_groups[group_name]["params"].append(p) + + print(f"parameter groups: \n{json.dumps(param_group_names, indent=2)}") + + return list(param_groups.values()) + + +def get_layer_id_for_vit(name, num_layers): + if name.startswith('image_adapter'): + return 0 + elif name.startswith('encoder.layers'): + return int(name.split('.')[2]) + 1 + else: + return num_layers + + +def inverse_sqrt_lr_decay(epoch): + """ + Inverse square-root learning rate decay. + """ + return 0.1 / math.sqrt(epoch + 1) \ No newline at end of file diff --git a/util/lr_sched.py b/util/lr_sched.py new file mode 100644 index 0000000..fbe8498 --- /dev/null +++ b/util/lr_sched.py @@ -0,0 +1,24 @@ +# Copyright 2023 The OFA-Sys Team. +# All rights reserved. +# This source code is licensed under the Apache 2.0 license +# found in the LICENSE file in the root directory. +# -------------------------------------------------------- +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. + +import math + + +def adjust_learning_rate(optimizer, epoch, args): + """Decay the learning rate with half-cycle cosine after warmup""" + if epoch < args.warmup_epochs: + lr = args.lr * epoch / args.warmup_epochs + else: + lr = args.min_lr + (args.lr - args.min_lr) * 0.5 * \ + (1. + math.cos(math.pi * (epoch - args.warmup_epochs) / (args.epochs - args.warmup_epochs))) + for param_group in optimizer.param_groups: + if "lr_scale" in param_group: + param_group["lr"] = lr * param_group["lr_scale"] + else: + param_group["lr"] = lr + return lr \ No newline at end of file diff --git a/util/optimizer.py b/util/optimizer.py new file mode 100644 index 0000000..f537875 --- /dev/null +++ b/util/optimizer.py @@ -0,0 +1,441 @@ +import torch +from torch import Tensor +from torch.optim.optimizer import Optimizer +from typing import List +import operator +import functools +from copy import copy +from math import sqrt + + + +__all__ = ['SophiaG', 'AdaFactor', 'LAMB'] + +# optimizer = SophiaG(model.parameters(), lr=args.lr, betas=(0.965, 0.99), rho=0.01, weight_decay=1e-1) + +# TODO https://arxiv.org/pdf/2209.06794v4 +# optimizer = AdaFactor(model.parameters(), lr=5e-3, beta1=0.0, beta2=0.8) + +class SophiaG(Optimizer): + def __init__(self, params, lr=1e-4, betas=(0.965, 0.99), rho=0.04, + weight_decay=1e-1, *, maximize: bool = False, + capturable: bool = False): + if not 0.0 <= lr: + raise ValueError("Invalid learning rate: {}".format(lr)) + if not 0.0 <= betas[0] < 1.0: + raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0])) + if not 0.0 <= betas[1] < 1.0: + raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1])) + if not 0.0 <= rho: + raise ValueError("Invalid rho parameter at index 1: {}".format(rho)) + if not 0.0 <= weight_decay: + raise ValueError("Invalid weight_decay value: {}".format(weight_decay)) + defaults = dict(lr=lr, betas=betas, rho=rho, + weight_decay=weight_decay, + maximize=maximize, capturable=capturable) + super(SophiaG, self).__init__(params, defaults) + + def __setstate__(self, state): + super().__setstate__(state) + for group in self.param_groups: + group.setdefault('maximize', False) + group.setdefault('capturable', False) + state_values = list(self.state.values()) + step_is_tensor = (len(state_values) != 0) and torch.is_tensor(state_values[0]['step']) + if not step_is_tensor: + for s in state_values: + s['step'] = torch.tensor(float(s['step'])) + + @torch.no_grad() + def update_hessian(self): + for group in self.param_groups: + beta1, beta2 = group['betas'] + for p in group['params']: + if p.grad is None: + continue + state = self.state[p] + + if len(state) == 0: + state['step'] = torch.zeros((1,), dtype=torch.float, device=p.device) \ + if self.defaults['capturable'] else torch.tensor(0.) + state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format) + state['hessian'] = torch.zeros_like(p, memory_format=torch.preserve_format) + + if 'hessian' not in state.keys(): + state['hessian'] = torch.zeros_like(p, memory_format=torch.preserve_format) + + state['hessian'].mul_(beta2).addcmul_(p.grad, p.grad, value=1 - beta2) + + @torch.no_grad() + def step(self, closure=None, bs=5120): + loss = None + if closure is not None: + with torch.enable_grad(): + loss = closure() + + for group in self.param_groups: + params_with_grad = [] + grads = [] + exp_avgs = [] + state_steps = [] + hessian = [] + beta1, beta2 = group['betas'] + + for p in group['params']: + if p.grad is None: + continue + params_with_grad.append(p) + + if p.grad.is_sparse: + raise RuntimeError('Hero does not support sparse gradients') + grads.append(p.grad) + state = self.state[p] + # State initialization + if len(state) == 0: + state['step'] = torch.zeros((1,), dtype=torch.float, device=p.device) \ + if self.defaults['capturable'] else torch.tensor(0.) + state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format) + state['hessian'] = torch.zeros_like(p, memory_format=torch.preserve_format) + + if 'hessian' not in state.keys(): + state['hessian'] = torch.zeros_like(p, memory_format=torch.preserve_format) + + exp_avgs.append(state['exp_avg']) + state_steps.append(state['step']) + hessian.append(state['hessian']) + + if self.defaults['capturable']: + bs = torch.ones((1,), dtype=torch.float, device=p.device) * bs + + sophiag(params_with_grad, + grads, + exp_avgs, + hessian, + state_steps, + bs=bs, + beta1=beta1, + beta2=beta2, + rho=group['rho'], + lr=group['lr'], + weight_decay=group['weight_decay'], + maximize=group['maximize'], + capturable=group['capturable']) + + return loss + + +def sophiag(params: List[Tensor], + grads: List[Tensor], + exp_avgs: List[Tensor], + hessian: List[Tensor], + state_steps: List[Tensor], + capturable: bool = False, + *, + bs: int, + beta1: float, + beta2: float, + rho: float, + lr: float, + weight_decay: float, + maximize: bool): + if not all(isinstance(t, torch.Tensor) for t in state_steps): + raise RuntimeError("API has changed, `state_steps` argument must contain a list of singleton tensors") + + func = _single_tensor_sophiag + + func(params, + grads, + exp_avgs, + hessian, + state_steps, + bs=bs, + beta1=beta1, + beta2=beta2, + rho=rho, + lr=lr, + weight_decay=weight_decay, + maximize=maximize, + capturable=capturable) + + +def _single_tensor_sophiag(params: List[Tensor], + grads: List[Tensor], + exp_avgs: List[Tensor], + hessian: List[Tensor], + state_steps: List[Tensor], + *, + bs: int, + beta1: float, + beta2: float, + rho: float, + lr: float, + weight_decay: float, + maximize: bool, + capturable: bool): + for i, param in enumerate(params): + grad = grads[i] if not maximize else -grads[i] + exp_avg = exp_avgs[i] + hess = hessian[i] + step_t = state_steps[i] + + if capturable: + assert param.is_cuda and step_t.is_cuda and bs.is_cuda + + if torch.is_complex(param): + grad = torch.view_as_real(grad) + exp_avg = torch.view_as_real(exp_avg) + hess = torch.view_as_real(hess) + param = torch.view_as_real(param) + + # update step + step_t += 1 + + # Perform stepweight decay + param.mul_(1 - lr * weight_decay) + + # Decay the first and second moment running average coefficient + exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1) + + if capturable: + step = step_t + step_size = lr + step_size_neg = step_size.neg() + + ratio = (exp_avg.abs() / (rho * bs * hess + 1e-15)).clamp(None, 1) + param.addcmul_(exp_avg.sign(), ratio, value=step_size_neg) + else: + step = step_t.item() + step_size_neg = - lr + + ratio = (exp_avg.abs() / (rho * bs * hess + 1e-15)).clamp(None, 1) + param.addcmul_(exp_avg.sign(), ratio, value=step_size_neg) + + + +class AdaFactor(Optimizer): + def __init__(self, params, lr=None, beta1=0.9, beta2=0.999, eps1=1e-30, + eps2=1e-3, cliping_threshold=1, non_constant_decay=True, + enable_factorization=True, ams_grad=True, weight_decay=0): + + enable_momentum = beta1 != 0 + self.beta1_glob = copy(beta1) + self.beta2_glob = copy(beta2) + self.lr_glob = copy(lr) + + beta1 = self.beta1_glob if hasattr(beta1, '__call__') else lambda x: self.beta1_glob + beta2 = self.beta2_glob if hasattr(beta2, '__call__') else lambda x: self.beta2_glob + + if non_constant_decay: + ams_grad = False + if isinstance(self.beta1_glob, float): + beta1 = lambda t: self.beta1_glob * (1 - self.beta1_glob ** (t - 1)) / (1 - self.beta1_glob ** t) + if isinstance(self.beta2_glob, float): + beta2 = lambda t: self.beta2_glob * (1 - self.beta2_glob ** (t - 1)) / (1 - self.beta2_glob ** t) + + relative_step_size = True + + if lr is None: + # default value from article + lr = lambda t: min(1e-2, 1 / sqrt(t)) + + if isinstance(self.lr_glob, float): + lr = lambda x: self.lr_glob + relative_step_size = False + + defaults = dict(lr=lr, beta1=beta1, beta2=beta2, eps1=eps1, + eps2=eps2, cliping_threshold=cliping_threshold, weight_decay=weight_decay, ams_grad=ams_grad, + enable_factorization=enable_factorization, + enable_momentum=enable_momentum, relative_step_size=relative_step_size) + + super(AdaFactor, self).__init__(params, defaults) + + def __setstate__(self, state): + super(AdaFactor, self).__setstate__(state) + + def _experimental_reshape(self, shape): + temp_shape = shape[2:] + if len(temp_shape) == 1: + new_shape = (shape[0], shape[1] * shape[2]) + else: + tmp_div = len(temp_shape) // 2 + len(temp_shape) % 2 + new_shape = (shape[0] * functools.reduce(operator.mul, temp_shape[tmp_div:], 1), + shape[1] * functools.reduce(operator.mul, temp_shape[:tmp_div], 1)) + return new_shape, copy(shape) + + def _check_shape(self, shape): + ''' + output1 - True - algorithm for matrix, False - vector; + output2 - need reshape + ''' + if len(shape) > 2: + return True, True + elif len(shape) == 2: + return True, False + elif len(shape) == 2 and (shape[0] == 1 or shape[1] == 1): + return False, False + else: + return False, False + + def _rms(self, x): + return sqrt(torch.mean(x.pow(2))) + + def step(self, closure=None): + loss = None + if closure is not None: + loss = closure() + for group in self.param_groups: + for p in group['params']: + if p.grad is None: + continue + grad = p.grad.data + data_backup = p.data.clone().detach() + + if grad.is_sparse: + raise RuntimeError('Adam does not support sparse gradients, please consider SparseAdam instead') + + is_matrix, is_need_reshape = self._check_shape(grad.size()) + new_shape = p.data.size() + if is_need_reshape and group['enable_factorization']: + new_shape, old_shape = \ + self._experimental_reshape(p.data.size()) + grad = grad.view(new_shape) + + state = self.state[p] + if len(state) == 0: + state['step'] = 0 + if group['enable_momentum']: + state['exp_avg'] = torch.zeros(new_shape, dtype=torch.float32, device=p.grad.device) + + if is_matrix and group['enable_factorization']: + state['exp_avg_sq_R'] = torch.zeros((1, new_shape[1]), dtype=torch.float32, + device=p.grad.device) + state['exp_avg_sq_C'] = torch.zeros((new_shape[0], 1), dtype=torch.float32, + device=p.grad.device) + else: + state['exp_avg_sq'] = torch.zeros(new_shape, dtype=torch.float32, device=p.grad.device) + if group['ams_grad']: + state['exp_avg_sq_hat'] = torch.zeros(new_shape, dtype=torch.float32, device=p.grad.device) + + if group['enable_momentum']: + exp_avg = state['exp_avg'] + + if is_matrix and group['enable_factorization']: + exp_avg_sq_R = state['exp_avg_sq_R'] + exp_avg_sq_C = state['exp_avg_sq_C'] + else: + exp_avg_sq = state['exp_avg_sq'] + + if group['ams_grad']: + exp_avg_sq_hat = state['exp_avg_sq_hat'] + + state['step'] += 1 + lr_t = group['lr'](state['step']) + if group['relative_step_size']: + lr_t *= max(group['eps2'], self._rms(p.data)) + + if group['enable_momentum']: + beta1_t = group['beta1'](state['step']) + exp_avg.mul_(beta1_t).add_(1 - beta1_t, grad) + + beta2_t = group['beta2'](state['step']) + + if is_matrix and group['enable_factorization']: + exp_avg_sq_R.mul_(beta2_t).add_(1 - beta2_t, + torch.sum(torch.mul(grad, grad).add_(group['eps1']), dim=0, + keepdim=True)) + exp_avg_sq_C.mul_(beta2_t).add_(1 - beta2_t, + torch.sum(torch.mul(grad, grad).add_(group['eps1']), dim=1, + keepdim=True)) + v = torch.mul(exp_avg_sq_C, exp_avg_sq_R).div_(torch.sum(exp_avg_sq_R)) + else: + exp_avg_sq.mul_(beta2_t).addcmul_(1 - beta2_t, grad, grad).add_((1 - beta2_t) * group['eps1']) + v = exp_avg_sq + + g = grad + if group['enable_momentum']: + g = torch.div(exp_avg, 1 - beta1_t ** state['step']) + + if group['ams_grad']: + torch.max(exp_avg_sq_hat, v, out=exp_avg_sq_hat) + v = exp_avg_sq_hat + u = torch.div(g, (torch.div(v, 1 - beta2_t ** state['step'])).sqrt().add_(group['eps1'])) + else: + u = torch.div(g, v.sqrt()) + + u.div_(max(1, self._rms(u) / group['cliping_threshold'])) + p.data.add_(-lr_t * (u.view(old_shape) if is_need_reshape and group['enable_factorization'] else u)) + + if group['weight_decay'] != 0: + p.data.add_(-group['weight_decay'] * lr_t, data_backup) + + return loss + + + +# Define the LAMB optimizer +# See paper [Large Batch Optimization for Deep Learning: Training BERT in 76 minutes](https://arxiv.org/abs/1904.00962). +class LAMB(Optimizer): + def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-6, weight_decay=0.01): + if not lr > 0.0: + raise ValueError("Invalid learning rate: {}".format(lr)) + if not 0.0 <= eps: + raise ValueError("Invalid epsilon value: {}".format(eps)) + if not 0.0 <= betas[0] < 1.0: + raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0])) + if not 0.0 <= betas[1] < 1.0: + raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1])) + + defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay) + super(LAMB, self).__init__(params, defaults) + + def step(self, closure=None): + loss = None + if closure is not None: + loss = closure() + + for group in self.param_groups: + for p in group['params']: + if p.grad is None: + continue + grad = p.grad.data + if grad.is_sparse: + raise RuntimeError('LAMB does not support sparse gradients') + + state = self.state[p] + + # State initialization + if len(state) == 0: + state['step'] = 0 + state['exp_avg'] = torch.zeros_like(p.data) + state['exp_avg_sq'] = torch.zeros_like(p.data) + + exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq'] + beta1, beta2 = group['betas'] + + state['step'] += 1 + bias_correction1 = 1 - beta1 ** state['step'] + bias_correction2 = 1 - beta2 ** state['step'] + + # Decay the first and second moment running average coefficient + exp_avg.mul_(beta1).add_(1 - beta1, grad) + exp_avg_sq.mul_(beta2).addcmul_(1 - beta2, grad, grad) + + denom = (exp_avg_sq.sqrt() / torch.sqrt(torch.tensor(bias_correction2))).add_(group['eps']) + step_size = group['lr'] / bias_correction1 + + # LAMB update + weight_norm = torch.norm(p.data) + update = exp_avg / denom + update_norm = torch.norm(update) + if weight_norm == 0 or update_norm == 0: + trust_ratio = 1 + else: + trust_ratio = weight_norm / update_norm + + p.data.add_(-step_size * trust_ratio, update) + + # Weight decay + if group['weight_decay'] != 0: + p.data.add_(-group['lr'] * group['weight_decay'], p.data) + + return loss \ No newline at end of file diff --git a/util/samplers.py b/util/samplers.py new file mode 100644 index 0000000..2a46609 --- /dev/null +++ b/util/samplers.py @@ -0,0 +1,66 @@ +# Copyright (c) 2015-present, Facebook, Inc. +# All rights reserved. +import torch +import torch.distributed as dist +import math + + +class RASampler(torch.utils.data.Sampler): + """Sampler that restricts data loading to a subset of the datasets for distributed, + with repeated augmentation. + It ensures that different each augmented version of a sample will be visible to a + different process (GPU) + Heavily based on torch.util.data.DistributedSampler + """ + + def __init__(self, dataset, num_replicas=None, rank=None, shuffle=True, num_repeats: int = 3): + if num_replicas is None: + if not dist.is_available(): + raise RuntimeError("Requires distributed package to be available") + num_replicas = dist.get_world_size() + if rank is None: + if not dist.is_available(): + raise RuntimeError("Requires distributed package to be available") + rank = dist.get_rank() + if num_repeats < 1: + raise ValueError("num_repeats should be greater than 0") + self.dataset = dataset + self.num_replicas = num_replicas + self.rank = rank + self.num_repeats = num_repeats + self.epoch = 0 + self.num_samples = int(math.ceil(len(self.dataset) * self.num_repeats / self.num_replicas)) + self.total_size = self.num_samples * self.num_replicas + # self.num_selected_samples = int(math.ceil(len(self.datasets) / self.num_replicas)) + self.num_selected_samples = int(math.floor(len(self.dataset) // 256 * 256 / self.num_replicas)) + self.shuffle = shuffle + + def __iter__(self): + if self.shuffle: + # deterministically shuffle based on epoch + g = torch.Generator() + g.manual_seed(self.epoch) + indices = torch.randperm(len(self.dataset), generator=g) + else: + indices = torch.arange(start=0, end=len(self.dataset)) + + # add extra samples to make it evenly divisible + indices = torch.repeat_interleave(indices, repeats=self.num_repeats, dim=0).tolist() + padding_size: int = self.total_size - len(indices) + if padding_size > 0: + indices += indices[:padding_size] + assert len(indices) == self.total_size + + # subsample + indices = indices[self.rank:self.total_size:self.num_replicas] + assert len(indices) == self.num_samples + + return iter(indices[:self.num_selected_samples]) + + def __len__(self): + return self.num_selected_samples + + def set_epoch(self, epoch): + self.epoch = epoch + + diff --git a/util/utils.py b/util/utils.py new file mode 100644 index 0000000..71b5018 --- /dev/null +++ b/util/utils.py @@ -0,0 +1,279 @@ +""" +Misc functions, including distributed helpers and model loaders +Also include a model loader specified for finetuning EfficientViT +""" +import io +import os +import time +from collections import defaultdict, deque +import datetime +import logging +import torch +import torch.nn as nn +import torch.distributed as dist + +logger = logging.getLogger() + + +class AverageMeter: + """Computes and stores the average and current value""" + def __init__(self): + self.reset() + + def reset(self): + self.val = 0 + self.avg = 0 + self.sum = 0 + self.count = 0 + + def update(self, val, n=1): + self.val = val + self.sum += val * n + self.count += n + self.avg = self.sum / self.count + + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], + dtype=torch.float64, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) + + +class MetricLogger(object): + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = [ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ] + if torch.cuda.is_available(): + log_msg.append('max mem: {memory:.0f}') + log_msg = self.delimiter.join(log_msg) + MB = 1024.0 * 1024.0 + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0 or i == len(iterable) - 1: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + if torch.cuda.is_available(): + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time), + memory=torch.cuda.max_memory_allocated() / MB)) + else: + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {} ({:.4f} s / it)'.format( + header, total_time_str, total_time / len(iterable))) + + + +def _load_checkpoint_for_ema(model_ema, checkpoint): + """ + Workaround for ModelEma._load_checkpoint to accept an already-loaded object + """ + mem_file = io.BytesIO() + torch.save(checkpoint, mem_file) + mem_file.seek(0) + model_ema._load_checkpoint(mem_file) + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + +def is_main_process(): + return get_rank() == 0 + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False + return + + args.distributed = True + + torch.cuda.set_device(args.gpu) + args.dist_backend = 'nccl' + print('| distributed init (rank {}): {}'.format( + args.rank, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=args.dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) + + +def replace_batchnorm(net): + for child_name, child in net.named_children(): + if hasattr(child, 'fuse'): + setattr(net, child_name, child.fuse()) + elif isinstance(child, torch.nn.BatchNorm2d): + setattr(net, child_name, torch.nn.Identity()) + else: + replace_batchnorm(child) + +def replace_layernorm(net): + import apex + for child_name, child in net.named_children(): + if isinstance(child, torch.nn.LayerNorm): + setattr(net, child_name, apex.normalization.FusedLayerNorm( + child.weight.size(0))) + else: + replace_layernorm(child) + +def load_model(modelpath, model: nn.Module): + ''' + A function to load model from a checkpoint, which is used + for fine-tuning on a different resolution. + ''' + checkpoint = torch.load(modelpath, map_location='cpu') + return checkpoint \ No newline at end of file