NLP-python-马尔科夫链(markov)-文本句子生成器实现

20 篇文章 1 订阅
2 篇文章 0 订阅

NLP-python-马尔科夫链(markov)-文本句子生成器实现


一、markov算法说明:
该算法的基本原理是将输入看成是由一些互相重叠的短语构成的序列。
其将每个短语分割为两个部分:一部分是由多个词构成的前缀,另一部分是只包含一个词的后缀。
在生成文本时依据原文本的统计性质(即前缀确定的情况下,得到所有可能的后缀),随机地选择某前缀后面的特定后缀。如何验证生成的文本是否符合实际的语言规范,就需要使用语言模型对齐进行校验打分,参见:NLP - ngram - N元语言模型 python 实现​​​​​​​。

二、markov算法分解示例
假设前缀长度为两词(中文可以为两个字),则马尔可夫链(Markov Chain)随机文本生成算法如下:
1.设滑动窗口长度为3,步长为1;
2.每次窗口中的值为 w1,w2,w3
3.其中 key=w1+w2,val=w3,存入div<string,string[]> 的字典中;
4.重复 2、3 步,直至字符串解析完成;

三、下面将通过一个例子来说明该算法原理,假设有一个原文如下:
txt = "一息若存,希望不灭。"

下面是上述原文的一些前缀和其后缀(注意只是部分)的统计:
key = vals
#start# = [一息]
一息 = [若]
息若 = [存]
若存 = [,]
....
灭。=[#end#]

说明:
为了得到更好的统计特性,在此标点符号等非字母字符(如’ “ . , ? – ()等)也被看成单词的一部分;
对于同一个前缀的后缀按出现顺序排放(不管该后缀是否已存在);

四、马尔科夫链 python 实现

#!/usr/bin/env python3
# coding=utf-8

import urllib
import re
import random
import string
import operator
'''
马尔科夫链,生产句子:
    尝试将使用不同句子进行训练,生成句子。
'''

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):
        '''
        :param txt: 一段话或一个句子
        :return: 返回迭代器,item 为 tuple,每项 3 个值
        '''
        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 = "../NLP/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 语料文本内容

对有些人来说,困难是放弃的借口,而对另外一部分人来说,困难是成长壮大的机遇。
你从不担心自己配不上优秀的人,你只会担心自己配不上喜欢的人。
一息若存,希望不灭。
来属于那些坚信自己梦想之美的家伙。
心是一个人的翅膀,心有多大,世界就有多大。很多时候,限制我们的,不是周遭的环境,也不是他人的言行,而是我们自己:看不开,忘不了,放不下,把自己囚禁在灰暗的记忆里;不敢想,不自信,不行动,把自己局限在固定的空间里。
找不到坚持下去的理由,那就找一个重新开始的理由,生活本来就这么简单。
一条路,人烟稀少,孤独难行。却不得不坚持前行。因为它的尽头,种着“梦想”。
生活的有趣还在于,你昨日的最大痛楚,极可能会造就你明日的最大力量。
如果没有那些愚蠢的想法, 我们也压根不可能有什么有趣的想法。
世上有很多不可能,不过不要在你未尽全力之前下结论。
希望每天醒来,都是不一样的人生色彩。
思念无声无息,弥漫你的心里。当夜深人静的时候,是不是又感到了寂寞,感到了心烦?那就送你一个好梦吧,愿你梦里能回到你媳妇的娘家……高老庄!
暮春之夜,微风渐暖,睡意缱绻,移身临窗,近看柳枝月色下翩舞摇曳,遥听池塘绵绵蛙鸣。携一份恬淡,悍然入梦。晚安。
睡一睡,精神好,烦恼消,快乐长;睡一睡,心情好,做美梦,甜蜜蜜;睡一睡,身体健,头脑清,眼睛明。愿你酣然入梦,晚安!
思念不因劳累而改变,问候不因疲惫而变懒,祝福不因休息而变缓,关怀随星星眨眼,牵挂在深夜依然,轻轻道声:祝你晚安!
如隔三秋,是因为有人在思念;长夜漫漫,是因为有人在想念;展转反侧,是因为有人在品味孤独;孤枕难眠,是因为有人在数绵羊,爱就两个字:晚安。

六、运行结果

0	来属于那些愚蠢的想法,我们的,不过不要在你未尽全力之前下结论。
1	希望不灭。
2	睡一睡,精神好,烦恼消,快乐长;睡一睡,身体健,头脑清,眼睛明。
3	找不到坚持下去的理由,生活本来就这么简单。
4	如隔三秋,是因为有人在想念;展转反侧,是因为有人在品味孤独;孤枕
5	思念无声无息,弥漫你的心里。
6	思念不因休息而变懒,祝福不因疲惫而变懒,祝福不因休息而变懒,祝福
7	睡一睡,精神好,烦恼消,快乐长;睡一睡,精神好,做美梦,甜蜜蜜;
8	如果没有那些坚信自己梦想”。
9	找不到坚持前行。却不得不坚持下去的理由,生活本来就这么简单。
10	世上有很多时候,限制我们自己配不上优秀的人生色彩。
11	一条路,人烟稀少,孤独难行。却不得不坚持前行。因为有人在想念;展
12	你从不担心自己配不上喜欢的人。
13	对有些人来说,困难是成长壮大的机遇。
14	对有些人来说,困难是放弃的借口,而对另外一部分人来说,困难是放弃
15	思念;展转反侧,是因为它的尽头,种着“梦想”。
16	对有些人来说,困难是成长壮大的机遇。
17	一息若存,希望不灭。
18	一息若存,希望不灭。
19	希望每天醒来,都是不是他人的翅膀,心有多大。很多不可能会造就你明

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值