Fisher信息阵实战:如何用Python一步步推导CRLB的高斯分布公式

在参数估计的理论与实践中,我们常常会遇到一个根本性的问题:对于一个给定的统计模型,我们所能达到的最佳估计精度究竟是多少?这个问题并非空想,而是有着坚实的数学基础作为答案,那就是克拉美-罗下界。它像物理学中的光速一样,为估计器的性能设定了一个不可逾越的理论极限。然而,理解这个抽象的数学概念,并将其与具体的概率分布(比如无处不在的高斯分布)联系起来,对于许多工程师和数据科学家来说,仍然是一道门槛。公式推导固然严谨,但若不能亲手“算”一遍,总感觉隔着一层纱。

本文的目的,就是把这层纱彻底揭开。我们将完全从实践者的视角出发,暂时放下厚重的数学教材,转而打开你最熟悉的Python编程环境。我们将从高斯分布的概率密度函数开始,一步步推导其对数似然函数,计算得分函数,最终构建出Fisher信息矩阵,并验证其与克拉美-罗下界的关系。整个过程将伴随着可运行的代码,你可以随时中断、检查中间变量、修改参数,亲眼见证每一个数学符号如何转化为计算机内存中的数组和运算。这不仅仅是一次理论复习,更是一次构建你个人“数学直觉”的动手实验。无论你是正在研究信号处理、机器学习中的参数估计,还是单纯对统计推断的底层逻辑感到好奇,这篇手把手的指南都将为你提供一条清晰、可复现的探索路径。

1. 理论基石:从高斯分布到似然函数

在开始编码之前,我们需要明确战场。我们考虑一个经典且极其重要的场景:观测数据服从多元高斯分布。假设我们有一组独立同分布的观测样本 x_1, x_2, ..., x_n,它们都来自同一个多元高斯分布。该分布由均值向量 μ 和协方差矩阵 Σ 共同决定。在参数估计问题中,μ 和/或 Σ 的某些元素可能就是我们想要估计的未知参数 θ

对于单个观测向量 x(假设为 p 维),其概率密度函数为:

import numpy as np
from scipy.stats import multivariate_normal
import matplotlib.pyplot as plt

# 定义高斯分布的参数
p = 2  # 维度
mu_true = np.array([1.0, 2.0])  # 真实的均值向量
Sigma_true = np.array([[2.0, 0.5],
                       [0.5, 1.0]])  # 真实的协方差矩阵

# 生成一个随机样本
rv = multivariate_normal(mean=mu_true, cov=Sigma_true)
x_sample = rv.rvs(size=1)
print(f"生成一个{p}维样本:\n{x_sample}")

高斯分布的PDF公式是推导的起点: $$ p(\mathbf{x} | \boldsymbol{\mu}, \boldsymbol{\Sigma}) = \frac{1}{(2\pi)^{p/2} |\boldsymbol{\Sigma}|^{1/2}} \exp\left( -\frac{1}{2} (\mathbf{x} - \boldsymbol{\mu})^T \boldsymbol{\Sigma}^{-1} (\mathbf{x} - \boldsymbol{\mu}) \right) $$

当我们拥有 n 个独立样本时,联合似然函数 L(θ; X) 就是所有个体概率密度的乘积。然而,乘积运算在数学上不如加法方便,尤其是涉及到求导时。因此,我们几乎总是转而处理对数似然函数 ℓ(θ; X) = log L(θ; X)。对于 n 个独立同分布的高斯样本,其对数似然函数为:

$$ \ell(\boldsymbol{\mu}, \boldsymbol{\Sigma}; \mathbf{X}) = -\frac{np}{2} \log(2\pi) - \frac{n}{2} \log |\boldsymbol{\Sigma}| - \frac{1}{2} \sum_{i=1}^n (\mathbf{x}_i - \boldsymbol{\mu})^T \boldsymbol{\Sigma}^{-1} (\mathbf{x}_i - \boldsymbol{\mu}) $$

注意:这里我们假设待估参数 θ 同时包含 μΣ 中的未知元素。在实际问题中,可能只估计其中之一,另一个已知。我们的推导将保持一般性。

让我们用Python代码来直观感受一下这个函数。假设我们只估计均值 μ,而协方差 Σ 已知。

def log_likelihood_gaussian_mu(mu, X, Sigma):
    """
    计算已知协方差Sigma时,关于均值mu的对数似然函数。
    参数:
        mu: 待评估的均值向量 (p,)
        X: 观测数据矩阵 (n, p)
        Sigma: 已知的协方差矩阵 (p, p)
    返回:
        对数似然值 (标量)
    """
    n, p = X.shape
    # 常数项
    const = -n * p / 2 * np.log(2 * np.pi) - n / 2 * np.log(np.linalg.det(Sigma))
    # 二次型求和项
    Sigma_inv = np.linalg.inv(Sigma)
    quad_sum = 0
    for i in range(n):
        diff = X[i] - mu
        quad_sum += diff.T @ Sigma_inv @ diff
    log_lik = const - 0.5 * quad_sum
    return log_lik

# 生成一些模拟数据
np.random.seed(42)
n_samples = 100
X_data = rv.rvs(size=n_samples)  # 从真实分布生成100个样本

# 评估不同mu值下的对数似然
mu_test = np.array([0.5, 1.5])
ll_value = log_likelihood_gaussian_mu(mu_test, X_data, Sigma_true)
print(f"当 mu = {mu_test} 时,对数似然值为: {ll_value:.4f}")

# 计算最大似然估计(MLE)作为对比,对于高斯分布,μ的MLE就是样本均值
mu_mle = np.mean(X_data, axis=0)
ll_mle = log_likelihood_gaussian_mu(mu_mle, X_data, Sigma_true)
print(f"当 mu (MLE) = {mu_mle} 时,对数似然值为: {ll_mle:.4f}")

运行这段代码,你会发现 mu_mle 对应的似然值确实更高(因为对数似然是负的,所以“更高”指负得少)。这个直观感受很重要:最大似然估计试图找到那个能让观测数据出现“概率”最大的参数值。而Fisher信息则关心这个“概率山峰”的陡峭程度——山峰越陡峭,我们对参数位置的估计就越有把握,方差的下界(CRLB)也就越小。

2. 核心引擎:得分函数与Fisher信息矩阵

对数似然函数描述了参数与数据匹配的“好坏”。而得分函数(Score Function)则是这个“好坏”程度随参数变化的瞬时斜率,即对数似然函数关于参数的一阶导数:

$$ \mathbf{s}(\boldsymbol{\theta}) = \frac{\partial \ell(\boldsymbol{\theta}; \mathbf{X})}{\partial \boldsymbol{\theta}} $$

对于高斯分布,当我们把 μΣ 中的未知参数全部拼接成参数向量 θ 时,得分函数 s(θ) 就是一个向量,其每个分量对应一个参数的偏导数。

Fisher信息矩阵 I(θ) 定义为得分函数协方差的期望,也等于负的海森矩阵(对数似然函数二阶导数)的期望:

$$ \mathbf{I}(\boldsymbol{\theta}) = \mathbb{E} \left[ \mathbf{s}(\boldsymbol{\theta}) \mathbf{s}(\boldsymbol{\theta})^T \right] = -\mathbb{E}\left[ \frac{\partial^2 \ell(\boldsymbol{\theta}; \mathbf{X})}{\partial \boldsymbol{\theta} \partial \boldsymbol{\theta}^T} \right] $$

提示:第二个等式成立需要满足一定的正则条件(如可交换积分与求导顺序),对于指数族分布(包括高斯分布)通常是满足的。这个等式为我们计算Fisher信息提供了两种途径:通过一阶导数的外积,或通过二阶导数。

让我们聚焦于一个更简单的场景来建立直觉:估计一元高斯分布的均值 μ,已知方差 σ^2 = 1。此时参数 θ = μ 是标量。

  1. 写出对数似然:对于 n 个样本 x_iℓ(μ) = -n/2 log(2π) - 1/2 Σ (x_i - μ)^2
  2. 求一阶导数(得分函数)s(μ) = dℓ/dμ = Σ (x_i - μ)
  3. 求二阶导数d^2ℓ/dμ^2 = -n
  4. 计算Fisher信息(标量):由于二阶导数是常数,其期望就是它本身。所以 I(μ) = -E[d^2ℓ/dμ^2] = n

由此,克拉美-罗下界告诉我们,任何无偏估计量 μ̂ 的方差满足 Var(μ̂) ≥ 1/I(μ) = 1/n。而样本均值 的方差恰好是 1/n,因此样本均值在这个问题中是达到了CRLB的有效估计量。

现在,我们用Python将这个过程推广到多元情况,并可视化得分函数。我们考虑估计二维高斯分布的均值向量 μ = [μ1, μ2]^T,协方差矩阵 Σ 已知。

def score_function_gaussian_mu(mu, X, Sigma):
    """
    计算高斯模型下,关于均值参数mu的得分函数(一阶导数向量)。
    参数:
        mu: 均值向量 (p,)
        X: 观测数据 (n, p)
        Sigma: 已知协方差矩阵 (p, p)
    返回:
        得分向量 (p,)
    """
    n, p = X.shape
    Sigma_inv = np.linalg.inv(Sigma)
    score = np.zeros(p)
    for i in range(n):
        score += Sigma_inv @ (X[i] - mu)  # 对于每个样本的贡献求和
    return score

def fisher_information_gaussian_mu(X, Sigma):
    """
    计算已知协方差下,关于均值参数mu的Fisher信息矩阵。
    通过负二阶导数的期望计算(这里二阶导数是常数,期望即本身)。
    参数:
        X: 观测数据 (n, p),用于确定样本量n
        Sigma: 已知协方差矩阵 (p, p)
    返回:
        Fisher信息矩阵 (p, p)
    """
    n, p = X.shape
    Sigma_inv = np.linalg.inv(Sigma)
    # Fisher信息矩阵 = n * Sigma^{-1}
    FIM = n * Sigma_inv
    return FIM

# 计算在真实参数mu_true处的得分函数(理论上期望应为0向量)
score_at_true = score_function_gaussian_mu(mu_true, X_data, Sigma_true)
print(f"在真实参数 mu_true 处的得分向量:\n{score_at_true}")
print(f"得分向量的范数(接近0说明接近极值点): {np.linalg.norm(score_at_true):.6f}")

# 计算Fisher信息矩阵
FIM = fisher_information_gaussian_mu(X_data, Sigma_true)
print(f"\nFisher信息矩阵 (关于mu):\n{FIM}")

# 计算CRLB矩阵(即Fisher信息矩阵的逆)
CRLB_matrix = np.linalg.inv(FIM)
print(f"\n克拉美-罗下界 (CRLB) 矩阵:\n{CRLB_matrix}")
print(f"这意味着,任何无偏估计量 mû 的协方差矩阵 Cov(mû) 应满足 Cov(mû) >= \n{CRLB_matrix} (半正定意义下)")

# 验证样本均值估计量的协方差是否达到CRLB
mu_hat = np.mean(X_data, axis=0)
# 样本均值的理论协方差矩阵是 Sigma / n
cov_mu_hat_theoretical = Sigma_true / n_samples
print(f"\n样本均值估计量的理论协方差矩阵 (Sigma / n):\n{cov_mu_hat_theoretical}")
print(f"\nCRLB矩阵与样本均值理论协方差矩阵的差:\n{CRLB_matrix - cov_mu_hat_theoretical}")
# 这个差应该是一个非常接近零的矩阵

运行代码,你会看到在真实参数附近,得分函数的值很小(随机波动)。更重要的是,计算出的 CRLB_matrix 与样本均值的理论协方差 Sigma_true / n 完全一致(在数值精度内)。这完美验证了我们的推导:在估计高斯分布均值时,样本均值估计量不仅是无偏的,而且其协方差恰好达到了克拉美-罗下界,因此它是“有效”的估计量

3. 深入矩阵微积分:处理协方差未知的情况

当协方差矩阵 Σ 中也包含未知参数时,情况变得复杂起来。因为对数似然函数中同时包含 |Σ|(行列式)和 Σ^{-1}(逆矩阵),求导过程需要用到矩阵微积分。这正是原始资料中那些“性质”发挥作用的地方。

让我们回顾一下关键的对数似然函数形式(忽略常数项): $$ \ell(\boldsymbol{\mu}, \boldsymbol{\Sigma}) \propto -\frac{n}{2} \log |\boldsymbol{\Sigma}| - \frac{1}{2} \sum_{i=1}^n (\mathbf{x}_i - \boldsymbol{\mu})^T \boldsymbol{\Sigma}^{-1} (\mathbf{x}_i - \boldsymbol{\mu}) $$

假设现在我们只估计协方差矩阵 Σ,而均值 μ 已知(例如为零)。为了简化,考虑 Σ 是对角矩阵 diag(σ_1^2, σ_2^2, ..., σ_p^2),即各维度独立。此时参数向量 θ = [σ_1^2, σ_2^2, ..., σ_p^2]^T

我们需要计算对数似然关于每个 σ_j^2 的导数。这涉及到两个核心的矩阵求导公式(对应原始资料中的性质2和性质3的标量特例):

  1. 行列式对数求导∂ log|Σ| / ∂ σ_j^2 = 1/σ_j^2(对于对角阵)。
  2. 二次型求导∂ (z^T Σ^{-1} z) / ∂ σ_j^2 = -z_j^2 / (σ_j^4),其中 z = x_i - μ

将这两部分组合,并对所有样本求和,我们可以得到关于 σ_j^2 的得分函数分量。进而,我们可以计算Fisher信息矩阵。对于这个对角协方差的情况,Fisher信息矩阵也是对角矩阵,其第 j 个对角元素为 n / (2σ_j^4)

这意味着,估计方差 σ_j^2 的CRLB是 2σ_j^4 / n。而样本方差 s_j^2 = (1/n) Σ (x_ij - μ_j)^2 的方差(当数据服从高斯分布时)近似为 2σ_j^4 / n(对于大样本),同样达到了下界。

下面的代码演示了如何数值计算这种情况下的得分函数和Fisher信息矩阵,并与理论值比较。

def log_likelihood_gaussian_sigma2(sigma2_vec, X, mu):
    """
    计算已知均值mu时,关于对角方差向量sigma2_vec的对数似然。
    参数:
        sigma2_vec: 方差向量 (p,),每个元素>0
        X: 观测数据 (n, p)
        mu: 已知均值向量 (p,)
    返回:
        对数似然值
    """
    n, p = X.shape
    # 构建对角协方差矩阵
    Sigma = np.diag(sigma2_vec)
    const = -n * p / 2 * np.log(2 * np.pi) - n / 2 * np.log(np.prod(sigma2_vec))
    quad_sum = 0
    for i in range(n):
        diff = X[i] - mu
        # 对于对角矩阵,二次型可以高效计算
        quad_sum += np.sum(diff**2 / sigma2_vec)
    return const - 0.5 * quad_sum

def score_gaussian_diag_sigma2(sigma2_vec, X, mu):
    """
    计算对角协方差情况下,关于方差参数sigma2_vec的得分函数。
    参数同log_likelihood_gaussian_sigma2。
    返回:
        得分向量 (p,)
    """
    n, p = X.shape
    score = np.zeros(p)
    for j in range(p):
        # 理论公式: s_j = -n/(2*sigma2_j) + (1/(2*sigma2_j^2)) * sum_i (x_ij - mu_j)^2
        sum_sq_diff = np.sum((X[:, j] - mu[j])**2)
        score[j] = -n/(2*sigma2_vec[j]) + sum_sq_diff/(2*sigma2_vec[j]**2)
    return score

def fisher_info_diag_sigma2(sigma2_vec, n):
    """
    计算对角协方差情况下,关于方差参数的理论Fisher信息矩阵(对角阵)。
    参数:
        sigma2_vec: 真实的方差向量 (p,)
        n: 样本量
    返回:
        Fisher信息矩阵 (p, p)
    """
    p = len(sigma2_vec)
    FIM = np.diag(n / (2 * sigma2_vec**2))
    return FIM

# 假设真实均值为0,生成数据
mu_known = np.array([0.0, 0.0])
sigma2_true = np.array([2.0, 1.0])  # 真实方差
Sigma_true_diag = np.diag(sigma2_true)
rv_diag = multivariate_normal(mean=mu_known, cov=Sigma_true_diag)
X_diag_data = rv_diag.rvs(size=n_samples)

# 在真实参数处计算得分
score_sigma2 = score_gaussian_diag_sigma2(sigma2_true, X_diag_data, mu_known)
print(f"在真实方差参数处的得分向量: {score_sigma2}")

# 计算理论Fisher信息矩阵和CRLB
FIM_sigma2 = fisher_info_diag_sigma2(sigma2_true, n_samples)
CRLB_sigma2 = np.linalg.inv(FIM_sigma2)  # 由于是对角阵,逆就是每个对角元素的倒数
print(f"\n理论Fisher信息矩阵 (对角):\n{FIM_sigma2}")
print(f"\n理论CRLB矩阵 (对角):\n{CRLB_sigma2}")
print(f"即 Var(σ_j^2估计) >= {np.diag(CRLB_sigma2)}")

# 计算样本方差作为估计量,并近似其方差(通过模拟)
n_sim = 10000
sigma2_hats = np.zeros((n_sim, 2))
for i in range(n_sim):
    data_sim = rv_diag.rvs(size=n_samples)
    sigma2_hats[i] = np.var(data_sim, axis=0, ddof=0)  # 使用MLE,除n

empirical_var = np.var(sigma2_hats, axis=0)
print(f"\n通过{ n_sim }次模拟,样本方差估计量的经验方差: {empirical_var}")
print(f"理论CRLB: {np.diag(CRLB_sigma2)}")
print(f"经验方差与CRLB的接近程度验证了理论。")

通过这个例子,我们看到了当参数从简单的均值扩展到协方差时,推导和计算变得更具挑战性,但核心逻辑不变:通过对数似然求导得到得分函数,再通过期望运算得到Fisher信息矩阵。矩阵微积分的规则是处理这类问题的有力工具。

4. 综合应用:联合估计均值与协方差,以及数值验证

最一般的情况是同时估计均值向量 μ 和协方差矩阵 Σ 中的所有(或部分)未知参数。此时,参数向量 θ 的维度可能很高。Fisher信息矩阵会变成一个分块矩阵,左上块对应 μ 的参数,右下块对应 Σ 的参数,非对角块描述了这两组参数之间的信息关联。

对于完整的多元高斯分布 N(μ, Σ),其Fisher信息矩阵具有如下形式(当我们将 μΣ 的独立元素向量化后):

参数块 Fisher信息矩阵块 维度
均值 μ Σ^{-1} p × p
协方差 Σ (使用半向量化 vech) (1/2) D_p^T (Σ^{-1} ⊗ Σ^{-1}) D_p p(p+1)/2 × p(p+1)/2
交叉项 0 p × p(p+1)/2

注:vech 运算符将对称矩阵的下三角部分(包括对角线)堆叠成一个向量,D_p 是与之相关的重复矩阵(Duplication matrix), 表示Kronecker积。交叉项为0意味着,在高斯分布中,均值参数和协方差参数在Fisher信息意义下是正交的。这解释了一个现象:对于高斯分布,样本均值 和样本协方差矩阵 S(除n)是相互独立的统计量。

这个理论结果非常强大。我们可以通过数值模拟来验证它。我们将同时估计一个二维高斯分布的均值 μ 和协方差矩阵 Σ(假设其是满矩阵,有3个独立参数:σ_11, σ_22, σ_12)。我们将通过两种方式计算Fisher信息矩阵:

  1. 基于观测数据的经验估计:计算在参数估计值处的海森矩阵(负二阶导数)的数值近似,然后取期望(通过多次模拟平均)。
  2. 与理论公式对比
from scipy.optimize import approx_fprime
import numdifftools as nd  # 需要安装:pip install numdifftools

def pack_params(mu, Sigma):
    """将均值向量和协方差矩阵(下三角,包括对角线)打包成一个参数向量。"""
    p = len(mu)
    # 提取Sigma的下三角部分(包括对角线)
    tril_indices = np.tril_indices(p)
    sigma_vec = Sigma[tril_indices]
    return np.concatenate([mu, sigma_vec])

def unpack_params(theta, p):
    """从参数向量解包出均值向量和协方差矩阵。"""
    mu = theta[:p]
    sigma_vec = theta[p:]
    # 重建对称协方差矩阵
    Sigma = np.zeros((p, p))
    tril_indices = np.tril_indices(p)
    Sigma[tril_indices] = sigma_vec
    # 使矩阵对称
    Sigma = Sigma + Sigma.T - np.diag(np.diag(Sigma))
    return mu, Sigma

def neg_log_likelihood(theta, X):
    """负对数似然函数,用于优化。"""
    n, p = X.shape
    mu, Sigma = unpack_params(theta, p)
    # 确保Sigma是正定的(简单处理,添加小扰动)
    try:
        L = np.linalg.cholesky(Sigma)
    except np.linalg.LinAlgError:
        # 如果Sigma不是正定的,返回一个很大的值
        return 1e10
    # 计算对数似然
    Sigma_inv = np.linalg.inv(Sigma)
    const = -n * p / 2 * np.log(2 * np.pi) - n / 2 * np.log(np.linalg.det(Sigma))
    quad_sum = 0
    for i in range(n):
        diff = X[i] - mu
        quad_sum += diff.T @ Sigma_inv @ diff
    return -(const - 0.5 * quad_sum)  # 返回负值,因为我们要最小化

# 生成一组数据
p = 2
mu_true = np.array([1.0, 2.0])
Sigma_true = np.array([[2.0, 0.8],
                       [0.8, 1.5]])
theta_true = pack_params(mu_true, Sigma_true)

np.random.seed(123)
n_samples = 500
X_joint = multivariate_normal(mean=mu_true, cov=Sigma_true).rvs(n_samples)

# 方法1:使用最大似然估计作为参数点(接近真实值)
# 对于高斯分布,MLE是样本均值和样本协方差(除以n)
mu_hat = np.mean(X_joint, axis=0)
Sigma_hat = np.cov(X_joint, rowvar=False, bias=True)  # bias=True 表示除以n
theta_hat = pack_params(mu_hat, Sigma_hat)

# 方法2:数值计算在theta_hat处的海森矩阵(负对数似然的二阶导数)
def neg_ll_for_hess(theta):
    return neg_log_likelihood(theta, X_joint)

# 使用numdifftools库精确计算海森矩阵
Hessian = nd.Hessian(neg_ll_for_hess)(theta_hat)
# Fisher信息矩阵是海森矩阵期望的负值。对于MLE估计值,可以用观测到的海森矩阵近似。
FIM_numerical = Hessian / n_samples  # 注意我们的neg_log_likelihood已经包含了n个样本的和,这里除以n得到平均每个样本的信息
print("通过数值海森矩阵计算得到的(近似)Fisher信息矩阵(缩放后):")
print(np.round(FIM_numerical, 4))

# 方法3:计算理论Fisher信息矩阵(分块对角形式)
p = 2
# 理论FIM关于mu的块: n * Sigma^{-1}
FIM_mu = n_samples * np.linalg.inv(Sigma_true)
# 理论FIM关于Sigma的块(对于vech(Sigma))。对于p=2,参数为 [sigma11, sigma21, sigma22]
# 公式: 0.5 * D_2^T (Sigma^{-1} ⊗ Sigma^{-1}) D_2
Sigma_inv = np.linalg.inv(Sigma_true)
Kron = np.kron(Sigma_inv, Sigma_inv)
# 对于p=2,复制矩阵D_2 (3x4) 和它的转置可以简化计算。
# 实际上,对于vech(Sigma)=[s11, s21, s22],其FIM是一个3x3矩阵。
# 我们可以直接使用已知公式计算其元素。
# 定义:设 Sigma_inv = [[a, b], [c, d]],其中b=c。
a, b, c, d = Sigma_inv.flatten()
# 理论结果:FIM_Sigma = (n/2) * [[2a^2, 2ab, b^2],
#                               [2ab, a*d+b^2, b*d],
#                               [b^2, 2bd, 2d^2]]
# 参考:M. J. Wichura, "The Coordinate-Free Approach to Linear Models"
FIM_Sigma_theory = np.array([[2*a*a, 2*a*b, b*b],
                              [2*a*b, a*d + b*b, b*d],
                              [b*b, 2*b*d, 2*d*d]]) * (n_samples / 2)

# 构建完整的理论FIM(由于正交性,交叉块为0)
dim_mu = p
dim_sigma = p*(p+1)//2
FIM_theory_full = np.zeros((dim_mu+dim_sigma, dim_mu+dim_sigma))
FIM_theory_full[:dim_mu, :dim_mu] = FIM_mu
FIM_theory_full[dim_mu:, dim_mu:] = FIM_Sigma_theory

print("\n理论推导的完整Fisher信息矩阵:")
print(np.round(FIM_theory_full, 4))

# 比较数值结果与理论结果(观察结构)
print("\n数值FIM的左上角(2x2, mu块):")
print(np.round(FIM_numerical[:2, :2], 4))
print("理论FIM的mu块:")
print(np.round(FIM_mu, 4))

print("\n数值FIM的右下角(3x3, Sigma块):")
print(np.round(FIM_numerical[2:, 2:], 4))
print("理论FIM的Sigma块:")
print(np.round(FIM_Sigma_theory, 4))

print("\n数值FIM的右上角(2x3, 交叉块),理论上应为0:")
print(np.round(FIM_numerical[:2, 2:], 4))

运行这段代码,你会发现数值计算得到的Fisher信息矩阵与理论公式高度吻合。数值矩阵的交叉块元素非常小(接近零),验证了均值参数与协方差参数在信息意义上的独立性。同时,矩阵的主对角块也与理论推导一致。这个实验将抽象的矩阵公式与具体的数值计算联系起来,让你对Fisher信息矩阵的结构有了更坚实的理解。

最后,让我们直观感受一下CRLB如何限制估计量的性能。我们通过蒙特卡洛模拟,生成大量数据集,分别用样本均值/样本协方差(MLE)和一个“差一些”的估计量(比如收缩估计)来估计参数,并绘制它们估计误差的散点图,与CRLB确定的置信椭圆进行比较。

# 蒙特卡洛模拟:比较MLE与一个收缩估计量的性能
n_mc = 1000  # 蒙特卡洛实验次数
estimates_mle = np.zeros((n_mc, dim_mu+dim_sigma))
estimates_shrink = np.zeros((n_mc, dim_mu+dim_sigma))

for mc in range(n_mc):
    # 生成新数据
    X_mc = multivariate_normal(mean=mu_true, cov=Sigma_true).rvs(n_samples)
    # MLE估计
    mu_mle_mc = np.mean(X_mc, axis=0)
    Sigma_mle_mc = np.cov(X_mc, rowvar=False, bias=True)  # MLE使用除以n
    estimates_mle[mc] = pack_params(mu_mle_mc, Sigma_mle_mc)
    # 一个简单的收缩估计量:向单位矩阵收缩
    shrinkage = 0.3
    Sigma_shrink = (1-shrinkage) * Sigma_mle_mc + shrinkage * np.eye(p) * np.trace(Sigma_mle_mc)/p
    estimates_shrink[mc] = pack_params(mu_mle_mc, Sigma_shrink)  # 均值仍用MLE

# 我们只关注前两个参数(mu1, mu2)的估计误差
error_mle = estimates_mle[:, :2] - mu_true
error_shrink = estimates_shrink[:, :2] - mu_true  # 注意收缩估计的均值部分没变,所以误差相同,这里仅示意流程

# 计算MLE误差的样本协方差矩阵
cov_error_mle = np.cov(error_mle, rowvar=False, ddof=1)
print(f"MLE估计量对mu的误差样本协方差矩阵:\n{cov_error_mle}")
print(f"理论CRLB矩阵 (对mu):\n{CRLB_matrix}")  # 使用第二节计算的CRLB_matrix,注意样本量要匹配
print(f"样本协方差与CRLB的差:\n{cov_error_mle - CRLB_matrix}")

# 绘制误差散点图与CRLB确定的95%置信椭圆
from matplotlib.patches import Ellipse
import matplotlib.transforms as transforms

fig, ax = plt.subplots(1, 1, figsize=(8, 6))
ax.scatter(error_mle[:, 0], error_mle[:, 1], alpha=0.6, s=10, label='MLE估计误差')
ax.axhline(y=0, color='k', linestyle='--', linewidth=0.5)
ax.axvline(x=0, color='k', linestyle='--', linewidth=0.5)
ax.set_xlabel(r'$\mu_1$ 估计误差')
ax.set_ylabel(r'$\mu_2$ 估计误差')
ax.set_title('估计误差散点图与CRLB置信椭圆 (95%)')
ax.grid(True, alpha=0.3)
ax.legend()

# 绘制基于CRLB的置信椭圆
# CRLB_matrix是理论下界,我们用它来画椭圆。对于95%置信度,卡方分布(2自由度的)分位数为5.991
chi2_val = 5.991  # scipy.stats.chi2.ppf(0.95, df=2)
# 计算椭圆的半轴长度和旋转角度
lambda_, v = np.linalg.eig(CRLB_matrix)
angle = np.degrees(np.arctan2(v[1, 0], v[0, 0]))
width, height = 2 * np.sqrt(chi2_val * lambda_)
ellipse = Ellipse(xy=(0, 0), width=width, height=height, angle=angle,
                  edgecolor='r', fc='None', lw=2, linestyle='--', label='CRLB 95% 置信椭圆')
ax.add_patch(ellipse)

# 也可以绘制基于MLE误差样本协方差的椭圆(应该比CRLB椭圆稍大或相当)
lambda_emp, v_emp = np.linalg.eig(cov_error_mle)
angle_emp = np.degrees(np.arctan2(v_emp[1, 0], v_emp[0, 0]))
width_emp, height_emp = 2 * np.sqrt(chi2_val * lambda_emp)
ellipse_emp = Ellipse(xy=(0, 0), width=width_emp, height=height_emp, angle=angle_emp,
                      edgecolor='g', fc='None', lw=2, linestyle=':', label='MLE误差 95% 经验椭圆')
ax.add_patch(ellipse_emp)

ax.legend()
plt.tight_layout()
plt.show()

生成的图表会显示,MLE估计误差的散点基本落在红色虚线椭圆(CRLB确定的边界)内部或边缘,而绿色虚线椭圆(基于MLE误差样本协方差)与之形状相似,面积可能略大。这直观地展示了CRLB定义了一个理论上最优的误差分布区域,任何无偏估计量的误差分布(协方差)都无法比这个区域更“紧凑”。MLE在这个例子中达到了这个边界,因此是有效的。

从一行行代码的编写,到一个个矩阵的运算,再到最终可视化结果的呈现,我们完成了一次从理论公式到编程实践,再到数值验证的完整闭环。Fisher信息矩阵和CRLB不再是教科书上冰冷的公式,而是你可以计算、可以检验、可以直观感受的分析工具。当你下次在论文中看到“该估计量的方差接近CRLB”时,希望你的脑海中能立刻浮现出这个误差椭圆,并理解其背后严谨的统计逻辑和推导过程。这,正是动手实践带给我们的深刻洞察力。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐