keras lstm 不正确 input_shape

keras lstm incorrect input_shape

我正在尝试使用 lstm 模型来预测天气(主要是为了了解 lstm 并使用 python)。

我有一个包含 500,000 行的数据集,每行代表一个日期,并且有 8 列是我的特征。

下面是我的模型。

 model = Sequential()      
 model.add(LSTM(50, input_shape=(30, 8), return_sequences=True))   
 model.add(Dropout(0.2))

 model.add(LSTM(100, return_sequences=True))
 model.add(Dropout(0.2))

 model.add(LSTM(50, return_sequences=False))
 model.add(Dropout(0.2))

 model.add(Dense(1))
 model.add(Activation('linear'))

 model.fit(
        X,
        y,
        batch_size=512,
        epochs=100,
        validation_split=0.05)

据我所知,对于输入参数,第一个参数是时间步长,所以我在这里说的是,我认为应该使用最后 30 个观察值来预测下一个值。我理解的8就是这样的特征,气压,温度等

所以我的 X 矩阵用下面的线转换成 3D 矩阵所以 X 现在是 500000, 8, 1 矩阵。

X = np.reshape(X, (X.shape[0], X.shape[1], 1))

当我 运行 模型时,我得到以下错误。

ValueError: Error when checking input: expected lstm_3_input to have shape (30, 8) but got array with shape (8, 1)

我做错了什么?

我认为您的输入形状不对。 NN 不理解您希望它采用 30 个点的切片来预测第 31 个点。您需要做的是将数据集切成长度为 30 的块(这意味着每个点将被复制 29 次)并对其进行训练,其形状为 (499969, 30, 8) ,假设最后点只进入 y。也不要在末尾添加虚拟维度,在 RGB 通道的转换层中需要它。

您的问题与数据准备有关。 查找有关 LSTM here.

数据准备的详细信息

LSTM 将一系列过去的观察结果映射为输出观察结果的输入。因此,必须将观察序列转换为多个样本考虑给定的单变量序列:

[10, 20, 30, 40, 50, 60, 70, 80, 90]

我们可以将序列分成多个 input/output 称为样本的模式,其中三个 n_steps 时间步用作输入,一个时间步用作一步预测的标签,即学习中。

X,              y
10, 20, 30      40
20, 30, 40      50
30, 40, 50      60
# ...

所以你想做的是在下面的split_sequence()函数中实现的:

# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
    X, y = list(), list()
    for i in range(len(sequence)):
        # find the end of this pattern
        end_ix = i + n_steps
        # check if we are beyond the sequence
        if end_ix > len(sequence)-1:
            break
        # gather input and output parts of the pattern
        seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
        X.append(seq_x)
        y.append(seq_y)
    return array(X), array(y)

回到我们的初始示例,发生以下情况:

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps = 3
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# summarize the data
for i in range(len(X)):
    print(X[i], y[i])

# [10 20 30] 40
# [20 30 40] 50
# [30 40 50] 60
# [40 50 60] 70
# [50 60 70] 80
# [60 70 80] 90

要点: 现在你的形状应该是你的 LSTM 模型所期望的,你应该能够根据你的需要调整你的数据形状。显然同样适用于多个输入特征行。

让我复制一个用于为 LSTM 准备数据的函数:

from itertools import islice

def slice_data_for_lstm(data, lookback):
    return np.array(list(zip(*[islice(np.array(data), i, None, 1) for i in range(lookback)])))

X_sliced = slice_data_for_lstm(X, 30)

在您的案例中,lookback 应为 30,并将创建 30 个 (8, 1) 特征堆栈。结果数据的形状为 (N, 30, 8, 1)。

我认为您可能只需要简单解释一下图层的工作原理。特别要注意的是,所有 Keras 层的行为都是这样的:

NAME(output_dim, input_shape = (...,input_dim))

例如,假设我有 15000 个 3 个长向量,我想将它们更改为 5 个长向量。然后像这样的事情会做到这一点:

import numpy as np, tensorflow as tf

X = np.random.random((15000,3))
Y = np.random.random((15000,5))

M = tf.keras.models.Sequential()
M.add(tf.keras.layers.Dense(5,input_shape=(3,)))

M.compile('sgd','mse')
M.fit(X,Y) # Take note that I provided complete working code here. Good practice. 
           # I even include the imports and random data to check that it works. 

同样,如果我的输入看起来像 (1000,10,5) 并且我 运行 通过 LSTM 之类的 LSTM(7);那么我应该(自动)知道我会得到类似 (...,7) 的输出。这 5 个长向量将变为 7 个长向量。规则来理解。最后一个维度始终是您要更改的向量,图层的第一个参数始终是要将其更改为的维度。

现在是学习 LSTM 的第二件事。他们使用时间轴(这不是最后一个轴,因为我们刚刚过去,它总是 "changing dimension axis")如果 return_sequences=False 则删除,如果 return_sequences= 则保留真的。一些示例:

LSTM(7) # (10000,100,5) -> (10000,7)
# Here the LSTM will loop through the 100, 5 long vectors (like a time series with memory),
# producing 7 long vectors. Only the last 7 long vector is kept. 
LSTM(7,return_sequences=True) # (10000,100,5) -> (10000,100,7)
# Same thing as the layer above, except we keep all the intermediate steps. 

您提供的图层如下所示:

LSTM(50,input_shape=(30,8),return_sequences=True) # (10000,30,8) -> (10000,30,50)

注意 30 是 LSTM 模型中使用的时间维度。 8和50是INPUT_DIM和OUTPUT_DIM,与时间轴无关。另一个常见的误解是,请注意 LSTM 希望您为每个样本提供它自己的完整过去和时间轴。也就是说,LSTM 不会将先前的样本点用于下一个样本点;每个样本都是独立的,并带有自己完整的过去数据。

让我们来看看您的模型。步骤1。你的模型在做什么,它期望什么样的数据?

from tensorflow.keras.layers import LSTM, Dropout, Activation
from tensorflow.keras.models import Sequential

model = Sequential()      
model.add(LSTM(50, input_shape=(30, 8), return_sequences=True))   
model.add(Dropout(0.2))
model.add(LSTM(100, return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(50, return_sequences=False))
model.add(Dropout(0.2))
model.add(Dense(1))
model.add(Activation('linear'))
model.compile('sgd','mse')

print(model.input_shape)
model.summary() # Lets see what your model is doing. 

所以,现在我清楚地看到你的模型做了: (10000,30,8) -> (10000,30,50) -> (10000,30,100) -> (10000,50) -> (10000,1)

你预料到了吗?您是否看到这些是中间步骤的尺寸?现在我知道您的模型期望的输入和输出是什么,我可以轻松地验证您的模型是否训练并处理此类数据。

from tensorflow.keras.layers import LSTM, Dropout, Activation
from tensorflow.keras.models import Sequential
import numpy as np

X = np.random.random((10000,30,8))
Y = np.random.random((10000,1))

model = Sequential()      
model.add(LSTM(50, input_shape=(30, 8), return_sequences=True))   
model.add(Dropout(0.2))
model.add(LSTM(100, return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(50, return_sequences=False))
model.add(Dropout(0.2))
model.add(Dense(1))
model.add(Activation('linear'))
model.compile('sgd','mse')

model.fit(X,Y)

您是否注意到您的模型需要像 (...,30,8) 这样的输入?您知道您的模型期望输出数据看起来像 (...,1) 吗?了解您的模型想要什么,也意味着您现在可以更改模型以适应您感兴趣的数据。如果您希望数据 运行 在 8 个参数(如时间轴)上,那么您的输入维度需要反映这一点.将 30 更改为 8,并将 8 更改为 1。如果您这样做,还要注意您的第一层正在将每个 1 长向量(单个数字)扩展为 50 长向量。这听起来像您希望模型执行的操作吗?也许您的 LSTM 应该是 LSTM(2) 或 LSTM(5) 而不是 50...等。您可能会花费接下来的 1000 小时来尝试找到适用于您正在使用的数据的正确参数。

也许您不想将 FEATURE space 作为 TIME SPACE,也许尝试将您的数据重复成大小为 10 的批次,其中每个样本都有自己的历史、维度说(10000,10,8)。然后 LSTM(50) 将使用您的 8 长特征 space 并将其更改为 50 长特征 space,同时超过 10 的时间轴。也许您只想保留最后一个 return_sequences=假。