Come creare il tuo primo modello di machine learning in Python: tutorial passo passo
Tutorial passo passo per creare il tuo primo modello di machine learning in Python con scikit-learn: dati, addestramento, metriche e salvataggio.

Leggere di machine learning è utile, ma c’è un momento in cui la teoria deve lasciare spazio alla pratica. Puoi conoscere a memoria le differenze tra apprendimento supervisionato e non supervisionato, sapere che cosa sono le feature e le etichette, eppure sentirti bloccato davanti a un editor vuoto. È normale: il passaggio dai concetti al codice è il punto in cui molti principianti si fermano, spesso perché pensano di dover padroneggiare la matematica avanzata prima di scrivere anche una sola riga.
In realtà non è così.
In questa guida costruirai il tuo primo modello di machine learning in Python, dall’inizio alla fine, con un approccio passo passo. Preparerai l’ambiente di lavoro, caricherai un dataset reale, lo esplorerai, dividerai i dati, addestrerai un classificatore, lo valuterai con le metriche giuste, proverai a migliorarlo e infine lo salverai per riutilizzarlo. Alla fine non avrai solo un modello funzionante, ma soprattutto un metodo che potrai applicare a qualsiasi altro problema.
Che cosa costruirai e cosa ti serve per iniziare
Il progetto di questo tutorial è un classico problema di classificazione binaria: insegnare a un algoritmo a distinguere tra due categorie a partire da una serie di misure numeriche. Userai il dataset Breast Cancer Wisconsin, incluso direttamente nella libreria scikit-learn, che raccoglie le caratteristiche di campioni di tessuto (raggio, consistenza, perimetro, area e così via) insieme all’indicazione se il campione è maligno o benigno.
La scelta non è casuale. È un dataset piccolo, pulito, già pronto all’uso e abbastanza ricco da permetterti di toccare con mano tutte le fasi di un vero progetto. Precisazione importante: si tratta di un esercizio didattico, il modello che costruirai non ha alcun valore diagnostico e non va mai usato per decisioni mediche.
Le competenze di partenza
Ti basta una conoscenza di base di Python: sapere che cosa sono variabili, liste, funzioni e come si importa una libreria. Non serve essere esperti di statistica, perché ogni passaggio verrà spiegato in modo discorsivo, con il significato di ciò che stai facendo prima ancora del come. Se hai già letto qualcosa sui concetti di fondo, tanto meglio, ma non è un prerequisito.
Gli strumenti che userai
Lavorerai con tre librerie che sono ormai uno standard nel mondo della data science. Pandas serve a manipolare dati in forma di tabella, come faresti con un foglio di calcolo ma con molta più potenza. Scikit-learn è la libreria di riferimento per il machine learning classico, con un’interfaccia coerente che rende facile passare da un algoritmo all’altro. Matplotlib, infine, ti permette di creare grafici per visualizzare i dati.
Se vuoi un quadro più ampio dell’ecosistema, trovi una panoramica nella nostra guida alle migliori librerie Python per l’intelligenza artificiale, dove scikit-learn viene messa a confronto con TensorFlow, PyTorch e gli altri strumenti più diffusi.
Passo 1: preparare l’ambiente di lavoro
Hai due strade per iniziare. La prima è lavorare nel browser con Google Colab, un servizio gratuito di Google che ti mette a disposizione un notebook Python già configurato, senza installare nulla sul tuo computer. Ti basta un account Google, crei un nuovo notebook e sei pronto. Pandas, scikit-learn e matplotlib sono di norma già presenti nell’ambiente.
La seconda strada è lavorare in locale. In questo caso installi Python dal sito ufficiale, crei un ambiente virtuale per tenere separate le dipendenze del progetto e installi le librerie con pip. Un ambiente virtuale è una sorta di cartella isolata: tutto ciò che installi lì dentro non interferisce con gli altri progetti presenti sul tuo computer.
python -m venv ml-progetto
# su Windows: ml-progetto\Scripts\activate
# su macOS e Linux: source ml-progetto/bin/activate
pip install scikit-learn pandas matplotlib jupyter
Se sei alle prime armi, il consiglio è partire da Colab.
Eviterai i piccoli intoppi legati all’installazione e potrai concentrarti subito su ciò che conta davvero. Quando il flusso ti sarà familiare, potrai spostarti in locale con Jupyter o con un editor come VS Code, che offre un ottimo supporto ai notebook.
Passo 2: caricare ed esplorare il dataset
Il primo gesto di qualsiasi progetto di machine learning non è addestrare un modello, ma guardare i dati. Scikit-learn include una serie di dataset di esempio che puoi caricare con una sola funzione. Con il parametro as_frame ottieni direttamente un DataFrame di pandas, cioè una tabella con righe e colonne etichettate.
from sklearn.datasets import load_breast_cancer
import pandas as pd
dati = load_breast_cancer(as_frame=True)
df = dati.frame
print(df.shape)
print(df.head())
print(df["target"].value_counts())
print(dati.target_names)
Il comando shape ti dice quante righe e quante colonne ci sono: 569 campioni e 31 colonne, cioè 30 caratteristiche numeriche più la colonna target. La funzione head mostra le prime righe, così puoi farti un’idea concreta di come sono fatti i valori. Il conteggio della colonna target, infine, ti dice quanti esempi appartengono a ciascuna classe.
Qui c’è un dettaglio da non trascurare. Nel dataset la classe 0 corrisponde a “malignant” (maligno) e la classe 1 a “benign” (benigno), e le due classi non sono perfettamente bilanciate: i campioni benigni sono più numerosi di quelli maligni. Questo ti servirà più avanti, quando dovrai scegliere come dividere i dati e come interpretare le metriche.
Statistiche descrittive e valori mancanti
Il passo successivo è controllare due cose: se ci sono valori mancanti e su quali scale si muovono le varie colonne. Il metodo isnull().sum() ti restituisce il numero di celle vuote per colonna, mentre describe() calcola media, deviazione standard, minimo, massimo e quartili di ogni caratteristica.
print(df.isnull().sum().sum())
print(df.describe().T[["mean", "std", "min", "max"]])
In questo dataset non troverai valori mancanti, ed è uno dei motivi per cui è così adatto a chi comincia. Noterai invece che le scale delle colonne sono molto diverse tra loro: alcune misure hanno valori piccoli, sotto l’unità, mentre l’area media arriva a valori nell’ordine delle centinaia o delle migliaia.
Tieni a mente questa differenza di scala, perché tornerà presto.
Visualizzare per capire
Un grafico vale spesso più di una tabella. Prova a confrontare la distribuzione di una singola caratteristica, per esempio il raggio medio, tra le due classi. Se gli istogrammi delle due classi appaiono ben separati, significa che quella caratteristica contiene informazione utile per distinguerle.
import matplotlib.pyplot as plt
for classe, nome in enumerate(dati.target_names):
df[df["target"] == classe]["mean radius"].plot.hist(alpha=0.5, bins=30, label=nome)
plt.xlabel("mean radius")
plt.legend()
plt.show()
Questo tipo di esplorazione non è un esercizio di stile. Ti aiuta a sviluppare un’intuizione sul problema, a individuare eventuali anomalie e a capire se il compito che stai affidando al modello è realistico. In un progetto reale, questa fase occupa spesso più tempo dell’addestramento vero e proprio.
Passo 3: separare feature, etichette e dati di test
Un modello supervisionato impara da esempi di cui conosce già la risposta corretta. Per questo devi separare le caratteristiche in ingresso, che per convenzione si chiamano X, dalla risposta da prevedere, che si chiama y. Nel nostro caso X contiene le 30 colonne numeriche e y contiene la colonna target.
Poi arriva il passaggio forse più importante di tutto il tutorial: dividere i dati in un insieme di addestramento e un insieme di test. Il modello imparerà solo dal primo, mentre il secondo resterà chiuso in un cassetto fino alla valutazione finale. È l’unico modo onesto per capire come si comporterà il modello su dati che non ha mai visto.
from sklearn.model_selection import train_test_split
X = df.drop(columns="target")
y = df["target"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
Vediamo i parametri uno per uno. Con test_size impostato a 0.2 riservi il 20% dei dati al test e usi il restante 80% per l’addestramento. Il parametro random_state fissa il generatore casuale, così ogni volta che esegui il codice ottieni la stessa divisione e i tuoi risultati restano riproducibili.
Il parametro stratify merita una spiegazione a parte. Poiché le due classi non sono bilanciate, una divisione puramente casuale potrebbe finire per mettere nel test una proporzione di casi maligni diversa da quella del dataset completo. Con stratify=y chiedi a scikit-learn di mantenere le stesse proporzioni in entrambi gli insiemi.
Un errore da evitare assolutamente: guardare i dati di test mentre prendi decisioni sul modello.
Ogni volta che scegli un parametro o una trasformazione osservando le prestazioni sul test, stai di fatto usando quei dati per addestrare, anche se indirettamente. Il risultato è una stima troppo ottimistica, che crollerà quando il modello incontrerà dati davvero nuovi. Questo fenomeno si chiama data leakage, cioè fuga di informazioni, ed è una delle trappole più comuni.
Passo 4: scegliere e addestrare il primo modello
Per il tuo primo classificatore userai la regressione logistica. Nonostante il nome, non serve a prevedere valori continui ma a stimare la probabilità che un esempio appartenga a una classe. È un algoritmo semplice, veloce, interpretabile e sorprendentemente efficace su molti problemi reali: il punto di partenza ideale.
Se vuoi capire nel dettaglio la matematica che c’è dietro e le differenze con la versione lineare, puoi approfondire con la nostra guida alla regressione lineare e logistica. Per ora ti basta sapere che il modello impara un peso per ciascuna caratteristica e combina questi pesi per arrivare a una probabilità.
Perché standardizzare le caratteristiche
Ricordi le scale molto diverse tra le colonne? Qui diventano rilevanti. Molti algoritmi, tra cui la regressione logistica con regolarizzazione, funzionano meglio quando tutte le caratteristiche si muovono su scale confrontabili. La standardizzazione trasforma ogni colonna in modo che abbia media zero e deviazione standard uno.
Il punto delicato è che i parametri della standardizzazione, cioè media e deviazione standard, vanno calcolati solo sui dati di addestramento e poi applicati anche al test. Se li calcolassi sull’intero dataset, introdurresti proprio quel data leakage di cui abbiamo parlato.
La soluzione elegante si chiama pipeline.
La pipeline: preprocessing e modello in un unico oggetto
Una pipeline di scikit-learn concatena più passaggi in un solo oggetto. Quando chiami fit, ogni trasformazione viene calcolata sui dati di addestramento e applicata in sequenza, fino al modello finale. Quando chiami predict, le stesse trasformazioni già apprese vengono applicate ai nuovi dati. In questo modo non rischi di mescolare per errore informazioni del test con quelle del training.
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
modello = make_pipeline(
StandardScaler(),
LogisticRegression(max_iter=1000)
)
modello.fit(X_train, y_train)
Tutto qui. La riga modello.fit è il momento in cui avviene l’apprendimento: l’algoritmo esamina gli esempi di addestramento e regola i propri pesi per ridurre al minimo gli errori. Il parametro max_iter alza il numero massimo di iterazioni consentite all’ottimizzatore, così eviti avvisi di mancata convergenza.
Colpisce quanto poco codice serva per addestrare un modello. È il frutto di anni di lavoro della comunità open source, che ha reso accessibili strumenti un tempo riservati ai laboratori di ricerca. Il vero lavoro, come vedrai, sta tutto intorno a queste poche righe.
Passo 5: valutare il modello con le metriche giuste
Adesso puoi finalmente aprire il cassetto e usare i dati di test. Chiedi al modello di fare previsioni sugli esempi che non ha mai visto e confrontale con le risposte corrette.
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
previsioni = modello.predict(X_test)
print(accuracy_score(y_test, previsioni))
print(confusion_matrix(y_test, previsioni))
print(classification_report(y_test, previsioni, target_names=dati.target_names))
Accuratezza: utile ma non sufficiente
L’accuratezza è la percentuale di previsioni corrette sul totale. È la metrica più intuitiva, ma da sola può trarre in inganno, soprattutto quando le classi sono sbilanciate. Immagina un modello pigro che risponde sempre “benigno”: avrebbe comunque un’accuratezza pari alla quota di casi benigni nel test, senza aver imparato nulla.
Per questo devi guardare più a fondo, a partire dalla matrice di confusione.
La matrice di confusione
La matrice di confusione mostra in una piccola tabella quanti esempi di ciascuna classe sono stati classificati correttamente e quanti sono finiti nella classe sbagliata. Le righe rappresentano la classe reale, le colonne la classe prevista. Sulla diagonale trovi le risposte giuste, fuori dalla diagonale gli errori.
Gli errori, però, non sono tutti uguali. In un contesto come questo, classificare come benigno un campione maligno è molto più grave del contrario. La matrice di confusione ti permette di vedere esattamente quale tipo di errore commette il tuo modello, informazione che l’accuratezza nasconde.
Precisione, richiamo e F1
Il classification_report calcola tre metriche per ogni classe. La precisione risponde alla domanda: tra tutti gli esempi che il modello ha assegnato a questa classe, quanti le appartenevano davvero? Il richiamo, o recall, risponde a un’altra domanda: tra tutti gli esempi che appartenevano davvero a questa classe, quanti il modello è riuscito a trovare?
Le due metriche tirano spesso in direzioni opposte. Un modello molto prudente nell’assegnare una classe avrà alta precisione ma basso richiamo, mentre uno che la assegna con generosità avrà l’effetto inverso. Il punteggio F1 è la media armonica tra precisione e richiamo e ti offre un unico numero che le bilancia.
Quale metrica privilegiare dipende sempre dal problema. Nel nostro esempio didattico il richiamo sulla classe maligna è quella più significativa, perché misura quanti casi critici il modello riesce a individuare. In un filtro antispam, al contrario, potresti preferire un’alta precisione, per non finire a cestinare email importanti.
Passo 6: validazione incrociata per risultati affidabili
Una singola divisione tra training e test ha un limite: il risultato dipende in parte da quali esempi sono finiti nell’uno o nell’altro insieme. Con un dataset piccolo come il nostro, una divisione diversa potrebbe darti un punteggio un po’ più alto o un po’ più basso. Come fai a sapere se il tuo numero è rappresentativo?
La risposta è la validazione incrociata, o cross-validation. L’idea è dividere i dati di addestramento in k parti uguali, chiamate fold. Il modello viene addestrato k volte, ogni volta usando k meno 1 parti per imparare e la parte rimanente per valutare. Alla fine ottieni k punteggi, di cui puoi calcolare media e deviazione standard.
from sklearn.model_selection import cross_val_score
punteggi = cross_val_score(modello, X_train, y_train, cv=5, scoring="f1")
print(punteggi)
print(punteggi.mean(), punteggi.std())
Nota che la validazione incrociata si esegue sui soli dati di addestramento. Il test resta intatto, pronto per la verifica finale. Una media alta con una deviazione standard bassa è un buon segnale: significa che il modello si comporta in modo stabile, indipendentemente da come vengono divisi i dati.
Se invece i punteggi oscillano molto, qualcosa non va.
Oscillazioni ampie possono indicare che il modello è troppo sensibile ai dati specifici su cui viene addestrato, uno dei sintomi tipici dell’overfitting. Se vuoi capire bene come riconoscere e contrastare questo problema, e il suo opposto, trovi tutto nella nostra guida su overfitting e underfitting.
Passo 7: confrontare algoritmi diversi
La regressione logistica è un ottimo punto di partenza, ma non è detto che sia la scelta migliore. Uno dei grandi vantaggi di scikit-learn è che tutti i modelli condividono la stessa interfaccia, con i metodi fit e predict. Puoi quindi confrontare algoritmi diversi cambiando una sola riga di codice.
Proviamo con una random forest, cioè un insieme di alberi decisionali addestrati su sottoinsiemi diversi dei dati, le cui previsioni vengono combinate per votazione. Gli alberi decisionali non hanno bisogno di standardizzazione, perché ragionano per soglie su una caratteristica alla volta, quindi qui la pipeline non è indispensabile.
from sklearn.ensemble import RandomForestClassifier
foresta = RandomForestClassifier(n_estimators=200, random_state=42)
punteggi_foresta = cross_val_score(foresta, X_train, y_train, cv=5, scoring="f1")
print(punteggi_foresta.mean(), punteggi_foresta.std())
Confronta la media e la deviazione standard con quelle della regressione logistica. Se le prestazioni sono simili, spesso conviene preferire il modello più semplice e interpretabile. È un principio noto come rasoio di Occam applicato al machine learning: a parità di risultati, la soluzione più semplice tende a generalizzare meglio ed è più facile da spiegare e mantenere.
Una random forest ha anche un vantaggio interessante: ti dice quali caratteristiche ha trovato più utili. Dopo averla addestrata con fit, l’attributo feature_importances_ restituisce un punteggio per ogni colonna. Ordinandolo, scopri su quali misure il modello fa più affidamento, un’informazione preziosa per capire il problema e per decidere eventuali semplificazioni.
foresta.fit(X_train, y_train)
importanze = pd.Series(foresta.feature_importances_, index=X.columns)
print(importanze.sort_values(ascending=False).head(10))
Passo 8: ottimizzare gli iperparametri
Ogni algoritmo ha dei parametri che non vengono appresi dai dati ma che devi scegliere tu prima dell’addestramento. Si chiamano iperparametri. Per la regressione logistica il più importante è C, che controlla l’intensità della regolarizzazione: valori piccoli rendono il modello più semplice e prudente, valori grandi gli permettono di adattarsi di più ai dati.
Invece di provare i valori a mano, puoi usare GridSearchCV. Questo strumento prova tutte le combinazioni che gli indichi, valuta ciascuna con la validazione incrociata e ti restituisce la migliore. Poiché lavora solo sui dati di addestramento, anche in questo caso il test rimane pulito.
from sklearn.model_selection import GridSearchCV
griglia = {"logisticregression__C": [0.01, 0.1, 1, 10, 100]}
ricerca = GridSearchCV(modello, griglia, cv=5, scoring="f1")
ricerca.fit(X_train, y_train)
print(ricerca.best_params_)
print(ricerca.best_score_)
Il nome del parametro nella griglia segue una convenzione precisa: il nome del passaggio della pipeline, due trattini bassi e il nome dell’iperparametro. La funzione make_pipeline assegna in automatico ai passaggi il nome della classe in minuscolo, per questo scrivi logisticregression__C.
Una volta trovata la configurazione migliore, GridSearchCV riaddestra automaticamente il modello su tutti i dati di training con quei parametri. Puoi usarlo direttamente tramite ricerca.best_estimator_ e, solo a questo punto, valutarlo una volta sul test per ottenere la stima finale delle prestazioni.
Non esagerare con la ricerca.
Griglie enormi richiedono molto tempo di calcolo e, su dataset piccoli, rischiano di trovare combinazioni che funzionano bene solo per caso sui fold di validazione. Parti con pochi valori distanziati in scala logaritmica, come nell’esempio, e restringi la ricerca solo se serve.
Passo 9: salvare il modello e usarlo su nuovi dati
Un modello che vive solo dentro un notebook serve a poco. Per riutilizzarlo devi salvarlo su disco, in modo da poterlo caricare in un altro script, in un’applicazione web o in un servizio. Con scikit-learn lo strumento più usato è joblib, che serializza l’intero oggetto, pipeline compresa.
import joblib
modello_finale = ricerca.best_estimator_
joblib.dump(modello_finale, "modello_classificatore.joblib")
caricato = joblib.load("modello_classificatore.joblib")
nuovo_campione = X_test.iloc[[0]]
print(caricato.predict(nuovo_campione))
print(caricato.predict_proba(nuovo_campione))
Il metodo predict_proba è particolarmente utile: invece di una semplice etichetta ti restituisce la probabilità stimata per ciascuna classe. In molte applicazioni reali è più informativo sapere che il modello è sicuro al 55% piuttosto che al 99%, perché puoi decidere di trattare diversamente i casi incerti, per esempio sottoponendoli a una verifica umana.
Due avvertenze pratiche. Carica file joblib solo da fonti di cui ti fidi, perché il formato può eseguire codice al momento del caricamento. E ricorda che un modello salvato dipende dalla versione di scikit-learn con cui l’hai creato: annota sempre le versioni delle librerie, così potrai ricostruire l’ambiente in futuro.
Gli errori più comuni da evitare
Arrivato fin qui, hai completato un ciclo intero. Prima di passare a progetti più ambiziosi, vale la pena fermarsi sugli sbagli che capitano più spesso a chi comincia, perché riconoscerli in anticipo ti farà risparmiare ore di frustrazione.
Valutare sui dati di addestramento
Misurare le prestazioni sugli stessi dati usati per addestrare dà quasi sempre risultati eccellenti e quasi sempre illusori. Un modello abbastanza complesso può semplicemente memorizzare gli esempi. Solo i dati mai visti, test o fold di validazione, ti dicono qualcosa sulla capacità di generalizzare.
Preprocessing prima della divisione
Standardizzare, riempire i valori mancanti o selezionare le caratteristiche sull’intero dataset prima di dividerlo introduce data leakage. È un errore subdolo, perché il codice funziona e i numeri sembrano buoni. Usa sempre le pipeline e applica ogni trasformazione dopo la divisione.
Fidarsi di una sola metrica
Un’accuratezza elevata non basta a dire che il modello è buono. Guarda sempre la matrice di confusione e le metriche per classe, e chiediti quale tipo di errore è più costoso nel tuo contesto specifico.
Trascurare la qualità dei dati
Il dataset di questo tutorial è pulito, ma nel mondo reale i dati raramente lo sono. Valori mancanti, errori di inserimento, categorie scritte in modi diversi e colonne inutili sono la norma. La qualità del modello dipende molto più dalla qualità dei dati e delle caratteristiche che dalla scelta dell’algoritmo. Per imparare a trasformare dati grezzi in input efficaci, il passo successivo naturale è la nostra guida al feature engineering.
Come proseguire dopo il primo modello
Hai imparato un flusso di lavoro che resta valido anche per progetti molto più complessi: esplorare, dividere, costruire una pipeline, addestrare, valutare con attenzione, confrontare, ottimizzare, salvare. Cambieranno i dati, gli algoritmi e gli strumenti, ma la logica rimarrà la stessa.
Il modo migliore per consolidare quanto hai appreso è ripetere l’esercizio su dataset diversi.
Scikit-learn include altri dataset di esempio, come quello sui vini o quello sulle cifre scritte a mano, che ti permettono di affrontare problemi multiclasse. Su piattaforme come Kaggle o nell’UCI Machine Learning Repository trovi migliaia di dataset pubblici su temi diversissimi, spesso accompagnati da notebook di altri utenti da cui imparare.
Prova poi a cambiare tipo di problema. La regressione, in cui prevedi un valore numerico come un prezzo o una temperatura, ti obbligherà a usare metriche diverse, come l’errore medio assoluto. L’apprendimento non supervisionato, per esempio il clustering, ti porterà a scoprire gruppi nascosti nei dati senza avere etichette di partenza.
Quando ti sentirai a tuo agio con il machine learning classico, potrai avvicinarti al deep learning con librerie come PyTorch o TensorFlow, indispensabili per lavorare con immagini, audio e testo. Il salto sarà molto più naturale, perché i concetti di base, dalla divisione dei dati alla valutazione, sono esattamente quelli che hai appena messo in pratica.
Conclusioni
Costruire il primo modello di machine learning è un piccolo rito di passaggio. Non richiede un dottorato né hardware costoso, ma un metodo rigoroso e un po’ di curiosità. In poche decine di righe di Python hai caricato e analizzato dati reali, hai addestrato un classificatore, lo hai valutato con metriche adeguate, lo hai confrontato con un’alternativa, ne hai ottimizzato i parametri e lo hai reso riutilizzabile.
La vera lezione, però, non sta nel codice. Sta nelle domande che hai imparato a porti: i dati sono puliti? Sto valutando in modo onesto? Quale errore è più grave nel mio caso? Il modello più complesso vale davvero la pena? Sono le stesse domande che si pongono ogni giorno i professionisti del settore.
Ora tocca a te.
Apri un notebook, riscrivi il codice di questa guida senza copiarlo, cambia qualcosa e osserva che cosa succede. Poi scegli un dataset che ti incuriosisce davvero e ripeti il percorso da capo. Se vuoi continuare a crescere, esplora le altre guide del nostro sito dedicate al machine learning e torna qui ogni volta che hai bisogno di un riferimento pratico: il tuo prossimo modello è più vicino di quanto pensi.