这问题是继续到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):
有没有办法实现我想要的(避免极端尖峰,同时预测概率),或者这是一个给定的事实?
任何解释,建议将不胜感激.
更新
感谢@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'])
结果:
最后的正确标签(即概率)(即时间步长200),但其间非常尖锐且波动.现在让我们将它与rep_test_model的有状态预测进行比较(即每个时间步使用一个标签训练的那个):
preds = stateful_predict(rep_test_model,'Class 1'])
结果:
再次,正确的标签预测结束,但这次正如预期的那样更加平滑和单调的趋势.
请注意,这只是演示的一个示例,因此我在这里使用了一个非常简单的模型,只有一个LSTM层,而我根本没有尝试调整它.我想通过更好地调整模型(例如调整层数,每层中的单元数,使用的激活函数,优化器类型和参数等),您可能会得到更好的结果.