1. 程式人生 > >長短期記憶(LSTM)系列_LSTM的資料準備(6)——如何處理序列預測問題中的缺失時間步長(附兩個完整LSTM例項)

長短期記憶(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