首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >使用GRU单元的RNN模型生成唐诗

使用GRU单元的RNN模型生成唐诗

作者头像
Michael阿明
发布于 2021-02-19 15:18:35
发布于 2021-02-19 15:18:35
1.3K0
举报

文章目录

参考 基于深度学习的自然语言处理

本文使用 GRU 单元建立 RNN 网络,使用唐诗三百首进行训练,使用模型生成唐诗。

GRU RNN 网络能够克服简单RNN网络的一些问题,如梯度消失,梯度很难从深层传递到浅层,导致浅层的参数更新非常缓慢,学习速度很慢,还导致深层浅层学习不均衡。

GRU,LSTM 使用更新门,遗忘门,来解决长距离的依赖关系,GRU相比LSTM参数更少。

RNN 网络的还有缺点就是无法采用并行计算,必须在上一个时间步的基础上计算下一个时间步。

1. 读取数据

代码语言:javascript
复制
# 读取文本
file = "tangshi300.txt"
with open(file,'r',encoding='utf-8') as f:
    text = f.read()
print(len(text))
print(text[:180])

输出:

代码语言:javascript
复制
29405  # 文本长度

唐诗300首 1-50

010杜甫:佳人

绝代有佳人,幽居在空谷。
自云良家子,零落依草木。
关中昔丧乱,兄弟遭杀戮。
官高何足论,不得收骨肉。
世情恶衰歇,万事随转烛。
夫婿轻薄儿,新人美如玉。
合昏尚知时,鸳鸯不独宿。
但见新人笑,那闻旧人哭!
在山泉水清,出山泉水浊。
侍婢卖珠回,牵萝补茅屋。
摘花不插发,采柏动盈掬。
天寒翠袖薄,日暮倚修竹。

2. 字符索引

代码语言:javascript
复制
# 创建字符序号索引
words = sorted(list(set(text)))
print("字和符号数量:{}".format(len(words)))

word_idx = {w : i for (i, w) in enumerate(words)}
idx_word = {i : w for (i, w) in enumerate(words)}

输出:

代码语言:javascript
复制
字和符号数量:2590

3. 创建文本序列

代码语言:javascript
复制
# 根据文本,创建序列
sample_maxlen = 40 # 样本句子长度
sentences = []
next_word = []
for i in range(len(text)-sample_maxlen):
    sentences.append(text[i : i+sample_maxlen]) # 滑窗取出样本句子
    next_word.append(text[i+sample_maxlen]) # 句子末尾的下一个字
print("样本数量:{}".format(len(sentences)))
代码语言:javascript
复制
样本数量:29365

4. 创建文本编码序列

代码语言:javascript
复制
# 将文本序列转化成数字序列(矩阵), 实际上就是一个one_hot 编码
import numpy as np
X = np.zeros((len(sentences), sample_maxlen, len(words)), dtype=np.bool)
#            样本数             1个样本字个数   一个字的OH编码长度
y = np.zeros((len(sentences), len(words)), dtype=np.bool)
#            样本数          一个字的OH编码长度
for i in range(len(sentences)):
    for t, w in enumerate(sentences[i]):
        X[i, t, word_idx[w]] = 1 # 把 i 样本 t 字符 对应的 OH 编码位置 写为 1
    y[i, word_idx[next_word[i]]] = 1

5. 使用GRU单元建立RNN模型

  • 建模
代码语言:javascript
复制
# 建模
from keras.models import Sequential
from keras.layers import GRU, Dense
from keras.optimizers import Adam
model = Sequential()
model.add(GRU(units=128,input_shape=(sample_maxlen, len(words)))) # GRU 层
model.add(Dense(units=len(words), activation='softmax')) # FC 层 多分类 softmax
  • 训练
代码语言:javascript
复制
optimizer = Adam(learning_rate=0.001) # adam 优化器
model.compile(optimizer=optimizer,
              loss='categorical_crossentropy',
              metrics=['accuracy']) # 配置模型
history = model.fit(X, y, batch_size=128, epochs=500) # 训练
model.save("tangshi_generator_model.h5") # 保存模型
  • 绘制训练曲线
代码语言:javascript
复制
import pandas as pd
import matplotlib.pyplot as plt
pd.DataFrame(history.history).plot(figsize=(8, 5)) # 绘制训练曲线
plt.grid(True)
plt.gca().set_ylim(0, 1) # set the vertical range to [0-1]
plt.show()

模型在 100 个 epochs 时已基本上完全拟合了训练数据

6. 文本生成

  • 采样函数
代码语言:javascript
复制
def sampling(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)
    probs = np.random.multinomial(1, preds, 1)
#   多项式分布,做n次试验,按照preds的概率分布(和=1),取出size组结果,如下
#     >>> np.random.multinomial(20, [1/6.]*6, size=1)
#             array([[4, 1, 7, 5, 2, 1]]) # random
    return np.argmax(probs) # 返回概率最大的idx
  • 随机选取训练文本里的一段开始生成后序文本
代码语言:javascript
复制
from keras.models import load_model
import random
model = load_model("tangshi_generator_model.h5")

def generate_tangshi(model, generate_len=200):
    start_idx = random.randint(0, len(text)-sample_maxlen-1) # 随机开始位置
    generated = ""
    sentence = text[start_idx : start_idx + sample_maxlen] # 开始的句子
    generated += sentence
    print("随机选取的开始句子为:{}".format(generated))
    for i in range(generate_len): # 后续要生成的句子长度
        x_pred = np.zeros((1, sample_maxlen, len(words)))
        for t, w in enumerate(sentence):
            x_pred[0, t, word_idx[w]] = 1 # 当前句子的 OH 编码
        preds = model.predict(x_pred)[0] # predict 返回 (1,2590)一个样本 2590个类预测值
        next_idx = sampling(preds, 1) # 采样出来下一个最有可能的词的idx
        next_w = idx_word[next_idx] # 取出这个词
        generated += next_w # 加到句子中
        sentence = sentence[1:] + next_w # 句子窗口后移一个位置,作为下次预测的输入
    return generated

generate_tangshi(model, 100)

输出:

模型完全记住了后续的诗句。

  • 自己随意编写训练集里没有的诗句作为开始,如下(不可有训练集中未出现的字)
代码语言:javascript
复制
with open('test.txt','r',encoding='utf-8') as f:
    test_text = f.read()
    
def generate_tangshi_test(model, generate_len=60):
    generated = ""
    sentence = test_text[0 : sample_maxlen]
    generated += sentence
    print("测试文本开始句子为:{}".format(generated))
    for i in range(generate_len):
        x_pred = np.zeros((1, sample_maxlen, len(words)))
        for t, w in enumerate(sentence):
            x_pred[0, t, word_idx[w]] = 1
        preds = model.predict(x_pred)[0]
        next_idx = sampling(preds, 1)
        next_w = idx_word[next_idx]
        generated += next_w
        sentence = sentence[1:] + next_w
    return generated

generate_tangshi_test(model, 100)

输出:

输出的诗句有部分一整句都是训练集里的,有的则不是,反正没有多少诗的美感,哈哈!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/12/11 ,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 文章目录
  • 1. 读取数据
  • 2. 字符索引
  • 3. 创建文本序列
  • 4. 创建文本编码序列
  • 5. 使用GRU单元建立RNN模型
  • 6. 文本生成
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档