如上面所说数据矩阵 M 是稀疏的,其中所有的 m 篇文章中出现的所有词 (字) 个数为 n。这就是我们所说的词袋子模型,就像把词语随机的扔进一个袋子里。这显然只是统计词频没有任何前后文。
这种稀疏性还带来另一个问题就是两句表述意思类似的话,但是由于没有相同词语出现使得两个文章向量距离很远,就像:
你技术很好,很有经验,我们需要研究一下,等通知吧
不好意思面试没通过
以上两句话没有出现任何相同词语,从词袋子模型看来是完全不同的意思,但是以我们经验来看表达的意思却是相似的。于是衍生出的一种改进方式就是挑出一些词加权组合到一起,整体做一个判别,这就是 LDA 算法。
我个人非常讨厌贝叶斯理论,于是从另一个角度理解其实他就是对于矩阵 M 进行的分解。通过这种方式将一些看似不相关的内容总结到了一起,表现为 “特征向量”。
注意文中用到的一个概念就是 以我们的经验来看 这种所谓的经验也就是知识。人在判断一句话是什么意思的时候融入了个人的经验知识。而神经网络是可以保存这种经验知识的,表现为权值。
一般人在说 RNN 可以借助于前后文信息进行判断的时候其实他忽略了一个问题,就是 RNN 在判断的时候融入了以 往学习到的经验 。
这相比于 LDA 算法又更近一步。本质来说 LDA 是对传统的只统计词频的方法的改进,而 RNN 却对于文字输入顺序也有了感知。
于是有了这一节中的主要内容,就是用神经网络完成一个语句预测的任务。首先来分析一下任务,我们需要给定一句话的前几个词,然后推测后续会出现哪些字。
这个过程是一个马尔可夫链,但是不太喜欢概率,所以直接描述过程,假设我给定一个三个字的开头:
晚来天(?)(?)
根据前三个字来预测接下来两个字是什么,那么下一个字可能是晴,可能是欲,也有可能是未,那么根据概率随机选择了(欲),接下来就根据 “晚来天欲” 这四个字进行预测,可能随机选择的字是(雨),那么最终五个字就是
晚来天(欲)(雨)
当然概率有随机性,可能第一步时选择的是晴,那么最终形成的诗句就是:
晚来天(晴)(处)
还是因为概率,可能我们最终选择的完全不像是诗句,比如:
晚来天(气)(息)
这就完全没有意境了。这里面起关键作用的实际上是给定下一步出现的字的概率,概率太平均的话也就是一种初始状态,最终可能形成完全不是人说的东西,这就很尴尬了。
所以前面我说,我并不喜欢随机性太强的东西,降低这种随机性需要持续大量的数据进行训练。概率这个很容易映射到神经网络之中就是 softmax,这个过程可以给定一个字接下来可能出现的概率。
至此其实可以看到其与前面所说的 “词袋子” 模型的文本向量化过程的区别。
在中文文本处理中与英文文本处理的一个区别就是需要进行分词处理,这是因为单个中文字与单个字母一样无法表达一个有效的信息。这就需要用几个字来表达一个意思。
对于英文来讲天然就有 “分词” 也就是空格,对于中文来讲需要人为的去完成这个过程。但是对于本次任务,我们只是对单个字行了 “编号”。
这里我们依靠的是神经网络自身的强大表达能力。于是诗句的处理就类似于一个查字典的过程:
word_dict = {',': 0, '。': 1, '
': 2, ' 不 ': 3, ' 人 ': 4, ' 山 ': 5, ' 风 ': 6, ' 日 ': 7, ' 云 ': 8, ' 无 ': 9, ' 何 ': 10, ' 一 ': 11, ' 春 ': 12, ' 月 ': 13, ' 水 ': 14, ' 花 ': 15, ' 来 ': 16, ' 有 ': 17, ' 中 ': 18, ' 秋 ': 19, ' 上 ': 20, ' 时 ': 21, ' 天 ': 22, ' 归 ': 23, ' 心 ': 24, ' 相 ': 25, ' 此 ': 26, ' 年 ': 27, ' 生 ': 28, ' 长 ': 29, ' 夜 ': 30, ' 自 ': 31, ' 去 ': 32, ' 知 ': 33, ' 空 ': 34, ..., ' 挪 ': 5386}
举个例子来说:
寒心睹肉林,飞魄看沉湎。
[42, 24, 1791, 1818, 72, 0, 88, 1337, 108, 670, 4206, 1, 2]
可以看到,句号、逗号以及语句结束标志 ‘
’ 都进行了编码。这里编号的规则很简单,统计字出现的次数按顺序编码。’
’ 等进行编码则用于判断语句的末尾,这可以辅助进行判断语句结束。
编码结束后所得的结果是一系列整型数字。如今又出现了一个问题就是编码后的一维数据并不适合神经网络处理,因为几 k 级别数字几乎可以肯定使得神经网络训练出现问题。
因此还需要的处理就是将其扩展成向量形式的表示,也就是相当于对于 one-hot 编码进行降维,TensorFlow 已经提供了相应的工具:
embedding = tf.get_variable("embedding", [len(words), rnn_size])
通常而言的处理过程中的 embedding 可以用 word2vector 的方式,也就是将意思相近的词所形成的向量距离也是相近的,这可以简化神经网络结构设计。
注意这里说的是简化网络结构设计,由于 word2vec 过程可以看成是一个神经网络层,因此借助于神经网络强大的表达能力有时候是可以省略这个过程的。
本文就是省略了这个过程这个过程将编码转换为适合神经网络处理的向量。至此文本预处理工作描述完毕。
神经网络结构与训练
RNN 神经网络结构其实并不复杂。对于最传统的 RNN 结构而言只是加入了一层向量用于保存上一时间步中的输出结果:
整个过程可以写成:
对于每一个时间步均会有一个对应的输出 。而对于多层的 RNN 网络而言是在之上加入相同的 RNN 层: