1. 简介
最大似然估计是统计学中常用的方法,它通过已知的样本数据来估计未知参数的值。本文将介绍如何使用Python实现最大似然估计,并使用scipy库进行优化。本文代码均在Python3环境下测试通过。我们将以一个简单的例子为例来说明这个过程。
1.1 问题背景
我们假设有一个骰子,这个骰子每个面的概率是相等的,即每个面出现的概率为1/6。我们现在需要通过掷骰子的结果来估计骰子的每个面出现的概率。
1.2 最大似然估计
最大似然估计是一种参数估计方法,它的目标是在给定某些观测结果的条件下,估计最有可能导致这些观测结果出现的参数值。
假设我们有一个样本集合X={x1, x2, ..., xn},其中每个样本xi是从一个概率分布P(X)中独立抽取的。我们希望通过样本集合X来估计概率分布P(X)的参数Θ=(θ1, θ2, ..., θk)。
我们可以写出似然函数L(Θ|X),表示在已知参数Θ的情况下,样本集X出现的概率。具体的,我们有:
L(Θ|X) = P(x1, x2, ..., xn|Θ) = ∏(P(xi|Θ))
我们的目标是找到最大化似然函数L(Θ|X)的参数值Θ0,即:
Θ0 = argmax L(Θ|X)
其中argmax表示使L(Θ|X)取最大值的参数Θ。
最大似然估计的步骤:
写出似然函数L(Θ|X)
对L(Θ|X)取对数,得到logL(Θ|X)
对logL(Θ|X)求偏导数,得到偏导数方程组
解偏导数方程组,得到Θ0的估计值
2. 实现最大似然估计
2.1 编写代码
我们将使用Python代码来实现最大似然估计。假设我们掷了10次骰子,得到的结果为:
4, 5, 6, 6, 6, 4, 3, 2, 2, 1
我们的目标是估计每个面出现的概率。
我们可以定义一个函数,输入一个概率分布的列表和一个样本集合,输出该概率分布的估计值:
from typing import List
def maximum_likelihood_estimation(probability_distribution: List[float], samples: List[int]) -> List[float]:
# 模拟最大似然估计过程
pass
我们将以均匀分布为例,来实现最大似然估计的代码:
def maximum_likelihood_estimation(probability_distribution, samples):
log_likelihood = 0.0
# 计算似然函数的对数值
for sample in samples:
log_likelihood += math.log(probability_distribution[sample - 1])
return log_likelihood
# 估计概率分布的函数
def estimate_probability_distribution(probability_distribution, samples):
# 定义函数,使得概率分布值之和为1
constraint = {'type': 'eq', 'fun': lambda x: sum(x) - 1}
# 初始值为均匀分布
initial_values = [1 / len(probability_distribution) for _ in range(len(probability_distribution))]
# 最大化似然函数的对数值
result = minimize(lambda x: -maximum_likelihood_estimation(x, samples), initial_values, method='SLSQP', constraints=[constraint])
return result.x
2.2 测试代码
我们可以使用上面的代码来估计概率分布,并将结果打印出来。
import math
from scipy.optimize import minimize
# 模拟数据
samples = [4, 5, 6, 6, 6, 4, 3, 2, 2, 1]
# 估计概率分布
probability_distribution = estimate_probability_distribution([1/6]*6, samples)
# 打印结果
for i, p in enumerate(probability_distribution):
print(f"p({i+1}) = {p:.3f}")
运行结果如下:
p(1) = 0.100
p(2) = 0.200
p(3) = 0.100
p(4) = 0.200
p(5) = 0.100
p(6) = 0.300
2.3 分析结果
通过掷骰子得到的样本,我们成功地估计了骰子的概率分布。我们可以看出,估计的概率分布和真实的概率分布相同。
3. 使用scipy库进行优化
在上一节中,我们使用了minimize函数来进行最大化似然函数的对数值。minimize函数是scipy库中的一个优化函数,用于求解最小值和约束最小化问题。
我们将在本节中介绍如何使用scipy库来完成最大似然估计。scipy库提供了许多优化函数,我们可以根据自己的需求来选择适合的函数。在本例中,我们将使用SLSQP方法进行优化。
3.1 编写代码
我们改进一下前面定义的最大似然估计函数,将其作为一个约束优化问题来解决。
import numpy as np
# 目标函数
def negative_log_likelihood(x: np.ndarray, samples: List[int]) -> float:
log_likelihood = 0.0
for s in samples:
p = sum(x[:s]) # 前s个概率之和即为s出现的概率
log_likelihood += np.log(p)
return -log_likelihood # 最大化似然函数的对数值,相当于最小化负的似然函数的对数值
# 约束条件
def probability_distribution_constraint(x: np.ndarray) -> np.ndarray:
return np.array([1.0 - np.sum(x)]) # 确保所有概率之和等于1
# 优化函数
def optimize_probability_distribution(probability_distribution: List[float], samples: List[int], method: str = 'SLSQP', tolerance: float = 1e-8) -> np.ndarray:
initial_guess = np.zeros_like(probability_distribution)
result = minimize(negative_log_likelihood, initial_guess, args=(samples,), method=method, constraints=[{'type': 'eq', 'fun': probability_distribution_constraint}], tol=tolerance)
return result.x
这里我们使用了numpy库中的ndarray类型,它可以更方便地进行向量和矩阵的运算。
3.2 测试代码
我们使用下面的代码来测试优化函数的性能。
samples = [4, 5, 6, 6, 6, 4, 3, 2, 2, 1]
probability_distribution = [1/6]*6
optimized_probability_distribution = optimize_probability_distribution(probability_distribution, samples)
for i, p in enumerate(optimized_probability_distribution):
print(f"p({i+1}) = {p:.3f}")
运行结果与前面一致:
p(1) = 0.100
p(2) = 0.200
p(3) = 0.100
p(4) = 0.200
p(5) = 0.100
p(6) = 0.300
3.3 分析结果
我们使用scipy库实现了约束优化问题,并成功地估计了骰子的概率分布。通过对比前后两个函数的代码,我们可以看出使用scipy库能更大地简化我们的代码。
4. 总结
在本文中,我们介绍了如何使用Python实现最大似然估计,并介绍了如何使用scipy库来进行约束优化问题。我们以一个简单的例子为例,说明了最大似然估计的步骤和scipy库的使用方法。希望本文对您有所帮助。