Nell’articolo precedente abbiamo dato un’occhiata a quali sono i concetti base del machine learning.

Per continuare il nostro viaggio, proviamo a focalizzarci sul primo problema che ogni sistema di apprendimento automatico deve risolvere per poter funzionare correttamente: la scelta del dataset.

Per chi non avesse letto la nostra introduzione al machine learning, un dataset è un insieme di feature (caratteristiche) che descrivono un determinato fenomeno.

Poiché non c’è cosa migliore che “imparare facendo”, di seguito ti propongo un problema reale e una possibile proposta di dataset.

Tutti gli esempi di codice che vedrai in questo e nei prossimi articoli di questa categoria sono pensati e scritti in Python 3.x.

Se non hai ancora installato l’interprete Python ti consiglio vivamente di installare Anaconda, un ambiente di sviluppo che contiene tutto ciò che serve per poter sviluppare i tuoi progetti di machine learning con Python.

Il problema e l’obiettivo

Vogliamo sviluppare un classificatore che sia in grado di apprendere la differenza tra un nome di persona maschile e femminile. Il classificatore, al termine dell’addestramento, dovrà essere in grado di riconoscere autonomamente e con una certa precisione se un nome italiano (Marco, Francesca, Luca,…) è associabile ad un individuo di sesso maschile o femminile.

Ecco che già si intravede l’utilità di un sistema di apprendimento: senza questo tipo di strumenti, un programmatore non potrebbe far altro che studiare manualmente le regole che fan sì che una parola sia riconosciuta come maschile o femminile per poi automatizzarne il riconoscimento.

Nel caso dei nomi di persona, sarebbe ragionevole affermare che nella stragrande maggioranza dei casi i nomi che terminano per ‘a’ sono femminili mentre quelli che terminano per ‘o’ sono maschili.

Il nostro obiettivo nei prossimi articoli sarà quello di capire quanto un classificatore può essere in grado di evincere da solo questa particolarità senza che noi ci si debba preoccupare di definirla esplicitamente in un programma.

Vai col dataset!

Dunque, il primo passo fondamentale, è quello di generare un dataset informativo che descriva il fenomeno che vogliamo apprendere.

Volendo studiare la distribuzione dei nomi di persona maschili e femminili italiani, procediamo con il raccoglierli su due file diversi. In giro sulla rete, si trovano diversi siti dove puoi trovare liste di nomi di persona.

Una volta raggruppati, il risultato sarà più o meno il seguente:

Le feature

Bene, adesso dobbiamo cominciare a pensare a quali feature estrarre.
Questo passo è fondamentale se vogliamo rendere questa lista di nomi “digeribile” da un classificatore. È necessario pensare a quali potrebbero essere le caratteristiche maggiormente informative di un dato.

Nel nostro caso, le caratteristiche che andremo a calcolare saranno le seguenti:

  • lunghezza del nome
  • numero di vocali
  • numero di consonanti
  • vocale di terminazione

Quando si parla di feature, se ne distinguono tipicamente due tipologie:

Ordinal feature

Le caratteristiche ordinali sono quei valori che possono intrinsecamente essere ordinati tra loro. Un esempio classico può essere l’altezza di una persona, o nel caso del nostro dataset la lunghezza di un nome, il numero di vocali e il numero delle consonanti.

Ha senso poter dire che il nome “Alessandra” è più lungo, ha più vocali e più consonanti del nome “Marco”.
Quando una caratteristica risponde a questa regola, prende il nome di ordinale.

È buona norma rappresentare tutte le feature di questo tipo con un numero.
Nel caso del nostro esempio, dobbiamo quindi:

Copy to Clipboard

Se alla variabile name passiamo quindi il valore “Marco”, i valori delle feature calcolate fino ad ora saranno:

Copy to Clipboard

Nominal feature

Le caratteristiche nominali, sono quelle feature che NON possono essere ordinate.
Prendendo il nostro dataset, le feature nominali sono rappresentate dalle vocali di terminazione.

In altre parole, il fatto che “Alessandra” termini con la ‘a’ e Marco con la ‘o’ non ci dà alcuna informazione ordinale sui due nomi. Assegnare dunque un numero a ciascun possibile valore (ad es. ‘a’ = 1, ‘e’= 2, ‘i’ = 3, ‘o’ = 4, ‘u’ = 5) farebbe erroneamente pensare al classificatore che “Alessandra” viene prima di “Marco”.

Per poter codificare questa informazione in modo che il classificatore possa quindi comprenderla dobbiamo ricorrere ad una tecnica chiamata one-hot-encoding.

L’idea dietro a questo approccio è quella di creare una nuova feature per ciascun valore assumibile dalla caratteristica nominale.
Partendo sempre dal nostro esempio e volendo caratterizzare un nome per la sua vocale di terminazione, dovremo creare 5 nuove feature, una per ciascuna vocale possibile (a, e, i , o, u).

Copy to Clipboard

Dato quindi il nome “Erica”, avremo una rappresentazione di questo tipo:

Copy to Clipboard

Scriviamo il dataset!

Abbiamo capito cosa vogliamo calcolare per ciascun nome e come farlo. Adesso è necessario generare il nostro dataset partendo dalle liste di nomi raccolte in precedenza.

Partiamo dunque dal raccogliere in una funzione tutte le istruzioni necessarie a calcolare le feature di un nome:

Copy to Clipboard

La funzione calculate_features, prende in input un nome e ne restituisce le feature sopra discusse.

Passiamo adesso a scrivere una funzione che si occupi di scrivere su un file quello che poi useremo come dataset. Chiameremo la funzione write_features; questa prende in input il path del file da cui recuperare i nomi, il nome del file di destinazione e la rappresentazione numerica dell’etichetta di classe che vogliamo associare ai nomi maschili e a quelli femminili.

Copy to Clipboard

Prima di procedere oltre, vale la pena spendere qualche parola sulla codifica delle etichette di classe. Nel nostro esempio, abbiamo 2 classi possibili: maschile e femminile. Sebbene le librerie di Python che andremo ad usare, spesso convertono internamente le classi in interi, è buona norma farlo da sé.

Nell’esempio che segue, all’interno della funzione generate_dataset assegneremo ad ogni nome ricavato dal file males.txt l’id 1, mentre ai nomi ricavati dal file females.txt l’id -1.

Copy to Clipboard

Al termine del processo, il risultato che otterremo sarà il vero e proprio dataset che potremo usare per addestrare i nostri classificatori: dataset-names.txt.

Come puoi vedere, la prima riga del dataset specifica il significato delle colonne:

Copy to Clipboard

mentre nelle restanti righe abbiamo i veri e propri dati, ad esempio:

Copy to Clipboard

Puoi trovare il codice per generare il dataset che abbiamo appena visto in questo repository.

Per eseguirlo, una volta installato Python 3 nel tuo PC, apri un prompt dei comandi nella stessa cartella in cui hai scaricato il progetto e lancia il seguente comando:

Copy to Clipboard

Se ti interessa approfondire la tua conoscenza sul machine learning e i concetti che trattiamo in questi articoli ti consiglio vivamente di dare un’occhiata a questo libro di Sebastian Raschka:

Nei 16 capitoli avrai modo, tra i tanti argomenti, di approfondire alcune tecniche per estrarre dai dati i migliori modelli predittivi possibili.
In particolare nel capitolo 4 Building Good Training Sets, potrai apprendere le best practice per processare al meglio i dati che hai recuperato.

Divide et impera!

Adesso che abbiamo ottenuto il nostro dataset, è bene terminare facendo alcune valutazioni utili per i prossimi articoli. Per ottenere i migliori risultati possibili è buona norma dividere sempre il nostro dataset in 2 differenti gruppi:

  • Training set: è la porzione di dati che verrà utilizzata per addestrare il classificatore scelto.
  • Test set: la restante parte dei dati utilizzata per provare la bontà del modello addestrato.

Per poter dividere i dati in questi due gruppi, Python ci viene in aiuto con la libreria sklearn.

Copy to Clipboard

La libreria sklearn fornisce una miriade di funzioni pronte all’uso per semplificare i nostri sviluppi.
In questo caso abbiamo utilizzato la funzione train_test_split che prendendo in ingresso una serie di parametri, restituisce il dataset già diviso nei 2 gruppi:

  • X_train: contiene il 70% dei dati contenuti nel dataset a meno dell’ultima colonna (quella contenente l’etichetta della classe)
  • y_train: contiene le etichette di classe dei dati contenuti nella lista precedente.
  • X_test: come X_train ma contiene il restante 30% dei dati.
  • y_test: come y_train ma contiene le etichette di classe del restante 30%.

Per un approfondimento sui parametri accettati da questa funzione ti rimando alla pagina della guida ufficiale.

Conclusioni

In questo articolo abbiamo visto un esempio di generazione di dataset per classificatori supervisionati. L’obbiettivo che ci siamo posti è sviluppare un classificatore in grado di riconoscere nomi di persona maschili e femminili italiani, su questa base dopo aver raccolto e diviso un set abbastanza grande di nomi possibili abbiamo calcolato le caratteristiche che ci sono sembrate più informative per “insegnare” ad un classificatore la distinzione corretta.