马尔可夫链
作者: Jacob Schreiber 联系方式: jmschreiber91@gmail.com
马尔可夫链是描述观测序列的最简单概率模型。本质上,对于n阶马尔可夫链,每个观测值被建模为\(P(X_{t} | X_{t-1}, ..., X_{t-n})\),整个序列的概率就是每个观测值这些概率的乘积。自然地,序列中的第一个观测值无法用这种条件分布建模,因此通常用边缘分布建模。剩下的\(n-1\)个观测值也无法用完整的条件分布建模,因此需要用更小的分布来建模。
这些链在pomegranate中易于实现和使用。
[1]:
%pylab inline
import seaborn; seaborn.set_style('whitegrid')
import torch
%load_ext watermark
%watermark -m -n -p torch,pomegranate
Populating the interactive namespace from numpy and matplotlib
torch : 1.13.0
pomegranate: 1.0.0
Compiler : GCC 11.2.0
OS : Linux
Release : 4.15.0-208-generic
Machine : x86_64
Processor : x86_64
CPU cores : 8
Architecture: 64bit
初始化与拟合
初始化马尔可夫链非常简单。如果您已经拟合了分布,可以直接传入并使用该模型进行推断。如果没有拟合分布,可以指定k参数,该参数表示每个观测值概率所依赖的前序观测值数量。pomegranate会自动构建前k-1个分布以及主要的条件分布。
[2]:
from pomegranate.markov_chain import MarkovChain
model = MarkovChain(k=3)
然后可以使用fit函数将模型拟合到数据。然而,这些数据必须是三维的,维度为(n_samples, length, dimensions)。pomegranate中的大多数其他模型仅使用前两个维度。这意味着马尔可夫链可以是多元的,但多元模型会假设每个维度彼此独立。
[3]:
X = torch.tensor([[[1], [0], [0], [1]],
[[0], [1], [0], [0]],
[[0], [0], [0], [0]],
[[0], [0], [0], [1]],
[[0], [1], [1], [0]]])
model.fit(X)
[3]:
MarkovChain()
然后我们可以检查分布情况并与数据进行比较,以确保它们是正确的。
[4]:
model.distributions[0].probs[0]
[4]:
tensor([0.8000, 0.2000])
[5]:
model.distributions[1].probs[0]
[5]:
Parameter containing:
tensor([[0.5000, 0.5000],
[1.0000, 0.0000]])
[6]:
model.distributions[2].probs[0]
[6]:
Parameter containing:
tensor([[[1.0000, 0.0000],
[0.5000, 0.5000]],
[[1.0000, 0.0000],
[0.5000, 0.5000]]])
如果我们想拟合一个多元模型,只需要增加第二维的大小即可。
概率与对数概率
马尔可夫链下序列的概率是每个观测值在前序观测值条件下的概率乘积。另一种表述方式是:对于n个观测值的序列,其联合概率\(P(X_{1} ... X_{n})\)沿链式分解,对于三阶马尔可夫链等于\(P(X_{1}) P(X_{2} | X_{1}) \prod\limits_{t=3}^{n} P(X_{t} | X_{t-1}, X_{t-2})\)。
如果您的数据是多维的,每个维度的概率是独立的,并在最后相乘。如果您希望维度之间存在依赖关系,应考虑将单个维度编码以包含所有特征观测值的组合。
我们可以像计算其他模型一样轻松地计算概率和对数概率。
[7]:
model.probability(X)
[7]:
tensor([0.2000, 0.2000, 0.2000, 0.2000, 0.2000])
[8]:
model.log_probability(X)
[8]:
tensor([-1.6094, -1.6094, -1.6094, -1.6094, -1.6094])
摘要
马尔可夫链可以像其他模型一样执行数据摘要,但该数据必须具有之前提到的三个维度。此外,每个被摘要的数据块必须具有相同的长度。这意味着如果您有不同长度的数据,您必须逐个进行摘要,或者将序列分桶处理,使得每个桶中包含所有长度相同的序列。
[9]:
X = torch.randint(2, size=(30, 8, 1))
model.summarize(X[:10])
model.summarize(X[10:])
model.from_summaries()
采样
[10]:
X_sample = model.sample(100000).type(torch.float32)
[11]:
model.distributions[0].probs
[11]:
Parameter containing:
tensor([[0.4667, 0.5333]])
[12]:
X_sample[:, 0, 0].mean()
[12]:
tensor(0.5332)
[13]:
model.distributions[1].probs[0]
[13]:
Parameter containing:
tensor([[0.3571, 0.6429],
[0.5000, 0.5000]])
[14]:
X_sample[X_sample[:, 0, 0] == 1, 1, 0].mean()
[14]:
tensor(0.5039)
[15]:
model.distributions[2].probs[0]
[15]:
Parameter containing:
tensor([[[0.4000, 0.6000],
[0.2222, 0.7778]],
[[0.1250, 0.8750],
[0.7500, 0.2500]]])
[16]:
X_sample[(X_sample[:, 0, 0] == 1) & (X_sample[:, 1, 0] == 1), 2, 0].mean()
[16]:
tensor(0.2550)