中文分词技术及在 58 搜索的实践
文本、语言作为记录和传播信息的重要载体,对它高效的理解一直是人们关注的问题。自现代电子计算机出现后,计算机在很多事情上做得比人还好,计算机与语言处理的相遇就出现了自然语言处理(Natural Language Process, NLP)技术。NLP通俗的理解就是利用计算机对文本进行分析、加工。
中文语言处理则是针对中文的语言处理技术,其基础技术一般包括以下几个层次:词法分析(分词、词性标注、实体识别)、句法分析、语义分析、语用分析。而中文分词是其中最基础也是使用最多的分析技术,像上层的信息检索、文本分类、机器翻译、问答系统、自动文摘等都会用到中文分词,可以说分词是中文语言处理技术的根基。
中文为什么要分词
一般认为,词是最小的能独立活动的有意义的语言成分(单个的字符是没有意义的,有的话它就是个单字词)。中文和英文不同,英文是小字符集上的词串,文本中词与词之间有天然的分隔符(空格)。而中文是大字符集上的字串,词与词之间没有明显的分隔符。中文分词就是将连续的中文语言文本切分成一系列独立的有意义的基本词汇的过程。下面,我们以搜索场景为例,阐述中文分词的重要性,其他场景也应有类似考虑。
我们知道,搜索引擎中用到的一种重要结构是倒排索引,由索引key和包含这个key的所有文档的id排列成的倒排链组成。搜索的过程就是对倒排链做布尔运算的过程(一般是求交运算)。假如没有中文分词,则一般只能以单字作为索引key建立倒排。如果一篇文档标题为“天河北租房”,则“天”、“河”、“北”、“租”、“房”各自索引,则该文档id会出现在这5个key的倒排链中。这种方式的第一个问题是搜索的准确率不佳,可能召回不相关的文档,比如搜索“河北租房”,该文档也是会被召回的。另一个更为严重的问题是计算量的激增。比如目前58主站搜索是10亿级的文档,按文档标题为10字长度(考虑正文、类目等文本时文本会更长)以及常用汉字为3000个来算,则每个索引key的倒排链长平均在300万以上。如果每次的查询词长度为4,则一次查询的计算量是4条300w+的倒排链同时求交。面对每天几十亿的查询,这样的索引方式仿佛是一场灾难。为解决搜索的准确率和搜索效率问题,准确的分词是必不可少的。
**中文分词中的基本问题 **
在介绍具体方法之前,我们先了解下中文分词所面临的几个基本问题。从定义可以知道,中文分词其实就是要识别词的边界,即在连续文本中的词与词之间打上分隔符一般用空格或斜线“/”。这个问题看似简单,却也让几代学人扼腕叹息。如下例:“结婚的和尚未结婚的”、“严守一把手机关了”、“南京市长江大桥”、“秦兵坑杀赵军四十万于长平”。让机器来切分这些文本实在有些强“机”所难了。
其实归纳起来,中文分词主要困难源自三个方面:分词规范、歧义切分和未登录词识别。分词规范讨论的是“词是什么”(词的抽象定义)及“什么是词”(词的具体界定,包括字、词、短语的划界),这俩问题略有些飘忽不定,也未能形成公认的权威的词表,此处暂按下不表。但这个问题在分词的过程中,却是无法回避且令人头疼的。比如词表的建立、分词语料标注等。
歧义切分问题
歧义切分指同样一句话,可能有两种或者更多的切分方法,这在中文文本中是普遍存在的。目前的歧义一般可简单划分为两种:交集型切分歧义和组合型切分歧义。如果中文字串AJB满足AJ和JB同时为词(A、J、B均为为中文字串),则称AJB为交集型切分歧义。例如“结合成”、“大学生”、“确实在理”。如果中文字串AB,满足AB、A、B同时为词,则称AB为组合型切分歧义。例如“人手”、“将来”、“学生会”。处理此类歧义切分问题单靠词典匹配不能达到好的效果,一般需要通过复杂的上下文分析解决。
**未登录词问题 **
所谓未登录词或称为生词(unknown word),一是指已有的词表中没有收录的词 ,或者指已有的训练语料中未曾出现过的词。在后者的情况下未登录词亦有称为集外词(out of vocabulary,OOV)的,即训练数据集以外的词。
未登录词情况比较复杂,可以粗略划分以下几种情况:1)新出现的普通词,尤其是网络用语、流行词汇,如“奥特”、“神马”、“不明觉厉”等。2)专有名词,包括人名、地名、组织机构名以及日期、时刻、百分比等,如“王二小”、“津市市”、“58”等。3)领域名词,如“三聚氰胺”、“禽流感”、“满五唯一”、“五险一金”等。4)其他专有名词,包括新出现的产品、电影、书籍等的名称。有学者统计过,未登录词中大约九成是专有名词,即上文中的第二类。而在大规模的真实文本中,未登录词对于分词精度的影响要远超(10-20倍)歧义切分,对于一个分词系统来说,如果不能及时更新词表或训练语料,一旦这类词语出现在待切分文本中,则切分结果基本是错误的,进而影响上层应用。因此未登录词的识别是分词工作者面临的最大挑战。
中文分词方法
中文分词经过多年的发展,已出现很多分词方法,其大体上可分为两类。一类是基于词表的分词方法,也有称机械分词法或基于规则分词方法的。包括正向最大匹配、逆向最大匹配、双向匹配、最少切分词法等。另一种是基于统计模型的分词方法,包括n元语言模型、HMM、CRF、RNN等模型的分词方法。下面会沿着中文分词的发展轨迹介绍几种典型的分词方法,58搜索的分词也基本是按照此轨迹演进。
词表分词法
顾名思义,该方法的核心思想就是查词表,把带切分文本从左向右扫描一遍,遇到词表中的词就标识出来,遇到复合词(比如“清华大学”)就找最长匹配,遇到不认识的字串就分割成单字词,于是简单的分词就完成了。这个方法的优点很明显,就是够简单,虽简单但也能解决七八成的分词问题。而它的缺点也同样明显,就是太简单,对于上文提到的歧义切分问题,它无法有效解决,完全看“人品”切分,而对于未登录词的识别更是无能为力。虽然基于词表的方法也有不同的匹配策略,但也只能在一定程度上解决有限类别的歧义问题,而为了识别新词需要不断往词表中加入更多的词,这也会加重分词的歧义问题。为了较系统的解决此类问题,需要新的思路。
词表与统计(n元语言模型)结合的分词方法
歧义切分是源于带切分文本有多种切分方式,比如“这话说的确实在理”,这里的“的确”、“确实”、“实在”、“在理”均能成词。那么如何来确定切出哪些词呢?有人可能会想到使用词频信息,即哪个词在真实语料库中出现频率更高,或者说出现概率_P(w)更大,那它更应被切分出来。比如我们可以计算_P(的)、_P(的确)来确定是切出“的”还是“的确”,但这种方式割裂了上下文的联系。那么,更进一步我们可以计算_P(的|这 话 说),_P(的确|这 话 说)_分别表示在出现分词序列“这 话 说”的条件下,接下来出现“的”和“的确”的概率,按照这种考虑了上下文的转移概率来确定切词的方式显然更准确。再进一步思考,考虑整个句子所有可能的切分,其实我们是想从所有可能的切分方式中找出成句概率最大的一种切分。假设待切分文本T可以有n种切分方法,比方说三种:
其中带下标的A、B、C均为中文词,最好的切分方法应该要保证分词序列出现概率最大,即若第一种切分方式最优,则应满足
且
计算切分序列的出现概率则要用到基于词的语言模型,一般我们常用到二元语言模型,公式表示如下:
其中,表示句首符,表示句尾符,式中条件概率可通过统计语料库中的词频和共现频率来计算。
词表加二元语言模型的分词法的基本思想完整表述如下:首先根据词表,对带切分文本进行匹配,找出所有可能的词(即全切分),这样就不会遗漏可能的正确切分方式。然后将它们和所有的单字作为节点,按下图所示方式构建切分有向无环图。图中节点表示可能的候选词,边表示路径,边的前后候选词间的二元转移概率表示边权重。最后利用相关的搜索算法(比如维特比算法)找出权重最大的路径,该路径对应的切分就是概率最大的切分方式。这种加入统计知识的方法已经可以比较好的解决歧义切分问题,再配合独立的未登录词识别模块,整个分词系统也算基本完善。
58搜索早期使用的分词系统就是基于此方法,它主要得解决两个问题:初始语言模型的训练以及未登录词识别模块的构建。n元语言模型本身就是一个大的方向,往深了做,在很多点上下功夫都能提升效果(解决歧义切分问题),比如词表扩展、词表匹配算法、专有名词识别、词类标注的使用等。但前面也提到过未登录词识别才是影响分词精度的最重要因素,因此在语言模型的优化上并未花费过多精力。我们仅采用的是基于词表的逆向最大匹配算法对原始的语料库进行切分,然后统计词频和词间转移概率得到语言模型。未登录词识别模块我们主要通过基于大规模语料的新词发现和人工审核的方式来挖掘新词并加入到词表中。其中新词发现则是利用词频、词的内部凝固度(通过互信息计算)和左右自由度(通过左右信息熵计算)来挖掘。综合这些,分词效果基本能达到工业可用的程度。
字序列标注法--CRF模型
由字构词的思想在02年首次提出,之后基于它的分词方法在各评测和比赛中表现优异且在未登录词识别上更是高居榜首,而前面提到过未登录词识别对分词精度的影响10倍于歧义切分,因此人们更青睐这种最高未登录词召回率的分词方法。由字构词的分词方法是将分词过程看做是序列中字的分类问题,每个字在构造一个特定词语时都占据着一个确定的构词位置,比如词首(B)、词中(M)、词尾(E)和单独成词(S),下例所示即为原文、字标注和分词结果的对照:
**原文:**这话说的确实在理
字标注: 这/S 话/S 说/S 的/S 确/B 实/E 在/B 理/E
**分词结果:**这 话 说 的 确实 在理
这种将分词结果表示成字标注的形式后,分词问题也就变成了序列标注问题。与词典方法相比,虽然看问题的角度和使用的方法变了,但目标是类似的,前者是要找到概率最大的词序列(由语言模型计算),后者是要找到概率最大的字标记序列,其数学形式如下:
其中_X _表示待切分文本,_GEN(X)_所有可能的标记序列,_Y _表示一种可能的标注(比如BMEBE)。
目前使用较多的序列标注方法有隐马尔科夫模型(HMM)和条件随机场(CRF),HMM相比CRF模型要小的多,因此模型训练和预测都要快的多。相对的CRF模型更大,在标注时更多的考虑了当前位置的前后观测序列(字序列)的特征,简单的说就是更好的利用了上下文信息,因此它的功能更强大。目前,58搜索使用的分词系统,其核心就是CRF模型。
条件随机场是给定条件下的马尔科夫随机场的意思,它是一种概率图模型(节点表示随机变量,边相连表示变量间有概率依赖关系)。从名字能想到,这个图中的节点集合应被划分为两组随机变量(一个是条件,另一个是随机场),比如_X_(可代入待切分的字序列)和_Y_(字序列对应的标记序列),如果在给定X的条件下,_Y _满足马尔科夫随机场,则称之为条件随机场。那么_Y _如何才算满足马尔科夫随机场呢?需要满足两个条件,一是马尔科夫性质,后一时刻变量取值(打标记)的概率分布仅与前一时刻的变量取值相关。另一个是随机场,要给每一个位置按照某种分布随机赋予相空间(B、M、E、S)的一个值,其全体称之为随机场。序列标注场景下要使用的是线性链条件随机场,即_X、Y _均为线性链表示的随机变量序列,可表示为下图,
其中_X _为字序列,也称为输入观测序列,_Y _为标记序列,也称为输出标记序列或状态序列。满足这种条件的概率图的联合概率有如下形式:
式中,_x、y_分别为随机变量_X、Y_的取值,_Z(x)_为归一化因子,f_为特征函数,为相应的特征的权值。特征函数取值一般为0或1,特征出现为1,否则为_0。以“这话说的确实在理”为例,若标记序列为“SSSSBEBE”,上下文窗口选择3的话,考虑i=5 时(即“确”字的位置)出现的特征:
上式列举的特征中前6个特征是定义在节点(_Y _序列)上的特征,称之为状态特征。第7个特征是定义在边上的特征,称之为转移特征,依赖当前位置和前一个位置。CRF模型的特征一般是通过配置的特征模板来提取,可以看出这些特征比较规整,也容易理解。CRF模型训练就是求解所有出现过的特征的权值的过程,有了特征权值,就能方便的计算各节点取值的概率分布,即被标记为B、M、E、S的概率,最后考虑边上的转移权值,我们只要找出概率最大的标记序列。如下图所示,每个节点和边上都有权值,最优的标记序列就对应全连接图中的权值最大的路径,一般采用维特比算法来搜索最优路径。
从以上的介绍中可以看出CRF模型可以比较充分的利用上下文信息来对文本进行切分,因此它能高效的解决歧义切分和未登录词的识别问题,而且分词准确率可以达到95%以上。但同时它也有其不足,比如分词不一致。下面的篇幅会介绍58搜索在CRF分词方法上的一些实践经验。
58搜索CRF分词实践
**
**
本部分主要针对CRF模型的不足以及58搜索场景对分词的更多诉求来讨论下我们实践过程中的几点模型之外的工作。目前58搜索所使用的基于CRF模型的分词系统如下图所示。图中模型之外的模块主要是针对性解决CRF模型的不足以及58搜索场景对分词的更多诉求。下面会展开讨论。
分词不一致问题
分词不一致是指在不同的上下文中,对相同的文本段,模型切分出了不同的结果。比如文档标题是“男装店母婴店装潢装修”,模型分词为“男装 店 母婴 店 装潢 装修”,用户查询词为“男装店装修”,模型分词为“男装店 装修”。这样在查询时,该目标贴不能被召回。分词不一致主要由两个原因引起,一个是分词语料标注时会出现不一致。在人工标注时,不同的人对同一个句子可能有不同的切分方式,就算是同一个人在不同时刻对同一个句子也可能会有不同的想法。因此,一个相对明确的分词规范是必要的,然后还需要对标注完的分词语料做一致性校验,比如“男装店”在分词语料中是否同时存在“男装店”和“男装 店”,将校验的结果反馈给标注人员check进行修正,重复此过程即可缓解分词模型的不一致切分问题。不一致的另一个原因是模型的固有缺陷,当上下文变化时,整个最优路径的搜索过程就发生了改变,局部文本的标记不一致难以避免。针对这个问题,我们通过配置特征词典和规则匹配来识别文本中成词特征非常明显的词,比如标点符号、成语、领域专有词汇以及url、email、电话号码、日期等,从该处将文本切开,得到短文本列表,然后分别对各短文本使用模型切词。这样可以在一定程度上削弱上下文不一致的概率,缓解不一致切分问题。当然这也会减弱切分过程对上下文信息的利用效率,对切分的精度会有影响。这个过程需要有严格的评测和权衡。尤其在搜索场景,我认为,对分词一致性的要求,也是不逊于对精度的要求。
分词粒度问题
分词粒度一般分为粗粒度和细粒度,比如“58同城”是否要切成“58”和“同城”,“将府家园”是否要切成“将府”和“家园”,“酒仙桥北路”是否要切成“酒仙桥”和“北路”。针对特定的应用场景,可以构造对应粒度的分词器。而更多的情况是让一个分词器同时支持不同粒度的切分。在搜索场景,粗粒度的分词,搜索结果会更准确、更相关,但容易出现少结果和无结果。而细粒度分词则能够保证足够的召回,然后配合有效的相关性打分策略保证更相关的结果出现在靠前的位置。因此我们采用较细粒度的分词规范,当然在索引分词模式下还需加入扩展分词来进一步提升召回率,这在下面的模块介绍。
索引分词扩展
索引分词扩展是指在索引分词模式下,需要切出尽可能多的有效分词,我们的策略中目前包括同义词扩展、蕴含词扩展以及规则扩展。同义词扩展比较好理解,通过同义词词典匹配完成。蕴含词是一个词中包含了另一个词,比如“健身房”蕴含“健身”,“理发厅”蕴含“理发”等,这也是通过挖掘出的蕴含词典匹配完成。规则扩展要解决的是如下情况,比如“第N医院”(N为数字)要扩展出“N医院”,“酒仙桥D路”(D表示东、南、西、北、中)要扩展出“酒仙桥”、“酒仙桥路”、“酒仙桥街”等。这部分通过配置规则模板解决。
语料收集及标注
常说NLP的应用中有60%-70%的时间是花在语料的收集和数据的处理上(包括归一化及前面提到一致性check和分词粒度的调整等),这是不假的,甚至会更多。这里介绍下语料收集的一些技巧。原始语料的积累包括两部分:一个是开源的已标注语料,比如人民日报语料和搜狗语料,另一个就是58场景的文本语料,这部分需要人工来标注,积累速度较慢。那么在积累了一定的初始标注语料后,我们不能仍像之前一样无差别的收集线上原始语料来标注,标注的重点应该集中在当前语料未覆盖到的或者基于当前语料训练出的模型不能做出较好预测的原始语料上。
这里给出我们使用的两个策略,一个是在线分析,模型在线预测是求解概率最大的标注路径的过程,我们可以在对生语料(未标注的语料)的预测时,将最优路径的概率小于某一阈值的生语料收集起来作为候选语料。另一个是离线分析,首先离线收集大规模的生语料,用当前模型进行分词。然后统计整个分词后的语料中各个分词的词频、内部凝固度、左右自由度(前文提到的新词发现也是使用这几个指标),用来评价各分词的成词得分(是个好词,还是坏词),进而可以计算一句话的切分得分,从而就能挖掘一批当前模型切分不佳的语料,并加入到标注平台进行人工标注。
结语
综上,我们沿着分词技术的发展介绍了几种有代表性的分词方法,包括词典分词、词典和统计结合的分词和基于统计模型的分词。可以看出,整个发展路径是从词典(规则)到统计的演进,但是这些方法所使用的统计特征主要是靠人的知识来指定的,像词频、转移概率、CRF的特征模板等。而NLP中更丰富的深层次的语义特征,往往是无法直观感知到的。此后,就出现了基于神经网络或者说深度学习模型的分词方法,它们能不依赖人的知识来提取特征,而能够从语料中自己学习到丰富的语义特征,这让它们拥有更大的潜力。再之后又出现了基于大规模未标注语料预训练模型和具体任务微调的方法,它能够在少量标注语料的情况下就达到很好的分词效果。在深度学习和预训练语言模型的方向上,我们也进行了尝试,并取得了一定成果,限于篇幅后面有机会将进一步介绍。