马尔可夫链文本生成的简单应用:不足20行的Python代码生成鸡汤文

提到自然语言的生成时,人们通常认为要会使用高级数学来思考先进的AI系统,然而,并不一定要这样。在这篇文章中,我将使用马尔可夫链和一个小的语录数据集来产生新的语录。

马尔可夫链

马尔可夫链是一个只根据先前事件来预测事件的随机模型。举一个简单的例子:我的猫可能的状态变化。我有一只猫,它一般都是在吃、睡或者玩。它大多时间在睡觉。不过,她偶尔会醒来吃点东西。通常情况下,吃完以后,她会变得很活泼,开始玩玩具,然后她要么回去睡觉,要么再次吃东西(我想他家的猫可能是橘色的)。

我的猫的状态可以很容易地用马尔可夫链建模,因为它决定接下来做什么,取决于它以前的状态。它不太可能醒来后马上开始玩,但在她吃完猫粮之后就很有可能开始玩。这些状态转换也可以用状态转换图说明:

每个圆圈代表一个状态,箭头指向下一个状态,每个箭头旁边的数字是从一个状态转换到另一个状态的概率。正如你所看到的,状态转变的几率完全基于以前的状态。

马尔可夫链的文本生成

马尔可夫链文本生成的思想与此相同,即试图找出某个词出现在另一个词之后的概率。为了确定转换的概率,我们用一些例句来训练模型。

打个比方,我们可以用下面的句子来训练一个模型。

I like to eat apples. You eat oranges.

只从训练数据中我们可以得出的结论是,“I”,“like”,“to”和“eat”都是这种顺序,而“you”和“eat”也总是在一起。然而,在“eat”这个词之后出现“oranges”或“apples”的概率是相等的。转换图如下:

这两个训练句子只能够产生两个新的句子。接下来,我用下面的四个句子训练了另一个模型。

my friend makes the best raspberry pies in town i think apple pies are the best pies steve thinks apple makes the best computers in the world I own two computers and they’re not apple because I am not steve or rich

由四个句子训练的模型,它的转换图要比两个大得多。

即使这个图与典型的马尔可夫链转换图看起来差异很大,但其背后的主要思想是一样的。路径从“START”节点开始,按概率选取下列单词直到结束节点。选取单词的概率用连接的粗细表示。

上面的模型能够产生数百个独特的句子,即使是只有四个句子的训练。

代码

生成器的代码非常简单,除了python的random模块外,不需要其他额外的模块或库。它由两部分组成,一部分用于训练,另一部分用于生成。

训练

训练代码构建了我们稍后用于生成句子的模型。我用字典(给定句子的所有单词)作为模型; 以单词作为关键帧,并将选取下个单词的概率列表作为相应的值。例如,前两行已经训练过的模型的字典:“I like to eat oranges”,“You eat apples”如下:

{'START': ['i', 'you'], 'i': ['like'], 'like': ['to'], 'to': ['eat'], 'you': 
['eat'], 'eat': ['apples', 'oranges'], 'END': ['apples', 'oranges']}

我们不需要计算下一个单词出现的概率,因为如果它们出现的概率较大,那么他们会在选取下个单词的列表中出现好几次。例如,如果我们加上“we eat apples”这个额外的训练语句,“eat”这个词之后的两个句子出现“apples”这个词,因此有更高的概率。这个更高的概率通过在“eat”的列表中出现两次的方式插入模型字典。

{'START': ['i', 'we', 'you'], 'i': ['like'], 'like': ['to'], 'to': ['eat'], '
you': ['eat'], 'we': ['eat'], 'eat': ['apples', 'oranges', 'apples'], 'END': 
['apples', 'oranges', 'apples']}

此外,在上面的模型字典中还有两个附加项,“START”和“END”,它们表示生成的句子的开始和结束词。

for line in dataset_file:
    line = line.lower().split()
    for i, word in enumerate(line):
        if i == len(line)-1:   
            model['END'] = model.get('END', []) + [word]
        else:    
            if i == 0:
                model['START'] = model.get('START', []) + [word]
            model[word] = model.get(word, []) + [line[i+1]]

生成

生成器部分由一个循环组成。它首先选择一个随机的启动词,并将其附加到一个列表。然后在字典中搜索它下一个可能的单词列表,随机选取其中一个单词,将新选择的单词附加到列表中。它继续在可能性的列表中随机选择下一个单词,重复此过程直到它到达结束词,然后停止循环,并输出生成的单词序列或者说鸡汤。

import random 

generated = []
while True:
    if not generated:
        words = model['START']
    elif generated[-1] in model['END']:
        break
    else:
        words = model[generated[-1]]
    generated.append(random.choice(words))

我现在使用马尔可夫链生成的是鸡汤文。当它们作为文本生成器时,你可以提供任何输入,它会按你提供的输入生成类似的文本。

“Don’t think of the overwhelming majority of the impossible.” “Grew up your bliss and the world.” “what we would end create, creates the ground and you are the one to warm it” “look and give up in miracles”

以上引用框中所有的句子都是由计算机生成的,使用的程序python代码不足20行。

马尔可夫链文本生成器也可以混合不同类型的文本。例如,在我最喜欢的电视节目之一Rick and Morty中,有一个叫Abradolf Lincler的人物,他是林肯和希特勒的混合体。如果你对此感兴趣,同样可以通过将两位领导人的演讲作为训练数据提供给马尔可夫链文本生成器来生成混合体会说的内容。

http://rickandmorty.wikia.com/wiki/Abradolf_Lincler

原文发布于微信公众号 - ATYUN订阅号(atyun_com)

原文发表时间:2018-01-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏聊聊技术

原 数据结构-二叉搜索树(Binary S

2867
来自专栏Hongten

ArrayList VS Vector(ArrayList和Vector的区别)_面试的时候经常出现

1692
来自专栏开发与安全

算法:AOV网(Activity on Vextex Network)与拓扑排序

在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,我们称之为AOV网(Activity on Vextex ...

2517
来自专栏xingoo, 一个梦想做发明家的程序员

20120918-向量实现《数据结构与算法分析》

#include <iostream> #include <list> #include <string> #include <vector> #include...

1716
来自专栏后端之路

LinkedList源码解读

List中除了ArrayList我们最常用的就是LinkedList了。 LInkedList与ArrayList的最大区别在于元素的插入效率和随机访问效率 ...

19510
来自专栏学海无涯

Android开发之奇怪的Fragment

说起Android中的Fragment,在使用的时候稍加注意,就会发现存在以下两种: v4包中的兼容Fragment,android.support.v4.ap...

3155
来自专栏聊聊技术

原 初学图论-Kahn拓扑排序算法(Kah

2878
来自专栏xingoo, 一个梦想做发明家的程序员

AOE关键路径

这个算法来求关键路径,其实就是利用拓扑排序,首先求出,每个节点最晚开始时间,再倒退求每个最早开始的时间。 从而算出活动最早开始的时间和最晚开始的时间,如果这两个...

2507
来自专栏Phoenix的Android之旅

Java 集合 Vector

List有三种实现,ArrayList, LinkedList, Vector, 它们的区别在于, ArrayList是非线程安全的, Vector则是线程安全...

662
来自专栏alexqdjay

HashMap 多线程下死循环分析及JDK8修复

1K4

扫码关注云+社区