数学与算法基础-概率统计部分

概率统计:

贝叶斯定理

<math xmlns="http://www.w3.org/1998/Math/MathML"> P ( A ∣ B ) = P ( B ∣ A ) P ( A ) P ( B ) P(A|B) = \frac{P(B|A)P(A)}{P(B)} </math>P(A∣B)=P(B)P(B∣A)P(A)

  • 其中 <math xmlns="http://www.w3.org/1998/Math/MathML"> P ( A ∣ B ) P(A|B) </math>P(A∣B)是在 <math xmlns="http://www.w3.org/1998/Math/MathML"> B B </math>B发生的情况下 <math xmlns="http://www.w3.org/1998/Math/MathML"> A A </math>A发生的可能性。
  • 在贝叶斯定理中,每个名词都有约定俗成的名称:
  1. P(A)是 A 的先验概率,之所以称为"先验"是因为它不考虑任何 B 方面的因素。
  2. P(A|B)是已知 B 发生后 A 的条件概率,也由于得自 B 的取值而被称作 A 的后验概率。
  3. P(B|A)是已知 A 发生后 B 的条件概率,也由于得自 A 的取值而被称作 B 的后验概率。似然概率/相似度
  4. P(B)是 B 的先验概率,也作标淮化常量(normalizing constant)。
  • 按这些术语,贝叶斯定理可表述为:

后验概率 = (相似度 * 先验概率)/标淮化常量

  • 也就是说,后验概率先验概率和相似度的乘积成正比。
  • 另外,比例 <math xmlns="http://www.w3.org/1998/Math/MathML"> P ( B ∣ A ) P ( B ) \frac{P(B|A)}{P(B)} </math>P(B)P(B∣A)也有时被称作标淮相似度(standardised likelihood),Bayes定理可表述为:

后验概率 = 标淮相似度 * 先验概率

  • 条件概率就是事件 A 在另外一个事件 B 已经发生条件下的发生概率。条件概率表示为P(A|B),读作"在 B 发生的条件下 A 发生的概率"。

以下代码就是简单的套公式。

python 复制代码
 def bayes_theorem(prior, likelihood, evidence, normalize=True):
     """贝叶斯定理实现: P(A|B) = P(B|A)P(A)/P(B)
     Args:
         prior: 先验概率 P(A),事件A发生的概率
         likelihood: 似然概率/相似度 P(B|A),在A发生的条件下B发生的概率
         evidence: 标准化常量/证据 P(B),事件B发生的概率
         normalize: 是否归一化,确保概率和为1
     Returns:
         posterior: 后验概率 P(A|B),在B发生的条件下A发生的概率
     Example:
         # 例如:已知一个人感冒的概率(先验)是0.1
         # 感冒的人发烧的概率(似然)是0.8
         # 人发烧的概率(证据)是0.2
         prior = 0.1  # P(感冒)
         likelihood = 0.8  # P(发烧|感冒)
         evidence = 0.2  # P(发烧)
         # 求发烧的人是感冒的概率
         posterior = bayes_theorem(prior, likelihood, evidence)
     """
     # 计算后验概率
     posterior = (likelihood * prior) / evidence
 ​
     # 归一化确保概率和为1
     if normalize and np.sum(posterior) != 0:
         posterior = posterior / np.sum(posterior)
 ​
     return posterior
归一化
  • 在概率论中,所有概率的和必须等于1
  • 有时计算出来的结果可能不是标准的概率(和不等于1)
  • 通过除以总和,我们可以将结果转换为标准概率分布
python 复制代码
 # 假设计算出的posterior是
 posterior = np.array([3, 5, 2])
 print("原始值:", posterior)  # [3, 5, 2]
 print("和:", np.sum(posterior))  # 10
 ​
 # 归一化后
 normalized = posterior / np.sum(posterior)
 print("归一化后:", normalized)  # [0.3, 0.5, 0.2]
 print("归一化后的和:", np.sum(normalized))  # 1.0

极大似然估计

这个代码之后再补充,定义肯定还记得

KL散度

KL散度(Kullback-Leibler Divergence)是用来衡量两个概率分布之间差异的一种度量方法。让我详细解释:

  1. 数学定义: 对于两个概率分布P和Q,它们的KL散度定义为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> D K L ( P ∣ ∣ Q ) = ∑ i P ( i ) log ⁡ ( P ( i ) Q ( i ) ) D_{KL}(P||Q) = \sum_{i} P(i) \log(\frac{P(i)}{Q(i)}) </math>DKL(P∣∣Q)=∑iP(i)log(Q(i)P(i))

  1. 主要特点:

    • 非负性:KL散度总是大于等于0
    • 不对称性: <math xmlns="http://www.w3.org/1998/Math/MathML"> D K L ( P ∣ ∣ Q ) ≠ D K L ( Q ∣ ∣ P ) D_{KL}(P||Q) \neq D_{KL}(Q||P) </math>DKL(P∣∣Q)=DKL(Q∣∣P)
    • 当且仅当两个分布完全相同时,KL散度为0
  2. 实际应用:

    • 机器学习中用于衡量模型预测分布与真实分布的差异
    • 变分推断中作为优化目标
    • 深度学习中用于模型压缩和知识蒸馏

代码实践:

python 复制代码
 @staticmethod
 def kl_divergence(p, q, epsilon=1e-10):
     """计算KL散度 (Kullback-Leibler divergence)
     KL(P||Q) = Σ P(i) * log(P(i)/Q(i))
 ​
     Args:
         p: 真实分布 P
         q: 预测分布 Q
         epsilon: 数值稳定性的小量,避免除0和log(0)
     Returns:
         KL散度值,越小表示两个分布越接近
     """
     # 确保概率和为1,归一化
     # 将输入转换为概率分布。
     p = F.softmax(p, dim=-1)# dim=-1 参数:
                                 # 指定在哪个维度上进行softmax运算
                                 # -1 表示最后一个维度
     q = F.softmax(q, dim=-1)
 ​
     # 添加epsilon避免log(0) 当概率值非常接近0时(比如1e-10或更小),在计算机中可能会被向下舍入为0
     p = p + epsilon 
     q = q + epsilon
 ​
     # 归一化 再次归一化 因为加了epsilon 所以和就不是1了
     p = p / p.sum()
     q = q / q.sum()
 ​
     # 计算KL散度 torch.sum(...) 对所有位置求和
     return torch.sum(p * torch.log(p / q)) # 对应公式

应用:

  1. 假设有两个硬币:

    • 真实硬币P:正面0.5,反面0.5
    • 有偏硬币Q:正面0.7,反面0.3
  2. KL散度可以告诉我们这两个硬币的分布有多不同:

python 复制代码
 # 计算两个硬币分布的KL散度
 p = torch.tensor([0.5, 0.5])  # 真实硬币
 q = torch.tensor([0.7, 0.3])  # 有偏硬币
 kl = Probability.kl_divergence(p, q)
 print(f"KL散度: {kl.item():.4f}")  # 值越大,表示分布差异越大
 ​

交叉熵

交叉熵(Cross Entropy) 是用来衡量两个概率分布之间差异的度量方法,在机器学习中常用作损失函数。

  • KL散度 : <math xmlns="http://www.w3.org/1998/Math/MathML"> K L ( P ∣ ∣ Q ) = Σ P ( i ) ∗ l o g ( P ( i ) / Q ( i ) ) KL(P||Q) = Σ P(i) * log(P(i)/Q(i)) </math>KL(P∣∣Q)=ΣP(i)∗log(P(i)/Q(i)) 衡量两个分布的差异,值为0表示两个分布完全相同

    • 需要p的信息
  • 交叉熵 : <math xmlns="http://www.w3.org/1998/Math/MathML"> H ( P , Q ) = − Σ P ( i ) ∗ l o g ( Q ( i ) ) H(P,Q) = -Σ P(i) * log(Q(i)) </math>H(P,Q)=−ΣP(i)∗log(Q(i)) 常用作损失函数,不需要计算真实分布的熵

    • 只需要q对p的预测
  • 关系 : <math xmlns="http://www.w3.org/1998/Math/MathML"> H ( P , Q ) = H ( P ) + K L ( P ∣ ∣ Q ) H(P,Q) = H(P) + KL(P||Q) </math>H(P,Q)=H(P)+KL(P∣∣Q)

    • 其中 H(P) 是 P 的熵。当 P 是真实分布(固定的)时,最小化交叉熵等价于最小化KL散度。

代码实现

python 复制代码
 @staticmethod
 def custom_cross_entropy(predictions, targets):
     """手动实现交叉熵计算
     H(P,Q) = -Σ P(i) * log(Q(i))
     """
     # 将预测值转换为概率分布 (归一化操作)
     probs = F.softmax(predictions, dim=-1)
 ​
     # 将targets转换为one-hot编码
     num_classes = predictions.size(-1)
     one_hot = F.one_hot(targets, num_classes).float()
 ​
     # 计算交叉熵
     # P(i) 对应 one_hot,即真实分布(标签的one-hot编码)
     # Q(i) 对应 probs,即预测分布(模型输出的概率)
     log_probs = torch.log(probs + 1e-10)  # log(Q(i))
     loss = -torch.sum(one_hot * log_probs, dim=-1)  # -Σ P(i) * log(Q(i))
 ​
     # 返回平均损失
     return torch.mean(loss)
  • num_classes = predictions.size(-1)

    • 获取预测张量的最后一个维度的大小,即类别数量
    • 例如,如果 predictions 形状是 [3, 2],表示3个样本、2个类别,那么 num_classes = 2
  • one_hot = F.one_hot(targets, num_classes).float()

    • 将标签转换为 one-hot 向量
    • 例如,如果 targets = [1, 0, 1],num_classes = 2,转换后会得到:

      css 复制代码
       [[0, 1],  # 标签1转换为[0,1]
        [1, 0],  # 标签0转换为[1,0]
        [0, 1]]  # 标签1转换为[0,1]
    • .float() 将结果转换为浮点数类型

调用函数实现:

python 复制代码
 @staticmethod
 def cross_entropy(predictions, targets, reduction='mean', label_smoothing=0.0):
     """计算交叉熵
     Args:
         predictions: 预测概率分布
         targets: 真实标签
         reduction: 降维方式 ['mean', 'sum', 'none']
         label_smoothing: 标签平滑参数
     Returns:
         交叉熵损失
     """
     return F.cross_entropy(
         predictions, 
         targets,
         reduction=reduction,
         label_smoothing=label_smoothing
     )
二元交叉熵

公式: <math xmlns="http://www.w3.org/1998/Math/MathML"> B C E = − Σ ( y ∗ l o g ( p ) + ( 1 − y ) ∗ l o g ( 1 − p ) ) BCE = -Σ(y*log(p) + (1-y)*log(1-p)) </math>BCE=−Σ(y∗log(p)+(1−y)∗log(1−p))

手动实现:

python 复制代码
 @staticmethod
 def custom_binary_cross_entropy(predictions, targets):
     """手动实现二元交叉熵计算
     BCE = -Σ(y*log(p) + (1-y)*log(1-p))
     """
     epsilon = 1e-10  # 避免log(0)
     predictions = torch.clamp(predictions, epsilon, 1 - epsilon)  # 限制在(0,1)之间
     loss = -(targets * torch.log(predictions) + (1 - targets) * torch.log(1 - predictions))
     return torch.mean(loss)

torch.clamp 是一个用于限制张量值在指定范围内的函数。它的作用是:

  • 如果值小于最小值,就设为最小值
  • 如果值大于最大值,就设为最大值
  • 如果值在范围内,保持不变

调用函数实现:

python 复制代码
 @staticmethod
 def binary_cross_entropy(predictions, targets, reduction='mean'):
     """计算二元交叉熵
     Args:
         predictions: 预测概率 (0-1之间)
         targets: 真实标签 (0或1)
         reduction: 降维方式
     Returns:
         二元交叉熵损失
     """
     return F.binary_cross_entropy(
         predictions,
         targets,
         reduction=reduction
     )
相关推荐
LinkerLin7 分钟前
因果推理的智慧:当大模型遇见因果图谱
算法
小华同学ai14 分钟前
17.1K star!两小时就能训练出专属于自己的个性化小模型,这个开源项目让AI触手可及!
算法·程序员·github
王RuaRua15 分钟前
[数据结构]1.时间复杂度和空间复杂度
c语言·数据结构·算法
愚戏师19 分钟前
C++:泛型算法
开发语言·数据结构·c++·算法
wuqingshun3141591 小时前
蓝桥杯 R格式
c语言·c++·算法·职场和发展·蓝桥杯·r语言
oioihoii1 小时前
C++20 中 `constexpr` 的强大扩展:算法、工具与复数库的变革
算法·c++20
圣保罗的大教堂1 小时前
《算法笔记》9.6小节 数据结构专题(2)并查集 问题 D: More is better
算法
小钊(求职中)1 小时前
Lambda 和 Stream 从 0 到 1,从基础到实战
java·开发语言·后端·算法
phoenix@Capricornus2 小时前
常用序列的离散时间傅里叶变换(DTFT)
图像处理·算法
昂子的博客2 小时前
热门面试题第14天|Leetcode 513找树左下角的值 112 113 路径总和 105 106 从中序与后序遍历序列构造二叉树 (及其扩展形式)以一敌二
java·数据结构·算法·leetcode·职场和发展