自然语言处理正向逆向双向最长匹配算法的 切分效果与速度测评

Posted 征途黯然.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自然语言处理正向逆向双向最长匹配算法的 切分效果与速度测评相关的知识,希望对你有一定的参考价值。

本文摘要
· 理论来源:【统计自然语言处理】第七章 自动分词;【自然语言处理入门】第二章 词典分词;
· 代码目的:手写三种算法:正向最长匹配、逆向最长匹配、双向最长匹配,比较它们的单词切分效果与速度
· 电脑配置:联想拯救者Y7000,Intel(R) Core(TM) i5-8300H CPU @ 2.30GHz 2.30 GHz
作者:CSDN 征途黯然.

一、关于测试用的语料库

  采用了2个预料库,一个是第二届国际分词的PKU测试集语料库(55303词),一个是网上随便找的(248303词)。它们的形式都如下:

中校 31
需要 353
圣彼得大 3
张德彝 2
周文德 2
……

  值得注意的是,语料库越大,代码搜索花费越大,会显著影响我们的算法速度测评环节。但是由于我们是比较三个算法的性能,不同系统配置、语料库不会影响三种算法的相对性能。

  获取本数据集或者代码工程,可以关注公众号‘三黄工作室’回复‘中文分词’。

二、正向最长匹配算法

  代码非常简单,见代码注释。

# 正向最长匹配
def forward_segment(text, dic):
    word_list = []
    i = 0
    while i < len(text):
        longest_word = text[i]  # 当前扫描位置的单字
        for j in range(i + 1, len(text) + 1):  # 所有可能的结尾
            word = text[i:j]  # 从当前位置到结尾的连续字符串
            if word in dic:  # 是否在词典中
                if len(word) > len(longest_word):  # 且更长
                    longest_word = word  # 则优先输出
        word_list.append(longest_word)  # 输出最长词
        i += len(longest_word)  # 正向扫描
    return word_list

三、逆向最长匹配算法

  代码非常简单,见代码注释。

# 逆向最长匹配
def backward_segment(text, dic):
    word_list = []
    i = len(text) - 1
    while i >= 0:
        longest_word = text[i]  # 当前扫描位置的单字,作为词的终点
        for j in range(0, i):  # 所有可能的前缀
            word = text[j:i + 1]  # 从前缀到当前位置连续字符串
            if word in dic:  # 是否在词典中
                if len(word) > len(longest_word):  # 且更长
                    longest_word = word  # 则优先输出
        word_list.insert(0, longest_word)  # 输出最长词
        i -= len(longest_word)  # 后向扫描
    return word_list

四、双向最长匹配算法

  代码非常简单,见代码注释。

# 双向最长匹配
def bidirectional_segment(text, dic):
    # 统计单字个数
    def count_single_char(word_list):
        return sum(1 for word in word_list if len(word) == 1)

    f = forward_segment(text, dic)
    b = backward_segment(text, dic)
    if len(f) < len(b):  # 词数更少优先级更高
        return f
    elif len(f) > len(b):
        return b
    else:
        if count_single_char(f) < count_single_char(b):  # 单字更少优先级更高
            return f
        else:  # 都相等时逆向匹配优先级更高
            return b

五、切分效果测评

  切分效果与所用的语料库大小有关,不具有使用价值。我们主要使用统计方法或者深度学习来进行分词。

# 加载数据集
def load_data():
    dic = []
    data = open("words.txt", "r", encoding="utf-8")
    for line in data:
        words = line.split(" ")
        dic.append(words[0])
    return dic


# 切分效果
sent = ["欢迎新老师生", "使用户满意"]
for s in sent:
    print(forward_segment(s, load_data()))
    print(backward_segment(s, load_data()))
    print(bidirectional_segment(s, load_data()))

  结果:

# 第一个语料库(55303):
['欢迎', '新', '老师', '生'] # 前向
['欢', '迎新', '老', '师生'] # 后向
['欢', '迎新', '老', '师生'] # 双向
['使用', '户', '满意'] # 前向
['使', '用户', '满意'] # 后向
['使', '用户', '满意'] # 双向
# 第二个语料库(248303):
['欢迎', '新老', '师生'] # 前向
['欢迎', '新老', '师生'] # 后向
['欢迎', '新老', '师生'] # 双向
['使用', '户', '满意'] # 前向
['使', '用户', '满意'] # 后向
['使', '用户', '满意'] # 双向

六、切分速度测评

  测评代码,就是让算法切分一句话,重复循环切分多次:

# 速度测评
def evaluate_speed(segment, text, dic):
    start_time = time.time()
    for i in range(100):
        segment(text, dic)
    elapsed_time = time.time() - start_time
    print('%.8f 万字每秒' % (len(text) * 100 / 10000 / elapsed_time))

text = "想要了解更多北京相关的内容"
dic = load_data()
evaluate_speed(forward_segment, text, dic)
evaluate_speed(backward_segment, text, dic)
evaluate_speed(bidirectional_segment, text, dic)

  结果:

# 第一个语料库(55303):
0.03538441 万字每秒 # 前向
0.04058394 万字每秒 # 后向
0.01625951 万字每秒 # 双向
# 第二个语料库(248303):
0.00714074 万字每秒 # 前向
0.00730858 万字每秒 # 后向
0.00372891 万字每秒 # 双向

七、结论

  1、切分速度与电脑配置、语料库大小强相关;
  2、后向匹配算法的速度略优于前向匹配算法;
  3、双向匹配算法的性能是前向、后向的一半,因为它执行了一次前向+一次后向;
  4、由于每次匹配都要去语料库判断是否含有字符串,吃了太多时间,我们要想办法通过一些数据结构的手段来解决这个问题,树就是一个好办法!

获取本项目的源代码

如果需要本组件的源代码,请扫描关注我的公众号,回复“中文分词”即可。

以上是关于自然语言处理正向逆向双向最长匹配算法的 切分效果与速度测评的主要内容,如果未能解决你的问题,请参考以下文章

正向_逆向匹配分词

nlp正向最大匹配算法逆向最大匹配算法双向最大匹配算法代码实现

基于中文词典的分词算法

中文分词:正向最大匹配与逆向最大匹配

规则分词法|自然语言

中文分词--逆向最大匹配