python – LSTM – 对部分序列进行预测

这问题是继续到previous question我问过.

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

数据:

[
    [[1,2,3],[1,3]... 100 sampels],[[1,... 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,stateful=stateful))
    model.add(Dropout(0.2))
    model.add(LeakyReLU())
    model.add(LSTM(8,return_sequences=False,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):

enter image description here

和Label = 0:

enter image description here

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

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

更新
感谢@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,32)           4608      
_________________________________________________________________
leaky_re_lu_1 (LeakyReLU)    (None,32)           0         
_________________________________________________________________
dropout_1 (Dropout)          (None,32)           0         
_________________________________________________________________
lstm_2 (LSTM)                (None,16)           3136      
_________________________________________________________________
dropout_2 (Dropout)          (None,16)           0         
_________________________________________________________________
leaky_re_lu_2 (LeakyReLU)    (None,16)           0         
_________________________________________________________________
lstm_3 (LSTM)                (None,8)            800       
_________________________________________________________________
leaky_re_lu_3 (LeakyReLU)    (None,8)            0         
_________________________________________________________________
dense_1 (Dense)              (None,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)

我需要修理什么?

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

Is there a way to achieve what I want (avoid extreme spikes while
predicting probability),or is that a given fact?

您可以执行此实验:将最后一个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的原始方法),另一次我复制标签,每个时间步长一个标签(如上所述).如果您想自己尝试,请输入以下代码:

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

结果:

<code>test_model</code> stateful predictions

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

preds = stateful_predict(rep_test_model,'Class 1'])

结果:

<code>rep_test_model</code> stateful predictions

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

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

相关文章

Python中的函数(二) 在上一篇文章中提到了Python中函数的定...
Python中的字符串 可能大多数人在学习C语言的时候,最先接触...
Python 面向对象编程(一) 虽然Python是解释性语言,但是它...
Python面向对象编程(二) 在前面一篇文章中谈到了类的基本定...
Python中的函数(一) 接触过C语言的朋友对函数这个词肯定非...
在windows下如何快速搭建web.py开发框架 用Python进行web开发...