I casi d’uso per queste tecniche possono essere dei più disparati, citiamone alcuni:

  • creazione di database. Se dobbiamo sperimentare il funzionamento di un nostro applicativo, supportato da un database e vogliamo verificare l’efficacia delle interrogazioni da noi progettate in presenza di carichi di dati notevoli, avremo spesso bisogno di grandi moli di dati, magari milioni di righe e non possiamo certo pensare ad un loro inserimento manuale;
  • sperimentazione di algoritmi di Intelligenza Artificiale. Esistono molti algoritmi per la risoluzione di problematiche e produzione di previsioni ma per scegliere quello adatto – oltre a valutare i risultati ottenuti – abbiamo bisogno di sperimentarli con moli di dati che possano rappresentare delle situazioni realistiche adatte al tipo specifico di procedimento;
  • ingegnerizzazione di archivi per la conservazione ed il recupero di file. Per allestire un sistema che sia in grado di stoccare e gestire grandi quantità di file (pensiamo a sperimentazioni in materia di Big Data) in maniera efficiente sarebbe utile avere a disposizione meccanismi per la generazione di file, possibilmente con caratteristiche simili a quelli realmente coinvolti nel business che il nostro servizio dovrebbe supportare.

In tutti questi casi, si può trovare dei dataset o degli archivi di file che possano fare al caso nostro ma molto spesso dobbiamo essere in grado di produrli noi stessi: l’aspetto più complesso consiste nel creare dei dataset che siano verosimili per il problema da trattare e possano davvero mettere alla prova il sistema che stiamo sviluppando.

Affronteremo un percorso che ci porterà all’esplorazione della problematica passando per:

  • generazione di dati casuali (con valutazione dell’importanza del seme);
  • generazione di dataset per il Machine Learning.

Generazione di dati casuali

Per la generazione dei dataset, il primo passo è quello di richiedere la creazione di dati casuali (o meglio pseudocasuali, come è più corretto denominarli). Tutto ciò può essere assolutamente utile ma come vedremo ha dei limiti. Facciamo qualche esempio in linguaggio Python sebbene i discorsi che faremo siano applicabili potenzialmente a qualsiasi linguaggio di programmazione.

Quando si generano dati casuali, tipicamente ci si potrebbe trovare in due situazioni:

  • si fornisce una lista di valori e si richiede che il risultato sia uno di questi estratto a sorte;
  • si richiede la generazione di uno o più numeri (appartenenti ad un intervallo prefissato o in maniera più generica compresi tra 0 e 1 in modo da poter poi essere proporzionati all’insieme di interesse).

Come esempio del primo caso, pensiamo a quando vogliamo generare dei nomi casuali per degli utenti o degli indirizzi e-mail scegliendo tra un elenco di provider. In casi di questo genere, a differenza dei numeri, conviene impostare un insieme di potenziali valori e farne estrarre uno o più di uno da questi.

Il linguaggio Python, ad esempio, di base offre il modulo random (un modulo è un insieme di funzionalità) in cui esiste la funzione choice che svolge proprio il compito di selezionare un valore in un elenco.

Vogliamo un nome a caso tra Alessia, Ivan, Serena, Roberto (l’elenco sarà molto più lungo, presumibilmente)?

Facciamo una lista e chiediamo a choice di scegliere un valore pseudocasualmente.

import random

nomi=['Alessia', 'Ivan', 'Serena', 'Roberto']

nome_a_caso=random.choice(nomi)

Dopo l’esecuzione di queste righe, la variabile nome_a_caso avrà al suo interno un valore, selezionato a caso tra i quattro nomi.

Se invece volessimo un numero in un range, potremmo farlo estrarre da una funzione come randrange (ne esistono molte altre).

Consideriamo queste righe:

import random

numero_a_caso=random.randrange(1,25)

Eseguendole, nella variabile numero_a_caso troveremmo un valore incluso tra 1 e 24 (il secondo argomento è il limite superiore dell’intervallo e non viene mai restituito).

Un aspetto importante nella generazione di numeri casuali e che va sempre tenuto in considerazione, è la ripetibilità dell’esperimento.

Ad esempio, se provassimo a far generare cinque numeri casuali:

import random

for i in range(0,5):

  print(random.randrange(1,25))

otterremmo, ad ogni esecuzione valori diversi. Questo potrebbe essere un problema in quanto durante il perfezionamento di un meccanismo di analisi avremmo spesso bisogno di testarlo ripetutamente sugli stessi dati. A meno che non possiamo immagazzinarli una volta creati, dovremmo istruire il generatore casuale nel fornire sempre lo stesso dataset. Questo è il caso in cui va usato un seme o, come si dice tecnicamente, un seed.

L’esempio precedente può essere riproposto usando, allo scopo, la funzione seed del modulo random:

import random

random.seed(10)

for i in range(0,5):

  print(random.randrange(1,25))

In questo caso sarà generata una sequenza numerica che rimarrà identica ad ogni sua ripetizione. Il concetto di seme è assolutamente fondamentale, applicabile a qualsiasi meccanismo di generazione di valori casuali e pertanto da individuare ogniqualvolta si voglia procedere alla creazione dinamica di propri dataset con queste funzioni o altre.

Il valore passato a seed è arbitrario. In questo caso, abbiamo scelto 10 ma avremmo potuto sceglierne qualsiasi altro. L’importante è ricordare che a quel valore corrisponderà la stessa generazione di dati: se seed(10) ha generato la sequenza 19, 2, 14, 16, 19, le stesse righe di codice con il medesimo valore del seme genereranno sempre 19, 2, 14, 16, 19.

Quanto visto sinora – sebbene si tratti dei fondamenti basilari della generazione di dati – potrebbe già essere sufficiente alla creazione di dataset piuttosto variegati. Immaginiamo di voler creare dei dati immaginari per degli ipotetici studenti universitari, le funzioni viste sinora potrebbero essere sufficienti per “inventare” nomi, indirizzi e-mail, esami sostenuti, date, voti e molto molto altro.

Eppure, per l’addestramento di algoritmi di Machine Learning, la situazione si complica pertanto esistono strumenti appositi.

Generazione di dataset per l’Intelligenza Artificiale

Il grosso degli algoritmi di Machine Learning è articolabile in due vaste famiglie: regressione e classificazione. La prima delle due si occupa di predire valori esatti, la seconda di attribuire una categoria a dei campioni di dati.

Volessimo creare un dataset per la regressione, anche semplicemente una regressione lineare, potrebbe bastare utilizzare le funzioni viste prima? No, sicuramente no.

Questo perché un dataset per algoritmi di Machine Learning deve possedere specifici requisiti affinchè abbia senso studiarlo.

Questo è il motivo per cui librerie importanti del settore offrono anche strumenti per la creazione di appositi dataset di sperimentazione. Analizziamo insieme cosa offre in merito sklearn (https://scikit-learn.org/), una delle librerie in Python più usate per il Machine Learning.

Creare dataset per il Machine Learning

La libreria sklearn dispone, nel package datasets, di vari metodi per la creazione di dataset destinati alle più comuni casistiche del Machine Learning, tra cui make_regression per la creazione di dati per la regressione lineare e make_classification per la creazione di dataset ideali per la classificazione. Aspetti interessanti di queste funzioni consistono nel possesso di alcuni parametri che permettono di rendere più “difficile” il loro studio per i nostri algoritmi.

Consideriamo ad esempio make_regression. Può essere uno strumento ideale per la sperimentazione della regressione lineare, uno degli algoritmi da cui tipicamente gli aspiranti data scientist muovono i primi passi per iniziare a studiare il Machine Learning. Lo vedremo al lavoro utilizzando la libreria grafica matplotlib (https://matplotlib.org/) per rappresentare il dataset creato e vederne i risultati.

Ad esempio, con:

X_test, y_test = make_regression(n_samples=200, n_features=1, random_state=1)

chiediamo di creare un dataset di duecento campioni, con una sola feature. In un caso del genere, saranno creati dati totalmente aderenti ad una linea di regressione infatti aggiungendo la rappresentazione di uno scatter plot per visualizzare i punti generati su un piano avremmo l’impressione di avere dati totalmente pensati per appartenere ad una retta di regressione.

Si noti il ruolo dei parametri utilizzati:

  • n_samples rappresenta il numero di campioni da generare ovvero il numero di elementi che l’algoritmo dovrà studiare;
  • n_features indica il numero di feature;
  • random_state ha un ruolo analogo al seed incontrato in precedenza. Permette la ripetibilità della generazione di dati per evitare che ad ogni esecuzione del comando vengano generati numeri diversi: anche in questo tipo di produzione casuale di dataset è fondamentale utilizzarlo.

Le righe di codice:

from sklearn.datasets import make_regression

from matplotlib import pyplot as plt

X_test, y_test = make_regression(n_samples=200, n_features=1, random_state=1)

plt.scatter(X_test, y_test)

plt.show()

genererebbero un grafico a punti come il seguente:

202206-GenerareDataset_img01

Si tratterebbe, come si può immaginare, di un caso facilmente risolvibile da un algoritmo di learning. Per questo, applicando un fattore di rumore, con il paremetro noise, potremmo avere dati più “confusi”, per così dire, ma pur sempre attinenti ad una problematica di regressione lineare.

Infatti, lo script:

from sklearn.datasets import make_regression

from matplotlib import pyplot as plt

X_test, y_test = make_regression(n_samples=200, n_features=1, noise=15, random_state=1)

plt.scatter(X_test, y_test)

plt.show()

fornirebbe quanto mostrato qui di seguito:

202206-GenerareDataset_img02

Come vediamo, si tratta di dati che seguono sempre una tipica retta di regressione ma senza adagiarsi su di essa totalmente bensì ruotandole attorno rendendo quindi lo studio del nostro algoritmo meno agevole.

Partendo da questi spunti si potrebbe proseguire, in primis, variando il valore di noise e di random_state fino a trovare un dataset su cui cimentarsi. Questo per quanto riguarda un approccio iniziale ma successivamente sarà opportuno aumentare il numero di feature da produrre per poi studiare la documentazione della funzione per individuare ulteriori parametri su cui agire per produrre un dataset totalmente ideale per le nostre sperimentazioni.

Conclusioni

Quali lezioni possiamo trarre perciò da questa introduzione alla generazione di dataset?

In primis che ci deve essere chiaro quale tipologia di problematica vogliamo affrontare. Se ci interessa creare dati per popolare un database e verificare le prestazioni delle nostre query possiamo utilizzare funzioni come choice o randrange applicando set di valori differenti. Ciò potrebbe essere sufficiente per creare dati in grandi quantità, adeguati alla descrizione realistica di set di informazioni ma che non necessitino di particolari caratteristiche statistiche.

Qualora dovessimo dedicarci allo studio del Machine Learning o ad altre problematiche in cui i dati devono necessariamente offrire specifiche proprietà a livello matematico e statistico dovremmo rivolgerci a strumenti ad hoc come i generatori offerti dalle librerie per l’Intelligenza Artificiale tipo sklearn. Abbiamo visto il caso di make_regression, ideale per problemi di regressione lineare, ma lo studio deve necessariamente proseguire con altre funzioni che possano adattarsi al tipo di problemi che ci interessa risolvere.

I dati sono una risorsa fondamentale ed esistono dataset perfetti per ogni sperimentazione ma non dimentichiamo che dobbiamo sempre mettere alla prova al massimo le interrogazioni e le analisi che prepariamo, per questo abbiamo bisogno di dataset più numerosi e dettagliati: generarli autonomamente potrebbe davvero essere una svolta fondamentale.