长短期记忆(LSTM)系列_LSTM的数据准备(6)——如何处理序列预测问题中的缺失时间步长(附两个完整LSTM实例)

导读:

本文讨论分析了输入数据中,有数据缺失的情况如何处理
一般有两种情况,分别是对缺失值进行替换和学习,忽略对缺失值的学习
同时文章演示了对于缺失值得补全方法。
文末附带两个LSTM代码,比较了两种对缺失值不同处理方式的网络模型优劣

正文:

通常从序列数据中缺少观察结果。

数据可能已损坏或不可用,但根据定义,您的数据也可能具有可变长度序列。具有较少时间步长的那些序列可被认为具有缺失值。

在本教程中,您将了解如何使用Keras深度学习库处理Python中序列预测问题缺失值的数据。

监督学习数据类型框架

在使用神经网络时,序列必须被设置为监督学习问题。

这意味着序列需要分为输入和输出对。

该问题可以被构造为基于当前和先前时间步的函数进行预测。

或者更正式地说:

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中的监督学习问题。

熊猫移()函数可以被用来创建可用于在时间步长之前以代表观测序列的移位版本。这可以与原始序列连接以提供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

# 生成一系列随机值
def generate_sequence(n_timesteps):
	return [random() for _ in range(n_timesteps)]

# 生成lstm的数据
def generate_data(n_timesteps):
    # 生成随机序列
    sequence = generate_sequence(n_timesteps)
    print(sequence)
    sequence = array(sequence)
    print(sequence)
    # 格式化成有序矩阵
    df = DataFrame(sequence)
    print(df)
    # 变成两两一组的有序数据矩阵,创建可用于在时间步长之前以代表观测序列的移位版
    df = concat([df.shift(1), df], axis=1)
    print(df)
    values = df.values
    print(values)
    # 指定输入和输出数据
    X, y = values, values[:, 0]
    print(y)
    return 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

[0.9499452498907361, 0.9577216990829638, 0.8375281747300221, 0.3841115541079436, 0.5743029557433733, 0.10128265088053356, 0.6283099216390347, 0.17457841317845058, 0.7219971035263132, 0.6687711784414226]
[0.94994525 0.9577217  0.83752817 0.38411155 0.57430296 0.10128265
 0.62830992 0.17457841 0.7219971  0.66877118]
          0
0  0.949945
1  0.957722
2  0.837528
3  0.384112
4  0.574303
5  0.101283
6  0.628310
7  0.174578
8  0.721997
9  0.668771
          0         0
0       NaN  0.949945
1  0.949945  0.957722
2  0.957722  0.837528
3  0.837528  0.384112
4  0.384112  0.574303
5  0.574303  0.101283
6  0.101283  0.628310
7  0.628310  0.174578
8  0.174578  0.721997
9  0.721997  0.668771
[[       nan 0.94994525]
 [0.94994525 0.9577217 ]
 [0.9577217  0.83752817]
 [0.83752817 0.38411155]
 [0.38411155 0.57430296]
 [0.57430296 0.10128265]
 [0.10128265 0.62830992]
 [0.62830992 0.17457841]
 [0.17457841 0.7219971 ]
 [0.7219971  0.66877118]]
[       nan 0.94994525 0.9577217  0.83752817 0.38411155 0.57430296
 0.10128265 0.62830992 0.17457841 0.7219971 ]
[       nan 0.94994525] => nan
[0.94994525 0.9577217 ] => 0.9499452498907361
[0.9577217  0.83752817] => 0.9577216990829638
[0.83752817 0.38411155] => 0.8375281747300221
[0.38411155 0.57430296] => 0.3841115541079436
[0.57430296 0.10128265] => 0.5743029557433733
[0.10128265 0.62830992] => 0.10128265088053356
[0.62830992 0.17457841] => 0.6283099216390347
[0.17457841 0.7219971 ] => 0.17457841317845058
[0.7219971  0.66877118] => 0.7219971035263132

我们可以看到第一行有NaN值。

这是因为我们没有事先观察序列中的第一个值。我们必须用一些东西填补这个空间。

但我们无法使用NaN输入拟合模型。

处理缺失的序列数据

处理丢失的序列数据有两种主要方法。

它们将删除缺少数据的行,并使用其他值填充缺少的时间步。

有关处理缺失数据的更常用方法,请参阅帖子:

处理缺失序列数据的最佳方法取决于您的问题和您选择的网络配置。我建议探索每种方法,看看哪种方法效果最好。

删除缺失的序列数据

在我们回显前一个时间步骤中的观察的情况下,第一行数据不包含任何有用的信息。

也就是说,在上面的例子中,给定输入:

1

[        nan  0.18961404]

和输出:

1

nan

没有任何有意义的东西可以学习或预测。

这里最好的情况是删除这一行。

我们可以通过删除包含NaN值的所有行,在序列的制定过程中将其作为监督学习问题。具体来说,可以在将数据拆分为X和y组件之前调用dropna()函数

完整示例如下:

from random import random
from numpy import array
from pandas import 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]
	return X, y

# 生成序列
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对,并删除第一行。

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的定义从值[:,0]更改为值[:,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值。

我们可以使用在输入中不会自然出现的特定值(例如-1)替换所有NaN值,而不是使用NaN值删除行。为此,我们可以使用fillna()Pandas函数

完整示例如下:

from random import random
from numpy import array
from pandas import concat
from pandas import DataFrame

# 生成一系列随机值
def generate_sequence(n_timesteps):
	return [random() for _ in range(n_timesteps)]

# 生成lstm的数据
def generate_data(n_timesteps):
	# generate sequence
	sequence = generate_sequence(n_timesteps)
	sequence = array(sequence)
	# 格式化成有序矩阵
	df = DataFrame(sequence)
	df = concat([df.shift(1), df], axis=1)
	# 把NAN值用-1来填充
	df.fillna(-1, inplace=True)
	values = df.values
	# 指定输入和输出数据
	X, y = values, values[:, 1]
	return X, y

# 生成序列
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

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优化算法,网络将适合。

# 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

# 生成一系列随机值
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)
	# 把NAN值用-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
# 构建一个网络模型
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])

回顾最终预测,我们可以看到网络已经学会了问题并预测了“足够好”的输出,即使存在缺失值。运行该示例打印每个时期的损失,并在运行结束时比较一个序列的预期输出与预测输出。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

...

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,并查看它如何影响模型的技能随时间的变化。

忽略缺失值

可以从网络中的所有计算中屏蔽标记的缺失输入值。

我们可以通过使用Masking层作为网络的第一层来实现。

定义图层时,我们可以指定要屏蔽的输入中的哪个值。如果时间步长的所有要素都包含蒙版值,则整个时间步长将从计算中排除。

这为完全排除行并强制网络了解标记缺失值的影响提供了一个中间立场。

由于Masking层是网络中的第一个,因此必须指定输入的预期形状,如下所示:

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

# 生成一系列随机值
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)
	#将NAN值替换为-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
# 定义网络
model = Sequential()
# mask_value=-1使得网络对-1的值进行忽略,不进行学习
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)
# 评估数据模型
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

...

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

猜你喜欢

转载自blog.csdn.net/yangwohenmai1/article/details/84837542
今日推荐