为啥要为结构化数据使用递归神经网络?
Posted
技术标签:
【中文标题】为啥要为结构化数据使用递归神经网络?【英文标题】:Why Bother With Recurrent Neural Networks For Structured Data?为什么要为结构化数据使用递归神经网络? 【发布时间】:2019-01-31 21:30:23 【问题描述】:我一直在 Keras 中开发前馈神经网络 (FNNs) 和递归神经网络 (RNNs),其结构化数据形状为 [instances, time, features]
,并且 FNNs 和 RNNs 的性能是相同的(除了 RNNs 需要更多计算时间)。
我还模拟了表格数据(下面的代码),我预计 RNN 的性能优于 FNN,因为系列中的下一个值取决于系列中的前一个值;但是,两种架构都可以正确预测。
对于 NLP 数据,我发现 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:
## 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 并希望它会鼓励某人,尽管不幸的是我不希望得到有用的答案:您的问题有点过于宽泛,并且固执己见的答案违反了这里的规则:***.com/help/on-topic(即可以解释某人-1)。有人说 RNN 只适用于序列,其他人说 CNN 更好,计算成本更低等等。事实是,找到一个好的方法仍然是一门艺术,而不是“管道”,所以没有保证的食谱,只是经验和类比。我希望有人能分享这些。堆栈交换可能是一个更好的地方 @fromkerasimportmichael 您的问题更关注机器学习的理论方面。请在Cross Validated 或Data Science SE 上提出此类问题。 交叉发布:datascience.stackexchange.com/q/37690/8560,***.com/q/52020748/781723。请do not post the same question on multiple sites。每个社区都应该诚实地回答问题,而不会浪费任何人的时间。 @today,我可以为未来提出要求吗?如果您要推荐其他网站,请让发布者知道不要交叉发布。您可以建议他们先在此处删除副本,然后再在其他地方发布。希望这将为所有人提供更好的体验。感谢您的收听! @D.W.我完全理解这一点,这都是我的错。感谢您提出这个问题并让我知道。当然,我以后会考虑这个。 【参考方案1】:在实践中,即使在 NLP 中,您也会看到 RNN 和 CNN 通常具有竞争力。 Here's 2017 年的评论论文更详细地展示了这一点。从理论上讲,RNN 可能可以更好地处理语言的全部复杂性和顺序性,但在实践中,更大的障碍通常是正确训练网络,而 RNN 很挑剔。
另一个可能有机会解决的问题是查看平衡括号问题(字符串中只有括号或括号以及其他干扰字符)之类的问题。这需要按顺序处理输入并跟踪某些状态,并且使用 LSTM 比 FFN 更容易学习。
更新: 一些看起来顺序的数据实际上可能不必按顺序处理。例如,即使您提供要相加的数字序列,因为加法是可交换的,FFN 与 RNN 一样好。这也可能适用于许多健康问题,其中主要信息不是连续的。假设每年测量一个病人的吸烟习惯。从行为的角度来看,轨迹很重要,但如果您要预测患者是否会患上肺癌,则预测将仅取决于患者吸烟的年数(对于 FFN,可能仅限于过去 10 年)。
因此,您想让玩具问题更复杂,并要求考虑数据的顺序。也许是某种模拟时间序列,您想预测数据中是否存在尖峰,但您并不关心绝对值,只关心尖峰的相对性质。
更新2
我修改了您的代码以展示 RNN 表现更好的情况。诀窍是使用更复杂的条件逻辑,在 LSTM 中比 FFN 更自然地建模。代码如下。对于 8 列,我们看到 FFN 在 1 分钟内训练并达到 6.3 的验证损失。 LSTM 的训练时间延长了 3 倍,但最终验证损失在 1.06 时降低了 6 倍。
随着列数的增加,LSTM 具有越来越大的优势,特别是如果我们在其中添加了更复杂的条件。对于 16 列,FFN 的验证损失为 19(您可以更清楚地将训练曲线视为模型无法立即拟合数据)。相比之下,LSTM 的训练时间要长 11 倍,但验证损失为 0.31,比 FFN 小 30 倍!您可以玩弄更大的矩阵,看看这种趋势会延伸多远。
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()
【讨论】:
谢谢,@emschorsch!您能否建议我如何将交互和依赖项添加到可能导致 RNN 优于 FNN 的模拟中?我的兴趣是非语言数据,特别是。对于实际的结构化数据(随着时间的推移具有许多特征的健康数据),RNN 需要比 FNN 长约 12 倍的时间来训练而不提高性能(这是我没想到的,因为已知过去的值会影响未来的值) .我认为最好从模拟数据开始解开为什么性能相当以及 RNN 是否值得计算时间。 当然!基本上,我已经尝试过序列的变化。变体 1:如果序列中值 A 在值 B 之前,则输出值与 B 在 A 之前时的输出值不同。 变体 2:函数本身依赖于序列中其他位置的二进制特征;有点像开关,我希望 RNN 记住开关值,但 FFN 不会。变体 3:当前特征值依赖于其大量的先验值。变体 4:一个特征会影响输出考虑的其他特征的时间步长。 感谢仿真代码!我能够对其进行修改以找到 LSTM 优于 FFN 的问题。我希望这就是你想要的。 是的,Y 正好是前 4 列的最大值(一般是前半部分)。我实际上是想让 Y 成为每一半的最大值中的较小者,但这似乎表明 LSTM 在任何一种情况下都更好。 再玩一些,如果 Y 是系列中两个连续数字的最大值,则 FNN 将正确建模。如果 Y 是三个或更多连续数字的最大值,则 RNN 将优于 FNN。以上是关于为啥要为结构化数据使用递归神经网络?的主要内容,如果未能解决你的问题,请参考以下文章