Xi 的可能取值构成一个可数集合 S,称为链的状态空间。状态空间可以是任何东西:字母、数字、篮球比分或天气状况。虽然时间参数通常是离散的,但离散时间马尔可夫链的状态空间并没有广泛一致的限制,而是指在任意状态空间上的过程。然而,许多马尔可夫链的应用采用有限或可数无限的状态空间,因为它们更易于进行统计分析。
现在定义状态及其概率:即转移矩阵。请记住,矩阵将是 3 X 3,因为您有三个状态。同时,您需要定义转移路径,也可以用矩阵来实现。
# The statespace
states =["Sleep","Icecream","Run"]# Possible sequences of events
transitionName =[["SS","SR","SI"],["RS","RR","RI"],["IS","IR","II"]]# Probabilities matrix (transition matrix)
transitionMatrix =[[0.2,0.6,0.2],[0.1,0.6,0.3],[0.2,0.7,0.1]]技术支持
哦,务必确保每行概率之和为 1。写代码时留点报错信息也无妨,至少能排查问题!
ifsum(transitionMatrix[0])+sum(transitionMatrix[1])+sum(transitionMatrix[1])!=3:print("Somewhere, something went wrong. Transition matrix, perhaps?")else:print("All is gonna be okay, you should move on!! ;)")技术支持
现在来写“正片”。您将使用 numpy.random.choice 从可能的转移集合中生成一个随机样本。大多数参数不言自明,但 p 可能不是。它是一个可选参数,用于为采样集合指定概率分布,在这里就是转移矩阵。
# A function that implements the Markov model to forecast the state/mood.defactivity_forecast(days):# Choose the starting state
activityToday ="Sleep"print("Start state: "+ activityToday)# Shall store the sequence of states taken. So, this only has the starting state for now.
activityList =[activityToday]
i =0# To calculate the probability of the activityList
prob =1while i != days:if activityToday =="Sleep":
change = np.random.choice(transitionName[0],replace=True,p=transitionMatrix[0])if change =="SS":
prob = prob *0.2
activityList.append("Sleep")passelif change =="SR":
prob = prob *0.6
activityToday ="Run"
activityList.append("Run")else:
prob = prob *0.2
activityToday ="Icecream"
activityList.append("Icecream")elif activityToday =="Run":
change = np.random.choice(transitionName[1],replace=True,p=transitionMatrix[1])if change =="RR":
prob = prob *0.5
activityList.append("Run")passelif change =="RS":
prob = prob *0.2
activityToday ="Sleep"
activityList.append("Sleep")else:
prob = prob *0.3
activityToday ="Icecream"
activityList.append("Icecream")elif activityToday =="Icecream":
change = np.random.choice(transitionName[2],replace=True,p=transitionMatrix[2])if change =="II":
prob = prob *0.1
activityList.append("Icecream")passelif change =="IS":
prob = prob *0.2
activityToday ="Sleep"
activityList.append("Sleep")else:
prob = prob *0.7
activityToday ="Run"
activityList.append("Run")
i +=1print("Possible states: "+str(activityList))print("End state after "+str(days)+" days: "+ activityToday)print("Probability of the possible sequence of states: "+str(prob))# Function that forecasts the possible state for the next 2 days
activity_forecast(2)技术支持
Start state: Sleep
Possible states:['Sleep','Sleep','Run']
End state after 2days: Run
Probability of the possible sequence ofstates:0.12技术支持
defactivity_forecast(days):# Choose the starting state
activityToday ="Sleep"
activityList =[activityToday]
i =0
prob =1while i != days:if activityToday =="Sleep":
change = np.random.choice(transitionName[0],replace=True,p=transitionMatrix[0])if change =="SS":
prob = prob *0.2
activityList.append("Sleep")passelif change =="SR":
prob = prob *0.6
activityToday ="Run"
activityList.append("Run")else:
prob = prob *0.2
activityToday ="Icecream"
activityList.append("Icecream")elif activityToday =="Run":
change = np.random.choice(transitionName[1],replace=True,p=transitionMatrix[1])if change =="RR":
prob = prob *0.5
activityList.append("Run")passelif change =="RS":
prob = prob *0.2
activityToday ="Sleep"
activityList.append("Sleep")else:
prob = prob *0.3
activityToday ="Icecream"
activityList.append("Icecream")elif activityToday =="Icecream":
change = np.random.choice(transitionName[2],replace=True,p=transitionMatrix[2])if change =="II":
prob = prob *0.1
activityList.append("Icecream")passelif change =="IS":
prob = prob *0.2
activityToday ="Sleep"
activityList.append("Sleep")else:
prob = prob *0.7
activityToday ="Run"
activityList.append("Run")
i +=1return activityList
# To save every activityList
list_activity =[]
count =0# `Range` starts from the first count up until but excluding the last countfor iterations inrange(1,10000):
list_activity.append(activity_forecast(2))# Check out all the `activityList` we collected #print(list_activity)# Iterate through the list to get a count of all activities ending in state:'Run'for smaller_list in list_activity:if(smaller_list[2]=="Run"):
count +=1# Calculate the probability of starting from state:'Sleep' and ending at state:'Run'
percentage =(count/10000)*100print("The probability of starting at state:'Sleep' and ending at state:'Run'= "+str(percentage)+"%")技术支持
The probability of starting at state:'Sleep' and ending at state:'Run'=62.419999999999995%技术支持
马尔可夫链是一种数学系统,通常定义为一组随机变量,它们按照某些概率规则从一个状态转移到另一个状态。这些转移集合满足马尔可夫性质:转移到任一特定状态的概率仅取决于当前状态和经过的时间,而不取决于之前状态的序列。由于这一独特特性,马尔可夫过程被称为无记忆。
想用 Python 探索更多统计主题?请查看 DataCamp 的《Python 中的统计思维》课程!
让我们开始转移……
为什么使用马尔可夫链?
马尔可夫链在数学中用途广泛。它们被广泛应用于经济学、博弈论、通信理论、遗传学和金融。在统计学,尤其是贝叶斯统计和信息论情境中也广泛出现。谈到现实问题,它们被用来提出研究方案,例如研究机动车巡航控制系统、机场排队系统、货币汇率等。为谷歌搜索引擎最初提出的 PageRank 算法就基于马尔可夫过程。Reddit 的 Subreddit Simulator 是一个全自动的子版块,使用马尔可夫链生成随机帖文和评论,超酷!
马尔可夫链
马尔可夫链是具有马尔可夫性质的随机过程。随机过程(也常称为随机性或随机过程)是定义为一组随机变量的数学对象。马尔可夫链要么具有离散的状态空间(随机变量可能取值的集合),要么具有离散的指标集(通常表示时间)——因此存在很多马尔可夫链的变体。通常,“马尔可夫链”一词保留用于时间集合为离散的过程,即离散时间马尔可夫链(DTMC)。
离散时间马尔可夫链
离散时间马尔可夫链涉及一个系统:在每一步处于某个状态,状态在各步之间随机变化。这些步通常被看作时间点(当然也可以指物理距离或任何其他离散度量)。离散时间马尔可夫链是具有马尔可夫性质的随机变量序列 X1、X2、X3、…,其性质是:转移到下一状态的概率仅取决于当前状态,而不取决于之前的状态。用数学概率公式表示为:
Pr( Xn+1 = x | X1 = x1, X2 = x2, …, Xn = xn) = Pr( Xn+1 = x | Xn = xn)
如您所见,Xn+1 的概率只依赖于它之前的 Xn 的概率。这意味着,仅需知道上一状态就足以确定当前状态的概率分布,满足条件独立的规则(换句话说:您只需要知道当前状态即可确定下一个状态)。
Xi 的可能取值构成一个可数集合 S,称为链的状态空间。状态空间可以是任何东西:字母、数字、篮球比分或天气状况。虽然时间参数通常是离散的,但离散时间马尔可夫链的状态空间并没有广泛一致的限制,而是指在任意状态空间上的过程。然而,许多马尔可夫链的应用采用有限或可数无限的状态空间,因为它们更易于进行统计分析。
模型
马尔可夫链可用概率自动机来表示(听起来复杂,其实并不!)。系统状态的变化称为转移。与各种状态变化相关联的概率称为转移概率。概率自动机会把给定转移的概率包含在转移函数中,从而形成转移矩阵。
您可以把它看作一系列有向图,其中第 n 个图的边用从时间 n 的某一状态到时间 n+1 的其他状态的概率标注,即 Pr(Xn+1 = x | Xn = xn)。这可以读作:在给定状态 Xn 的值时转到状态 Xn+1 的概率。同样的信息也可以用从时间 n 到时间 n+1 的转移矩阵来表示。状态空间中的每个状态作为一行又作为一列出现一次,矩阵中的每个单元格告诉您从该行状态转移到该列状态的概率。
如果马尔可夫链有 N 个可能状态,则矩阵为 N x N 矩阵,其中条目 (I, J) 表示从状态 I 转移到状态 J 的概率。此外,转移矩阵必须是随机矩阵,即每一行的各项之和必须恰好为 1。为什么?因为每一行代表其自身的概率分布。
因此,该模型由状态空间、描述特定转移概率的转移矩阵,以及跨状态空间的初始状态(由初始分布给出)来表征。
一大串术语,对吧?
让我们看一个简单的示例来理解这些概念:
当 Cj 伤心时(这并不常见):她要么去跑步,要么大吃冰淇淋,要么小睡一会儿。
根据历史数据,如果她在难过的一天选择睡觉消磨时光,第二天她去跑步的概率为 60%,继续宅在床上的概率为 20%,大吃冰淇淋的概率为 20%。
当她伤心去跑步时,第二天继续跑步的概率为 60%,大吃冰淇淋为 30%,而睡觉只有 10% 的概率。
最后,当她在难过的一天沉迷于冰淇淋时,第二天继续吃冰淇淋的概率仅为 10%,去跑步的概率为 70%,而睡觉的概率为 20%。
在该状态图中展示的马尔可夫链有 3 个可能状态:sleep、run、icecream。因此,转移矩阵将是一个 3 x 3 的矩阵。请注意,从某一状态发出的箭头之和总是恰好为 1,同样,转移矩阵中每一行的各项之和也必须恰好为 1——表示一个概率分布。在转移矩阵中,各单元格与状态图中的箭头起到相同的作用。
现在您已经看过这个例子,应该对与马尔可夫链相关的不同概念有了直观认识。但是,这些理论在现实生活中如何以及在哪里可以使用呢?
结合上述示例,您现在可以回答类似这样的问题:“从状态:sleep 出发,在一个难过的 2 天周期结束时,Cj 处于跑步(状态:run)的概率是多少?”
我们来算一下:为了从状态:sleep 转到状态:run,Cj 可能在第一次(或第一天)保持在状态:sleep,然后在第二次(第二天)转到状态:run(0.2 $\cdot$ 0.6);或者第一天转到状态:run 并在第二天仍停留在那里(0.6 $\cdot$ 0.6);或者她在第一次转到状态:icecream,然后在第二次转到状态:run(0.2 $\cdot$ 0.7)。因此概率为:((0.2 $\cdot$ 0.6) + (0.6 $\cdot$ 0.6) + (0.2 $\cdot$ 0.7)) = 0.62。也就是说,如果她从状态:sleep 开始,在难过的两天后转到状态:run 的概率为 62%。
希望这能让您了解使用马尔可夫链网络可以回答的各种问题。
同时,在这一点弄清楚之后,也更容易理解马尔可夫链的一些重要性质:
提示:如果您还想看马尔可夫链的可视化讲解,请访问这个页面。
Python 中的马尔可夫链
让我们尝试用 Python 为上面的示例编写代码。尽管在实际应用中,您可能会使用一个更高效封装马尔可夫链的库,但这些代码能帮助您入门……
先导入您将用到的一些库。
现在定义状态及其概率:即转移矩阵。请记住,矩阵将是 3 X 3,因为您有三个状态。同时,您需要定义转移路径,也可以用矩阵来实现。
哦,务必确保每行概率之和为 1。写代码时留点报错信息也无妨,至少能排查问题!
现在来写“正片”。您将使用
numpy.random.choice从可能的转移集合中生成一个随机样本。大多数参数不言自明,但p可能不是。它是一个可选参数,用于为采样集合指定概率分布,在这里就是转移矩阵。您会得到一组可能的随机转移及其发生概率,起始于状态:Sleep。进一步扩展该程序,例如在相同起始状态下迭代数百次,您就能看到在任一特定状态结束的期望概率及其估计值。让我们重写
activity_forecast函数,并新增一些循环来完成这件事……我们是如何逼近目标的 62% 的?
注意 这实际上是“大数定律”,它是概率论中的一个原理,指出具有相同发生可能性的事件在足够多的试验或实例下,其发生频率会趋于稳定。换句话说,随着实验次数的增加,实际结果比率会收敛于理论或期望的结果比率。
马尔可夫的思维方式
本教程到此结束。您已经了解了马尔可夫链并看到其一些性质。简单的马尔可夫链是使用 Python 开始数据科学之旅所需的基础主题之一。若您想获取更多在 Python 中学习统计的资源,请务必查看此页面。
您是否有兴趣用 Python 探索更多统计学的实践案例?看看 DataCamp 的《统计思维案例研究》或《Python 网络分析入门》课程。