NLP规则分词实验报告
完成日期:2018.11.03
GitHub:https://github.com/fyinh/NLPLearning_MM_RMM_segment
Java版:https://blog.csdn.net/fyinh_/article/details/88890515
一、摘要
作为自然语言处理的分支,中文信息处理是指用计算机对中文进行处理,和大部分西方语言不同,书面汉语的词语之间没有明显的空格标记,句子是以字串的形式出现。因此对中文进行处理的第一步就是进行中文自动分词。中文分词是中文自然语言处理的一项基础性工作,也是中文信息处理的一个重要问题,只有不断提高中文分词算法的效率才能跟上信息爆炸增长的现状。因此本文将进行使用双向匹配算法对给定测试文本进行分词的实验,并就此算法构建一个小软件以供用户进行方便快捷的中文分词。
二、理论描述
中文分词是指把没有明显分界标志的字串自动切分为词串。包括标点符号、数字、数学符号、各种标记、人名、地名、机构名等未登录词的识别。因此中文分词主要包括下面两个步骤:首先根据分词规范,建立机器词典,然后根据分词算法和机器词典,把字串切分成词串。目前根据所使用的知识资源不同分为基于规则的方法,基于统计的方法,以及两者结合的方法。
基于规则的方法一般都需要事先有人工建立好的分词词典和分词规则库。主要是基于字符串匹配的原理进行分词,往往以足够大的词表为依据,采用一定的处理策略将中文文本的字符串与词表中的词逐一匹配,如若成功,就认为该字串为词。主要有正向最大匹配法、逆向最大匹配法、双向匹配法、逐词遍历匹配法、设立切分标志法、正向最佳匹配法和逆向最佳匹配法等。而本次实验使用的方法就是双向匹配法。
三、算法描述
-
正向匹配算法(MM):设MaxLen表示最大词长,D为分词词典
(1)从待切分语料中按正向取长度为MaxLen的字串str,令Len=MaxLen;
(2)把str与D中的词相匹配;
(3)若匹配成功,则认为该字串为词,指向待切分语料的指针向前移Len个汉字(字节),返回(1);
(4)若不成功:如果Len>1, 则将Len减2,从待切分语料中取长度为Len的字串str,返回(2)。否则,得到长度为2的单字词,指向待切分语料的指针向前移1个汉字,返回(1)。 -
逆向最大匹配法(RMM):设MaxLen表示最大词长,D为分词词典
(1)从待切分语料中按逆向取长度为MaxLen的字串str,令Len=MaxLen;
(2)把str与D中的词相匹配;
(3)若匹配成功,则认为该字串为词,指向待切分语料的指针向前移Len个汉字(字节),返回(1);
(4)若不成功:如果Len>1, 则将Len减2,从待切分语料中取长度为Len的字串str,返回(2)。否则,得到长度为2的单字词,指向待切分语料的指针向前移1个汉字,返回(1)。 -
双向匹配法:
对同一个字符串分别采用MM和RMM两种方法进行切分处理,如果能够得到相同的切分结果,则认为切分成功,否则认为有疑点。针对疑点,采用上下文信息,根据歧义规则库进行排歧或者进行人工干预,选取一种切分为正确的切分。
本实验采用的是后面的方法,对于疑点进行人工干预。
四、详例描述
以“对外经济技术合作与交流不断扩大。”为例,详细描述算法如下:
- MM:MaxLen = 4
(1)Len = 4 “对外经济”不在字典中,Len = 3 “对外经”不在字典中,Len = 2 “对外”在字典中,则此时的结果为“对外/ 经济技术合作与交流不断扩大”;
(2)Len = 4 “经济技术”不在字典中,Len = 3 “经济技”不在字典中,Len = 2 “经济”在字典中,则此时的结果为“对外/ 经济/ 技术合作与交流不断扩大”;
(3)Len = 4 “技术合作”不在字典中,Len = 3 “技术合”不在字典中,Len = 2 “技术”在字典中,则此时的结果为“对外/ 经济/ 技术/ 合作与交流不断扩大”;
(4)Len = 4 “合作与交”不在字典中,Len = 3 “合作与”不在字典中,Len = 2 “合作”在字典中,则此时的结果为“对外/ 经济/ 技术/ 合作/ 与交流不断扩大”;
(5)Len = 4 “与交流不”不在字典中,Len = 3 “与交流”不在字典中,Len = 2 “与交”不在字典中,Len = 1 “与”在字典中,则此时的结果为“对外/ 经济/ 技术/ 合作/ 与/ 交流不断扩大”;
(6)Len = 4 “交流不断”不在字典中,Len = 3 “交流不”不在字典中,Len = 2 “交流”在字典中,则此时的结果为“对外/ 经济/ 技术/ 合作/ 与/ 交流/ 不断扩大”;
(7)Len = 4 “不断扩大”不在字典中,Len = 3 “不断扩”不在字典中,Len = 2 “不断”在字典中,则此时的结果为“对外/ 经济/ 技术/ 合作/ 与/ 交流/ 不断/ 扩大”;
(8)Len = 2(因为只剩下两个字,长度为2 < MaxLen)“扩大”在字典中,则此时的结果为“对外/ 经济/ 技术/ 合作/ 与/ 交流/ 不断/ 扩大/”。 - RMM:
与MM的过程类似,不同的是RMM是从后往前对待切分语料扫描的,因此切分出来的词前后顺序为是“扩大、不断、交流、与、合作、技术、经济、对外”。 - 双向匹配法:
比对RMM与MM切分的结果,可以看到两种切分方法的结果是一样的,因此最终的分词结果为“对外/ 经济/ 技术/ 合作/ 与/ 交流/ 不断/ 扩大/”。
五、算法代码
import traceback
class segment:
def __init__(self,):
self.dict = []
self.result1 = ""
self.result2 = ""
self.MM_result = []
self.RMM_result = []
# 读字典
def readDict(self):
try:
file = open('chineseDic.txt','r',encoding='utf-8')
lines = file.readlines()
file.close()
for line in lines:
if line == '\n':
continue
# 以","切分,只获取词语部分并添加到dict列表中
words = line.strip('\n').split(",")
self.dict.append(words[0])
except Exception as e:
traceback.print_exc()
# 正向最大匹配算法 source为句子, Le为 MaxLen, npos为开始时指针的位置(npos=0)
def MM(self, source, Le, npos):
leng = len(source)
if npos+1 > leng: # 当指针位置超出句子长度时,退出
return
while (npos+Le) > leng: # 当正向截取的下一段词超出句子总长度,则控制 Le - 1
Le = Le - 1
substr = source[npos:npos+Le] # 截取句子中 从npos到 npos+Le 的词句为 substr,判断若
if substr in self.dict: # substr 词典dict中,则添加进 MM_result列表中作为一个分词,
npos = npos + Le # 指针位置正向移动 Le 个单位
self.result1 = self.result1 + substr + "/"
self.MM_result.append(substr)
Le = 4 # Le 重新赋值为 4, 并进行下一次的匹配
self.MM(source,Le,npos)
else: # 若 substr 不在词典dict中, Le=Le-1, 并进行下一次的匹配
if Le >= 1:
Le = Le - 1
self.MM(source,Le,npos)
# 逆向最大匹配算法 source为句子, Le为 MaxLen, npos为开始时指针的位置(从句末开始, npos=len(source))
def RMM(self, source, Le, npos):
if npos-1 < 0: # 如果指针位置来到了句首,则退出
return
while (npos-Le) < 0: # 当逆向截取的下一段词超出了句首,则控制 Le - 1
Le = Le - 1
substr = source[npos-Le:npos] # 截取句子中 从npos-Le到 npos 的词句为 substr,判断若
if substr in self.dict: # substr 词典dict中,则添加进 RMM_result列表中作为一个分词,
npos = npos - Le # 指针位置逆向移动 Le 个单位
self.RMM_result.append(substr)
Le = 4 # Le 重新赋值为 4, 并进行下一次的匹配
self.RMM(source,Le,npos)
else: # 若 substr 不在词典dict中, Le=Le-1, 并进行下一次的匹配
if Le >= 1:
Le = Le - 1
self.RMM(source,Le,npos)
# 由于运行RMM函数后,得到的结果列表 RMM_result里的分词是反过来的,需要 get_result2()函数矫正
def get_result2(self):
self.RMM_result.reverse()
for word in self.RMM_result:
self.result2 += word + "/"
return self.result2
def get_result1(self):
return self.result1
# resultword()函数用来处理选择正向逆向最大匹配后的结果
def resultword(self, list1, list2, result1, result2):
if len(list1) != len(list2): # 原则1: 如果正反向分词结果词数不同,则取分词数量较少的那个;
if len(list1) > len(list2):
return result2
else:
return result1
else: # 原则2: 如果分词结果词数相同:
FSingle = 0
BSingle = 0
if result1 == result2: # 分词结果相同,就说明没有歧义,可返回任意一个;
return result1
else: # 分词结果不同,返回其中单字较少的那个。
for i in range(len(list1)):
if len(list1[i]) == 1:
FSingle += 1
if len(list2[i]) == 1:
BSingle += 1
if BSingle > FSingle:
return result1
else:
return result2
# 写在类的外面用于测试
def get_Result(sentence,MaxLen):
s = segment()
s.readDict();
# str1 = "对外经济技术合作与交流不断扩大。"
# str1 = "幼儿园地节目"
pos = 0
# MaxLen = 4
# s.MM(sentence,MaxLen,pos)
s.RMM(sentence,MaxLen,len(sentence))
s.MM(sentence,MaxLen,pos)
s.get_result2()
result3 = s.resultword(s.MM_result,s.RMM_result,s.result1,s.result2)
return s.result1,s.result2,result3
五、演示
1. 控制台结果展示
(1)以“对外经济技术合作与交流不断扩大。”为例,演示页面如下:
可以看到,当RMM与MM的分词结果相同时,就直接输出分词结果。
(2)以“幼儿园地节目。”为例,演示页面如下:
可以看到,当RMM与MM的分词结果不相同时,将输出两种方法的分词结果,然后请人工选择。
2. UI界面展示
(1) 以“对外经济技术合作与交流不断扩大。”为例,演示页面如下:
因在搭建UI界面时出现一个还未解决的bug,导致在界面上只能返回并展示正向最大匹配分词的结果,其他两个结果无法展示。
(2)以“幼儿园地节目。”为例,演示页面如下:
因在搭建UI界面时出现一个还未解决的bug,导致在界面上只能返回并展示正向最大匹配分词的结果,其他两个结果无法展示。
六、遇到的问题
如上面展示的UI界面效果所示,在本次作业的主代码 MM_RMM.py中 调用 get_Result函数来获取相应匹配算法的结果列表 result。 例如: result = [‘幼儿园/地/节目/’, ‘幼儿/园地/节目/’, ‘幼儿/园地/节目/’],其三个元素分别对应正向、逆向、双向算法所得结果。然而在 界面代码GUI2.py 上调用 相同的 get_Result函数 返回得到的 result 列表里 却只有正向匹配得到的分词结果, 即result = [‘幼儿园/地/节目/’, ‘’, ‘’],其他两个结果为空(调换了元素位置还是只能得到正向的结果),这导致了UI界面的文本输出框只有正向匹配分词结果又内容,其余两个输出框为空。如图所示:
该问题目前尚未解决,接下来,会继续尝试解决这个问题,努力完善好该次作业的UI界面,并学习掌握更多UI界面搭建的知识,为后续作业的展示做好准备。
七、总结
因为上学期上的自然语言处理课程中已经尝试过用Java来打双向匹配算法,因此在做这个作业的时候没有太大的困难。
然而在做双向匹配的时候,因为没有歧义规则库,所以就直接选择了人工干预。因为上课的时候认真学习了老师正向最大匹配算法的代码,所以下课之后回去写逆向最大匹配的时候并没有遇到太大的困难,既然正向最大匹配和逆向最大匹配都做出来了,双向最大匹配自然就迎刃而解啦!
接下来,对于这块的学习,会继续学习一下怎么样去使用歧义规则库,然后再完善一下这个分词算法。
不得不提,我真的不会做页面…感觉自己做的是真的丑,而且这个问题真的不知道怎么解决= = 如果有大佬知道的话,球球舅舅菜鸡!