返回小栈
关于自然语言处理系列-分词
bq_wang2020-02-26 10:11:30


分词就是将连续的字序列按照一定的规范重新组合成词序列的过程(见百度百科)

在英文中单词之间是以空格作为自然分界符的,大多数情况下一个字即一个词;而中文分词则缺乏形式上的分界符,词以双字或多字组合居多。


中文分词技术是自然语言处理技术的基础,分词算法主要分为:基于字符串匹配的分词方法、基于理解的分词方法和基于统计的分词方法。

1、基于字符串匹配的分词方法又分为:

1)正向最大匹配法(由左到右的方向),错误率高于逆向匹配;

2)逆向最大匹配法(由右到左的方向);

3)最少切分(使每一句中切出的词数最小);

4)双向最大匹配法(将正向最大匹配方法和逆向最大匹配方法结合起来,进行由左到右、由右到左两次扫描,同词不管,优先去逆向)

据统计新华字典现收录20959个汉字,52万个词语;而且随着互联网的发展,互联网词汇、术语词汇、外语词汇等词汇量与日俱增;2018年底中文网站数量为523万,中文网页数量为2816亿,新增约200亿;所以采用字符串匹配的分词的前提是得先人工维护一个海量的词库,当然这是不太可能的。


2、基于理解的分词方法:

是让计算机模拟人对句子的理解,达到识别词的效果。其基本思想就是在分词的同时进行句法、语义分析,利用句法信息和语义信息来处理歧义现象。它通常包括三个部分:分词子系统、句法语义子系统、总控部分。在总控部分的协调下,分词子系统可以获得有关词、句子等的句法和语义信息来对分词歧义进行判断,即它模拟了人对句子的理解过程。这种分词方法需要使用大量的语言知识和信息。由于汉语语言知识的笼统、复杂性,难以将各种语言信息组织成机器可直接读取的形式,因此目前基于理解的分词系统还处在试验阶段。

目前基于理解的分词方法主要有专家系统分词法和神经网络分词法等。


3、基于统计的分词方法

该方法的主要思想:词是稳定的组合,因此在上下文中,相邻的字同时出现的次数越多,就越有可能构成一个词。因此字与字相邻出现的概率或频率能较好地反映成词的可信度。可以对训练文本中相邻出现的各个字的组合的频度进行统计,计算它们之间的互现信息。互现信息体现了汉字之间结合关系的紧密程度。当紧密程 度高于某一个阈值时,便可以认为此字组可能构成了一个词。该方法又称为无字典分词。但这种方法也有一定的局限性,会经常抽出一些共现频度高、但并不是词的常用字组。实际应用的统计分词系统都要使用一部基本的分词词典(常用词词典)进行串匹配分词,同时使用统计方法识别一些新的词,即将串频统计和串匹配结合起来,既发挥匹配分词切分速度快、效率高的特点,又利用了无词典分词结合上下文识别生词、自动消除歧义的优点。

基于统计的分词方法所应用的主要的统计模型有:N 元文法模型(N-gram)、隐马尔可夫模型(Hiden Markov Model,HMM)、最大熵模型(ME)、条件随机场模型(Conditional Random Fields,CRF)等。


中文分词的技术难点

1、命名实体识别

就是人名、机构名、地名以及其他所有以名称为标识的实体。其他实体还包括数字、日期、货币、地址、化学、医药、物理、数学等等。

2、歧义识别

歧义是指同样的一句话,可能有两种或者更多的切分方法。主要的歧义有两种:交集型歧义和组合型歧义

2.1交集型歧义(交叉歧义)- 例如:表面的,因为“表面”和“面的”都是词,那么这个短语就可以分成“表面 的”和“表 面的”。

2.2组合型歧义 - 要根据整个句子来判断。例如,在句子“这个门把手坏了”中,“把手”是个词,但在句子“请把手拿开”中,“把手”就不是一个词。

2.3真歧义 - 真歧义意思是给出一句话,由人去判断也不知道哪个应该是词,哪个应该不是词。例如:“乒乓球拍卖完了”,可以切分成“乒乓 球拍 卖 完 了”、也可切分成“乒乓球 拍卖 完 了”。

3、新词识别

命名实体、新词,专业术语称为未登录词。也就是那些在分词词典中没有收录,但又确实能称为词的那些词。最典型的是人名、机构名、地名、产品名、商标名、简称、省略语等。

4、同义词识别

同义词是指名字不同的两个词,但所指代的含义完全相同或绝大部分人对这两个词的认知一致时。例如如“鲁迅”和“周树人”指代的是同一个人


目前中文分词工具主要有Jieba, PkuSeg, HanLP,SnowNLP,  THULAC,FudanNLP,盘古分词,庖丁解牛,SCWS中文分词,LTP,NLPIR

Jieba:https://github.com/fxsjy/jieba   

PkuSeg:https://github.com/lancopku/pkuseg-python       北京大学开源分词工具

HanLP:https://github.com/hankcs/HanLP

SnowNLP: https://github.com/isnowfy/snownlp

THULAC:https://github.com/thunlp/THULAC-Python

本文主要讨论jieba和pkuseg的分词效果吧

jieba的特点:(来自github官方)

一、支持四种分词模式:

1、精确模式,试图将句子最精确地切开,适合文本分析;

2、全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义;

3、搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。

4、paddle模式,利用PaddlePaddle深度学习框架,训练序列标注(双向GRU)网络模型实现分词。同时支持词性标注。

二、支持繁体分词

三、支持自定义词典


pkuseg的特点:(来自github官方):

一、多领域分词。

目前支持新闻领域,网络领域,医药领域,旅游领域,以及混合领域的分词预训练模型。在使用中,如果用户明确待分词的领域,可加载对应的模型进行分词。

二、更高的分词准确率。相比于其他的分词工具包,当使用相同的训练数据和测试数据,pkuseg可以取得更高的分词准确率。

三、支持用户自训练模型。支持用户使用全新的标注数据进行训练。

四、支持词性标注。

本文暂时只做基本的语法验证和比较。

代码示例

  1. import jieba,pkuseg

  2. import jieba.posseg as pseg


  3. print('-----------------jieba和pkuseg基本语法----------------')

  4. str='我爱北京故宫,东直门西直门中间有个天安门'

  5. seg = jieba.cut(str) #<generator object Tokenizer.cut at 0x0000027C53AF38B8>

  6. seg_str='|'.join(seg) #我|爱|北京故宫|,|东直门|西直门|中间|有个|天安门

  7. seg_list=list(seg) #['我', '爱', '北京故宫', ',', '东直门', '西直门', '中间', '有个', '天安门']

  8. seg_str='|'.join(seg_list) #我|爱|北京故宫|,|东直门|西直门|中间|有个|天安门


  9. # pkuseg直接返回一个list,不需要再次转换

  10. seg = pkuseg.pkuseg().cut(str) # ['我', '爱', '北京', '故宫', ',', '东直门', '西直门', '中间', '有', '个', '天安门']

  11. seg_list=list(seg) #['我', '爱', '北京', '故宫', ',', '东直门', '西直门', '中间', '有', '个', '天安门']

  12. seg_str='|'.join(seg_list) #我|爱|北京|故宫|,|东直门|西直门|中间|有|个|天安门


  13. print('-----------------jieba和pkuseg简单对比----------------')

  14. # 从携程网上随便找了一家酒店的评论语,有正面的也有负面的,进行分词处理

  15. hotelcomments=["跟姐妹过来旅游,选择了这家酒店离机场近离市区也近,过来度假的出差的真的是最佳选择。前台服务态度特别好各项娱乐设施挺齐全的!",

  16. "酒店位置有点偏僻,房间干净整洁,早餐丰富周边没什么玩的地方,吃饭也不太方便,得开车出去。服务态度很好。",

  17. "这家酒店真不错,一进门房间的环境非常好,前台服务也很好,看来出差选择的这家酒店没有错,下次来海口出差还选这家酒店。",

  18. "地址偏了一些,但服务态度非常好,环境也很棒,睡觉很安静,外出有点不太方便,滴滴只能叫出租车。如果出差不方便,但度假是非常好的选择",

  19. "千万不要来好差劲的,还有血,床上毛巾都有脏东西,周围都是工地噪声很大,路上全是灰尘,服务很不人性化,地方偏远。"]

  20. for hotelcomment in hotelcomments:

  21. seg_list = jieba.cut(hotelcomment,use_paddle=True)

  22. print('-----------------{}----------------'.format(hotelcomment))

  23. print("paddle模式 : "+ '|'.join(list(seg_list))) #使用paddle模式

  24. seg_list = jieba.cut(hotelcomment, cut_all=True)

  25. print("全模式 : "+ "|".join(list(seg_list))) # 全模式

  26. seg_list = jieba.cut(hotelcomment, cut_all=False)

  27. print("精确模式 : "+ "|".join(list(seg_list))) # 精确模式,也是默认模式

  28. seg_list = jieba.cut_for_search(hotelcomment)

  29. print("搜索引擎模式: "+ "|".join(list(seg_list))) # # 搜索引擎模式

  30. seg_list = pkuseg.pkuseg().cut(hotelcomment) # 不开启词性标注功能

  31. print("pkuseg分词 : "+ "|".join(seg_list)) # # 搜索引擎模式

  32. # 对第一条语句的分词效果

  33. # paddle模式 :跟|姐妹|过来|旅游|,|选择|了|这家|酒店|离|机场|近离|市区|也|近|,|过来|度假|的|出差|的|真的|是|最佳|选择|。|前台|服务态度|特别|好|各项|娱乐|设施|挺|齐全|的|!

  34. # 全模式 :跟|姐妹|过来|旅游|,|选择|了|这家|酒店|离|机场|近|离市|市区|也|近|,|过来|度假|的|出差|的|真的|是|最佳|选择|。|前台|台服|服务|服务态度|态度|特别|好|各项|娱乐|设施|挺|齐全|的|!

  35. # 精确模式 :跟|姐妹|过来|旅游|,|选择|了|这家|酒店|离|机场|近离|市区|也|近|,|过来|度假|的|出差|的|真的|是|最佳|选择|。|前台|服务态度|特别|好|各项|娱乐|设施|挺|齐全|的|!

  36. # 搜索引擎模式:跟|姐妹|过来|旅游|,|选择|了|这家|酒店|离|机场|近离|市区|也|近|,|过来|度假|的|出差|的|真的|是|最佳|选择|。|前台|服务|态度|服务态度|特别|好|各项|娱乐|设施|挺|齐全|的|!

  37. # pkuseg分词 :跟|姐妹|过来|旅游|,|选择|了|这家|酒店|离|机场|近离|市区|也|近|,|过来|度假|的|出差|的|真的|是|最佳|选择|。|前台|服务|态度|特别|好|各项|娱乐|设施|挺|齐全|的|!

  38. # 结论

  39. # 在分词的冗余度上,全模式 >= 搜索引擎模式 > pkuseg > paddle模式 = 精确模式

  40. # 在分词的处理时间上,pkuseg要远远劣于jieba


  41. print('-----------------jieba和pkuseg常规分词----------------')

  42. # 选取了百度百科里关于万达的介绍

  43. str='万达集团创立于1988年,经过30年发展,已成为以现代服务业为主的大型跨国企业集团。' \

  44. '万达是世界领先的不动产企业、世界领先的影视企业、世界领先的体育企业、世界领先的儿童产业企业。' \

  45. '万达广场、万达影城、万达酒店、万达文化旅游城、万达宝贝王成为中国知名品牌。'

  46. seg_list = jieba.cut(str)

  47. print("jieba分词 : "+ "|".join(seg_list)) # jieba常规分词

  48. # jieba分词 : 万达|集团|创立|于|1988|年|,|经过|30|年|发展|,|已|成为|以|现代|服务业|为主|的|大型|跨国企业|集团|。

  49. # |万达|是|世界领先|的|不动产|企业|、|世界领先|的|影视|企业|、|世界领先|的|体育|企业|、|世界领先|的|儿童|产业|企业|。

  50. # |万达|广场|、|万达|影城|、|万达|酒店|、|万达|文化|旅游城|、|万达|宝贝|王|成为|中国|知名品牌|。

  51. seg_list = pkuseg.pkuseg().cut(str)

  52. print("pkuseg分词: "+ "|".join(seg_list)) # pkuseg常规分词

  53. # pkuseg分词: 万达|集团|创立|于|1988年|,|经过|30年|发展|,|已|成为|以|现代|服务业|为主|的|大型|跨国|企业|集团|。

  54. # |万达|是|世界|领先|的|不动产|企业|、|世界|领先|的|影视|企业|、|世界|领先|的|体育|企业|、|世界|领先|的|儿童|产业|企业|。

  55. # |万达|广场|、|万达|影城|、|万达|酒店|、|万达|文化|旅游城|、|万达宝|贝王|成为|中国|知名|品牌|。

  56. print('-----------------自定义字典后,jieba和pkuseg常规分词----------------')

  57. # userdict.txt

  58. # 万达集团

  59. # 万达广场

  60. # 万达影城

  61. # 万达酒店

  62. # 万达文化旅游城

  63. # 万达宝贝王

  64. # 现代服务业

  65. # 体育企业

  66. # 影视企业

  67. jieba.load_userdict("userdict.txt")

  68. seg_list = jieba.cut(str)

  69. print("jieba分词 : "+ "|".join(seg_list)) # 自定义字典后,jieba常规分词

  70. # jieba分词 : 万达集团|创立|于|1988|年|,|经过|30|年|发展|,|已|成为|以|现代服务业|为主|的|大型|跨国企业|集团|。

  71. # |万达|是|世界领先|的|不动产|企业|、|世界领先|的|影视企业|、|世界领先|的|体育企业|、|世界领先|的|儿童|产业|企业|。

  72. # |万达广场|、|万达影城|、|万达酒店|、|万达文化旅游城|、|万达宝贝王|成为|中国|知名品牌|。

  73. seg = pkuseg.pkuseg(user_dict='userdict.txt')

  74. seg_list = seg.cut(str)

  75. print("pkuseg分词: "+ "|".join(seg_list)) # 自定义字典后,jieba常规分词

  76. # pkuseg分词: 万达集团|创立|于|1988年|,|经过|30年|发展|,|已|成为|以|现代服务业|为主|的|大型|跨国|企业|集团|。

  77. # |万达|是|世界|领先|的|不动产|企业|、|世界|领先|的|影视企业|、|世界|领先|的|体育企业|、|世界|领先|的|儿童|产业|企业|。

  78. # |万达广场|、|万达影城|、|万达酒店|、|万达文化旅游城|、|万达宝贝王|成为|中国|知名|品牌|。

  79. # 结论,在使用自定义词典上分词后,两者效果是一致的,不过按照pkuseg的说法,提供了医药、新闻、微博、旅游的训练模型


  80. print('-----------------关于词性分析,jieba和pkuseg常规分词----------------')

  81. # 选取了金庸射雕英雄传第一章的第一段话

  82. novel='''钱塘江浩浩江水,日日夜夜无穷无休的从临安牛家村边绕过,东流入海。

  83. 江畔一排数十株乌柏树,叶子似火烧般红,正是八月天时。

  84. 村前村后的野草刚起始变黄,一抹斜阳映照之下,更增了几分萧索。

  85. 两株大松树下围着一堆村民,男男女女和十几个小孩,正自聚精会神的听着一个瘦削的老者说话。

  86. 那说话人五十来岁年纪,一件青布长袍早洗得褪成了蓝灰色。

  87. 只听他两片梨花木板碰了几下,左手中竹棒在一面小羯鼓上敲起得得连声。'''

  88. seg_list = jieba.cut(novel)

  89. print("pkuseg分词 : "+ "|".join(seg_list))

  90. # pkuseg分词 : 钱塘江|浩浩|江水|,|日日夜夜|无穷|无休|的|从|临安|牛家村|边|绕过|,|东|流入|海|。|

  91. seg_list = pseg.cut(novel)

  92. for j in seg_list:

  93. print('({},{})'.format(j.word,j.flag)) #j返回一组标注,word为词汇,flag为词性

  94. # (钱塘江,nr)

  95. # (浩浩,nz)

  96. # (江水,ns)

  97. # (,,x)

  98. # (日日夜夜,i)

  99. # (无穷,d)

  100. # (无休,v)

  101. # (的,uj)

  102. # (从,p)

  103. # (临安,ns)

  104. # (牛家村,nr)

  105. # (边,d)

  106. # (绕过,v)

  107. # (,,x)

  108. # (东,ns)

  109. # (流入,v)

  110. # (海,n)

  111. # (。,x)

  112. seg_list = pkuseg.pkuseg().cut(novel)

  113. print("pkuseg分词 : "+ "|".join(seg_list))

  114. # pkuseg分词 : 钱塘江|浩浩江水|,|日日夜夜|无穷无休|的|从|临安|牛家|村边|绕|过|,|东流入海|。

  115. seg_list = pkuseg.pkuseg(postag = True).cut(novel)

  116. for j in seg_list:

  117. print('({},{})'.format(j[],j[1])) #j返回一组标准,j[]为词汇,j[1]为词性

  118. # (钱塘江,nr)

  119. # (浩浩江水,i)

  120. # (,,w)

  121. # (日日夜夜,i)

  122. # (无穷无休,i)

  123. # (的,u)

  124. # (从,p)

  125. # (临安,ns)

  126. # (牛家,n)

  127. # (村边,s)

  128. # (绕,v)

  129. # (过,u)

  130. # (,,w)

  131. # (东流入海,i)

  132. # (。,w)

  133. # 结论

  134. # 从单纯的分词角度,两者略有不同,各有利弊,无法确认谁更准确

  135. # 从词性的角度,两者处理的也不一致

  136. # jieba包含词性标签24个(小写字母),专名类别标签4个(大写字母)。

  137. # pkuseg包含36类词性标签

  138. # jieba和pkuseg的词性标签有23类是相同的,基本上均采用北大词性标注集标准。

  139. # jieba的词性多了xc 其他虚词,PER人名,LOC地名,ORG机构名,TIME时间,来自github官方

  140. # pkuseg的词性则多了b区别词、e叹词、x非语素字、y语气词、z状态词、vx形式动词、o拟声词、g语素、h前接成分、i成语、j简称、k后接成分、l习惯用语等13类,来自github官方

  141. # 总体过一遍数据,感觉pkuseg的词性标注还是更靠谱一些。



2
0