文章目录
- 1. 读取数据
- 2. 字符索引
- 3. 创建文本序列
- 4. 创建文本编码序列
- 5. 使用GRU单元建立RNN模型
- 6. 文本生成
参考 基于深度学习的自然语言处理
本文使用 GRU 单元建立 RNN 网络,使用唐诗三百首进行训练,使用模型生成唐诗。
GRU RNN 网络能够克服简单RNN网络的一些问题,如梯度消失,梯度很难从深层传递到浅层,导致浅层的参数更新非常缓慢,学习速度很慢,还导致深层浅层学习不均衡。
GRU,LSTM 使用更新门,遗忘门,来解决长距离的依赖关系,GRU相比LSTM参数更少。
RNN 网络的还有缺点就是无法采用并行计算,必须在上一个时间步的基础上计算下一个时间步。
1. 读取数据
# 读取文本
file = "tangshi300.txt"
with open(file,'r',encoding='utf-8') as f:text = f.read()
print(len(text))
print(text[:180])
输出:
29405 # 文本长度唐诗300首 1-50010杜甫:佳人绝代有佳人,幽居在空谷。
自云良家子,零落依草木。
关中昔丧乱,兄弟遭杀戮。
官高何足论,不得收骨肉。
世情恶衰歇,万事随转烛。
夫婿轻薄儿,新人美如玉。
合昏尚知时,鸳鸯不独宿。
但见新人笑,那闻旧人哭!
在山泉水清,出山泉水浊。
侍婢卖珠回,牵萝补茅屋。
摘花不插发,采柏动盈掬。
天寒翠袖薄,日暮倚修竹。
2. 字符索引
# 创建字符序号索引
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)}
输出:
字和符号数量:2590
3. 创建文本序列
# 根据文本,创建序列
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)))
样本数量:29365
4. 创建文本编码序列
# 将文本序列转化成数字序列(矩阵), 实际上就是一个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 编码位置 写为 1y[i, word_idx[next_word[i]]] = 1
5. 使用GRU单元建立RNN模型
- 建模
# 建模
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
- 训练
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") # 保存模型
- 绘制训练曲线
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. 文本生成
- 采样函数
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]]) # randomreturn np.argmax(probs) # 返回概率最大的idx
- 随机选取训练文本里的一段开始生成后序文本
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 += sentenceprint("随机选取的开始句子为:{}".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) # 采样出来下一个最有可能的词的idxnext_w = idx_word[next_idx] # 取出这个词generated += next_w # 加到句子中sentence = sentence[1:] + next_w # 句子窗口后移一个位置,作为下次预测的输入return generatedgenerate_tangshi(model, 100)
输出:
模型完全记住了后续的诗句。
- 自己随意编写训练集里没有的诗句作为开始,如下(不可有训练集中未出现的字)
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 += sentenceprint("测试文本开始句子为:{}".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]] = 1preds = model.predict(x_pred)[0]next_idx = sampling(preds, 1)next_w = idx_word[next_idx]generated += next_wsentence = sentence[1:] + next_wreturn generatedgenerate_tangshi_test(model, 100)
输出:
输出的诗句有部分一整句都是训练集里的,有的则不是,反正没有多少诗的美感,哈哈!