Getting Error: TypeError: cross_entropy_loss(): argument 'target' (position 2) must be Tensor, not tuple

I am working on a CNN multi-class classification of different concentrations (10uM, 30uM, etc.) I create my dataset to include the images as the features and the concentrations as labels. Note that the concentrations are left as a string. When running the code, I am getting the following error:

TypeError: cross_entropy_loss(): argument 'target' (position 2) must be Tensor, not tuple

The following is my dataset class:

class CustomDataset(Dataset):
def __init__(self, path, method):
    """
    Args:
        csv_path (string): path to csv file
        data_path (string): path to the folder where images are
        transform: pytorch transforms for transforms and tensor conversion
    """
    # Transforms
    self.to_tensor = transforms.ToTensor()
    # Read the excel file
    self.data_path = pd.read_excel(path, sheet_name=method)
    # First column contains the image paths
    self.img_arr = np.asarray(self.data_path.iloc[:, 0])
    # Second column is the labels
    self.label_arr = np.asarray(self.data_path.iloc[:, 1])

def __getitem__(self, index):
    # Get image name from the pandas df
    img_path = self.img_arr[index]
    
    # Open image
    img = cv2.imread(img_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # Converts the image from BGR to RGB

    # Transform image to tensor
    img_tensor = self.to_tensor(img)

    # Get label(class) of the image based on the cropped pandas column
     img_label = self.to_tensor(self.label_arr[index])
    img_label = self.label_arr[index]

    return (img_tensor, img_label)

def __len__(self):
    return len(self.data_path)

I am aware that the reason is most probably due to the fact that the labels are left as tuples, so the loss function is unable to compare the CNN output with the label. However, I am unable to find any resources that explain how labels are dealt with in multi-class classifications of tuple type labels. The solution seems simple, but I am a bit confused on how to solve it. Can anyone direct me?
This is the implemented training loop:

def train_epoch(model,dataloader,loss_fn,optimizer):
train_loss,train_correct = 0.0, 0
model.train() #Sets the mode to train (Helpful when using layers such as DropOut and BatchNorm)

for features,labels in dataloader:
    #Zero grad
    optimizer.zero_grad()
    
    #Forward Pass
    output=model(features)
    print(output)
    print(labels)
    loss=loss_fn(output,labels)
    
    #Backward Pass
    loss.backward()
    optimizer.step()
    
    train_loss += loss.item()*features.size(0) #features.size is useful when using batches.
    scores, predictions = torch.max(output.data,1) # 1 is to create a 1 dimensional tensor with max values from each row
    
    train_correct += (predictions==labels).sum().item()
    
return train_loss, train_correct

This is the output of “output” and “labels”, respectively:

tensor([[-0.0528, -0.0150, -0.0153, -0.0939, -0.0887, -0.0863]],
       grad_fn=<AddmmBackward0>)
('70uM',)

Your label seems to be a tuple which contains strings, which is not supported.
You would have to map your labels/strings to class indices in the range [0, nb_classes-1] and pass the target as a LongTensor to the loss function.

Oh, I see. How can I go on about that? Since it’s not very clear to me how to do it

It depends on your targets and what kind of information they contain.
E.g. I don’t see a reason why your target should be a tuple assuming it only contains a single string.
In case you want to map strings to integers you could use a dict:

class_to_index = {
    "70uM": 0,
    "71uM": 1,
    "80uN": 2,
}

targets = ["71uM", "80uN", "70uM"]
targets = torch.tensor([class_to_index[t] for t in targets])
print(targets)
# tensor([1, 2, 0])

Oh I understand now, thanks. But wouldn’t that affect how many nodes in my final output? For example my last layer’s output nodes are the number of classes (6). So my output tensor when feeding the model is 1x6. However, since my target would be a 1x1 tensor, would it normally compute the loss function, or do I need to have a 1 node output in my model?

nn.CrossEntropyLoss expects a model output in the shape [batch_size, nb_classes] containing raw logits and a target tensor in the shape [bathc_size] containing class indices in the range [0, nb_classes-1] for a multi-class classification use case.
It can accept additional dimensions (e.g. for a multi-class segmentation) as well as “soft” targets, but these use cases do not fit your use case.

I understand, thanks! But is this the standard way of how multi-classification problems are approached, i.e indexing all the classes?

Yes, using class indices for a multi-class classification use case is the standard approach.

I see, I applied your approach and my code is now correctly running, thank you!