N元语法模型的数据稀疏问题解决方法之一:Good-Turing平滑

本文探讨了统计语言模型中常见的数据稀疏问题,并介绍了如何使用古德-图灵平滑算法来解决这一难题。通过一个具体示例展示了算法的实际应用过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


在统计语言模型章节中,我们谈到了N元语法模型不可避免的一个问题,就是数据稀疏,其原因是大规模语料统计与有限语料的矛盾。根据齐普夫(Zipf)法则,我们能够推测知零概率问题不可避免。数据稀疏问题的解决办法就是进行平滑处理。平滑处理的算法有很多,本文将介绍众多算法中的佼佼者:古德-图灵(Good-Turing)平滑算法。

  古德-图灵(Good-Turing)估计法是很多平滑技术的核心,于1953年有古德(I.J.Good)引用图灵(Turing)的方法而提出来的。其基本思想是:利用频率的类别信息来平滑频率。对于任何发生r次数的n元语法,都假设它发生了r*次。

  其中,nr是训练语料中正好发生r次的N元组的个数。也就是说,发生r次的N元组的调整由发生r次的N元组与发生r+1次的N元组两个类别共同决定。统计数为r*次的N元组,其概率为:


  我们注意到:

  也就是说,N等于这个分布中最初的统计。那样,样本中所有事件的概率之和为

  因此,可以这样说我们把你n1/N的概率剩量分配给未见事件。为了更好地理解古德-图灵(Good-Turing)估计法,以一个例子来讲解。

  训练集合:T={<s>what is it what is small?}|T|=8

  验证集合:V={what is it small ? <s> flying birds are a bird.}, |V|=12

  在训练集合上,我们得到:p(<s>)=p(it)=p(small)=p(?)=0.125, p(what)=p(is)=0.25,其他为0

 如果不经过平滑处理,则验证集上两句子的概率分别为:p(what is it?)=(0.25*2)*(0.125*2)≈0.001  p(it is flying.)=0.125*0.25*(0*2)=0

  现在用古德-图灵算法进行平滑处理,如下:

 首先计算,各发生r次N元组类别的数目,依次为N(0)=6,N(1)=4,N(2)=2,N(i)=0 ,i>2:

 其次,重新估计各概率值。

  对于发生0次的事件概率:Pr(.)=p(flying)=p(birds)=p(are)=p(bird)=p(a)=(0+1)*N(0+1)/(8*N(0))=1*4/(8*6)≈0.083

  对于发生1次的时间概率:Pr(it)=p(<s>)=p(small)=p(?)=(1+1)*N(1+1)/(8*N(1))=2*2/(8*4)=0.125

  对于发生两次的时间概率:Pr(what)=Pr(is)=(2+1)*N(2+1)/(8*N(2))=3*0/(8*2)=0: 保持原值0.25

  归一化处理,6*P0+4*P1+2*P2=1.5。.

 所以,归一化处理后,p’(it)=p’(<s>)=p’(small)=p’(?)= 0.125/1.5 ≈0.08,  p’(what)=p’(is)= 0.25/1.5 ≈0.17, 

p’(.)=p’(birds)=p’(are)=p’(bird)=p’(a) = 0.083/1.5  ≈0.06

 因此:p’(what is it?)=(0175*2)*(0.08*2)≈0.0002   p’(it is flying.) ≈ 0.08*0.17*(0.06*2)≈0.00004

任务描述 本关任务:实现二语言模型数据平滑,并利用平滑后的数据计算句子概率。 相关知识 为了完成本关任务,你需要掌握:1.模型平滑化。2.good-turning平滑模型平滑 在使用语言模型直接计算某个句子出现的概率时,可能会由于某个单词或单词对出现的概率为0而导致整个句子出现的概率为0。 例如下面这个场景: 在上面的场景中,由于部分单词对出现的概率为0,导致最终两句话出现的概率均为0。但实际上,s1=“今天没有训练营”比s2=“今天训练营没有”更符合语法习惯,我们也更希望计算出来的P(s1)大于P(s2)。 一般来说,语言模型平滑处理可分为以下三类: Discounting(折扣):通过给概率不为0的项打折扣,来提高概率为0的项的概率; Interpolation(插值):在使用N-gram模型计算某一项的概率时,同时结合低阶的模型所计算出的概率; Back‐off:approximate counts of unobserved N‐gram based on the proportion of back‐off events (e.g., N‐1 gram)。 这里我们主要介绍与使用Discounting中的good-turning平滑方法good-turning平滑 Good-Turing技术是在1953年由古德(I.J.Good)引用图灵(Turing)的方法而提出来的,其基本思想是:用观察计数较高的N语法数重新估计概率量的大小,并把它指派给那些具有零计数或者较低计数的N语法。涉及的符号含义为: c:某个N语法出现的频数。 Nc:出现次数为c的 N-gram 词组的个数,是频数的频数 c*:Good-Turing平滑计数 设N为测试组集合中组的数目,则有如下公式: 通过新频数可计算出经过good-turing平滑后的组概率,公式如下: 编程要求 根据提示,在右侧编辑器补充代码,编写平滑函数,计算句子的概率 测试说明 平台会对你编写的代码进行测试: 语料库: 研究生物很有意思。他大学时代是研究生物的。生物专业是他的首选目标。他是研究生。 测试输入:他是研究物理的 预期输出:5.6888888888888895e-05 开始你的任务吧,祝你成功! import jieba #语料句子 sentence_ori="研究生物很有意思。他大学时代是研究生物的。生物专业是他的首选目标。他是研究生。" #测试句子 sentence_test=input() #任务:编写平滑函数完成数据平滑,利用平滑数据完成对2-gram模型的建立,计算测试句子概率并输出结果 # ********** Begin *********# # ********** End **********#
03-29
<think>嗯,用户想知道如何在Python中实现基于Good-Turing平滑的二语言模型来计算句子概率。首先,我需要回忆一下Good-Turing平滑的基本原理。记得Good-Turing主要用于处理未见事件,通过调整已有频率的计数,将部分概率分配给零概率事件。那具体到二模型的话,就是处理那些没出现过的bigram对吧? 首先,用户的问题涉及两个部分:数据平滑和计算句子概率。数据平滑部分,Good-Turing方法需要统计每个频率r出现的次数Nr,然后用(r+1)*Nr+1/Nr来估计调整后的频率。不过实际操作中可能需要处理很多边缘情况,比如当r+1的Nr+1不存在的时候怎么办。可能需要使用一些插值或者回退的方法。 然后是二语言模型的实现。二模型需要考虑前一个词和当前词的关系。所以需要先统计训练语料中的bigram和unigram的出现次数。然后根据Good-Turing方法调整概率。这里可能需要先计算所有bigram的频率,然后统计每个r对应的Nr,也就是出现r次的bigram有多少个。比如,出现1次的bigram有多少个,出现2次的有多少个,以此类推。 接下来,实现Good-Turing平滑后的概率计算。对于出现的bigram,使用调整后的频率,对于未出现的,使用估计的零概率部分。但要注意,总概率需要归一化,所以可能需要计算所有未见事件的概率总和,然后分配给每个未见事件。不过具体实现的时候,可能需要更高效的方式,比如预先计算调整后的频率表,或者使用某种查找方式。 然后,关于句子概率的计算,应该将句子分解为bigram,然后对每个bigram计算概率,再将这些概率相乘,同时还要考虑句子的起始和结束标记,比如在句子开头加上<s>,结尾加上</s>。例如,句子“菜很香”会被分解为['<s>', '菜', '很', '香', '</s>'],对应的bigrams是('<s>', '菜'), ('菜', '很'), ('很', '香'), ('香', '</s>')。每个bigram的概率相乘就是整个句子的概率。 现在要考虑在Python中如何实现这些统计。可能需要使用字典来存储unigram和bigram的计数。然后遍历语料库,统计每个unigram和bigram的出现次数。接着计算每个r对应的Nr,即对于每个出现次数r,有多少个bigram出现了r次。这一步可能需要遍历所有的bigram计数,统计频率分布。 在处理未见过的bigram时,Good-Turing方法是将它们的概率估计为N1/N,其中N1是出现1次的bigram的数量,N是总bigram的数量。或者可能需要更复杂的计算,比如对于出现r次的bigram,其调整后的计数是(r+1)*Nr+1/Nr,那么概率是这个调整后的计数除以总调整后的计数总和。不过这可能比较复杂,特别是当某些r+1的Nr+1为零的时候,需要进行平滑处理,比如使用线性回归或者其他方法来估计。 另外,参考用户提供的引用,特别是引用[3]提到了Good-Turing平滑的示例和Python实现,可能需要参考这些内容。引用[2]中提到了将训练语料分为两部分,一部分用于估计概率,另一部分用于计算系数,可能在这里不需要,但需要注意数据的分割是否会影响Good-Turing的实现。 代码方面,首先需要读取训练数据,进行分词,然后统计unigram和bigram的计数。然后计算每个r对应的Nr。接着计算调整后的计数,进而得到每个bigram的Good-Turing概率。对于未出现的bigram,使用N1/(N0*N)之类的公式,其中N0是未见bigram的数量。不过具体如何计算可能需要更仔细的推导。 此外,还需要处理低频bigram的情况,比如当r比较小时,Nr可能不稳定,需要插值或者其他方法。但为了简化,可能先实现基本的Good-Turing,不考虑这些优化。 然后,在计算句子概率时,遍历每个bigram,查找其调整后的概率,如果是未出现的,则使用Good-Tuning估计的概率。最后将所有概率相乘得到结果。 可能还需要处理数据稀疏问题,比如某些unigram可能没有出现过,导致bigram的概率无法计算,这时候可能需要回退到unigram或者加一平滑,但用户特别要求使用Good-Turing,所以应该严格按照该方法处理。 综上,实现步骤大致如下: 1. 预处理语料,分词,添加开始和结束标记。 2. 统计unigram和bigram的出现次数。 3. 统计每个频率r对应的bigram数量Nr。 4. 计算调整后的频率r* = (r+1)*Nr+1 / Nr。 5. 计算总调整后的频率之和,进而得到每个bigram的概率。 6. 对于未出现的bigram,使用N1/N0作为概率估计,或者根据Good-Turing公式计算。 7. 实现计算句子概率的函数,将句子分解为bigram,并累乘每个bigram的概率。 需要注意的是,实际实现中可能会遇到很多细节问题,比如如何处理r的最大值的情况,当r+1的Nr+1不存在时如何处理,这时候可能需要用其他方法估计,比如将r*设为0,或者使用线性插值。另外,计算N0的时候,N0是可能的bigram总数减去已出现的bigram数量,可能的bigram总数是词汇量的平方,但这对于大词汇量来说可能不现实,需要近似处理。 可能还需要在代码中使用defaultdict来存储计数,避免键不存在的错误。同时,处理低频bigram时,确保概率不为零。 现在,根据这些思路,可以开始编写Python代码的大纲,然后逐步填充细节,并进行测试,比如引用[5]中的示例,检查是否能够得到合理的结果。</think>### 分步实现基于Good-Turing平滑的二语言模型 #### **一、数据预处理** 1. **添加句子边界符** 在每条训练文本的首尾添加特殊标记,如`<s>`表示句子开始,`</s>`表示句子结束。例如:"菜很香"转换为`<s> 菜 很 香 </s>`。 2. **构建词表** 统计所有训练语料中的词频,过滤低频词(如出现次数<2的词),用`<UNK>`替换未知词,减少数据稀疏问题。 #### **二、统计n-gram频率** ```python from collections import defaultdict # 统计unigram和bigram频率 unigram_counts = defaultdict(int) bigram_counts = defaultdict(int) for sentence in training_data: tokens = ['<s>'] + sentence.split() + ['</s>'] for i in range(len(tokens)-1): unigram_counts[tokens[i]] += 1 bigram = (tokens[i], tokens[i+1]) bigram_counts[bigram] += 1 # 统计频率r对应的bigram数量Nr r_counts = defaultdict(int) for count in bigram_counts.values(): r_counts[count] += 1 ``` #### **三、Good-Turing平滑计算** 1. **计算调整后的频率** 对每个出现次数为r的bigram,调整频率为: $$r^* = (r+1) \cdot \frac{N_{r+1}}{N_r}$$ 其中$N_r$表示出现r次的bigram数量。 2. **处理未登录词对** 未登录bigram的概率估算为: $$P_{\text{GT}}(w_i|w_{i-1}) = \frac{N_1}{N_0 \cdot N}$$ - $N_1$:出现1次的bigram数量 - $N_0$:理论上可能的bigram组合数 - 实际出现的bigram数 - $N$:训练语料总词数 ```python def get_smoothed_prob(bigram): r = bigram_counts.get(bigram, 0) if r == 0: # 未登录词处理 N1 = r_counts.get(1, 0) V = len(unigram_counts) # 词表大小 N0 = V**2 - len(bigram_counts) return N1 / (N0 * sum(bigram_counts.values())) else: # 调整后的r* Nr = r_counts.get(r, 0) Nr_plus1 = r_counts.get(r+1, 0) if Nr_plus1 == 0: return r / sum(bigram_counts.values()) adjusted_r = (r + 1) * Nr_plus1 / Nr return adjusted_r / sum(bigram_counts.values()) ``` #### **四、句子概率计算** ```python def sentence_probability(sentence): tokens = ['<s>'] + sentence.split() + ['</s>'] prob = 1.0 for i in range(len(tokens)-1): bigram = (tokens[i], tokens[i+1]) prob *= get_smoothed_prob(bigram) return prob # 示例测试 print(sentence_probability("菜 很 香")) # 输出类似0.00617的结果[^5] ``` #### **五、优化策略** 1. **低频词处理** 对出现次数<2的unigram替换为`<UNK>`,降低稀疏性。 2. **插值法回退** 若某个bigram的$N_{r+1}=0$,可结合Kneser-Ney平滑进行插值。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值