In questo articolo presenterò dei libri che ho trovato ottimi per iniziare o migliorare nel mondo del machine learning. Come ben si sa non è facile iniziare in questo campo da 0 senza avere una conoscenza della matematica e in parte degli argomenti propri del settore, con questa lista spero di aiutarvi a iniziare in questo mondo. Partiamo con la lista!


Machine Learning for Hackers

★★★★★



Questo libro tratta le tematiche di machine learning con molta semplicità, prima viene esposta la teoria e poi viene corredato l’esempio con Python o R. Gli esempi sono semplici e molto bene commentati, non è difficile seguire l’ordine degli argomenti proposti dal libro. I capitoli che lo compongono si basano su argomenti sia teorici che pratici e si va dai problemi di classificazione a metodi di visualizzazione.
Il libro è molto basilare ed è adatto a un programmatore che inizialmente vuole approcciarsi a questo mondo e avere una infarinatura senza finire in pura teoria. Gli esempi sono corredati da codice su Github e vengono offerti alcuni spunti per sistemi in produzione che utilizzano questi algoritmi.


Hands-On Machine Learning With Scikit-Learn and Tensorflow: Concepts, Tools, and Techniques to Build Intelligent Systems

★★★★★



Questo libro è molto più pratico del precedente. Esso illustra gli algoritmi più importanti e copre sia la parte di machine learning classica, che di Deep learning. I modelli di machine learning trattati sono: Classificatori con Stocastic Gradient Descent, Regressione Semplice e regressione Logistica, Support Vector Machine, Decision Tree, Ensamble, Random Forest, Riduzione delle dimensionalità e infine reti neurali feed forward e recurrent utilizzando Tensorflow. Il libro usa Python come linguaggio di riferimento, è molto chiaro e semplice comprendendo tutti i punti che sono richiesti in un progetto vero e proprio come l’ottimizzazione dei modelli e la loro valutazione.
Il libro è più avanzato del precedente, espone gli algoritmi piu’ richiesti in questo campo e la loro applicazione. Lo consiglio per chi vuole avere una panoramica generale.


Pattern Recognition And Machine Learning

★★★★★



Questo libro è un vero classico del Machine Learning, all’interno è contenuta tutta la teoria matematica sottostante a ogni modello ed algoritmo più usato. L’approccio dell’autore è puramente probabilistico basato sulla teoria bayesiana.
Viene trattato il tutto partendo dalle basi: si inizia dalla teoria della probabilità, poi la teoria dell’informazione e la statistica per arrivare ai modelli lineari, alle reti neurali e ai metodi kernel. Purtroppo questo libro manca di applicazioni a livello di codice rimanendo di teoria, tuttavia è una base fondamentale per chiunque voglia approndire queste tematiche.
Il sito web del libro è disponibile a questo link: https://goo.gl/uFpD7i


Machine Learning: An Algorithmic Perspective, Second Edition

★★★★★



In questo volume vengono trattati elementi di machine learning e data mining dal punto di vista algoritmico e pratico. È molto più diretto del libro di Bishop e più complesso dei primi due in quanto tratta sì le formule matematiche nello specifico, ma non va a spiegarne ogni punto ed è secondo me molto utile per completare la conoscenza della teoria con gli algoritmi.
È un libro consigliato, la qualità dell’esposizione del libro rimane alta ed è per questo molto utile per integrare le conoscenze.

In questo articolo spiegherò come fare una analisi basilare di un dataset da me creato e di come applicare il modello di Decision Tree con Python. Ho utilizzato Scikit e Pandas per la parte di analisi dati e dataframe, per la parte relativa ai grafici ho scoperto ultimamente questa libreria Seaborn che va a sostituire a matplotlib.

Il dataset che prendo in considerazione è stato costruito da me facendo un semplice scraping della pagina di ogni regione di Booking.com . Dopo avere effettuato lo scraping, fatto a mano dal sito, ho preso ogni singola città e ne ho categorizzato un tipo possibile di turismo.
Esso è costituito da: Nome città, regione, Hotel, Latitudine, Longitudine, tipo di turismo
Nella pratica considero tutte le regioni italiane e elenco le prime 25 città per numero di hotel. Il dataset è disponibile in fondo al blogpost.

L’analisi che farò sarà quella di creare dei Decision Tree sul dataset in base al tipo di turismo, usando più o meno diverse feature in tre casi differenti e vedendo quale dei tre casi funziona meglio. Infine userò le funzioni interne delle librerie di scikit per creare la grafica.
Ecco il gist contenente tutti procedimenti commentati passo passo.



Loading

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Ecco ora il dataset che ho usato per la mia analisi dei dati. Lo potete usare sotto licenza GPL v2

I Decision Tree o Alberi Decisionali sono uno strumento di apprendimento supervisionato, essi risolvono principalmente tematiche di classificazione o regressione. Sono molto facili da interpretare e da applicare, non basandosi su un modello lineare sono capaci di apprendere anche associazioni non lineari. Funzionano sia su dati numerici che categorici.

I Decision Tree si categorizzano rispetto alla variabile in output come:

  • Categorical Decision Tree
  • Continuous Decision Tree

Nella figura seguente si vede un esempio di Decision Tree sul dataset di Iris.

  • Il nodo principale si chiama Root Node o Radice
  • Quando un nodo porta a una divisione in rami nei sottonodi l’operazione si chiama Splitting
  • Quando un nodo si divide in più sottonodi senza arrivare a quello finale, i sottonodi si chiamano Decision Node o Nodo di decisione
  • Una intera sezione dell’albero si chiama Branch o Ramo

Vantaggi dei Decision Tree

  • Sono facili da capire: Siccome essi offrono una rappresentazione grafica molto facile da interpretare, i Decision Tree sono anche interpretabili dalla gente non propria del campo informatico.
  • Utili nell’analisi del dataset: Siccome sono un algoritmo molto veloce e semplice da applicare, è utile applicarlo per vedere le relazioni fra le variabili

Svantaggi dei Decision Tree

  • Sono proni all’overfitting: È il problema più frequente con i Decision Tree, il metodo migliore per risolverlo è settare dei vincoli o fare Pruning dei rami

Divisione in rami

Il criterio secondo il quale l’algoritmo divide in più rami i vari nodi dell’albero è critico per la precisione deloritmo. Esso poi è differente nel caso si scelga un ambito di regressione o un ambito di classificazione. In base ai criteri disponibili abbiamo i seguenti approcci:

  • Gini Index (o indice Gini): L’indice Gini dice calcola se gli elementi considerati fanno parte della stessa popolazione. Se la popolazione è pura essi devono essere della stessa classe e la probabilità associata a questo evento è 1 (cioè non contiene elementi di classe diverse)
  • Chi-Square (o Chi quadrato): L’algoritmo Chi Square si preoccupa di trovare una significanza statistica fra i sotto nodi e il nodo padre. Questa la misuriamo sommando il quadrato delle differenze standardizzate fra i valori osservati e i valori che ci aspettiamo
  • Information Gain: L’algoritmo Information Gain si basa sul principio di entropia di un insieme di dati, e usa questo per calcolare la divisione in rami.
  • Riduzione della varianza: Questo algoritmo utilizza la formula della varianza per decidere il migliore modo per dividere i rami, minore è la varianza maggiore è la probabilità che quell’attributo venga usato per dividere i rami

Overfitting

Il problema principale dei Decision Tree è dovuto all’overfitting. Essi arriveranno a fare tante osservazioni, fino ad arrivare a fare una foglia per ogni osservazione e raggiungere quindi il 100% di precisione. Per ridurre questo problema ci sono i seguenti approcci:

  • Si settano dei vincoli sul numero massimo di foglie o sul numero massimo di nodi finali o sul numero massimo di attributi su cui creare nuove foglie. Allo stesso modo anche sul numero minimo di foglie e sul numero minimo di elementi su cui creare nuove foglie
  • Si fa il Pruning dei rami, cioè si eliminano i rami possibili, attraverso un approccio greedy. Nella pratica si sceglie la strada che attualmente sembra la migliore e si prosegue con quella finchè non se ne trovano di migliori

Quando usiamo un Decision Tree?

Dovremmo usare un Decision Tree quando c’è una relazione complessa fra i vari attributi che è difficile da spiegare, in questo modo l’approccio non lineare del Decision Tree batte l’approccio lineare della Regressione Lineare ad Esempio. Inoltre un Decision Tree è molto utile se dobbiamo spiegare come si effettua una classificazione ai non addetti ai lavori.


Nel prossimo articolo includerò degli esempi in Python con la libreria Scikit per mostrare una implementazione dei Decision Tree su un dataset di Regioni città e Attrazioni.

Un modo molto semplice di implementare lo Stochastic Gradient Descent nei nostri script è quello di usare l’implementazione di Scikit. La classe SGDClassifier implementa un classificatore che utilizza lo Stochastic Gradient Descent per  classificare valori. Il mio script in python applica questo classificatore sul MNIST database, un dataset contente dei numeri scritti a mano e utilizzati come dataset d’esempio nei problemi di classificazione.

Ho utilizzato questo semplice script inoltre per gareggiare su Kaggle alla competizione https://www.kaggle.com/c/digit-recognizer, dove si chiede di scrivere un classificatore di cifre e fare il submit del punteggio.

Il libro che sto attualmente seguendo e che mi ha permesso di scrivere questo codice è:Hands-On Machine Learning With Scikit-Learn and Tensorflow: Concepts, Tools, and Techniques to Build Intelligent Systems

Questo libro offre una buona introduzione pratica agli algoritmi di machine learning, approfondisce via via nei capitoli tematiche sempre più complesse rimanendo ancorati al codice pur avendo una piccola disgressione matematica.

 

 

 

Di seguito l’esempio del mio Notebook Jupyter dove viene implementato questo approccio.



Loading

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

In questo articolo spiegherò come creare degli oggetti Clusterizzati in Python usando la libreria SciPy.
SciPy mette a disposizione per noi un sacco di metodi per il Clustering, noi utilizzeremo quelli che abbiamo visto nell’articolo precedente.

Il seguente file IPython fa vedere perfettamente la procedura con un metodo di collegamento fra i cluster di tipo singolo basato sulla distanza euclidea.

Il file ha i commenti che indicano ogni singola operazione avvenuta.



Loading

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

L’analisi del cluster è un processo in cui si raggruppano insiemi di oggetti in modale tale da creare insiemi di oggetti i cui elementi siano piu’ simili fra loro rispetto agli elementi negli altri gruppi. I gruppi vengono detti Cluster

Schermata del 2017-08-27 12-59-16

Il clustering è una tecnica base del data mining e viene usata in altri ambiti molto simili (Machine learning, pattern recognition, analisi immagini,ecc)

Per Clustering Gerarchico (Hierarchical Clustering) si intende un metodo di analisi dei cluster che vuole ottenere gerarchie di cluster. Si può effettuare in due modi:

  • Agglomerativo: Si parte dai dati singoli e si uniscono via via i cluster
  • Divisorio: Si parte da un unico cluster e si dividono via via

Per capire quale cluster va diviso o unito si fa riferimento alla metrica, cioè una misura di similarità o dissimilarità, la metrica viene usata come distanza fra le coppie.

Le metriche (Metric) sono influenzate dal tipo di dati che abbiamo, esse possono essere:

  • Distanza Euclidea
  • Distanza Euclidea Quadra
  • Distanza di Manhattan
  • Distanza Massima
  • Distanza di Mahalanobis
  • Distanza di Hamming
  • Distanza di Levenshtein

Per capire invece quando degli insiemi vanno uniti si usano i Criteri di Collegamento (Linkage Criteria). I criteri di collegamento vengono effettuati basandosi sulle distanze fra gli elementi dei cluster e fanno capire se occorre unire i cluster o meno. I criteri sono:

  • Collegamento Singolo (Single Linkage): la distanza di collegamento fra due cluster viene definita come la distanza minima fra i due elementi del cluster

Schermata del 2017-08-27 12-51-59

  • Collegamento completo (Complete Linkage): la distanza di collegamento fra due cluster viene definita come la distanza massima fra i due elementi del cluster

Schermata del 2017-08-27 12-52-06

  • Collegamento Medio (Average Linkage): la distanza di collegamento fra due cluster viene definita come la distanza media fra tutti i punti del cluster con tutti gli altri punti del cluster

Schermata del 2017-08-27 12-59-02

Nel prossimo articolo illustrerò un esempio di clustering gerarchico con codice in Python!

In questo articolo esporrò lo pseudo codice dell’algoritmo A-Priori e una versione funzionante in Go.

L’algoritmo A-Priori si può riassumere nel seguente modo:

Ammettiamo di avere questo Dataset

Transazioni Cestino
1 {“Mela”,”Lampone”,”Ananas”}
2 {“Mela”,”Kiwi”,”Ananas”}
3 {“Lampone”,”Ananas”}
4 {“Banana”,”Kiwi”,”Ananas”}
5 {“Kiwi”}
6 {“Mela”,”Kiwi”}

Primo passaggio di Apriori

Creiamo un insieme contenente tutti i nostri elementi singoli presi dal Dataset e creiamo una mappa contenete la frequenza dei nostri oggetti

Set = {“Mela”,”Kiwi”,”Ananas”,”Lampone”,”Banana”}
Frequency Set = {“Mela”: 3, “Kiwi”,4 “Ananas”: 4, “Lampone”: 1, “Banana”: 1}

Ammettiamo di avere settato un valore di supporto parti a 0.2, eliminiamo ora tutti gli elementi che nel Frequency Set non hanno supporto pari a 0.2. Otteniamo quindi:

Set = {“Mela”,”Kiwi”,”Ananas”,”Lampone”}
Frequency Set = {“Mela”: 3, “Kiwi”,4 “Ananas”: 4}

Secondo passaggio di A-Priori

Creiamo i nuovi insieme di candidati dal Set precedente a 2 a 2, cioè
Set = {“Mela-Kiwi”,”Mela-Ananas”,”Kiwi-Ananas”}

Questo è il nostro nuovo set di candidati che sottoporremo alla regola del supporto.

Terzo passaggio di A-Priori

Verifichiamo se il set di candidati ha nuovi elementi frequenti:
Set = {“Mela-Kiwi”,”Mela-Ananas”,”Kiwi-Ananas”}
Frequency Set = {“Mela-Kiwi”: 2 ,”Mela-Ananas”: 2,”Kiwi-Ananas”:2}

Quarto passaggio di A-Priori

Creiamo i nuovi set di candidati dal Set precedente a 3 a 3, cioè
Set = {“Mela-Kiwi-Ananas”}

Quinto passaggio di A-Priori:

Set = {“Mela-Kiwi-Ananas”}
Frequency Set = {“Mela-Kiwi-Ananas”: 1}

L’insieme di elementi piu’ frequente generato da A-Priori è “Mela-Kiwi-Ananas”, e ogni sotto insieme generato da questo è a sua volta il più frequente.

Si deduce da questo processo quindi il seguente algoritmo:


L[1] = Genera gli elementi di dimensione 1
Calcola la frequenza degli elementi di dimensione 1 che superano minsup
C[1] = L[1]
Per K=2, finchè L[K-1] != vuoto; k++ {
C[K] = genera le coppie da L[K-1] con cardinalità K
L[K] = Calcola la frequenza degli elementi in C[K] che superano minsup
}
Ritorna L[K]

view raw

gistfile1.txt

hosted with ❤ by GitHub

Il pattern con cui ci muoviamo è quindi questo:
Schermata del 2017-08-22 19-28-54

Prima filtriamo i dati e poi costruiamo i dati successivi finchè non otteniamo il nostro risultato finale.

Ho scritto un codice in Go che ci permette di applicare A-Priori come specificato dal nostro algoritmo.


package main
import (
"github.com/deckarep/golang-set"
"strings"
"fmt"
"sort"
)
func main() {
dataset := [][]string{
[]string{"pane", "cereali", },
[]string{"pane", "latte", "caffè"},
[]string{"latte", "cereali"},
[]string{"latte", "caffè"},
[]string{"pane", "latte", "cereali"},
[]string{"latte", "cereali"},
[]string{"pane", "cereali"},
[]string{"pane", "latte", "te"},
[]string{"pane", "latte", "cereali", "te"},
[]string{"pane", "te"},
[]string{"pane","caffè"},
}
minimumSupport := 0.4
minConfidence := 0.2
apriori(dataset, minimumSupport, minConfidence)
}
func apriori(dataset [][]string, support float64, confidence float64) {
elements := elements(dataset)
freqSet := make(map[string]float64)
largeSet := make(map[int]mapset.Set)
oneCSet := returnItemsWithMinSupport(elements, dataset, support, &freqSet)
currentLSet := oneCSet
k := 2
for currentLSet.Cardinality() != 0 {
largeSet[k-1] = currentLSet
currentLSet = joinSet(currentLSet, k)
currentCSet := returnItemsWithMinSupport(currentLSet, dataset, support, &freqSet)
currentLSet = currentCSet
k = k + 1
}
fmt.Println(largeSet)
}
func returnItemsWithMinSupport(itemSet mapset.Set, dataset [][]string, minSupport float64, freqSet *map[string]float64) mapset.Set {
localItemSet := mapset.NewSet()
localSet := make(map[string]float64)
for _, item := range (itemSet.ToSlice()) {
dkey := strings.Split(item.(string), "-")
sort.Strings(dkey)
for _, line := range(dataset) {
if contains(line, dkey) {
key := strings.Join(dkey, "-")
(*freqSet)[key] += 1.0
localSet[key] += 1.0
}
}
}
for item, count := range (localSet) {
support := count / float64(len(dataset))
if support >= minSupport {
localItemSet.Add(item)
}
}
return localItemSet
}
func joinSet(itemSet mapset.Set, length int) mapset.Set {
ret := mapset.NewSet()
for _, i := range (itemSet.ToSlice()) {
for _, j := range (itemSet.ToSlice()) {
i := i.(string)
j := j.(string)
i_a := strings.Split(i, "-")
j_a := strings.Split(j, "-")
dkey := (union(i_a, j_a))
if len(dkey) == length {
sort.Strings(dkey)
key := strings.Join(dkey, "-")
ret.Add(key)
}
}
}
return ret
}
func union(a []string, b []string) []string {
ret := mapset.NewSet()
for _, v := range (a) {
ret.Add(v)
}
for _, v := range (b) {
ret.Add(v)
}
rets := []string{}
for _, v := range (ret.ToSlice()) {
rets = append(rets, v.(string))
}
return rets
}
func elements(dataset [][]string) mapset.Set {
ret := mapset.NewSet()
for i := 0; i < len(dataset); i++ {
for j := 0; j < len(dataset[i]); j++ {
if ret.Contains(dataset[i][j]) == false {
ret.Add(dataset[i][j])
}
}
}
return ret
}
func contains_dataset(s [][]string, e []string) bool {
ret := false
for _, v := range (s) {
ret = contains(v, e)
if ret == true {
break
}
}
return ret
}
func contains_element(s []string, e string) bool {
ret := false
for _, a := range s {
if a == e {
ret = true
break
}
}
return ret
}
func contains(s []string, e []string) bool {
count := 0
if len(s) < len(e) {
return false
}
mm := make(map[string]bool)
for _, a := range e {
mm[a] = true
}
for _, a := range s {
if _, ok := mm[a]; ok {
count += 1
}
}
return count == len(e)
}

view raw

Apriori.go

hosted with ❤ by GitHub

Limitazioni di A-Priori

  • È molto esoso dal punto di vista della computazione. Seppure riducendo il numero di candidati da considerare, il numero di questi è sempre molto grande quando il numero di elementi nei cestini della gente è alto o quando il valore limite di supporto è basso.
  • Associazioni False. Riducendo il valore limite di supporto per notare alcuni tipi di associazioni, può succede che ci siano delle associazioni non giuste e quindi false. Per ridurre questo problema occorre filtrare prima il Dataset o verificare il valore di supporto e confidenza in un Test Set separato.

Conclusioni

A-Priori si dimostra essere un algoritmo molto interessate per studiare le associazioni all’interno di un Dataset con transazioni. Nonostante abbia delle limitazioni ci sono stati degli algoritmi che lo hanno migliorato come ad esempio l’algoritmo PCY o l’algoritmo Multistage.

L’algoritmo A-Priori ha un semplice obiettivo, trovare oggetti comprati assieme dentro dei carrelli, cioè trovare le regole di associazione degli elementi all’interno di un insieme di dati.

Il nome A-Priori deriva da come l’algoritmo opera, cioè senza avere nessuna conoscenza effettiva dei dati, ma lavorando sull’intuizione delle associazioni fra gli elementi.

Quando un cliente compra in un supermercato ha di solito una lista delle cose che vuole comprare. Ogni cliente ha bisogni diversi, dalla casalinga al lavoratore single, ma dietro questi clienti ci sono pattern di oggetti comprati spesso assieme. Scoprire questi pattern è molto utile in quanto permette di fare promozioni questi oggetti quando sono comprati assieme.

Un problema che tratta le regole di associazione di elementi si basa su un Dataset formato in questo modo:

Transazioni Cestino
1 {“Mela”,”Lampone”,”Ananas”}
2 {“Mela”,”Kiwi”,”Ananas”}
3 {“Lampone”,”Ananas”}
4 {“Banana”,”Kiwi”,”Ananas”}
5 {“Kiwi”}
6 {“Mela”,”Kiwi”}

Abbiamo cioè delle transazioni con carrelli di frutta comprati assieme, dobbiamo trovare quindi quando un elemento è stato comprato assieme ad un altro.
L’algoritmo utilizza un approccio bottom-up, parte cioè dai singoli elementi per arrivare poi a creare insiemi di oggetti.

L’effettività dell’algoritmo A-Priori si basa sul fatto che l’insieme degli oggetti  frequenti derivato dall’insieme di tutti gli oggetti è monotono.

Per monotono si intende questo:

Se per il nostro Dataset, l’insieme di elementi {“A”,”B”,”C”} è il più frequente (cioè l’insieme di elementi che compare più spesso), allora i sottoinsiemi di questo (cioè {“A”,”B”},{“B”,”C”},{“A”,”C”},{“A”},{“B”},{“C”}) saranno a loro volta fra i più frequenti.

Trovando cioè la transizione più lunga più frequente, sappiamo automaticamente che tutti i sottoinsieme derivati da questa sono frequenti!

Un altro vantaggio dell’algoritmo A-Priori è che riduce la memoria necessaria a individuare gli insiemi di oggetti associati. Senza l’algoritmo A-Priori dovremmo enumerare tutti i gli insiemi possibili di oggetti a 2 a 2, a 3 a 3, a 4 a 4 e trovare poi i più frequenti nel nostro Dataset.

Le regole associative dei nostri dati si basano sugli attributi che legano i valori delle transazioni. Le regole associative sono nella forma A=>B,  che si legge come: Le transazioni in cui compare A compare anche B.

Le regole associative sono soddisfatte quando superano due valori statistici: Il supporto e la confidenza.

Per supporto intendo la percentuale di transazioni che contengono sia A che B sul totale. Per confidenza invece intendo la percentuale percentuale di transazioni che contengono sia A che B sul numero di transazioni che contengono A.

Nel nostro caso precedente se prendiamo il dataset della frutta troviamo che:

Supporto(Mela,Kiwi) = 2/6
Confidenza(Mela,Kiwi) = 2/2

Abbiamo cosi’ notato che la confidenza che Mela e Kiwi siano comprate assieme è molto alta mentre il numero valore di supporto è basso.

Formalmente quindi l’algoritmo A-Priori si basa sull’estrarre regole di associazione in un dato Dataset con un supporto superiore al minimo stabilito e/o una confidenza superiore alla minima stabilita.

Le regole che soddisfano questi dati sono dette Regole di associazione forte.

Nel prossimo articolo illustrerò lo pseudo codice e il codice per l’algoritmo A-Priori che utilizza come metrica il supporto.