在序列数据中缺少观察值是常见的。
数据可能已损坏或不可用,但也有可能,根据定义,您的数据具有可变长度序列。那些具有更少时间步长的序列可能被认为缺少值。
在本教程中,您将发现如何使用Keras深度学习库来处理Python中序列预测问题缺失值的数据。
完成本教程后,您将知道:
- 如何删除包含缺少时间步长的行。
- 如何标记丢失的时间步长,并强制网络了解其意义。
- 如何屏蔽缺少的时间步长,并将其从模型中的计算中排除。
让我们开始吧。
如何使用Python处理序列预测问题中丢失的时间步长(图片版权所有Sybil Liberty)
概观
本节分为3部分; 他们是:
- 回波序列预测问题
- 处理丢失的序列数据
- 学习利用缺失序列值
环境
本教程假定您已安装Python SciPy环境。您可以使用Python 2或3与此示例。
本教程假定您使用TensorFlow(v1.1.0 +)或Theano(v0.9 +)后端安装了Keras(v2.0.4 +)。
本教程还假定您已经安装了scikit-learn,Pandas,NumPy和Matplotlib。
如果您需要帮助设置您的Python环境,请参阅这篇文章:
回波序列预测问题
回波问题是一个人为设计的序列预测问题,其目的是通过之前的观测来预测一个固定的时间步长(称为延迟观测)。
例如,最简单的例子是预测之前的观察结果,然后将结果回传。例如:
Time 1: Input 45
Time 2: Input 23, Output 45
Time 3: Input 73, Output 23
...
问题是,我们如何处理时间步长1?
我们可以在Python中实现回波序列预测问题。
这涉及到两个步骤:产生随机序列和将随机序列转化为一个监督学习的问题。
生成随机序列
我们可以使用随机模块中的random()函数生成0到1之间的随机值序列。
我们可以将其放在一个名为generate_sequence()的函数中,该函数将生成所需数量的时间步长的随机浮点值序列。
此功能如下所示:
# generate a sequence of random values
def generate_sequence(n_timesteps):
return [random() for _ in range(n_timesteps)]
监督式学习的架构
使用神经网络时,序列必须被构建为监督学习问题。
这意味着序列需要分为输入对和输出对。
该问题可以被构建为基于当前和先前时间步长的函数进行预测。
正式点的说法是:
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)输入值。
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
然后,我们可以将Pandas DataFrame中的值作为输入序列(X),并使用第一列作为输出序列(y)。
# specify input and output data
X, y = values, values[:, 0]
将这一切放在一起,我们可以定义一个函数,它以时间步长作为参数,并返回X,y数据,用于序列学习,称为generate_data()。
# generate data for the lstm
def generate_data(n_timesteps):
# generate sequence
sequence = generate_sequence(n_timesteps)
sequence = array(sequence)
# create lag
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
values = df.values
# specify input and output data
X, y = values, values[:, 0]
return X, y
序列问题演示
我们可以将generate_sequence()和generate_data()代码合并成一个工作的例子。
完整的示例如下所示:
from random import random
from numpy import array
from pandas import concat
from pandas import DataFrame
# generate a sequence of random values
def generate_sequence(n_timesteps):
return [random() for _ in range(n_timesteps)]
# generate data for the lstm
def generate_data(n_timesteps):
# generate sequence
sequence = generate_sequence(n_timesteps)
sequence = array(sequence)
# create lag
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
values = df.values
# specify input and output data
X, y = values, values[:, 0]
return X, y
# generate sequence
n_timesteps = 10
X, y = generate_data(n_timesteps)
# print sequence
for i in range(n_timesteps):
print(X[i], '=>', y[i])
运行此示例生成一个序列,将其转换为监督的表示法,并打印每个X,Y。
[ 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输入模型。
处理缺失的序列数据
处理丢失的序列数据有两种主要方法。
删除缺少数据的行,或使用另一个值填充缺少的时间步长。
有关处理丢失数据的更通用的方法,请参阅帖子:
处理丢失序列数据的最佳方法将取决于您的问题和您筛选的网络结构。我推荐你探索每种方法,看看哪些效果最好。
删除缺失的序列数据
在这种情况下我们观察之前的时间步长,第一行数据不包含任何有用的信息。
也就是说,在上面的例子中,给出了输入:
[ nan 0.18961404]
并输出:
nan
这对预测或者学习没有任何意义。
最好的情况是删除这一行。
通过删除包含NaN值的所有行,我们可以为监督学习的问题编制序列。具体来说,在将数据分解成X和Y分量之前,可以调用
dropna()函数。
完整的例子如下:
from random import random
from numpy import array
from pandas import concat
from pandas import DataFrame
# generate a sequence of random values
def generate_sequence(n_timesteps):
return [random() for _ in range(n_timesteps)]
# generate data for the lstm
def generate_data(n_timesteps):
# generate sequence
sequence = generate_sequence(n_timesteps)
sequence = array(sequence)
# create lag
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
# remove rows with missing values
df.dropna(inplace=True)
values = df.values
# specify input and output data
X, y = values, values[:, 0]
return X, y
# generate sequence
n_timesteps = 10
X, y = generate_data(n_timesteps)
# print sequence
for i in range(len(X)):
print(X[i], '=>', y[i])
运行示例的结果是有9对 X,y对而不是10对,第一行被删除了。
[ 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的定义从值[:, 0]更改为值[0, :],并重新运行上个示例以生成此问题的示例,如下所示:
[ 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
我们可以看到第一行给出的输入是:
[ nan 0.50513289]
输出是:
0.505132894821
也可以看到哪些可以从输入中学到。
问题是,我们还有一个NaN值需要处理。
不使用出现NaN值就删除行的方法,我们可以用输入中不会自然出现的特定值替换所有NaN值,例如-1。要做到这一点,我们可以使用fillna() Pandas function。
完整的例子如下:
from random import random
from numpy import array
from pandas import concat
from pandas import DataFrame
# generate a sequence of random values
def generate_sequence(n_timesteps):
return [random() for _ in range(n_timesteps)]
# generate data for the lstm
def generate_data(n_timesteps):
# generate sequence
sequence = generate_sequence(n_timesteps)
sequence = array(sequence)
# create lag
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
# replace missing values with -1
df.fillna(-1, inplace=True)
values = df.values
# specify input and output data
X, y = values, values[:, 1]
return X, y
# generate sequence
n_timesteps = 10
X, y = generate_data(n_timesteps)
# print sequence
for i in range(len(X)):
print(X[i], '=>', y[i]
运行示例,我们可以看到第一行的第一列中的NaN值被替换为-1。
[-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以及具有线性激活功能的单个输出层。
该网络将使用均值平方误差函数和有效的亚当优化算法与缺省配置进行匹配。
# define model
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)),我们将生成一个新的随机序列。该网络将适用于500个训练次数,并在每个序列(batch_size = 1)之后执行更新。
# fit model
for i in range(500):
X, y = generate_data(n_timesteps)
model.fit(X, y, epochs=1, batch_size=1, verbose=2)
一旦匹配,将产生另一个随机序列,并将来自模型的预测与预期值进行比较。这将提供一个具体的模型技能的概念。
# evaluate model on new data
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])
将所有这些结合在一起,完整的代码如下所示:
from random import random
from numpy import array
from pandas import concat
from pandas import DataFrame
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
# generate a sequence of random values
def generate_sequence(n_timesteps):
return [random() for _ in range(n_timesteps)]
# generate data for the lstm
def generate_data(n_timesteps):
# generate sequence
sequence = generate_sequence(n_timesteps)
sequence = array(sequence)
# create lag
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
# replace missing values with -1
df.fillna(-1, inplace=True)
values = df.values
# specify input and output data
X, y = values, values[:, 1]
# reshape
X = X.reshape(len(X), 2, 1)
y = y.reshape(len(y), 1)
return X, y
n_timesteps = 10
# define model
model = Sequential()
model.add(LSTM(5, input_shape=(2, 1)))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
# fit model
for i in range(500):
X, y = generate_data(n_timesteps)
model.fit(X, y, epochs=1, batch_size=1, verbose=2)
# evaluate model on new data
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/1
0s - loss: 1.5992e-04
Epoch 1/1
0s - loss: 1.3409e-04
Epoch 1/1
0s - loss: 1.1581e-04
Epoch 1/1
0s - loss: 2.6176e-04
Epoch 1/1
0s - loss: 8.8303e-05
Expected 0.390784174343 Predicted 0.394238
Expected 0.688580469278 Predicted 0.690463
Expected 0.347155799665 Predicted 0.329972
Expected 0.345075533266 Predicted 0.333037
Expected 0.456591840482 Predicted 0.450145
Expected 0.842125610156 Predicted 0.839923
Expected 0.354087132135 Predicted 0.342418
Expected 0.601406667694 Predicted 0.60228
Expected 0.368929815424 Predicted 0.351224
Expected 0.716420996314 Predicted 0.719275
您可以进一步对此示例进行实验,并将给定序列的t-1观测值的50%标记为-1,并查看它如何影响模型随时间的技能。
掩盖缺失值
标记的缺失的输入值可以从网络中的所有计算中屏蔽。
我们可以通过使用掩盖层作为网络的第一层来做到这一点。
定义层时,我们可以指定输入中的哪个值进行掩盖。如果时间步长的所有功能都包含屏蔽值,则整个时间步长将从计算中排除。
这提供了和完全排除行之间的中间地带,迫使网络学习标有缺失的值的影响。
因为屏蔽层是网络中的第一个,它必须指定输入的预期形状,如下所示:
model.add(Masking(mask_value=-1, input_shape=(2, 1)))
我们可以把所有这一切结合在一起,重新运行这个例子。完整的代码清单如下:
from random import random
from numpy import array
from pandas import concat
from pandas import DataFrame
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import Masking
# generate a sequence of random values
def generate_sequence(n_timesteps):
return [random() for _ in range(n_timesteps)]
# generate data for the lstm
def generate_data(n_timesteps):
# generate sequence
sequence = generate_sequence(n_timesteps)
sequence = array(sequence)
# create lag
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
# replace missing values with -1
df.fillna(-1, inplace=True)
values = df.values
# specify input and output data
X, y = values, values[:, 1]
# reshape
X = X.reshape(len(X), 2, 1)
y = y.reshape(len(y), 1)
return X, y
n_timesteps = 10
# define model
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')
# fit model
for i in range(500):
X, y = generate_data(n_timesteps)
model.fit(X, y, epochs=1, batch_size=1, verbose=2)
# evaluate model on new data
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/1
0s - loss: 1.0252e-04
Epoch 1/1
0s - loss: 6.5545e-05
Epoch 1/1
0s - loss: 3.0831e-05
Epoch 1/1
0s - loss: 1.8548e-04
Epoch 1/1
0s - loss: 7.4286e-05
Expected 0.550889403319 Predicted 0.538004
Expected 0.24252028132 Predicted 0.243288
Expected 0.718869927574 Predicted 0.724669
Expected 0.355185878917 Predicted 0.347479
Expected 0.240554707978 Predicted 0.242719
Expected 0.769765554707 Predicted 0.776608
Expected 0.660782450416 Predicted 0.656321
Expected 0.692962017672 Predicted 0.694851
Expected 0.0485233839401 Predicted 0.0722362
Expected 0.35192019185 Predicted 0.339201
选择哪种方法?
这些一次性实验不足以评估在简单回波序列预测问题上最有效的方法。
他们提供的模板可以用于您自己的问题。
我 鼓励您探索在序列预测问题中处理缺失值的3种不同方式。他们是:
- 删除缺少值的行。
- 标记并学习缺失的值。
- 标记但不学习缺失的值。
尝试你的顺序预测问题的每一种方法,并将其看起来效果最好。
概要
如果您的序列具有可变长度,通常在序列预测问题中缺少值。
在本教程中,您已经发现如何使用Keras处理Python中序列预测问题中的缺失数据。
具体来说,你学到了:
- 如何删除包含缺少值的行。
- 如何标记缺失值并强制模型来学习其意义。
- 如何掩盖缺失的值,将其从模型中的计算中排除。