使用LSTM

时间:2018-12-30 12:43:39

标签: tensorflow keras time-series lstm sequence-to-sequence

我正在研究序列预测问题,在这一领域我经验不足,因此以下一些问题可能很幼稚。

仅供参考::我创建了一个针对CRF here

的后续问题

我遇到以下问题:

我想预测多个非独立变量的二进制序列。

输入:

我有一个包含以下变量的数据集:

  1. 时间戳
  2. A组和B组
  3. 在特定的时间戳下与每个组相对应的二进制信号

另外,假设以下内容:

  1. 我们可以从时间戳(例如一天中的小时)中提取其他属性,这些属性可以用作外部预测变量
  2. 我们认为A组和B组不是独立的,因此最好共同模拟他们的行为

binary_signal_group_Abinary_signal_group_B是我要使用(1)它们的过去行为和(2)从每个时间戳提取的其他信息来预测的2个非独立变量。

我到目前为止所做的事情:

# required libraries
import re
import numpy as np
import pandas as pd
from keras import Sequential
from keras.layers import LSTM

data_length = 18  # how long our data series will be
shift_length = 3  # how long of a sequence do we want

df = (pd.DataFrame  # create a sample dataframe
    .from_records(np.random.randint(2, size=[data_length, 3]))
    .rename(columns={0:'a', 1:'b', 2:'extra'}))
# NOTE: the 'extra' variable refers to a generic predictor such as for example 'is_weekend' indicator, it doesn't really matter what it is

# shift so that our sequences are in rows (assuming data is sorted already)
colrange = df.columns
shift_range = [_ for _ in range(-shift_length, shift_length+1) if _ != 0]
for c in colrange:
    for s in shift_range:
        if not (c == 'extra' and s > 0):
            charge = 'next' if s > 0 else 'last'  # 'next' variables is what we want to predict
            formatted_s = '{0:02d}'.format(abs(s))
            new_var = '{var}_{charge}_{n}'.format(var=c, charge=charge, n=formatted_s)
            df[new_var] = df[c].shift(s)

# drop unnecessary variables and trim missings generated by the shift operation
df.dropna(axis=0, inplace=True)
df.drop(colrange, axis=1, inplace=True)
df = df.astype(int)
df.head()  # check it out

#   a_last_03  a_last_02      ...        extra_last_02  extra_last_01
# 3          0          1      ...                    0              1
# 4          1          0      ...                    0              0
# 5          0          1      ...                    1              0
# 6          0          0      ...                    0              1
# 7          0          0      ...                    1              0
# [5 rows x 15 columns]

# separate predictors and response
response_df_dict = {}
for g in ['a','b']:
    response_df_dict[g] = df[[c for c in df.columns if 'next' in c and g in c]]

# reformat for LSTM
# the response for every row is a matrix with depth of 2 (the number of groups) and width = shift_length
# the predictors are of the same dimensions except the depth is not 2 but the number of predictors that we have

response_array_list = []
col_prefix = set([re.sub('_\d+$','',c) for c in df.columns if 'next' not in c])
for c in col_prefix:
    current_array = df[[z for z in df.columns if z.startswith(c)]].values
    response_array_list.append(current_array)

# reshape into samples (1), time stamps (2) and channels/variables (0)
response_array = np.array([response_df_dict['a'].values,response_df_dict['b'].values])
response_array = np.reshape(response_array, (response_array.shape[1], response_array.shape[2], response_array.shape[0]))
predictor_array = np.array(response_array_list)
predictor_array = np.reshape(predictor_array, (predictor_array.shape[1], predictor_array.shape[2], predictor_array.shape[0]))

# feed into the model
model = Sequential()
model.add(LSTM(8, input_shape=(predictor_array.shape[1],predictor_array.shape[2]), return_sequences=True))  # the number of neurons here can be anything
model.add(LSTM(2, return_sequences=True))  # should I use an activation function here? the number of neurons here must be equal to the # of groups we are predicting
model.summary()

# _________________________________________________________________
# Layer (type)                 Output Shape              Param #   
# =================================================================
# lstm_62 (LSTM)               (None, 3, 8)              384       
# _________________________________________________________________
# lstm_63 (LSTM)               (None, 3, 2)              88        
# =================================================================
# Total params: 472
# Trainable params: 472
# Non-trainable params: 0

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])  # is it valid to use crossentropy and accuracy as metric?
model.fit(predictor_array, response_array, epochs=10, batch_size=1)
model_preds = model.predict_classes(predictor_array)  # not gonna worry about train/test split here
model_preds.shape  # should return (12, 3, 2) or (# of records, # of timestamps, # of groups which are a and b)

# (12, 3)

model_preds
# array([[1, 0, 0],
#        [0, 0, 0],
#        [1, 0, 0],
#        [0, 0, 0],
#        [1, 0, 0],
#        [0, 0, 0],
#        [0, 0, 0],
#        [0, 0, 0],
#        [0, 0, 0],
#        [0, 0, 0],
#        [1, 0, 0],
#        [0, 0, 0]])

问题:

这里的主要问题是:我如何使它起作用,以便模型可以预测两组的下N个序列?

此外,我想问以下问题:

  1. A组和B组应该是互相关的,但是,尝试通过单个模型输出A和B序列是否有效,或者我是否适合2个单独的模型,一个预测A,另一个预测B但是都使用历史A和B数据作为输入?
  2. 虽然我模型的最后一层是形状为(None,3,2)的LSTM,但当我期望它为(12,2)时,预测输出的形状为(12,3)我在这里做错了,如果是,我该如何解决?
  3. 就输出LSTM层而言,在这里使用激活函数(例如Sigmoid)是否是一个好主意?为什么/为什么不呢?
  4. 使用分类类型损失(二进制交叉熵)和指标(准确性)来优化序列是否有效?
  5. 这里LSTM模型是最佳选择吗?有人认为CRF或某些HMM类型的模型在这里会更好吗?

非常感谢!

1 个答案:

答案 0 :(得分:3)

我将依次回答所有问题

  

我如何使它工作,以便模型可以预测下一个N   两组的顺序?

我建议对您的模型进行两次修改。
第一在最后一层使用了S型激活。

为什么?考虑二进制交叉熵损失函数(我从here借用了方程式)
eq
其中L是计算出的损耗,p是网络预测,y是目标值。

损失定义为eq。 如果p在此打开间隔范围之外,则损耗不确定。 keras is tanh中lstm层的默认激活及其输出范围是(-1,1)。这意味着模型的输出不适合二进制交叉熵损失。如果尝试训练模型,最终可能会损失nan

第二修改(是第一修改的一部分)可以在最后一层之前添加S型激活。为此,您有三个选择。

  1. 在输出和最后一个lstm层之间添加具有S型激活的密集层。
  2. 或将lstm层的激活更改为S型。
  3. 或在输出层之前添加具有S型激活的激活层。

即使所有情况都可以使用,我还是建议使用具有S字形激活的密集层,因为它几乎总是可以更好地工作。 现在,建议更改的模型将是

model = Sequential()
model.add(LSTM(8, input_shape=(predictor_array.shape[1],predictor_array.shape[2]), return_sequences=True))  
model.add(LSTM(2, return_sequences=True)) 
model.add(TimeDistributed(Dense(2, activation="sigmoid")))
model.summary()
  

...尝试一次输出A和B序列是否有效   型号还是我应该适合2个单独的型号...?

理想情况下,两种情况都可以起作用。但是最新的研究,例如this one表明,前一种情况(两个组都使用单一模型)往往表现更好。该方法通常称为Multi Task Learning。为简单起见,多任务学习背后的想法非常广泛,但可以认为是通过强迫模型学习多个任务共有的隐藏表示来增加归纳偏差。

  

...当我期望时,预测输出的形状为(12,3)   变成(12,2)-我在这里做错什么了吗...?

之所以会这样,是因为您使用的是predict_classes方法。与predict方法不同,predict_classes方法返回通道轴的最大索引(在您的情况下为第三个索引)。正如我上面解释的,如果在最后一层使用Sigmoid激活,并用predict替换了predict_classes,您将得到期望的结果。

  

就输出LSTM层而言,这是一个好主意吗   在这里使用激活功能,例如乙状结肠?为什么/为什么不呢?

我希望我已经在上面进行了解释。答案是肯定的。

  

使用分类类型损失(二进制交叉熵)是否有效   和用于优化序列的指标(准确性)?

由于您的目标是二进制信号(分布为Bernoulli distribution),因此可以使用二进制损耗和精度指标。 This answer gives进一步详细说明了为什么二进制交叉熵对这种类型的目标变量有效。

  

这里LSTM模型是最佳选择吗?有人认为CRF   还是某些HMM类型的模型在这里会更好?

这取决于可用数据和所选网络的复杂性。 CRF和HMM网络很简单,如果可用数据较少,则可以更好地工作。但是,如果可用数据集很大,那么LSTM几乎总是会胜过CRF和HMM。我的建议是,如果您有大量数据使用LSTM。但是,如果您有少量数据或正在寻找简单的模型,则可以使用CRF或HMM。