import torch import torch.nn as nn import torch.nn.functional as F import torchvision import matplotlib.pyplot as plt from tqdm import tqdm import numpy as np from torch_lr_finder import LRFinder from pytorch_grad_cam import GradCAM from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget from pytorch_grad_cam.utils.image import show_cam_on_image import albumentations as A from albumentations.pytorch import ToTensorV2 # Train data transformations means = [0.4914, 0.4822, 0.4465] stds = [0.2470, 0.2435, 0.2616] train_transforms = A.Compose( [ A.Normalize(mean=means, std=stds, always_apply=True), A.PadIfNeeded(min_height=36, min_width=36, always_apply=True), A.RandomCrop(height=32, width=32, always_apply=True), A.HorizontalFlip(), A.CoarseDropout(max_holes=1, max_height=16, max_width=16, min_holes=1, min_height=8, min_width=8, fill_value=means), ToTensorV2(), ] ) test_transforms = A.Compose( [ A.Normalize(mean=means, std=stds, always_apply=True), ToTensorV2(), ] ) class Cifar10SearchDataset(torchvision.datasets.CIFAR10): def __init__(self, root="~/data", train=True, download=True, transform=None): super().__init__(root=root, train=train, download=download, transform=transform) def __getitem__(self, index): image, label = self.data[index], self.targets[index] if self.transform is not None: transformed = self.transform(image=image) image = transformed["image"] return image, label def dataloader(data_path,batch_size):#,train_transforms,test_transforms): trainset = Cifar10SearchDataset(root=data_path, train=True,download=True, transform=train_transforms) trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size,shuffle=True) testset = Cifar10SearchDataset(root=data_path, train=False, download=True, transform=test_transforms) testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size,shuffle=False) classes = trainset.classes return trainloader, testloader, classes def plot_sample_data(dataloader): batch_data, batch_label = next(iter(dataloader)) fig = plt.figure() for i in range(12): plt.subplot(3, 4, i + 1) plt.tight_layout() plt.imshow(torch.permute(batch_data[i], (1, 2, 0))) plt.title(batch_label[i].item()) plt.xticks([]) plt.yticks([]) class trainer: def __init__(self,model,device,optimizer,scheduler): self.model = model self.device = device self.optimizer = optimizer self.scheduler = scheduler self.device = device self.train_losses = [] self.test_losses = [] self.train_acc = [] self.test_acc = [] def getcorrectpredcount(self,prediction, labels): return prediction.argmax(dim=1).eq(labels).sum().item() def train(self,train_loader): self.model.train() pbar = tqdm(train_loader) train_loss = 0 correct = 0 processed = 0 criterion = nn.CrossEntropyLoss() for batch_idx, (data, target) in enumerate(pbar): data, target = data.to(self.device), target.to(self.device) self.optimizer.zero_grad() # Predict pred = self.model(data) # Calculate loss loss = criterion(pred, target) train_loss += loss.item() # Backpropagation loss.backward() self.optimizer.step() correct += self.getcorrectpredcount(pred, target) processed += len(data) pbar.set_description( desc=f'Train: Loss={loss.item():0.4f} Batch_id={batch_idx} Accuracy={100 * correct / processed:0.2f}') self.train_acc.append(100 * correct / processed) self.train_losses.append(train_loss / len(train_loader)) return self.train_acc, self.train_losses def test(self,test_loader): self.model.eval() test_loss = 0 correct = 0 with torch.no_grad(): for batch_idx, (data, target) in enumerate(test_loader): data, target = data.to(self.device), target.to(self.device) output = self.model(data) test_loss += F.cross_entropy(output, target, reduction='sum').item() # sum up batch loss correct += self.getcorrectpredcount(output, target) test_loss /= len(test_loader.dataset) self.test_acc.append(100. * correct / len(test_loader.dataset)) self.test_losses.append(test_loss) print('Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)\n'.format( test_loss, correct, len(test_loader.dataset), 100. * correct / len(test_loader.dataset))) return self.test_acc, self.test_losses def visualize_graphs(self): t = [t_items.item() for t_items in self.train_losses] fig, axs = plt.subplots(2,2,figsize=(15,10)) axs[0, 0].plot(t) axs[0, 0].set_title("Training Loss") axs[1, 0].plot(self.train_acc[4000:]) axs[1, 0].set_title("Training Accuracy") axs[0, 1].plot(self.test_losses) axs[0, 1].set_title("Test Loss") axs[1, 1].plot(self.test_acc) axs[1, 1].set_title("Test Accuracy") def evaluate_all_class(self,classes,test_loader): # prepare to count predictions for each class correct_pred = {classname: 0 for classname in classes} total_pred = {classname: 0 for classname in classes} # again no gradients needed with torch.no_grad(): for data in test_loader: images, labels = data outputs = self.model(images) _, predictions = torch.max(outputs, 1) # collect the correct predictions for each class for label, prediction in zip(labels, predictions): if label == prediction: correct_pred[classes[label]] += 1 total_pred[classes[label]] += 1 # print accuracy for each class for classname, correct_count in correct_pred.items(): accuracy = 100 * float(correct_count) / total_pred[classname] print(f'Accuracy for class: {classname:5s} is {accuracy:.1f} %') def evaluate_model(model, loader, device): cols, rows = 4, 6 figure = plt.figure(figsize=(20, 20)) for index in range(1, cols * rows + 1): k = np.random.randint(0, len(loader.dataset)) # random points from test dataset img, label = loader.dataset[k] # separate the image and label img = img.unsqueeze(0) # adding one dimention pred = model(img.to(device)) # Prediction figure.add_subplot(rows, cols, index) # making the figure plt.title(f"Predcited label {pred.argmax().item()}\n True Label: {label}") # title of plot plt.axis("off") # hiding the axis plt.imshow(img.squeeze(), cmap="gray") # showing the plot plt.show() def get_lr(optimizer): """" for tracking how your learning rate is changing throughout training """ for param_group in optimizer.param_groups: return param_group['lr'] def lr_calc(model, train_loader, optimizer, criterion): # model = Net().to(device) # optimizer = optim.Adam(model.parameters(), lr=0.03, weight_decay=1e-4) # criterion = nn.CrossEntropyLoss() lr_finder = LRFinder(model, optimizer, criterion, device="cuda") lr_finder.range_test(train_loader, end_lr=10, num_iter=200, step_mode="exp") lr_finder.plot() # to inspect the loss-learning rate graph lr_finder.reset() # to reset the model and optimizer to their initial state def unnormalize(img): channel_means = (0.4914, 0.4822, 0.4465) channel_stdevs = (0.2470, 0.2435, 0.2616) img = img.numpy().astype(dtype=np.float32) for i in range(img.shape[0]): img[i] = (img[i]*channel_stdevs[i])+channel_means[i] return np.transpose(img, (1,2,0)) def plot_grad_cam_images(model, test_loader, classes, device): # set model to evaluation mode model.eval() target_layers = [model.layer4[-1]] # Construct the CAM object once, and then re-use it on many images: cam = GradCAM(model=model, target_layers=target_layers) misclassified_images = [] actual_labels = [] actual_targets = [] predicted_labels = [] with torch.no_grad(): for data, target in test_loader: data, target = data.to(device), target.to(device) output = model(data) _, pred = torch.max(output, 1) for i in range(len(pred)): if pred[i] != target[i]: actual_targets.append(target[i]) misclassified_images.append(data[i]) actual_labels.append(classes[target[i]]) predicted_labels.append(classes[pred[i]]) # Plot the misclassified images fig = plt.figure(figsize=(12, 5)) for i in range(10): sub = fig.add_subplot(2, 5, i+1) input_tensor = misclassified_images[i].unsqueeze(dim=0) targets = [ClassifierOutputTarget(actual_targets[i])] grayscale_cam = cam(input_tensor=input_tensor, targets=targets) grayscale_cam = grayscale_cam[0, :] visualization = show_cam_on_image(unnormalize(misclassified_images[i].cpu()), grayscale_cam, use_rgb=True,image_weight=0.7) plt.imshow(visualization) sub.set_title("Actual: {}, Pred: {}".format(actual_labels[i], predicted_labels[i]), color='red') plt.tight_layout() plt.show() def plot_misclassified_images(model, test_loader, classes, device): # set model to evaluation mode model.eval() misclassified_images = [] actual_labels = [] predicted_labels = [] with torch.no_grad(): for data, target in test_loader: data, target = data.to(device), target.to(device) output = model(data) _, pred = torch.max(output, 1) for i in range(len(pred)): if pred[i] != target[i]: misclassified_images.append(data[i]) actual_labels.append(classes[target[i]]) predicted_labels.append(classes[pred[i]]) # Plot the misclassified images fig = plt.figure(figsize=(12, 5)) for i in range(10): sub = fig.add_subplot(2, 5, i+1) npimg = unnormalize(misclassified_images[i].cpu()) plt.imshow(npimg, cmap='gray', interpolation='none') sub.set_title("Actual: {}, Pred: {}".format(actual_labels[i], predicted_labels[i]),color='red') plt.tight_layout() plt.show()