Hello everyone! I’m trying to create a basic CNN using PyTorch, to classify 21 different types of land use. I have very limited time and my teachers are never available, so I’m placing my little S.O.S. here instead…
My data consists of 2100 RGB images, 100 for each class, and is stored in a list of format [image, label].
Data preparation is not the problem, but in case anyone needs to see how my data is loaded and formatted:
# Libraries.
import matplotlib.pyplot as plt
import cv2
import numpy as np
from skimage.io import imread
import random
from sklearn.model_selection import train_test_split
import os
import zipfile

# Create an empty training data list.
training_data = []

#Importing data:
datadir = "/content/ucmdata/Images" # Setting the common directory.
categories = ['agricultural', 'airplane', 'baseballdiamond', 'beach', 'buildings', 'chaparral', 'denseresidential', 'forest', 'freeway', 
              'golfcourse', 'harbor', 'intersection', 'mediumresidential', 'mobilehomepark', 'overpass', 'parkinglot', 'river',
              'runway', 'sparseresidential', 'storagetanks', 'tenniscourt']

# A function for loading in all data.
def create_training_data():
  # For each category of image:
  for category in categories:
    # Open the folder.
    path = os.path.join(datadir, category)
    # Take the index as a class number.
    class_number = categories.index(category)
    # For all images within:
    for image in os.listdir(path):
      # Read the image.
      image_array = cv2.imread(os.path.join(path, image))
      # Turn it into type float.
      image_array = image_array.astype('float32')
      # Normalize the image values between 0 and 1.
      image_array /= 255.0
      # Add it to the training data list.
      training_data.append([image_array, class_number])

# Run the function.
create_training_data()
# Review the length: 100 pictures x 21 categories should yield 2100 images.
print(len(training_data))
# Shuffle the data so that it is randomly ordered.
random.shuffle(training_data)


This is all fine. The problem is creating and running an actual CNN with this data. I understand the theory behind how CNNs work and thought I knew how to code one in a basic manner, but I was wrong.
I’ve scoured the internet for tutorials but every one I find seems to disagree with the other. I have made several attempts at different kinds of models, but all to no avail.
Here is my latest attempt… It’s a lot of code, and it’s a mess! I’m sorry 
# Some tutorials tell me I need to split my training data into X and y lists.
# Others don't. I'm unsure what difference it makes.

# Create empty lists X (images), and y (labels).
X = []
y = []

# Create lists with images and class numbers respectively.
for image, classnum in training_data:
  # Transform the image into a tensor.
  image = torch.as_tensor(image)
  # Add it to list X.
  X.append(image)
  # Add the class numbers to list y.
  y.append(classnum)
# Each individual image is 256 by 256 by 3.

# I know what these parameters do, just haven't finetuned them.
num_epochs = 5
num_classes = 10
batch_size = 100
learning_rate = 0.001

# The model I ideally would like to run: 
model = nn.Sequential(
        # 1st convolution 5x5, padding 2, 6 output channels 
        nn.Conv2d(in_channels = 1, out_channels = 6, kernel_size = 5, padding = 0),
        # 1st activation function (sigmoid)
        nn.Sigmoid(),
        # 1st average pooling 2x2
        nn.MaxPool2d(2),

        # 2nd convolution 5x5, no padding, 16 output channels
        nn.Conv2d(in_channels = 6, out_channels = 16, kernel_size = 5),
        # 2nd activation function (sigmoid)
        nn.Sigmoid(),
        # 2nd avg pooling
        nn.MaxPool2d(2),

        # Flatten for the dense layers
        nn.Flatten(),
        
        # 3rd layer fully connected, outputs 120
        nn.Linear(16 * 5 * 5, 120), 
        # 3rd activation function (sigmoid)
        nn.Sigmoid(),
        
        # 4th layer fully connected, outputs 84
        nn.Linear(120, 84), 
        # 4th activation function (sigmoid)
        nn.Sigmoid(),

        # 5th layer fully connected, outputs 21 (for 21 classes?)
        nn.Linear(84, 21)
        )

# Forward: Some tutorials tell me I need this, others say I don't? What is forward?
#def forward(self, x):
#    out = self.layer1(x)
#    out = self.layer2(out)
#    out = out.reshape(out.size(0), -1)
#    out = self.drop_out(out)
#    out = self.fc1(out)
#    out = self.fc2(out)
#    return out

# The loss and optimizer, copypasted from a tutorial:
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

# The following code was also taken from one of the tutorials. I simply adjusted the variables.
# Gives me a "NotImplemented Error".
# No idea what's going wrong...
total_step = len(training_data)
loss_list = []
acc_list = []
# Training:
for epoch in range(num_epochs):
    for i, (images, labels) in enumerate(training_data):
        # Run the forward pass
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss_list.append(loss.item())

        # Backprop and perform Adam optimisation
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        # Track the accuracy
        total = labels.size(0)
        _, predicted = torch.max(outputs.data, 1)
        correct = (predicted == labels).sum().item()
        acc_list.append(correct / total)

        if (i + 1) % 100 == 0:
            print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}, Accuracy: {:.2f}%'
                  .format(epoch + 1, num_epochs, i + 1, total_step, loss.item(),
                          (correct / total) * 100))

There’s a lot going on here, and I understand I can’t ask help for everything. My main question would therefore be limited to how I can get the basic model, the nn.Sequential(), in my code to work?
Any help would be greatly appreciated! I’m on a tight deadline and the teachers just practically not existing makes the task very overwhelming…
(I wish I could also attach the images I’m using, but unfortunately it’s a large zip and I’m (understandably) not allowed to. I hope what I was able to share still gives a good indication!)