平滑方法(smoothing)

发布于:2025-08-17 ⋅ 阅读:(13) ⋅ 点赞:(0)

什么是平滑方法

平滑(smoothing)是机器学习中处理概率估计的“保险丝”:当数据里出现 0 或接近 0 的计数时,用人为的小量把概率“撑”起来,防止模型因为没见过就彻底否定某事件的可能性。

平滑 = 在经验频率上人为加一个“伪计数”(pseudo-count),使所有可能事件的概率都 > 0,且总和仍为 1。这种做法不但有效处理了稀疏数据(稀疏数据:文本、推荐系统等高维离散空间里,绝大多数组合在训练集里都是 0。),同时,MLE 把训练集偶然出现的 0 当成“绝对不可能”,平滑相当于引入先验,降低方差、提高泛化,防止过拟合,常用于大语言模型和文本分类当中。

常见的平滑方法

拉普拉斯平滑

拉普拉斯平滑(Laplace Smoothing)是一种在概率计算中常用的平滑技术,主要用于解决零概率问题。当某个事件在训练数据中从未出现过时,直接计算其概率会得到 0,这可能导致模型在预测时出现偏差或错误。拉普拉斯平滑通过对概率计算进行微调,避免了这种极端情况的发生。
以离散型随机变量为例(如文本分类中的单词出现概率),假设:

  • 某类别下有 N 个样本(如某类文档中所有单词的总数)
  • 某个事件(如单词 w)在该类别中出现的次数为 count(w)
  • 该类别中所有可能事件的总数(如词汇表大小)为 V
    则未平滑的概率为:
    在这里插入图片描述
    拉普拉斯平滑后的概率为:
    在这里插入图片描述
def laplace_smoothing(count_w, N, V):
    """
    拉普拉斯平滑计算概率
    :param count_w: 单词 w 在某类别中的出现次数
    :param N: 该类别下的总样本数(如某类文档的总单词数)
    :param V: 事件总数(如词汇表大小)
    :return: 平滑后的概率
    """
    return (count_w + 1) / (N + V)

# 示例:假设场景为 “水果类” 文档的单词概率计算
# 词汇表大小(所有可能的单词总数)
vocab_size = 100  
# “水果类” 文档的总单词数
total_words_in_fruit = 50  
# 单词 “苹果” 在 “水果类” 文档中的出现次数(假设没出现过,次数为 0)
apple_count = 0  

# 计算平滑后的概率
probability = laplace_smoothing(apple_count, total_words_in_fruit, vocab_size)
print(f"单词 '苹果' 在 '水果类' 文档中平滑后的概率为: {probability:.4f}")

拉普拉斯平滑虽然简单,但存在局限性:

  • 当事件总数 V 很大时(如词汇表庞大),分母加 V 会显著影响概率估计,导致偏差较大
  • 对所有未出现的事件都赋予相同的概率(1/(N+V)),但实际中不同事件的 “稀有程度” 可能不同

古德——图灵平滑

古德 - 图灵平滑(Good-Turing Smoothing)是一种用于处理概率估计中 “未观测事件” 的经典平滑技术,由统计学家古德(I.J. Good)基于图灵(A.M. Turing)的思想提出。它的核心是通过 “事件出现的频率” 来动态调整概率分配,尤其适合处理稀有事件(包括从未出现过的事件)。

  • 假设我们有一个事件集合(如文本中的 n 元组、分类任务中的特征等),令:N_c:表示 “在训练数据中恰好出现 c 次”的事件总数(即计数为 c 的事件有多少个)。
    (例如,N_1是只出现 1 次的事件总数,N_0是未出现的事件总数(需通过词汇表等外部知识估计)。)
  • c:某个事件在训练数据中的出现次数。
    古德 - 图灵平滑为每个事件重新估计一个 “调整后的计数” c,再用c 计算概率:
    在这里插入图片描述
    最终,事件的概率为:
    在这里插入图片描述
    其中,N是所有事件的总出现次数(训练数据的总计数)。
from collections import defaultdict

def count_ngrams(text, n):
    """统计文本中 n 元组的出现次数"""
    ngrams = defaultdict(int)
    words = text.split()
    # 遍历生成所有 n 元组并计数
    for i in range(len(words) - n + 1):  
        ngram = tuple(words[i:i + n])
        ngrams[ngram] += 1
    return ngrams

def good_turing_smoothing(ngrams, vocab_size, n):
    """
    古德-图灵平滑计算概率
    :param ngrams: n 元组的计数字典(键:n 元组,值:出现次数)
    :param vocab_size: 词汇表大小(用于估计未出现的 n 元组数量)
    :param n: n 元组的长度(如 1-gram、2-gram 等)
    :return: 平滑后的概率字典(键:n 元组,值:概率)
    """
    # 统计 N_c:出现 c 次的 n 元组数量
    count_stats = defaultdict(int)
    for count in ngrams.values():
        count_stats[count] += 1

    # 计算总事件数(训练数据的总 n 元组数量)
    total_ngrams = sum(ngrams.values())

    # 计算未出现的 n 元组数量(理论值,简化处理)
    # 总可能的 n 元组数量 = vocab_size^n (假设词汇表中每个位置独立)
    total_possible_ngrams = vocab_size ** n  
    # 未出现的 n 元组数量
    N0 = total_possible_ngrams - len(ngrams)  

    # 为未出现的 n 元组(c=0)补充统计
    count_stats[0] = N0  

    # 计算平滑后的概率
    smoothed_probs = {}
    for ngram, count in ngrams.items():
        if count + 1 in count_stats:
            c_star = (count + 1) * count_stats[count + 1] / count_stats[count]
        else:
            # 若 count+1 无统计,简化处理(如高频时近似 c*=c)
            c_star = count  
        prob = c_star / total_ngrams
        smoothed_probs[ngram] = prob

    # 处理未出现的 n 元组的概率(可选:若需要显式计算,可遍历所有可能的 n 元组)
    # 这里仅演示核心逻辑,实际应用可能需更完整的遍历
    return smoothed_probs

# 示例:用简单文本训练 2-gram 模型并平滑
text = "apple banana apple orange banana grape"
words = text.split()
# 词汇表大小(去重后的单词数量)
vocab_size = len(set(words))  
# 统计 2-gram 计数
ngrams = count_ngrams(text, n=2)

# 古德-图灵平滑计算概率
smoothed_probs = good_turing_smoothing(ngrams, vocab_size, n=2)

# 打印结果
for ngram, prob in smoothed_probs.items():
    print(f"n 元组 {ngram} 的平滑概率为: {prob:.6f}")

古德 - 图灵平滑通过 “用相邻频率的事件数来调整当前事件的概率”,实现了对稀有事件更合理的估计,是处理数据稀疏性的重要工具。它比拉普拉斯平滑更灵活,但实现也更复杂,通常在对精度要求较高的场景(如语音识别、机器翻译)中使用。

Jelinek-Mercer平滑

Jelinek-Mercer 平滑(Jelinek-Mercer Smoothing),也叫插值平滑(Interpolation Smoothing),是一种在自然语言处理(尤其是语言模型)中常用的平滑技术,用于解决数据稀疏问题,让概率估计更合理。
在 n-gram 语言模型(比如用 “前面 n-1 个词预测下一个词”)里,经常遇到 “训练数据中没出现过的 n-gram 组合” ,直接算概率会得到 0,导致模型预测失效。比如用 2-gram(二元组)模型时,训练语料里没出现过 “吃 火箭” 这个组合,预测 “我 吃 ___” 时,“火箭” 就会因概率 0 被忽略,显然不合理。Jelinek-Mercer 平滑通过 “插值不同阶数的语言模型” 来解决这问题,让稀有或未出现的组合也能有合理概率。

核心思路:多阶模型插值

Jelinek-Mercer 平滑的核心是 把不同阶数的 n-gram 模型线性插值,公式一般长这样:
在这里插入图片描述
简单说就是:用 高阶模型(比如 3-gram)抓长距离依赖(比如 “吃 苹果” 这种常见组合的精确概率),用 低阶模型(比如 2-gram、1-gram)兜底(遇到没见过的组合,用短距离或单字词的概率补上),各阶模型的权重 λ 加起来等于 1(λ_1+λ_2 +⋯+λ_k=1 )——最常见的是 3-gram、2-gram、1-gram 插值,公式简化为:
在这里插入图片描述

怎么确定权重 λ

  • 经验法:凭对数据的理解调参(比如给高阶模型更高权重,让精准度优先)
  • EM 算法(期望最大化):用训练数据自动优化 λ,让模型在验证集上的 perplexity(困惑度,衡量语言模型好坏的指标)最小。

Katz平滑

Katz 平滑(Katz Smoothing)也叫后退估计(Back-off Estimation) ,也是自然语言处理中用于解决 n-gram 语言模型数据稀疏问题的经典方法。它结合了古德 - 图灵平滑(Good-Turing) 的思想,又引入后退机制(Back-off) ,比简单的插值平滑(如 Jelinek-Mercer)更灵活。
在 n-gram 模型里,最大的痛点是 数据稀疏:

  • 训练数据中没出现过的 n-gram(比如 3-gram “吃 火箭 苹果”),直接算概率会是 0,导致模型失效
  • 就算出现过,低频 n-gram 的概率也容易被 “高估”(因为样本少,统计不稳定)

Katz 平滑的核心是:用 “高阶模型优先,低阶模型兜底” 的策略,给稀疏的 n-gram 分配更合理的概率,同时避免概率为 0。

Katz 平滑分两步:

  1. 折扣(Discounting):对高频 n-gram(出现次数多的),“扣掉” 一部分概率,把这部分概率让给低频 / 未出现的
    n-gram
  2. 后退(Back-off):对低频 / 未出现的 n-gram,不用当前阶的模型(比如 3-gram),而是 “退一步” 用低阶模型(比如2-gram、1-gram)的概率兜底。

假设我们要计算 3-gram 概率 P(w_i∣w_i−2 ,w_i−1),Katz 平滑的逻辑是:

  1. 先看 3-gram 是否 “可信”

如果 3-gram 出现过(次数 ≥ 某个阈值):
用折扣后的概率:
在这里插入图片描述
其中:

  • c(⋅)= n-gram 的出现次数
  • d= 折扣系数(比如用古德 - 图灵估计动态计算,高频 n-gram 的 d 接近 1,低频的 d更小)
  • 分子 max(…, 0):保证扣完后概率非负
  • 如果 3-gram 没出现过(次数 = 0):
    就 “后退” 到2-gram,用 2-gram 的概率乘以一个后退权重 α:
    在这里插入图片描述
    其中:
  • α = 后退权重(确保所有可能的 w_i概率和为 1,需要归一化计算)
  • P(w_i∣w_i−1) = 2-gram 的概率(同样会递归应用 Katz 平滑,直到退到 1-gram)
    Katz 平滑是 “精致版的 n-gram 平滑”:用 “折扣高频、后退低频” 的策略,既抓局部依赖,又解决稀疏问题。比拉普拉斯、线性插值更精细,是传统 NLP 中 n-gram 模型的 “顶流” 方法,至今仍被用作基线对比

绝对折扣平滑

绝对折扣平滑(Absolute Discounting Smoothing)是一种用于处理 n-gram 语言模型数据稀疏问题的平滑技术,核心思想是通过对所有非零计数的 n-gram 统一减去一个固定的 “折扣值”,将这部分 “让出” 的概率分配给未出现的 n-gram,从而避免零概率问题。它比拉普拉斯平滑更精细,比 Katz 平滑更简单,是平衡效果与复杂度的常用方法。
在 n-gram 模型中,直接用频率估计概率时存在两个问题:

  • 未出现的 n-gram(计数 = 0)概率为 0,不合理
  • 出现次数少的 n-gram(如计数 = 1、2)的概率被高估(因数据稀疏,实际频率可能低于观测值)

绝对折扣平滑的解决思路是:给所有 “有计数的 n-gram” 统一减去一个小的固定值(折扣 d),再将这部分 “扣下来” 的概率总和分配给未出现的 n-gram,既修正高频 n-gram 的概率,又给低频 / 未出现的 n-gram 赋予合理概率。

以计算 n-gram 概率 P(w_i∣w_i−n+1i−1) 为例(即 “前 n-1 个词预测第 i 个词” 的概率):

  1. 对有计数的 n-gram(计数 c ≥ 1):

在这里插入图片描述
其中:

  • c是当前 n-gram 的出现次数(如 “w_{i-2}, w_{i-1}, w_i” 的计数)

  • d是固定折扣值(通常取 0.5 左右,需通过验证集调整)

  • 分母是 “前 n-1 个词” 条件下所有可能后续词的总计数

  • 对未出现的 n-gram(计数 c = 0)
    未出现的 n-gram 的概率来自于 “扣下来的总折扣概率”,这部分概率会通过低阶 n-gram 模型(如 n-1 gram)进行分配,公式为:
    在这里插入图片描述
    其中:

  • α 是归一化系数,确保所有可能后续词的概率和为 1

  • P_abs (⋅∣w_i−n+2i−1)是低阶 n-gram(如 n-1 gram)的平滑概率,形成 “后退式” 兜底(类似 Katz
    平滑的后退机制,但更简单)

归一化系数 α 的计算

α 的作用是将 “扣下来的折扣概率” 合理分配给未出现的 n-gram,计算公式为:
在这里插入图片描述
其中,N_1是 “前 n-1 个词” 条件下,恰好出现 1 次的后续词的总数(用于衡量数据稀疏程度)

Witten-Bell 平滑

Witten-Bell 平滑(Witten-Bell Smoothing)是一种用于 n-gram 语言模型的平滑技术,由 Witten 和 Bell 于 1991 年提出。它的核心特点是通过 “数据驱动的权重分配” 平衡高阶模型(如 3-gram)和低阶模型(如 2-gram、1-gram),尤其擅长处理 “已见事件”(训练中出现过的 n-gram)和 “未见事件”(未出现的 n-gram)的概率分配,且无需手动调整参数,适应性较强。
Witten-Bell 平滑的核心逻辑可概括为:

  • 对于已在训练中出现过的 n-gram(已见事件),优先用高阶模型的概率,但 “打折扣” 后保留一部分概率
  • 对于未出现的 n-gram(未见事件),将高阶模型 “让出来” 的概率,通过低阶模型(如 n-1 gram)重新分配,且分配比例由数据中的“新事件数量” 决定
    假设我们要计算 3-gram 概率 P(w_i∣w_i−2 ,w_i−1)(即 “前两个词w_i−2 ,w_i−1预测下一个词 w_i” 的概率),Witten-Bell 平滑的公式如下:

已见事件(w_i在上下文 w_i−2 ,w_i−1中出现过)

在这里插入图片描述
其中:

  • c(⋅)为 n-gram 的出现次数: c(w_i−2 ,w_i−1,w_i)是 3-gram的计数,c(w_i−2,w_i−1)是前两个词的总计数
  • λ是高阶模型的权重(0<λ<1),由数据自动计算
  • P_WB (w_i∣w_i−1)是低阶模型(2-gram)的 Witten-Bell 平滑概率(递归应用同一逻辑,直到退到 1-gram)

未见事件(w_i在上下文 w_i−2 ,w_i−1中未出现过)

此时高阶模型的概率为 0,因此直接用低阶模型的概率分配:
在这里插入图片描述

权重λ的关键计算

Witten-Bell 平滑的核心是自动计算 λ,公式为:
在这里插入图片描述
T(w_i−2,w_i−1 )表示 “在上下文 w_i−2 ,w_i−1下,首次出现的后续词 w_i的总数”(即 “新事件数量”)。
例如:若上下文 “吃 了” 的后续词有 “饭”(首次出现)、“面”(首次出现)、“饭”(再次出现),则 T=2(仅 “饭”“面” 是首次出现)

T 的意义:衡量数据的 “新颖性”

T(w_i−2,w_i−1)是 Witten-Bell 平滑的 “灵魂”,它反映了当前上下文的数据新颖性:
T越大:说明在该上下文下,有更多 “首次出现的后续词”,数据越稀疏(高阶模型不可靠),因此 λ 越小(低阶模型权重越高)
T越小:说明上下文较常见(如 “的 人”),后续词重复出现多,数据稠密(高阶模型可靠),因此 λ 越大(高阶模型权重越高)

Kneser-Ney平滑

Kneser-Ney 平滑(Kneser-Ney Smoothing)是 n-gram 语言模型中性能最优的平滑技术之一,由 Kneser 和 Ney 于 1995 年提出。它在绝对折扣平滑的基础上,通过重新定义低阶模型的概率估计(强调词的 “延续性”),解决了传统平滑方法中低阶模型过度依赖高频词的问题,尤其在处理稀疏数据和长距离上下文时表现优异。

在之前的平滑方法(如绝对折扣、Katz)中,当高阶模型(如 3-gram)未出现时,会 “后退” 到低阶模型(如 2-gram、1-gram),但低阶模型的概率通常基于词的边际频率(即词的总出现次数)。这种做法存在严重缺陷:

  • 高频虚词(如 “的”“是”)会主导低阶模型:这些词总出现次数极高(边际频率高),但在新的上下文(如 “吃 火箭 ___”)中,它们作为后续词的合理性很低;
  • 低频实词(如 “苹果”“跑步”)被低估:这些词总出现次数可能不高,但能被很多不同的前序词跟随(如 “吃苹果”“买苹果”“洗苹果”),在新上下文中有更强的 “延续能力”。

例如:“的” 在语料中出现 1000 次(边际频率高),但几乎只跟随 “美丽”“我” 等少数前序词;“苹果” 只出现 100 次,但能跟随 “吃”“买”“切” 等 20 个不同前序词。传统低阶模型会给 “的” 更高概率,但在 “吃 火箭 ___” 中,“苹果” 显然更合理。

低阶模型应衡量 “词的延续性”

Kneser-Ney 平滑的革命性改进在于:低阶模型的概率不基于词的总出现次数,而基于词的 “延续性”(即一个词能被多少不同的前序词跟随)

  • 延续性强的词(如 “苹果”):能被更多不同的前序词带动,在新上下文中有更高的合理概率
  • 延续性弱的词(如 “的”):前序词固定,即使总出现次数高,在新上下文中标配概率也较低

在此基础上,Kneser-Ney 保留了绝对折扣的 “高阶模型折扣” 逻辑,形成 “高阶折扣 + 低阶延续性” 的双层平滑框架。
Kneser-Ney 平滑的概率计算分两步:高阶模型折扣低阶延续性后退

Kneser-Ney 平滑的革命性改进在于:低阶模型的概率不基于词的总出现次数,而基于词的 “延续性”(即一个词能被多少不同的前序词“跟随”

  • 被延续性强的词(如 “苹果”):能被更多不同的前序词带动,在新上下文中有更高的合理概率
  • 被延续性弱的词(如 “的”):前序词固定,即使总出现次数高,在新上下文中标配概率也较低
    在此基础上,Kneser-Ney 保留了绝对折扣的 “高阶模型折扣” 逻辑,形成 “高阶折扣 + 低阶延续性” 的双层平滑框架。

Kneser-Ney 平滑的概率计算分两步:高阶模型折扣和低阶延续性后退:

  • 高阶模型(3-gram)的折扣概率对出现过的 3-gram(计数c≥1),先应用固定折扣d(通常取 0.75):
    在这里插入图片描述
    其中:
  • c(⋅)为 n-gram 的出现次数
  • α(w_i−2,w_i−1)是 “后退权重”,用于将高阶模型扣掉的折扣概率分配给低阶模型
  • P_KN (w_i∣w_i−1)是 2-gram 的 Kneser-Ney 概率(递归应用同一逻辑)

后退权重α的计算

α的作用是将高阶模型中 “扣掉的总折扣概率” 分配给低阶模型,公式为:
在这里插入图片描述
其中,N_1 (w_i−2 ,w_i−1)是 “在上下文w_i−2 ,w_i−1下,恰好出现 1 次的后续词总数”(与绝对折扣类似,衡量当前上下文的稀疏性)

低阶模型的 “延续性概率”(关键改进)

当后退到最底层的 1-gram 时,Kneser-Ney 重新定义 1-gram 概率为 “延续概率”,而非边际频率:
在这里插入图片描述

  • 分子:不同前序词的数量(w)表示 “有多少个不同的词u,使得(u,w)在训练数据中出现过”(即 w 被多少前序词带动过)
  • 分母:所有词的 “不同前序词数量” 之和,确保概率和为 1
    Kneser-Ney 平滑的核心是 “用延续性重新定义低阶模型”,解决了传统平滑中高频词主导低阶模型的问题。它通过 “高阶折扣 + 低阶延续性” 的组合,在稀疏数据和新上下文场景中表现远超其他平滑方法,是 n-gram 语言模型中最先进、应用最广泛的平滑技术之一。尽管实现复杂度高于绝对折扣等方法,但其效果优势使其成为工业界的主流选择。

网站公告

今日签到

点亮在社区的每一天
去签到