从零实现循环神经网络
引言
本文一起来看下什么是RNN(循环神经网络),以及如何从零实现一个RNN。
RNN的结构
RNN的特点是能适应可变长度序列,下面是RNN的5种结构:
本文我们构建一个多对一的RNN来实现情感分类。
我们来考虑多对多的RNN结构,输入是x⟨1⟩,x⟨2⟩,⋯,x⟨Tx⟩x^{\langle 1 \rangle} ,x^{\langle 2 \rangle},\cdots,x^{\langle T_x \rangle}x⟨1⟩,x⟨2⟩,⋯,x⟨Tx⟩,想要产生的输出为y⟨1⟩,y⟨2⟩,⋯,y⟨Tx⟩y^{\langle 1 \rangle} ,y^{\langle 2 \rangle},\cdots,y^{\langle T_x \rangle}y⟨1⟩,y⟨2⟩,⋯,y⟨Tx⟩。其中x⟨i⟩x^{\langle i \rangle}x⟨i⟩和y⟨i⟩y^{\langle i \rangle}y⟨i⟩是任意维度的向量。
RNN原理是循环地更新隐藏状态(激活值a⟨i⟩a^{\langle i \rangle}a⟨i⟩),激活值也可以是任意维度,在任意时间步ttt:
- 下个隐藏状态a⟨t⟩a^{\langle t \rangle}a⟨t⟩是通过前一个隐藏状态a⟨t−1⟩a^{\langle t-1 \rangle}a⟨t−1⟩和当前输入x⟨t⟩x^{\langle t \rangle}x⟨t⟩来计算的。
- 而输出值y⟨t⟩y^{\langle t \rangle}y⟨t⟩(预测值)是基于a⟨t⟩a^{\langle t \rangle}a⟨t⟩来计算的。
要注意的是上图是RNN的一个展开图例,实际上都是同一个网络结构,权重和偏差都是一样的。
下面介绍涉及到的符号。
总体来说是这样的: a5(2)[3]⟨4⟩a^{(2)[3]\langle 4 \rangle}_5a5(2)[3]⟨4⟩ 表示第2个训练样本 (2), 第3层 [3], t=4t=4t=4时 <4>, 激活值向量的第5个元素。
输入向量
对于单个时间点(时间步)的单个样本,x(i)⟨t⟩x^{(i) \langle t \rangle }x(i)⟨t⟩是一维的向量。比如在NLP中,假设字典大小为5000,那么单词就有(5000,)(5000,)(5000,)大小的one-hot编码,x(i)⟨t⟩x^{(i) \langle t \rangle }x(i)⟨t⟩的形状也是(5000,)(5000,)(5000,)。
我们用nxn_xnx来表示单个时间点的单个样本的单元数,这个例子是5000。
用ttt来索引时间步,输入向量时间步的长度记为TxT_xTx,假设我们的例子中Tx=10T_x=10Tx=10。
如果我们用了小批次(mini-batch),每个批次有20个样本,批次样本数记为mmm,为了向量化,我们会按列叠加20个样本,得到一个形状为(5000,20,10)(5000,20,10)(5000,20,10)的张量。
所以小批次的形状(nx,m,Tx)(n_x,m,T_x)(nx,m,Tx)。
对于每个时间步x⟨t⟩x^{\langle t \rangle}x⟨t⟩的形状为(nx,m)(n_x,m)(nx,m)。
隐藏状态
从一个时间步传递到另一个时间步的激活值a⟨t⟩a^{\langle t \rangle}a⟨t⟩叫做隐藏状态。
单个训练样本的隐藏状态长度记为nan_ana
预测值
y^\hat yy^也是一个3维的张量(ny,m,Ty)(n_y,m,T_y)(ny,m,Ty)。
- nyn_{y}ny: 预测向量的单元数
- mmm: 批次大小
- TyT_{y}Ty: 预测的时间步长
同样,对于单个时间点,我们有(ny,m)(n_y,m)(ny,m)大小的预测值y^⟨t⟩\hat{y}^{\langle t \rangle}y^⟨t⟩。
前向传播
从RNN前向传播(预测过程)来看是如何从输入到输出的。整个过程是上面的两个公式:
a⟨t⟩=tanh(Waaa⟨t−1⟩+Waxx⟨t⟩+ba)(1)a^{\langle t \rangle} = \tanh(W_{aa} a^{\langle t-1 \rangle} + W_{ax} x^{\langle t \rangle} + b_a) \tag{1} a⟨t⟩=tanh(Waaa⟨t−1⟩+Waxx⟨t⟩+ba)(1)
z⟨t⟩=Wyaa⟨t⟩+byy^⟨t⟩=softmax(z⟨t⟩)(2)z^{\langle t \rangle} = W_{ya} a^{\langle t \rangle} + b_y \\ \hat{y}^{\langle t \rangle} = softmax(z^{\langle t \rangle} ) \tag{2} z⟨t⟩=Wyaa⟨t⟩+byy^⟨t⟩=softmax(z⟨t⟩)(2)
其中WaaW_{aa}Waa是由a⟨t−1⟩a^{\langle t-1 \rangle}a⟨t−1⟩计算a⟨t⟩a^{\langle t \rangle}a⟨t⟩的相关权重, WaxW_{ax}Wax是由xxx计算aaa的相关权重,以及计算aaa的偏差bab_aba;
经过tanh\tanhtanh激活函数后得到新的激活值,经过这个公式可以看出,新的激活值同时考虑了当前的输入和前个时间点的激活值。
得到了新的激活值后再进行一次线性运算,把结果传入softmaxsoftmaxsoftmax(多分类时)中得到输出。涉及到的权重是WyaW_{ya}Wya,由激活值aaa计算输出yyy,以及计算输出的相关偏差byb_yby。
上面是计算时间点ttt的输出过程,如果考虑整个输入序列的话就是下图所示:
一般初始激活值a⟨0⟩=0a^{\langle0 \rangle}=0a⟨0⟩=0,是一个零向量。
代码实现
回到我们本文情感分析的实例,是一个多对一的结构,我们的RNN读完整段句子,然后来判断这段句子对应的情感类别(只有一个输出y^\hat yy^)。
import numpy as npdef softmax(x):e_x = np.exp(x - np.max(x))return e_x / e_x.sum(axis=0)def sigmoid(x):return 1 / (1 + np.exp(-x))class RNN:def __init__(self, n_x, n_y, n_a=32):'''初始化n_x : 输入向量x的大小 词典大小n_y : 输出向量y的大小 类别数量n_a :隐藏单元数'''#np.random.seed(1)self.Wax = np.random.randn(n_a, n_x) / 10000self.Waa = np.random.randn(n_a, n_a) / 10000self.Wya = np.random.randn(n_y, n_a) / 10000# 偏差self.ba = np.random.randn(n_a, 1)self.by = np.random.randn(n_y, 1)def forward(self, x):'''前向传播(预测过程)x : 所有时间步的输入,形状(n_x,m,T_x)返回: n_y,m'''n_x, m, T_x = x.shapen_y, n_a = self.Wya.shapea = np.zeros((n_a, m)) # 初始激活值 a<⁰> = 0# 保存输入x,用于反向传播self.x = x# 保存每个时间点的激活值self.a_his = [a]for t in range(T_x):# 时间点t时的输入xt = x[:, :, t]a = np.tanh(self.Wax.dot(xt) + self.Waa.dot(a) + self.ba) # ba会广播为(n_a,m)self.a_his.append(a)# 多对一的结构,只有在读完整个序列,即最后一个 时间步才计算输出y_pred = softmax(self.Wya.dot(a) + self.by)return y_pred
反向传播
为了进行反向传播,我们需要定义一个损失函数。鉴于我们的输出类别可能有多个,我们就用交叉熵损失函数。其中y^\hat yy^是我们的预测值,这里是由最后一个时间步的激活值a⟨Tx⟩a^{\langle T_x \rangle}a⟨Tx⟩进行线性运算后经过softmax得到的;而yyy是真实值。
L=−∑cyclogy^c=−∑cyclog(softmax(zc))(3)L = -\sum_c y_c \log \hat y_c =-\sum_c y_c \log (softmax(z_c)) \tag{3} L=−c∑yclogy^c=−c∑yclog(softmax(zc))(3)
我们求LLL对zzz的导数dz
,具体过程可以参考博客 Softmax与Cross-entropy的求导,得到:
y^−y(4)\hat y- y \tag{4} y^−y(4)
我们上面说过,这里输入和真实值都是one-hot向量,也就是说,这个公式可以简化为:
y^i−1i(5)\hat y_i - 1_i \tag{5} y^i−1i(5)
也就是说,就是用预测值的某一列减去111即可,其他为零的列不变。
对于最后一个时间步来说:
z⟨Tx⟩=Wyaa⟨Tx⟩+byy^⟨t⟩=softmax(z⟨Tx⟩)z^{\langle T_x \rangle} = W_{ya} a^{\langle T_x \rangle} + by \\ \hat y ^{\langle t \rangle} = softmax(z^{\langle T_x \rangle} ) z⟨Tx⟩=Wyaa⟨Tx⟩+byy^⟨t⟩=softmax(z⟨Tx⟩)
下面我们求对Wya,byW_{ya},b_yWya,by的梯度,此时只需要考虑最后一个激活值到RNN的输出值:
对于WyaW_{ya}Wya,我们有:
∂L∂Wya=∂L∂z⟨Tx⟩⋅∂z⟨Tx⟩∂Wya(6)\frac{\partial L}{\partial W_{ya}} = \frac{\partial L}{\partial z^{\langle T_x \rangle}} \cdot \frac{\partial z^{\langle T_x \rangle}}{\partial W_{ya}} \tag{6} ∂Wya∂L=∂z⟨Tx⟩∂L⋅∂Wya∂z⟨Tx⟩(6)
其中a⟨Tx⟩a^{\langle T_x \rangle}a⟨Tx⟩是最后一个时间步的激活值。有:
∂z⟨Tx⟩∂Wya=a⟨Tx⟩(7)\frac{\partial z^{\langle T_x \rangle}}{\partial W_{ya}} = a^{\langle T_x \rangle} \tag{7} ∂Wya∂z⟨Tx⟩=a⟨Tx⟩(7)
∂L∂Wya=∂L∂z⟨Tx⟩a⟨Tx⟩(8)\frac{\partial L}{\partial W_{ya}} = \boxed{ \frac{\partial L}{\partial z^{\langle T_x \rangle}} a^{\langle T_x \rangle}} \tag{8} ∂Wya∂L=∂z⟨Tx⟩∂La⟨Tx⟩(8)
同理,可以得到
∂z⟨Tx⟩∂by=1(9)\frac{\partial z^{\langle T_x \rangle}}{\partial b_y} = 1 \tag{9} ∂by∂z⟨Tx⟩=1(9)
∂L∂by=∂L∂z⟨Tx⟩(10)\frac{\partial L}{\partial b_y} = \boxed {\frac{\partial L}{\partial z^{\langle T_x \rangle}}} \tag{10} ∂by∂L=∂z⟨Tx⟩∂L(10)
最后,我们需要计算Waa,Wax,baW_{aa},W_{ax},b_aWaa,Wax,ba的梯度,它们会在RNN的每个时间步中使用。有:
∂L∂Wax=∂L∂z⟨Tx⟩∑tTx∂z⟨Tx⟩∂a⟨t⟩⋅∂a⟨t⟩∂Wax\frac{\partial L}{\partial W_{ax}} = \frac{\partial L}{\partial z^{\langle T_x \rangle}} \sum_t^{T_x} \frac{\partial z^{\langle T_x \rangle}}{\partial a^{\langle t \rangle}} \cdot \frac{\partial a^{\langle t \rangle}}{\partial W_{ax}} ∂Wax∂L=∂z⟨Tx⟩∂Lt∑Tx∂a⟨t⟩∂z⟨Tx⟩⋅∂Wax∂a⟨t⟩
因为改变WaxW_{ax}Wax会影响所有的a⟨t⟩a^{\langle t \rangle}a⟨t⟩,然后会影响z⟨Tx⟩z^{\langle T_x \rangle}z⟨Tx⟩和最终的LLL。在计算前向传播时,从左到右,时间点不断增加;而计算反向传播时,我们需要考虑所有的时间,时间点不断减小,来进行反向传播,这被称为BPTT(Backpropagation Through Time)。
在给定的时间点ttt,我们需要计算∂a⟨t⟩∂Wax\frac{\partial a^{\langle t \rangle}}{\partial W_{ax}}∂Wax∂a⟨t⟩:
a⟨t⟩=tanh(Waaa⟨t−1⟩+Waxx⟨t⟩+ba)a^{\langle t \rangle} = \tanh(W_{aa} a^{\langle t-1 \rangle} + W_{ax} x^{\langle t \rangle} + b_a) a⟨t⟩=tanh(Waaa⟨t−1⟩+Waxx⟨t⟩+ba)
tanh\tanhtanh的导数我们也推导过:
∂tanh(x)∂x=1−tanh2(x)\frac{\partial \tanh(x)} {\partial x} = 1 - \tanh^2(x) ∂x∂tanh(x)=1−tanh2(x)
使用链式求导法则可以得到:
∂a⟨t⟩∂Wax=(1−a⟨t⟩2)x⟨t⟩(11)\frac{\partial a^{\langle t \rangle}}{\partial W_{ax}} = \boxed{(1-{a^{\langle t \rangle}}^2)x^{\langle t \rangle}} \tag{11} ∂Wax∂a⟨t⟩=(1−a⟨t⟩2)x⟨t⟩(11)
同理,
∂a⟨t⟩∂Waa=(1−a⟨t⟩2)a⟨t−1⟩(12)\frac{\partial a^{\langle t \rangle}}{\partial W_{aa}} = \boxed{(1-{a^{\langle t \rangle}}^2)a^{\langle t -1\rangle}} \tag{12} ∂Waa∂a⟨t⟩=(1−a⟨t⟩2)a⟨t−1⟩(12)
∂a⟨t⟩∂ba=(1−a⟨t⟩2)(13)\frac{\partial a^{\langle t \rangle}}{\partial b_a} = \boxed{(1-{a^{\langle t \rangle}}^2)} \tag{13} ∂ba∂a⟨t⟩=(1−a⟨t⟩2)(13)
现在就剩下∂z⟨Tx⟩∂a⟨t⟩\frac{\partial z^{\langle T_x \rangle}}{\partial a^{\langle t \rangle}}∂a⟨t⟩∂z⟨Tx⟩了,,我们可以递归地求解:
∂z⟨Tx⟩∂a⟨t⟩=∂z⟨Tx⟩∂a⟨t+1⟩⋅∂a⟨t+1⟩∂a⟨t⟩=∂z⟨Tx⟩∂a⟨t+1⟩(1−a⟨t⟩2)Waa(14)\begin{aligned} \frac{\partial z^{\langle T_x \rangle}}{\partial a ^{\langle t \rangle}} &=\frac{\partial z^{\langle T_x \rangle}}{\partial a ^{\langle t+1 \rangle}} \cdot \frac{\partial a^{\langle t+1 \rangle}}{\partial a ^{\langle t \rangle}} \\ &= \frac{\partial z^{\langle T_x \rangle}}{\partial a ^{\langle t+1 \rangle}}(1 - {a ^{\langle t \rangle}}^2) W_{aa} \end{aligned} \tag{14} ∂a⟨t⟩∂z⟨Tx⟩=∂a⟨t+1⟩∂z⟨Tx⟩⋅∂a⟨t⟩∂a⟨t+1⟩=∂a⟨t+1⟩∂z⟨Tx⟩(1−a⟨t⟩2)Waa(14)
上述公式是我们这个多对一RNN结构的公式,如果是多对多的情况,公式会不一样。
如上图所示,在多对多结构中,在TxT_xTx时,y^⟨Tx⟩\hat y ^{\langle T_x \rangle}y^⟨Tx⟩作为后续节点,此时的计算公式如下(16)(16)(16)所示。而在时间步t(1≤t<Tx)t\,\,(1 \leq t < T_x)t(1≤t<Tx)时,后续节点有两个,分别是当前时刻的输出z⟨t⟩z ^{\langle t \rangle}z⟨t⟩和当前时刻的激活值a⟨t⟩a ^{\langle t \rangle}a⟨t⟩。在求梯度时要考虑这两种情况。
我们实现BPTT的时候会从最后一个激活值开始,然后反向传播。所以当需要计算∂z⟨Tx⟩a⟨t⟩\frac{\partial z^{\langle T_x \rangle}}{a ^{\langle t \rangle}}a⟨t⟩∂z⟨Tx⟩时,我们已经计算了∂z⟨Tx⟩a⟨t+1⟩\frac{\partial z^{\langle T_x \rangle}}{a ^{\langle t+1 \rangle}}a⟨t+1⟩∂z⟨Tx⟩,除了计算最后一个激活值:
∂z⟨Tx⟩a⟨Tx⟩=Wya(15)\frac{\partial z^{\langle T_x \rangle}}{a ^{\langle T_x \rangle}}= W_{ya} \tag{15} a⟨Tx⟩∂z⟨Tx⟩=Wya(15)
∂La⟨Tx⟩=∂LZ⟨Tx⟩⋅∂z⟨Tx⟩a⟨Tx⟩=∂LZ⟨Tx⟩Wya(16)\frac{\partial L}{a ^{\langle T_x \rangle}}= \frac{\partial L}{Z ^{\langle T_x \rangle}} \cdot \frac{\partial z^{\langle T_x \rangle}}{a ^{\langle T_x \rangle}} =\boxed{ \frac{\partial L}{Z ^{\langle T_x \rangle}} W_{ya}} \tag{16} a⟨Tx⟩∂L=Z⟨Tx⟩∂L⋅a⟨Tx⟩∂z⟨Tx⟩=Z⟨Tx⟩∂LWya(16)
现在我们有了需要实现BPTT的所有等式了。
代码实现
def backward(self, dz, learning_rate):'''反向传播的实现dz: 对z的梯度 形状(n_y,m)learning_rate: 学习率'''n_x, m, T_x = self.x.shapen_a = self.Wax.shape[0]# 计算dWya和dbydWya = dz.dot(self.a_his[T_x].T)dby = np.sum(dz, axis=1, keepdims=True)dWax = np.zeros((n_a, n_x))dWaa = np.zeros((n_a, n_a))dba = np.zeros((n_a, 1))# 计算最后一个激活值的梯度 公式(16)da = np.dot(self.Wya.T, dz) # (n_a,m)# BPTTfor t in reversed(range(T_x)):# 计算da * (1-a^2)temp = np.multiply(da, 1 - self.a_his[t + 1] ** 2) # (n_a,m)# 计算dbadba += np.sum(temp, axis=1, keepdims=True)dWaa += temp.dot(self.a_his[t].T)dWax += temp.dot(self.x[:, :, t].T) # n_a,n_xda += np.dot(self.Waa, temp)# 防止梯度爆炸for d in [dWax, dWaa, dWya, dba, dby]:np.clip(d, -1, 1, out=d) # 将梯度限制在[-1,1]self.Waa -= learning_rate * dWaaself.Wax -= learning_rate * dWaxself.Wya -= learning_rate * dWyaself.ba -= learning_rate * dbaself.by -= learning_rate * dby
完整代码
import numpy as npdef softmax(x):e_x = np.exp(x - np.max(x))return e_x / e_x.sum(axis=0)def sigmoid(x):return 1 / (1 + np.exp(-x))class RNN:def __init__(self, n_x, n_y, n_a=32):'''初始化n_x : 输入向量x的大小 词典大小n_y : 输出向量y的大小 类别数量n_a :隐藏单元数'''#np.random.seed(1)self.Wax = np.random.randn(n_a, n_x) / 10000self.Waa = np.random.randn(n_a, n_a) / 10000self.Wya = np.random.randn(n_y, n_a) / 10000# 偏差self.ba = np.random.randn(n_a, 1)self.by = np.random.randn(n_y, 1)def forward(self, x):'''前向传播(预测过程)x : 所有时间步的输入,形状(n_x,m,T_x)返回: n_y,m'''n_x, m, T_x = x.shapen_y, n_a = self.Wya.shapea = np.zeros((n_a, m)) # 初始激活值 a<⁰> = 0# 保存输入x,用于反向传播self.x = x# 保存每个时间点的激活值self.a_his = [a]for t in range(T_x):# 时间点t时的输入xt = x[:, :, t]a = np.tanh(self.Wax.dot(xt) + self.Waa.dot(a) + self.ba) # ba会广播为(n_a,m)self.a_his.append(a)# 多对一的结构,只有在读完整个序列,即最后一个 时间步才计算输出y_pred = softmax(self.Wya.dot(a) + self.by)return y_preddef backward(self, dz, learning_rate):'''反向传播的实现dz: 对z的梯度 形状(n_y,m)learning_rate: 学习率'''n_x, m, T_x = self.x.shapen_a = self.Wax.shape[0]# 计算dWya和dbydWya = dz.dot(self.a_his[T_x].T)dby = np.sum(dz, axis=1, keepdims=True)dWax = np.zeros((n_a, n_x))dWaa = np.zeros((n_a, n_a))dba = np.zeros((n_a, 1))# 计算最后一个激活值的梯度 公式(16)da = np.dot(self.Wya.T, dz) # (n_a,m)# BPTTfor t in reversed(range(T_x)):# 计算da * (1-a^2)temp = np.multiply(da, 1 - self.a_his[t + 1] ** 2) # (n_a,m)# 计算dbadba += np.sum(temp, axis=1, keepdims=True)dWaa += temp.dot(self.a_his[t].T)dWax += temp.dot(self.x[:, :, t].T) # n_a,n_xda += np.dot(self.Waa, temp)# 防止梯度爆炸for d in [dWax, dWaa, dWya, dba, dby]:np.clip(d, -1, 1, out=d) # 将梯度限制在[-1,1]self.Waa -= learning_rate * dWaaself.Wax -= learning_rate * dWaxself.Wya -= learning_rate * dWyaself.ba -= learning_rate * dbaself.by -= learning_rate * dbydef fit(self, X_train, Y_train, epochs=30, mini_batch_size=20, learning_rate=2e-2, print_cost=False, X_test=None,Y_test=None):'''param X_train: input data of size (n_x, m,T_x)param Y_train: labels of shape (n_y,m)return'''m = X_train.shape[1]for i in range(epochs):indexes = np.random.permutation(m)X_mini_batches = [X_train[:, indexes, :][:, k:k + mini_batch_size, :] for k in range(0, m, mini_batch_size)]y_mini_batches = [Y_train[:, indexes][:, k:k + mini_batch_size] for k in range(0, m, mini_batch_size)]num_correct = 0for X_batch, y_batch in zip(X_mini_batches, y_mini_batches):y_pred = self.forward(X_batch)dz = y_pred - y_batchself.backward(dz, learning_rate)i_pred = np.argmax(y_pred, axis=0)i_batch = np.argmax(y_batch, axis=0)num_correct += np.sum(i_pred == i_batch)if print_cost and i % 100 == 99 and X_test is not None:print("Train accuracy : %.3f \t Test accuracy : %.3f after iteration %i" % (num_correct/m,self.evaluate(X_test,Y_test) , i+1))def evaluate(self, X_test, Y_test):y_pred = self.forward(X_test)i_pred = np.argmax(y_pred, axis=0)i_test = np.argmax(Y_test, axis=0)return np.sum(i_pred == i_test) / X_test.shape[1]
下面测试一下我们的RNN
train_data = {'good': 'T','bad': 'F','happy': 'T','sad': 'F','not good': 'F','not bad': 'T','not happy': 'F','not sad': 'T','very good': 'T','very bad': 'F','very happy': 'T','very sad': 'F','i am happy': 'T','this is good': 'T','i am bad': 'F','this is bad': 'F','i am sad': 'F','this is sad': 'F','i am not happy': 'F','this is not good': 'F','i am not bad': 'T','this is not sad': 'T','i am very happy': 'T','this is very good': 'T','i am very bad': 'F','this is very sad': 'F','this is very happy': 'T','i am good not bad': 'T','this is good not bad': 'T','i am bad not good': 'F','i am good and happy': 'T','this is not good and not happy': 'F','i am not at all good': 'F','i am not at all bad': 'T','i am not at all happy': 'F','this is not at all sad': 'T','this is not at all happy': 'F','i am good right now': 'T','i am bad right now': 'F','this is bad right now': 'F','i am sad right now': 'F','i was good earlier': 'T','i was happy earlier': 'T','i was bad earlier': 'F','i was sad earlier': 'F','i am very bad right now': 'F','this is very good right now': 'T','this is very sad right now': 'F','this was bad earlier': 'F','this was very good earlier': 'T','this was very bad earlier': 'F','this was very happy earlier': 'T','this was very sad earlier': 'F','i was good and not bad earlier': 'T','i was not good and not happy earlier': 'F','i am not at all bad or sad right now': 'T','i am not at all good or happy right now': 'F','this was not happy and not good earlier': 'F',
}test_data = {'this is happy': 'T','i am good': 'T','this is not happy': 'F','i am not good': 'F','this is not bad': 'T','i am not sad': 'T','i am very good': 'T','this is very bad': 'F','i am very sad': 'F','this is bad not good': 'F','this is good and happy': 'T','i am not good and not happy': 'F','i am not at all sad': 'T','this is not at all good': 'F','this is not at all bad': 'T','this is good right now': 'T','this is sad right now': 'F','this is very bad right now': 'F','this was good earlier': 'T','i was not happy and not good earlier': 'F',
}# -*- coding: utf-8 -*-
# @Time : 2020-9-22 13:51
# @Author : Jue
import numpy as npdef text2vector(text, vocab_size, T_x):'''返回一个句子的one-hot向量表示 (n_x,T_x)- text :句子- vocab_size : 词典大小- T_x : 句子最长长度'''inputs = np.zeros((vocab_size, T_x))t = 0for w in text.split(' '):v = np.zeros(vocab_size)v[word_to_idx[w]] = 1inputs[:, t] = vt += 1if t == T_x:breakreturn inputs# 返回句子最大长度
def getTx(train_data):return max(len(text.split(' ')) for text in train_data)def getData(data, vocab_size, label_dic):'''return:x : 所有时间步的输入,形状(n_x,m,T_x)'''# text ,labelt_x = getTx(data.keys())items = list(data.items())m = len(items)X = np.zeros((vocab_size, t_x, m)) # n_x,t_x,mY = np.zeros((2, m))i = 0for x, y in items:inputs = text2vector(x, vocab_size, t_x)X[..., i] = inputsY[label_dic[y], i] = 1i += 1X = X.transpose(0, 2, 1)return X, Yif __name__ == '__main__':labels = ['T', 'F']d = {l: i for i, l in enumerate(labels)}vocab = list(set([w for text in train_data.keys() for w in text.split(' ')]))vocab_size = len(vocab)print('%d unique words found' % vocab_size)# Assign indices to each word.word_to_idx = {w: i for i, w in enumerate(vocab)}idx_to_word = {i: w for i, w in enumerate(vocab)}X_train, Y_train = getData(train_data, vocab_size, d)X_test, Y_test = getData(test_data, vocab_size, d)
上面是把数据集转换成我们想要的向量化表示
rnn = RNN(vocab_size, 2)
rnn.fit(X_train, Y_train, mini_batch_size=8, epochs=2000, print_cost=True,X_test=X_test,Y_test=Y_test)
同时打印出训练集的准确率和测试集的准确率。由于数据比较简单,这里可以得到100%的准确率。
可以看到RNN能学到not
这种否定表达。
参考
- 吴恩达深度学习课程
- An Introduction to Recurrent Neural Networks for Beginners
- 吴恩达深度学习——循环神经网络
- Softmax与Cross-entropy的求导
从零实现循环神经网络相关推荐
- 零基础入门深度学习(5) - 循环神经网络
往期回顾 在前面的文章系列文章中,我们介绍了全连接神经网络和卷积神经网络,以及它们的训练和使用.他们都只能单独的取处理一个个的输入,前一个输入和后一个输入是完全没有关系的.但是,某些任务需要能够更好的 ...
- 零基础入门深度学习(5) - 循环神经网络【转】
本文转载自:https://zybuluo.com/hanbingtao/note/541458 在前面的文章系列文章中,我们介绍了全连接神经网络和卷积神经网络,以及它们的训练和使用.他们都只能单独的 ...
- 循环神经网络 递归神经网络_如何用递归神经网络预测空气污染
循环神经网络 递归神经网络 After the citizen science project of Curieuze Neuzen, I wanted to learn more about air ...
- RNN 扫盲:循环神经网络解读及其 PyTorch 应用实现
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 来自 | 知乎 作者 | Lucas 地址 | https://z ...
- Nat. Mach. Intell. | 利用条件循环神经网络生成特定性质分子
作者 | 陆丰庆 今天给大家介绍瑞士知名药企阿斯利康和伯尔尼大学的 Esben Jannik Bjerrum团队在Nature Machine Intelligence上的一篇论文.该研究提出基于分子 ...
- 独家 | 菜鸟必备的循环神经网络指南(附链接)
作者:Victor Zhou 翻译:王雨桐 校对:吴金迪 本文约3800字,建议阅读15分钟. 本文将介绍最基础的循环神经网络(Vanilla RNNs)的概况,工作原理,以及如何在Python中实 ...
- 一文搞懂RNN(循环神经网络)
基础篇|一文搞懂RNN(循环神经网络) https://mp.weixin.qq.com/s/va1gmavl2ZESgnM7biORQg 神经网络基础 神经网络可以当做是能够拟合任意函数的黑盒子,只 ...
- 简单入门循环神经网络RNN:时间序列数据的首选神经网络
更多深度文章,请关注:https://yq.aliyun.com/cloud 随着科学技术的发展以及硬件计算能力的大幅提升,人工智能已经从几十年的幕后工作一下子跃入人们眼帘.人工智能的背后源自于大数据 ...
- 循环神经网络(RNN, Recurrent Neural Networks)介绍
循环神经网络(RNN, Recurrent Neural Networks)介绍 循环神经网络(Recurrent Neural Networks,RNNs)已经在众多自然语言处理(Natural ...
- 【阿里云课程】循环神经网络:RNN及其改进
大家好,继续更新有三AI与阿里天池联合推出的深度学习系列课程,本次更新内容为第8课中的一节,介绍如下: RNN及其改进 本节课内容为:深度学习系列课程第8期,讲述循环神经网络原理与优化,门控时序网络之 ...
最新文章
- 沈向洋出任董事长李笛任CEO,「微软」小冰变身「中国」小冰
- Firebug和Yslow是个好工具
- 网络分析系统_MetagenoNets:在线宏基因组网络分析实操教程
- 【Java面试题】48 GC是什么? 为什么要有GC?
- Statistical language model 统计语言模型
- 数据源名称和 64 位操作系统
- 还在维护吗_你的模具生锈了吗?来了解一下这些防锈维护事项
- 文档丨Oracle 三种迁移方案
- 在 Windows 10 中查找 BitLocker 恢复密钥
- 21. Upgrade-Insecure-Requests: 1
- eclipse中输入@符号自动提示Annotation
- typescript step by step interface class
- Windows下配置安装Git(一)
- lisp方格网法计算土方量_CASS方格网法如何计算土方量
- 超级计算机计算峰值,世界运算最快计算机,中国神威·太湖之光(其峰值计算速度达每秒1...
- 【树莓派】树莓派系统安装
- 编译原理(二)文法和语言、符号和符号串、文法的类型、语法树
- 网站 被降权的四种处理方法
- 详解EBS接口开发之销售订单挑库发放
- 配置IKAnalyzer扩展词库
热门文章
- DMA驱动开发(6,参考资料)有用链接
- Laser Reflections solutions
- [MATLAB]MATLAB中SIMULINK常用命令表
- [微软官网] SQLSERVER 执行页面还原
- Openresty 与 Tengine
- 用 mCustomScrollbar 滚动条插件实现滚动更新添加数据
- jQuery - slice( start, [end] ) Method
- JAVA笔记12__字节、字符缓冲流/打印流/对象流/
- Delphi7中默认没有安装的官方控件
- 如何用C#写一个简单的Login窗口