Essentials of Deep Learning : Introduktion til Long Short Term Memory

Introduktion

Sekvensforudsigelsesproblemer har eksisteret i lang tid. De betragtes som et af de sværeste problemer at løse i datalogiindustrien. De omfatter en bred vifte af problemer; fra forudsigelse af salg til at finde mønstre i aktiemarkedets data, fra forståelse af filmplots til genkendelse af din måde at tale på, fra sprogoversættelser til forudsigelse af dit næste ord på din iPhones tastatur.

Med de nylige gennembrud, der er sket inden for datalogi, er det konstateret, at for næsten alle disse sekvensforudsigelsesproblemer er Long short Term Memory-netværk, alias LSTM’er, blevet observeret som den mest effektive løsning.

LSTM’er har en fordel i forhold til konventionelle feed-forward neurale netværk og RNN på mange måder. Dette skyldes deres egenskab til selektivt at huske mønstre i lange tidsrum. Formålet med denne artikel er at forklare LSTM og sætte dig i stand til at bruge den i virkelige problemer. Lad os tage et kig!

Bemærk: For at gennemgå artiklen skal du have grundlæggende viden om neurale netværk og om, hvordan Keras (et deep learning-bibliotek) fungerer. Du kan henvise til de nævnte artikler for at forstå disse begreber:

  • Understanding Neural Network From Scratch
  • Fundamentals of Deep Learning – Introduction to Recurrent Neural Networks
  • Tutorial: Optimering af neurale netværk ved hjælp af Keras (med case study om billedgenkendelse)

Indholdsfortegnelse

  1. Flashback: Et kig på Recurrent Neural Networks (RNN)
  2. Begrænsninger af RNNs
  3. Forbedringer i forhold til RNN : Long Short Term Memory (LSTM)
  4. Arkitektur af LSTM
    1. Forget Gate
    2. Input Gate
    3. Output Gate
  5. Tekstgenerering ved hjælp af LSTM’er.

Flashback: Et kig på Recurrent Neural Networks (RNN)

Tag et eksempel på sekventielle data, som kan være aktiemarkedets data for en bestemt aktie. En simpel maskinlæringsmodel eller et kunstigt neuralt netværk kan lære at forudsige aktiekurserne på grundlag af en række træk: aktiens volumen, åbningsværdien osv. Selv om aktiekursen afhænger af disse funktioner, er den også i høj grad afhængig af aktiernes værdi i de foregående dage. For en erhvervsdrivende er disse værdier i de foregående dage (eller tendensen) faktisk en vigtig afgørende faktor for forudsigelser.

I de konventionelle feed-forward neurale netværk anses alle testcases for at være uafhængige. Det vil sige, at når modellen tilpasses for en bestemt dag, tages der ikke hensyn til aktiekurserne på de foregående dage.

Denne afhængighed af tid opnås via Recurrent Neural Networks. Et typisk RNN ser således ud:

Det kan virke skræmmende ved første øjekast, men når det først er foldet ud, ser det meget enklere ud:

Nu er det lettere for os at visualisere, hvordan disse netværk tager hensyn til udviklingen i aktiekurserne, før de forudsiger aktiekurserne for i dag. Her er hver forudsigelse på tidspunkt t (h_t) afhængig af alle tidligere forudsigelser og de oplysninger, der er lært af dem.

RNN’er kan løse vores formål med sekvenshåndtering i stor udstrækning, men ikke helt. Vi ønsker, at vores computere skal være gode nok til at skrive Shakespeares sonetter. Nu er RNN’er gode, når det drejer sig om korte sammenhænge, men for at kunne opbygge en historie og huske den, skal vores modeller kunne forstå og huske sammenhængen bag sekvenserne, ligesom en menneskelig hjerne. Dette er ikke muligt med en simpel RNN.

Hvorfor? Lad os se på det.

Begrænsninger ved RNN’er

Recurrent Neural Networks fungerer fint, når vi har med kortsigtede afhængigheder at gøre. Det vil sige, når de anvendes på problemer som:

RNN’er viser sig at være ganske effektive. Det skyldes, at dette problem ikke har noget at gøre med sammenhængen i udsagnet. RNN’erne behøver ikke at huske, hvad der blev sagt før dette, eller hvad der var meningen med det, de behøver blot at vide, at himlen i de fleste tilfælde er blå. Således ville forudsigelsen være:

Vanilla RNN’er formår imidlertid ikke at forstå konteksten bag et input. Noget, der blev sagt længe før, kan ikke genkaldes, når man laver forudsigelser i nutiden. Lad os forstå dette som et eksempel:

Her kan vi forstå, at da forfatteren har arbejdet i Spanien i 20 år, er det meget sandsynligt, at han kan besidde en god beherskelse af spansk. Men for at kunne foretage en korrekt forudsigelse skal RNN’en huske denne kontekst. De relevante oplysninger kan være adskilt fra det punkt, hvor de er nødvendige, af en stor mængde irrelevante data. Det er her, at et recurrent neuralt netværk fejler!

Grunden til dette er problemet med forsvindende gradient. For at forstå dette skal du have en vis viden om, hvordan et feed-forward neuralt netværk lærer. Vi ved, at for et konventionelt feed-forward neuralt netværk er den vægtopdatering, der anvendes på et bestemt lag, et multiplum af indlæringshastigheden, fejltermen fra det foregående lag og input til det pågældende lag. Fejlterminen for et bestemt lag er således et eller andet sted et produkt af alle tidligere lags fejl. Når der er tale om aktiveringsfunktioner som den sigmoide funktion, bliver de små værdier af dens derivater (som forekommer i fejlfunktionen) multipliceret flere gange, efterhånden som vi bevæger os mod startlagene. Som følge heraf forsvinder gradienten næsten, efterhånden som vi bevæger os mod startlagene, og det bliver vanskeligt at træne disse lag.

Et lignende tilfælde ses i recurrent neural networks. RNN husker ting i kun små tidsrum, dvs. hvis vi har brug for oplysningerne efter et lille stykke tid, kan de måske reproduceres, men når der tilføres en masse ord, går disse oplysninger tabt et eller andet sted. Dette problem kan løses ved at anvende en let justeret version af RNN’er – Long Short-Term Memory Networks.

Forbedring i forhold til RNN: LSTM-netværk (Long Short-Term Memory)

Når vi arrangerer vores kalender for dagen, prioriterer vi vores aftaler, ikke? Hvis vi i tilfælde af at vi har brug for at gøre plads til noget vigtigt, ved vi, hvilket møde der kan aflyses for at give plads til et muligt møde.

Det viser sig, at en RNN ikke gør det. For at tilføje en ny information transformerer den den eksisterende information fuldstændigt ved at anvende en funktion. På grund af dette ændres hele informationen i det hele taget, dvs. der tages ikke hensyn til “vigtige” oplysninger og “mindre vigtige” oplysninger.

LSTM’er foretager på den anden side små ændringer af oplysningerne ved hjælp af multiplikationer og tilføjelser. Med LSTM’er strømmer informationerne gennem en mekanisme, der er kendt som celletilstande. På denne måde kan LSTM’er selektivt huske eller glemme ting. Oplysningerne i en bestemt celletilstand har tre forskellige afhængigheder.

Vi vil visualisere dette med et eksempel. Lad os tage eksemplet med forudsigelse af aktiekurser for en bestemt aktie. Aktiekursen i dag vil afhænge af:

  1. Den trend, som aktien har fulgt i de foregående dage, måske en nedadgående trend eller en opadgående trend.
  2. Aktiens pris den foregående dag, fordi mange handlende sammenligner aktiens pris den foregående dag, før de køber den.
  3. De faktorer, der kan påvirke aktiens pris for i dag. Det kan være en ny virksomhedspolitik, som bliver kritiseret bredt, eller et fald i virksomhedens overskud, eller måske en uventet ændring i virksomhedens øverste ledelse.

Disse afhængigheder kan generaliseres til ethvert problem som:

  1. Den tidligere celletilstand (dvs. den information, der var til stede i hukommelsen efter det foregående tidstrin)
  2. Den tidligere skjulte tilstand (dvs. dette er det samme som output fra den foregående celle)
  3. Input på det aktuelle tidstrin (dvs. de nye oplysninger, der tilføres på det pågældende tidspunkt)

Et andet vigtigt træk ved LSTM er dens analogi med transportbånd!

Det er rigtigt!

Industrierne bruger dem til at flytte produkter rundt til forskellige processer. LSTM’er bruger denne mekanisme til at flytte oplysninger rundt.

Vi kan have en vis tilføjelse, ændring eller fjernelse af information, mens den flyder gennem de forskellige lag, ligesom et produkt kan blive støbt, malet eller pakket, mens det befinder sig på et transportbånd.

Det følgende diagram forklarer det tætte forhold mellem LSTM’er og transportbånd.

Kilde

Og selv om dette diagram ikke engang er tæt på den faktiske arkitektur af en LSTM, løser det vores formål for nu.

Det er netop på grund af denne egenskab ved LSTM’er, hvor de ikke manipulerer hele informationen, men snarere ændrer dem lidt, at de er i stand til at glemme og huske ting selektivt. Hvordan de gør det, er det, vi skal lære i det næste afsnit?

Arkitektur af LSTM’er

LSTM’ernes funktionsmåde kan visualiseres ved at forstå, hvordan en nyhedskanals hold, der dækker en mordhistorie, fungerer. Nu er en nyhedshistorie bygget op omkring fakta, beviser og udsagn fra mange personer. Hver gang en ny begivenhed indtræffer, tager man et af de tre trin.

Lad os sige, at vi antog, at mordet blev begået ved at ‘forgifte’ offeret, men i obduktionsrapporten, der lige er kommet ind, står der, at dødsårsagen var ‘et slag i hovedet’. Hvad gør du som en del af dette nyhedshold? Du glemmer straks den tidligere dødsårsag og alle historier, der blev vævet omkring denne kendsgerning.

Hvad, hvis en helt ny mistænkt kommer ind i billedet. En person, som havde nag til offeret og som kunne være morderen? Du indtaster disse oplysninger i dit nyhedsfeed, ikke?

Nu kan alle disse brudstykker af oplysninger ikke serveres på mainstream-medierne. Så efter et vist tidsinterval er du nødt til at opsummere disse oplysninger og udsende de relevante ting til dit publikum. Måske i form af “XYZ viser sig at være den hovedmistænkte.”.

Nu skal vi gå i detaljer med arkitekturen i LSTM-netværket:

Source

Nu er dette slet ikke i nærheden af den forenklede version, som vi så før, men lad mig gå den igennem. Et typisk LSTM-netværk består af forskellige hukommelsesblokke kaldet celler
(de rektangler, som vi ser på billedet). Der er to tilstande, der overføres til den næste celle; celletilstanden og den skjulte tilstand. Hukommelsesblokkene er ansvarlige for at huske ting, og manipulationer til denne hukommelse sker gennem tre hovedmekanismer, kaldet gates. Hver af dem bliver gennemgået nedenfor.

4.1 Forget Gate

Med udgangspunkt i eksemplet med et tekstforudsigelsesproblem. Lad os antage, at en LSTM får indlæst følgende sætning:

Så snart det første punktum efter “person” er mødt, indser forget gate’en, at der kan ske en ændring af konteksten i den næste sætning. Som følge heraf glemmes sætningens subjekt, og pladsen for subjektet bliver ledig. Og når vi begynder at tale om “Dan”, tildeles denne plads for subjektet til “Dan”. Denne proces med at glemme subjektet fremkaldes af forget gate.

En forget gate er ansvarlig for at fjerne information fra cellens tilstand. De oplysninger, der ikke længere er nødvendige for, at LSTM’en kan forstå tingene, eller de oplysninger, der er af mindre betydning, fjernes via multiplikation af et filter. Dette er nødvendigt for at optimere LSTM-netværkets ydeevne.

Denne gate modtager to input; h_t-1 og x_t.

h_t-1 er den skjulte tilstand fra den foregående celle eller output fra den foregående celle, og x_t er input på det pågældende tidstrin. De givne input multipliceres med vægtmatricerne, og der tilføjes en bias. Herefter anvendes sigmoidfunktionen på denne værdi. Sigmoidfunktionen udsender en vektor med værdier fra 0 til 1, der svarer til hvert tal i cellens tilstand. I bund og grund er sigmoidfunktionen ansvarlig for at afgøre, hvilke værdier der skal bevares og hvilke der skal kasseres. Hvis der outputes et “0” for en bestemt værdi i celletilstanden, betyder det, at forglemmegaten ønsker, at celletilstanden helt skal glemme den pågældende oplysning. Tilsvarende betyder en “1”, at forget gate ønsker at huske hele den pågældende oplysning. Dette vektoroutput fra sigmoidfunktionen multipliceres med celletilstanden.

4.2 Input Gate

Okay, lad os tage et andet eksempel, hvor LSTM’en analyserer en sætning:

Nu er de vigtige oplysninger her, at “Bob” kan svømme, og at han har tjent i flåden i fire år. Dette kan tilføjes til celletilstanden, men det faktum, at han har fortalt alt dette over telefonen, er en mindre vigtig oplysning og kan ignoreres. Denne proces med at tilføje nogle nye oplysninger kan ske via indgangsporten.

Her er dens opbygning:

Ingangsporten er ansvarlig for tilføjelsen af oplysninger til celletilstanden. Denne tilføjelse af information er grundlæggende en proces i tre trin, som det fremgår af diagrammet ovenfor.

  1. Regulering af, hvilke værdier der skal tilføjes til celletilstanden, ved at inddrage en sigmoidfunktion. Dette svarer grundlæggende meget til glemmegaten og fungerer som et filter for alle oplysninger fra h_t-1 og x_t.
  2. Skabelse af en vektor, der indeholder alle mulige værdier, der kan tilføjes (som opfattet fra h_t-1 og x_t) til celletilstanden. Dette gøres ved hjælp af tanh-funktionen, som udsender værdier fra -1 til +1.
  3. Multiplikation af værdien af det regulerende filter (sigmoidporten) med den oprettede vektor (tanh-funktionen) og derefter tilføjelse af disse nyttige oplysninger til celletilstanden via en additionsoperation.

Når denne proces i tre trin er gennemført, sikrer vi, at der kun tilføjes de oplysninger til celletilstanden, som er vigtige og ikke er overflødige.

4.3 Output Gate

Når ikke al information, der løber langs cellestaten, er egnet til at blive output på et bestemt tidspunkt. Vi vil visualisere dette med et eksempel:

I denne sætning kunne der være en række muligheder for den tomme plads. Men vi ved, at det aktuelle input af “modig”, er et adjektiv, der bruges til at beskrive et navneord. Derfor har det ord, der følger efter, en stærk tendens til at være et substantiv. Og derfor kunne Bob være et passende output.

Denne opgave med at udvælge nyttige oplysninger fra den aktuelle cellestatus og vise dem som et output udføres via output-gaten. Her er dens opbygning:

Funktionen af en udgangsport kan igen opdeles i tre trin:

  1. Oprettelse af en vektor efter anvendelse af tanh-funktionen på cellestaten, hvorved værdierne skaleres til intervallet -1 til +1.
  2. Fremstilling af et filter ved hjælp af værdierne for h_t-1 og x_t, således at det kan regulere de værdier, der skal udgå fra den ovenfor oprettede vektor. Dette filter anvender igen en sigmoidfunktion.
  3. Multiplikere værdien af dette reguleringsfilter med den vektor, der blev oprettet i trin 1, og sende den ud som output og også til den skjulte tilstand i den næste celle.

Filtret i ovenstående eksempel vil sørge for, at det formindsker alle andre værdier undtagen ‘Bob’. Filteret skal således opbygges på input- og hidden state-værdierne og anvendes på celletilstandsvektoren.

Tekstgenerering ved hjælp af LSTM’er

Vi har fået nok af de teoretiske begreber og LSTM’ernes funktion. Nu ville vi forsøge at opbygge en model, der kan forudsige nogle n antal tegn efter den oprindelige tekst af Macbeth. De fleste af de klassiske tekster er ikke længere beskyttet af copyright og kan findes her. En opdateret version af .txt-filen kan findes her.

Vi vil bruge biblioteket Keras, som er et API på højt niveau for neurale netværk og fungerer oven på TensorFlow eller Theano. Så sørg for, at du har Keras installeret og funktionelt, inden du dykker ned i denne kode.

Okay, så lad os generere noget tekst!

  • Import af afhængigheder

# 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

Vi importerer alle de nødvendige afhængigheder, og dette er stort set selvforklarende.

  • Indlæsning af tekstfil og oprettelse af tegn til heltalstilknytninger

# 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})

Tekstfilen er åben, og alle tegn er konverteret til små bogstaver. For at lette de følgende trin skal vi mappe hvert enkelt tegn til et tilsvarende tal. Dette gøres for at gøre LSTM’ens beregningsdel lettere.

  • Forberedelse af datasæt

# 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 forberedes i et sådant format, at hvis vi ønsker, at LSTM’en skal forudsige “O” i “HELLO”, ville vi indlæse det som input og som det forventede output. På samme måde fastsætter vi her længden af den ønskede sekvens (sat til 50 i eksemplet) og gemmer derefter kodningerne af de første 49 tegn i X og det forventede output, dvs. det 50. tegn i Y.

  • Formning af X

# 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)

Et LSTM-netværk forventer, at input er i form af, hvor samples er antallet af datapunkter, vi har, tidstrin er antallet af tidsafhængige trin, der er der i et enkelt datapunkt, features henviser til antallet af variabler, vi har for den tilsvarende sande værdi i Y. Vi skalerer derefter værdierne i X_modificeret mellem 0 til 1 og en hot koder vores sande værdier i Y_modificeret.

  • Definering af LSTM-modellen

# 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')

Der anvendes en sekventiel model, som er en lineær stak af lag. Det første lag er et LSTM-lag med 300 hukommelsesenheder, og det returnerer sekvenser. Dette gøres for at sikre, at det næste LSTM-lag modtager sekvenser og ikke blot tilfældigt spredte data. Der anvendes et dropout-lag efter hvert LSTM-lag for at undgå overtilpasning af modellen. Endelig har vi det sidste lag som et fuldt forbundet lag med en “softmax”-aktivering og neuroner svarende til antallet af unikke tegn, fordi vi har brug for at outputte ét varmt kodet resultat.

  • Apasning af modellen og generering af tegn

# 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

Modellen tilpasses over 100 epochs med en batchstørrelse på 30. Vi fastsætter derefter et tilfældigt frø (for at gøre det let reproducerbart) og begynder at generere tegn. Forudsigelsen fra modellen giver tegnkodningen af det forudsagte tegn, det afkodes derefter tilbage til tegnværdien og føjes til mønsteret.

Sådan ville netværkets output se ud

Eventuelt, efter nok træningsepoke, vil det give bedre og bedre resultater med tiden. Sådan ville man bruge LSTM til at løse en sekvensforudsigelsesopgave.

Slutnoter

LSTM’er er en meget lovende løsning på sekvens- og tidsserierelaterede problemer. Den ene ulempe, som jeg finder ved dem, er imidlertid, at det er vanskeligt at træne dem. Der går meget tid og mange systemressourcer til at træne selv en simpel model. Men det er bare en hardwarebegrænsning! Jeg håber, at det er lykkedes mig at give dig en grundlæggende forståelse af disse netværk. For eventuelle problemer eller spørgsmål i forbindelse med bloggen, er du velkommen til at kommentere nedenfor.

Lær, engager dig , hack og bliv ansat!

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.