序列数据中出现缺失观测值是很常见的。
数据可能已损坏或不可用,但也可能是您的数据按定义具有可变长度序列。那些时间步较少的序列可能被认为具有缺失值。
在本教程中,您将学习如何使用 Keras 深度学习库在 Python 中处理带有缺失值的序列预测问题。
完成本教程后,您将了解:
- 如何删除包含缺失时间步的行。
- 如何标记缺失的时间步并强制网络学习其含义。
- 如何遮盖缺失的时间步并将其从模型计算中排除。
通过我的新书《深度学习时间序列预测》**启动您的项目**,其中包括**分步教程**和所有示例的 **Python 源代码**文件。
让我们开始吧。

线性代数简明介绍
图片由 Steve Corey 提供,保留部分权利。
概述
本节分为 3 个部分;它们是
- 回显序列预测问题
- 处理缺失的序列数据
- 学习带有缺失序列值
环境
本教程假定您已安装 Python SciPy 环境。您可以使用 Python 2 或 3。
本教程假定您已安装 Keras (v2.0.4+),并使用 TensorFlow (v1.1.0+) 或 Theano (v0.9+) 作为后端。
本教程还假定您已安装 scikit-learn、Pandas、NumPy 和 Matplotlib。
如果您需要帮助设置 Python 环境,请参阅此帖子
回显序列预测问题
回显问题是一个人为设计的序列预测问题,其目标是记住并预测固定先验时间步(称为滞后观测值)处的观测值。
例如,最简单的情况是预测前一个时间步的观测值,即将其回显。例如:
1 2 3 4 |
时间 1:输入 45 时间 2:输入 23,输出 45 时间 3:输入 73,输出 23 ... |
问题是,我们如何处理时间步 1?
我们可以在 Python 中实现回显序列预测问题。
这包括两个步骤:生成随机序列和将随机序列转换为监督学习问题。
生成随机序列
我们可以使用 random 模块中的 random() 函数生成 0 到 1 之间的随机值序列。
我们可以将其放入一个名为 generate_sequence() 的函数中,该函数将为所需的时间步数生成随机浮点值序列。
该函数如下所示。
1 2 3 |
# 生成随机值序列 def generate_sequence(n_timesteps): return [random() for _ in range(n_timesteps)] |
时间序列深度学习需要帮助吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
作为监督学习框架
在使用神经网络时,序列必须被框架为监督学习问题。
这意味着序列需要被分为输入和输出对。
该问题可以被框架为基于当前和先前时间步的函数进行预测。
或者更正式地:
1 |
y(t) = f(X(t), X(t-1)) |
其中 y(t) 是当前时间步的所需输出,f() 是我们试图用神经网络近似的函数,X(t) 和 X(t-1) 是当前和先前时间步的观测值。
输出可以等于先前的观测值,例如 y(t) = X(t-1),但也可以是 y(t) = X(t)。我们在这个问题上训练的模型不知道真实的公式,必须学习这种关系。
这模拟了真实的序列预测问题,我们在其中将模型指定为一组固定序列时间步的函数,但我们不知道从过去观测值到所需输出值的实际函数关系。
我们可以在 python 中将这种回显问题框架为监督学习问题。
Pandas shift() 函数可用于创建序列的移位版本,该版本可用于表示先前时间步的观测值。这可以与原始序列连接,以提供 X(t-1) 和 X(t) 输入值。
1 2 |
df = DataFrame(sequence) df = concat([df.shift(1), df], axis=1) |
然后我们可以将 Pandas DataFrame 中的值作为输入序列 (X),并使用第一列作为输出序列 (y)。
1 2 |
# 指定输入和输出数据 X, y = values, values[:, 0] |
将所有这些放在一起,我们可以定义一个函数,该函数将时间步数作为参数,并返回用于序列学习的 X,y 数据,称为 generate_data()。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 为 lstm 生成数据 def generate_data(n_timesteps): # 生成序列 sequence = generate_sequence(n_timesteps) sequence = array(sequence) # 创建滞后 df = DataFrame(sequence) df = concat([df.shift(1), df], axis=1) values = df.values # 指定输入和输出数据 X, y = values, values[:, 0] return X, y |
序列问题演示
我们可以将 generate_sequence() 和 generate_data() 代码组合成一个工作示例。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
from random import random from numpy import array 从 pandas 导入 concat from pandas import DataFrame # 生成随机值序列 def generate_sequence(n_timesteps): return [random() for _ in range(n_timesteps)] # 为 lstm 生成数据 def generate_data(n_timesteps): # 生成序列 sequence = generate_sequence(n_timesteps) sequence = array(sequence) # 创建滞后 df = DataFrame(sequence) df = concat([df.shift(1), df], axis=1) values = df.values # 指定输入和输出数据 X, y = values, values[:, 0] 返回 X, y # 生成序列 n_timesteps = 10 X, y = generate_data(n_timesteps) # 打印序列 for i in range(n_timesteps): print(X[i], '=>', y[i]) |
运行此示例会生成一个序列,将其转换为监督表示,并打印每个 X,y 对。
1 2 3 4 5 6 7 8 9 10 |
[ nan 0.18961404] => nan [ 0.18961404 0.25956078] => 0.189614044109 [ 0.25956078 0.30322084] => 0.259560776929 [ 0.30322084 0.72581287] => 0.303220844801 [ 0.72581287 0.02916655] => 0.725812865047 [ 0.02916655 0.88711086] => 0.0291665472554 [ 0.88711086 0.34267107] => 0.88711086298 [ 0.34267107 0.3844453 ] => 0.342671068373 [ 0.3844453 0.89759621] => 0.384445299683 [ 0.89759621 0.95278264] => 0.897596208691 |
我们可以看到第一行有 NaN 值。
这是因为序列中的第一个值没有先前的观测值。我们必须用一些东西来填充那个空间。
但我们不能用 NaN 输入拟合模型。
处理缺失的序列数据
处理缺失序列数据有两种主要方法。
它们是删除带有缺失数据的行,并用另一个值填充缺失的时间步。
有关处理缺失数据的更通用方法,请参阅帖子:
处理缺失序列数据的最佳方法将取决于您的问题和您选择的网络配置。我建议探索每种方法,看看哪种效果最好。
移除缺失序列数据
在回显前一个时间步的观测值的情况下,数据的第一行不包含任何有用信息。
也就是说,在上面的例子中,给定输入
1 |
[ nan 0.18961404] |
和输出
1 |
nan |
没有什么有意义的东西可以学习或预测。
最好的情况是删除此行。
我们可以在将序列表述为监督学习问题时通过删除所有包含 NaN 值的行来实现这一点。具体来说,可以在将数据拆分为 X 和 y 组件之前调用 dropna() 函数。
完整的示例列在下面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
from random import random from numpy import array 从 pandas 导入 concat from pandas import DataFrame # 生成随机值序列 def generate_sequence(n_timesteps): return [random() for _ in range(n_timesteps)] # 为 lstm 生成数据 def generate_data(n_timesteps): # 生成序列 sequence = generate_sequence(n_timesteps) sequence = array(sequence) # 创建滞后 df = DataFrame(sequence) df = concat([df.shift(1), df], axis=1) # 删除带有缺失值的行 df.dropna(inplace=True) values = df.values # 指定输入和输出数据 X, y = values, values[:, 0] 返回 X, y # 生成序列 n_timesteps = 10 X, y = generate_data(n_timesteps) # 打印序列 for i in range(len(X)): print(X[i], '=>', y[i]) |
运行示例会得到 9 对 X,y,而不是 10 对,因为第一行已被删除。
1 2 3 4 5 6 7 8 9 |
[ 0.60619475 0.24408238] => 0.606194746194 [ 0.24408238 0.44873712] => 0.244082383195 [ 0.44873712 0.92939547] => 0.448737123424 [ 0.92939547 0.74481645] => 0.929395472523 [ 0.74481645 0.69891311] => 0.744816453809 [ 0.69891311 0.8420314 ] => 0.69891310578 [ 0.8420314 0.58627624] => 0.842031399202 [ 0.58627624 0.48125348] => 0.586276240292 [ 0.48125348 0.75057094] => 0.481253484036 |
替换缺失序列数据
当回显问题配置为回显当前时间步的观测值时,第一行将包含有意义的信息。
例如,我们可以将 y 的定义从 values[:, 0] 更改为 values[:, 1],然后重新运行演示以生成此问题的一个样本,如下所示:
1 2 3 4 5 6 7 8 9 10 |
[ nan 0.50513289] => 0.505132894821 [ 0.50513289 0.22879667] => 0.228796667421 [ 0.22879667 0.66980995] => 0.669809946421 [ 0.66980995 0.10445146] => 0.104451463568 [ 0.10445146 0.70642423] => 0.70642422679 [ 0.70642423 0.10198636] => 0.101986362328 [ 0.10198636 0.49648033] => 0.496480332278 [ 0.49648033 0.06201137] => 0.0620113728356 [ 0.06201137 0.40653087] => 0.406530870804 [ 0.40653087 0.63299264] => 0.632992635565 |
我们可以看到第一行给出的输入是
1 |
[ nan 0.50513289] |
和输出
1 |
0.505132894821 |
这可以从输入中学习。
问题是,我们仍然有一个 NaN 值需要处理。
我们不必删除包含 NaN 值的行,而是可以将所有 NaN 值替换为输入中不会自然出现的特定值,例如 -1。为此,我们可以使用 fillna() Pandas 函数。
完整的示例列在下面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
from random import random from numpy import array 从 pandas 导入 concat from pandas import DataFrame # 生成随机值序列 def generate_sequence(n_timesteps): return [random() for _ in range(n_timesteps)] # 为 lstm 生成数据 def generate_data(n_timesteps): # 生成序列 sequence = generate_sequence(n_timesteps) sequence = array(sequence) # 创建滞后 df = DataFrame(sequence) df = concat([df.shift(1), df], axis=1) # 将缺失值替换为 -1 df.fillna(-1, inplace=True) values = df.values # 指定输入和输出数据 X, y = values, values[:, 1] 返回 X, y # 生成序列 n_timesteps = 10 X, y = generate_data(n_timesteps) # 打印序列 for i in range(len(X)): print(X[i], '=>', y[i]) |
运行该示例,我们可以看到第一行第一列的 NaN 值被替换为 -1 值。
1 2 3 4 5 6 7 8 9 10 |
[-1. 0.94641256] => 0.946412559807 [ 0.94641256 0.11958645] => 0.119586451733 [ 0.11958645 0.50597771] => 0.505977714614 [ 0.50597771 0.92496641] => 0.924966407025 [ 0.92496641 0.15011979] => 0.150119790096 [ 0.15011979 0.69387197] => 0.693871974256 [ 0.69387197 0.9194518 ] => 0.919451802966 [ 0.9194518 0.78690337] => 0.786903370269 [ 0.78690337 0.17017999] => 0.170179993691 [ 0.17017999 0.82286572] => 0.822865722747 |
学习带有缺失序列值
在学习带有标记缺失值的序列预测问题时,有两种主要选择。
该问题可以按原样建模,我们可以鼓励模型学习特定值表示“缺失”。或者,特殊缺失值可以被遮盖并明确地从预测计算中排除。
我们将针对人为设计的“回显当前观测值”问题(带有两个输入)来看看这两种情况。
学习缺失值
我们可以为预测问题开发一个 LSTM。
输入由 2 个时间步和 1 个特征定义。第一个隐藏层中定义了一个包含 5 个记忆单元的小型 LSTM,以及一个具有线性激活函数的单一输出层。
网络将使用均方误差损失函数和默认配置的高效 ADAM 优化算法进行拟合。
1 2 3 4 5 |
# 定义模型 model = Sequential() model.add(LSTM(5, input_shape=(2, 1))) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') |
为了确保模型学习到问题的通用解决方案,即始终将输入作为输出返回(y(t) == X(t)),我们将每隔一个 epoch 生成一个新的随机序列。网络将拟合 500 个 epoch,并且每个序列中的每个样本(batch_size=1)之后都会进行更新。
1 2 3 4 |
# 拟合模型 for i in range(500): X, y = generate_data(n_timesteps) model.fit(X, y, epochs=1, batch_size=1, verbose=2) |
一旦拟合,将生成另一个随机序列,并将模型的预测与预期值进行比较。这将提供一个模型技能的具体概念。
1 2 3 4 5 |
# 在新数据上评估模型 X, y = generate_data(n_timesteps) yhat = model.predict(X) for i in range(len(X)): print('Expected', y[i,0], 'Predicted', yhat[i,0]) |
将所有这些结合起来,完整的代码清单如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
from random import random from numpy import array 从 pandas 导入 concat from pandas import DataFrame from keras.models import Sequential 从 keras.layers 导入 LSTM from keras.layers import Dense # 生成随机值序列 def generate_sequence(n_timesteps): return [random() for _ in range(n_timesteps)] # 为 lstm 生成数据 def generate_data(n_timesteps): # 生成序列 sequence = generate_sequence(n_timesteps) sequence = array(sequence) # 创建滞后 df = DataFrame(sequence) df = concat([df.shift(1), df], axis=1) # 将缺失值替换为 -1 df.fillna(-1, inplace=True) values = df.values # 指定输入和输出数据 X, y = values, values[:, 1] # 重塑 X = X.reshape(len(X), 2, 1) y = y.reshape(len(y), 1) 返回 X, y n_timesteps = 10 # 定义模型 model = Sequential() model.add(LSTM(5, input_shape=(2, 1))) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') # 拟合模型 for i in range(500): X, y = generate_data(n_timesteps) model.fit(X, y, epochs=1, batch_size=1, verbose=2) # 在新数据上评估模型 X, y = generate_data(n_timesteps) yhat = model.predict(X) for i in range(len(X)): print('Expected', y[i,0], 'Predicted', yhat[i,0]) |
运行示例会打印每个 epoch 的损失,并在运行结束时将模型的预测与一个序列的预期输出进行比较。
**注意**:由于算法或评估过程的随机性,或者数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
查看最终预测,我们可以看到网络学习了问题并预测了“足够好”的输出,即使存在缺失值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
... 纪元 1/1 0s - loss: 1.5992e-04 纪元 1/1 0s - loss: 1.3409e-04 纪元 1/1 0s - loss: 1.1581e-04 纪元 1/1 0s - loss: 2.6176e-04 纪元 1/1 0s - loss: 8.8303e-05 预期 0.390784174343 预测 0.394238 预期 0.688580469278 预测 0.690463 预期 0.347155799665 预测 0.329972 预期 0.345075533266 预测 0.333037 预期 0.456591840482 预测 0.450145 预期 0.842125610156 预测 0.839923 预期 0.354087132135 预测 0.342418 预期 0.601406667694 预测 0.60228 预期 0.368929815424 预测 0.351224 预期 0.716420996314 预测 0.719275 |
您可以进一步尝试此示例,并将给定序列中 50% 的 t-1 观测值标记为 -1,看看这会如何影响模型随时间的技能。
遮蔽缺失值
已标记的缺失输入值可以从网络中的所有计算中遮蔽。
我们可以通过使用 Masking 层作为网络的第一层来实现这一点。
在定义层时,我们可以指定要遮蔽输入中的哪个值。如果时间步的所有特征都包含遮蔽值,则整个时间步将从计算中排除。
这在完全排除该行和强制网络学习已标记缺失值的影响之间提供了一个中间地带。
因为 Masking 层是网络中的第一层,所以它必须指定输入的预期形状,如下所示:
1 |
model.add(Masking(mask_value=-1, input_shape=(2, 1))) |
我们可以将所有这些结合起来并重新运行示例。完整的代码清单如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
from random import random from numpy import array 从 pandas 导入 concat from pandas import DataFrame from keras.models import Sequential 从 keras.layers 导入 LSTM from keras.layers import Dense from keras.layers import Masking # 生成随机值序列 def generate_sequence(n_timesteps): return [random() for _ in range(n_timesteps)] # 为 lstm 生成数据 def generate_data(n_timesteps): # 生成序列 sequence = generate_sequence(n_timesteps) sequence = array(sequence) # 创建滞后 df = DataFrame(sequence) df = concat([df.shift(1), df], axis=1) # 将缺失值替换为 -1 df.fillna(-1, inplace=True) values = df.values # 指定输入和输出数据 X, y = values, values[:, 1] # 重塑 X = X.reshape(len(X), 2, 1) y = y.reshape(len(y), 1) 返回 X, y n_timesteps = 10 # 定义模型 model = Sequential() model.add(Masking(mask_value=-1, input_shape=(2, 1))) model.add(LSTM(5)) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') # 拟合模型 for i in range(500): X, y = generate_data(n_timesteps) model.fit(X, y, epochs=1, batch_size=1, verbose=2) # 在新数据上评估模型 X, y = generate_data(n_timesteps) yhat = model.predict(X) for i in range(len(X)): print('Expected', y[i,0], 'Predicted', yhat[i,0]) |
同样,每个 epoch 都打印损失,并将预测与最终序列的预期值进行比较。
**注意**:由于算法或评估过程的随机性,或者数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
同样,预测在小数点后几位似乎足够准确。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
... 纪元 1/1 0s - loss: 1.0252e-04 纪元 1/1 0s - loss: 6.5545e-05 纪元 1/1 0s - loss: 3.0831e-05 纪元 1/1 0s - loss: 1.8548e-04 纪元 1/1 0s - loss: 7.4286e-05 预期 0.550889403319 预测 0.538004 预期 0.24252028132 预测 0.243288 预期 0.718869927574 预测 0.724669 预期 0.355185878917 预测 0.347479 预期 0.240554707978 预测 0.242719 预期 0.769765554707 预测 0.776608 预期 0.660782450416 预测 0.656321 预期 0.692962017672 预测 0.694851 预期 0.0485233839401 预测 0.0722362 预期 0.35192019185 预测 0.339201 |
选择哪种方法?
这些一次性的实验不足以评估在简单的回声序列预测问题上哪种方法效果最好。
它们确实提供了您可以在自己的问题上使用的模板。
我鼓励您探索处理序列预测问题中缺失值的 3 种不同方法。它们是:
- 删除包含缺失值的行。
- 标记并学习缺失值。
- 遮蔽并学习不带缺失值。
在您的序列预测问题上尝试每种方法,并加倍努力使用效果最好的方法。
总结
如果您的序列长度可变,则序列预测问题中出现缺失值是很常见的。
在本教程中,您学习了如何使用 Python 和 Keras 处理序列预测问题中的缺失数据。
具体来说,你学到了:
- 如何删除包含缺失值的行。
- 如何标记缺失值并强制模型学习其含义。
- 如何遮蔽缺失值以将其从模型计算中排除。
您对处理缺失序列数据有任何疑问吗?
在评论中提出您的问题,我将尽力回答。
太棒了!
很高兴能帮到你,Nader。
我真的很喜欢你的书,它们对我帮助很大,我正在使用其中的四本:《时间序列预测》、《机器学习》、《深度学习》和《从零开始的机器学习》。尤其是《从零开始的机器学习》对我的 Python 技能帮助很大。我希望《从零开始的深度学习》(不使用 Tensor Flow 和 Keras)能很快出版。非常感谢。
感谢您的支持,James。
如果我想归一化输入数据,我应该先替换缺失数据还是先归一化输入数据?
是的,我会在缩放之前进行插补。
嗨 Jason
感谢您的教程。
我不确定我是否正确理解了您对 Adam 的回答:您是否建议我们先用一个值(比如“-1”)替换 NaN,然后进行缩放?
1) 如果是这样,数据将把 -1 值考虑在内进行缩放:即:如果我的数据范围是 10 到 50,但包含 NaN,那么 10 将不再是最小值;-1 将是。
2) 此外,如果我在缩放之前替换,我将需要将“mask_value=-1”从 -1 更改为 -1 现在已缩放到的值。这是正确的吗?
先缩放然后替换 NaN 不是更好吗?
如果您用一个值(例如平均值)替换缺失值,请先这样做,然后进行缩放。
如果您想将它们遮盖掉,请先进行缩放,方法是忽略缺失值。
在“替换缺失序列数据”中,我们应该将 y 的定义从 values[:, 0] 更改为 values[:, 1],对吗?
是的,根据帖子
如果我修改 generate_sequence
def generate_sequence(n_timesteps)
return random.randint(34,156,n_timesteps)
结果和真实值会有很大的误差
为什么?
因为神经网络无法预测伪随机序列。
如果我每 5 分钟从互联网上收集值,但有时服务器出现问题,我错过了一些值。解决方案是否可以添加另一个特征作为输入,作为捕获每组特征时的时间戳?LSTM 会比特征通常每 300n 出现一次但有时数字不同更有意义吗?
你可以添加零值以达到所需的长度,并在模型中使用掩码来忽略它们。
我缺少明智地插补单变量或多变量时间序列缺失数据的方法及其 Python 实现。我正在考虑插值、自相关或可能其他复杂的无监督学习方法。您在某个地方写过关于这方面的内容吗?
也许可以尝试几种不同的方法,看看哪种最适合您的具体问题。
另请参阅此帖子
https://machinelearning.org.cn/handle-missing-data-python/
我希望将来能介绍更复杂的方法。
只是注意到 Keras 中的无状态 Sequential 模型 (RNN) 可以用未指定的批处理大小构建。这允许使用不同的批处理大小进行训练/验证/预测
https://stackoverflow.com/questions/43702481/why-does-keras-lstm-batch-size-used-for-prediction-have-to-be-the-same-as-fittin
谢谢,我这里也给出了一个例子
https://machinelearning.org.cn/develop-encoder-decoder-model-sequence-sequence-prediction-keras/
遮罩是否适用于输出序列中的缺失值?
不是以同样的方式。你可以使用“我不知道”输出,例如预测 0 或其他。在 NLP 问题中非常有用。
嗨,Jason,
谢谢你。
您觉得在训练过程中,在 Sklearn Pipeline 中使用迭代插补器或 KNN 模型插补时间序列怎么样?
缺失值很可能已经从未来的数据中插补。
谢谢
这实际上取决于时间序列。
通常简单的持久化更好。
“在定义图层时,我们可以指定要遮盖输入中的哪个值。如果时间步的所有特征都包含遮盖值,那么整个时间步将从计算中排除。”
这是否意味着所有特征都会被排除?还是只排除带有 NaN 值的特征?
我们告诉遮罩层要忽略什么,例如,默认情况下是 0.0。
谢谢,有用的帖子!尽管在您的示例中,与总数据量相比,间隙相对较小。我面临一个问题,我有一个包含 450 个向量的数据集,它们之间有 250 个连续缺失向量的间隙。在这种情况下,您有什么推荐的模板、示例或其他博客文章可以指出吗?
也许可以尝试使用零填充和遮罩层?
也许可以尝试忽略这个空白?
也许可以尝试进行插补?
也许可以尝试以某种方式分割样本,使得缺失的空间是一个可以跳过的样本?
告诉我进展如何。
对于“替换缺失序列数据”部分,我们应该将 y 的定义从 values[:, 0] 更改为 values[:, 0],然后重新运行演示以生成此问题的一个样本,如下所示:
这应该修改如下:
例如,我们可以将 y 的定义从 values[:, 0] 更改为 values[:, -1],然后重新运行演示以生成此问题的一个样本,如下所示:
对吗?
差不多,我想是从完整示例中的 values[:,1]。
谢谢,已修正。
很棒的教程。我有一个问题。我正在使用 keras 进行序列标注工作(Bi-LSTM + CRF 模型),序列长度不同。我使用遮罩层来遮罩 0 值,并使用 sequence.pad_sequences() 来用 0 填充训练数据。我成功地训练了模型,但是,当我对测试数据进行预测时遇到了问题。
我用 0 填充测试实例,例如,23 -> 100(最大长度)。理论上,模型会忽略 77 个“0”并只预测 23 个时间步。但我得到了 100 个预测结果,而且后 77 个结果不是 0 或空。我感到困惑。您以前遇到过这种情况吗?遮罩层是否在使用中?还是我只需要忽略后 77 个结果。谢谢。
我不确定我是否理解,您是在谈论遮盖输入还是使用填充进行预测,还是两者兼而有之?
两者都有。如果您在训练模型时遮蔽输入,则在模型进行预测时,您必须以相同的方式遮蔽测试数据。这是我的部分模型代码
model = Sequential()
model.add(Masking(mask_value=0, input_shape=(seq_length, features_length)))
model.add(Bidirectional(LSTM(lstm_units_num, return_sequences=True)))
model.add(Dropout(dropout_rate))
model.add(Bidirectional(LSTM(lstm_units_num, return_sequences=True)))
model.add(Dropout(dropout_rate))
model.add(TimeDistributed(Dense(num_class, activation=”softmax”)))
crf_layer = CRF(num_class)
model.add(crf_layer)。
当我使用模型进行预测时,我得到了 100(seq_length)个预测结果(都不是 0 或 null),但是,这 100 个输入时间步中的 77 个被遮蔽了,它们不应该被预测为非 0 结果。所以我很困惑。我不确定预测结果是否正确……
预测结果全为 0 可能表明模型尚未学习到问题。也许可以尝试更长时间的训练或不同的模型配置?
抱歉,您说的不在我的问题范围内……我们举个例子,在我的实验中,maxlen 是 100,现在模型已经成功训练(带遮罩层)。假设有一个测试实例(长度为 23),模型想要预测它。首先,我使用填充将此测试用例用 0 填充,然后测试实例的长度变为 100(后 77 个值都为 0)。然后模型将得到长度为 100 的预测结果。模型遮罩了 0 值,因此理论上,这 100 个预测结果中的后 77 个应该都为 0,因为它们不应该被预测(被遮罩)。然而,在我的实验中,后 77 个预测结果不为 0,它们似乎也被预测了,并且遮罩没有效果。您以前遇到过这个问题吗?或者在您的实验中,后“77”个预测结果都为 0 吗?
这是一个关于相同问题的链接 (https://groups.google.com/forum/#!topic/keras-users/M7BVggL7cG0)。谢谢。
我们不能遮盖预测,只能填充它们。
也许您的模型需要进一步调整。
我在谷歌上搜索了一下,发现有人和我遇到了同样的问题。(https://groups.google.com/forum/#!topic/keras-users/M7BVggL7cG0)。希望它能帮助您理解我的问题。非常感谢。
在新数据上评估模型
X, y = generate_data(n_timesteps)
所以,在这种情况下,您应该知道 10 个数据来评估模型,但是如果您知道结果,为什么还要预测?
这个模型如何预测新数据
为了向初学者演示如何操作。
嗨 Jason,在 generate_data() 函数中,有一行代码如下所示:
X, y = values, values[:, 1]
这似乎将我们想要预测的值 'y' 作为 X 中的第二列包含在内。这难道不会让模型很容易预测 'y' 吗(它只需要从 X 的第二列中提取值)?这一行难道不应该像这样吗?
X, y = values[:, 0], values[:, 1]
当然,我们需要更改 Keras 模型中第一层的 input_shape()。
是的,现在,也许这篇帖子会更清楚
https://machinelearning.org.cn/convert-time-series-supervised-learning-problem-python/
以及关于步进验证的这一篇
https://machinelearning.org.cn/backtest-machine-learning-models-time-series-forecasting/
这是一篇 CMU 的论文,它使用了一种改进版 LSTM,称为“相位 LSTM”,并对数据进行了各种操作。
https://www.cs.cmu.edu/~epxing/Class/10708-17/project-reports/project8.pdf
其中一项数据操作涉及构造一个遮罩(公式 1),然后将此遮罩作为新列添加到预测值“X”的输入矩阵中。这是 CMU 的“PLSTM-Masking”模型(参见论文中的表 1)。这有效地使矩阵 X 的列数翻倍。(这可能与您之前在此线程中的评论非常相似:https://machinelearning.org.cn/handle-missing-timesteps-sequence-prediction-problems-python/#comment-424701 回应 Darius 的问题。它也可能与 White 之前的问题相关:https://machinelearning.org.cn/handle-missing-timesteps-sequence-prediction-problems-python/#comment-452953)
在上面教程的“遮蔽缺失值”部分中,添加了一个遮蔽层。如果这与 CMU 论文中的 PLSTM-Masking 模型本质上做相同的事情,我本以为遮蔽层的输出会有两倍的列数,即 4。但是当我运行
model.summary()
来检查输出的形状时,我看到遮蔽层的输出仍然只有 2 列:“(None, 2, 1)”。我是否可以推断遮蔽层没有实现与 CMU 论文中的 PLSTM-Masking 模型相同的遮蔽方法?我认为答案是“是”,遮蔽层只是“跳过”输入矩阵 X 中所有值都具有遮蔽值“-1”的行。
即使跳过了一行缺失值,LSTM 是否“知道”它仍然必须使其记忆衰减一个时间步?(时间上更靠后的值应该比最近的值权重更小。)
我相信被遮蔽的输入会从每个 LSTM 单元的所有前向/后向计算中排除。
您可以查阅 Keras API/代码以确认。
嘿 Jason – 非常感谢这篇文章。你提到遮罩介于完全移除缺失值/行和插补/学习缺失值之间。你能解释一下为什么遮罩与仅仅从时间序列中移除值有什么不同吗?根据我目前的理解是:如果输入张量中的所有值都等于遮罩值,那么该时间步将被跳过,并且状态将被传输(如果是有状态的)。这与从时间序列中排除该行有什么不同?
是的,被遮盖的值被跳过。
但是如果该行包含稀疏值,则该行不会被跳过。
嗨,Jason,
假设我有一个数据集,其中有 4 个输入特征,如果只有其中 2 个输入特征存在 NaN,我不想跳过这些行,也不想用超出范围的值替换 NaN。对于介于两者之间的数据点,可以进行向前填充。如果缺失值位于数据集的开头,如何在不进行向后填充的情况下处理它们。
你填充或跳过它们。除此之外,我想你已经没有其他选择了。
谢谢,Jason
我一直在想同样的事情,我还有一个关于上面例子的问题,如果我用一个超出范围的值替换 NaN,模型(假设是 LSTM)会识别出 4 个输入特征中有 2 个是无意义的,然后使用另外 2 个输入特征吗?
它可能会,如果你用一个特殊值标记它们,或者将它们标记为缺失并使用一个遮罩层。尝试一下看看。
Brownlee 大师您好,我尝试在多头 MLP 中实现遮罩,在展平输入后,我一直收到错误“Layer dense does not support masking but was passed an input_mask…” 任何关于如何解决这个问题的想法?提前感谢
是的,我想只有 RNN 支持遮罩。
您好,
请您澄清一件事
您将时间步定义为 2,
但在代码中,您正在生成 10 个时间步的序列以拟合模型。这些东西有什么区别?
代码示例有 10 个观测值,分为两个滞后观测值作为输入和单个观测值作为输出的子序列。
更多详情请见此
https://machinelearning.org.cn/time-series-forecasting-supervised-learning/
嗨,Jason,
非常感谢。总是那么棒!!
关于时间序列和 lstm 的一个问题
我使用时间序列(工厂传感器每天的物理值随时间变化)并不得不处理缺失数据。这不是“真正”的缺失数据,我们没有值是因为工厂停工……例如进行清洁。我有很长一段时间没有值(几天)。对您来说,处理这种情况的最佳解决方案是什么?
谢谢你
Christophe
我在这里有一些建议
https://machinelearning.org.cn/faq/single-faq/how-do-i-handle-discontiguous-time-series-data
嗨,Jason,
我有一个时间序列数据,但时间序列中有几次中断,而且中断时间相当长,这意味着通过中断前的数据来预测数据是不合理的。
如果我想使用这些数据训练一个 LSTM。我该如何处理这些中断?
这是我在这里回答的一个常见问题
https://machinelearning.org.cn/faq/single-faq/how-do-i-handle-discontiguous-time-series-data
谢谢!
不客气。
我的一个输入特征大约有 50% 的缺失数据,这种情况下该怎么办?
尝试一系列不同的插补方案,看看哪种最适合您的特定数据集?
使用均值或中位数可能是一个不错的开始?
你好,
感谢您的有益资料。
我正在尝试使用 ANN 来完成我的时间序列,其中存在缺失数据,并使用另一个完整的数据集。我意识到首先我需要删除缺失值及其在两个数据集中的对应值,然后用这些数据集训练模型。我的问题是,我如何将这些对应值作为新数据集输入到我训练好的模型中以预测缺失值?
好问题。
时间序列中处理缺失数据的方法有很多。
您可以创建真实数据中的假缺失值,并让您的模型学习填补空白。我预计这些将是使用完整或部分输入进行的一步预测。数据可以被“删除”,例如随机假缺失。
这有帮助吗?
感谢您的回复。
实际上我的问题是,我们如何将新数据作为输入输入到训练好的模型中以预测输出。
好问题,我相信这会有所帮助
https://machinelearning.org.cn/how-to-make-classification-and-regression-predictions-for-deep-learning-models-in-keras/
还有这个。
https://machinelearning.org.cn/make-predictions-long-short-term-memory-models-keras/
你好!
遮罩需要传播吗?
https://tensorflowcn.cn/guide/keras/masking_and_padding
谢谢!
Keras 会为您处理。并非所有层都支持它,但我相信 LSTM 支持。
嗨,Jason,
我已将网络流量数据输入 Keras RNN,但该模型似乎在周末错误预测数据。🙁
这是屏幕截图链接
https://github.com/RSwarnkar/temporary/blob/master/RNN-Mis-Predict-Weekends.jpg
我应该删除周末的数据,这样 RNN 就不会错误学习它吗?
此致,Rajesh
也许可以使用受控实验来发现哪种方法最适合您的特定数据集。
嗨,Jason,
感谢所有出色的工作。您的博客和时事通讯总是受到欢迎。
我还没有阅读所有回复,但我发现了一个缺失的命令?如果我正确遵循了遮罩方法,我们应该在
“model.add(Masking(mask_value=-1, input_shape=(2, 1)))”
在“model.add(Dense(1))”命令之后
以及在“model.compile(loss='mean_squared_error', optimizer='adam')”之前插入吗?
代码这样运行良好。
祝好,
Loïc
该死,在 L.35 漏掉了……!!
对此我很抱歉。命令的位置重要吗?
非常感谢您的工作!
是的,这有助于您复制粘贴代码示例
https://machinelearning.org.cn/faq/single-faq/how-do-i-copy-code-from-a-tutorial
谢谢。
复制整个代码示例 – 它会直接执行。
嗨 Jason
我有每小时记录的时间序列数据,所以我需要预测数据集中的缺失值并进行填充。请问哪种技术或算法最适合?您可以给我一些建议吗?
也许可以尝试一系列方法,看看哪种方法能够生成一个数据集,当用它来训练模型时,在新数据上取得最佳性能。
嗨,Jason,除了神经网络,还有其他方法可以填充时间序列缺失值吗?比如使用其他机器学习模型(算法),如回归等。
请分享任何链接或帖子。
是的,平均值、中位数、持久性、机器学习模型等等。
也许可以测试一系列方法,看看哪种效果最好。
你能告诉我哪些是机器学习模型吗?如果你有相关的帖子,请分享给我,非常感谢Jason,我喜欢你的文章。
例如:
https://scikit-learn.cn/stable/modules/impute.html
嗨,Jason,
我正在处理csv文件中的一些缺失数据。我想使用LSTM方法中的掩码缺失值。在您的代码中,您生成了随机数据,我如何将我自己的csv文件数据放入其中以预测缺失值?
这将向您展示如何加载CSV文件。
https://machinelearning.org.cn/load-machine-learning-data-python/
嗨,Jason,
我有一个多元时间序列问题,但我缺失的是目标值而不是输入值。对此有什么想法吗?
对于缺失的预测值进行掩码处理,只使用那些对应于可用值的预测值,这样如何?通过这种方式,我们可以在每个小批量中计算损失并进行反向传播,尽管这个损失是基于小批量中少量值计算的。但是,由于可用的观测值代表了整体函数,即它捕捉了函数的大部分方面,我们能得到合理的预测。
您认为这种方法合理吗?
我将非常感谢您的反馈。
一些缺失数据可以进行插补。所有数据都缺失意味着您想要进行预测——您需要在一个有可用预测数据的数据集上拟合模型。
也许可以尝试您的方法,并与其他方法进行比较。
嗨,Ather
我也有同样的问题,你尝试过你的方法吗?它有效吗?
我首先使用 fillna() 将 Nan 替换为一个值,然后使用 fit_transform() 将所有训练数据转换为稀疏矩阵,我可以在 fit_transform() 之后进行替换值的掩码操作吗?因为我想获得准确性,所以我需要将 Nan 替换为一个值,但我想知道哪种方法更好:1-仅将 Nan 替换为一个值,2-替换后,使用掩码使模型学习缺失数据?
大多数算法和转换都要求您在使用之前删除含有NaN的行或对NaN值进行插补。
即使输入数据中没有NA值,LSTM时间序列预测模型是否也能返回NA值作为预测?如果可以,为什么会发生这种情况?
也许不是NA,但你可以训练它预测“我不知道”。
嗨,Jason,
我的编码器-解码器LSTM模型在多步时间序列预测中预测NA,尽管历史时间序列中没有NA值。这只发生在少数情况下,其他情况下它给出了正确的值(我正在使用多核并行训练多个时间序列)。我想知道为什么会发生这种情况。在出现NA的情况下,历史时间序列有一长串的0。这会是原因吗?
梯度可能溢出/爆炸了。或许可以添加一些梯度裁剪,并确保在隐藏层中使用relu激活函数。
你好,Jason!
“在定义层时,我们可以指定输入中要进行掩码处理的值。
如果时间步的所有特征都包含被掩码处理的值,那么整个时间步将被排除在计算之外。”
根据定义;当我有5个时间步,例如“2, 4, 7, 1, 0”,并且掩码值为0时。我理解这个时间步不会被排除,0值也不会被排除在计算之外。我说的对吗?根据定义,它应该是“0, 0, 0, 0, 0”才会被排除。
顺便说一句,感谢这篇精彩的帖子。
是的,掩码层允许你指定要掩码的值。
谢谢你的回答。但是有些事情对我来说还不清楚。我需要屏蔽 0,我的时间步长为 5 的所有时间步长看起来像“8, 6, 7, 0, 0”,“7, 9, 23, 5, 0”,“1, 2,0, 0,0”因为填充,但它们都没有像“0, 0,0,0,0”那样。掩码对我有帮助吗?定义中的以下句子让我感到困惑。
“如果时间步的所有特征都包含被掩码处理的值,那么整个时间步将从计算中排除。”它是否会在某些特征(但不是所有特征)具有被掩码处理的值时排除被掩码处理的值?
是的,任何包含你指定掩码值的时间步都会被跳过。无论是连续的掩码值还是零星的,都不影响。
你好,
我正在尝试拟合一个CNN模型,但是我的输出数据集中有NA数据,我该如何告诉模型在训练期间忽略这些带有NA数据的像素?我尝试了几种方法,但总是得到一个损失:nan。
也许可以将 N/A 标记为 0 值?
早上好,我有一个真实数据集。有很多缺失值。上面提供的程序适用于合成数据。如何将“fill”命令用于真实数据集。
也许可以从简单地保留上一个观测值开始。
from pandas import concat
# 创建滞后
df = concat([df.shift(1), df],axis=1)
# 将缺失值替换为 -1
df.fillna(-1, inplace=True)
values = df.values
###获取独立特征
X=df.drop(‘rating’, axis=1)
# 指定输入
X = values
# 重塑
X = X.reshape(len(X), 100836, 5)
return X
#################
ValueError 回溯 (最近一次调用)
in ()
10 X = values
11 # 重塑
—> 12 X = X.reshape(len(X), 100836, 5)
13 return X
ValueError: cannot reshape array of size 2420064 into shape (201672,100836,5)
###########################
你好,我的真实数据集中有响应变量。如何操作上述代码。请更正
我的代码。
这是我在这里回答的一个常见问题
https://machinelearning.org.cn/faq/single-faq/can-you-read-review-or-debug-my-code
你好,请分享具体链接。
Jason - 我已经读过你的LSTM书,并且(感觉像是)这里关于LSTM模型的每一篇帖子。我有一个时间序列长度不同的问题,并按照你这里的建议使用了pad_sequences和Masking层。
由于我正在构建一个LSTM自编码器,我意识到RepeatVector层和任何`return_sequences=False`的LSTM层都会失去掩码。经过一些研究,我发现了这篇帖子(https://stackoverflow.com/questions/58144336/how-to-mask-the-inputs-in-an-lstm-autoencoder-having-a-repeatvector-layer)。我采用了推荐的自定义瓶颈层方法。
你同意这篇帖子中的方法吗?这是否使过程过于复杂,或者有没有其他方法来处理掩码和自编码器?
提前感谢——这个网站对我的所有机器学习任务都非常有帮助,不仅仅是LSTM。
这无关紧要/不需要(我粗略地想了一下),因为重复层/瓶颈处的压缩信号已经排除了被掩码的值。
谢谢——直观上说,这很有道理。对于一个重构自编码器,在RepeatVector之后我应该不需要掩码,因为在那之前输入的学习已经完成了。
还有一个问题——对于自编码器,这是否也意味着特征压缩,因此,你应该有多个堆叠层,在每个层中减少神经元的数量,直到瓶颈层?我假设你使用减少的神经元来压缩“特征”。
是的,自编码器将输入压缩到瓶颈向量。
Jason,请帮帮我。假设我有一个以30分钟间隔采样的电力时间序列数据,其中有一些缺失的时间戳,比如有12月12日下午2:30的数据,但没有12月12日下午3点、3:30到大约6点的数据,然后数据在6:30继续。如果我使用传统的pandas isna()函数,这些缺失的时间戳不会被记录为缺失。所以我的问题是,有没有办法可以查看时间序列数据中的不连续性?即,有没有办法查看是否有任何时间段被跳过?
请我非常希望能得到一个答案。
这里有一些想法
https://machinelearning.org.cn/ufaqs/how-do-i-handle-discontiguous-time-series-data/
嗨!
非常感谢,这很有帮助!但是有一点小问题…
用数字替换NaN是数据造假。
我来这里是看你的掩码示例。掩码似乎是一种勉强可接受的临时解决方案,用于在无法妥善处理NaN的库中将NaN排除在计算之外……除了它很危险。你确实在伪造数据,然后(希望能)忽略被伪造的部分。你如何保证被伪造的数据不会破坏真实数据?
我们能否发明一个特殊值来表示“不是数字”,然后将其正确地传播到计算中?
回到你的“掩码”代码,NaN被转换为-1然后忽略-1。因为它只是一个用于掩码的占位符,如果我们将-1替换为其他值,比如99999999,应该不会有区别,对吗?你是这样认为的吗?在我相当平庸、近期、纯粹的Python环境中,你展示的“伪造和掩码”技术导致伪造的数据污染了结果,不是NaN,而是数字,这些数字会愉快地进入我的合作者数据处理管道的接下来18个阶段。或者问题是由于非掩码输入归一化,或者完全是其他原因?谁知道什么时候能发现问题?
我来这里是因为我不知道答案,我仍然不知道。你有没有更好的方法可以由最终用户实现?
再次感谢我在这里学到的一切!
使用-1或9999999作为掩码值应该是一样的,但大多数传输函数会偏爱数值较小的。另外,伪造数据可能并非坏事,因为它可以帮助防止模型过拟合。如果数据即使存在NaN仍然包含一些信息,我希望模型能够指出这一点。图像识别中的一个例子可能有助于解释这一点:改变图像中的几个像素不应该影响你识别的内容。所以你不应该关心我如何改变那几个像素。
你好 Jason,
非常感谢您通过有用的帖子,为机器学习算法的学习付出了不懈的努力。
我有一个关于缺失值学习的问题,我的问题是,如果我们的时间序列数据中不希望有-1作为值怎么办?例如,压力值是-1或风速值是-1是不合逻辑的。而且,将0赋值与现有特征值的范围相比,也可能是一个异常值。在这种情况下,在我的数据集上运行这些选项有用吗?
我实际上尝试了回归技术,它们为我的多元时间序列的空白填充提供了合理的结果,尽管我在某些特征中存在明显的异常值。我只是不确定是否也需要尝试您建议的方法。
谢谢
您也可以尝试插值,即考虑时间序列中缺失值之前和之后的值,并取其平均值进行填充。
嗨!
精彩的帖子!
有没有什么技术可以处理图像序列问题中的缺失数据?也就是说,如果序列中缺少整个图像,我该怎么办?
谢谢