自然语言处理入门学习--切分算法

本文介绍了四种基本的分词算法:完全切分、正向最长匹配、逆向最长匹配和双向最长匹配。这些算法在速度上有显著差异,其中双向匹配速度最慢,而正向和逆向匹配速度相近。通过代码示例展示了每种算法的工作原理,并进行了速度测试。词典分词注重速度而非精度,这些朴素的分词方法无法消除歧义,适用于简单场景。

切分算法

分词算法的核心是速度。

1、完全切分

完全切分:找出一段文本中所有的分词。主要这实际上不是标准意义上的分词,因为这个算法会把单个字全部输出,并没有考虑到是否是有意义的词语序列。例如:“北京大学”切分得到的结果是[‘北’, ‘北京’, ‘北京大学’, ‘京’, ‘大’, ‘大学’, ‘学’]。

代码实现:遍历文本中词是否在词典中即可。

from load_dictionary import load_dictionary


def fully_segment(text, dic):
    word_list = []
    for i in range(len(text)):
        for j in range(i, len(text)+1):
            word = text[i:j]
            if word in dic:
                word_list.append(word)
    return word_list


if __name__ == '__main__':
    dic = load_dictionary()
    word_list = fully_segment("商品和服务", dic)
    print(word_list)

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]
            # 这里其实只需要判断word是否在词典中,而不用判断是否是最长的
            # 因为j是在不断增大的
            if word in dic:
                longest_word = word
        i += len(longest_word)
        word_list.append(longest_word)
    return word_list

3、逆向最长匹配

逆向最长匹配:顾名思义就是逆向开始匹配,和正向最长匹配正好相反。

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 and len(longest_word)<len(word):
                longest_word = word
        i -= len(longest_word)
        word_list.append(longest_word)
    return word_list

4、双向最长匹配

双向最长匹配:
(1)同时执行正向、逆向最长匹配,若词数不同,返回词数少的那个分词;
(2)若词数相同,则返回单字词更少的那个

def count_single_char(word_list):
    """
    统计单个字符的单词
    :param word_list:
    :return:
    """
    return sum(1 for word in word_list if len(word))==1

def bidirectional_segment(text, dic):
    forward = forward_segment(text=text, dic=dic)
    backward = backward_segment(text=text, dic=dic)
    # print(f"forward segment: {forward}")
    # print(f"backward segment: {backward}")
    if len(forward)<len(backward):
        return forward
    elif len(forward)>len(backward):
        return backward
    else:
        if count_single_char(forward)>count_single_char(backward):
            return backward
        else:
            return forward

5、总结

词典分词的核心在速度,不在精度。其实上面几种切分算法,都只是在借用简单的分词规则或者经验,来进行朴素的分词,达不到消除消除歧义等能力,而且没有借助复杂数据结构,相对于的算法复杂度较高。
就只考虑后三种切分算法而言,显然双向匹配算法速度最慢,正向和逆向差不多。
速度测试代码如下:

import time
from load_dictionary import load_dictionary
from forward_segment import forward_segment
from backward_segment import backward_segment
from bidirectional_segment import bidirectional_segment

def evaluate_speed(segment, text, dic, pressure):
    start = time.time()
    for i in range(pressure):
        segment(text, dic)
    elapsed_time = time.time()-start
    print("%s :%.2f 万字/秒" %(segment.__name__,len(text)*pressure/10000/elapsed_time))

if __name__ == '__main__':
    text = "江西鄱阳湖干枯,中国最大淡水湖变成大草原"
    pressure = 10000
    dic = load_dictionary()

    evaluate_speed(forward_segment, text, dic, pressure)
    evaluate_speed(backward_segment, text, dic, pressure)
    evaluate_speed(bidirectional_segment, text, dic, pressure)

本机效果:

forward_segment :89.16 万字/秒
backward_segment :84.22 万字/秒
bidirectional_segment :39.64 万字/秒
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值