概率统计:
贝叶斯定理
<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发生的可能性。
- 在贝叶斯定理中,每个名词都有约定俗成的名称:
P(A)是 A 的先验概率,之所以称为"先验"是因为它不考虑任何 B 方面的因素。
P(A|B)是已知 B 发生后 A 的条件概率,也由于得自 B 的取值而被称作 A 的后验概率。
P(B|A)是已知 A 发生后 B 的条件概率,也由于得自 A 的取值而被称作 B 的后验概率。似然概率/相似度
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)是用来衡量两个概率分布之间差异的一种度量方法。让我详细解释:
- 数学定义: 对于两个概率分布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))
-
主要特点:
- 非负性: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
-
实际应用:
- 机器学习中用于衡量模型预测分布与真实分布的差异
- 变分推断中作为优化目标
- 深度学习中用于模型压缩和知识蒸馏
代码实践:
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)) # 对应公式
应用:
-
假设有两个硬币:
- 真实硬币P:正面0.5,反面0.5
- 有偏硬币Q:正面0.7,反面0.3
-
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
)