1. 程式人生 > >長短期記憶(LSTM)系列_3.1~3.3、第一個LSTM小例子:Keras中長短期記憶模型的5個核心步驟(python)

長短期記憶(LSTM)系列_3.1~3.3、第一個LSTM小例子:Keras中長短期記憶模型的5個核心步驟(python)

 

本文是演示了一個最簡單的LSTM模型的構建,以0.1~0.9序列為例,做了一個預測的小例項,文章最後我新增了一個測試集,重新使用訓練的模型進行一次預測,詳細程式碼以及註釋見文末。

後續的3.2和3.3課程是一組課程的學習目錄,以及作者自己新書的推薦,3.2章節會在後續學習中更新上來,3.3的內容大家對作者書記感興趣的話可以進去看看。

3.2、https://machinelearningmastery.com/long-short-term-memory-recurrent-neural-networks-mini-course/

3.3、https://machinelearningmastery.com/lstms-with-python/

3.1的原文摘錄如下:

在這篇文章中,您將發現在Keras中建立,訓練和評估長期短期記憶(LSTM)迴歸神經網路的分步生命週期,以及如何使用訓練有素的模型進行預測。

閱讀這篇文章後,你會知道:

  • 如何在Keras中定義,編譯,擬合和評估LSTM。
  • 如何為迴歸和分類序列預測問題選擇標準預設值。
  • 如何將它們結合在一起,在Keras開發和執行您的第一個LSTM迴圈神經網路。

步驟1.定義網路

第一步是定義您的網路。

神經網路在Keras中被定義為層序列。這些圖層的容器是Sequential類。

第一步是建立Sequential類的例項。然後,您可以建立圖層並按照它們應連線的順序新增它們。由儲存器單元組成的LSTM迴圈層稱為LSTM()。通常跟隨LSTM層並用於輸出預測的完全連線層稱為Dense()。

例如,我們可以分兩步完成:

model = Sequential()
model.add(LSTM(2))
model.add(Dense(1))

但是我們也可以通過建立一個圖層陣列並將其傳遞給Sequential的建構函式來一步完成。

layers = [LSTM(2), Dense(1)]
model = Sequential(layers)

網路中的第一層必須定義預期的輸入數量。輸入必須是三維的,包括樣本,時間步和特徵。

  • 樣品。這些是資料中的行。
  • 時間步。這些是過去對特徵的觀察,例如滯後變數。
  • 特色。這些是資料中的列。

假設您的資料作為NumPy陣列載入,您可以使用NumPy中的reshape()函式將2D資料集轉換為3D資料集。如果您希望列成為一個功能的時間步長,您可以使用:

data = data.reshape((data.shape[0], 1, data.shape[1]))

如果您希望2D資料中的列成為具有一個時間步長的要素,則可以使用:

data = data.reshape((data.shape[0], 1, data.shape[1]))

您可以指定input_shape引數,該引數需要包含時間步數和要素數的元組。例如,如果我們有兩個時間步長和一個特徵用於單變數時間序列,每行有兩個滯後觀察值,則將指定如下:

model = Sequential()
model.add(LSTM(5, input_shape=(2,1)))
model.add(Dense(1))

可以通過將LSTM圖層新增到Sequential模型來堆疊LSTM圖層。重要的是,當堆疊LSTM層時,我們必須為每個輸入輸出序列而不是單個值,以便後續LSTM層可以具有所需的3D輸入。我們可以通過將return_sequences引數設定為True來完成此操作。例如:

model = Sequential()
model.add(LSTM(5, input_shape=(2,1), return_sequences=True))
model.add(LSTM(5))
model.add(Dense(1))

將Sequential模型視為一個管道,其中您的原始資料最終被輸入,而預測則來自另一個。

這在Keras中是一個有用的容器,因為傳統上與圖層相關的關注點也可以拆分並作為單獨的圖層新增,清楚地顯示它們在從輸入到預測的資料轉換中的作用。將Sequential模型視為一個管道,其中您的原始資料最終被輸入,而預測則來自另一個。

例如,可以提取轉換來自層中每個神經元的求和訊號的啟用函式,並將其作為稱為啟用的層狀物件新增到Sequential中。

model = Sequential()
model.add(LSTM(5, input_shape=(2,1)))
model.add(Dense(1))
model.add(Activation('sigmoid'))

啟用函式的選擇對於輸出層是最重要的,因為它將定義預測將採用的格式。

例如,下面是一些常見的預測建模問題型別以及可以在輸出層中使用的結構和標準啟用函式:啟用函式的選擇對於輸出層是最重要的,因為它將定義預測將採用的格式。

  • 迴歸:線性啟用函式,或“線性”,以及與輸出數量匹配的神經元數量。
  • 二進位制分類(2類):邏輯啟用函式,或'sigmoid',以及輸出層的一個神經元。
  • 多類分類(> 2類):假設單熱編碼輸出模式,Softmax啟用函式或'softmax',每個類值一個輸出神經元。

第2步。編譯網路

一旦我們定義了網路,我們就必須編譯它。

編譯是一個效率步驟。它將我們定義的簡單圖層序列轉換為高效的矩陣變換系列,其格式應在GPU或CPU上執行,具體取決於Keras的配置方式。

將編譯視為網路的預計算步驟。定義模型後始終需要它。

編譯需要指定許多引數,專門用於培訓您的網路。具體地,用於訓練網路的優化演算法和用於評估由優化演算法最小化的網路的損失函式。

例如,下面是編譯定義模型並指定隨機梯度下降(sgd)優化演算法和均值誤差(mean_squared_error)損失函式的情況,用於迴歸型別問題。

model.compile(optimizer='sgd', loss='mean_squared_error')

或者,可以在作為編譯步驟的引數提供之前建立和配置優化程式。

algorithm = SGD(lr=0.1, momentum=0.3)
model.compile(optimizer=algorithm, loss='mean_squared_error')

預測建模問題的型別對可以使用的損失函式的型別施加約束。

例如,下面是不同預測模型型別的一些標準損失函式:預測建模問題的型別對可以使用的損失函式的型別施加約束。

  • 迴歸:均值平方誤差或'mean_squared_error'。
  • 二進位制分類(2類):對數損失,也稱為交叉熵或“binary_crossentropy”。
  • 多類分類(> 2類):多對數損失或'categorical_crossentropy'。

最常見的優化演算法是隨機梯度下降,但Keras還支援一套其他最先進的優化演算法,這些演算法在很少或沒有配置的情況下都能很好地工作。

也許最常用的優化演算法因為它們通常具有更好的效能:

  • 隨機梯度下降,或'sgd',需要調整學習速度和動量。
  • ADAM,或“亞當”,需要調整學習率。
  • RMSprop或'rmsprop',需要調整學習速率。

最後,除了損失函式之外,您還可以指定在擬合模型時收集的度量標準。通常,要收集的最有用的附加度量標準是分類問題的準確性。要收集的度量標準由陣列中的名稱指定。

例如:

model.compile(optimizer='sgd', loss='mean_squared_error', metrics=['accuracy'])

步驟3.fit網路

一旦網路被編譯,它就可以適合,這意味著在訓練資料集上調整權重。

安裝網路需要指定訓練資料,包括輸入模式矩陣X和匹配輸出模式陣列y。

使用反向傳播演算法訓練網路,並根據編譯模型時指定的優化演算法和損失函式進行優化。

反向傳播演算法要求網路訓練指定數量的時期或暴露於訓練資料集。

每個紀元可以被劃分為稱為批次的輸入 - 輸出模式對的組。這定義了在一個紀元內更新權重之前網路所接觸的模式數。它也是一種效率優化,確保一次不會將太多輸入模式載入到記憶體中。

擬合網路的最小例子如下:

history = model.fit(X, y, batch_size=10, epochs=100)

適合後,將返回歷史物件,該物件提供訓練期間模型效能的摘要。這包括損失和編譯模型時指定的任何其他指標,記錄每個時期。

訓練可能需要很長時間,從幾秒到幾小時到幾天,具體取決於網路的大小和培訓資料的大小。適合後,將返回歷史物件,該物件提供訓練期間模型效能的摘要。這包括損失和編譯模型時指定的任何其他指標,記錄每個時期。

預設情況下,每個紀元的命令列上都會顯示一個進度條。這可能會給您帶來太多噪音,或者可能會對您的環境造成問題,例如您使用的是互動式膝上型電腦或IDE。

通過將詳細引數設定為2,可以減少每個時期顯示的資訊量。您可以通過將詳細設定為1來關閉所有輸出。例如:

history = model.fit(X, y, batch_size=10, epochs=100, verbose=0)

第4步。評估網路

一旦網路被訓練,就可以對其進行評估。

可以在訓練資料上評估網路,但是這不會提供作為預測模型的網路效能的有用指示,因為它之前已經看到了所有這些資料。

我們可以在測試期間看不到的單獨資料集上評估網路的效能。這將在未來對未見資料進行預測時提供網路效能的估計。

該模型評估所有測試模式的損失,以及編譯模型時指定的任何其他指標,如分類準確性。返回評估指標列表。

例如,對於使用精度度量編制的模型,我們可以在新資料集上對其進行評估,如下所示:

loss, accuracy = model.evaluate(X, y)

與擬合網路一樣,提供詳細輸出以瞭解評估模型的進度。我們可以通過將verbose引數設定為0來關閉它。

loss, accuracy = model.evaluate(X, y, verbose=0)


第5步。做出預測

一旦我們對擬合模型的效能感到滿意,我們就可以用它來預測新資料。

這就像使用一組新輸入模式呼叫模型上的predict()函式一樣簡單。

例如:

predictions = model.predict(X)

預測將以網路輸出層提供的格式返回。

在迴歸問題的情況下,這些預測可以是直接問題的格式,由線性啟用函式提供。預測將以網路輸出層提供的格式返回。

對於二元分類問題,預測可以是第一類的概率陣列,其可以通過舍入轉換為1或0。

對於多類分類問題,結果可以是概率陣列的形式(假設一個熱編碼輸出變數),可能需要使用argmax()NumPy函式將其轉換為單個類輸出預測。

或者,對於分類問題,我們可以使用predict_classes()函式,該函式會自動將uncrisp預測轉換為清晰的整數類值。

predictions = model.predict_classes(X)

與擬合和評估網路一樣,提供詳細輸出以給出預測模型進展的概念。我們可以通過將verbose引數設定為0來關閉它。

predictions = model.predict(X, verbose=0)

 

端到端工作示例

讓我們將這一切與一個小例子結合起來。

這個例子將使用一個學習10個數字序列的簡單問題。我們將向網路顯示一個數字,例如0.0,並期望它預測為0.1。然後顯示它0.1並期望它預測0.2,依此類推至0.9。

  1. 定義網路:我們將構建一個LSTM神經網路,在可見層中具有1個輸入時間步長和1個輸入特徵,在LSTM隱藏層中具有10個儲存器單元,在完全連線的輸出層中具有1個具有線性(預設)啟用功能的神經元。
  2. 編譯網路:我們將使用具有預設配置和均方誤差丟失函式的高效ADAM優化演算法,因為它是迴歸問題。
  3. 適合網路:我們將使網路適合1,000個時期,並使用等於訓練集中模式數量的批量大小。我們還將關閉所有詳細輸出。
  4. 評估網路。我們將在訓練資料集上評估網路。通常,我們會在測試或驗證集上評估模型。
  5. 做出預測。我們將對訓練輸入資料進行預測。同樣,通常我們會對我們不知道正確答案的資料做出預測。

完整的程式碼清單如下。

# Example of LSTM to learn a sequence
from pandas import DataFrame
from pandas import concat
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
# create sequence
length = 10
sequence = [i/float(length) for i in range(length)]
print(sequence)
# create X/y pairs
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
df.dropna(inplace=True)
# convert to LSTM friendly format
values = df.values
X, y = values[:, 0], values[:, 1]
X = X.reshape(len(X), 1, 1)
# 1. define network
model = Sequential()
model.add(LSTM(10, input_shape=(1,1)))
model.add(Dense(1))
# 2. compile network
model.compile(optimizer='adam', loss='mean_squared_error')
# 3. fit network
history = model.fit(X, y, epochs=1000, batch_size=len(X), verbose=0)
# 4. evaluate network
loss = model.evaluate(X, y, verbose=0)
print(loss)
# 5. make predictions
predictions = model.predict(X, verbose=0)
print(predictions[:, 0])

執行此示例將生成以下輸出,顯示10個數字的原始輸入序列,對整個序列進行預測時網路的均方誤差丟失,以及每個輸入模式的預測。


輸出間隔開以便於閱讀。執行此示例將生成以下輸出,顯示10個數字的原始輸入序列,對整個序列進行預測時網路的均方誤差丟失,以及每個輸入模式的預測。

我們可以看到序列被很好地學習,特別是如果我們將預測舍入到第一個小數位。

 

1

2

3

4

5

6

[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]

 

4.54527471447e-05

 

[ 0.11612834 0.20493418 0.29793766 0.39445466 0.49376178 0.59512401

0.69782174 0.80117452 0.90455914]

 

本人又新增了一個小測試,原文是預測了0.1~0.9的數列,我將數列修改成0.5~1.4,仍然利用上述結果進行預測,測試預測值的準確率,效果如下:

[0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4]

1

2

3

4

5

6

[0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4]

 

 

[0.5920643  0.6938976  0.79730105 0.90165794 1.0063956  1.1109957
 1.2150018  1.3180232  1.419735  ]

 

可以感覺到誤差有所擴大,但對於這個預測結果還是可以接受的,說明模型仍有優化的空間,修改後的程式碼如下:

from pandas import DataFrame
from pandas import concat
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
# 建立一個0.1~0.9的序列
length = 10
sequence = [i/float(length) for i in range(length)]
print(sequence)
# 構建一個X->y的對映關係
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
df.dropna(inplace=True)
# 使用reshape方法,把序列轉換為LSTM可識別的陣列格式
values = df.values
X, y = values[:, 0], values[:, 1]
X = X.reshape(len(X), 1, 1)
# 1. 定義網路型別
model = Sequential()
model.add(LSTM(10, input_shape=(1,1)))
model.add(Dense(1))
# 2. 編譯網路,設定損失引數
model.compile(optimizer='adam', loss='mean_squared_error')
# 3. 呼叫網路開始訓練模型
history = model.fit(X, y, epochs=1000, batch_size=len(X), verbose=0)
# 4. 評估網路
loss = model.evaluate(X, y, verbose=0)
print(loss)
# 5. 利用訓練好的模型,帶入原始的X進行單步預測
predictions = model.predict(X, verbose=0)
print(predictions[:, 0])



# 建立一個0.1~0.9的序列
length = 10
sequence = [(i+5)/float(length) for i in range(length)]
print(sequence)
# 構建一個X->y的對映關係
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
df.dropna(inplace=True)
# 使用reshape方法,把序列轉換為LSTM可識別的陣列格式
values = df.values
X, y = values[:, 0], values[:, 1]
X = X.reshape(len(X), 1, 1)
predictions = model.predict(X, verbose=0)
print(predictions[:, 0])