Trying to print F1-Score and Confusion Matrix - TypeError: unsupported format string passed to Tensor.__format__

I tried using Sklearn to get the F1-Score and Confusion Matrix for the following data but with no success. Here is the working code.

import torch, torch.nn as nn, torch.nn.functional as F
from torch.autograd import Variable
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

from sklearn.metrics import confusion_matrix

import torch.utils.data as data_utils

from sklearn.metrics import f1_score

from torchvision import datasets
from torchvision import transforms
from torch.utils.data import DataLoader
from torch.utils.data import SubsetRandomSampler
import torch.nn.functional as F
import matplotlib.pyplot as plt
import torch
import time

from sklearn.datasets import load_breast_cancer

%matplotlib inline


RANDOM_SEED = 123
BATCH_SIZE = 32
NO_OF_FEATURES=30
NO_OF_CLASSES=2
NUM_HIDDEN_1 = 75
NUM_HIDDEN_2 = 65
DROP_PROBA = 0.5
NUM_EPOCHS = 17

DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

df = load_breast_cancer()
X, y = df.data, df.target

sc = StandardScaler()
X = sc.fit_transform(X)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1)
X_train, X_val, y_train, y_val  = train_test_split(X_train, y_train, test_size=0.25, random_state=1) # 0.25 x 0.8 = 0.2
    
X_train = torch.from_numpy(X_train)
y_train = torch.from_numpy(y_train).double()

train = data_utils.TensorDataset(X_train, y_train)
train_loader = data_utils.DataLoader(train, batch_size=BATCH_SIZE, shuffle=True)

X_val = torch.from_numpy(X_val)
y_val = torch.from_numpy(y_val).double()

valid = data_utils.TensorDataset(X_val, y_val)
valid_loader = data_utils.DataLoader(valid, batch_size=BATCH_SIZE, shuffle=True)

X_test = torch.from_numpy(X_test)
y_test = torch.from_numpy(y_test).double()

test = data_utils.TensorDataset(X_test, y_test)
test_loader = data_utils.DataLoader(test, batch_size=BATCH_SIZE, shuffle=True)


class MultilayerPerceptron(torch.nn.Module):

    def __init__(self, num_features, num_classes,
                 num_hidden_1, num_hidden_2):
        super(MultilayerPerceptron, self).__init__()
        
        self.my_network = torch.nn.Sequential(
            torch.nn.Linear(num_features, num_hidden_1),
            torch.nn.ReLU(),
            torch.nn.Linear(num_hidden_1, num_hidden_2),
            torch.nn.ReLU(),
            torch.nn.Linear(num_hidden_2, num_classes)
        )
           
    def forward(self, x):
        logits = self.my_network(x)
        probas = F.softmax(logits, dim=1)
        return logits, probas
    
def compute_accuracy_and_loss_f1score_metrics(model, data_loader, device):
    correct_pred, num_examples = 0, 0
    cross_entropy = 0.
    for i, (features, targets) in enumerate(data_loader):
            
        #features = features.view(-1, 28*28)
        features = Variable(features.type(torch.FloatTensor))
        targets = Variable(targets.type(torch.FloatTensor))
        features = features.to(device)
        targets = targets.to(device)

        logits, probas = model(features)
        cross_entropy += F.cross_entropy(logits, targets.type(torch.LongTensor)).item()
        _, predicted_labels = torch.max(probas, 1)
        num_examples += targets.size(0)
        correct_pred += (predicted_labels == targets.type(torch.LongTensor)).sum()
        
        #F1-score
        target_   = targets.data.cpu().numpy()
        output_  = predicted_labels.data.cpu().numpy()
        f1_batch = f1_score(output_  , target_   , average='macro')
        #f1_batch = f1_score(targets.cpu(),logits.sigmoid().cpu() > 0.15,average='macro')
        #conf_matrix = confusion_matrix(predicted_labels.view(-1), targets.view(-1))
        #TP = predicted_labels * targets
        #FP = predicted_labels * (1-targets)
        #FN = (1-predicted_labels) * targets
        #TN = (1-predicted_labels) * (1-targets)
        
    return correct_pred.float()/num_examples * 100, cross_entropy/num_examples, f1_batch


# Model Initialization

torch.manual_seed(RANDOM_SEED)
model = MultilayerPerceptron(num_features=NO_OF_FEATURES,
                             num_hidden_1=NUM_HIDDEN_1,
                             num_hidden_2=NUM_HIDDEN_2,
                             num_classes=NO_OF_CLASSES)

model = model.to(DEVICE)

optimizer = torch.optim.SGD(model.parameters(), lr=0.1)


# Training

start_time = time.time()
train_acc_lst, valid_acc_lst = [], []
train_loss_lst, valid_loss_lst = [], []
train_f1_lst, valid_f1_lst = [], []

for epoch in range(NUM_EPOCHS):
    
    model.train()
    
    for batch_idx, (features, targets) in enumerate(train_loader):
    
        ### PREPARE MINIBATCH
        #features = features.view(-1, 28*28)
        features = Variable(features.type(torch.FloatTensor))
        targets = Variable(targets.type(torch.FloatTensor))
        features = features.to(DEVICE)
        targets = targets.to(DEVICE)
            
        ### FORWARD AND BACK PROP
        logits, probas = model(features)
        cost = F.cross_entropy(logits, targets.type(torch.LongTensor))
        optimizer.zero_grad()
        
        cost.backward()
        
        ### UPDATE MODEL PARAMETERS
        optimizer.step()
        
        ### LOGGING
        if not batch_idx % 120:
            print (f'Epoch: {epoch+1:03d}/{NUM_EPOCHS:03d} | '
                   f'Batch {batch_idx:03d}/{len(train_loader):03d} | ' 
                   f'Cost: {cost:.4f}')

    # no need to build the computation graph for backprop when computing accuracy
    model.eval()
    with torch.set_grad_enabled(False):
        train_acc, train_loss, f1 = compute_accuracy_and_loss_f1score_metrics(model, train_loader, device=DEVICE)
        valid_acc, valid_loss, f1 = compute_accuracy_and_loss_f1score_metrics(model, valid_loader, device=DEVICE)
        train_acc_lst.append(train_acc)
        valid_acc_lst.append(valid_acc)
        train_loss_lst.append(train_loss)
        valid_loss_lst.append(valid_loss)
        train_f1_lst.append(train_f1)
        valid_f1_lst.append(valid_f1)
        print(f'Epoch: {epoch+1:03d}/{NUM_EPOCHS:03d} Train Acc.: {train_acc:.4f}%'
              f' | Validation Acc.: {valid_acc:.4f}% | '
              f' |Validation f1.: {valid_f1:.4f}%')
        
    elapsed = (time.time() - start_time)/60
    print(f'Time elapsed: {elapsed:.2f} min')
  
elapsed = (time.time() - start_time)/60
print(f'Total Training Time: {elapsed:.2f} min')

Error

Epoch: 001/017 | Batch 000/011 | Cost: 0.6913
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-88-e8b9b1f72c62> in <module>
    174         train_f1_lst.append(train_f1)
    175         valid_f1_lst.append(valid_f1)
--> 176         print(f'Epoch: {epoch+1:03d}/{NUM_EPOCHS:03d} Train Acc.: {train_acc:.4f}%'
    177               f' | Validation Acc.: {valid_f1:.4f}%')
    178 

~/miniconda2/lib/python3.6/site-packages/torch/tensor.py in __format__(self, format_spec)
    385         if self.dim() == 0:
    386             return self.item().__format__(format_spec)
--> 387         return object.__format__(self, format_spec)
    388 
    389     def __ipow__(self, other):

TypeError: unsupported format string passed to Tensor.__format__

Changing the print statement seem to work a bit but all F1-scores are zeroes.

        print(f'Epoch: {epoch+1:03d}/{NUM_EPOCHS:03d} Train Acc.: {train_acc:.4f}%'
              f' | Validation Acc.: {valid_acc:.4f}% | Validation F1.:format{valid_f1}')
    ```

Epoch: 001/017 | Batch 000/011 | Cost: 0.6913
Epoch: 001/017 Train Acc.: 91.4956% | Validation Acc.: 95.6140% | Validation F1.:formattensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
Time elapsed: 0.00 min
Epoch: 002/017 | Batch 000/011 | Cost: 0.4336
Epoch: 002/017 Train Acc.: 93.8416% | Validation Acc.: 97.3684% | Validation F1.:formattensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
Time elapsed: 0.00 min
Epoch: 003/017 | Batch 000/011 | Cost: 0.1471
Epoch: 003/017 Train Acc.: 94.4282% | Validation Acc.: 96.4912% | Validation F1.:formattensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
Time elapsed: 0.00 min

It seems compute_accuracy_and_loss_f1score_metrics returns some metrics with the F1 score, which is assigned to f1, while you are appending valid_f1 in:

valid_f1_lst.append(valid_f1)

Could you check, where valid_f1 is coming from?

There was typo. I wrote f1 instead of valid_f1. It works fine now. Thanks.