Gépi tanulás R-ben

Kategória: Adat
Alkategória: Adattudomány R-ben

Osztályozás

Az osztályozási feladatokban a cél változó kategória jellegű, pl. kutya vagy macska van a képen, milyen fajta íriszvirág adatait látjuk, vagy akár az, hogy igaz vagy hamis valami.

Felügyelt osztályozás

A felügyelt osztályozási feladatok azt jelentik, hogy a tanuló adatok címkékkel vannak ellátva, tehát tudjuk, hogy mi az eredmény. Az algoritmus során az adatokat két részre osztjuk:

  • Jellemző (feature): ami alapján tanulunk, ill. ami rendelkezésre áll a tesztelés során. Például a klasszikus íriszvirág esetén ez a csészelevél és a sziromlevél magassága és szélessége.
  • Cél (target): ez az, amire tanulunk, ill. amit megpróbálunk kitalálni. Az íriszvirág esetén ez a fajta, ami háromféle értéket vehet fel.

Számos felügyelt osztályozási algoritmus létezik, melyek közül néhányat itt megnézünk. A felügyelt osztályozási feladatok általános felépítése a következő:

  • Algoritmus függő: az adatok átalakítása, pl. normalizálása.
  • Az adatok két részre osztása: tanuló és teszt. Ha a tanuló algoritmus nem tartalmaz validálást, akkor tipikusan kétharmad-egyharmad arányban szokás felosztani, egyébként a tanuló-validáló-teszt felosztás tipikusan 80%-20%-20%.
  • Mindkét adat további 2-2 részre osztása: jellemző és cél.
  • A modell felépítése.
  • A predikciók végrehajtása a teszt adatokon.
  • Az eredmény összehasonlítása a tényleges adatokkal és a teszt kiértékelése: konfúziós mátrix létrehozása, különböző jósági mutatók kiszámolása.

A k legközelebbi szomszéd

Angolul k Nearest Neighbour, szokásos rövidítése kNN. Az algoritmus veszi a paraméterül kapott k darab legközelebbi szomszédot, tehát amire leginkább hasonlít az ismeretlen példány, és a többség alapján dönt.

A lenti példában a klasszikusnak számító Írisz virág fajtáját próbálja kitalálni a csésze- és sziromlevél szélessége és magassága alapján.

Első lépésben az adatokat normalizálni célszerű annak érdekében, hogy a távolság értelmezhető legyen. Ebben a példában ugyan nincs jelentősége, de ha más jellegű lenne a feladat, és az adatokban nagyságrendi eltérés lenne, akkor az algoritmus hibásan futna. Például autók esetén a tömeg kb-ban kifejezve ezres nagyságrendű, míg a fogyasztás 100 km-enként literben kifejezve általában 10 alatti, ezáltal normalizálás nélkül a távolságot szinte kizárólag a tömeg határozná meg.

Az Írisz virág esetén a 4 szám adatok normalizáljuk, azaz a [0, 1] intervallumba helyezzük:

iris_scaled <- as.data.frame(lapply(iris[, 1:4], scale))

Megjegyzés: ez egy egyszerűsített eset. Ez a módszer nem teljesen korrekt, ugyanis adatszivárgást tartalmazhat. Ha ugyanis a legnagyobb adat a teszt adatokba kerül, akkor a normalizálás után abban a dimenzióban nem lesz egyes, azaz az algoritmus tudni fogja, hogy a teszt adatokban van egy nagyobb érték, és azt is pontosan meg fogja tudni mondani, hogy mekkora. A valóságban először felosztjuk az adatokat, majd először a tanuló adatokat normalizáljuk saját magán belül, majd a teszt adatokat, a tanuló adatok intervallumával. Az írisz adatokon az eltérés kicsi (ha egyáltalán van), emiatt az egyszerűség érdekében ezt a módszert alkalmazzuk.

Következő lépésben az adatokat tanuló és teszt adatokra osztjuk. Először beállítjuk a véletlen szám generátor magját, hogy reprodukálható legyen. Utána kiválasztunk 100 megfigyelést, azaz az összesen 150 adat kétharmadát. A jellemző a 4 szám, amit fent normalizáltunk, a cél pedig a species. Ezt felhasználva osztjuk az adatokat 4 részre:

set.seed(123)
ind <- sample(1:nrow(iris), 100)
train_feature <- iris_scaled[ind, ]
test_feature <- iris_scaled[-ind, ]
train_target <- iris$Species[ind]
test_target <- iris$Species[-ind]

Következő lépésben felépítjük a modellt. A k értéket 5-re állítjuk:

library(class)
predicted_target <- knn(train_feature, test_feature, train_target, k = 5)

Létrehozzuk a konfúziós mátrixot, ami megmutatja, hogy a prediktált és a tényleges adatok hogyan viszonyulnak egymáshoz:

confusion_matrix <- table(predicted_target, test_target)
confusion_matrix

Az eredmény a következő:

                test_target
predicted_target setosa versicolor virginica
      setosa         16          0         0
      versicolor      0         18         0
      virginica       0          3        13

A találati arány meglehetősen jó, mindössze 3 versicolor fajtájú virágot osztályozott helytelenül virginicaként.

Részletesebb jósági mutatókat a caret külső csomag segítéségével tudunk kinyerni. Ehhez először fel kell telepíteni:

install.packages("caret")

Használata:

library(caret)
confusionMatrix(confusion_matrix)

Eredmény:

Confusion Matrix and Statistics

                test_target
predicted_target setosa versicolor virginica
      setosa         16          0         0
      versicolor      0         18         0
      virginica       0          3        13

Overall Statistics

               Accuracy : 0.94            
                 95% CI : (0.8345, 0.9875)
    No Information Rate : 0.42            
    P-Value [Acc > NIR] : 7.853e-15       

                  Kappa : 0.9095          

 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: setosa Class: versicolor Class: virginica
Sensitivity                   1.00            0.8571           1.0000
Specificity                   1.00            1.0000           0.9189
Pos Pred Value                1.00            1.0000           0.8125
Neg Pred Value                1.00            0.9062           1.0000
Prevalence                    0.32            0.4200           0.2600
Detection Rate                0.32            0.3600           0.2600
Detection Prevalence          0.32            0.3600           0.3200
Balanced Accuracy             1.00            0.9286           0.9595

Az eredmény néhány lényeges része az alábbi:

  • Accuracy (pontosság): a helyesen osztályozott esetek aránya.
  • 95% CI: a pontosság 95%-os konfidencia-intervalluma.
  • Sensitivity (érzékenység, más néven felidézés (recall))): a tényleges pozitív esetek megtalálási aránya.
  • Specificity (specificitás, más néven valós negatív ráta (true negative rate)): a ténylegesen negatív esetek megtalálási aránya.

Az algoritmus paramétere a k érték. Ezzel el lehet játszani. Pl. ha k = 10, akkor jobb eredményt kapunk:

table(knn(train_feature, test_feature, train_target, k = 10), test_target)

Mindössze egyet rontott el:

                test_target
predicted_target setosa versicolor virginica
      setosa         16          0         0
      versicolor      0         20         0
      virginica       0          1        13

Logisztikus regresszió

A nevével ellentétben a logisztikus regresszió osztályozási feladat, azon belül csak bináris osztályozást tesz lehetővé. Egy szám és egy bináris változó közötti kapcsolatot alakítja ki. Egy ún. szigmoid függvén segítségével teremt kapcsolatot a két adat között, ami megmondja, hogy adott folytonos érték mellett mekkora eséllyel lesz az egyik vagy a másik bináris érték.

Például egy vizsga vagy sikerül vagy nem, tehát bináris, a ráfordított tanulás hossza viszont folytonos. Az intuíciónk az, hogy minél többet tanulunk, annál nagyobb eséllyel sikerül a vizsga. A logisztikus regresszióval olyanokat tudunk kiszámolni, hogy hány perc tanulással lesz mondjuk 10%, 50% vagy 90% a sikeres vizsga valószínűsége.

A kétmintás t-próbánál már láttunk, hogy a fogyasztás függ a sebességváltók számától: a kézi sebességváltós autók fogyasztása alacsonyabb, azaz több mérföldet lehet megtenni egy gallon üzemanyaggal. Most megfordítjuk a dolgot, és a fogyasztásból próbálunk következtetni a sebességváltó típusára.

R-ben ezt a tesztet a glm() függvény segítéségével tudjuk végrehajtani, ami a generalizált lineáris modellt jelenti, és később még lesz szó róla a regressziónál.

model_logistic_mtcars <- glm(am ~ mpg, data = mtcars, family = "binomial")
summary(model_logistic_mtcars)

Eredmény:

Call:
glm(formula = am ~ mpg, family = "binomial", data = mtcars)

Coefficients:
            Estimate Std. Error z value Pr(>|z|)   
(Intercept)  -6.6035     2.3514  -2.808  0.00498 **
mpg           0.3070     0.1148   2.673  0.00751 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 43.230  on 31  degrees of freedom
Residual deviance: 29.675  on 30  degrees of freedom
AIC: 33.675

Number of Fisher Scoring iterations: 5

A p-érték alapján erős a kapcsolat a fogyasztás és a sebességváltó típusa között. Az eredmény vizuális ábrázolása:

plot(
    mtcars$mpg,
    mtcars$am,
    main = "Logisztikus regresszió",
    xlab = "Fogyasztás (mérföld gallononként)",
    ylab = "Sebességváltó (0: automata, 1: kézi)",
    pch = 16
)
curve(
    predict(model_logistic_mtcars, newdata = data.frame(mpg = x), type = "response"),
    add = TRUE,
    col = "red",
    lwd = 2
)
logisztikus_regresszio.png

Ez alapján kb. 22 mérföld per gallon alatt (nagyobb fogyasztás) valószínűbb, hogy a sebességváltó típusa automata, míg afelett kézi.

Döntési fa

A döntési fa egy felügyelt tanulási algoritmus, amely egy sor döntési szabályt használ az adatok osztályozására. A döntési szabályok egyszerű eldöntendő kérdések, pl. egy feature kisebb-e egy adott értéknél, és vagy további kérdéseket teszt fel, vagy dönt.

Először bontsuk az adatokat tanló és teszt adatokra. Ugyanazt a magot használjuk mint korábban.

set.seed(123)
train_index <- sample(1:nrow(iris), 100)
train_data <- iris[train_index, ]
test_data <- iris[-train_index, ]

A modell felépítése:

library(rpart)
tree_model <- rpart(Species ~ ., data = train_data, method = "class")

A predikciók végrehajtása és az eredmény megjelenítése:

predicted_target <- predict(tree_model, test_data, type = "class")
table(predicted_target, test_data$Species)

Eredmény:

predicted_target setosa versicolor virginica
      setosa         16          0         0
      versicolor      0         20         1
      virginica       0          1        12

A k legközelebbi szomszéd első verziójához képest itt eggyel kevesebb hibás osztályozás lett, két különböző helyen.

A döntési fa jelentős előnye a kiváló interpretálhatóság, amiből gyakran további következtetéseket tudunk levonni. Jelenítsük meg a modellt:

print(tree_model)

Eredmény:

n= 100 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

1) root 100 63 virginica (0.3400000 0.2900000 0.3700000)  
  2) Petal.Length< 2.45 34  0 setosa (1.0000000 0.0000000 0.0000000) *
  3) Petal.Length>=2.45 66 29 virginica (0.0000000 0.4393939 0.5606061)  
    6) Petal.Width< 1.75 33  4 versicolor (0.0000000 0.8787879 0.1212121) *
    7) Petal.Width>=1.75 33  0 virginica (0.0000000 0.0000000 1.0000000) *

Magyarázat:

  • A gyökérben 100 elem van. A virginica van többségben, de ha itt megállnánk, és mindegyikre azt mondanánk, hogy virginica, akkor 63 esetet helytelenül osztályoznánk már a tanuló adatok alapján. Ráadásul ez az eltérés amiatt van, mert a véletlen felosztás ebből választott legtöbbet. Itt tehét folytatjuk.
  • Az első kérdés az, hogy a sziromlevél hossza 2.45-nél kisebb, vagy nagyobb-e vagy egyenlő vele.
  • Ha kisebb, akkor a 34 eset mindegyiket setosa, és ezen az ágon meg is állunk, meg van za eredmény.
  • Ha nagyobb, akkor a 66 esetből virginicából van a legtöbb, de 29 nem virginica, ami kb. 44%, emiatt itt még nem állunk meg.
  • A következő kérdés (a csészelevél hossza >= 2.45 eseten belül) az, hogy a csészelevél szélessége kisebb-e mint 1.75 vagy nem.
  • Ha kisebb, akkor a 33 esetből 4 kivételével versicolor van. Ez elég meggyőző az algoritmus szerint, és itt meg is áll.
  • Ha nem kisebb, akkor az összes megfigyelés virginica, szintén megállunk.

Látható, hogy a csészelevél méreteit nem is vette figyelembe. Ha a sziromlevél mérete kisebb mint 1.75, akkor akár mehetne is tovább; minden bizonnyal beállítás kérdése, hogy megy-e.

Mivel csak kétféle értéket vett figyelembe, szórásdiagramon is jól látható, hogy mi alapján döntött algoritmus. Ez az összes adatot tartalmazza, de jelentősen nem tér el a teszt adatoktól:

plot(
    iris$Petal.Length,
    iris$Petal.Width,
    col = iris$Species,
    main = "Iris fajták szórásdiagramja",
    xlab = "Szirom hossza",
    ylab = "Szirom szélessége",
    pch = 16
)
legend("topleft", legend = levels(iris$Species), col = 1:3, pch = 16)
dontesi_fa.png

A 2.45 cm-es szirom hossz egyértelműen leválasztja a setosa virágot. Az 1.75-ös sziromszélesség pedig majdnem teljesen kettéválasztja a másik kettőt. Látható, hogy ez tökéletesen nem választható ketté, és túl bonyolult modellt nem is érdemes készíteni, mert túltanulás veszélye léphet fel.

Együttes módszer véletlen erdő segítségével

Az együttes módszerek egyszerre több modellt használnak. A modellépítés és a számolás is lassúbbá válik, cserébe pontosabb eredményre számíthatunk.

A véletlen erdő a bagging módszer családjába tartozik. Ugyanabból a modellből eltérő paraméterezéssel több modellt épít fel (jelen esetben több különböző döntési fát), és többségi szavazással dönt.

R-ben ehhez a randomForest külső könyvtárat célszerű használni, amit a szokásos módon tudunk feltelepíteni:

install.packages("randomForest")

A teszt végrehajtása (alapértelmezésben 500 döntési fát épít fel):

library(randomForest)
rf_model <- randomForest(Species ~ ., data = train_data)
predictions_rf <- predict(rf_model, test_data)
table(predictions_rf, test_data$Species)

Eredmény:

             setosa versicolor virginica
  setosa         16          0         0
  versicolor      0         19         0
  virginica       0          2        13

Egy kicsit más lett, de nem lett jobb.

Felügyelet nélküli osztályozás a k közép klaszterezéssel

Ez a gépi tanulás típus címkék nélkül próbálja az adatokat kategóriákba sorolni. A legismertebb ilyen algoritmus a k közép klaszterezés (k means clustering). A klaszterek száma (k) bemenő érték. Ez az algoritmus úgy működik, hogy véletlenszerűen kiválaszt k darab elemet, mindegyikről megállapítja, hogy melyikhez van legközelebb, majd veszi az így kialakuló klaszterek átlagát (egy képzeletbeli pontot), azokkal ismét végrehajtja a besorolást, és ezt mindaddig ismétli, amíg történt változás.

Az írisz virág esetén a számok alapján próbáljuk 3 kategóriába sorolni:

iris_numeric <- iris[, 1:4]
set.seed(123)
klaszterek <- kmeans(iris_numeric, centers = 3)
klaszterek

Eredmény:

K-means clustering with 3 clusters of sizes 50, 62, 38

Cluster means:
  Sepal.Length Sepal.Width Petal.Length Petal.Width
1     5.006000    3.428000     1.462000    0.246000
2     5.901613    2.748387     4.393548    1.433871
3     6.850000    3.073684     5.742105    2.071053

Clustering vector:
  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [38] 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [75] 2 2 2 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 2 3 3 3 3 2 3 3 3 3
[112] 3 3 2 2 3 3 3 3 2 3 2 3 2 3 3 2 2 3 3 3 3 3 2 3 3 3 3 2 3 3 3 2 3 3 3 2 3
[149] 3 2

Within cluster sum of squares by cluster:
[1] 15.15100 39.82097 23.87947
 (between_SS / total_SS =  88.4 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
[6] "betweenss"    "size"         "iter"         "ifault"

Mivel a komponensek száma 50, 62 és 38, az már ebből látható, hogy jelentősen több hibára számíthatunk. A Clustering vector először csupa egyest tartalmaz, majd ketteseket és hármasokat részben elkülönülten, részben vegyesen.

Készítsük el a konfúziós mátrixot:

table(klaszterek$cluster, iris$Species)

Eredmény:

    setosa versicolor virginica
  1     50          0         0
  2      0         48        14
  3      0          2        36

Az egyes kategória egyértelműen a setosa, ami telitalálat. A kettes a versicolor, viszont 14 virginicát is ebbe a kategóriába sorolt. A hármas a virginicának felel meg, és ez csak 2 hibát tartalmaz.

Regressziós elemzés

A regressziós elemzés egy felügyelt tanulási módszer, amely két vagy több szám változó közötti kapcsolatot modellezi.

Egyszeres lineáris regresszió

A lineáris regresszió egy egyenes vonallal (regressziós egyenessel) próbálja leírni ezt a kapcsolatot, így az egyik változó (függő változó) értékét a másik (független változó) alapján próbálja meg előre jelezni.

Az R-ben a lineáris regresszió az alaptelepítés részét képező lm() (linear model) függvénnyel valósítható meg. Korábban már láttuk a kapcsolatot a fogyasztás és a tömeg között; most vizsgáljuk meg az lm() függvénnyel:

model <- lm(mpg ~ wt, data = mtcars)
summary(model)

Eredmény:

Call:
lm(formula = mpg ~ wt, data = mtcars)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.5432 -2.3647 -0.1252  1.4096  6.8727 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  37.2851     1.8776  19.858  < 2e-16 ***
wt           -5.3445     0.5591  -9.559 1.29e-10 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.046 on 30 degrees of freedom
Multiple R-squared:  0.7528,    Adjusted R-squared:  0.7446 
F-statistic: 91.38 on 1 and 30 DF,  p-value: 1.294e-10

Az eredmény értelmezése:

  • Coefficients: a modell egyenesének egyenletét írja le (y = 37.285 - 5.344 * x). Az (Intercept) az egyenes Y-tengelyt metsző pontja, a wt pedig a meredeksége.
  • p-value: a wt változóhoz tartozó p-érték (a Pr(>|t|) oszlopban) nagyon kicsi (1.29e-10), ami azt jelenti, hogy a tömeg szignifikánsan befolyásolja a fogyasztást.
  • R-squared: az R2 = 0.7528 érték azt jelzi, hogy a fogyasztás változékonyságának mintegy 75%-át magyarázza a tömeg.

Többszörös lineáris regresszió

A lineáris regresszió többszörös is lehet. Például nézzük meg, hogy a fogyasztás hogyan függ a tömegtől és a teljesítménytől:

model_multi <- lm(mpg ~ wt + hp, data = mtcars)
summary(model_multi)

Eredmény:

Call:
lm(formula = mpg ~ wt + hp, data = mtcars)

Residuals:
   Min     1Q Median     3Q    Max 
-3.941 -1.600 -0.182  1.050  5.854 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 37.22727    1.59879  23.285  < 2e-16 ***
wt          -3.87783    0.63273  -6.129 1.12e-06 ***
hp          -0.03177    0.00903  -3.519  0.00145 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.593 on 29 degrees of freedom
Multiple R-squared:  0.8268,    Adjusted R-squared:  0.8148 
F-statistic: 69.21 on 2 and 29 DF,  p-value: 9.109e-12

Az eredmény értelmezése:

  • Coefficients: a modell egyenlete mpg = 37.22 - 3.87 * wt - 0.03 * hp lett. Mindkét változóhoz tartozó meredekség megmutatja, hogy a másik változó rögzítése mellett a tömeg vagy a lóerő hogyan befolyásolja a fogyasztást.
  • p-value: a wt és a hp változók p-értékei is nagyon kicsik (< 0.01), ami azt jelenti, hogy mindkét változó szignifikánsan hozzájárul a fogyasztás előrejelzéséhez.
  • R-squared: A többszörös regresszióval az R2 = 0.826 értékre javult, ami azt jelzi, hogy a fogyasztás változékonyságának immár 82.6%-át magyarázza a tömeg és a lóerő együttvéve. Ez a korábbi, egyetlen változós modellnél jobban illeszkedik az adatokra.

Az eredmény vizuális illusztrálása: egy scatterplot a modell által prediktált értékek és a valós értékek között jól mutatja a modell teljesítményét.

predikciok <- predict(model_multi, newdata = mtcars)
plot(
    mtcars$mpg,
    predikciok,
    main = "Többszörös regresszió: valós vs. predikált értékek",
    xlab = "Valós fogyasztás (mpg)",
    ylab = "Predikált fogyasztás (mpg)",
    pch = 16,
    col = "blue",
    xlim = c(8, 34),
    ylim = c(8, 34)
)
abline(a = 0, b = 1, col = "red", lwd = 2)

Eredmény:

regresszio_pred.png

Az ideális illeszkedés a 45°-os piros vonal lenne, de így is igen erős a kapcsolat a prediktált és a tényleges értékek között.

Poisson regresszió

A Poisson regresszió a számláló jellegű adatok modellezésére szolgál, ahol a függő változó valaminek (pl. események) számát jelenti.

R-ben ezt a glm() függvény segítségével tudjuk meghatározni, ami a generalizált lineáris modell rövidítése. Paraméterként át kell adni a regresszió fajtáját, ami ebben az esetben family = "poisson".

Példaként tekintsük a teljesítmény és a karburátorok száma közötti kapcsolatot:

model_poisson_mtcars <- glm(carb ~ hp, data = mtcars, family = "poisson")
summary(model_poisson_mtcars)

Alapvetően arra vagyunk kíváncsiak, hogy hogyan függ a teljesítmény a karburátorok számától, itt viszont - a feladat jellegéből fakadóan pont a fordítottját tudjuk elemezni. Az eredmény:

Call:
glm(formula = carb ~ hp, family = "poisson", data = mtcars)

Coefficients:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept) 0.148971   0.265018   0.562    0.574    
hp          0.005517   0.001387   3.977 6.97e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for poisson family taken to be 1)

    Null deviance: 27.043  on 31  degrees of freedom
Residual deviance: 12.279  on 30  degrees of freedom
AIC: 105.64

Number of Fisher Scoring iterations: 4

A p-érték alapján kijelenthető, hogy szignifikáns a kapcsolat a karburátorok száma és a teljesítmény között. Az eredmény vizualizálása:

plot(mtcars$hp, mtcars$carb,
     main = "Poisson regresszió: karburátorok száma a lóerő függvényében",
     xlab = "Teljesítmény (lóerő)",
     ylab = "Karburátorok száma",
     pch = 16)
curve(predict(model_poisson_mtcars, newdata = data.frame(hp = x), type = "response"),
      add = TRUE, col = "red", lwd = 2)
regresszio_poisson.png

Dimenziószám csökkentés

A dimenziócsökkentés a változók számának csökkentésére szolgál. A cél az, hogy a sokváltozós adatok legfontosabb jellemzőit megtaláljuk, miközben a lehető legtöbb információt megőrizzük. A főkomponens-analízis (PCA) a leggyakoribb dimenziócsökkentési módszer, amely a változók egy új, kisebb csoportját hozza létre, amely a legnagyobb varianciát (szórás) hordozza az adatokban.

R-ben a prcomp() függvény hajtja végre a főkomponens-analízist:

iris_numeric <- iris[, 1:4]
pca_result <- prcomp(iris_numeric, scale. = TRUE)
pca_result

Eredmény:

Standard deviations (1, .., p=4):
[1] 1.7083611 0.9560494 0.3830886 0.1439265

Rotation (n x k) = (4 x 4):
                    PC1         PC2        PC3        PC4
Sepal.Length  0.5210659 -0.37741762  0.7195664  0.2612863
Sepal.Width  -0.2693474 -0.92329566 -0.2443818 -0.1235096
Petal.Length  0.5804131 -0.02449161 -0.1421264 -0.8014492
Petal.Width   0.5648565 -0.06694199 -0.6342727  0.5235971

Az egyes PC-k a főkomponensek. Pl. a PC1-ben a csészelevél hossza valamint a sziromlevél dimenziói nagyjából egyforma pozitív súllyal szerepelnek, míg a csészelevél szélessége - kissé meglepő módon - egy negatív értékkel.

Lássunk további statisztikát:

summary(pca_result)

Eredmény:

Importance of components:
                          PC1    PC2     PC3     PC4
Standard deviation     1.7084 0.9560 0.38309 0.14393
Proportion of Variance 0.7296 0.2285 0.03669 0.00518
Cumulative Proportion  0.7296 0.9581 0.99482 1.00000

Itt láthatjuk, hogy az első főkomponens a varianciának közel 73%-ét, az első kettő együtt pedig majdnem 96%-át megmagyarázza. Azaz ha csak az első két főkomponenst tartanánk meg (magyarán megfeleznénk az adatot), akkor 96%-ban visszaállítható lenne a teljes adathalmaz.

Mélytanulás

A mélytanulás áttekintése

A mélytanulás lényege a következő. A modell ún. neuron rétegeket alakít ki. Van egy bemenő réteg, tetszőleges számú rejtett réteg és egy kimenő réteg. A bemenő réteg neuronjainak a száma általában megfelel az input méretének (pl. a feature változók számának), a kimenő réteg osztályozási feladat esetén tipikusan a lehetőségek száma, mindegyiknek megadva a valószínűségét.

A predikció során minden egyes neuron átadja az eredményét a következő réteg összes neuronjának. Ez azt is jelenti, hogy az előtte levő réteg összes neuronjától megkapja az eredményt. Az eredményt a kapcsolatuk súlyával megszorozza, a szorzatokat összeadja, és ezt adja tovább a következő réteg összes neuronjának.

A modellépítés valójában a súlyoknak a beállítását jelenti. A súlyok finomhangolása több iterációban (epoch) történik. A konkrét értékek feladatfüggőek, de tipikusan néhány rejtett réteg van, tízes nagyságrendű neuron egy-egy rétegen, és több tíz vagy akár több száz tanulási iteráció.

A túlillesztés elkerülésére alkalmazhatjuk a neuronok bizonyos arányának a figyelmen kívül hagyását.

A szükséges R könyvtárak és telepítésük

R-ben (és Pythonban is) az egyik leggyakrabban használt mélytanulásos módszer a Tensorflow és a Keras páros. A Tensorflow hajtja végre a tényleges mélytanulást, annak az interfésze viszont meglehetősen bonyolult. Ezt egyszerűsíti a Keras, ami valójában egy API réteg a Tensorflow felett.

A telepítés nem szokványos. Először a Tensorflow-t célszerű feltelepíteni egy frissen indított környezetből, ahol nincs betöltve semmi. A folyamat szokásosan a tensorflow telepítésével indul:

install.packages("tensorflow")

Ez viszont nem magát a Tensorflow csomagot telepíti, hanem azt a parancsot, amivel fel tudjuk telepíteni. Ez jelentős mennyiségű adatot tölt le az internetről, emiatt célszerű stabil vezetékes korlátlan internet segítségével végrehajtani.

library(tensorflow)
install_tensorflow()

Ha kész, akkor szokásos módon telepíthetjük a Kerast (keras3):

install.packages("keras3")

Elvileg úgy is fel lehet telepíteni, hogy a nagyméretű telepítést Kerason keresztül hajtjuk végre; nálam Python verzió inkompatibilitás miatt nem sikerült.

Mélytanulás példa

Könyvtár betöltése:

library(keras3)

A mélytanulás 0 és 1 közötti számokkal dolgozik, emiatt az adatokat ebbe kell alakítani. A szám adatokat normalizálni kell, ahogy azt már láttuk. A kategorikus adatokat szám adatokká a következő módszerrel tudjuk alakítani. Minden lehetséges kategóriához felveszünk egy oszlopot. Ha az adott sor abba a kategóriába esik, akkor az oszlop értéke 1 lesz, egyébként 0. Ennek a módszernek a neve one hot encoder, és a Keras to_categorical() függvényének segítségével tudjuk ezt egyszerűen végrehajtani.

iris_scaled <- as.matrix(iris[, 1:4])
species_labels <- as.numeric(iris$Species) - 1
set.seed(123)
ind <- sample(1:nrow(iris), 100)
train_feature <- iris_scaled[ind, ]
test_feature <- iris_scaled[-ind, ]
train_target <- to_categorical(species_labels[ind])
test_target <- to_categorical(species_labels[-ind])

A tanuló-teszt felosztásnál ugyanazt a magot használtuk mint korábban, az összehasonlíthatóság érdekében.

A modell felépítése:

model <- keras_model_sequential(
  layers = list(
    keras3::layer_input(shape = c(4)),
    keras3::layer_dense(units = 64, activation = "relu"),
    keras3::layer_dropout(rate = 0.2),
    keras3::layer_dense(units = 64, activation = "relu"),
    keras3::layer_dropout(rate = 0.2),
    keras3::layer_dense(units = 3, activation = "softmax")
  )
)

A bemenő réteg 4 neuronból áll, ami az íriszvirág 4 adata lesz. Van 2 rejtett réteg, két 64 neuronból álló rejtett réteg, amelyek után 20%-át eldobjuk. A kimenő réteg 3 neuronból áll, mindhárom virág típusra egy-egy. A modell lefordítása (kompilálása):

model %>% keras3::compile(
  loss = "categorical_crossentropy",
  optimizer = "adam",
  metrics = "accuracy"
)

A modell tanítása:

model %>% keras3::fit(
  train_feature,
  train_target,
  epochs = 300,
  batch_size = 32,
  validation_split = 0.2
)

Ez a leghosszabb folyamat. Itt az iterációk számát 300-ra állítottuk be.

A predikció a következőképpen történik:

predictions <- model %>% predict(test_feature)
predicted_classes <- max.col(predictions) - 1
confusion_matrix <- table(predicted_classes, species_labels[-ind])
print(confusion_matrix)

Az eredmény:

predicted_classes  0  1  2
                0 16  0  0
                1  0 19  0
                2  0  2 13

Ez a modell szintén kettőt rontott. Több végrehajtással eltérő eredmény kaphatunk, mivel magában a mélytanulásos algoritmusban is van véletlenítés.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License