W3docs

Machine Learning: Addestramento e Test in Python

Dividi i dati in set di addestramento e test in Python con scikit-learn. Tratta test_size, random_state, stratify e metriche di valutazione.

La divisione train/test è il passaggio più fondamentale nella costruzione di un modello di machine learning. L'idea è semplice: tieni una parte dei tuoi dati nascosta al modello durante l'addestramento, poi misura quanto bene il modello si comporta su quella porzione nascosta. Senza questa separazione, non hai un modo onesto per sapere se il tuo modello ha davvero appreso uno schema o ha semplicemente memorizzato gli esempi di addestramento.

Questo capitolo spiega come funziona train_test_split di scikit-learn, cosa fa ogni parametro e come valutare il modello risultante — sia per problemi di regressione che di classificazione.

Perché Separare i Dati di Addestramento da Quelli di Test?

Un modello che si addestra e si valuta sugli stessi dati sembrerà molto più accurato di quanto non sia in realtà. Questo fenomeno si chiama data leakage o overfitting: il modello ha memorizzato gli esempi di addestramento anziché apprendere uno schema generalizzabile.

Immagina uno studente che studia 100 domande di pratica e poi sostiene un esame usando quelle stesse 100 domande. Potrebbe ottenere 100% — ma quel punteggio non dice nulla sulla sua effettiva comprensione della materia.

La divisione train/test previene questo problema:

  • Addestrando il modello su una porzione dei dati in modo che possa apprendere gli schemi.
  • Testando il modello su una porzione separata e non vista per misurare le prestazioni nel mondo reale.

Una divisione tipica è 80% addestramento / 20% test, anche se il rapporto giusto dipende dalla quantità di dati disponibili.

La Funzione train_test_split

scikit-learn fornisce train_test_split nel suo modulo model_selection. Mescola casualmente il dataset e lo divide in due parti:

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

I quattro valori restituiti sono sempre in quest'ordine: feature di addestramento, feature di test, etichette di addestramento, etichette di test.

Parametri Principali

ParametroTipoDescrizione
test_sizefloat o intFrazione (0–1) o conteggio assoluto dei campioni di test. Il valore predefinito è 0.25.
train_sizefloat o intComplemento di test_size. Di solito si imposta uno o l'altro, non entrambi.
random_stateintSeme per il generatore di numeri casuali. Usa qualsiasi intero per rendere la divisione riproducibile.
stratifyarray-likePassa y qui per preservare le proporzioni delle classi in entrambe le divisioni. Essenziale per dataset sbilanciati.
shuffleboolSe mescolare prima di dividere. Il valore predefinito è True. Imposta False per dati di serie temporali.

Effetto di test_size

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris

X, y = load_iris(return_X_y=True)   # 150 samples

for ts in [0.1, 0.2, 0.3]:
    X_tr, X_te, _, _ = train_test_split(X, y, test_size=ts, random_state=42)
    print(f"test_size={ts}: train={len(X_tr)}, test={len(X_te)}")

Output:

test_size=0.1: train=135, test=15
test_size=0.2: train=120, test=30
test_size=0.3: train=105, test=45

random_state e Riproducibilità

Senza un random_state, la divisione è diversa ogni volta che si esegue lo script. Impostalo su qualsiasi intero per ottenere risultati riproducibili:

import numpy as np
from sklearn.model_selection import train_test_split

X = np.arange(10).reshape(-1, 1)
y = np.arange(10)

_, X_te1, _, _ = train_test_split(X, y, test_size=0.3, random_state=42)
_, X_te2, _, _ = train_test_split(X, y, test_size=0.3, random_state=42)

print("Same random_state → same split:", list(X_te1.ravel()) == list(X_te2.ravel()))
# True

La scelta dell'intero (42, 0, 1, ecc.) non conta — l'importante è usare sempre lo stesso valore.

Esempio di Regressione: Previsione dei Prezzi delle Case

L'esempio seguente genera un dataset sintetico di dimensioni e prezzi delle case, addestra un modello di regressione lineare e lo valuta sul set di test tenuto da parte.

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score

# Generate synthetic data: house size (sq ft) → price
np.random.seed(42)
n = 200
sqft = np.random.randint(500, 3500, n).astype(float)
price = 150 * sqft + np.random.randn(n) * 20000

X = sqft.reshape(-1, 1)
y = price

# Split
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
print("Training samples:", len(X_train))   # 160
print("Testing samples:", len(X_test))     # 40

# Train
model = LinearRegression()
model.fit(X_train, y_train)

# Evaluate on the test set
y_pred = model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
r2  = r2_score(y_test, y_pred)

print(f"Mean Squared Error: {mse:,.0f}")
print(f"R² Score:           {r2:.4f}")
print(f"Coefficient:        {model.coef_[0]:.2f}")
print(f"Intercept:          {model.intercept_:.2f}")

Output:

Training samples: 160
Testing samples: 40
Mean Squared Error: 489,271,263
R² Score:           0.9651
Coefficient:        147.55
Intercept:          5237.02

Interpretazione delle metriche:

  • MSE (Mean Squared Error) è la differenza quadratica media tra le previsioni e i valori reali. Più basso è meglio, ma la scala dipende dalla variabile target (qui, in dollari).
  • va da 0 a 1. Un valore di 0.965 significa che il modello spiega circa il 96,5% della varianza nei prezzi delle case — un ottimo adattamento su questo semplice dataset.

Per ulteriori informazioni sulla regressione lineare, consulta Regressione Lineare in Python.

Esempio di Classificazione: Specie di Fiori Iris

Per un'attività di classificazione, l'accuratezza e un report di classificazione sono più informativi dell'MSE.

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report

X, y = load_iris(return_X_y=True)

# stratify=y ensures each class is proportionally represented in both splits
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

print("Train class distribution:", np.bincount(y_train))  # [40 40 40]
print("Test class distribution: ", np.bincount(y_test))   # [10 10 10]

model = LogisticRegression(max_iter=200)
model.fit(X_train, y_train)

y_pred = model.predict(X_test)
print("\nAccuracy:", round(accuracy_score(y_test, y_pred), 4))
print()
print(classification_report(y_test, y_pred,
      target_names=["setosa", "versicolor", "virginica"]))

Output:

Train class distribution: [40 40 40]
Test class distribution:  [10 10 10]

Accuracy: 0.9667

              precision    recall  f1-score   support

      setosa       1.00      1.00      1.00        10
  versicolor       1.00      0.90      0.95        10
   virginica       0.91      1.00      0.95        10

    accuracy                           0.97        30
   macro avg       0.97      0.97      0.97        30
weighted avg       0.97      0.97      0.97        30

Perché stratify=y è importante: Senza di esso, una divisione casuale di un dataset sbilanciato potrebbe inserire la maggior parte dei campioni di una classe rara nell'addestramento, non lasciandone nessuno nel set di test. stratify=y garantisce che ogni classe appaia nelle stesse proporzioni in entrambe le divisioni.

Per ulteriori informazioni sulla classificazione, consulta Regressione Logistica in Python e Matrice di Confusione in Python.

Errori Comuni

Pre-elaborazione Prima o Dopo la Divisione?

Dividi sempre prima di addestrare qualsiasi passaggio di pre-elaborazione (scaling, codifica, imputazione). Se scali tutti i tuoi dati e poi dividi, il set di test ha influenzato lo scaler — una forma di data leakage.

L'ordine corretto:

from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)  # fit ONLY on training data
X_test_scaled  = scaler.transform(X_test)       # transform test with same parameters

Consulta Feature Scaling in Python per una guida completa.

Mescolamento e Dati di Serie Temporali

train_test_split mescola i dati per impostazione predefinita. Per i dati di serie temporali questo è sbagliato — si addestrerebbe su dati futuri per prevedere il passato. Imposta shuffle=False e assicurati che i tuoi dati siano ordinati cronologicamente prima di dividere:

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, shuffle=False
)

Quando una Singola Divisione Non Basta

Una singola divisione 80/20 fornisce una stima delle prestazioni del modello che dipende da quali campioni sono finiti nel set di test. La cross-validation ripete la divisione più volte e fa la media dei punteggi, ottenendo una stima molto più stabile — specialmente su dataset piccoli.

Scegliere una Metrica di Valutazione

La metrica giusta dipende dal tipo di problema:

ProblemaMetriche Comuni
RegressioneMSE, RMSE, MAE, R²
Classificazione binariaAccuratezza, Precisione, Recall, F1, AUC-ROC
Classificazione multi-classeAccuratezza, F1 macro/pesato

Per la classificazione binaria, consulta Curva AUC-ROC in Python e Matrice di Confusione in Python. Per la regolazione degli iperparametri dopo aver ottenuto una divisione funzionante, consulta Grid Search in Python.

  • Dividi i tuoi dati prima di qualsiasi pre-elaborazione per evitare il data leakage.
  • Usa test_size=0.2 come valore predefinito ragionevole; adatta in base alla dimensione del dataset.
  • Imposta random_state su qualsiasi intero per ottenere divisioni riproducibili.
  • Usa stratify=y per attività di classificazione, specialmente su dati sbilanciati.
  • Imposta shuffle=False per dati di serie temporali.
  • Una singola divisione train/test è una base di riferimento rapida; usa la cross-validation per stime delle prestazioni più affidabili.
Was this page helpful?