手把手構建LSTM的向前傳播(Building a LSTM step by step)
阿新 • • 發佈:2020-03-22
本篇是在之前兩篇基礎上接著寫的:
吳恩達deepLearning.ai迴圈神經網路RNN學習筆記(理論篇)
從頭構建迴圈神經網路RNN的向前傳播(rnn in pure python)
也可以不看,如果以下有看不懂的,再回過頭來看上面兩篇也行。
前言
目錄
更新門 概念:
單元狀態 概念:
輸出門 概念:
隱藏狀態 概念:
預測值 概念:
- 閥門和狀態描述
- LSTM cell
- LSTM整個過程
- 遺忘門,更新門,輸出門的作用是什麼,它們是怎麼發揮作用的。
- 單元狀態 cell state 是如何來選擇性保留資訊。
- 假設我們正在閱讀一段文字中的單詞,並計劃使用LSTM跟蹤語法結構,例如判斷主體是單數(“ puppy”)還是複數(“ puppies”)。
- 如果主體更改其狀態(從單數詞更改為複數詞),那麼先前的記憶狀態將過時,因此我們“忘記”過時的狀態。
- “遺忘門”是一個張量,它包含介於0和1之間的值。
- 如果遺忘門中的一個單元的值接近於0,則LSTM將“忘記”之前單元狀態相應單位的儲存值。
- 如果遺忘門中的一個單元的值接近於1,則LSTM將記住大部分相應的值。
- 包含控制遺忘門行為的權重。
- 之前時間步長的隱藏狀態和當前時間步長的輸入連線在一起乘以。
- sigmoid函式讓每個門的張量值在0到1之間。
- 遺忘門和之前的單元狀態有相同的shape。
- 這就意味著它們可以按照元素相乘。
- 將張量和相乘相當於在之前的單元狀態應用一層蒙版。
- 如果中的單個值是0或者接近於0,那麼乘積就接近0.
- 這就是使得儲存在對應單位的值在下一個時間步長不會被記住。
- 同樣,如果中的1個值接近於1,那麼乘積就接近之前單元狀態的原始值。
- LSTM就會在下一個時間步長中保留對應單位的值。
- Wf: 遺忘門的權重
- Wb: 遺忘門的偏差
- ft: 遺忘門
- 候選值是包含當前時間步長資訊的張量,它可能會儲存在當前單元狀態中。
- 傳遞候選值的哪些部分取決於更新門。
- 候選值是一個張量,它的範圍從-1到1。
- 代字號“〜”用於將候選值與單元狀態區分開。
- 'tanh'函式產生的值介於-1和+1之間。
- cct: 候選值
更新門 概念:
- 我們使用更新門來確定候選的哪些部分要新增到單元狀態中。
- 更新門是包含0到1之間值的張量。
- 當更新門中的單位接近於0時,它將阻止候選值中的相應值傳遞到。
- 當更新門中的單位接近1時,它允許將候選的值傳遞到。
- 注意,我們使用下標“i”而不是“u”來遵循文獻中使用的約定。
- 類似於遺忘門(此處為),用sigmoid函式乘後值就落在了0到1之間。
- 將更新門與候選元素逐元素相乘,並將此乘積()用於確定單元狀態。
- wi是更新門的權重
- bi是更新門的偏差
- it是更新門
單元狀態 概念:
- 單元狀態是傳遞到未來時間步長的“記憶/記憶體(memory)”。
- 新單元狀態是先前單元狀態和候選值的組合。
- 之前的單元狀態通過遺忘門調整(加權)。
- 候選值通過更新門調整(加權)。
- c: 單元狀態,包含所有的時間步長,c的shape是(na, m, T)
- c_next: 下一個時間步長的單元狀態,的shape (na, m)
- c_prev: 之前的單元狀態,的shape (na, m)
輸出門 概念:
- 輸出門決定時間步長要輸出的預測值。
- 輸出門與其他門一樣,它包含從0到1的值。
- 輸出門由之前的隱藏狀態和當前的輸入 決定。
- sigmoid函式讓值的範圍在0到1之間。
- wo: 輸出門的權重
- bo: 輸出門的偏差
- ot: 輸出門
隱藏狀態 概念:
- 隱藏狀態將傳遞到LSTM單元的下一個時間步長。
- 它用於確定下一個時間步長的三個門()。
- 隱藏狀態也用於預測。
- 隱藏狀態由單元狀態結合輸出門確定。
- 單元狀態通過“ tanh”函式把值縮放到-1和+1之間。
- 輸出門的作用就像一個“掩碼mask”,它既可以保留的值,也可以使這些值不包含在隱藏狀態中。
- a: 隱藏狀態,包含時間步長,shape (na, m, Tx)
- a_prev: 前一步的隱藏狀態,的shape (na, m)
- a_next: 下一步的隱藏狀態,的shape (na, m)
預測值 概念:
- 此用例的預測是分類,所以我們用softmax。
- y_pred: 預測,包含所有的時間步長,的shape (ny, m, Tx),注意,本例中Tx=Ty。
- yt_pred: 當前時間步長t的預測值,shape是(ny, m)
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, cacheLSTM向前傳播 我們已經實現了一個時間步長的LSTM,現在我們可以用for迴圈對它進行迭代,處理一系列的Tx輸入。 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維張量中。
- 把快取加入到快取列表。
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恭喜你!現在,你已經為LSTM實現了前向傳播。使用深度學習框架時,實施前向傳播足以構建出色效能的系統。 &nbs