Esittely
Sekvenssin ennustamiseen liittyviä ongelmia on ollut jo pitkään. Niitä pidetään yhtenä vaikeimmin ratkaistavista ongelmista datatieteen alalla. Niihin kuuluu monenlaisia ongelmia; myynnin ennustamisesta kuvioiden löytämiseen pörssidatasta, elokuvien juonikuvioiden ymmärtämisestä puhetapasi tunnistamiseen, kielikäännöksistä seuraavan sanan ennustamiseen iPhonen näppäimistöllä.
Datatieteen viimeaikaisten läpimurtojen myötä on havaittu, että lähes kaikissa näissä sekvenssin ennustamiseen liittyvissä ongelmissa Long short term memory -verkot eli LSTM:t on havaittu tehokkaimmaksi ratkaisuksi.
LSTM:t ovat monella tapaa etulyöntiasemassa perinteisiin feed-forward-neuraaliverkkoihin ja RNN:iin verrattuna. Tämä johtuu niiden ominaisuudesta muistaa valikoivasti kuvioita pitkiä aikoja. Tämän artikkelin tarkoituksena on selittää LSTM:ää ja antaa sinulle mahdollisuus käyttää sitä tosielämän ongelmissa. Katsotaanpa!
Huomautus: Jotta voit käydä artikkelin läpi, sinulla on oltava perustiedot neuroverkoista ja siitä, miten Keras (syväoppimiskirjasto) toimii. Voit tutustua mainittuihin artikkeleihin ymmärtääksesi nämä käsitteet:
- Understanding Neural Network From Scratch
- Fundamentals of Deep Learning – Introduction to Recurrent Neural Networks
- Tutorial: Optimizing Neural Networks using Keras (with Image recognition case study)
Sisällysluettelo
- Flashback: Katsaus rekursiivisiin neuroverkkoihin (Recurrent Neural Networks, RNN)
- Limitations of RNNs
- Improvement over RNN : Long Short Short Term Memory (LSTM)
- LSTM:n arkkitehtuuri
- Forget Gate
- Input Gate
- Output Gate
- Tekstin tuottaminen LSTM:n avulla.
Flashback: Katsaus rekursiivisiin neuroverkkoihin (Recurrent Neural Networks, RNN)
Tarkastellaan esimerkkinä peräkkäistä dataa, joka voi olla tietyn osakkeen pörssidata. Yksinkertainen koneoppimismalli tai keinotekoinen neuroverkko voi oppia ennustamaan osakekursseja useiden ominaisuuksien perusteella: osakkeen volyymi, avausarvo jne. Vaikka osakkeen hinta riippuu näistä ominaisuuksista, se on myös suurelta osin riippuvainen osakkeen arvoista edellisinä päivinä. Itse asiassa kauppiaalle nämä edellisten päivien arvot (tai trendi) ovat yksi tärkeimmistä ratkaisevista tekijöistä ennusteita tehtäessä.
Tavanomaisissa feed-forward-neuraalisissa verkoissa kaikkia testitapauksia pidetään riippumattomina. Toisin sanoen, kun mallia sovitetaan tietylle päivälle, aiempien päivien osakekursseja ei oteta huomioon.
Tämä ajasta riippuvuus saavutetaan rekursiivisten neuroverkkojen avulla. Tyypillinen RNN näyttää seuraavalta:
Tämä voi olla ensisilmäyksellä pelottavaa, mutta kun se on kerran avattu, se näyttää paljon yksinkertaisemmalta:
Nyt meidän on helpompi havainnollistaa, miten nämä verkot ottavat huomioon osakekurssien trendin, ennen kuin ennustavat tämän päivän osakekurssit. Tässä jokainen ennuste hetkellä t (h_t) on riippuvainen kaikista aiemmista ennusteista ja niistä opitusta informaatiosta.
RNN:t voivat ratkaista sekvenssien käsittelyä koskevan tarkoituksemme suurelta osin, mutta eivät täysin. Haluamme, että tietokoneemme ovat riittävän hyviä kirjoittamaan Shakespearen sonetteja. Nyt RNN:t ovat loistavia, kun kyse on lyhyistä konteksteista, mutta pystyäkseen rakentamaan tarinan ja muistamaan sen, tarvitsemme mallimme ymmärtämään ja muistamaan kontekstin sekvenssien takana, aivan kuten ihmisaivot. Tämä ei ole mahdollista yksinkertaisella RNN:llä.
Miksi? Katsotaanpa.
RNN:ien rajoitukset
Rekursiiviset neuroverkot toimivat hienosti, kun käsittelemme lyhytaikaisia riippuvuuksia. Eli kun niitä sovelletaan seuraavanlaisiin ongelmiin:
RNN:t osoittautuvat varsin tehokkaiksi. Tämä johtuu siitä, että tällä ongelmalla ei ole mitään tekemistä lausekkeen kontekstin kanssa. RNN:n ei tarvitse muistaa, mitä ennen tätä sanottiin tai mikä oli sen merkitys, sen tarvitsee vain tietää, että useimmissa tapauksissa taivas on sininen. Näin ollen ennuste olisi:
Vanilla RNN:t eivät kuitenkaan ymmärrä kontekstia syötteen takana. Jotain, joka on sanottu kauan sitten, ei voida palauttaa mieleen, kun tehdään ennusteita nykyhetkessä. Ymmärretään tämä esimerkkinä:
Tässä voidaan ymmärtää, että koska kirjoittaja on työskennellyt Espanjassa 20 vuotta, on hyvin todennäköistä, että hänellä saattaa olla hyvä espanjan kielen taito. Mutta voidakseen tehdä kunnollisen ennusteen RNN:n on muistettava tämä konteksti. Merkityksellinen tieto voi olla erillään siitä kohdasta, jossa sitä tarvitaan, valtavan määrän epäolennaisen tiedon vuoksi. Tässä rekursiivinen neuroverkko epäonnistuu!
Syy tähän on häviävän gradientin ongelma. Ymmärtääksesi tämän sinun on tiedettävä jonkin verran siitä, miten feed-forward-neuraaliverkko oppii. Tiedämme, että perinteisessä feed-forward-neuraaliverkossa tiettyyn kerrokseen sovellettava painojen päivitys on oppimisnopeuden, edellisen kerroksen virhetermin ja kyseisen kerroksen syötteen monikerta. Tietyn kerroksen virhetermi on siis jossain määrin kaikkien edellisten kerrosten virheiden tulo. Kun käsitellään aktivointifunktioita, kuten sigmoidifunktiota, sen derivaattojen pienet arvot (jotka esiintyvät virhefunktiossa) kerrotaan moninkertaisesti siirryttäessä kohti alkavia kerroksia. Tämän seurauksena gradientti lähes häviää, kun siirrytään kohti aloituskerroksia, ja näiden kerrosten kouluttaminen vaikeutuu.
Vastaava tapaus on havaittavissa rekursiivisissa neuroverkoissa. RNN muistaa asioita vain pieniä aikajaksoja, eli jos tarvitsemme tietoa pienen ajan kuluttua, se voi olla toistettavissa, mutta kun sanoja syötetään paljon, tämä tieto katoaa jonnekin. Tämä ongelma voidaan ratkaista soveltamalla hieman viritettyä versiota RNN:stä – Long Short-Term Memory Networks.
Parannus RNN:ään verrattuna: LSTM (Long Short-Term Memory) Networks
Kun järjestämme kalenterimme päiväksi, priorisoimme tapaamisemme, eikö niin? Siinä tapauksessa, että meidän täytyy tehdä tilaa jollekin tärkeälle, tiedämme, mikä tapaaminen voitaisiin perua mahdollisen tapaamisen vuoksi.
Kävi ilmi, että RNN ei tee näin. Lisätäkseen uutta tietoa se muuttaa olemassa olevan tiedon kokonaan soveltamalla funktiota. Tästä johtuen koko informaatio muuttuu kokonaisuudessaan, eli ei oteta huomioon ”tärkeää” informaatiota ja ”ei niin tärkeää” informaatiota.
LSTM:t taas tekevät pieniä muutoksia informaatioon kertomalla ja lisäämällä. LSTM:ssä tieto kulkee solutiloiksi kutsutun mekanismin kautta. Näin LSTM:t voivat valikoivasti muistaa tai unohtaa asioita. Tieto tietyssä solutilassa on kolmessa eri riippuvuussuhteessa.
Visualisoidaan tätä esimerkin avulla. Otetaan esimerkki tietyn osakkeen kurssin ennustamisesta. Tämän päivän osakekurssi riippuu:
- Trendistä, jota osake on noudattanut edellisinä päivinä, ehkä laskevasta tai nousevasta trendistä.
- Osakkeen hinnasta edellisenä päivänä, koska monet kauppiaat vertailevat osakkeen edellisen päivän hintaa ennen sen ostamista.
- Tekijöistä, jotka voivat vaikuttaa osakkeen tämän päivän hintaan. Tämä voi olla yrityksen uusi politiikka, jota kritisoidaan laajasti, tai yrityksen voiton lasku, tai ehkä odottamaton muutos yrityksen ylimmässä johdossa.
Nämä riippuvuudet voidaan yleistää mihin tahansa ongelmaan seuraavasti:
- Edellinen solutila (eli tieto, joka oli muistissa edellisen aika-askeleen jälkeen)
- Edellinen piilotila (ts. tämä on sama kuin edellisen solun ulostulo)
- Sisäänsyöttö nykyisellä aika-askeleella (eli uusi informaatio, joka syötetään sillä hetkellä)
Toinen tärkeä piirre LSTM:ssä on sen analogia liukuhihnojen kanssa!
Aivan oikein!
Teollisuus käyttää niitä tuotteiden siirtämiseen eri prosesseissa. LSTM:t käyttävät tätä mekanismia tiedon liikutteluun.
Voi olla, että informaatiota lisätään, muutetaan tai poistetaan, kun se kulkee eri kerrosten läpi, aivan kuten tuotetta voidaan muovata, maalata tai pakata sen ollessa liukuhihnalla.
Oheinen kaavio selittää LSTM:ien ja liukuhihnojen läheisen suhteen.
Lähde
Vaikka tämä kaavio ei ole lähelläkään LSTM:n varsinaista arkkitehtuuria, se ratkaisee tarkoituksemme toistaiseksi.
Juuri tämän LSTM:ien ominaisuuden vuoksi, jossa ne eivät käsittele koko informaatiota vaan pikemminkin muokkaavat sitä hiukan, LSTM:t kykenevät unohtamaan ja muistamaan asioita valikoiden. Miten ne tekevät näin, sen opimme seuraavassa luvussa?
LSTM:ien arkkitehtuuri
LSTM:ien toimintaa voidaan havainnollistaa ymmärtämällä uutiskanavan uutisryhmän toimintaa, joka raportoi murhajutusta. Nyt uutisjuttu rakentuu tosiasioista, todisteista ja monien ihmisten lausunnoista. Aina kun uusi tapahtuma tapahtuu, otetaan jompikumpi näistä kolmesta vaiheesta.
Esitettäköön, että oletimme, että murha tehtiin ”myrkyttämällä” uhri, mutta juuri tulleen ruumiinavausraportin mukaan kuolinsyy oli ”isku päähän”. Mitä teette uutisryhmän jäsenenä? Unohdat välittömästi aiemman kuolinsyyn ja kaikki tarinat, jotka oli kudottu tämän tosiasian ympärille.
Mitä, jos kuvaan tulee täysin uusi epäilty. Henkilö, jolla oli kaunaa uhrin kanssa ja joka voisi olla murhaaja? Syötät tämän tiedon uutissyötteeseesi, eikö niin?
Nyt kaikkia näitä rikkinäisiä tiedonpalasia ei voi tarjoilla valtamediassa. Joten tietyn ajan kuluttua sinun on tiivistettävä nämä tiedot ja tulostettava olennaiset asiat yleisöllesi. Ehkä muodossa ”XYZ osoittautuu pääepäillyksi.”.
Mennään nyt LSTM-verkon arkkitehtuurin yksityiskohtiin:
Lähde
Nyt tämä ei ole lähelläkään sitä yksinkertaistettua versiota, jonka näimme aiemmin, mutta käyn sen läpi. Tyypillinen LSTM-verkko koostuu erilaisista muistilohkoista, joita kutsutaan soluiksi
(kuvassa näkyvät suorakulmiot). Seuraavaan soluun siirretään kaksi tilaa; solun tila ja piilotettu tila. Muistilohkot ovat vastuussa asioiden muistamisesta, ja tähän muistiin kohdistuvat manipulaatiot tehdään kolmen päämekanismin, porttien, avulla. Jokaista niistä käsitellään seuraavassa.
4.1 Unohda-portti
Olemme esimerkkinä tekstin ennustamisongelmasta. Oletetaan, että LSTM:lle syötetään, seuraava lause:
Heti kun ensimmäinen piste ”henkilö” sanan jälkeen tulee vastaan, forget-portti huomaa, että seuraavassa lauseessa saattaa tapahtua kontekstin muutos. Tämän seurauksena lauseen subjekti unohtuu ja subjektin paikka vapautuu. Ja kun alamme puhua ”Danista”, tämä subjektin paikka osoitetaan ”Danille”. Tämän subjektin unohtamisprosessin saa aikaan unohdusportti.
Unohdusportti on vastuussa tiedon poistamisesta solutilasta. Tieto, jota LSTM ei enää tarvitse asioiden ymmärtämiseen tai joka on vähemmän tärkeää, poistetaan suodattimen kertoimen avulla. Tätä tarvitaan LSTM-verkon suorituskyvyn optimoimiseksi.
Tämä portti ottaa sisään kaksi syötettä; h_t-1 ja x_t.
h_t-1 on edellisen solun piilotettu tila tai edellisen solun ulostulo ja x_t on kyseisen aika-askeleen tulo. Annetut syötteet kerrotaan painomatriiseilla ja niihin lisätään bias. Tämän jälkeen tähän arvoon sovelletaan sigmoidifunktiota. Sigmoidifunktio tuottaa vektorin, jonka arvot vaihtelevat 0:sta 1:een ja joka vastaa kutakin solun tilan numeroa. Sigmoidifunktion tehtävänä on periaatteessa päättää, mitkä arvot säilytetään ja mitkä hylätään. Jos solutilan tietylle arvolle annetaan ”0”, se tarkoittaa, että unohdusportti haluaa solutilan unohtavan kyseisen tiedon kokonaan. Vastaavasti ’1’ tarkoittaa, että forget-portti haluaa muistaa koko tiedon. Tämä sigmoidifunktion tuottama vektori kerrotaan solun tilaan.
4.2 Syöttöportti
Okei, otetaan toinen esimerkki, jossa LSTM analysoi lauseen:
Nyt tässä on tärkeää tietoa, että ”Bob” osaa uida ja että hän on palvellut laivastossa neljä vuotta. Tämä voidaan lisätä solutilaan, mutta se, että hän kertoi kaiken tämän puhelimessa, on vähemmän tärkeä seikka ja voidaan jättää huomiotta. Tämä uuden tiedon lisääminen voidaan tehdä input-portin kautta.
Tässä on sen rakenne:
Syöttöportti vastaa tiedon lisäämisestä solun tilaan. Tämä informaation lisääminen on periaatteessa kolmivaiheinen prosessi, kuten yllä olevasta kaaviosta nähdään.
- Säätää, mitä arvoja solun tilaan on lisättävä, ottamalla mukaan sigmoidifunktion. Tämä on periaatteessa hyvin samanlainen kuin forget gate ja toimii suodattimena kaikelle informaatiolle h_t-1:stä ja x_t:stä.
- Luodaan vektori, joka sisältää kaikki mahdolliset arvot, jotka voidaan lisätä (h_t-1:stä ja x_t:stä havaittuna) solun tilaan. Tämä tehdään käyttämällä tanh-funktiota, joka antaa arvoja -1:n ja +1:n välillä.
- Luotuun vektoriin (tanh-funktio) kerrotaan säätösuodattimen (sigmoidiportti) arvo ja lisätään sitten tämä hyödyllinen tieto solutilaan yhteenlaskuoperaatiolla.
Kun tämä kolmivaiheinen prosessi on tehty, varmistetaan, että solutilaan lisätään vain se tieto, joka on tärkeää ja joka ei ole turhaa.
4.3 Lähtöportti
Ei kaikki solutilaa pitkin kulkeva informaatio sovi ulostuloon tietyllä hetkellä. Havainnollistetaan tätä esimerkillä:
Tässä lauseessa tyhjälle tilalle voisi olla useita vaihtoehtoja. Tiedämme kuitenkin, että nykyinen tulo ’rohkea’, on adjektiivi, jota käytetään kuvaamaan substantiivia. Näin ollen, mikä tahansa sana seuraa, sillä on vahva taipumus olla substantiivi. Ja näin ollen Bob voisi olla sopiva ulostulo.
Tämä tehtävä, jossa solun senhetkisestä tilasta valitaan hyödyllistä tietoa ja näytetään se ulostulona, tehdään ulostuloportin kautta. Tässä on sen rakenne:
Lähtöportin toiminta voidaan taas jakaa kolmeen vaiheeseen:
- Vektorin luominen sen jälkeen, kun solun tilaan on sovellettu tanh-funktiota, jolloin arvot skaalautuvat välille -1 ja +1.
- Suodattimen tekeminen käyttäen h_t-1:n ja x_t:n arvoja siten, että sillä voidaan säätää edellä luodusta vektorista ulostuloon tarvittavia arvoja. Tämä suodatin käyttää jälleen sigmoidifunktiota.
- Kerroin tämän säätösuodattimen arvon vaiheessa 1 luotuun vektoriin ja lähetän sen ulostulona ja myös seuraavan solun piilotetuksi tilaksi.
Yllä olevan esimerkin suodatin huolehtii siitä, että se pienentää kaikkia muita arvoja paitsi ’Bob’. Siten suodatin on rakennettava syötteen ja piilotetun tilan arvoista ja sitä on sovellettava solun tilavektoriin.
Tekstin tuottaminen LSTM:ien avulla
Olemme saaneet tarpeeksemme LSTM:ien teoreettisista käsitteistä ja toiminnasta. Nyt yrittäisimme rakentaa mallin, joka pystyy ennustamaan jonkin n merkkimäärän Macbethin alkuperäistekstin jälkeen. Suurin osa klassisista teksteistä ei ole enää tekijänoikeussuojattu, ja ne löytyvät täältä. Päivitetty versio .txt-tiedostosta löytyy täältä.
Käytämme Keras-kirjastoa, joka on korkean tason API neuroverkoille ja toimii TensorFlow’n tai Theanon päällä. Varmista siis ennen tähän koodiin sukeltamista, että sinulla on Keras asennettuna ja toiminnassa.
Okei, generoidaan siis tekstiä!
-
Riippuvuuksien tuonti
# Importing dependencies numpy and kerasimport numpyfrom keras.models import Sequentialfrom keras.layers import Densefrom keras.layers import Dropoutfrom keras.layers import LSTMfrom keras.utils import np_utils
Ituomme kaikki tarvittavat riippuvuudet ja tämä on melko itsestään selvää.
-
Tekstitiedoston lataaminen ja merkkien ja kokonaislukujen vastaavuuksien luominen
# load textfilename = "/macbeth.txt"text = (open(filename).read()).lower()# mapping characters with integersunique_chars = sorted(list(set(text)))char_to_int = {}int_to_char = {}for i, c in enumerate (unique_chars): char_to_int.update({c: i}) int_to_char.update({i: c})
Tekstitiedosto avataan, ja kaikki merkit muutetaan pieniksi kirjaimiksi. Seuraavien vaiheiden helpottamiseksi kartoitamme jokaisen merkin vastaavaan numeroon. Tämä tehdään, jotta LSTM:n laskentaosuus olisi helpompi.
-
Datan valmistelu
# preparing input and output datasetX = Y = for i in range(0, len(text) - 50, 1): sequence = text label =text X.append( for char in sequence]) Y.append(char_to_int)
Data valmistellaan sellaisessa muodossa, että jos haluaisimme LSTM:n ennustavan ’O:n’ sanassa ’HELLO’, syöttäisimme sen syötteenä ja odotettuna tuloksena. Vastaavasti tässä määritetään haluamamme sarjan pituus (esimerkissä 50) ja tallennetaan sitten 49 ensimmäisen merkin koodaukset X:ään ja odotettu tuloste eli 50. merkki Y:hen.
-
X:n muokkaaminen
# reshaping, normalizing and one hot encodingX_modified = numpy.reshape(X, (len(X), 50, 1))X_modified = X_modified / float(len(unique_chars))Y_modified = np_utils.to_categorical(Y)
LSTM-verkko odottaa syötteen olevan muotoa, jossa näytteet on datapisteiden määrä, joka meillä on, aika-askeleet on aikariippuvaisten askeleiden määrä, joita on yhdessä datapisteessä, piirteet viittaa muuttujien lukumäärään, joka meillä on vastaavalle todelliselle arvolle Y:ssä. Tämän jälkeen skaalaamme X_modifiedin arvot välillä 0-1 ja yksi kuuma koodaamme todelliset arvomme Y_modifiediin.
-
LSTM-mallin määrittely
# defining the LSTM modelmodel = Sequential()model.add(LSTM(300, input_shape=(X_modified.shape, X_modified.shape), return_sequences=True))model.add(Dropout(0.2))model.add(LSTM(300))model.add(Dropout(0.2))model.add(Dense(Y_modified.shape, activation='softmax'))model.compile(loss='categorical_crossentropy', optimizer='adam')
Käytetään sekventiaalista mallia, joka on lineaarinen kerrospino. Ensimmäinen kerros on LSTM-kerros, jossa on 300 muistiyksikköä ja se palauttaa sekvenssejä. Näin varmistetaan, että seuraava LSTM-kerros saa sekvenssejä eikä vain satunnaisesti hajallaan olevaa dataa. Jokaisen LSTM-kerroksen jälkeen käytetään pudotuskerrosta, jotta vältetään mallin liiallinen sovittaminen. Lopuksi viimeinen kerros on täysin kytketty kerros, jossa on ”softmax”-aktivointi ja jossa neuronien määrä on yhtä suuri kuin yksilöllisten merkkien määrä, koska meidän on tulostettava yksi kuuma koodattu tulos.
-
Mallin sovittaminen ja merkkien tuottaminen
# fitting the modelmodel.fit(X_modified, Y_modified, epochs=1, batch_size=30)# picking a random seedstart_index = numpy.random.randint(0, len(X)-1)new_string = X# generating charactersfor i in range(50): x = numpy.reshape(new_string, (1, len(new_string), 1)) x = x / float(len(unique_chars)) #predicting pred_index = numpy.argmax(model.predict(x, verbose=0)) char_out = int_to_char seq_in = for value in new_string] print(char_out) new_string.append(pred_index) new_string = new_string
Malli sovitetaan 100 epookin aikana, ja eräkoko on 30. Tämän jälkeen määritetään satunnainen siemen (toistettavuuden helpottamiseksi) ja aloitetaan merkkien generointi. Mallin ennuste antaa ennustetun merkin merkkikoodauksen, joka dekoodataan takaisin merkkiarvoksi ja liitetään malliin.
Tältä verkon ulostulo näyttäisi
Tulevaisuudessa riittävän monen harjoitteluepookin jälkeen se antaa ajan myötä yhä parempia tuloksia. Näin käyttäisit LSTM:ää sekvenssin ennustustehtävän ratkaisemiseen.
Loppuhuomautukset
LSTM:t ovat erittäin lupaava ratkaisu sekvenssiin ja aikasarjoihin liittyviin ongelmiin. Yksi haittapuoli, jonka koen niissä olevan, on kuitenkin niiden kouluttamisen vaikeus. Yksinkertaisenkin mallin kouluttamiseen kuluu paljon aikaa ja järjestelmäresursseja. Mutta se on vain laitteistoon liittyvä rajoitus! Toivottavasti onnistuin antamaan teille perustiedot näistä verkoista. Jos sinulla on blogiin liittyviä ongelmia tai kysymyksiä, voit kommentoida alla.