IBM Cognitive – Milano – 26 Novembre 2018

Lorenzo Laderchi – IBM Cognitive Specialist (IBM Power AI)

“Per un grande dataset non ci vuole una piattaforma grande, ma una grande piattaforma”
Quando si ha a che fare con grandi quantitativi di dati (in termini di numerosità e dimensione dei campioni) spesso si incontrano limitazioni hardware che possono rendere difficile – se non impossibile – il raggiungimento delle accuratezze volute in tempi accettabili. In questa sessione analizzeremo nel dettaglio alcune librerie sviluppate da IBM e integrabili nei framework di DL più diffusi che rendono possibile lo scaling del training su più server e che permettono di aggirare le limitazioni dovute alla ridotta capacità di memoria presente a bordo delle GPU

 

 

ALGEBRA LINEARE PER TUTTI [Parte 2]

 

Autore: Paolo Caressa

 

 

Punti, vettori e loro algebra

Abbiamo fin qui parlato di punti come la totalità dei punti del piano che si identificano in modo biunivoco con la totalità di tutte le coppie possibili di numeri reali.

Per quanto riguarda il machine Learning, siamo interessati a insiemi finiti di punti, in quanto vogliamo rappresentare particolari oggetti da classificare o fenomeni da correlare come punti in uno spazio cartesiano. Per trovare una regolarità o un “pattern” in questi punti, spesso ci interessa considerare rette, piani oltre che di figure geometriche più complesse come le coniche nel piano o le quadriche nello spazio.

Abbiamo visto prima, con un esempio, che alcune figure geometriche possono essere rappresentate da singole o da sistemi di equazioni.

Limitiamoci al caso delle rette e dei piani per capire in generale come li si rappresenta, dato che spesso queste rappresentazioni sono utilizzate nel machine learning.

 

Una retta è intuitivamente un insieme allineato di punti: uno degli assiomi di Euclide afferma che per due punti passa una sola retta, cioè che per determinare univocamente una retta basta indicarne due punti distinti.

Per capire come questo si possa fare nel piano cartesiano, consideriamo due punti distinti P e Q, di coordinate P=(x1,y1) e Q=(x2,y2).

Definiamo il vettore che va da P a Q come la coppia di numeri

Cattura 6

Cioè un vettore è per definizione una differenza di punti, dove con il termine differenza intendiamo la differenza fra le componenti che hanno lo stesso indice.

Dal punto di vista informatico possiamo pensarla in questo modo: un punto è un array (o lista) con due elementi che sono numeri. Normalmente un array A = [x,y] si indicizza a partire da 0, cioè A[0] = x e A[1] = y. Allora, se A e B sono array, il vettore \overline{AB}  si può rappresentare con l’array [B[0] – A[0], B[1] – A[1]].

Resta da capire perché chiamiamo “vettore” una differenza di punti: perché è un oggetto dotato di direzione, verso e intensità, come i vettori della fisica.

La direzione è semplicemente la retta che passa per P e Q; il verso è quello che va da P verso Q (il vettore opposto sarebbe \overline{PQ}  che va da Q verso P: quali sono le sue componenti?)

 

L’intensità è la distanza fra i punti P e Q. Pertanto un vettore determina una retta, e su di essa una direzione: naturalmente esistono infiniti vettori che determinano la stessa retta.

Per esempio la retta che passa per i punti  P= (0,1)  e Q= (0,-2)  è individuata dal vettore \overline{PQ}= (-2,2)   , ma anche dal vettore (-1,1)  che ha stessa direzione e verso ma intensità diversa.

In generale, per ogni numero  (-a,a) a non nullo  rappresenta questa retta. Diciamo che due vettori sono paralleli se sono l’uno multiplo dell’altro per un fattore di scala non nullo.

In effetti, un altro modo di determinare una retta è individuarne un punto P  e un vettore \overline{v}  in modo da esprimere tutti i punti della retta come traslati per i vettori paralleli a \overline{v}

 X= P+ \overline{av}

Questa si chiama l’equazione parametrica della retta, perché esprime il generico punto  della retta per mezzo di un parametro  che varia in tutti i numeri reali.

Si noti che abbiamo usato l’operazione di somma fra un punto e un vettore, definita in modo ovvio: se  P= (X,Y)  e  v= \overline{RS} allora   P + \overline{RS} \left ( x+w-a,y+z -b\right )  , supponendo che le coordinate dei punti R e  S siano  R=(a,b)  e S=(w,z) . Cioè sommiamo le coordinate del punto e le componenti del vettore per ottenere un nuovo punto.

Quando sommiamo un vettore a un punto diciamo anche che applichiamo il vettore in quel punto.

 

I vettori, oltre a consentire di definire le rette ed in dimensioni più alte piani ed iperpiani possiamo notare che:

  1. I vettori possono essere sommati e fra loro ottenendo altri vettori:

\overline{PQ} + \overline{RS}= Q-P+S-R

Notiamo che allora  \overline{PQ} + \overline{QP}=0  (il vettore nullo) e  \overline{QP} è il vettore opposto  \overline{PQ}.

Inoltre, questa operazione di somma soddisfa le usuali proprietà commutativa e associativa

2. I vettori possono essere moltiplicati per un numero ottenendo altri vettori: Inoltre questa operazione di prodotto soddisfa le usuali proprietà commutativa, associativa e distributiva.In particolare il vettore nullo  \overline{0} è ottenuto moltiplicando per zero ed il vettore  opposto di un vettore dato  \overline{v}, cioè quel vettore  \overline{w} tale che  \overline{v} + \overline{w}=0  , è ottenuto   \overline{v}  moltiplicando per  -1.

Inoltre questa operazione di prodotto soddisfa le usuali proprietà commutativa, associativa e distributiva.

 

Con il termine spazio vettoriale si intende un insieme di elementi con due operazioni, di somma fra gli elementi e di moltiplicazione di un elemento per un numero, che soddisfi le stesse proprietà delle operazioni fra i vettori del piano.

vettore in un piano

Figura 3. Il vettore ottenuto come somma  di due vettori del piano si costruisce prendendo la diagonale del  parallelogramma individuato dai due vettori se applicati in uno stesso punto.

In realtà, nel machine learning si utilizzano invariabilmente spazi vettoriali di dimensione finita i cui vettori sono espressi in coordinate, e in questo caso i vettori sono differenze di punti proprio come nell’esempio che stiamo discutendo.

In effetti un elemento di confusione per i principianti è che sia i punti che i vettori dello spazio cartesiano sono rappresentati come coppie di numeri.

Vogliamo ricordare che, anche se si rappresentano allo stesso modo, sono oggetti concettualmente distinti. Un punto individua un singolo punto dello spazio, un vettore individua uno spostamento lungo una direzione, verso e di una certa lunghezza dove si può far corrispondere un punto a un vettore e viceversa nel modo seguente:

al punto P possiamo far corrispondere il vettore   \overline{OP}  che va dall’origine del piano cartesiano al punto P; se invece  \overline{v}   è un vettore, possiamo associargli il punto   0+\overline{v} .

 

Questa distinzione fra punti e vettori è spesso trascurata ma è importante intanto per distinguere concetti apparentemente identici, e poi perché aiuta a interpretare le applicazioni della teoria: per esempio nella seguente figura sono rappresentate, secondo un certo algoritmo di machine learning, alcune parole di un corpus di documenti in inglese con i punti del piano cartesiano:

 

italy

 

Come si vede i punti rappresentano parole.

I vettori cosa rappresentano in questo caso?

Prendiamo il vettore che va da “pizza” a “italy” e applichiamolo a “sushi”: otteniamo indicativamente “japan”. Ne deduciamo che quel vettore rappresenta la risposta a una domanda, cioè “dato un piatto tipico qual è la sua nazione?”.

In questo caso è chiaro che punti e vettori rappresentano concetti distinti.

 

Lunghezza e angolo fra vettori, similarità del coseno

Per definire la lunghezza di un vettore introduciamo una notazione molto usata: il prodotto scalare (o dot-product).

Sia  \overline{v} =(v_1,v_2) e  \overline{v} = (v_1,w_2) diciamo che la il prodotto scalare di   \overline{v}  e  \overline{w}  è il numero:

ecuacion 1

 

ottenuto come somma dei prodotti delle componenti di pari indice dei due vettori.

In particolare, il prodotto scalare di un vettore per se stesso è, per definizione, il quadrato della sua lunghezza:

ecuacion 2

La lunghezza di un vettore si chiama anche norma: si noti che la formula mostra chiaramente come la norma che abbiamo considerato sia “euclidea”, e il motivo è dato dal fatto che, se scriviamo  come differenza di punti troviamo che

nueva ecuacion 3

La lunghezza di un vettore che abbiamo qui definito è quella che in fisica si chiama la sua intensità: oltre alla lunghezza, possiamo anche definire l’angolo  \theta = \overline{V} \overline{W}  fra due vettori, implicitamente per mezzo della formula :

ecuacion 4

Usando la funzione arcocoseno è possibile calcolare questo angolo a partire dal prodotto scalare dei due vettori e dalle loro lunghezze: il motivo per cui questa formula definisce effettivamente l’angolo fra i vettori è legato alla trigonometria.

Notiamo che   \theta  è un numero compreso fra -1 e 1  tale che:

imagen 3

Dunque, mentre la distanza confronta la similarità fra due punti, che sono tanto più simili quanto più la loro distanza è prossima a zero, il coseno misura la similarità fra due vettori, nel senso che più è prossimo a 1 in valore assoluto e più i vettori determinano la stessa retta.

Questa misura è spesso usata in machine learning per classificare oggetti mappati su spazi vettoriali.

Lo spazio cartesiano a N dimensioni

Nel tour de force di geometria cartesiana e vettoriale che abbiamo fin qui seguito, ci siamo limitati al caso della dimensione N = 2 per aiutarci con i disegni e sviluppare i concetti in un ambiente familiare: ma nelle applicazioni abbiamo a che fare con spazi di dimensione anche molto alta.

In questo caso dobbiamo rinunciare all’intuizione, ma in realtà tutte le formule che abbiamo dato e tutti i concetti che abbiamo definito si trasportano “pari pari” al caso di dimensione qualsiasi.

Infatti tutte le formule dipendono da somme e sottrazioni delle coordinate dei punti e delle componenti dei vettori: se queste componenti sono 2 oppure 20.000 non fa una reale differenza. Ancora meglio, tutto questo si implementa in modo molto efficiente su un computer, cui possiamo far digerire punti e vettori in spazi di dimensione altissima senza problemi!

Un concetto dobbiamo tuttavia spiegarlo, quello di iperpiano: nel caso del piano coincide col concetto di retta e nel caso dello spazio ordinario col concetto di piano.

Fissata una dimensione N consideriamo lo spazio cartesiano R^n.

 

Una retta di questo spazio è, come nel caso di dimensione 2, determinata da una coppia di punti o da un punto e da un vettore: l’equazione parametrica è la stessa che nel caso bidimensionale.

Se N > 2 possiamo anche considerare equazioni parametriche del tipo:

ecuacion 5

In questo caso abbiamo due parametri che variano indipendentemente, quindi, a intuito, l’insieme dei punti X che soddisfano questa equazione al variare di a e b nei numeri reali corrisponde alle coppie (a,b) cioè al piano. In altri termini, è un oggetto bidimensionale.

In realtà questo non è sempre vero: per esempio se    \overline{v}= \overline{w} l’equazione parametrica precedente si riduce alla

ecuacion 6

quindi in realtà rappresenta una retta.

Quindi se scriviamo una equazione parametrica con più parametri, la dimensione dell’insieme dei punti descritti da questa equazione dipende dalle relazioni fra i vettori usati per scriverla: nel caso precedente, se i vettori sono paralleli l’equazione rappresenta una retta e non un piano.

Se in generale, in uno spazio di dimensione N, scriviamo una equazione parametrica in N – 1 parametri, abbiamo:

ecuacion 7

Se tutti i vettori che vi figurano sono paralleli, otteniamo ancora una retta! Per assicurarci di avere un oggetto di dimensione N – 1 dobbiamo trovare un analogo della condizione di parallelismo fra due vettori che sia esprimibile per più di due.

 

Diciamo che n vettori :

Cattura 1

sono linearmente indipendenti se l’unico modo per soddisfare l’equazione

ecuacion 8

È che tutti i coefficienti a_1, a_2,a_n   siano nulli! Cioè l’unica soluzione di questa equazione vettoriale deve essere

Cattura2

Ora possiamo definire un iperpiano di dimensione N – 1 in uno spazio cartesiano di dimensione N come l’insieme dei punti definiti dall’equazione parametrica

ecuacion 9

al variare in tutti i modi possibili dei numeri:

Cattura 3

e supponendo che i vettori rappresentanti qui sotto, siano linearmente indipendenti.

Cattura4

Un iperpiano può essere descritto anche da una equazione cartesiana, cioè da una relazione lineare fra le coordinate del generico punto che gli appartiene:

ecuacion 10

dove  b_i non tutti i  sono nulli.

Un modo per determinare questa equazione è notare che la somma di prodotti dei coefficienti e delle coordinate è il prodotto scalare fra il vettore   \overline{b} = (b_1, b_2,..,b_n) e il vettore   \overline{x} = (x_1, x_2,..,x_n) delle coordinate, di modo che possiamo scriverla

Cattura 5

Questo vuol dire che l’iperpiano è formato dai vettori che sono perpendicolari al vettore  che rappresenta la direzione perpendicolare all’iperpiano, quella mancante per colmare tutto lo spazio.

Un iperpiano di dimensione N – 1 in uno spazio di dimensione N separa in due parti lo spazio, esattamente come una retta separa in due il piano e un piano separa in due lo spazio tridimensionale: i punti che appartengono a una delle due parti in cui viene separato lo spazio dall’iperpiano hanno le coordinate che soddisfano la disequazione.

 

ecuacion 13

Mentre i punti le cui coordinate soddisfano la disequazione

ecuacion 14

costituiscono l’altra parte dello spazio delimitata dall’iperpiano.

Questa idea di usare un iperpiano per separare in due parti lo spazio è la separabilità lineare ed è utilizzata negli algoritmi classici di machine learning, come alberi decisionali o le support vector machines.

 

Matrici e loro algebra

Una delle caratteristiche dell’algebra lineare è la facilità e universalità dei suoi metodi numerici: in sostanza basta aver implementato un algoritmo (o una sua variante), cioè l’eliminazione di Gauss, per poter sostanzialmente fare qualsiasi cosa [una introduzione a questo algoritmo si trova per esempio in queste note]. Questi algoritmi sono tipicamente già implementati in tutte le librerie standard di calcolo numerico, per esempio numpy.linalg di Python.

Per chiudere questo tutorial (ormai fin troppo lungo) è opportuno introdurre la nozione chiave che coinvolge tutti questi algoritmi, e che è utile anche in tutti gli sviluppi concettuali dell’algebra lineare: il concetto di matrice.

Una matrice è semplicemente una tabella di numeri: oppure anche definibile come un array bidimensionale. Diciamo quindi che una matrice n×m è una tabella di numeri che denotiamo con due indici, i e j, dove il primo indice individua la riga e il secondo la colonna: all’incrocio della riga i e della colonna j si trova il numero  individuato da questi indici (in matematica gli indici partono da 1 e non da 0 come in informatica)

Quando la scriviamo per esteso, una matrice si rappresenta nel modo tabellare seguente:

ecuacion15

Una matrice in cui n = m si dice matrice quadrata.

Dal punto di vista pratico, una matrice sembra semplicemente un vettore di lunghezza nm i cui elementi sono arrangiati in forma tabellare anziché scritti in sequenza.

Tuttavia questo cambio notazionale è fondamentale per poter usare questi oggetti.

In particolare, le matrici arricchiscono l’algebra dei vettori con una operazione di moltiplicazione vera e propria: prima notiamo che possiamo sommarle e moltiplicarle per un numero ottenendo ancora matrici dello stesso tipo

Catturab6

In altri termini le matrici n×m formano uno spazio vettoriale di dimensione nm.

Ricordiamo che abbiamo detto che un vettore si può moltiplicare per un numero e ottenere ancora un vettore. Ma non sappiamo come moltiplicare un vettore per se stesso e ottenere ancora un vettore: il prodotto scalare di due vettori produce un numero e non un vettore.

Per introdurre una moltiplicazione dobbiamo scrivere i vettori in due modi distinti: vettori riga e vettori colonna. Un vettore riga è una sequenza di numeri, come lo abbiamo scritto fin qui, per esempio (1,2,0).

Un vettore colonna è una sequenza di numeri scritti dall’alto verso il basso, come :

Cattura66

Apparentemente non c’è differenza sostanziale, ma in realtà se interpretiamo un vettore come un particolare tipo di matrice, il vettore riga è una matrice 1×3 mentre il vettore colonna è una matrice 3×1.

Quel che si può fare è in effetti, data una matrice A del tipo n×m e una matrice B del tipo n×r moltiplicare A per B ottenendo una matrice n×r. Il coefficiente di indici i e j della matrice AB è calcolato come la somma seguente:

Cattura 12

Si noti che questo è il prodotto scalare del vettore riga dato dalla riga i-esima di A per il vettore colonna dato dalla colonna j-esima di B: ecco perché il prodotto di matrici di chiama prodotto righe per colonne.

Esempio: moltiplichiamo una matrice 2×3 per una matrice 2×3:

image1

Torniamo ora ai nostri vettori: possiamo moltiplicare il vettore riga per il vettore colonna, e ne risulta una matrice 1×1 vale a dire un numero (il prodotto scalare). Ma possiamo anche moltiplicare il vettore colonna 3×1 per il vettore riga 1×3 ottenendo una matrice 3×3:

catura 12

 

Notiamo quindi che moltiplicando due vettori di uno spazio di dimensione N otteniamo un vettore di uno spazio di un’altra dimensione: 1 oppure N×N.

La matrice identità è la matrice quadrata che contiene zero dappertutto tranne che sulla diagonale dove contiene 1 (gli elementi diagonali di una matrice sono quelli il cui indice di riga è uguale all’indice di colonna: ). Per esempio in dimensione 3 la matrice identità è

ecuacion 16

Come suggerisce il nome, moltiplicando una matrice A per la matrice identità si ottiene ancora A. Inoltre il prodotto di matrici è distributivo rispetto alla somma.

Tuttavia l’algebra delle matrici ha una particolarità: il prodotto non è commutativo, il che vuol dire che AB è diversa da BA (in effetti BA potrebbe non avere senso, se per esempio n m).

Per esempio

ultima ecuacion

 

Un’altra operazione tipica che si effettua è la moltiplicazione di una matrice n×m per un vettore colonna di m componenti: ne risulta un vettore colonna di n componenti.

ALGEBRA LINEARE PER TUTTI [Parte 1]

Autore: Paolo Caressa

 

Premessa

Come in ogni altro settore interdisciplinare nel machine learning ed nel deep learning si utilizzano necessariamente nozioni, concetti e formalismi che provengono da diverse fonti e, per molti versi, che richiedono mentalità diverse per essere compresi.

Per questo motivo, si è soliti dire che il data scientist dovrebbe essere un “incrocio” fra un informatico, uno statistico e un matematico ma pochi sono versati in tutte e tre le materie e difficilmente tutte e tre risulteranno egualmente facili, intuitive e belle a una stessa mente.

In particolare, delle varie nozioni matematiche che sono indispensabili ad un data scientist, come per chi è unicamente interessato a capire come funziona un sistema di machine learning per poterlo gestire al meglio, una delle nemesi è l’algebra lineare.

Ciò è sorprendente per i matematici che invece considerano l’algebra lineare una cosa facilissima.

Mi permetto di dire, citando il matematico I.M. Herstein, che l’algebra lineare ha solo tre caratteristiche, è facile, utile e bella!

Lo scopo di questo tutorial è di spiegare in modo semplice ma non privo di rigore le nozioni di base dell’algebra pertanto se già sapete calcolare autovettori ed autovalori di una matrice o magari una decomposizione in valori singolari (SVD) o un’analisi delle componenti principali (PCA) ci salutiamo qua. In caso contrario, buona lettura!

 

Il piano e lo spazio cartesiano

Prendiamola un po’ alla lontana, andando a scavare nei nostri ricordi delle scuole superiori: “la geometria analitica (o cartesiana)”

Inventata da Cartesio e Fermat nel XVII secolo, l’idea di base della geometria cartesiana è di fornire un modello numerico per il piano e lo spazio. In breve, la scoperta consiste nell’identificare l’insieme dei punti del piano (cioè mappato in maniera biunivoca in modo da esserne indistinguibile) con l’insieme delle coppie di numeri (x,y) con x ed y indipendenti.

Per prima cosa ricordiamo che i punti della retta si identificano all’insieme dei numeri reali (cioè razionali ed irrazionali) ordinato dalla solita relazione <.

Fissiamo un punto O sulla retta e corrispondente a 0, si fissa un punto U (diverso da O) e lo si fa corrispondere a 1; tutti gli altri punti sono determinati dalla posizione relativa rispetto a questi:

 

recta

È quindi chiaro cosa voglia dire la distanza fra due punti P e Q sulla retta: se x ed y sono i numeri corrispondenti, la distanza PQ è pari a |x – y| (valore assoluto della differenza):

 

Cattura 1 1

 

 

Quindi su una retta basta fissare lo zero e l’uno, col che si è fissato il verso dei numeri positivi (quelli che stanno tutti da una parte di O e comprendono U) e dei numeri negativi (quelli che stanno tutti da una parte di O e non comprendono U).

In questo modo:

La retta della geometria si identifica completamente nell’insieme R dei numeri reali

 

Ad un punto corrisponde uno e un solo numero, dove ogni numero reale possiamo immaginarlo come un numero decimale avente un numero finito di cifre dopo la virgola, avente un numero finito di cifre dopo la virgola che si ripetono indefinitamente, o avente un numero infinito non periodico di cifre dopo la virgola (a rigore bisogna escludere le sequenze che terminano con un 9 periodico).

Passiamo ora al piano.

Si fissa un punto O nel piano e si prende una retta x che passi per il punto ed una seconda retta ortogonale a x, chiamata y. Su x e y si sceglie una stessa unità di misura (cioè un punto U diverso da O e si stabilisce convenzionalmente che la distanza OU vale 1).

In questo modo si può misurare la distanza fra due punti che stanno su x rispetto all’unità di misura fissata.

La corrispondenza fra un punto P del piano e una coppia (a,b) di numeri avviene come segue:

  1. Si proietta il punto P sull’asse x ottenendo un punto P’ e si pone a = distanza fra P’ e O.
  2. Si proietta il punto P sull’asse y ottenendo un punto P’’ e si pone b = distanza fra P’’ e O.

Questo associa in modo unico a P la coppia (a,b): per passare invece dalle coordinate (a,b) ai punti basterà trovare i punti P’ e P’’ sulle rette x e y che distano per a e b dall’origine O, tracciare le perpendicolare agli assi per questi punti e determinare P come la loro intersezione.

Plano cartesiano

Figura 1. Tre punti del piano cartesiano, le loro coordinate e le loro proiezioni sugli assi, Fonte: wikipedia

 

 

Quindi:

Il piano della geometria si identifica completamente all’insieme R2 delle coppie di numeri reali: a un punto corrisponde una e una sola coppia.

La notazione R2 indica le coppie ordinate di elementi di R. Se X è un insieme Xn è in generale l’insieme delle n-ple di elementi di X.

Che vantaggio c’è nel considerare il piano come l’insieme delle coppie di numeri (a,b)?

  • In primo luogo sappiamo dire cosa sia, un insieme di coppie appunto, non ci servono assiomi o altro.
  • In secondo luogo trasformiamo i problemi geometrici, che normalmente coinvolgono costruzioni complicate e ragionamenti assai lunghi, in calcoli.

La geometria cartesiana è quindi la geometria ideale da fare al computer: possiamo ridurre tutto a numeri e calcoli. Non a caso, le librerie 2D che si usano per disegnare sullo schermo del computer, o su una finestra di una applicazione, rappresentano i punti come coppie di numeri (le coordinate) e un attributo che ne rappresenta il colore.

Per esempio: come si rappresenta una curva nel piano cartesiano?

Ci sono due modi:

  • Tramite una equazione cartesiana, una singola relazione che lega le coordinate di un suo generico punto
  • Tramite una equazione parametrica, che esibisce il generico punto della retta in funzione di un parametro.

Per esempio consideriamo l’equazione x2+y2 = 1 e l’equazione 2x+y = 0. La prima rappresenta una circonferenza di centro l’origine e raggio 1, la seconda una retta passante per l’origine.

Supponiamo di volerne calcolare le intersezioni: utilizzando la geometria classica dovremmo costruire queste intersezioni in qualche modo, mentre con la geometria cartesiana ci stiamo facendo la seguente domanda: quali sono i punti di coordinate (x,y) soggetti simultaneamente alle relazioni  x2+y2 = 1 e 2x+y = 0?

La seconda equazione equivale a dire y = –2x, che sostituita nella prima offre x2+(–2x)2 = 1, cioè x2+4x2 = 1 cioè x2 = 1/5, che ha due soluzioni x = 1/√5 e x = –1/√5, col che troviamo le due intersezioni richieste: i punti (1/√5, –2/√5) e (–1/√5, 2/√5).

A questo punto identifichiamo lo spazio cartesiano in tre dimensioni con l’insieme delle triple (x,y,z) di numeri reali, cioè con l’insieme R3.

Un punto nello spazio è quindi univocamente determinato dalle sue tre coordinate cartesiane, su cui possiamo lavorare per risolvere i problemi geometrici, dopo averli tradotti in problemi algebrici

 

Distanza fra due punti

Abbiamo detto che lungo una retta, identificata con l’insieme dei numeri reali, le distanze si calcolano col valore assoluto della differenza. Come calcoliamo invece le distanze nel piano?

La cosa interessante è che esistono diversi modi di farlo, che costituiscono tutti generalizzazioni della distanza sulla retta, citiamo le tre principali tutte appartenenti alla famiglia delle distanze di Minkonski:

  • la distanza euclidea
  • la distanza di Manhattan
  • la distanza uniforme.

La distanza euclidea fra due punti P = (x1,y1) e Q = (x2,y2) nel piano è data dalla formula seguente:

Cattura 2

Intanto vediamo che se P e Q stanno su una stessa retta, per esempio l’asse delle x, questa distanza si riduce al valore assoluto della differenza delle coordinate: infatti se supponiamo P = (x1,0) e Q = (x2,0) la  formula precedente ci porge :

 

cattura 3

(come al solito quando prendiamo la radice quadrata di un numero consideriamo quella positiva).

La distanza euclidea andrebbe in realtà chiamata pitagorica, in quanto segue dal teorema di Pitagora: supponiamo di avere un triangolo rettangolo OPQ, con un vertice nell’origine, un lato sull’asse delle x e l’altro lato perpendicolare all’asse delle x.

Dunque le coordinate di P saranno del tipo (x,0) e quelle di Q del tipo (x,y): la distanza fra O e P è x e quella fra Q e P è y, col che, per il teorema di Pitagora, d(O,Q)2 = d(O,P)2 + d(P,Q)2 = x2 + y2. Per cui in questo caso, prendendo la radice quadrata,

Cattura 4

e la formula è verificata. Nel caso generale il ragionamento è analogo ma intervengono anche le coordinate di O (che nel nostro caso risultano essere nulle).

Quindi la distanza euclidea si riduce alla distanza fra due punti su una retta e ha un evidente contenuto geometrico.

recta.opq

 

La distanza nello spazio si calcola in modo analogo tenendo conto che i punti hanno una coordinata in più: se P = (x1,y1,z1) e Q = (x2,y2,z2) sono punti dello spazio cartesiano, la loro distanza è:

Cattura 5 1

Per i più curiosi, spiego nel resto di questo paragrafo altri due modi di calcolare la distanza nel piano, che danno luogo a valori diversi della distanza euclidea ma che sono modi comunque validi di misurare quanto due punti sono vicini (un bel teorema che non è il caso di riportare qui dice comunque che, ai fini della relazione di “essere nei pressi di”, tutte queste distanze sono equivalenti).

Un’altra distanza che talvolta si usa in machine learning è la “distanza di Manhattan” anche conosciuta come “distanza dei taxi”:

d_1\left ( P,Q \right )= \left \| X_1-X_2 \right \| + \left \| Y_1-Y_2 \right \|

Di nuovo, se P e Q stanno sull’asse delle x, questa distanza coincide con il valore assoluto della differenza. Il nome distanza dei taxi è data dal fatto che la distanza fra P e Q non si misura col teorema di Pitagora, che cerca una scorciatoia diagonale fra P e Q ma partendo da P e muovendosi lungo l’asse delle x fino a giungere sopra o sotto a Q, per poi spostarsi lungo l’asse delle y e raggiungere Q.

 

 

 

 

 

 

DITANZA EUCLIDEA TRIANGULO

Figura 2. Distanza euclidea (verde) vs. distanza dei taxi (rosso)

È la manovra che deve fare un taxi per andare da un punto di un isolato al punto opposto: non può passare in mezzo all’isolato ma deve costeggiarne i lati.

Un’altra distanza ancora, che talvolta è utilizzata, è la “distanza uniforme”, che è definita comed_0 \left ( P,Q \right )= max \left \{ |x_1-x_2 | \right \}\left \{ |y_1-y_2 | \right \}

Cioè questa si calcola prendendo la lunghezza massima del segmento ottenuto proiettando i due punti sugli assi delle x e delle y: di nuovo è ovvio che se i punti giacciono sull’asse delle x questa si riduce al valore assoluto della differenza fra due punti.

Una notazione di colore: nello spazio, una “palla” si definisce come l’insieme dei punti che hanno distanza minore di un numero fissato, il raggio, da un punto fissato, il centro: cioè se O è il centro e r il raggio, tutti i punti tali che d(P,O) < r costituiscono la palla. Questa terminologia è chiara se usiamo la distanza euclidea.

Ma, usando la distanza dei taxi, che figura geometrica viene fuori considerando tutti i punti P tali che d1(P,O) < r? (suggerimento: si dice che questa distanza abbia le palle quadrate…).

Introduzione a Metodi di riduzioni della Dimensionalità ed elementi di Algebra lineare [Parte 2]

 

Autore: Matteo Alberti

                            

Sommario

Metodi lineari per la riduzione: 2

Identificazione attraverso l’individuazione di sottospazi 2

Approssimazioni Matriciali per la riduzione. 10

Casi di applicazione base: Decomposizione in Valori Singolari (SVD). 11

Norme Matriciali 11

Norme vettoriali 13

Norme indotte. 13

Norme di Schatten. 13

Norma di Frobenius. 13

Casi di applicazione base: Analisi dei Cluster. 16

Definizione di metrica. 16

Distanze di Minkowski (Manhattan, Euclidea, Lagrange). 16

 

 

 

Lo scopo di questo primo tutorial è di introdurre le nozioni base di riduzione della dimensionalità dal punto di vista matematico (spazi, sottospazi, mappe lineari) e di riprendere gli elementi necessari di algebra lineare (norme, isometria, isomorfismo…) per ogni algoritmo di machine learning.

 

Norme Matriciali

 

Abbiamo a questo punto impostato il problema della riduzione della dimensionalità dei dati come un problema di approssimazione tra matrici, dobbiamo a questo punto andar a stabilire valutare e quindi calcolare la distanza fra la matrice dei dati originari e quella approssimante attraverso lo studio delle differenti norme:

Esistono tre principali tipologie di norme:

  • Norme vettoriali
  • Norme indotte
  • Norme di Schatten

Dove nel campo dell’analisi dei dati essenzialmente ci si riconduce, salvo eccezioni, alla norma di Frobenius (distanza euclidea)

 

 

 

 

Elementi di algebra:

Norma

Una norma (comunemente contrassegnata con ) è una funzione dallo spazio vettoriale delle matrici se:

 

Art2 1

 

Norme vettoriali

 

La famiglia delle norme vettoriali trattano la matrice X_n_x_k  come un vettore di nk componenti dove possiamo definire la norma utilizzando una delle qualsiasi norme seguenti:

art2 imagr2

Nota:

Ponendo p=2 ci si ricollega alla norma euclidea

 

Norme indotte

 

Una matrice X_n_x_k  può essere vista come un operatore lineare da   R^k \mapsto R^n .

Misurando in R^k  le lunghezze con una norma fissata e facciamo altrettanto in R^n , con una differente norma, possiamo andare a misurare quanto X allunga o accorcia un vettore , confrontando la norma di v  \in R^k con la relativa norma della sua immagine Xv.

La norma indotta  \parallel X \parallel _k _n  risulta definita come:

 

art2 3

Norme di Schatten

 

La norma di Schatten, di ordine p, di una matrice X è data semplicemente da:

Art2 4

Dove \omega_i  sono i valori singolari

 

 

Norma di Frobenius

 

La norma di Frobenius della nostra matrice  X_n_x_k di partenza è data da:

ART25

 

 

Andando a svolgere i conti, esplicitando il prodotto matriciale si ottiene:

art6.art2

Ne corrisponde che la norma di Frobenius è pari alla radice quadrata della somma del quadrato degli elementi ossia una norma euclidea vista come un vettore che coincide con la norma vettoriale di X di ordine 2.

 

 

 

 

 

Elementi di algebra:

Traccia

L’operatore traccia, indicata con Tr \left ( . \right ), è definita come la somma degli elementi diagonali della matrice argomento

 

 

 

 

 

 

 

 

Casi di applicazione base: Analisi dei Cluster

 

La cluster analysis e’ una tecnica di analisi multivariata attraverso la quale e’ possibile raggruppare le unità statistiche, in modo da minimizzare la “lontananza logica” interna a ciascun gruppo e di massimizzare quella tra i gruppi.

Rientra fra le tecniche di apprendimento non supervisionato.

Nasce quindi spontaneo dover definire che cosa si intende per lontananza logica ed in base a quale metrica.

 

 

 

Definizione di metrica

Sia  X^-\in R^p ed Y^-\in R^p definiamo metrica una funzione tale che   R^p x R^p\mapsto R^+   U\left \{ 0 \right \} 

che goda delle seguenti proprietà:

  •  d \left ( X^-, Y^- \right ) \geq O non negativa
  •  d \left ( X^-, Y^- \right ) = d \left ( Y^-,X^- \right ) simmetria
  •  d \left ( X^-, Y^- \right ) = 0 \leftrightarrow X= Y identità
  •  d \left ( X^-, Y^- \right ) \leq d \left ( X^-, Z^- \right ) + d \left ( Z^-, X^- \right )   diseguaglianza triangolare

 

Distanze di Minkowski (Manhattan, Euclidea, Lagrange)

 

Andiamo a questo punto ad analizzare i casi principali delle distanze appartenenti alla famiglia delle distanze di Minkowski dove:

art27

Evidenziamo i seguenti casi:

  • k=1 Distanza di Manhattan
  • k=2  Distanza Euclidea
  • k\rightarrow \infty Distanza Lagrangiana (Čebyšëv)

In particolare:

 

 

euclidea

Riprendendo dunque con l’esempio della Cluster Analysis, risulta fondamentale quindi definire il tipo di distanza con cui vogliamo affrontare la nostra analisi.

Principalmente nei pacchetti già implementati si trovano le tre varianti delle distanze di Minkowski (per variabili quantitative)

Importando da sklearn:

AgglomerativeClustering(n_clusters=2, affinity=’euclidean’, memory=None, connectivity=None, compute_full_tree=’auto’, linkage=’ward’

 

 

distanze

 

 

Come usare PySpark sul computer

 

Autore: Favio Vázquez

Traduttrice: Sabrina Sala

 

Apache scla image.

https://www.mytectra.com/apache-spark-and-scala-training.html

Partiamo dal presupposto che conosciate Apache Spark e PySpark ma se volete guardare una breve introduzione leggete qui.

Il simbolo $ sta per eseguire il comando nella shell, quindi non dovete copiarlo.

Eseguire PySpark in Jupyter

Jupiter

 

  1. Installare Jupyter Notebook

$ pip install jupyter

  1. Installare PySpark

Controllate di avere Java 8, o successive versioni, sul vostro computer. Vi servirà ovviamente anche Python (consigliamo Python 3.5 da Anaconda).

Ora andate sulla pagina download di Spark. Selezionate l’ultima versione, un pacchetto predefinito per Hadoop, scaricatelo.

Se volete il supporto Hive o altre funzionalità a piacere, dovrete costruire autonomamente la distribuzione spark: per farlo andate qui.

Aprite il file zip e spostatelo nella cartella directory / opt:

$ tar -xzf spark-2.3.0-bin-hadoop2.7.tgz
$ mv spark-2.3.0-bin-hadoop2.7 /opt/spark-2.3.0

Create un link simbolico che vi permetterà di avere molteplici versioni di Spark:

$ ln -s /opt/spark-2.3.0 /opt/spark̀

Infine, inserite nella vostra shell bash (o zsh ecc.) dove trovare Spark in questo modo: configurate le vostre varibili $PATH aggiungendo le seguenti righe nel vostro file ~/.bashrc (o ~/.zshrc):

export SPARK_HOME=/opt/spark
export PATH=$SPARK_HOME/bin:$PATH

Ora, per eseguire PySpark in Jupyter, avete bisogno di aggiornare le variabili di ambiente di PySpark. Aggiungete queste righe al vostro file ~/.bashrc (or ~/.zshrc):

export PYSPARK_DRIVER_PYTHON=jupyter
export PYSPARK_DRIVER_PYTHON_OPTS=’notebook’

Riavviate il terminale e lanciate PySpark:

$ pyspark

Questo comando dovrebbe avviare Jupiter Notebook nel vostro browser. Create quindi un nuovo notebook cliccando ‘New’ > ‘Notebooks Python [default]’. Avrete così SparkContext e SqlContext (o solo SparkSession per Spark > 2.x) sul vostro computer e potere ora avviare PySpark da pc. Qui alcuni esempi per testare l’ambiente.

Eseguire PySpark nell’IDE scelto (Integrated Devolopment Environment)

Alcune volte avrete bisogno di un intero IDE per poter creare codici più complessi e PySpark non è di dafult in sys.path. ciò però non significa che non possiate usarlo come una normale libreria: è sufficiente aggiungerlo al sys.path in fase di esecuzione. Il pacchetto findspark ha proprio questa funzione.

Per installarlo, inserite:

$ pip install findspark

Successivamente, per avviare PySpark nel vostro IDE, scrivete:

import findspark
findspark.init()import pyspark
sc = pyspark.SparkContext(appName=”myAppName”)

È questo è tutto. Di seguito un esempio per testare PySpark localmente:

 

import

findspark

findspark.init(“/opt/spark”)
import random
from pyspark import SparkContext
sc = SparkContext(appName=”EstimatePi”)
def inside(p):
    x, y = random.random(), random.random()
    return x*x + y*y < 1
NUM_SAMPLES = 1000000
count = sc.parallelize(range(0, NUM_SAMPLES)) \
             .filter(inside).count()
print(“Pi is roughly %f” % (4.0 * count / NUM_SAMPLES))
sc.stop()

 

dubbio o domanda, non esitate a commentare e chiedere chiarimenti!

 

Jupyter Notebook Python, Scala, R, Spark, Mesos Stack

docker

 

 

Docker è simile a una “macchina virtuale” leggera (anche se, in realtà, Dockery fornisce “immagini” e “cointainer” e non macchine virtuali), come se avessimo un secondo computer con un proprio sistema operativo all’interno della nostra macchina. Possiamo accedere a questa macchina virtuale dal nostro computer e usarla come se stessimo accedendo a un computer remoto via ssh.

jupyter/docker-stacks
docker-stacks – Opinionated stacks of ready-to-run Jupyter applications in Docker.github.com

cliccate sul link e seguite gli step indicati per avere una versione “containerizzata” di Spark (2.3 con Hadoop 2.7)

Utilizzo base

Il comando che segue starts a container with the Notebook server listening for HTTP connections on port 8888 con un token di autenticazione generato in maniera casuale.

$ docker run -it –rm -p 8888:8888 jupyter/pyspark-notebook

 

Installazione di Pip

Pip

Pip

 

Questo pacchetto è ancora sperimentale e potrà cambiare nelle versioni future.

Il pacchetto Python per Spark non è inteso per rimpiazzare tutti gli altri casi d’uso. Questa versione Python packaged di Spark è adeguata per interagire con un cluster esistente, che può essere Spark in sè, YARN o Mesos, ma non contiene gli strumenti necessari per configurare nuovi cluster.

Per istallarlo, eseguite:

$ pip install pyspark

 

 

 

 

Introduzione ai Metodi di riduzione della Dimensionalità ed elementi di Algebra lineare [Parte 1]

Autore: Matteo Alberti

 

Sommario

Metodi lineari per la riduzione: 2

Identificazione attraverso l’individuazione di sottospazi 2

Approssimazioni Matriciali per la riduzione. 10

Casi di applicazione base: Decomposizione in Valori Singolari (SVD). 11

Norme Matriciali 11

Norme vettoriali 13

Norme indotte. 13

Norme di Schatten. 13

Norma di Frobenius. 13

Casi di applicazione base: Analisi dei Cluster. 16

Definizione di metrica. 16

Distanze di Minkowski (Manhattan, Euclidea, Lagrange). 16

 

Lo scopo di questo primo tutorial è di introdurre le nozioni base di riduzione della dimensionalità dal punto di vista matematico (spazi, sottospazi, mappe lineari) e di riprendere gli elementi necessari di algebra lineare (norme, isometria, isomorfismo…) per ogni algoritmo di machine learning.

 

 

 

 

Metodi lineari per la riduzione:

 

Andiamo dunque ad introdurre con termini generali la logica dei processi di riduzione lineare della dimensionalità andando in prima fase ad identificare sottospazi “ottimali”

 

Identificazione attraverso l’individuazione di sottospazi

 

Abbiamo un insieme di vettori k-dimensionali  x_1,…,x_n  (n unità statistiche su k covariante) dove vogliamo individuare un sottospazio p-dimensionale V_p   in R^k   ed un mappa lineare   \varphi\left ( . \right ) da R^k  a  V_p  tale per cui le relative immagini  \varphi(x_i)   approssimino in maniera sufficiente i nostri vettori k-dimensionali di input (in riferimento a qualche metrica che andremo a definire)

Riscriviamo come:

\varphi: R^k\mapsto V_p

: x_i \mapsto\varphi\(x_i )

 

Elementi di algebra:

 

Spazio vettoriale

Si definisce spazio vettoriale su R un insieme V, i cui elementi sono detti vettori con le seguenti proprietà:

 Esiste in V un’operazione definita Addizione vettoriale che associa ad ogni  

 il vettore  x+y  \epsilon V

  • Addizione vettoriale è commutativa, associativa
  • Esiste in V un vettore, indicato con 0 e definito origine
  • Ogni vettore  x  \epsilon   V ha un suo opposto, indicato con –x t.c. x + (-x) =0

Esiste in V un’operazione definita moltiplicazione per gli scalari che associa ad ogni    x  \epsilon V ed ad ogni \alpha \epsilon V  il vettore  \alphax \epsilon V in modo che:

  • Moltiplicazione fra scalari è associativa
  • 1x=x  \forall x \epsilon V

 

Inoltre vale anche:

  • Moltiplicazione scalare è distributiva rispetto all’addizione vettoriale

 

Sottospazio vettoriale

Si definisce S un insieme non vuoto dello sottospazio di V se per ogni: x-x=0  \epsilon S ed ogni loro combinazione lineare \alpha x +\beta x  \epsilon S  

 

Nucleo ed immagine

Siano V e W due spazi vettoriali e sia  L : V \mapsto W un’ applicazione lineare.

Si dice nucleo di L l’insieme dei vettori di V la cui immagine è il vettore nullo di W.

Tale insieme si indica con ker L.

 

ker(L)  =  \left \{ v \epsilon V | L(v) = 0 \right \}

Si dice immagine di L l’insieme dei vettori di W che sono immagini di qualche vettore appartenente al dominio V, cioè:

 

Si dice immagine di L l’insieme dei vettori di W che sono immagini di qualche vettore appartenente al dominio V, cioè:

 

Im(L)  = \left \{ w \epsilon W | w = L(v) , v \epsilon V \right \}

 

Mappa Lineare

Una mappa (o applicazione) lineare  fV \rightarrow W  fra spazi vettoriali reali è una funzione per cui valgano le proprietà:

 

ecuacion de recta

per ogni :

Ecuacion 2

Andiamo a definire solo i casi di principale rilevanza:

 Sia  f: V \mapsto W   una mappa lineare. Allora f è detta:

  • un monomorfismo se è iniettiva
  • un epimorfismo se è suriettiva
  • un isomorfismo se è biiettiva (iniettiva + suriettiva)
  • un endomorfismo se V = W
  • un automorfismo se V = W ed è biettiva.
  • di rango r se r = dim f(V).

Supponiamo a questo punto di aver individuato un sottospazio (specificato successivamente)  V_p  che risulti sufficientemente “approssimante” e sia  b_1,…,b_p     una base di  V_p (Nota: la base è formata da vettori k-dimensionali in quanto V_p  è sottospazio di R^k )

 

La mappa lineare  \varphi\left ( . \right )  associa all’input x_i  l’elemento  \varphi \left ( x_i \right )  di  V_p, avente le seguente forma:

 

Formula 2

a_i_j scelti opportunamente.

L’errore di approssimazione da R^k \rightarrow V_p  quindi da x_1 .. x_n ..a..\varphi\left ( x_i \right )...\varphi\left ( x_n \right )

consisterà nell’opportuna norma vettoriale:

Formula 3

 

 

Fissata la nostra base b_1,…,b_p i nostri vettori \varphi\left ( x_1 \right ) possono essere trovati direttamente dalle coordinate a_i_j.

 

Data la mappa  \varphi\left ( . \right )  che associa al generico vettore  il suo vettore \varphi\left ( x_i \right )  di coordinate a_i_j  e componiamo con la mappa \varphi\left ( . \right )  otteniamo una mappa che associa ai vettori di input le coordinate delle loro immagini in  V_p\div \theta\left ( . \right ) =  \left ( \varphi \circ \varphi \right ) \left ( . \right )

formula 4

 

Attraverso la nostra mappa i vettori k-dimensionali di input vengono rappresentati in vettori di p-dimensionali ossia come elementi di R^p .

A questo punto risulta immediato che si può procedere con ulteriori analisi in R^p .

 

Andiamo dunque ad indagare in che modo questa riduzione della dimensionalità va a mantenere ed a perdere in particolare:

IMAGEN5

Naturalmente passare da k a p dimensioni   p \lt k con  comporta una perdita di informazioni ed una deformazioni della geometria originaria del dato. Di particolare rilevanza è che qualunque mappa lineare non potrà essere un isomorfismo né un’isometria.

Ciò comporta che tutte le norme, prodotti scalari e distanze non vengono preservate

Esempio:

  • V_p  sottospazio dimensionale
  • V_p^\bot complemento ortogonale di R^k

Ogni vettore  x\in R^k  può esser decomposto in una somma di vettori ( x\in V_p  e di un vettore  x \in V_p^\bot )

 

In generale, presi i due vettori u,v \in R^k abbiamo:

formula 7

Come si può constatare il prodotto scalare non è conservato.

 

 

Elementi di algebra:

complemento ortogonale

Sia S \subseteq V  un sottospazio di V, definiamo complemento ortogonale di D in V indicandolo con  S^\bot il sottoinsieme di V definito da:

ecucion 9

Ossia un sottoinsieme di tutti i vettori di V ortogonali a tutti i vettori di S

 

 

 

 

 

V_p\rightarrow R^p

La mappa lineare \varphi\left ( . \right ) è un isomorfismo gli spazi vettoriali V_p e R^p, ciò comporta che non vi sarà alcuna perdita di informazioni .

 

Volendo chiarire ulteriormente questo concetto:

consideriamo il prodotto euclideo e supponiamo che la base b_1 ... b_p  sia ortonormale e siano  h , w  due vettori di  V_p  con i relativi vettori di coordinate:  a^h=  \left \{ a^h_i \right \}  e   a^w\left \{ a^w_i \right \} allora risulta vera la seguente condizione:

formula 9

Significa che il prodotto scalare tra h,w è identico al prodotto scalare dei rispettivi vettori di coordinate iniziali (giusto specificare che i due prodotti scalari sono definiti in spazi differenti sebbene siano stati scritti allo stesso modo)

Se la base \left ( b_i, b_j \right )  non fosse ortonormale i fattori  non si ridurrebbero a 0.

Nel primo caso il prodotto scalare fra i vettori di coordinate è dato da:

x

mentre che nel secondo caso sarebbe dato da : \left [a^h \right ] ^t G a^w , con G matrice di Gram ne consegue quindi che matrici costruite nel secondo modo ed associate a basi differenti sono fra loro diverse (dovendo riadattare il calcolo dei prodotti scalari, norme, distanze indotte …)

 

 

 

Elementi di algebra:

ortonormale:

Una base si definisce ortonormale quando è composta da vettori di norma unitaria e fra loro ortogonali

 

Approssimazioni Matriciali per la riduzione

 

Vogliamo dare una seconda visione sulla riduzione alla dimensionalità basata sull’approssimazione matriciale (ciò che verrà utilizzata in pratica in ogni linguaggio di programmazione)

Data:

  • Matrice  A_n_x_p di rango p che abbia per righe i vettori di coordinate a_i con i= 1..n
  • Matrice B_p_x_q di rango p con i vettori di coordinate b_i con i = 1 .... p
  • Matrice   \theta_n_x_k di rango p con i vettori di coordinate \varphi \left ( x_1 \right ) con i= 1....n
  • Matrice x matrice di rango k che ha come righe i vettori x_i con i= 1..n

Allora possiamo riscriverlo come:

X\approx \theta = A\cdot B

d.p

Le righe di  \theta  sono date dalle combinazioni lineari delle righe di B ossia dalla nostra base, con i coefficienti dati dalle righe di A, le coordinate sulla base scelta.

Il nostro problema di riduzione della dimensionalità corrisponde quindi ad individuare un sottospazio vettoriale di dimensione p (p<k) la nostra base scelta (B) e delle relative coordinate date dalla matrice A.

 

Nell’analisi dei dati, dove il nostro punto di partenza è la matrice dei dati, le diverse tecniche di riduzione si differenziano in base al tipo di approssimazione, decomposizione e scelta fra le tante basi possibili.

 

 

Casi di applicazione base: Decomposizione in Valori Singolari (SVD)

 

Andiamo ad implementare in Python una semplice decomposizione in valori singolari (SVD) ossia andiamo a suddividere la nostra matrice X di partenza nelle due matrici A e B viste in precedenza:

import numpy as np

X = np.array([3,2,4],[3,6,7],[2,1,4])

autoval, autovett = np.linalg.eig(X)

Deep Learning Italia – Milano – 19 ottobre 2018

Il Meetup #AperiTech di ottobre di Deep Learning Italia!

• Cosa faremo
“Deep Learning Italia” è lieta di invitarvi al suo Meetup a Milano.

Registrazione obbligatori su eventbrite:

https://www.eventbrite.it/e/biglietti-milano-meetup-aperitech-di-deep-learning-italia-50642063776

Info e prenotazioni a: info@deeplearningitalia.com

Vi aspettiamo!!!

Scaletta Talks:

1) Matteo Testi Founder Deep Learning Italia & Luciano Di Benedetto Head of Big Data at Sferanet

“Introduzione alla piattaforma Deep Learning Italia”
Abstract: Illustrazione della community Deep Learning Italia e degli strumenti oggi sviluppati e a disposizione della community. Introduzione alle features del sito www.deeplearningitalia.com: i tutorials, il question & answer, le references, gli sviluppi futuri.

2) SK Reddy Chief Product Officer AI for Hexagon, located in Silicon Valley

Title: Making sense with 3D Point Clouds using Deep Learning

Abstract: Processing 3D images has many use cases. For example, to improve autonomous car driving, to enable digital conversions of old factories, enable augmented reality solutions for medical surgeries, etc. 3D image processing brings enormous benefits but also amplifies computing cost. The size of the point cloud, the number of points, sparse and irregular point cloud, and the adverse impact of the light reflections, (partial) occlusions, etc., make it difficult for engineers to process point clouds. Moving from using handcrafted features to using deep learning techniques to semantically segment the images, to classify objects, to detect objects, to detect actions in 3D videos, etc., we have come a long way in 3D image processing. 3D Point Cloud image processing is increasingly used to solve Industry 4.0 use cases to help architects, builders and product managers. I will share some of the innovations that are helping the progress of 3D point cloud processing. I will share the practical implementation issues we faced while developing deep learning models to make sense of 3D Point Clouds.

Linkedin: https://www.linkedin.com/in/sk-reddy/

Website: http://hexagon.com

3) Matteo Alberti Techedge & Deep Learning Italia

Applicazione dei principali algoritmi di Deep Learning al mondo Finance:

0) I limiti della modellistica AR, ARIMA,ARMA

1) Applicazione di Reti Ricorrenti (LSTM)

2) Dalle Convoluzioni 2D all’estrazione di Pattern

3) Strumenti di Riduzione della dimensionalità per inglobare indici nella nostra predizione (dalle linearità ad Autoencoder)

4) Cenni al Reinforcement Learning per Automated Stock Trading

Il tutto con alucni esempi pratici con codice in Tensorflow

Programmazione funzionale per il deep learning

Autore: Joyce Xu

Traduttrice: Sabrina Sala

 

Fino a poco tempo fa il concetto di “programmazione funzionale” e “machine learning”(Apprendimento Automatico)  erano attribuiti a due mondi nettamente separati. Il primo è un paradigma di programmazione che ha guadagnato popolarità nel momento in cui si il mondo si è rivolto alla semplicità ed alla componibilità per produrre pplicazioni scalabili complesse; il secondo è uno strumento utilizzato per istruire un computer come un completamento automatico di scarabocchi e a comporre musica. Non vi era quindi dialogo tra questi due elementi.

Ma studiandoli ed approfondendoli con attenzione ci si rende conto che la loro sovrapposizione è sia pratica sia teorica. Innanzitutto, il machine learning non è un elemento a sé stante ma piuttosto, per essere sfruttato, deve essere incorporato in applicazioni scalabili complesse. In secondo luogo, il machine learning, e in particolare il deep learning(apprendimento profondo) , è funzionale nel suo design:

  • I modelli di deep learning (apprendimento profondo) sono componibili: la programmazione funzionale riguarda comporre catene di funzioni di ordine superiore per operare su semplici strutture di dati. Le reti neurali sono disegnate nella stessa maniera, concatenando funzioni di trasformazione da uno strato a quello successivo, per operare su una semplice matrice di dati di input. Infatti l’intero processo di deep learning può essere visto come l’ottimizzazione di un set di funzioni composte. Ciò comporta che i modelli in sé siano intrinsecamente funzionali.

 

  • Le componenti di deep learning (apprendimento profondo) sono immutabili: quando applichiamo le funzioni sui dati di input, i nostri dati non cambiano ma, piuttosto, viene prodotto un nuovo set di valori. In aggiunta, quando i pesi sono aggiornati, non hanno bisogno di essere “mutati” ma rimpiazzi da un nuovo valore. In teoria l’aggiornamento dei pesi può essere applicato in qualsiasi ordine (ovvero non sono dipendenti l’uno dall’altro) e non c’è quindi necessità di tenere traccia in maniera sequenziale del relativo cambiamento.

 

  • La programmazione funzionale offre facilità nella parallelizzazione. Ancor più importante, però, è che le funzioni che sono totalmente componibili sono facili da parallelizzare. Ciò comporta maggiori velocità e potenza computazionale. La programmazione funzionale fornisce concurrency e parallelismo a costi quasi pari a zero, rendendo così più semplice lavorare in apprendimento profondo con modelli larghi e distribuiti.

Ci sono differenti teorie e prospettiva riguardanti la combinazione tra programmazione funzionale e deep learning (apprendimento profondo) , sia dal punto di vista matematico sia da quello pratico, ma tuttavia alcune volte è più semplice vederla in maniera pratica. In questo articolo andremo a introdurre le idee alla base della programmazione funzionale e di come applicarle in un modello Cortex di deep learning per la classificazione dei valori anomali.

Le basi di Clojure

Prima di continuare con il tutorial su Cortex, introduciamo alcune nozioni di base in merito a Clojure. Clojure è un linguaggio di programmazione funzionale ottimo per concurrency e data processing. Per nostra fortuna, questi sono entrambi estremamente utili nel machine learning. Infatti la ragione primaria per cui utilizziamo Clojure per il machine learning è che il lavoro di preparazione dei dataset di allenamento (manipolazione dei dati, processing ecc.) può facilmente compensare il lavoro di implementazione degli algoritmi, specialmente quando si hanno librerie solide come Cortex.  Utilizzare Clojure e .edn (piuttosto che C++ e protobuf) ci da un vantaggio in termini di velocità su progetti di machine learning.

Potete trovare un’introduzione più approfondita sul linguaggio qui.

Partiamo con le basi: il codice di Clojure è formato da un insieme di espressioni. Queste sono racchiuse in parentesi e tipicamente trattate come funzioni di chiamata.

(+ 2 3)          ; => 5
(if false 1 0)   ; => 0

Ci sono 4 strutture di dati base: vettori, liste, hash-map e set. Le virgole sono considerate come spazi bianchi, quindi vengono solitamente omesse.

[1 2 3]            ; vector (ordered)
‘(1 2 3)           ; lists (ordered)
{:a 1 :b 2 :c 3}   ; hashmap or map (unrdered)
#{1 2 3}           ; set (unordered, unique values)

Il virgolettato singolo, che precede la lista, è unicamente uno strumento per far sì che essa non venga rilevata come espressione.

Clojure ha anche molte funzioni integrate per funzionare su queste strutture di dati. Parte del fascino di Clojure sta nel suo design ricco di funzioni per poche tipologie di dati molto ridotti, il che si contrappone alla comune prassi di avere poche funzioni specializzate per il maggior numero possibile di strutture dati. Clojure, essendo un linguaggio di functional programming, supporta funzioni di alto livello, il che significa che le funzioni possono essere importate come argomento su altre funzioni.

(count [a b c])              ; => 3

(range 5)                    ; => (0 1 2 3 4)

(take 2 (drop 5 (range 10))) ; => (5 6)

(:b {:a 1 :b 2 :c 3})        ; use keyword as function => 2

(map inc [1 2 3])            ; map and increment => (2 3 4)

(filter even? (range 5))     ; filter collection based off predicate => (0 2 4)

(reduce + [1 2 3 4])         ; apply + to first two elements, then apply + to that result and the 3rd element, and so forth => 10

Potremmo ovviamente scrivere le nostre funzioni in Clojure utilizzando defn. La definizione delle funzioni di Clojure seguono la forma (defn fn-name [params*] expressions) e inoltre restituiscono il valore dell’ultima espressione nel corpo.

[x]
(+ x 2))(add2 5)     ; => 7

Le espressioni “let” creano e associano le variabili all’interno dello scope lessicale, definito lexical scope, di “let”. Ciò viene fatto nell’espressione (let [a 4] (…)), in cui la variabile “a” presenta un valore di 4 solamente nelle parentesi interne. Queste variabili sono chiamate “locali”.

(defn square-and-add
[a b]
(let [a-squared (* a a)
b-squared (* b b)]
(+ a-squared b-squared)))

(square-and-add 3 4)       ; => 225

Abbiamo, infine, due modi per creare funzioni “anonime”, che possono essere assegnate a una funzionale locale o ad una di ordine superiore.

(fn [x] (* 5 x))          ; anonymous function#(* 5 %)                  ; equivalent anonymous function, where the % represents the function’s argument(map #(* 5 %) [1 2 3])    ; => (5 10 15)

Questo è tutto per le informazione di base. Ora che abbiamo imparato qualche nozione su Clojure, torniamo al machine learning.

Cortex

Cortex è scritta in Clojure, ed è attualmente una delle librerie di machine learning più vaste e in rapida crescita, che utilizza un linguaggio di programmazione funzionale. Il resto dell’articolo si focalizzerà su come costruire un modello di classificazione in Cortex, insieme ai Paradigmi di programmazione funzionale e le tecniche di arricchimento dei dati (data augmentation) richieste.

Data preprocessing

Prendiamo un dataset di frodi legate a carte di credito, fornito da questo sito. Queste dataset è molto sbilanciato, per il fatto che container solo 492 casi di fronde positivi su un totale di 248’807, in pratica lo 0,172%. Ciò ci creerà dei problemi ma, per ora, limitiamoci a guardare i dati e a vedere il funzionamento del modello.

Per assicurare l’anonimità dei dati personali, tutte le caratteristiche originarie, eccetto “time” e “amount”, sono già state trasformate in componenti principali, o PCA (dove ogni entry rappresenta una nuova variabile che contiene le informazioni più rilevanti dei dati grezzi). Una breve occhiata ai dati ci mostra che la prima variabile “time” ha un limitato contenuto informativo, quindi la lasciamo da parte. Di seguito vediamo come appare il nostro gruppo di codici:

 

(ns fraud-detection.core

(:require [clojure.java.io :as io]

[clojure.string :as string] [clojure.data.csv :as csv] [clojure.core.matrix :as mat] [clojure.core.matrix.stats :as matstats]

[cortex.nn.layers :as layers]

[cortex.nn.network :as network]

[cortex.nn.execute :as execute]

[cortex.optimize.adadelta :as adadelta]

[cortex.optimize.adam :as adam]

[cortex.metrics :as metrics]

[cortex.util :as util]

[cortex.experiment.util :as experiment-util]

[cortex.experiment.train :as experiment-train]))

(def orig-data-file “resources/creditcard.csv”)

(def log-file “training.log”)

(def network-file “trained-network.nippy”)

;; Read input csv and create a vector of maps {:data […] :label [..]},

;; where each map represents one training instance in the data

(defonce create-dataset

(memoize

(fn []

(let [credit-data (with-open [infile (io/reader orig-data-file)]

(rest (doall (csv/read-csv infile))))

data (mapv #(mapv read-string %) (map #(drop 1 %) (map drop-last credit-data))) ; drop label and time

labels (mapv #(util/idx->one-hot (read-string %) 2) (map last credit-data))

dataset (mapv (fn [d l] {:data d :label l}) data labels)]

dataset))))

 

Le reti neurali di Cortex utilizzano dati di input in forma di mappe, dove ogni mappa rappresenta un singolo dato con i relativi label associati (per esempio, se ho l’immagine di un cane, il label sarà “cane”). Una classificazione, per esempio, può apparire come [{:data [12 10 38] :label “cat”} {:data [20 39 3] :label “dog“} … ]

Nella nostra funzione per la creazione di dataset ad hoc, vediamo che nel data file di formato comma-separated value, tutte le colonne a parte l’ultima formano i nostri “data” (o caratteristiche), mentre l’ultima colonna rappresenta il label, ovvero l’etichetta. Nel frattempo, trasformiamo i label in one-hot vector (per esempio [0 1 0 0]) basati sulla classe di classificazione. Ciò perché l’ultimo strato soft max nella nostra rete neurale produce un vettore di probabilità di classe, e non il label stesso. Infine, creiamo la mappa da queste due variabili e la salviamo come dataset.

Descrizione del modello

Creare un modello in Cortex è un’operazione piuttosto semplice e diretta. Prima di tutto dobbiamo definire una mappa di parametri superiori che verrà usata successivamente durante l’allenamento. In seguito, per definire il modello, uniamo semplicemente gli strati:

 

(def params

{:test-ds-size      50000 ;; total = 284807, test-ds ~= 17.5%

:optimizer         (adam/adam)   ;; alternately, (adadelta/adadelta)

:batch-size        100

:epoch-count       50

:epoch-size        200000})

(def network-description

[(layers/input (count (:data (first (create-dataset)))) 1 1 :id :data) ;width, height, channels, args

(layers/linear->relu 20) ; num-output & args

(layers/dropout 0.9)

(layers/linear->relu 10)

(layers/linear 2)

(layers/softmax :id :label)])

Dove network-description è un vettore di strati di rete neurale. Il nostro modello consiste in:

  • Uno strato di input
  • Uno strato completamente connesso (e lineare) con la funzione di attivazione ReLu
  • Un layer dropout
  • Un altro strano completamente connesso con ReLu
  • Un strato di output di dimensione 2 , passato attraverso la funzione softmax

Nel primo e ultimo strato, dobbiamo specificare un :id. questo si riferisce alla chiave nella mappa dei dati a cui la nostra rete dovrebbe guardare. Ricordiamo che la mappa risulta come {:data […] :label […]}). Per il nostro strato di input, passiamo in :data id affinché il modello prenda i dati di allenamento nei passaggi successivi. Nello strato finale forniamo, invece, :label come :id, così che possiamo utilizzare il vero label per calcolare il nostro errore.

Allenamento e valutazione

Da qui in poi diventa più complesso. La funzione di allenamento non è in realtà così complicata: Cortex da infatti funzioni preistruite per l’allenamento di alto livello, quindi tutto ciò che dobbiamo fare è settare nostri parametri (la rete, dataset di allenamento e verifica ecc). L’unico avvertimento che vi diamo è che il sistema si aspetta un “infinito” dataset per l’allenamento. tuttavia, Cortex  presenta una funzione infinite-class-balanced-dataset che ci aiuta a trasformarlo.

(defn train)

“Trains network for :epoch-count number of epochs”

[]

(let [network (network/linear-network network-description)

[train-orig test-ds] (get-train-test-dataset)

train-ds (experiment-util/infinite-class-balanced-dataset train-orig

:class-key :label

:epoch-size (:epoch-size params))]

(experiment-train/train-n network train-ds test-ds

:batch-size (:batch-size params)

:epoch-count (:epoch-count params)

:optimizer (:optimizer params)

:test-fn f1-test-fn)))

Quindi arriviamo alla parte complessa: f1-test-fn. Durante l’allenamento, infatti, la funzione train-n si aspetta che le venga fornito un :test-fn che ne verifichi la performance e che determini se debba essere salvata come “best network”. È presente una funzione test di default che individua la perdita cross-entropica (cross-entropy loss)ma il suo valore di perdita non è così semplice da interpretare e, inoltre, non si adatta perfettamente il nostro dataset poco bilanciato. Per risolvere questo problema, andremo a scrivere una nostra personale funzione test.

Ma sorge ora una domanda: come possiamo verificare le performance del nostro modello? La metrica standard nei task di classificazione è solitamente l’accuratezza ma, con il nostro dataset sbilanciato, questa misura è pressoché inutile. Poiché gli esempi positivi contano solo per lo 0,172% del dataset totale, anche un modello che si limiti a prevedere esempi negativi, potrebbe raggiungere il 99.828% di accuratezza. Una percentuale particolarmente elevata, ma nel caso in cui questo modello venisse utilizzato nella realtà, sorgerebbero diversi problemi.

Un migliore gruppo di metriche è quindi: precisione, recupero, e F1 score (o genericamente F-beta).

1 1

1 1

La precisione ci pone quindi di fronte a una domanda: “di tutti gli esempi che ho previsto essere positivi, quale proporzione è di fatto positiva?” Mentre per il recupero ci chiediamo: “di tutti gli esempi  positivi, quale proporzione ho previsto con esattezza essere positiva?”
Visualizzazione di precisione e recupero. Fonte https://en.wikipedia.org/wiki/Precision_and_recall

Il F-beta score (una generalizzazione del tradizionale F1 score) è una media ponderata di precisione e recupero, misurata in una scala da 0 a 1:

2.jpg 2

Quando beta = 1, otteniamo F1 come of 2 * (precisione * recupero) / (precisione + recupero). Solitamente beta rappresenta quante volte recupero debba essere più importante di precisione. Nel nostro modello utilizziamo il F1 score come nostro punteggio da ben approssimare ma registriamo anche i punteggi di precisione e recupero per rilevare il bilanciamento. Questo è il nostro f1-test-fn:

 

(defn f-beta

“F-beta score, default uses F1”

([precision recall] (f-beta precision recall 1))

([precision recall beta]

(let [beta-squared (* beta beta)]

(* (+ 1 beta-squared)

(try                         ;; catch divide by 0 errors

(/ (* precision recall)

(+ (* beta-squared precision) recall))

(catch ArithmeticException e

0))))))

(defn f1-test-fn

“Test function that takes in two map arguments, global info and local epoch info.

Compares F1 score of current network to that of the previous network,

and returns map:

{:best-network? boolean

:network (assoc new-network :evaluation-score-to-compare)}”

[;; global arguments

{:keys [batch-size context]}

;per-epoch arguments

{:keys [new-network old-network test-ds]} ]

(let [batch-size (long batch-size)

test-results (execute/run new-network test-ds

:batch-size batch-size

:loss-outputs? true

:context context)

;;; test metrics

test-actual (mapv #(vec->label [0.0 1.0] %) (map :label test-ds))

test-pred (mapv #(vec->label [0.0 1.0] % [1 0.9]) (map :label test-results))

precision (metrics/precision test-actual test-pred)

recall (metrics/recall test-actual test-pred)

f-beta (f-beta precision recall)

;; if current f-beta higher than the old network’s, current is best network

best-network? (or (nil? (get old-network :cv-score))

(> f-beta (get old-network :cv-score)))

updated-network (assoc new-network :cv-score f-beta)

epoch (get new-network :epoch-count)]

(experiment-train/save-network updated-network network-file)

(log (str “Epoch: ” epoch “\n”

“Precision: ” precision  “\n”

“Recall: ” recall “\n”

“F1: ” f-beta “\n\n”))

{:best-network? best-network?

:network updated-network}))

La funzione esegue la rete sul test set, calcola il F1 score e, infine, aggiorna e salva la rete di conseguenza. Inoltre, stampa ognuna delle nostre metriche di valutazione ad ogni punto. Se ora eseguissimo il REPL, avremmo uno score che sarebbe all’incirca così:

Epoch: 30
Precision: 0.2515923566878981
Recall: 0.9186046511627907
F1: 0.395

Un risultato piuttosto scarso.

Arricchimento dei dati (Data Augmentation)

Eccoci arrivati al problema di cui abbiamo parlato all’inizio di questo articolo, dovuto allo sbilanciamento del dataset. Il modello non presenta ora sufficienti esempi positivi da cui apprendere. Quando richiamiamo experiment-util/infinite-class-balanced-dataset nella nostra funzione di allenamento, stiamo di fatto creando centinaia di copie di ogni istanza di allenamento positiva per bilanciare il nostro dataset. Ne risulta che il modello memorizza quei valori di caratteristiche, piuttosto che apprendere la distinzione tra le classi.

Un modo per ovviare a questo problema è utilizzare l’arricchimento dei dati, con il quale generi amo dati aggiuntivi e artificiale basati sugli esempi che già abbiamo disponibili. Per creare degli esempi di allenamento che siano positivi, dobbiamo aggiungere una qualsiasi quantità di rumore ai delle features vectors (vettori contenenti le caratteristiche) per ogni esempio positivo esistente. La quantità di rumore da noi aggiunta dipenderà dalla varianza di ogni caratteristica nelle classi positive, in modo che le feature con un’ampia varianza siano arricchite con una grande quantità di rumore, e l’opposto per quelle con bassa varianza.

Di seguito il codice per l’arricchimento dei dati:

(defonce get-scaled-variances

(memoize

(fn []

(let [{positives true negatives false} (group-by #(= (:label %) [0.0 1.0]) (create-dataset))

pos-data (mat/matrix (map #(:data %) positives))

variances (mat/matrix (map #(matstats/variance %) (mat/columns pos-data)))

scaled-vars (mat/mul (/ 5000 (mat/length variances)) variances)]

scaled-vars))))

(defn add-rand-variance

“Given vector v, add random vector based off the variance of each feature”

[v scaled-vars]

(let [randv (map #(- (* 2 (rand %)) %) scaled-vars)]

(mapv + v randv)))

(defn augment-train-ds

“Takes train dataset and augments positive examples to reach 50/50 balance”

[orig-train]

(let [{train-pos true train-neg false} (group-by #(= (:label %) [0.0 1.0]) orig-train)

pos-data (map #(:data %) train-pos)

num-augments (- (count train-neg) (count train-pos))

augments-per-sample (int (/ num-augments (count train-pos)))

augmented-data (apply concat (repeatedly augments-per-sample

#(mapv (fn [p] (add-rand-variance p (get-scaled-variances))) pos-data)))

augmented-ds (mapv (fn [d] {:data d :label [0 1]}) augmented-data)]

(shuffle (concat orig-train augmented-ds))))

augment-train-ds prende il nostro dataset di allenamento originario, calcola il numero di arricchimenti necessario per raggiungere un bilanciamento di classe 50/50, e applica poi questi arricchimenti ai nostri esempi esistenti attraverso un qualsiasi vettore di rumore (add-rand-variance) basato sulla varianza consentita (get-scaled-variances). Infine, concateniamo gli esempi arricchiti con il dataset originario ed otteniamo un dataset bilanciato.

Durante l’allenamento, il modello rileva una quantità irrealisticamente ampia di esempi positivi, mentre il set di verifica rimarrà positivo al 0.172%. come risultato, nonostante il modello potrà essere in grado di apprendere in modo migliore la differenza tra le due classi, comunque overfitteremo la nostra classe con di esempi positivi. Per risolvere tale problema, possiamo cambiare le relative soglie in modo da forzare la predizione positiva durante il testing. In altre parole, piuttosto che richiedere al modello almeno il 50% di certezza della positività degli esempi per classificarli come tali, possiamo aumentare la richiesta ad almeno il 70%. Dopo alcuni test, abbiamo notato che la soglia minima dovrebbe essere impostata al 90%. Il codice può essere trovato in  vec->labelfunction nel codice sorgente, ed è richiamato alla linea 31 del f1-test-fn.

Usando il nuovo e arricchito dataset per l’allenamento, i nostri scores si presentano all’incirca così:

Epoch: 25
Precision: 0.8658536585365854
Recall: 0.8255813953488372
F1: 0.8452380952380953

Risultati nettamente migliori rispetto a quelli precedenti.

Conclusioni

Questo modello può ovviamente essere ancora migliorato. Di seguito, alcuni consigli:

  • Tutte le PCA feature sono informative? Osservate la distribuzione dei valori per gli esempi positivi e negativi attraverso le feature e scartate ogni feature che non aiuta a distinguere tra le due classi
  • Vi sono altre architetture di reti neurali, funzioni di attivazione ecc che performano meglio?
  • Vi sono diverse tecniche di arricchimento dei dati che potrebbero performare meglio?
  • Come si rapporta la performance del modello in Cortex rispetto a Keras/Tensorflow/Theano/Caffe?

L’intero codice sorgente per questo progetto può essere trovato qui. Vi incoraggiamo a sperimentare gli step successivi, ad esplorare differenti architetture di reti (qui c’è un ottimo esempio di classificazione di immagini su CNN che potete prendere come riferimento).

 

Utilizzo di deep learning per il riconoscimento di oggetti

Autrice: Joyce Xu

Traduttrice: Sabrina Sala

 

 

1

 

Con la comparsa di veicoli a guida autonoma, sistemi di videosorveglianza intelligenti e varie applicazioni come quella del conteggio delle persone, la richiesta di sistemi di riconoscimento facciale è ormai in continua crescita. Questi sistemi riguardano non solo il riconoscimento e la classificazione degli oggetti presenti nelle immagini, ma anche la localizzazione di ciascuno di essi, tracciando attorno ad essi un appropriato riquadro di delimitazione. Ciò ha reso il sistema di riconoscimento di oggetti un compito sempre più complesso rispetto al suo predecessore nell’ambito della visione artificiale: la classificazione di immagini.

Fortunatamente gli approcci più appropriati per il riconoscimento di oggetti sono, attualmente, estensioni di modelli per la classificazione di immagini. Qualche mese fa Google ha rilasciato una nuova API (interfaccia di programmazione di un’applicazione) di riconoscimento di oggetti in Tensorflow. In questa release troviamo modelli pre-addestrati con i relativi pesi:

Nello scorso articolo, abbiamo parlato delle tre architetture di reti neurali sopra citate (MobileNets, Inception e ResNet), oggi affronteremo invece i modelli di riconoscimento di oggetti per Tensorflow: Faster R-CNN, R-FCN e SSD. Attraverso questo articolo capiremo come il deep learning è applicato a questi modelli per il riconoscimento di oggetti, come questi ultimi si differenzino tra loro e quali siano i punti in comune.

Faster R-CNN

La faster R-CNN è ormai un modello di riferimento per la rilevazione di oggetti basata sul deep learning  (apprendimento profondo), che ha ispirato molti modelli successivi di rilevazione e segmentazione, inclusi i due che andremo ad esaminare. Sfortunatamente non possiamo comprendere appieno la Faster R-CNN senza analizzare R-CNN e Fast R-CNN, sue predecessori.

R-CNN

Possiamo certamente dire che la R-CNN, Region-based Convolutional Neural Network, è la rete che ha dato il via ai giochi. Consiste in tre fasi:

  1. Scansione dell’immagine di input per la rilevazione di possibili oggetti, utilizzando un algoritmo chiamato Selective Search, che estrae approssimativamente 2000 regioni di questa immagine (Region Proposal)
  2. Esecuzione della rete neurale convoluzionale (CNN) in cima a ciascuna regione.
  3. Estrapolazione dell’output da ogni CNN per immetterlo dentro ad:
    1. una SVM (Support Vector Machine) per classificare la regione
    2. Effettuare una regressione lineare per restringere il riquadro di delimitazione dell’oggetto, se esistente.

Le fasi sono illustrate (dal basso verso l’alto) nella figura sottostante

2

2

In altre parole, prima generiamo le possibili regioni, quindi estraiamo le caratteristiche e infine classifichiamo quelle regioni sulla base delle caratteristiche estratte. In sostanza, abbiamo trasformato la rilevazione di oggetti in un problema di classificazione. La R-CNN risulta molto intuitiva ma, sfortunatamente, anche molto lenta.

Fast R-CNN

Lo sviluppo immediatamente successivo è la Fast R-CNN. Questa ricorda l’originale in molte delle sue caratteristiche ma è migliore in termini di velocità di rilevazione per due aspetti:

  1. L’estrazione di caratteristiche viene effettuata prima di estrarre le regioni di interesse, ma utilizzando solo una CNN per l’intera immagine al posto che 2000 CNN sulle 2000 regioni sovrapposte.
  2. La SVM viene rimpiazzata da una funzione softmax e, inoltre, la rete neurale viene utilizzata anche per le previsioni anziché creare un nuovo modello.

Il modello appare similmente a:

 

3 1

Come possiamo vedere dall’immagine, stiamo ora generando proposte regionali basate non sull’immagine originale, ma piuttosto sull’ultima mappa delle caratteristiche estrapolate dalla rete. Ciò ci permette di allenare una sola CNN per l’intera immagine.

In aggiunta, al posto che allenare differenti SVM per classificare ogni oggetto, vi è un singolo strato softmax che genera direttamente la probabilità per la classe di riferimento. Quindi, abbiamo ora una sola rete neurale da allenare, a differenza di quanto succedeva prima dove invece c’era una rete neurale affiancata a molteplici SVM.

La Fast R-CNN performa, perciò, molto meglio in termini di velocità. Tuttavia, rimane un grosso difetto: l’algoritmo di selective search per proporre le possibili regioni.

Faster R-CNN

Siamo giunti ora all’obiettivo iniziale del nostro articolo: Faster R-CNN. L’intuizione è stata quella di rimpiazzare il lento algoritmo di selective search con una rete neurale veloce. Nello specifico essa ha introdotto la region proposal network (RPN), rete di proposta regionale.

Come funziona la RPN:

  • All’ultimo strato di una CNN iniziale, una finestra scorrevole 3×3 si muove attraverso la mappa delle caratteristiche per mapparla poi in una dimensione inferiore (ad esempio 256-d).
  • Per ogni posizione della finestra scorrevole, la RPN genera molteplici regioni possibili basate su vincoli spaziali di dimensioni fisse chiamati riquadri di ancoraggio (anchor boxes).
  • Ogni proposta regionale consiste in:
    • un punteggio (score) per la presenza dell’oggetto in quella determinata regione
    • 4 coordinate rappresentanti il riquadro di delimitazione della regione.

In altre parole, guardiamo alla nostra regione nell’ultima mappa delle caratteristiche, considerando i differenti k riquadri di ancoraggio attorno ad essa. Per ciascun riquadro viene visualizzata l’eventualità che contenga un oggetto e quali siano le coordinate del riquadro. Nell’immagine è rappresentato come appare dalla posizione di una finestra scorrevole:

 

4 1

4 1

Il punteggio 2k rappresenta la probabilità data da softmax per ciascun riquadro k per la presenza di un oggetto. Da notare è che, nonostante la RPN elabori le coordinate dei riquadri di delimitazione, essa non classifica comunque i possibili oggetti: ha il solo scopo di individuare regioni in cui siano presenti oggetti e quindi comunicare le coordinate dei relativi riquadri. Se un riquadro di ancoraggio ha un punteggio, relativo alla presenza di un oggetto, superiore a una determinata soglia, allora quel dato riquadro verrà selezionato come possibile regione.

Avendo ora le nostre possibili regioni, le introduciamo direttamente nella Fast R-CNN. Aggiungiamo uno strato di Pooling, alcuni strati completamente connessi, infine uno strato di classificazione softmax e un regressore dei riquadri di delimitazione (bounding box regressor). Possiamo dire, in un certo senso, che Faster R-CNN = RPN + Fast R-CNN.

 

5 1

5 1

La Faster R-CNN raggiunge così miglior velocità e accuratezza. Nonostante siano stati molteplici i tentativi successivi di aumentare la velocità di riconoscimento degli oggetti, solo pochi modelli sono stati davvero in grado di superare questa rete. In altre parole, la Faster R-CNN non rappresenta certo il metodo più semplice e veloce per la object detection ma presenta tuttora una delle migliori performance. Quindi, la Faster R-CNN in Tensorflow con la Inception ResNet è il modello più lento ma anche il più accurato.

Nonostante la Faster R-CNN possa sembrare complessa, è possibile notare che il cuore della sua struttura è il medesimo di quello originario della R-CNN: identificare possibili regioni di oggetti e classificarle. Questo è l’approccio dominante della maggior parte degli odierni modelli di rilevazione di oggetti, incluso quello che andremo ad analizzare ora.

R-FCN

Ritorniamo per un momento indietro. Abbiamo detto che la Fast R-CNN ha migliorato di molto la sua velocità di rilevazione condividendo una singola CNN per tutte le regioni. Questo meccanismo di base è lo stesso della R-FCN: aumentare la velocità massimizzando il numero di parametri condivisi.

La Region-based FullyConvolutional Net (R-FCN) condivide la totalità della computazione per ogni singolo output.

Da una parte, quando classifichiamo un oggetto vogliamo che il modello risulti invariante alle traslazioni, in modo da identificarlo ovunque esso appaia. Dall’altra, abbiamo però la necessità di conoscere anche un’eventuale varianza alle traslazioni: se il nostro oggetto si trova nell’angolo in alto a sinistra, il modello deve tracciare un riquadro di delimitazione in quel determinato punto. Perciò, come trovare un compromesso tra i due estremi, varianza e invarianza?

La soluzione proposta dalla R-FCN è rappresentata dalle position-sentitive score maps: mappe dei punteggi (relative alle features, estratte tramite convoluzioni, alla quale è attribuito un punteggio in base alla presenza della data classe) sensibili alla posizione degli oggetti che intendiamo classificare.

Ognuna di queste mappe rappresenta una posizione relativa di una classe di oggetti. Per esempio, una mappa dei punteggi potrebbe attivarsi qualora identifichi la parte in alto a destra di un gatto, mentre un’altra potrebbe attivarsi quando rileva la parte inferiore sinistra di una macchina. Essenzialmente queste score maps sono mappe di caratteristiche convoluzionali che sono state allenate a riconoscere determinate parti di ogni oggetto.

La R-FCN funziona in questo modo:

  1. Eseguire una CNN (in questo caso una ResNet) sull’immagine di input.
  2. Aggiungere uno strato convoluzionale che coinvolga tutte le mappe generate dal passaggio precedente in modo da ottenere un insieme di punteggi complessivi dell’immagine. Ottenendo k²(C+1) mappe di punteggio, dove k² rappresenta il numero di posizioni relative per dividere un oggetto (per esempio 3² per una griglia 3×3), e dove C+1 rappresenta il numero di classi più lo sfondo.
  3. Eseguire una RPN per generare le regioni di interesse (RoI)
  4. Dividere ciascuna RoI nelle medesime k² sottoregioni di cui è composta la mappa dei punteggi
  5. Dobbiamo ora verificare se nell’insieme dei punteggi complessivi, ogni sottoregione combaci con la corrispondente posizione relativa di un oggetto. Per esempio, se vogliamo considerare la sottoregione in alto a sinistra, dobbiamo prendere la mappa dei punteggi che corrisponde all’angolo in alto a sinistra di un oggetto e farne una media all’interno della RoI. Questo processo è ripetuto per ogni classe.
  6. Dopo che ogni k² sottoregione ha un valore di “object match” per ogni classe, si fa una media delle sottoregioni per ottenere un punteggio per ogni classe.
  7. Classificare le RoI con una funzione softmax sui rimanenti C+1 vettori dimensionali.

Una R-FCN, avente una RPN che genera le RoI, apparirà all’incirca così:

6 1

6 1

Capire come funzioni questo modello è più semplice se si può visualizzare concretamente ciò che fa. Qui di seguito abbiamo un esempio del suo funzionamento mentre cerca di individuare un neonato.

 

 

 

 

7Figura 4: visualizzazione di una RoI che non si sovrappone correttamente all’oggetto

In parole semplici: la R-FCN considera ogni regione, suddividendole poi in sottoregioni e chiedendosi per ciascuna di queste se corrispondano o meno alla relativa porzione dell’immagine di input. Ad esempio “assomiglia alla porzione in centro a destra di un bambino?”. Ciò viene ripetuto per ogni classe. Se la maggior parte delle sottoregioni risulta corrispondente, allora la RoI viene classificata come neonato, dopo che la funzione softmax viene eseguita su tutte le classi.

In questo modo, la R-FCN è capace di identificare contemporaneamente la varianza alle traslazioni, proponendo differenti regioni di oggetti, e l’invarianza alle traslazioni attraverso il riferimento di ogni regione alle stesse mappe di punteggio. Queste mappe di punteggio dovrebbero imparare a classificare un gatto come tale, ovunque esso appaia. Inoltre, è completamente convoluzionale: ciò significa che la computazione è condivisa nell’intera rete.

Ciò permette alla R-FCN di essere nettamente più veloce rispetto alla Faster R-CNN, raggiungendo comunque lo stesso grado di accuratezza.

SSD

L’ultimo modello che andremo ad analizzare è la Single-Shot Detector (SDD). Come la R-FCN ha raggiunto una velocità molto maggiore rispetto alla Faster R-CNN ma in modo nettamente diverso.

I primi due modelli eseguono region proposal e region classification in due fasi distinte. Inanzitutto, utilizzano una RPN (region proposal network) per generare le regioni di interesse, successivamente classificano queste regioni attraverso i propri strati interamente connessi o gli strati convoluzionali sensibili alla posizione degli oggetti da classificare. La SSD unisce questi due processi in un singolo passaggio, prevedendo simultaneamente i riquadri di delimitazione e le classi, nel momento in cui esamina l’immagine.

Data un’immagine di input ed un insieme di etichette al dataset, la SSD agisce in questo modo:

  • Passa l’immagine attraverso una serie di strati convoluzionali, producendo mappe delle caratteristiche a scale differenti (per esempio 10×10, poi 6×6 e ancora 3×3 ecc.)
  • Per ogni posizione in ciascuna di queste mappe delle caratteristiche, usa un filtro convoluzionale 3×3 per valutare un piccolo set di riquadri di delimitazione predefiniti. Questi ultimi sono l’equivalente dei riquadri di ancoraggio (anchor boxes) della Faster R-CNN.
  • Per ogni riquadro prevede simultaneamente:
    1. Il riquadro del bounding box
    2. La probabilità per ogni classe.
  • Durante l’allenamento fa corrispondere il riquadro effettivo con quello predetto sulla base del metodo Intersection over Union (IoU), ovvero l’Indice di Jaccard. I riquadri meglio predetti verranno etichettati come “positivi”, come anche tutti gli altri riquadri che abbiano un valore IoU >0.5.

L’allenamento della SSD ha però una difficoltà peculiare rispetto ai precedenti modelli. Nei primi due modelli, la RPN permetteva di prendere in considerazione solo ciò che aveva anche solo la minima possibilità di essere un oggetto. Con la SSD, invece, questo primo filtro viene eliminato: esso classifica e traccia riquadri di delimitazione in ogni singola posizione nell’immagine, usando riquadri di differenti forme e di molteplici scale. Ciò ha come risultato l’avere un numero molto più elevato di riquadri dove, però, la maggior parte di questi è negativo (dal momento che non rileva oggetti).

La SSD corregge questo problema in due modi. Per prima cosa utilizza la non-maximum suppression (NMS) per raggruppare diversi riquadri sovrapposti in un unico riquadro. Quindi se ad esempio quattro riquadri di simile forma e grandezza contengono lo stesso oggetto, la NMS permette di tenere il riquadro migliore, il più accurato, e di scartare i restanti. In secondo luogo, utilizza una tecnica chiamata hard negative mining per bilanciare le classi durante l’allenamento. Con questa tecnica, solo un sottogruppo degli esempi negativi con la più elevata loss in fase di training (i cosiddetti falsi positivi) è utilizzato per ogni iterazione in fase di allenamento. La SSD mantiene un rapporto 3:1 tra negativi e positivi.

La sua architettura appare così:

 

8

Come detto poco sopra, vediamo esserci degli “extra feature layers”. Queste mappe delle caratteristiche dalle misure variabili aiutano a individuare oggetti di diverse dimensioni. Eccone un esempio:

9

In mappe delle caratteristiche di dimensioni più piccole (4×4), ogni riquadro ricopre una regione più ampia dell’immagine, permettendo così di individuare oggetti più grandi. La region proposal e la classificazione sono perciò eseguite nello stesso momento: avendo p classi di oggetti, ogni riquadro di delimitazione è associato a un vettore dimensionale (4+p) che produce 4 riquadri di coordinate e p probabilità di classe. In ultima istanza, viene utilizzata anche qui una funzione sotmax per classificare gli oggetti.

Possiamo perciò dire che la SSD non è poi così diversa rispetto ai due modelli precedenti. Tuttavia omette il passaggio di individuazione delle regioni, considerando ogni riquadro in ogni posizione dell’immagine insieme alla sua classificazione. Eseguendo tutti questi processi simultaneamente, la SSD è di certo il più veloce tra i tre modelli presi in considerazione.

Conclusioni

La Faster R-CNN, la R-FCN, e la SSD sono tre dei migliori e più usati modelli per la rilevazione di oggetti. Tutti fanno affidamento su reti neurali convoluzionali per le prime fasi del processo e seguono, all’incirca, lo stesso metodo di proposta delle regioni e classificazione.

 

Guida alle architetture di reti profonde

Autrice: Joyce Xu

Traduttrice: Sabrina Sala

 

 

1

GoogLeNet, 2014

Negli ultimi anni, molto del progresso ottenuto nell’utilizzo del deep learning (apprendimento profondo) per la visione artificiale può essere ricondotto a un gruppo ristretto di architetture di reti neurali. Lasciando da parte la matematica, i codici e i dettagli dell’implementazione, in questo articolo intendiamo analizzare come questi modelli funzionino.

Al momento, Keras domina con sei di questi modelli pre-addestrati già inseriti all’interno della libreria.

  • VGG16
  • VGG19
  • ResNet50
  • Inception v3
  • Xception
  • MobileNet

Le reti VGG, insieme alla precedente AlexNet del 2012, segue lo schema archetipico delle reti convoluzionali classiche: una serie di convoluzioni, max-pooling, strati di attivazione e infine alcuni strati di classificazione completamente connessi. MobileNet è, essenzialmente, una versione semplificata dell’achitettura di Xception, ottimizzata per le applicazioni mobile. Tuttavia, sono stati i rimanenti tre modelli a ridefinire davvero il modo in cui attualmente guardiamo alle reti neurali. Questo articolo si focalizzerà quindi sull’intuizione avuta con le architetture di ResNet, Inception e Xception, e sul perché esse sino diventate il punto di riferimento per i successivi studi di visione artificiale.

ResNet

ResNet è nata da una semplice osservazione: “perché aggiungendo ulteriori strati a reti neurali profonde l’accuratezza non migliora ma, addirittura, peggiora?”

Intuitivamente, reti neurali più profonde non dovrebbero performare peggio di quelle poco profonde, o almeno non durante l’allenamento quando non vi è alcun rischio di overfitting. Prendiamo una rete di esempio con n strati che raggiungono una certa accuratezza. Come minimo, una rete con n+1 strati dovrebbe essere in grado di raggiungere lo stesso grado di accuratezza, copiando i primi n strati ed eseguendo un identity mapping per l’ultimo strato. Allo stesso modo, reti di n+2, n+3 e n+4 strati possono, con lo stesso metodo, ottenere la medesima accuratezza. Tuttavia, al crescere della profondità della rete questo non è sempre vero.

Gli sviluppatori di ResNet hanno ricondotto questo problema all’ipotesi che le mappature dirette sono difficili da allenare. Hanno così proposto un rimedio: a posto che cercare di apprendere da mappature sottostanti da x di H(x), è possibile invece apprendere la differenza tra i due, ovvero il “residuale” e, successivamente, aggiustare quest’ultimo all’input.

Poniamo che il residuale sia F(x)=H(x)-x. Ora la nostra rete cerca di apprendere da F(x)+x.

Ciò ha dato vita a ai famosi blocchi di ResNet (rete residuale):

 

2.jpg

ResNet block

 

Ogni “blocco” di ResNet consiste in una serie di strati e una identity mapping che aggiunge l’input del blocco al su output. Questa operazione di “addizione” viene fatta elemento per elemento. E nel caso in cui l’input e l’output siano di dimensioni differenti, possono essere utilizzate le tecniche di zero-padding o di proiezioni (attraverso convoluzioni 1×1) per creare dimensioni corrispondenti.

Ritornando alla nostra rete esempio, questo metodo può semplificare notevolmente la costruzione di strati di identità di cui è composta. È infatti molto più semplice imparare a spingere F(x) verso 0 e lasciare l’output come x. ResNet fornisce, quindi, agli strati un punto di riferimento x da cui apprendere, piuttosto che partire da zero da una trasformazione di identità.

Questa idea è davvero efficace nella pratica. Prima di questa, nelle reti neurali profonde si riscontrava un problema di annullamento del gradiente, la cui discesa, data dalla minimizzazione della funzione di errore, si riduce esponenzialmente attraverso la retropropagazione degli strati precedenti. In sostanza, la lunga strada attraverso gli strati precedenti rendeva i segni degli errori talmente piccoli da non permettere alla rete di apprendere. Ciò nonostante, grazie all’innovazione introdotta da ResNet, descritta sopra, possiamo ora costruire reti di innumerevoli strati (potenzialmente anche più di mille) che abbiano un elevato grado di accuratezza. Questo è stato un grande passo in avanti, considerato che il modello più profondo creato fino ad allora, era di 22 strati (vincitore della competizione ILSVRC del 2014).

Sono stati pubblicati molti articoli successivi sul deep learning (apprendimento profondo)  ma senza grandi miglioramenti. ResNet rimane, fondamentalmente, il modello che ha cambiato la nostra comprensione delle reti neurali e di come esse apprendano.

La rete di 1000+ strati è open source! La potete travare qui.

Inception

Se ResNet si concentra sulla profondità, Inception Family™ è invece focalizzata sull’estensione. In particolare, gli sviluppatori di Inception erano interessati all’efficienza computazionale dell’allenamento di reti più larghe. In altre parole: come possiamo aumentare la larghezza delle reti neurali senza eccedere la capacità computazionale di un elaboratore?

Il lavoro originario riguardava una componente nota con il nome di “Inception model”. Il cuore di questo modello, racchiudeva due elementi innovativi.

  1. La prima intuizione riguarda operazioni sugli strati. In una tradizionale CNN, ogni strato estrae informazioni dallo strato precedente al fine di trasformare i dati di origine in un differente tipo di informazione. L’output di una convoluzione 5×5 estrae caratteristiche differenti da quelle di un 3×3 o di un max-pooling e così via. Per ogni strato, come possiamo quindi intuire quale kernel fornisca informazioni più rilevanti?

Perché non lasciare quindi che sia il modello a scegliere?

Un modulo Inception elabora molteplici e differenti trasformazioni sugli stessi dati di input, in contemporanea, concatenando i risultati in un singolo output. In altre parole, ogni strato “Inception module” consiste in una convoluzione 5×5, una 3×3 e un max-pool.  È lasciato allo strato successivo la selezione delle features più rilevanti.

3

3

La maggiore densità di informazione dell’architettura di questo modello ha un problema rilevante: il drastico aumento della capacità computazionale necessaria. Non solo i filtri convoluzionali più larghi (es 5×5) sono impegnativi e onerosi da calcolare, ma anche la sovrapposizione di differenti filtri incrementa il numero di mappe delle caratteristiche per ogni strato. Ciò risulta un vero impedimento per il nostro modello.

Vediamola in un altro modo: per ogni filtri che aggiungiamo, dobbiamo eseguire una convoluzione su tutte le mappe di input per calcolare un singolo output. Nell’immagine sottostante vediamo come la creazione di una mappa di output da un singolo filtro implichi la computazione su ogni singola mappa degli strati precedenti.

4

Poniamo che vi siano M mappe di input.  Per ogni filtro aggiuntivo, dobbiamo farlo convolvere lungo tutte le M mappe di input; allo stesso modo, se abbiamo ulteriori N filtri, dovremmo farli convolvere lungo tutte le N*M mappe. In altre parole, “ogni aumento uniforme nel numero di [filtri] risulta in una aumento al quadrato della computazione”. Il nostro modulo Inception ha così triplicato o quadruplicato il numero di filtri. Dal punto di vista computazionale, è un vero guaio.

Ciò porta alla seconda intuizione: usare convoluzioni 1×1 per una riduzione della dimensionalità. Al fine di risolvere la difficoltà computazionale sopra descritta, gli sviluppatori di Inception hanno usato le convoluzioni 1×1 per “filtrare” la profondità degli output. Queste convoluzioni prendo in considerazione un valore alla volta, ma attraverso molteplici canali, e possono inoltre Estrarre informazioni spaziali e comprimerle in una dimensione minore. Per esempio, usando 2° filtri 1×1, un input di dimensione 64x64x100 (con 100 mappe delle caratteristiche) può essere compresso a 64x64x20. Riducendo il numero di mappe di input, gli sviluppatori sono stati in grado di sovrapporre parallelamente differenti trasformazioni di strati, con il risultato di avere reti che erano, al tempo stesso, profonde (avendo numerosi strati) e larghe (con operazioni parallele).

5

5

a) modulo Inception, prima versione b) modulo Inception con riduzione della dimensionalità

la prima versione di Inception, chiamata “GoogLeNet” è quella citata in precedenza da 22 strati che ha vinto la competizione ILSVRC del 2014. Inception v2 e v3, sviluppate un anno dopo, sono migliori rispetto alla versione precedente per diversi fattori: il più rilevante è il refactoring di convoluzioni larghe in altre più piccole e consecutive, che siano quindi più semplici da apprendere. Ad esempio nella Inception v3, la convoluzione 5×5 è stata sostituita da due convoluzione consecutive 3×3.

Inception è diventata rapidamente un riferimento per l’architettura dei modelli successivi. La sua ultima versione, la v4, aggiunge anche connessioni residuale tra ogni modulo, creando un ibrido tra Inception e ResNet. Ciò nonostane, il fattore più importante è che questo modello dimostra la forza di architetture “network-in-network” ben concepite, raggiungendo un nuovo livello nella legittimazione del potere delle reti neurali.

Da notare è che la ricerca sulle versioni successive v2 e v3 è stata pubblicata il giorno immediatamente successivo alla data di uscita della ricerca su ResNet.

Xception

Xception, “extreme inception”, a differenza delle precedenti architetture, ha mutato la nostra comprensione delle reti neurali, in particolare le reti convoluzionali. Come suggerisce il nome, porta il principio di Inception al suo estremo.

L’ipotesi è stata: “le correlazioni spaziali e quelle tra i canali (B/N, RGB) sono sufficientemente svincolati da rendere preferibile non mapparle insieme”

Ciò significa che in una CNN tradizionale, gli stati convoluzionali individuavano correlazioni tra spazio e profondità. Nell’immagine seguente possiamo rivederne il funzionamento.

4

Il filtro considera simultaneamente una dimensione spaziale (ogni quadrato colorato 2×2) e una cross-channel o dimensione di “profondità” (la pila di quattro quadrati). Allo strato di input di un’immagine, questo è l’equivalente di un filtro convoluzionale che esamina un gruppo di pixel 2×2 attraverso tutti i tre canali RGB. Quindi ci poniamo una domanda: che ragione abbiamo di considerare la regione di immagine e i canali simultaneamente?

 

6

Inception comincia a separare questi due elementi: grazie a convoluzioni 1×1 proietta l’input originale in molteplici spazi di input di dimensioni minori, e da ognuno di questi utilizza differenti filtri per trasformare quei piccoli blocchi tridimensionali di dati. Xception va anche oltre: piuttosto che dividere i dati di input in molteplici gruppi compressi, mappa separatamente per eseguire, successivamente, una convoluzione 1×1 in profondità per cogliere le correlazioni cross-channel. Questa operazione è conosciuta con il nome di “depthwise separable convolution” che consiste in una convoluzione spaziale (depthwise convolution) eseguita in modo indipendente per ogni canale, seguita da una convoluzione 1×1 (pointwise convolution) tra i canali. È quindi una ricerca di correlazioni dapprima in uno spazio bidimensionale e, successivamente, in uno spazio unidimensionale. Questa mappatura 2D+1D è più semplice da apprendere, rispetto a una completamente 3D.

 

Xception supera le prestazioni di Inception v3 sui dataset di ImageNet, e lo fa altrettanto bene con dataset per la classificazione di immagini di 17mila classi. In aggiunta, ha lo stesso numero di parametri di modello di Inception, permettendo così un’ottima efficienza. Nonostante Xception sia molto recente, la sua architettura è già entrata a far parte delle applicazioni di Google per la mobile vision, attraverso MobileNet.

Da notare è che lo sviluppatore di Xception, François Chollet, è anche l’autore di Keras.

Prospettive e applicazioni

La conoscenza di ResNet, Inception e Xception è diventata ormai sempre più importante, sia in ambito di ricerca che in quello aziendale. Un’applicazione interessante è il transfer learning, una tecnica del machine learning in cui vengono applicate conoscenze prese da una fonte (prendiamo in esempio un modello ResNet allenato) ad un dominio target . Ciò generalmente comprende due azioni: inizializzare un modello con pesi pre-istruiti da ResNet, Inception ecc, utilizzandolo sia per l’estrazione di caratteristiche, sia per l’ottimizzazione (fine-tuning) degli ultimi strati su un nuovo dataset. Attraverso il transfer learning questi modelli possono essere riutilizzati per qualsiasi operazione ad esso correlata, dal rilevamento di oggetti per veicoli a guida autonoma, alla creazione di didascalie e sottotitoli per registrazioni video.

Potete trovare una guida di Keras ai modelli di fine-tuning qui.