HuggingFace è oggigiorno uno dei punti di riferimento per l’Intelligenza Artificiale e costituisce un vero e proprio hub ovvero un punto di incontro di modelli, dati, documentazione e contributi provenienti da un’ampissima community. Abbiamo visto le sue funzionalità generali in questo articolo.

Qui entriamo invece nel dettaglio parliamo della sezione Dataset di HuggingFace, per scoprire come usare i dati pubblicati dai tantissimi contributor e sfruttare le librerie Python per creare e condividere dataset prodotti da noi: soprattutto quest’ultimo punto è cruciale in quanto permette di coinvolgere i nostri documenti nel fine tuning dei Large Language Model.

Caricare dataset pubblicati su HuggingFace

Per tutte le operazioni che svolgeremo verrà utilizzato il linguaggio Python, il più popolare per l’Intelligenza Artificiale. Come sempre, in questi casi, si parte dall’installazione nel proprio ambiente di lavoro di un’apposita libreria Python (per farlo in modo semplice e gratuito suggeriamo di usare Google Colab):

pip install datasets

Svolta questa operazione, potremo scegliere un dataset da importare. In questi nostri esempi non utilizzeremo i dati per elaborazioni in stile AI, ma piuttosto cercheremo di capire come questi siano organizzati a livello di architettura interna.

Importiamo, a titolo di esempio, il dataset ag_news, un contenitore di notizie ognuna contraddistinta da un testo e una label che ne indirizza la categoria, preimpostato per impieghi nella classificazione del testo:

from datasets import load_dataset
dataset = load_dataset("ag_news")

Così facendo, importiamo, mediante l’oggetto dataset, tutti i dati messi a disposizione che popoleranno una struttura dati di questo tipo:


DatasetDict({
train: Dataset({
features: ['text', 'label'],
num_rows: 120000
})
test: Dataset({
features: ['text', 'label'],
num_rows: 7600
})
})

Notiamone subito le caratteristiche fondamentali:

  • la classe centrale è DatasetDict, contributo della libreria datasets;
  • la struttura è suddivisa in due split ovvero train e test, nulla di insolito nell’ambito dell’Intelligenza Artificiale in cui i modelli vengono addestrati con i dati di train e verificati con quelli di test;
  • ogni split è rappresentato da un oggetto di classe Dataset;
  • le righe a disposizione dello split di train sono 120.000 mentre quelle per il test sono 7.600;
  • le features (quelle che potremmo considerare le colonne di una tabella) ovvero le caratteristiche per ogni campione di dati, sono chiamate test e label rappresentate, rispettivamente, dal contenuto della notizia e dal codice di classificazione.

Visualizzare le informazioni di un dataset di Hugging Face

Prima di mettere un dataset a disposizione di un processo di Intelligenza Artificiale, potremmo voler visualizzare i dati presenti al suo interno. Per farlo, potremo utilizzare i consueti meccanismi di indicizzazione Python con parentesi quadre, ponendo attenzione a quale sarà lo split cui vorremo accedere.

Potremo richiedere la prima riga di train:

dataset['train'][0]

ottenendo un normale dizionario Python con notizia e la relativa classificazione:

{'text': "Wall St. Bears Claw Back Into the Black (Reuters) Reuters - Short-sellers, Wall Street's dwindling\\band of ultra-cynics, are seeing green again.",
 'label': 2}

oppure mediante lo slicing potremo utilizzare i due punti per richiedere più elementi:

dataset['train'][2:5]

come in questo caso in cui otterremo tre notizie (da quella in posizione 2 a quella in posizione 5 esclusa):

{'text': ["Oil and Economy Cloud Stocks' Outlook (Reuters) Reuters - Soaring crude prices plus worries\\about the economy and the outlook for earnings are expected to\\hang over the stock market next week during the depth of the\\summer doldrums.",
  'Iraq Halts Oil Exports from Main Southern Pipeline (Reuters) Reuters - Authorities have halted oil export\\flows from the main pipeline in southern Iraq after\\intelligence showed a rebel militia could strike\\infrastructure, an oil official said on Saturday.',
  'Oil prices soar to all-time record, posing new menace to US economy (AFP) AFP - Tearaway world oil prices, toppling records and straining wallets, present a new economic menace barely three months before the US presidential elections.'],
 'label': [2, 2, 2]}

Per informazione, le etichette di classificazione possibili sono quattro: 0 per la categoria World, 1 per Sports, 2 per Business e 3 per Sci/Tech.

Notiamo tuttavia come è strutturato in maniera “colonnare” il risultato con una lista di notizie per la voce text e una sequenza di etichette alla voce label.

Infine, prima di passare alla creazione di nostri dataset, ricordiamo che è anche possibile caricare solo uno split specifico del dataset indicandolo nell’invocazione di load_dataset:

dataset_train = load_dataset("ag_news", split="train")

ottenendo in questo caso direttamente un oggetto Dataset.

Creare i propri dataset su Hugging Face

Tutto quello che abbiamo imparato sinora sulla struttura dei dataset di HuggingFace torna utile anche nella volontà di creare i propri da utilizzare nell’istruzione dei modelli.

Si possono creare dataset in vari modi:

  • partendo da cartelle di file, utilizzabile soprattutto per audio e immagini, passando per i builder ImageFolder e AudioFolder;
  • partendo da un dizionario Python utilizzando il metodo from_dict della classe Dataset;
  • utilizzando un generatore, costrutto Python che produce uno streaming di dati.

I nostri dataset potranno essere frutto di archivi scaricati, di elaborazioni eseguite o di creazioni di dati di test. Il vantaggio che offre HuggingFace è che questi, indipendentemente dalla loro origine, saranno gestiti dalle medesime strutture dati (quelle viste sinora) permettendoci di uniformare i processi di calcolo o mettendoli a disposizione degli LLM.

Se volessimo, ad esempio, produrre un dataset a partire da un dizionario Python dovremmo fornire questa struttura mediante il metodo from_dict fornendo i dati di ogni singola feature sotto forma di lista dove la chiave associata sarebbe proprio il nome della feature.

Ad esempio, volessimo creare un set di dati in grado di includere un elenco di studenti e, per ognuno di essi, il voto conseguito in un esame potremmo fare così:


>> from datasets import Dataset
>> ds = Dataset.from_dict(
      {"alunno": ["Mario Rossi", "Giovanni Verdi", "Enzo Neri", "Silvia Bianchi", "Alessia Marroni"], 
       "voto": [5,9,7,8,9]}
      )
>> ds
Dataset({
    features: ['alunno', 'voto'],
    num_rows: 5
})
>> ds[0]
{'alunno': 'Mario Rossi', 'voto': 5}

Altrimenti, potremmo definire un generatore in grado di produrre dati ogni volta che viene invocato. Un generatore è una sorta di “rubinetto” che emette informazioni ogni qualvolta viene aperto e spesso le reperisce da fonti remote o strutture dati. Nel nostro esempio, il generatore produce fino ad un massimo di 1000 righe in cui vengono generati casualmente nominativi di persone che hanno affrontato una sorta di esame associato con un livello, da 1 a 5, conseguito:


>> from random import choice, randint
>> def gen():
      names=['John', 'Edward', 'Paul', 'Simon', 'Eve', 'Helen', 'Michael', 'George' ]
      surnames=['Smith', 'Johnson', 'Williams', 'White', 'Miller', 'Davis', 'Lopez', 'Martin', 'Robinson', 'Lee' ]
      for i in range(0,1000):
          yield {"student": choice(surnames)+" "+choice(names), "level": randint(1,5)}
    
>> ds = Dataset.from_generator(gen)
>> ds
Dataset({
    features: ['student', 'level'],
    num_rows: 1000
})
>> ds[0:5]
{'student': ['Lee Simon',
  'Robinson Edward',
  'White George',
  'Robinson John',
  'Smith Simon'],
 'level': [3, 2, 1, 5, 4]}

Cogliamo l’occasione anche per mostrare come poter creare gli split train/test, usando la funzione train_test_split con una proporzione impostata da noi (in questo caso specifico, il 30% dei dati sarà dedicato al test):


>> ds_split=ds.train_test_split(test_size=0.3)
>> ds_split
4]
0 s
ds_split # datasets.dataset_dict.DatasetDict type
DatasetDict({
    train: Dataset({
        features: ['student', 'level'],
        num_rows: 700
    })
    test: Dataset({
        features: ['student', 'level'],
        num_rows: 300
    })
})

Infine, ricordiamo che un dataset da noi prodotto potrà essere condiviso mediante HuggingFace seguendo le istruzioni di caricamento descritte nell’apposita pagina della documentazione ufficiale.

Conclusioni

Imparare a gestire e creare dataset con HuggingFace è un passaggio fondamentale nell’approccio all’Intelligenza Artificiale. In questo modo, potremo mettere a disposizione dei modelli i nostri documenti e sfruttare un vastissimo bacino di dataset offerti dalla community. E’ fondamentale prendere confidenza con la loro architettura e le possibilità di preprocessamento per la loro preparazione all’elaborazione ma soprattutto tenere sempre presente che è basilare consultare bene la documentazione di modelli e dataset per fare in modo di impiegare gli uni e gli altri in maniera coerente alla loro struttura.

(Immagine di apertura generata con la IA, Stable Diffusion)