天然语言处理(NLP,Natural Language Processing)是一个信息时代最重要的技术之一,简单来说,就是让计算机可以理解人类语言的一种技术。在其中,分词技术是一种比较基础的模块。对于英文等拉丁语系的语言而言,因为词之间有空格做为词边际表示,词语通常状况下都能简单且准确地提取出来。而中文日文等文字,除了标点符号以外,字之间紧密相连,没有明显的词边界,所以很难将词提取出来。分词的意义很是大,在中文中,单字做为最基本的语义单位,虽然也有本身的意义,但表意能力较差,意义较分散,而词的表意能力更强,能更加准确地描述一个事物,所以在天然语言处理中,一般状况下词(包括单字成词)是最基本的处理单位。html
在具体的应用上,好比在经常使用的搜索引擎中,term若是是词粒度的话,不只可以减小每一个term的倒排列表长度,提高系统性能,而且召回的结果相关性高更准确。好比搜索query“的确”,若是是单字切分的话,则有可能召回“你讲的确实在理”这样的doc。分词方法大体分为两种:基于词典的机械切分,基于统计模型的序列标注切分两种方式。node
基于词典的方法本质上就是字符串匹配的方法,将一串文本中的文字片断和已有的词典进行匹配,若是匹配到,则此文字片断就做为一个分词结果。可是基于词典的机械切分会遇到多种问题,最为常见的包括歧义切分问题和未登陆词问题python
歧义切分指的是经过词典匹配给出的切词结果和原来语句所要表达的意思不相符或差异较大,在机械切分中比较常见,好比下面的例子:“结婚的和还没有结婚的人”,经过机械切分的方式,会有两种切分结果:1,“结婚/的/和/还没有/结婚/的/人”;2,“结婚/的/和尚/未/结婚/的/人”。能够明显看出,第二种切分是有歧义的,单纯的机械切分很难避免这样的问题。算法
未登陆词识别也称做新词发现,指的是词没有在词典中出现,好比一些新的网络词汇,如“网红”,“走你”;一些未登陆的人名,地名;一些外语音译过来的词等等。基于词典的方式较难解决未登陆词的问题,简单的case能够经过加词典解决,可是随着字典的增大,可能会引入新的bad case,而且系统的运算复杂度也会增长。编程
为了解决歧义切分的问题,在中文分词上有不少优化的方法,常见的包括正向最大匹配,逆向最大匹配,最少分词结果,全切分后选择路径等多种算法。后端
正向最大匹配指的是从左到右对一个字符串进行匹配,所匹配的词越长越好,好比“中国科学院计算研究所”,按照词典中最长匹配原则的切分结果是:“中国科学院/计算研究所”,而不是“中国/科学院/计算/研究所”。可是正向最大匹配也会存在一些bad case,常见的例子如:“他从东通过我家”,使用正向最大匹配会获得错误的结果:“他/从/东经/过/我/家”。 api
逆向最大匹配的顺序是从右向左倒着匹配,若是能匹配到更长的词,则优先选择,上面的例子“他从东通过我家”逆向最大匹配可以获得正确的结果“他/从/东/通过/我/家”。可是逆向最大匹配一样存在bad case:“他们昨日本应该回来”,逆向匹配会获得错误的结果“他们/昨/日本/应该/回来”。 微信
针对正向逆向匹配的问题,将双向切分的结果进行比较,选择切分词语数量最少的结果。可是最少切分结果一样有bad case,好比“他未来上海”,正确的切分结果是“他/将/来/上海”,有4个词,而最少切分结果“他/未来/中国”只有3个词。网络
全切分方法就是将全部可能的切分组合所有列出来,并从中选择最佳的一条切分路径。关于路径的选择方式,通常有n最短路径方法,基于词的n元语法模型方法等。 架构
n最短路径方法的基本思想就是将全部的切分结果组成有向无环图,每一个切词结果做为一个节点,词之间的边赋予一个权重,最终找到权重和最小的一条路径做为分词结果。
基于词的n元语法模型能够看做是n最短路径方法的一种优化,不一样的是,根据n元语法模型,路径构成时会考虑词的上下文关系,根据语料库的统计结果,找出构成句子最大模型几率。通常状况下,使用unigram和bigram的n元语法模型的状况较多。
针对基于词典的机械切分所面对的问题,尤为是未登陆词识别,使用基于统计模型的分词方式可以取得更好的效果。基于统计模型的分词方法,简单来说就是一个序列标注问题。
在一段文字中,咱们能够将每一个字按照他们在词中的位置进行标注,经常使用的标记有如下四个label:B,Begin,表示这个字是一个词的首字;M,Middle,表示这是一个词中间的字;E,End,表示这是一个词的尾字;S,Single,表示这是单字成词。分词的过程就是将一段字符输入模型,而后获得相应的标记序列,再根据标记序列进行分词。举例来讲:“达观数据是企业大数据服务商”,通过模型后获得的理想标注序列是:“BMMESBEBMEBME”,最终还原的分词结果是“达观数据/是/企业/大数据/服务商”。
在NLP领域中,解决序列标注问题的常见模型主要有HMM和CRF。
HMM(Hidden Markov Model)隐马尔科夫模型应用很是普遍,基本的思想就是根据观测值序列找到真正的隐藏状态值序列。在中文分词中,一段文字的每一个字符能够看做是一个观测值,而这个字符的词位置label(BEMS)能够看做是隐藏的状态。使用HMM的分词,经过对切分语料库进行统计,能够获得模型中5大要要素:起始几率矩阵,转移几率矩阵,发射几率矩阵,观察值集合,状态值集合。在几率矩阵中,起始几率矩阵表示序列第一个状态值的几率,在中文分词中,理论上M和E的几率为0。转移几率表示状态间的几率,好比B->M的几率,E->S的几率等。而发射几率是一个条件几率,表示当前这个状态下,出现某个字的几率,好比p(人|B)表示在状态为B的状况下人字的几率。
有了三个矩阵和两个集合后,HMM问题最终转化成求解隐藏状态序列最大值的问题,求解这个问题最长使用的是Viterbi算法,这是一种动态规划算法,具体的算法能够参考维基百科词条,在此不详细展开。
图1.1 HMM模型示意图
CRF(Conditional random field,条件随机场)是用来标注和划分结构数据的几率化结构模型,一般使用在模式识别和机器学习中,在天然语言处理和图像处理等领域中获得普遍应用。和HMM相似,当对于给定的输入观测序列X和输出序列Y,CRF经过定义条件几率P(Y|X),而不是联合几率分布P(X,Y)来描述模型。CRF算法的具体算法能够参考维基百科词条。
图1.2 不一样几率模型之间的关系及演化图
在实际应用中有不少工具包可使用,好比CRF++,CRFsuite,SGD,Wapiti 等,其中CRF++的准确度较高。在分词中使用CRF++时,主要的工做是特征模板的配置。CRF++支持unigram,bigram两种特征,分别以U和B开头。举例来说U00:%x[-2,0] 表示第一个特征,特征取值是当前字的前方第二个字,U01:%x[-1,0] 表示第二个特征,特征取值当前字前一个字,U02:%x[0,0] 表示第三个特征,取当前字,以此类推。特征模板能够支持多种特征,CRF++会根据特征模板提取特征函数,用于模型的创建和使用。特征模板的设计对分词效果及训练时间影响较大,须要分析尝试找到适用的特征模板。
随着AlphaGo的大显神威,Deep Learning(深度学习)的热度进一步提升。深度学习来源于传统的神经网络模型。传统的神经网络通常由输入层,隐藏层,输出层组成,其中隐藏层的数目按需肯定。深度学习能够简单的理解为多层神经网络,可是深度学习的却不只仅是神经网络。深度模型将每一层的输出做为下一层的输入特征,经过将底层的简单特征组合成为高层的更抽象的特征来进行学习。在训练过程当中,一般采用贪婪算法,一层一层地训练,好比在训练第k层时,固定训练好的前k-1层的参数进行训练,训练好第k层以后的以此类推动行一层层训练。
图2.1 AlphaGo的神经网络模型的训练过程及架构
图2.2 Google Tensorflow官网的神经网络演示示意图
深度学习在不少领域都有所应用,在图像和语音识别领域中已经取得巨大的成功。从2012年开始,LSVRC(Large Scale Visual Recognition Challenge)比赛中,基于Deep Learningd 计算框架一直处于领先。2015年LSVRC(http://www.image-net.org/challenges/LSVRC/2015/results)的比赛中,微软亚洲研究院(MSRA)在图像检测(Object detection),图像分类定位(Object Classification+localization)上夺冠,他们使用的神经网络深达152层。
在天然语言处理上,深度学习在机器翻译、自动问答、文本分类、情感分析、信息抽取、序列标注、语法解析等领域都有普遍的应用。2013年底google发布的word2vec工具,能够看作是深度学习在NLP领域的一个重要应用,虽然word2vec只有三层神经网络,可是已经取得很是好的效果。经过word2vec,能够将一个词表示为词向量,将文字数字化,更好的让计算机理解。使word2vec模型,咱们能够方便地找到同义词或联系紧密的词,或者意义相反的词等。
图2.3 基于微信数据制做的word2vec模型测试:编程
图2.4 基于微信数据制做的word2vec模型测试:韦德
词向量的意思就是经过一个数字组成的向量来表示一个词,这个向量的构成能够有不少种。最简单的方式就是所谓的one-hot向量。假设在一个语料集合中,一共有n个不一样的词,则可使用一个长度为n的向量,对于第i个词(i=0…n-1),向量index=i处值为1外,向量其余位置的值都为0,这样就能够惟一的经过一个[0,0,1,…,0,0]形式的向量表示一个词。one-hot向量比较简单也容易理解,可是有不少问题,好比当加入新词时,整个向量的长度会改变,而且存在维数太高难以计算的问题,以及向量的表示方法很难体现两个词之间的关系,所以通常状况下one-hot向量较少的使用。
若是考虑到词和词之间的联系,就要考虑词的共现问题。最简单的是使用基于文档的向量表示方法来给出词向量。基本思想也很简单,假设有n篇文档,若是某些词常常成对出如今多篇相同的文档中,咱们则认为这两个词联系很是紧密。对于文档集合,能够将文档按顺编号(i=0…n-1),将文档编导做为向量索引,这样就有一个n维的向量。当一个词出如今某个文档i中时,向量i处值为1,这样就能够经过一个相似[0,1,0,…,1,0]形式的向量表示一个词。基于文档的词向量可以很好地表示词之间的关系,可是向量的长度和语料库的大小相关,一样会存在维度变化问题。
考虑用一个固定窗口大小的文本片断来解决维度变化问题,若是在这样的片断中,两个词出现了,就认为这两个词有关。举例来说,有如下三句话: “我\喜欢\你”,“我\爱\运动”,“我\爱\摄影”,若是考虑窗口的大小为1,也就是认为一个词只和它前面和后面的词有关,经过统计共现次数,咱们可以获得下面的矩阵。
能够看到这是一个n*n的对称矩阵X,这个矩阵的维数会随着词典数量的增长而增大,经过SVD(Singular Value Decomposition,奇异值分解),咱们能够将矩阵维度下降,但仍存在一些问题: 矩阵X维度常常改变,而且因为大部分词并非共现而致使的稀疏性,矩阵维度太高计算复杂度高等问题。
Word2vec是一个多层的神经网络,一样能够将词向量化。在Word2vec中最重要的两个模型是CBOW(Continuous Bag-of-Word)模型和Skip-gram(Continuous Skip-gram)模型,两个模型都包含三层: 输入层,投影层,输出层。CBOW模型的做用是已知当前词Wt的上下文环境(Wt-2,Wt-1,Wt+1,Wt+2)来预测当前词,Skip-gram模型的做用是根据当前词Wt来预测上下文(Wt-2,Wt-1,Wt+1,Wt+2)。在模型求解中,和通常的机器学习方法相似,也是定义不一样的损失函数,使用梯度降低法寻找最优值。Word2vec模型求解中,使用了Hierarchical Softmax方法和Negative Sampling两种方法。经过使用Word2vec,咱们能够方便的将词转化成向量表示,让计算机和理解图像中的每一个点同样,数字化词的表现。
深度学习有不少种不一样类型的网络,在图像识别领域,CNN(Convolutional Neural Network,卷积神经网络)使用的较多,而在NLP领域,考虑到上下文的RNN(Recurrent Neural Networks,循环神经网络)取得了巨大的成功。在传统的神经网络中,从输入层到隐藏层到输出层,层之间是全链接的,可是每层内部的节点之间是无链接的。由于这样的缘由,传统的神经网络不能利用上下文关系, 而在天然语言处理中,上下文关系很是重要,一个句子中先后词并不独立,不一样的组合会有不一样的意义,好比”优秀”这个词,若是前面是”不”字,则意义彻底相反。RNN则考虑到网络前一时刻的输出对当前输出的影响,将隐藏层内部的节点也链接起来,即当前时刻一个节点的输入除了上一层的输出外,还包括上一时刻隐藏层的输出。RNN在理论上能够储存任意长度的转态序列,可是在不一样的场景中这个长度可能不一样。好比在词的预测例子中: 1,“他是亿万富翁,他很?”; 2,“他的房子每平米物业费40元,而且像这样的房子他有十几套,他很?”。从这两个句子中咱们已经能猜到?表明“有钱”或其余相似的词汇,可是明显,第一句话预测最后一个词时的上线文序列很短,而第二段话较长。若是预测一个词汇须要较长的上下文,随着这个距离的增加,RNN将很难学到这些长距离的信息依赖,虽然这对咱们人类相对容易。在实践中,已被证实使用最普遍的模型是LSTM(Long Short-Term Memory,长短时记忆)很好的解决了这个问题。
LSTM最先由Hochreiter 及 Schmidhuber在1997年的论文中提出。首先LSTM也是一种RNN,不一样的是LSTM可以学会远距离的上下文依赖,可以存储较远距离上下文对当前时间节点的影响。
全部的RNN都有一串重复的神经网络模块。对于标准的RNN,这个模块都比较简单,好比使用单独的tanh层。LSTM拥有相似的结构,可是不一样的是,LSTM的每一个模块拥有更复杂的神经网络结构: 4层相互影响的神经网络。在LSTM每一个单元中,由于门结构的存在,对于每一个单元的转态,使得LSTM拥有增长或减小信息的能力。
图2.6 标准RNN模型中的重复模块包括1层结构
图2.7 LSTM模型中的重复模块包括4层结构
Keras(http://keras.io)是一个很是易用的深度学习框架,使用python语言编写,是一个高度模块化的神经网络库,后端同时支持Theano和TensorFlow,而Theano和TensorFlow支持GPU,所以使用keras可使用GPU加速模型训练。Keras中包括了构建模型经常使用的模块,如Optimizers优化方法模块,Activations激活函数模块,Initializations初始化模块,Layers多种网络层模块等,能够很是方便快速的搭建一个网络模型,使得开发人员能够快速上手,并将精力放在模型设计而不是具体实现上。常见的神经网络模型如CNN,RNN等,使用keras均可以很快搭建出来,开发人员只须要将数据准备成keras须要的格式丢进网络训练便可。若是对keras中自带的layer有更多的需求,keras还能够本身定制所需的layer。
Keras项目中的example自带了多个示例,包括经典的mnist手写识别测试等,其中和NLP相关的示例有不少,好比基于imdb数据的情感分析、文本分类、序列标注等。其中lstm_text_generation.py示例能够用来参考设计序列标注问题,这个示例试图经过LSTM学习尼采的做品,经过序列标注的思想来训练一个文本生成器模型。下面着重看一下两个关键点:模型数据格式及模型设计。
这段代码是数据准备的状况。将尼采全文进行数据切割,每40个字符为一个片断,将紧接这个片断的字符做为预测值,来进行训练。字符片断的间隔为3。
在模型设计上,主要是使用了两层LSTM,每层的输出维度为512,并在每层LSTM后面加入了Dropout层,来防止过拟合。整个模型的输入维度是字符类别的个数,输入字符串长度是40,模型的输出维度也是字符类别长度。整个模型表达的意思是每输入40个字符,就会从模型中输出一个预测的字符。由于LSTM的对长依赖term的记忆性,所以在上下文很长(40个字符)的状况下也能够表现的很好。
基于上面的知识,能够考虑使用深度学习的方法进行中文分词。分词的基础思想仍是使用序列标注问题,将一个句子中的每一个字标记成BEMS四种label。模型整的输入是字符序列,输出是一个标注序列,所以这是一个标准的sequence to sequence问题。由于一个句子中每一个字的上下文对这个字的label类型影响很大,所以考虑使用RNN模型来解决。
测试硬件是Macbook Pro 2014 Mid高配版,带Nvidia GT 750M GPU,虽然GPU性能有限,但经过测试性能仍是强过mac自带的i7 CPU。使用GPU进行模型运算,须要安装Nvidia的cuda相关程序及cuDNN库,会有较大的性能提高。软件方面使用python2.7,安装好了keras,theano及相关库。关于keras使用GPU训练的环境搭建问题,能够参考这篇文章(Run Keras on Mac OS with GPU,http://blog.wenhaolee.com/run-keras-on-mac-os-with-gpu/)。
模型训练使用的是经典的bakeoff2005中的微软研究院的切分语料,将其中的train部分拿过来作训练,将test做为最终的测试。
首先,将训练样本中出现的全部字符所有映射成对应的数字,将文本数字化,造成一个字符到数据的映射。在分词中,一个词的label受上下文影响很大,所以参考以前提到的lstm_text_generation.py示例,咱们将一个长度为n个字符的输入文本处理成n个长度为k的向量,k为奇数。举例来讲,当k=7时,表示考虑了一个字前3个字和后三个字的上下文,将这个七个字做为一个输入,输出就是这个字的label类型(BEMS)。
参考lstm_text_generation.py 中的模型搭建方式,咱们采用一层的LSTM构建网络,代码以下:
其中,输入的维度input_dim 是字符类别总数,hidden_node 是隐藏层的结点个数。在上面的模型中,第一层输入层Embedding的做用是将输入的整数向量化。在如今这个模型中,输入是一个一维向量,里面每一个值是字符对应的整数,Embedding层就能够将这些整数向量化,简单来说就是生成了每一个字的字向量。接下来紧跟着一层是LSTM,它输出维度也是隐藏层的结点个数。Dropout层的做用是让一些神经节点随机不工做,来防止过拟合现象。Dense层是最后的输出,这里nb_classes的数目是4,表明一个字符的label。模型创建好后开始训练,重复20次,训练的结果以下:
图4.1 基础模型(1层LSTM优化器RMSprop)训练20次
训练好后,咱们使用msr_test的测试数据进行分词,并将最终的分词结果使用icwb2自带的脚本进行测试,结果以下:
图4.2 基础模型F Score: 0.845
能够看到基础模型的F值通常,比传统的CRF效果差的较多,所以考虑优化模型。
1) 模型参数调整
首先想到的是模型参数的调整。Keras官方文档中提到,RMSprop优化方法在RNN网络中一般是一个好的选择,可是在尝试了其余的优化器后,好比Adam,发现能够取得更好的效果。
图4.3 1层LSTM优化器Adam训练20次
能够看到,Adam在训练过程当中的精度就已经高于RMSprop,使用icwb2的测试结果为:
图4.4 修改优化器Adam后的模型F Score:0.889
2) 模型结构改变
如今网络结构较简单,只有一层LSTM,参考文档示例中的模型设计,考虑使用两层的LSTM来进行测试,修改后的代码以下:
注意,第一层LSTM有个return_sequences =True能够将最后一个结果出入到输出序列,保证输出的tensor是3D的,由于LSTM的输入要求是3D的tensor。
两层LSTM模型训练过程以下:
图4.5 2层LSTM优化器Adam训练20次的模型
能够看到,两层LSTM使得模型更加复杂,训练时常也增长很多。模型训练后,使用icwb2的测试结果为:
图4.6 两层LSTM的模型F Score:0.889
能够看到,随着模型的复杂,虽然F Score无提高,可是其余的指标有必定的提高。通常来讲,神经网络在大量训练数据下也会有更好的效果,后续会继续尝试更大数据集更复杂模型的效果。
深度学习技术给NLP技术中的中文分词技术带来了新鲜血液,改变了传统的思路。深度神经网络的优势是能够自动发现特征,大大减小了特征工程的工做量。随着技术的进一步发展,深度学习在NLP领域将会发挥更大的做用。达观数据将在已有成熟的NLP算法及模型基础上,逐渐融合基于深度神经网络的NLP模型,在文本分类、序列标注、情感分析、语义分析等功能上进一步优化提高效果,来更好为客户服务。