Machine Learning Academy · Lezione

Valutazione e inferenza: dai logits alle etichette predette

Imparerete a calcolare la softmax sui logits, decodificare gli indici delle classi predette in etichette e valutare il modello sottoposto a fine-tuning con accuratezza e F1 su un test set separato.

Lezione 4 di 413 passaggi

Valutazione e inferenza: dai logits alle etichette predette è una lezione Machine Learning Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Machine Learning Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Machine Learning Academy include 4 lezioni in totale.

Che cosa sono i logits?

L'output del layer lineare finale di un modello di classificazione viene chiamato logits: si tratta di punteggi grezzi e non normalizzati per ogni classe. In un problema a 2 classi (negativa/positiva), un vettore di logits potrebbe essere [-1.2, 3.5], indicando che il modello propende nettamente per la classe 1 (positiva). I logits possono essere qualsiasi numero reale; non sono probabilità. È necessario un passaggio aggiuntivo (softmax o sigmoid) per convertirli in valori di probabilità interpretabili la cui somma sia 1.

import torch
import torch.nn.functional as F

# Example logits for 2-class problem (batch of 3)
logits = torch.tensor([[-1.2, 3.5], [2.1, -0.3], [0.4, 0.6]])
print('Logits:\n', logits)

# Convert to probabilities with softmax
probs = F.softmax(logits, dim=1)
print('Probabilities:\n', probs)
# Each row sums to 1
print('Row sums:', probs.sum(dim=1))

Softmax: conversione dei logits in probabilità

La funzione softmax converte un vettore di logits reali in una distribuzione di probabilità. Per la classe i: softmax(z_i) = exp(z_i) / sum(exp(z_j)). L'esponenziale amplifica le differenze: una differenza di 2 tra i logits fa sì che una classe sia circa 7 volte più probabile dell'altra. Softmax viene utilizzata per la classificazione multiclasse, in cui esiste esattamente una classe corretta.

import torch
import torch.nn.functional as F
import numpy as np

logits = torch.tensor([1.0, 3.0, 0.5])  # 3 classes
probs = F.softmax(logits, dim=0)
print('Class probabilities:', probs.numpy().round(4))
# [0.0900, 0.6652, 0.2449] -- class 1 is most likely
print('Sum:', probs.sum().item())  # 1.0

# argmax gives the predicted class index
pred_class = torch.argmax(probs).item()
print('Predicted class:', pred_class)  # 1

argmax: estrazione delle label predette

Una volta ottenuti i vettori di logits per un batch di esempi, torch.argmax(logits, dim=1) restituisce l'indice della classe con il punteggio più alto per ogni esempio. Questo è la label della classe predetta. Per BERT sottoposto a fine-tuning con num_labels=2, l'output è 0 (negativa) oppure 1 (positiva). Non è necessario applicare softmax prima di argmax perché softmax è monotona: il logit più grande corrisponde sempre alla probabilità più alta.

import torch

# Logits from model output for a batch of 4 examples
logits = torch.tensor([
    [-2.1,  3.4],  # strongly positive
    [ 1.8, -0.5],  # negative
    [ 0.1,  0.2],  # uncertain, leans positive
    [-3.0, -1.0]   # both negative, less-negative wins
])

predictions = torch.argmax(logits, dim=1)
print('Predictions:', predictions.tolist())  # [1, 0, 1, 1]

labels = {'0': 'negative', '1': 'positive'}
for pred in predictions.tolist():
    print(labels[str(pred)])

Decodifica degli indici predetti nei nomi delle classi

Gli output del modello sono indici interi. In un sistema di produzione, mantenga una corrispondenza tra l'indice e una label comprensibile. La memorizzi in una lista o in un dizionario e vi acceda utilizzando l'intero predetto. Per BERT sottoposto a fine-tuning su IMDB, la corrispondenza è semplicemente {0: 'NEGATIVE', 1: 'POSITIVE'}. Per task multiclasse come la classificazione degli argomenti, la corrispondenza potrebbe contenere 20 o più voci.

import torch
from transformers import BertForSequenceClassification, BertTokenizer

# Assume model and tokenizer are already loaded and fine-tuned
id2label = {0: 'NEGATIVE', 1: 'POSITIVE'}

def predict(text, model, tokenizer, device):
    model.eval()
    inputs = tokenizer(text, return_tensors='pt',
                       truncation=True, max_length=256)
    inputs = {k: v.to(device) for k, v in inputs.items()}
    with torch.no_grad():
        logits = model(**inputs).logits
    pred_id = torch.argmax(logits, dim=1).item()
    return id2label[pred_id]

# result = predict('This film was absolutely wonderful!', model, tokenizer, device)
# print(result)  # POSITIVE

Punteggi di confidenza dalle probabilità

Restituire soltanto la label predetta spesso non è sufficiente nelle applicazioni reali. Un punteggio di confidenza (la probabilità della classe predetta) indica quanto il modello sia sicuro. Una predizione POSITIVE con una confidenza del 99% è molto diversa da una con una confidenza del 52%. Contrassegnare le predizioni con bassa confidenza per una revisione umana è una pratica comune nei sistemi di produzione che gestiscono decisioni sensibili.

import torch
import torch.nn.functional as F

def predict_with_confidence(text, model, tokenizer, device):
    model.eval()
    id2label = {0: 'NEGATIVE', 1: 'POSITIVE'}
    inputs = tokenizer(text, return_tensors='pt',
                       truncation=True, max_length=256)
    inputs = {k: v.to(device) for k, v in inputs.items()}
    with torch.no_grad():
        logits = model(**inputs).logits
    probs = F.softmax(logits, dim=1).squeeze()
    pred_id = torch.argmax(probs).item()
    confidence = probs[pred_id].item()
    return id2label[pred_id], round(confidence, 4)

# label, conf = predict_with_confidence('Terrible movie.', model, tokenizer, device)
# print(label, conf)  # NEGATIVE 0.9732

Valutazione con Accuracy e F1

Dopo aver raccolto le predizioni per l'intero set di test, calcoli le metriche standard utilizzando scikit-learn. Accuracy è la frazione di predizioni corrette ed è utile quando le classi sono bilanciate. F1-score è la media armonica di precision e recall ed è importante quando una classe (ad esempio le recensioni positive) è più rilevante o le classi sono sbilanciate. classification_report stampa tutte le metriche in un'unica tabella leggibile.

from sklearn.metrics import classification_report, accuracy_score
import torch

all_preds, all_labels = [], []

model.eval()
with torch.no_grad():
    for batch in test_loader:
        outputs = model(
            input_ids=batch['input_ids'].to(device),
            attention_mask=batch['attention_mask'].to(device)
        )
        preds = torch.argmax(outputs.logits, dim=1).cpu().numpy()
        all_preds.extend(preds)
        all_labels.extend(batch['label'].numpy())

print('Accuracy:', accuracy_score(all_labels, all_preds))
print(classification_report(all_labels, all_preds,
      target_names=['NEGATIVE', 'POSITIVE']))

Matrice di confusione per BERT

Una matrice di confusione mostra la suddivisione delle predizioni rispetto alle label corrette. Nell'analisi del sentiment, le celle fuori diagonale mostrano i falsi positivi (predetti come POSITIVE ma in realtà NEGATIVE) e i falsi negativi (predetti come NEGATIVE ma in realtà POSITIVE). Visualizzare la matrice con una heatmap di seaborn evidenzia gli errori sistematici: il modello predice eccessivamente una classe? Alcuni tipi di recensioni vengono classificati erroneamente con regolarità?

from sklearn.metrics import confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

cm = confusion_matrix(all_labels, all_preds)

plt.figure(figsize=(6, 5))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
            xticklabels=['NEG', 'POS'],
            yticklabels=['NEG', 'POS'])
plt.ylabel('True Label')
plt.xlabel('Predicted Label')
plt.title('BERT Confusion Matrix on IMDB Test Set')
plt.tight_layout()
plt.savefig('bert_confusion.png')

Inferenza a batch per una maggiore efficienza

Eseguire l'inferenza su un esempio alla volta è lento. Elabori gli esempi in batch per sfruttare il parallelismo della GPU. Imposti un batch_size fisso nel DataLoader, passi batch interi al modello e raccolga i risultati. Su una GPU, l'inferenza a batch può essere 50 volte più veloce di quella su singoli esempi. Utilizzi torch.no_grad() e sposti i tensori sul dispositivo corretto per ottenere prestazioni ottimali.

import torch
from torch.utils.data import DataLoader, TensorDataset
from transformers import BertTokenizer

def batch_predict(texts, model, tokenizer, device, batch_size=32):
    model.eval()
    all_predictions = []
    for i in range(0, len(texts), batch_size):
        batch_texts = texts[i:i+batch_size]
        enc = tokenizer(batch_texts, truncation=True, padding=True,
                        max_length=256, return_tensors='pt')
        enc = {k: v.to(device) for k, v in enc.items()}
        with torch.no_grad():
            logits = model(**enc).logits
        preds = torch.argmax(logits, dim=1).cpu().tolist()
        all_predictions.extend(preds)
    return all_predictions

Sigmoid per la classificazione multilabel

Quando un testo può appartenere a più classi contemporaneamente (ad esempio una recensione che è sia 'divertente' sia 'emozionante'), utilizzi sigmoid invece di softmax. Sigmoid viene applicata indipendentemente al logit di ogni classe e produce una probabilità compresa tra 0 e 1 per ciascuna classe; tali probabilità non devono sommare 1. Applichi una soglia (solitamente 0,5) a ogni probabilità per ottenere una predizione binaria per ciascuna classe. Imposti problem_type='multi_label_classification' in BertForSequenceClassification.

import torch
import torch.nn.functional as F

# 5 classes, multi-label: a text can have multiple labels
logits = torch.tensor([[1.2, -0.5, 2.1, -1.8, 0.3]])
probs = torch.sigmoid(logits)
print('Per-class probabilities:', probs)

threshold = 0.5
predicted_labels = (probs > threshold).int()
print('Predicted labels (multi-hot):', predicted_labels)
# e.g., [1, 0, 1, 0, 0] -- classes 0 and 2 are predicted

Gestione degli esempi classificati erroneamente

Esamini sempre manualmente gli esempi classificati erroneamente. Stampi gli esempi in cui il modello ha predetto 0 ma la label corretta è 1 (falsi negativi) e viceversa. Tra gli schemi comuni rientrano il sarcasmo ('Oh, che disastro di film davvero brillante'), il cambiamento di dominio (vocabolario antiquato) o le recensioni lunghe, in cui il modello vede soltanto i primi 256 token. Comprendere le modalità di errore guida le attività di feature engineering o di raccolta dei dati.

import numpy as np

all_preds = np.array(all_preds)
all_labels = np.array(all_labels)
texts = test_dataset['text'] if hasattr(test_dataset, '__getitem__') else []

false_negatives = np.where((all_preds == 0) & (all_labels == 1))[0]
false_positives = np.where((all_preds == 1) & (all_labels == 0))[0]

print('False negatives (predicted NEG, true POS):')
for idx in false_negatives[:3]:
    print(' -', str(texts[idx])[:120] if texts else idx)

print('False positives (predicted POS, true NEG):')
for idx in false_positives[:3]:
    print(' -', str(texts[idx])[:120] if texts else idx)

Esportazione delle predizioni in CSV

In contesti di produzione o nelle competizioni, spesso è necessario esportare le predizioni in un file CSV. Utilizzi pandas per creare un DataFrame con il testo originale, la label corretta, la label predetta e il punteggio di confidenza. Questo formato è facile da condividere con gli stakeholder, sottoporre ad audit e utilizzare come input per le pipeline di reporting successive. Includa sempre nei metadati la versione del modello e il timestamp della predizione.

import pandas as pd
import torch.nn.functional as F
import torch

results = []
id2label = {0: 'NEGATIVE', 1: 'POSITIVE'}

model.eval()
for i, text in enumerate(sample_texts):
    inputs = tokenizer(text, return_tensors='pt',
                       truncation=True, max_length=256)
    with torch.no_grad():
        logits = model(**inputs).logits
    probs = F.softmax(logits, dim=1).squeeze()
    pred = torch.argmax(probs).item()
    results.append({
        'text': text[:80],
        'true_label': id2label[sample_labels[i]],
        'predicted_label': id2label[pred],
        'confidence': round(probs[pred].item(), 4)
    })

df = pd.DataFrame(results)
df.to_csv('bert_predictions.csv', index=False)
print(df.head())

Verifica rapida

Verifichi la comprensione dei concetti di Machine Learning con Python presentati in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato che: i logits sono punteggi grezzi del modello, convertiti in probabilità tramite softmax per i task multiclasse o sigmoid per quelli multilabel, argmax restituisce l'indice della classe predetta, che viene associato a una label comprensibile tramite un dizionario id2label e classification_report e la matrice di confusione forniscono insieme un quadro completo delle prestazioni del modello, oltre la sola accuracy. Nella prossima sezione esploreremo MLflow per tenere traccia di esperimenti, parametri e metriche tra più esecuzioni di addestramento.

Gratis per iniziare

Impara Python con un tutor IA — gratis

Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.

Corsi
30
Lezioni
120

Domande Frequenti

La lezione «Valutazione e inferenza: dai logits alle etichette predette» è gratuita?

Sì — il testo completo di «Valutazione e inferenza: dai logits alle etichette predette» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Machine Learning Academy, passa a CoddyKit PRO. Il corso Machine Learning Academy include 4 lezioni in totale.

Cosa imparerò in «Valutazione e inferenza: dai logits alle etichette predette»?

Imparerete a calcolare la softmax sui logits, decodificare gli indici delle classi predette in etichette e valutare il modello sottoposto a fine-tuning con accuratezza e F1 su un test set separato. Eserciti Machine Learning Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Machine Learning Academy?

Non è richiesta alcuna esperienza precedente. Machine Learning Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Valutazione e inferenza: dai logits alle etichette predette»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Machine Learning Academy?

Sì. Ogni lezione Machine Learning Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Architettura Transformer: attention, token e contesto
  2. Tokenizer Hugging Face: codificare il testo per BERT
  3. Fine-tuning di BertForSequenceClassification
  4. Valutazione e inferenza: dai logits alle etichette predette
← Torna a Machine Learning Academy