现在的任务是要利用如下语料来给apple打标签:
第一句话:I like eating apple!(我喜欢吃苹果!)
第二句话:The Apple is a great company!(苹果真是一家很棒的公司!)
第一个apple是一种水果,第二个apple是苹果公司。
全连接神经网络没有利用上下文来训练模型,模型在训练的过程中,预测的准确程度,取决于训练集中哪个标签多一些,如果水果多,就打上水果的标签,如果苹果公司多,就打上苹果公司;显然这样的模型不能对未知样本进行准确的预测。
左图如果不考虑WWW,就是一个全连接神经网络:
如果考虑WWW,可以展开为右图:
核心公式如下:
ot=g(V⋅st)st=f(U⋅xt+W⋅st−1)\begin{array}{l} o_{t}=g\left(V \cdot s_{t}\right) \\ s_{t}=f\left(U \cdot x_{t}+W \cdot s_{t-1}\right) \end{array} ot=g(V⋅st)st=f(U⋅xt+W⋅st−1)
循环神经网络的核心思想就是输出oto_tot不仅仅与xtx_txt有关,还与xt−1x_{t-1}xt−1及前序时刻的输入有关。
每一时刻的隐藏状态都不仅由该时刻的输入决定,还取决于上一时刻的隐藏层的值,如果一个句子很长,到句子末尾时,它将记不住这个句子的开头的内容详细内容。详细分析见https://zhuanlan.zhihu.com/p/76772734。
LSTM是高级的RNN,与RNN的主要区别在于:
从上图可以看到,LSTM多了三个门:输入门,遗忘门,输出门。
将一个时间点上RNN和LSTM内部结构进行对比,就可以看出两者的区别:
Z=tanh(W[xt,ht−1])Zi=σ(Wi[xt,ht−1])Zf=σ(Wf[xt,ht−1])Zo=σ(Wo[xt,ht−1])\begin{gathered} Z=\tanh \left(W\left[x_t, h_{t-1}\right]\right) \\ Z_i=\sigma\left(W_i\left[x_t, h_{t-1}\right]\right) \\ Z_f=\sigma\left(W_f\left[x_t, h_{t-1}\right]\right) \\ Z_o=\sigma\left(W_o\left[x_t, h_{t-1}\right]\right) \end{gathered} Z=tanh(W[xt,ht−1])Zi=σ(Wi[xt,ht−1])Zf=σ(Wf[xt,ht−1])Zo=σ(Wo[xt,ht−1])
it=σ(Wi⋅[ht−1,xt]+bi)C~t=tanh(WC⋅[ht−1,xt]+bC)\begin{aligned} i_t & =\sigma\left(W_i \cdot\left[h_{t-1}, x_t\right]+b_i\right) \\ \tilde{C}_t & =\tanh \left(W_C \cdot\left[h_{t-1}, x_t\right]+b_C\right) \end{aligned} itC~t=σ(Wi⋅[ht−1,xt]+bi)=tanh(WC⋅[ht−1,xt]+bC)
这个门有两个部分:
ft=σ(Wf⋅[ht−1,xt]+bf)f_t = \sigma(W_f \cdot [h_{t-1}, x_t] +b_f) ft=σ(Wf⋅[ht−1,xt]+bf)
首先说一下[ht−1,xt][h_{t−1},x_t][ht−1,xt]这个东西就代表把两个向量连接起来(操作与numpy.concatenate相同)。然后ftf_tft就是一个网络的输出,看起来还是很简单的,执行的是上图中的公式。 具体的操作如下图所示:
为什么称为遗忘门呢?因为σ\sigmaσ的输出在0到1之间,这个输出 ftf_tft逐位与Ct−1C_{t-1}Ct−1的元素相乘,当ftf_tft的某一位的值为0的时候,这Ct−1C_{t-1}Ct−1对应位的信息被去掉,而值为(0, 1),对应位的信息就保留了一部分,只有值为1的时候,对应的信息才会完整的保留。
A. Graves, A. Mohamed and G. Hinton, Speech Recognition with Deep Recurrent Neural Networks, arXiv:1303.5778
输入特征向量,输出对应的文字。用RNN进行Triphone的识别,在TIMIT数据集上获得了比DNN-HMM更高的识别率。
训练样本:中国古诗集
O. Vinyals et al. Show and tell: A neural image caption generator, arXiv:1411.4555v1, 2014.
输入:图像;
输出:描述性文字。
描述准确的例子:
描述不准确的例子:
用RNN来实现一个八位的二进制数加法运算。
代码实现地址:
http://iamtrask.github.io/2015/11/15/anyone-can-code-lstm/
import copy, numpy as np
np.random.seed(0)
# sigmoid函数
def sigmoid(x):output = 1 / (1 + np.exp(-x))return output
# sigmoid导数
def sigmoid_output_to_derivative(output):return output * (1 - output)
# 训练数据生成
int2binary = {}
binary_dim = 8
largest_number = pow(2, binary_dim)
binary = np.unpackbits(np.array([range(largest_number)], dtype=np.uint8).T, axis=1)
for i in range(largest_number):int2binary[i] = binary[i]
# 初始化一些变量
alpha = 0.1 #学习率
input_dim = 2 #输入的大小
hidden_dim = 8 #隐含层的大小
output_dim = 1 #输出层的大小
# 随机初始化权重
synapse_0 = 2 * np.random.random((hidden_dim, input_dim)) - 1 #(8, 2)
synapse_1 = 2 * np.random.random((output_dim, hidden_dim)) - 1 #(1, 8)
synapse_h = 2 * np.random.random((hidden_dim, hidden_dim)) - 1 #(8, 8)
synapse_0_update = np.zeros_like(synapse_0) #(8, 2)
synapse_1_update = np.zeros_like(synapse_1) #(1, 8)
synapse_h_update = np.zeros_like(synapse_h) #(8, 8)
# 开始训练
for j in range(100000):# 二进制相加a_int = np.random.randint(largest_number / 2) # 随机生成相加的数a = int2binary[a_int] # 映射成二进制值b_int = np.random.randint(largest_number / 2) # 随机生成相加的数b = int2binary[b_int] # 映射成二进制值# 真实的答案c_int = a_int + b_int #结果c = int2binary[c_int] #映射成二进制值# 待存放预测值d = np.zeros_like(c)overallError = 0layer_2_deltas = list() #输出层的误差layer_2_values = list() #第二层的值(输出的结果)layer_1_values = list() #第一层的值(隐含状态)layer_1_values.append(copy.deepcopy(np.zeros((hidden_dim, 1)))) #第一个隐含状态需要0作为它的上一个隐含状态#前向传播for i in range(binary_dim):X = np.array([[a[binary_dim - i - 1], b[binary_dim - i - 1]]]).T #(2,1)y = np.array([[c[binary_dim - i - 1]]]).T #(1,1)layer_1 = sigmoid(np.dot(synapse_h, layer_1_values[-1]) + np.dot(synapse_0, X)) #(1,1)layer_1_values.append(copy.deepcopy(layer_1)) #(8,1)layer_2 = sigmoid(np.dot(synapse_1, layer_1)) #(1,1)error = -(y-layer_2) #使用平方差作为损失函数layer_delta2 = error * sigmoid_output_to_derivative(layer_2) #(1,1)layer_2_deltas.append(copy.deepcopy(layer_delta2))d[binary_dim - i - 1] = np.round(layer_2[0][0])future_layer_1_delta = np.zeros((hidden_dim, 1))#反向传播for i in range(binary_dim):X = np.array([[a[i], b[i]]]).Tprev_layer_1 = layer_1_values[-i-2]layer_1 = layer_1_values[-i-1]layer_delta2 = layer_2_deltas[-i-1]layer_delta1 = np.multiply(np.add(np.dot(synapse_h.T, future_layer_1_delta),np.dot(synapse_1.T, layer_delta2)), sigmoid_output_to_derivative(layer_1))synapse_0_update += np.dot(layer_delta1, X.T)synapse_h_update += np.dot(layer_delta1, prev_layer_1.T)synapse_1_update += np.dot(layer_delta2, layer_1.T)future_layer_1_delta = layer_delta1synapse_0 -= alpha * synapse_0_updatesynapse_h -= alpha * synapse_h_updatesynapse_1 -= alpha * synapse_1_updatesynapse_0_update *= 0synapse_1_update *= 0synapse_h_update *= 0# 验证结果if (j % 100 == 0):print("Error:" + str(overallError))print("Pred:" + str(d))print("True:" + str(c))out = 0for index, x in enumerate(reversed(d)):out += x * pow(2, index)print(str(a_int) + " + " + str(b_int) + " = " + str(out))print("------------")
https://zhuanlan.zhihu.com/p/518848475