基于凝聚度和自由度的非监督词库生成

中文分词是中文文本自然语言处理的第一步,然而分词效果的好坏取决于所使用的语料词库和分词模型。主流的分词模型比较固定,而好的语料词库往往很难获得,并且大多需要人工标注。这里介绍一种基于词频、凝聚度和自由度的非监督词库生成方法,什么是非监督呢?输入一大段文本,通过定义好的模型和算法,即可自动生成词库,不需要更多的工作,听起来是不是还不错?

参考文章:互联网时代的社会语言学:基于SNS的文本数据挖掘,点击阅读原文即可查看。访问我的个人网站查看更详细的内容,包括所使用的测试文本和代码。

获取所有的备选词语

假设对于一段很长的文本,例如《西游记》的全文,我的网站上提供了utf-8和gbk两个版本,我在mac上进行处理,因此使用的是utf-8版本,我关注的最大词语长度为5,因此可以使用正则匹配出全部的单个汉字、双汉字、三汉字、四汉字、五汉字,作为可能的备选词语。

由于python中的re模块进行的是非重叠匹配,因此在匹配多汉字词语时返回的数量会有遗漏,以下是python的re模块官方文档中的说明。

# Return all non-overlapping matches of pattern in string, # as a list of strings. The string is scanned left-to-right, # and matches are returned in the order found. # If one or more groups are present in the pattern, # return a list of groups; # this will be a list of tuples if the pattern has more than one group. # Empty matches are included in the result unless they touch the beginning of another match. re.findall(pattern, string, flags=0)

所以我用的是python的regex模块,可以进行多汉字的重叠匹配。

import regex as re # 以下为在utf-8编码中匹配汉字的正则表达式 reg = ur'[\u4e00-\u9fa5]{2}' # 返回的reg为一个list,即为去重后的全部双汉字词语 reg = list(set(re.findall(reg, self.content, overlapped=True)))

因此使用正则表达式,可以很方便地提取出全部可能的备选词语。

词频

一个很直观的认识是,真正的词语应该至少出现一定次数,而那些局部切割出来的碎片出现次数则较少,因此可以统计一下以上全部可能备选词语的词频,作为我们判断是否成词的第一个标准。

对于《西游记》而言,一共出现了4459个汉字,而长度不超过5个汉字的全部可能备选词语共824567个。为了得到这些词语的词频,我写了一个循环,挨个在《西游记》中查找每一个词的词频。但是事实证明这样相当浪费时间,因为这个循环需要进行824567次!所以更好的方法是,同样还是使用regex匹配单汉字、双汉字、三汉字、四汉字和五汉字词语,只不过不进行set、list的去重操作,这样返回的匹配结果中便包含了全部备选词语的词频,而且一共只需执行五次正则匹配,所需时间会少很多。

聚合度

我们已经得到了全部可能备选词语的词频了,但这并不是判断成词的全部标准。自然语言处理中有停用词的概念,也就是那些使用频繁,但是不包含有用信息的词语,如“的”、“了”、“着”等,因此还需要计算更多用于判断是否成词的标准。

聚合度的概念很好理解,例如“齐天”会和“大圣”出现在一起,因为“齐天大圣”这个词的聚合度很高。同样,我们还能想到类似“葡萄”、“蝙蝠”、“师父”这样的词,这些词往往作为一个整体而出现,说明它们确实是有意义的词语。

那么如何计算词语的聚合度呢?假设该词语为S,首先计算该词语出现的概率P(S),然后尝试S的所有可能的二切分,即分为左半部分sl和右半部分sr并计算P(sl)和P(sr),例如双汉字词语存在一种二切分、三汉字词语存在两种二切分。接下来计算所有二切分方案中,P(S)/(P(sl)×P(sr))的最小值,取对数之后即可作为聚合度的衡量。

以双汉字词语为例,可以想象到,如果该词语的聚合度很低,说明其第一个字和第二个字的相关性很弱,甚至是不相关的,那么P(S)和P(sl)×P(sr)将处于同一个数量级。相反,如果该词语的聚合度很高,“齐天”、“大圣”和“齐天大圣”三者的概率都很接近,因此P(S)/(P(sl)×P(sr))将是一个远大于1的数值。

取对数有三个好处:

  • 避免概率过低造成下溢出;
  • 将取值范围映射到更平滑的区间中;
  • 当P(S)和P(sl)×P(sr)处于同一个数量级时,P(S)/(P(sl)×P(sr))接近1,取对数后为0,对应一个很低的聚合度。

通过以上方式,我们可以计算全部可能备选词语的聚合度。有意思的是,《西游记》的824567个备选词中,对应的聚合度范围为-6至20。为什么会出现负数呢?说明P(S)比P(sl)×P(sr)更小,即sl和sr同时出现的可能性更低,因此别提聚合,可能还存在某些排斥。

在具体实现上,由于上一步中已经计算了全部可能备选词语的词频,当然也包括全部的二切分词,因此计算词语的概率以及聚合度都是很方便的。

自由度

有了聚合度的概念,我们可以自动识别出类似“齐天大圣”这样的词语。那么问题来了,“天大圣”是一个有效词语吗?“齐天大”呢?可以推测到,这两个词语的聚合度也很高,但是它们却不应该成为有效的词语。

一个有效的词语,应该能够被灵活地运用到各种语句中,其上下文搭配应当是丰富的,这便是自由度的概念。“天大圣”的左边绝大多数都是“齐”,“齐天大”的右边绝大多数都是“圣”,因此它们的自由度很低。

可以用熵来衡量一个词语的自由度。假设一个词语一共出现了N次,其左边共出现过n个汉字,每个汉字依次出现N1,N2,……,Nn次,则满足N = N1 + N2 + …… + Nn,因此可以计算该词语左边各个汉字出现的概率,并根据熵公式计算左邻熵。熵越小则自由度越低,例如“天大圣”的左邻熵接近于0,因为“齐”字的概率几乎为1;熵越大则自由度越高,表示用词搭配越混乱、越自由、越多样。因为“天大圣”的左邻熵很小,而右邻熵则相对较大,因此我们将一个词语左邻熵和右邻熵中较小者作为最终的自由度。

通过以上方式,我们可以计算出全部可能备选词语的自由度,《西游记》的824567个备选词中,对应的自由度范围为0至8,这和熵的非负性相吻合。

在具体实现中,一开始我也是写了个循环,对《西游记》的824567个词语各写一个正则,匹配出其左右可能搭配的汉字,结果依旧非常耗时。更好更快的解决方案是,依旧使用regex只写五次正则,分别处理单汉字、双汉字、三汉字、四汉字和五汉字,只不过在原来的基础上在两边各加一个字符,然后将全部的匹配结果映射到对应的词语中即可。

综合起来

有了频数、聚合度和自由度,就可以对全部可能的备选词语进行筛选了。例如,对频率、聚合度和自由度分别设置阈值,仅保留三项指标都超过阈值的词语。对于筛选过后的词语,可以考虑使用频率,或者频率、聚合度、自由度三者的乘积,作为最终输出的排序指标。基于以上方法,能够很好地去除停用词,并自动生成有意义和有代表性的词语。

《西游记》输出的词语包括行者、师父、八戒、唐僧、菩萨、大圣、和尚、三藏、妖精、沙僧、老孙等,还是很有代表性的。

实现

我的网站上提供了一个代码corpus.py,里面的CorpusGenerator类构造函数的参数包括待分析的文本content、最大词长maxlen、返回得分靠前的词数量topK、频率阈值tfreq、凝聚度阈值tDOA和自由度阈值tDOF,还提供了以下几个函数:

  • get_characters(),返回一个list,对应全部可能的单字;
  • get_possible_words(),返回一个list,对应长度不超过maxlen的全部可能备选词语;
  • get_frequency(),计算全部可能备选词语的词频,存储在self.result[key]['freq']中;
  • get_doa(),计算全部可能备选词语的凝聚度,存储在self.result[key]['doa']中;
  • get_dof(),计算全部可能备选词语的自由度,存储在self.result[key]['dof']中;
  • get_score(),基于频率、凝聚度、自由度计算全部可能备选词语的得分,存储在self.result[key]['score']中;
  • generate(),依次执行以上四个计算函数,并根据得分和topK返回一个list,每个元素即为一个经过筛选的备选词语;
  • distribution(),在执行generate()之后可调用,根据self.result绘制全部可能备选词语的频率、凝聚度、自由度的散点图矩阵,以便查看各参数分布情况。

进一步的工作

我们建立了一个基于凝聚度和自由度的非监督词库生成模型,输入一大段文本,可以自动输出符合要求的词语。那么除此之外,我们还可以做些什么?

假设我们的输入不是类似《西游记》这样的大段文本,而是具有时间戳的互联网语料,例如每天发布的用户微博和评论等内容。通过对每一天的UGC(User Generated Content,用户产生内容)进行成词,我们可以发现每天的热点词语有哪些。如果使用更短的时间间隔,则可以进一步发现更为实时的热词结果。

通过对子类别分别进行成词,可以做一些群体画像的工作。例如分别对男性和女性的UGC成词,可以发现男女用词的特点和不同;分别对不同城市和地区的UGC成词,可以发现语言用词的地域变化;分别对不同年龄段的UGC成词,可以发现70后、80后、90后和00后们分别在关注什么。如果文本语料有足够的额外信息,能够进一步做的工作是相当丰富的。

原文发布于微信公众号 - 宏伦工作室(HonlanFarm)

原文发表时间:2016-08-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器人网

一图向菜鸟解释机器学习、数据挖掘

随着数据科学在人工智能发展中大放异彩,数据挖掘、机器学习进入了越来越多人的视野。而对于很多人来说,诸如机器学习之类的名次听起来是神乎其技,但其真正的内涵却不为一...

29870
来自专栏CSDN技术头条

MIT牛人梳理脉络详解宏伟现代数据体系

在过去的一年中,我一直在数学的海洋中游荡,research进展不多,对于数学世界的阅历算是有了一些长进。 【为什么要深入数学的世界】 作为计算机的学生,我没有任...

216100
来自专栏AI科技评论

韩家炜在数据挖掘上开辟的「小路」是什么

AI 科技评论按:前些日子,数据挖掘领域的巨擘韩家炜教授 [1] 在中国计算机学会(CCF)主办的第 87 期 CCF 学科前沿讲习班(CCF Advanced...

57380
来自专栏牛客网

牛客网平均水平的算法工程师面经分享

记录下渣硕的秋招经历,粗略估算大约海投了59家,真正面了9家左右吧,笔试大概也做了几十家吧,目前的情况是拿到了苏宁(准备拒)、好未来(已拒)、百度、腾讯的off...

926110
来自专栏ATYUN订阅号

为初学者打造的Fastai学习课程指南

“学习Fastai从哪开始?”这个问题可能并不合适。那么是不是要直接看第一个视频?并不是。

46340
来自专栏段石石的专栏

Word2Vec 的迁移实践:Tag2Vec

今天我们就来重点关注下基于用户行为的内容表示的一些有意思的东西。

1.1K20
来自专栏程序员叨叨叨

3.1 Shader Language 原理第 3 章 Shader Language

In the last year I have never had to write a single HLSL/GLSL shader. Bottom lin...

9430
来自专栏云时之间

NLP系列学习:文本分词

中文分词是中文自然语言处理的一个非常重要的组成部分,在学界和工业界都有比较长时间的研究历史,也有一些比较成熟的解决方案

17120
来自专栏量化投资与机器学习

【年度系列】股市风起云涌,我用Python分析周期之道

股票市场周期是股票市场长期的价格模式,通常与商业周期有关。 它是技术分析的关键,其中投资方法基于周期或重复的价格模式。 如果我们对股市周期有了更好的理解,我们总...

18620
来自专栏IT派

Python数据分析与挖掘学习路线图

语言的学习,真正掌握语言的方式,是交流与实践,所以,这三本书,是由浅入深的步骤。大家在学习过程中,可以到群里面去进行交流沟通。

15720

扫码关注云+社区

领取腾讯云代金券