代写接单-CSCE 421 :: Machine Learning :: Texas A&M University :: Spring 2023 Homework 6 (HW-6)

欢迎使用51辅导,51作业君孵化低价透明的学长辅导平台,服务保持优质,平均费用压低50%以上! 51fudao.top

CSCE 421 :: Machine Learning :: Texas A&M University :: Spring 2023

Homework 6 (HW-6)


Convolutional Neural Networks

In this assignment, you'll be coding up a convolutional neural network from scratch to classify images using PyTorch.

Instructions

Install PyTorch following the instructions here (https://pytorch.org/).

Install the torchinfo package (https://github.com/TylerYep/torchinfo) to visualize the network architecture and the number of parameters. The maximum number of parameters you are allowed to use for your network is 100,000.

You are required to complete the functions defined in the code blocks following each question. Fill out sections of the code marked "YOUR CODE HERE" .

You're free to add any number of methods within each class.

You may also add any number of additional code blocks that you deem necessary.

Once you've filled out your solutions, submit the notebook on Canvas.

Do NOT forget to type in your name and UIN at the beginning of the notebook.

Data Preparation

In [ ]:

In [ ]:

     !pip install torchinfo

 # Importing the libraries

import os

import torch

import torchvision

from torchvision.utils import make_grid

import numpy as np

In this assignment, we will use the Fashion-MNIST dataset. Fashion-MNIST is a dataset of Zalando's article images—consisting of a training set of 60,000 examples and a test set of 10,000 examples. Each example is a 28x28 grayscale image, associated with a label from 10 classes.

 

Data

Each image is 28 pixels in height and 28 pixels in width, for a total of 784 pixels in total. Each pixel has a single pixel-value associated with it, indicating the lightness or darkness of that pixel, with higher numbers meaning darker. This pixel-value is an integer between 0 and 255.

Labels

Each training and test example is assigned to one of the following labels:

Fashion-MNIST is included in the

In [ ]:

In [ ]:

torchvision

library.

Label Description

    0 1 2 3 4 5 6 7 8 9

T-shirt/top Trouser Pullover Dress Coat Sandal Shirt Sneaker Bag Ankle boot

 from torchvision.datasets import FashionMNIST

from torchvision.transforms import Compose, ToTensor, Normalize

 # Transform to normalize the data and convert to a tensor

transform = Compose([ToTensor(), Normalize((0.5,), (0.5,))

])

# Download the data

dataset = FashionMNIST('MNIST_data/', download = True, train = True, transform = transform)

Data Exploration

Let's take a look at the classes in our dataset.

In [ ]:

 print(dataset.classes)

 

In [ ]:

 import matplotlib.pyplot as plt

def show_example(img, label):

print('Label: {} ({})'.format(dataset.classes[label], label)) plt.imshow(img.squeeze(), cmap='Greys_r')

plt.axis(False)

In [ ]:

In [ ]:

Creating Training and Validation Datasets

The split_indices function takes in the size of the entire dataset, n , the fraction of data to be used as validation set, val_frac , and the random seed and returns the indices of the data points to be added to the validation dataset.

Choose a suitable fraction for your validation set and experiment with the seed. Remember that the better your validation set, the higher the chances that your model would do well on the test set.

In [ ]:

 show_example(* dataset[20])

 show_example(* dataset[20000])

 def split_indices(n, val_frac, seed):

# Determine the size of the validation set n_val = int(val_frac * n) np.random.seed(seed)

# Create random permutation between 0 to n-1 idxs = np.random.permutation(n)

# Pick first n_val indices for validation set return idxs[n_val:], idxs[:n_val]

In [ ]:

 ######################

# YOUR CODE BELOW #

######################

val_frac = ## Set the fraction for the validation set rand_seed = ## Set the random seed

train_indices, val_indices = split_indices(len(dataset), val_frac, rand_seed) print("#samples in training set: {}".format(len(train_indices))) print("#samples in validation set: {}".format(len(val_indices)))

Next, we make use of the built-in dataloaders in PyTorch to create iterables of our our training and validation sets. This helps in avoiding fitting the whole dataset into memory and only loads a batch of the data that we can decide.

 

Set the batch_size depending on the hardware resource (GPU/CPU RAM) you are using for the assignment

In [ ]:

In [ ]:

In [ ]:

 from torch.utils.data.sampler import SubsetRandomSampler from torch.utils.data.dataloader import DataLoader

 ######################

# YOUR CODE BELOW # ######################

batch_size = ## Set the batch size

 # Training sampler and data loader

train_sampler = SubsetRandomSampler(train_indices) train_dl = DataLoader(dataset,

batch_size,

sampler= train_sampler)

# Validation sampler and data loader

val_sampler = SubsetRandomSampler(val_indices) val_dl = DataLoader(dataset,

batch_size,

sampler= val_sampler)

Plot images in a sample batch of data.

In [ ]:

In [ ]:

Building the Model

Grading: 20 pts for Build the Model section

They don't have to have the exact same model, e.g., the number of layers can be different, the parameters in the model

The very first parameter of the first layer has to be 1 nn.Conv2d(1, ....), because this is black-white dataset, otherwise -5pts

The very last parameter of the last layer has to be 10 nn.Linear(..., 10), otherwise -20pts.

 def show_batch(dl):

for images, labels in dl:

fig, ax = plt.subplots(figsize=(10,10))

ax.set_xticks([]); ax.set_yticks([])

ax.imshow(make_grid(images, 8).permute(1, 2, 0), cmap='Greys_r') break

 show_batch(train_dl)

 

using nn.Conv1d (if ever happens) -20pts. M P l2d10

Create your model by defining the network architecture in the ImageClassifierNet class. NOTE: The number of parameters in your network must be 100,000.

In [ ]:

In [ ]:

In [ ]:

The following code block prints your network architecture. It also shows the total number of parameters in your network (see Total params ).

NOTE: The total number of parameters in your model should be <= 100,000.

In [ ]:

##Check Model Requirements status

Below code cell is to check if your model meets the requirements specefied Run the cell to check the status of your model

 # Import the libraries

import torch.nn as nn

import torch.nn.functional as F

from torchinfo import summary

 class ImageClassifierNet(nn.Module): def __init__(self, n_channels=3):

        super(ImageClassifierNet, self).__init__()

        ######################

        #   YOUR CODE HERE   #

        ######################

def forward(self, X): ###################### # YOUR CODE HERE # ######################

 model = ImageClassifierNet()

 summary(model, input_size=(batch_size, 1, 28, 28))

 

In [ ]:

 def check_model(model):

first_layer = list(model.children())[0]

last_layer = list(model.children())[- 1]

num_params = sum(p.numel() for p in model.parameters()) has_conv1d = False

has_maxpool2d = False

first_layer_status= False

last_layer_status = False

if isinstance(first_layer, nn.Sequential):

if isinstance(first_layer[0], nn.Conv2d) and first_layer[0].in_channels == 1:

first_layer_status= True else:

if isinstance(first_layer, nn.Conv2d) and first_layer.in_channels == 1: first_layer_status= True

if isinstance(last_layer, nn.Sequential):

if isinstance(last_layer[-1], nn.Linear) and last_layer[-1].out_features == 10:

last_layer_status = True else:

print(type(last_layer))

if isinstance(last_layer, nn.Linear) and last_layer.out_features == 10:

last_layer_status = True

for layer in model.modules():

if isinstance(layer, nn.Conv1d):

has_conv1d = True

if isinstance(layer, nn.MaxPool2d):

has_maxpool2d = True flag = False

if first_layer_status == False:

print("The very first parameter of the first layer is not 1 nn.Conv2d(1, ....)") flag = True

if last_layer_status == False:

print("The very last parameter of the last layer is not 10 nn.Linear(..., 10)") flag = True

if has_conv1d == True:

print("Using nn.Conv1d, which should not happen") flag = True

if has_maxpool2d == False: print("No nn.MaxPool2d") flag = True

if num_params > 100000:

print("Parameters > 100000, please make the network less complex to recieve full grade")

if flag == False:

print("The Network looks good")

check_model(model)

Enable training on a GPU

NOTE: This section is necessary if you're training your model on a GPU.

 

In [ ]:

 def get_default_device():

"""Use GPU if available, else CPU""" if torch.cuda.is_available():

return torch.device('cuda') else:

return torch.device('cpu')

def to_device(data, device):

"""Move tensor(s) to chosen device""" if isinstance(data, (list,tuple)):

return [to_device(x, device) for x in data] return data.to(device, non_blocking=True)

class DeviceDataLoader():

"""Wrap a dataloader to move data to a device""" def __init__(self, dl, device):

self.dl = dl self.device = device

def __iter__(self):

"""Yield a batch of data after moving it to device""" for b in self.dl:

yield to_device(b, self.device)

def __len__(self): """Number of batches""" return len(self.dl)

In [ ]:

Train the model

Grading: the two cells below 20 pts

Have to use both training set and validation set, if no validation set -15 pts

In training part, zero_grad() needs to be before backward(), and backwards() need to be before step(), wrong order or missing one -20

If any of zero_grad(), backward(), or step() happens in validation, -10pts

If MSE-related loss is used in loss_fn (the second cell), -15 pts

Complete the train_model function to train your model on a dataset. Tune your network architecture and hyperparameters on a validation set.

 device = get_default_device()

train_dl = DeviceDataLoader(train_dl, device)

val_dl = DeviceDataLoader(val_dl, device) to_device(model, device)

 

In [ ]:

 def train_model(n_epochs, model, train_dl, val_dl, loss_fn, opt_fn, lr): """

    Trains the model on a dataset.

    Args:

        n_epochs: number of epochs

        model: ImageClassifierNet object

        train_dl: training dataloader

        val_dl: validation dataloader

        loss_fn: the loss function

        opt_fn: the optimizer

        lr: learning rate

    Returns:

        The trained model.

        A tuple of (model, train_losses, val_losses, train_accuracies, val_accuracies)

"""

    # Record these values the end of each epoch

train_losses, val_losses, train_accuracies, val_accuracies = [], [], [], []

    ######################

    #   YOUR CODE HERE   #

    ######################

    # Validation process

val_loss = None val_accuracy = None ###################### # YOUR CODE HERE # ######################

    # Record the loss and metric

    ######################

    #   YOUR CODE HERE   #

    ######################

    # Print progress

if val_accuracy is not None:

print("Epoch {}/{}, train_loss: {:.4f}, val_loss: {:.4f}, train_accuracy: {:.4f}, va

.format(epoch+1, n_epochs, train_loss, val_loss, train_accuracy, val_accuracy) print("Epoch {}/{}, train_loss: {:.4f}, train_accuracy: {:.4f}"

.format(epoch+1, n_epochs, train_loss, train_accuracy)) return model, train_losses, val_losses, train_accuracies, val_accuracies

else:

l

)

 

In [ ]:

 ######################

# YOUR CODE BELOW # ######################

num_epochs = # Max number of training epochs loss_fn = # Define the loss function

opt_fn = # Select an optimizer lr = # Set the learning rate

Grading: results 20 pts

The final train_accuracy and val_accuracy both need to above 0.9 0.85 t0 0.9 -5pts

0.80 to 0.85 -15pts

below 0.80 -20 pts

In [ ]:

Plot loss and accuracy

In [ ]:

 history = train_model(num_epochs, model, train_dl, val_dl, loss_fn, opt_fn, lr) model, train_losses, val_losses, train_accuracies, val_accuracies = history

 def plot_accuracy(train_accuracies, val_accuracies): """Plot accuracies"""

plt.plot(train_accuracies, "-x") plt.plot(val_accuracies, "-o") plt.xlabel("Epoch")

    plt.ylabel("Accuracy")

    plt.legend(["Training", "Validation"])

    plt.title("Accuracy vs. No. of epochs")

Grading: 10 pts for the first figure Both training and validation need to go up It's okay if validation is not stable as long as it roughly goes up

Any of them apparently goes down -10pts

In [ ]:

 plot_accuracy(train_accuracies, val_accuracies)

 

In [ ]:

 def plot_losses(train_losses, val_losses): """Plot losses""" plt.plot(train_losses, "-x") plt.plot(val_losses, "-o") plt.xlabel("Epoch")

    plt.ylabel("Loss")

    plt.legend(["Training", "Validation"])

    plt.title("Loss vs. No. of Epochs")

Grading: 10 pts for the second figure Both training and validation need to go down Again, it's okay if validation is not stable or going down first then going up

If training apparently goes up, -10pts

If validation goes up from the beginning, -5pts

In [ ]:

Train a model on the entire dataset

We create a new model with same architecture and train it on the whole training data.

In [ ]:

NOTE: The next cell is necessary if you're training your new_model on a GPU. In [ ]:

In [ ]:

Grading: results 10 pts

The final Train_accuracy needs to above 0.9

0.85 to 0.9 -3pts 0.80 to 0.85 -5pts below 0.80 -10 pts

 plot_losses(train_losses, val_losses)

 new_model = ImageClassifierNet()

 to_device(new_model, device)

 indices, _ = split_indices(len(dataset), 0, rand_seed)

sampler = SubsetRandomSampler(indices)

dl = DataLoader(dataset, batch_size, sampler=sampler) dl = DeviceDataLoader(dl, device)

 

In [ ]:

 ######################

# YOUR CODE BELOW # ######################

num_epochs = # Max number of training epochs lr = # Set the learning rate

history = train_model(num_epochs, new_model, dl, [], loss_fn, opt_fn, lr) new_model = history[0]

Check Predictions

In [ ]:

 def view_prediction(img, label, probs, classes): """

Visualize predictions.

"""

probs = probs.cpu().numpy().squeeze()

fig, (ax1, ax2) = plt.subplots(figsize=(8,15), ncols=2) ax1.imshow(img.resize_(1, 28, 28).cpu().numpy().squeeze(), cmap='Greys_r') ax1.axis('off')

ax1.set_title('Actual: {}'.format(classes[label]))

ax2.barh(np.arange(10), probs)

ax2.set_aspect(0.1)

ax2.set_yticks(np.arange(10))

ax2.set_yticklabels(classes, size='small');

ax2.set_title('Predicted: probabilities')

ax2.set_xlim(0, 1.1)

    plt.tight_layout()

In [ ]:

 # Calculate the class probabilites (log softmax) for img

images = iter(dl)

for imgs, labels in images:

with torch.no_grad(): new_model.eval()

        # Calculate the class probabilites (log softmax) for img

probs = torch.nn.functional.softmax(new_model(imgs[0].unsqueeze(0)), dim=1) # Plot the image and probabilites

view_prediction(imgs[0], labels[0], probs, dataset.classes)

break

 

Save the model

In [ ]:

Compute accuracy on the test set

Here we load the test data pickle file provided.

You need to provide the path for the test_data pickle file and run the cells.

Note: Your score will be given based on your model's performance on a test data set hidden from you. But you can expect similar results from both the test data set provided to you ans the one hidden from you.

Grading: results 10 pts

The final Test_accuracy needs to above 0.9

0.85 to 0.9 -3pts 0.80 to 0.85 -5pts below 0.80 -10 pts

In [ ]:

In [ ]:

 torch.save(new_model, 'new_model')

 import pickle

 ######################

# YOUR Answer BELOW # ###################### test_dataset_file_path = "test_data.pickle"

with open(test_dataset_file_path, 'rb') as f: test_dataset = pickle.load(f)

In [ ]:

 test_dl = DataLoader(test_dataset, batch_size) test_dl = DeviceDataLoader(test_dl, device)

 

In [ ]:

 def evaluate(model, test_dl): with torch.no_grad():

model.eval() total_test_dl = 0 preds, labels = [], [] for xb, yb in test_dl:

            # Model output

y_pred = model(xb)

_, y_pred = torch.max(y_pred, dim=1) preds.extend(y_pred) labels.extend(yb)

total_test_dl += len(yb)

preds, labels = torch.tensor(preds), torch.tensor(labels) test_accuracy = torch.sum(preds == labels).item() / len(preds) return test_accuracy

In [ ]:

 print("Test Accuracy = {:.4f}".format(evaluate(new_model, test_dl)))

 

 

51作业君

Email:51zuoyejun

@gmail.com

添加客服微信: abby12468