概率论深度指南
文档概述
本文档系统梳理概率论的核心知识体系,涵盖从公理化基础到高级概率分布族的完整理论框架,为机器学习与人工智能研究提供坚实的数学基础。本指南特别强调直观理解、代码实现和ML应用场景,让读者不仅”知道”,更能”动手”。
关键词速查表
| 序号 | 关键词 | 英文 | 核心概念 |
|---|---|---|---|
| 1 | 概率空间 | Probability Space | |
| 2 | 随机变量 | Random Variable | |
| 3 | 条件概率 | Conditional Probability | |
| 4 | 贝叶斯定理 | Bayes’ Theorem | |
| 5 | 期望值 | Expectation | |
| 6 | 方差 | Variance | |
| 7 | 协方差 | Covariance | |
| 8 | 大数定律 | Law of Large Numbers | |
| 9 | 中心极限定理 | Central Limit Theorem | |
| 10 | 指数族 | Exponential Family | |
| 11 | 共轭先验 | Conjugate Prior | Beta-Binomial, Dirichlet-Multinomial |
| 12 | 测度论 | Measure Theory | Lebesgue积分基础 |
零、概率论入门:用生活案例讲清楚
0.1 为什么你需要懂概率论?
很多同学学概率论的时候会有个疑问:这玩意儿买菜用得上吗?说实话,买菜确实用不上。但如果你想做机器学习、做数据分析、训练神经网络,概率论就是你的”内力”——不是招式,但没它你使不出任何厉害的武功。
举个实际例子。你训练一个垃圾邮件分类器,输入是邮件内容,输出是”是垃圾邮件”或”不是垃圾邮件”。但模型不可能100%准确,它会给你一个概率:P(垃圾邮件|邮件内容)。这个概率怎么来的?怎么训练这个模型让它的判断更准?这些问题,都需要概率论来回答。
再比如,你想预测明天的股价,或者估计某个用户会不会点击广告——本质上都是概率问题。机器学习的核心,就是用概率论的语言来描述不确定性,然后用数据来学习这个概率分布。
0.2 掷骰子:最直观的概率入门
先从最简单的东西开始:一颗公平的六面骰子。
什么是概率?
掷一次骰子,出现”3”这个事件记作 。在公平骰子下,每个面出现的可能性一样,所以:
这就是古典概型——所有基本事件等可能发生。
用Python验证:
import numpy as np
# 模拟掷骰子10000次
np.random.seed(42)
rolls = np.random.randint(1, 7, size=10000)
# 统计每个数字出现的频率
unique, counts = np.unique(rolls, return_counts=True)
for num, count in zip(unique, counts):
print(f"数字{num}: 出现{count}次, 频率={count/10000:.4f}")
# 输出会接近 1/6 ≈ 0.1667什么是随机变量?
“骰子的点数”就是一个随机变量。它不是随机的,而是用一个数值来表示随机实验的结果。我们用大写字母 来表示随机变量:
离散 vs 连续的区别:
骰子的点数只能取1-6这6个整数,这就是离散随机变量。如果问”某个人身高是多少”,答案可以是1.73米、1.731米、1.7312米……有无穷多种可能,这就是连续随机变量。
关键区别:离散变量可以一个一个数出来,连续变量只能取区间里的任何值。
0.3 抛硬币:理解概率分布
抛一次硬币,正面朝上的概率记作 (公平硬币的话 )。
# 用NumPy模拟抛硬币
np.random.seed(123)
flips = np.random.rand(1000) # 生成[0,1)之间的随机数
heads = (flips < 0.5).astype(int) # 小于0.5算正面
print(f"正面出现次数: {heads.sum()}")
print(f"正面频率: {heads.mean():.4f}") # 应该接近0.5伯努利分布(Bernoulli Distribution):
抛一次硬币就是最简单的伯努利实验:
二项分布(Binomial Distribution):
抛 次硬币,正面出现 次的概率:
这个公式什么意思? 是”从 次中选 次是正面”的选法数量, 是这 次正面的概率, 是剩下 次反面的概率。
from scipy import stats
# 抛10次硬币,恰好5次正面的概率
n, p = 10, 0.5
k = 5
prob = stats.binom.pmf(k, n, p)
print(f"P(X=5) = {prob:.4f}") # ≈ 0.2461
# 绘制二项分布的概率质量函数
import matplotlib.pyplot as plt
k_values = np.arange(0, 11)
probs = stats.binom.pmf(k_values, n, p)
plt.bar(k_values, probs)
plt.xlabel('正面次数 k')
plt.ylabel('概率 P(X=k)')
plt.title('二项分布 B(10, 0.5)')
plt.show()0.4 条件概率:已知信息改变概率
什么是条件概率?
假设你知道今天早上堵车了(事件B),在这个信息下,你迟到的概率(事件A)是多少?这就是条件概率 ——在B发生的条件下A发生的概率。
用文氏图理解:就是在B这个圆里,A和B重叠部分占B的比例。
经典例子:体检结果
假设有一种疾病:
- 患病率(先验概率):(1%的人患病)
- 检测准确率:(患病者99%会检测阳性)
- 误报率:(健康者5%会误报阳性)
如果你检测结果阳性,你真正患病的概率是多少?
# 使用贝叶斯公式计算
P_disease = 0.01
P_positive_given_disease = 0.99
P_positive_given_healthy = 0.05
# 全概率公式
P_positive = (P_positive_given_disease * P_disease +
P_positive_given_healthy * (1 - P_disease))
# 贝叶斯公式
P_disease_given_positive = (P_positive_given_disease * P_disease) / P_positive
print(f"检测阳性时真正患病的概率: {P_disease_given_positive:.4f}")
# 输出约 0.1664 —— 只有16.6%!这个结果很反直觉对吧?检测阳性后,你可能觉得自己多半中标了,但实际上因为患病率很低(1%),假阳性(健康人误报)的影响很大。
直觉陷阱:我们总是高估小概率事件在已知信息下的影响力。条件概率告诉我们,已知部分信息后要重新”校准”你的信念。
一、概率空间与公理化体系
1.1 概率论的三元组结构
现代概率论建立在测度论的基础之上,采用公理化方法构建完整的理论体系。概率空间由三元组 定义。
样本空间 表示所有可能基本结果的集合。
| 实验 | 样本空间 |
|---|---|
| 抛硬币 | 或 |
| 掷骰子 | |
| 测量身高 | (米) |
| 掷两枚骰子 |
σ-代数 是样本空间上可测的子集族,必须满足:
- (包含空集)
- 若 ,则 (对补运算封闭)
- 若 ,则 (对可数并封闭)
σ-代数定义了哪些事件可以被赋予概率。在连续情形(如身高),不是所有子集都有概率——我们只关心”Borel可测”的集合。
概率测度 满足Kolmogorov公理:
| 公理 | 含义 |
|---|---|
| 非负性 | |
| 归一性 | |
| 可数可加性 | ( 两两不相交) |
1.2 条件概率与乘法公式
条件概率定义:
乘法公式(Chain Rule):
对于多个事件:
独立性:
事件 与 独立,当且仅当:
这也等价于(当 时):
独立性意味着知道B发生,不会改变A的概率。
# 验证独立性
# 抛两枚硬币:A = "第一枚正面",B = "第二枚正面"
# 这两个事件独立吗?
p_A = 0.5
p_B = 0.5
p_A_and_B = 0.25 # 两枚都正面的概率
is_independent = np.isclose(p_A_and_B, p_A * p_B)
print(f"独立吗?{is_independent}") # True1.3 全概率公式
全概率公式是贝叶斯定理的”地基”:
其中 是样本空间的一个划分(互不相交、合并为整个空间)。
直观理解:要计算事件B发生的总概率,就考虑B在各种”场景”下发生的概率,然后按场景的可能性加权求和。
# 全概率公式的例子:混合盒子问题
# 盒子A有80%红球、20%蓝球;盒子B有40%红球、60%蓝球
# 随机选一个盒子(各50%概率),从中摸一个球
P_box_A = 0.5
P_box_B = 0.5
P_red_given_A = 0.8
P_red_given_B = 0.4
# 摸到红球的概率
P_red = P_red_given_A * P_box_A + P_red_given_B * P_box_B
print(f"摸到红球的概率: {P_red:.2f}") # 0.6二、贝叶斯定理与统计推断基础
2.1 贝叶斯定理——从怀疑到确信
贝叶斯定理是概率论中最重要的公式之一。用一句话概括:当新的证据出现时,如何更新我们对世界的看法?
定理导出:
从条件概率出发:
把 用全概率公式展开,得到贝叶斯定理:
在统计推断中的形式:
| 术语 | 含义 | 通俗理解 |
|---|---|---|
| 先验概率(Prior) | 在看到数据之前,我们的信念是什么? | |
| 似然函数(Likelihood) | 如果参数真的是 ,这个数据出现的概率有多大? | |
| 后验概率(Posterior) | 看到数据后,我们应该相信什么? | |
| 边际似然(Evidence) | 这个数据出现的总概率(归一化常数) |
生活类比:看病的过程就是贝叶斯推断。
- 先验:你平时生病的概率(大多数人不太容易生病)
- 似然:如果你真的病了,某种症状出现的概率
- 后验:有了这个症状后,你真正生病的概率
医生看病,本质上就是在做贝叶斯推断!
2.2 为什么贝叶斯思维改变了我们思考问题的方式?
频率派 vs 贝叶斯派:
- 频率派( Frequentist):概率是长期重复实验的频率。参数是固定但未知的,我们需要用数据去”点估计”它。
- 贝叶斯派(Bayesian):概率是信念的程度。参数本身是随机的,我们用概率分布来描述它。
贝叶斯思维的核心:
- 一切皆是概率:不仅数据有随机性,未知的参数也可以用概率分布来描述。
- 先验的重要性:我们可以把领域知识或历史经验编码成先验分布。
- 更新信念:看到新数据后,用贝叶斯公式更新我们对参数的认知。
实际应用场景:
| 场景 | 贝叶斯思维的应用 |
|---|---|
| 垃圾邮件过滤 | 先验:大多数邮件不是垃圾;似然:邮件中出现某些词的频率;后验:给定邮件内容,判断是垃圾邮件的概率 |
| A/B测试 | 先验:对新版本效果的信念;似然:实验数据;后验:新版本真的更好的概率 |
| 医学诊断 | 先验:疾病患病率;似然:症状在有病/无病时的表现;后验:给定症状,患者患病的概率 |
2.3 共轭先验实战:Beta-Binomial共轭
在贝叶斯推断中,后验分布通常没有解析形式。但有一种特殊情况——共轭先验——可以让后验分布保持和先验相同的形式。
Beta-Binomial共轭是最经典的例子:
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
# 场景:估计一枚硬币正面朝上的概率 p
# 我们观测到 n 次抛掷,其中 k 次正面
# 先验:Beta(α, β) 分布
# Beta分布的概率密度函数
def beta_pdf(x, alpha, beta):
return stats.beta.pdf(x, alpha, beta)
# 可视化不同先验
x = np.linspace(0, 1, 100)
priors = [(1, 1), (2, 2), (10, 10), (1, 5), (5, 1)]
labels = ['Beta(1,1) 无信息', 'Beta(2,2) 轻度不确定',
'Beta(10,10) 强烈相信接近0.5', 'Beta(1,5) 偏向反面', 'Beta(5,1) 偏向正面']
plt.figure(figsize=(10, 6))
for (a, b), label in zip(priors, labels):
plt.plot(x, beta_pdf(x, a, b), label=label, linewidth=2)
plt.xlabel('p (正面概率)')
plt.ylabel('概率密度')
plt.title('Beta分布作为硬币概率的先验')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
print("""
# 共轭先验的更新公式
# 先验: p ~ Beta(α, β)
# 数据: k 次正面, n-k 次反面
# 后验: p | data ~ Beta(α + k, β + n - k)
""")实际计算示例:
def bayesian_coin_flip_update(alpha_prior, beta_prior, n_flips, k_heads):
"""
贝叶斯更新:给定抛掷数据,更新硬币正面概率的分布
参数:
alpha_prior, beta_prior: 先验Beta分布参数
n_flips: 抛掷总次数
k_heads: 正面次数
返回:
后验Beta分布参数
"""
alpha_post = alpha_prior + k_heads
beta_post = beta_prior + (n_flips - k_heads)
return alpha_post, beta_post
# 例子:抛10次硬币,7次正面
n_flips = 10
k_heads = 7
# 使用无信息先验 Beta(1, 1)
alpha_post, beta_post = bayesian_coin_flip_update(1, 1, n_flips, k_heads)
print(f"后验分布: Beta({alpha_post}, {beta_post})")
# 计算后验均值和95%可信区间
posterior_mean = alpha_post / (alpha_post + beta_post)
ci_low, ci_high = stats.beta.ppf([0.025, 0.975], alpha_post, beta_post)
print(f"后验均值: {posterior_mean:.4f}")
print(f"95%可信区间: [{ci_low:.4f}, {ci_high:.4f}]")
# 对比:频率派估计(最大似然估计)
mle_estimate = k_heads / n_flips
print(f"最大似然估计: {mle_estimate:.4f}")为什么共轭先验有用?
- 计算简便:后验分布有解析形式,不需要数值近似
- 可解释性强:参数更新的意义很清晰(加数据)
- 递推更新:新来一批数据,直接在当前后验上加就行
2.4 从先验/后验/似然理解ML模型
在机器学习中,先验-似然-后验的框架无处不在:
线性回归的贝叶斯视角:
- 先验 :我们对系数 的先验信念(通常假设 )
- 似然 :给定系数后,数据出现的概率
- 后验 :看到数据后,系数应该是什么分布?
正则化的本质:
L2正则化(岭回归)对应的是系数的高斯先验:
L1正则化(Lasso)对应的是系数的拉普拉斯先验:
所以,正则化项在贝叶斯框架下就是先验对参数空间的惩罚。
三、大数定律与中心极限定理——为什么正态分布无处不在?
3.1 大数定律——重复出真知
弱大数定律(辛钦大数定律):
设 是独立同分布的随机变量,,则:
翻译成人话:样本均值会越来越接近真实的期望值。
蒙特卡洛方法就是建立在这个定律上的:
# 用蒙特卡洛方法估算圆周率 π
np.random.seed(42)
n_samples = 1000000
# 在 [0,1]×[0,1] 正方形内随机投点
x = np.random.uniform(0, 1, n_samples)
y = np.random.uniform(0, 1, n_samples)
# 计算落在单位圆内的比例
inside_circle = (x**2 + y**2) <= 1
pi_estimate = 4 * inside_circle.sum() / n_samples
print(f"π 的蒙特卡洛估计: {pi_estimate:.6f}")
print(f"真实 π 值: {np.pi:.6f}")
print(f"误差: {abs(pi_estimate - np.pi):.6f}")为什么这能估算π?因为单位圆面积是 ,落在圆内的概率也是 ,用大数定律,频率趋向概率。
强大数定律:样本均值几乎必然(almost surely)收敛到期望。比弱大数定律更强——不是”依概率收敛”,而是”几乎所有实现都收敛”。
3.2 中心极限定理——概率论最神奇的定理
中心极限定理(CLT):
设 是独立同分布的随机变量,,,则:
其中 是标准正态分布的CDF。
用代码直观理解CLT:
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# 不同的原始分布
distributions = {
'均匀分布': lambda n: np.random.uniform(0, 1, n),
'二项分布': lambda n: np.random.binomial(10, 0.5, n),
'泊松分布': lambda n: np.random.poisson(5, n),
'指数分布': lambda n: np.random.exponential(1, n),
'离散分布 [0,1,3]': lambda n: np.random.choice([0, 1, 3], n, p=[0.2, 0.5, 0.3]),
'柯西分布': lambda n: np.random.standard_cauchy(n),
}
for ax, (name, dist_func) in zip(axes.flat, distributions.items()):
sample_means = []
n_experiments = 1000
sample_size = 100
for _ in range(n_experiments):
sample = dist_func(sample_size)
sample_means.append(np.mean(sample))
# 标准化
sample_means = np.array(sample_means)
# 假设知道真实均值(对某些分布用中位数估计)
if name == '柯西分布':
mu_estimate = np.median(sample_means)
else:
mu_estimate = np.mean(sample_means)
std_estimate = np.std(sample_means)
standardized = (sample_means - mu_estimate) / (std_estimate / np.sqrt(sample_size))
ax.hist(standardized, bins=50, density=True, alpha=0.7, label='样本均值的标准化')
x = np.linspace(-4, 4, 100)
ax.plot(x, stats.norm.pdf(x), 'r-', lw=2, label='标准正态分布')
ax.set_title(f'{name}\n(样本量={sample_size})')
ax.legend()
ax.set_xlim(-4, 4)
plt.suptitle('中心极限定理:无论原始分布是什么,样本均值都趋向正态分布', fontsize=14)
plt.tight_layout()
plt.show()CLT为什么重要?
- 解释了正态分布的普遍性:大量微小独立因素叠加的结果就是正态分布
- 统计推断的理论基础:置信区间、假设检验都依赖正态性假设
- 近似计算:即使不知道原始分布,也可以用正态分布近似样本均值的分布
3.3 正态分布——统计学之王
正态分布(高斯分布):
# 绘制正态分布
x = np.linspace(-5, 5, 1000)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 不同均值
ax1 = axes[0]
for mu in [-1, 0, 1]:
y = stats.norm.pdf(x, mu, 1)
ax1.plot(x, y, label=f'μ={mu}, σ=1', lw=2)
ax1.set_title('不同均值,相同方差')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 不同方差
ax2 = axes[1]
for sigma in [0.5, 1, 2]:
y = stats.norm.pdf(x, 0, sigma)
ax2.plot(x, y, label=f'μ=0, σ={sigma}', lw=2)
ax2.set_title('相同均值,不同方差')
ax2.legend()
ax2.grid(True, alpha=0.3)
plt.show()68-95-99.7法则:
| 范围 | 包含概率 |
|---|---|
| 约 68.3% | |
| 约 95.5% | |
| 约 99.7% |
# 验证68-95-99.7法则
samples = np.random.randn(1000000)
within_1sigma = (samples > -1) & (samples < 1)
within_2sigma = (samples > -2) & (samples < 2)
within_3sigma = (samples > -3) & (samples < 3)
print(f"在 μ±1σ 范围内: {within_1sigma.mean():.4f} (理论: 0.6827)")
print(f"在 μ±2σ 范围内: {within_2sigma.mean():.4f} (理论: 0.9545)")
print(f"在 μ±3σ 范围内: {within_3sigma.mean():.4f} (理论: 0.9973)")四、随机变量与概率分布
4.1 常用离散分布速查
| 分布 | 记号 | PMF | 适用场景 | sklearn/PyTorch |
|---|---|---|---|---|
| 伯努利 | 抛一次硬币 | — | ||
| 二项 | n次实验的成功次数 | scipy.stats.binom | ||
| 几何 | 首次成功需要的次数 | scipy.stats.geom | ||
| 泊松 | 稀有事件计数 | scipy.stats.poisson |
泊松分布的代码示例:
# 泊松分布:描述稀有事件在固定时间/空间内的发生次数
# 例子:一个网站每小时平均收到50次请求
lambda_param = 50 # 平均每小时50次请求
# 计算各种请求次数的概率
k_values = np.arange(30, 71)
probs = stats.poisson.pmf(k_values, lambda_param)
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.bar(k_values, probs)
plt.xlabel('请求次数 k')
plt.ylabel('概率 P(X=k)')
plt.title(f'泊松分布 Poisson(λ={lambda_param})')
# 累积分布
plt.subplot(1, 2, 2)
cdf = stats.poisson.cdf(k_values, lambda_param)
plt.plot(k_values, cdf)
plt.xlabel('请求次数 k')
plt.ylabel('累积概率 P(X≤k)')
plt.title('泊松分布的CDF')
plt.tight_layout()
plt.show()
# 常见计算
print(f"P(X=50) = {stats.poisson.pmf(50, lambda_param):.4f}")
print(f"P(X≤40) = {stats.poisson.cdf(40, lambda_param):.4f}")
print(f"P(X≥60) = {1 - stats.poisson.cdf(59, lambda_param):.4f}")4.2 常用连续分布速查
| 分布 | 记号 | 适用场景 | |
|---|---|---|---|
| 均匀 | 没有任何先验偏好 | ||
| 正态 | 钟形曲线 | 大多数连续测量值 | |
| 指数 | 等待时间、衰减 | ||
| Gamma | 正态的共轭先验 | ||
| Beta | [0,1]区间上的概率 | ||
| t分布 | 重尾正态 | 小样本估计 |
指数分布——无记忆性的等待时间:
# 指数分布:λ 是率参数(平均等待时间 = 1/λ)
lambda_param = 2 # 平均每分钟来2个顾客
# PDF
x = np.linspace(0, 4, 1000)
pdf = stats.expon.pdf(x, scale=1/lambda_param) # scale = 1/λ
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(x, pdf, 'b-', lw=2)
plt.xlabel('时间 x (分钟)')
plt.ylabel('概率密度 f(x)')
plt.title(f'指数分布 Exp(λ={lambda_param})')
plt.grid(True, alpha=0.3)
# 验证无记忆性
plt.subplot(1, 2, 2)
# P(T > s + t | T > s) = P(T > t)
t = 0.5
s_values = [0, 0.5, 1.0, 1.5]
for s in s_values:
# 条件概率 P(T > s+t | T > s) = P(T > s+t) / P(T > s)
# = exp(-λ(s+t)) / exp(-λs) = exp(-λt)
conditional_prob = np.exp(-lambda_param * t)
print(f"已等待 {s} 分钟,再等 {t} 分钟总时间超过 {s+t} 分钟的条件概率: {conditional_prob:.4f}")
plt.text(0.5, 0.8, '无记忆性:无论已经等了多久\n再等待t时间的概率都一样',
transform=plt.gca().transAxes, fontsize=12,
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
plt.show()4.3 概率分布速查表:常用分布在sklearn/PyTorch中
| 分布 | NumPy | SciPy | PyTorch | 用途 |
|---|---|---|---|---|
| 正态 | np.random.normal | stats.norm | torch.distributions.Normal | 回归、VAE |
| 均匀 | np.random.uniform | stats.uniform | torch.distributions.Uniform | 采样初始化 |
| 伯努利 | np.random.binomial(1,p) | stats.bernoulli | torch.distributions.Bernoulli | 二分类 |
| 类别 | np.random.choice | — | torch.distributions.Categorical | 多分类 |
| Beta | scipy.stats.beta | stats.beta | torch.distributions.Beta | 概率参数 |
| 指数 | np.random.exponential | stats.expo | torch.distributions.Exponential | 率参数 |
import torch
# PyTorch 分布示例
# 定义一个正态分布
loc = torch.tensor([0.0, 0.0])
scale = torch.tensor([1.0, 0.5])
normal_dist = torch.distributions.Normal(loc, scale)
# 采样
samples = normal_dist.sample((1000,)) # shape: (1000, 2)
print(f"采样形状: {samples.shape}")
# 计算对数概率
x = torch.tensor([0.5, 0.5])
log_prob = normal_dist.log_prob(x)
print(f"log P(x): {log_prob}")
# VAE中重参数化采样的典型用法
def reparameterized_sample(mu, log_var):
"""重参数化技巧:让采样操作可导"""
std = torch.exp(0.5 * log_var)
eps = torch.randn_like(std) # 标准正态采样
return mu + eps * std五、数字特征:期望、方差、协方差
5.1 期望值——概率加权的平均值
离散情形:
连续情形:
期望的本质:用概率加权的”平均”值。如果重复实验无数次,长期平均就是期望。
# 掷骰子的期望值
# 方法1:理论计算
X_values = np.array([1, 2, 3, 4, 5, 6])
probabilities = np.array([1/6] * 6)
E_X = np.sum(X_values * probabilities)
print(f"理论期望: {E_X}") # 应该是 3.5
# 方法2:蒙特卡洛
rolls = np.random.randint(1, 7, size=1000000)
E_X_estimate = np.mean(rolls)
print(f"蒙特卡洛估计: {E_X_estimate:.4f}")期望的性质:
- 线性性:
- 对独立变量:
# 验证线性性
np.random.seed(42)
X = np.random.randn(1000)
Y = np.random.randn(1000)
a, b = 2, -3
lhs = np.mean(a * X + b * Y) # E[aX + bY]
rhs = a * np.mean(X) + b * np.mean(Y) # aE[X] + bE[Y]
print(f"E[aX + bY] = {lhs:.4f}")
print(f"aE[X] + bE[Y] = {rhs:.4f}")
print(f"线性性成立: {np.isclose(lhs, rhs)}")5.2 方差与标准差——衡量”散”的程度
方差:
标准差:,与原变量量纲相同
# 计算方差
X = np.array([1, 2, 3, 4, 5, 6])
# 方法1:定义公式
mean_X = np.mean(X)
var_X = np.mean((X - mean_X)**2)
print(f"定义公式: Var(X) = {var_X:.4f}")
# 方法2:简化公式 E[X²] - (E[X])²
var_X_simple = np.mean(X**2) - np.mean(X)**2
print(f"简化公式: Var(X) = {var_X_simple:.4f}")
# 方法3:NumPy内置
var_X_numpy = np.var(X) # 默认ddof=0(总体方差)
var_X_sample = np.var(X, ddof=1) # 样本方差
print(f"NumPy 总体方差: {var_X_numpy:.4f}")
print(f"NumPy 样本方差: {var_X_sample:.4f}")注意:NumPy默认计算的是总体方差(除以 ),统计学中通常用样本方差(除以 ,ddof=1)。样本方差是对总体方差的无偏估计。
方差的性质:
- 对独立变量:
- 常数不贡献方差:
# 验证:独立变量方差的叠加性
np.random.seed(42)
X = np.random.randn(10000) * 2 + 1 # N(1, 4)
Y = np.random.randn(10000) * 3 - 2 # N(-2, 9)
Z = X + Y
print(f"Var(X) = {np.var(X):.4f} (理论: 4)")
print(f"Var(Y) = {np.var(Y):.4f} (理论: 9)")
print(f"Var(X+Y) = {np.var(Z):.4f} (理论: 13)")5.3 协方差与相关系数——衡量两个变量的”同涨同跌”
协方差:
相关系数(Pearson相关系数):
# 生成相关数据
np.random.seed(42)
n = 1000
# 正相关
X_pos = np.random.randn(n)
Y_pos = X_pos + 0.5 * np.random.randn(n)
# 负相关
X_neg = np.random.randn(n)
Y_neg = -0.7 * X_neg + 0.5 * np.random.randn(n)
# 不相关
X_ind = np.random.randn(n)
Y_ind = np.random.randn(n)
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for ax, (x, y, title) in zip(axes, [
(X_pos, Y_pos, f'正相关 ρ ≈ {np.corrcoef(x, y)[0,1]:.2f}'),
(X_neg, Y_neg, f'负相关 ρ ≈ {np.corrcoef(x, y)[0,1]:.2f}'),
(X_ind, Y_ind, f'不相关 ρ ≈ {np.corrcoef(x, y)[0,1]:.2f}')
]):
ax.scatter(x, y, alpha=0.5, s=10)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_title(title)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()协方差矩阵:
对于多维变量 :
# 协方差矩阵
np.random.seed(42)
n = 1000
# 生成二元正态分布数据
mean = [0, 0]
cov = [[1, 0.7], [0.7, 1]] # 协方差矩阵
X = np.random.multivariate_normal(mean, cov, n)
# 计算协方差矩阵
cov_matrix = np.cov(X.T)
print("协方差矩阵:")
print(cov_matrix)
# 可视化
plt.scatter(X[:, 0], X[:, 1], alpha=0.5, s=10)
plt.xlabel('X₁')
plt.ylabel('X₂')
plt.title(f'二元正态分布 (ρ = {cov_matrix[0,1]/np.sqrt(cov_matrix[0,0]*cov_matrix[1,1]):.2f})')
# 绘制置信椭圆
from matplotlib.patches import Ellipse
def confidence_ellipse(ax, mean, cov, n_std=2.0, **kwargs):
"""绘制协方差椭圆的置信区间"""
pearson = cov[0, 1] / np.sqrt(cov[0, 0] * cov[1, 1])
ell_radius_x = np.sqrt(1 + pearson)
ell_radius_y = np.sqrt(1 - pearson)
ellipse = Ellipse((0, 0), width=ell_radius_x * 2, height=ell_radius_y * 2, **kwargs)
scale_x = np.sqrt(cov[0, 0]) * n_std
mean_x, mean_y = mean
transf = (plt.matplotlib.transforms.Affine2D()
.rotate_deg(45)
.scale(scale_x, np.sqrt(cov[1, 1]) * n_std)
.translate(mean_x, mean_y))
ellipse.set_transform(transf + ax.transData)
return ax.add_patch(ellipse)
confidence_ellipse(plt.gca(), [0, 0], cov_matrix, n_std=1,
edgecolor='red', linestyle='--', fill=False, linewidth=2, label='1σ')
confidence_ellipse(plt.gca(), [0, 0], cov_matrix, n_std=2,
edgecolor='blue', linestyle='--', fill=False, linewidth=2, label='2σ')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()警告:相关不等于因果! 只说明两个变量”同涨同跌”,不意味着X导致了Y,或者Y导致了X。
六、最大似然估计MLE——从概率论到参数学习
6.1 MLE的核心思想
最大似然估计(Maximum Likelihood Estimation)的思想很简单:找到让观测数据出现的概率(似然)最大的参数值。
换句话说:“既然这批数据已经出现了,那最合理的参数应该是使这批数据出现概率最大的那个”。
似然函数:
通常我们最大化对数似然(因为乘积变加法,好处理):
6.2 MLE的代码实战
例1:估计硬币正面概率
# 观测数据:抛10次硬币,7次正面
n = 10
k = 7 # 正面次数
# 似然函数 L(p) = C(n,k) * p^k * (1-p)^(n-k)
# 取对数后:ℓ(ρ) = log C + k*log(p) + (n-k)*log(1-p)
p_values = np.linspace(0.01, 0.99, 100)
log_likelihood = k * np.log(p_values) + (n - k) * np.log(1 - p_values)
# 找到MLE
p_mle = k / n # 对二项分布,MLE就是样本均值
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.plot(p_values, log_likelihood, 'b-', lw=2)
plt.axvline(p_mle, color='r', linestyle='--', label=f'MLE p̂ = {p_mle}')
plt.xlabel('p')
plt.ylabel('对数似然 ℓ(p)')
plt.title('对数似然函数')
plt.legend()
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
likelihood = p_values**k * (1 - p_values)**(n - k)
plt.plot(p_values, likelihood, 'g-', lw=2)
plt.axvline(p_mle, color='r', linestyle='--', label=f'MLE p̂ = {p_mle}')
plt.xlabel('p')
plt.ylabel('似然 L(p)')
plt.title('似然函数')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"最大似然估计: p̂ = {p_mle}")例2:估计正态分布参数
# 观测数据:从正态分布中采样
np.random.seed(42)
true_mu, true_sigma = 5, 2
data = np.random.normal(true_mu, true_sigma, 100)
# 对数似然函数
def log_likelihood_normal(data, mu, sigma):
n = len(data)
return -n/2 * np.log(2*np.pi) - n*np.log(sigma) - np.sum((data - mu)**2) / (2*sigma**2)
# 可视化对数似然
mu_values = np.linspace(3, 7, 100)
sigma_values = np.linspace(1, 4, 100)
LL = np.zeros((len(sigma_values), len(mu_values)))
for i, sigma in enumerate(sigma_values):
for j, mu in enumerate(mu_values):
LL[i, j] = log_likelihood_normal(data, mu, sigma)
# 绘制等高线图
plt.figure(figsize=(10, 8))
contour = plt.contour(mu_values, sigma_values, LL, levels=20)
plt.clabel(contour, inline=True, fontsize=8)
# 标记真实值和MLE
plt.scatter([true_mu], [true_sigma], c='red', s=200, marker='*', label=f'真实值: μ={true_mu}, σ={true_sigma}')
mu_mle = np.mean(data)
sigma_mle = np.std(data, ddof=1)
plt.scatter([mu_mle], [sigma_mle], c='blue', s=200, marker='x', label=f'MLE: μ̂={mu_mle:.2f}, σ̂={sigma_mle:.2f}')
plt.xlabel('μ (均值)')
plt.ylabel('σ (标准差)')
plt.title('正态分布对数似然等高线图')
plt.legend()
plt.colorbar(contour, label='对数似然')
plt.show()
print(f"真实参数: μ={true_mu}, σ={true_sigma}")
print(f"MLE估计: μ̂={mu_mle:.4f}, σ̂={sigma_mle:.4f}")6.3 MLE在机器学习中的角色
逻辑回归的MLE:
逻辑回归本质上是在做MLE。给定输入 ,输出 ,假设:
似然函数:
最大化似然等价于最小化交叉熵损失:
所以,训练模型 = MLE = 最小化损失函数,它们是同一回事!
七、蒙特卡洛方法实战——用随机采样估算一切
7.1 蒙特卡洛方法的基本思想
蒙特卡洛方法的核心思想是:用随机采样的均值来估计期望值。
7.2 蒙特卡洛积分
# 蒙特卡洛积分:计算 π
np.random.seed(42)
N = 1000000
# 方法:在单位正方形内随机投点
x = np.random.uniform(-1, 1, N)
y = np.random.uniform(-1, 1, N)
# 计算落在单位圆内的比例
inside = (x**2 + y**2) <= 1
pi_estimate = 4 * inside.sum() / N
print(f"蒙特卡洛估计: π ≈ {pi_estimate:.6f}")
print(f"真实值: π = {np.pi:.6f}")
print(f"绝对误差: {abs(pi_estimate - np.pi):.6f}")
# 收敛速度:画出估计值随样本量的变化
sample_sizes = [10, 100, 1000, 10000, 100000, 1000000]
estimates = []
for n in sample_sizes:
x = np.random.uniform(-1, 1, n)
y = np.random.uniform(-1, 1, n)
inside = (x**2 + y**2) <= 1
estimates.append(4 * inside.sum() / n)
plt.figure(figsize=(10, 5))
plt.semilogx(sample_sizes, estimates, 'bo-', label='蒙特卡洛估计')
plt.axhline(np.pi, color='r', linestyle='--', label=f'真实值 π = {np.pi:.6f}')
plt.xlabel('样本量 N (对数尺度)')
plt.ylabel('π 的估计值')
plt.title('蒙特卡洛方法的收敛性')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()7.3 重要性采样
当直接采样困难时,重要性采样(Importance Sampling)引入一个容易采样的分布 :
其中 是重要性权重。
# 重要性采样示例:估计 E[exp(-X)] 其中 X ~ N(0,1)
# 这个积分没有解析解
from scipy import stats
np.random.seed(42)
# 目标分布 p(x) 和提议分布 q(x)
p = stats.norm(0, 1) # 标准正态
q = stats.norm(2, 1.5) # 选择一个偏移的正态分布作为提议
N = 100000
x_q = q.rvs(N) # 从 q 采样
# 计算重要性权重
log_weights = p.logpdf(x_q) - q.logpdf(x_q)
weights = np.exp(log_weights)
weights = weights / weights.sum() # 归一化
# 估计 E[exp(-X)]
f = np.exp(-x_q) # f(x) = exp(-x)
estimate = np.sum(weights * f)
# 直接蒙特卡洛(从 p 采样)作为对比
x_p = p.rvs(N)
direct_estimate = np.mean(np.exp(-x_p))
print(f"重要性采样估计: {estimate:.6f}")
print(f"直接蒙特卡洛估计: {direct_estimate:.6f}")
# 理论值(通过数值积分)
from scipy import integrate
true_value, _ = integrate.quad(lambda x: np.exp(-x) * p.pdf(x), -np.inf, np.inf)
print(f"理论值: {true_value:.6f}")7.4 马尔可夫链蒙特卡洛(MCMC)
当高维积分难以计算时(如贝叶斯后验),MCMC是常用工具。
# Metropolis-Hastings 算法简明实现
def metropolis_hastings(target_log_pdf, proposal_std=1, n_samples=10000, burn_in=1000):
"""
Metropolis-Hastings MCMC采样器
参数:
target_log_pdf: 目标分布的对数概率密度函数
proposal_std: 提议分布(正态)的标准差
n_samples: 样本数量
burn_in: 预热期(丢弃前burn_in个样本)
"""
samples = []
x_current = 0 # 初始值
for i in range(n_samples + burn_in):
# 提议
x_proposed = np.random.normal(x_current, proposal_std)
# 计算接受概率
log_alpha = target_log_pdf(x_proposed) - target_log_pdf(x_current)
# Metropolis 接受准则
if np.log(np.random.rand()) < log_alpha:
x_current = x_proposed
# 丢弃预热期,保存剩余样本
if i >= burn_in:
samples.append(x_current)
return np.array(samples)
# 示例:从 Beta(2, 5) 分布采样
def beta_log_pdf(x, alpha=2, beta=5):
if x <= 0 or x >= 1:
return -np.inf
return (alpha - 1) * np.log(x) + (beta - 1) * np.log(1 - x)
samples = metropolis_hastings(beta_log_pdf, proposal_std=0.5, n_samples=50000, burn_in=5000)
# 可视化
x = np.linspace(0, 1, 1000)
true_pdf = stats.beta.pdf(x, 2, 5)
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.hist(samples, bins=50, density=True, alpha=0.7, label='MCMC样本')
plt.plot(x, true_pdf, 'r-', lw=2, label='真实密度 Beta(2,5)')
plt.xlabel('x')
plt.ylabel('概率密度')
plt.title('Metropolis-Hastings 采样效果')
plt.legend()
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
plt.plot(samples[:500], 'b-', lw=0.5, alpha=0.7)
plt.xlabel('迭代次数')
plt.ylabel('x')
plt.title('马尔可夫链轨迹(前500步)')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 检查自相关(判断混合效果)
from scipy import signal
def autocorrelation(x, lag):
return np.corrcoef(x[:-lag], x[lag:])[0, 1]
lags = np.arange(1, 100)
acs = [autocorrelation(samples, lag) for lag in lags]
plt.figure(figsize=(10, 4))
plt.plot(lags, acs)
plt.xlabel('滞后 (Lag)')
plt.ylabel('自相关')
plt.title('样本自相关函数')
plt.axhline(0, color='k', linestyle='--')
plt.grid(True, alpha=0.3)
plt.show()八、新手易错点:概率论中的直觉陷阱
8.1 独立 ≠ 不相关
陷阱:相关(协方差为零)不等于独立!
# 经典反例:X ~ Uniform(-1, 1), Y = X²
# Cov(X, Y) = 0 但 X 和 Y 明显不独立
np.random.seed(42)
X = np.random.uniform(-1, 1, 10000)
Y = X**2
cov = np.cov(X, Y)[0, 1]
corr = np.corrcoef(X, Y)[0, 1]
print(f"协方差: {cov:.6f}")
print(f"相关系数: {corr:.6f}")
print(f"Cov = 0 但 X 和 Y 独立吗?{'是' if cov == 0 else '否'}(明显不是!)")
# 验证:P(Y > 0.5 | X > 0) ≠ P(Y > 0.5)
p_y_given_x = np.mean(Y[X > 0] > 0.5)
p_y = np.mean(Y > 0.5)
print(f"P(Y > 0.5) = {p_y:.4f}")
print(f"P(Y > 0.5 | X > 0) = {p_y_given_x:.4f}")
print("说明 Y 和 X 条件独立?", "是" if np.isclose(p_y, p_y_given_x) else "否")8.2 赌徒谬误
陷阱:认为随机事件会”自我修正”,之前发生太多次”正面”会导致接下来更可能出”反面”。
但硬币没有记忆!每次抛掷都是独立的。
# 模拟10000次抛硬币,连续出现10次正面后,第11次正面还是反面的概率?
np.random.seed(42)
# 理论上,每次抛掷正面概率都是0.5
# 但人的直觉会认为第11次更可能是反面
# 模拟:找到连续10次正面的序列,看之后第11次是什么
n_simulations = 100000
next_flip_after_10_heads = []
for _ in range(n_simulations):
flips = np.random.randint(0, 2, 25)
for i in range(len(flips) - 10):
if np.all(flips[i:i+10] == 1):
next_flip_after_10_heads.append(flips[i+10])
break
next_flip = np.array(next_flip_after_10_heads)
print(f"连续10次正面后,第11次正面的频率: {np.mean(next_flip):.4f}")
print("直觉会认为应该小于0.5,但实际应该接近0.5!")8.3 条件概率的顺序效应
陷阱:。混淆条件概率的前后顺序是最常见的错误之一。
# 再看那个体检的例子
# P(患病|阳性) vs P(阳性|患病)
# P(阳性|患病) = 0.99 (检测对病人的准确率)
// P(患病|阳性) ≠ 0.99!
P_disease = 0.01
P_positive_given_disease = 0.99
P_positive_given_healthy = 0.05
P_disease_given_positive = (P_positive_given_disease * P_disease) / (
P_positive_given_disease * P_disease + P_positive_given_healthy * (1 - P_disease))
print(f"P(阳性|患病) = {P_positive_given_disease}")
print(f"P(患病|阳性) = {P_disease_given_positive:.4f}")
print("\n这两个概率差别巨大!特别是当疾病很罕见时。")8.4 期望的线性 vs 方差的非线性
陷阱:对独立变量, 成立,但 要求X和Y独立。
# 构造一个反例:Cov(X,Y) ≠ 0
np.random.seed(42)
X = np.random.randn(10000)
Y = 2 * X + np.random.randn(10000) * 0.1 # Y 与 X 相关
print(f"Var(X) = {np.var(X):.4f}")
print(f"Var(Y) = {np.var(Y):.4f}")
print(f"Var(X + Y) = {np.var(X + Y):.4f}")
print(f"Var(X) + Var(Y) = {np.var(X) + np.var(Y):.4f}")
print("\nVar(X+Y) ≠ Var(X) + Var(Y) 因为 X 和 Y 不独立!")8.5 大数定律的收敛速度
陷阱:大数定律说”样本均值趋向期望”,但没说需要多少样本才能接近!
# 演示:不同样本量下,样本均值的波动
np.random.seed(42)
sample_means = []
for n in [10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000]:
samples = np.random.uniform(0, 1, n)
sample_means.append(np.mean(samples))
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.semilogx([10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000],
sample_means, 'bo-')
plt.axhline(0.5, color='r', linestyle='--', label='真实期望 0.5')
plt.xlabel('样本量 (对数)')
plt.ylabel('样本均值')
plt.title('样本量 vs 样本均值')
plt.legend()
plt.grid(True, alpha=0.3)
# 收敛区间
plt.subplot(1, 2, 2)
ns = np.array([10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000])
stds = 1 / np.sqrt(ns) # 样本均值的标准误
plt.fill_between(ns, 0.5 - 1.96*stds, 0.5 + 1.96*stds, alpha=0.3, label='95%置信区间')
plt.semilogx(ns, sample_means, 'bo-', label='样本均值')
plt.axhline(0.5, color='r', linestyle='--', label='真实期望')
plt.xlabel('样本量')
plt.ylabel('样本均值')
plt.title('95%置信区间随样本量变化')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print("结论:要达到0.01的精度,需要至少 n ≈ 1/0.01² = 10000 个样本!")九、概率分布速查表
9.1 常用分布一览
| 分布 | 符号 | 参数 | PMF/PDF | 期望 | 方差 | 应用场景 |
|---|---|---|---|---|---|---|
| 伯努利 | 单次二元实验 | |||||
| 二项 | n次实验成功数 | |||||
| 几何 | 首次成功次数 | |||||
| 泊松 | 稀有事件计数 | |||||
| 均匀 | 无先验偏好 | |||||
| 正态 | 钟形曲线 | 测量误差、CLT | ||||
| 指数 | 等待时间 | |||||
| Gamma | 正态的共轭先验 | |||||
| Beta | 概率参数 | |||||
| t分布 | (自由度) | 重尾曲线 | (若 ) | (若 ) | 小样本推断 | |
| 卡方 | (自由度) | 方差估计 |
9.2 共轭先验对照表
| 似然分布 | 共轭先验 | 后验更新规则 |
|---|---|---|
| Bernoulli() | Beta() | , |
| Binomial() | Beta() | , |
| Poisson() | Gamma() | , |
| Normal()(已知) | Normal() | |
| Normal()(已知) | Inverse-Gamma() | |
| Multinomial() | Dirichlet() |
十、概率论在机器学习中的高级应用
10.1 变分推断
变分推断(Variational Inference)将后验分布的近似问题转化为优化问题:
"""
变分推断简明示例:用平均场变分近似估计硬币正面概率的后验
目标:用简单的分布 q(p) 近似真实后验 p(p|X)
平均场变分族:q(p) = Beta(α_q, β_q)
优化目标:最大化 ELBO = E_q[log p(X|p)] + E_q[log p(p)] - E_q[log q(p)]
"""
def elbo(alpha_q, beta_q, n_heads, n_flips, alpha_prior=1, beta_prior=1):
"""
计算 ELBO (Evidence Lower Bound)
"""
# 更新后的参数
alpha_post = alpha_prior + n_heads
beta_post = beta_prior + (n_flips - n_heads)
# 使用 scipy 的 beta 分布
q = stats.beta(alpha_q, beta_q)
p_prior = stats.beta(alpha_prior, beta_prior)
p_post = stats.beta(alpha_post, beta_post)
# 数值积分计算期望
x = np.linspace(0.001, 0.999, 1000)
# E_q[log p(X|p)] - 使用泊松二项分布的近似
# log p(X|p) = log C + k*log(p) + (n-k)*log(1-p)
E_log_likelihood = np.sum(q.logpdf(x) * (n_heads * np.log(x) + (n_flips - n_heads) * np.log(1 - x)))
# E_q[log p(p)] - 先验
E_log_prior = np.sum(q.logpdf(x) * p_prior.logpdf(x))
# -E_q[log q(p)]
neg_entropy = -np.sum(q.logpdf(x) * q.logpdf(x))
# 近似计算
from scipy.special import gammaln, digamma
# 使用均值场近似的解析形式
E_log_likelihood_approx = (
(n_heads - alpha_q) * (digamma(alpha_q) - np.log(alpha_q + beta_q)) +
(n_flips - n_heads - beta_q) * (digamma(beta_q) - np.log(alpha_q + beta_q))
)
E_log_prior_approx = (
(alpha_prior - alpha_q) * (digamma(alpha_q) - np.log(alpha_q + beta_q)) +
(beta_prior - beta_q) * (digamma(beta_q) - np.log(alpha_q + beta_q)) +
gammaln(alpha_q + beta_q) - gammaln(alpha_q) - gammaln(beta_q) +
(alpha_q - 1) * (digamma(alpha_q) - np.log(alpha_q + beta_q)) +
(beta_q - 1) * (digamma(beta_q) - np.log(alpha_q + beta_q))
)
return E_log_likelihood_approx + E_log_prior_approx + (
gammaln(alpha_q + beta_q) - gammaln(alpha_q) - gammaln(beta_q) +
(alpha_q - 1) * digamma(alpha_q) +
(beta_q - 1) * digamma(beta_q) -
(alpha_q + beta_q - 2) * digamma(alpha_q + beta_q)
)
# 观测数据
n_flips = 100
n_heads = 70
# 优化找到最优的 q 参数
from scipy.optimize import minimize
def neg_elbo(params):
alpha_q, beta_q = params[0], params[1]
if alpha_q <= 0 or beta_q <= 0:
return 1e10
return -elbo(alpha_q, beta_q, n_heads, n_flips)
result = minimize(neg_elbo, x0=[2, 2], method='L-BFGS-B',
bounds=[(0.01, 100), (0.01, 100)])
alpha_q_opt, beta_q_opt = result.x
print(f"最优变分参数: α_q = {alpha_q_opt:.2f}, β_q = {beta_q_opt:.2f}")
# 真实后验
alpha_post = 1 + n_heads
beta_post = 1 + (n_flips - n_heads)
print(f"真实后验: α = {alpha_post}, β = {beta_post}")
# 可视化
x = np.linspace(0.01, 0.99, 1000)
true_posterior = stats.beta.pdf(x, alpha_post, beta_post)
variational_posterior = stats.beta.pdf(x, alpha_q_opt, beta_q_opt)
plt.figure(figsize=(10, 5))
plt.plot(x, true_posterior, 'b-', lw=2, label='真实后验')
plt.plot(x, variational_posterior, 'r--', lw=2, label=f'变分近似 q(p)')
plt.xlabel('p (正面概率)')
plt.ylabel('概率密度')
plt.title('变分推断:近似后验分布')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()10.2 高斯混合模型(GMM)
"""
高斯混合模型:用EM算法拟合
"""
from sklearn.mixture import GaussianMixture
# 生成模拟数据:两个簇
np.random.seed(42)
cluster1 = np.random.multivariate_normal([0, 0], [[1, 0.5], [0.5, 1]], 300)
cluster2 = np.random.multivariate_normal([5, 5], [[1, -0.3], [-0.3, 1]], 200)
X = np.vstack([cluster1, cluster2])
# 用GMM拟合
gmm = GaussianMixture(n_components=2, random_state=42)
gmm.fit(X)
print(f"混合权重: {gmm.weights_}")
print(f"均值:\n{gmm.means_}")
print(f"协方差矩阵:\n{gmm.covariances_}")
# 可视化
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.scatter(X[:, 0], X[:, 1], c=gmm.predict(X), cmap='viridis', alpha=0.5, s=10)
plt.xlabel('X₁')
plt.ylabel('X₂')
plt.title('GMM 聚类结果')
plt.subplot(1, 2, 2)
x1 = np.linspace(-3, 8, 200)
x2 = np.linspace(-3, 8, 200)
X1, X2 = np.meshgrid(x1, x2)
pos = np.dstack((X1, X2))
# 计算混合概率密度
Z = np.exp(gmm.score_samples(pos)) # 返回 log-likelihood,需要 exp
plt.contourf(X1, X2, Z, levels=20, cmap='viridis', alpha=0.8)
plt.scatter(X[:, 0], X[:, 1], c='white', alpha=0.3, s=5)
plt.colorbar(label='概率密度')
plt.xlabel('X₁')
plt.ylabel('X₂')
plt.title('GMM 概率密度等高线')
plt.tight_layout()
plt.show()10.3 朴素贝叶斯分类器
"""
朴素贝叶斯分类器实战:垃圾邮件分类
"""
from sklearn.naive_bayes import MultinomialNB, GaussianNB
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
# 模拟邮件数据
emails = [
"免费 赢取 奖金 点击 这里",
"亲爱的 用户 恭喜 您 中奖",
"会议 安排 在 明天 三点",
"项目 进度 报告 请查收",
"限时 优惠 立即 购买",
"周五 讨论 技术方案",
"账户 安全 问题 需要 处理",
"新产品 发布 会 邀请",
"赌博 平台 稳定 收益",
"季度 财务 总结 会议",
] * 50 # 重复以增加样本量
labels = [1, 1, 0, 0, 1, 0, 0, 0, 1, 0] * 50 # 1=垃圾邮件, 0=正常邮件
# 文本向量化
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(emails)
y = np.array(labels)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练朴素贝叶斯分类器
nb = MultinomialNB()
nb.fit(X_train, y_train)
# 预测
y_pred = nb.predict(X_test)
print("分类报告:")
print(classification_report(y_test, y_pred, target_names=['正常邮件', '垃圾邮件']))
# 预测新邮件
new_email = "免费 赢取 奖金"
new_X = vectorizer.transform([new_email])
pred = nb.predict(new_X)
prob = nb.predict_proba(new_X)
print(f"\n新邮件: '{new_email}'")
print(f"预测结果: {'垃圾邮件' if pred[0] == 1 else '正常邮件'}")
print(f"预测概率: 正常={prob[0][0]:.4f}, 垃圾={prob[0][1]:.4f}")十一、概率论基础公式速查
11.1 基本公式
| 公式 | 表达式 |
|---|---|
| 条件概率 | |
| 乘法公式 | |
| 全概率公式 | |
| 贝叶斯定理 |
11.2 期望与方差公式
| 公式 | 表达式 |
|---|---|
| 期望线性性 | |
| 独立变量乘积期望 | (独立时) |
| 方差定义 | |
| 方差简化 | |
| 独立变量方差和 | (独立时) |
| 协方差 |
11.3 常用不等式
| 不等式 | 表达式 |
|---|---|
| Markov | () |
| Chebyshev | |
| Hoeffding | $P( |
| Chernoff |
十二、延伸阅读
概率论是一座博大精深的学科,以上内容只是冰山一角。以下是进一步学习的推荐路径:
入门进阶:
- 深度学习花书第3章:概率与信息论
- 《概率论与数理统计》陈希孺
- 《All of Statistics》Larry Wasserman
贝叶斯方法:
- 《Bayesian Data Analysis》Gelman et al.
- 《Statistical Rethinking》Richard McElreath
- 《Bayesian Methods for Hackers》Cameron Davidson-Pilon
机器学习应用:
- 《Pattern Recognition and Machine Learning》Christopher Bishop
- 《Machine Learning: A Probabilistic Perspective》Kevin Murphy
- 《Probabilistic Machine Learning》Kevin Murphy (2022版)
数学深度:
- 《Probability and Measure》Patrick Billingsley
- 《Probability: Theory and Examples》Rick Durrett
参考文献
- Kolmogorov, A. N. (1933). Grundbegriffe der Wahrscheinlichkeitsrechnung. Springer-Verlag.
- Billingsley, P. (1995). Probability and Measure (3rd ed.). Wiley.
- Casella, G., & Berger, R. L. (2002). Statistical Inference (2nd ed.). Duxbury Press.
- Murphy, K. P. (2022). Probabilistic Machine Learning: An Introduction. MIT Press.
- Durrett, R. (2019). Probability: Theory and Examples (5th ed.). Cambridge University Press.
- Cover, T. M., & Thomas, J. A. (2006). Elements of Information Theory (2nd ed.). Wiley.
- Øksendal, B. (2003). Stochastic Differential Equations: An Introduction (6th ed.). Springer.
- MacKay, D. J. C. (2003). Information Theory, Inference, and Learning Algorithms. Cambridge University Press.
- Gelman, A., Carlin, J. B., Stern, H. S., Dunson, D. B., Vehtari, A., & Rubin, D. B. (2013). Bayesian Data Analysis (3rd ed.). CRC Press.
- Anderson, T. W. (2003). An Introduction to Multivariate Statistical Analysis (3rd ed.). Wiley.
- Karatzas, I., & Shreve, S. E. (1991). Brownian Motion and Stochastic Calculus (2nd ed.). Springer.
- Jacod, J., & Shiryaev, A. N. (2003). Limit Theorems for Stochastic Processes (2nd ed.). Springer.
- Dembo, A., & Zeitouni, O. (2010). Large Deviations Techniques and Applications (2nd ed.). Springer.
- Samorodnitsky, G., & Taqqu, M. S. (1994). Stable Non-Gaussian Random Processes. Chapman & Hall.
- de Finetti, B. (1974). Theory of Probability (2 vols.). Wiley.
- Daley, D. J., & Vere-Jones, D. (2008). An Introduction to the Theory of Point Processes (2nd ed.). Springer.
- Resnick, S. I. (2007). Heavy-Tail Phenomena: Probabilistic and Statistical Modeling. Springer.
- Cont, R., & Tankov, P. (2004). Financial Modelling with Jump Processes. Chapman & Hall.
- Kingman, J. F. C. (1993). Poisson Processes. Oxford University Press.
- Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning. MIT Press.