k-means data clustering - vita da studente

34
Università degli Studi del Sannio Facoltà di Ingegneria Corso di Laurea Specialistica in Ingegneria Informatica Corso di Programmazione Concorrente PROGETTO: K-Means Data Clustering Studenti Docente Flavio Pace Prof. Umberto Villano Rita Di Candia Anno Accademico 2009/2010

Upload: others

Post on 19-Oct-2021

4 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: K-Means Data Clustering - Vita Da Studente

Università degli Studi del Sannio

Facoltà di Ingegneria

Corso di Laurea Specialistica in Ingegneria Informatica

Corso di Programmazione Concorrente

PROGETTO:

K-Means Data Clustering

Studenti Docente

Flavio Pace Prof. Umberto Villano

Rita Di Candia

Anno Accademico 2009/2010

Page 2: K-Means Data Clustering - Vita Da Studente

Indice

1. K-MEANS ................................................................................................................................... 1

1.1 Cos’è? ................................................................................................................................... 1

1.2 Vantaggi e svantaggi ........................................................................................................... 2

2. Implementazione ......................................................................................................................... 3

2.1 Sequenziale ........................................................................................................................... 3

2.2 Parallelo ................................................................................................................................ 3

2.3 Risultati ................................................................................................................................ 5

2.3.1 Testbad .......................................................................................................................... 5

2.3.2 1° Soluzione ................................................................................................................... 5

2.3.3 2° Soluzione ................................................................................................................... 8

3 Hadoop ....................................................................................................................................... 15

3.1 Implementazione K-Means con Hadoop ......................................................................... 17

4 Hadoop vs Open MPI ............................................................................................................... 19

Appendice A – Open MPI e Sequenziale ....................................................................................... 23

Appendice B – MapReduce ............................................................................................................. 24

KmeansDriver.java ...................................................................................................................... 24

KmeansMapper.java .................................................................................................................... 28

KmeansCombiner.java ................................................................................................................ 30

KmeansReducer.java ................................................................................................................... 31

Page 3: K-Means Data Clustering - Vita Da Studente

1

1. K-MEANS

1.1 Cos’è?

L'algoritmo K-Means è un algoritmo di clustering, progettato nel 1967 da MacQeen, che permette

di suddividere gruppi di oggetti in K partizioni sulla base dei loro attributi. È una variante

dell'Algoritmo di aspettazione-massimizzazione il cui obiettivo è determinare i K gruppi di dati

generati da distribuzioni gaussiane.

Si assume che gli attributi degli oggetti possano essere rappresentati come vettori, e che quindi

formino uno spazio vettoriale.

L'obiettivo che l'algoritmo si prepone è di minimizzare la varianza totale intra-cluster (o la

deviazione standard). Ogni cluster viene identificato mediante un centroide o punto medio.

L'algoritmo segue una procedura iterativa:

inizialmente crea K partizioni e assegna ad ogni partizione i punti d'ingresso o casualmente o

usando alcune informazioni euristiche;

calcola il centroide di ogni gruppo;

costruisce quindi una nuova partizione associando ogni punto d'ingresso al cluster il cui

centroide è più vicino ad esso;

vengono ricalcolati i centroidi per i nuovi cluster e così via, finché l'algoritmo non converge.

Figura 1.1: Esempio algoritmo K-Means

Come già accennato l’obiettivo è quello di minimizzare la somma delle distanze di ciascun oggetto

dal centroide del cluster cui è assegnato:

Page 4: K-Means Data Clustering - Vita Da Studente

2

Dove d è una misura di dissimilarità generalmente definita dal quadrato della distanza Euclidea. Per

due punti in uno spazio n-dimensionale, P = (p1,p2,...,pn) e Q = (q1,q2,...,qn), la distanza Euclidea è

calcolata come:

Esistono diverse varianti dell’algoritmo che differiscono nella selezione iniziale dei centri dei

cluster, nel calcolo della dissimilarità e nelle strategie per calcolare i centri dei cluster.

1.2 Vantaggi e svantaggi

Per quanto riguarda i vantaggi:

è possibile variare la posizione iniziale dei centroidi per cercare di ridurre la dipendenza dalla

condizioni iniziali;

è efficiente nel gestire grosse quantità di dati. La complessità computazionale dell’algoritmo è

O(tkmn) dove m è il numero di attributi, n il numero di oggetti, k il numero dei cluster e t è il

numero di iterazioni sull’intero data set. In genere k, m, t << n;

spesso l’algoritmo termina con un ottimo locale. Per trovare un ottimo globale possono essere

adottate altre tecniche (deterministic annealing, algoritmi generici) da incorporare al k-means;

Per quanto riguarda gli svantaggi:

funziona solo su valori numerici in quanto minimizza una funzione di costo calcolando la

media dei clusters;

i cluster hanno forma convessa, pertanto è difficile usare il k-means per trovare cluster di forma

non convessa;

occorre fissare a priori k;

si potrebbe verificare la situazione in cui a uno o più cluster non vengono associati dei punti.

Page 5: K-Means Data Clustering - Vita Da Studente

3

2. Implementazione

Schema algoritmo:

Figura 2.1: Struttura dell’algoritmo

2.1 Sequenziale

L’algoritmo sequenziale è tratto dal libro di J. MacQueen "Some Methods for Classification and

Analysis of Multivariate Observations."

2.2 Parallelo

L’implementazione parallela è basata sul parallelismo dei dati. I dati in ingresso sono equamente

divisi tra tutti i processi, mentre i punti scelti come centroidi (o cluster) vengono replicati.

Page 6: K-Means Data Clustering - Vita Da Studente

4

Successivamente alla fine di ogni iterazione, su ogni processo, vengono generati i nuovi cluster,

fino a quando l’algoritmo non termina.

Il file di ingresso è costituito da un numero di punti ognuno dei quali ha 20 coordinate. Mentre in

uscita vengono generati due tipi di file:

nel primo file vengono scritte le coordinate dei k cluster finali che sono stati generati, chiamato

"nomefileinput.cluster_centres";

nel secondo file vengono scritte le coppie “punto-cluster”, ossia il numero del punto e il

numero del cluster al quale è stato associato, chiamato "nomefileinput.membership".

Per il calcolo parallelo è stato utilizzato Open MPI e un numero di processi, detti anche rank, pari a

8.

Il valore del CPU time è dato dalla somma dei tempi impiegati per eseguire ogni singolo ciclo.

Alla fine dell’esecuzione dell’algoritmo tutti i processi mandano i dati al rank 0, il quale è l’unico

responsabile della scrittura dei risultati sul file di output.

RANK0 RANK1 RANK2 RANK3

FILE_OUTPUT

Figura 2.2: Tecnica di scrittura dei dati nel file di output

Per calcolare l’I/O time, ossia il tempo impiegato dal rank 0 per scrivere i risultati nel file di output,

sono state utilizzate due soluzioni:

la prima soluzione consiste nel calcolare tale valore facendo la somma del tempo speso per la

lettura dei punti dal file in input più la somma del tempo speso per la scrittura del file di uscita;

la seconda soluzione consiste nello spostare la scrittura a fine ciclo ed introdurre una variabile

str1 che raccoglie prima tutti i dati e successivamente fa la scrittura su file.

Page 7: K-Means Data Clustering - Vita Da Studente

5

2.3 Risultati

2.3.1 Testbad

Il test è stato effettuato applicando due tipi di input:

un file da 188 MB contenente 1 milione di punti ognuno con 20 coordinate;

un file da 944 MB contenente 5 milioni di punti ognuno con 20 coordinate.

L’esecuzione è stata effettuata con due differenti tipi di infrastruttura di rete:

Myrinet

Fast Ethernet

Inoltre le dimensioni dei due tipi di file ottenuti come output sono:

output ottenuto dall’esecuzione di 1 milione di punti:

il file “.cluster_centres” è da 9.5 KB;

il file “.membership” è da 9.3 MB;

output ottenuto dall’esecuzione di 5 milioni di punti:

il file “.cluster_centres” è da 9.5 KB;

il file “.membership” è da 51 MB.

2.3.2 1° Soluzione

Di seguito sono riportati i risultati ottenuti dalla prima soluzione. Sono stati messi a confronto due

tipi di esecuzione effettuate:

Sequenziale;

Open MPI attraverso Fast Ethernet.

Page 8: K-Means Data Clustering - Vita Da Studente

6

Figura 2.3: Risultati prima soluzione con input 1 milione di punti e 8 processi

8

CPU time I/O time Total time Speed-up Efficienza % real user sys

MPI_Ethe 61,21 24,88 86,09 5,26 65,74% 1m24.406s 0m0.389s 0m0.068s

Seq 436,63 16,13 452,76 7m32.762s 7m32.339s 0m0.392s

MPI_Ethe 163,53 24,85 188,38 6,55 81,92% 3m6.646s 0m0.363s 0m0.079s

Seq 1218,6 15,96 1234,56 20m34.566s 20m34.204s 0m0.361s

MPI_Ethe 748,04 24,98 773,02 7,65 95,59% 12m51.262s 0m0.373s 0m0.080s

Seq 5895,22 16,14 5911,36 98m31.366s 98m31.009s 0m0.374s50 CLUSTER Float

Risultati 1 milione n° processi

10 CLUSTER Float

20 CLUSTER Float

0

1000

2000

3000

4000

5000

6000

MPI_Ethe Seq MPI_Ethe Seq MPI_Ethe Seq

seco

ndi

CPU time I/O time

10 CLUSTER 20 CLUSTER 50 CLUSTER

Page 9: K-Means Data Clustering - Vita Da Studente

7 Figura 2.4: Risultati prima soluzione con input 5 milioni di punti e 8 processi

8

CPU time I/O time Total time Speed-up Efficienza % real user sys

MPI_Ethe 469,45 123,68 593,13 6,19 77,41% 9m41.211s 0m0.393s 0m0.079s

Seq 3593,16 79,94 3673,1 61m13.112s 61m11.152s 0m1.903s

MPI_Ethe 1046,75 122,28 1169,03 7,08 88,44% 19m17.091s 0m0.371s 0m0.079s

Seq 8190,06 81,44 8271,5 137m51.518s 137m49.586s 0m1.924s

MPI_Ethe 2981,46 122,12 3103,58 7,67 95,88% 51m31.664s 0m0.377s 0m0.077s

Seq 23724,23 82,27 23806,5 396m46.502s 396m44.732s 0m1.880s

n° processi

50 CLUSTER Float

Risultati 5 milioni

10 CLUSTER Float

20 CLUSTER Float

0

5000

10000

15000

20000

25000

MPI_Ethe Seq MPI_Ethe Seq MPI_Ethe Seq

seco

ndi

CPU time I/O time

10 CLUSTER 20 CLUSTER 50 CLUSTER

Page 10: K-Means Data Clustering - Vita Da Studente

8

2.3.3 2° Soluzione

Di seguito sono riportati i risultati ottenuti dalla seconda soluzione, mettendo a confronto, in questo

caso, i tre tipi di esecuzione effettuate:

Sequenziale;

Open MPI attraverso Fast Ethernet;

Open MPI attraverso Myrinet.

Come si può notare dai risultati questa seconda soluzione è più prestante, sia in termini di

computazione e che di I/O, rispetto alla precedente soluzione, in quanto si è passati, per quanto

riguarda il file da 1 milione di punti, da un tempo medio pari a ~25 sec a un tempo medio pari a ~19

sec, mentre per quanto riguarda il file da 5 milioni di punti, si è passati da un tempo medio di ~123

sec a un tempo medio di ~95 sec.

Page 11: K-Means Data Clustering - Vita Da Studente

9

Figura 2.5: Risultati seconda soluzione con input 1 milione di punti e 8 processi

8

CPU time I/O time Total time Speed-up Efficienza % real user sys

MPI_Myri 57,89 18,73 76,62 5,91 73,86% 1m18.118s 0m0.403s 0m0.074s

MPI_Ethe 61,12 19,9 81,02 5,59 69,85% 1m19.265s 0m0.390s 0m0.074s

Seq 436,63 16,13 452,76 7m32.762s 7m32.339s 0m0.392s

MPI_Myri 55,09 18,91 74 6,14 76,69% 1m14.621s 0m0.383s 0m0.081s

MPI_Ethe 60,23 23,27 83,5 5,44 67,96% 1m19.278s 0m0.376s 0m0.078s

Seq 437,84 16,15 453,99 7m34.038s 7m33.483s 0m0.487s

MPI_Myri 159,13 18,76 177,89 6,94 86,75% 2m58.601s 0m0.389s 0m0.076s

MPI_Ethe 163,39 19,79 183,18 6,74 84,25% 3m1.426s 0m0.368s 0m0.077s

Seq 1218,6 15,96 1234,56 20m34.566s 20m34.204s 0m0.361s

MPI_Myri 155,92 18,71 174,63 7,22 90,19% 2m55.255s 0m0.388s 0m0.088s

MPI_Ethe 161,28 23,28 184,56 6,83 85,34% 3m0.280s 0m0.379s 0m0.077s

Seq 1243,83 16,13 1259,96 20m59.971s 20m59.468s 0m0.501s

MPI_Myri 739,34 18,71 758,05 7,80 97,48% 12m38.761s 0m0.372s 0m0.088s

MPI_Ethe 747,04 19,82 766,86 7,71 96,36% 12m45.121s 0m0.380s 0m0.066s

Seq 5895,22 16,14 5911,36 98m31.366s 98m31.009s 0m0.374s

MPI_Myri 506,09 19,15 525,24 7,72 96,55% 8m45.856s 0m0.356s 0m0.100s

MPI_Ethe 512,38 23 535,38 7,58 94,72% 8m51.100s 0m0.370s 0m0.077s

Seq 4040,82 16,28 4057,1 67m37.100s 67m36.622s 0m0.489s

50 CLUSTER

Float

Double

Risultati 1 milione n° processi

10 CLUSTER

Float

Double

20 CLUSTER

Float

Double

Page 12: K-Means Data Clustering - Vita Da Studente

10 Figura 2.6: Rappresentazione risultati seconda soluzione con input 1 milione di punti e 8 processi

0

1000

2000

3000

4000

5000

6000

seco

ndi

CPU time I/O time

10 CLUSTER 20 CLUSTER 50 CLUSTER

FLOAT

FLOAT

FLOAT

DOUBLE

DOUBLE

DOUBLE

Page 13: K-Means Data Clustering - Vita Da Studente

11

Figura 2.7: Risultati seconda soluzione con input 5 milioni di punti e 8 processi

8

CPU time I/O time Total time Speed-up Efficienza % real user sys

MPI_Myri 452,81 93,2 546,01 6,73 84,09% 9m6.309s 0m0.380s 0m0.092s

MPI_Ethe 469,18 100,92 570,1 6,44 80,54% 9m18.192s 0m0.370s 0m0.079s

Seq 3593,16 79,94 3673,1 61m13.112s 61m11.152s 0m1.903s

MPI_Myri 447,36 96,57 543,93 6,70 83,74% 9m3.717s 0m0.384s 0m0.077s

MPI_Ethe 497,48 145,21 642,69 5,67 70,87% 9m52.697s 0m0.372s 0m0.080s

Seq 3563,27 80,63 3643,9 60m43.920s 60m41.394s 0m2.424s

MPI_Myri 1025,04 93 1118,04 7,40 92,48% 18m38.316s 0m0.380s 0m0.078s

MPI_Ethe 1044,69 99,21 1143,9 7,23 90,39% 18m51.980s 0m0.380s 0m0.082s

Seq 8190,06 81,44 8271,5 137m51.518s 137m49.586s 0m1.924s

MPI_Myri 1020,27 95,95 1116,22 7,37 92,16% 18m36.006s 0m0.374s 0m0.091s

MPI_Ethe 1045,22 115,82 1161,04 7,09 88,60% 18m56.467s 0m0.384s 0m0.097s

Seq 8147,44 81,92 8229,36 137m9.369s 137m5.976s 0m2.556s

MPI_Myri 2947,06 92,18 3039,24 7,83 97,91% 50m39.534s 0m0.381s 0m0.089s

MPI_Ethe 2981,83 99,25 3081,08 7,73 96,58% 51m9.175s 0m0.381s 0m0.072s

Seq 23724,23 82,27 23806,5 396m46.502s 396m44.732s 0m1.880s

MPI_Myri 3502,48 93,41 3595,89 7,81 97,61% 59m55.675s 0m0.370s 0m0.099s

MPI_Ethe 3528,11 114,95 3643,06 7,71 96,35% 60m18.422s 0m0.387s 0m0.085s

Seq 27997,48 82,53 28080,01 468m0.012s 467m57.314s 0m2.520s

50 CLUSTER

Float

Double

Risultati 5 milioni n° processi

10 CLUSTER

Float

Double

20 CLUSTER

Float

Double

Page 14: K-Means Data Clustering - Vita Da Studente

12 Figura 2.8: Rappresentazione risultati seconda soluzione con input 5 milioni di punti e 8 processi

0

5000

10000

15000

20000

25000

30000

seco

ndi

CPU time I/O time10 CLUSTER 20 CLUSTER 50 CLUSTER

FLOAT

FLOAT

FLOAT

DOUBLE

DOUBLE

DOUBLE

Page 15: K-Means Data Clustering - Vita Da Studente

13

L'implementazione parallela considera i dati in ingresso come variabili float (rapprentazione su 4

byte), valutando i dati ottenuti dalle varie misurazioni abbiamo notato che la differenza tra i due tipi

di infrastruttura di rete utilizzati è minima, come mostrato in Figura 2.5 e 2.7. Questa differenza

minima è data dal fatto che tra i vari processi non c'è molta comunicazione, come riportato di

seguito, in modo da non far prevalere nettamente una infrastruttura su un'altra.

--------------------------------------------------------------------------------------------

@--- Callsites: 24 ----------------------------------------------------------------------

--------------------------------------------------------------------------------------------

ID Lev File/Address Line Parent_Funct MPI_Call

1 0 mpi_kmeans.c 131 mpi_kmeans Allreduce

2 0 mpi_io.c 208 mpi_write File_write

3 0 mpi_io.c 216 mpi_write File_close

4 0 mpi_main.c 153 main Allreduce

5 0 mpi_io.c 101 mpi_read Bcast

6 0 mpi_kmeans.c 129 mpi_kmeans Allreduce

7 0 mpi_io.c 102 mpi_read Bcast

8 0 mpi_io.c 274 mpi_write Recv

9 0 mpi_kmeans.c 144 mpi_kmeans Allreduce

10 0 mpi_io.c 282 mpi_write File_write

11 0 mpi_main.c 119 main Barrier

12 0 mpi_kmeans.c 105 mpi_kmeans Allreduce

13 0 mpi_io.c 211 mpi_write File_write

14 0 mpi_io.c 255 mpi_write File_open

15 0 mpi_io.c 191 mpi_write File_open

16 0 mpi_io.c 268 mpi_write File_write

17 0 mpi_io.c 213 mpi_write File_write

18 0 mpi_main.c 194 main Reduce

19 0 mpi_main.c 192 main Reduce

20 0 mpi_io.c 121 mpi_read Send

21 0 mpi_io.c 284 mpi_write File_close

22 0 mpi_main.c 161 main Bcast

23 0 mpi_io.c 287 mpi_write Send

24 0 mpi_io.c 145 mpi_read Recv

Page 16: K-Means Data Clustering - Vita Da Studente

14

--------------------------------------------------------------------------------------------

@--- Aggregate Sent Message Size (top twenty, descending, bytes) ----------

--------------------------------------------------------------------------------------------

Call Site Count Total Avrg Sent%

Send 20 7 7e+07 1e+07 93.69

Send 23 7 3.5e+06 5e+05 4.68

Allreduce 6 1432 1.15e+06 800 1.53

Allreduce 1 1432 5.73e+04 40 0.08

--------------------------------------------------------------------------------------------

@--- Aggregate I/O Size (top twenty, descending, bytes) -----------------------

--------------------------------------------------------------------------------------------

Call Site Count Total Avrg I/O%

File_write 16 125000 1.01e+06 8.11 99.80

File_write 13 200 1.9e+03 9.49 0.19

File_write 10 7 63 9 0.01

File_write 2 10 20 2 0.00

File_write 17 10 10 1 0.00

Come ulteriore controprova abbiamo effettuato una modifica sul tipo di dato in ingresso, passando

da un tipo float a un tipo double (rappresentazione su 8 byte).

Page 17: K-Means Data Clustering - Vita Da Studente

15

3 Hadoop

Hadoop è un framework sviluppato dalla community Open-Source per applicazioni parallele su

cluster di grandi dimensioni. Sviluppato con tecnologia Java, è stato pensato per l’elaborazione di

grosse quantità di dati in applicazioni distribuite.

Uno dei paradigmi computazionali utilizzati dal framework è il MapReduce, il quale divide

l’applicazione in piccoli task, ed ognuno di essi potrà essere eseguito su uno o più nodi in modo da

rendere la nostra applicazione distribuita, senza l’ausilio di una particolare sintassi grammaticale.

Figura 3.1: Descrizione del processo MapReduce

MapReduce è diventato popolare grazie a Google che lo utilizza per elaborare ogni giorno molti

petabyte di dati. E’ costituito da due task scritti dall’utente, chiamati Map e Reduce, e da un

Framework che splitta l’input in ingresso in data set indipendenti che vengono processati in modo

parallelo su un cluster.

Il Map task legge un insieme di record da un file di input, svolge le operazioni di filtraggio e le

trasformazioni desiderate, quindi produce una serie di record di output nella forma convenuta (key,

value).

Dopo essere stati raccolti dal Framework i record di input vengono raggruppati per chiavi

(attraverso operazioni di sorting o hashing) e sottoposti al task Reduce. Come per il task Map,

Reduce esegue una elaborazione arbitraria attraverso un linguaggio general purpose. Di

conseguenza può compiere qualsiasi sorta di operazioni sui record. Per esempio può elaborare

Page 18: K-Means Data Clustering - Vita Da Studente

16

alcune funzioni addizionali per altri campi dati del record. Ciascuna istanza Reduce può scrivere

record a un file di output e quest’ultimo rappresenta una parte della risposta soddisfatta da una

elaborazione MapReduce.

La coppia key/value prodotta dal task Map può contenere qualsiasi tipo di dati nel campo assegnato

al valore del campo.

I sistemi attuali implementano MapReduce utilizzando linguaggi come Java, C++, Python, Perl,

Ruby, e altri.

Le coppie key/value utilizzate nell’elaborazione MapReduce possono essere archiviate in un file o

in un database.

In aggiunta è stato implementato un file system distribuito (HDFS), il quale è stato pensato per

avere una elevata larghezza di banda ed un solido meccanismo di recovery-failure.

Hadoop ha un’architettura master-slave, con un unico host master e con host slave multipli. L’host

master gestisce:

JobTracker il quale schedula e gestisce tutti i task appartenenti a un processo in esecuzione;

NameNode il quale gestisce il file system HDFS e regola l’accesso ai file da parte dei clienti

Mentre l’host slave gestisce:

TaskTracker il quale lancia i task sul proprio host sulla base delle istruzioni ricevute dal

JobTracker; inoltre tiene traccia dei progressi di ogni task sul proprio host;

DataNode il quale fornisce blocchi di dati, i quali sono memorizzati sul proprio disco locale, ai

clienti HDFS.

Figura 3.2: Architettura Hadoop

Page 19: K-Means Data Clustering - Vita Da Studente

17

3.1 Implementazione K-Means con Hadoop

In figura è riportato un esempio di come funziona l’algoritmo K-Means attraverso MapReduce:

Figura 3.3: Implementazione dell’algoritmo K-Means con Hadoop

0 Vengono scelti come cluster iniziali i primi k punti del file in input, dove k è il numero di

cluster scelti;

1 Il set dati in ingresso viene suddiviso in N parti, successivamente ogni parte viene inviata ad un

mapper;

2 Nella funzione Map viene calcolata la distanza tra ogni punto e ogni centro del cluster (cluster

center), ogni punto è etichettato con l'indice del cluster avente distanza minima. Il Mapper

genera in uscita le coppie key-value, dove la key è il numero del cluster assegnato a ogni punto

e la value sono le coordinate del punto;

3 Tutti i punti dello stesso cluster (tutti i record con la stessa key) vengono inviati ad un unico

Reducer, nella quale vengono calcolate le nuove coordinate dei k cluster attraverso la media dei

punti, l’output del Reducer è dato dal numero del cluster con le corrispondenti nuove

coordinate;

Page 20: K-Means Data Clustering - Vita Da Studente

18

4 Successivamente le nuove coordinate vengono confrontate con quelle originali, se sono uguali

oppure la loro differenza è all’interno di una soglia prefissata, allora vuol dire che i cluster

calcolati sono quelli finali e il programma termina, altrimenti vengono considerati gli ultimi

cluster calcolati e si ripete iterativamente il procedimento dal punto 2 al punto 4.

Molti delle implementazioni effettuate con MapReduce sono limitate dalla larghezza di banda

disponibile sul cluster, quindi conviene ridurre al minimo la quantità di dati trasferiti tra i task Map

e Reduce. Per tale motivo spesso Hadoop consiglia di utilizzare una funzione Combiner che riceve

in ingresso l’output della funzione Map e fornisce in uscita l’input della funzione Reduce. La

funzione Combiner è introdotta per cercare di ottimizzare l’implementazione, nel nostro caso è stata

molto utile in quanto, avendo un solo Reducer a disposizione, esso fungeva da collo di bottiglia. La

funzione Combiner non sostituisce la funzione Reduce, ma contribuisce a ridurre la quantità di dati

che vengono scambiati tra Map e Reduce.

Input Output

Mapper <num point, coords point> < num cluster, coords point associate >

Combiner <num cluster, coords point associate> < num cluster, sum partial point >

Reducer < num cluster, sum partial point > <num cluster, new coords cluster>

Figura 3.4: Input e Output della funzione Map, Combiner e Reducer

Page 21: K-Means Data Clustering - Vita Da Studente

19

4 Hadoop vs Open MPI

In questo capitolo mettiamo a confronto l’implementazione dell’algoritmo K-Means sviluppato con

OpenMPI e Hadoop.

Per poter correttamente interpretare i risultati ottenuti è da tenere in considerazione che il

Framework Hadoop è stato pensato e ideato per:

una quantità di dati maggiori di 1 TB;

massively parallel, cioè con sistemi distribuiti aventi centinai o migliaia di CPUs;

facile implementazione:

linguaggi Object Oriented;

il programmatore non ha bisogno di conoscere i dettagli strutturali del sistema di calcolo.

Fault-tolerance.

Di seguito sono riportati i risultati ottenuti, mettendo a confronto i valori ottenuti con Hadoop e i

valori ottenuto con Open MPI.

Page 22: K-Means Data Clustering - Vita Da Studente

20 Figura 4.1: Confronto tra Hadoop e Open MPI mettendo in input 1 milione di punti e 4 processi

4

Total time Loop time

Map-Red 10689 71/72

Map-Comb-Red 7104 46/47

Open MPI Ethe 135 0,64

Map-Red 8837 71/72

Map-Comb-Red 5961 46/47

Open MPI Ethe 360 1,22

Map-Red 8892 75

Map-Comb-Red 5995 50

Open MPI Ethe 1502 3

Risultati 1 milione n° processi

10 CLUSTER

20 CLUSTER

50 CLUSTER

0

2000

4000

6000

8000

10000

12000

seco

nd

i

Total time

10 CLUSTER 20 CLUSTER 50 CLUSTER

Page 23: K-Means Data Clustering - Vita Da Studente

21 Figura 4.2: Confronto tra Hadoop e Open MPI mettendo in input 5 milioni di punti e 4 processi

4

Total time Loop time

Map-Comb-Red 9268 68/69

Open MPI 981 3,2

Map-Comb-Red 14251 75

Open MPI 2131 6,1

Map-Comb-Red 28038 78

Open MPI 6007 14,73

Risultati 5 milioni n° processi

10 CLUSTER

20 CLUSTER

50 CLUSTER

0

5000

10000

15000

20000

25000

30000

seco

nd

i

Total time

10 CLUSTER 20 CLUSTER 50 CLUSTER

Page 24: K-Means Data Clustering - Vita Da Studente

22

I risultati ottenuti sono stati confrontati con i risultati pubblicati da Wei Jiang, Vignesh T. Ravi e

Gagan Agrawal nella seguente documentazione:

http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=05289199

3.1 Osservazioni

Con l’introduzione del task Combiner abbiamo ottenuto un’ottimizzazione sul tempo di

esecuzione di ogni singolo loop del 36%, in quanto si è passati da un tempo pari a ~71 sec a un

tempo pari a ~46 sec, applicando come input 1 milione di punti.

Al variare della quantità di dati in input la variazione del tempo impiegato per effettuare ogni

singolo loop è minima, cosa che non accade con OpenMPI. Ciò implica che la maggior parte

del tempo speso da Hadoop è da imputare all’inizializzazione e alla gestione del Framework.

Per la stessa quantità di dati in input il numero di loop impiegati da OpenMPI e Hadoop per

eseguire l’algoritmo differisce. In quanto non è stato possibile implementare lo stesso

algoritmo per il calcolo del valore del threshold a causa dell’implementazione del Framework

stesso.

Page 25: K-Means Data Clustering - Vita Da Studente

23

Appendice A – Open MPI e Sequenziale

Di seguito sono riportati i comandi utilizzati per l’esecuzione dell’algoritmo K-Means:

Comando esecuzione over eth0

mpirun -np 8 --mca btl_tcp_if_include eth0 -machinefile allmachine

Simple_Kmeans_fileDouble_mpiP/mpi_main -i /state/partition1/fpace/file_5m_20c -n

10 -o

Comando Esecuzione Over myri0

mpirun -np 8 --mca btl_tcp_if_include myri0 -machinefile allmachine

Simple_Kmeans_fileDouble_mpiP/mpi_main -i /state/partition1/fpace/file_1m_20c -n

10 -o

Comando Esecuzione Sequenziale

Simple_Kmeans_fileDouble_mpiP/seq_main -i /state/partition1/fpace/file_1m_20c -n 10

–o

Page 26: K-Means Data Clustering - Vita Da Studente

24

Appendice B – MapReduce

Di seguito è riportato il codice, in linguaggio Java, dell’implementazione dell’algoritmo K-

Means attraverso MapReduce:

KmeansDriver.java

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.PrintStream;

import java.util.HashMap;

import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FSDataInputStream;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IOUtils;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.util.GenericOptionsParser;

public class KmeansDriver {

public static void main(String[] args) throws Exception {

long start, start_loop, stop, stop_loop;

//start global timer

start = System.currentTimeMillis();

Configuration conf = new Configuration();

boolean converged=true;

String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();

if (otherArgs.length != 4) {

System.err.println("Usage: KmeansDriver <in_directory> <out_directory>

<in_cluster/cluster_file> num_coord_point");

System.exit(2);

}

Page 27: K-Means Data Clustering - Vita Da Studente

25

int i=0;

do{

//set global coord value from commandline

conf.set("my.num.coord", otherArgs[3]);

if(i==0){

conf.set("my.cluster.coord",otherArgs[2] );

}

else

if(i==1)

conf.set("my.cluster.coord",otherArgs[1]+"0"+"/part-r-00000" );

else

conf.set("my.cluster.coord",otherArgs[1]+(i-1)+"/part-r-00000");

Job job = new Job(conf, "Kmeans_unicondor");

//only one reduce

job.setNumReduceTasks(1);

job.setJarByClass(KmeansDriver.class);

job.setMapperClass(KmeansMapper.class);

job.setCombinerClass(KmeansCombiner.class);

job.setReducerClass(KmeansReducer.class);

job.setOutputKeyClass(Text.class);

job.setOutputValueClass(Text.class);

FileInputFormat.addInputPath(job, new Path(otherArgs[0]));

FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]+i));

//start timer loop

start_loop = System.currentTimeMillis();

job.waitForCompletion(true);

//check if cluster_centroid are equal

if(i>=2)

converged=isConverged(i,Integer.parseInt(otherArgs[3]), otherArgs[1]);

i++;

//stop timer loop

stop_loop = System.currentTimeMillis();

System.out.println("Time_loop_"+(i-1) +": "+ (stop_loop - start_loop)/1000 + " s");

}

while(i<500 && converged);

System.out.println("Clustering...");

//start timer clustering

long start_clustering = System.currentTimeMillis();

clustering(otherArgs[0],otherArgs[1]+"points",conf.get("my.cluster.coord"),otherArg

s[3]);

Page 28: K-Means Data Clustering - Vita Da Studente

26

//stop timer clustering

long stop_clustering = System.currentTimeMillis();

System.out.println("Time Clustering: " + (stop_clustering - start_clustering)/1000 + "

s");

//stop timer programma

stop = System.currentTimeMillis();

System.out.println("Time Total: " + (stop - start)/1000 + " s");

}

private static boolean isConverged(int iteration, int num_coord, String dir_output) throws

IOException {

boolean ret=true;

ByteArrayOutputStream byte1=new ByteArrayOutputStream();

PrintStream out2 = new PrintStream(byte1);

HashMap<String,double[]> cluster= new HashMap<String,double[]>();

HashMap<String,double[]> cluster1= new HashMap<String,double[]>();

Configuration conf = new Configuration();

FileSystem fs = FileSystem.get(new Path(dir_output+(iteration-1)).toUri(),conf);

FSDataInputStream in = null, in1=null;

try {

in = fs.open(new Path(dir_output+(iteration-1)+"/part-r-00000"));

IOUtils.copyBytes(in, out2, 4096, false);

String s=byte1.toString();

String lines[]= s.split("\n");

for(int i=0; i<lines.length; i++){

double[] centers= new double[num_coord];

StringTokenizer itr = new StringTokenizer(lines[i]);

String id_cluster= itr.nextElement().toString();

int j=0;

while(itr.hasMoreElements()){

centers[j]=(Double.parseDouble(itr.nextElement().toString()));

j++;

}

cluster.put(id_cluster, centers);

}

ByteArrayOutputStream byte2=new ByteArrayOutputStream();

PrintStream out3 = new PrintStream(byte2);

Page 29: K-Means Data Clustering - Vita Da Studente

27

FileSystem fs1 = FileSystem.get(new Path(dir_output+iteration).toUri(),conf);

in1 = fs1.open(new Path(dir_output+iteration+"/part-r-00000"));

IOUtils.copyBytes(in1, out3, 4096, false);

String s1=byte2.toString();

String lines1[]= s1.split("\n");

for(int i=0; i<lines1.length; i++){

double[] centers1= new double[num_coord];

StringTokenizer itr = new StringTokenizer(lines1[i]);

String id_cluster= itr.nextElement().toString();

int j=0;

while(itr.hasMoreElements()){

centers1[j]=(Double.parseDouble(itr.nextElement().toString()));

j++;

}

cluster1.put(id_cluster, centers1);

}

}

finally {

IOUtils.closeStream(in);

}

int cont=0;

double[] first_cluster;

double[] second_cluster;

for(String key :cluster.keySet()){

first_cluster = cluster.get(key);

second_cluster = cluster1.get(key);

if(KmeansUtil.isEqualThresHold(first_cluster, second_cluster)) {

cont++;

}

}

if(cont==cluster.size()) {

ret=false;

//debug

System.out.println("PATH is: " +dir_output+(iteration)+"/part-r-00000");

System.out.println("PATH is: " +dir_output+(iteration-1)+"/part-r-00000");

System.out.println("debug all clusters are equal");

System.out.println("HashMap size:"+ cluster.size());

System.out.println("HashMap1 size:"+ cluster1.size());

System.out.println("Cont :" + cont);

}

return ret;

Page 30: K-Means Data Clustering - Vita Da Studente

28

}

public static void clustering(String input, String output,String cluster, String num_coord)

throws Exception {

Configuration conf = new Configuration();

conf.set("my.num.coord", num_coord);

conf.set("my.cluster.coord",cluster );

Job job = new Job(conf, "Kmeans_unicondor_clustering");

job.setJarByClass(KmeansDriver.class);

job.setMapperClass(KmeansMapper.class);

job.setOutputKeyClass(Text.class);

job.setOutputValueClass(Text.class);

FileInputFormat.addInputPath(job, new Path(input));

FileOutputFormat.setOutputPath(job, new Path(output));

job.waitForCompletion(true);

}

}

KmeansMapper.java

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.PrintStream;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Set;

import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FSDataInputStream;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IOUtils;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Mapper;

public class KmeansMapper extends Mapper<Object, Text, Text, Text>{

private HashMap<String,double[]> meansCluster= new HashMap<String,double[]>();

@Override

public void setup(Context context){

try {

Page 31: K-Means Data Clustering - Vita Da Studente

29

ByteArrayOutputStream byte1=new ByteArrayOutputStream();

PrintStream out2 = new PrintStream(byte1);

String uri = context.getConfiguration().get("my.cluster.coord");

Configuration conf = new Configuration();

FileSystem fs = FileSystem.get(new Path(uri).toUri(),conf);

FSDataInputStream in = null;

try {

in = fs.open(new Path(uri));

IOUtils.copyBytes(in, out2, 4096, false);

String s=byte1.toString();

String lines[]= s.split("\n");

for(int i=0; i<lines.length; i++){

double[] centers= new

double[Integer.parseInt(context.getConfiguration().get("my.num.coord"))];

StringTokenizer itr = new StringTokenizer(lines[i]);

String id_cluster= itr.nextElement().toString();

int j=0;

while(itr.hasMoreElements()){

centers[j]=(Double.parseDouble(itr.nextElement().toString()));

j++;

}

meansCluster.put(id_cluster, centers);

}

}

finally {

IOUtils.closeStream(in);

}

} catch (IOException e) {

e.printStackTrace();

}

}

public void map(Object key, Text value, Context context ) throws IOException,

InterruptedException {

StringTokenizer itr = new StringTokenizer(value.toString());

//get global value passing from main arguments

int num_coord= Integer.parseInt(context.getConfiguration().get("my.num.coord"));

Text word = new Text();

word.set(itr.nextElement().toString());

Page 32: K-Means Data Clustering - Vita Da Studente

30

double[] array_points = new double[num_coord];

int k=0;

while (itr.hasMoreTokens()) {

array_points[k]=Double.parseDouble(itr.nextToken());

k++;

}

//get coordinate cluster

double nearestDistanceOut=Double.MAX_VALUE;

double nearestDistance = 100;

double[] array_cluster = new double[num_coord];

String cluster=null;

Set<String> list = meansCluster.keySet();

for(String s:list) {

array_cluster = meansCluster.get(s);

double distance = KmeansUtil.getEuclideanDistance( array_cluster, array_points);

if (distance < nearestDistance) {

nearestDistance = distance;

cluster=s;

}

nearestDistanceOut=nearestDistance;

}

context.write(new Text(cluster), new Text(KmeansUtil.getString(array_points)));

}

}

KmeansCombiner.java

import java.io.IOException;

import java.util.StringTokenizer;

import java.util.Vector;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Reducer;

public class KmeansCombiner extends Reducer<Text,Text,Text,Text> {

public void reduce(Text key, Iterable<Text> values, Context context) throws IOException,

InterruptedException {

int num_coord=Integer.parseInt(context.getConfiguration().get("my.num.coord"));

int i;

// all points vector

Vector<double[]> all_points_vector = new Vector<double[]>();

for (Text val : values) {

Page 33: K-Means Data Clustering - Vita Da Studente

31

// single vector of points only one centroid

double[] vec_point_by_key= new double[num_coord];

StringTokenizer itr = new StringTokenizer(val.toString());

i=0;

while(itr.hasMoreElements()) {

vec_point_by_key[i]=Double.parseDouble(itr.nextElement().toString());

i++;

}

all_points_vector.add(vec_point_by_key);

}

double sum;

double[] vec_point_get_by_key= new double[num_coord];

double[] centroid_cluster_partial_sum = new double[num_coord];

int cont=0;

for( int j=0; j<num_coord;j++){

cont=0;

sum=0;

vec_point_get_by_key= new double[num_coord];

for(int k=0; k<all_points_vector.size(); k++){

vec_point_get_by_key=(double[])all_points_vector.get(k);

sum+= vec_point_get_by_key[j];

cont++;

}

centroid_cluster_partial_sum[j]=sum;

}

context.write(key, new Text(Integer.toString(cont)+ " "+

KmeansUtil.getStringPadding(centroid_cluster_partial_sum)));

}

}

KmeansReducer.java

import java.io.IOException;

import java.util.StringTokenizer;

import java.util.Vector;

import java.util.Iterator;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Reducer;

Page 34: K-Means Data Clustering - Vita Da Studente

32

public class KmeansReducer extends Reducer<Text,Text,Text,Text> {

public void reduce(Text key, Iterable<Text> values, Context context) throws IOException,

InterruptedException {

int num_coord=Integer.parseInt(context.getConfiguration().get("my.num.coord"));

// Double vector all points of certain centroid

Vector<double[]> all_points_vector = new Vector<double[]>();

int i;

int total_points=0;

for( Text val : values){

StringTokenizer itr = new StringTokenizer(val.toString());

//sum number of element

total_points+= Integer.parseInt(itr.nextToken());

double[] vec_point_by_key= new double[num_coord];

i=0;

while(itr.hasMoreElements()) {

vec_point_by_key[i]=Double.parseDouble(itr.nextElement().toString());

i++;

}

all_points_vector.add(vec_point_by_key);

}

double sum;

double[] vec_point_get_by_key= new double[num_coord];

double[] new_centroid_cluster = new double[num_coord];

for(int j=0; j<num_coord;j++){

sum=0;

vec_point_get_by_key= new double[num_coord];

for(int k=0; k<all_points_vector.size(); k++){

vec_point_get_by_key=(double[])all_points_vector.get(k);

sum+= vec_point_get_by_key[j];

}

new_centroid_cluster[j]= sum/total_points;

}

context.write(key, new Text(KmeansUtil.getStringPadding(new_centroid_cluster)));

}

}