中文分词算法

分词算法

基于规则的分词

最大匹配法

最大匹配法设定一个最大词长度,每次匹配尽可能匹配最长的词

算法过程示例

最大词长度为4

s1s2w
结合成分子时null结合成分
结合成分子时null结合成
结合成分子时null结合
成分子时结合/成分子时
成分子时结合/成分子
成分子时结合/成分
子时结合/成分/子时
子时结合/成分/
结合/成分/子
null结合/成分/子/时null
实现代码

先贴一段统计词频的代码,其它算法也使用这个

class Tokenizer:
    def __init__(self,path,delta=0,trained=False) -> None:
        self.vocab = self.get_vocab(path,delta,trained)
    def get_vocab(self,path,delta,trained):
        words = []
        tags = []
        if trained:
            with open(path,mode='r') as f:
                counter = json.loads(f.read())
        else:
            with open(path,mode='r',encoding='utf-8') as f:
                content_split = f.read().split(" ")
                for content in content_split:
                    if content.strip()!="":
                        words.append(content.split("/")[0])
                        tags.append(content.split("/")[1])

            counter = Counter(words)
            counter = {key:(value+delta)/(len(words)+len(counter)*delta) for key,value in counter.items()}
            with open("chinese_word_split/data/vocab.json",mode='w') as f:
                f.write(json.dumps(counter))
                f.close()
        return counter
def max_match(s,vocab,max_word_length=4):
    words = []
    while len(s)>0:
        length = min(max_word_length,len(s))
        for i in range(length):
            w = s[0:length-i]
            if w in vocab or len(w)==1:
                words.append(w)
                s = s[length-i:]
                break
    return words
if __name__ == '__main__':
    tokenizer = Tokenizer('chinese_word_split\data\PeopleDaily_clean.txt',delta=1)
    print(max_match("结合成分子时",tokenizer.vocab))

在这里插入图片描述

最少分词法

略,因其和最大概率法相近,最大概率法的概率均设为1即为最少分词法。而且即有权和无权的差别

最大概率法

切分候选词(带前驱线索)
算法示例

最大词长度为4

s1s2w
结合成分子时null
结合成分子时结(0,0)/结合
结合成分子时结/结合(0,1)/结合成
结合成分子时结/结合/结合成分
合成分子时结/结合/
合成分子时结/结合/合(1,0)/合成
合成分子时结/结合/合/合成(1,1)/合成分
合成分子时结/结合/合/合成/合成分子
成分子时结/结合/合/合成/
成分子时结/结合/合/合成/成(2,0)/成分
成分子时结/结合/合/合成/成/成分(2,1)/成分子
成分子时结/结合/合/合成/成/成分/成分子时
分子时结/结合/合/合成/成/成分/
分子时结/结合/合/合成/成/成分/分(3,0)/分子
分子时结/结合/合/合成/成/成分/分/分子(3,1)/分子时
子时结/结合/合/合成/成/成分/分/分子/
子时结/结合/合/合成/成/成分/分/分子/子(4,0)/子时
结/结合/合/合成/成/成分/分/分子/子/
null结/结合/合/合成/成/成分/分/分子/子/时(5,0)/null
实现代码
def get_candidates(self,s,max_word_length=4):
    candidates = {}
    j = 0
    while len(s)>0:
        for i in range(min(max_word_length,len(s))):
            w = s[0:i+1]
            if len(w)==1 or (w in self.vocab):
                candidates[(j,i)] = w
        s = s[1:]
        j += 1
    return candidates
tokenizer = Tokenizer('chinese_word_split\data\PeopleDaily_clean.txt',delta=1)
print(tokenizer.get_candidates("结合成分子时"))

在这里插入图片描述

寻找前驱
算法示例

前驱信息隐藏在候选词的键当中,比如分的键是(3,0),那么3的前驱计算如下

index = 3-1 = 2,满足index1+index2 = index的所有(index1,index2)都是潜在的前驱

(index1,index2) = (2,0) => candidates => 成

(index1,index2) = (1,1) => candidates => 合成

(index1,index2) = (0,2) => candidates => 不存在,把它丢弃

实现代码
def get_prior(self,index):
    if index==0:
        return [-1]
    index = index-1
    priors = []
    for i in range(index+1):
        temp = (index-i,i)
        if temp in self.candidates:
            priors.append(self.candidates_index[self.candidates[temp]])
    return priors
构建DAG
采用静态链表的形式存储DAG
textprobbestpriorsindex
20(乱填的)-1[-1]0
结合20(乱填的)-1[-1]0
20(乱填的)-1[0]1
20(乱填的)-1[5,6]4
20(乱填的)-1[8]5
实现代码
class DAG:
    class Word:
        def __init__(self,prob,priors,text,index) -> None:
            self.best = -1 # 最佳前驱索引
            self.priors = priors # 前驱索引列表
            self.prob = -np.log(prob) # 概率转化成负对数
            self.index = index # 候选词首字符索引
            self.text = text # 候选词

    def __init__(self,candidates,vocab) -> None:
        self.words = []
        index = 0
        self.candidates = candidates
        self.candidates_index = {value:i for i,value in enumerate(candidates.values())}
        for (index,_),candidate in candidates.items():
            word = DAG.Word(vocab[candidate],self.get_prior(index),candidate,index)
            self.words.append(word)
        self.final_index = index
前向计算
算法示例
费用前趋词最佳前趋词累积费用
3.573NullNull0+3.573=3.573
结合3.543NullNull0+3.543=3.543
3.5183.573+3.518=7.091
合成4.1943.573+4.194=7.767
2.800合、结合结合3.543+2.800=6.343
成分3.908合、结合结合3.543+3.908=7.451
2.862成、合成6.343+2.862=9.205
分子3.465成、合成6.343+3.465=9.808
3.304分、成分成分7.451+3.304=10.755
子时6.000分、成分成分7.451+6.000=13.451
2.478子、分子分子9.808+2.478=12.286
实现代码
def forward(self):
    # 前向计算累积概率,并记录最佳前驱
    for word in self.words:
        min_prob = -1
        min_prior = -1
        for prior in word.priors:
            if min_prob==-1 or self.words[prior].prob < min_prob:
                min_prob = self.words[prior].prob
                min_prior = prior
        word.prob += min_prob
        word.best = min_prior
回溯
算法示例

找到最佳终点

终点有子时的费用低,所以是终点

按最佳前驱词回溯:时=>分子=>成=>结合

实现代码
def backward(self):
    # 找到所有终点中累积费用最小的作为真正的终点
    min_final_word = None
    min_final_prob = -1
    for word in self.words:
        if word.index==self.final_index:
            if min_final_prob==-1 or word.prob<min_final_prob:
                min_final_prob = word.prob
                min_final_word = word
	# 从该终点开始进行回溯
    results = []
    results.append(min_final_word.text)
    while min_final_word.best!=-1:
        min_final_word = self.words[min_final_word.best]
        results.insert(0,min_final_word.text)
    return results
if __name__== '__main__':
    tokenizer = Tokenizer('chinese_word_split\data\PeopleDaily_clean.txt',delta=1)
    dag = DAG(tokenizer.get_candidates("结合成分子时"),tokenizer.vocab)
    print(dag.min_path())

在这里插入图片描述

基于统计的分词

待补充…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳成荫~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值