Un medico può dirti due cose dopo gli esami. La prima: «il colesterolo ha inciso per il 40% sul tuo indice di rischio, il fumo per il 30%». La seconda: «se scendi sotto 200 e smetti di fumare, esci dalla fascia di rischio». Sono entrambe vere e riguardano lo stesso modello, ma solo la seconda si può usare per fare qualcosa lunedì mattina.
Nell’interpretabilità del machine learning la prima è l’attribuzione — quanto ogni feature ha spinto verso quella risposta — ed è ciò che danno LIME e SHAP. La seconda è la spiegazione controfattuale: il punto più vicino al tuo caso in cui il modello avrebbe risposto diversamente. Quando la spiegazione la legge la persona che ha subito la decisione, e non il data scientist che ha addestrato il modello, quasi sempre la domanda vera è la seconda.
L’attribuzione scompone la decisione presa; il controfattuale descrive la decisione più vicina che sarebbe stata diversa. Il secondo è azionabile solo se qualcuno gli ha detto quali colonne una persona può davvero cambiare.
Che cosa siano attribuzione e controfattuale, e perché siano risposte a domande diverse, il libro lo costruisce per intero nel capitolo sulle spiegazioni locali — LIME, i valori di Shapley e i controfattuali insieme. Qui si passa direttamente a produrli.
Generarli con DiCE
DiCE — Diverse Counterfactual Explanations — nasce dal lavoro di Ramaravind Mothilal, Amit Sharma e Chenhao Tan presentato a FAT* 2020. Funziona con modelli scikit-learn, PyTorch e TensorFlow, e la sequenza minima è tre oggetti e una chiamata.
pip install dice-ml
import dice_ml
dati = dice_ml.Data(
dataframe=train_df,
continuous_features=["reddito", "anzianita_mesi", "debito", "eta"],
outcome_name="approvato",
)
modello = dice_ml.Model(model=clf, backend="sklearn")
spiegatore = dice_ml.Dice(dati, modello, method="random")
risultato = spiegatore.generate_counterfactuals(
pratica_rifiutata, # un DataFrame di una riga
total_CFs=4,
desired_class="opposite", # «fammi vedere i casi in cui sarebbe stato sì»
)
risultato.visualize_as_dataframe(show_only_changes=True)
Il parametro method sceglie come si cercano i punti: random campiona, genetic usa un algoritmo evolutivo, gradient sfrutta i gradienti quando il modello è differenziabile. Il primo è il più veloce e va benissimo per capire se il metodo dice qualcosa di sensato sul tuo problema; conviene salire dopo, non prima.
Il vincolo che separa l’utile dall’assurdo
Eseguito così com’è, il codice qui sopra produce quasi sempre almeno un consiglio ridicolo: abbassa l’età di vent’anni, cambia paese di nascita, riduci il numero di figli. Non è un difetto della libreria: il modello è una funzione di quelle colonne, e la funzione non sa che alcune non si possono muovere. Deve saperlo chi la usa, e va detto esplicitamente.
risultato = spiegatore.generate_counterfactuals(
pratica_rifiutata,
total_CFs=4,
desired_class="opposite",
# solo ciò su cui la persona può agire
features_to_vary=["reddito", "anzianita_mesi", "debito"],
# e dentro limiti realistici: nessuno raddoppia lo stipendio in un mese
permitted_range={"reddito": [28000, 34000], "anzianita_mesi": [14, 30]},
)
Questa è la parte del lavoro che non si può automatizzare e che decide la qualità del risultato. features_to_vary traccia il confine fra ciò che una persona può cambiare e ciò che è, e va discusso con chi conosce il dominio. permitted_range aggiunge la scala del possibile: un controfattuale valido ma irraggiungibile è una risposta formalmente corretta e praticamente offensiva.
Perché servono più risposte, non una
Il nome della libreria mette al centro la diversità, e non è un vezzo. Gli autori sostengono che una spiegazione controfattuale efficace debba avere due proprietà: la fattibilità delle azioni proposte dato il contesto e i vincoli di chi le riceve, e la diversità fra i controfattuali presentati.
La ragione pratica è che una sola strada è quasi sempre la strada sbagliata per qualcuno. «Aumenta il reddito di 3.500 euro» non serve a chi ha un contratto bloccato; «riduci il debito di 4.000» non serve a chi non ha risparmi. Presentandone quattro diversi, la probabilità che almeno uno sia percorribile per quella persona cresce molto — ed è il motivo per cui il metodo cerca punti distanti fra loro invece di limitarsi al più vicino in assoluto.
Controllare che funzionino davvero
Un controfattuale è il risultato di una ricerca, non di una dimostrazione, e la ricerca può fermarsi su un punto che il modello classifica ancora come prima — soprattutto con i metodi campionari e con vincoli stretti, dove lo spazio in cui cercare si restringe molto. Il controllo costa tre righe e va fatto sempre, perché consegnare a una persona un consiglio che non ottiene il risultato promesso è peggio che non consegnare nulla.
proposte = risultato.cf_examples_list[0].final_cfs_df
esito = clf.predict(proposte.drop(columns=["approvato"]))
print(f"controfattuali validi: {esito.sum()} su {len(proposte)}")
Se la resa è bassa, le strade sono due e conviene provarle in quest’ordine: allargare i permitted_range, che spesso sono stati stretti a occhio, e solo dopo passare a method="genetic", che cerca meglio e costa di più.
Quello che un controfattuale non è
Due avvertenze, e sono serie perché il metodo si presta a essere frainteso proprio da chi ne trae più conforto.
Non è una relazione causale. Dice dove passa il confine decisionale del modello, non che cosa succederebbe nel mondo. Se il modello ha imparato che chi ha un’anzianità lavorativa maggiore ripaga meglio, il controfattuale dirà «aspetta dieci mesi»; ma se quella correlazione nascondeva un’altra variabile, aspettare dieci mesi cambierà la risposta del modello senza cambiare il rischio reale. Il controfattuale spiega il modello, mai la realtà.
È anche un manuale per aggirarlo. La stessa informazione che aiuta una persona a migliorare la propria posizione insegna a un’altra il valore minimo da dichiarare per superare la soglia. Nei domini dove il dato è auto-dichiarato — questionari, moduli, autocertificazioni — pubblicare controfattuali precisi significa pubblicare la mappa del confine. È una decisione da prendere consapevolmente, non un dettaglio di implementazione.
E la normativa
C’è una ragione in più per cui vale la pena costruirseli adesso, e riguarda la forma che le spiegazioni dovranno avere. L’articolo 86 dell’AI Act — «diritto alla spiegazione dei singoli processi decisionali» — riconosce a chi è interessato da una decisione presa con un sistema ad alto rischio il diritto di ottenere dal deployer spiegazioni «chiare e significative» sul ruolo del sistema nella procedura decisionale e sui principali elementi della decisione presa.
Su quando quell’obbligo morda, il calendario è in movimento: il grosso degli adempimenti sull’alto rischio è stato rinviato, ed è una vicenda che merita di essere seguita a parte. Ma la direzione della norma è leggibile fin d’ora, ed è interessante per chi progetta: «spiegazioni chiare e significative» destinate alla persona interessata assomigliano molto più a una frase in italiano su che cosa avrebbe cambiato la decisione che a un grafico a barre di valori di Shapley. Il secondo è uno strumento da laboratorio; la prima è quello che una persona può leggere e contestare.
Un’attribuzione spiega il modello a chi l’ha costruito. Un controfattuale spiega la decisione a chi l’ha subita. Sono due destinatari diversi, e solo uno dei due ha dei diritti.
Vale la pena provarlo su un modello che hai già in produzione: mezz’ora, quattro controfattuali su dieci casi rifiutati, letti ad alta voce. Se suonano assurdi, non è il metodo che non funziona — sono i vincoli che nessuno ha ancora scritto, ed è comunque una cosa che era meglio scoprire adesso.

I commenti sono riservati agli iscritti.
Accedi per commentare