机器学习面试:从马尔可夫链到MH采样

1.马尔可夫链

有一种动物,每天只会吃喝睡,在这三种状态之间来回切换。有一个好事者,立了个摄像头,监控这个动物的每一分钟状态变化
在这里插入图片描述
做了大量的统计,得到了状态之间的转移概率,并且给出了一个状态转移的概率矩阵。
在这里插入图片描述

有了这个概率转移矩阵,我们就可以知道下一时刻的状态分布了。假设现在的状态是吃,由于当前的状态是确定的,所以概率为1,初始状态的概率分布就为[1,0,0],那么下一时刻的概率分布是什么呢?这个很好计算,根据贝叶斯定律可得


其实更准确的是 P ( S t ) = ∑ P ( S t ∣ S t − 1 ) P ( S t − 1 ) P(S_{t})=\sum P(S_{t}|S_{t-1})P(S_{t-1}) P(St)=P(StSt1)P(St1)

P ^ ( 吃 ) = P ( 吃 ∣ 吃 ) P ( 吃 ) + P ( 吃 ∣ 喝 ) P ( 喝 ) + P ( 吃 ∣ 睡 ) P ( 睡 ) P ^ ( 喝 ) = P ( 喝 ∣ 吃 ) P ( 吃 ) + P ( 喝 ∣ 喝 ) P ( 喝 ) + P ( 喝 ∣ 睡 ) P ( 睡 ) P ^ ( 睡 ) = P ( 睡 ∣ 吃 ) P ( 吃 ) + P ( 睡 ∣ 喝 ) P ( 喝 ) + P ( 睡 ∣ 睡 ) P ( 睡 ) \begin{aligned} \hat{P}(吃)&=P(吃|吃)P(吃)+P(吃|喝)P(喝)+P(吃|睡)P(睡)\\ \hat{P}(喝)&=P(喝|吃)P(吃)+P(喝|喝)P(喝)+P(喝|睡)P(睡)\\ \hat{P}(睡)&=P(睡|吃)P(吃)+P(睡|喝)P(喝)+P(睡|睡)P(睡)\\ \end{aligned} P^()P^()P^()=P()P()+P()P()+P()P()=P()P()+P()P()+P()P()=P()P()+P()P()+P()P()
整理一下可得
[ P ^ ( 吃 ) P ^ ( 喝 ) P ^ ( 睡 ) ] = [ P ( 吃 ) P ( 喝 ) P ( 睡 ) ] [ P ( 吃 ∣ 吃 ) P ( 喝 ∣ 吃 ) P ( 睡 ∣ 吃 ) P ( 吃 ∣ 喝 ) P ( 喝 ∣ 喝 ) P ( 睡 ∣ 喝 ) P ( 吃 ∣ 睡 ) P ( 喝 ∣ 睡 ) P ( 睡 ∣ 睡 ) ] \begin{aligned} \begin{bmatrix} \hat{P}(吃) & \hat{P}(喝) & \hat{P}(睡) \end{bmatrix}=\begin{bmatrix} P(吃) & P(喝) & P(睡) \end{bmatrix} \begin{bmatrix} P(吃|吃) & P(喝|吃) & P(睡|吃)\\ P(吃|喝) & P(喝|喝) & P(睡|喝)\\ P(吃|睡) & P(喝|睡) & P(睡|睡) \end{bmatrix} \end{aligned} [P^()P^()P^()]=[P()P()P()] P()P()P()P()P()P()P()P()P()
看着有种骗吃骗喝的感觉,泛化一下可知
[ P ^ ( s 1 ) P ^ ( s 2 ) P ^ ( s 3 ) ] = [ P ( s 1 ) P ( s 2 ) P ( s 3 ) ] [ P ( s 1 ∣ s 1 ) P ( s 2 ∣ s 1 ) P ( s 3 ∣ s 1 ) P ( s 1 ∣ s 2 ) P ( s 2 ∣ s 2 ) P ( s 3 ∣ s 2 ) P ( s 1 ∣ s 3 ) P ( s 2 ∣ s 3 ) P ( s 3 ∣ s 3 ) ] \begin{aligned} \begin{bmatrix} \hat{P}(s_1) & \hat{P}(s_2) & \hat{P}(s_3) \end{bmatrix}=\begin{bmatrix} P(s_1) & P(s_2) & P(s_3) \end{bmatrix} \begin{bmatrix} P(s_1|s_1) & P(s_2|s_1) & P(s_3|s_1)\\ P(s_1|s_2) & P(s_2|s_2) & P(s_3|s_2)\\ P(s_1|s_3) & P(s_2|s_3) & P(s_3|s_3)\\ \end{bmatrix} \end{aligned} [P^(s1)P^(s2)P^(s3)]=[P(s1)P(s2)P(s3)] P(s1s1)P(s1s2)P(s1s3)P(s2s1)P(s2s2)P(s2s3)P(s3s1)P(s3s2)P(s3s3)

很好,转换成了矩阵相乘
π k = π k − 1 P \pi_{k}=\pi_{k-1} P πk=πk1P
其中, π \pi π是状态分布, P P P是状态转移矩阵。我们把其他状态也算一下

import numpy as np

pi = np.array([[1,0,0]])
P = np.array([[0.52,0.36,0.12],[0.67,0.18,0.15],[0.28,0.65,0.07]])

for i in range(10):
    pi = np.dot(pi,P)
    print('pi_{} = {}'.format(i,np.round(pi,3).flatten()))

pi_0 = [0.52 0.36 0.12]
pi_1 = [0.545 0.33 0.125]
pi_2 = [0.54 0.337 0.124]
pi_3 = [0.541 0.335 0.124]
pi_4 = [0.541 0.336 0.124]
pi_5 = [0.541 0.336 0.124]
pi_6 = [0.541 0.336 0.124]
pi_7 = [0.541 0.336 0.124]
pi_8 = [0.541 0.336 0.124]
pi_9 = [0.541 0.336 0.124]

非常的神奇,到第4次的时候各个状态的分布就稳定下来了,这是特例吗,我们再试试初始状态为喝的时候是不是这个分布

import numpy as np

pi = np.array([[0,1,0]])
P = np.array([[0.52,0.36,0.12],[0.67,0.18,0.15],[0.28,0.65,0.07]])

for i in range(10):
    pi = np.dot(pi,P)
    print('pi_{} = {}'.format(i,np.round(pi,3).flatten()))

pi_0 = [0.67 0.18 0.15]
pi_1 = [0.511 0.371 0.118]
pi_2 = [0.547 0.327 0.125]
pi_3 = [0.539 0.337 0.124]
pi_4 = [0.541 0.335 0.124]
pi_5 = [0.541 0.336 0.124]
pi_6 = [0.541 0.336 0.124]
pi_7 = [0.541 0.336 0.124]
pi_8 = [0.541 0.336 0.124]
pi_9 = [0.541 0.336 0.124]

这回是第5代的时候分布变得稳定了,但是分布并没有改变。这称之为马氏平稳状态分布,还有几个定理,看着挺有道理的,但是证明过程我并不会

定理1

lim ⁡ x → ∞ P n = [ π ( 1 ) π ( 2 ) . . . π ( n ) π ( 1 ) π ( 2 ) . . . π ( n ) π ( 1 ) π ( 2 ) . . . π ( n ) π ( 1 ) π ( 2 ) . . . π ( n ) ] \lim_{x \to \infty}P^n=\begin{bmatrix} \pi(1) & \pi(2) & ... & \pi(n)\\ \pi(1) & \pi(2) & ... & \pi(n)\\ \pi(1) & \pi(2) & ... & \pi(n)\\ \pi(1) & \pi(2) & ... & \pi(n)\\ \end{bmatrix} xlimPn= π(1)π(1)π(1)π(1)π(2)π(2)π(2)π(2)............π(n)π(n)π(n)π(n)
这个比较好理解,通过上面的计算,我们知道,矩阵最后会趋于稳定状态。我也不知道为什么会这样,但是求解 P n P^n Pn之后发现的确是平稳的

import numpy as np

P = np.array([[0.52,0.36,0.12],[0.67,0.18,0.15],[0.28,0.65,0.07]])

for i in range(10):
    P = np.dot(P,P)
    print('P^{} = {}'.format(i+2, np.round(P,3)) )
定理2

π ( j ) = ∑ i = 0 ∞ π ( i ) P i j \pi(j)=\sum_{i=0}^{\infty } \pi(i)P_{ij} π(j)=i=0π(i)Pij
这个也好理解,当所有状态趋于稳定的时候,当前状态等于之前所有状态的乘以转移概率
在这里插入图片描述

定理3

π = π P \pi=\pi P π=πP
其中 π = [ π ( 1 ) , π ( 2 ) . . . π ( n ) ] \pi=[\pi(1),\pi(2)...\pi(n)] π=[π(1),π(2)...π(n)],且 ∑ π ( i ) = 1 \sum \pi(i)=1 π(i)=1 π \pi π P P P的唯一非负解
上面的计算其实也是在解释这个定理
π k = π k − 1 P \pi_k=\pi_{k-1}P πk=πk1P
可以知道,随着不断的计算,最后趋于平稳 π k = π k − 1 \pi_k=\pi_{k-1} πk=πk1

2. 细致平稳条件

这个时候其实就会想,所有的转移矩阵P都有这种现象吗?需要满足什么条件呢?有一个充分条件是细致平稳条件, 如果非周期马氏链的转移矩阵 P P P 和分布 π ( x ) \pi(x) π(x) 满足 π ( i ) P i j = π ( j ) P j i \pi(i)P_{ij} = \pi(j)P_{ji} π(i)Pij=π(j)Pji,则 π ( x ) \pi(x) π(x) 是马氏链的平稳分布,上式被称为细致平稳条件 (detailed balance condition)。

细致平稳条件是马尔可夫链平稳的状态的充分条件,如果有一个矩阵满足细致平稳条件,那么一定是马尔可夫平稳状态,但是反过来就不一定了

证明还是比较简单的,先假设 π ( i ) P i j = π ( j ) P j i \pi(i)P_{ij} = \pi(j)P_{ji} π(i)Pij=π(j)Pji,对两边求积分
∑ i π ( i ) P i j = ∑ i π ( j ) P j i \sum_i \pi(i)P_{ij} = \sum_i \pi(j)P_{ji} iπ(i)Pij=iπ(j)Pji

由于是对变量 i i i进行积分,而右侧的 π ( j ) \pi(j) π(j) i i i是无关的,因此可以提到外面来
∑ i π ( i ) P i j = π ( j ) ∑ i P j i \sum_i \pi(i)P_{ij} = \pi(j)\sum_i P_{ji} iπ(i)Pij=π(j)iPji

可以知道,矩阵的每一行的和为1,即对第 j j j行每一列求和
∑ i p j i = p j 1 + p j 2 + . . . + p j i + . . . = 1 \sum_i p_{ji}=p_{j1}+p_{j2}+...+p_{ji}+...=1 ipji=pj1+pj2+...+pji+...=1
这就变得非常有意思了
∑ i π ( i ) P i j = π ( j ) \sum_i \pi(i)P_{ij}= \pi(j) iπ(i)Pij=π(j)
我们把左式展开成两个向量点乘的形式可得
∑ i π ( i ) P i j = [ π ( 1 ) . . . π ( i ) . . . ] [ P 1 j . . . P i j . . . ] = π p j \sum_i \pi(i)P_{ij}=\begin{bmatrix} \pi(1) & ... & \pi(i) &...\\ \end{bmatrix}\begin{bmatrix} P_{1j} \\ ... \\ P_{ij} \\ ... \\ \end{bmatrix}=\boldsymbol{\pi} \boldsymbol{p_j} iπ(i)Pij=[π(1)...π(i)...] P1j...Pij... =πpj
最后可以得到
π ( j ) = π p j \pi(j)=\boldsymbol{\pi} \boldsymbol{p_j} π(j)=πpj
我们再扩展一下可知
[ π ( 1 ) , π ( 2 ) . . . π ( j ) . . . ] = [ π p 1 , π p 2 . . . π p j . . . ] π = π [ p 1 , p 2 . . . p j . . . ] π = π P \begin{aligned} [\pi(1),\pi(2)...\pi(j)...]&=[\boldsymbol{\pi} \boldsymbol{p_1},\boldsymbol{\pi} \boldsymbol{p_2}...\boldsymbol{\pi} \boldsymbol{p_j}...]\\ \boldsymbol{\pi}&=\boldsymbol{\pi}[ \boldsymbol{p_1}, \boldsymbol{p_2}... \boldsymbol{p_j}...]\\ \boldsymbol{\pi}&=\boldsymbol{\pi}P \end{aligned} [π(1),π(2)...π(j)...]ππ=[πp1,πp2...πpj...]=π[p1,p2...pj...]=πP

可以看到这个条件最终又满足了马尔可夫平稳条件,也就是说 π \pi π是马尔可夫矩阵 P P P的平稳状态,前面定理可知 π \pi π π = π P \pi=\pi P π=πP的唯一解。

3. 马尔可夫采样

对于给定的概率分布 p ( x ) p(x) p(x) , 我们希望能有便捷的方式生成它对应的样本。由于马氏链能收敛到平稳分布, 于是一个很的漂亮想法是:如果我们能构造一个转移矩阵为 P P P 的马氏链,使得该马氏链的平稳分布恰好是 p ( x ) p(x) p(x) , 那么我们从任何一个初始状态 x 0 x_0 x0 出发沿着马氏链转移, 得到一个转移序列 x 0 , x 1 , x 2 , ⋯ x n , x n + 1 . . . x_0 , x_1 , x_2 , ⋯ x_n , x_{n + 1}... x0,x1,x2,xn,xn+1..., , 如果马氏链在第 n n n 步已经收敛了,于是我们就得到了 π ( x ) \pi(x) π(x) 的样本 x n , x n + 1 . . . x_n , x_{n + 1}... xn,xn+1...

上面的分析可以看到,最终我们将一个马尔科夫链的状态转移矩阵与一个平稳分布对应了起来。如果我们得到了某个平稳分布所对应的马尔科夫链状态转移矩阵,我们就很容易采样出这个平稳分布的样本集。

可以很容易得到马尔科夫链的采样过程:

  1. 得到马尔科夫链的状态转移矩阵P,设置状态转移次数的阈值 t t t(需要达到平稳之后再采样)
  2. 从任意的简单概率分布采样得到初始状态 s 0 s_0 s0,(与初始状态无关,所以可以随便取值)
  3. 当转移次数道道平稳阈值的时候,从条件概率分布 P ( s t ∣ s t − 1 ) P(s_t|s_{t-1}) P(stst1)开始采样,得到样本 s t s_t st
  4. 样本集 s t , s t + 1 , . . . . s_{t},s_{t+1},.... st,st+1,....就是对应的平稳分布的样本集

我们就以上面的例子实现一下,加入我们的平稳分布是 π = [ 0.54 , 0.34 , 0.12 ] \boldsymbol{\pi}=[0.54, 0.34, 0.12] π=[0.54,0.34,0.12],即 P ( 吃 ) = 0.54 , P ( 喝 ) = 0.34 , P ( 睡 ) = 0.12 P(吃)=0.54,P(喝)=0.34,P(睡)=0.12 P()=0.54,P()=0.34,P()=0.12

a. 转移矩阵和阈值

我们首先需要得到状态转移矩阵P,这个已经知道了
P = [ 0.52 0.36 0.12 0.67 0.18 0.15 0.28 0.65 0.07 ] P=\begin{bmatrix} 0.52 & 0.36 & 0.12\\ 0.67 & 0.18 & 0.15\\ 0.28 & 0.65 & 0.07 \end{bmatrix} P= 0.520.670.280.360.180.650.120.150.07
状态转移阈值该怎么设置呢?我也不知道,这里就假设阈值为 t = 1000 t=1000 t=1000

b. 确定初始状态

初始状态是随便产生的,所以我这里直接确定为 t 0 = "eat" t_0=\text{"eat"} t0="eat",也就是第一个状态时

c. 从条件概率采样
import numpy as np
import random
from collections import Counter
def trans(s):
    r = random.random()
    if s == "eat":
        if r < 0.52:
            s = "eat"
        elif r < 0.52+0.36:
            s = "drink"
        else:
            s = "sleep"
    
    elif s == "drink":
        if r < 0.67:
            s = "eat"
        elif r < 0.67+0.18:
            s = "drink"
        else:
            s = "sleep"
    elif s == "sleep":
        if r < 0.28:
            s = "eat"
        elif r < 0.28+0.65:
            s = "drink"
        else:
            s = "sleep" 
    else: raise NameError("{} is not exits".format(s))   
    return s

s = "eat"

samples = []
for i in range(20000):
    s = trans(s)
    if i > 10000:
        samples.append(s)
c = Counter(samples)
print(c)

Counter({‘eat’: 5432, ‘drink’: 3331, ‘sleep’: 1236})

我们对采样的后的样本进行统计分析,可以看到跟期望的分布是一致的。也就是说,我们通过一个对一个马尔可夫转移矩阵采样得到了我们期望的分布。

d. 得到样本

samples就是我们采样的样本序列

4.MCMC采样

现实情况是,我们一般都有一个分布,我们期望对这个分布采样,但是并不知道这个分布的马尔可夫转移矩阵是什么。假设我们已经有一个转移矩阵为 Q Q Q马氏链 ( Q i j Q_{ij} Qij 表示从状态 i i i 转移到状态 j j j 的概率),通常情况下 π ( i ) Q i j ≠ π ( j ) Q j i \pi(i)Q_{ij} ≠ \pi(j)Q_{ji} π(i)Qij=π(j)Qji 也就是细致平稳条件不成立,所以 π ( x ) \pi(x) π(x) 不太可能是这个马氏链的平稳分布。
我们可否对马氏链做一个改造,使得细致平稳条件成立呢?譬如,我们引入一个 α ( i , j ) \alpha ( i , j ) α(i,j),我们希望 π ( i ) Q i j α ( i , j ) = π ( j ) Q j i α ( j , i ) \pi( i ) Q_{ij} α ( i , j ) = \pi ( j )Q_{ji}α ( j , i ) π(i)Qijα(i,j)=π(j)Qjiα(j,i) 取什么样的 α ( i , j ) \alpha( i , j ) α(i,j) 以上等式能成立呢?最简单的,按照对称性,我们可以取
α ( i , j ) = π ( j ) Q i j α ( j , i ) = π ( i ) Q j i \begin{aligned} \alpha( i , j ) = \pi(j)Q_{ij}\\ \alpha( j , i ) = \pi(i)Q_{ji} \end{aligned} α(i,j)=π(j)Qijα(j,i)=π(i)Qji
因此可以有
π ( i ) Q i j ′ = π ( j ) Q j i ′ \pi ( i ) Q^′_{ij} = \pi ( j ) Q^′_{ji} π(i)Qij=π(j)Qji
其中 Q ′ ( i , j ) = α ( i , j ) Q i j , Q ′ ( j , i ) = α ( j , i ) Q j i Q^′ ( i , j )=\alpha( i , j )Q_{ij} ,Q^′ ( j , i )=\alpha( j , i )Q_{ji} Q(i,j)=α(i,j)Qij,Q(j,i)=α(j,i)Qji

于是我们把原来具有转移矩阵 Q Q Q 的一个很普通的马氏链,改造为了具有转移矩阵 Q ′ Q^′ Q 的马氏链,而 Q ′ Q^′ Q恰好满足细致平稳条件,由此马氏链 Q ′ Q^′ Q的平稳分布就是 π ( x ) \pi( x ) π(x)

总结下来就有几点

  1. 引入接受概率 α ( i , j ) \alpha(i,j) α(i,j),使得原本的马氏链转移概率由 Q i j Q_{ij} Qij变成 Q i j ′ = α ( i , j ) Q i j Q'_{ij}=\alpha(i,j)Q_{ij} Qij=α(i,j)Qij
  2. 转移概率实现的方式是,先按照 Q i j Q_{ij} Qij生成转移状态,然后再根据接受概率 α ( i , j ) \alpha(i,j) α(i,j)的概率接受转移结果, Q Q Q可以被称之为提议矩阵,也就是说先提议按照这个矩阵转移,还要考虑这个提议是否能被接受
  3. 拒绝抽样中,如果 r > p ( z ) r>p(z) r>p(z),那么就会放弃抽样结果,所以不会将抽样的结果保留下来,不会纳入统计,而接受转移表示 r > α r>\alpha r>α,意味着 t + 1 t+1 t+1时刻状态和 t t t时刻状态相同,也应该把这个采样结果保留下来,而不是放弃。

编程的伪代码如下

  1. 确定任意选择好的马尔可夫状态转移矩阵 Q Q Q,我们想要采样的分布 π \pi π,以及状态稳定次数 T T T和采样的个数 n n n
  2. 从任意简单的概率分布采样得到初始状态 S t = s 0 S_t=s_0 St=s0
  3. t = T + 1 , T + 2 , . . . T + n t=T+1,T+2,...T+n t=T+1,T+2,...T+n,循环执行一下过程
    • 从条件概率分布 q ( s t + 1 ∣ s t ) q(s_{t+1}|s_t) q(st+1st)采样出下一个状态 y y y
    • 计算接受概率 α ( s t , y ) = π ( y ) q ( y ∣ s t ) \alpha(s_t, y)=\pi(y)q(y|s_t) α(st,y)=π(y)q(yst)
    • 从均匀分布 U ( 0 , 1 ) U(0,1) U(0,1)中生成一个随机值 r r r
    • 如果 r < α r<\alpha r<α,那么就接受生成的新状态 s t + 1 = y s_{t+1}=y st+1=y,否则依然使用原来的状态 s t + 1 = s t s_{t+1}=s_t st+1=st
  4. 样本集 ( s T + 1 , s T + 2 . . . ) (s_{T+1},s_{T+2}...) (sT+1,sT+2...)即为我们需要的平稳分布对应的样本集

5.M-H采样

MCMC采样算法已经能很漂亮的工作了,不过它有一个小的问题:马尔科夫链在转移的过程中的接受率 α ( s t , y ) = π ( y ) q ( y ∣ s t ) \alpha(s_t, y)=\pi(y)q(y|s_t) α(st,y)=π(y)q(yst) 可能偏小,(两个概率相乘肯定得到更小的值)。这样采样过程中容易原地踏步,拒绝大量的跳转,使得马尔科夫链收敛到平稳分布的速度太慢。有可能我们采样了上百万次马尔可夫链还没有收敛,这让人难以接受,怎么办呢?M-H采样解决了我们上一节MCMC采样接受率过低的问题。

假设平稳分布为 p p p,马尔可夫转移概率为 q ( i , j ) q(i,j) q(i,j),接受概率 α ( i , j ) = 0.1 , α ( j , i ) = 0.2 \alpha(i,j)=0.1,\alpha(j,i)=0.2 α(i,j)=0.1,α(j,i)=0.2如果此时满足细致平稳条件

p ( i ) q ( i , j ) × 0.1 = p ( j ) q ( j , i ) × 0.2 p(i)q(i,j)\times 0.1=p(j)q(j,i) \times 0.2 p(i)q(i,j)×0.1=p(j)q(j,i)×0.2
我们两边同时扩大5倍
p ( i ) q ( i , j ) × 0.5 = p ( j ) q ( j , i ) × 1 p(i)q(i,j)\times 0.5=p(j)q(j,i) \times 1 p(i)q(i,j)×0.5=p(j)q(j,i)×1
可以看到接受概率提升了5倍之后并没有打破细致平稳条件,MH算法对接受概率做了如下改进
α ( i , j ) = m i n { π ( j ) Q j i π ( i ) Q i j , 1 } \alpha(i,j)=min\{\frac{\pi(j)Q_{ji}}{\pi(i)Q_{ij}},1\} α(i,j)=min{π(i)Qijπ(j)Qji,1}

证明过程如下
π ( i ) Q i j ′ = π ( i ) α ( i , j ) Q i j = π ( i ) × min { π ( j ) Q j i π ( i ) Q i j , 1 } × Q i j = min { π ( j ) Q j i , π ( i ) Q i j } = π ( j ) × min { 1 , π ( i ) Q i j π ( j ) Q j i } × Q j i = π ( j ) α ( j , i ) Q j i = π ( j ) Q j i ′ \begin{aligned} \pi(i)Q'_{ij}&=\pi(i)\alpha(i,j)Q_{ij} \\ &=\pi(i)\times \text{min}\{\frac{\pi(j)Q_{ji}}{\pi(i)Q_{ij}},1\} \times Q_{ij} \\ &=\text{min}\{ \pi(j)Q_{ji},\pi(i)Q_{ij} \}\\ &=\pi(j)\times \text{min}\{ 1, \frac{\pi(i)Q_{ij}}{\pi(j)Q_{ji}}\} \times Q_{ji} \\ &= \pi(j)\alpha(j,i)Q_{ji} \\ &= \pi(j)Q'_{ji} \end{aligned} π(i)Qij=π(i)α(i,j)Qij=π(i)×min{π(i)Qijπ(j)Qji,1}×Qij=min{π(j)Qji,π(i)Qij}=π(j)×min{1,π(j)Qjiπ(i)Qij}×Qji=π(j)α(j,i)Qji=π(j)Qji

可以看到修改了 α ( i , j ) \alpha(i,j) α(i,j)依然可以满足细致平稳条件。

  • 如果取 Q i j = Q j i Q_{ij}=Q_{ji} Qij=Qji,转移矩阵是对称的,那么有 α ( i , j ) = min { π ( j ) π ( i ) , 1 } \alpha(i,j)=\text{min} \{ \frac{\pi(j)}{\pi(i)} ,1 \} α(i,j)=min{π(i)π(j),1},对应的算法称为Metropolis抽样法。
  • 如果 Q ( j ∣ i ) = g ( j ) Q(j|i)=g(j) Q(ji)=g(j),意味着下一个状态不依赖于上一个状态,则 α ( i , j ) = min { π ( j ) / g ( j ) π ( i ) / g ( i ) , 1 } \alpha(i,j)=\text{min} \{ \frac{\pi(j) / g(j)}{\pi(i)/g(i)} ,1 \} α(i,j)=min{π(i)/g(i)π(j)/g(j),1},相应的算法称为Metropolis独立抽样法

talk is cheap,show me code,代码到底该如何实现呢?

5.1 马尔可夫矩阵该怎么寻找

  • 提出这个问题是因为前面一直在说只有满足细致平稳条件的转移矩阵才能得到平稳分布,但是通常来说都不会满足细致平稳条件的,所以我们才加入了接受概率 α ( i , j ) \alpha(i,j) α(i,j)使得转移矩阵满足细致平稳条件
  • 因为转移矩阵只是表示了状态之间的概率,只要保证每一行的概率和为1即可,没有任何其他任何约束。所以随便弄一个满足的矩阵都可以
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import random
from itertools import accumulate
from collections import Counter

# 随便编一个期望分布
pi = [1,2,3,4,5,5,5,5,5,5,4,3,2,1]
sum_pi = sum(pi)
pi = list(map(lambda x : x / sum_pi, pi))
n_status = len(pi)
# plt.bar(range(n_status),pi)
# plt.show()

# 创建一个随机二维矩阵
Q_random = [[round(random.random(),2) for _ in range(n_status)] for _ in range(n_status)]
# 对矩阵中每一行求和
Q_sum = [sum(v) for v in Q_random]
# 随机二维矩阵中每一行元素归一化,最后求和为1
Q = [] # 每一行和为1的马尔可夫转移矩阵
for q,q_sum in zip(Q_random,Q_sum):
    Q.append(list(map(lambda x : x/q_sum, q)))


def transPxy(i, Q, pi):
	# i是当前状态
    j = i # j是下一状态
    Q_s = Q[i] # 取出转移矩阵i这一行,表示的是状态i转移到其他各个状态的概率分布
    
    # 使用轮盘赌的方式来生成下一个转移状态
    Q_norm = list(accumulate(Q_s))
    r = random.random()
    for k in range(n_status):
        if r < Q_norm[k]:
            j = k
            break
    # 通过均匀分布产生一个随机数r
    r = random.random()
    # 求接受概率alpha
    a = min((pi[j]*Q[j][i]) / (pi[i]*Q[i][j]),1)
    if r < a: # 如果这个数小于alpha,就返回新的状态,相当于是i->j
        return j
    else:
        return i # 如果大于alpha,表示拒绝了转移,返回当前的状态,可以认为是i->i

# 随机生成初始状态,说的专业点就是通过均匀分布采样出初始状态
s = random.randint(0, n_status-1)
T = 100000 # 为了让马尔可夫链区域平稳,先采样T次
for _ in range(T):
    s = transPxy(s, Q, pi)

n_samples = 100000 # 想要采样的样本数
samples = []
for _ in range(n_samples):
    s = transPxy(s, Q, pi)
    samples.append(s)

res = Counter(samples)
fenbu = []
for i in range(n_status):
    fenbu.append(round(res[i] / n,2))

print(pi)
print(fenbu)

自己根据上面的公式写出了这段代码,跟预期是符合的,不得不感慨有的人真的聪明。

5.2 采样分布是连续的,转移矩阵是什么样子的

离散状态下,每一行的概率求和为1
∑ j q ( i , j ) = 1 \sum_j q(i,j)=1 jq(i,j)=1

123下一状态 j j j
1 q ( 1 , 1 ) q(1,1) q(1,1) q ( 1 , 2 ) q(1,2) q(1,2) q ( 1 , 3 ) q(1,3) q(1,3) q ( 1 , j ) q(1,j) q(1,j)
2 q ( 2 , 1 ) q(2,1) q(2,1) q ( 2 , 2 ) q(2,2) q(2,2) q ( 2 , 3 ) q(2,3) q(2,3) q ( 2 , j ) q(2,j) q(2,j)
当前状态 i i i q ( i , 1 ) q(i,1) q(i,1) q ( i , 2 ) q(i,2) q(i,2) q ( i , 3 ) q(i,3) q(i,3) q ( i , j ) q(i,j) q(i,j)

当采样分布是连续的时候,其实可以认为每一个实数其实就是一个状态,当然我不可能把所有实数都写出来,下面只是一个示例

0.0010.0020.003下一状态 y y y
当前状态 x x x q ( x , 0.001 ) q(x,0.001) q(x,0.001) q ( x , 0.002 ) q(x,0.002) q(x,0.002) q ( x , 0.003 ) q(x,0.003) q(x,0.003) q ( x , y ) q(x,y) q(x,y)

由于变成连续的了,因此使用 x x x表示当前状态,使用 y y y表示下一状态, i , j i,j i,j一般来表示离散状态的。这个时候,状态转移矩阵的每一行就是一个连续的实数分布了,这样每一行可以用一个概率密度pdf函数表示,例如正态分布
q ( x , y ) ∼ N ( μ , σ 2 ) q(x,y)\sim N(\mu,\sigma^2) q(x,y)N(μ,σ2)
很明显,概率分布求积分也肯定为1。也就是说,我们可以使用正态分布来生成了一个实数 y y y,从而表示下一个转移状态

12下一状态 y y y
当前状态 x x x X 1 ∼ N ( μ , σ 2 ) X_1 \sim N(\mu,\sigma^2) X1N(μ,σ2) X 2 ∼ N ( μ , σ 2 ) X_2 \sim N(\mu,\sigma^2) X2N(μ,σ2) X j ∼ N ( μ , σ 2 ) X_j \sim N(\mu,\sigma^2) XjN(μ,σ2)

每一行的 μ , σ \mu,\sigma μ,σ都是可以随机指定的,但是指定之后不能再改变。但是你想一想,纵向来看,当前状态也是整个实数集,那么 μ , σ \mu,\sigma μ,σ也是无穷多个,这该怎么指定呢?有一种非常简单的方法就是把所有的 μ = 0 , σ = 1 \mu=0, \sigma=1 μ=0,σ=1。我现在还不知道这种有没有问题,但是我看到别人用的是另一种方法让 μ = x , σ = 1 \mu=x,\sigma=1 μ=x,σ=1,也就是说当前状态 i = x i=x i=x的转移概率是 q ( x , y ) ∼ N ( x , 1 ) q(x,y)\sim N(x,1) q(x,y)N(x,1)

正态分布还有一个好处是对称的,假如我们现在有两个状态 x , y x,y x,y,两个状态对应的转移概率分布如下
X = 1 2 π e x p ( − ( z − x ) 2 2 ) Y = 1 2 π e x p ( − ( z − y ) 2 2 ) \begin{aligned} X=\frac{1}{\sqrt{2\pi}}exp({-\frac{(z-x)^2}{2}}) \\ Y=\frac{1}{\sqrt{2\pi}}exp({-\frac{(z-y)^2}{2}}) \\ \end{aligned} X=2π 1exp(2(zx)2)Y=2π 1exp(2(zy)2)

我们从状态 x x x转移到状态 y y y的概率可以由正态分布 X X X计算
p ( x , y ) = 1 2 π e x p ( − ( y − x ) 2 2 ) p(x,y)=\frac{1}{\sqrt{2\pi}}exp({-\frac{(y-x)^2}{2}}) p(x,y)=2π 1exp(2(yx)2)
我们从状态 y y y转移到状态 x x x的概率可以由正态分布 Y Y Y计算
p ( y , x ) = 1 2 π e x p ( − ( x − y ) 2 2 ) p(y,x)=\frac{1}{\sqrt{2\pi}}exp({-\frac{(x-y)^2}{2}}) p(y,x)=2π 1exp(2(xy)2)

下一状态x下一状态 y y y
当前状态 x x x N ( x , 1 ) N(x,1) N(x,1) N ( x , 1 ) ∼ e x p ( x − y ) 2 N(x,1)\sim exp(x-y)^2 N(x,1)exp(xy)2
当前状态 y y y N ( y , 1 ) ∼ e x p ( y − x ) 2 N(y,1)\sim exp(y-x)^2 N(y,1)exp(yx)2 N ( y , 1 ) N(y,1) N(y,1)

上面这个表格会更加清晰一点,我们最终得到的 q ( x , y ) = q ( y , x ) q(x,y)=q(y,x) q(x,y)=q(y,x),如果是对称的话,接受概率可以进行化简
α ( x , y ) = π ( y ) q ( y , x ) π ( x ) q ( x , y ) = π ( y ) π ( x ) \begin{aligned} \alpha(x,y)&=\frac{\pi(y)q(y,x)}{\pi(x)q(x,y)}\\ &=\frac{\pi(y)}{\pi(x)} \end{aligned} α(x,y)=π(x)q(x,y)π(y)q(y,x)=π(x)π(y)

我们接着实现代码

import random
import math
import matplotlib.pyplot as plt
import numpy as np

def pi(x):
    y = (0.3*np.exp(-(x-0.3)**2)+0.7*np.exp(-(x-2.)**2/0.3)) / 1.123
    return y


def transPxy(x, pi):
    # 从正态分布N(x,1)中采样,得到下一个状态
    y = np.random.normal(loc=x, scale=1, size=1)
    y = y[0]
    alpha = min(1, pi(y)/pi(x))
    r = random.random()
    if r < alpha:
        return y
    else:
        return x

# 随机生成初始状态,说的专业点就是通过均匀分布采样出初始状态
x = random.random()
T = 10000 # 为了让马尔可夫链区域平稳,先采样T次
for _ in range(T):
    x = transPxy(x, pi)

n_samples = 100000 # 想要采样的样本数
samples = []
for _ in range(n_samples):
    x = transPxy(x, pi)
    samples.append(x)

x = np.arange(-2,4,0.01)

plt.plot(x,pi(x),color='r')
plt.hist(samples,bins=100,density=True)
plt.show()


  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值