第19章 马尔可夫链蒙特卡罗法
习题19.1
用蒙特卡罗积分法求
解答:
解答思路:
- 给出蒙特卡罗积分计算方法。
- 将被积分的函数分解,得到其数学期望表示形式。
- 自编程实现,使用样本均值求近似计算积分。
解答步骤
第1步:蒙特卡罗积分计算方法
根据书中第19.1.3节的关于蒙特卡罗积分法的描述:
假设有一个函数
,目标是计算该函数的积分 如果能够将函数
分解成一个函数 和一个概率密度函数 的乘积的形式, 那么就有 于是函数
的积分可以表示为函数 关于概率密度函数 的数学期望。实际上, 给定一个概率密度函数 , 只要取 ,就可得上式。就是说, 任何一个函数的积分都可以表示为某一个函数的数学期望的形式。而函数的数学期望又可以通过函数的样本均值估计。于是,就可以利用样本均值来近似计算积分。
第2步:将所积分的函数分解,其得到数学期望表示形式
取概率密度函数为标准正态分布的密度函数
将原函数积分表示为函数
第3步:自编程实现,使用样本均值求近似计算积分
import numpy as np
class MonteCarloIntegration:
def __init__(self, func_f, func_p):
# 所求期望的函数
self.func_f = func_f
# 抽样分布的概率密度函数
self.func_p = func_p
def solve(self, num_samples):
"""
蒙特卡罗积分法
:param num_samples: 抽样样本数量
:return: 样本的函数均值
"""
samples = self.func_p(num_samples)
vfunc_f = lambda x: self.func_f(x)
vfunc_f = np.vectorize(vfunc_f)
y = vfunc_f(samples)
return np.sum(y) / num_samplesdef func_f(x):
"""定义函数f"""
return x ** 2 * np.sqrt(2 * np.pi)
def func_p(n):
"""定义在分布上随机抽样的函数g"""
return np.random.standard_normal(int(n))
# 设置样本数量
num_samples = 1e6
# 使用蒙特卡罗积分法进行求解
monte_carlo_integration = MonteCarloIntegration(func_f, func_p)
result = monte_carlo_integration.solve(num_samples)
print("抽样样本数量:", num_samples)
print("近似解:", result)抽样样本数量: 1000000.0
近似解: 2.5071535842506965
习题19.2
证明如果马尔可夫链是不可约的,且有一个状态是非周期的,则其他所有状态也是非周期的,即这个马尔可夫链是非周期的。
解答:
解答思路:
- 给出马尔科夫链的基本定义
- 给出不可约的定义
- 给出非周期的定义
- 证明如果马尔可夫链是不可约的,则所有状态周期都相同
- 证明原命题
解答步骤:
第1步:马尔可夫链
根据书中第19章的定义19.1马尔科夫链的基本定义:
定义19.1(马尔科夫链) 考虑一个随机变量的序列
,这里 表示时刻 的随机变量, 。每个随机变量 的取值集合相同,称为状态空间,表示为 。随机变量可以是离散的,也可以是连续的。以上随机变量的序列构成随机过程(stochastic process)。 假设在时刻0的随机变量
遵循概率分布 ,称为初始状态分布。在某个时刻 的随机变量 与前一时刻的随机变量 之间有条件分布 ,如果 只依赖于 ,而不依赖于过去的随机变量 ,这一性质称为马尔可夫性,即 具有马尔可夫性的随机序列
称为马尔可夫链,或马尔可夫过程。条件概率分布 称为马尔可夫链的转移概率分布。转移概率分布决定了马尔可夫链的特性。
根据书中第19.2.2节的状态转移概率:
表示时刻0从状态
出发,时刻 到达状态 的 步转移概率。
第2步:不可约的定义
根据书中第19章的定义19.3的不可约的定义:
定义19.3(不可约) 设有马尔可夫链
,状态空间为 ,对于任意状态 ,如果存在一个时刻 满足 也就是说,时刻0从状态
出发, 时刻 到达状态 的概率大于0,则称此马尔可夫链 是不可约的(irreducible),否则称马尔可夫链是可约的(reducible)。
第3步:非周期的定义
根据书中第19章的定义19.4的非周期的定义:
定义19.4(非周期) 设有马尔可夫链
,状态空间为 ,对于任意状态 ,如果时刻0从状态 出发, 时刻返回状态的所有时间长 的最大公约数是1,则称此马尔可夫链 是非周期的(aperiodic),否则称马尔可夫链是周期的(periodic)。
第4步:证明如果马尔可夫链是不可约的,则所有状态周期都相同
如果该马尔可夫链是不可约的,则对于
根据状态转移概率的定义,有
可得
则必然存在
假设状态
所以
又因为
故
第5步:证明原命题
在不可约的马尔可夫链中如果有一个状态的是非周期的,即其周期为1,则其他所有状态的周期也为1,所以该马尔可夫链是非周期的。原命题得证。
习题19.3
验证具有以下转移概率矩阵的马尔可夫链是可约的,但是非周期的。
解答:
解答思路:
- 给出平稳分布的定义
- 计算该马尔可夫链的平稳分布
- 验证该马尔可夫链是可约的
- 验证该马尔可夫链是非周期的
解答步骤:
第1步:平稳分布的定义
根据书中第19章的定义19.2的平稳分布的定义:
定义19.2(平稳分布) 设有马尔可夫链
,其状态空间为 ,转移概率矩阵为 ,如果存在状态空间 上的一个分布 使得
则称
为马尔可夫链 的平稳分布。
根据书中第19章的引理19.1:
引理19.1 给定一个马尔可夫链 $ X = {X_0, X_1, \cdots, X_t, \cdots }$,状态空间为
,转移概率矩阵为 ,则分布 为 的平稳分布的充分必要条件是 $ \pi = (\pi_1, \pi_2, \cdots )^T$ 是下列方程组的解:
第2步:计算该马尔可夫链的平稳分布
设平稳分布为
解方程组,得到唯一的平稳分布
第3步:验证该马尔可夫链是不可约的
此马尔可夫链,转移到状态4后,就在该状态上循环跳转,不能到达状态1、状态2和状态3,最后停留在状态4,故该马尔可夫链是可约的。
第4步:验证该马尔可夫链是非周期的
- 对于状态1,它返回状态1的最小时间长为1,故其所有时间长的最大公约数是1;
- 对于状态2,它通过
返回状态2的时间长为2,通过 返回状态2的时间长为3,故其所有时间长的最大公约数是1 - 对于状态3,它通过
返回状态3的时间长为2,通过 返回状态3的时间长为5,故其所有时间长的最大公约数是1 - 对于状态4,它返回状态4的最小时间长为1,故其所有时间长的最大公约数是1
综上,该马尔可夫链是非周期的。
习题19.4
验证具有以下转移概率矩阵的马尔可夫链是不可约的,但是周期性的。
解答:
解答思路:
- 计算该马尔可夫链的平稳分布
- 验证该马尔可夫链是不可约的
- 验证该马尔可夫链是周期性的
解答步骤:
第1步:计算该马尔可夫链的平稳分布
根据书中第19章的定义19.2的平稳分布的定义和引理19.1(同习题19.4),设平稳分布为
解方程组,得到唯一的平稳分布
第2步:验证该马尔可夫链是不可约的
观察到平稳分布各项均大于0,说明此马尔可夫链从任意状态出发,经过若干步之后,可以到达任意状态,故该马尔可夫链是不可约的。
第3步:验证该马尔可夫链是周期性的
该马尔可夫链的转移仅发生在相邻奇偶状态之间,从每个状态出发,返回该状态的时刻都是2的倍数:
习题19.5
证明可逆马尔可夫链一定是不可约的。
解答:
解答思路
- 给出可逆马尔可夫链的定义
- 构造特殊的可逆马尔可夫链
- 验证其是可约的,原命题不成立
解答步骤
第1步:可逆马尔可夫链
根据书中第19章的定义19.6的可逆马尔可夫链的定义:
定义19.6(可逆马尔可夫链) 设有马尔可夫链 $ X = {X_0, X_1, \cdots, X_t, \cdots }$,状态空间为
,转移概率矩阵为 ,如果有状态分布 ,对于任意状态 ,对任意一个时刻 满足 或简写为
则称此马尔可夫链
为可逆马尔可夫链,上式称为细致平衡方程。
第2步:构造特殊的可逆马尔可夫链
对于以单位矩阵做转移矩阵的马尔可夫链,其转移矩阵P
对于任意平稳分布
第3步:验证其是可约的,原命题被证伪
对于任意状态
这与原命题矛盾,故原命题不成立。
习题19.6
从一般的Metropolis-Hastings算法推导出单分量Metropolis-Hastings算法。
解答:
解答思路:
- 给出一般的Metropolis-Hastings算法
- 给出单分量Metropolis-Hastings算法的基本思想
- 推导单分量Metropolis-Hastings算法
解答步骤:
第1步:一般的Metropolis-Hastings算法
根据书中第19章的算法19.2的Metropolis-Hastings算法:
算法19.2(Metropolis-Hastings算法)
输入:抽样的目标分布的密度函数,函数 ;
输出:的随机样本 ,函数样本均值 ;
参数:收敛步数,迭代步数 。
(1)任意选择一个初始值
(2) 对循环执行
(a)设状态,按照建议分布 随机抽取一个候选状态 。
(b)计算接受概率(c)从区间
中按均匀分布随机抽取一个数 。
若,则状态 ;否则,状态 。
(3)得到样本集合 $ { x_{m+1}, x_{m+2}, \cdots, x_n }$
计算
2. 理解单分量 Metropolis-Hastings 算法的基本思想
根据书中第19.4.3节的单分量Metropolis-Hastings算法:
在 Metropolis-Hastings 算法中,通常需要对多元变量分布进行抽样,有时对多元变量分布的抽样是困难的。可以对多元变量的每一变量的条件分布依次分别进行抽样,从而实现对整个多元变量的一次抽样, 这就是单分量 Metropolis-Hastings 算法。
假设马尔可夫链的状态由维随机变量表示 其中
表示随机变量 的第 个分量, ,而 表示马尔可夫链在时刻 的状态 其中
是随机变量 的第 个分量, 。
为了生成容量为的样本集合 ,单分量 Metropolis-Hastings 算法由下面的 步迭代实现 Metropolis-Hastings 算法的一次迭代。
设在第次迭代结束时分量 的取值为 ,在第 次迭代的第 步,对分量 根据 Metropolis-Hastings 算法更新,得到其新的取值 。首先,由建议分布 抽样产生分量 的候选值 ,这里 表示在第 次迭代的第 步后的 除去 的所有值,即 其中分量
已经更新。然后,按照接受概率 抽样决定是否接受候选值
。如果 被接受,则令 ;否则令 。其余分量在第 步不改变。马尔可夫链的转移概率为
3. 推导单分量 Metropolis-Hastings 算法
单分量 Metropolis-Hastings 算法
输入:抽样的目标分布的密度函数
输出:
参数:收敛步数
(1)任意选择一个初始值
(2)对
设在第
(a)对
(i)在第
这里
(ii)计算接受概率
(iii)从区间
若
(3)得到样本集合
计算
习题19.7
假设进行伯努利实验,后验概率为
解答:
解题思路
- 给出 Metropolis-Hastings 算法
- 写出目标分布和建议分布
- 自编程实现 Metropolis-Hastings 算法求解
解答步骤
第1步:Metropolis-Hastings 算法
Metropolis-Hastings 算法的步骤详见书中第373~374页算法19.2。
第2步:写出目标分布和建议分布
根据题意,可知后验概率
则后验概率分布
可取建议分布为
第3步:自编程实现 Metropolis-Hastings 算法求解
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import beta, binom
class MetropolisHastings:
def __init__(self, proposal_dist, accepted_dist, m=1e4, n=1e5):
"""
Metropolis Hastings
:param proposal_dist: 建议分布
:param accepted_dist: 接受分布
:param m: 收敛步数
:param n: 迭代步数
"""
self.proposal_dist = proposal_dist
self.accepted_dist = accepted_dist
self.m = m
self.n = n
@staticmethod
def __calc_acceptance_ratio(q, p, x, x_prime):
"""
计算接受概率
:param q: 建议分布
:param p: 接受分布
:param x: 上一状态
:param x_prime: 候选状态
"""
prob_1 = p.prob(x_prime) * q.joint_prob(x_prime, x)
prob_2 = p.prob(x) * q.joint_prob(x, x_prime)
alpha = np.min((1., prob_1 / prob_2))
return alpha
def solve(self):
"""
Metropolis Hastings 算法求解
"""
all_samples = np.zeros(self.n)
# (1) 任意选择一个初始值
x_0 = np.random.random()
for i in range(int(self.n)):
x = x_0 if i == 0 else all_samples[i - 1]
# (2.a) 从建议分布中抽样选取
x_prime = self.proposal_dist.sample()
# (2.b) 计算接受概率
alpha = self.__calc_acceptance_ratio(self.proposal_dist, self.accepted_dist, x, x_prime)
# (2.c) 从区间 (0,1) 中按均匀分布随机抽取一个数 u
u = np.random.uniform(0, 1)
# 根据 u <= alpha,选择 x 或 x_prime 进行赋值
if u <= alpha:
all_samples[i] = x_prime
else:
all_samples[i] = x
# (3) 随机样本集合
samples = all_samples[self.m:]
# 函数样本均值
dist_mean = samples.mean()
# 函数样本方差
dist_var = samples.var()
return samples[self.m:], dist_mean, dist_var
@staticmethod
def visualize(samples, bins=50):
"""
可视化展示
:param samples: 抽取的随机样本集合
:param bins: 频率直方图的分组个数
"""
fig, ax = plt.subplots()
ax.set_title('Metropolis Hastings')
ax.hist(samples, bins, alpha=0.7, label='Samples Distribution')
ax.set_xlim(0, 1)
ax.legend()
plt.show()class ProposalDistribution:
"""
建议分布
"""
@staticmethod
def sample():
"""
从建议分布中抽取一个样本
"""
# B(1,1)
return beta.rvs(1, 1, size=1)
@staticmethod
def prob(x):
"""
P(X = x) 的概率
"""
return beta.pdf(x, 1, 1)
def joint_prob(self, x_1, x_2):
"""
P(X = x_1, Y = x_2) 的联合概率
"""
return self.prob(x_1) * self.prob(x_2)class AcceptedDistribution:
"""
接受分布
"""
@staticmethod
def prob(x):
"""
P(X = x) 的概率
"""
# Bin(4, 10)
return binom.pmf(4, 10, x)import warnings
warnings.filterwarnings("ignore")
# 收敛步数
m = 1000
# 迭代步数
n = 10000
# 建议分布
proposal_dist = ProposalDistribution()
# 接受分布
accepted_dist = AcceptedDistribution()
metropolis_hastings = MetropolisHastings(proposal_dist, accepted_dist, m, n)
# 使用 Metropolis-Hastings 算法进行求解
samples, dist_mean, dist_var = metropolis_hastings.solve()
print("均值:", dist_mean)
print("方差:", dist_var)
# 对结果进行可视化
metropolis_hastings.visualize(samples, bins=20)均值: 0.4214185307283342
方差: 0.019466091658555083

习题19.8
设某试验可能有五种结果,其出现的概率分别为
模型含有两个参数
其中
解答
解题思路
- 给出吉布斯抽样算法
- 定义目标概率的密度函数和各参数的满条件分布
- 自编程实现吉布斯抽样算法进行求解
解答步骤
第1步:吉布斯抽样算法
根据书中第19.5.2节的吉布斯抽样算法的描述:
算法19.3(吉布斯抽样)
输入:目标概率分布的密度函数,函数 ;
输出:的随机样本 ,函数样本均值 ;
参数:收敛步数,迭代步数 。
(1)初始化。给出初始样本。
(2)对循环执行
设第次迭代结束时的样本为 ,则第 次迭代进行如下几步操作: 得到第
次迭代值 。
(3)得到样本集合(4)计算
第2步:定义目标概率的密度函数和各参数的满条件分布
根据题意,可知目标概率的分布函数为
可得:
求解得到:
则参数
第2步:编程实现吉布斯抽样算法进行求解
import matplotlib.pyplot as plt
import numpy as np
class GibbsSampling:
def __init__(self, target_dist, j, m=1e4, n=1e5):
"""
Gibbs Sampling 算法
:param target_dist: 目标分布
:param j: 变量维度
:param m: 收敛步数
:param n: 迭代步数
"""
self.target_dist = target_dist
self.j = j
self.m = int(m)
self.n = int(n)
def solve(self):
"""
Gibbs Sampling 算法求解
"""
# (1) 初始化
all_samples = np.zeros((self.n, self.j))
# 任意选择一个初始值
x_0 = np.random.random(self.j)
# x_0 = np.array([0.5, 0.5])
# (2) 循环执行
for i in range(self.n):
x = x_0 if i == 0 else all_samples[i - 1]
# 满条件分布抽取
for k in range(self.j):
x[k] = self.target_dist.sample(x, k)
all_samples[i] = x
# (3) 得到样本集合
samples = all_samples[self.m:]
# (4) 计算函数样本均值
dist_mean = samples.mean(0)
dist_var = samples.var(0)
return samples[self.m:], dist_mean, dist_var
@staticmethod
def visualize(samples, bins=50):
"""
可视化展示
:param samples: 抽取的随机样本集合
:param bins: 频率直方图的分组个数
"""
fig, ax = plt.subplots()
ax.set_title('Gibbs Sampling')
ax.hist(samples[:, 0], bins, alpha=0.7, label='$\\theta$')
ax.hist(samples[:, 1], bins, alpha=0.7, label='$\\eta$')
ax.set_xlim(0, 1)
ax.legend()
plt.show()class TargetDistribution:
"""
目标概率分布
"""
def __init__(self):
# 联合概率值过小,可对建议分布进行放缩
self.c = self.__select_prob_scaler()
def sample(self, x, k=0):
"""
使用接受-拒绝方法从满条件分布中抽取新的分量 x_k
"""
theta, eta = x
if k == 0:
while True:
new_theta = np.random.uniform(0, 1 - eta)
alpha = np.random.uniform()
if (alpha * self.c) < self.__prob([new_theta, eta]):
return new_theta
elif k == 1:
while True:
new_eta = np.random.uniform(0, 1 - theta)
alpha = np.random.uniform()
if (alpha * self.c) < self.__prob([theta, new_eta]):
return new_eta
def __select_prob_scaler(self):
"""
选择合适的建议分布放缩尺度
"""
prob_list = []
step = 1e-3
for theta in np.arange(step, 1, step):
for eta in np.arange(step, 1 - theta + step, step):
prob = self.__prob((theta, eta))
prob_list.append(prob)
searched_max_prob = max(prob_list)
upper_bound_prob = searched_max_prob * 10
return upper_bound_prob
@staticmethod
def __prob(x):
"""
P(X = x) 的概率
"""
theta = x[0]
eta = x[1]
p1 = (theta / 4 + 1 / 8) ** 14
p2 = theta / 4
p3 = eta / 4
p4 = (eta / 4 + 3 / 8)
p5 = 1 / 2 * (1 - theta - eta) ** 5
p = (p1 * p2 * p3 * p4 * p5)
return p# 收敛步数
m = 1e3
# 迭代步数
n = 1e4
# 目标分布
target_dist = TargetDistribution()
# 使用 Gibbs Sampling 算法进行求解
gibbs_sampling = GibbsSampling(target_dist, 2, m, n)
samples, dist_mean, dist_var = gibbs_sampling.solve()
print(f'theta均值:{dist_mean[0]}, theta方差:{dist_var[0]}')
print(f'eta均值:{dist_mean[1]}, eta方差:{dist_var[1]}')
# 对结果进行可视化
GibbsSampling.visualize(samples, bins=20)theta均值:0.5200493163002229, theta方差:0.018093366768230302
eta均值:0.12315468306475189, eta方差:0.006544978550617504

