从数据框中获取神经网络LSTM的输入形状

41
我正在尝试使用Keras实现LSTM。 我知道在Keras中,LSTM需要一个3D张量作为输入,形状为(nb_samples,timesteps,input_dim)。然而,在我的情况下,每个输入只有一个样本T观察值,而不是多个样本,即(nb_samples = 1,timesteps = T,input_dim = N)。是否最好将每个输入拆分为长度为T / M的样本? 对于我来说,T大约有几百万个观察值,那么在这种情况下,每个样本应该有多长,即我应该选择M的值是多少?此外,我是否正确理解这个张量应该看起来像:
[[[a_11, a_12, ..., a_1M], [a_21, a_22, ..., a_2M], ..., [a_N1, a_N2, ..., a_NM]], 
 [[b_11, b_12, ..., b_1M], [b_21, b_22, ..., b_2M], ..., [b_N1, b_N2, ..., b_NM]], 
 ..., 
 [[x_11, x_12, ..., a_1M], [x_21, x_22, ..., x_2M], ..., [x_N1, x_N2, ..., x_NM]]]

其中M和N的定义如前所述,x对应于我从上述讨论中拆分所获得的最后一个样本?

最后,给定一个每列有T个观测值且有N个列的Pandas数据帧,每个输入都有一个列,那么我该如何创建这样一个输入来馈送给Keras?


请问您能否在问题中添加一个示例数据集?因为目前不清楚在您的模型中,哪种输入序列会产生什么样的目标输出。 - mertyildiran
你能解释一下一个观测的格式或数据类型是什么吗?它是一个单独的数值、一组数值还是其他什么? - Andrew
2个回答

58
以下是一个设置时间序列数据来训练LSTM的示例。模型输出是无意义的,因为我只是设置它来演示如何构建模型。
import pandas as pd
import numpy as np
# Get some time series data
df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/timeseries.csv")
df.head()

时间序列数据框:

Date      A       B       C      D      E      F      G
0   2008-03-18  24.68  164.93  114.73  26.27  19.21  28.87  63.44
1   2008-03-19  24.18  164.89  114.75  26.22  19.07  27.76  59.98
2   2008-03-20  23.99  164.63  115.04  25.78  19.01  27.04  59.61
3   2008-03-25  24.14  163.92  114.85  27.41  19.61  27.84  59.41
4   2008-03-26  24.44  163.45  114.84  26.86  19.53  28.02  60.09

您可以将输入放入向量中,然后使用Pandas的.cumsum()功能构建时间序列的序列。
# Put your inputs into a single list
df['single_input_vector'] = df[input_cols].apply(tuple, axis=1).apply(list)
# Double-encapsulate list so that you can sum it in the next step and keep time steps as separate elements
df['single_input_vector'] = df.single_input_vector.apply(lambda x: [list(x)])
# Use .cumsum() to include previous row vectors in the current row list of vectors
df['cumulative_input_vectors'] = df.single_input_vector.cumsum()

输出可以用类似的方式设置,但它将是一个单一的向量而不是一个序列:
# If your output is multi-dimensional, you need to capture those dimensions in one object
# If your output is a single dimension, this step may be unnecessary
df['output_vector'] = df[output_cols].apply(tuple, axis=1).apply(list)

为了将输入序列输入模型,它们需要具有相同的长度,因此您需要将它们填充到累积向量的最大长度:

# Pad your sequences so they are the same length
from keras.preprocessing.sequence import pad_sequences

max_sequence_length = df.cumulative_input_vectors.apply(len).max()
# Save it as a list   
padded_sequences = pad_sequences(df.cumulative_input_vectors.tolist(), max_sequence_length).tolist()
df['padded_input_vectors'] = pd.Series(padded_sequences).apply(np.asarray)

可以从数据框中提取训练数据并放入numpy数组中。请注意,从数据帧中得到的输入数据将不会生成一个3D数组,它生成的是一个数组的数组,这两者不同。

您可以使用hstack和reshape来构建一个3D输入数组。

# Extract your training data
X_train_init = np.asarray(df.padded_input_vectors)
# Use hstack to and reshape to make the inputs a 3d vector
X_train = np.hstack(X_train_init).reshape(len(df),max_sequence_length,len(input_cols))
y_train = np.hstack(np.asarray(df.output_vector)).reshape(len(df),len(output_cols))

为了证明它:

>>> print(X_train_init.shape)
(11,)
>>> print(X_train.shape)
(11, 11, 6)
>>> print(X_train == X_train_init)
False

一旦你有了训练数据,你就可以定义输入层和输出层的维度。

# Get your input dimensions
# Input length is the length for one input sequence (i.e. the number of rows for your sample)
# Input dim is the number of dimensions in one input vector (i.e. number of input columns)
input_length = X_train.shape[1]
input_dim = X_train.shape[2]
# Output dimensions is the shape of a single output vector
# In this case it's just 1, but it could be more
output_dim = len(y_train[0])

构建模型:

from keras.models import Model, Sequential
from keras.layers import LSTM, Dense

# Build the model
model = Sequential()

# I arbitrarily picked the output dimensions as 4
model.add(LSTM(4, input_dim = input_dim, input_length = input_length))
# The max output value is > 1 so relu is used as final activation.
model.add(Dense(output_dim, activation='relu'))

model.compile(loss='mean_squared_error',
              optimizer='sgd',
              metrics=['accuracy'])

最后,您可以训练模型并将训练日志保存为历史记录:
# Set batch_size to 7 to show that it doesn't have to be a factor or multiple of your sample size
history = model.fit(X_train, y_train,
              batch_size=7, nb_epoch=3,
              verbose = 1)

输出:

Epoch 1/3
11/11 [==============================] - 0s - loss: 3498.5756 - acc: 0.0000e+00     
Epoch 2/3
11/11 [==============================] - 0s - loss: 3498.5755 - acc: 0.0000e+00     
Epoch 3/3
11/11 [==============================] - 0s - loss: 3498.5757 - acc: 0.0000e+00 

就是这样。使用model.predict(X)来进行预测,其中X的格式(除了样本数量)与X_train相同。


1
这太棒了,正是我所需要的!非常感谢!有一件事情对我来说不是很清楚,那就是你在输出维度方面的意思。一方面,你说“在这种情况下,它只是1,但它可能会更多”,而另一方面,你又说“我随意选择了输出维度为4”。输出维度只是y中的列数吗(即不是观测值的数量,而是您尝试同时预测的变量的数量)?为什么你可以在这里选择4,而实际上只有1个呢? - dreamer
1
顺便说一句,真希望在奖励过期之前就把它给你了,你应该得到它。非常感谢你的回答!我为此苦苦挣扎。 - dreamer
太棒了!只有一个问题,我尝试使用不同的数据,一切都很好,但是当你执行以下操作时:`# 将其保存为列表 padded_sequences = pad_sequences(df.cumulative_input_vectors.tolist(), max_sequence_length).tolist()df['padded_input_vectors'] = pd.Series(padded_sequences).apply(np.asarray)`我的填充序列全是零,而累积值是正确的?我尝试了不同大小的max_sequence_length。 - lorenzori
2
太棒了!这是一个非常优雅的技巧。你能否使用“rolling”对象以类似的方式创建滚动窗口?我需要一个滚动窗口而不是累积总和。 - John Strong
1
输入序列必须具有相同的长度才能通过模型运行,因此您需要将它们填充到累积向量的最大长度。当输入数据具有例如128个浮点特征、4000个时间步长和1个样本(即(1, 4000, 128))时,这将使用大量内存。 - There
显示剩余12条评论

7

张量形状

你说得对,Keras 对于 LSTM 神经网络期望是一个三维张量,但我认为你忽略了一点,那就是 Keras 期望每个样本可以有多个维度

例如,在 Keras 中,我使用词向量表示自然语言处理的文档。文档中的每个单词由一个 n 维数字向量表示(所以如果n = 2,单词'cat'将被表示为类似[0.31, 0.65]的东西)。为了表示一个单独的文档,这些单词向量按顺序排列(例如'The cat sat.' = [[0.12, 0.99], [0.31, 0.65], [0.94, 0.04]])。在 Keras LSTM 中,一个文档将成为一个样本。

这类似于时间序列观测。一个文档就像是一个时间序列,一个单词就像是你的时间序列中的单个观测,但在你的情况下,你的观测表示仅有 n = 1 个维度。

因此,我认为你的张量应该是这样的:[[[a1], [a2], ... , [aT]], [[b1], [b2], ..., [bT]], ..., [[x1], [x2], ..., [xT]]],其中x对应于nb_samplestimesteps = T,而且input_dim = 1,因为你的每个观测只有一个数。

批量大小

批量大小应该设置为在不超过机器内存容量的情况下最大化吞吐量,参见这个Cross Validated post。据我所知,当训练模型和从中进行预测时,输入并不需要是批量大小的倍数。

示例

如果你正在寻找示例代码,在 Keras Github 上有许多使用 LSTM 和其他网络类型的示例,其具有序列化输入。


谢谢你的回答。我发现从Pandas数据框开始,实际上很难按照你描述的方式获取数据。至于批量大小,我相信Keras要求它是nb_samples的倍数,因为我曾经看到它抛出关于此的错误消息,这使得事情变得更加困难。我之前看过你提供的示例,但它们并不适用于时间序列和多个输入,这确实让事情变得更加困难(当你尝试时,你会发现的)。你介意给我一个例子吗?即使只是使用SKLearn波士顿数据集的基本示例也可以。 - dreamer
不,我认为我们之间有误解。我的输入是n列T个观测值(n个时间序列)。每个观测值都是一个数字,我以为这就是你在评论部分想问我的内容。为了明确起见:输出:y=(y1,y2,...,yT),输入x=(x11,x12,...,x1T; x11,x12,...,x1T;...;xn1,x1n2,...,xnT)(一个由n个长度为T的输入向量组成的矩阵)。 - dreamer
你是在尝试预测每个时间步长的输出吗?例如,如果你有一组观测数据,每小时一个,共9个小时,你想要在给定所有先前小时的数据作为输入的情况下,预测每小时的输出,还是只需要在9个小时后输出1个结果? - Andrew
好的,我添加了另一个答案,我认为它可以给你所需的东西。 - Andrew
如何为嵌套数组创建嵌入层并对每个句子进行LSTM分类?一个文档由多个句子组成,每个句子又由多个标记组成:doc = [[sent1tok1,sent1tok2],[sent2tok1,sent2tok2]],每个标记是一个具有50个维度的列表。如何在保持顺序的文档序列中对每个句子执行LSTM分类? - dter
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接