Adversarial Validation: da Kaggle al Machine Learning in produzione
Una tecnica semplice, utile e per qualche motivo poco nota alla maggior parte dei data scientists
Original in Italian; automatic translation into English available here.
Intro
Chi si occupa da un po’ di tempo di machine learning e data science sa che questo mondo è dato dalla sintesi di diverse componenti molto diverse tra loro:
in primis, l’universo della statistica e della matematica applicata, fatto di metodologia, rigore ed un disprezzo mal celato per soluzioni brute force e poco eleganti1
non meno importante, l’informatica e tutto ciò che riguarda il calcolo massivo, su cui si fonda il moderno deep learning, amato dagli ingegneri e dalle figure più tecniche
alla base di tutto, il mondo dei dati appartenenti a specifici campi e domini, la cui conoscenza è necessaria per andare oltre a proof-of-concept naïf che hanno ormai poco spazio (fortunatamente)
Le principali evoluzioni degli ultimi anni hanno tipicamente riguardato una (o più) di queste tre componenti.
Eppure, ogni tanto capita di imbattersi in qualche metodologia molto efficace che finisce col non trovare supporto da nessuna delle tre anime di cui sopra. Qualcosa di non abbastanza elegante per essere amata dagli statistici, non legata all’efficienza computazionale che tanto piace agli informatici, non troppo vicina al mondo dei dati per piacere ai vari esperti di dominio.
È il caso di tante soluzioni furbe a problemi reali: l’adversarial validation ne è un esempio perfetto.
Le performance dei modelli di ML in produzione
Partiamo dal problema: i modelli di machine learning in produzione non funzionano (quasi) mai come sperato. Anche escludendo veri e propri epic fails, le performance che si registrano in produzione sono spesso un po’ inferiori al previsto.
C’è una lunga lista di motivazioni valide e credibili, tipicamente riconducibili ad un singolo concetto: il data drift, ossia il fatto che i dati (sottostanti al modello di ML) cambino nel tempo.
Pensiamo ad un modello che utilizzi, tra le sue variabili, gli importi in euro di un qualsiasi bene. Con l’inflazione che si sta registrando di questi tempi, un modello addestrato qualche mese fa e usato oggi potrà facilmente portare a risultati imprevisti.
Da questo tipo di considerazioni scaturiscono naturalmente esigenze di aggiornamenti dei modelli, retraining e simili. Tutto corretto.
Ma siamo sicuri che il problema sia sempre e solo quello?
Nella mia esperienza, no. Una parte sostanziosa del problema sta nella difficoltà del disegnare un corretto framework di valutazione di un modello di ML, già nella fase di train-validation-test.
Ma non basta misurare le performance su un test set?
Partiamo da un punto fermo: no, per avere risultati robusti non basta valutare la performance di un modello di ML sul dataset di test.
È infatti un attimo finire con l’ottimizzare direttamente quel dataset, facendo overfitting: non è un caso che, dove le performance predittive sono misurate esternamente e muovono premi importanti (ossia su Kaggle, la piattaforma di ML di Google che ho già menzionato), di fatto esistono due test set. Sono la public leaderboard e la private leaderboard, dove quest’ultima è usata solo alla chiusura della competizione.
Non penso che un setup analogo a quello di Kaggle sia facilmente implementabile ed economicamente conveniente per la maggior parte delle grandi imprese. Ci si ritrova quindi con l’equivalente della private leaderboard che è di fatto… la performance in produzione! Con tutti i rischi del caso.
Il problema va quindi posto in una forma diversa. La domanda corretta, a mio modo di vedere, deve essere:
Come realizzo un modello efficace di ML, limitando l’uso di variabili facilmente soggette a data drift e garantendo che train-validation-test siano indistinguibili tra loro?
Questo approccio è fondamentale (e complicato) soprattutto su problemi tempo-varianti2, dove tipicamente il train viene prima del validation, che a sua volta viene prima del test.
Se già il test fosse in qualche maniera distinguibile dal train, vorrebbe dire che stiamo addestrando un modello su dati che già non sono ben rappresentativi della realtà attuale… figuriamoci sul vero test, ossia la produzione, che è ancora più avanti nel tempo!
L’approccio tradizionale
Chi ha qualche base statistica penserà subito ad una serie di test statistici che si possono fare in casi come questo.
Di fatto, potremmo riformulare il concetto di indistinguibilità tra i dataset come un confronto tra le distribuzioni delle variabili che li costituiscono, magari usando il test di Kolmogorov-Smirnov. Ma non sono sicuro che sia il modo migliore.
E soprattutto, chi ha pratica con dati reali sa che è facile imbattersi in dataset:
con un mix di variabili continue, discrete, magari categoriche
con valori mancanti
con outlier
con tutta una serie di caratteristiche “antipatiche” che richiedono un lungo e tedioso preprocessing dei dati
La soluzione “street-smart”: adversarial validation
Mi piace molto l’espressione inglese street-smart: fa riferimento a quella “furbizia da strada” che serve proprio in casi come questi, in cui un approccio robusto e rigoroso, ma anche molto time-consuming, non è forse quello migliore.
Ripensiamo al problema di partenza: garantire che train e test siano indistinguibili3. Non vediamolo più come un confronto tra distribuzioni… proviamo a formularlo così:
Verifichiamo se un classificatore addestrato per distinguere tra train e test è in grado di assegnare un record ad uno dei due dataset con probabilità maggiore del lancio di una monetina!
L’idea è semplice:
Dimentichiamoci del problema di ML di partenza (buttiamo via il target originario)
Prendiamo train e test e uniamoli, assegnando al primo target = 0 e al secondo target = 1
Addestriamo un classificatore (ad esempio un semplice xgboost che supporta qualsiasi tipo di feature, valori mancanti, etc.)
Vediamo se l’AUC è maggiore di 0,5!
Se sì, verifichiamo la feature importance per scoprire quali sono le variabili che davvero fanno distinguere train e test
Facciamo un drill-down (statistico, ma anche di processo e di business) solo su queste variabili!
Tutto qui! Questa è l’adversarial validation: confesso di averla trovata una metodologia geniale nella sua semplicità, la prima volta che l’ho scoperta su un forum di Kaggle.
Su quella piattaforma è usata ampiamente per studiare le differenze tra train e test set, ma l’ho trovata utilissima anche sul lavoro, per vari motivi:
Troubleshooting: cosa succede se dimentichiamo un ID progressivo su train e test? Finirà col creare solo rumore e magari non sporcare così tanto le performance predittive sul vero target… rischiando di passare inosservato. Invece nell’adversarial validation, porterà l’AUC subito a 1 e potremo rimuoverlo!
Leak su feature engineering: è facile che variabili create con feature engineering siano in qualche maniera diverse tra train e test… anche qui, sicuramente porteranno l’AUC dell’adversarial validation sopra 0.5. Lo stesso vale per valori presenti solo su train o solo su test
Vero data drift: tolti errori e leak, nel mondo reale l’AUC difficilmente sarà esattamente a 0.5. Almeno avremo chiaro quali sono le variabili più soggette a data drift e potremo fare tutte le valutazioni del caso
Per la cronaca, ricordo che l’AUC è una metrica che indica la capacità di separare due classi e non è influenzata dall’eventuale sbilanciamento (es. train più grande di test). Quindi fa proprio al caso nostro!
Conclusioni
Data science e machine learning sono discipline tuttora molto nuove, ed è facile farsi travolgere dalle mille novità che escono ormai ogni giorno.
È anche molto facile pensare, specialmente per chi si affaccia oggi a questo mondo, che sia indispensabile disporre di grandissima potenza di calcolo per fare qualcosa di significativo; oppure avere una conoscenza enciclopedica di un’infinità di metodologie, algoritmi e approcci statistici, per poter arrivare a qualche conclusione valida.
Invece penso che ci sia ancora spazio per molta creatività! Quello che ho descritto oggi probabilmente non troverà spazio su An Introduction to Statistical Learning o altri testi di riferimento per la data science, ma sicuramente risolve un problema reale in maniera semplice e brillante!
Matematicamente parlando.
Ossia che dipendono dal tempo, come quasi tutti i problemi reali.
E anche validation ovviamente: lo escludo solo per scorrevolezza.