Datahajautettu klusterointi ITKC35 Rinnakkaislaskenta Harjoitustyö Sami Äyrämö JYVÄSKYLÄN YLIOPISTO Informaatioteknologian tiedekunta Tietotekniikan laitos syksy 2003
Sisältö 1 Johdanto 1 2 Tutkimussuunnitelma 1 3 Klusterointi 2 4 K-means 4 5 Rinnakkainen K-means-algoritmi 5 6 Toteutus 5 7 Tulokset 6 7.1 Suorituskyky Josef-koneella....................... 7 7.2 Suorituskyky its-koneilla......................... 8 8 Yhteenveto 9 References 10 A Suoritusajat (Josef) 11 B Suoritusajat (Its) 12 C Testauksessa käytetyt koodit 13
1 Johdanto Tämän dokumentti on tarkoitettu loppuraportiksi kurssinitck35 Rinnakkaislaskenta harjoitustyölle. Työssä sovelletaan rinnakkaislaskennan menetelmiä klusterointitehtävän ratkaisemisessa. Klusterointimenetelmiä hyödynnetään mm. data-analyysija tiedonlouhintatehtävien ratkaisuun [7, 3, 8, 2]. Varsinkin tiedonlouhintatehtävissä kohteena on usein hyvin suuret tietomassat. Tietomäärä voi kasvaa suureksi joko havaintojen lukumäärän tai havainnoista mitattavien muuttujien lukumäärän suhteen, tai pahimmassa tapauksessa molempien. Tästä seuraa haasteita klusterointimenetelmien kehittäjille algoritmien laskennallisen tehokkuuden suhteen, jotta tehtävät voidaan suorittaa inhimillisessä ajassa tinkimättä kuitenkaan liikaa algoritmin tuottaman tuloksen laadusta. Näistä lähtökohdista klusterointitehtävän hajauttaminen useammalle koneelle on varteenotettava vaihtoehto hyvä laatuisten klusterointituloksien tuottamiselle riittävän lyhyessä ajassa. Tämä raportti etenee siten, että ensin esitellään työn tutkimussuunnitelma, jossa kuvataan lähtökohdat ja tavoite. Seuraavaksi esitellään klusteroinnin perusidea, eri menetelmien luokittelu ja esimerkkinä käytettävä menetelmä. Tämän jälkeen esitetään eräs ratkaisumalli klusteroinnin rinnakkaistamisongelmalle ja sen toteutus. Tuloksia esitellään ja johtopäätökset työstä esitellään viimeisenä. 2 Tutkimussuunnitelma Harjoitustyön tarkoituksena on rinnakkaistaa yleisesti tunnettu ja usein hyödynnetty osittava klusterointialgoritmi nimeltä k-means ja testata sen rinnakkaistamisen vaikutusta algoritmin laskentanopeuteen. Pohjana työlle käytetään artikkelia [1]. Harjoitustyön ajankäyttösuunnitelma on seuraava: Vaihe Ajankäyttö Perehtyminen 2.5h Suunnittelu 10h Toteutus 12.5h Testaus 5h Raportointi ja esitykset 10h
Algoritmi toteutetaan Fortran 90 -kielellä käyttäen MPI-viestinvälityskirjastoa [6]. Rinnakkaistetun algoritmin toimivuutta testataan its-työasemista koostuvassa rinnakkaisverkossa sekä yhteisen muistin rinnakkaiskoneessa Josefissa eri suuruisilla datajoukoilla (tarkoittaa eri kokoisilla datapisteiden määrillä ja kahdella eri dimensiolla). Tulokset raportoidaan speedup-arvoina (tarkoittaa tehtävän suorittamiseen yhdellä prosessorilla kuluvan ajan suhdetta n:llä prosessorilla kuluvaan aikaan [5]) ja graafisina kuvina. 3 Klusterointi Klusterointi on hyvin yleisesti käytetty menetelmä tilastotieteessä ja mallinnuksessa [4, 2, 8]. Tämä kappale esittelee muutamia klusteroinnin perusasioita. Yksinkertaisesti kuvailtuna klusterointi on samankaltaisten havaintojen (tai objektien, datapisteiden, jne.) ryhmittelyä. Klusterointi liittyy hyvin läheisesti luokitteluun, mutta peruslähtökohta näiden kahden tehtävän välillä on kuitenkin erilainen. Luokittelussa on etukäteen annettu luokat, joihin analysoitava data pyritään sijoittamaan. Eli kukin havainto kuuluu johonkin ennalta määrättyyn luokkaan. Tämän vuoksi luokittelua voidaan kutsua myös ohjatuksi oppimiseksi (supervised learning). Klusteroinnissa alkutilanne on taas päinvastainen: on olemassa vain joukko havaintoja, mutta emme tiedä kuuluvatko ne joihinkin luokkiin tai onko luokkia yleensä ottaen edes olemassa. Tästä seuraa nimitys ohjaamaton oppiminen (unsupervised learning). Klusteroinnin tehtävä on löytää nämä luokat eli klusterit. Sana klusteri tarkoittaa tässä yhteydessä samanlailla käyttäytyvien objektien joukkoa. Klusteroinnissa tavoitteena on, että samaan klusteriin liitetyt havainnot ovat mahdollisimman samanlaisia, ja toisaalta eri klustereihin liitetyt havainnot mahdollisimman erilaisia mitattujen ominaisuuksiensa suhteen. Jos annettu havaintodata onnistutaan rypästelemään ja saadut klusterit voidaan todeta valideiksi, voidaan klustereita puolestaan tämän jälkeen käyttää luokittelun kriteerinä. Sovelluskohteita klusteroinnille on runsaasti, sillä lähes lähes kaikenlaista dataa voidaan klusteroida. Esimerkkeinä voidaan mainita asiakkaiden profilointi ja segmentointi ostoskäyttäytymisen mukaan, dokumenttien ja kuvien klusterointi, taivaankappaleiden ryhmittely ja taudinkuvausten muodostaminen.
Kuten em. listasta voidaan huomata, menetelmät eivät rajoitu vain suoraan numeerisen datan käsittelyyn. Käyttämällä sopivia esikäsittelymenetelmiä, esimerkiksi tekstin ja kuvien klusterointi on myös mahdollista. Esikäsittely jätetään kuitenkin tämän työn ulkopuolelle ja keskitytään vain numeerisen tiedon käsittelyyn. Klusterointimenetelmät jaetaan tyypillisesti kahteen eri pääryhmään: hierarkiset (hierarchical) ja ositukseen perustuvat (partition-based) menetelmät. Suurin ero näiden kahden ryhmän välillä on siinä, että hierarkisissa menetelmissä klustereiden lukumäärää ei päätetä etukäteen, kun taas ositukseen perustuvissa menetelmissä algoritmille alustetaan jokin ratkaisu, jota algoritmilla pyritään iteratiivisesti parantamaan. Hiearkiset menetelmät voidaan edelleen jakaa kokoaviin (agglomerative, merge) ja hajauttaviin menetelmiin (divisive, split). Kokoavat menetelmät lähtevät liikkeelle siten, että kukin datapiste muodostaa oman klusterin ja ensimmäisellä kierroksella yhdistetään pareittain lähimpänä toisiaan olevat pisteet. Näin saadaan joukko klustereita joiden lukumäärä on puolet alkuperäisten havaintojen määrästä. Seuraavalla kierroksella yhdistetään lähimpänä toisiaan olevat ensimmäisellä kierroksella saadut klusterit toisiinsa edelleen suuremmiksi klustereiksi. Näin jatketaan, kunnes koko havaintojoukko muodostaa yhden klusterin. Klusterien välisen etäisyyden mittana voidaan käyttää esimerkiksi lähimpänä toisiaan olevia pisteitä, etäisimpiä pisteitä tai klusterihavaintojen keskiarvojen välistä etäisyyttä. Hajauttavien menetelmien idea on päinvastainen: aloitetaan yhdestä isosta klusterista ja lopetaan yksittäisten havaintojen muodostamiin klustereihin. Hierarkisten menetelmien etenemistä ja niiden tuottamia klustereita voidaan kuvata ns. dendogram-puiden avulla. Dendogram-puista voidaan valita haluttu taso, josta lopullinen klusterijako valitaan. Hierarkisten menetelmien heikkous on mm. se, että yhdellä tasolla havainnon klusteriin liittämisessä tapahtunut virhe vaikuttaa kaikkiin seuraaviinkin iteraatioihin. Tässä työssä käytetään kuitenkin osittavaa periaatetta ja sen vuoksi seuraavassa kappaleessa esitetään tarkemmin niistä yleisin eli K-means-algoritmi.
4 K-means K-means-algoritmi on ehkä tunnetuin (osittava) klusterointialgoritmi ja siitä on johdettu monia erilaisia versioita ja toteutuksia [9, 7, 3, 2]. Perusalgoritmi on seuraava: 1. Valitaan klustereiden lukumäärä K ja aloituspisteet (prototyypit). 2. Lasketaan kunkin havainnon etäisyydet prototyyppeihin ja liitetään havainto lähimpään protoyyppiin. 3. Lasketaan uusi prototyyppi kullekin klusterille (= klusteriin liitettyjen havaintojen keskiarvo). 4. Jos lopetusehto saavutettu niin lopetetaan. Muutoin jatketaan kohdasta 2. Algoritmin laskennallinen vaativuus on luokkaa O(tKn), missä t on iteraatioiden lukumäärä, K klustereiden lukumäärä ja n datapisteiden lukumäärä. Algoritmi ei välttämättä löydä aina löydä globaalia minimiä vaan saattaa ajautua lokaaliin minimiin. Koska K-means-algoritmissa klusteriprototyyppien estimaattorina käytetään keskiarvoa, niin se löytää parhaiten normaalijakautuneet klusterit ja tuottaa siten vain konvekseja klustereita. K-means-algoritmin heikkoutena voidaan pitää sitä, että käyttäjän täytyy määritellä klustereiden lukumäärä etukäteen. Menetelmä on myös herkkä poikkeaville muuttujien arvoille (outliers) sillä estimaattina käytetyn keskiarvon murtumispiste on nolla mikä tarkoittaa, että klusterin paikkaa voidaan muuttaa mielivaltaisen paljon siirtämällä vain yhtä datapistettä riittävän kauas sen oikeasta paikasta. K-means ei sovellu myöskään suoraan diskreettien arvojen klusterointiin, mutta sitä varten on kehitetty muunneltu versio nimeltä K-modes. K-means-algoritmi voidaan itseasiassa kuvata minimointitehtävänä, jossa prototyyppien ja datapisteiden välisten etäisyyksien summaa minimoidaan: minj 2 2 (u), forj 2 2 (u) = N cl N j j=1 x i u 2 2, (1) missä N cl on klustereiden lukumäärä ja N j on j:nteen klusteriin liitettyjen datapisteiden lukumäärä. Jos datapisteiden väliset etäisyydet joudutaan laskemaan joka kierroksella uudestaan on algoritmi hyvin raskas. Tällöin algoritmin rinnakkaistamisella voidaan pienentää tehtävän ratkaisemiseen kuluvaa aikaa huomattavasti. i=1
5 Rinnakkainen K-means-algoritmi Rinnakkainen K-means-algoritmi voidaan toteuttaa hajauttamalla data kullekin laskentatehtävälle siten, että kukin laskentatehtävä liittää jokaisella iteraatiolla sille annetut datapisteet niitä lähimpänä oleviin prototyyppeihin ja laskee etäisyydet datapisteistä niiden prototyyppeihin sekä kaikkien datapisteiden etäisyyksien neliöllisen summan niihin liitettyjen klustereiden keksipisteistä, mikä tarkoittaa neliöllinen keskivirhettä, jonka avulla voidaan arvioida ratkaisun hyvyyttä (eli kuinka lähellä (lokaalia) minimiä ollaan). Kun jokainen laskentatehtävä on suorittanut laskennan, niin ne lähettävät toisilleen etäisyydet kutakin klusteriprototyyppiä kohden, keskivirheen ja lukumäärän kuinka monta datapistettä kuhunkin prototyyppiin on liitetty. Tämän jälkeen jokainen tehtävä voi laskea uudet prototyypit ja tarkistaa lopetusehdon ja tarvittaessa jatkaa uudella iteraatiolla. Tämä aiheuttaa hieman rinnakkaista laskentaa, mutta tähän kuluva aika on pieni verrattuna siihen, että kaikki data lähettäisiin yhdelle tehtävälle, joka sitten laskisi keskipisteet ja lähettäisi ne takaisin muille tehtäville. 6 Toteutus Rinnakkainen K-means-algoritmi voidaan toteuttaa kollektiivisen viestinnän periaatteita käyttäen MPI-kirjaston avulla. Viestinnässä tarvittavia operaatioita ovat (tarkemmat määrittelyt aliohjelmille esim. MPI-oppaassa [6]: Viestinnän alustaminen: MPI_INIT MPI_COMM_SIZE MPI_COMM_RANK Tyyppimuunnokset: MPI_TYPE_VECTOR MPI_TYPE_COMMIT Viestintä:
MPI_BCAST MPI_SEND MPI_RECV MPI_BARRIER MPI_ALLREDUCE Viestinnän päättäminen: MPI_FINALIZE Viestinnän alustamisen jälkeen juurisolmu laskee kuinka suuri osuus datasta kullekin laskentatehtävälle kuuluu ja lähettää sen jälkeen ne MPI_SEND-aliohjelmalla käyttäen apuna MPI_TYPE_VECTOR- ja MPI_TYPE_COMMIT-aliohjelmilla tehtyjä ns. johdettuja tietotyyppejä. MPI_BARRIER-kutsulla synkronoidaan laskentatehtävien suoritus laskenta-ajan mittaamista varten. Itse laskennan aikana prototyyppeihin liitettyjen datapisteiden lukumäärät, etäisyyksien summat ja neliöllinen keskivirhe lähetetään "all-to-all-periaatteella MPI_ALLREDUCE-aliohjelmaa käyttäen kaikille laskentatehtäville, jotka kukin laskevat niistä summan. Protoyyppien laskenta lopetetaan kun neliöllinen keskivirhe ei enää muutu. Toteutuksen lähdekoodit ovat liittessä C. 7 Tulokset Tuloksia tarkastellaan vertaamalla speedup-arvoja, kun sama klusterointitehtävä suoritetaan erilaisilla prosessorien lukumäärillä. Speedup lasketaan seuraavasti [5]: S relative = T 1 T n, (2) missä T 1 on tehtävän yhdellä prosessorilla suorittamiseen kuluva aika ja T n on sen n:llä prosessorilla suorittamiseen kuluva aika. Kunkin speedup-arvon laskemiseen on ajettu viisi onnistunutta klusterointia (globaali minimi siis löytyi) ennalta määrätyistä aloituspisteistä. Näistä vertailuarvoksi on otettu aina paras aika.
7.1 Suorituskyky Josef-koneella Kuvissa 1 ja 2 on esitetty Josef-koneella suoritettujen testien speedup-kuvaajat. Testeissä saadut suoritusajat löytyvät liitteestä A. Kuvaajista voidaan havaita, että speedup on suurella datajoukolla jopa superlineaarista. Eli hyötysuhde on yli teoreettisen raja-arvon mikä tarkoittaa, että prosessorien määrän kaksinkertaistuessa laskennan nopeutuminen on yli kaksinkertaisesta. Tämä näkyy suuremmalla 6- ulotteisella datalla, jossa suurimmalla (n = 80000) datalla kahdeksalla prosesorilla saatu speedup-arvo on noin 9.76. Tämä on seurausta mahdollisesti paremmasta tehokkaammasta muistin käytöstä, jolloin hajautetun datan hakemiseen kulutetaan suhteessa vähemmän aikaa kuin yhden koneen tapauksessa. Kuva 1: Speedup-käyrät eri kokoisille datoille Josefissa 2-ulotteisen datan tapauksessa. Kuva 2: Speedup-käyrät eri kokoisille datoille Josefissa 6-ulotteisen datan tapauksessa.
7.2 Suorituskyky its-koneilla Kuvissa 3 ja 4 on puolestaan esitetty its-koneilla suoritettujen testien speedupkuvaajat. Testeissä saadut suoritusajat löytyvät liitteestä B. Kuvaajista voidaan havaita, että speedup-arvo muuttuu suurilla datoilla käytännössä lineaarisesti. Pienemmillä datoilla kommunikointiin kuluvan ajan suhteellinen osuus on suurempi, joten speedup-arvot eivät saavuta aivan lineaarista tasoa. Josefiin verratuna hieman heikompi suorituskyky johtunee kommunikointiin kuluvasta ajasta. Josefissa kommunikointi tapahtuu yhteismuistin kautta, jolloin siihen kuluu vähemmän aikaa. Nämäkin tulokset ovat kuitenkin hyviä ajatellen algoritmin skaalautuvuutta suurten, esimerkiksi tiedonlouhinnassa analysoitavien, datamassojen suhteen. Kuva 3: Speedup-käyrät eri kokoisille datoille its-koneilla 2-ulotteisen datan tapauksessa. Kuva 4: Speedup-käyrät eri kokoisille datoille its-koneilla 6-ulotteisen datan tapauksessa.
8 Yhteenveto Tässä työssä testattiin rinnakkaistetun k-means-algoritmin toimivuutta kahdessa eri rinnakkaislaskenta ympäristössä. Erityisesti yhteismuistia käyttävän Josef-rinnakkaislaskentakoneen tulokset olivat hyviä. Sillä päästiin jopa superlineaarisiin speedup-arvoihin, minkä oletettiin johtuvan kommunikoinnin nopeudesta yhteismuistia käyttävässä rinnakkaiskoneessa. Its-työasemakoneista koostuvassa verkossa saatiin lineearisesti käyttäytyviä speedup-käyriä, mikä osoittaa myös hyvää skaalautuvuutta prosessori- ja datamäärän suhteen. Koska tavoitteena oli tutkia rinnakkaistamisesta saatavia hyötyjä suuriin datamassoihin kohdistuvissa tiedonlouhintatehtävissä, olivat varsin tulokset rohkaisevia. Varsinkin superlineaarinen käyttäytyminen mahdollistaa suurtenkin datamassojen louhinnan inhimillisessä ajassa. Lisäksi tiedossa on, että robustimpia klusterointimenetelmiä käytettäessä (kuten K-medians ja K-spatial medians), laskennallinen vaativuus vain kasvaa, jolloin ainakin tämän työn perusteella rinnakkaistamisesta voidaan olettaa olevan hyötyä laskenta-ajan ja muistin käytön suhteen.
Viitteet [1] Dhillon, I.S., and Modha, D.S., A data clustering algorithm on distributed memory machines. IBM Almaden Research Center, USA, Workshop on Large-Scale Parallel KDD Systems August 15th, 1999, San Diego, CA, USA in conjunction with ACM SIGKDD International Conference on Knowledge Discovery and Data Mining. [2] Duda R.O., Hart, P.E. and Stork, D.G., Pattern classification, John Wiley & Sons Inc, 2001. [3] Dunham M. H., Data mining introductory and advanced topics, Pearson Education Inc, 2003. [4] Everitt, B.S., Landau, S. and Leese, M., Cluster Analysis, Arnold, a member of the Hodder Headline Group, London, 2002. [5] Foster, I., Designing and Building Parallel Programs, 1995. Available at http://www-unix.mcs.anl.gov/dbpp/text/book.html, (26.12.2003). [6] Haataja, J. and Mustikkamäki, K., Rinnakkaisohjelmointi MPI:llä. CSC - Tieteellinen laskenta Oy, 2001. Available at http://www.csc.fi/oppaat/mpi/mpi.pdf, (26.12.2003). [7] Hand, D., Mannila H. and Smyth, P., Principles of data mining, MIT Press, 2001. [8] Hastie, T., Tibshirani R. and Friedman, J., The elements of statistical learning; data mining, inference, and prediction. Springer-Verlag, 2001. [9] Jain, A.K., Murty, M.N. and Flynn, P.J., Data clustering: A review. ACM Computing surveys, Vol. 31, No. 3, September 1999.
A Suoritusajat (Josef) Josef, d=2 Nbr of processors Datasize 1 2 4 8 10000 1.38 0.70 0.36 0.18 20000 3.18 1.40 0.80 0.41 40000 5.55 2.81 1.42 0.71 80000 11.1 5.62 2.82 1.41 Josef, d=6 Nbr of processors Datasize 1 2 4 8 10000 1.51 0.61 0.31 0.16 20000 3.03 1.23 0.62 0.31 40000 6.07 2.47 1.24 0.62 80000 12.1 4.95 2.47 1.24
B Suoritusajat (Its) Its, d=2 Nbr of processors Datasize 1 2 4 8 10000 1.08 0.55 0.29 0.18 20000 2.37 1.09 0.65 0.35 40000 4.29 2.18 1.10 0.57 80000 8.58 4.34 2.19 1.12 Its, d=6 Nbr of processors Datasize 1 2 4 8 10000 0.92 0.47 0.26 0.14 20000 1.89 0.96 0.49 0.26 40000 3.68 1.91 0.97 0.50 80000 7.37 3.78 1.91 0.99
C Testauksessa käytetyt koodit MODULE mpi IMPLICIT NONE! Otetaan käyttöön MPI-määrittelyt: INCLUDE mpif.h! Otetaan käyttöön MPI-määrittelyjä INTEGER, SAVE :: ntasks, rc, my_id END MODULE mpi MODULE prec1 IMPLICIT NONE INTEGER, PARAMETER :: prec = SELECTED_REAL_KIND(12,50) END MODULE prec1 MODULE file_io IMPLICIT NONE PUBLIC read_params, read_data CONTAINS SUBROUTINE read_params(fname,r,c,k) USE prec1 IMPLICIT NONE CHARACTER(len=*),INTENT(IN) :: fname INTEGER, INTENT(OUT) :: r,c,k INTEGER :: status OPEN(10,FILE=fname, IOSTAT=status) IF(status /= 0)THEN WRITE(*,*) Error (, status, ) in opening file:,fname,! STOP READ(10, (I5) ) r READ(10, (I1) ) c READ(10, (I1) ) k CLOSE(10) END SUBROUTINE read_params SUBROUTINE read_data(fname,data) USE prec1
IMPLICIT NONE CHARACTER(len=*),INTENT(IN) :: fname REAL, DIMENSION(:,:), INTENT(OUT):: data INTEGER :: r,c,k,i,j,status,err OPEN(10,FILE=fname, IOSTAT=status) IF(status /= 0)THEN WRITE(*,*) Error (, status, ) in opening file:,fname,! STOP READ(10, (I5) ) r READ(10, (I1) ) c READ(10, (I1) ) k DO i = 4,r+3 READ(10,*,IOSTAT=err) (data(i-3,j),j=1,c) IF(err < 0) THEN WRITE (*,*) End of file reached, execution continues! EXIT END DO CLOSE(10) END SUBROUTINE read_data END MODULE file_io MODULE clustering IMPLICIT NONE PUBLIC kmeans CONTAINS SUBROUTINE kmeans(data, k, cntrs, ncl, maxiters,comm) USE mpi USE prec1 IMPLICIT NONE REAL, DIMENSION(:,:), INTENT(IN) :: data REAL, DIMENSION(:,:), INTENT(INOUT) :: cntrs INTEGER, DIMENSION(:), INTENT(INOUT) :: ncl INTEGER, INTENT(IN) :: k, maxiters, comm INTRINSIC SIZE, MAX!Lokaalit datataulukot!globaalit klusterivektorit!globaalit pisteiden lukumäärät REAL :: msqe, msqe_tmp, oldmsqe, d, dmin
INTEGER :: i, j, n, ndim, kmin, h, it, allocstat INTEGER, DIMENSION(k) :: ncl_tmp REAL, DIMENSION(:,:), ALLOCATABLE :: cntrs_tmp REAL (KIND=prec) :: t0, t1!lokaalit pisteiden lukumäärät!lokaalit klusterivektorit!muuttujat kellotusta varten n = SIZE(data,1) ndim = SIZE(data,2)!Lokaalien data taulukoiden havaintolukumäärät!datan dimensioiden lukumäärä ALLOCATE(cntrs_tmp(k,ndim), STAT=allocstat) IF(allocstat/=0) STOP Memory allocation failed!!varataan tilaa lokaaleille aputaulukoille msqe = HUGE(msqe)!Alustetaan virhe (pitää olla suuri luku!) t0 = MPI_WTIME()!Aloitetaan kellotus DO it =1, maxiters oldmsqe = msqe msqe_tmp = 0 ncl_tmp = 0 cntrs_tmp = 0 DO i = 1,n dmin = HUGE(dmin) kmin = 1 DO j = 1,k d = SQRT(SUM((data(i,:)-cntrs(j,:))**2)) IF (d < dmin) THEN dmin = d kmin = j END DO cntrs_tmp(kmin,:) = cntrs_tmp(kmin,:) + data(i,:) ncl_tmp(kmin) = ncl_tmp(kmin) + 1 msqe_tmp = msqe_tmp + dmin END DO CALL MPI_ALLREDUCE(ncl_tmp, ncl, k, MPI_INTEGER, MPI_SUM, comm, rc) CALL MPI_ALLREDUCE(cntrs_tmp, cntrs, k*ndim, MPI_REAL, MPI_SUM, comm, rc) DO j = 1,k h = MAX(ncl(j),1) cntrs(j,:) = cntrs(j,:) / h END DO CALL MPI_ALLREDUCE(msqe_tmp, msqe, 1, MPI_REAL, MPI_SUM, comm, rc) IF(msqe >= oldmsqe) EXIT!Lopetusehto
END DO t1 = MPI_WTIME() IF(my_id == 0) THEN WRITE(*,*) Iters:,it WRITE(*,*) MSE :,msqe DO j = 1,k WRITE(*,*) Cntrs:, cntrs(j,:) END DO WRITE(*, ("Suoritusaika:",F0.4) ) t1 - t0 END SUBROUTINE kmeans END MODULE clustering PROGRAM kmeans_test USE mpi USE file_io, ONLY : read_data, read_params USE clustering, ONLY : kmeans USE prec1 IMPLICIT NONE CHARACTER(len=30) :: fname REAL, DIMENSION(:,:), ALLOCATABLE :: data REAL, DIMENSION(:,:), ALLOCATABLE :: l_data, cntrs INTEGER, DIMENSION(:), ALLOCATABLE :: l_datacount, displs INTEGER, DIMENSION(:), ALLOCATABLE :: ncl!globaalit pisteiden lukumäärät INTEGER, DIMENSION(MPI_STATUS_SIZE) :: status INTEGER :: r, c, k, my_cnt, local_nr, ival_mod, datamx1, datamx2, allocstat INTEGER :: i, j INTEGER, PARAMETER :: root_id = 0, tag = 50 CALL MPI_INIT(rc) IF(rc /= MPI_SUCCESS) THEN WRITE(*,*) MPI Initialization failed! STOP CALL MPI_COMM_SIZE(MPI_COMM_WORLD, ntasks, rc) CALL MPI_COMM_RANK(MPI_COMM_WORLD, my_id, rc) IF(my_id == root_id) THEN
fname = n8d6k4.dat CALL read_params(fname,r,c,k) ALLOCATE(data(r,c), STAT=allocstat) IF(allocstat/=0) STOP Memory allocation failed! CALL read_data(fname,data) WRITE(*,*) Rows:, r WRITE(*,*) Dims:, c WRITE(*,*) Clusters:, k!lähetetään dimensiot kaikille CALL MPI_BCAST(r, 1, MPI_INTEGER, root_id, MPI_COMM_WORLD, rc) CALL MPI_BCAST(c, 1, MPI_INTEGER, root_id, MPI_COMM_WORLD, rc) CALL MPI_BCAST(k, 1, MPI_INTEGER, root_id, MPI_COMM_WORLD, rc) local_nr = r/ntasks ival_mod = MOD(r,ntasks) IF(my_id < ival_mod) THEN my_cnt = local_nr + 1 ELSE my_cnt = local_nr!varataan paikalliset datataulukot ja taulukko pisteiden lukumäärille ALLOCATE(l_data(my_cnt,c), ncl(k), STAT=allocstat) IF(allocstat/=0) STOP Memory allocation failed! ncl = 0!Lasketaan osuudet globaalista datataulukosta IF(my_id == root_id) THEN ALLOCATE(l_datacount(0:ntasks-1), displs(0:ntasks-1),& STAT=allocstat) IF(allocstat /= 0) STOP Memory allocation failed! l_datacount(0:ival_mod-1) = local_nr+1 l_datacount(ival_mod:) = local_nr displs(0)=0 DO i=0, ntasks-2 displs(i+1) = displs(i) + l_datacount(i) END DO!create datatypes for local data matrix CALL MPI_TYPE_VECTOR(c, local_nr, r, MPI_REAL, datamx1, rc) CALL MPI_TYPE_VECTOR(c, local_nr+1, r, MPI_REAL, datamx2, rc) CALL MPI_TYPE_COMMIT(datamx1, rc) CALL MPI_TYPE_COMMIT(datamx2, rc) IF(my_id == root_id) THEN l_data = data(1:my_cnt,:)!juurisolmu kopioi oman datan ennen lähetystä
DO i=1,ntasks-1!lähetetään jokaiselle laskentatehtävälle niiden osuus datoista IF(i < ival_mod) THEN CALL MPI_SEND(data(displs(i)+1,1), 1, datamx2, i, tag, MPI_COMM_WORLD, rc) ELSE CALL MPI_SEND(data(displs(i)+1,1), 1, datamx1, i, tag, MPI_COMM_WORLD, rc) END DO ELSE CALL MPI_RECV(l_data, my_cnt*c, MPI_REAL, root_id, tag, MPI_COMM_WORLD, status, rc) ALLOCATE(cntrs(k,c))!Kuusi dimensioinen data cntrs(1,1:6)=(/5.0, 5.0, 4.0, 6.0, -7.0, -5.0/) cntrs(2,1:6)=(/0.0, -5.0, 6.0, -5.0, 0.0, -9.0/) cntrs(3,1:6)=(/5.0, 0.0, -1.0, 1.0, 8.0, 3.0 /) cntrs(4,1:6)=(/-5.0, 0.0, -4.0, -4.0, -4.0, 0.0/)!cntrs(1,1:2)=(/5.0, 5.0/)!cntrs(2,1:2)=(/0.0, -5.0/)!cntrs(3,1:2)=(/5.0, 0.0/)!cntrs(4,1:2)=(/-5.0, 0.0/) CALL MPI_BARRIER(MPI_COMM_WORLD,rc) CALL kmeans(l_data, k, cntrs, ncl, 50000, MPI_COMM_WORLD) CALL MPI_FINALIZE(rc)!DO i = 1,k! WRITE(*, (/,(4F0.3)) ) (cntrs(i,j),j=1,c)!end DO END PROGRAM kmeans_test