A sorozat korábbi bejegyzéseiben áttekintést adtunk a tanuló algoritmusokról, illetve azok pénzügyi hasznosításának lehetőségeiről. Először egy csődvalószínűség modellezési példán keresztül vizsgáltuk "gradient descent" (vagy gradiens) módszer és a genetikus algoritmusok működését. Az előző részben pedig a neurális hálózatok működését mutattuk be az említett példa segítségével.
Most ezt folytatjuk tovább a neurális hálózat kimeneti értékeinek meghatározásával és a hálózat tanításának összefoglalójával: egy R programon keresztül szemléltetjük a főbb lépéseket.
R kód – feedforward és training
A neurális hálózatok tanítására az R-ban használhatjuk a neuralnet könyvtárat. A jelen példában azonban nem ezt fogjuk használjuk, hanem betekintést adunk a “black box”-ba: ezért egy kifejezetten 2+1 rétegű neurális hálózatok optimalizálására szolgáló, a példa kedvéért megírt kódot mutatunk be (mivel ezen keresztül a jobban szemléltethető a neurális hálózatok tanítása)
Először is lássuk a neurális háló kimeneti értékét kiszámoló “feedforward” algoritmust, amely valójában néhány egyszerű mátrix szorzás.
[1] Első lépésben a Z1 értékeket, vagyis a rejtett réteg neuronjainak bemenetét számoljuk ki az X mátrixot szorozva a kapcsolati súlyokat tartalmazó W1 mátrixszal.
Az X mátrix 1-1 sora 1-1 mintát jelent a tanuló adathalmazban, vagyis egy adott cégre vonatkozóan tartalmazza az egyes pénzügyi mutatók értékeit (és ennek a mátrix oszlopai jelentik a pénzügyi mutatókat illetve azok értékeit az egyes mintaelemek tekintetében). A lenti ábrán az algoritmus szempontjából az X mátrix 1-1 sorát a pénzügyi mutatókat tartalmazó bekeretezett rész jelenti.
A W1 mátrix soronként tartalmazza a súlyokat, amely egy adott pénzügyi mutató és a rejtett réteg neuronjai közötti kapcsolatot jellemzi, ld. az ábrán:
A súlymátrixban az oszlopok itt a rejtett réteg neuronjait jelölik -> mivel a példában 3 neuron van a rejtett rétegben, ezért a példában 3 oszlopa lesz a W1 mátrixnak (és annyi sora, ahány pénzügyi mutatót figyelmbe veszünk).
[2] A Z1 mátrix végeredményben mintaelemenként tartalmazza a rejtett réteg neuronjainak input értékét. Ebből a sigmoid() függvény alkalmazásával kiszámolhatók a rejtett neuronok aktivációs értékei (act_h).
[3] Majd végül a kimeneti réteg aktivációját is meghatározzuk az 1-2. lépéshez hasonlóan! A Z2 értékeke itt is az act_h és a W2 mátrix szorzata, illetve ebből a neurális hálónk kimeneti neuronjának aktivációja (act_out) is a sigmoid függvény segítségével kerül kiszámításra.
feedforward_2Lyrs <- function(x, w1, w2) {
z1 <- cbind(1,x) %*% w1
act_h <- sigmoid(z1)
z2 <- cbind(1, act_h) %*% w2
act_out <- sigmoid(z2)
list(output = act_out, act_h = act_h)
}
A feedforward_2Lyrs() függvény kiszámolja a kimeneti neuron aktivációs értékekét (output), illetve a rejtett réteg neuronjainak aktivációs értékét (act_h). A kódban látható sigmoid() függvény a korábban is említett aktivációs függvény leképzése R-ban:
sigmoid <- function(z) {
1.0 / (1.0 + exp(-z))
}
A következő kódrészlet a neurális hálózat optimalizálásának lényegét mutatja be: ez maga a hálózat tanítása - vagyis a “helyes” súlyok kiszámítás - iteratív módon. A tanítást a train() függvény meghívásával végezzük.
train <- function(X, Y, hidden = 3, learn_rate = 0.005, iterations = 1000) {
### 1) neuronok közötti kapcsolatokat reprezentáló súlyok inicializálása
d <- ncol(X)
w1 <- matrix(rnorm(d * hidden), d, hidden)
w2 <- as.matrix(rnorm(hidden))
bias1 <- matrix(rnorm(hidden),1,hidden)
bias2 <- matrix(rnorm(ncol(Y)),1,ncol(Y))
w1 <- rbind(bias1, w1)
w2 <- rbind(bias2, w2)
### 2) súlyok újraszámolása – alapesetben 1000 iterációs lépés
for (i in 1:iterations) {
### 2/A) aktuális súlyok mellett a neurális háló neuronjainak – kimeneti és rejtett is! – aktivációs értékeit is kiszámoljuk
ffRes <- feedforward_2Lyrs(X, w1, w2)
### 2/B) backpropagation algoritmussal újraszámoljuk a w1 és w2 súly mátrixok értékeit
bp <- backPropNN_2Lyrs(X, Y,
Yhat = ffRes$output,
w1, w2,
sigm_h = ffRes$act_h,
lRate = learn_rate)
w1 <- bp$w1
w2 <- bp$w2
}
list(output = ffRes$output, w1 = w1, w2 = w2)
}
A hálózat tanításának lépései röviden összefoglalva:
- lépés – kezdeti súlyok inicializálása
- Véletlenszerű értékekkel inicializáljuk a W1 és W2 súlymátrixokat, illetve mindegyikhez hozzáillesztünk egy ún. eltolás (bias) értéket/vektort az rbind() függvény segítségével – ezek az aktiváció függvény kimenetét tolják el bizonyos mértékben jobbra vagy balra (bővebben a bias szerepéről itt).
- A W1 mátrix esetében az eltolás értékek 3 elemű vektorként kerülnek hozzáadásra egy új sorként, hiszen mindhárom rejtett neuronnak a bemeneti értékét képezik (vagyis az 5 bemenő pénzügyi mutatót kiegészítjük mindhárom neuron esetében plusz egy-egy eltolás értékkel)
- lépés – iteráció, vagyis a súlyok újraszámolása
- Után a paraméterként megadott számú iterációt hajtunk végre (az iterációk számának default értéke 1000) a neurális hálózat súlyainak optimalizálása céljából.
- Az iterációk során feedforward_2Lyrs() függvény segítségével először kiszámoljuk a neurális hálózat kimeneti értékeit illetve a rejtett réteg neuronjainak aktivációs értékeit.
- A backprop_2Lyr() függvény meghívásával újraszámoljuk W1 és W2 súlyokat: a tanulási ráta alap esetben 0.005 – vagyis az egyes neuronok „hibájának” (pontosabb az output neuron hibájához való „hozzájárulását”) 0.5%-val módosítjuk a súlyokat. A függvény további bemenetei az eredeti minta független változói (X) és tényleges kimentek (Y), valamint az aktuális súlyokkal számolt kimenet (Yhat), illetve az 1. és 2. réteg aktuális súlyai (w1,w2).
- A backprop_2Lyr() függvény kimenetként megkapott újraszámolt súlyokat eltároljuk az eredeti W1/W2 mátrixokban és újra indul a következő iterációs kör.
A neruális hálózat súlyainak meghatározása:
library(readxl)
setwd("C:/InputDat/")
dfNNTrain <- read_excel("defaultData_NN.xlsx", col_names = TRUE, sheet = 'train')
X <- as.matrix(dfNNTrain[,2:6])
Y <- as.matrix(dfNNTrain$csod)
bbResult <- train(X, Y, 3, 0.005, 1000)
dfNNTest <- read_excel("defaultData_NN.xlsx", col_names = TRUE, sheet = 'test')
Xt <- as.matrix(dfNNTest[,2:6])
Yt <- as.matrix(dfNNTest$csod)
ffTest <- feedforward_2Lyrs(Xt,bbResult$w1,bbResult$w2)
ffTest$csod <- Yt
ffTest$csod_pr <- round(ffTest$output, digits = 0)
mean(abs(ffTest$csod - ffTest$csod_pr))
Továbblépési lehetőségek, a neurális háló előrejelző képességének javítása
- További pénzügyi mutatók figyelembe vétele.
- A pénzügyi mutatók tendenciájának felhasználása inputként: míg pl. a likviditási mutatók alacsony értéke sokszor egy hosszabb folyamat következménye és a csődöt közvetlenül megelőző időkben romlik le, addig bizonyos hatékonysági mutatók tartósan alacsony értéke illetve azok negatív tendenciája hosszabb távon vezethet csődhöz
- Nem csak pénzügyi, hanem piaci vagy egyéb mutatók, attribútumok figyelembe vétele a neurális hálózat bemenetei között: pl. iparág szerinti differenciálás, piaci részesedés, cég életkora vagy tulajdonosi kör tőkeereje.