Підручник з RNN (рекурентна нейронна мережа): приклад TensorFlow

Навіщо нам потрібна рекурентна нейронна мережа (RNN)?

Повторювана нейронна мережа (RNN) дозволяє моделювати одиниці пам’яті для збереження даних і моделювати короткочасні залежності. Він також використовується в прогнозуванні часових рядів для виявлення кореляції даних і закономірностей. Це також допомагає отримати прогнозні результати для послідовних даних, забезпечуючи поведінку, подібну до людського мозку.

Структура штучної нейронної мережі є відносно простою і в основному стосується множення матриць. Під час першого кроку вхідні дані множаться на початково випадкові ваги, а зсув, перетворений за допомогою функції активації, і вихідні значення використовуються для прогнозування. Цей крок дає уявлення про те, наскільки далека мережа від реальності.

Застосованим показником є ​​втрата. Чим вище функція втрат, тим дурніша модель. Щоб покращити знання мережі, потрібна деяка оптимізація шляхом коригування ваг мережі. Стохастичний градієнтний спуск — це метод, який використовується для зміни значень ваг у правильному напрямку. Після того, як коригування зроблено, мережа може використовувати інший пакет даних для перевірки своїх нових знань.

Помилка, на щастя, нижча, ніж раніше, але недостатньо мала. Етап оптимізації виконується ітераційно, доки помилка не буде мінімізована, тобто більше інформації не можна буде отримати.

Проблема з моделлю цього типу полягає в тому, що вона не має пам’яті. Це означає, що вхід і вихід незалежні. Іншими словами, моделі все одно, що було раніше. Це викликає деякі запитання, коли вам потрібно передбачити часові ряди або речення, оскільки мережа потребує інформації про історичні дані або минулі слова.

Щоб подолати цю проблему, було розроблено новий тип архітектури: повторювана нейронна мережа (далі — RNN).

Що таке рекурентна нейронна мережа (RNN)?

A Повторювана нейронна мережа (RNN) є класом Штучна нейронна мережа у якому зв’язок між різними вузлами утворює орієнтований граф, що забезпечує часову динамічну поведінку. Це допомагає моделювати послідовні дані, отримані з мереж прямого зв’язку. Він працює подібно до людського мозку, щоб забезпечити прогнозні результати.

Повторювана нейронна мережа виглядає дуже схожою на традиційну нейронну мережу, за винятком того, що до нейронів додається стан пам’яті. Розрахунок для включення пам’яті простий.

Уявіть просту модель лише з одним нейроном, що живиться пакетом даних. У традиційній нейронній мережі модель створює вихідні дані шляхом множення вхідних даних на вагу та функцію активації. З RNN цей вихід надсилається назад самому собі певну кількість разів. Ми називаємо крок за часом проміжок часу, протягом якого вихід стає вхідним для наступного множення матриці.

Наприклад, на малюнку нижче ви бачите, що мережа складається з одного нейрона. Мережа обчислює множення матриць між введенням і вагою та додає нелінійність за допомогою функції активації. Він стає вихідним за t-1. Цей вихід є входом другого множення матриці.

Повторювана нейронна мережа (RNN)
Повторювана нейронна мережа (RNN)

Нижче ми кодуємо простий RNN у TensorFlow, щоб зрозуміти крок, а також форму результату.

Мережа складається з:

  • Чотири входи
  • Шість нейронів
  • 2-разові кроки

Мережа працюватиме так, як показано на малюнку нижче.

Повторювана нейронна мережа (RNN)

Мережа називається «рекурентною», тому що вона виконує ту саму операцію в кожному квадраті активації. Перед використанням функції активації мережа обчислила ваги входів і попереднього виходу.

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

Ми можемо побудувати мережу з заповнювачем для даних, повторюваного етапу та результату.

  1. Визначте заповнювач для даних
X = tf.placeholder(tf.float32, [None, n_timesteps, n_inputs])

тут:

  • Немає: невідомо та буде прийнято розмір партії
  • n_timesteps: скільки разів мережа надсилатиме вихідні дані нейрону
  • n_inputs: кількість вхідних даних на пакет
  1. Визначте рекурентну мережу

Як зазначено на малюнку вище, мережа складається з 6 нейронів. Мережа обчислить два скалярних добутку:

  • Вхідні дані з першим набором ваг (тобто 6: дорівнює кількості нейронів)
  • Попередній вихід із другим набором ваг (тобто 6: відповідає номеру виводу)

Зауважте, що під час першого передавання значення попереднього результату дорівнюють нулю, оскільки ми не маємо жодного доступного значення.

Об’єктом для створення RNN є tf.contrib.rnn.BasicRNNCell з аргументом num_units для визначення кількості вхідних даних

basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)

Тепер, коли мережу визначено, ви можете обчислити виходи та стани

outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)

Цей об’єкт використовує внутрішній цикл для множення матриць відповідну кількість разів.

Зверніть увагу, що рекурентний нейрон є функцією всіх входів попередніх часових кроків. Так мережа створює власну пам’ять. Інформація з попереднього часу може поширюватися в майбутньому. Це магія рекурентної нейронної мережі

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

Для пояснення ви друкуєте значення попереднього стану. Результат, надрукований вище, показує вихід з останнього стану. Тепер надрукуйте всі результати, ви можете помітити, що стани є попередніми результатами кожної партії. Тобто попередній вихід містить інформацію про всю послідовність.e

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)

Повторювана нейронна мережа (RNN)

Вихід має вигляд (3, 2, 6):

  • 3: Кількість партій
  • 2: Номер тимчасового кроку
  • 6: Кількість нейронів

Оптимізація рекурентної нейронної мережі ідентична традиційній нейронній мережі. Ви побачите більш детальну інформацію про оптимізацію коду в наступній частині цього навчального посібника з рекурентної нейронної мережі.

Застосування RNN

RNN має багато застосувань, особливо коли йдеться про передбачення майбутнього. У фінансовій індустрії RNN може бути корисним для прогнозування цін на акції або знака напрямку фондового ринку (тобто позитивного чи негативного).

RNN корисний для автономного автомобіля, оскільки він може уникнути автомобільної аварії, передбачаючи траєкторію транспортного засобу.

RNN широко використовується в аналізі тексту, підписах до зображень, аналізі настроїв і машинному перекладі. Наприклад, можна використовувати рецензію на фільм, щоб зрозуміти відчуття, які відчував глядач після перегляду фільму. Автоматизація цього завдання дуже корисна, коли кінокомпанія не має достатньо часу для перегляду, маркування, консолідації та аналізу оглядів. Машина може виконувати роботу з більш високою точністю.

Обмеження RNN

Теоретично RNN має передавати інформацію до разів. Однак поширювати всю цю інформацію, коли часовий крок занадто великий, досить складно. Коли мережа має занадто багато глибоких шарів, вона стає непридатною для навчання. Ця проблема називається: проблема зникаючого градієнта. Якщо ви пам'ятаєте, нейронна мережа оновлює вагу за допомогою алгоритму градієнтного спуску. Градієнти стають меншими, коли мережа просувається вниз до нижчих рівнів.

Підсумовуючи, градієнти залишаються постійними, тобто немає місця для вдосконалення. Модель навчається зі зміни градієнта; ця зміна впливає на вихід мережі. Однак, якщо різниця в градієнті надто мала (тобто вагові коефіцієнти трохи змінюються), мережа нічого не може дізнатися, а отже, і вихід. Таким чином, мережа, яка стикається з проблемою зникаючого градієнта, не може знайти хороше рішення.

Удосконалення ЛСТМ

Щоб подолати потенційну проблему зникнення градієнта, з якою стикається RNN, троє дослідників, Hochreiter, Schmidhuber і Bengio вдосконалили RNN за допомогою архітектури під назвою Довга короткочасна пам’ять (LSTM). Коротше кажучи, LSMT надає в мережу відповідну минулу інформацію до пізнішого часу. Машина використовує кращу архітектуру для вибору та перенесення інформації на пізніший час.

Архітектура LSTM доступна в TensorFlow, tf.contrib.rnn.LSTMCell. LSTM виходить за межі підручника. Можна звернутися до офіц документація Для отримання додаткової інформації

RNN у часових рядах

У цьому підручнику TensorFlow RNN ви використовуватимете RNN із даними часових рядів. Часові ряди залежать від попереднього часу, що означає, що минулі значення містять відповідну інформацію, з якої мережа може навчитися. Ідея прогнозування часових рядів полягає в тому, щоб оцінити майбутню вартість ряду, скажімо, ціни акцій, температури, ВВП тощо.

Підготовка даних для Keras RNN і часових рядів може бути дещо складною. Перш за все, мета полягає в тому, щоб передбачити наступне значення ряду, тобто ви будете використовувати минулу інформацію для оцінки значення при t + 1. Мітка дорівнює вхідній послідовності та зсувається на один період вперед. По-друге, число вхідних даних встановлено на 1, тобто одне спостереження за час. Нарешті, крок у часі дорівнює послідовності числових значень. Наприклад, якщо встановити часовий крок на 10, послідовність введення повертатиметься десять разів поспіль.

Подивіться на графік нижче, ми представили дані часового ряду ліворуч і фіктивну послідовність введення праворуч. Ви створюєте функцію для повернення набору даних із випадковим значенням для кожного дня з січня 2001 року по грудень 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)

Вихід

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

RNN у часових рядах

Права частина графіка показує всі серії. Він почався з 2001 року і закінчується в 2019. Немає сенсу передавати всі дані в мережу, натомість потрібно створити пакет даних довжиною, що дорівнює часовому кроку. Цей пакет буде змінною X. Змінна Y така сама, як X, але зміщена на один період (тобто ви хочете спрогнозувати t+1).

Обидва вектори мають однакову довжину. Ви можете побачити це в правій частині графіка вище. Лінія представляє десять значень вхідного X, а червоні крапки — десять значень мітки Y. Зауважте, що мітка починається на один період перед X і закінчується на один період після.

Створіть RNN для прогнозування часових рядів у TensorFlow

Тепер у цьому навчанні RNN настав час створити свій перший RNN для прогнозування серії вище. Вам потрібно вказати деякі гіперпараметри (параметри моделі, тобто кількість нейронів тощо) для моделі:

  • Кількість входів: 1
  • Часовий крок (вікна в часовому ряді): 10
  • Кількість нейронів: 120
  • Кількість виходів: 1

Ваша мережа навчатиметься з послідовності 10 днів і міститиме 120 рекурентних нейронів. Ви живите модель одним входом, тобто одним днем. Не соромтеся змінити значення, щоб побачити, чи покращилася модель.

Перш ніж побудувати модель, вам потрібно розділити набір даних на набір поїздів і тестовий набір. Повний набір даних містить 222 точки даних; ви будете використовувати першу 201 точку для навчання моделі, а останні 21 точку для тестування моделі.

Після того, як ви визначите набір тренувань і тестів, вам потрібно створити об’єкт, що містить пакети. У цих пакетах у вас є значення X і значення Y. Пам’ятайте, що значення X є із запізненням на один період. Тому ви використовуєте перші 200 спостережень, а часовий крок дорівнює 10. Об’єкт X_batches має містити 20 пакетів розміром 10*1. y_batches має ту саму форму, що й об’єкт X_batches, але з крапкою вперед.

Крок 1) Створіть потяг і випробуйте

Перш за все, ви перетворюєте серію в a нумпі масив; потім ви визначаєте вікна (тобто кількість часу, з якого мережа навчатиметься), кількість входів, виходів і розмір набору поїздів, як показано в прикладі TensorFlow RNN нижче.

series = np.array(ts)
n_windows = 20   
n_input =  1
n_output = 1
size_train = 201

Після цього ви просто розділите масив на два набори даних.

## Split data
train = series[:size_train]
test = series[size_train:]
print(train.shape, test.shape)
(201,) (21,)

Крок 2) Створіть функцію для повернення X_batches і y_batches

Щоб зробити це простіше, ви можете створити функцію, яка повертає два різні масиви: один для X_batches і один для y_batches.

Давайте напишемо функцію RNN TensorFlow для створення пакетів.

Зверніть увагу, що партії X відстають на один період (ми беремо значення t-1). Вихідні дані функції повинні мати три виміри. Перший розмір дорівнює кількості пакетів, другий - розміру вікон, а останній - кількості вхідних даних.

Найскладніше – правильно вибрати точки даних. Для точок даних X ви вибираєте спостереження від t = 1 до t = 200, тоді як для точки даних Y ви повертаєте спостереження від t = 2 до 201. Отримавши правильні точки даних, можна легко змінити форму серії.

Щоб побудувати об’єкт із пакетами, вам потрібно розділити набір даних на десять пакетів однакової довжини (тобто 20). Ви можете використати метод зміни форми та передати -1, щоб серія була подібною до розміру партії. Значення 20 — це кількість спостережень на партію, а 1 — кількість введених даних.

Вам потрібно зробити той самий крок, але для етикетки.

Зауважте, що вам потрібно зсунути дані на кількість разів, яку ви хочете спрогнозувати. Наприклад, якщо ви хочете спрогнозувати на один час наперед, ви зрушуєте ряд на 1. Якщо ви хочете спрогнозувати два дні, тоді зсуваєте дані на 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

Тепер, коли функцію визначено, ви можете викликати її для створення пакетів, як показано в наведеному нижче прикладі RNN.

X_batches, y_batches = create_batches(df = train,
                                      windows = n_windows,
                                      input = n_input,
                                      output = n_output)

Ви можете роздрукувати форму, щоб переконатися, що розміри правильні.

print(X_batches.shape, y_batches.shape)
(10, 20, 1) (10, 20, 1)

Вам потрібно створити тестовий набір лише з однієї партії даних і 20 спостережень.

Зауважте, що якщо ви прогнозуєте день за днем, це означає, що друге прогнозоване значення базуватиметься на справжньому значенні першого дня (t+1) тестового набору даних. Власне, буде відома справжня вартість.

Якщо ви хочете спрогнозувати t+2 (тобто на два дні вперед), вам потрібно використовувати прогнозоване значення t+1; якщо ви збираєтеся передбачити t+3 (на три дні вперед), вам потрібно використовувати прогнозовані значення t+1 і t+2. Цілком зрозуміло, що важко точно передбачити t+n днів наперед.

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)

Гаразд, розмір вашої партії готовий, ви можете створювати архітектуру RNN. Пам’ятайте, що у вас є 120 рекурентних нейронів.

Крок 3) Побудуйте модель

Щоб створити модель, потрібно визначити три частини:

  1. Змінна з тензорами
  2. РНН
  3. Втрати та оптимізація

Крок 3.1) Змінні

Потрібно вказати змінні X і y з відповідною формою. Цей крок є тривіальним. Тензор має ту саму розмірність, що й об’єкти X_batches і y_batches.

Наприклад, тензор X є заповнювачем (перегляньте підручник на Вступ до Тензорний потік щоб оновити ваші думки про оголошення змінних) має три виміри:

  • Примітка: розмір партії
  • n_windows: довжина вікон. тобто кількість разів, коли модель дивиться назад
  • n_input: номер введення

Результат:

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

Крок 3.2) Створіть RNN

У другій частині цього прикладу RNN TensorFlow вам потрібно визначити архітектуру мережі. Як і раніше, ви використовуєте об’єкт BasicRNNCell і dynamic_rnn з оцінювача TensorFlow.

## 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.3) Створення втрат і оптимізація

Оптимізація моделі залежить від завдання, яке ви виконуєте. У попередньому підручнику на CNN, вашою метою було класифікувати зображення, у цьому підручнику RNN мета дещо інша. Вас просять спрогнозувати безперервну змінну в порівнянні з класом.

Ця відмінність важлива, оскільки вона змінить проблему оптимізації. Проблема оптимізації для безперервної змінної полягає в мінімізації середньої квадратичної помилки. Щоб побудувати ці показники в TF, ви можете використовувати:

  • tf.reduce_sum(tf.square(виходи – y))

Решта коду RNN така ж, як і раніше; Ви використовуєте оптимізатор Adam для зменшення втрат (тобто MSE):

  • tf.train.AdamOptimizer(швидкість_навчання=швидкість_навчання)
  • optimizer.minimize(loss)

Ось і все, ви можете пакувати все разом, і ваша модель готова до тренувань.

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

Ви навчатимете модель за допомогою 1500 епох і друкуватимете втрати кожні 150 ітерацій. Коли модель навчена, ви оцінюєте її на тестовому наборі та створюєте об’єкт, що містить прогнози, як показано в наведеному нижче прикладі повторюваної нейронної мережі.

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

Нарешті в цьому посібнику з глибокого навчання RNN ви можете побудувати фактичне значення ряду з прогнозованим значенням. Якщо вашу модель виправлено, передбачувані значення слід поставити поверх фактичних значень.

Як бачите, модель має куди вдосконалюватися. Ви самі можете змінити гіперпараметри, такі як вікна, розмір партії кількості рекурентних нейронів.

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()
Прогноз проти фактичного

Прогноз проти фактичного

Підсумки

Рекурентна нейронна мережа — це надійна архітектура для аналізу часових рядів або тексту. Вихід попереднього стану є зворотним зв'язком для збереження пам'яті мережі протягом часу або послідовності слів.

У TensorFlow ви можете використовувати наступні коди для навчання повторюваної нейронної мережі TensorFlow для часових рядів:

Параметри моделі

n_windows = 20   
n_input =  1
n_output = 1
size_train = 201

Дайте визначення моделі

X = tf.placeholder(tf.float32, [None, n_windows, n_input])   
y = tf.placeholder(tf.float32, [None, n_windows, n_output])

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

Побудуйте оптимізацію

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