All Posts By

Dávid Gyurkó

Automatikus gépi tanulás H2O-val

By | Data Science, Machine Learning, R | No Comments

Bevezetés

Az utóbbi években a gépi tanulás (machine learning, ML) növekedésének köszönhetően megnőtt az igény egy felhasználóbarát megoldásra, amellyel akár kevésbé tapasztalt felhasználók is elboldogulhatnak, ugyanakkor segítheti a data scientist-ek munkáját is. Ebben a cikkben az H2O AutoML eszközét fogom bemutatni, amit még 2016-ban kezdett el fejleszteni a cég.

Az H2O elérhető Java, R, Python és Scala platformokon is. Ebben a cikkben R-ből fogjuk használni.

Adatok

A tanuláshoz az MNIST számjegy adatait fogjuk használni. 70.000 db kézzel írt számról készültek képek 28×28-as felbontásban. Minden egyes pixelhez (28×28 = 784 pixel) egy egész szám tartozik 0 és 255 között, ami a szürkeárnyalatot mutatja.

Az adatok elérhetők több forrásból is, mi most a kaggle versenyén fogunk elindulni https://www.kaggle.com/c/digit-recognizer/data

Ahhoz hogy eljussunk az adatoktól a modellig a következő lépéseket fogjuk megtenni:

  1. Adatok beolvasása
  2. Betöltés H2O-ba
  3. Modellek tanítása
  4. Modell futtatása a teszt adatra
  5. Feltöltés kaggle-re

Modellezés

1. Adatok beolvasása

A Kaggle-ön a két csv fájlt (train.csv, test.csv) töltsük le és helyezzük a projekt mappánkba. A readr csomag segítségével olvassuk be a fájlokat R-be.

A train.csv 785 oszlopot tatartalmaz: az első a label, ami egy 0-9-ig terjedő faktor. Ez mutatja meg, hogy az egyes képek milyen számot ábrázolnak. A többi oszlop a pixelek szürkeárnyalata pixel0-tól egészen pixel 783-ig.

A test.csv csak a pixelek adatait tartalmazza. Az ellenőrzést a Kaggle fogja végezni feltöltés után.

 
library(readr)
library(magrittr)
library(h2o)

train <- read_csv("train.csv", col_types = cols(
  label = col_factor(levels = as.character(0:9)),
  .default = col_integer()
))

test <- read_csv("test.csv")

Fontos, hogy a label oszlopot faktorként olvassuk be, mert ha ezt elmúlasztjuk, akkor mennyiségi változóként fog a modell eredményt adni (pl. 2.31, 4.72). A train.csv fájl csak a pixelek adatait tartalmazza, ezért nem szükséges megadnunk az oszloptípusokat: automatikusan integer-ként fogja beolvasni.

2. Betöltés H2O-ba

Először indítsuk el a H2O-t , ami egy Java virutális környezetben fut (JVM)


h2o.init()

Ezután töltsük be a H2O JVM-be a train és test adatokat az as.h2o függvény segítségével:

train_h2o <- as.h2o(train)
test_h2o <- as.h2o(test)

3. Modellek tanítása

Az AutoML funkciót az h2o.automl függvénnyel érjük el, amely a „motorháztető alatt” különböző ML modelleket tanít be (GLM, Deep learning, GBM, DRF), illetve ezek együttesét is (ensemble).

A függvény legfontosabb argumentumai:

  • training_frame: az adathalmaz amely tartalmazza a befolyásoló változókat és a címkét. Ez alapján tanulnak a H2O modellek. (train.csv)
  • x: a befolyásoló (predictor) változók, amik alapján a modell besorolja a 0-9 számjegyek valamelyikébe a képeket. (pixel0, …, pixel783 változók)
  • y: címke: az oszlop amely tartalmazza a címkéket (label)
  • max_runtime_secs: meddig fusson maximum a modell (tetszőleges, én 10 percre állítom, alapértelmezetten 1 óráig fut)
  • exclude_algos: egyes modellek kizárása

Természetesen jóval több paramétert lehet állítani, ezekről az H2O dokumentációban olvashatsz bővebben.

 
automl_h2o <- h2o.automl(
  x = paste0("pixel", 0:783), 
  y = "label",
  training_frame = train_h2o,
  max_runtime_secs = 1200
)

Az AutoML a következő modelleket használja alapértelmezetten:

  • Random Forest
  • Extremely-Randomized Forest
  • Gradient Boosting Machines (GBM)
  • Mély neurális hálók (deep neural nets)
  • Generalized Linear Models (GLM),
  • illetve két ún. együttest (ensemble):
    • StackedEnsemble_AllModels minden modellt figyelembe vesz. Általában ez a legjobban teljesítő modell.
    • A StackedEnsemble_BestOfFamily minden modellcsaládból csak a legjobban teljesítő modellt használja.

A modellek a ranglistán (leaderboard) találhatók:

 

automl_h2o@leaderboard

                                               model_id mean_per_class_error   logloss      rmse        mse
1 StackedEnsemble_BestOfFamily_0_AutoML_20180706_143720           0.03660115 0.1188238 0.1810178 0.03276743
2    StackedEnsemble_AllModels_0_AutoML_20180706_143720           0.03660115 0.1188238 0.1810178 0.03276743
3                          DRF_0_AutoML_20180706_143720           0.04008693 0.2899887 0.2826129 0.07987007
4                          XRT_0_AutoML_20180706_143720           0.04107182 0.2913078 0.2832940 0.08025547

Összesen négy modell készült el. Két önálló modell (DRF (distributed random forest), XRT (extremely randomized trees)), illetve két együttes (ensemble).

Mivel a két különböző modellcsaládból (DRF, XRT) csak 1-1 modell készült el, így a StackedEnsemble_AllModels és a StackedEnsemble_BestOfFamily megegyezik.

4. Modell futtatása a teszt adatra

Használjuk a legjobban teljesítő modellt (automl_h2o@leader). Az h2o.predict függvénnyel futtathatjuk a modellt a teszt adatra:

 
model_pred <- h2o.predict(object = automl_h2o@leader, newdata = test_h2o)
model_pred

 

  predict           p0           p1           p2           p3           p4           p5           p6           p7
1       2 1.780870e-06 1.255231e-04 9.991817e-01 6.537366e-05 1.897766e-06 1.562988e-06 6.652430e-07 6.090773e-04
2       0 9.996370e-01 1.115405e-06 6.297555e-05 4.453476e-07 4.386726e-05 7.841050e-06 1.237303e-04 2.414906e-05
3       9 1.524223e-04 2.595324e-04 1.165269e-03 1.906389e-03 5.899059e-03 1.082756e-03 5.011537e-05 6.173151e-03
4       4 4.308413e-03 5.748119e-03 2.186001e-02 3.612890e-03 5.807449e-01 2.184924e-03 2.505739e-03 1.305842e-01
5       3 2.119758e-04 8.248249e-03 2.915150e-01 6.790016e-01 1.361538e-04 2.304432e-03 1.106066e-04 5.670405e-03
6       7 2.832275e-04 3.723656e-04 4.514339e-03 1.054196e-02 6.153598e-04 4.646419e-04 1.263733e-04 9.688050e-01
            p8           p9
1 8.066652e-06 4.339676e-06
2 4.947527e-06 9.393295e-05
3 1.908435e-03 9.814029e-01
4 7.465877e-03 2.409849e-01
5 6.964271e-03 5.837324e-03
6 5.466614e-04 1.373007e-02

A model_pred objektumnak 28.000 sora van (minden teszt képre egy) és 11 oszlopa: az első a predict, ami a modell által jósolt számjegyet tartalmazza, illetve p0-tól p9-ig az egyes számjegyek valószínűségét.

Pl. nézzük meg az első képet:

 
matrix(data = as.integer(test[1, ]), nrow = 28, ncol = 28, byrow = TRUE) %>% 
  as.raster(max = 255) %>% 
  plot()

erre a modell 2-es számjegyet prediktál. Annak a valószínűsége, hogy a kép 0-ás számjegyet ábrázol: 1.780870e-06, tehát 0.00000178. A 2-es számjegy valószínűsége: 9.991817e-01, vagyis 0.99. A model 99%-os valószínűséget állapított meg arra, hogy az első számjegy 2-est ábrázol, ezért lett a predict oszlop értéke 2.

5. Feltöltés Kaggle-re, eredmény

A Kaggle-re csv formátumban tudjuk feltölteni ImageId és Label oszlopnevekkel. A model_pred objektum H2O-ban van, ezért as.data.frame függvénnyel tudjuk R data.frame-re konvertálni.


data <- data.frame(
  ImageId = 1:nrow(model_pred),
  Label = as.data.frame(model_pred)$predict
)
write_csv(x = data, path = "submission.csv")

Feltöltés után a Kaggle rögtön kiértékeli, az eredmény pedig 96.67%, tehát a számjegyek kb. 3.4%-át osztályozta félre a modell. Ez a 2036.ik helyre elég, ami egész jó eredmény, ahhoz képest, hogy csak 2 modell együttese alapján értük el ezt az eredményt.

Összegzés

Az H2O AutoML eszköz egy nagyszerű lehetőség junior és senior data scientist-eknek egyaránt, hogy minimális paraméterezéssel több különböző ML modellt tanítsanak be. Az eredmény útmutatást adhat, hogy melyik modellt érdemes használni (amelyet aztán később manuállisan paraméterezhetünk), vagy akár benchmarknak is használható.

Ha tetszett a cikk, iratkozz fel hírlevelünkre (a jobb felső sarokban az “értesítésre” kattintva), vagy kövess minket LinkedIn és Facebook csatornákon!

Python, vagy R?

By | Data Science, Python, R | No Comments

Bevezetés

Gyakran előkerül a kérdés, hogy melyik nyelvet érdemes választani adatelemzési/adatbányászati (data science) feladatokra. A válasz természetesen az R, Python, hogy attól függ, melyik nyelv fedi le jobban az üzleti és fejlesztői igényeket.

Egyes data science csapatokban az R és a Python megfér egymás mellett, a vezetők mégis gyakran teszik le a voksukat az egyik nyelv mellett a könnyebb adminisztráció (azonos kódbázis), oktatás/mentorálás, stb. miatt. A vitát azonban Wes McKinney (korábban a Cloudera, Two Sigma fejlesztője, az egyik legnépszerűbb csomag, a Pandas alkotója) és Hadley Wickham (RStudio, a ggplot2, tidyverse csomagok fő szerzője) örökre lezárná. Ez a cikk a clickbait cím ellenére nem hivatott állást foglalni egyik nyelv mellett sem.

Átjárhatóság a két nyelv között

A csomagok fejlesztésekor az R gyakran szembesül a Pythonhoz hasonló problémákkal (pl. memóriában történő adatfeldolgozás), ezért célszerűvé vált a közösségek közt egy szorosabb együttműködés. A cél, hogy az egyes funkciók R-ben és Pythonban is egyaránt jól működjenek. Erre remek példa a két deep learning csomag, a tensorflow és a keras. A két nyelv közti átjárhatóság minden data scientist érdeke: pl. a Spark rendelkezik Python és R API-jal is (PySpark és SparkR), az átjárás a két API között azonban gyakran memória-, illetve teljesítményproblémákat okozhat. A témához kevésbé jártasak ezt tévesen úgy konstatálják, hogy a Python, vagy az R lassú, pedig csak egy közös

Ursa Labs

Wes 2018 májusában az RStudio és a Two Sigma segítségével megalapította az Ursa Labs-et, mely platformfüggetlen data science megoldásokat fejleszt. A projektben technikai tanácsadóként részt vesz Hadley Wickham, így garantálva lesz, hogy az R felhasználók igényeit is kielégítik. Adminisztrációban, HR területen, illetve finanszírozásban az RStudio segíti az Ursa Labs-et, ezzel is megerősítve, hogy a nyelvek közti háborúnak semmi értelme: mindkét közösség fejlesztői ugyanazon célért dolgoznak, a data scientist-ek munkájának megkönnyítésén.

Az Ursa Labs termékei nyílt forráskódúak, és az alábbi nyelveken érhetők el:

A projekt főbb témái közül néhány:

  • Hordozható C++ könyvtárak az adott nyelvekhez (Python, R, Ruby, stb.)
  • Átjárhatóság biztosítása meglévő adatmegjelenítések között (pl. data frame R-ben, pandas / NumPy Python-ban).
  • Új frontend interfészek a nyelvekhez (pl. dplyr R-ben, pandas fejlesztése Python-ban)
  • Hordozható több szálon futó Apache Arrow-alapú végrehajtó motor (Big data)

Összegzés

Az Ursa Labs egy remek kezdeményezés a két közösség összekötésére. A Python és az R csak eszközök a data scientist-ek kezében, de a cél közös. És ki tudja? Lehet pár év múlva már nem lesz ennyire izgalmas a kérdés, hogy melyik nyelvet érdemes választani…

Ha tetszett a cikk, iratkozz fel hírlevelünkre (a jobb felső sarokban az “értesítésre” kattintva), vagy kövess minket LinkedIn és Facebook csatornákon!

Aszinkron programozás Shiny-ban

By | Data Science, R | One Comment

Az eRum konferencia a második legnagyobb esemény az R programozás világában. Idén Budapest adott otthont a rendezvénynek, így nekem is nyílt lehetőségem előadni. Előadásom megtekinthető a youtubeon.

Miért?

Az R egy szálon futó (single threaded) nyelv, ezért ugyanarra az instance-ra érkező feladatok blokkolják egymást. Ez alap esetben nem okoz gondot, mert rövid feladatoknál csak millimásodperceket kell várni, ezért fel sem tűnik a felhasználónak.

Hosszabb feladatok futtatásánál viszont előfordulhat, hogy az egyik felhasználó (akár rövid, millimásodperces kérése) beragad egy hosszabb folyamat mögé. A rövid feladat futása csak akkor fog elkezdődni, amikor a sorban előtte lévő már befejeződött. Ez azt jelenti, hogy ha egy 0.1 mp alatt lefutó feladat a sorban egy 15 másodperces feladat mögé szorul (pl.: egy nagyobb csv fájl beolvasása miatt), akkor 15 másodpercig sorba áll, majd 0.1 mp alatt lefut, ez idő alatt a felhasználó pedig egyáltalán semmit nem tud csinálni.

Megfelelően loadbalance-olt alkalmazások esetén ez a blokkoló viselkedés ritkábban fordulhat elő, de az RStudio promises csomagja végleg megoldja a problémát. Az aszinkron futtatáshoz három csomag szükséges: a shiny aszinkron verziója, a future package és a promises package. Ezeket a csomagokat az alábbi utasításokkal installálhatjuk:

devtools::install_github("rstudio/promises")
devtools::install_github("rstudio/shiny")
install.packages("future")

Hogyan?

Egy standard (szinkron) hívást az alábbi módon írnánk meg: (csv fájl beolvasása, szűrés, majd oszlopkiválasztás)

library(dplyr)
read.csv("eRum.csv") %>%
  filter(talk_type == "shiny") %>%
  select(speaker_name)

 

Ugyanez a kód aszinkron hívás esetén így módosul:

future(read.csv("eRum.csv")) %...>%
  filter(talk_type == "shiny") %...>%
  select(speaker_name)

 

Az alapértelmezett (szinkron) hívást úgy tudjuk aszinkronná konvertálni, ha a lassan futó függvényt ‘future’ hívásba ágyazzuk, illetve  további függvényhívásokat intéző pipe operátort lecseréljük promise pipe operátorra (‘%…>%’).

Miért?

Ilyenkor a Shiny szerver egy teljesen új folyamatot indít a future package segítségével, majd amikor ez a folyamat végzett, akkor visszatér, és a szerver kiértékeli. Mivel külön szálon fut, így nem blokkolja más felhasználók folyamatait.

Ez a funkcionalitás csak különböző felhasználó session-ök között működnek (egyelőre). A felhasználónak, aki aszinkron módon hív meg egy függvényt, ugyanúgy meg kell várnia, amíg ez a folyamat lefut, mint szinkron mód esetén.

Demó

Összegzés

Összességében az aszinkron hívási mód kiváló lehetőséget biztosít a lassan futó függvényhívások loadbalance-olására, viszont nem használható a JavaScript-hez hasonló módon, egyazon felhasználó folyamatainak aszinkron menedzselésére.

További információk:

Ha tetszett a cikk, iratkozz fel hírlevelünkre (a jobb felső sarokban az “értesítésre” kattintva), vagy kövess minket LinkedIn és Facebook csatornákon!