1、中文分词算法介绍
中文分词算法是指将一个汉字序列切分成一个一个单独的词,与英文以空格作为天然的分隔符不同,中文字符在语义识别时,需要把数个字符组合成词,才能表达出真正的含义。分词算法是文本挖掘的基础,通常应用于自然语言处理、搜索引擎、智能推荐等领域。
分类:
- 第一类是基于字符串匹配,即扫描字符串,如果发现字符串的子串和词典中的词相同,就算匹配,比如机械分词方法。这类分词通常会加入一些启发式规则,比如“正向/反向最大匹配”,“长词优先”等
- 第二类是基于统计以及机器学习的分词方法,它们基于人工标注的词性和统计特征,对中文进行建模,即根据观测到的数据(标注好的语料)对模型参数进行训练,在分词阶段再通过模型计算各种分词出现的概率,将概率最大的分词结果作为最终结果。常见的序列标注模型有HMM和CRF。这类分词算法能很好处理歧义和未登录词问题,效果比前一类效果好,但是需要大量的人工标注数据,以及较慢的分词速度。
- 第三类是通过让计算机模拟人对句子的理解,达到识别词的效果,由于汉语语义的复杂性,难以将各种语言信息组织成机器能够识别的形式,目前这种分词系统还处于试验阶段,这里不讲解和实现
2、机械分词
机械分词又称基于字符串匹配的分词方法、按照一定的策略将待分析的字符串与一个“充分大的”机器词典中的词条进行匹配,若在词典中找到某个字符串,则匹配成功(识别出一个词)。这是最简单的分词方法,但非常高效和常见。
1、最大匹配方法----分为正向匹配和逆向匹配
正向匹配算法:
待分词文本: text = '计算机语言学课程有意思'
词表: dict[]={"计算", "计算语言学", "课程", "有", "意思"}
- 从ste[0:***]开始,当扫描到ste[0:2]的时候,发现"计算"已经在词表dict[]中了。但还不能切分出来,因为我们不知道后面的词语能不能组成更长的词(最大匹配)。
- 继续扫描ste[0:3],发现"计算语"并不是dict[]中的词。但是我们还不能确定是否前面找到的"计算语"已经是最大的词了。因为"计算语"是dict[2]的前缀。
- 扫描ste[0:4],发现"计算语言"并不是dict[]中的词。但是是dict[2]的前缀。继续扫描:
- 扫描ste[0:5],发现"计算语言学"是dict[]中的词。继续扫描下去:
- 当扫描ste[0:6]的时候,发现"计算语言学课"并不是词表中的词,也不是词的前缀。则切分出"计算语言学"为最大词汇
# 文本
text = '我们的计算语言学课程有意思'
textlen = len([i for i in text])
print([i for i in text], '\n', len([i for i in text]), '\n'*2)
""" """
# sentence=["计", "算", "语", "言", "学", "课", "程", "有", "意", "思"]
# 词库
dict_1 = ["计算", "计算语言学", "课程", "有", "意思", "我们", "的", "我们的"]
len_dict = [len(i) for i in dict_1]
print(len_dict, '\n', max(len_dict), '\n'*2)
# 当前匹配起始位置 和 结束位置
start_loc = 0
end_loc = 0
# 匹配结果存储
word_split = []
i = 0
for _ in range(textlen):
if i < textlen:
# 每一次匹配结果存储
word = []
for j in range(max(len_dict)+1):
word_try = text[i:(i+j)]
print(text[i:(i+j)])
if word_try in dict_1:
word.append(word_try)
# 取出最大匹配结果
word_split.append(word[-1:])
print(len(word))
print(len(word[len(word)-1]), word, '\n')
i = i + len(word[len(word)-1])
else:
break
print(word_split)
"""
[['我们的'], ['计算语言学'], ['课程'], ['有'], ['意思']]
"""
负向匹配算法:
首先我们定义一个最大分割长度5,从右往左开始分割:
- (1) 首先取出来的候选词W是 “课程有意思”。
- (2) 查词表,W不在词表中,将W最左边的第一个字去掉,得到W“程有意思”;
- (3) 查词表,W也不在词表中,将W最左边的第一个字去掉,得到W“有意思”;
- (4) 查词表,W也不在词表中,将W最左边的第一个字再去掉,得到W“意思”;
- (5) 查词表,W在词表中,就将W从整个句子中拆分出来,此时原句子为“计算语言学课程有”
- (6) 根据分割长度5,截取句子内容,得到候选句W是“言学课程有”;
- (7) 查词表,W不在词表中,将W最左边的第一个字去掉,得到W“学课程有”;
- (8) 查词表,W也不在词表中,将W最左边的第一个字去掉,得到W“课程有”;
- (9) 依次类推,直到W为“有”一个词的时候,这时候将W从整个句子中拆分出来,此时句子为“计算语言学课程”
- (10) 根据分割长度5,截取句子内容,得到候选句W是“算语言学课程”;
- (11) 查词表,W不在词表中,将W最左边的第一个字去掉,得到W“语言学课程”;
- (12) 依次类推,直到W为“课程”的时候,这时候将W从整个句子中拆分出来,此时句子为“计算语言学”
- (13) 根据分割长度5,截取句子内容,得到候选句W是“计算语言学”;
- (14) 查词表,W在词表,分割结束。
""" 2、负向匹配
# 文本
text = '我们的计算语言学课程有意思'
textlen = len([i for i in text])
print([i for i in text], '\n', len([i for i in text]), '\n'*2)
# sentence=["计", "算", "语", "言", "学", "课", "程", "有", "意", "思"]
# 词库
dict_1 = ["计算", "计算语言学", "课程", "有", "意思", "我们", "的", "我们的"]
len_dict = [len(i) for i in dict_1]
print(len_dict, '\n', max(len_dict), '\n'*2)
# 匹配结果存储
word_split = []
# 定义负向匹配最大长度,一般为词库中最大长度的词的长度
nega_len = max(len_dict)
i = 0
for _ in range(textlen):
for j in range(nega_len):
# print(text[(textlen-i-nega_len+j):(textlen-i)])
if text[(textlen-i-nega_len+j):(textlen-i)] in dict_1:
word_split.append(text[(textlen-i-nega_len+j):(textlen-i)])
i = i + len([ia for ia in text[(textlen-i-nega_len+j):(textlen-i)] ])
print(word_split)
"""
"""
['我', '们', '的', '计', '算', '语', '言', '学', '课', '程', '有', '意', '思']
13
[2, 5, 2, 1, 2, 2, 1, 3]
5
['意思', '有', '课程', '计算语言学', '我们的']
"""