手把手構建LSTM的向前傳播(Building a LSTM step by step)

目錄

  • 閥門和狀態描述
  • LSTM cell
  • LSTM整個過程

需要理解:

  • 遺忘門,更新門,輸出門的作用是什麼,它們是怎麼發揮作用的。
  • 單元狀態 cell state 是如何來選擇性保留信息。

下面這張圖將示意LSTM的操作。

手把手構建LSTM的向前傳播(Building a LSTM step by step)

LSTM單元,它在每一個時間步長跟蹤更新“單元狀態”或者是記憶變量。

同之前講的RNN例子一樣,我們將以一個時間步長的LSTM單元執行開始,接著你就可以用for循環處理Tx個時間步長。

​閥門和狀態概述

遺忘門

概念:

  • 假設我們正在閱讀一段文本中的單詞,並計劃使用LSTM跟蹤語法結構,例如判斷主體是單數(“ puppy”)還是複數(“ puppies”)。
  • 如果主體更改其狀態(從單數詞更改為複數詞),那麼先前的記憶狀態將過時,因此我們“忘記”過時的狀態。
  • “遺忘門”是一個張量,它包含介於0和1之間的值。
  • 如果遺忘門中的一個單元的值接近於0,則LSTM將“忘記”之前單元狀態相應單位的存儲值。
  • 如果遺忘門中的一個單元的值接近於1,則LSTM將記住大部分相應的值。

公式:

手把手構建LSTM的向前傳播(Building a LSTM step by step)

公式的解釋:

  • 包含控制遺忘門行為的權重。
  • 之前時間步長的隱藏狀態和當前時間步長的輸入連接在一起乘以。
  • sigmoid函數讓每個門的張量值在0到1之間。
  • 遺忘門和之前的單元狀態有相同的shape。
  • 這就意味著它們可以按照元素相乘。
  • 將張量和相乘相當於在之前的單元狀態應用一層蒙版。
  • 如果中的單個值是0或者接近於0,那麼乘積就接近0.
  • 這就是使得存儲在對應單位的值在下一個時間步長不會被記住。
  • 同樣,如果中的1個值接近於1,那麼乘積就接近之前單元狀態的原始值。
  • LSTM就會在下一個時間步長中保留對應單位的值。

在代碼中的變量名:

  • Wf: 遺忘門的權重
  • Wb: 遺忘門的偏差
  • ft: 遺忘門

候選值

概念:

  • 候選值是包含當前時間步長信息的張量,它可能會存儲在當前單元狀態中。
  • 傳遞候選值的哪些部分取決於更新門。
  • 候選值是一個張量,它的範圍從-1到1。
  • 代字號“〜”用於將候選值與單元狀態區分開。

公式:

手把手構建LSTM的向前傳播(Building a LSTM step by step)

公式的解釋:

  • 'tanh'函數產生的值介於-1和+1之間。

在代碼中的變量名:

  • cct: 候選值

更新門

概念:

  • 我們使用更新門來確定候選的哪些部分要添加到單元狀態中。
  • 更新門是包含0到1之間值的張量。
  • 當更新門中的單位接近於0時,它將阻止候選值中的相應值傳遞到。
  • 當更新門中的單位接近1時,它允許將候選的值傳遞到。
  • 注意,我們使用下標“i”而不是“u”來遵循文獻中使用的約定。

公式:

手把手構建LSTM的向前傳播(Building a LSTM step by step)

公式的解釋:

  • 類似於遺忘門(此處為),用sigmoid函數乘後值就落在了0到1之間。
  • 將更新門與候選元素逐元素相乘,並將此乘積()用於確定單元狀態。

在代碼中的變量名:

在代碼中,我們將使用學術文獻中的變量名。這些變量不使用“ u”表示“更新”。

  • wi是更新門的權重
  • bi是更新門的偏差
  • it是更新門

單元狀態

概念:

  • 單元狀態是傳遞到未來時間步長的“記憶/內存(memory)”。
  • 新單元狀態是先前單元狀態和候選值的組合。

公式:

手把手構建LSTM的向前傳播(Building a LSTM step by step)

公式的解釋:

  • 之前的單元狀態通過遺忘門調整(加權)。
  • 候選值通過更新門調整(加權)。

在代碼中的變量名:

  • c: 單元狀態,包含所有的時間步長,c的shape是(na, m, T)
  • c_next: 下一個時間步長的單元狀態,的shape (na, m)
  • c_prev: 之前的單元狀態,的shape (na, m)

輸出門

手把手構建LSTM的向前傳播(Building a LSTM step by step)

概念:

  • 輸出門決定時間步長要輸出的預測值。
  • 輸出門與其他門一樣,它包含從0到1的值。

公式:

公式的解釋:

  • 輸出門由之前的隱藏狀態和當前的輸入 決定。
  • sigmoid函數讓值的範圍在0到1之間。

在代碼中的變量名:

  • wo: 輸出門的權重
  • bo: 輸出門的偏差
  • ot: 輸出門

隱藏狀態

概念:

  • 隱藏狀態將傳遞到LSTM單元的下一個時間步長。
  • 它用於確定下一個時間步長的三個門()。
  • 隱藏狀態也用於預測。

公式:

手把手構建LSTM的向前傳播(Building a LSTM step by step)

公式的解釋:

  • 隱藏狀態由單元狀態結合輸出門確定。
  • 單元狀態通過“ tanh”函數把值縮放到-1和+1之間。
  • 輸出門的作用就像一個“掩碼mask”,它既可以保留的值,也可以使這些值不包含在隱藏狀態中。

在代碼中的變量名:

  • a: 隱藏狀態,包含時間步長,shape (na, m, Tx)
  • a_prev: 前一步的隱藏狀態,的shape (na, m)
  • a_next: 下一步的隱藏狀態,的shape (na, m)

預測值

概念:

  • 此用例的預測是分類,所以我們用softmax。

公式:

手把手構建LSTM的向前傳播(Building a LSTM step by step)

在代碼中的變量名:

  • y_pred: 預測,包含所有的時間步長,的shape (ny, m, Tx),注意,本例中Tx=Ty。
  • yt_pred: 當前時間步長t的預測值,shape是(ny, m)

LSTM cell ‍

一共三個步驟:

1. 連接隱藏狀態和輸入成一個單獨的矩陣

2. 依次計算上面那6個公式

3. 計算預測值

<code>def lstm_cell_forward(xt, a_prev, c_prev, parameters):
"""
Implement a single forward step of the LSTM-cell as described in Figure (4)

Arguments:
xt -- your input data at timestep "t", numpy array of shape (n_x, m).
a_prev -- Hidden state at timestep "t-1", numpy array of shape (n_a, m)
c_prev -- Memory state at timestep "t-1", numpy array of shape (n_a, m)
parameters -- python dictionary containing:
Wf -- Weight matrix of the forget gate, numpy array of shape (n_a, n_a + n_x)
bf -- Bias of the forget gate, numpy array of shape (n_a, 1)
Wi -- Weight matrix of the update gate, numpy array of shape (n_a, n_a + n_x)
​ bi -- Bias of the update gate, numpy array of shape (n_a, 1)
Wc -- Weight matrix of the first "tanh", numpy array of shape (n_a, n_a + n_x)
bc -- Bias of the first "tanh", numpy array of shape (n_a, 1)
Wo -- Weight matrix of the output gate, numpy array of shape (n_a, n_a + n_x)
bo -- Bias of the output gate, numpy array of shape (n_a, 1)
Wy -- Weight matrix relating the hidden-state to the output, numpy array of shape (n_y, n_a)
by -- Bias relating the hidden-state to the output, numpy array of shape (n_y, 1)

Returns:
a_next -- next hidden state, of shape (n_a, m)

c_next -- next memory state, of shape (n_a, m)
yt_pred -- prediction at timestep "t", numpy array of shape (n_y, m)
cache -- tuple of values needed for the backward pass, contains (a_next, c_next, a_prev, c_prev, xt, parameters)

Note: ft/it/ot stand for the forget/update/output gates, cct stands for the candidate value (c tilde),
c stands for the cell state (memory)
"""
# 從 "parameters" 中取出參數。
Wf = parameters["Wf"] # 遺忘門權重
bf = parameters["bf"]
Wi = parameters["Wi"] # 更新門權重 (注意變量名下標是i不是u哦)
bi = parameters["bi"] # (notice the variable name)
Wc = parameters["Wc"] # 候選值權重
bc = parameters["bc"]
Wo = parameters["Wo"] # 輸出門權重
bo = parameters["bo"]
Wy = parameters["Wy"] # 預測值權重
by = parameters["by"]
# 連接 a_prev 和 xt
concat = np.concatenate((a_prev, xt), axis=0)
# 等價於下面代碼
# 從 xt 和 Wy 中取出維度
# n_x, m = xt.shape
# n_y, n_a = Wy.shape
# concat = np.zeros((n_a + n_x, m))
# concat[: n_a, :] = a_prev
# concat[n_a :, :] = xt
# 計算 ft (遺忘門), it (更新門)的值
# cct (候選值), c_next (單元狀態),
# ot (輸出門), a_next (隱藏單元)
ft = sigmoid(np.dot(Wf, concat) + bf) # 遺忘門
it = sigmoid(np.dot(Wi, concat) + bi) # 更新門
cct = np.tanh(np.dot(Wc, concat) + bc) # 候選值
c_next = ft * c_prev + it * cct # 單元狀態
ot = sigmoid(np.dot(Wo, concat) + bo) # 輸出門
a_next = ot * np.tanh(c_next) # 隱藏狀態
# 計算LSTM的預測值
yt_pred = softmax(np.dot(Wy, a_next) + by)
# 用於反向傳播的緩存

cache = (a_next, c_next, a_prev, c_prev, ft, it, cct, ot, xt, parameters)

return a_next, c_next, yt_pred, cache/<code>

LSTM向前傳播

我們已經實現了一個時間步長的LSTM,現在我們可以用for循環對它進行迭代,處理一系列的Tx輸入。

手把手構建LSTM的向前傳播(Building a LSTM step by step)

LSTM的多個時間步長

指導:

  • 從變量x 和 parameters中獲得 的維度。
  • 初始化三維張量 , 和 .
  • : 隱藏狀態, shape
  • : 單元狀態, shape
  • : 預測, shape (注意在這個例子裡 ).
  • 注意 將一個變量設置來和另一個變量相等是"按引用複製". 換句話說,就是不用使用c = a, 否則這兩個變量指的是同一個變量,更改任何其中一個變量另一個變量的值都會跟著變。
  • 初始化二維張量
  • 儲存了t時間步長的隱藏狀態,它的變量名是a_next。
  • , 時間步長0時候的初始隱藏狀態,調用該函數時候傳入的值,它的變量名是a0。
  • 和 代表單個時間步長,所以他們的shape都是
  • 通過傳入函數的初始化隱藏狀態來初始化 。
  • 用0來初始化 。
  • 變量名是 c_next.
  • 表示單個時間步長, 所以它的shape是
  • 注意 : create c_next as its own variable with its own location in memory. 不要將它通過3維張量的切片來初始化,換句話說, 不要 c_next = c[:,:,0].
  • 對每個時間步長,做以下事情:
  • 從3維的張量 中, 獲取在時間步長t處的2維切片 。
  • 調用你之前定義的 lstm_cell_forward 函數,獲得隱藏狀態,單元狀態,預測值。
  • 存儲隱藏狀態,單元狀態,預測值到3維張量中。
  • 把緩存加入到緩存列表。
<code>def lstm_forward(x, a0, parameters):
"""
Implement the forward propagation of the recurrent neural network using an LSTM-cell described in Figure (4).

Arguments:
x -- Input data for every time-step, of shape (n_x, m, T_x).
a0 -- Initial hidden state, of shape (n_a, m)
parameters -- python dictionary containing:
Wf -- Weight matrix of the forget gate, numpy array of shape (n_a, n_a + n_x)
bf -- Bias of the forget gate, numpy array of shape (n_a, 1)
Wi -- Weight matrix of the update gate, numpy array of shape (n_a, n_a + n_x)
bi -- Bias of the update gate, numpy array of shape (n_a, 1)
Wc -- Weight matrix of the first "tanh", numpy array of shape (n_a, n_a + n_x)
bc -- Bias of the first "tanh", numpy array of shape (n_a, 1)
Wo -- Weight matrix of the output gate, numpy array of shape (n_a, n_a + n_x)
bo -- Bias of the output gate, numpy array of shape (n_a, 1)
Wy -- Weight matrix relating the hidden-state to the output, numpy array of shape (n_y, n_a)
by -- Bias relating the hidden-state to the output, numpy array of shape (n_y, 1)

Returns:
a -- Hidden states for every time-step, numpy array of shape (n_a, m, T_x)
y -- Predictions for every time-step, numpy array of shape (n_y, m, T_x)
c -- The value of the cell state, numpy array of shape (n_a, m, T_x)
caches -- tuple of values needed for the backward pass, contains (list of all the caches, x)
"""

# 初始化 "caches", 用來存儲每個時間步長的cache值的
caches = []


Wy = parameters['Wy']
# 從 x 和 parameters['Wy'] 的shape中獲取緯度值
n_x, m, T_x = x.shape
n_y, n_a = Wy.shape

# 初始化 "a", "c" and "y"
a = np.zeros((n_a, m, T_x))
c = np.zeros((n_a, m, T_x))
y = np.zeros((n_y, m, T_x))

# 初始化 a_next and c_next
a_next = a0
c_next = np.zeros(a_next.shape)

# loop over all time-steps
for t in range(T_x):
# 從3維張量x中獲取t時間步長的2維張量xt
xt = x[:, :, t]
# 更新下一個時間步長的隱藏狀態, 下一個單元狀態, 計算預測值
a_next, c_next, yt, cache = lstm_cell_forward(xt, a_next, c_next, parameters)
# 把下一個時間步長長的隱藏狀態保存起來
a[:,:,t] = a_next
# 把下一個時間步長長的單元狀態保存起來
c[:,:,t] = c_next
# 把預測值保存起來
y[:,:,t] = yt
# 保存緩存值
caches.append(cache)

# 用於向後傳播
caches = (caches, x)

return a, y, c, caches/<code>

恭喜你!現在,你已經為LSTM實現了前向傳播。使用深度學習框架時,實施前向傳播足以構建出色性能的系統。


分享到:


相關文章: