Учебное пособие по RNN (рекуррентной нейронной сети): пример TensorFlow

Зачем нам нужна рекуррентная нейронная сеть (RNN)?

Рекуррентная нейронная сеть (RNN) позволяет моделировать блоки памяти для сохранения данных и моделирования краткосрочных зависимостей. Он также используется при прогнозировании временных рядов для выявления корреляций и закономерностей данных. Это также помогает получать результаты прогнозирования для последовательных данных, обеспечивая поведение, аналогичное человеческому мозгу.

Структура искусственной нейронной сети относительно проста и в основном связана с умножением матриц. На первом этапе входные данные умножаются на изначально случайные веса, а смещение преобразуется с помощью функции активации, а выходные значения используются для прогнозирования. Этот шаг дает представление о том, насколько сеть далека от реальности.

Применяемая метрика – это потери. Чем выше функция потерь, тем глупее модель. Чтобы улучшить знание сети, необходима некоторая оптимизация путем корректировки весов сети. Стохастический градиентный спуск — это метод, используемый для изменения значений весов в правильном направлении. После внесения корректировок сеть может использовать еще один пакет данных для проверки своих новых знаний.

Ошибка, к счастью, ниже, чем раньше, но недостаточно мала. Шаг оптимизации выполняется итеративно до тех пор, пока ошибка не будет минимизирована, т. е. больше невозможно извлечь информацию.

Проблема с моделями этого типа в том, что у них нет памяти. Это означает, что вход и выход независимы. Другими словами, модель не заботится о том, что было раньше. Возникает некоторый вопрос, когда вам нужно предсказать временные ряды или предложения, поскольку сети необходима информация об исторических данных или прошлых словах.

Чтобы решить эту проблему, был разработан новый тип archiРазработана технология: Рекуррентная нейронная сеть (далее 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]]

В пояснительных целях вы печатаете значения предыдущего состояния. Вывод, напечатанный выше, показывает вывод из последнего состояния. Теперь распечатайте весь вывод. Вы можете заметить, что состояния — это предыдущий вывод каждого пакета. То есть предыдущий вывод содержит информацию обо всей последовательности.

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 с помощью archiТехнология под названием «Долгая кратковременная память» (LSTM). Короче говоря, LSMT предоставляет сети соответствующую информацию о прошлом и более недавнем времени. Машина использует лучший archiвозможность выбирать и переносить информацию обратно в later времени.

LSTM archiтектура доступна в 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
  • Шаг времени (windows во временном ряду): 10
  • Количество нейронов: 120
  • Количество выходов: 1

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

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

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

Шаг 1) Создайте поезд и протестируйте

Прежде всего, вы преобразуете серию в NumPy множество; тогда вы определяете windows (т. е. количество времени, в течение которого сеть будет учиться), количество входных и выходных данных и размер набора поездов, как показано в примере 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). Выходные данные функции должны иметь три измерения. Первые размеры равны количеству партий, вторые — размеру партии. windows и последний номер ввода.

Сложная часть — правильно выбрать точки данных. Для точек данных 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. archiтектура. Помните, у вас 120 рекуррентных нейронов.

Шаг 3) Построить модель

Чтобы создать модель, вам необходимо определить три части:

  1. Переменная с тензорами
  2. РНН
  3. Потери и оптимизация

Шаг 3.1) Переменные

Вам необходимо указать переменные X и y соответствующей формы. Этот шаг тривиален. Тензор имеет ту же размерность, что и объекты X_batches и y_batches.

Например, тензор X является заполнителем (см. руководство «Введение в Tensorflow чтобы освежить ваше представление об объявлении переменных) имеет три измерения:

  • Примечание: размер партии
  • n_windows: Длина 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 TensorFlow вам необходимо определить archiструктура сети. Как и раньше, вы используете объект BasicRNNCell и динамический_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 такая же, как и раньше; вы используете оптимизатор Адама для уменьшения потерь (т. е. MSE):

  • tf.train.AdamOptimizer(learning_rate=learning_rate)
  • оптимизатор.минимизировать(потеря)

Вот и все, вы можете собрать все воедино, и ваша модель готова к обучению.

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 вы можете построить график фактического значения ряда с прогнозируемым значением. Если ваша модель исправлена, прогнозируемые значения должны быть помещены поверх фактических значений.

Как видите, модели есть куда совершенствоваться. Вы можете изменить гиперпараметры, такие как windows, размер партии количества рекуррентных нейронов.

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

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

Итоги

Рекуррентная нейронная сеть — это надежная archiтектуры для работы с временными рядами или анализом текста. Выходом предыдущего состояния является обратная связь для сохранения памяти сети с течением времени или последовательности слов.

В TensorFlow вы можете использовать следующееwing коды для обучения рекуррентной нейронной сети 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})