Зачем беспокоиться о повторяющихся нейронных сетях для структурированных данных?

Я развиваю фидерные нейронные сети (FNN) и рекуррентные нейронные сети (RNN) в Keras со структурированными данными формы [instances, time, features], а производительность FNN и RNN была одинаковой (за исключением того, что RNN требуют больше время вычисления).

Я также смоделировал данные (код ниже), где я ожидал, что RNN превысит FNN, потому что следующее значение в серии зависит от предыдущего значения в серии; однако обе архитектуры предсказывают правильно.

С данными NLP я видел, что RNN превосходят FNN, но не со структурированными данными. Как правило, когда можно ожидать, что RNN превысит FNN со структурированными данными? В частности, может ли кто-нибудь опубликовать код моделирования со структурированными данными, демонстрирующими RNN, превосходящий FNN?

Спасибо! Если мой код моделирования не идеален для моего вопроса, пожалуйста, приспособите его или разделите более идеальный! Если RNN (как правило) лучше, чем FNN для данных структурированных временных рядов, его не должно быть слишком сложно продемонстрировать?

from keras import models
from keras import layers

from keras.layers import Dense, LSTM

import numpy as np
import matplotlib.pyplot as plt

Две функции были смоделированы в течение 10 временных шагов, где значение второй функции зависит от значения обеих функций на предыдущем временном шаге.

## Simulate data.

np.random.seed(20180825)

X = np.random.randint(50, 70, size = (11000, 1)) / 100

X = np.concatenate((X, X), axis = 1)

for i in range(10):

    X_next = np.random.randint(50, 70, size = (11000, 1)) / 100

    X = np.concatenate((X, X_next, (0.50 * X[:, -1].reshape(len(X), 1)) 
        + (0.50 * X[:, -2].reshape(len(X), 1))), axis = 1)

print(X.shape)

## Training and validation data.

split = 10000

Y_train = X[:split, -1:].reshape(split, 1)
Y_valid = X[split:, -1:].reshape(len(X) - split, 1)
X_train = X[:split, :-2]
X_valid = X[split:, :-2]

print(X_train.shape)
print(Y_train.shape)
print(X_valid.shape)
print(Y_valid.shape)

БНС:

## FNN model.

# Define model.

network_fnn = models.Sequential()
network_fnn.add(layers.Dense(64, activation = 'relu', input_shape = (X_train.shape[1],)))
network_fnn.add(Dense(1, activation = None))

# Compile model.

network_fnn.compile(optimizer = 'adam', loss = 'mean_squared_error')

# Fit model.

history_fnn = network_fnn.fit(X_train, Y_train, epochs = 10, batch_size = 32, verbose = False,
    validation_data = (X_valid, Y_valid))

plt.scatter(Y_train, network_fnn.predict(X_train), alpha = 0.1)
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.show()

plt.scatter(Y_valid, network_fnn.predict(X_valid), alpha = 0.1)
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.show()

LSTM:

## LSTM model.

X_lstm_train = X_train.reshape(X_train.shape[0], X_train.shape[1] // 2, 2)
X_lstm_valid = X_valid.reshape(X_valid.shape[0], X_valid.shape[1] // 2, 2)

# Define model.

network_lstm = models.Sequential()
network_lstm.add(layers.LSTM(64, activation = 'relu', input_shape = (X_lstm_train.shape[1], 2)))
network_lstm.add(layers.Dense(1, activation = None))

# Compile model.

network_lstm.compile(optimizer = 'adam', loss = 'mean_squared_error')

# Fit model.

history_lstm = network_lstm.fit(X_lstm_train, Y_train, epochs = 10, batch_size = 32, verbose = False,
    validation_data = (X_lstm_valid, Y_valid))

plt.scatter(Y_train, network_lstm.predict(X_lstm_train), alpha = 0.1)
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.show()

plt.scatter(Y_valid, network_lstm.predict(X_lstm_valid), alpha = 0.1)
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.show()

Ответ 1

На практике даже в НЛП вы видите, что RNN и CNN часто конкурируют. Вот обзорный обзор 2017 года, который показывает это более подробно. Теоретически это может быть так, что RNN могут справиться с полной сложностью и последовательным характером языка лучше, но на практике более серьезное препятствие, как правило, правильно обучает сеть, а RNN - изящно.

Другая проблема, которая может иметь возможность работать, - это рассмотреть проблему, такую как проблема с сбалансированными скобками (либо с помощью скобок или скобок, либо с помощью других символов-искажений). Это требует последовательной обработки входных данных и отслеживания состояния и может быть легче изучить с помощью LSTM, а затем FFN.

Обновление. Некоторые данные, которые выглядят последовательно, на самом деле не могут обрабатываться последовательно. Например, даже если вы предоставляете последовательность чисел для добавления, поскольку добавление является коммутативным, FFN будет делать так же, как и RNN. Это также может быть справедливо для многих проблем со здоровьем, где доминирующая информация не имеет последовательного характера. Предположим, что каждый год измеряются привычки к курению пациентов. С поведенческой точки зрения траектория важна, но если вы прогнозируете, будет ли у пациента развиваться рак легких, в прогнозировании будет доминировать только количество лет, в течение которых пациент курил (возможно, ограниченный последними 10 годами для FFN).

Поэтому вы хотите сделать игрушку более сложной и потребовать учета порядка данных. Может быть, какой-то симулированный временной ряд, где вы хотите предсказать, был ли всплеск данных, но вы не заботитесь об абсолютных значениях относительно относительной природы всплеска.

Update2

Я изменил ваш код, чтобы показать случай, когда RNN работают лучше. Хитрость заключалась в использовании более сложной условной логики, которая более естественно моделируется в LSTM, чем FFN. Код ниже. Для 8 столбцов мы видим, что FFN тренируется за 1 минуту и достигает потери на проверку 6.3. LSTM занимает 3 раза дольше, чтобы тренироваться, но окончательная потеря проверки в 6 раз ниже на 1,06.

Поскольку мы увеличиваем количество столбцов, LSTM имеет большее и большее преимущество, особенно если мы добавили более сложные условия. Для 16 столбцов потери проверки FFNs равны 19 (и вы можете более четко видеть кривую обучения, поскольку модель не является способный мгновенно соответствовать данным). В сравнении LSTM занимает 11 раз дольше, чтобы тренироваться, но имеет потерю валидации 0,31, в 30 раз меньше, чем FFN! Вы можете поиграть с еще более крупными матрицами, чтобы увидеть, как далеко эта тенденция будет расширяться.

from keras import models
from keras import layers

from keras.layers import Dense, LSTM

import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import time

matplotlib.use('Agg')

np.random.seed(20180908)

rows = 20500
cols = 10

# Randomly generate Z
Z = 100*np.random.uniform(0.05, 1.0, size = (rows, cols))

larger = np.max(Z[:, :cols/2], axis=1).reshape((rows, 1))
larger2 = np.max(Z[:, cols/2:], axis=1).reshape((rows, 1))
smaller = np.min((larger, larger2), axis=0)
# Z is now the max of the first half of the array.
Z = np.append(Z, larger, axis=1)
# Z is now the min of the max of each half of the array.
# Z = np.append(Z, smaller, axis=1)

# Combine and shuffle.

#Z = np.concatenate((Z_sum, Z_avg), axis = 0)

np.random.shuffle(Z)

## Training and validation data.

split = 10000

X_train = Z[:split, :-1]
X_valid = Z[split:, :-1]
Y_train = Z[:split, -1:].reshape(split, 1)
Y_valid = Z[split:, -1:].reshape(rows - split, 1)

print(X_train.shape)
print(Y_train.shape)
print(X_valid.shape)
print(Y_valid.shape)

print("Now setting up the FNN")

## FNN model.

tick = time.time()

# Define model.

network_fnn = models.Sequential()
network_fnn.add(layers.Dense(32, activation = 'relu', input_shape = (X_train.shape[1],)))
network_fnn.add(Dense(1, activation = None))

# Compile model.

network_fnn.compile(optimizer = 'adam', loss = 'mean_squared_error')

# Fit model.

history_fnn = network_fnn.fit(X_train, Y_train, epochs = 500, batch_size = 128, verbose = False,
    validation_data = (X_valid, Y_valid))

tock = time.time()

print()
print(str('%.2f' % ((tock - tick) / 60)) + ' minutes.')

print("Now evaluating the FNN")

loss_fnn = history_fnn.history['loss']
val_loss_fnn = history_fnn.history['val_loss']
epochs_fnn = range(1, len(loss_fnn) + 1)
print("train loss: ", loss_fnn[-1])
print("validation loss: ", val_loss_fnn[-1])

plt.plot(epochs_fnn, loss_fnn, 'black', label = 'Training Loss')
plt.plot(epochs_fnn, val_loss_fnn, 'red', label = 'Validation Loss')
plt.title('FNN: Training and Validation Loss')
plt.legend()
plt.show()

plt.scatter(Y_train, network_fnn.predict(X_train), alpha = 0.1)
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.title('training points')
plt.show()

plt.scatter(Y_valid, network_fnn.predict(X_valid), alpha = 0.1)
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.title('valid points')
plt.show()

print("LSTM")

## LSTM model.

X_lstm_train = X_train.reshape(X_train.shape[0], X_train.shape[1], 1)
X_lstm_valid = X_valid.reshape(X_valid.shape[0], X_valid.shape[1], 1)

tick = time.time()

# Define model.

network_lstm = models.Sequential()
network_lstm.add(layers.LSTM(32, activation = 'relu', input_shape = (X_lstm_train.shape[1], 1)))
network_lstm.add(layers.Dense(1, activation = None))

# Compile model.

network_lstm.compile(optimizer = 'adam', loss = 'mean_squared_error')

# Fit model.

history_lstm = network_lstm.fit(X_lstm_train, Y_train, epochs = 500, batch_size = 128, verbose = False,
    validation_data = (X_lstm_valid, Y_valid))

tock = time.time()

print()
print(str('%.2f' % ((tock - tick) / 60)) + ' minutes.')

print("now eval")

loss_lstm = history_lstm.history['loss']
val_loss_lstm = history_lstm.history['val_loss']
epochs_lstm = range(1, len(loss_lstm) + 1)
print("train loss: ", loss_lstm[-1])
print("validation loss: ", val_loss_lstm[-1])

plt.plot(epochs_lstm, loss_lstm, 'black', label = 'Training Loss')
plt.plot(epochs_lstm, val_loss_lstm, 'red', label = 'Validation Loss')
plt.title('LSTM: Training and Validation Loss')
plt.legend()
plt.show()

plt.scatter(Y_train, network_lstm.predict(X_lstm_train), alpha = 0.1)
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.title('training')
plt.show()

plt.scatter(Y_valid, network_lstm.predict(X_lstm_valid), alpha = 0.1)
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.title("validation")
plt.show()