循环神经网络RNN的基本组件(五)
生活随笔
收集整理的這篇文章主要介紹了
循环神经网络RNN的基本组件(五)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
RNN的基礎知識和公式推導參考:
https://www.zybuluo.com/hanbingtao/note/541458
下面的部分實現代碼也是基于上面的文章給出的,通過實現其中的組件來加深對RNN原理的認識,并且可以熟悉代碼實現的框架。
這里強調下:循環神經網絡的訓練
循環神經網絡的訓練算法:BPTT
BPTT算法是針對循環層的訓練算法,它的基本原理和BP算法是一樣的,也包含同樣的三個步驟:
最后再用隨機梯度下降算法更新權重。
這里直接貼出代碼部分:
activators.py
下面是代碼的主要部分:
# -*- coding: UTF-8 -*-import numpy as np #from cnn import element_wise_op from activators import ReluActivator, IdentityActivator# 對numpy數組進行element wise操作 # element_wise_op函數實現了對numpy數組進行按元素操作,并將返回值寫回到數組中 def element_wise_op(array, op):for i in np.nditer(array,op_flags=['readwrite']):i[...] = op(i)# 用RecurrentLayer類來實現一個循環層 class RecurrentLayer(object):def __init__(self, input_width, state_width,activator, learning_rate):self.input_width = input_widthself.state_width = state_widthself.activator = activatorself.learning_rate = learning_rateself.times = 0 # 當前時刻初始化為t0self.state_list = [] # 保存各個時刻的stateself.state_list.append(np.zeros((state_width, 1))) # 初始化s0self.U = np.random.uniform(-1e-4, 1e-4,(state_width, input_width)) # 初始化Uself.W = np.random.uniform(-1e-4, 1e-4,(state_width, state_width)) # 初始化Wdef forward(self, input_array):'''根據『式2』進行前向計算'''self.times += 1state = (np.dot(self.U, input_array) +np.dot(self.W, self.state_list[-1]))element_wise_op(state, self.activator.forward)self.state_list.append(state)print 'state_list:\n',self.state_listdef backward(self, sensitivity_array,activator):'''實現BPTT算法'''self.calc_delta(sensitivity_array, activator)self.calc_gradient()def update(self):'''按照梯度下降,更新權重'''self.W -= self.learning_rate * self.gradientdef calc_delta(self, sensitivity_array, activator):self.delta_list = [] # 用來保存各個時刻的誤差項for i in range(self.times):self.delta_list.append(np.zeros((self.state_width, 1)))self.delta_list.append(sensitivity_array) print 'delta_list:\n',self.delta_list# 迭代計算每個時刻的誤差項for k in range(self.times - 1, 0, -1): # [1] self.calc_delta_k(k, activator)def calc_delta_k(self, k, activator):'''根據k+1時刻的delta計算k時刻的delta'''state = self.state_list[k+1].copy()print 'state_1:\n',state,np.shape(state)print 'state_2:\n',state[:,0],np.shape(state[:,0])print 'state_3:\n',np.diag(state[:,0]),np.shape(np.diag(state[:,0]))element_wise_op(state,activator.backward)self.delta_list[k] = np.dot(np.dot(self.delta_list[k+1].T, self.W),np.diag(state[:,0])).Tdef calc_gradient(self):self.gradient_list = [] # 保存各個時刻的權重梯度for t in range(self.times + 1):self.gradient_list.append(np.zeros((self.state_width, self.state_width)))for t in range(self.times, 0, -1):self.calc_gradient_t(t)# 實際的梯度是各個時刻梯度之和self.gradient = reduce(lambda a, b: a + b, self.gradient_list,self.gradient_list[0]) # [0]被初始化為0且沒有被修改過def calc_gradient_t(self, t):'''計算每個時刻t權重的梯度'''print 'tt..\n',self.delta_list[t],'\n',self.state_list[t-1].Tgradient = np.dot(self.delta_list[t],self.state_list[t-1].T)print 'np.shape(gradient):',np.shape(gradient)self.gradient_list[t] = gradient# 初始化數據 def data_set():x = [np.array([[1], [2], [3]]),np.array([[2], [3], [4]])]d = np.array([[1], [2]])return x, ddef gradient_check():'''梯度檢查'''# 設計一個誤差函數,取所有節點輸出項之和error_function = lambda o: o.sum()rl = RecurrentLayer(3, 2, IdentityActivator(), 1e-3)# 計算forward值x, d = data_set()rl.forward(x[0])rl.forward(x[1])# 求取sensitivity mapsensitivity_array = np.ones(rl.state_list[-1].shape,dtype=np.float64)# 計算梯度rl.backward(sensitivity_array, IdentityActivator())def test():# input_width, state_width,activator, learning_ratel = RecurrentLayer(3, 2, ReluActivator(), 1e-3)x, d = data_set()l.forward(x[0])l.forward(x[1])l.backward(d, ReluActivator())return lif __name__ == '__main__':test()運行結果:
state_list: [array([[ 0.],[ 0.]]), array([[ 0. ],[ 0.0001993]])] state_list: [array([[ 0.],[ 0.]]), array([[ 0. ],[ 0.0001993]]), array([[ 0. ],[ 0.00026813]])] delta_list: [array([[ 0.],[ 0.]]), array([[ 0.],[ 0.]]), array([[1],[2]])] state_1: [[ 0. ][ 0.00026813]] (2L, 1L) state_2: [ 0. 0.00026813] (2L,) state_3: [[ 0. 0. ][ 0. 0.00026813]] (2L, 2L) tt.. [[1][2]] [[ 0. 0.0001993]] np.shape(gradient): (2L, 2L) tt.. [[ 0.00000000e+00][ -3.08449714e-05]] [[ 0. 0.]] np.shape(gradient): (2L, 2L)在代碼中添加打印輸出,可以清楚的看到中間的運行細節。
注意:
- 這里僅僅是計算了循環層權重矩陣W的梯度更新,沒有對誤差函數在t時刻對權重矩陣U的梯度進行更新計算,所以這里只是RNN實現過程中的一步。
- 還有一點就是對作者的代碼進行了一點改動
個人認為這里的element_wise_op中的應用在self.state_list[k+1]的拷貝,而不是self.state_list[k+1],這樣就和文章中的推導公式一致了。
總結
以上是生活随笔為你收集整理的循环神经网络RNN的基本组件(五)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 面向对象实现CNN(四)
- 下一篇: 温州瓯海白象家园房价有升值空间吗