Word Embedding是一种词的向量表示,好比,对于这样的“A B A C B F G”的一个序列,也许咱们最后能获得:A对应的向量为[0.1 0.6 -0.5],B对应的向量为[-0.2 0.9 0.7]。前端
之因此但愿把每一个单词变成一个向量,目的仍是为了方便计算,好比“求单词A的同义词”,就能够经过“求与单词A在cos距离下最类似的向量”来作到。git
那么如何进行词嵌入呢?目前主要有三种算法:github
Embedding Layer是与特定天然语言处理上的神经网络模型联合学习的单词嵌入。该嵌入方法将清理好的文本中的单词进行one hot编码(热编码),向量空间的大小或维度被指定为模型的一部分,例如50、100或300维。向量以小的随机数进行初始化。Embedding Layer用于神经网络的前端,并采用反向传播算法进行监督。算法
被编码过的词映射成词向量,若是使用多层感知器模型MLP,则在将词向量输入到模型以前被级联。若是使用循环神经网络RNN,则能够将每一个单词做为序列中的一个输入。网络
这种学习嵌入层的方法须要大量的培训数据,可能很慢,可是能够学习训练出既针对特定文本数据又针对NLP的嵌入模型。函数
Word2Vec是由Tomas Mikolov 等人在《Efficient Estimation of Word Representation in Vector Space》一文中提出,是一种用于有效学习从文本语料库嵌入的独立词语的统计方法。其核心思想就是基于上下文,先用向量表明各个词,而后经过一个预测目标函数学习这些向量的参数。post
该算法给出了两种训练模型,CBOW (Continuous Bag-of-Words Model) 和 Skip-gram (Continuous Skip-gram Model)。CBOW将一个词所在的上下文中的词做为输入,而那个词自己做为输出,也就是说,看到一个上下文,但愿大概能猜出这个词和它的意思。经过在一个大的语料库训练,获得一个从输入层到隐含层的权重模型;而Skip-gram它的作法是,将一个词所在的上下文中的词做为输出,而那个词自己做为输入,也就是说,给出一个词,但愿预测可能出现的上下文的词。学习
经过在一个大的语料库训练,获得一个从输入层到隐含层的权重模型。给定xx预测xxx的模型的输入都是词的向量,而后经过中间各类深度学习DL的CNN或RNN模型预测下一个词的几率。经过优化目标函数,最后获得这些词汇向量的值。
Word2Vec虽然取得了很好的效果,但模型上仍然存在明显的缺陷,好比没有考虑词序,再好比没有考虑全局的统计信息。优化
Doc2Vec与Word2Vec的CBOW模型相似,也是基于上下文训练词向量,不一样的是,Word2Vec只是简单地将一个单词转换为一个向量,而Doc2Vec不只能够作到这一点,还能够将一个句子或是一个段落中的全部单词汇成一个向量,为了作到这一点,它只是将一个句子标签视为一个特殊的词。ui
LSA、LDA等主题模型,创建词和主题的关系。
循环神经网络,是非线性动态系统,将序列映射到序列,主要参数有五个:$[W_{hv}, W_{hh}, W_{oh}, b_h, b_o, h_0] $,典型的结构图以下:
给定一个损失函数 \(L(z,y) = \sum_{t=1}^T{L(z_t, y_t)}\)
RNN由于加入了时间序列,所以训练过程也是和以前的网络不同,RNN的训练使用的是BPTT(Back Prropagation Through TIme),该方法是由Werbo等人在1990年提出来的。
上面的算法也就是求解梯度的过程,使用的也是经典的BP算法,并无什么新鲜的。可是值得一提的是,在 t-1 时刻对 \(h_{t−1}\)的求导值,也需加上t时刻的求导中对\(h_{t−1}\) 的求导值,所以BPTT也是一个链式的求导过程。
可是由于上面算法中的第10行,在训练t时刻的时候,出现了t-1的参数,所以对单个的求导就变成了对整个以前状态的求导之和。
也正是由于存在长依赖关系,BPTT没法解决长时依赖问题(即当前的输出与前面很长的一段序列有关,通常超过十步就无能为力了),由于BPTT会带来所谓的梯度消失或梯度爆炸问题(the vanishing/exploding gradient problem)。
这篇文章很好的解释了为何会产生梯度消失和为何会梯度爆炸的问题,其实主要问题就是由于在BPTT算法中,以w为例,其求导过程的链太长,而太长的求导链在以tanh为激活函数(其求导值在0~1之间的BPTT中,连乘就会使得最终的求导为0,这就是梯度消失问题,也就是t时刻已经学习不到t-N时刻的参数了。固然,有不少方法去解决这个问题,如LSTMs即是专门应对这种问题的,还有一些方法,好比设计一个更好的初始参数以及更换激活函数(如换成ReLU激活函数)。
model.add(Embedding(output_dim=32, input_dim=2800, input_length=380)) model.add(SimpleRNN(units=16)) model.add(Dense(uints=256, activation=relu)) ... model.summary() #output simple_rnn_1 (SimpleRNN) param # 784 dense_1 (Dense) param # 4352
其中:784=16+1616+1632( \(W_{hv}\) + \(W_{hh}\) + \(b_h\))
假设咱们试着去预测“I grew up in France... I speak fluent French”最后的词。当前的信息建议下一个词多是一种语言的名字,可是若是咱们须要弄清楚是什么语言,咱们是须要先前提到的离当前位置很远的 France的上下文的。这说明相关信息和当前预测位置之间的间隔就确定变得至关的大。
不幸的是,在这个间隔不断增大时,RNN会丧失学习到链接如此远的信息的能力。在理论上,RNN绝对能够处理"长期依赖"问题。人们能够仔细挑选参数来解决这类问题中的最初级形式,但在实践中,RNN 确定不可以成功学习到这些知识。Bengio, et al.等人对该问题进行了深刻的研究,他们发现一些使训练 RNN 变得很是困难的根本缘由。
然而,幸运的是,LSTM 并无这个问题!
LSTM 由Hochreiter & Schmidhuber (1997)提出,并在近期被Alex Graves进行了改良和推广。在不少问题,LSTM 都取得至关巨大的成功,并获得了普遍的使用。
LSTM 经过刻意的设计来避免长期依赖问题。记住长期的信息在实践中是 LSTM 的默认行为,而非须要付出很大代价才能得到的能力!
全部 RNN 都具备一种重复神经网络模块的链式的形式。在标准的 RNN 中,这个重复的模块只有一个很是简单的结构。
LSTM 一样是这样的结构,可是重复的模块拥有一个不一样的结构。不一样于单一神经网络层,以一种很是特殊的方式进行交互。
LSTM 中的第一步是决定咱们会从细胞状态中丢弃什么信息。这个决定经过一个称为忘记门层完成。
下一步是肯定什么样的新信息被存放在细胞状态中。
咱们把旧状态与 \(f_t\) 相乘,丢弃掉咱们肯定须要丢弃的信息。接着加上 \(i_t * \tilde{C}_t\)。这就是新的候选值,根据咱们决定更新每一个状态的程度进行变化。
最终,咱们须要肯定输出什么值。这个输出将会基于咱们的细胞状态,可是也是一个过滤后的版本。
咱们到目前为止都还在介绍正常的 LSTM。可是不是全部的 LSTM 都长成一个样子的。实际上,几乎全部包含 LSTM 的论文都采用了微小的变体。
图中最上面的一条线的状态即 s(t) 表明了长时记忆,而下面的 h(t)则表明了工做记忆或短时记忆。
model.add(Embedding(output_dim=32, input_dim=2800, input_length=380)) model.add(LSTM(32)) model.add(Dense(uints=256, activation=relu)) ... model.summary() #output lstm_1 (LSTM) param # 8320
其中:8320=(32+32)324+4*32( \(W_o\) + \(W_C\) + \(W_i\) + \(W_f\) + \(b_o\) + \(b_C\) + \(b_i\) + \(b_f\))。
LSTM有不少变体,其中较大改动的是Gated Recurrent Unit (GRU),这是由 Cho, et al. (2014)提出。它将忘记门和输入门合成了一个单一的 更新门。一样还混合了细胞状态和隐藏状态,和其余一些改动。最终的模型比标准的 LSTM模型要简单。效果和LSTM差很少,可是参数少了1/3,不容易过拟合。