RNN (Recurrent Neural Network) Handledning: TensorFlow Exempel
⚡ Smart sammanfattning
Återkommande neurala nätverk lägger till ett minnestillstånd till vanliga neuroner och matar varje utdata tillbaka som nästa indata så att modellen kan lära sig av ordningen. Den här genomgången bygger ett minnestillstånd i TensorFlow och prognostiserar en tidsserie.

Varför behöver vi ett återkommande neuralt nätverk (RNN)?
Recurrent Neural Network (RNN) låter dig modellera minnesenheter för att bevara data och modellera kortsiktiga beroenden. Det används också i tidsserieprognoser för identifiering av datakorrelationer och mönster. Det hjälper också till att producera prediktiva resultat för sekventiell data genom att leverera liknande beteende som en mänsklig hjärna.
Strukturen för en Artificiellt neuralt nätverk är relativt enkelt och handlar huvudsakligen om matrismultiplikation. Under det första steget multipliceras ingångarna med initialt slumpmässiga vikter och bias, transformeras med en aktiveringsfunktion och utdatavärdena används för att göra en förutsägelse. Detta steg ger en uppfattning om hur långt nätverket är från verkligheten.
Det mått som används är förlusten. Ju högre förlustfunktion, desto dummare är modellen. För att förbättra kunskapen om nätverket krävs viss optimering genom att justera nätets vikter. Den stokastiska gradientnedgången är metoden som används för att ändra vikternas värden i rätt riktning. När justeringen är gjord kan nätverket använda ytterligare en sats data för att testa sin nya kunskap.
Felet är lyckligtvis lägre än tidigare, men inte tillräckligt litet. Optimeringssteget görs iterativt tills felet minimeras, dvs. ingen mer information kan utläsas.tracted.
Problemet med denna typ av modell är att den inte har något minne. Det betyder att ingången och utgången är oberoende. Modellen bryr sig med andra ord inte om vad som kom innan. Det väcker vissa frågor när du behöver förutsäga tidsserier eller meningar eftersom nätverket behöver ha information om historiska data eller tidigare ord.
För att lösa detta problem har en ny typ av arkitektur utvecklats: Recurrent Neural Network (RNN härefter)
Vad är ett återkommande neuralt nätverk (RNN)?
Ett återkommande neuralt nätverk (RNN) är en klass av artificiella neurala nätverk där kopplingen mellan olika noder bildar en riktad graf för att ge ett temporalt dynamiskt beteende. Det hjälper till att modellera sekventiell data som härrör från feedforward-nätverk. Det fungerar på liknande sätt som mänskliga hjärnor för att leverera prediktiva resultat.
Ett återkommande neuralt nätverk ser ganska likt ett traditionellt neuralt nätverk förutom att ett minnestillstånd läggs till neuronerna. Beräkningen för att inkludera ett minne är enkel.
Tänk dig en enkel modell med endast en neuron som matas av en databatch. I ett traditionellt neuralt nätverk producerar modellen utdata genom att multiplicera indata med vikten och aktiveringsfunktionen. Med ett RNN skickas denna utdata tillbaka till sig själv ett antal gånger. Vi kallar det. tidssteg den tid som utdata blir indata för nästa matrismultiplikation.
På bilden nedan kan du till exempel se att nätverket består av en neuron. Nätverket beräknar matrismultiplikationen mellan ingången och vikten och lägger till icke-linjäritet med aktiveringsfunktionen. Det blir utgången vid t-1. Denna utsignal är ingången för den andra matrismultiplikationen.
Återkommande neurala nätverk (RNN)
Nedan kodar vi en enkel RNN i TensorFlow för att förstå steget och även formen på utdata.
Nätverket består av:
- Fyra ingångar
- Sex neuroner
- 2-gångssteg
Nätverket kommer att fortsätta som bilden nedan visar.
Nätverket kallas "återkommande" eftersom det utför samma operation i varje aktiveringsruta. Nätverket beräknade vikterna av ingångarna och den tidigare utsignalen innan för att använda en aktiveringsfunktion.
Versionsnotering: Koden i den här handledningen riktar sig mot TensorFlow 1.x graf-API. På TensorFlow 2.x körs samma skript fortfarande efter import tensorflow.compat.v1 as tf plus tf.disable_v2_behavior(), eftersom platshållare, sessioner och rnn_cell-modulen nu finns under tf.compat.v1Den idiomatiska 2.x-ekvivalenten är tf.keras.layers.SimpleRNN.
import numpy as np import tensorflow as tf n_inputs = 4 n_neurons = 6 n_timesteps = 2 The data is a sequence of a number from 0 to 9 and divided into three batches of data. ## Data X_batch = np.array([ [[0, 1, 2, 5], [9, 8, 7, 4]], # Batch 1 [[3, 4, 5, 2], [0, 0, 0, 0]], # Batch 2 [[6, 7, 8, 5], [6, 5, 4, 2]], # Batch 3 ])
Vi kan bygga nätverket med en platshållare för data, det återkommande steget och utdata.
Definiera platshållaren för data
X = tf.placeholder(tf.float32, [None, n_timesteps, n_inputs])
Här:
- Ingen: Okänd och kommer att ta storleken på batchen
- n_timesteps: Antal gånger nätverket kommer att skicka utdata tillbaka till neuronen
- n_inputs: Antal input per batch
Definiera det återkommande nätverket
Som nämnts i bilden ovan består nätverket av 6 neuroner. Nätverket kommer att beräkna produkt med två punkter:
- Indata med den första uppsättningen vikter (dvs 6: lika med antalet neuroner)
- Föregående utdata med en andra uppsättning vikter (dvs. 6: motsvarande antalet utdata)
Observera att, under den första feedforwarden, är värdena för föregående utdata lika med nollor eftersom vi inte har något tillgängligt värde.
Objektet för att bygga ett RNN är tf.contrib.rnn.BasicRNNCell med argumentet num_units för att definiera antalet indata
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
Nu när nätverket är definierat kan du beräkna utdata och tillstånd
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)
Detta objekt använder en intern loop för att multiplicera matriserna ett lämpligt antal gånger.
Observera att den återkommande neuronen är en funktion av alla insignaler från föregående tidssteg. Det är så nätverket bygger sitt eget minne. Informationen från föregående tidpunkt kan fortplanta sig i framtida tid. Detta är magin med det återkommande neurala nätverket.
## Define the shape of the tensor X = tf.placeholder(tf.float32, [None, n_timesteps, n_inputs]) ## Define the network basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons) outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32) init = tf.global_variables_initializer() init = tf.global_variables_initializer() with tf.Session() as sess: init.run() outputs_val = outputs.eval(feed_dict={X: X_batch}) print(states.eval(feed_dict={X: X_batch})) [[ 0.38941205 -0.9980438 0.99750966 0.7892596 0.9978241 0.9999997 ] [ 0.61096436 0.7255889 0.82977575 -0.88226104 0.29261455 -0.15597084] [ 0.62091285 -0.87023467 0.99729395 -0.58261937 0.9811445 0.99969864]]
För att förklara detta skriver du ut värdena för det föregående tillståndet. Utdata som skrivs ut ovan visar utdata från det senaste tillståndet. Skriv nu ut all utdata, du kan se att tillstånden är den föregående utdatan för varje batch. Det vill säga, den föregående utdatan innehåller information om hela sekvensen.
print(outputs_val) print(outputs_val.shape) [[[-0.75934666 -0.99537754 0.9735819 -0.9722234 -0.14234993 -0.9984044 ] [ 0.99975264 -0.9983206 0.9999993 -1. -0.9997506 -1. ]] [[ 0.97486496 -0.98773265 0.9969686 -0.99950117 -0.7092863 -0.99998885] [ 0.9326837 0.2673438 0.2808514 -0.7535883 -0.43337247 0.5700631 ]] [[ 0.99628735 -0.9998728 0.99999213 -0.99999976 -0.9884324 -1. ] [ 0.99962527 -0.9467421 0.9997403 -0.99999714 -0.99929446 -0.9999795 ]]] (3, 2, 6)
Den kommenterade vyn nedan delar upp samma utskrivna tensor batch för batch.
Utgången har formen av (3, 2, 6):
- 3: Antal partier
- 2: Tidsstegets nummer
- 6: Antal neuroner
Optimeringen av ett återkommande neuralt nätverk är identiskt med ett traditionellt neuralt nätverk. Du kommer att se mer i detalj hur du kodar optimering i nästa del av denna självstudie för återkommande neurala nätverk.
Tillämpningar av RNN
RNN har flera användningsområden, särskilt när det gäller att förutsäga framtiden. Inom finansbranschen kan RNN vara till hjälp för att förutsäga aktiekurser eller tecken på börsriktningen (dvs. positiv eller negativ).
RNN är användbart för en autonom bil eftersom det kan undvika en bilolycka genom att förutse fordonets bana.
RNN används flitigt inom textanalys, bildtextning, sentimentanalys och maskinöversättning. Till exempel kan man använda en filmrecension för att förstå känslan åskådaren upplevde efter att ha sett filmen. Att automatisera denna uppgift är mycket användbart när filmbolaget inte har tillräckligt med tid att granska, märka, konsolidera och analysera recensionerna. Maskinen kan göra jobbet med högre noggrannhet.
Begränsningar för RNN
I teorin är RNN tänkt att bära informationen upp till tider. Det är dock ganska utmanande att sprida all denna information när tidssteget är för långt. När ett nätverk har för många djupa lager blir det oträningsbart. Detta problem kallas: försvinnande gradientproblem. Om du kommer ihåg uppdaterar det neurala nätverket vikten med algoritmen för gradientnedstigning. Gradienterna blir mindre när nätverket går ner till lägre lager.
Sammanfattningsvis förblir gradienterna konstanta, vilket innebär att det inte finns utrymme för förbättring. Modellen lär sig av en förändring i gradienten; denna förändring påverkar nätverkets utdata. Men om skillnaden i gradienten är för liten (dvs. vikterna ändras lite) kan nätverket inte lära sig någonting och därmed inte utdata. Därför kan ett nätverk som står inför ett problem med försvinnande gradienter inte konvergera mot en bra lösning.
Förbättring LSTM
För att övervinna det potentiella problemet med försvinnande gradienter som RNN står inför, förbättrade tre forskare, Hochreiter, Schmidhuber och Bengio, RNN med en arkitektur som kallas Long Short-Term Memory (LSTM). Kortfattat förser LSTM nätverket med relevant information från det förflutna till en senare tid. Maskinen använder en bättre arkitektur för att välja och föra information tillbaka till en senare tidpunkt.
LSTM-arkitekturen finns tillgänglig i TensorFlow, tf.contrib.rnn.LSTMCell. LSTM omfattas inte av handledningen. Du kan hänvisa till den officiella dokumentation för mer information
På TensorFlow 2.x nås den cellen som tf.compat.v1.nn.rnn_cell.LSTMCell, medan ny kod normalt använder tf.keras.layers.LSTM eller den tändare-grindade tf.keras.layers.GRU, vilket uppnår jämförbar noggrannhet med färre parametrar.
RNN i tidsserier
I den här handledningen för TensorFlow RNN kommer du att använda ett RNN med tidsseriedata. Tidsserier är beroende av tidigare tid, vilket innebär att tidigare värden inkluderar relevant information som nätverket kan lära sig av. Idén bakom tidsserieprognoser är att uppskatta det framtida värdet av en serie, låt oss säga aktiekurs, temperatur, BNP och så vidare.
Dataförberedelsen för Keras RNN och tidsserier kan vara lite knepig. Först och främst är målet att förutsäga nästa värde i serien, vilket innebär att du kommer att använda den tidigare informationen för att uppskatta värdet till t + 1. Etiketten är lika med inmatningssekvensen och flyttas en period framåt. För det andra sätts antalet inmatningar till 1, dvs en observation per gång. Slutligen är tidssteget lika med sekvensen av det numeriska värdet. Om du till exempel ställer in tidssteget till 10 kommer inmatningssekvensen tillbaka tio gånger i följd.
Titta på grafen nedan, vi har representerat tidsseriedata till vänster och en fiktiv inmatningssekvens till höger. Du skapar en funktion för att returnera en datauppsättning med slumpmässigt värde för varje dag från januari 2001 till december 2016
# To plot pretty figures %matplotlib inline import matplotlib import matplotlib.pyplot as plt import pandas as pd def create_ts(start = '2001', n = 201, freq = 'M'): rng = pd.date_range(start=start, periods=n, freq=freq) ts = pd.Series(np.random.uniform(-18, 18, size=len(rng)), rng).cumsum() return ts ts= create_ts(start = '2001', n = 192, freq = 'M') ts.tail(5)
Produktion
2016-08-31 -93.459631 2016-09-30 -95.264791 2016-10-31 -95.551935 2016-11-30 -105.879611 2016-12-31 -123.729319 Freq: M, dtype: float64
ts = create_ts(start = '2001', n = 222) # Left plt.figure(figsize=(11,4)) plt.subplot(121) plt.plot(ts.index, ts) plt.plot(ts.index[90:100], ts[90:100], "b-", linewidth=3, label="A training instance") plt.title("A time series (generated)", fontsize=14) # Right plt.subplot(122) plt.title("A training instance", fontsize=14) plt.plot(ts.index[90:100], ts[90:100], "b-", markersize=8, label="instance") plt.plot(ts.index[91:101], ts[91:101], "bo", markersize=10, label="target", markerfacecolor='red') plt.legend(loc="upper left") plt.xlabel("Time") plt.show()
Den högra delen av grafen visar alla serier. Den startade från 2001 och slutar 2019. Det är meningslöst att mata in all data i nätverket, istället behöver du skapa en databatch med en längd som är lika med tidssteget. Denna batch kommer att vara X-variabeln. Y-variabeln är densamma som X men förskjuten med en period (dvs. du vill prognostisera t+1).
Båda vektorerna har samma längd. Du kan se det i den högra delen av grafen ovan. Linjen representerar de tio värdena för X-inmatningen, medan de röda prickarna är de tio värdena för etiketten, Y. Observera att etiketten börjar en period före X och slutar en period efter.
Bygg ett RNN för att förutsäga tidsserier i TensorFlow
Nu i denna RNN-utbildning är det dags att bygga ditt första RNN för att förutsäga serien ovan. Du måste ange några hyperparametrar (modellens parametrar, dvs antal neuroner, etc.) för modellen:
- Antal ingångar: 1
- Tidssteg (fönster i tidsserier): 10
- Antal neuroner: 120
- Antal utdata: 1
Ditt nätverk kommer att lära sig från en sekvens på 10 dagar och innehålla 120 återkommande neuroner. Du matar modellen med en inmatning, dvs. en dag. Du kan gärna ändra värdena för att se om modellen förbättrades.
Innan du konstruerar modellen måste du dela upp datamängden i en tåguppsättning och en testuppsättning. Hela datamängden har 222 datapunkter; du kommer att använda de första 201 punkterna för att träna modellen och de sista 21 punkterna för att testa din modell.
När du har definierat ett tåg och en testuppsättning måste du skapa ett objekt som innehåller batcherna. I dessa batchar har du X-värden och Y-värden. Kom ihåg att X-värdena är fördröjda med en period. Därför använder du de första 200 observationerna och tidssteget är lika med 10. X_batches-objektet ska innehålla 20 batchar av storleken 10*1. y_batches har samma form som X_batches-objektet men med en period före.
Steg 1) Skapa tåget och testa
Först och främst konverterar du serien till en numpy array; sedan definierar du fönstren (dvs. antalet gånger nätverket kommer att lära sig av), antalet ingångar, utdata och storleken på tågsetet som visas i TensorFlow RNN-exemplet nedan.
series = np.array(ts) n_windows = 20 n_input = 1 n_output = 1 size_train = 201
Efter det delar du helt enkelt upp arrayen i två datamängder.
## Split data train = series[:size_train] test = series[size_train:] print(train.shape, test.shape) (201,) (21,)
Steg 2) Skapa funktionen för att returnera X_batches och y_batches
För att göra det enklare kan du skapa en funktion som returnerar två olika arrayer, en för X_batches och en för y_batches.
Låt oss skriva en RNN TensorFlow-funktion för att konstruera satserna.
Observera att X-satserna är fördröjda med en period (vi tar värdet t-1). Funktionens utdata ska ha tre dimensioner. De första måtten motsvarar antalet partier, den andra storleken på fönstren och den sista är antalet inmatade.
Det knepiga är att välja datapunkterna korrekt. För X-datapunkterna väljer du observationerna från t = 1 till t =200, medan för Y-datapunkten returnerar du observationerna från t = 2 till 201. När du väl har rätt datapunkter är det enkelt att omforma serien.
För att konstruera objektet med satserna måste du dela upp datasetet i tio satser av samma längd (dvs. 20). Du kan använda omformningsmetoden och skicka -1 så att serien liknar batchstorleken. Värdet 20 är antalet observationer per batch och 1 är antalet indata.
Du måste göra samma steg utom för etiketten.
Observera att du måste förskjuta data till det antal gånger du vill prognostisera. Om du till exempel vill förutspå en tid framåt, förskjuter du serien med 1. Om du vill prognostisera två dagar, förskjuter du data med 2.
x_data = train[:size_train-1]: Select all the training instance minus one day X_batches = x_data.reshape(-1, windows, input): create the right shape for the batch e.g (10, 20, 1) def create_batches(df, windows, input, output): ## Create X x_data = train[:size_train-1] # Select the data X_batches = x_data.reshape(-1, windows, input) # Reshape the data ## Create y y_data = train[n_output:size_train] y_batches = y_data.reshape(-1, windows, output) return X_batches, y_batches
Nu när funktionen är definierad kan du anropa den för att skapa batcherna som visas i nedanstående RNN-exempel.
X_batches, y_batches = create_batches(df = train,
windows = n_windows,
input = n_input,
output = n_output)
Du kan skriva ut formen för att se till att måtten är korrekta.
print(X_batches.shape, y_batches.shape)
(10, 20, 1) (10, 20, 1)
Du behöver skapa testsetet med endast en batch data och 20 observationer.
Observera att du prognostiserar dagar efter dagar betyder att det andra förutsagda värdet kommer att baseras på det sanna värdet för den första dagen (t+1) i testdatauppsättningen. Faktum är att det verkliga värdet kommer att vara känt.
Om du vill prognostisera t+2 (dvs två dagar framåt), måste du använda det förutsagda värdet t+1; om du ska förutsäga t+3 (tre dagar framåt), måste du använda det förutsagda värdet t+1 och t+2. Det är logiskt att det är svårt att förutsäga exakt t+n dagar framåt.
X_test, y_test = create_batches(df = test, windows = 20,input = 1, output = 1)
print(X_test.shape, y_test.shape)
(10, 20, 1) (10, 20, 1)
Okej, din batchstorlek är klar, du kan bygga RNN-arkitekturen. Kom ihåg att du har 120 återkommande neuroner.
Steg 3) Bygg modellen
För att skapa modellen måste du definiera tre delar:
- Variabeln med tensorerna
- RNN
- Förlusten och optimeringen
Steg 3.1) variabler
Du måste ange X- och y-variablerna med lämplig form. Detta steg är trivialt. Tensorn har samma dimension som objekten X_batches och y_batches.
Till exempel är tensorn X en platshållare (se handledningen på Introduktion till TensorFlow för att fräscha upp ditt sinne om variabeldeklaration) har tre dimensioner:
- Obs: satsens storlek
- n_windows: Fönstrens längd, dvs. hur många gånger modellen tittar bakåt
- n_input: Antal ingångar
Resultatet är:
tf.placeholder(tf.float32, [None, n_windows, n_input])
## 1. Construct the tensors X = tf.placeholder(tf.float32, [None, n_windows, n_input]) y = tf.placeholder(tf.float32, [None, n_windows, n_output])
Steg 3.2) Skapa RNN
I den andra delen av detta RNN TensorFlow-exempel måste du definiera nätverkets arkitektur. Som tidigare använder du objektet BasicRNNCell och dynamic_rnn från TensorFlow estimator.
## 2. create the model
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=r_neuron, activation=tf.nn.relu)
rnn_output, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)
Nästa del är lite knepigare men möjliggör snabbare beräkning. Du måste omvandla körutdata till ett tätt lager och sedan konvertera det igen för att ha samma dimension som indata.
stacked_rnn_output = tf.reshape(rnn_output, [-1, r_neuron]) stacked_outputs = tf.layers.dense(stacked_rnn_output, n_output) outputs = tf.reshape(stacked_outputs, [-1, n_windows, n_output])
Steg 3.3) Skapa förlusten och optimering
Modelloptimeringen beror på vilken uppgift du utför. I den föregående handledningen om CNN, ditt mål var att klassificera bilder, i den här RNN-handledningen är målet något annorlunda. Du ombeds att göra en förutsägelse på en kontinuerlig variabel jämfört med en klass.
Denna skillnad är viktig eftersom den kommer att förändra optimeringsproblemet. Optimeringsproblemet för en kontinuerlig variabel är att minimera medelkvadratfelet. För att konstruera dessa mätvärden i TF kan du använda:
tf.reduce_sum(tf.square(utgångar – y))
Resten av RNN-koden är densamma som tidigare; du använder en Adam-optimerare för att minska förlusten (dvs. MSE):
tf.train.AdamOptimizer(learning_rate=learning_rate)
optimizer.minimize(förlust)
Det är allt, du kan packa ihop allt och din modell är redo att träna.
tf.reset_default_graph() r_neuron = 120 ## 1. Construct the tensors X = tf.placeholder(tf.float32, [None, n_windows, n_input]) y = tf.placeholder(tf.float32, [None, n_windows, n_output]) ## 2. create the model basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=r_neuron, activation=tf.nn.relu) rnn_output, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32) stacked_rnn_output = tf.reshape(rnn_output, [-1, r_neuron]) stacked_outputs = tf.layers.dense(stacked_rnn_output, n_output) outputs = tf.reshape(stacked_outputs, [-1, n_windows, n_output]) ## 3. Loss + optimization learning_rate = 0.001 loss = tf.reduce_sum(tf.square(outputs - y)) optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate) training_op = optimizer.minimize(loss) init = tf.global_variables_initializer()
Du kommer att träna modellen med 1500 epoker och skriva ut förlusten var 150:e iteration. När modellen är tränad utvärderar du modellen på testsetet och skapar ett objekt som innehåller förutsägelserna som visas i exemplet nedan för återkommande neurala nätverk.
iteration = 1500 with tf.Session() as sess: init.run() for iters in range(iteration): sess.run(training_op, feed_dict={X: X_batches, y: y_batches}) if iters % 150 == 0: mse = loss.eval(feed_dict={X: X_batches, y: y_batches}) print(iters, "\tMSE:", mse) y_pred = sess.run(outputs, feed_dict={X: X_test}) 0 MSE: 502893.34 150 MSE: 13839.129 300 MSE: 3964.835 450 MSE: 2619.885 600 MSE: 2418.772 750 MSE: 2110.5923 900 MSE: 1887.9644 1050 MSE: 1747.1377 1200 MSE: 1556.3398 1350 MSE: 1384.6113
Äntligen i denna RNN Deep Learning-handledning kan du plotta det faktiska värdet av serien med det förutsagda värdet. Om din modell är korrigerad bör de förutsagda värdena läggas ovanpå de faktiska värdena.
Som du kan se finns det utrymme för förbättringar i modellen. Det är upp till dig att ändra hyperparametrar som fönster, batchstorlek eller antalet återkommande neuroner.
plt.title("Forecast vs Actual", fontsize=14) plt.plot(pd.Series(np.ravel(y_test)), "bo", markersize=8, label="Actual", color='green') plt.plot(pd.Series(np.ravel(y_pred)), "r.", markersize=8, label="Forecast", color='red') plt.legend(loc="lower left") plt.xlabel("Time") plt.show()
Prognos kontra faktisk





