一、认识马尔科夫链(Markov Chains)
1.定义
从数学意义上看,第n+1步状态为x的概率仅取决于第n步的状态,而不只是之前的所有状态。
2.例子
假设现在有三种食物,披萨、汉堡和热狗,存在连续的三天,每天只能选择一种食物。若当前一天吃披萨,则下一天吃披萨的概率是0,吃汉堡的概率是0.3,吃热狗的概率是0.7,如果当前一天吃汉堡和热狗,又会有不同的结果,最终构成的状态图就是马尔科夫链。
下面是三种状态转换所示图:
思考这样一个问题, 若存在连续的有限天,我们想计算选择每种食物的概率,也就是状态的概率分布。这里显然可以使用这有限天中,每种食物出现的次数除以总数。但单纯的模拟计算所需的时间较长,下面有线性代数特征矩阵相关知识来理解。
观察上面的状态转换图,可以理解为一个有向图,我们用如下的特征矩阵A表示。
由特征方程进一步得到 ,当=1时可得稳定状态。
由一个状态的转出概率之和为1,可得:
由(2)(3)联立,,这种方法可以更快速的计算出结果。
二、马尔科夫链的两种状态以及性质
两种状态
常返态:从某一状态出发以概率1在有限时间内放回该状态。
暂留态:从某一状态以一个正的概率不再返回该状态。
性质1
不可约性:说明马尔科夫链从一个状态可以转移到任意一个状态,也即状态转换图是强联通的,反之就是可约的。因此马尔科夫链的不可约性也称为连通性。
性质2
高阶转移矩阵与稳态的关系: 即想计算从状态i经过n步到达状态j的概率,只需要看n阶转移矩阵的第i行第j列。
性质3
的无穷次幂需要满足一定的条件才能收敛:1.非可约 2.周期性
三、马尔科夫链的应用与代码
应用问题1:给定三个单词pizza、humburger、hot dog,假设起始情况已知,每天只能选择一种食物,使用马尔科夫链预测n天内可能的一种选择。
代码如下:
import random # 该模块提供了生成随机数的函数的方法,包含生成随机整数、浮点数和随机序列。
# 定义马尔科夫链
markov_chain = {
"hello": {"world": 1.0},
"world": {"hello": 0.5, "there": 0.5},
"there": {"world": 1.0}
}
# 定义初始状态和生成的文本长度
initial_state = "hello"
text_length = int(input("Please enter the length of the text to be predicted:\n"))
# 生成文本
current_state = initial_state
text = [current_state]
for i in range(text_length):
next_states = list(markov_chain[current_state].keys())
probabilities = list(markov_chain[current_state].values())
current_state = random.choices(next_states, weights=probabilities)[0]
text.append(current_state)
print("Generated text:", " ".join(text))
运行结果为:
已连接到 pydev 调试器(内部版本号 213.6461.77)Please enter the length of the text to be predicted:
Generated text: hello world there world there world hello world there world hello
进程已结束,退出代码0
应用问题2:使用马尔科夫链构造语言模型,用语言模型去生成与给定数据集相似的文本。
实现上述要求的方法有很多,比如RNNS、VAEs、GANs,今天我们使用马尔科夫链实现。 代码如下:
import re # re模块提供各种正则表达式的匹配操作,在文本解析、复杂字符串分析和信息提取时常用。
import random
## 马尔科夫链,生产句子:
## 尝试将使用不同句子进行训练,生成句子。
class MarkovLinked:
__dic = dict()
__keyStart = "#start#"
__keyEnd = "#end#"
__maxLoop = 30
__topicMaxLenth = 1000 # 句子长度
def __init__(self):
self.__dic[self.__keyStart] = []
def printMarkovLinked(self):
for key in self.__dic.keys():
print('%s\t%s' % (key, self.__dic[key]))
def append(self, content):
## 训练文本加入到 markov 连中
## :param content:
## :return:
# clear
content = re.sub('\s|\n|\t', '', content)
ie = self.getIterator(content)
i = 0
for x in ie:
key = '%s%s' % (x[0], x[1])
val = x[2]
if key not in self.__dic.keys():
self.__dic[key] = []
self.__dic[key].append(val)
# 记录开始 key
if i == 0:
self.__dic[self.__keyStart].append(key)
i += 1
pass
def getIterator(self, txt):
ct = len(txt)
if ct < 3:
return
for i in range(ct - 2 + 1):
w1 = txt[i]
w2 = txt[i + 1]
w3 = txt[i + 2] if i + 2 < ct else self.__keyEnd
yield (w1, w2, w3)
def topicBuilder(self, topicMax=0):
# 随机选择一个开始词
startKeyArr = self.__dic[self.__keyStart]
j = random.randint(0, len(startKeyArr) - 1)
key = startKeyArr[j] # tuple 类型
# 待返回的句子
topic = key
i = 0
if topicMax <= 0:
topicMax = self.__topicMaxLenth
while i < self.__maxLoop:
i += 1
if key not in self.__dic.keys():
break
arr = self.__dic[key]
if not arr:
break
j = random.randint(0, len(arr) - 1)
if j < 0:
j = 0
sufix = arr[j]
# 后缀为结束符时,终止生成
if sufix == self.__keyEnd:
break
# 构建 topic
topic += sufix
tLen = len(topic)
if tLen >= topicMax:
break
nextKey = '%s%s' % (key[1], sufix)
# markovLinked.append(nextKey)
# 无
if nextKey not in self.__dic.keys():
break
key = nextKey
# print('markovLinked ',markovLinked)
return topic
def fileReader():
path = "test_markov_data_v2.txt"
with open(path, 'r', encoding='utf-8') as f:
rows = 0
# 按行统计
while True:
rows += 1
line = f.readline()
if not line:
print('读取结束 %s' % path)
return
print('content rows=%s len=%s type=%s' % (rows, len(line), type(line)))
yield line
pass
def main():
markov = MarkovLinked()
reader = fileReader()
for row in reader:
print(row)
markov.append(row)
# markov.printMarkovLinked()
# 生成句子
for i in range(20):
topic = markov.topicBuilder(topicMax=300)
print('%s\t%s' % (i, topic))
pass
# main()
if __name__ == '__main__':
main()
pass
其中test_markov_data_v2.txt数据集中的内容如下:
对有些人来说,困难是放弃的借口,而对另外一部分人来说,困难是成长壮大的机遇。
你从不担心自己配不上优秀的人,你只会担心自己配不上喜欢的人。
一息若存,希望不灭。
来属于那些坚信自己梦想之美的家伙。
心是一个人的翅膀,心有多大,世界就有多大。很多时候,限制我们的,不是周遭的环境,也不是他人的言行,而是我们自己:看不开,忘不了,放不下,把自己囚禁在灰暗的记忆里;不敢想,不自信,不行动,把自己局限在固定的空间里。
找不到坚持下去的理由,那就找一个重新开始的理由,生活本来就这么简单。
一条路,人烟稀少,孤独难行。却不得不坚持前行。因为它的尽头,种着“梦想”。
生活的有趣还在于,你昨日的最大痛楚,极可能会造就你明日的最大力量。
如果没有那些愚蠢的想法, 我们也压根不可能有什么有趣的想法。
世上有很多不可能,不过不要在你未尽全力之前下结论。
希望每天醒来,都是不一样的人生色彩。
思念无声无息,弥漫你的心里。当夜深人静的时候,是不是又感到了寂寞,感到了心烦?那就送你一个好梦吧,愿你梦里能回到你媳妇的娘家……高老庄!
暮春之夜,微风渐暖,睡意缱绻,移身临窗,近看柳枝月色下翩舞摇曳,遥听池塘绵绵蛙鸣。携一份恬淡,悍然入梦。晚安。
睡一睡,精神好,烦恼消,快乐长;睡一睡,心情好,做美梦,甜蜜蜜;睡一睡,身体健,头脑清,眼睛明。愿你酣然入梦,晚安!
思念不因劳累而改变,问候不因疲惫而变懒,祝福不因休息而变缓,关怀随星星眨眼,牵挂在深夜依然,轻轻道声:祝你晚安!
如隔三秋,是因为有人在思念;长夜漫漫,是因为有人在想念;展转反侧,是因为有人在品味孤独;孤枕难眠,是因为有人在数绵羊,爱就两个字:晚安。
运行结果如下:
读取结束 test_markov_data_v2.txt
0 思念不因疲惫而变缓,关怀随星星眨眼,牵挂在深夜依然,轻轻道声:祝
1 来属于那些愚蠢的想法,我们的,不行动,把自己配不上优秀的人。
2 睡一睡,精神好,做美梦,甜蜜蜜;睡一睡,精神好,烦恼消,快乐长;
3 睡一睡,精神好,烦恼消,快乐长;睡一睡,心情好,烦恼消,快乐长;
4 对有些人来说,困难是放弃的借口,而是我们也压根不可能,不是又感到
5 生活本来就这么简单。
6 睡一睡,身体健,头脑清,眼睛明。愿你梦里能回到你媳妇的娘家……高
7 暮春之夜,微风渐暖,睡意缱绻,移身临窗,近看柳枝月色下翩舞摇曳,
8 来属于那些坚信自己配不上喜欢的人生色彩。
9 找不到坚持前行。因为它的尽头,种着“梦想”。
10 世上有很多不可能有什么有趣的想法,我们也压根不可能,不行动,把自
11 你从不担心自己配不上喜欢的人生色彩。
12 希望每天醒来,都是不是周遭的环境,也不是周遭的环境,也不是他人的
13 你从不担心自己梦想”。
14 思念;展转反侧,是因为有人在数绵羊,爱就两个字:晚安!
15 一条路,人烟稀少,孤独难行。却不得不坚持下去的理由,生活的有趣还
16 找不到坚持前行。却不得不坚持下去的理由,生活的有趣还在于,你只会
17 思念无声无息,弥漫你的心里。当夜深人静的时候,限制我们也压根不可
18 暮春之夜,微风渐暖,睡意缱绻,移身临窗,近看柳枝月色下翩舞摇曳,
19 找不到坚持前行。却不得不坚持下去的理由,那就找一个好梦吧,愿你酣
进程已结束,退出代码0
好啦,现在相信你有所收获!