From d11f08b52ed84d28374125c00ecb61dbe7ef3697 Mon Sep 17 00:00:00 2001 From: wangwl Date: Thu, 18 Dec 2025 01:17:55 +0000 Subject: [PATCH 1/3] add BagOfTricks --- .../README.md | 61 ++ .../conf/__init__.py | 15 + .../conf/settings.py | 45 ++ .../criterion/FocalLoss.py | 5 + .../criterion/LabelSmoothing.py | 88 +++ .../criterion/__init__.py | 2 + .../dataset/dataset.py | 182 +++++ .../lr_find.py | 126 ++++ .../lr_scheduler/FindLR.py | 23 + .../lr_scheduler/WarmUpLR.py | 24 + .../lr_scheduler/__init__.py | 3 + .../train.py | 195 +++++ .../transforms/__init__.py | 13 + .../transforms/transforms.py | 404 +++++++++++ .../utils.py | 219 ++++++ .../Classification/BagOfTricks/coverage.txt | 3 + .../Classification/BagOfTricks/vgg.py | 101 +++ .../Classification/BagOfTricks/vgg_loss.jpg | Bin 0 -> 36139 bytes .../Classification/BagOfTricks/vgg_loss.txt | 29 + .../BagOfTricks/weloTrainStep.py | 674 ++++++++++++++++++ 20 files changed, 2212 insertions(+) create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/README.md create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/__init__.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/settings.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/FocalLoss.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/LabelSmoothing.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/__init__.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/dataset/dataset.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_find.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/FindLR.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/WarmUpLR.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/__init__.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/train.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/__init__.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/transforms.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/utils.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/coverage.txt create mode 100644 PyTorch/build-in/Classification/BagOfTricks/vgg.py create mode 100644 PyTorch/build-in/Classification/BagOfTricks/vgg_loss.jpg create mode 100644 PyTorch/build-in/Classification/BagOfTricks/vgg_loss.txt create mode 100644 PyTorch/build-in/Classification/BagOfTricks/weloTrainStep.py diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/README.md b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/README.md new file mode 100644 index 000000000..5c62fbd7e --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/README.md @@ -0,0 +1,61 @@ +# Bag of Tricks for Image Classification with Convolutional Neural Networks + + +This repo was inspired by Paper [Bag of Tricks for Image Classification with Convolutional Neural Networks](https://arxiv.org/abs/1812.01187) + +I would test popular training tricks as many as I can for improving image classification accuarcy, feel +free to leave a comment about the tricks you want me to test(please write the referenced paper along with +the tricks) + +## hardware +Using 4 Tesla P40 to run the experiments + +## dataset + +I will use [CUB_200_2011](http://www.vision.caltech.edu/visipedia/CUB-200-2011.html) dataset instead of ImageNet, +just for simplicity, this is a fine-grained image classification dataset, which contains 200 birds categlories, +5K+ training images, and 5K+ test images.The state of the art acc on vgg16 is around 73%(please correct me if +I was wrong).You could easily change it to the ones you like: [Stanford Dogs](http://vision.stanford.edu/aditya86/ImageNetDogs/), [Stanford Cars](http://vision.stanford.edu/aditya86/ImageNetDogs/). +Or even ImageNet. + +## network + +Use a VGG16 network to test my tricks, also for simplicity reasons, since VGG16 is easy to implement. I'm considering +switch to AlexNet, to see how powerful these tricks are. + +## tricks + +tricks I've tested, some of them were from the Paper [Bag of Tricks for Image Classification with Convolutional Neural Networks](https://arxiv.org/abs/1812.01187) : + +|trick|referenced paper| +|:---:|:---:| +|xavier init|[Understanding the difficulty of training deep feedforward neural networks](http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf)| +|warmup training|[Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour](https://arxiv.org/abs/1706.02677v2)| +|no bias decay|[Highly Scalable Deep Learning Training System with Mixed-Precision: Training ImageNet in Four Minutes](https://arxiv.org/abs/1807.11205vx)| +|label smoothing|[Rethinking the inception architecture for computer vision](https://arxiv.org/abs/1512.00567v3))| +|random erasing|[Random Erasing Data Augmentation](https://arxiv.org/abs/1708.04896v2)| +|cutout|[Improved Regularization of Convolutional Neural Networks with Cutout](https://arxiv.org/abs/1708.04552v2)| +|linear scaling learning rate|[Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour](https://arxiv.org/abs/1706.02677v2)| +|cosine learning rate decay|[SGDR: Stochastic Gradient Descent with Warm Restarts](https://arxiv.org/abs/1608.03983)| + +**and more to come......** + + +## result + +baseline(training from sctrach, no ImageNet pretrain weights are used): + +vgg16 64.60% on [CUB_200_2011](http://www.vision.caltech.edu/visipedia/CUB-200-2011.html) dataset, lr=0.01, batchsize=64 + +effects of stacking tricks + +|trick|acc| +|:---:|:---:| +|baseline|64.60%| +|+xavier init and warmup training|66.07%| +|+no bias decay|70.14%| +|+label smoothing|71.20%| +|+random erasing|does not work, drops about 4 points| +|+linear scaling learning rate(batchsize 256, lr 0.04)|71.21%| +|+cutout|does not work, drops about 1 point| +|+cosine learning rate decay|does not work, drops about 1 point| \ No newline at end of file diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/__init__.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/__init__.py new file mode 100644 index 000000000..ac1b73748 --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/__init__.py @@ -0,0 +1,15 @@ +""" dynamically load settings +author baiyu +""" + +import conf.settings as settings + + +class Settings: + def __init__(self, settings): + for attr in dir(settings): + if attr.isupper(): + setattr(self, attr, getattr(settings, attr)) + +settings = Settings(settings) + diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/settings.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/settings.py new file mode 100644 index 000000000..0c312806d --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/settings.py @@ -0,0 +1,45 @@ + +#TRAIN_PATH = '/Users/didi/Downloads/train' +#LABEL_PATH = '/Users/didi/Downloads/' + +#TRAIN_STD = [0.2648431067741028, 0.2606256902653438, 0.26538460981082046] +#TRAIN_MEAN = [0.39091378793393816, 0.45037754368027466, 0.47364248847284746] + +from datetime import datetime + + +#cv2 +#TRAIN_MEAN = [0.43237713785804116, 0.49941626449353244, 0.48560741861744905] +#TRAIN_STD = [0.2665100547329813, 0.22770540015765814, 0.2321024260764962] + +TRAIN_MEAN = [0.48560741861744905, 0.49941626449353244, 0.43237713785804116] +TRAIN_STD = [0.2321024260764962, 0.22770540015765814, 0.2665100547329813] + +#TEST_MEAN = [0.4311430419332438, 0.4998156522834164, 0.4862169586881995] +#TEST_STD = [0.26667253517177186, 0.22781080253662814, 0.23264268069040475] + +TEST_MEAN = [0.4862169586881995, 0.4998156522834164, 0.4311430419332438] +TEST_STD = [0.23264268069040475, 0.22781080253662814, 0.26667253517177186] + +#DATA_PATH = '/nfs/cold_project/baiyu/Caltech-UCSD Birds-200-2011/CUB_200_2011' +DATA_PATH = '/nfs/project/baiyu/CUB200_2011/CUB_200_2011' + +#MILESTONES = [100, 130, 160] +MILESTONES = [300, 350, 400] + +#weights file directory +CHECKPOINT_PATH = 'checkpoints' + +TIME_NOW = datetime.now().isoformat() + +#tensorboard log file directory +LOG_DIR = 'runs' + +#save weights file per SAVE_EPOCH epoch +SAVE_EPOCH = 10 + +#input image size for network +IMAGE_SIZE = 224 + + + diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/FocalLoss.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/FocalLoss.py new file mode 100644 index 000000000..4be827330 --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/FocalLoss.py @@ -0,0 +1,5 @@ + + +import torch +import torch.nn as nn + diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/LabelSmoothing.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/LabelSmoothing.py new file mode 100644 index 000000000..7136dad45 --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/LabelSmoothing.py @@ -0,0 +1,88 @@ + +import torch +import torch.nn as nn + + +class LSR(nn.Module): + + def __init__(self, e=0.1, reduction='mean'): + super().__init__() + + self.log_softmax = nn.LogSoftmax(dim=1) + self.e = e + self.reduction = reduction + + def _one_hot(self, labels, classes, value=1): + """ + Convert labels to one hot vectors + + Args: + labels: torch tensor in format [label1, label2, label3, ...] + classes: int, number of classes + value: label value in one hot vector, default to 1 + + Returns: + return one hot format labels in shape [batchsize, classes] + """ + + one_hot = torch.zeros(labels.size(0), classes) + + #labels and value_added size must match + labels = labels.view(labels.size(0), -1) + value_added = torch.Tensor(labels.size(0), 1).fill_(value) + + value_added = value_added.to(labels.device) + one_hot = one_hot.to(labels.device) + + one_hot.scatter_add_(1, labels, value_added) + + return one_hot + + def _smooth_label(self, target, length, smooth_factor): + """convert targets to one-hot format, and smooth + them. + + Args: + target: target in form with [label1, label2, label_batchsize] + length: length of one-hot format(number of classes) + smooth_factor: smooth factor for label smooth + + Returns: + smoothed labels in one hot format + """ + one_hot = self._one_hot(target, length, value=1 - smooth_factor) + one_hot += smooth_factor / length + + return one_hot.to(target.device) + + def forward(self, x, target): + + if x.size(0) != target.size(0): + raise ValueError('Expected input batchsize ({}) to match target batch_size({})' + .format(x.size(0), target.size(0))) + + if x.dim() < 2: + raise ValueError('Expected input tensor to have least 2 dimensions(got {})' + .format(x.size(0))) + + if x.dim() != 2: + raise ValueError('Only 2 dimension tensor are implemented, (got {})' + .format(x.size())) + + + smoothed_target = self._smooth_label(target, x.size(1), self.e) + x = self.log_softmax(x) + loss = torch.sum(- x * smoothed_target, dim=1) + + if self.reduction == 'none': + return loss + + elif self.reduction == 'sum': + return torch.sum(loss) + + elif self.reduction == 'mean': + return torch.mean(loss) + + else: + raise ValueError('unrecognized option, expect reduction to be one of none, mean, sum') + diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/__init__.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/__init__.py new file mode 100644 index 000000000..13c4f358e --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/__init__.py @@ -0,0 +1,2 @@ + +from .LabelSmoothing import LSR \ No newline at end of file diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/dataset/dataset.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/dataset/dataset.py new file mode 100644 index 000000000..016d9c67c --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/dataset/dataset.py @@ -0,0 +1,182 @@ + +import os + +import cv2 +import numpy as np +from torch.utils.data import Dataset +from PIL import Image + +class CUB_200_2011_Train(Dataset): + + def __init__(self, path, transform=None, target_transform=None): + + self.root = path + self.transform = transform + self.target_transform = target_transform + self.images_path = {} + with open(os.path.join(self.root, 'images.txt')) as f: + for line in f: + image_id, path = line.split() + self.images_path[image_id] = path + + self.class_ids = {} + with open(os.path.join(self.root, 'image_class_labels.txt')) as f: + for line in f: + image_id, class_id = line.split() + self.class_ids[image_id] = class_id + + self.train_id = [] + with open(os.path.join(self.root, 'train_test_split.txt')) as f: + for line in f: + image_id, is_train = line.split() + if int(is_train): + self.train_id.append(image_id) + + def __len__(self): + return len(self.train_id) + + def __getitem__(self, index): + """ + Args: + index: index of training dataset + Returns: + image and its corresponding label + """ + image_id = self.train_id[index] + class_id = int(self._get_class_by_id(image_id)) - 1 + path = self._get_path_by_id(image_id) + image = cv2.imread(os.path.join(self.root, 'images', path)) + #image = Image.open(os.path.join(self.root, 'images', path)) + #if image.mode != 'RGB': + # image = image.convert('RGB') + #if len(image.shape) != 3: + # image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) + #image = np.array(image) + + if self.transform: + image = self.transform(image) + + if self.target_transform: + class_id = self.target_transform(class_id) + return image, class_id + + def _get_path_by_id(self, image_id): + + return self.images_path[image_id] + + def _get_class_by_id(self, image_id): + + return self.class_ids[image_id] + + +class CUB_200_2011_Test(Dataset): + + def __init__(self, path, transform=None, target_transform=None): + + self.root = path + self.transform = transform + self.target_transform = target_transform + self.images_path = {} + with open(os.path.join(self.root, 'images.txt')) as f: + for line in f: + image_id, path = line.split() + self.images_path[image_id] = path + + self.class_ids = {} + with open(os.path.join(self.root, 'image_class_labels.txt')) as f: + for line in f: + image_id, class_id = line.split() + self.class_ids[image_id] = class_id + + self.train_id = [] + with open(os.path.join(self.root, 'train_test_split.txt')) as f: + for line in f: + image_id, is_train = line.split() + if not int(is_train): + self.train_id.append(image_id) + + def __len__(self): + return len(self.train_id) + + def __getitem__(self, index): + """ + Args: + index: index of training dataset + Returns: + image and its corresponding label + """ + image_id = self.train_id[index] + class_id = int(self._get_class_by_id(image_id)) - 1 + path = self._get_path_by_id(image_id) + image = cv2.imread(os.path.join(self.root, 'images', path)) + #image = Image.open(os.path.join(self.root, 'images', path)) + #if image.mode != 'RGB': + # image = image.convert('RGB') + #image = np.array(image) + + + #if len(image.shape) != 3: + # image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) + + + if self.transform: + image = self.transform(image) + + if self.target_transform: + class_id = self.target_transform(class_id) + + return image, class_id + + def _get_path_by_id(self, image_id): + + return self.images_path[image_id] + + def _get_class_by_id(self, image_id): + + return self.class_ids[image_id] + + +def compute_mean_and_std(dataset): + """Compute dataset mean and std, and normalize it + + Args: + dataset: instance of CUB_200_2011_Train, CUB_200_2011_Test + + Returns: + return: mean and std of this dataset + """ + + mean_r = 0 + mean_g = 0 + mean_b = 0 + + for img, _ in dataset: + mean_b += np.mean(img[:, :, 0]) + mean_g += np.mean(img[:, :, 1]) + mean_r += np.mean(img[:, :, 2]) + + mean_b /= len(dataset) + mean_g /= len(dataset) + mean_r /= len(dataset) + + diff_r = 0 + diff_g = 0 + diff_b = 0 + + N = 0 + + for img, _ in dataset: + + diff_b += np.sum(np.power(img[:, :, 0] - mean_b, 2)) + diff_g += np.sum(np.power(img[:, :, 1] - mean_g, 2)) + diff_r += np.sum(np.power(img[:, :, 2] - mean_r, 2)) + + N += np.prod(img[:, :, 0].shape) + + std_b = np.sqrt(diff_b / N) + std_g = np.sqrt(diff_g / N) + std_r = np.sqrt(diff_r / N) + + mean = (mean_b.item() / 255.0, mean_g.item() / 255.0, mean_r.item() / 255.0) + std = (std_b.item() / 255.0, std_g.item() / 255.0, std_r.item() / 255.0) + return mean, std diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_find.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_find.py new file mode 100644 index 000000000..4a6ea5f3d --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_find.py @@ -0,0 +1,126 @@ + +import argparse +import glob +import os + +import cv2 +import torch +import torch.nn as nn +import torch.optim as optim +from torch.utils.data import DataLoader +import numpy as np + +#from PIL import Image +import transforms +#from torchvision import transforms +from tensorboardX import SummaryWriter +from conf import settings +from utils import * +from lr_scheduler import FindLR +from criterion import LSR + +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as plt + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-net', type=str, required=True, help='net type') + parser.add_argument('-w', type=int, default=2, help='number of workers for dataloader') + parser.add_argument('-b', type=int, default=64, help='batch size for dataloader') + parser.add_argument('-base_lr', type=float, default=1e-7, help='min learning rate') + parser.add_argument('-max_lr', type=float, default=10, help='max learning rate') + parser.add_argument('-num_iter', type=int, default=100, help='num of iteration') + parser.add_argument('-gpus', nargs='+', type=int, default=0, help='gpu device') + args = parser.parse_args() + + + train_transforms = transforms.Compose([ + #transforms.ToPILImage(), + transforms.ToCVImage(), + transforms.RandomResizedCrop(settings.IMAGE_SIZE), + transforms.RandomHorizontalFlip(), + transforms.ColorJitter(brightness=0.4, saturation=0.4, hue=0.4), + #transforms.RandomErasing(), + #transforms.CutOut(56), + transforms.ToTensor(), + transforms.Normalize(settings.TRAIN_MEAN, settings.TRAIN_STD) + ]) + + train_dataloader = get_train_dataloader( + settings.DATA_PATH, + train_transforms, + args.b, + args.w + ) + + net = get_network(args) + net = init_weights(net) + + if isinstance(args.gpus, int): + args.gpus = [args.gpus] + + net = nn.DataParallel(net, device_ids=args.gpus) + net = net.cuda() + + lsr_loss = LSR() + + #apply no weight decay on bias + params = split_weights(net) + optimizer = optim.SGD(params, lr=args.base_lr, momentum=0.9, weight_decay=1e-4, nesterov=True) + + #set up warmup phase learning rate scheduler + lr_scheduler = FindLR(optimizer, max_lr=args.max_lr, num_iter=args.num_iter) + epoches = int(args.num_iter / len(train_dataloader)) + 1 + + n = 0 + learning_rate = [] + losses = [] + for epoch in range(epoches): + + #training procedure + net.train() + + for batch_index, (images, labels) in enumerate(train_dataloader): + if n > args.num_iter: + break + + lr_scheduler.step() + + images = images.cuda() + labels = labels.cuda() + + optimizer.zero_grad() + predicts = net(images) + loss = lsr_loss(predicts, labels) + if torch.isnan(loss).any(): + n += 1e8 + break + loss.backward() + optimizer.step() + + n_iter = (epoch - 1) * len(train_dataloader) + batch_index + 1 + print('Iterations: {iter_num} [{trained_samples}/{total_samples}]\tLoss: {:0.4f}\tLR: {:0.8f}'.format( + loss.item(), + optimizer.param_groups[0]['lr'], + iter_num=n, + trained_samples=batch_index * args.b + len(images), + total_samples=len(train_dataloader.dataset), + )) + + learning_rate.append(optimizer.param_groups[0]['lr']) + losses.append(loss.item()) + n += 1 + + learning_rate = learning_rate[10:-5] + losses = losses[10:-5] + + fig, ax = plt.subplots(1,1) + ax.plot(learning_rate, losses) + ax.set_xlabel('learning rate') + ax.set_ylabel('losses') + ax.set_xscale('log') + ax.xaxis.set_major_formatter(plt.FormatStrFormatter('%.0e')) + + fig.savefig('result.jpg') + diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/FindLR.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/FindLR.py new file mode 100644 index 000000000..72a31c42e --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/FindLR.py @@ -0,0 +1,23 @@ + +import torch +from torch.optim.lr_scheduler import _LRScheduler + + +class FindLR(_LRScheduler): + """exponentially increasing learning rate + + Args: + optimizer: optimzier(e.g. SGD) + num_iter: totoal_iters + max_lr: maximum learning rate + """ + def __init__(self, optimizer, max_lr=10, num_iter=100, last_epoch=-1): + + self.total_iters = num_iter + self.max_lr = max_lr + super().__init__(optimizer, last_epoch) + + def get_lr(self): + + return [base_lr * (self.max_lr / base_lr) ** (self.last_epoch / (self.total_iters + 1e-32)) for base_lr in self.base_lrs] + diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/WarmUpLR.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/WarmUpLR.py new file mode 100644 index 000000000..3d9cd832e --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/WarmUpLR.py @@ -0,0 +1,24 @@ + +import torch +from torch.optim.lr_scheduler import _LRScheduler + + +class WarmUpLR(_LRScheduler): + """warmup_training learning rate scheduler + + Args: + optimizer: optimzier(e.g. SGD) + total_iters: totoal_iters of warmup phase + """ + def __init__(self, optimizer, total_iters, last_epoch=-1): + + self.total_iters = total_iters + super().__init__(optimizer, last_epoch) + + def get_lr(self): + """we will use the first m batches, and set the learning + rate to base_lr * m / total_iters + """ + return [base_lr * self.last_epoch / (self.total_iters + 1e-8) for base_lr in self.base_lrs] + + diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/__init__.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/__init__.py new file mode 100644 index 000000000..f5273d512 --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/__init__.py @@ -0,0 +1,3 @@ + +from .WarmUpLR import WarmUpLR +from .FindLR import FindLR \ No newline at end of file diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/train.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/train.py new file mode 100644 index 000000000..2607c39d2 --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/train.py @@ -0,0 +1,195 @@ + +"""author + baiyu +""" + +import argparse +import glob +import os + +import torch +import torch.nn as nn +import torch.optim as optim +import numpy as np + +#from PIL import Image +import transforms +#from torchvision import transforms +from tensorboardX import SummaryWriter +from conf import settings +from utils import * +from lr_scheduler import WarmUpLR +from criterion import LSR + +if __name__ == '__main__': + + parser = argparse.ArgumentParser() + parser.add_argument('-net', type=str, required=True, help='net type') + parser.add_argument('-w', type=int, default=2, help='number of workers for dataloader') + parser.add_argument('-b', type=int, default=256, help='batch size for dataloader') + parser.add_argument('-lr', type=float, default=0.04, help='initial learning rate') + parser.add_argument('-e', type=int, default=450, help='training epoches') + parser.add_argument('-warm', type=int, default=5, help='warm up phase') + parser.add_argument('-gpus', nargs='+', type=int, default=0, help='gpu device') + args = parser.parse_args() + + #checkpoint directory + checkpoint_path = os.path.join(settings.CHECKPOINT_PATH, args.net, settings.TIME_NOW) + if not os.path.exists(checkpoint_path): + os.makedirs(checkpoint_path) + checkpoint_path = os.path.join(checkpoint_path, '{net}-{epoch}-{type}.pth') + + #tensorboard log directory + log_path = os.path.join(settings.LOG_DIR, args.net, settings.TIME_NOW) + if not os.path.exists(log_path): + os.makedirs(log_path) + writer = SummaryWriter(log_dir=log_path) + + #get dataloader + train_transforms = transforms.Compose([ + #transforms.ToPILImage(), + transforms.ToCVImage(), + transforms.RandomResizedCrop(settings.IMAGE_SIZE), + transforms.RandomHorizontalFlip(), + transforms.ColorJitter(brightness=0.4, saturation=0.4, hue=0.4), + #transforms.RandomErasing(), + #transforms.CutOut(56), + transforms.ToTensor(), + transforms.Normalize(settings.TRAIN_MEAN, settings.TRAIN_STD) + ]) + + test_transforms = transforms.Compose([ + transforms.ToCVImage(), + transforms.CenterCrop(settings.IMAGE_SIZE), + transforms.ToTensor(), + transforms.Normalize(settings.TRAIN_MEAN, settings.TRAIN_STD) + ]) + + train_dataloader = get_train_dataloader( + settings.DATA_PATH, + train_transforms, + args.b, + args.w + ) + + test_dataloader = get_test_dataloader( + settings.DATA_PATH, + test_transforms, + args.b, + args.w + ) + + #device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + net = get_network(args) + net = init_weights(net) + + + if isinstance(args.gpus, int): + args.gpus = [args.gpus] + + net = nn.DataParallel(net, device_ids=args.gpus) + net = net.cuda() + + #visualize the network + visualize_network(writer, net.module) + + #cross_entropy = nn.CrossEntropyLoss() + lsr_loss = LSR() + + #apply no weight decay on bias + params = split_weights(net) + optimizer = optim.SGD(params, lr=args.lr, momentum=0.9, weight_decay=1e-4, nesterov=True) + + #set up warmup phase learning rate scheduler + iter_per_epoch = len(train_dataloader) + warmup_scheduler = WarmUpLR(optimizer, iter_per_epoch * args.warm) + + #set up training phase learning rate scheduler + train_scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=settings.MILESTONES) + #train_scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, args.e - args.warm) + + best_acc = 0.0 + for epoch in range(1, args.e + 1): + if epoch > args.warm: + train_scheduler.step(epoch) + + #training procedure + net.train() + + for batch_index, (images, labels) in enumerate(train_dataloader): + if epoch <= args.warm: + warmup_scheduler.step() + + images = images.cuda() + labels = labels.cuda() + + optimizer.zero_grad() + predicts = net(images) + loss = lsr_loss(predicts, labels) + loss.backward() + optimizer.step() + + n_iter = (epoch - 1) * len(train_dataloader) + batch_index + 1 + print('Training Epoch: {epoch} [{trained_samples}/{total_samples}]\tLoss: {:0.4f}\t'.format( + loss.item(), + epoch=epoch, + trained_samples=batch_index * args.b + len(images), + total_samples=len(train_dataloader.dataset), + )) + + #visualization + visualize_lastlayer(writer, net, n_iter) + visualize_train_loss(writer, loss.item(), n_iter) + + visualize_learning_rate(writer, optimizer.param_groups[0]['lr'], epoch) + visualize_param_hist(writer, net, epoch) + + net.eval() + + total_loss = 0 + correct = 0 + for images, labels in test_dataloader: + + images = images.cuda() + labels = labels.cuda() + + predicts = net(images) + _, preds = predicts.max(1) + correct += preds.eq(labels).sum().float() + + loss = lsr_loss(predicts, labels) + total_loss += loss.item() + + test_loss = total_loss / len(test_dataloader) + acc = correct / len(test_dataloader.dataset) + print('Test set: loss: {:.4f}, Accuracy: {:.4f}'.format(test_loss, acc)) + print() + + visualize_test_loss(writer, test_loss, epoch) + visualize_test_acc(writer, acc, epoch) + + #save weights file + if epoch > settings.MILESTONES[1] and best_acc < acc: + torch.save(net.state_dict(), checkpoint_path.format(net=args.net, epoch=epoch, type='best')) + best_acc = acc + continue + + if not epoch % settings.SAVE_EPOCH: + torch.save(net.state_dict(), checkpoint_path.format(net=args.net, epoch=epoch, type='regular')) + + writer.close() + + + + + + + + + + + + + + + diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/__init__.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/__init__.py new file mode 100644 index 000000000..ade5c6c47 --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/__init__.py @@ -0,0 +1,13 @@ + +from .transforms import ( + CenterCrop, + ColorJitter, + Compose, + CutOut, + Normalize, + RandomHorizontalFlip, + RandomResizedCrop, + RandomErasing, + ToTensor, + ToCVImage +) \ No newline at end of file diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/transforms.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/transforms.py new file mode 100644 index 000000000..9f14e8fbb --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/transforms.py @@ -0,0 +1,404 @@ + +import random +import math +import numbers + +import cv2 +import numpy as np + +import torch + +class Compose: + """Composes several transforms together. + + Args: + transforms(list of 'Transform' object): list of transforms to compose + + """ + + def __init__(self, transforms): + self.transforms = transforms + + def __call__(self, img): + + for trans in self.transforms: + img = trans(img) + + return img + + def __repr__(self): + format_string = self.__class__.__name__ + '(' + for t in self.transforms: + format_string += '\n' + format_string += ' {0}'.format(t) + format_string += '\n)' + return format_string + + +class ToCVImage: + """Convert an Opencv image to a 3 channel uint8 image + """ + + def __call__(self, image): + """ + Args: + image (numpy array): Image to be converted to 32-bit floating point + + Returns: + image (numpy array): Converted Image + """ + if len(image.shape) == 2: + image = cv2.cvtColor(iamge, cv2.COLOR_GRAY2BGR) + + image = image.astype('uint8') + + return image + + +class RandomResizedCrop: + """Randomly crop a rectangle region whose aspect ratio is randomly sampled + in [3/4, 4/3] and area randomly sampled in [8%, 100%], then resize the cropped + region into a 224-by-224 square image. + + Args: + size: expected output size of each edge + scale: range of size of the origin size cropped + ratio: range of aspect ratio of the origin aspect ratio cropped (w / h) + interpolation: Default: cv2.INTER_LINEAR: + """ + + def __init__(self, size, scale=(0.08, 1.0), ratio=(3.0 / 4.0, 4.0 / 3.0), interpolation='linear'): + + self.methods={ + "area":cv2.INTER_AREA, + "nearest":cv2.INTER_NEAREST, + "linear" : cv2.INTER_LINEAR, + "cubic" : cv2.INTER_CUBIC, + "lanczos4" : cv2.INTER_LANCZOS4 + } + + self.size = (size, size) + self.interpolation = self.methods[interpolation] + self.scale = scale + self.ratio = ratio + + def __call__(self, img): + h, w, _ = img.shape + + area = w * h + + for attempt in range(10): + target_area = random.uniform(*self.scale) * area + target_ratio = random.uniform(*self.ratio) + + output_h = int(round(math.sqrt(target_area * target_ratio))) + output_w = int(round(math.sqrt(target_area / target_ratio))) + + if random.random() < 0.5: + output_w, output_h = output_h, output_w + + if output_w <= w and output_h <= h: + topleft_x = random.randint(0, w - output_w) + topleft_y = random.randint(0, h - output_h) + break + + if output_w > w or output_h > h: + output_w = min(w, h) + output_h = output_w + topleft_x = random.randint(0, w - output_w) + topleft_y = random.randint(0, h - output_w) + + cropped = img[topleft_y : topleft_y + output_h, topleft_x : topleft_x + output_w] + + resized = cv2.resize(cropped, self.size, interpolation=self.interpolation) + + return resized + + def __repr__(self): + for name, inter in self.methods.items(): + if inter == self.interpolation: + inter_name = name + + interpolate_str = inter_name + format_str = self.__class__.__name__ + '(size={0}'.format(self.size) + format_str += ', scale={0}'.format(tuple(round(s, 4) for s in self.scale)) + format_str += ', ratio={0}'.format(tuple(round(r, 4) for r in self.ratio)) + format_str += ', interpolation={0})'.format(interpolate_str) + + return format_str + + +class RandomHorizontalFlip: + """Horizontally flip the given opencv image with given probability p. + + Args: + p: probability of the image being flipped + """ + def __init__(self, p=0.5): + self.p = p + + def __call__(self, img): + """ + Args: + the image to be flipped + Returns: + flipped image + """ + if random.random() < self.p: + img = cv2.flip(img, 1) + + return img + +class ColorJitter: + + """Randomly change the brightness, contrast and saturation of an image + + Args: + brightness: (float or tuple of float(min, max)): how much to jitter + brightness, brightness_factor is choosen uniformly from[max(0, 1-brightness), + 1 + brightness] or the given [min, max], Should be non negative numbe + contrast: same as brightness + saturation: same as birghtness + hue: same as brightness + """ + + def __init__(self, brightness=0, contrast=0, saturation=0, hue=0): + self.brightness = self._check_input(brightness) + self.contrast = self._check_input(contrast) + self.saturation = self._check_input(saturation) + self.hue = self._check_input(hue) + + def _check_input(self, value): + + if isinstance(value, numbers.Number): + assert value >= 0, 'value should be non negative' + value = [max(0, 1 - value), 1 + value] + + elif isinstance(value, (list, tuple)): + assert len(value) == 2, 'brightness should be a tuple/list with 2 elements' + assert 0 <= value[0] <= value[1], 'max should be larger than or equal to min,\ + and both larger than 0' + + else: + raise TypeError('need to pass int, float, list or tuple, instead got{}'.format(type(value).__name__)) + + return value + + def __call__(self, img): + """ + Args: + img to be jittered + Returns: + jittered img + """ + + img_dtype = img.dtype + h_factor = random.uniform(*self.hue) + b_factor = random.uniform(*self.brightness) + s_factor = random.uniform(*self.saturation) + c_factor = random.uniform(*self.contrast) + + img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + img = img.astype('float32') + + #h + img[:, :, 0] *= h_factor + img[:, :, 0] = np.clip(img[:, :, 0], 0, 179) + + #s + img[:, :, 1] *= s_factor + img[:, :, 1] = np.clip(img[:, :, 1], 0, 255) + + #v + img[:, :, 2] *= b_factor + img[:, :, 2] = np.clip(img[:, :, 2], 0, 255) + + img = img.astype(img_dtype) + img = cv2.cvtColor(img, cv2.COLOR_HSV2BGR) + + #c + img = img * c_factor + img = img.astype(img_dtype) + img = np.clip(img, 0, 255) + + return img + +class ToTensor: + """convert an opencv image (h, w, c) ndarray range from 0 to 255 to a pytorch + float tensor (c, h, w) ranged from 0 to 1 + """ + + def __call__(self, img): + """ + Args: + a numpy array (h, w, c) range from [0, 255] + + Returns: + a pytorch tensor + """ + #convert format H W C to C H W + img = img.transpose(2, 0, 1) + img = torch.from_numpy(img) + img = img.float() / 255.0 + + return img + +class Normalize: + """Normalize a torch tensor (H, W, BGR order) with mean and standard deviation + + for each channel in torch tensor: + ``input[channel] = (input[channel] - mean[channel]) / std[channel]`` + + Args: + mean: sequence of means for each channel + std: sequence of stds for each channel + """ + + def __init__(self, mean, std, inplace=False): + self.mean = mean + self.std = std + self.inplace = inplace + + def __call__(self, img): + """ + Args: + (H W C) format numpy array range from [0, 255] + Returns: + (H W C) format numpy array in float32 range from [0, 1] + """ + assert torch.is_tensor(img) and img.ndimension() == 3, 'not an image tensor' + + if not self.inplace: + img = img.clone() + + mean = torch.tensor(self.mean, dtype=torch.float32) + std = torch.tensor(self.std, dtype=torch.float32) + img.sub_(mean[:, None, None]).div_(std[:, None, None]) + + return img + +class CenterCrop: + """resize each image’s shorter edge to r pixels while keeping its aspect ratio. + Next, we crop out the cropped region in the center + Args: + resized: resize image' shorter edge to resized pixels while keeping the aspect ratio + cropped: output image size(h, w), if cropped is an int, then output cropped * cropped size + image + """ + + def __init__(self, cropped, resized=256, interpolation='linear'): + + methods = { + "area":cv2.INTER_AREA, + "nearest":cv2.INTER_NEAREST, + "linear" : cv2.INTER_LINEAR, + "cubic" : cv2.INTER_CUBIC, + "lanczos4" : cv2.INTER_LANCZOS4 + } + self.interpolation = methods[interpolation] + + self.resized = resized + + if isinstance(cropped, numbers.Number): + cropped = (cropped, cropped) + + self.cropped = cropped + + def __call__(self, img): + + shorter = min(*img.shape[:2]) + + scaler = float(self.resized) / shorter + + img = cv2.resize(img, (0, 0), fx=scaler, fy=scaler, interpolation=self.interpolation) + + h, w, _ = img.shape + + topleft_x = int((w - self.cropped[1]) / 2) + topleft_y = int((h - self.cropped[0]) / 2) + + center_cropped = img[topleft_y : topleft_y + self.cropped[0], + topleft_x : topleft_x + self.cropped[1]] + + return center_cropped + +class RandomErasing: + """Random erasing the an rectangle region in Image. + Class that performs Random Erasing in Random Erasing Data Augmentation by Zhong et al. + + Args: + sl: min erasing area region + sh: max erasing area region + r1: min aspect ratio range of earsing region + p: probability of performing random erasing + """ + + def __init__(self, p=0.5, sl=0.02, sh=0.4, r1=0.3): + + self.p = p + self.s = (sl, sh) + self.r = (r1, 1/r1) + + + def __call__(self, img): + """ + perform random erasing + Args: + img: opencv numpy array in form of [w, h, c] range + from [0, 255] + + Returns: + erased img + """ + + assert len(img.shape) == 3, 'image should be a 3 dimension numpy array' + + if random.random() > self.p: + return img + + else: + while True: + Se = random.uniform(*self.s) * img.shape[0] * img.shape[1] + re = random.uniform(*self.r) + + He = int(round(math.sqrt(Se * re))) + We = int(round(math.sqrt(Se / re))) + + xe = random.randint(0, img.shape[1]) + ye = random.randint(0, img.shape[0]) + + if xe + We <= img.shape[1] and ye + He <= img.shape[0]: + img[ye : ye + He, xe : xe + We, :] = np.random.randint(low=0, high=255, size=(He, We, img.shape[2])) + + return img + +class CutOut: + """Randomly mask out one or more patches from an image. An image + is a opencv format image (h,w,c numpy array) + + Args: + n_holes (int): Number of patches to cut out of each image. + length (int): The length (in pixels) of each square patch. + """ + + def __init__(self, length, n_holes=1): + self.n_holes = n_holes + self.length = length + + def __call__(self, img): + + while self.n_holes: + + y = random.randint(0, img.shape[0] - 1) + x = random.randint(0, img.shape[1] - 1) + + tl_x = int(max(0, x - self.length / 2)) + tl_y = int(max(0, y - self.length / 2)) + + img[tl_y : tl_y + self.length, tl_x : tl_x + self.length, :] = 0 + + self.n_holes -= 1 + + return img + + diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/utils.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/utils.py new file mode 100644 index 000000000..a30a915e9 --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/utils.py @@ -0,0 +1,219 @@ + + +import os + +import numpy as np + +import torch +import torch.nn as nn + +from torch.utils.data import DataLoader +from torch.autograd import Variable +from conf import settings +from dataset.dataset import CUB_200_2011_Train, CUB_200_2011_Test + +def get_network(args): + + if args.net == 'vgg16': + from models.vgg import vgg16 + net = vgg16() + + elif args.net == 'vgg11': + from models.vgg import vgg11 + net = vgg11() + + elif args.net == 'vgg13': + from models.vgg import vgg13 + net = vgg13() + + elif args.net == 'vgg19': + from models.vgg import vgg19 + net = vgg19() + + return net + +def get_train_dataloader(path, transforms, batch_size, num_workers, target_transforms=None): + """ return training dataloader + Args: + path: path to CUB_200_2011 dataset + transforms: transforms of dataset + target_transforms: transforms for targets + batch_size: dataloader batchsize + num_workers: dataloader num_works + Returns: train_data_loader:torch dataloader object + """ + train_dataset = CUB_200_2011_Train( + path, + transform=transforms, + target_transform=target_transforms + ) + train_dataloader = DataLoader( + train_dataset, + batch_size=batch_size, + num_workers=num_workers, + shuffle=True + ) + + return train_dataloader + +def get_test_dataloader(path, transforms, batch_size, num_workers, target_transforms=None): + """ return training dataloader + Args: + path: path to CUB_200_2011 dataset + transforms: transforms of dataset + target_transforms: transforms for targets + batch_size: dataloader batchsize + num_workers: dataloader num_works + Returns: train_data_loader:torch dataloader object + """ + test_dataset = CUB_200_2011_Test( + path, + transform=transforms, + target_transform=target_transforms + ) + + test_dataloader = DataLoader( + test_dataset, + batch_size=batch_size, + num_workers=num_workers, + shuffle=True + ) + + return test_dataloader + +def get_lastlayer_params(net): + """get last trainable layer of a net + Args: + network architectur + + Returns: + last layer weights and last layer bias + """ + last_layer_weights = None + last_layer_bias = None + for name, para in net.named_parameters(): + if 'weight' in name: + last_layer_weights = para + if 'bias' in name: + last_layer_bias = para + + return last_layer_weights, last_layer_bias + +def visualize_network(writer, net): + """visualize network architecture""" + input_tensor = torch.Tensor(3, 3, settings.IMAGE_SIZE, settings.IMAGE_SIZE) + input_tensor = input_tensor.to(next(net.parameters()).device) + writer.add_graph(net, Variable(input_tensor, requires_grad=True)) + +def visualize_lastlayer(writer, net, n_iter): + """visualize last layer grads""" + weights, bias = get_lastlayer_params(net) + writer.add_scalar('LastLayerGradients/grad_norm2_weights', weights.grad.norm(), n_iter) + writer.add_scalar('LastLayerGradients/grad_norm2_bias', bias.grad.norm(), n_iter) + +def visualize_train_loss(writer, loss, n_iter): + """visualize training loss""" + writer.add_scalar('Train/loss', loss, n_iter) + +def visualize_param_hist(writer, net, epoch): + """visualize histogram of params""" + for name, param in net.named_parameters(): + layer, attr = os.path.splitext(name) + attr = attr[1:] + writer.add_histogram("{}/{}".format(layer, attr), param, epoch) + +def visualize_test_loss(writer, loss, epoch): + """visualize test loss""" + writer.add_scalar('Test/loss', loss, epoch) + +def visualize_test_acc(writer, acc, epoch): + """visualize test acc""" + writer.add_scalar('Test/Accuracy', acc, epoch) + +def visualize_learning_rate(writer, lr, epoch): + """visualize learning rate""" + writer.add_scalar('Train/LearningRate', lr, epoch) + +def init_weights(net): + """the weights of conv layer and fully connected layers + are both initilized with Xavier algorithm, In particular, + we set the parameters to random values uniformly drawn from [-a, a] + where a = sqrt(6 * (din + dout)), for batch normalization + layers, y=1, b=0, all bias initialized to 0. + """ + for m in net.modules(): + if isinstance(m, nn.Conv2d): + nn.init.xavier_uniform_(m.weight) + #nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') + if m.bias is not None: + nn.init.constant_(m.bias, 0) + + elif isinstance(m, nn.BatchNorm2d): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + + elif isinstance(m, nn.Linear): + nn.init.xavier_uniform_(m.weight) + + if m.bias is not None: + nn.init.constant_(m.bias, 0) + + return net + +def split_weights(net): + """split network weights into to categlories, + one are weights in conv layer and linear layer, + others are other learnable paramters(conv bias, + bn weights, bn bias, linear bias) + + Args: + net: network architecture + + Returns: + a dictionary of params splite into to categlories + """ + + decay = [] + no_decay = [] + + for m in net.modules(): + if isinstance(m, nn.Conv2d) or isinstance(m, nn.Linear): + decay.append(m.weight) + + if m.bias is not None: + no_decay.append(m.bias) + + else: + if hasattr(m, 'weight'): + no_decay.append(m.weight) + if hasattr(m, 'bias'): + no_decay.append(m.bias) + + assert len(list(net.parameters())) == len(decay) + len(no_decay) + + return [dict(params=decay), dict(params=no_decay, weight_decay=0)] + +def mixup_data(x, y, alpha=0.2): + + """Returns mixed up inputs pairs of targets and lambda""" + if alpha > 0: + lam = np.random.beta(alpha, alpha) + else: + lam = 1 + + batch_size = x.size(0) + index = torch.randperm(batch_size) + index = index.to(x.device) + + lam = max(lam, 1 - lam) + + mixed_x = lam * x + (1 - lam) * x[index, :] + + y_a = y + y_b = y[index, :] + + return mixed_x, y_a, y_b, lam + + + + \ No newline at end of file diff --git a/PyTorch/build-in/Classification/BagOfTricks/coverage.txt b/PyTorch/build-in/Classification/BagOfTricks/coverage.txt new file mode 100644 index 000000000..fc5251079 --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/coverage.txt @@ -0,0 +1,3 @@ +all api: ['_amp_foreach_non_finite_check_and_unscale_', '_amp_update_scale_', '_copy_from', '_has_compatible_shallow_copy_type', '_local_scalar_dense', '_log_softmax', '_log_softmax_backward_data', '_pin_memory', '_reshape_alias', 'add_', 'addmm', 'as_strided', 'convolution', 'convolution_backward', 'copy_stride', 'div', 'dropout', 'dropout_backward', 'dropout_forward', 'eq', 'fill_', 'fused_sgd', 'is_pinned', 'linear', 'max_pool2d', 'maxpool2d_backward', 'maxpool2d_forward', 'mm', 'mul', 'mul_', 'native_batch_norm', 'native_batch_norm_backward', 'nll_loss_backward', 'nll_loss_forward', 'reciprocal', 'relu_', 'sum', 'threshold_backward', 'topk_out', 'view'], total: 40 +fallback op: [], total: 0 +coverage rate: 100.00% diff --git a/PyTorch/build-in/Classification/BagOfTricks/vgg.py b/PyTorch/build-in/Classification/BagOfTricks/vgg.py new file mode 100644 index 000000000..dd1fd24e3 --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/vgg.py @@ -0,0 +1,101 @@ +"""[1] Simonyan, Karen, and Andrew Zisserman. “Very Deep Convolutional + Networks for Large-Scale Image Recognition.” International Conference + on Learning Representations, 2015.""" + + +import torch +import torch.nn as nn + +class BasicConv(nn.Module): + + def __init__(self, input_channels, output_channels, kernel_size, **kwargs): + super().__init__() + self.conv = nn.Conv2d(input_channels, output_channels, kernel_size, **kwargs) + self.bn = nn.BatchNorm2d(output_channels) + self.relu = nn.ReLU(inplace=True) + self._blocks = [] + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + x = self.relu(x) + + return x + +class VGG(nn.Module): + + def __init__(self, blocks, num_class=100): + super().__init__() + self.input_channels = 3 + self.conv1 = self._make_layers(64, blocks[0]) + self.conv2 = self._make_layers(128, blocks[1]) + self.conv3 = self._make_layers(256, blocks[2]) + self.conv4 = self._make_layers(512, blocks[3]) + self.conv5 = self._make_layers(512, blocks[4]) + + self.classifier = nn.Sequential( + nn.Linear(512 * 7 * 7, 4096), + nn.ReLU(inplace=True), + nn.Dropout(), + nn.Linear(4096, 4096), + nn.ReLU(inplace=True), + nn.Dropout(), + nn.Linear(4096, num_class) + ) + + def forward(self, x): + + x = self.conv1(x) + x = self.conv2(x) + x = self.conv3(x) + x = self.conv4(x) + x = self.conv5(x) + # x = x.view(x.size(0), -1) + x = x.reshape(x.size(0), -1) + x = self.classifier(x) + + return x + + def _make_layers(self, output_channels, layer_num): + layers = [] + while layer_num: + layers.append( + BasicConv( + self.input_channels, + output_channels, + kernel_size=3, + padding=1, + bias=False + ) + ) + self.input_channels = output_channels + layer_num -= 1 + layers.append(nn.MaxPool2d(2, stride=2)) + + return nn.Sequential(*layers) + +def vgg11(num_classes): + return VGG([1, 1, 2, 2, 2], num_class=num_classes) + +def vgg13(num_classes): + return VGG([2, 2, 2, 2, 2], num_class=num_classes) + +def vgg16(num_classes): + return VGG([2, 2, 3, 3, 3], num_class=num_classes) + +def vgg19(num_classes): + return VGG([2, 2, 4, 4, 4], num_class=num_classes) + +# welo: minimal VGG factory — clean, simple +def Model(num_classes=100, model_type='vgg16'): + model_map = { + 'vgg11': vgg11, + 'vgg13': vgg13, + 'vgg16': vgg16, + 'vgg19': vgg19, + } + + if model_type not in model_map: + raise ValueError(f"Unknown model_type: {model_type}") + + return model_map[model_type](num_classes=num_classes) \ No newline at end of file diff --git a/PyTorch/build-in/Classification/BagOfTricks/vgg_loss.jpg b/PyTorch/build-in/Classification/BagOfTricks/vgg_loss.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2a4f5b9f5918f87dd8d49fa4a0e13478828d179c GIT binary patch literal 36139 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

b(7E*) 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 支持 DDP / 单卡,AMP,resume,日志,checkpoint) +保存为 train_template_localmodel.py +""" +import torch +import torch.nn as nn +import torch.optim as optim +import torch.backends.cudnn as cudnn +import torchvision.transforms as transforms +import torchvision.datasets as datasets +import torchvision.models as tv_models + +import torch.distributed as dist +from torch.nn.parallel import DistributedDataParallel as DDP +from torch.utils.data import DataLoader +from torch.utils.data.distributed import DistributedSampler + +from torch.sdaa import amp +# from torch.cuda import amp + + +# ---------------------------- +# Helper utilities (self-contained) +# ---------------------------- +class AverageMeter(object): + def __init__(self, name='Meter', fmt=':.4f'): + self.name = name + self.fmt = fmt + 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 / max(1, self.count) + def __str__(self): + fmtstr = '{name} {val' + self.fmt + '} (avg {avg' + self.fmt + '})' + return fmtstr.format(name=self.name, val=self.val, avg=self.avg) + +def accuracy(output, target, topk=(1,)): + """Computes the precision@k for the specified values of k + 返回一个 list,每个元素是 tensor(百分比形式) + """ + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + # output: (N, C) -> pred: (maxk, N) + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() # (maxk, N) + correct = pred.eq(target.view(1, -1).expand_as(pred)) # (maxk, N) bool + + res = [] + for k in topk: + # 把前 k 行展平后求和(返回 0-dim tensor),随后换算为百分比 + correct_k = correct[:k].reshape(-1).float().sum() # 注意:不传 keepdim + # 乘以 100.0 / batch_size,保持返回 tensor(和之前代码兼容) + res.append(correct_k.mul_(100.0 / batch_size)) + return res + +def save_checkpoint(state, is_best, save_dir, filename='checkpoint.pth'): + save_path = os.path.join(save_dir, filename) + torch.save(state, save_path) + if is_best: + best_path = os.path.join(save_dir, 'model_best.pth') + torch.save(state, best_path) + +def set_seed(seed, deterministic=False): + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + if deterministic: + cudnn.deterministic = True + cudnn.benchmark = False + else: + cudnn.deterministic = False + cudnn.benchmark = True + +# ---------------------------- +# Argument parser +# ---------------------------- +def parse_args(): + parser = argparse.ArgumentParser(description='Generic PyTorch training template (DDP/AMP) with LocalModel priority') + parser.add_argument('--name', default='run', type=str, help='experiment name (log/checkpoints dir)') + parser.add_argument('--seed', default=42, type=int, help='random seed') + parser.add_argument('--arch', default='None', type=str, help='model name') + parser.add_argument('--deterministic', action='store_true', help='set cudnn deterministic (may be slower)') + parser.add_argument('--dataset', default='cifar10', choices=['cifar10','cifar100','imagenet','custom'], help='which dataset') + parser.add_argument('--datapath', default='./data', type=str, help='dataset root / imagenet root / custom root') + parser.add_argument('--imagenet_dir', default='./imagenet', type=str, help='if dataset=imagenet, path to imagenet root') + parser.add_argument('--custom_eval_dir', default=None, help='if dataset=custom, provide val dir') + parser.add_argument('--num_workers', default=4, type=int, help='dataloader workers per process') + parser.add_argument('--epochs', default=200, type=int) + parser.add_argument('--steps', default=0, type=int, help='max steps to run (if >0, training will stop when global_step reaches this).') + parser.add_argument('--batch_size', default=128, type=int) + parser.add_argument('--model_name', default='resnet18', help='torchvision model name or python path e.g. mypkg.mymodule.Model (used if no local Model)') + parser.add_argument('--num_classes', default=None, type=int, help='override num classes (auto-detect for common sets)') + parser.add_argument('--pretrained', action='store_true', help='use torchvision pretrained weights when available') + parser.add_argument('--optimizer', default='sgd', choices=['sgd','adam','adamw'], help='optimizer') + parser.add_argument('--lr', '--learning_rate', default=0.01, type=float) + parser.add_argument('--momentum', default=0.9, type=float) + parser.add_argument('--weight_decay', default=5e-4, type=float) + parser.add_argument('--nesterov', action='store_true') + parser.add_argument('--scheduler', default='multistep', choices=['multistep','step','cosine','none'], help='lr scheduler') + parser.add_argument('--milestones', default='100,150', type=str, help='milestones for multistep (comma sep)') + parser.add_argument('--step_size', default=30, type=int, help='step size for StepLR or cosine max epochs') + parser.add_argument('--gamma', default=0.1, type=float) + parser.add_argument('--scheduler_step_per_batch', action='store_true', help='call scheduler.step() per batch (for some schedulers)') + parser.add_argument('--resume', default='', type=str, help='path to checkpoint to resume from') + parser.add_argument('--start_epoch', default=0, type=int) + parser.add_argument('--print_freq', default=100, type=int) + parser.add_argument('--save_freq', default=10, type=int, help='save checkpoint every N epochs (rank0 only)') + parser.add_argument('--amp', action='store_true', default = True,help='use automatic mixed precision (AMP)') + parser.add_argument('--grad_accum_steps', default=1, type=int, help='gradient accumulation steps') + parser.add_argument('--local_rank', default=None, type=int, help='local rank passed by torchrun (if any). Use -1 or None for non-distributed') + parser.add_argument('--cutmix_prob', default=0.0, type=float) + parser.add_argument('--beta', default=1.0, type=float) + parser.add_argument('--seed_sampler', default=False, action='store_true', help='set sampler epoch seeds to make deterministic distributed shuffling') + args = parser.parse_args() + args.milestones = [int(x) for x in args.milestones.split(',')] if args.milestones else [] + return args + +# ---------------------------- +# build model (优先 LocalModel) +# ---------------------------- +def build_model_with_local_priority(args, device=None): + """ + 用参数 args.arch 作为模块名导入 Model() + 如果模块不存在或没有 Model 类,则报错停止。 + """ + try: + # 动态导入模块,比如 args.arch = "rexnet" + mod = importlib.import_module(args.arch) + Model = getattr(mod, "Model") # 从模块中获取 Model 类 + except Exception as e: + raise RuntimeError( + f"无法导入模型模块 '{args.arch}' 或未找到类 Model。" + f"\n错误信息:{e}" + ) + + # 解析数据集类别数 + if args.dataset == 'cifar10': + num_classes = 10 + elif args.dataset == 'cifar100': + num_classes = 100 + else: + print(f"[ERROR] 不支持的数据集类型:{args.dataset},无法确定类别数。程序终止。") + sys.exit(1) + + + # 实例化 + try: + model = Model(num_classes) + except Exception as e: + raise RuntimeError( + f"Model() 实例化失败,请检查模型构造函数。\n错误信息:{e}" + ) + + return model + +# ---------------------------- +# Data loader factory +# ---------------------------- +def build_dataloaders(args, rank, world_size): + if args.dataset == 'cifar10' or args.dataset == 'cifar100': + mean = (0.4914, 0.4822, 0.4465) + std = (0.2470, 0.2435, 0.2616) if args.dataset == 'cifar10' else (0.2023, 0.1994, 0.2010) + # train_transform = transforms.Compose([ + # transforms.RandomCrop(32, padding=4), + # transforms.RandomHorizontalFlip(), + # transforms.ToTensor(), + # transforms.Normalize(mean, std), + # ]) + # test_transform = transforms.Compose([ + # transforms.ToTensor(), + # transforms.Normalize(mean, std), + # ]) + + train_transform = transforms.Compose([ # 2025/12/3 从visformer模型开始 + transforms.Resize(256), # 先放大到 256 + transforms.RandomCrop(224), # 再随机裁剪为 224(更符合 ImageNet 风格增强) + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + transforms.Normalize(mean, std), + ]) + test_transform = transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(224), + transforms.ToTensor(), + transforms.Normalize(mean, std), + ]) + root = args.datapath + if args.dataset == 'cifar10': + train_set = datasets.CIFAR10(root=root, train=True, download=False, transform=train_transform) + val_set = datasets.CIFAR10(root=root, train=False, download=False, transform=test_transform) + num_classes = 10 + else: + train_set = datasets.CIFAR100(root=root, train=True, download=False, transform=train_transform) + val_set = datasets.CIFAR100(root=root, train=False, download=False, transform=test_transform) + num_classes = 100 + + elif args.dataset == 'imagenet': + train_dir = os.path.join(args.imagenet_dir, 'train') + val_dir = os.path.join(args.imagenet_dir, 'val') + train_transform = transforms.Compose([ + transforms.RandomResizedCrop(224), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + transforms.Normalize((0.485,0.456,0.406), (0.229,0.224,0.225)), + ]) + test_transform = transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(224), + transforms.ToTensor(), + transforms.Normalize((0.485,0.456,0.406), (0.229,0.224,0.225)), + ]) + train_set = datasets.ImageFolder(train_dir, train_transform) + val_set = datasets.ImageFolder(val_dir, test_transform) + num_classes = args.num_classes or 1000 + + elif args.dataset == 'custom': + train_dir = os.path.join(args.datapath, 'train') + val_dir = args.custom_eval_dir or os.path.join(args.datapath, 'val') + train_transform = transforms.Compose([ + transforms.RandomResizedCrop(224), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + ]) + test_transform = transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(224), + transforms.ToTensor(), + ]) + train_set = datasets.ImageFolder(train_dir, train_transform) + val_set = datasets.ImageFolder(val_dir, test_transform) + num_classes = len(train_set.classes) + else: + raise ValueError("Unknown dataset") + + if dist.is_initialized() and world_size > 1: + train_sampler = DistributedSampler(train_set, num_replicas=world_size, rank=rank, shuffle=True) + else: + train_sampler = None + + train_loader = DataLoader(train_set, + batch_size=args.batch_size, + shuffle=(train_sampler is None), + num_workers=args.num_workers, + pin_memory=True, + sampler=train_sampler, + drop_last=False) + val_loader = DataLoader(val_set, + batch_size=args.batch_size, + shuffle=False, + num_workers=args.num_workers, + pin_memory=True) + + return train_loader, val_loader, num_classes, train_sampler + +# ---------------------------- +# Train & validate +# ---------------------------- +def train_one_epoch(args, epoch, model, criterion, optimizer, train_loader, device, scaler, scheduler=None, train_sampler=None, global_step_start=0, max_global_steps=None): + """ + 现在支持:若 max_global_steps 非 None,则当 global_step 达到该值时提前退出 + 返回: epoch_summary_dict, step_logs_list, global_step_end + step_logs_list: list of dicts with per-step info (for logging to CSV if需要) + """ + batch_time = AverageMeter('Time') + data_time = AverageMeter('Data') + losses = AverageMeter('Loss') + top1 = AverageMeter('Acc@1') + top5 = AverageMeter('Acc@5') + + model.train() + end = time.time() + optimizer.zero_grad() + + iters = len(train_loader) + step_logs = [] + global_step = global_step_start + + for i, (images, targets) in enumerate(train_loader): + # check global steps limit + if (max_global_steps is not None) and (global_step >= max_global_steps): + break + + data_time.update(time.time() - end) + images = images.to(device, non_blocking=True) + targets = targets.to(device, non_blocking=True) + + if args.amp: + with amp.autocast(): + outputs = model(images) + loss = criterion(outputs, targets) / args.grad_accum_steps + else: + outputs = model(images) + loss = criterion(outputs, targets) / args.grad_accum_steps + + if args.amp: + scaler.scale(loss).backward() + else: + loss.backward() + + # 每当累积步满足 grad_accum_steps 就 step + if (i + 1) % args.grad_accum_steps == 0: + if args.amp: + scaler.step(optimizer) + scaler.update() + else: + optimizer.step() + optimizer.zero_grad() + if scheduler is not None and args.scheduler_step_per_batch: + scheduler.step() + + with torch.no_grad(): + acc1, acc5 = accuracy(outputs, targets, topk=(1,5)) + losses.update(loss.item() * args.grad_accum_steps, images.size(0)) + top1.update(acc1.item(), images.size(0)) + top5.update(acc5.item(), images.size(0)) + + batch_time.update(time.time() - end) + end = time.time() + + # increment global step AFTER processing this batch + global_step += 1 + + # per-step print (controlled by print_freq) + if ((global_step % args.print_freq == 0) or (i == iters - 1)) and ((dist.get_rank() if dist.is_initialized() else 0) == 0): + lr = optimizer.param_groups[0]['lr'] + print(f"Epoch[{epoch}]:step[{i+1}/{iters}] step_train_loss {losses.val:.4f} acc1 {top1.val:.2f} acc5 {top5.val:.2f}") + + # collect per-step log + step_logs.append({ + 'epoch': epoch, + 'batch_idx': i, + 'global_step': global_step, + 'lr': optimizer.param_groups[0]['lr'], + 'loss': losses.val, + 'loss_avg': losses.avg, + 'acc1': top1.val, + 'acc1_avg': top1.avg, + 'acc5': top5.val, + 'acc5_avg': top5.avg, + 'time': batch_time.val + }) + + # if reached max_global_steps inside epoch, break (handled at loop start next iter) + if (max_global_steps is not None) and (global_step >= max_global_steps): + if (dist.get_rank() if dist.is_initialized() else 0) == 0: + print(f"[Info] 达到 max_global_steps={max_global_steps},将在 epoch 内提前停止。") + break + + # --- flush remaining grads if needed (handle gradient accumulation leftovers) --- + processed_batches = global_step - global_step_start # 实际处理的 batch 数 + if args.grad_accum_steps > 1 and (processed_batches % args.grad_accum_steps) != 0: + # only step if there are gradients + grads_present = any((p.grad is not None and p.requires_grad) for p in model.parameters()) + if grads_present: + if args.amp: + try: + scaler.step(optimizer) + scaler.update() + except Exception as e: + # 防御性:若 scaler.step 因某些原因失败,尝试普通 step(只在极端情况下) + print("[Warning] scaler.step 失败,尝试普通 optimizer.step():", e) + optimizer.step() + else: + optimizer.step() + optimizer.zero_grad() + if scheduler is not None and args.scheduler_step_per_batch: + scheduler.step() + if (dist.get_rank() if dist.is_initialized() else 0) == 0: + print(f"[Info] flushed remaining gradients after early stop (processed_batches={processed_batches}, grad_accum={args.grad_accum_steps}).") + + if scheduler is not None and not args.scheduler_step_per_batch: + scheduler.step() + + return OrderedDict([('loss', losses.avg), ('acc1', top1.avg), ('acc5', top5.avg)]), step_logs, global_step + +def validate(args, model, val_loader, criterion, device): + losses = AverageMeter('Loss') + top1 = AverageMeter('Acc@1') + top5 = AverageMeter('Acc@5') + + model.eval() + with torch.no_grad(): + for i, (images, targets) in enumerate(tqdm(val_loader)): + images = images.to(device, non_blocking=True) + targets = targets.to(device, non_blocking=True) + outputs = model(images) + loss = criterion(outputs, targets) + acc1, acc5 = accuracy(outputs, targets, topk=(1,5)) + losses.update(loss.item(), images.size(0)) + top1.update(acc1.item(), images.size(0)) + top5.update(acc5.item(), images.size(0)) + return OrderedDict([('loss', losses.avg), ('acc1', top1.avg), ('acc5', top5.avg)]) + +# ---------------------------- +# Main +# ---------------------------- +def main(): + args = parse_args() + + # handle local_rank from env if not provided + local_rank_env = os.environ.get('LOCAL_RANK', None) + if args.local_rank is None and local_rank_env is not None: + args.local_rank = int(local_rank_env) + + distributed = (args.local_rank is not None and args.local_rank != -1) + if distributed: + dist.init_process_group(backend='nccl', init_method='env://') + rank = dist.get_rank() + world_size = dist.get_world_size() + else: + rank = 0 + world_size = 1 + + if distributed: + torch.cuda.set_device(args.local_rank) + device = torch.device('cuda', args.local_rank) + else: + device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + + set_seed(args.seed + (rank if distributed else 0), deterministic=args.deterministic) + + save_dir = os.path.join('models', args.name) + if rank == 0: + os.makedirs(save_dir, exist_ok=True) + with open(os.path.join(save_dir, 'args.json'), 'w') as f: + json.dump(vars(args), f, indent=2) + if distributed: + dist.barrier() + + train_loader, val_loader, auto_num_classes, train_sampler = build_dataloaders(args, rank, world_size) + if args.num_classes is None: + args.num_classes = auto_num_classes + + # 使用本地 Model 优先(LocalModel 已在文件顶部尝试导入) + model = build_model_with_local_priority(args, device) + model.to(device) + + if distributed: + model = DDP(model, device_ids=[args.local_rank], output_device=args.local_rank, find_unused_parameters=True) + + criterion = nn.CrossEntropyLoss().to(device) + params = [p for p in model.parameters() if p.requires_grad] + if args.optimizer == 'sgd': + optimizer = optim.SGD(params, lr=args.lr, momentum=args.momentum, + weight_decay=args.weight_decay, nesterov=args.nesterov) + elif args.optimizer == 'adam': + optimizer = optim.Adam(params, lr=args.lr, weight_decay=args.weight_decay) + elif args.optimizer == 'adamw': + optimizer = optim.AdamW(params, lr=args.lr, weight_decay=args.weight_decay) + else: + raise ValueError('Unknown optimizer') + + scheduler = None + if args.scheduler == 'multistep': + scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=args.milestones, gamma=args.gamma) + elif args.scheduler == 'step': + scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=args.step_size, gamma=args.gamma) + elif args.scheduler == 'cosine': + scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=args.epochs) + elif args.scheduler == 'none': + scheduler = None + + scaler = amp.GradScaler() if args.amp else None + + start_epoch = args.start_epoch + best_acc = 0.0 + if args.resume: + if os.path.isfile(args.resume): + ckpt = torch.load(args.resume, map_location='cpu') + model_state = ckpt.get('state_dict', ckpt) + if isinstance(model, DDP): + model.module.load_state_dict(model_state) + else: + model.load_state_dict(model_state) + if 'optimizer' in ckpt: + optimizer.load_state_dict(ckpt['optimizer']) + start_epoch = ckpt.get('epoch', start_epoch) + best_acc = ckpt.get('best_acc', best_acc) + print(f"=> resumed from {args.resume}, start_epoch={start_epoch}") + else: + print(f"=> resume path {args.resume} not found") + + log_columns = ['epoch', 'lr', 'loss', 'acc1', 'acc5', 'val_loss', 'val_acc1', 'val_acc5'] + log_df = pd.DataFrame(columns=log_columns) + # step-level log + step_log_columns = ['epoch', 'batch_idx', 'global_step', 'lr', 'loss', 'loss_avg', 'acc1', 'acc1_avg', 'acc5', 'acc5_avg', 'time'] + step_log_df = pd.DataFrame(columns=step_log_columns) + + total_epochs = args.epochs + # global_step计数器(训练过程中跨epoch持续) + global_step = 0 + + epoch = start_epoch + # loop until either epoch criteria or step criteria met + while True: + if train_sampler is not None: + if args.seed_sampler: + train_sampler.set_epoch(epoch + args.seed) + else: + train_sampler.set_epoch(epoch) + + if rank == 0: + print(f"==== Epoch {epoch}/{total_epochs - 1} ====") + + # 如果传入了 args.steps (>0),则把剩余允许的 step 数传给 train_one_epoch, + # 否则 max_global_steps=None(按整 epoch 执行完) + if args.steps and args.steps > 0: + max_global_steps = args.steps + else: + max_global_steps = None + + train_log, step_logs, global_step = train_one_epoch( + args, epoch, model, criterion, optimizer, train_loader, device, scaler, + scheduler, train_sampler, global_step_start=global_step, max_global_steps=max_global_steps + ) + + # 如果启用了按 steps 的模式且已经达到上限,标记需要在做一次验证后退出 + if max_global_steps is not None and global_step >= max_global_steps: + if rank == 0: + print(f"[Main] 达到 max_global_steps={max_global_steps}(global_step={global_step}),将在完成验证后退出训练。") + # 我们不 return 立刻退出;后面的 validate / 保存逻辑会执行一次,然后 main 返回/结束 + end_due_to_steps = True + else: + end_due_to_steps = False + + # 验证并记录 epoch 级别日志(如果在 step 模式下很可能在中间某个 epoch 提前结束,但我们仍做一次 validate) + val_log = validate(args, model, val_loader, criterion, device) + current_lr = optimizer.param_groups[0]['lr'] + + if rank == 0: + # epoch summary print, 格式与示例对齐 + print(f"Epoch[{epoch}]: epoch_train_loss {train_log['loss']:.4f} acc1 {train_log['acc1']:.2f} acc5 {train_log['acc5']:.2f} | " + f"val_loss {val_log['loss']:.4f} acc1 {val_log['acc1']:.2f} acc5 {val_log['acc5']:.2f} lr {current_lr:.6f}") + row = { + 'epoch': epoch, + 'lr': current_lr, + 'loss': train_log['loss'], + 'acc1': train_log['acc1'], + 'acc5': train_log['acc5'], + 'val_loss': val_log['loss'], + 'val_acc1': val_log['acc1'], + 'val_acc5': val_log['acc5'], + } + new_row_df = pd.DataFrame([row]) + log_df = pd.concat([log_df, new_row_df], ignore_index=True) + log_df.to_csv(os.path.join(save_dir, 'log.csv'), index=False) + + is_best = val_log['acc1'] > best_acc + if is_best: + best_acc = val_log['acc1'] + if (epoch % args.save_freq == 0) or is_best or ( (max_global_steps is None) and (epoch == total_epochs - 1) ) : + state = { + 'epoch': epoch, + 'state_dict': model.module.state_dict() if isinstance(model, DDP) else model.state_dict(), + 'best_acc': best_acc, + 'optimizer': optimizer.state_dict(), + 'args': vars(args) + } + save_checkpoint(state, is_best, save_dir, filename=f'checkpoint_epoch_{epoch}.pth') + + # 如果是因为 steps 模式达到上限,则在完成 validation / 保存后退出训练 + if end_due_to_steps: + if rank == 0: + print(f"[Main] 已在 steps 模式下完成最后一次验证并保存,训练结束(global_step={global_step})。") + break + + # increment epoch + epoch += 1 + + # stopping conditions: + # 1) if steps mode enabled and reached steps -> stop + if args.steps and args.steps > 0: + if global_step >= args.steps: + if rank == 0: + print(f"[Main] 已达到指定 steps={args.steps}(global_step={global_step}),训练结束。") + break + + # 2) if steps not used, stop when epoch >= epochs + else: + if epoch >= total_epochs: + if rank == 0: + print(f"[Main] 已达到指定 epochs={total_epochs}(epoch={epoch}),训练结束。") + break + + if dist.is_initialized(): + dist.barrier() + if rank == 0: + print("Training finished. Best val acc1: {:.2f}".format(best_acc)) + +if __name__ == '__main__': + main() \ No newline at end of file From ad2609822b0aba295676812dd338d29a8efa2485 Mon Sep 17 00:00:00 2001 From: wangwl Date: Wed, 7 Jan 2026 06:39:33 +0000 Subject: [PATCH 2/3] fix: cleanup code and update --- .../README.md | 61 --- .../conf/__init__.py | 15 - .../conf/settings.py | 45 -- .../criterion/FocalLoss.py | 5 - .../criterion/LabelSmoothing.py | 88 ---- .../criterion/__init__.py | 2 - .../dataset/dataset.py | 182 -------- .../lr_find.py | 126 ------ .../lr_scheduler/FindLR.py | 23 - .../lr_scheduler/WarmUpLR.py | 24 -- .../lr_scheduler/__init__.py | 3 - .../train.py | 195 --------- .../transforms/__init__.py | 13 - .../transforms/transforms.py | 404 ------------------ .../Classification/BagOfTricks/coverage.txt | 3 - .../Classification/BagOfTricks/readme | 65 +++ .../BagOfTricks/requirements_exact.txt | 89 ++++ .../utils.py | 0 .../Classification/BagOfTricks/vgg_loss.jpg | Bin 36139 -> 0 bytes .../Classification/BagOfTricks/vgg_loss.txt | 29 -- 20 files changed, 154 insertions(+), 1218 deletions(-) delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/README.md delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/__init__.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/settings.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/FocalLoss.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/LabelSmoothing.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/__init__.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/dataset/dataset.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_find.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/FindLR.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/WarmUpLR.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/__init__.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/train.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/__init__.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/transforms.py delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/coverage.txt create mode 100644 PyTorch/build-in/Classification/BagOfTricks/readme create mode 100644 PyTorch/build-in/Classification/BagOfTricks/requirements_exact.txt rename PyTorch/build-in/Classification/BagOfTricks/{Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks => }/utils.py (100%) delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/vgg_loss.jpg delete mode 100644 PyTorch/build-in/Classification/BagOfTricks/vgg_loss.txt diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/README.md b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/README.md deleted file mode 100644 index 5c62fbd7e..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# Bag of Tricks for Image Classification with Convolutional Neural Networks - - -This repo was inspired by Paper [Bag of Tricks for Image Classification with Convolutional Neural Networks](https://arxiv.org/abs/1812.01187) - -I would test popular training tricks as many as I can for improving image classification accuarcy, feel -free to leave a comment about the tricks you want me to test(please write the referenced paper along with -the tricks) - -## hardware -Using 4 Tesla P40 to run the experiments - -## dataset - -I will use [CUB_200_2011](http://www.vision.caltech.edu/visipedia/CUB-200-2011.html) dataset instead of ImageNet, -just for simplicity, this is a fine-grained image classification dataset, which contains 200 birds categlories, -5K+ training images, and 5K+ test images.The state of the art acc on vgg16 is around 73%(please correct me if -I was wrong).You could easily change it to the ones you like: [Stanford Dogs](http://vision.stanford.edu/aditya86/ImageNetDogs/), [Stanford Cars](http://vision.stanford.edu/aditya86/ImageNetDogs/). -Or even ImageNet. - -## network - -Use a VGG16 network to test my tricks, also for simplicity reasons, since VGG16 is easy to implement. I'm considering -switch to AlexNet, to see how powerful these tricks are. - -## tricks - -tricks I've tested, some of them were from the Paper [Bag of Tricks for Image Classification with Convolutional Neural Networks](https://arxiv.org/abs/1812.01187) : - -|trick|referenced paper| -|:---:|:---:| -|xavier init|[Understanding the difficulty of training deep feedforward neural networks](http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf)| -|warmup training|[Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour](https://arxiv.org/abs/1706.02677v2)| -|no bias decay|[Highly Scalable Deep Learning Training System with Mixed-Precision: Training ImageNet in Four Minutes](https://arxiv.org/abs/1807.11205vx)| -|label smoothing|[Rethinking the inception architecture for computer vision](https://arxiv.org/abs/1512.00567v3))| -|random erasing|[Random Erasing Data Augmentation](https://arxiv.org/abs/1708.04896v2)| -|cutout|[Improved Regularization of Convolutional Neural Networks with Cutout](https://arxiv.org/abs/1708.04552v2)| -|linear scaling learning rate|[Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour](https://arxiv.org/abs/1706.02677v2)| -|cosine learning rate decay|[SGDR: Stochastic Gradient Descent with Warm Restarts](https://arxiv.org/abs/1608.03983)| - -**and more to come......** - - -## result - -baseline(training from sctrach, no ImageNet pretrain weights are used): - -vgg16 64.60% on [CUB_200_2011](http://www.vision.caltech.edu/visipedia/CUB-200-2011.html) dataset, lr=0.01, batchsize=64 - -effects of stacking tricks - -|trick|acc| -|:---:|:---:| -|baseline|64.60%| -|+xavier init and warmup training|66.07%| -|+no bias decay|70.14%| -|+label smoothing|71.20%| -|+random erasing|does not work, drops about 4 points| -|+linear scaling learning rate(batchsize 256, lr 0.04)|71.21%| -|+cutout|does not work, drops about 1 point| -|+cosine learning rate decay|does not work, drops about 1 point| \ No newline at end of file diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/__init__.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/__init__.py deleted file mode 100644 index ac1b73748..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -""" dynamically load settings -author baiyu -""" - -import conf.settings as settings - - -class Settings: - def __init__(self, settings): - for attr in dir(settings): - if attr.isupper(): - setattr(self, attr, getattr(settings, attr)) - -settings = Settings(settings) - diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/settings.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/settings.py deleted file mode 100644 index 0c312806d..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/conf/settings.py +++ /dev/null @@ -1,45 +0,0 @@ - -#TRAIN_PATH = '/Users/didi/Downloads/train' -#LABEL_PATH = '/Users/didi/Downloads/' - -#TRAIN_STD = [0.2648431067741028, 0.2606256902653438, 0.26538460981082046] -#TRAIN_MEAN = [0.39091378793393816, 0.45037754368027466, 0.47364248847284746] - -from datetime import datetime - - -#cv2 -#TRAIN_MEAN = [0.43237713785804116, 0.49941626449353244, 0.48560741861744905] -#TRAIN_STD = [0.2665100547329813, 0.22770540015765814, 0.2321024260764962] - -TRAIN_MEAN = [0.48560741861744905, 0.49941626449353244, 0.43237713785804116] -TRAIN_STD = [0.2321024260764962, 0.22770540015765814, 0.2665100547329813] - -#TEST_MEAN = [0.4311430419332438, 0.4998156522834164, 0.4862169586881995] -#TEST_STD = [0.26667253517177186, 0.22781080253662814, 0.23264268069040475] - -TEST_MEAN = [0.4862169586881995, 0.4998156522834164, 0.4311430419332438] -TEST_STD = [0.23264268069040475, 0.22781080253662814, 0.26667253517177186] - -#DATA_PATH = '/nfs/cold_project/baiyu/Caltech-UCSD Birds-200-2011/CUB_200_2011' -DATA_PATH = '/nfs/project/baiyu/CUB200_2011/CUB_200_2011' - -#MILESTONES = [100, 130, 160] -MILESTONES = [300, 350, 400] - -#weights file directory -CHECKPOINT_PATH = 'checkpoints' - -TIME_NOW = datetime.now().isoformat() - -#tensorboard log file directory -LOG_DIR = 'runs' - -#save weights file per SAVE_EPOCH epoch -SAVE_EPOCH = 10 - -#input image size for network -IMAGE_SIZE = 224 - - - diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/FocalLoss.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/FocalLoss.py deleted file mode 100644 index 4be827330..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/FocalLoss.py +++ /dev/null @@ -1,5 +0,0 @@ - - -import torch -import torch.nn as nn - diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/LabelSmoothing.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/LabelSmoothing.py deleted file mode 100644 index 7136dad45..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/LabelSmoothing.py +++ /dev/null @@ -1,88 +0,0 @@ - -import torch -import torch.nn as nn - - -class LSR(nn.Module): - - def __init__(self, e=0.1, reduction='mean'): - super().__init__() - - self.log_softmax = nn.LogSoftmax(dim=1) - self.e = e - self.reduction = reduction - - def _one_hot(self, labels, classes, value=1): - """ - Convert labels to one hot vectors - - Args: - labels: torch tensor in format [label1, label2, label3, ...] - classes: int, number of classes - value: label value in one hot vector, default to 1 - - Returns: - return one hot format labels in shape [batchsize, classes] - """ - - one_hot = torch.zeros(labels.size(0), classes) - - #labels and value_added size must match - labels = labels.view(labels.size(0), -1) - value_added = torch.Tensor(labels.size(0), 1).fill_(value) - - value_added = value_added.to(labels.device) - one_hot = one_hot.to(labels.device) - - one_hot.scatter_add_(1, labels, value_added) - - return one_hot - - def _smooth_label(self, target, length, smooth_factor): - """convert targets to one-hot format, and smooth - them. - - Args: - target: target in form with [label1, label2, label_batchsize] - length: length of one-hot format(number of classes) - smooth_factor: smooth factor for label smooth - - Returns: - smoothed labels in one hot format - """ - one_hot = self._one_hot(target, length, value=1 - smooth_factor) - one_hot += smooth_factor / length - - return one_hot.to(target.device) - - def forward(self, x, target): - - if x.size(0) != target.size(0): - raise ValueError('Expected input batchsize ({}) to match target batch_size({})' - .format(x.size(0), target.size(0))) - - if x.dim() < 2: - raise ValueError('Expected input tensor to have least 2 dimensions(got {})' - .format(x.size(0))) - - if x.dim() != 2: - raise ValueError('Only 2 dimension tensor are implemented, (got {})' - .format(x.size())) - - - smoothed_target = self._smooth_label(target, x.size(1), self.e) - x = self.log_softmax(x) - loss = torch.sum(- x * smoothed_target, dim=1) - - if self.reduction == 'none': - return loss - - elif self.reduction == 'sum': - return torch.sum(loss) - - elif self.reduction == 'mean': - return torch.mean(loss) - - else: - raise ValueError('unrecognized option, expect reduction to be one of none, mean, sum') - diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/__init__.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/__init__.py deleted file mode 100644 index 13c4f358e..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/criterion/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ - -from .LabelSmoothing import LSR \ No newline at end of file diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/dataset/dataset.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/dataset/dataset.py deleted file mode 100644 index 016d9c67c..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/dataset/dataset.py +++ /dev/null @@ -1,182 +0,0 @@ - -import os - -import cv2 -import numpy as np -from torch.utils.data import Dataset -from PIL import Image - -class CUB_200_2011_Train(Dataset): - - def __init__(self, path, transform=None, target_transform=None): - - self.root = path - self.transform = transform - self.target_transform = target_transform - self.images_path = {} - with open(os.path.join(self.root, 'images.txt')) as f: - for line in f: - image_id, path = line.split() - self.images_path[image_id] = path - - self.class_ids = {} - with open(os.path.join(self.root, 'image_class_labels.txt')) as f: - for line in f: - image_id, class_id = line.split() - self.class_ids[image_id] = class_id - - self.train_id = [] - with open(os.path.join(self.root, 'train_test_split.txt')) as f: - for line in f: - image_id, is_train = line.split() - if int(is_train): - self.train_id.append(image_id) - - def __len__(self): - return len(self.train_id) - - def __getitem__(self, index): - """ - Args: - index: index of training dataset - Returns: - image and its corresponding label - """ - image_id = self.train_id[index] - class_id = int(self._get_class_by_id(image_id)) - 1 - path = self._get_path_by_id(image_id) - image = cv2.imread(os.path.join(self.root, 'images', path)) - #image = Image.open(os.path.join(self.root, 'images', path)) - #if image.mode != 'RGB': - # image = image.convert('RGB') - #if len(image.shape) != 3: - # image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) - #image = np.array(image) - - if self.transform: - image = self.transform(image) - - if self.target_transform: - class_id = self.target_transform(class_id) - return image, class_id - - def _get_path_by_id(self, image_id): - - return self.images_path[image_id] - - def _get_class_by_id(self, image_id): - - return self.class_ids[image_id] - - -class CUB_200_2011_Test(Dataset): - - def __init__(self, path, transform=None, target_transform=None): - - self.root = path - self.transform = transform - self.target_transform = target_transform - self.images_path = {} - with open(os.path.join(self.root, 'images.txt')) as f: - for line in f: - image_id, path = line.split() - self.images_path[image_id] = path - - self.class_ids = {} - with open(os.path.join(self.root, 'image_class_labels.txt')) as f: - for line in f: - image_id, class_id = line.split() - self.class_ids[image_id] = class_id - - self.train_id = [] - with open(os.path.join(self.root, 'train_test_split.txt')) as f: - for line in f: - image_id, is_train = line.split() - if not int(is_train): - self.train_id.append(image_id) - - def __len__(self): - return len(self.train_id) - - def __getitem__(self, index): - """ - Args: - index: index of training dataset - Returns: - image and its corresponding label - """ - image_id = self.train_id[index] - class_id = int(self._get_class_by_id(image_id)) - 1 - path = self._get_path_by_id(image_id) - image = cv2.imread(os.path.join(self.root, 'images', path)) - #image = Image.open(os.path.join(self.root, 'images', path)) - #if image.mode != 'RGB': - # image = image.convert('RGB') - #image = np.array(image) - - - #if len(image.shape) != 3: - # image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) - - - if self.transform: - image = self.transform(image) - - if self.target_transform: - class_id = self.target_transform(class_id) - - return image, class_id - - def _get_path_by_id(self, image_id): - - return self.images_path[image_id] - - def _get_class_by_id(self, image_id): - - return self.class_ids[image_id] - - -def compute_mean_and_std(dataset): - """Compute dataset mean and std, and normalize it - - Args: - dataset: instance of CUB_200_2011_Train, CUB_200_2011_Test - - Returns: - return: mean and std of this dataset - """ - - mean_r = 0 - mean_g = 0 - mean_b = 0 - - for img, _ in dataset: - mean_b += np.mean(img[:, :, 0]) - mean_g += np.mean(img[:, :, 1]) - mean_r += np.mean(img[:, :, 2]) - - mean_b /= len(dataset) - mean_g /= len(dataset) - mean_r /= len(dataset) - - diff_r = 0 - diff_g = 0 - diff_b = 0 - - N = 0 - - for img, _ in dataset: - - diff_b += np.sum(np.power(img[:, :, 0] - mean_b, 2)) - diff_g += np.sum(np.power(img[:, :, 1] - mean_g, 2)) - diff_r += np.sum(np.power(img[:, :, 2] - mean_r, 2)) - - N += np.prod(img[:, :, 0].shape) - - std_b = np.sqrt(diff_b / N) - std_g = np.sqrt(diff_g / N) - std_r = np.sqrt(diff_r / N) - - mean = (mean_b.item() / 255.0, mean_g.item() / 255.0, mean_r.item() / 255.0) - std = (std_b.item() / 255.0, std_g.item() / 255.0, std_r.item() / 255.0) - return mean, std diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_find.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_find.py deleted file mode 100644 index 4a6ea5f3d..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_find.py +++ /dev/null @@ -1,126 +0,0 @@ - -import argparse -import glob -import os - -import cv2 -import torch -import torch.nn as nn -import torch.optim as optim -from torch.utils.data import DataLoader -import numpy as np - -#from PIL import Image -import transforms -#from torchvision import transforms -from tensorboardX import SummaryWriter -from conf import settings -from utils import * -from lr_scheduler import FindLR -from criterion import LSR - -import matplotlib -matplotlib.use('Agg') -import matplotlib.pyplot as plt - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('-net', type=str, required=True, help='net type') - parser.add_argument('-w', type=int, default=2, help='number of workers for dataloader') - parser.add_argument('-b', type=int, default=64, help='batch size for dataloader') - parser.add_argument('-base_lr', type=float, default=1e-7, help='min learning rate') - parser.add_argument('-max_lr', type=float, default=10, help='max learning rate') - parser.add_argument('-num_iter', type=int, default=100, help='num of iteration') - parser.add_argument('-gpus', nargs='+', type=int, default=0, help='gpu device') - args = parser.parse_args() - - - train_transforms = transforms.Compose([ - #transforms.ToPILImage(), - transforms.ToCVImage(), - transforms.RandomResizedCrop(settings.IMAGE_SIZE), - transforms.RandomHorizontalFlip(), - transforms.ColorJitter(brightness=0.4, saturation=0.4, hue=0.4), - #transforms.RandomErasing(), - #transforms.CutOut(56), - transforms.ToTensor(), - transforms.Normalize(settings.TRAIN_MEAN, settings.TRAIN_STD) - ]) - - train_dataloader = get_train_dataloader( - settings.DATA_PATH, - train_transforms, - args.b, - args.w - ) - - net = get_network(args) - net = init_weights(net) - - if isinstance(args.gpus, int): - args.gpus = [args.gpus] - - net = nn.DataParallel(net, device_ids=args.gpus) - net = net.cuda() - - lsr_loss = LSR() - - #apply no weight decay on bias - params = split_weights(net) - optimizer = optim.SGD(params, lr=args.base_lr, momentum=0.9, weight_decay=1e-4, nesterov=True) - - #set up warmup phase learning rate scheduler - lr_scheduler = FindLR(optimizer, max_lr=args.max_lr, num_iter=args.num_iter) - epoches = int(args.num_iter / len(train_dataloader)) + 1 - - n = 0 - learning_rate = [] - losses = [] - for epoch in range(epoches): - - #training procedure - net.train() - - for batch_index, (images, labels) in enumerate(train_dataloader): - if n > args.num_iter: - break - - lr_scheduler.step() - - images = images.cuda() - labels = labels.cuda() - - optimizer.zero_grad() - predicts = net(images) - loss = lsr_loss(predicts, labels) - if torch.isnan(loss).any(): - n += 1e8 - break - loss.backward() - optimizer.step() - - n_iter = (epoch - 1) * len(train_dataloader) + batch_index + 1 - print('Iterations: {iter_num} [{trained_samples}/{total_samples}]\tLoss: {:0.4f}\tLR: {:0.8f}'.format( - loss.item(), - optimizer.param_groups[0]['lr'], - iter_num=n, - trained_samples=batch_index * args.b + len(images), - total_samples=len(train_dataloader.dataset), - )) - - learning_rate.append(optimizer.param_groups[0]['lr']) - losses.append(loss.item()) - n += 1 - - learning_rate = learning_rate[10:-5] - losses = losses[10:-5] - - fig, ax = plt.subplots(1,1) - ax.plot(learning_rate, losses) - ax.set_xlabel('learning rate') - ax.set_ylabel('losses') - ax.set_xscale('log') - ax.xaxis.set_major_formatter(plt.FormatStrFormatter('%.0e')) - - fig.savefig('result.jpg') - diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/FindLR.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/FindLR.py deleted file mode 100644 index 72a31c42e..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/FindLR.py +++ /dev/null @@ -1,23 +0,0 @@ - -import torch -from torch.optim.lr_scheduler import _LRScheduler - - -class FindLR(_LRScheduler): - """exponentially increasing learning rate - - Args: - optimizer: optimzier(e.g. SGD) - num_iter: totoal_iters - max_lr: maximum learning rate - """ - def __init__(self, optimizer, max_lr=10, num_iter=100, last_epoch=-1): - - self.total_iters = num_iter - self.max_lr = max_lr - super().__init__(optimizer, last_epoch) - - def get_lr(self): - - return [base_lr * (self.max_lr / base_lr) ** (self.last_epoch / (self.total_iters + 1e-32)) for base_lr in self.base_lrs] - diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/WarmUpLR.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/WarmUpLR.py deleted file mode 100644 index 3d9cd832e..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/WarmUpLR.py +++ /dev/null @@ -1,24 +0,0 @@ - -import torch -from torch.optim.lr_scheduler import _LRScheduler - - -class WarmUpLR(_LRScheduler): - """warmup_training learning rate scheduler - - Args: - optimizer: optimzier(e.g. SGD) - total_iters: totoal_iters of warmup phase - """ - def __init__(self, optimizer, total_iters, last_epoch=-1): - - self.total_iters = total_iters - super().__init__(optimizer, last_epoch) - - def get_lr(self): - """we will use the first m batches, and set the learning - rate to base_lr * m / total_iters - """ - return [base_lr * self.last_epoch / (self.total_iters + 1e-8) for base_lr in self.base_lrs] - - diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/__init__.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/__init__.py deleted file mode 100644 index f5273d512..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/lr_scheduler/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ - -from .WarmUpLR import WarmUpLR -from .FindLR import FindLR \ No newline at end of file diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/train.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/train.py deleted file mode 100644 index 2607c39d2..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/train.py +++ /dev/null @@ -1,195 +0,0 @@ - -"""author - baiyu -""" - -import argparse -import glob -import os - -import torch -import torch.nn as nn -import torch.optim as optim -import numpy as np - -#from PIL import Image -import transforms -#from torchvision import transforms -from tensorboardX import SummaryWriter -from conf import settings -from utils import * -from lr_scheduler import WarmUpLR -from criterion import LSR - -if __name__ == '__main__': - - parser = argparse.ArgumentParser() - parser.add_argument('-net', type=str, required=True, help='net type') - parser.add_argument('-w', type=int, default=2, help='number of workers for dataloader') - parser.add_argument('-b', type=int, default=256, help='batch size for dataloader') - parser.add_argument('-lr', type=float, default=0.04, help='initial learning rate') - parser.add_argument('-e', type=int, default=450, help='training epoches') - parser.add_argument('-warm', type=int, default=5, help='warm up phase') - parser.add_argument('-gpus', nargs='+', type=int, default=0, help='gpu device') - args = parser.parse_args() - - #checkpoint directory - checkpoint_path = os.path.join(settings.CHECKPOINT_PATH, args.net, settings.TIME_NOW) - if not os.path.exists(checkpoint_path): - os.makedirs(checkpoint_path) - checkpoint_path = os.path.join(checkpoint_path, '{net}-{epoch}-{type}.pth') - - #tensorboard log directory - log_path = os.path.join(settings.LOG_DIR, args.net, settings.TIME_NOW) - if not os.path.exists(log_path): - os.makedirs(log_path) - writer = SummaryWriter(log_dir=log_path) - - #get dataloader - train_transforms = transforms.Compose([ - #transforms.ToPILImage(), - transforms.ToCVImage(), - transforms.RandomResizedCrop(settings.IMAGE_SIZE), - transforms.RandomHorizontalFlip(), - transforms.ColorJitter(brightness=0.4, saturation=0.4, hue=0.4), - #transforms.RandomErasing(), - #transforms.CutOut(56), - transforms.ToTensor(), - transforms.Normalize(settings.TRAIN_MEAN, settings.TRAIN_STD) - ]) - - test_transforms = transforms.Compose([ - transforms.ToCVImage(), - transforms.CenterCrop(settings.IMAGE_SIZE), - transforms.ToTensor(), - transforms.Normalize(settings.TRAIN_MEAN, settings.TRAIN_STD) - ]) - - train_dataloader = get_train_dataloader( - settings.DATA_PATH, - train_transforms, - args.b, - args.w - ) - - test_dataloader = get_test_dataloader( - settings.DATA_PATH, - test_transforms, - args.b, - args.w - ) - - #device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") - net = get_network(args) - net = init_weights(net) - - - if isinstance(args.gpus, int): - args.gpus = [args.gpus] - - net = nn.DataParallel(net, device_ids=args.gpus) - net = net.cuda() - - #visualize the network - visualize_network(writer, net.module) - - #cross_entropy = nn.CrossEntropyLoss() - lsr_loss = LSR() - - #apply no weight decay on bias - params = split_weights(net) - optimizer = optim.SGD(params, lr=args.lr, momentum=0.9, weight_decay=1e-4, nesterov=True) - - #set up warmup phase learning rate scheduler - iter_per_epoch = len(train_dataloader) - warmup_scheduler = WarmUpLR(optimizer, iter_per_epoch * args.warm) - - #set up training phase learning rate scheduler - train_scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=settings.MILESTONES) - #train_scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, args.e - args.warm) - - best_acc = 0.0 - for epoch in range(1, args.e + 1): - if epoch > args.warm: - train_scheduler.step(epoch) - - #training procedure - net.train() - - for batch_index, (images, labels) in enumerate(train_dataloader): - if epoch <= args.warm: - warmup_scheduler.step() - - images = images.cuda() - labels = labels.cuda() - - optimizer.zero_grad() - predicts = net(images) - loss = lsr_loss(predicts, labels) - loss.backward() - optimizer.step() - - n_iter = (epoch - 1) * len(train_dataloader) + batch_index + 1 - print('Training Epoch: {epoch} [{trained_samples}/{total_samples}]\tLoss: {:0.4f}\t'.format( - loss.item(), - epoch=epoch, - trained_samples=batch_index * args.b + len(images), - total_samples=len(train_dataloader.dataset), - )) - - #visualization - visualize_lastlayer(writer, net, n_iter) - visualize_train_loss(writer, loss.item(), n_iter) - - visualize_learning_rate(writer, optimizer.param_groups[0]['lr'], epoch) - visualize_param_hist(writer, net, epoch) - - net.eval() - - total_loss = 0 - correct = 0 - for images, labels in test_dataloader: - - images = images.cuda() - labels = labels.cuda() - - predicts = net(images) - _, preds = predicts.max(1) - correct += preds.eq(labels).sum().float() - - loss = lsr_loss(predicts, labels) - total_loss += loss.item() - - test_loss = total_loss / len(test_dataloader) - acc = correct / len(test_dataloader.dataset) - print('Test set: loss: {:.4f}, Accuracy: {:.4f}'.format(test_loss, acc)) - print() - - visualize_test_loss(writer, test_loss, epoch) - visualize_test_acc(writer, acc, epoch) - - #save weights file - if epoch > settings.MILESTONES[1] and best_acc < acc: - torch.save(net.state_dict(), checkpoint_path.format(net=args.net, epoch=epoch, type='best')) - best_acc = acc - continue - - if not epoch % settings.SAVE_EPOCH: - torch.save(net.state_dict(), checkpoint_path.format(net=args.net, epoch=epoch, type='regular')) - - writer.close() - - - - - - - - - - - - - - - diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/__init__.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/__init__.py deleted file mode 100644 index ade5c6c47..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ - -from .transforms import ( - CenterCrop, - ColorJitter, - Compose, - CutOut, - Normalize, - RandomHorizontalFlip, - RandomResizedCrop, - RandomErasing, - ToTensor, - ToCVImage -) \ No newline at end of file diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/transforms.py b/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/transforms.py deleted file mode 100644 index 9f14e8fbb..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/transforms/transforms.py +++ /dev/null @@ -1,404 +0,0 @@ - -import random -import math -import numbers - -import cv2 -import numpy as np - -import torch - -class Compose: - """Composes several transforms together. - - Args: - transforms(list of 'Transform' object): list of transforms to compose - - """ - - def __init__(self, transforms): - self.transforms = transforms - - def __call__(self, img): - - for trans in self.transforms: - img = trans(img) - - return img - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - format_string += '\n' - format_string += ' {0}'.format(t) - format_string += '\n)' - return format_string - - -class ToCVImage: - """Convert an Opencv image to a 3 channel uint8 image - """ - - def __call__(self, image): - """ - Args: - image (numpy array): Image to be converted to 32-bit floating point - - Returns: - image (numpy array): Converted Image - """ - if len(image.shape) == 2: - image = cv2.cvtColor(iamge, cv2.COLOR_GRAY2BGR) - - image = image.astype('uint8') - - return image - - -class RandomResizedCrop: - """Randomly crop a rectangle region whose aspect ratio is randomly sampled - in [3/4, 4/3] and area randomly sampled in [8%, 100%], then resize the cropped - region into a 224-by-224 square image. - - Args: - size: expected output size of each edge - scale: range of size of the origin size cropped - ratio: range of aspect ratio of the origin aspect ratio cropped (w / h) - interpolation: Default: cv2.INTER_LINEAR: - """ - - def __init__(self, size, scale=(0.08, 1.0), ratio=(3.0 / 4.0, 4.0 / 3.0), interpolation='linear'): - - self.methods={ - "area":cv2.INTER_AREA, - "nearest":cv2.INTER_NEAREST, - "linear" : cv2.INTER_LINEAR, - "cubic" : cv2.INTER_CUBIC, - "lanczos4" : cv2.INTER_LANCZOS4 - } - - self.size = (size, size) - self.interpolation = self.methods[interpolation] - self.scale = scale - self.ratio = ratio - - def __call__(self, img): - h, w, _ = img.shape - - area = w * h - - for attempt in range(10): - target_area = random.uniform(*self.scale) * area - target_ratio = random.uniform(*self.ratio) - - output_h = int(round(math.sqrt(target_area * target_ratio))) - output_w = int(round(math.sqrt(target_area / target_ratio))) - - if random.random() < 0.5: - output_w, output_h = output_h, output_w - - if output_w <= w and output_h <= h: - topleft_x = random.randint(0, w - output_w) - topleft_y = random.randint(0, h - output_h) - break - - if output_w > w or output_h > h: - output_w = min(w, h) - output_h = output_w - topleft_x = random.randint(0, w - output_w) - topleft_y = random.randint(0, h - output_w) - - cropped = img[topleft_y : topleft_y + output_h, topleft_x : topleft_x + output_w] - - resized = cv2.resize(cropped, self.size, interpolation=self.interpolation) - - return resized - - def __repr__(self): - for name, inter in self.methods.items(): - if inter == self.interpolation: - inter_name = name - - interpolate_str = inter_name - format_str = self.__class__.__name__ + '(size={0}'.format(self.size) - format_str += ', scale={0}'.format(tuple(round(s, 4) for s in self.scale)) - format_str += ', ratio={0}'.format(tuple(round(r, 4) for r in self.ratio)) - format_str += ', interpolation={0})'.format(interpolate_str) - - return format_str - - -class RandomHorizontalFlip: - """Horizontally flip the given opencv image with given probability p. - - Args: - p: probability of the image being flipped - """ - def __init__(self, p=0.5): - self.p = p - - def __call__(self, img): - """ - Args: - the image to be flipped - Returns: - flipped image - """ - if random.random() < self.p: - img = cv2.flip(img, 1) - - return img - -class ColorJitter: - - """Randomly change the brightness, contrast and saturation of an image - - Args: - brightness: (float or tuple of float(min, max)): how much to jitter - brightness, brightness_factor is choosen uniformly from[max(0, 1-brightness), - 1 + brightness] or the given [min, max], Should be non negative numbe - contrast: same as brightness - saturation: same as birghtness - hue: same as brightness - """ - - def __init__(self, brightness=0, contrast=0, saturation=0, hue=0): - self.brightness = self._check_input(brightness) - self.contrast = self._check_input(contrast) - self.saturation = self._check_input(saturation) - self.hue = self._check_input(hue) - - def _check_input(self, value): - - if isinstance(value, numbers.Number): - assert value >= 0, 'value should be non negative' - value = [max(0, 1 - value), 1 + value] - - elif isinstance(value, (list, tuple)): - assert len(value) == 2, 'brightness should be a tuple/list with 2 elements' - assert 0 <= value[0] <= value[1], 'max should be larger than or equal to min,\ - and both larger than 0' - - else: - raise TypeError('need to pass int, float, list or tuple, instead got{}'.format(type(value).__name__)) - - return value - - def __call__(self, img): - """ - Args: - img to be jittered - Returns: - jittered img - """ - - img_dtype = img.dtype - h_factor = random.uniform(*self.hue) - b_factor = random.uniform(*self.brightness) - s_factor = random.uniform(*self.saturation) - c_factor = random.uniform(*self.contrast) - - img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) - img = img.astype('float32') - - #h - img[:, :, 0] *= h_factor - img[:, :, 0] = np.clip(img[:, :, 0], 0, 179) - - #s - img[:, :, 1] *= s_factor - img[:, :, 1] = np.clip(img[:, :, 1], 0, 255) - - #v - img[:, :, 2] *= b_factor - img[:, :, 2] = np.clip(img[:, :, 2], 0, 255) - - img = img.astype(img_dtype) - img = cv2.cvtColor(img, cv2.COLOR_HSV2BGR) - - #c - img = img * c_factor - img = img.astype(img_dtype) - img = np.clip(img, 0, 255) - - return img - -class ToTensor: - """convert an opencv image (h, w, c) ndarray range from 0 to 255 to a pytorch - float tensor (c, h, w) ranged from 0 to 1 - """ - - def __call__(self, img): - """ - Args: - a numpy array (h, w, c) range from [0, 255] - - Returns: - a pytorch tensor - """ - #convert format H W C to C H W - img = img.transpose(2, 0, 1) - img = torch.from_numpy(img) - img = img.float() / 255.0 - - return img - -class Normalize: - """Normalize a torch tensor (H, W, BGR order) with mean and standard deviation - - for each channel in torch tensor: - ``input[channel] = (input[channel] - mean[channel]) / std[channel]`` - - Args: - mean: sequence of means for each channel - std: sequence of stds for each channel - """ - - def __init__(self, mean, std, inplace=False): - self.mean = mean - self.std = std - self.inplace = inplace - - def __call__(self, img): - """ - Args: - (H W C) format numpy array range from [0, 255] - Returns: - (H W C) format numpy array in float32 range from [0, 1] - """ - assert torch.is_tensor(img) and img.ndimension() == 3, 'not an image tensor' - - if not self.inplace: - img = img.clone() - - mean = torch.tensor(self.mean, dtype=torch.float32) - std = torch.tensor(self.std, dtype=torch.float32) - img.sub_(mean[:, None, None]).div_(std[:, None, None]) - - return img - -class CenterCrop: - """resize each image’s shorter edge to r pixels while keeping its aspect ratio. - Next, we crop out the cropped region in the center - Args: - resized: resize image' shorter edge to resized pixels while keeping the aspect ratio - cropped: output image size(h, w), if cropped is an int, then output cropped * cropped size - image - """ - - def __init__(self, cropped, resized=256, interpolation='linear'): - - methods = { - "area":cv2.INTER_AREA, - "nearest":cv2.INTER_NEAREST, - "linear" : cv2.INTER_LINEAR, - "cubic" : cv2.INTER_CUBIC, - "lanczos4" : cv2.INTER_LANCZOS4 - } - self.interpolation = methods[interpolation] - - self.resized = resized - - if isinstance(cropped, numbers.Number): - cropped = (cropped, cropped) - - self.cropped = cropped - - def __call__(self, img): - - shorter = min(*img.shape[:2]) - - scaler = float(self.resized) / shorter - - img = cv2.resize(img, (0, 0), fx=scaler, fy=scaler, interpolation=self.interpolation) - - h, w, _ = img.shape - - topleft_x = int((w - self.cropped[1]) / 2) - topleft_y = int((h - self.cropped[0]) / 2) - - center_cropped = img[topleft_y : topleft_y + self.cropped[0], - topleft_x : topleft_x + self.cropped[1]] - - return center_cropped - -class RandomErasing: - """Random erasing the an rectangle region in Image. - Class that performs Random Erasing in Random Erasing Data Augmentation by Zhong et al. - - Args: - sl: min erasing area region - sh: max erasing area region - r1: min aspect ratio range of earsing region - p: probability of performing random erasing - """ - - def __init__(self, p=0.5, sl=0.02, sh=0.4, r1=0.3): - - self.p = p - self.s = (sl, sh) - self.r = (r1, 1/r1) - - - def __call__(self, img): - """ - perform random erasing - Args: - img: opencv numpy array in form of [w, h, c] range - from [0, 255] - - Returns: - erased img - """ - - assert len(img.shape) == 3, 'image should be a 3 dimension numpy array' - - if random.random() > self.p: - return img - - else: - while True: - Se = random.uniform(*self.s) * img.shape[0] * img.shape[1] - re = random.uniform(*self.r) - - He = int(round(math.sqrt(Se * re))) - We = int(round(math.sqrt(Se / re))) - - xe = random.randint(0, img.shape[1]) - ye = random.randint(0, img.shape[0]) - - if xe + We <= img.shape[1] and ye + He <= img.shape[0]: - img[ye : ye + He, xe : xe + We, :] = np.random.randint(low=0, high=255, size=(He, We, img.shape[2])) - - return img - -class CutOut: - """Randomly mask out one or more patches from an image. An image - is a opencv format image (h,w,c numpy array) - - Args: - n_holes (int): Number of patches to cut out of each image. - length (int): The length (in pixels) of each square patch. - """ - - def __init__(self, length, n_holes=1): - self.n_holes = n_holes - self.length = length - - def __call__(self, img): - - while self.n_holes: - - y = random.randint(0, img.shape[0] - 1) - x = random.randint(0, img.shape[1] - 1) - - tl_x = int(max(0, x - self.length / 2)) - tl_y = int(max(0, y - self.length / 2)) - - img[tl_y : tl_y + self.length, tl_x : tl_x + self.length, :] = 0 - - self.n_holes -= 1 - - return img - - diff --git a/PyTorch/build-in/Classification/BagOfTricks/coverage.txt b/PyTorch/build-in/Classification/BagOfTricks/coverage.txt deleted file mode 100644 index fc5251079..000000000 --- a/PyTorch/build-in/Classification/BagOfTricks/coverage.txt +++ /dev/null @@ -1,3 +0,0 @@ -all api: ['_amp_foreach_non_finite_check_and_unscale_', '_amp_update_scale_', '_copy_from', '_has_compatible_shallow_copy_type', '_local_scalar_dense', '_log_softmax', '_log_softmax_backward_data', '_pin_memory', '_reshape_alias', 'add_', 'addmm', 'as_strided', 'convolution', 'convolution_backward', 'copy_stride', 'div', 'dropout', 'dropout_backward', 'dropout_forward', 'eq', 'fill_', 'fused_sgd', 'is_pinned', 'linear', 'max_pool2d', 'maxpool2d_backward', 'maxpool2d_forward', 'mm', 'mul', 'mul_', 'native_batch_norm', 'native_batch_norm_backward', 'nll_loss_backward', 'nll_loss_forward', 'reciprocal', 'relu_', 'sum', 'threshold_backward', 'topk_out', 'view'], total: 40 -fallback op: [], total: 0 -coverage rate: 100.00% diff --git a/PyTorch/build-in/Classification/BagOfTricks/readme b/PyTorch/build-in/Classification/BagOfTricks/readme new file mode 100644 index 000000000..f0cf25fce --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/readme @@ -0,0 +1,65 @@ +```markdown +## 1. 模型链接 +- 原始仓库链接: +https://github.com/huggingface/pytorch-image-models?tab=readme-ov-file#models + +## 2. 快速开始 + +使用本模型执行训练的主要流程如下: + +1. **基础环境安装**:介绍训练前需要完成的基础环境检查和安装。 +2. **获取数据集**:介绍如何获取训练所需的数据集。 +3. **构建环境**:介绍如何构建模型运行所需要的环境。 +4. **启动训练**:介绍如何运行训练。 + +### 2.1 基础环境安装 + +请参考主仓库的基础环境安装章节,完成训练前的基础环境检查和安装(如驱动、固件等)。 + +### 2.2 准备数据集 + +#### 2.2.1 获取数据集 + +训练使用 **CIFAR-100** 数据集。该数据集为开源数据集,包含 100 个类别的 60000 张彩色图像。 + +#### 2.2.2 处理数据集 + +请确保数据集已下载并解压。根据训练脚本的默认配置,建议将数据集存放在模型目录的上级 `data` 目录中(即 `../data`),或者根据实际路径修改训练命令中的 `--datapath` 参数。 + +### 2.3 构建环境 + +所使用的环境下需包含 PyTorch 框架虚拟环境。 + +1. 执行以下命令,启动虚拟环境(根据实际环境名称修改): + + ```bash + conda activate torch_env_py310 + +``` + +2. 安装 Python 依赖。确保已安装项目所需的依赖包: +```bash +pip install -r requirements_exact.txt + +``` + + + +### 2.4 启动训练 + +1. 在构建好的环境中,进入模型训练脚本所在目录。 + +2. 运行训练。该模型支持单机单卡训练。 +执行以下命令启动训练(使用 CIFAR-100 数据集,Batch Size 为 128): +```bash +python weloTrainStep.py \ + --name train \ + --arch vgg \ + --print_freq 1 \ + --steps 100 \ + --dataset cifar100 \ + --datapath ../data \ + --batch_size 32 \ + --epochs 100 + +``` diff --git a/PyTorch/build-in/Classification/BagOfTricks/requirements_exact.txt b/PyTorch/build-in/Classification/BagOfTricks/requirements_exact.txt new file mode 100644 index 000000000..7394b3319 --- /dev/null +++ b/PyTorch/build-in/Classification/BagOfTricks/requirements_exact.txt @@ -0,0 +1,89 @@ +addict==2.4.0 +aliyun-python-sdk-core==2.16.0 +aliyun-python-sdk-kms==2.16.5 +anyio==4.11.0 +astunparse==1.6.3 +certifi==2024.12.14 +cffi==2.0.0 +charset-normalizer==3.4.1 +click==8.3.1 +colorama==0.4.6 +contourpy==1.3.2 +crcmod==1.7 +cryptography==46.0.3 +cycler==0.12.1 +einops==0.8.1 +exceptiongroup==1.3.1 +filelock==3.14.0 +fonttools==4.60.1 +fsspec==2024.12.0 +future @ file:///croot/future_1730902796226/work +git-filter-repo==2.47.0 +h11==0.16.0 +hf-xet==1.2.0 +httpcore==1.0.9 +httpx==0.28.1 +huggingface_hub==1.1.5 +idna==3.10 +inplace-abn @ git+https://github.com/mapillary/inplace_abn.git@b50bfe9c7cd7116a3ab091a352b48d6ba5ee701c +Jinja2==3.1.5 +jmespath==0.10.0 +joblib==1.5.2 +kiwisolver==1.4.9 +Markdown==3.10 +markdown-it-py==4.0.0 +MarkupSafe==3.0.2 +matplotlib==3.10.7 +mdurl==0.1.2 +mmdet==3.3.0 +mmengine==0.10.7 +model-index==0.1.11 +mpmath==1.3.0 +networkx==3.4.2 +numpy==1.23.5 +opencv-python==4.12.0.88 +opendatalab==0.0.10 +openmim==0.3.9 +openxlab==0.1.3 +ordered-set==4.1.0 +oss2==2.17.0 +packaging @ file:///croot/packaging_1734472117206/work +pandas==2.3.3 +pillow==11.1.0 +platformdirs==4.5.1 +pycocotools==2.0.11 +pycparser @ file:///tmp/build/80754af9/pycparser_1636541352034/work +pycryptodome==3.23.0 +Pygments==2.19.2 +pyparsing==3.2.5 +python-dateutil==2.9.0.post0 +pytz==2023.4 +PyYAML @ file:///croot/pyyaml_1728657952215/work +requests==2.28.2 +rich==13.4.2 +safetensors==0.7.0 +scikit-learn==1.7.2 +scipy==1.15.3 +shapely==2.1.2 +shellingham==1.5.4 +six @ file:///tmp/build/80754af9/six_1644875935023/work +sniffio==1.3.1 +sympy==1.13.3 +tabulate==0.9.0 +termcolor==3.2.0 +terminaltables==3.1.10 +threadpoolctl==3.6.0 +timm==1.0.22 +tomli==2.3.0 +torch @ file:///apps/torch-2.4.0a0%2Bgit4451b0e-cp310-cp310-linux_x86_64.whl#sha256=2e472c916044cac5a1a0e0d8b0e12bb943d8522b24ff826c8014dd444dccd378 +torch_sdaa @ file:///apps/torch_sdaa-2.0.0-cp310-cp310-linux_x86_64.whl#sha256=5aa57889b002e1231fbf806642e1353bfa016297bc25178396e89adc2b1f92e7 +torchaudio @ file:///apps/torchaudio-2.0.2%2Bda3eb8d-cp310-cp310-linux_x86_64.whl#sha256=46525c02fb7eaa8dafea860428de3d01e437ba8d6ff2cc228d7c71975ac4054b +torchdata @ file:///apps/torchdata-0.6.1%2Be1feeb2-py3-none-any.whl#sha256=aa2dc1a7732ea68adfad186978049bf68cc1afdbbdd1e17a8024227ab770e433 +torchtext @ file:///apps/torchtext-0.15.2a0%2B4571036-cp310-cp310-linux_x86_64.whl#sha256=7e42c684ba366f97b59ec37488bf95e416cce3892b6589200d2b3ad159ee5788 +torchvision @ file:///apps/torchvision-0.15.1a0%2B42759b1-cp310-cp310-linux_x86_64.whl#sha256=4b904db2d50102415536bc764bbc31c669b90b1b014f90964e9eccaadb2fd9eb +tqdm==4.65.2 +typer-slim==0.20.0 +typing_extensions==4.15.0 +tzdata==2025.2 +urllib3==1.26.20 +yapf==0.43.0 diff --git a/PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/utils.py b/PyTorch/build-in/Classification/BagOfTricks/utils.py similarity index 100% rename from PyTorch/build-in/Classification/BagOfTricks/Bag_of_Tricks_for_Image_Classification_with_Convolutional_Neural_Networks/utils.py rename to PyTorch/build-in/Classification/BagOfTricks/utils.py diff --git a/PyTorch/build-in/Classification/BagOfTricks/vgg_loss.jpg b/PyTorch/build-in/Classification/BagOfTricks/vgg_loss.jpg deleted file mode 100644 index 2a4f5b9f5918f87dd8d49fa4a0e13478828d179c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36139 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

b(7E*) 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 Date: Thu, 8 Jan 2026 10:29:07 +0000 Subject: [PATCH 3/3] fix: rename files and update code --- PyTorch/build-in/Classification/BagOfTricks/{readme => readme.md} | 0 .../BagOfTricks/{requirements_exact.txt => requirements.txt} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename PyTorch/build-in/Classification/BagOfTricks/{readme => readme.md} (100%) rename PyTorch/build-in/Classification/BagOfTricks/{requirements_exact.txt => requirements.txt} (100%) diff --git a/PyTorch/build-in/Classification/BagOfTricks/readme b/PyTorch/build-in/Classification/BagOfTricks/readme.md similarity index 100% rename from PyTorch/build-in/Classification/BagOfTricks/readme rename to PyTorch/build-in/Classification/BagOfTricks/readme.md diff --git a/PyTorch/build-in/Classification/BagOfTricks/requirements_exact.txt b/PyTorch/build-in/Classification/BagOfTricks/requirements.txt similarity index 100% rename from PyTorch/build-in/Classification/BagOfTricks/requirements_exact.txt rename to PyTorch/build-in/Classification/BagOfTricks/requirements.txt