使用LSTM生成文本

一:softmax温度

从模型的softmax输出中进行概率采样是一种很巧妙的方法,甚至可以在某些时候采样到不常见的字符,从而生成看起来更加有趣的句子,而且有时会得到训练数据中没有的、听起来像是真实存在的新单词,从而表现出创造性。但这种方法有一个问题,就是在采样过程中无法控制随机性的大小。为什么需要一定的随机性,考虑极端的例子,纯随机采样,从均匀分布中抽取下一个字符,其中每一个字符的概率相同,这种方案具有最大的随机性,换句话说,这种概率分布具有最大的熵。当然它不会生成任何有趣的内容。另一个极端就是贪婪采样。贪婪采样也不会生成任何有趣的内容,它没有任何的随机性,即相应的概率分布具有最小的熵。从真实概率分布(即模型softmax函数输出的分布)中进行采样,是这两个极端之间的一个中间点。但是,还有许多其他中间点具有更大或更小的熵,你可能希望研究一下。更小的熵可以让生成序列具有更加可预测的结构,更大的熵会得到更加出人预料和更加有创造性的序列。引入softmax温度,用于表示采样概率分布的熵,即表示所选择的下一个字符会有多么出人意料或多么可预测。给定一个temperature值,将按照下列方法对原始概率分布(即softmax输出)进行重新加权,计算得到一个新的概率分布。

#对于不同的softmax温度,对概率分布进行重新加权
import numpy as np
def reweight_distribution(original_distribution,temperature=0.5):
    distribution=np.log(original_distribution)/temperature
    distribution=np.exp(distribution)
    return distribution/np.sum(distribution)#返回原始分布重新加权后的结果。
#original_distribution是概率值组成的一维数组,这些概率值之和必须等于1,temperature是一个因子,用于定量描述输出分布的熵

更高的温度得到的是熵更大的采样分布,会生成更加出乎意料的结果,更低的温度对应更小的随机性,以及更加可预测的生成数据。

二:实现字符级的LSTM文本生成

使用Keras模块,需要可用于学习语言模型的大量文本数据。我们可以使用任意足够大的一个或多个文本文件--维基百科、指环王等等。本例使用尼采的一些作品。因此我们要学习的语言模型将是针对于尼采的写作风格和主题的模型,而不是关于英语的通用模型。

1.准备数据

#1.准备数据
import keras
path=keras.utils.get_file(
    'nietzsche.txt',
    origin='https://s3.amazonaws.com/text-datasets/nietzsche.txt'
)
text=open(path).read().lower()

接下来,我们要提取长度为maxlen的序列(这些序列之间存在部分重叠),对它们进行one-hot编码,然后将其打包为形状为(sequences,maxlen,unique_characters)的三维Numpy数组。与此同时,我们还需要准备一个数组y,其中包含对应的目标,即在每一个所提取的序列之后出现的字符(已进行one-hot编码)。

maxlen=60#提取60个字符组成的序列
step=3#每三个字符采样一个新序列
sentences=[]#保存所提取序列
next_chars=[]#保存目标(下一个字符)
for i in range(0,len(text)-maxlen,step):
    sentences.append(text[i:i+maxlen])
    next_chars.append(text[i+maxlen])
print('number of sequences:',len(sentences))
chars=sorted(list(set(text)))#语料中唯一字符组成的列表
print('unique characters:',len(chars))
char_indices=dict((char,chars.index(char))for char in chars)#一个字典,将唯一字符映射为它所在列表chars中的索引
print('vectorization.....')
x=np.zeros((len(sentences),maxlen,len(chars)),dtype=np.bool_)
y=np.zeros((len(sentences),len(chars)),dtype=np.bool_)
for i,sentence in enumerate(sentences):#enumerate用于将一个可迭代的数据对象组合为一个索引列表,同时列出数据对应的值
    for t,char in enumerate(sentence):
        x[i,t,char_indices[char]]=1
    y[i,char_indices[next_chars[i]]]=1

2.构建网络

这个网络是一个单层LSTM,然后是一个Dense分类器和对所有可能字符的softmax。注意,循环神经网络并不是序列数据生成的唯一方法,最近已经证明一维卷积神经网络也可以成功用于序列数据生成。

from keras import layers
model=keras.models.Sequential()
model.add(layers.LSTM(128,input_shape=(maxlen,len(chars))))
model.add(layers.Dense(len(chars),activation='softmax'))

目标是经过onehot编码的,所以训练模型需要使用categorical_crossentropy作为损失。

#模型编译配置
optimizer=keras.optimizers.RMSprop(lr=0.01)
model.compile(loss='categorical_crossentropy',optimizer=optimizer)

3.训练语言模型并从中采样

给定一个训练好的模型和一个种子文本片段,我们可以通过重复以下操作来生成新文本。

给定目前已生成的文本,从模型中得到下一个字符的概率分布

根据某个温度对分布进行重新加权

根据重新加权后的分布对下一个字符进行随机采样

将新字符添加到文本末尾

下列代码将对模型得到的原始概率分布进行重新加权,并从中抽取一个字符索引【采样函数】

def sample(preds,temperature=1.0):
    preds=np.asarray(preds).astype('float64')
    preds=np.log(preds)/temperature
    exp_preds=np.exp(preds)
    preds=exp_preds/np.sum(exp_preds)
    probas=np.random.multinomial(1,preds,1)
    return np.argmax(probas)

最后,下面这个循环将反复训练并生成文本。在每轮过后都使用一系列不同的温度值来生成文本,这样我们可以看到,随着模型收敛,生成的文本如何变化,以及温度对采样策略的影响。

#文本生成序列
import random
import sys

for epoch in range(1,60):#将模型训练60轮
    print('epoch',epoch)
    model.fit(x,y,batch_size=128,epochs=1)#将模型在数据上拟合一次
    start_index=random.randint(0,len(text)-maxlen-1) #随机选择一个文本种子
    generated_text=text[start_index:start_index+maxlen]
    print('----Generating with seed:"'+ generated_text+'"')
    for temperature in [0.2,0.5,1.0,1.2]:#尝试一系列不同的采样温度
        print('---temperature:',temperature)
        sys.stdout.write(generated_text)

        for i in range(400):#从种子文本开始,生成400个字符
            sampled=np.zeros((1,maxlen,len(chars)))#对目前生成的字符进行onehot编码
            for t,char in enumerate(generated_text):
                sampled[0,t,char_indices[char]]=1

            preds=model.predict(sampled,verbose=0)[0]#对下一个字符进行采样
            next_index=sample(preds,temperature)
            next_char=chars[next_index]

            generated_text+=next_char
            generated_text=generated_text[1:]

            sys.stdout.write(next_char)

这里我们使用的随机种子文本时new faculty,and the jubilation reached its climax when kant。第二十轮时,temperature=0.2的输出如下所示,此时模型还没有完全收敛。

输出结果太长,在这里就不进行展示了。

从结果可以看出,较小的温度值会得到极端重复和可预测文本,但局部结构是非常真实的,特别时所有单词都是真正的英文单词(单词就是字符的局部模式)。随着温度值越来越大,生成的文本也变得更有趣,更出乎意料,甚至更有创造性,它有时会创造出全新的单词,听起来有几分可信。对于较大的温度值,局部模式开始分解,大部分单词看起来像是半随机的字符串,毫无疑问,在这个特定的设置下,0.5的温度值生成的文本最有趣。

写的比较匆忙,有任何问题欢迎留言,谢谢大家!一键三连!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值