feat: 完整中文翻译 maths-cs-ai-compendium(数学·计算机科学·AI 知识大全)

翻译自英文原版 maths-cs-ai-compendium,共 20 章全部完成。

第01章 向量 | 第02章 矩阵 | 第03章 微积分
第04章 统计学 | 第05章 概率论 | 第06章 机器学习
第07章 计算语言学 | 第08章 计算机视觉 | 第09章 音频与语音
第10章 多模态学习 | 第11章 自主系统 | 第12章 图神经网络
第13章 计算与操作系统 | 第14章 数据结构与算法
第15章 生产级软件工程 | 第16章 SIMD与GPU编程
第17章 AI推理 | 第18章 ML系统设计
第19章 应用人工智能 | 第20章 前沿人工智能

翻译说明:
- 所有数学公式 $...$ / $$...$$、代码块、图片引用完整保留
- mkdocs.yml 配置中文导航 + language: zh
- README.md 已翻译为中文(兼 docs/index.md)
- docs/ 目录包含指向各章文件的 symlink
- 约 29,000 行中文内容,排除 .cache/ 构建缓存
This commit is contained in:
2026-05-03 10:23:20 +08:00
commit 2536c937e3
400 changed files with 49040 additions and 0 deletions
+186
View File
@@ -0,0 +1,186 @@
# 统计学基础
*统计学提供了描述数据和量化不确定性的语言。本节涵盖分布、随机变量、PMF、PDF、CDF、期望、方差、矩以及中心极限定理——这些概念支撑着每一个机器学习评估指标和损失函数。*
- 统计学是从数据中学习的科学。你收集观测值,对其进行汇总,并得出结论——通常针对那些无法直接测量的事物。
- 假设你想知道某个国家所有成年人的平均身高。你不可能测量每一个人,因此你测量一个**样本**,并利用统计学对整个**总体**做出有根据的推测。
- 统计学有两个主要分支:
- **描述性统计**:对已有数据进行汇总(平均值、图表、表格)
- **推断性统计**:利用样本对更大群体做出推断
- 统计学的基本构件是**分布**——一种描述数值如何分布的方式。其他一切——平均值、检验、预测——都源于对分布的理解。
- **频率分布**统计数据中每个值(或值区间)出现的次数。想象一下把考试成绩分到不同的区间,然后统计每个区间中有多少学生。结果就是直方图。
- **概率分布**用概率代替原始计数。它不说"12 名学生的分数在 70 到 80 之间",而是说"分数在 70 到 80 之间的概率为 0.24"。当数据连续时,直方图的柱状会变成一条平滑曲线。
![频率分布直方图与概率分布平滑曲线对比](../images/distribution_types.svg)
- 左侧的直方图基于你实际收集的数据构建。右侧的平滑曲线是一个数学模型,描述了数据背后的模式。一个是经验性的,另一个是理论性的。
- 为了从数学上处理分布,我们需要一种将结果赋予数值的方法。这正是**随机变量**所做的。
- 随机变量是一个将每次试验的结果映射到实数的函数。抛一枚硬币:结果是"正面"或"反面",但随机变量 $X$ 将其转换为 $X(正面) = 1$ 和 $X(反面) = 0$。现在我们就可以进行算术运算了。
![随机变量将结果(硬币、骰子)映射到数轴](../images/random_variable.svg)
- **离散**随机变量取值为可数集:10 次抛掷中的正面次数、骰子的点数、一小时内收到的电子邮件数量。
- **连续**随机变量可以在一个区间内取任意值:你的精确身高、下一班公交车到达的时间、中午的温度。
- 这种区别很重要,因为它改变了我们计算概率的方式。对于离散变量,我们求和。对于连续变量,我们积分(回顾第 3 章的积分内容)。
- 对于离散随机变量,**概率质量函数(PMF)**给出每个具体值的概率:
$$P(X = x) = p(x), \quad \text{其中 } \sum_{x} p(x) = 1$$
- 对于连续随机变量,**概率密度函数(PDF)**给出落在某个区间内的概率。任何单个精确值的概率为零;只有区间才具有正概率:
$$P(a \le X \le b) = \int_a^b f(x)\, dx, \quad \text{其中 } \int_{-\infty}^{\infty} f(x)\, dx = 1$$
- 既然我们可以将结果赋予数值,最自然的问题就是:平均而言我们期望得到什么值?
- **期望**(或期望值)是所有可能值的加权平均值,权重即为概率。可以将其视为分布的"重心"。
- 如果你多次掷一个公平的骰子,你的平均掷点数会收敛到 3.5。这就是期望值,尽管你实际上永远掷不出 3.5。
- 对于离散随机变量:
$$E[X] = \sum_{x} x \cdot p(x)$$
- 对于连续随机变量(使用第 3 章的积分):
$$E[X] = \int_{-\infty}^{\infty} x \cdot f(x)\, dx$$
- 示例:一个公平的六面骰子,对于 $x = 1, 2, 3, 4, 5, 6$,有 $p(x) = 1/6$。
$$E[X] = 1 \cdot \tfrac{1}{6} + 2 \cdot \tfrac{1}{6} + 3 \cdot \tfrac{1}{6} + 4 \cdot \tfrac{1}{6} + 5 \cdot \tfrac{1}{6} + 6 \cdot \tfrac{1}{6} = \frac{21}{6} = 3.5$$
- 期望具有线性性质,即 $E[aX + b] = aE[X] + b$。这一性质极其有用,在机器学习损失函数中频繁出现。
- 期望告诉我们中心位置,但完全没有说明数值的分散程度。为了描述分布的完整形状,我们需要**矩**。
- 矩是 $X$ 的某次幂的期望。第 $k$ 阶**原点矩**为:
$$\mu_k' = E[X^k]$$
- 一阶原点矩($k = 1$)就是均值:$\mu_1' = E[X] = \mu$。
- 原点矩是从零点开始度量的。通常我们更关心相对于均值的偏差。第 $k$ 阶**中心矩**将测量中心化:
$$\mu_k = E[(X - \mu)^k]$$
- 一阶中心矩始终为零(均值上下方的偏差相互抵消)。二阶中心矩就是**方差**。
- 为了比较不同尺度上的分布,我们通过除以标准差 $\sigma$ 的适当幂次来进行**标准化**:
$$\tilde{\mu}_k = \frac{\mu_k}{\sigma^k}$$
- 每个矩捕捉分布形状的不同方面:
![钟形曲线标注了每个矩所捕捉的特征:均值(中心)、方差(分散程度)、偏度(不对称性)、峰度(尾部重量)](../images/moments_shape.svg)
- **1 阶矩(均值)**:分布的中心位置。平衡点。
- **2 阶矩(方差)**:数值围绕均值的分散程度。方差越大,分布越宽。
- **3 阶矩(偏度)**:分布向左还是向右倾斜。偏度为零表示对称。
- **4 阶矩(峰度)**:尾部的重量。峰度越高,极端异常值越多。
- 让我们对具体数据集 $X = \{2, 4, 4, 4, 5, 5, 7, 9\}$ 计算全部四个矩。
- **步骤 1:均值**(一阶原点矩)
$$\mu = \frac{2 + 4 + 4 + 4 + 5 + 5 + 7 + 9}{8} = \frac{40}{8} = 5$$
- **步骤 2:方差**(二阶中心矩)。从每个值中减去均值,平方,然后取平均:
$$\sigma^2 = \frac{(2{-}5)^2 + (4{-}5)^2 + (4{-}5)^2 + (4{-}5)^2 + (5{-}5)^2 + (5{-}5)^2 + (7{-}5)^2 + (9{-}5)^2}{8}$$
$$= \frac{9 + 1 + 1 + 1 + 0 + 0 + 4 + 16}{8} = \frac{32}{8} = 4$$
- **标准差**为 $\sigma = \sqrt{4} = 2$。
- **步骤 3:偏度**(标准化三阶中心矩)。偏差取三次方,求平均,再除以 $\sigma^3$
$$\tilde{\mu}_3 = \frac{1}{8} \cdot \frac{(-3)^3 + (-1)^3 + (-1)^3 + (-1)^3 + 0^3 + 0^3 + 2^3 + 4^3}{2^3}$$
$$= \frac{1}{8} \cdot \frac{-27 -1 -1 -1 + 0 + 0 + 8 + 64}{8} = \frac{42}{64} = 0.656$$
- 正偏度表示右尾更长,这很合理,因为 9 远高于均值。
- **步骤 4:峰度**(标准化四阶中心矩)。偏差取四次方:
$$\tilde{\mu}_4 = \frac{1}{8} \cdot \frac{(-3)^4 + (-1)^4 + (-1)^4 + (-1)^4 + 0^4 + 0^4 + 2^4 + 4^4}{2^4}$$
$$= \frac{1}{8} \cdot \frac{81 + 1 + 1 + 1 + 0 + 0 + 16 + 256}{16} = \frac{356}{128} = 2.781$$
- 正态分布的峰度为 3(称为"常峰态")。我们的 2.781 很接近,表明尾部大致呈正态。大于 3 的值("尖峰态")表示尾部更重;小于 3("低峰态")表示尾部更轻。某些公式会报告**超值峰度**(减去 3),因此我们的超值峰度为 $-0.219$。
## 编程练习(使用 CoLab 或 notebook
1. 计算一个加载骰子的期望值,其中面 6 的概率为 0.3,其余面均分剩余概率。通过模拟 100,000 次投掷进行验证。
```python
import jax
import jax.numpy as jnp
# 加载骰子:面 6 的 p=0.3,其余面均分 0.7
probs = jnp.array([0.14, 0.14, 0.14, 0.14, 0.14, 0.30])
faces = jnp.array([1, 2, 3, 4, 5, 6])
# 解析法计算期望值
ev = jnp.sum(faces * probs)
print(f"期望值(公式法): {ev:.4f}")
# 模拟
key = jax.random.PRNGKey(42)
rolls = jax.random.choice(key, faces, shape=(100_000,), p=probs)
print(f"期望值(模拟法): {rolls.mean():.4f}")
```
2. 计算示例数据集的所有四个矩(均值、方差、偏度、峰度),然后修改数据并观察每个矩如何变化。
```python
import jax.numpy as jnp
x = jnp.array([2, 4, 4, 4, 5, 5, 7, 9], dtype=jnp.float32)
mean = jnp.mean(x)
variance = jnp.mean((x - mean) ** 2)
std = jnp.sqrt(variance)
skewness = jnp.mean(((x - mean) / std) ** 3)
kurtosis = jnp.mean(((x - mean) / std) ** 4)
print(f"均值: {mean:.3f}")
print(f"方差: {variance:.3f}")
print(f"标准差: {std:.3f}")
print(f"偏度: {skewness:.3f}")
print(f"峰度: {kurtosis:.3f}")
print(f"超值峰度: {kurtosis - 3:.3f}")
```
3. 并排可视化公平骰子的 PMF 和 CDF。尝试修改概率以观察形状如何变化。
```python
import jax.numpy as jnp
import matplotlib.pyplot as plt
faces = jnp.array([1, 2, 3, 4, 5, 6])
pmf = jnp.ones(6) / 6 # 公平骰子;试试修改这些值!
cdf = jnp.cumsum(pmf)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
ax1.bar(faces, pmf, color="#3498db", alpha=0.8)
ax1.set_title("PMF")
ax1.set_xlabel("面值")
ax1.set_ylabel("P(X = x)")
ax1.set_ylim(0, 0.5)
ax2.step(faces, cdf, where="mid", color="#e74c3c", linewidth=2)
ax2.set_title("CDF")
ax2.set_xlabel("面值")
ax2.set_ylabel("P(X ≤ x)")
ax2.set_ylim(0, 1.1)
plt.tight_layout()
plt.show()
```
+178
View File
@@ -0,0 +1,178 @@
# 统计量
*统计量用单个数值概括数据,捕捉其离散程度、位置、形状和关联。本节涵盖方差、标准差、四分位数、偏度、峰度、协方差、相关和 z 分数——这是探索性数据分析和机器学习特征工程的基础工具集。*
- 在上一节中,我们介绍了矩作为一组概括性统计量家族。在此,我们展开讨论从矩中衍生出的实用工具:度量离散程度、位置、形状和关联的统计量。
- **离散程度**回答了这样一个问题:数据的分布有多分散?两个班级的平均考试成绩可能相同,但其分散程度却可能大相径庭。
![均值相同但离散程度不同的两个分布](../images/variance_spread.svg)
- 窄(蓝色)分布的方差较小:大部分数值紧密聚集在均值周围。宽(红色)分布的方差较大:数值散布得更远。
- **方差**是距均值距离的平方的平均值。取平方是为了避免正负偏差相互抵消。
$$\sigma^2 = \frac{1}{N} \sum_{i=1}^{N} (x_i - \mu)^2$$
- 当处理样本(而非整个总体)时,我们用 $N - 1$ 而不是 $N$ 来除。这种修正(称为**贝塞尔校正**)是因为样本往往会低估真实的变异性:
$$s^2 = \frac{1}{N-1} \sum_{i=1}^{N} (x_i - \bar{x})^2$$
- **标准差**是方差的平方根:$\sigma = \sqrt{\sigma^2}$。它将度量单位恢复为原始单位。如果数据的单位是厘米,方差的单位是 cm$^2$,而标准差的单位又回到了 cm。
- **平均绝对偏差(MAD)**是一个更简单的替代方案。它不取平方,而是取每个偏差的绝对值:
$$\text{MAD} = \frac{1}{N} \sum_{i=1}^{N} |x_i - \mu|$$
- MAD 对方差而言对异常值更稳健,因为它不会通过平方来放大大的偏差。然而,方差在数学上更便利(在证明和机器学习优化中更容易分解)。
- **位置**回答了一个不同的问题:特定数值相对于其余数据的位置在哪里?
- **四分位数**将排序后的数据分成四个相等的部分。Q1(第 25 百分位数)是低于该值的数据占 25% 的值。Q2 是中位数(第 50 百分位数)。Q3 是第 75 百分位数。
- **四分位距(IQR**是 $Q3 - Q1$。它捕捉了中间 50% 数据的离散程度,排除了极端值。
![显示 Q1、中位数、Q3、IQR、须线和异常值的箱线图](../images/quartiles_boxplot.svg)
- **箱线图**是统计学中最有用的可视化工具之一。箱体从 Q1 延伸到 Q3,中间的线为中位数,须线延伸到最远的非异常值,而须线之外的点则为异常值。
- **百分位数**是四分位数的推广。第 $p$ 百分位数是低于该值的观测值占 $p\%$ 的值。Q1 是第 25 百分位数,中位数是第 50 百分位数,Q3 是第 75 百分位数。
- **z 分数**告诉你一个值距均值有多少个标准差:
$$z = \frac{x - \mu}{\sigma}$$
- z 分数为 2 表示该值高于均值 2 个标准差。z 分数为 $-1.5$ 表示低于均值 1.5 个标准差。这也称为**标准化**,在机器学习中广泛用于特征缩放,因为它将任何分布变换为均值为 0、标准差为 1。
- **形状**描述了分布超出其中心和离散程度之外的几何特征。
- **偏度**(上一节中的标准化三阶矩)衡量不对称性。像正态曲线这样完全对称的分布,其偏度为零。正偏度表示右尾较长(如收入分布)。负偏度表示左尾较长(如退休年龄分布)。
$$\text{偏度} = \frac{1}{N} \sum_{i=1}^{N} \left(\frac{x_i - \mu}{\sigma}\right)^3$$
- **峰度**(标准化四阶矩)衡量尾部厚度。正态分布的峰度为 3。尾部更厚(更容易出现异常值)的分布的峰度大于 3。
$$\text{峰度} = \frac{1}{N} \sum_{i=1}^{N} \left(\frac{x_i - \mu}{\sigma}\right)^4$$
- **相关**衡量两个变量之间关系的强度和方向。它回答了:当一个变量上升时,另一个变量倾向于上升、下降,还是基本不变?
![三个散点图,分别显示正相关、无相关和负相关](../images/correlation_scatter.svg)
- **皮尔森相关**($r$)衡量*线性*关联。其取值范围从 $-1$(完全负相关)到 $0$(无相关)再到 $+1$(完全正相关)。
$$r = \frac{\sum_{i=1}^{N} (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum (x_i - \bar{x})^2} \cdot \sqrt{\sum (y_i - \bar{y})^2}}$$
- 如果你还记得第 1 章中的点积,皮尔森相关本质上就是 $\mathbf{x}$ 和 $\mathbf{y}$ 均值中心化之后的余弦相似度。
- **斯皮尔曼相关**($\rho$)衡量*单调*关联。它不使用原始值,而是先对它们进行排序,然后在排序上计算皮尔森相关。这使得它对异常值稳健,并且即使关系是非线性的,只要是一致递增或递减的,也能正常工作。
- **几何平均数**是当数值相互乘除时(如增长率)合适的平均值。如果你的投资分别增长了 10%、20% 和 30%,那么平均增长因子并不是这些增长率的算术平均数。而是:
$$\bar{x}_{\text{geo}} = \left(\prod_{i=1}^{N} x_i\right)^{1/N}$$
- 具体到增长率,先将百分比转换为因子(1.10、1.20、1.30),计算几何平均数,再减去 1。
- **指数移动平均(EMA)**赋予最近观测值更高的权重。与简单移动平均中窗口内所有点权重相等不同,EMA 呈指数衰减:
$$\text{EMA}_t = \alpha \cdot x_t + (1 - \alpha) \cdot \text{EMA}_{t-1}$$
- 平滑因子 $\alpha$(介于 0 和 1 之间)控制旧观测值失去影响的速度。$\alpha$ 越大,对近期变化的响应越灵敏;$\alpha$ 越小,曲线越平滑。在机器学习中,EMA 被用于 Adam 等优化器以及批归一化的运行统计中。
- **异常值检测**识别出与其余数据异常遥远的数点。两种常用方法:
- **IQR 法**:如果一个点低于 $Q1 - 1.5 \times \text{IQR}$ 或高于 $Q3 + 1.5 \times \text{IQR}$,则为异常值
- **Z 分数法**:如果 $|z| > 3$(距均值超过 3 个标准差),则为异常值
- IQR 法更稳健,因为它不假设正态分布。Z 分数法在数据近似正态时效果良好,但当分布高度偏斜时可能失效。
## 编程练习(使用 CoLab 或 notebook
1. 计算数据集的方差、标准差和 MAD,并进行比较。观察添加极端异常值时发生的变化。
```python
import jax.numpy as jnp
data = jnp.array([4, 8, 6, 5, 3, 7, 9, 5, 6, 7], dtype=jnp.float32)
mean = jnp.mean(data)
variance = jnp.var(data)
std = jnp.std(data)
mad = jnp.mean(jnp.abs(data - mean))
print("原始数据:")
print(f" 方差:{variance:.3f},标准差:{std:.3f}MAD{mad:.3f}")
# 添加一个异常值并重新计算
data_outlier = jnp.append(data, 100.0)
mean2 = jnp.mean(data_outlier)
print(f"\n添加异常值(100)后:")
print(f" 方差:{jnp.var(data_outlier):.3f},标准差:{jnp.std(data_outlier):.3f}MAD{jnp.mean(jnp.abs(data_outlier - mean2)):.3f}")
```
2. 计算两个变量之间的皮尔森相关和斯皮尔曼相关。尝试不同的关系。
```python
import jax
import jax.numpy as jnp
# 完全线性关系
x = jnp.array([1, 2, 3, 4, 5, 6, 7, 8], dtype=jnp.float32)
y = 2 * x + 1 # 试试修改这个!
def pearson(a, b):
a_c = a - jnp.mean(a)
b_c = b - jnp.mean(b)
return jnp.sum(a_c * b_c) / (jnp.sqrt(jnp.sum(a_c**2)) * jnp.sqrt(jnp.sum(b_c**2)))
def spearman(a, b):
rank_a = jnp.argsort(jnp.argsort(a)).astype(jnp.float32)
rank_b = jnp.argsort(jnp.argsort(b)).astype(jnp.float32)
return pearson(rank_a, rank_b)
print(f"皮尔森 r {pearson(x, y):.4f}")
print(f"斯皮尔曼 ρ:{spearman(x, y):.4f}")
```
3. 分别使用 IQR 和 Z 分数方法实现异常值检测,然后比较它们在偏斜数据上的结果。
```python
import jax.numpy as jnp
data = jnp.array([2, 3, 3, 4, 5, 5, 5, 6, 6, 7, 50], dtype=jnp.float32)
# IQR 方法
q1, q3 = jnp.percentile(data, 25), jnp.percentile(data, 75)
iqr = q3 - q1
lower, upper = q1 - 1.5 * iqr, q3 + 1.5 * iqr
iqr_outliers = data[(data < lower) | (data > upper)]
print(f"IQR 边界:[{lower:.1f}, {upper:.1f}]")
print(f"IQR 异常值:{iqr_outliers}")
# Z 分数方法
z_scores = (data - jnp.mean(data)) / jnp.std(data)
z_outliers = data[jnp.abs(z_scores) > 3]
print(f"\nZ 分数:{z_scores}")
print(f"Z 分数异常值(|z| > 3):{z_outliers}")
```
4. 在不同平滑因子下计算并绘制带噪声数据的指数移动平均。
```python
import jax.numpy as jnp
import matplotlib.pyplot as plt
# 生成带噪声的数据
key = __import__("jax").random.PRNGKey(0)
noise = __import__("jax").random.normal(key, shape=(50,))
signal = jnp.linspace(0, 5, 50) + noise
def ema(data, alpha):
result = jnp.zeros_like(data)
result = result.at[0].set(data[0])
for t in range(1, len(data)):
result = result.at[t].set(alpha * data[t] + (1 - alpha) * result[t - 1])
return result
plt.figure(figsize=(10, 4))
plt.plot(signal, "o", alpha=0.3, label="原始数据", color="#999")
for alpha, color in [(0.1, "#e74c3c"), (0.3, "#3498db"), (0.7, "#27ae60")]:
plt.plot(ema(signal, alpha), label=f"α={alpha}", color=color, linewidth=2)
plt.legend()
plt.title("不同平滑因子下的 EMA")
plt.show()
```
+164
View File
@@ -0,0 +1,164 @@
# 抽样
*抽样决定了我们如何收集数据,并直接控制着我们所做每项结论的质量。本文涵盖随机抽样、分层抽样、整群抽样与系统抽样、抽样分布、大数定律以及自助法——这些方法对于机器学习中的训练/测试划分和数据集整理至关重要。*
- 在理想世界中,你会测量所关心群体中的每一个成员。但在实践中,这几乎永远不可能做到。你无法调查每一位选民,无法测试每一只灯泡,也无法扫描每一位患者。所以你只能抽取一个**样本**,并用它来了解整体。
- **总体**是你想研究的个体或项目的完整集合。**样本**是你实际观测到的子集。
- **参数**是描述总体的数值(例如,某个国家所有成年人的真实平均身高)。
- **统计量**是从样本中计算出的数值(例如,你测量的 500 人的平均身高)。统计量用于估计参数。
- 结论的质量完全取决于你如何选择样本。一个有偏的样本会导致有偏的结论,无论你的分析多么复杂。
- **抽样框**是你实际从中抽取样本的所有个体的列表。理想情况下,抽样框与总体完全吻合,但在实践中总会存在差距。
- 例如,如果你通过电话调查人群,就会漏掉所有没有电话的人。抽样框与总体之间的差异称为**覆盖误差**。
- **抽样误差**是样本统计量与总体参数之间的自然差异。
- 即使是完全随机的样本也不会与总体完全一致。更大的样本可以减少抽样误差。
- 抽样有两大类:概率抽样和非概率抽样。
- **概率抽样**意味着总体中的每一个成员都有已知的、非零的概率被选中。这让你能够量化不确定性并推广结果。
- **简单随机抽样**:每个个体被选中的概率相等,且每个大小为 $n$ 的可能样本出现的概率相同。就像把每个名字放进一顶帽子里,然后蒙眼抽取。
- **分层抽样**:根据某个共同特征(如年龄组、地区)将总体划分为互不重叠的组(层),然后从每一层中随机抽样。这保证了每个群体的代表性,并且当层与层之间存在差异时,可以降低方差。
- **整群抽样**:将总体划分为若干组(群),随机选择一些群,然后将所选群中的全部个体都纳入样本。当总体在地理上分散时这种方法很实用,比如在整个学区中抽取整所学校而非单个学生。
- **系统抽样**:随机选择一个起点,然后从列表中每隔 $k$ 个个体选取一个。例如,从第 7 个人开始,然后每隔 10 个人取一个(7, 17, 27, ...)。这种方法易于实施,但如果列表中存在隐藏模式,则可能引入偏差。
![三种概率抽样方法对比:简单随机、分层和整群](../images/sampling_methods.svg)
- **非概率抽样**并不给每个成员已知的入选机会。其结果无法被严格推广,但这些方法通常更快、更便宜。
- **便利抽样**:选择最容易接触到的人。在购物中心调查人群很方便,但会遗漏那些不去购物中心的人。
- **配额抽样**:与分层抽样类似,但没有随机性。研究者通过从每个群体中选取方便接触的个体来填补配额(例如 50 名男性和 50 名女性)。
- **雪球抽样**:从少数参与者开始,然后请他们招募其他人。适用于难以接触到的人群(例如研究罕见疾病),但会严重偏向于有社交联系的个体。
- 一旦你有了抽样方法,一个自然的问题就出现了:如果抽取一个不同的样本,会得到不同的统计量吗?几乎肯定会。**抽样分布**是一个统计量(如样本均值)在所有相同大小的可能样本上的分布。
- 想象一下抽取 1000 个不同的 30 人样本,并计算每个样本的平均身高。这 1000 个均值形成了一个分布。有些会略高于真实的总体均值,有些会略低于,而大多数会聚集在真实值周围。
- 这个抽样分布的标准差称为**标准误**:
$$SE = \frac{\sigma}{\sqrt{n}}$$
- 注意标准误随着 $n$ 的增大而缩小。更大的样本能给出更精确的估计。样本量扩大到四倍,标准误减半。
- 统计学中最重要的结果是**中心极限定理(CLT)**。它指出:无论原始总体的分布形态如何,随着样本量的增大,样本均值的分布都趋近于正态分布。
![CLT:偏态总体产生正态分布的样本均值](../images/central_limit_theorem.svg)
- 更精确地说,如果 $X_1, X_2, \ldots, X_n$ 是来自任意分布的独立观测值,该分布具有均值 $\mu$ 和有限方差 $\sigma^2$,那么随着 $n$ 增大:
$$\bar{X} \approx \text{Normal}\!\left(\mu, \frac{\sigma^2}{n}\right)$$
- CLT 是大部分推断统计得以进行的基础。它让我们能够使用正态分布作为近似,即使底层数据不是正态分布,只要样本量足够大即可。
- "足够大"是多大?一个常见的经验法则是 $n \ge 30$,但这取决于总体的非正态程度。对于高度偏态的分布,你可能需要更大的样本量。对于大致对称的总体,即使 $n = 10$ 也可能足够了。
- CLT 有三个关键条件:
- **独立性**:每个观测值不能影响其他观测值
- **有限方差**:总体方差必须存在(排除了某些特殊分布)
- **同分布**:所有观测值来自同一分布
## 编程任务(使用 CoLab 或 notebook
1. 可视化演示 CLT:从高度偏态的分布中抽取样本,计算样本均值,观察均值直方图如何变成钟形。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
key = jax.random.PRNGKey(0)
# 指数分布(高度偏态)
population = jax.random.exponential(key, shape=(100_000,))
fig, axes = plt.subplots(1, 4, figsize=(14, 3))
sample_sizes = [1, 5, 30, 100]
for ax, n in zip(axes, sample_sizes):
keys = jax.random.split(key, 2000)
means = jnp.array([jax.random.choice(k, population, shape=(n,)).mean() for k in keys])
ax.hist(means, bins=40, color="#3498db", alpha=0.7, density=True)
ax.set_title(f"n = {n}")
ax.set_xlim(0, 4)
fig.suptitle("CLT:随着 n 增大,样本均值趋近正态分布", fontsize=13)
plt.tight_layout()
plt.show()
```
2. 比较简单随机抽样与分层抽样。创建一个具有不同分组的总体,展示分层抽样能给出更低的估计方差。
```python
import jax
import jax.numpy as jnp
key = jax.random.PRNGKey(42)
# 总体:两个不同的组
group_a = jax.random.normal(key, shape=(500,)) + 10 # 均值 ~10
key, subkey = jax.random.split(key)
group_b = jax.random.normal(subkey, shape=(500,)) + 20 # 均值 ~20
population = jnp.concatenate([group_a, group_b])
# 简单随机抽样:1000 次试验,样本量 20
srs_means = []
for i in range(1000):
key, subkey = jax.random.split(key)
sample = jax.random.choice(subkey, population, shape=(20,), replace=False)
srs_means.append(sample.mean())
srs_means = jnp.array(srs_means)
# 分层抽样:每组各取 10 个
strat_means = []
for i in range(1000):
key, k1, k2 = jax.random.split(key, 3)
s_a = jax.random.choice(k1, group_a, shape=(10,), replace=False)
s_b = jax.random.choice(k2, group_b, shape=(10,), replace=False)
strat_means.append(jnp.concatenate([s_a, s_b]).mean())
strat_means = jnp.array(strat_means)
print(f"简单随机 - 均值: {srs_means.mean():.3f}, 标准差: {srs_means.std():.3f}")
print(f"分层抽样 - 均值: {strat_means.mean():.3f}, 标准差: {strat_means.std():.3f}")
print(f"分层抽样降低了方差 {(1 - strat_means.var()/srs_means.var())*100:.1f}%")
```
3. 探索样本量如何影响标准误。绘制标准误随样本量变化的曲线,验证 $1/\sqrt{n}$ 的关系。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
key = jax.random.PRNGKey(7)
population = jax.random.normal(key, shape=(50_000,)) * 10 + 50
sample_sizes = [5, 10, 20, 50, 100, 200, 500, 1000]
std_errors = []
for n in sample_sizes:
means = []
for _ in range(500):
key, subkey = jax.random.split(key)
sample = jax.random.choice(subkey, population, shape=(n,))
means.append(sample.mean())
std_errors.append(jnp.array(means).std())
plt.figure(figsize=(8, 4))
plt.plot(sample_sizes, std_errors, "o-", color="#e74c3c", label="观测到的 SE")
theoretical = population.std() / jnp.sqrt(jnp.array(sample_sizes, dtype=jnp.float32))
plt.plot(sample_sizes, theoretical, "--", color="#3498db", label="σ/√n(理论值)")
plt.xlabel("样本量 (n)")
plt.ylabel("标准误")
plt.legend()
plt.title("标准误随样本量增大而缩小")
plt.show()
```
@@ -0,0 +1,177 @@
# 假设检验
*假设检验提供了一个严谨的框架,用于判断观测到的效应是真实存在的还是由随机因素造成的。本文涵盖原假设与备择假设、p值、显著性水平、t检验、卡方检验、方差分析以及第一类/第二类错误——这些逻辑同样应用于A/B测试、模型比较和研究中。*
- 统计学不仅仅是对数据进行描述。你经常需要做出决策:新药是否有效?某个算法是否比另一个更快?平均值是否发生了变化?假设检验为你提供了一个基于数据回答这些问题的结构化框架。
- 其思路很简单:假设没有任何变化("原假设"),然后检验数据是否极端到让这个假设难以令人置信。
- **原假设**($H_0$)是默认的主张,通常表述为"无效应"或"无差异"。例如:"平均配送时间仍为30分钟"或"新模型并不比旧模型更好"。
- **备择假设**$H_1$ 或 $H_a$)是你认为可能成立的替代情况:"平均配送时间发生了变化"或"新模型更好"。
- 你永远无法直接证明 $H_1$。相反,你提出这样一个问题:如果 $H_0$ 成立,观察到如此极端的数据的可能性有多大?如果这种可能性非常小,你就拒绝 $H_0$,转而接受 $H_1$。
- **检验统计量**是一个单一数值,它概括了你的样本结果与 $H_0$ 预测值之间的偏差程度。不同的检验使用不同的公式,但逻辑始终一致:度量观测值与期望值之间的距离。
- **p值**是在假设 $H_0$ 成立的前提下,观察到至少与当前检验统计量一样极端的结果的概率。p值越小,意味着在 $H_0$ 下数据越令人意外。
- **显著性水平**($\alpha$)是你在看到数据之前设定的阈值。如果 $p \le \alpha$,则拒绝 $H_0$。常用的选择有 $\alpha = 0.05$5%)和 $\alpha = 0.01$1%)。
![正态曲线,阴影区域为拒绝域,标出了检验统计量,并高亮了p值区域](../images/hypothesis_test.svg)
- 阴影尾部即拒绝域。如果你的检验统计量落在此区域,说明在 $H_0$ 下数据足够极端,你拒绝 $H_0$。绿色区域显示了某个特定检验统计量对应的p值。
- 以下是逐步流程:
- **第1步**:提出 $H_0$ 和 $H_1$
- **第2步**:选择显著性水平 $\alpha$
- **第3步**:收集数据并计算检验统计量
- **第4步**:计算p值(或将检验统计量与临界值比较)
- **第5步**:如果 $p \le \alpha$,拒绝 $H_0$;否则,无法拒绝 $H_0$
- **实例演练**:某工厂声称其螺栓的平均长度为10 cm。你测量了36个螺栓,得到样本均值为10.3 cm。已知总体标准差为0.9 cm。是否有证据表明均值发生了变化?
- $H_0$$\mu = 10$$H_1$$\mu \neq 10$$\alpha = 0.05$
- 检验统计量(z检验,因为 $\sigma$ 已知且 $n$ 较大):
$$z = \frac{\bar{x} - \mu_0}{\sigma / \sqrt{n}} = \frac{10.3 - 10}{0.9 / \sqrt{36}} = \frac{0.3}{0.15} = 2.0$$
- 对于 $\alpha = 0.05$ 的双侧检验,临界值为 $\pm 1.96$。我们的 $z = 2.0 > 1.96$,因此拒绝 $H_0$。p值约为0.046,小于0.05。
- 结论:有统计学上的显著证据表明,螺栓的平均长度与10 cm不同。
- **单侧检验**检查效应是否朝着某个特定方向发生($H_1$$\mu > 10$ 或 $\mu < 10$)。整个 $\alpha$ 集中于一个尾部,使得在该方向上更容易拒绝 $H_0$,但无法检测到相反方向的效应。
- **双侧检验**检查是否存在任何差异($H_1$$\mu \neq 10$)。$\alpha$ 被分配到两个尾部(各 $\alpha/2$)。这种方法更保守,但能捕捉到两个方向上的效应。
- 即使有了良好的流程,错误仍然可能发生。共有两种类型的错误:
![2×2矩阵图,展示第一类错误和第二类错误:实际情况与决策结果](../images/type_errors.svg)
- **第一类错误**(假阳性):当 $H_0$ 实际为真时,你错误地拒绝了它。其概率为 $\alpha$,你可以通过选择显著性水平来控制。就像没有火灾时火灾报警器却响了。
- **第二类错误**(假阴性):当 $H_0$ 实际为假时,你未能拒绝它。其概率为 $\beta$。就像发生真实火灾时火灾报警器保持沉默。
- **检验功效**为 $1 - \beta$,即正确拒绝错误 $H_0$ 的概率。功效越高,意味着你检测真实效应的能力越强。功效随以下因素增加:
- 真实效应量更大(差异越大越容易检测)
- 样本量更大(更多数据 = 更高精度)
- 显著性水平 $\alpha$ 更大(但这会增加第一类错误的风险)
- 变异性更低(噪声更小)
- 第一类错误与第二类错误之间存在权衡关系。降低 $\alpha$(对假阳性更加谨慎)会增加 $\beta$(更多假阴性)。在固定样本量下,你无法同时最小化这两类错误。
- **参数检验**假设数据服从特定的分布(通常是正态分布)。当假设条件成立时,参数检验的功效更高。
- **Z检验**:在 $\sigma$ 已知且 $n$ 较大($n \ge 30$)时,将样本均值与已知值进行比较。检验统计量:
$$z = \frac{\bar{x} - \mu_0}{\sigma / \sqrt{n}}$$
- **T检验**:类似于z检验,但适用于 $\sigma$ 未知(由样本估计)或 $n$ 较小的情况。使用t分布,其尾部比正态分布更厚。更厚的尾部反映了估计 $\sigma$ 所引入的额外不确定性。
$$t = \frac{\bar{x} - \mu_0}{s / \sqrt{n}}$$
- t分布有一个称为**自由度**($df = n - 1$)的参数。随着 $df$ 增大,t分布趋近于正态分布。
- t检验有几种变体:
- **单样本t检验**:样本均值是否与某个特定值不同?
- **独立双样本t检验**:两个独立组的均值是否不同?
- **配对t检验**:两个相关测量值的均值是否不同(例如同一批受试者治疗前后的测量值)?
- **方差分析**:检验三个或更多组的均值是否相等。与运行多次t检验(这会膨胀第一类错误率)不同,方差分析通过比较组间方差与组内方差进行一次统一检验。
$$F = \frac{\text{组间方差}}{\text{组内方差}}$$
- 较大的 $F$ 比值意味着各组之间的差异超出了随机变异所能解释的范围。
- **非参数检验**对数据分布的假设较少。它们基于秩次而非原始值进行运算,因此对异常值和非正态性具有稳健性。
- **卡方检验**($\chi^2$):检验观测频数与期望频数是否一致。用于分类数据。例如:红、蓝、绿三种颜色汽车的比例是否与制造商声称的比例一致?
$$\chi^2 = \sum \frac{(O_i - E_i)^2}{E_i}$$
- **Mann-Whitney U检验**:独立双样本t检验的非参数替代方法。通过比较秩次来检验一组是否倾向于比另一组有更大的值。
- **Wilcoxon符号秩检验**:配对t检验的非参数替代方法。通过考察差异的大小和方向来比较配对观测值。
- **Kruskal-Wallis检验**:单因素方差分析的非参数替代方法。通过比较所有组的秩次来检验多个组是否来自同一分布。
- **拟合优度检验**检查数据是否服从某个特定的理论分布。卡方拟合优度检验将观测到的区间计数与假设分布下的期望计数进行比较。
- **正态性检验**专门检验数据是否服从正态分布。常用的检验包括Shapiro-Wilk检验(对小样本检验力强)和Kolmogorov-Smirnov检验(将样本经验分布函数与理论分布函数进行比较)。
- 在机器学习中,假设检验出现在比较模型性能时。如果模型A达到92%的准确率,模型B达到91%的准确率,这种差异是真实的还是仅仅是噪声?对交叉验证得分进行配对t检验可以回答这个问题。
## 编程练习(使用CoLab或notebook
1. 对文中的螺栓工厂示例执行z检验。计算检验统计量、p值并做出决策。
```python
import jax.numpy as jnp
x_bar = 10.3 # 样本均值
mu_0 = 10.0 # 原假设值
sigma = 0.9 # 已知总体标准差
n = 36 # 样本量
alpha = 0.05
# 检验统计量
z = (x_bar - mu_0) / (sigma / jnp.sqrt(n))
print(f"z = {z:.4f}")
# p值(双侧检验)使用正态CDF近似
# 对于 |z| = 2.0p ≈ 0.0456
from jax.scipy.stats import norm
p_value = 2 * (1 - norm.cdf(jnp.abs(z)))
print(f"p值 = {p_value:.4f}")
print(f"拒绝H₀?{p_value <= alpha}")
```
2. 模拟第一类错误:当 $H_0$ 为真时,我们犯错误的频率有多高?运行10,000次实验,检验拒绝率是否与 $\alpha$ 一致。
```python
import jax
import jax.numpy as jnp
key = jax.random.PRNGKey(0)
mu_0 = 50.0
sigma = 10.0
n = 30
alpha = 0.05
n_experiments = 10_000
rejections = 0
for i in range(n_experiments):
key, subkey = jax.random.split(key)
sample = mu_0 + sigma * jax.random.normal(subkey, shape=(n,))
z = (sample.mean() - mu_0) / (sigma / jnp.sqrt(n))
p_value = 2 * (1 - __import__("jax").scipy.stats.norm.cdf(jnp.abs(z)))
if p_value <= alpha:
rejections += 1
print(f"拒绝率:{rejections/n_experiments:.4f}")
print(f"期望值(α): {alpha}")
```
3. 对两组数据分别运行t检验和Mann-Whitney U检验。生成一组均值略高于另一组的数据,观察哪种检验能检测出差异。
```python
import jax
import jax.numpy as jnp
key = jax.random.PRNGKey(99)
k1, k2 = jax.random.split(key)
group_a = jax.random.normal(k1, shape=(25,)) * 5 + 100
group_b = jax.random.normal(k2, shape=(25,)) * 5 + 103 # 均值略高
# 双样本t检验(假设方差相等)
n_a, n_b = len(group_a), len(group_b)
mean_a, mean_b = group_a.mean(), group_b.mean()
pooled_var = ((n_a - 1) * group_a.var() + (n_b - 1) * group_b.var()) / (n_a + n_b - 2)
se = jnp.sqrt(pooled_var * (1/n_a + 1/n_b))
t_stat = (mean_a - mean_b) / se
print(f"t检验统计量:{t_stat:.4f}")
# Mann-Whitney:统计group_a的值小于group_b值的次数
u_stat = jnp.sum(group_a[:, None] < group_b[None, :])
print(f"Mann-Whitney U {u_stat}")
print(f"\nA组均值:{mean_a:.2f}B组均值:{mean_b:.2f}")
```
+210
View File
@@ -0,0 +1,210 @@
# 统计推断
*统计推断超越了简单的"是/否"决策,以量化的不确定性来估计总体参数。本节涵盖置信区间、点估计与区间估计、极大似然估计、矩法以及回归分析——这是连接原始数据与机器学习预测模型的桥梁。*
- 假设检验给出一个"是/否"的结论:拒绝或不拒绝原假设。但通常你希望得到更有信息量的结果——你正在估计的参数的一个合理取值区间。这正是**置信区间**所提供的。
- **点估计**是从样本中计算出的单一数值,比如样本均值 $\bar{x}$。它是你对总体参数的最佳猜测,但仅凭它本身无法反映估计的精确程度。
- **置信区间**在点估计周围包裹一个反映不确定性的范围。其形式为:
$$\text{CI} = \bar{x} \pm \text{ME}$$
- **误差范围**取决于三个因素:你希望多高的置信度、数据的变异程度有多大、以及样本量有多大:
$$\text{ME} = z^\ast \cdot \frac{\sigma}{\sqrt{n}}$$
- 其中 $z^\ast$ 是从正态分布中查得的临界值,与你期望的置信水平对应。对于 95% 置信度,$z^\ast = 1.96$;对于 99% 置信度,$z^\ast = 2.576$。
![置信区间:点估计及其两侧的误差范围](../images/confidence_interval.svg)
- **95% 置信区间**的含义是:如果你重复进行多次实验,每次构建一个区间,那么大约 95% 的区间会包含真实的总体参数。这并不意味着该参数有 95% 的概率落在这个特定的区间内。参数是一个固定值;变化的是区间本身。
- **示例**:你测量了 50 人的身高,得到 $\bar{x} = 170$ cm$\sigma = 8$ cm。构建一个 95% 置信区间。
$$\text{ME} = 1.96 \cdot \frac{8}{\sqrt{50}} = 1.96 \cdot 1.131 = 2.22 \text{ cm}$$
$$\text{CI} = [170 - 2.22, \; 170 + 2.22] = [167.78, \; 172.22]$$
- 你可以说,有 95% 的把握认为真正的平均身高介于 167.78 cm 和 172.22 cm 之间。
- 当 $\sigma$ 未知时(这是常见情况),改用样本标准差 $s$ 和 t 分布:
$$\text{CI} = \bar{x} \pm t^\ast_{n-1} \cdot \frac{s}{\sqrt{n}}$$
- 越宽的区间置信度越高,但精度越低;越窄的区间精度越高,但置信度越低。在不降低置信度的前提下,增大样本量可以缩小区间。
- **功效分析**帮助你在实验开始前进行规划。要回答的问题是:为了检测到某个给定大小的效应并达到指定的检验功效,我需要多大的样本量?
- 回顾上一节的内容,功效 = $1 - \beta$,即正确拒绝错误原假设 $H_0$ 的概率。常见的功效目标是 80%。
- 对于 z 检验,检测差异 $\delta$ 所需样本量(给定显著性水平 $\alpha$ 和功效 $1-\beta$)为:
$$n = \left(\frac{(z_{\alpha/2} + z_{\beta}) \cdot \sigma}{\delta}\right)^2$$
- 例如,要检测平均身高 2 cm 的差异($\sigma = 8$),取 $\alpha = 0.05$、功效 80%$z_{0.025} = 1.96$$z_{0.20} = 0.84$):
$$n = \left(\frac{(1.96 + 0.84) \cdot 8}{2}\right)^2 = \left(\frac{22.4}{2}\right)^2 = 11.2^2 \approx 126$$
- 你大约需要每组 126 人。
- 功效分析可以防止两种常见错误:实验规模太小,无法检测到真实的效应(功效不足);或者浪费资源做远超必要规模的实验(功效过剩)。
- **蒙特卡洛方法**利用随机抽样来求解难以或无法解析求解的问题。其核心思想是:如果你无法精确计算某个量,那就多次模拟并用结果作为近似值。
- 名称来源于蒙特卡洛赌场,寓意随机性的角色。这些方法是机器学习中的重要工具,用于估计积分、评估模型不确定性以及近似复杂分布等任务。
- 蒙特卡洛的一般步骤:
- 定义可能输入的取值范围
- 从该范围中随机生成输入
- 对每个输入评估某个函数
- 汇总结果(平均值、计数等)
- 一个经典例子是估算 $\pi$。想象一个边长为 2 的正方形,中心在原点,内切一个半径为 1 的圆。正方形的面积为 4,圆的面积为 $\pi$。
![正方形及其内切圆,随机点按圆内/圆外着色](../images/monte_carlo_pi.svg)
- 在正方形内均匀地随机投点。落在圆内的点的比例近似 $\pi/4$:
$$\pi \approx 4 \times \frac{\text{圆内点数}}{\text{总点数}}$$
- 点 $(x, y)$ 在圆内的条件是 $x^2 + y^2 \le 1$。投的点越多,估算值就越接近 $\pi$ 的真实值。
- 在机器学习中,蒙特卡洛方法出现在:
- **蒙特卡洛 Dropout**:多次执行推理(启用 dropout)来估计预测不确定性
- **MCMC(马尔可夫链蒙特卡洛)**:在贝叶斯模型中从复杂的后验分布中抽样
- **策略梯度方法**:通过采样轨迹来估计强化学习中的梯度
- **因子分析**是一种发现隐藏(潜在)变量的技术,这些变量解释了观测变量之间的相关性。如果 10 个个性调查问题可以由 3 个潜在特质(外向性、宜人性、责任心)解释,因子分析就能找出这些特质。
- 该模型假设每个观测变量 $x_i$ 是少数潜在因子 $f_j$ 的线性组合加上噪声:
$$x_i = \lambda_{i1} f_1 + \lambda_{i2} f_2 + \ldots + \lambda_{ik} f_k + \epsilon_i$$
- $\lambda$ 值称为**因子载荷**,表示每个观测变量与各因子的关联强度。这与第 2 章的矩阵分解直接相关;因子分析与特征值分解和 SVD 密切相关。
- **实验设计**是安排实验结构的艺术,使你能够得出有效的结论。糟糕的设计甚至会使大量数据变得毫无价值。
- 良好实验设计的关键要素:
- **自变量**:你操控的变量(例如药物剂量、模型架构)
- **因变量**:你测量的变量(例如恢复时间、准确率)
- **对照组**:不接受处理(或接受安慰剂),提供比较的基线
- **随机分配**:参与者被随机分配到各组,从而平衡掉未测量的混杂变量
- **常见的实验设计**
- **完全随机设计**:受试者被随机分配到处理组。在各组可比的情况下,简单有效。
- **随机区组设计**:受试者先按区组分组(例如按年龄),然后在每个区组内随机分配到处理组。这降低了区组因素带来的变异,类似于分层抽样的思路。
- **析因设计**:同时测试多个自变量。一个 $2 \times 3$ 的析因设计包含一个变量的 2 个水平和另一个变量的 3 个水平,共 6 种处理组合。这使你能够检测到**交互作用**——即一个变量的效应取决于另一个变量的水平。
- **交叉设计**:每个受试者按顺序接受所有处理(其间有洗脱期)。每个受试者作为自身的对照,减少了个体差异的影响。
- 在机器学习实验中,这些原则至关重要。比较模型时,应控制随机种子、数据集划分和硬件环境。交叉验证是一种交叉设计形式。逐次移除一个组件的消融研究则遵循析因设计的逻辑。
## 编程练习(在 CoLab 或 notebook 中完成)
1. 为身高示例构建一个 95% 置信区间,然后尝试不同的置信水平和样本量。
```python
import jax.numpy as jnp
x_bar = 170.0 # 样本均值
sigma = 8.0 # 总体标准差(已知)
n = 50 # 样本量
# 常用置信水平的临界值
z_stars = {0.90: 1.645, 0.95: 1.960, 0.99: 2.576}
for conf, z_star in z_stars.items():
me = z_star * (sigma / jnp.sqrt(n))
lower, upper = x_bar - me, x_bar + me
print(f"{conf*100:.0f}% CI: [{lower:.2f}, {upper:.2f}] (ME = {me:.2f})")
```
2. 使用蒙特卡洛模拟估算 $\pi$。绘制随着点数增加估算值收敛的曲线。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
key = jax.random.PRNGKey(42)
# 在 [-1, 1] x [-1, 1] 内生成随机点
n_points = 100_000
k1, k2 = jax.random.split(key)
x = jax.random.uniform(k1, shape=(n_points,), minval=-1, maxval=1)
y = jax.random.uniform(k2, shape=(n_points,), minval=-1, maxval=1)
# 检查哪些点在单位圆内
inside = (x**2 + y**2) <= 1.0
cumulative_inside = jnp.cumsum(inside)
counts = jnp.arange(1, n_points + 1)
pi_estimates = 4.0 * cumulative_inside / counts
plt.figure(figsize=(10, 4))
plt.plot(pi_estimates, color="#3498db", alpha=0.7, linewidth=0.5)
plt.axhline(y=jnp.pi, color="#e74c3c", linestyle="--", label=f"π = {jnp.pi:.6f}")
plt.xlabel("点数")
plt.ylabel("π 的估算值")
plt.title("蒙特卡洛估算 π")
plt.legend()
plt.ylim(2.8, 3.5)
plt.show()
print(f"最终估算值: {pi_estimates[-1]:.6f}")
print(f"真实值: {jnp.pi:.6f}")
print(f"误差: {abs(pi_estimates[-1] - jnp.pi):.6f}")
```
3. 执行一个简单的功效分析:给定效应大小和标准差,计算所需样本量并通过模拟验证。
```python
import jax
import jax.numpy as jnp
# 参数
delta = 2.0 # 效应大小(均值差)
sigma = 8.0 # 总体标准差
alpha = 0.05
power_target = 0.80
# 解析计算的样本量
z_alpha = 1.96 # 双尾,alpha=0.05
z_beta = 0.84 # power=0.80
n_required = ((z_alpha + z_beta) * sigma / delta) ** 2
print(f"每组所需样本量: {n_required:.0f}")
# 通过模拟验证
key = jax.random.PRNGKey(7)
n = int(jnp.ceil(n_required))
n_sims = 5000
rejections = 0
for _ in range(n_sims):
key, k1, k2 = jax.random.split(key, 3)
group_a = jax.random.normal(k1, shape=(n,)) * sigma + 50
group_b = jax.random.normal(k2, shape=(n,)) * sigma + 50 + delta
pooled_se = jnp.sqrt(2 * sigma**2 / n)
z = (group_b.mean() - group_a.mean()) / pooled_se
p = 2 * (1 - __import__("jax").scipy.stats.norm.cdf(jnp.abs(z)))
if p <= alpha:
rejections += 1
print(f"模拟功效: {rejections/n_sims:.3f}")
print(f"目标功效: {power_target:.3f}")
```
4. 可视化置信区间宽度随样本量的变化。这展示了为什么收集更多数据可以得到更精确的估计。
```python
import jax.numpy as jnp
import matplotlib.pyplot as plt
sigma = 8.0
z_star = 1.96 # 95% 置信度
sample_sizes = jnp.array([10, 20, 30, 50, 100, 200, 500, 1000], dtype=jnp.float32)
margins = z_star * sigma / jnp.sqrt(sample_sizes)
plt.figure(figsize=(8, 4))
plt.bar([str(int(n)) for n in sample_sizes], margins, color="#3498db", alpha=0.7)
plt.xlabel("样本量")
plt.ylabel("误差范围 (cm)")
plt.title("95% CI 误差范围随样本量增大而缩小")
plt.show()
```