""" Helper functions used in Assignment 3 """ import torch import torchvision import eecs598 import matplotlib.pyplot as plt import random import math def hello_helper(): """ This is a sample function that we will try to import and run to ensure that our environment is correctly set up on Google Colab. """ print('Hello from a3_helper.py!') def get_CIFAR10_data(validation_ratio = 0.02, flatten=False): """ Load the CIFAR-10 dataset from disk and perform preprocessing to prepare it for the linear classifier. These are the same steps as we used for the SVM, but condensed to a single function. """ X_train, y_train, X_test, y_test = eecs598.data.cifar10() # load every data on cuda X_train = X_train.cuda() y_train = y_train.cuda() X_test = X_test.cuda() y_test = y_test.cuda() # 0. Visualize some examples from the dataset. classes = [ 'plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck' ] samples_per_class = 12 samples = [] eecs598.reset_seed(0) for y, cls in enumerate(classes): plt.text(-4, 34 * y + 18, cls, ha='right') idxs, = (y_train == y).nonzero(as_tuple=True) for i in range(samples_per_class): idx = idxs[random.randrange(idxs.shape[0])].item() samples.append(X_train[idx]) img = torchvision.utils.make_grid(samples, nrow=samples_per_class) plt.imshow(eecs598.tensor_to_image(img)) plt.axis('off') plt.show() # 1. Normalize the data: subtract the mean RGB (zero mean) mean_image = X_train.mean(dim=0, keepdim=True).mean(dim=2, keepdim=True).mean(dim=3, keepdim=True) X_train -= mean_image X_test -= mean_image # 2. Reshape the image data into rows if flatten: X_train = X_train.reshape(X_train.shape[0], -1) X_test = X_test.reshape(X_test.shape[0], -1) # 3. take the validation set from the training set # Note: It should not be taken from the test set # For random permumation, you can use torch.randperm or torch.randint # But, for this homework, we use slicing instead. num_training = int( X_train.shape[0] * (1.0 - validation_ratio) ) num_validation = X_train.shape[0] - num_training # return the dataset data_dict = {} data_dict['X_val'] = X_train[num_training:num_training + num_validation] data_dict['y_val'] = y_train[num_training:num_training + num_validation] data_dict['X_train'] = X_train[0:num_training] data_dict['y_train'] = y_train[0:num_training] data_dict['X_test'] = X_test data_dict['y_test'] = y_test return data_dict ################# Visualizations ################# def plot_stats(stat_dict): # Plot the loss function and train / validation accuracies plt.subplot(1, 2, 1) plt.plot(stat_dict['loss_history'], 'o') plt.title('Loss history') plt.xlabel('Iteration') plt.ylabel('Loss') plt.subplot(1, 2, 2) plt.plot(stat_dict['train_acc_history'], 'o-', label='train') plt.plot(stat_dict['val_acc_history'], 'o-', label='val') plt.title('Classification accuracy history') plt.xlabel('Epoch') plt.ylabel('Clasification accuracy') plt.legend() plt.gcf().set_size_inches(14, 4) plt.show() def visualize_grid(Xs, ubound=255.0, padding=1): """ Reshape a 4D tensor of image data to a grid for easy visualization. Inputs: - Xs: Data of shape (N, H, W, C) - ubound: Output grid will have values scaled to the range [0, ubound] - padding: The number of blank pixels between elements of the grid """ (N, H, W, C) = Xs.shape # print(Xs.shape) grid_size = int(math.ceil(math.sqrt(N))) grid_height = H * grid_size + padding * (grid_size - 1) grid_width = W * grid_size + padding * (grid_size - 1) grid = torch.zeros((grid_height, grid_width, C), device=Xs.device) next_idx = 0 y0, y1 = 0, H for y in range(grid_size): x0, x1 = 0, W for x in range(grid_size): if next_idx < N: img = Xs[next_idx] low, high = torch.min(img), torch.max(img) grid[y0:y1, x0:x1] = ubound * (img - low) / (high - low) next_idx += 1 x0 += W + padding x1 += W + padding y0 += H + padding y1 += H + padding return grid # Visualize the weights of the network def show_net_weights(net): W1 = net.params['W1'] W1 = W1.reshape(3, 32, 32, -1).transpose(0, 3) plt.imshow(visualize_grid(W1, padding=3).type(torch.uint8).cpu()) plt.gca().axis('off') plt.show() def plot_acc_curves(stat_dict): plt.subplot(1, 2, 1) for key, single_stats in stat_dict.items(): plt.plot(single_stats['train_acc_history'], label=str(key)) plt.title('Train accuracy history') plt.xlabel('Epoch') plt.ylabel('Clasification accuracy') plt.subplot(1, 2, 2) for key, single_stats in stat_dict.items(): plt.plot(single_stats['val_acc_history'], label=str(key)) plt.title('Validation accuracy history') plt.xlabel('Epoch') plt.ylabel('Clasification accuracy') plt.legend() plt.gcf().set_size_inches(14, 5) plt.show() ############################ Loss Functions from A2 ############################ def svm_loss(x, y): """ Computes the loss and gradient using for multiclass SVM classification. Inputs: - x: Input data, of shape (N, C) where x[i, j] is the score for the jth class for the ith input. - y: Vector of labels, of shape (N,) where y[i] is the label for x[i] and 0 <= y[i] < C Returns a tuple of: - loss: Scalar giving the loss - dx: Gradient of the loss with respect to x """ N = x.shape[0] correct_class_scores = x[torch.arange(N), y] margins = (x - correct_class_scores[:, None] + 1.0).clamp(min=0.) margins[torch.arange(N), y] = 0. loss = margins.sum() / N num_pos = (margins > 0).sum(dim=1) dx = torch.zeros_like(x) dx[margins > 0] = 1. dx[torch.arange(N), y] -= num_pos.to(dx.dtype) dx /= N return loss, dx def softmax_loss(x, y): """ Computes the loss and gradient for softmax classification. Inputs: - x: Input data, of shape (N, C) where x[i, j] is the score for the jth class for the ith input. - y: Vector of labels, of shape (N,) where y[i] is the label for x[i] and 0 <= y[i] < C Returns a tuple of: - loss: Scalar giving the loss - dx: Gradient of the loss with respect to x """ shifted_logits = x - x.max(dim=1, keepdim=True).values Z = shifted_logits.exp().sum(dim=1, keepdim=True) log_probs = shifted_logits - Z.log() probs = log_probs.exp() N = x.shape[0] loss = (-1.0/ N) * log_probs[torch.arange(N), y].sum() dx = probs.clone() dx[torch.arange(N), y] -= 1 dx /= N return loss, dx