马尔可夫链¶
Ⅰ. 背景 –> Ⅱ. 原理 –> Ⅲ. 示例 –> Ⅳ. Python 代码
Ⅰ. 背景¶
“The future is independent of the past given the present” — Andrey Andreyevich Markov.
马尔可夫链(Markov Chain)是机器学习和人工智能的基石. 马尔可夫链的核心思想是,过去所有的信息都蕴含在当下,未来只基于此时此刻而和过去无关. 虽然在哲学角度,这种极端的说法不具有辩证性,但马尔可夫链在很多时间序列模型中得到广泛的应用.
Ⅱ. 原理¶
定义序列状态为 . 例如,第 0 天是晴天,第 1 天是雨天,第 2 天是雪天.
满足马尔可夫假设:
第 n+1 次出现某状态的概率独立于先前所有状态,等价于在第 n 次状态发生后第 n+1 次的概率. 这种现象称之为无后效性、无记忆性.
统计以前所有相同状态转变的频率,得到状态转移矩阵(经济上也称支付矩阵),代表状态转变发生的可能性:
其中, 代表状态
转变到状态
发生的频率.
如果此时此刻各状态发生的概率所构成的向量已知:
那么,接下来的状态发生概率向量则可以表示为:
也可以递归表出为:
Ⅲ. 示例¶
三位选手在比赛,裁判记录了得分情况如下:
局号 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
|---|---|---|---|---|---|---|---|---|---|
得分 |
3 |
3 |
2 |
3 |
1 |
2 |
1 |
3 |
1 |
例如第 7 局是选手 1 得分. 那么,状态转移链就可以写成:
转移矩阵则统计为:
例如选手 3 转移到选手 1 一共发生 2 次,即有 2 次都是选手 3 输,随之选手 1 胜.
假设第十局三位选手得分情况未知,权且都标记为 33% 的概率得分,那么:
简言之,接下来选手 1 有 33% 的概率得分,选手 2 有 25% 的概率得分,选手 3 有 41.67% 的概率得分.
Ⅳ. 代码¶
Markov.py¶
1'''
2# System --> Windows & Python3.10.0
3# File ----> Markov.py
4# Author --> Illusionna
5# Create --> 2024/02/02 22:45:54
6'''
7# -*- Encoding: UTF-8 -*-
8
9# numpy==1.26.3
10# pandas==2.0.3
11
12import os
13import numpy as np
14import pandas as pd
15from typing import Literal
16
17def cls() -> None:
18 os.system('')
19 os.system('cls')
20cls()
21
22
23class MARKOV:
24 """
25 马尔可夫类.
26 """
27 def __init__(self, statuslink:Literal['状态链字符串, 例如: abbabaacaa']) -> None:
28 """
29 初始化构造函数.
30 """
31 self.__statuslink = statuslink
32
33 def __CountAdjacentStatusFrequency(
34 statuslink:str,
35 adjacentStatus:Literal['例如: 相邻状态字符串 ab, 表示 a -> b']
36 ) -> int:
37 """
38 私有函数: 计算状态链中相邻状态 adjacentStatus 出现的频数.
39 """
40 adjacentStatusNumber = 0
41 for i in range(0, len(statuslink)-1, 1):
42 if statuslink[i:i+2] == adjacentStatus:
43 adjacentStatusNumber = adjacentStatusNumber + 1
44 return adjacentStatusNumber
45
46 def PayOffMatrix(self, probability:bool=False) -> pd.DataFrame:
47 """
48 公有函数: 计算支付矩阵.
49 """
50 uniqueItems = np.unique(list(self.__statuslink))
51 df = pd.DataFrame(index=uniqueItems, columns=uniqueItems)
52 for i in uniqueItems:
53 for j in uniqueItems:
54 df.loc[i, j] = MARKOV.__CountAdjacentStatusFrequency(self.__statuslink, i+j)
55 if probability == True:
56 df = df.div(df.sum(axis=1), axis='index')
57 self.__df = df.div(df.sum(axis=1), axis='index')
58 return df
59
60 @staticmethod
61 def NextTransitionProbability(
62 payOffMatrixProbability:pd.DataFrame,
63 occurrenceProbability:dict|Literal['状态发生概率, 如字典形式 {a:0.5, b:0.3, c:0.2}'],
64 step:int|Literal['接下来第 step 步, 默认为 1 步'] = 1,
65 significant:int|Literal['小数点精度, 自然数, 如果异常, 可降低'] = 4
66 ) -> np.array:
67 """
68 静态函数: 计算接下来的出现概率.
69 """
70 if len(occurrenceProbability.keys()) != payOffMatrixProbability.shape[0]:
71 assert print(f'\033[3m\033[33m转移矩阵 {payOffMatrixProbability.shape[0]}x{payOffMatrixProbability.shape[0]} 维, 状态发生概率字典向量 1x{len(occurrenceProbability.keys())} 维, 无法相乘.\033[0m')
72 tmp = 0
73 L = []
74 for (key, value) in occurrenceProbability.items():
75 tmp = tmp + value
76 L.append(value)
77 try:
78 np.testing.assert_approx_equal(tmp, 1.0, significant=significant)
79 except:
80 assert print(f'\033[3m\033[33m状态发生概率字典向量求和 {tmp} 不近似 1, 检查字典向量正确性或降低 significant={significant} 精度值\033[0m')
81 del tmp
82 output = np.array(L).dot(
83 np.linalg.matrix_power(
84 np.array(payOffMatrixProbability, dtype=np.float64),
85 n = step
86 )
87 )
88 return output
89
90
91if __name__ == '__main__':
92 obj = MARKOV(statuslink='332312131')
93
94 print(obj.PayOffMatrix(probability=False))
95
96 nextTransitionProbability = MARKOV.NextTransitionProbability(
97 payOffMatrixProbability = obj.PayOffMatrix(probability=True),
98 occurrenceProbability = {
99 '1': 1/3,
100 '2': 1/3,
101 '3': 1/3
102 },
103 step = 1 # 如果步长很大, 则收敛情况下, 趋于转移矩阵的极限分布.
104 )
105 print(nextTransitionProbability)