Python与自然语言处理——中文分词
中文分词技术(一)
中文分词问题主要来源于:在汉语中,句子是以字为单位的,但是语义理解仍然是需要以词为单位,所以也就存在了中文分词问题。
主要的技术可以分为:规则分词、统计分词以及混合分词(规则+统计)
规则分词
基于规则的分词是一种机械分词,主要依赖于维护词典,在切分时将与剧中的字符串与词典中的词进行匹配。
主要的切分方法包括三种:正向最大匹配法、逆向最大匹配法以及双向最大匹配法。
正向最大匹配法(MM法)
- 基本思想
假设分词词典中最长词由 i i i个汉字组成,则用被处理文档当前前 i i i个字作为匹配字段,若字典中存在这样的词则匹配成功,否则去掉最后一个字再进行查找。 - 代码示例
#定义正向最大匹配法类
class MM(object):
def __init__(self):
self.window_size=3 #词典中最长字符串包含的字数
def cut(self,text):
result=[]
index=0
text_length=len(text)
dic=['研究','研究生','生命','命','的','起源'] #词典
while text_length>index: #只要还有字就进行匹配
for size in range(self.window_size+index,index,-1): #生成可能长度
piece=text[index:size]
if piece in dic:
index=size-1 #匹配成功将index设置为匹配成功的最后一个字的位置
break
index=index+1 #开始下一个字符串的匹配
result.append(piece+'----')
print(result)
if __name__=='__main__':
text='研究生命的起源'
tokenizer=MM()
tokenizer.cut(text)
结果如下所示:
[‘研究生----’, ‘命----’, ‘的----’, ‘起源----’]
逆向最大匹配法(RMM法)
- 基本思路
基本思路与正向相同,只是切分方向正好与MM法相反。从文档末尾开始处理,每次去除第一个字进行匹配,维护的也是逆序词典,每个词都按逆序方式存放。
RMM比MM的误差小。 - 代码示例
#定义逆向最大匹配法类
class RMM(object):
def __init__(self):
self.window_size=3
def cut(self,text):
result=[]
index=len(text) #从文本末尾开始
dic=['研究','研究生','生命','命','的','起源']
while index>0:
for size in range(index-self.window_size,index):
piece=text[size:index] #找到最后几个字组成的字符串
if piece in dic:
index=size+1 #将位置更新为匹配到的最后一个字的位置
break
index=index-1 #开始新的位置
result.append(piece+'----')
result.reverse() #由于从最后进行匹配,所以顺序是反的,需要颠倒过来
print(result)
if __name__=='__main__':
text='研究生命的起源'
tokenizer=RMM()
tokenizer.cut(text)
结果如下所示:
[‘研究----’, ‘生命----’, ‘的----’, ‘起源----’]
双向最大匹配法
- 基本思想
双向最大匹配法将正向和逆向的结果进行比较,按照最大匹配原则,选择词数切分最少的作为结果。 - 双向最大匹配规则
- 如果结果词数不同,返回分词数量较少的一个;
- 如果结果词数相同
- 分词结果相同,任意返回一个
- 分词结果不同,返回单个字较少的一个
- 代码示例
#定义双向最大匹配法的类
class BMM(object):
def __init__(self):
self.window_size=3
self.result_MM=[]
self.result_RMM=[]
self.num_MM=0
self.num_RMM=0
#正向最大
def MM(self,text):
index=0
text_length=len(text)
dic=['研究','研究生','生命','命','的','起源'] #词典
while text_length>index: #只要还有字就进行匹配
for size in range(self.window_size+index,index,-1): #生成可能长度
piece=text[index:size]
if piece in dic:
index=size-1 #匹配成功将index设置为匹配成功的最后一个字的位置
break
index=index+1 #开始下一个字符串的匹配
self.result_MM.append(piece+'----')
if len(piece)==1:
self.num_MM+=1
#逆向最大
def RMM(self,text):
index=len(text) #从文本末尾开始
dic=['研究','研究生','生命','命','的','起源']
while index>0:
for size in range(index-self.window_size,index):
piece=text[size:index] #找到最后几个字组成的字符串
if piece in dic:
index=size+1 #将位置更新为匹配到的最后一个字的位置
break
index=index-1 #开始新的位置
self.result_RMM.append(piece+'----')
if len(piece)==1:
self.num_RMM+=1
self.result_RMM.reverse() #由于从最后进行匹配,所以顺序是反的,需要颠倒过来
def cut(self,text):
if len(self.result_MM)>len(self.result_RMM):
result=self.result_RMM
elif len(self.result_MM)<len(self.result_RMM):
result=self.result_MM
elif len(self.result_MM)==len(self.result_RMM):
if self.result_MM==self.result_RMM:
result=self.result_RMM
else:
if self.num_MM>self.num_RMM:
result=self.result_RMM
else:
result=self.result_MM
print(result)
if __name__=='__main__':
text='研究生命的起源'
tokenizer=BMM()
tokenizer.MM(text)
tokenizer.RMM(text)
tokenizer.cut(text)
结果如下所示:
[‘研究----’, ‘生命----’, ‘的----’, ‘起源----’]
统计分词
主要思想:将每个词视作由字组成,如果相连的字在不同文本中出现次数越多,就越可能是一个词。
基于统计的分词一般有以下两步:
- 建立统计语言模型
- 对句子进行单词划分,对划分结果进行概率计算(隐马尔可夫【HMM】、条件随机场【CRF】等)
语言模型
- 语言模型
为长度为 m m m的字符串确定其概率分布 P ( x 1 , x 2 , ⋯   , x m ) P\left( { {x_1},{x_2}, \cdots ,{x_m}} \right) P(x1,x2,⋯,xm),其中 x 1 , x 2 , ⋯   , x m { {x_1},{x_2}, \cdots ,{x_m}} x1,x2,⋯,xm为文本中的各个词语,计算公式如下:
P ( x 1 , x 2 , ⋯   , x m ) = P ( x 1 ) P ( x 2 ∣ x 1 ) P ( x 3 ∣ x 1 , x 2 ) ⋯ P ( x m ∣ x 1 , x 2 , ⋯   , x m − 1 ) P\left( { {x_1},{x_2}, \cdots ,{x_m}} \right) = P\left( { {x_1}} \right)P\left( { {x_2}\left| { {x_1}} \right.} \right)P\left( { {x_3}\left| { {x_1},{x_2}} \right.} \right) \cdots P\left( { {x_m}\left| { {x_1},{x_2}, \cdots ,{x_{m - 1}}} \right.} \right) P(x1,x2,⋯,xm)=P(x1)P(x2∣x1)P(x3∣x1,x2)⋯</