正向最大匹配分词和逆向最大匹配分词(附免费源码)

目录

1.介绍

2.方法概述

2.1 正向最大匹配法

2.2 逆向最大匹配法

3.实现代码

3.1 正向最大匹配法

3.2 逆向最大匹配法

4.结果分析

1.介绍

       近年来,随着社会化媒体(如博客、微博、社交网站等)的兴起和普及,中文文本的分词成为了中文自然语言处理中的重要任务。分词的目标是将连续的汉字序列切分成有意义的词语,为后续的语义分析和信息处理提供基础。分词对中文文本的理解和处理起着关键作用。分词算法的发展可以追溯到上世纪80年代初,最早的分词方法是基于词典的方法。这些方法通过比对文本与词典中的词语,来确定切分位置。

       然而随着互联网和社会化媒体的快速发展,传统的基于词典的方法面临着扩展性和准确性的挑战。为了应对上述挑战,正向最大匹配法和逆向最大匹配法被提出并广泛应用于中文分词。正向最大匹配法从左到右进行匹配和切分,选择最长的词进行切分。而逆向最大匹配法则是从右到左进行匹配和切分。这两种方法都通过建立词典和文本的匹配关系来切分文本,能够有效地处理未登录词和歧义问题,在实践中表现出了一定的优势和适用性。需要注意的是,正向和逆向最大匹配法在处理特定文本和语境下可能存在局限性,例如在处理长词和上下文相关的短语时,可能存在分词错误或切分不准确的情况。

       为了进一步提高分词的准确性和适应性,研究人员不断探索和提出新的分词算法和技术。总而言之,正向和逆向最大匹配法作为中文分词的经典方法,已经成为中文自然语言处理中不可或缺的工具。它们在社会化媒体等大规模文本数据处理中发挥着重要的作用,并不断被改进和优化,以满足不断变化的语境和需求。

2.方法概述

2.1 正向最大匹配法

       正向最大匹配分词(Forward maximum matching segmentation)通常简称为FMM法。其基本思想为:假定分词词典中的最长词有i个汉字字符,则用被处理文档的当前字串中的前i个字作为匹配字段,查找字典。若字典中存在这样的一个i字词,则匹配成功,匹配字段被作为一个词切分出来。如果词典中找不到这样的一个i字词,则匹配失败,将匹配字段中的最后一个字去掉,对剩下的字串重新进行匹配处理…… 如此进行下去,直到匹配成功,即切分出一个词或剩余字串的长度为零为止。这样就完成了一轮匹配,然后取下一个i字字串进行匹配处理,直到文档被扫描完为止。

2.2 逆向最大匹配法

       逆向最大匹配法 (Reverse maximum matching method)通常简称为RMM法。RMM法的基本原理与FMM法相同 ,不同的是分词切分的方向与FMM法相反,而且使用的分词辞典也不同。逆向最大匹配法从被处理文档的末端开始匹配扫描,每次取最末端的2i个字符(i字字串)作为匹配字段,若匹配失败,则去掉匹配字段最前面的一个字,继续匹配。相应地,它使用的分词词典是逆序词典,其中的每个词条都将按逆序方式存放。在实际处理时,先将文档进行倒排处理,生成逆序文档。然后,根据逆序词典,对逆序文档用正向最大匹配法处理即可。

       由于汉语中偏正结构较多,若从后向前匹配,可以适当提高精确度。所以,逆向最大匹配法比正向最大匹配法的误差要小。统计结果表明 ,单纯使用正向最大匹配的错误率为 1/16 9,单纯使用逆向最大匹配的错误率为 1/245。

3.实现代码

3.1 正向最大匹配法

注:具体文件在评论区可获得

       首先需要将任务给出的停用词文件、分词词表文件和需要分词的句子文件读入,需要注意的是,停用词文件和分词词表文件的编码为gbk,需要分词的句子文件编码为utf-8。

# 读取停用词文件
stopwords = []
with open('停用词.txt', 'r', encoding='gbk') as f:
    for line in f:
        stopwords.append(line.strip())

# 读取分词词表文件
fencidict = []
with open('中文分词词典.txt', 'r', encoding='gbk') as f:
    for line in f:
        fencidict.append(line.strip())

# 读取需要分词的句子文件
sentences = []
with open('测试样本.txt', 'r', encoding='utf-8') as f:
    for line in f:
        line = line.strip()
        if line != '':
            sentences.append(line)

       随后,为了确定一个恰当的最大匹配长度,需要首先提取分词词典中所有词的长度,提取结果发现,词语最大长度为15。在后续的测试中,发现如果选取词语最大长度为15,代码运行效率太低,选取5为词语最大长度是比较适合的。

# 查看分词词典中最长词语数
number = []
for ci in fencidict:
    if len(ci) not in number:
        number.append(len(ci))
print(max(number)) # 输出分词词典中最长词语数

       首先,提取单行句子进行分词,并创建存放分词结果的segment_list数组,随后进行遍历循环,提取句子中的前5个词语,判断逻辑为是否存在于分词词典中,如果存在词典中,则需要判断是否存在于停用词词典中,如果存在于停用词词典中,就需要作为停用词被剔除,跳转到下一个词语的循环;如果不存在于停用词词典中,就可以作为分词结果,将其从原句中剔除后,保存到分词结果segment_list数组中。如果前5个词语不在分词词典中,就需要检查前4个词语是否在分词词典中,不断重复过程。

for sentence in sentences: # 提取单行句子进行分词
    segment_list = '' # 存放分词结果
    
    while len(sentence) >= 1: # 当未分词完时,继续分词(句子长度大于1表明还有内容)
        max_match_len = 5 # 设置最大匹配长度,设置为5就够,数越大运行速度越慢

        while max_match_len > 1: # 当匹配单词长度大于1时,循环判断分词
            if sentence[0:max_match_len] in fencidict: # 判断前5个字符是否存在于分词字典
                if sentence[0:max_match_len] not in stopwords: # 判断该词是否在停用词词典中
                    segment_list = segment_list + '/' + sentence[0:max_match_len] # 追加到分词词组中
                    sentence = sentence[max_match_len:len(sentence)] # 将符合的词语从原例句中截取
                    break # 退出循环,重新从最大匹配长度开始匹配截取
                elif sentence[0:max_match_len] in stopwords:
                    sentence = sentence[max_match_len:len(sentence)] # 将符合的词语从原例句中截取
                    break # 退出循环,重新从最大匹配长度开始匹配截取
            else:
                max_match_len -= 1 # max_match_len累减,开始匹配4、3、2、1个字 
        if max_match_len == 1: # 只剩下一个字时
            if sentence[0:max_match_len] not in stopwords: # 如果该词不在停用词词典中
                segment_list = segment_list + '/' + sentence[0:1] # 追加单个字作为分词结果
                sentence = sentence[1:len(sentence)] # 将该词从原句中剔除
            else:
                sentence = sentence[1:len(sentence)] # 将该词从原句中剔除
    print(segment_list) # 输出分词结果

       如果最后缩减到只剩1个词语,需要检查是否在停用词词典中,如果在停用词词典中,就需要作为停用词被剔除,如果不在停用词词典中,就可以作为分词结果保存到segment_list中。

       最后输出分词结果:

3.2 逆向最大匹配法

除了方法代码外,读取文件等都与正向最大匹配法相同,方法代码如下:

for sentence in sentences: # 提取单行句子进行分词
    segment_list = '' # 存放分词结果
    
    while len(sentence) >= 1: # 当未分词完时,继续分词(句子长度大于1表明还有内容)
        max_match_len = 5 # 设置最大匹配长度,设置为5就够,数越大运行速度越慢
    
        while max_match_len > 1: # 当匹配单词长度大于1时,循环判断分词
            if sentence[len(sentence)-max_match_len:len(sentence)] in fencidict: # 判断前5个字符是否存在于分词字典
                if sentence[len(sentence)-max_match_len:len(sentence)] not in stopwords: # 判断该词是否在停用词词典中
                    segment_list = sentence[len(sentence)-max_match_len:len(sentence)] + '/' + segment_list # 追加到分词词组中
                    sentence = sentence[0:len(sentence)-max_match_len] # 将符合的词语从原句中剔除
                    break # 退出循环,重新从最大匹配长度开始匹配截取
                elif sentence[len(sentence)-max_match_len:len(sentence)] in stopwords: # 判断该词是否在停用词词典中
                    sentence = sentence[0:len(sentence)-max_match_len] # 将符合的词语从原句中剔除
                    break # 退出循环,重新从最大匹配长度开始匹配截取
            else:
                max_match_len -= 1  # max_match_len累减,开始匹配4、3、2、1个字 
        if max_match_len == 1: # 只剩下一个字时,说明当前不再存在任何符合的词语,直接截取一个字作为分词结果
            if sentence[len(sentence)-1:len(sentence)] not in stopwords: # 如果该词不在停用词词典中
                segment_list = sentence[len(sentence)-1:len(sentence)] + '/' + segment_list # 追加单个字作为分词结果
                sentence = sentence[0:len(sentence)-1] # 将该词从原句中剔除
            else:
                sentence = sentence[0:len(sentence)-1] # 将该词从原句中剔除
    print(segment_list) # 输出分词结果

       逆向最大匹配法与正向最大匹配法的代词没有太大差别,只需要改动一下从原句提取的5个词语是倒着取。例如:正向最大匹配法中的前5个词语是sentence[0:max_match_len],而逆向最大匹配法中的前5个词语是sentence[len(sentence)-max_match_len:len(sentence)]。

       首先,提取单行句子进行分词,并创建存放分词结果的segment_list数组,随后进行遍历循环,提取句子中的后5个词语,判断逻辑为是否存在于分词词典中,如果存在词典中,则需要判断是否存在于停用词词典中,如果存在于停用词词典中,就需要作为停用词被剔除,跳转到下一个词语的循环;如果不存在于停用词词典中,就可以作为分词结果,将其从原句中剔除后,保存到分词结果segment_list数组中。如果后5个词语不在分词词典中,就需要检查后4个词语是否在分词词典中,不断重复过程。

       如果最后缩减到只剩1个词语,需要检查是否在停用词词典中,如果在停用词词典中,就需要作为停用词被剔除,如果不在停用词词典中,就可以作为分词结果保存到segment_list中。

       最后输出分词结果。

4.结果分析

       汉语中偏正结构较多,从后向前匹配可以适当提高精确度,从分词结果的最后一句举例,正向最大匹配会把“的说法”中的“的说”提取出来,把“法”视为停用词;而逆向最大匹配会把“的说法”中的“说法”提取出来,把“的”视为停用词,这是更合理语言规范的。当然,正向最大匹配也有所优点,在对“进行了”分词时,会把“进行”提取出来,把“了”视为停用词;而逆向最大匹配会把“进”和“行了”提取出来,不太符合语言规范。

       但是总体上,逆向最大匹配的准确率会更高一些。

逆向最大匹配法是一种基于词典的分词算法,其核心思想是从右到左扫描输入文本,寻找最长的词。以下是使用Python编写的一个简化版逆向最大匹配分词函数,分为三个部分: ```python # 部分一:字典提取(创建一个简单的词典) def build_dict(words_list): word_dict = {} for word in words_list: word_dict[word] = len(word) return word_dict # 部分二:获取词典的最大长度 def get_max_length(dict): max_len = 0 for value in dict.values(): if value > max_len: max_len = value return max_len # 部分三:切分文本并进行匹配 def reverse_mm_segmentation(text, word_dict): text_len = len(text) segments = [] max_len = get_max_length(word_dict) # 右边界从文本末尾开始 right = text_len - 1 while right >= 0: match = False for length in range(1, max_len + 1): start = right - length + 1 if start < 0 or text[start:start+length] not in word_dict: break if start == right - length + 1: # 长度为1,直接添加单个字符 segment = text[start] else: # 否则添加完整的词 segment = text[start:start+length] match = True segments.append(segment) right = start - 1 # 更新右边界 break if not match: segments.append(text[right]) # 如果无法找到匹配,将剩余字符单独作为一个词处理 right -= 1 return segments[::-1] # 因为逆向搜索,所以结果需要反转 # 示例 word_list = ["北", "京", "市", "民", "办", "高", "中"] word_dict = build_dict(word_list) text = "北京市民办高中" segments = reverse_mm_segmentation(text, word_dict) print("分词结果:", segments) # 输出:["高中", "民办", "北京", "市"]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值