Multi Label Classification in pytorch

Yes, I have used the multi class classification and find the CEL() function as my loss function. It solved my problem.

with small dataset go for agumentation process

hey bro , i want to ask one question to you , i have satellite image as a input which is [3,128,128] and mask image is [1,128,128] and i want to divide into 5 classs , and using softmax at output layer and want to use cross entropy loss , do i have to change anything on mask image or it is ok?

hello sir , i want to ask one question to you , i have satellite image as a input which is [3,128,128] and mask image is [1,128,128] and i want to divide into 5 classs , and using softmax at output layer and want to use cross entropy loss , do i have to change anything on mask image or it is ok?

If you are working on a multi-class segmentation use case, you could use nn.CrossEntropyLoss and pass the model output in the shape [batch_size, nb_classes, height, width] directly to the criterion without using a softmax layer.
The target should have the shape [batch_size, height, width] and contain the class indices in the range [0, nb_classes-1].

Thank you so much sir, since i am very new on this concept , I am doing hit and trial of coding , i want to visulaize the results of testing image [1,5,128,128], could you suggest me something over this code for conversion of a tensor into mask image which is [1,128,128]

def predict_fundus_image(img_path,model):

img = Image.open(img_path)

transform = transforms.Compose([transforms.Resize((128,128)),
                                transforms.ToTensor()])
img_transformed = transform(img)
img_transformed = transform(img).unsqueeze_(0)
img_transformed = img_transformed.cpu()
model = model.cpu()
output = model(img_transformed)
output = torch.argmax(output,1)
return output

img = predict_fundus_image(r"E:\new_mission _data\test\testing1.x000.y000.png",model)
print(img)
sys.exit()
img = img.squeeze(0)
mapping = {(0, 0, 0): 0, (0, 0, 255): 1, (255, 0, 0): 2, (0,255,0):3,(255, 255, 255): 4}
rev_mapping = {mapping[k]: k for k in mapping}
pred = img
pred_image = torch.zeros(3, img.size(0), img.size(1), dtype=torch.uint8)
for k in rev_mapping:
    print(k)
    pred_image[:, pred==k] = torch.tensor(rev_mapping[k]).byte().view(3,1)
    plt.imshow(pred_image.permute(1, 2,0).numpy())

The output tensor would contain the class indices, since you’ve applied torch.argmax(output, 1) on it, so you should be able to visualize it.

However, to create the mask colors, the reverse mapping would be needed from your second code snippet. Are you seeing any issues with it, i.e. is it creating an error or are you seeing unexpected results?

1 Like

@ptrblck , how can we change the above script , if my y (target) data has 2 , 1 D tensors - one each for 2 targets.

You can compute two losses, sum them together, and call backward() on the final loss.

@ptrblck your suggestion worked very well for me. I tried a quick and dirty model architecture and for my data , getting 80% of training accuracy, for testing it is little less…that i will be able to improve. I hope. wanted to share the basic architecture. I used.

class Net(nn.Module):
    def __init__(self,n1,n2):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 13 *13, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, n1)
        self.fc4=  nn.Linear(84,n2)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(x.size(0), 16 * 13 *13)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x1 = self.fc3(x)
        x2=  self.fc4(x)
        return x1,x2


net = Net(10,10)

Here  10 is number of classes in each of the labels in my input data.
wanted to take your feedback.

I used following for  training .

loss_arr = []
loss_epoch_arr = []
max_epochs = 20
min_loss = 1000
best_model = None

for epoch in range(max_epochs): # loop over the dataset multiple times

for i, data in enumerate(train_loader, 0):
    # get the inputs; data is a list of [inputs, labels]
    inputs, in_labels = data
    
    #labels_out = net(input)
    _,v_actual = torch.max(in_labels[:,0,:].data,1)
    _,c_actual = torch.max(in_labels[:,1,:].data,1)
    
    # zero the parameter gradients
    optimizer.zero_grad()

    # forward + backward + optimize
    v_out,c_out = net(inputs)
    
    _,v_pred = torch.max(v_out,1)
    _,c_pred =torch.max(c_out,1)

    #outv= outputs[0]
    #outc = outputs[1]
    loss_v = criterion(v_out, v_actual) #loss in vowel
    loss_c = criterion(c_out, c_actual) #loss in consonent
    loss = torch.add(loss_v,loss_c)
    loss.backward()
    optimizer.step()

    # print statistics
    #running_loss += loss.item()
    #if i % 200 == 199:    # print every 2000 mini-batches
    #    print('[%d, %5d] loss: %.3f' %
    #          (epoch + 1, i + 1, running_loss / 200))
    running_loss = 0.0
    if min_loss > loss.item():
        min_loss = loss.item()
        best_model = copy.deepcopy(net.state_dict()) #create a fully independent clone of the original object and all of its children.
    
    loss_arr.append(loss.item())
    
    del inputs, in_labels, v_out, c_out
    #torch.cuda.empty_cache() ##to empty the unused memory after processing each batch
    
loss_epoch_arr.append(loss.item())
    
print('Epoch: %d/%d, Test acc: %0.2f, Train acc: %0.2f' % (epoch, max_epochs, evaluation(validation_loader), evaluation(train_loader)))

net.load_state_dict(best_model) #Selecting the best model
plt.plot(loss_epoch_arr)
plt.show()

print(‘Finished Training’)

2 Likes

It’s useful!Thanks!!!

What would change if we have input as a 1D signal and each of these input files have a target in the fashion [0,1,2,2,1,0,3…].
Input: 1D array
Target/Input : [0,1,2,2,1,0,3…]
Any help would be great as my previous implementation does not yield any result.

Based on the target description this would be a multi-class classification and you could use e.g. nn.CrossEntropyLoss.
I’m not sure, if “1D signal” means you are working with a temporal signal, but assuming it’s the case, the output of the model would have the shape [batch_size, nb_classes, time_steps] and the target [batch_size, time_steps] and would contain the class indices in the range [0, nb_classes-1].

@ptrblck Thanks a lot, Just wanted to confirm, if I am using the correct logic.

Thanks!
Maybe this method is ‘binary relevance’?

@ptrblck I perform multi-label classification for 50 different classes. I am using the torchmetrics package. I struggle with torchmetrics.precision, especially with the following:

#instantiate metric
num_classes=50
self.precision_macro = torchmetrics.Precision(num_classes=num_classes, threshold=0.5, average=‘macro’,multilabel=True)
#compute metric
prec_macro = self.precision_macro(y_pred, y_true)
#where y_pred is dtype=float and has shape (batch_size, num_classes): [0.53, 0.52, …0.48]
#where y_true is dtype=int and has shape (batch_size, num_classes): [1,0,…1]

If I compute the metric for a batch_size>1 I do not get an error. If I compute it for a batch_size=1 I get the following error:

~User/site-packages/torchmetrics/classification/checks.py in _check_num_classes_binary(num_classes, is_multiclass)
121 if num_classes > 2:
→ 122 raise ValueError(“Your data is binary, but num_classes is larger than 2.”)

ValueError: Your data is binary, but num_classes is larger than 2.

I am struggling understanding it and would really appreciate any help!

Based on this description I guess your single sample input might miss the batch dimension.
Could you check, if the batch dimension is still there and has a size of 1? Usually it’s the first dim (dim0) of the tensor.

Thank you. Indeed it seems to miss the batch_size dim:

#batch_size=2
In [3]: y_pred.shape
Out[3]: torch.Size([2, 50])

#batch_size=1
In [4]: y_pred[0].shape
Out[4]: torch.Size([50])

Any idea of how to maintain the batch size as a tensor dimension in this case?

I manually validated the results for the batch_size=2 and the result of the metric seems to be correct. Interestingly, when using pytorch_lightning.metrics.classification.Precision (which seems to be deprecated but computes the same metric), it does not throw an error for a batch_size=1.

Based on the posted code snippet it seems you are indexing y_pred in the batch dimension, which will thus remove it.
You could either slice it via y_pred[0:1] or add it back via y_pred[0].unsqueeze(0).

1 Like

Thank you very much @ptrblck - this fixed the error and cleared up things.