自然语言处理--Keras 实现LSTM生成文本

令人兴奋的是,基于上一篇《keras实现LSTM字符级建模》的原理,使用LSTM我们可以根据之前文档出现过的字符来预测下一个字符,并且根据训练数据文本的特定的“风格”或“看法”生成新的文本。这很有趣,但我们将选择一个风格独特的人——威廉·莎士比亚(William Shakespeare),现根据他现有的作品来生成乍一看都有点儿像莎士比亚的作品的文本。

from nltk.corpus import gutenberg
from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.layers import LSTM
from keras.optimizers import RMSprop
import numpy as np
import random
import sys
import time

# 导入古腾堡计划数据集
print(gutenberg.fileids())
# 预处理莎士比亚的戏剧
text = ''
for txt in gutenberg.fileids():
    if 'shakespeare' in txt:
        text += gutenberg.raw(txt).lower()
chars = sorted(list(set(text)))
# 为索引建立一个字符字典,以便在独热编码中引用
char_indices = dict((c, i) for i, c in enumerate(chars))
# 把一个独热编码解释回字符时,建立一个反向查找字典
indices_char = dict((i, c) for i, c in enumerate(chars))
print('corpus length: {} total chars: {}'.format(len(text), len(chars)))
print(text[:500])

'''
模型的目标是在给定前 40 个字符的情况下,学习预测任意序列中的第 41 个字符
'''
# 组装一个训练集
maxlen = 40
step = 3
sentences = []
next_chars = []
# 步长为 3 个字符,这样生成的训练样本会部分重叠,但不会完全相同
for i in range(0, len(text) - maxlen, step):
    sentences.append(text[i: i + maxlen])
    # 收集下一个预期字符
    next_chars.append(text[i + maxlen])
print('nb sequences:', len(sentences))

# 训练样本的独热编码
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):
    for t, char in enumerate(sentence):
        X[i, t, char_indices[char]] = 1
    y[i, char_indices[next_chars[i]]] = 1

# 组装一个基于字符的 LSTM 模型来生成文本
model = Sequential()
# 不返回整个序列,我们只需要最后一个输出字符
model.add(LSTM(128, input_shape=(maxlen, len(chars))))
# 这里没有 dropout。因为我们要对这个数据集进行特定的建模,而没有兴
# 趣将其推广到其他问题,所以过拟合不仅是可以的,还是理想的。
model.add(Dense(len(chars)))
model.add(Activation('softmax'))
# RMSProp 的工作原理是通过使用“该权重最近梯度大小的平均值”,来调整学习率以更新各个权重
optimizer = RMSprop(lr=0.01)
# 使用 softmax 激活函数,因此输出向量将等效为整个50 维向量上的概率分布(该向量中的值之和总是为 1)
# 使用 categorical_crossentropy使结果的概率分布与独热编码预期字符之间的差异最小化
model.compile(loss='categorical_crossentropy', optimizer=optimizer)
print(model.summary())

# 训练一个莎士比亚风格的聊天机器人
epochs = 6
batch_size = 128
model_structure = model.to_json()
with open("shakes_lstm_model.json", "w") as json_file:
    json_file.write(model_structure)
# 每隔 6 个训练周期保存模型一次,并继续训练
for i in range(5):
    model.fit(X, y, batch_size=batch_size, epochs=epochs)
    model.save_weights("shake_lstm_weights_{}.h5".format(i+1))

# 产生字符序列的采样器:预测接下来会是什么词,从而从概率分布中生成全新的文本。
# log 函数除以 temperature 的效果是使概率分布变平(temperature > 1)或变尖
# (temperature < 1)。因此,小于 1 的 temperature(或称调用参数中的多样性)倾向于试
# 图更严格地重新创建原始文本。而大于 1 的 temperature 会产生更多样化的结果,但是随着分
# 布变平,学习到的模式开始被遗忘,我们就会回到胡言乱语的状态。多样性越高就会越有趣。
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)

# 使用 3 种多样化等级产生 3 种类型文本
start_index = random.randint(0, len(text) - maxlen - 1)
for diversity in [0.2, 0.5, 1.0, 1.2]:
    print()
    print('----- diversity:', diversity)
    generated = ''
    sentence = text[start_index: start_index + maxlen]
    generated += sentence
    print('----- Generating with seed: "' + sentence + '"')
    sys.stdout.write(generated)
    for i in range(400):
        x = np.zeros((1, maxlen, len(chars)))
        for t, char in enumerate(sentence):
            x[0, t, char_indices[char]] = 1.
        preds = model.predict(x, verbose=0)[0]
        next_index = sample(preds, diversity)
        next_char = indices_char[next_index]
        generated += next_char
        sentence = sentence[1:] + next_char
        # 将预测的字符写入控制台(或字符串缓冲区)
        sys.stdout.write(next_char)
        # 执行 flush()函数,以便字符即刻进入控制台
        sys.stdout.flush()
        time.sleep(0.02)
    print()

重要-文本生成器的增强方法
如果我们希望生成模型不只是为了消遣,可以做些什么来使它变得更具一致性和更有用呢?
1.增加语料库的数量并提高质量;
2.增加模型的复杂度(神经元的数量);
3.实现一个更精细的字符一致性的算法;
4.句子分段;
5.根据需要在语法、拼写和语气上添加过滤器;
6.生成比实际展示给用户更多的一些结果案例;
7.使用在会话上下文中选择的种子文本引导聊天机器人转向有用的主题;
8.在每一轮对话中使用多个不同的种子文本来探索聊天机器人擅长谈论什么领域的话题以及用户认为哪些内容会有帮助。

猜你喜欢

转载自blog.csdn.net/fgg1234567890/article/details/113532901