RNN学习笔记(六)-GRU,LSTM 代码实现
2016-04-22 18:07
731 查看
RNN学习笔记(六)-GRU,LSTM 代码实现
在这篇文章里,我们将讨论GRU/LSTM的代码实现。在这里,我们仍然沿用RNN学习笔记(五)-RNN 代码实现里的例子,使用GRU/LSTM网络建立一个2-gram的语言模型。项目源码:https://github.com/rtygbwwwerr/RNN
参考项目:https://github.com/dennybritz/rnn-tutorial-gru-lstm
1.网络结构
为了解决当词典中的words数量很大时,输入向量过长的问题,我们在输入层和隐层之间引入了Embedding Layer,通过该层,输入的one-hot将被转换为word的Embedding vector。1.1 GRU网络
1.2 LSTM网络
略。2.代码实现
这里我们重点讨论bptt部分(*:“⊙”表示elemwise乘法运算)。对于GRU网络来说,有zrhstz(o)(t)ot=σ(xtUz+st−1Wz)=σ(xtUr+st−1Wr)=tanh(xtUh+(st−1⊙r)Wh)=(1−z)⊙h+z⊙st−1=stV+c=softmax(z(o)(t))
softmax(x)′=softmax(x)[1−softmax(x)]
输出层节点的输入值z(o)k(t)导数如下:
δ(o)k(t)=∂Lt∂z(o)k(t)
=ok(t)−1
写成向量形式为:
δ(o)(t)=o(t)−1
∂Lt∂V=δ(o)k(t)∂z(o)k(t)∂V=[ok(t)−1]⊙st
可以看到,这一层的导数与常规RNN是一致的。
从隐层开始,导数将有所不同,我们先来看下单个GRU网络节点结构:
这里,先对符号做一下约定:
iz(t)=xtUz+s(t−1)Wz:t时刻update gate 对应的输入
ir(t)=xtUr+s(t−1)Wr:t时刻rest gate 对应的输入
ih(t)=xtUh+(s(t−1)⊙r(t))Wh:t时刻隐单元对应的输入
io(t)=(1−z(t))⊙h(t)+z(t)⊙s(t−1):t时刻output gate对应的输入
f(io(t))=io(t)=s(t)=(1−z(t))⊙h(t)+z(t)⊙s(t−1)
δo(t)=∂Lt∂io(t)=δ(o)(t)∂z(o)(t)∂s(t)∂s(t)∂io(t)=δ(o)(t)∂z(o)(t)∂s(t)∂f(io(t))∂io(t)
=δ(o)(t)⋅V⋅1
δz(t)
=δo(t)∂io(t)∂z(t)∂z(t)∂iz(t)
=δo(t)⊙[s(t−1)−h(t)]⊙σ(iz(t))′
这里我们设:
f(x)=0.2∗x+0.5
σ(x)=⎧⎩⎨f(x)010≤f(x)≤1f(x)<0f(x)>1
σ(x)′=⎧⎩⎨0.2000≤f(x)≤1f(x)<0f(x)>1
δh(t)
=δo(t)∂io(t)∂h(t)∂h(t)∂ih(t)
=δo(t)⊙(1−z(t))⊙[1−tanh(ih(t))2]
=δo(t)⊙(1−z(t))⊙[1−h(t)2]
δr(t)
=δh(t)∂ih(t)∂r(t)∂r(t)∂ir(t)
=[δh(t)⊙s(t−1)]⋅Wh
根据复合函数的求导法则,前一时刻的输出门误差δo(t−1)由四部分的偏导数组成(对应图中的4条蓝线)
δo(t−1)=∂Lt∂io(t−1)
=δz(t)∂iz(t)∂s(t−1)+δr(t)∂ir(t)∂s(t−1)+δh(t)∂ih(t)∂s(t−1)+δo(t)∂io(t)∂s(t−1)
=δz(t)⋅Wz+δr(t)⋅Wr+(δh(t)⊙r(t))⋅Wh+δo(t)⊙z(t)
得到了δo(t−1)依次带入前边的公式,即可求出δz(t−1),δr(t−1),δh(t−1)
而对于t时刻的输入x(t)求导,可得:
δx(t)=∂Lt∂x(t)
=δz(t)∂iz(t)∂x(t)+δr(t)∂ir(t)∂x(t)+δh(t)∂ih(t)∂x(t)
=δz(t)⋅Uz+δr(t)⋅Ur+δh(t)⋅Uh
当存在多层GRU的时候,上一层的输出等于当前层的输入,即:s(l−1)(t)=x(l)(t)
所以就有:
δ(l−1)o(t)=δ(l)x(t)=δ(l)z(t)⋅U(l)z+δ(l)r(t)⋅U(l)r+δ(l)h(t)⋅U(l)h
以此为基础,可以按上边的方法推导出δ(l−1)z(t),δ(l−1)r(t),δ(l−1)h(t)
的计算公式,这里就不一一推导了。
Output Layer的梯度:
ΔV(t)=∂Lt∂V=δ(o)(t)∂z(o)(t)∂V=[o(t)−1]⊙s(t)
Δc(t)=∂Lt∂c=δ(o)(t)∂z(o)(t)∂c=o(t)−1
Hidden Layer:
ΔWr(t)=δr(t)∂ir(t)∂Wr=δr(t)⊙s(t−1)
ΔWh(t)=δh(t)∂ih(t)∂Wh=δh(t)⊙s(t−1)⊙r(t)
ΔWz(t)=δz(t)∂iz(t)∂Wz=δz(t)⊙s(t−1)
ΔUr(t)=δr(t)∂ir(t)∂Ur=δr(t)⊙x(t)
ΔUh(t)=δh(t)∂ih(t)∂Uh=δh(t)⊙x(t)
ΔUz(t)=δz(t)∂iz(t)∂Uz=δz(t)⊙x(t)
对于Embedding层,E为Nh×Ni的矩阵,E的每一列可以看做词典中一个单词的Embedding向量表示
ix(t)=E⋅word(t)
ΔE(t)=δ(1)x(t)∂ix(t)∂E=δ(1)x(t)word(t)=δ(1)x(t)
word(t)为单词的one-hot向量,其中的分量只有一个为1,其余为0:
[w1,w2,...,wi,...,wNi]=[0,0,...,1,...,0],其中只有分量wi=1.所以E⋅word(t)的结果相当于取出E的第i列。在使用numpy的时候,为了实现的方便,使用了一个lookup Table,输入的为非零向量的索引i,通过取E矩阵的第i列直接得出word的Embedding向量x:
x = self.E[:,word];
bias项的梯度(上边的公式中省略了,例如:iz(t)=xtUz+st−1Wz+bz)
Δbr(t)=δr(t)∂ir(t)∂br=δr(t)
Δbh(t)=δh(t)∂ih(t)∂bh=δh(t)
Δbz(t)=δz(t)∂iz(t)∂bz=δz(t)
对应源码:
def bptt(self, x, y): word = x T = len(y) o, s, z, r, h = self.forward_propagation(word) x = self.E[:,word]; dLdE = np.zeros(self.E.shape) dLdU = np.zeros(self.U.shape) dLdV = np.zeros(self.V.shape) dLdW = np.zeros(self.W.shape) dLdb = np.zeros(self.b.shape) dLdc = np.zeros(self.c.shape) #self.bptt_truncate delta_o = o delta_o[np.arange(len(y)), y] -= 1. for t in np.arange(T)[::-1]: dLdV += np.outer(delta_o[t], s[t][0].T) dLdc += delta_o[t] delta_x = 0 for l in np.arange(self.layer_num)[::-1]: # Initial delta calculation delta_ho = self.V.T.dot(delta_o[t]) for bptt_step in np.arange(max(0, t-self.bptt_truncate), t+1)[::-1]: delta_func = map(delta_hard_sigmoid, z[bptt_step][l]) delta_hz = delta_ho * (s[bptt_step - 1][l] - h[bptt_step][l]) * delta_func delta_hh = delta_ho * (1 - z[bptt_step][l]) * (1-(h[bptt_step][l]**2)) delta_hr = self.W[l][1].T.dot(delta_hh * s[bptt_step - 1][l]) delta_x = self.U[l][0].T.dot(delta_hz) + self.U[l][1].T.dot(delta_hr) + self.U[l][2].T.dot(delta_hh) #dW^z dLdW[l][0] += delta_hz * s[bptt_step-1][l] #dW^r dLdW[l][1] += delta_hr * s[bptt_step-1][l] #dW^h dLdW[l][2] += delta_hh * s[bptt_step-1][l] * r[bptt_step][l] input = (x[:, bptt_step] if l < 1 else (s[bptt_step][l - 1])) #dU^z dLdU[l][0] += delta_hz * input #dU^r dLdU[l][1] += delta_hr * input #dU^h dLdU[l][2] += delta_hh * input #db^z dLdb[l][0] += delta_hz #db^r dLdb[l][1] += delta_hr #db^h dLdb[l][2] += delta_hh #while is the first hidden layer,you need to count the dE if l < 1: dLdE[:, word[t]] += delta_x delta_ho = self.W[l][0].T.dot(delta_hz) + self.W[l][1].T.dot(delta_hr) + self.W[l][2].T.dot(delta_hh * r[bptt_step][l]) + delta_ho * z[bptt_step][l] return [dLdE, dLdU, dLdV, dLdW, dLdb, dLdc]
把整个网络展开(单层):
相关文章推荐
- 用Python从零实现贝叶斯分类器的机器学习的教程
- My Machine Learning
- 机器学习---学习首页 3ff0
- bp神经网络及matlab实现
- 反向传播(Backpropagation)算法的数学原理
- 关于SVM的那点破事
- 也谈 机器学习到底有没有用 ?
- 如何用70行代码实现深度神经网络算法
- 量子计算机编程原理简介 和 机器学习
- 近200篇机器学习&深度学习资料分享(含各种文档,视频,源码等)
- 已经证实提高机器学习模型准确率的八大方法
- 基于神经网络的预测模型
- 初识机器学习算法有哪些?
- 机器学习相关的库和工具
- 10个关于人工智能和机器学习的有趣开源项目
- 机器学习实践中应避免的7种常见错误
- 机器学习常见的算法面试题总结
- 机器学习书单
- 北美常用的机器学习/自然语言处理/语音处理经典书籍
- 人工智能扫盲漫谈篇 & 2018年1月新课资源推荐