六. 文本处理
一. 文本分析流程与分词
1. 文本分词流程
就像其他的领域有自己的经典流程一样,一个文本分析的项目也有属于自己的流程。虽然每一个NLP项目有所不同,但至于流程来说没有太多本质的区别。这里会涉及到如分词、停用词过滤、文本向量的转化等步骤。
2. 分词工具的使用
分词是所有工作的第一步,分词的准确性直接影响对后续任务的表现。但分词技术相对比较成熟,也有很多开源的工具可用来做中文或者对其他语言的分词。在这里,结巴分词算是最经典且简单的中文分词工具。下面以结巴分词为例来说明如何使用工具来分词。
3. 最大匹配算法
是贪心算法,效率很高,只能给到局部最优解。
前向最大匹配:需要设置窗口h大小,需要给定一个词库,从前面到后面最大匹配。h取决于词库里的单词有多长,尽量匹配词库里的所有单词。
后向最大匹配
双向最大匹配
匹配算法缺点:没有考虑语义,没考虑单词与单词之间的语义关系。
算法并不能保证找到的是最好的分词结果。整个过程是贪心策略,寻求的是局部最好的选项,但从全局来讲未必是最好的。另一方面,由于是贪心策略,所以分词的效率也很高。
4. 考虑语义的一种分词方法
前提是给定一个词典,并且给定在语言模型下每个单词的概率。概率乘积最大的是最好的分词。
看到乘积很多的,需要将其转化为加法,比如加上log,因为如果某个值小于1,乘以其他小的数,会导致计算机溢出。
维特比算法:看一下维基百科的解释,维特比算法(Viterbi algorithm)是一种动态规划算法。它用于寻找最有可能产生观测事件序列的维特比路径——隐含状态序列,特别是在马尔可夫信息源上下文和隐马尔可夫模型中。维特比算法就是求所有观测序列中的最优,求所有路径中最优路径,最容易想到的就是暴力解法,直接把所有路径全部计算出来,然后找出最优的。这方法理论上是可行,但当序列很长时,时间复杂夫很高。而且进行了大量的重复计算,viterbi算法就是用动态规划的方法就减少这些重复计算。
viterbi算法是每次记录到当前时刻,每个观察标签的最优序列,假设在t时刻已经保存了从0到t时刻的最优路径,那么t+1时刻只需要计算从t到t+1的最优就可以了。
二. 停用词与词的标准化
1. 词的过滤
在文本处理过程中,对于有些词需要做过滤。这些被过滤掉的单词可认为是对语义理解帮助不大,或者反而影响语义理解的单词。同时,过滤单词有助于减小词库的大小,进而提高训练的效率和减少内存空间的使用。
去掉停用词和出现频率低的词,一般要根据应用场景来判断。
2. 词的标准化
stemming 和 lemmazation
porter stemmer是stemming的非常经典的算法,是基于规则的,背后有大量的语言学家。
三. 拼写纠错
1. 拼写纠错与编辑距离
分为单词拼写错误和语法错误。语法错误涉及语言模型,下面主要先说单词拼写错误怎么改。
总结起来,关于拼写纠错单词的一种方法是:
第一步:寻找拼写错误的单词
第二步:寻找跟上面单词“长得”最像的,可通过循环词库,并计算编辑距离来获得。
第三步:从上述候选集里,根据上下文进一步做筛选和排序,最终寻找最合适的单词。
2. 循环词库的问题以及改进方法
通过上述方法,我们便可以灵活生成编辑距离为某一个值的单词,这种做法要比直接循环词库里的每个单词简单很多,高效很多。但也可以从结果中看到,包含了大量的非法单词。所以我们需要在这些单词中做进一步过滤。当然过滤的条件可以很简单,就是来检查是否出现在了词库中,如果没有出现则剔除掉。
最大化P(s|c)*P(c):P(s|c)指的是在给定一个正确字符串c的情况下,拼成错误字符串s的概率。P(c)指的是正确字符串c在词库中出现的概率。
对于拼写纠错,我们来做简单的总结:
第一步:找到拼写错误的单词
第二步:生成跟上述单词类似的其他单词,当作是候选集
第三步:根据单词在上下文中的统计信息来排序并选出最好的。
至于拼写纠错的完整实现,由于跟语言模型相关,我们将放到下一章来讲解完整的代码。
七. 文本表示
一. 文本表示基础
1. 单词的表示
对于自然语言处理各类应用,最基础的任务为文本表示。因为我们都知道一个文本是不能直接作为模型的输入的,所以我们必须要先把文本转换成向量的形式之后,再导入到模型中训练。所谓文本的表示,其实就是研究如何把文本表示成向量或者矩阵的形式。
文本的最小单元为单词,其次为短语、句子、或者段落。我们需要懂得如何把这些表示成向量的形式。其中,单词的表示法是最基础的。另外,对于句子或者更长的文本来说,它们的表示依赖于单词的表示法。 在这里想说的一点是,单词的表示法不止一种,比如有独热编码的表示法,词向量的表示法等等。
2. 句子的表示
知道了如何表示一个单词之后,我们很自然地就可以得到如何表示一个句子了。一个句子由多个单词来组成,那实际上记录一下哪些单词出现,哪些单词没有出现就可以了。当然,很多时候我们也需要记录一个单词所出现的次数。
3. tf-idf向量
所以,如果只记录单词的个数也是不够的,我们还需要考虑单词的权重,也可以认为是质量。这有点类似于,一个人有很多朋友不代表这个人有多厉害,还需要社交的质量,其实是同一个道理。 那如何把这种所谓的“质量”引入到表示中呢?答案是tf-idf。
tf-idf的应用非常广泛,即便放在当前,也是表示文本的最核心的技术之一。 之前我们讲过什么是基准,那tf-idf是文本表示领域的最有效的基准。很多时候,基于深度学习的文本表示也未必要优于tf-idf的表示。
二. 文本相似度
1. 计算欧式距离
如何计算两个文本之间的相似度?这个问题实际上可以认为是计算两个向量之间的相似度。因为通过上一节的内容已经知道了如何把文本转换成向量。 所以本节所涉及到的相似度计算公式适合任何向量化的场景,不仅仅局限于文本之间的相似度。有两种常见的相似度计算方法,分别为基于欧式距离的计算,另外一种方式为基于余弦相似度的计算。
距离越大,相似度越小;距离越小,相似度越大。
向量之间的相似度实际上要考虑到向量的方向,因为向量最重要的特性为它的方向性。如果两个向量相似,那也需要它俩的方向也比较相似。然而,计算欧式距离的过程并没有把方向考虑进去,这是欧式距离的最大的问题。
2. 计算余弦相似度
为了弥补欧式距离所存在的问题,我们不得不要提出另外一种相似度计算方法,这就是最著名的方法-余弦相似度。通过余弦相似度事实上我们计算的是两个向量之间的夹角大小。两个向量的方向上越一致就说明它俩的相似度就越高。
余弦相似度计算的直接是相似度,结果越小,相似度越小;结果越大,相似度越大。
三. 词向量基础
1. 计算单词之间的相似度
我们一直在讨论如何计算两个文本之间的相似度,但至今还没有讨论过如何计算两个单词之间的相似度。单词作为文本的最基本的要素,如何表示单词的含义以及两个单词之间的相似度也极其重要。我们一起来了解一下在独热编码的基础下,如何计算两个单词之间的相似度。
显然是,通过欧式距离或者余弦相似度是没有办法算出单词之间的相似度,因为不管我们怎么计算,俩俩之间的结果都是一样的。那问题到底处在哪儿呢?答案是,一开始的独热编码的表示!既然独热编码表示不支持计算两个单词之间的相似度,我们需要想另外一种单词的表示法了,这就自然引出词向量的概念。 除了不能计算相似度,独热编码也存在稀疏性的问题。
2. 词向量基础
词向量是分布式表示。词向量的最终目的是用向量来表示单词的含义。
如果单词用独热编码,后续的句子要用tf-idf或count vector来表示;如果单词用了词向量来表示,后续的句子也要沿用这个路线,比词向量训练了300维,句子也要是300维或者200维(?)。这是两套不同的编码方式。想结合也可以。
我们可以看到在分布式表示方法下,两个单词之间的相似度是可以算出来的。当然,效果取决于词向量的质量。所以,接下来的话题是如何得出这些词向量?在这一章节,我们只做简单的介绍,具体详细的方法论贯穿之后的很多的章节中。
语料库-模型-词向量。模型是黑盒子(Bert, ELMO, Glove, SkipGram etc.)。
如何评价一个词向量?简单的方法是做词向量的可视化。
语义上比较相似的单词聚集在了一起,这其实变相地说明,词向量在某种意义上表达出了一个单词的含义。为了可视化词向量而使用的降维技术通常包括 https://scikit-learn.org/stable/modules/generated/sklearn.manifold.TSNE.html ),也是一种常用的降维算法。
3. 句子向量
假如我们手里已经有了训练好的词向量,那如何通过这些词向量来表示一个完整的文本呢,或者一个句子呢?有一种最简单且常用的方法,就是做平均!
有了文本表示之后,我们就可以开始对文本做建模了,比如计算两个文本之间的相似度,或者对某个文本做分类。在这里我们来做个简单的小结:
单词的独热编码和分布式表示是两种完全不一样的编码方式
这两种不同的编码方式是目前文本表示的两个方向,有些时候传统的独热编码的方式可能更适合,有些时候分布式表示法更适合,具体还是要通过测试来获得结论
独热编码的最大的问题是不能表示一个单词的含义
词向量的质量取决于词向量训练模型,不同的模型所给出的结果是不一样的
九. 词向量技术
目标:掌握SkipGram
一. 词向量基础
1. 单词的表示
2. 从独热编码到分布式表示
通常情况下,词库里的单词越多,词向量的长度可以长一些。这有点类似于,当我们有很多特征的时候,可以让模型包含更多参数一样。另外,词向量的长度跟效果并没有正向关系,很长的词向量反而会导致过拟合现象。
理解一下这里的“可以表示含义”:这个是任何词向量训练的目标,即这个词向量是真正表示这个单词的意义的,可视化之后可以明显看出意思相近的单词是聚在一堆的。
3. 词向量的训练
词向量是训练出来的。所以,我们可以认为,中间有一个模型可以帮助我们训练出每个单词的向量。这个模型到底是什么呢?这个黑盒子就是我们即将要解开的秘密。词向量技术也是带动NLP发展的最有利的催化器,自从2013年提出word2vec开始,之后整个NLP领域有了飞跃式的发展。如果说,ImageNet是CV领域的催化剂,那么word2vec有着同样的重要性。
那到底这个黑盒子里是什么呢?在具体剖析词向量模型之前,我们先看一下如何用一些模型来训练出词向量。也就是给定一个语料库,它的输出长得是怎样的。输入是一个很长的文章txt文件也叫语料库,输出就是每个单词的词向量,中间是用于训练词向量的模型,这个模型很多包括skipgram,cbow等。
到目前为止我们简单地了解了词向量以及它的训练是怎样的。在这里做个简单的总结:
词向量可以认为在某种程度上代表单词的含义
词向量是需要训练出来的,也就是提前要设计好词向量训练模型
词向量技术极大推动了NLP领域的发展
二. SkipGram模型详解:重点难点
1. 训练词向量的核心思想
词向量模型其实很多,包括大家所熟悉的BERT等。每一种词向量,它的目标和作用是不一样的,我们在后续的章节中会一一做介绍。但不管怎么样,这些模型都享有着共同的核心思想。等我们深入理解了这个思想,便可以更容易理解模型为什么会这么设计,而且甚至将来也可以提出自己的模型。
首先说明一点,词向量的学习通常是无监督学习,也就是不需要标注好的文本。那对于这样的无监督学习,我们应该如何合理地设计目标函数并学出词向量呢?
2. SkipGram的目标函数
具体来学习一下SkipGram模型。它是一个非常著名的词向量训练模型。它的核心思想是通过中心词来预测它周围的单词。也就是说,如果我们的词向量训练比较到位,则这方面的预测能力会更强。实际上,这就是我们需要构建的目标函数。
仔细看!这遍没看懂???
3. SkipGram的负采样
得到了SkipGram目标函数之后,发现了这个目标函数其实不好优化。所以需要换一种方式去优化,其中比较流行的方法是使用负采样。从名字中也可以猜到,这个方法用到了采样的做法。接下来我们从另外一个角度来推导SkipGram的目标函数。
当我们有了目标函数之后,剩下的过程无非就是优化并寻找最优参数了。实际上,通过优化最终得出来的最优参数就是训练出来的词向量。优化方法可采用梯度下降法或者随机梯度下降法。
推导没看懂??
三. 其他词向量技术
SkipGram是训练词向量的其中一种方式,但并不是唯一的方法。实际上,从第一次提出word2vec开始,学者们已经提出了各种各样的训练方式。每个方法论的侧重点、目标是有所不同的。
1. 矩阵分解法
在推荐领域用的比较好。
矩阵分解作为全局方法也有它的优点和缺点。一个最大的缺点是每次分解依赖于整个矩阵,这就导致假如有些个别文本改变了,按理来讲是需要重新训练的,但优点是学习过程包含了全局的信息。相反,对于SkipGram模型,由于训练发生在局部,所以训练起来效率高,且能够很好把握局部的文本特征,但问题是它并不能从全局的视角掌握语料库的特点。
所以,接下来的问题是能否把各自的都优点发挥出来?答案是设计一个融合矩阵分解和SkipGram模型的方法,这个答案其实是Glove模型。
2. Glove向量
3. 高斯词嵌入
带有方差和标准差,可以看出来学出的词向量的置信度高还是低,即是否靠谱。
4. 词向量总结
到此为止讲完了SkipGram。在最后,我来给大家总结一下:
词向量技术的目的是通过向量来表示一个单词的语义。
SkipGram是流行的一种词向量学习技术。
SkipGram通过中心词来预测它周围的单词。
SkipGram原始的目标函数很难优化,所以采用负采样的方式来解决。
除了SkipGram还有很多不同的学习词向量的方式。
另外,我们还需要留意一点:对于任意一个单词,SkipGram,矩阵分解,Glove等模型均学出对应的一个词向量。那这又有什么问题呢?
由于一个单词在不同的上下文中所表达的含义可能不一样,所以只学出对应的一个向量是不够的。
四. 论文解读
SkipGram不仅仅可以学习词向量,也可以应用推荐系统中。
十. 语言模型
一. 语言模型基础
1. 什么是语言模型
语言模型最主要的作用是保证文本的语法结构,得到通顺的语句。语言模型是一种概率统计的方法,已经训练好的语言模型可以对任何一个文本给出概率,概率越高说明语法上越通顺。通过比较两句话在同一个语言模型上的概率,我们就可以得出哪一句话更通顺一些。
2. 计算语言模型的概率
上述的条件概率是从语料库中统计出来的,而且语言模型本身是无监督学习,不需要数据标签。
3. 马尔可夫假设
当条件为较长的一段话时,一模一样的语句出现在语料库中的概率会非常小,很多时候为0,造成了稀疏性,就失去了统计的意义。那如何解决此问题呢?答案是做一些近似!
二. 语言模型训练
1. 不同的语言模型
根据不同的马尔科夫假设(1阶、2阶、3阶。。。),我们可以得到不同种类的语言模型。如,一阶马尔科夫假设下的语言模型为bi-gram, 二阶马尔科夫假设下的模型为tri-gram,以此类推。另外,假如前后单词之间不存在任何的依赖关系,得到的语言模型为unigram。
2. 语言模型的训练
做一个简单的总结:
语言模型的概率值可基于语料库来统计。
根据使用的马尔科夫假设的不同,可以把语言模型分为unigram, bigram, trigram, ngram。。。
当考虑多个单词的时候,条件概率往往变得稀疏,导致大部分都变成0。
3. 语言模型的评估
假如训练好了语言模型,我们应该如何判断语言模型的好坏呢?比如手里有两个不同的语言模型,如何判断哪个更好?为了回答此问题,首先需要一个评价标准,也就是如何评估一个语言模型的好坏,并且结果是能够量化的!
困惑度(Perplexity)越小越好。
四. 语言模型的平滑
1. 不同的平滑方法
平滑对于语言模型至关重要,假如没有平滑操作,很多概率都会变成0。
2. Add-one Smoothing
Add-one平滑其实也称之为Laplace平滑,在朴素贝叶斯等模型中也会经常用到。这种方法简单且有效,不需要任何的训练过程。
3. Add-K Smoothing
Add-one平滑可看作是add-k平滑的特例,也就是当K=1时就变成了Add-one平滑。这里的K可以理解为是超参数,可以试着去调节从而获得比较好的K值。
4. Interpolation
以上是比较常见的几个平滑的方法。当然,除了这些,还有很多平滑的操作可供使用,感兴趣的朋友们可以在网上搜一下如Good-turning smoothing平滑等。对于本章做一个总结:
语言模型可用来判断语法的准确性以及语句的通顺与否。
当任务中涉及到文本生成时,语言模型扮演着关键的角色。
语言模型中的概率是基于语料库统计出来的,这个过程也可以理解为是训练过程。
根据马尔科夫假设的不同,语言模型可分为unigram, bigram, trigram。。。。
在估算语言模型概率时,需要用到平滑操作。
Add-one平滑是最简单且有效的平滑操作,广泛用在各类机器学习模型中。