Essentials of Deep Learning : Introduktion till Long Short Term Memory

Introduktion

Problem med sekvensförutsägelser har funnits länge. De anses vara ett av de svåraste problemen att lösa inom datavetenskap. De omfattar ett brett spektrum av problem; från att förutsäga försäljning till att hitta mönster i aktiemarknadsdata, från att förstå filmhandlingar till att känna igen ditt sätt att tala, från språköversättningar till att förutsäga ditt nästa ord på din iPhones tangentbord.

Med de senaste genombrotten inom datavetenskap har det visat sig att för nästan alla dessa problem med sekvensförutsägelser har Long short term memory-nätverk, även kallade LSTMs, observerats som den mest effektiva lösningen.

LSTMs har ett övertag över konventionella feed-forward neurala nätverk och RNN på många sätt. Detta beror på deras egenskap att selektivt komma ihåg mönster under lång tid. Syftet med den här artikeln är att förklara LSTM och göra det möjligt för dig att använda den i verkliga problem. Låt oss ta en titt!

Anmärkning: För att kunna gå igenom artikeln måste du ha grundläggande kunskaper om neurala nätverk och hur Keras (ett bibliotek för djupinlärning) fungerar. Du kan hänvisa till de nämnda artiklarna för att förstå dessa begrepp:

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

Innehållsförteckning

  1. Flashback: En titt på Recurrent Neural Networks (RNN)
  2. Begränsningar av RNNs
  3. Förbättringar jämfört med RNN: Long Short Term Memory (LSTM)
  4. Arkitektur för LSTM
    1. Forget Gate
    2. Input Gate
    3. Output Gate
  5. Textgenerering med hjälp av LSTMs.

Flashback: En titt på Recurrent Neural Networks (RNN)

Tag ett exempel på sekventiella data, vilket kan vara aktiemarknadens data för en viss aktie. En enkel maskininlärningsmodell eller ett artificiellt neuralt nätverk kan lära sig att förutsäga aktiekurserna utifrån ett antal egenskaper: aktiens volym, öppningsvärdet osv. Även om aktiekursen beror på dessa egenskaper är den också till stor del beroende av aktievärdena under de föregående dagarna. För en näringsidkare är dessa värden under de föregående dagarna (eller trenden) faktiskt en viktig avgörande faktor för förutsägelser.

I de konventionella feed-forward neurala nätverken anses alla testfall vara oberoende. Det vill säga när modellen anpassas för en viss dag tas ingen hänsyn till aktiekurserna under de föregående dagarna.

Detta tidsberoende uppnås via Recurrent Neural Networks. Ett typiskt RNN ser ut så här:

Detta kan vara skrämmande vid första anblicken, men när det väl vecklas ut ser det mycket enklare ut:

Nu är det lättare för oss att visualisera hur dessa nätverk tar hänsyn till aktiekursernas utveckling, innan de förutspår aktiekurserna för idag. Här är varje förutsägelse vid tidpunkt t (h_t) beroende av alla tidigare förutsägelser och den information man lärt sig av dem.

RNNs kan lösa vårt syfte med sekvenshantering i stor utsträckning men inte helt och hållet. Vi vill att våra datorer ska vara tillräckligt bra för att skriva Shakespeares sonetter. Nu är RNNs bra när det gäller korta sammanhang, men för att kunna bygga upp en berättelse och komma ihåg den behöver vi att våra modeller kan förstå och komma ihåg sammanhanget bakom sekvenserna, precis som en mänsklig hjärna. Detta är inte möjligt med en enkel RNN.

Varför? Låt oss ta en titt.

Begränsningar för RNN

Recurrent Neural Networks fungerar alldeles utmärkt när vi har att göra med kortsiktiga beroenden. Det är när de tillämpas på problem som:

RNNs visar sig vara ganska effektiva. Detta beror på att detta problem inte har något att göra med sammanhanget i uttalandet. RNN behöver inte komma ihåg vad som sades innan detta, eller vad som var dess innebörd, allt de behöver veta är att himlen i de flesta fall är blå. Således skulle förutsägelsen vara:

Vanilla RNNs misslyckas dock med att förstå sammanhanget bakom en inmatning. Något som har sagts för länge sedan kan inte återkallas när man gör förutsägelser i nuet. Låt oss förstå detta som ett exempel:

Här kan vi förstå att eftersom författaren har arbetat i Spanien i 20 år är det mycket troligt att han kan ha goda kunskaper i spanska. Men för att göra en korrekt förutsägelse måste RNN komma ihåg detta sammanhang. Den relevanta informationen kan vara avskild från den punkt där den behövs genom en stor mängd irrelevanta uppgifter. Det är här som ett återkommande neuralt nätverk misslyckas!

Anledningen till detta är problemet med försvinnande gradient. För att förstå detta måste du ha viss kunskap om hur ett feed-forward neuralt nätverk lär sig. Vi vet att för ett konventionellt feed-forward neuralt nätverk är den viktuppdatering som tillämpas på ett visst lager en multipel av inlärningshastigheten, feltermen från det föregående lagret och inmatningen till det lagret. Feltermen för ett visst lager är alltså någonstans en produkt av alla tidigare lagers fel. När det gäller aktiveringsfunktioner som sigmoidfunktionen multipliceras de små värdena för dess derivat (som förekommer i felfunktionen) flera gånger när vi rör oss mot startskikten. Som ett resultat av detta försvinner gradienten nästan när vi rör oss mot startlagren, och det blir svårt att träna dessa lager.

Ett liknande fall observeras i återkommande neurala nätverk. RNN minns saker bara under små tidsperioder, dvs. om vi behöver informationen efter en kort tid kan den vara reproducerbar, men när många ord matas in går informationen förlorad någonstans. Detta problem kan lösas genom att tillämpa en något modifierad version av RNN – Long Short-Term Memory Networks.

Förbättringar jämfört med RNN: LSTM-nätverk (Long Short-Term Memory)

När vi ordnar vår kalender för dagen prioriterar vi våra möten, eller hur? Om vi i händelse av att vi behöver göra plats för något viktigt vet vi vilket möte som kan ställas in för att ge plats åt ett eventuellt möte.

Det visar sig att en RNN inte gör det. För att lägga till ny information omvandlar den den befintliga informationen helt och hållet genom att tillämpa en funktion. På grund av detta ändras hela informationen i sin helhet, dvs. det tas ingen hänsyn till ”viktig” information och ”mindre viktig” information.

LSTM:er å andra sidan gör små ändringar i informationen genom multiplikationer och tillägg. Med LSTM:er flödar informationen genom en mekanism som kallas celltillstånd. På så sätt kan LSTMs selektivt komma ihåg eller glömma saker. Informationen i ett visst celltillstånd har tre olika beroenden.

Vi visualiserar detta med ett exempel. Låt oss ta exemplet med att förutsäga aktiekurser för en viss aktie. Dagens aktiekurs kommer att bero på:

  1. Trenden som aktien har följt under de föregående dagarna, kanske en nedåtgående trend eller en uppåtgående trend.
  2. Aktiens pris föregående dag, eftersom många handlare jämför aktiens pris föregående dag innan de köper den.
  3. Faktorer som kan påverka aktiens pris för idag. Det kan vara en ny företagspolicy som kritiseras allmänt, eller en nedgång i företagets vinst, eller kanske en oväntad förändring i företagets högsta ledning.

Dessa beroenden kan generaliseras till vilket problem som helst som:

  1. Det tidigare celltillståndet (dvs. den information som fanns i minnet efter föregående tidssteg)
  2. Det tidigare dolda tillståndet (dvs. detta är samma som den föregående cellens utgång)
  3. Inmatningen vid det aktuella tidssteget (dvs. den nya information som matas in vid det tillfället)

En annan viktig egenskap hos LSTM är dess analogi med löpande band!

Det stämmer!

Industrin använder dem för att flytta produkter för olika processer. LSTM använder denna mekanism för att flytta information.

Vi kan ha vissa tillägg, ändringar eller borttagningar av information när den flyter genom de olika lagren, precis som en produkt kan gjutas, målas eller packas medan den befinner sig på ett transportband.

Följande diagram förklarar det nära sambandet mellan LSTMs och transportband.

Källa

Även om detta diagram inte ens är i närheten av den faktiska arkitekturen hos en LSTM, så löser det vårt syfte för tillfället.

Bara på grund av denna egenskap hos LSTMs, där de inte manipulerar hela informationen utan snarare ändrar den något, kan de glömma och komma ihåg saker selektivt. Hur de gör det ska vi lära oss i nästa avsnitt?

Arkitektur för LSTMs

LSTMs funktionssätt kan åskådliggöras genom att förstå hur ett nyhetsteam på en nyhetskanal som bevakar ett mordfall fungerar. En nyhetsberättelse bygger på fakta, bevis och många personers uttalanden. När en ny händelse inträffar tar man något av de tre stegen.

Säg att vi antog att mordet skedde genom att ”förgifta” offret, men i obduktionsrapporten som just kom in stod det att dödsorsaken var ”ett slag mot huvudet”. Vad gör du som en del av detta nyhetsteam? Du glömmer omedelbart den tidigare dödsorsaken och alla historier som vävdes runt detta faktum.

Vad händer om en helt ny misstänkt kommer in i bilden. En person som hade agg till offret och som skulle kunna vara mördaren? Du lägger in denna information i ditt nyhetsflöde, eller hur?

Nu kan alla dessa trasiga delar av informationen inte serveras på mainstream media. Så efter ett visst tidsintervall måste du sammanfatta denna information och ge ut de relevanta sakerna till din publik. Kanske i form av ”XYZ visar sig vara huvudmisstänkt.”.

Nu ska vi gå in på detaljerna i arkitekturen för LSTM-nätverket:

Source

Nu är detta inte alls i närheten av den förenklade versionen som vi såg tidigare, men låt mig gå igenom den med dig. Ett typiskt LSTM-nätverk består av olika minnesblock som kallas celler
(rektanglarna som vi ser i bilden). Det finns två tillstånd som överförs till nästa cell; celltillståndet och det dolda tillståndet. Minnesblocken ansvarar för att komma ihåg saker och ting och manipulationer av detta minne sker genom tre huvudmekanismer, som kallas grindar. Var och en av dem diskuteras nedan.

4.1 Forget Gate

Med ett exempel på ett problem med textförutsägelse. Låt oss anta att en LSTM matas in, följande mening:

Sedan den första punkt efter ”person” påträffas, inser forget gate att det kan ske en förändring av sammanhanget i nästa mening. Som ett resultat av detta glöms subjektet i meningen bort och platsen för subjektet blir ledig. Och när vi börjar tala om ”Dan” tilldelas denna plats för subjektet ”Dan”. Denna process med att glömma subjektet åstadkoms av forget gate.

En forget gate ansvarar för att ta bort information från celltillståndet. Den information som inte längre behövs för att LSTM ska förstå saker och ting eller den information som är av mindre betydelse avlägsnas via multiplikation av ett filter. Detta krävs för att optimera LSTM-nätverkets prestanda.

Denna grind tar emot två ingångar; h_t-1 och x_t.

h_t-1 är det dolda tillståndet från den föregående cellen eller utgången från den föregående cellen och x_t är inmatningen vid det aktuella tidssteget. De givna inmatningarna multipliceras med viktmatriserna och en bias läggs till. Därefter tillämpas sigmoidfunktionen på detta värde. Sigmoidfunktionen ger en vektor med värden mellan 0 och 1 som motsvarar varje nummer i celltillståndet. I princip ansvarar sigmoidfunktionen för att avgöra vilka värden som ska behållas och vilka som ska förkastas. Om en ”0” ges ut för ett visst värde i celltillståndet betyder det att forget gate vill att celltillståndet ska glömma den informationen helt och hållet. På samma sätt betyder en ”1” att glömma-porten vill komma ihåg hela den informationen. Denna vektorutgång från sigmoidfunktionen multipliceras med celltillståndet.

4.2 Input Gate

Okej, låt oss ta ett annat exempel där LSTM analyserar en mening:

Nu är den viktiga informationen här att ”Bob” kan simma och att han har tjänstgjort i flottan i fyra år. Detta kan läggas till i celltillståndet, men det faktum att han berättade allt detta över telefon är en mindre viktig fakta och kan ignoreras. Denna process att lägga till ny information kan göras via ingångsporten.

Här är dess struktur:

Ingångsporten ansvarar för att information läggs till celltillståndet. Detta tillägg av information är i princip en trestegsprocess som framgår av diagrammet ovan.

  1. Reglera vilka värden som måste läggas till celltillståndet genom att involvera en sigmoidfunktion. Detta är i princip mycket likt forget gate och fungerar som ett filter för all information från h_t-1 och x_t.
  2. Skapa en vektor som innehåller alla möjliga värden som kan läggas till (enligt vad som uppfattas från h_t-1 och x_t) till celltillståndet. Detta görs med hjälp av tanh-funktionen, som ger värden från -1 till +1.
  3. Multiplicera värdet av regleringsfiltret (sigmoidporten) till den skapade vektorn (tanh-funktionen) och sedan lägga till denna användbara information till celltillståndet via additionsoperationen.

När denna trestegsprocess är genomförd ser vi till att endast den information läggs till celltillståndet som är viktig och som inte är överflödig.

4.3 Utgångsporten

Inte all information som löper längs celltillståndet är lämplig för att ges ut vid en viss tidpunkt. Vi visualiserar detta med ett exempel:

I denna mening kan det finnas ett antal alternativ för det tomma utrymmet. Men vi vet att den aktuella inmatningen av ”modig”, är ett adjektiv som används för att beskriva ett substantiv. Det ord som följer efter har alltså en stark tendens att vara ett substantiv. Och därmed skulle Bob kunna vara en lämplig utgång.

Detta arbete med att välja ut användbar information från det aktuella celltillståndet och visa upp den som en utgång sker via utgångsporten. Här är dess struktur:

Funktionen för en utgångsgrind kan återigen delas upp i tre steg:

  1. Skapa en vektor efter att ha tillämpat tanh-funktionen på celltillståndet och därigenom skalat värdena till intervallet -1 till +1.
  2. Göra ett filter med hjälp av värdena för h_t-1 och x_t, så att det kan reglera de värden som måste ges ut från den vektor som skapats ovan. Detta filter använder återigen en sigmoidfunktion.
  3. Multiplicera värdet av detta reglerande filter till vektorn som skapades i steg 1, och skicka ut det som utgång och även till det dolda tillståndet i nästa cell.

Filtret i exemplet ovan kommer att se till att det förminskar alla andra värden utom ”Bob”. Filtret måste alltså byggas på inmatningsvärdena och de dolda tillståndsvärdena och tillämpas på celltillståndsvektorn.

Textgenerering med hjälp av LSTMs

Vi har fått nog av teoretiska begrepp och hur LSTMs fungerar. Nu skulle vi försöka bygga en modell som kan förutsäga något n antal tecken efter originaltexten av Macbeth. De flesta av de klassiska texterna är inte längre upphovsrättsligt skyddade och kan hittas här. En uppdaterad version av .txt-filen finns här.

Vi kommer att använda biblioteket Keras, som är ett API på hög nivå för neurala nätverk och fungerar ovanpå TensorFlow eller Theano. Så se till att du har Keras installerat och fungerande innan du dyker in i den här koden.

Okej, så låt oss generera lite text!

  • Import av beroenden

# 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 importerar alla nödvändiga beroenden och detta är ganska självförklarande.

  • Laddning av textfil och skapande av mappningar mellan tecken och heltal

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

Textfilen är öppen och alla tecken konverteras till små bokstäver. För att underlätta de följande stegen mappar vi varje tecken till ett respektive tal. Detta görs för att underlätta LSTM:s beräkningsdel.

  • Förberedelse av dataset

# 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 förbereds i ett sådant format att om vi vill att LSTM ska förutsäga ”O” i ”HELLO” så skulle vi mata in som indata och som förväntat resultat. På samma sätt fastställer vi längden på den sekvens vi vill ha (50 i exemplet) och sparar sedan kodningarna av de första 49 tecknen i X och det förväntade resultatet, dvs. det 50:e tecknet i Y.

  • Reskapande av 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)

Ett LSTM-nätverk förväntar sig att indata ska ha formen där samplingar är antalet datapunkter som vi har, tidssteg är antalet tidsberoende steg som finns i en enskild datapunkt, funktioner hänvisar till det antal variabler som vi har för det motsvarande sanna värdet i Y. Vi skalar sedan värdena i X_modified mellan 0 till 1 och en hot kodar våra sanna värden i Y_modified.

  • Definiering av 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')

En sekventiell modell som är en linjär stapel av lager används. Det första lagret är ett LSTM-lager med 300 minnesenheter och det returnerar sekvenser. Detta görs för att se till att nästa LSTM-skikt får sekvenser och inte bara slumpmässigt utspridda data. Efter varje LSTM-skikt används ett dropout-skikt för att undvika överanpassning av modellen. Slutligen har vi det sista lagret som ett fullt sammankopplat lager med en ”softmax”-aktivering och neuroner som är lika med antalet unika tecken, eftersom vi måste ge ut ett varmkodat resultat.

  • Att anpassa modellen och generera tecken

# 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 anpassas över 100 epoker, med en batchstorlek på 30. Vi fastställer sedan ett slumpmässigt frö (för att underlätta reproducerbarhet) och börjar generera tecken. Prognosen från modellen ger teckenkodningen för det förutspådda tecknet, det avkodas sedan tillbaka till teckenvärdet och läggs till i mönstret.

Så här skulle nätverkets utdata se ut

Eventuellt, efter tillräckligt många träningsepoker, kommer det att ge bättre och bättre resultat med tiden. Så här skulle du använda LSTM för att lösa en sekvensförutsägelseuppgift.

Slutanmärkningar

LSTM är en mycket lovande lösning på sekvens- och tidsserierelaterade problem. Den enda nackdel som jag finner med dem är dock svårigheten att träna dem. Mycket tid och systemresurser går åt för att träna även en enkel modell. Men det är bara en hårdvarubegränsning! Jag hoppas att jag lyckades ge dig en grundläggande förståelse för dessa nätverk. För eventuella problem eller frågor relaterade till bloggen är du välkommen att kommentera nedan.

Lär dig, engagera dig , hacka och bli anställd!

Lämna ett svar

Din e-postadress kommer inte publiceras.