LSTM - 对部分序列进行预测

Posted

技术标签:

【中文标题】LSTM - 对部分序列进行预测【英文标题】:LSTM - Making predictions on partial sequence 【发布时间】:2019-04-21 22:31:29 【问题描述】:

这个问题在我问过的previous question 中继续。

我已经训练了一个 LSTM 模型来预测一个包含 100 个样本的批次的二进制类(1 或 0),每个样本有 3 个特征,即:数据的形状是 (m, 100, 3),其中 m 是批次数。

数据:

[
    [[1,2,3],[1,2,3]... 100 sampels],
    [[1,2,3],[1,2,3]... 100 sampels],
    ... avaialble batches in the training data
]

目标:

[
   [1]
   [0]
   ...
]

型号代码:

def build_model(num_samples, num_features, is_training):
    model = Sequential()
    opt = optimizers.Adam(lr=0.0005, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0001)

    batch_size = None if is_training else 1
    stateful = False if is_training else True
    first_lstm = LSTM(32, batch_input_shape=(batch_size, num_samples, num_features), return_sequences=True,
                      activation='tanh', stateful=stateful)

    model.add(first_lstm)
    model.add(LeakyReLU())
    model.add(Dropout(0.2))
    model.add(LSTM(16, return_sequences=True, activation='tanh', stateful=stateful))
    model.add(Dropout(0.2))
    model.add(LeakyReLU())
    model.add(LSTM(8, return_sequences=False, activation='tanh', stateful=stateful))
    model.add(LeakyReLU())
    model.add(Dense(1, activation='sigmoid'))

    if is_training:
        model.compile(loss='binary_crossentropy', optimizer=opt,
                      metrics=['accuracy', keras_metrics.precision(), keras_metrics.recall(), f1])
    return model

对于训练阶段,模型是有状态的。在预测我使用有状态模型时,迭代数据并输出每个样本的概率:

for index, row in data.iterrows():
    if index % 100 == 0:
        predicting_model.reset_states()
    vals = np.array([[row[['a', 'b', 'c']].values]])
    prob = predicting_model.predict_on_batch(vals)

当查看批次结束时的概率时,这正是我在预测整个批次时得到的值(不是一个一个)。但是,我预计当新样本到达时,概率将始终朝着正确的方向发展。实际发生的是概率输出可能会在任意样本上飙升到错误的类别(见下文)。


预测时间内的 100 个样本批次中的两个样本(标签 = 1):

和标签 = 0:

有没有办法实现我想要的(在预测概率时避免极端峰值),或者这是一个既定的事实?

任何解释,建议将不胜感激。


更新 感谢@today 的建议,我尝试在最后一个 LSTM 层上使用return_sequence=True 为每个输入时间步使用隐藏状态输出训练网络。

所以现在标签看起来像这样(形状(100,100)):

[[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
  1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
  1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
...]

模型总结:

Layer (type)                 Output Shape              Param #   
=================================================================
lstm_1 (LSTM)                (None, 100, 32)           4608      
_________________________________________________________________
leaky_re_lu_1 (LeakyReLU)    (None, 100, 32)           0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 100, 32)           0         
_________________________________________________________________
lstm_2 (LSTM)                (None, 100, 16)           3136      
_________________________________________________________________
dropout_2 (Dropout)          (None, 100, 16)           0         
_________________________________________________________________
leaky_re_lu_2 (LeakyReLU)    (None, 100, 16)           0         
_________________________________________________________________
lstm_3 (LSTM)                (None, 100, 8)            800       
_________________________________________________________________
leaky_re_lu_3 (LeakyReLU)    (None, 100, 8)            0         
_________________________________________________________________
dense_1 (Dense)              (None, 100, 1)            9         
=================================================================
Total params: 8,553
Trainable params: 8,553
Non-trainable params: 0
_________________________________________________________________

但是,我得到了一个例外:

ValueError: Error when checking target: expected dense_1 to have 3 dimensions, but got array with shape (75, 100)

我需要解决什么问题?

【问题讨论】:

训练准确率是多少?由于您使用的是LeakyReLU 层,您是否尝试过为 LSTM 层设置 activation='linear' 请不要使用“samples”代替“timesteps”。它们是不同的东西,这会导致混乱。在您的示例中,每个样本(即序列)的形状为(100, 3),这意味着每个样本由 100 个时间步长组成,其中每个时间步长是长度为 3 的特征向量。此外,“数据的形状为 (m, 100, 3) ,其中m 是批次数”有点错误:m 是样本数(或者可能是一批中的样本数),而不是批次数。每批可能包含一个或多个样本。 我不知道关于概率不应该波动或尖峰并且它们应该随着我们处理更多时间步而单调增加或减少的说法是对还是错。但是您必须考虑到 1) 模型已经在长度为 100 的序列上进行了训练,2) 在查看了所有 100 个时间步之后,它已经被训练为输出正确的标签, 3) 它不会在训练期间为中间时间步生成任何输出。因此,我认为我们不应该期望预测阶段的中间输出具有特定的行为;而最后一个很重要。 我想我同意“今天”。我认为这不是问题,但您可以通过创建包含所有 100 个步骤的目标来防止这种情况。而不是y = [[0],[1],[0],...],使用y = [[0,0,0...],[1,1,1...],[0,0,0...x100], ....]——为此你需要return_sequences=True直到结束。 哦,这已经是下面的答案了 :) -- 点赞 【参考方案1】:

注意:这只是一个想法,可能是错误的。如果您愿意,请尝试一下,如果您有任何反馈,我将不胜感激。


有没有办法实现我想要的(避免极端尖峰,而 预测概率),或者这是一个给定的事实?

您可以这样做实验:将最后一个 LSTM 层的return_sequences 参数设置为True,并将每个样本的标签复制到每个样本的长度。例如,如果一个样本的长度为 100,其标签为 0,则为该样本创建一个由 100 个零组成的新标签(您可以使用像 np.repeat 这样的 numpy 函数轻松完成此操作)。然后重新训练你的新模型,然后在新样本上进行测试。我不确定这一点,但我希望这次会有更多单调递增/递减的概率图。


更新:你提到的错误是由于标签应该是一个 3D 数组(查看模型摘要中最后一层的输出形状)。使用np.expand_dims 在末尾添加另一个尺寸为 1 的轴。重复标签的正确方法如下所示,假设y_train 的形状为(num_samples,)

rep_y_train = np.repeat(y_train, num_reps).reshape(-1, num_reps, 1)

IMDB数据集实验:

实际上,我使用带有一个 LSTM 层的简单模型在 IMDB 数据集上尝试了上面建议的实验。有一次,我每个样本只使用一个标签(如@Shlomi 的原始方法),另一次我复制标签以每个样本的每个时间步长一个标签 strong>(正如我上面建议的)。如果您想自己尝试,这里是代码:

from keras.layers import *
from keras.models import Sequential, Model
from keras.datasets import imdb
from keras.preprocessing.sequence import pad_sequences
import numpy as np

vocab_size = 10000
max_len = 200
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=vocab_size)
X_train = pad_sequences(x_train, maxlen=max_len)

def create_model(return_seq=False, stateful=False):
    batch_size = 1 if stateful else None
    model = Sequential()
    model.add(Embedding(vocab_size, 128, batch_input_shape=(batch_size, None)))
    model.add(CuDNNLSTM(64, return_sequences=return_seq, stateful=stateful))
    model.add(Dense(1, activation='sigmoid'))

    model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
    return model

# train model with one label per sample
train_model = create_model()
train_model.fit(X_train, y_train, epochs=10, batch_size=128, validation_split=0.3)

# replicate the labels
y_train_rep = np.repeat(y_train, max_len).reshape(-1, max_len, 1)

# train model with one label per timestep
rep_train_model = create_model(True)
rep_train_model.fit(X_train, y_train_rep, epochs=10, batch_size=128, validation_split=0.3)

然后我们可以创建训练模型的有状态副本并在一些测试数据上运行它们以比较它们的结果:

# replica of `train_model` with the same weights
test_model = create_model(False, True)
test_model.set_weights(train_model.get_weights())
test_model.reset_states()

# replica of `rep_train_model` with the same weights
rep_test_model = create_model(True, True)
rep_test_model.set_weights(rep_train_model.get_weights())
rep_test_model.reset_states()

def stateful_predict(model, samples):
    preds = []
    for s in samples:
        model.reset_states()
        ps = []
        for ts in s:
            p = model.predict(np.array([[ts]]))
            ps.append(p[0,0])
        preds.append(list(ps))
    return preds

X_test = pad_sequences(x_test, maxlen=max_len)

其实X_test的第一个样本标签为0(即属于负类),X_test的第二个样本标签为1(即属于正类)。因此,让我们首先看看test_model(即每个样本使用一个标签训练的那个)对这两个样本的状态预测会是什么样子:

import matplotlib.pyplot as plt

preds = stateful_predict(test_model, X_test[0:2])

plt.plot(preds[0])
plt.plot(preds[1])
plt.legend(['Class 0', 'Class 1'])

结果:

最后(即时间步长 200)正确的标签(即概率),但在两者之间非常尖锐和波动。现在让我们将其与rep_test_model 的状态预测(即每个时间步使用一个标签训练的预测)进行比较:

preds = stateful_predict(rep_test_model, X_test[0:2])

plt.plot(preds[0])
plt.plot(preds[1])
plt.legend(['Class 0', 'Class 1'])

结果:

再次,最后正确的标签预测,但这次的趋势更加平滑和单调,正如预期的那样。

请注意,这只是一个演示示例,因此我在这里使用了一个非常简单的模型,只有一个 LSTM 层,我根本没有尝试对其进行调整。我想通过更好地调整模型(例如调整层数、每层中的单元数、使用的激活函数、优化器类型和参数等),您可能会得到更好的结果。

【讨论】:

你的意思是我会有 100 个概率作为输出,还是只是最后一个密集层的输入? @ShlomiSchwartz 是的,仅在训练时间。但是不要改变最后一层的单元数。我错了,修改了我的答案。但是,在预测时间中,您将给出一个时间步长,而每个时间步长只会得到一个概率(而不是 100)。 @ShlomiSchwartz 从本质上讲,在训练时间内,每个长度为 L 的子序列都有一个概率。 @ShlomiSchwartz 查看我的答案。我已经用解决您遇到的错误的方法以及对 IMDB 数据集的简单实验对其进行了更新。 @ShlomiSchwartz 欢迎您!哦,我希望我是!但实际上我离最好的非常非常非常远 :)

以上是关于LSTM - 对部分序列进行预测的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 LSTM 对图像进行时间序列预测?

如何让 Keras LSTM 在多变量设置中对多个时间序列进行预测?

用于罕见事件时间序列预测的LSTM模型体系结构

在Python中使用LSTM和PyTorch进行时间序列预测

86使用Tensorflow实现,LSTM的时间序列预测,预测正弦函数

如何对销售额进行预测?