别再死记硬背了!用Python模拟马尔可夫链,5分钟搞懂‘平稳分布’是啥
用Python模拟马尔可夫链5分钟可视化理解平稳分布马尔可夫链作为概率论中的重要概念在自然语言处理、金融建模、生物信息学等领域有着广泛应用。但对于初学者来说那些复杂的数学公式和抽象定义往往让人望而却步。本文将带你用Python代码构建一个简单的马尔可夫链模型通过直观的可视化来理解平稳分布这一核心概念完全避开繁琐的数学证明。1. 马尔可夫链基础概念想象你正在观察一个城市的天气变化。假设这个城市只有两种天气状态晴天和雨天。今天的天气只取决于昨天的天气而与更早的天气无关——这就是马尔可夫性质的核心思想未来只取决于现在。在Python中我们可以用一个简单的字典来表示这种转移关系weather_transition { 晴天: {晴天: 0.7, 雨天: 0.3}, 雨天: {晴天: 0.6, 雨天: 0.4} }这个转移矩阵表示如果今天是晴天明天有70%的概率还是晴天30%的概率会下雨如果今天下雨明天有60%的概率转晴40%的概率继续下雨马尔可夫链的几个关键特征状态空间所有可能状态的集合这里就是{晴天,雨天}转移概率从一个状态转移到另一个状态的概率无记忆性下一状态的概率分布只取决于当前状态2. 构建马尔可夫链模拟器让我们用Python实现一个简单的马尔可夫链模拟器。我们将使用NumPy来处理概率计算Matplotlib来进行可视化。首先我们需要将转移矩阵表示为NumPy数组import numpy as np # 定义转移矩阵 transition_matrix np.array([ [0.7, 0.3], # 晴天到晴天晴天到雨天 [0.6, 0.4] # 雨天到晴天雨天到雨天 ]) # 状态标签 states [晴天, 雨天]接下来我们编写一个函数来模拟马尔可夫链的演化过程def simulate_markov_chain(initial_state, transition_matrix, n_steps): current_state initial_state state_sequence [current_state] state_indices [states.index(current_state)] for _ in range(n_steps): next_state np.random.choice( states, ptransition_matrix[states.index(current_state)] ) state_sequence.append(next_state) state_indices.append(states.index(next_state)) current_state next_state return state_sequence, state_indices这个函数接受初始状态、转移矩阵和步数作为输入返回状态序列和对应的索引序列。3. 可视化马尔可夫链演化现在让我们模拟并可视化一个50天的天气变化过程import matplotlib.pyplot as plt # 模拟从晴天开始的50天天气变化 state_sequence, state_indices simulate_markov_chain(晴天, transition_matrix, 50) # 绘制结果 plt.figure(figsize(12, 4)) plt.plot(state_indices, o-) plt.yticks([0, 1], states) plt.xlabel(天数) plt.ylabel(天气状态) plt.title(50天天气变化模拟) plt.grid(True) plt.show()运行这段代码你会看到一个直观的天气变化图。观察这个图你会发现虽然每天的天气都在变化但长期来看晴天和雨天出现的频率似乎趋于稳定。4. 探索平稳分布平稳分布是马尔可夫链的一个重要特性它表示经过足够多的状态转移后系统达到的稳定状态分布。在这个状态下状态的概率分布不再随时间变化。我们可以通过计算状态的概率分布随时间的变化来观察这个过程def compute_state_distribution(initial_dist, transition_matrix, n_steps): dist_history [initial_dist] current_dist initial_dist for _ in range(n_steps): current_dist np.dot(current_dist, transition_matrix) dist_history.append(current_dist) return np.array(dist_history) # 初始分布假设第一天是晴天 initial_dist np.array([1.0, 0.0]) # 100%晴天0%雨天 # 计算20天内的状态分布变化 dist_history compute_state_distribution(initial_dist, transition_matrix, 20) # 可视化分布变化 plt.figure(figsize(10, 5)) plt.plot(dist_history[:, 0], label晴天概率) plt.plot(dist_history[:, 1], label雨天概率) plt.xlabel(天数) plt.ylabel(概率) plt.title(状态概率分布随时间变化) plt.legend() plt.grid(True) plt.show()你会看到两条曲线逐渐收敛到某个固定值。这就是我们的马尔可夫链达到了平稳分布。5. 计算理论平稳分布除了通过模拟观察收敛行为我们还可以通过数学方法计算理论上的平稳分布。平稳分布π满足以下方程π πP其中P是转移矩阵。这意味着平稳分布是转移矩阵的左特征向量对应的特征值为1。我们可以用NumPy来计算这个特征向量# 计算转移矩阵的特征值和特征向量 eigenvalues, eigenvectors np.linalg.eig(transition_matrix.T) # 找到特征值为1的特征向量 stationary_dist eigenvectors[:, np.isclose(eigenvalues, 1)] stationary_dist stationary_dist[:, 0] / np.sum(stationary_dist[:, 0]) print(f理论平稳分布: 晴天{stationary_dist[0]:.3f}, 雨天{stationary_dist[1]:.3f})运行这段代码你会得到类似这样的输出理论平稳分布: 晴天0.667, 雨天0.333这与我们之前通过模拟观察到的收敛值一致。这意味着无论初始天气如何长期来看这个城市将有约2/3的时间是晴天1/3的时间是雨天。6. 不同初始状态的影响一个有趣的问题是平稳分布是否依赖于初始状态让我们通过实验来验证# 定义不同的初始分布 initial_dists [ [1.0, 0.0], # 初始为晴天 [0.0, 1.0], # 初始为雨天 [0.5, 0.5] # 初始50%晴天50%雨天 ] plt.figure(figsize(10, 5)) for initial_dist in initial_dists: dist_history compute_state_distribution(initial_dist, transition_matrix, 20) plt.plot(dist_history[:, 0], --, labelf初始分布{initial_dist}) # 绘制理论平稳分布 plt.axhline(ystationary_dist[0], colorblack, linestyle-, label理论平稳分布) plt.xlabel(天数) plt.ylabel(晴天概率) plt.title(不同初始分布下的收敛行为) plt.legend() plt.grid(True) plt.show()从图中可以看到无论初始分布如何系统最终都会收敛到相同的平稳分布。这正是马尔可夫链的一个重要性质长期行为与初始状态无关。7. 马尔可夫链与MCMC的联系马尔可夫链蒙特卡洛(MCMC)方法是一类重要的采样技术它利用马尔可夫链的平稳分布性质来从复杂分布中采样。其核心思想是设计一个马尔可夫链使其平稳分布等于我们想要采样的目标分布从任意初始状态开始运行这个链经过足够多的步骤后链的状态就可以看作是从目标分布中抽取的样本虽然本文的天气模型很简单但它展示了MCMC方法的基本原理。在实际应用中MCMC可以用来解决高维空间中的复杂采样问题如贝叶斯统计中的后验分布采样。8. 进阶实验改变转移矩阵为了更深入理解马尔可夫链的行为让我们尝试修改转移矩阵观察平稳分布如何变化# 定义一个新的转移矩阵 new_transition np.array([ [0.9, 0.1], # 晴天更倾向于保持晴天 [0.2, 0.8] # 雨天也更倾向于保持雨天 ]) # 计算新的平稳分布 eigenvalues, eigenvectors np.linalg.eig(new_transition.T) new_stationary eigenvectors[:, np.isclose(eigenvalues, 1)] new_stationary new_stationary[:, 0] / np.sum(new_stationary[:, 0]) print(f新平稳分布: 晴天{new_stationary[0]:.3f}, 雨天{new_stationary[1]:.3f}) # 可视化收敛过程 initial_dist [1.0, 0.0] dist_history compute_state_distribution(initial_dist, new_transition, 50) plt.figure(figsize(10, 5)) plt.plot(dist_history[:, 0], label晴天概率) plt.plot(dist_history[:, 1], label雨天概率) plt.axhline(ynew_stationary[0], colorblack, linestyle--) plt.axhline(ynew_stationary[1], colorblack, linestyle--) plt.xlabel(天数) plt.ylabel(概率) plt.title(修改转移矩阵后的收敛行为) plt.legend() plt.grid(True) plt.show()你会注意到当状态更倾向于保持当前状态时对角线元素增大收敛速度会变慢但最终仍然会达到一个平稳分布。