字母级语言模型 - Dinosaurus land
欢迎来到恐龙大陆! 6500万年前,恐龙就已经存在,并且在该作业下它们又回来了。假设你负责一项特殊任务,领先的生物学研究人员正在创造新的恐龙品种,并计划将它们带入地球,而你的工作就是为这些新恐龙起名字。如果恐龙不喜欢它的名字,它可能会变得疯狂,所以需要明智地选择!
幸运的是,你掌握了深度学习的一些知识,你将使用它来节省时间。你的助手已收集了它们可以找到的所有恐龙名称的列表,并将其编译到此dataset中。(请单击上一个链接查看)要创建新的恐龙名称,你将构建一个字母级语言模型来生成新名称。你的算法将学习不同的名称模式,并随机生成新名称。希望该算法可以使你和你的团队免受恐龙的愤怒!
完成此作业,你将学习:
- 如何存储文本数据以供RNN使用
- 如何在每个时间步采样预测并将其传递给下一个RNN单元以合成数据
- 如何建立一个字母级的文本生成循环神经网络
- 为什么梯度裁剪很重要
外面将加载rnn_utils
中为你提供的一些函数开始。具体来说,你可以访问诸如rnn_forward
和rnn_backward
之类的函数,这些函数与你在上一个作业中实现的函数等效。
import numpy as np
from utils import *
import random
from random import shuffle
1 问题陈述
1.1 数据集和预处理
运行以下单元格以读取包含恐龙名称的数据集,创建唯一字符列表(例如a-z),并计算数据集和词汇量。
# 获取名称
data = open("datasets/dinos.txt", "r").read()
# 转化为小写字符
data = data.lower()
# 转化为无序且不重复的元素列表
chars = list(set(data))
# 获取大小信息
data_size, vocab_size = len(data), len(chars)
print(chars)
print("共计有%d个字符,唯一字符有%d个"%(data_size,vocab_size))
['y', 'o', 'm', 'q', 'g', 'v', 'j', 'i', 't', 'r', 'h', 's', 'b', 'n', 'd', 'k', 'p', 'c', '\n', 'w', 'e', 'a', 'z', 'l', 'u', 'f', 'x']
共计有19909个字符,唯一字符有27个
这些字符是a-z(26个字符)加上“\n”(换行符),在此作业中,其作用类似于我们在讲座中讨论的<EOS
>(句子结尾)标记,仅在此处表示恐龙名称的结尾,而不是句子的结尾。在下面的单元格中,外面创建一个python字典(即哈希表),以将每个字符映射为0-26之间的索引。外面还创建了第二个python字典,该字典将每个索引映射回对应的字符。这将帮助你找出softmax层的概率分布输出中哪个索引对应于哪个字符。下面的char_to_ix
和ix_to_char
是python字典。
char_to_ix = { ch:i for i,ch in enumerate(sorted(chars)) }
ix_to_char = { i:ch for i,ch in enumerate(sorted(chars)) }
print(ix_to_char)
{0: '\n', 1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i', 10: 'j', 11: 'k', 12: 'l', 13: 'm', 14: 'n', 15: 'o', 16: 'p', 17: 'q', 18: 'r', 19: 's', 20: 't', 21: 'u', 22: 'v', 23: 'w', 24: 'x', 25: 'y', 26: 'z'}
1.2 模型概述
你的模型将具有以下结构:
- 初始化参数
- 运行优化循环
- 正向传播以计算损失函数
- 反向传播以计算相对于损失函数的梯度
- 剪裁梯度以避免梯度爆炸
- 使用梯度下降方法更新参数
- 返回学习的参数
图1:循环神经网络,类似于你在上一个笔记本“手把手实现循环神经网络”中构建的内容。
在每个时间步,RNN都会根据给定的先前字符来预测下一个字符。数据集 X = ( x ⟨ 1 ⟩ , x ⟨ 2 ⟩ , . . . , x ⟨ T x ⟩ ) X = (x^{\langle 1 \rangle}, x^{\langle 2 \rangle}, ..., x^{\langle T_x \rangle}) X=(x⟨1⟩,x⟨2⟩,...,x⟨Tx⟩)是训练集中的字符列表,而 Y = ( y ⟨ 1 ⟩ , y ⟨ 2 ⟩ , . . . , y ⟨ T x ⟩ ) Y = (y^{\langle 1 \rangle}, y^{\langle 2 \rangle}, ..., y^{\langle T_x \rangle}) Y=(y⟨1⟩,y⟨2⟩,...,y⟨Tx⟩)使得每个时间步 t t t,我们有 y ⟨ t ⟩ = x ⟨ t + 1 ⟩ y^{\langle t \rangle} = x^{\langle t+1 \rangle} y⟨t⟩=x⟨t+1⟩。
2 构建模型模块
在这一部分中,你将构建整个模型的两个重要模块:
- 梯度裁剪:避免梯度爆炸
- 采样:一种用于生成字符的技术
然后,你将应用这两个函数来构建模型。
2.1 在优化循环中裁剪梯度
在本节中,你将实现在优化循环中调用的clip
函数。回想一下,你的总体循环结构通常由正向传播,损失计算,反向传播和参数更新组成。在更新参数之前,你将在需要时执行梯度裁剪,以确保你的梯度不会“爆炸”,这意味着要采用很大的值。
在下面的练习中,你将实现一个函数clip
,该函数接收梯度字典,并在需要时返回裁剪后的梯度。梯度裁剪有很多方法。我们将使用简单的暗元素裁剪程序,其中将梯度向量的每个元素裁剪为位于范围
[
−
N
,
N
]
[-N, N]
[−N,N]之间。通常,你将提供一个maxValue
(例如10).在此示例中,如果梯度向量的任何分量大于10,则将其设置为10;并且如果梯度向量的任何分量小于-10,则将其设置为-10.如果介于-10和10之间,则将其保留。
图2:在网络遇到轻微的“梯度爆炸”的情况下,使用与不使用梯度裁剪的梯度下降对比。
练习:实现以下函数以返回字典gradients
的裁剪梯度。你的函数接受最大阈值,并返回裁剪后的梯度。你可以查看此hint,以获取有关如何裁剪numpy的示例。你将需要使用参数out = ...
。
def clip(gradients, maxValue):
"""
使用maxValue来修剪梯度
参数:
gradients -- 字典类型,包含了以下参数:"dWaa", "dWax", "dWya", "db", "dby"
maxValue -- 阈值,把梯度值限制在[-maxValue, maxValue]内
返回:
gradients -- 修剪后的梯度
"""
# 获取参数
dWaa, dWax, dWya, db, dby = gradients['dWaa'], gradients['dWax'], gradients['dWya'], gradients['db'], gradients['dby']
# 梯度修剪
for gradient in [dWaa, dWax, dWya, db, dby]:
np.clip(gradient, -maxValue, maxValue, out=gradient)
gradients = {"dWaa": dWaa, "dWax": dWax, "dWya": dWya, "db": db, "dby": dby}
return gradients
np.random.seed(3)
dWax = np.random.randn(5,3)*10
dWaa = np.random.randn(5,5)*10
dWya = np.random.randn(2,5)*10
db = np.random.randn(5,1)*10
dby = np.random.randn(2,1)*10
gradients = {"dWax": dWax, "dWaa": dWaa, "dWya": dWya, "db": db, "dby": dby}
gradients = clip(gradients, 10)
print("gradients[\"dWaa\"][1][2] =", gradients["dWaa"][1][2])
print("gradients[\"dWax\"][3][1] =", gradients["dWax"][3][1])
print("gradients[\"dWya\"][1][2] =", gradients["dWya"][1][2])
print("gradients[\"db\"][4] =", gradients["db"][4])
print("gradients[\"dby\"][1] =", gradients["dby"][1])
gradients["dWaa"][1][2] = 10.0
gradients["dWax"][3][1] = -10.0
gradients["dWya"][1][2] = 0.2971381536101662
gradients["db"][4] = [10.]
gradients["dby"][1] = [8.45833407]
2.2 采样
现在假设你的模型已经训练好。你想生成新文本(字符)。下图说明了生成过程:
图3:在此图中,我们假设模型已经训练好。我们在第一步中传入 x ⟨ 1 ⟩ = 0 ⃗ x^{\langle 1\rangle} = \vec{0} x⟨1⟩=0,然后让网络一次采样一个字符。
练习:实现以下的sample
函数来采样字母。你需要执行4个步骤:
- 步骤1:将第一个"dummy"输入 x ⟨ 1 ⟩ = 0 ⃗ x^{\langle 1 \rangle} = \vec{0} x⟨1⟩=0(零向量)传递给网络。这是我们生成任意字母之前的默认输入。我们还设置 x ⟨ 0 ⟩ = 0 ⃗ x^{\langle 0 \rangle} = \vec{0} x⟨0⟩=0。
- 步骤2:执行向正向传播的步骤,即可获得
a
⟨
1
⟩
a^{\langle 1 \rangle}
a⟨1⟩和
y
^
⟨
1
⟩
\hat{y}^{\langle 1 \rangle}
y^⟨1⟩。以下是等式:
a ⟨ t + 1 ⟩ = tanh ( W a x x ⟨ t ⟩ + W a a a ⟨ t ⟩ + b ) (1) a^{\langle t+1 \rangle} = \tanh(W_{ax} x^{\langle t \rangle } + W_{aa} a^{\langle t \rangle } + b)\tag{1} a⟨t+1⟩=tanh(Waxx⟨t⟩+Waaa⟨t⟩+b)(1)
z ⟨ t + 1 ⟩ = W y a a ⟨ t + 1 ⟩ + b y (2) z^{\langle t + 1 \rangle } = W_{ya} a^{\langle t + 1 \rangle } + b_y \tag{2} z⟨t+1⟩=Wyaa⟨t+1⟩+by(2)
y ^ ⟨ t + 1 ⟩ = s o f t m a x ( z ⟨ t + 1 ⟩ ) (3) \hat{y}^{\langle t+1 \rangle } = softmax(z^{\langle t + 1 \rangle })\tag{3} y^⟨t+1⟩=softmax(z⟨t+1⟩)(3)
注意 y ^ ⟨ t + 1 ⟩ \hat{y}^{\langle t+1 \rangle } y^⟨t+1⟩是一个(softmax)概率向量(其条目在0到1之间且总和为1)。 y ^ i ⟨ t + 1 ⟩ \hat{y}^{\langle t+1 \rangle}_i y^i⟨t+1⟩表示有"i"索引的字符是下一个字符的概率。我们提供了一个softmax()
函数供你使用。 - 步骤3:执行采用:根据
y
^
⟨
t
+
1
⟩
\hat{y}^{\langle t+1 \rangle }
y^⟨t+1⟩指定的概率分布,选择下一个字符的索引。这意味着,如果
y
^
i
⟨
t
+
1
⟩
=
0.16
\hat{y}^{\langle t+1 \rangle }_i = 0.16
y^i⟨t+1⟩=0.16,你将以16%的概率选择索引"i"。要实现它,你可以使用
np.random.choice
。
以下是一个使用np.random.choice()
的例子:
np.random.seed(0)
p = np.array([0.1, 0.0, 0.7, 0.2])
index = np.random.choice([0, 1, 2, 3], p = p.ravel())
这意味着你将根据分布选择`index`:$P(index = 0) = 0.1, P(index = 1) = 0.0, P(index = 2) = 0.7, P(index = 3) = 0.2$。
- 步骤4:要在
sample()
中实现的最后一步是覆盖变量x
,该变量当前存储 x ⟨ t ⟩ x^{\langle t \rangle } x⟨t⟩,其值为 x ⟨ t + 1 ⟩ x^{\langle t+1 \rangle } x⟨t+1⟩。通过创建与预测字符相对应的one-hot向量以表示为 x ⟨ t + 1 ⟩ x^{\langle t+1 \rangle } x⟨t+1⟩。然后,你将在步骤1中前向传播 x ⟨ t + 1 ⟩ x^{\langle t+1 \rangle } x⟨t+1⟩,并继续重复此过程,直到获得"\n"字符,表明你已经到达恐龙名称的末尾。
def sample(parameters, char_to_is, seed):
"""
根据RNN输出的概率分布序列对字符序列进行采样
参数:
parameters -- 包含了Waa, Wax, Wya, by, b的字典
char_to_ix -- 字符映射到索引的字典
seed -- 随机种子
返回:
indices -- 包含采样字符索引的长度为n的列表。
"""
# 从parameters 中获取参数
Waa, Wax, Wya, by, b = parameters['Waa'], parameters['Wax'], parameters['Wya'], parameters['by'], parameters['b']
vocab_size = by.shape[0]
n_a = Waa.shape[1]
# 步骤1
## 创建独热向量x
x = np.zeros((vocab_size,1))
## 使用0初始化a_prev
a_prev = np.zeros((n_a,1))
# 创建索引的空列表,这是包含要生成的字符的索引的列表。
indices = []
# IDX是检测换行符的标志,我们将其初始化为-1。
idx = -1
# 循环遍历时间步骤t。在每个时间步中,从概率分布中抽取一个字符,
# 并将其索引附加到“indices”上,如果我们达到50个字符,
#(我们应该不太可能有一个训练好的模型),我们将停止循环,这有助于调试并防止进入无限循环
counter = 0
newline_character = char_to_ix["\n"]
while (idx != newline_character and counter < 50):
# 步骤2:使用公式1、2、3进行前向传播
a = np.tanh(np.dot(Wax, x) + np.dot(Waa, a_prev) + b)
z = np.dot(Wya, a) + by
y = softmax(z)
# 设定随机种子
np.random.seed(counter + seed)
# 步骤3:从概率分布y中抽取词汇表中字符的索引
idx = np.random.choice(list(range(vocab_size)), p=y.ravel())
# 添加到索引中
indices.append(idx)
# 步骤4:将输入字符重写为与采样索引对应的字符。
x = np.zeros((vocab_size,1))
x[idx] = 1
# 更新a_prev为a
a_prev = a
# 累加器
seed += 1
counter +=1
if(counter == 50):
indices.append(char_to_ix["\n"])
return indices
np.random.seed(2)
_, n_a = 20, 100
Wax, Waa, Wya = np.random.randn(n_a, vocab_size), np.random.randn(n_a, n_a), np.random.randn(vocab_size, n_a)
b, by = np.random.randn(n_a, 1), np.random.randn(vocab_size, 1)
parameters = {"Wax": Wax, "Waa": Waa, "Wya": Wya, "b": b, "by": by}
indices = sample(parameters, char_to_ix, 0)
print("Sampling:")
print("list of sampled indices:", indices)
print("list of sampled characters:", [ix_to_char[i] for i in indices])
Sampling:
list of sampled indices: [12, 17, 24, 14, 13, 9, 10, 22, 24, 6, 13, 11, 12, 6, 21, 15, 21, 14, 3, 2, 1, 21, 18, 24, 7, 25, 6, 25, 18, 10, 16, 2, 3, 8, 15, 12, 11, 7, 1, 12, 10, 2, 7, 7, 11, 17, 24, 12, 12, 0, 0]
list of sampled characters: ['l', 'q', 'x', 'n', 'm', 'i', 'j', 'v', 'x', 'f', 'm', 'k', 'l', 'f', 'u', 'o', 'u', 'n', 'c', 'b', 'a', 'u', 'r', 'x', 'g', 'y', 'f', 'y', 'r', 'j', 'p', 'b', 'c', 'h', 'o', 'l', 'k', 'g', 'a', 'l', 'j', 'b', 'g', 'g', 'k', 'q', 'x', 'l', 'l', '\n', '\n']
3 建立语言模型
现在是时候建立用于文字生成的字母级语言模型了。
3.1 梯度下降
在本部分中,你将实现一个函数,该函数执行随机梯度下降的一个步骤(梯度裁剪)。你将一次查看一个训练示例,因此优化算法为随机梯度下降。提醒一下,以下是RNN常见的优化循环的步骤:
- 通过RNN正向传播以计算损失
- 随时间反向传播以计算相对于参数的损失梯度
- 必要时裁剪梯度
- 使用梯度下降更新参数
练习:实现此优化过程(随机梯度下降的一个步骤)。
我们来实现这一优化过程(单步随机梯度下降),这里我们提供了一些函数:
# 示例,请勿执行。
def rnn_forward(X, Y, a_prev, parameters):
"""
通过RNN进行前向传播,计算交叉熵损失。
它返回损失的值以及存储在反向传播中使用的“缓存”值。
"""
....
return loss, cache
def rnn_backward(X, Y, parameters, cache):
"""
通过时间进行反向传播,计算相对于参数的梯度损失。它还返回所有隐藏的状态
"""
...
return gradients, a
def update_parameters(parameters, gradients, learning_rate):
"""
使用梯度下降更新参数
"""
...
return parameters
我们来构建优化函数:
def optimize(X, Y, a_prev, parameters, learning_rate = 0.01):
"""
执行训练模型的单步优化。
参数:
X -- 整数列表,其中每个整数映射到词汇表中的字符。
Y -- 整数列表,与X完全相同,但向左移动了一个索引。
a_prev -- 上一个隐藏状态
parameters -- 字典,包含了以下参数:
Wax -- 权重矩阵乘以输入,维度为(n_a, n_x)
Waa -- 权重矩阵乘以隐藏状态,维度为(n_a, n_a)
Wya -- 隐藏状态与输出相关的权重矩阵,维度为(n_y, n_a)
b -- 偏置,维度为(n_a, 1)
by -- 隐藏状态与输出相关的权重偏置,维度为(n_y, 1)
learning_rate -- 模型学习的速率
返回:
loss -- 损失函数的值(交叉熵损失)
gradients -- 字典,包含了以下参数:
dWax -- 输入到隐藏的权值的梯度,维度为(n_a, n_x)
dWaa -- 隐藏到隐藏的权值的梯度,维度为(n_a, n_a)
dWya -- 隐藏到输出的权值的梯度,维度为(n_y, n_a)
db -- 偏置的梯度,维度为(n_a, 1)
dby -- 输出偏置向量的梯度,维度为(n_y, 1)
a[len(X)-1] -- 最后的隐藏状态,维度为(n_a, 1)
"""
# 前向传播
loss, cache = rnn_forward(X, Y, a_prev, parameters)
# 反向传播
gradients, a = rnn_backward(X, Y, parameters, cache)
# 梯度修剪,[-5 , 5]
gradients = clip(gradients,5)
# 更新参数
parameters = update_parameters(parameters,gradients,learning_rate)
return loss, gradients, a[len(X)-1]
np.random.seed(1)
vocab_size, n_a = 27, 100
a_prev = np.random.randn(n_a, 1)
Wax, Waa, Wya = np.random.randn(n_a, vocab_size), np.random.randn(n_a, n_a), np.random.randn(vocab_size, n_a)
b, by = np.random.randn(n_a, 1), np.random.randn(vocab_size, 1)
parameters = {"Wax": Wax, "Waa": Waa, "Wya": Wya, "b": b, "by": by}
X = [12,3,5,11,22,3]
Y = [4,14,11,22,25, 26]
loss, gradients, a_last = optimize(X, Y, a_prev, parameters, learning_rate = 0.01)
print("Loss =", loss)
print("gradients[\"dWaa\"][1][2] =", gradients["dWaa"][1][2])
print("np.argmax(gradients[\"dWax\"]) =", np.argmax(gradients["dWax"]))
print("gradients[\"dWya\"][1][2] =", gradients["dWya"][1][2])
print("gradients[\"db\"][4] =", gradients["db"][4])
print("gradients[\"dby\"][1] =", gradients["dby"][1])
print("a_last[4] =", a_last[4])
Loss = 126.50397572165362
gradients["dWaa"][1][2] = 0.19470931534720892
np.argmax(gradients["dWax"]) = 93
gradients["dWya"][1][2] = -0.007773876032003782
gradients["db"][4] = [-0.06809825]
gradients["dby"][1] = [0.01538192]
a_last[4] = [-1.]
3.2 训练模型
给定恐龙名称数据集,我们将数据集的每一行(一个名称)用作一个训练示例。每100步随机梯度下降,你将抽样10个随机选择的名称,以查看算法的运行情况。请记住要对数据集进行混洗,以便随机梯度下降以随机顺序访问示例。
练习:按照说明进行操作并实现model()
。当examples[index]
包含一个恐龙名称(字符串)时,创建示例(X,Y),可以使用以下方法:
index = j % len(examples)
X = [None] + [char_to_ix[ch] for ch in examples[index]]
Y = X[1:] + [char_to_ix["\n"]]
注意,我们使用:index= j % len(examples)
,其中j = 1....num_iterations
,以确保examples[index]
始终是有效的语句(index
小于len(examples)
)。
X
的第一个条目为None将被rnn_forward()
解释为设置
x
⟨
0
⟩
=
0
⃗
x^{\langle 0 \rangle} = \vec{0}
x⟨0⟩=0。此外,这确保了Y
等于X
,但向左移动了一步,并附加了“\n
”以表示恐龙名称的结尾。
def model(data, ix_to_char, char_to_ix, num_iterations=3500,
n_a=50, dino_names=7,vocab_size=27):
"""
训练模型并生成恐龙名字
参数:
data -- 语料库
ix_to_char -- 索引映射字符字典
char_to_ix -- 字符映射索引字典
num_iterations -- 迭代次数
n_a -- RNN单元数量
dino_names -- 每次迭代中采样的数量
vocab_size -- 在文本中的唯一字符的数量
返回:
parameters -- 学习后了的参数
"""
# 从vocab_size中获取n_x、n_y
n_x, n_y = vocab_size, vocab_size
# 初始化参数
parameters = initialize_parameters(n_a, n_x, n_y)
# 初始化损失
loss = get_initial_loss(vocab_size, dino_names)
# 构建恐龙名称列表
with open("datasets/dinos.txt") as f:
examples = f.readlines()
examples = [x.lower().strip() for x in examples]
# 打乱全部的恐龙名称
np.random.seed(0)
np.random.shuffle(examples)
# 初始化LSTM隐藏状态
a_prev = np.zeros((n_a,1))
# 循环
for j in range(num_iterations):
# 定义一个训练样本
index = j % len(examples)
X = [None] + [char_to_ix[ch] for ch in examples[index]]
Y = X[1:] + [char_to_ix["\n"]]
# 执行单步优化:前向传播 -> 反向传播 -> 梯度修剪 -> 更新参数
# 选择学习率为0.01
curr_loss, gradients, a_prev = optimize(X, Y, a_prev, parameters)
# 使用延迟来保持损失平滑,这是为了加速训练。
loss = smooth(loss, curr_loss)
# 每2000次迭代,通过sample()生成“\n”字符,检查模型是否学习正确
if j % 2000 == 0:
print("第" + str(j+1) + "次迭代,损失值为:" + str(loss))
seed = 0
for name in range(dino_names):
# 采样
sampled_indices = sample(parameters, char_to_ix, seed)
print_sample(sampled_indices, ix_to_char)
# 为了得到相同的效果,随机种子+1
seed += 1
print("\n")
return parameters
#开始时间
start_time = time.clock()
#开始训练
parameters = model(data, ix_to_char, char_to_ix, num_iterations=35000)
#结束时间
end_time = time.clock()
#计算时差
minium = end_time - start_time
print("执行了:" + str(int(minium / 60)) + "分" + str(int(minium%60)) + "秒")
第1次迭代,损失值为:23.087336085484605
Nkzxwtdmfqoeyhsqwasjkjvu
Kneb
Kzxwtdmfqoeyhsqwasjkjvu
Neb
Zxwtdmfqoeyhsqwasjkjvu
Eb
Xwtdmfqoeyhsqwasjkjvu
第2001次迭代,损失值为:27.884160491415777
Liusskeomnolxeros
Hmdaairus
Hytroligoraurus
Lecalosapaus
Xusicikoraurus
Abalpsamantisaurus
Tpraneronxeros
第4001次迭代,损失值为:25.90181489335302
Mivrosaurus
Inee
Ivtroplisaurus
Mbaaisaurus
Wusichisaurus
Cabaselachus
Toraperlethosdarenitochusthiamamumamaon
第6001次迭代,损失值为:24.608778900832394
Onwusceomosaurus
Lieeaerosaurus
Lxussaurus
Oma
Xusteonosaurus
Eeahosaurus
Toreonosaurus
第8001次迭代,损失值为:24.070350147705277
Onxusichepriuon
Kilabersaurus
Lutrodon
Omaaerosaurus
Xutrcheps
Edaksoje
Trodiktonus
第10001次迭代,损失值为:23.84444646002657
Onyusaurus
Klecalosaurus
Lustodon
Ola
Xusodonia
Eeaeosaurus
Troceosaurus
第12001次迭代,损失值为:23.291971267939104
Onyxosaurus
Kica
Lustrepiosaurus
Olaagrraiansaurus
Yuspangosaurus
Eealosaurus
Trognesaurus
第14001次迭代,损失值为:23.38233761730599
Meutromodromurus
Inda
Iutroinatorsaurus
Maca
Yusteratoptititan
Ca
Troclosaurus
第16001次迭代,损失值为:23.28294611567149
Mdyusaurus
Indaacosaupisaurus
Justolong
Maca
Yuspandosaurus
Cabaspadantes
Trodon
第18001次迭代,损失值为:22.85081326872384
Phyusaurus
Meja
Mystoosaurus
Pegamosaurus
Yusmaphosaurus
Eiahosaurus
Trolonosaurus
第20001次迭代,损失值为:22.929325599583475
Nlyusaurus
Logbalosaurus
Lvuslangosaurus
Necalosaurus
Ytrrangosaurus
Ekairus
Troenesaurus
第22001次迭代,损失值为:22.760728439539477
Onvusaroglolonoshareimus
Llecaerona
Myrrocephoeurus
Pedacosaurus
Ytrodonosaurus
Eiadosaurus
Trodonosaurus
第24001次迭代,损失值为:22.57720843347023
Meustrhe
Inca
Jurrollesaurus
Medaislleamlis
Yusaurus
Cabasigaansaurus
Trocerator
第26001次迭代,损失值为:22.616434369927045
Onyxosaurus
Lelealosaurus
Lustolialneusaurns
Olaahus
Yusichesaurus
Ehairuodan
Trochenomusaurus
第28001次迭代,损失值为:22.52468955468878
Meutosaurus
Kracafosaurus
Lvtosaurus
Necaisaurus
Yusiadgosaurus
Eiadosaurus
Trocephicrisaurus
第30001次迭代,损失值为:22.719787638280476
Peutroknethithosaurus
Llacalosaurus
Lutroenator
Pacalosaurus
Yusolonosaurus
Edacosaurus
Trocheshathystatepiscaptiuiman
第32001次迭代,损失值为:22.23296009953549
Meustrathus
Kojcanosaurus
Lustrasaurus
Macalosaurus
Yusiandon
Eeainor
Usianatorsaurus
第34001次迭代,损失值为:22.318447496893175
Olusaurus
Klacaesaurus
Lusnsaurus
Ola
Ytosaurus
Egaisaurus
Trhangosaurus
执行了:0分38秒
d:\vr\virtual_environment\lib\site-packages\ipykernel_launcher.py:8: DeprecationWarning: time.clock has been deprecated in Python 3.3 and will be removed from Python 3.8: use time.perf_counter or time.process_time instead
结论
你可以看到,在训练即将结束时,你的算法已开始生成合理的恐龙名称。刚开始时,它会生成随机字符,但是到最后,你会看到恐龙名字的结尾很酷。运行该算法更长时间,并调整超参数来看看是否可以获得更好的结果。我们的实现产生了一些非常酷的名称,例如“maconucon”,“marloralus”和“macingsersaurus”。你的模型还有望了解到恐龙名称往往以saurus
,don
,aura
,tor
等结尾。
如果你的模型生成了一些不酷的名字,请不要完全怪罪模型-并非所有实际的恐龙名字听起来都很酷。(例如,dromaeosauroides是实际存在的恐龙名称,并且也在训练集中。)但是此模型应该给你提供了一组可以从中挑选的候选名字!
该作业使用了相对较小的数据集,因此你可以在CPU上快速训练RNN。训练英语模型需要更大的数据集,并且通常需要更多的计算,在GPU上也要运行多个小时。我们使用恐龙的名字已经有一段时间了,到目前为止,我们最喜欢的名字是great, undefeatable,且fierce的:Mangosaurus!
4 像莎士比亚一样创作
该笔记本的其余部分是可选的,尚未评分,但我们希望你都尝试做一下,因为它非常有趣且内容丰富。
一个类似(但更复杂)的任务是生成莎士比亚诗歌。无需从恐龙名称的数据集中学习,而是使用莎士比亚诗歌集。使用LSTM单元,你可以学习跨文本中许多字符的长期依赖关系。例如,某个字符出现在序列的某个地方可能会影响序列后面的其他字符。这些长期依赖关系对于恐龙名称来说不太重要,因为它们的名称很短。
让我们成为诗人!
我们已经用Keras实现了莎士比亚诗歌生成器。运行以下单元格以加载所需的软件包和模型。这可能需要几分钟的时间。
from __future__ import print_function
from keras.callbacks import LambdaCallback
from keras.models import Model, load_model, Sequential
from keras.layers import Dense, Activation, Dropout, Input, Masking
from keras.layers import LSTM
from keras.utils.data_utils import get_file
from keras.preprocessing.sequence import pad_sequences
from shakespeare_utils import *
import sys
import io
Using TensorFlow backend.
Loading text data...
Creating training set...
number of training examples: 31412
Vectorizing training set...
Loading model...
WARNING:tensorflow:From d:\vr\virtual_environment\lib\site-packages\tensorflow_core\python\ops\resource_variable_ops.py:1630: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
Instructions for updating:
If using Keras pass *_constraint arguments to layers.
WARNING:tensorflow:From d:\vr\virtual_environment\lib\site-packages\tensorflow_core\python\ops\math_grad.py:1424: where (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where
WARNING:tensorflow:From d:\vr\virtual_environment\lib\site-packages\keras\backend\tensorflow_backend.py:422: The name tf.global_variables is deprecated. Please use tf.compat.v1.global_variables instead.
d:\vr\virtual_environment\lib\site-packages\keras\engine\saving.py:384: UserWarning: Error in loading the saved optimizer state. As a result, your model is starting with a freshly initialized optimizer.
warnings.warn('Error in loading the saved optimizer '
为了节省你的时间,我们已经在莎士比亚的十四行诗"The Sonnets"诗歌集上训练了大约1000个epoch的模型。
让我们再训练模型完成一个新epoch,这也将花费几分钟。你可以运行generate_output
,这将提示你输入小于40个字符的句子。这首诗将从你输入的句子开始,我们的RNN-Shakespeare将为你完成其余的部分!例如,尝试"Forsooth this maketh no sense "(不要输入引号)。根据是否在末尾加上空格,你的结果也可能会有所不同,两种方法都应尝试,也可以尝试其他输入法。
print_callback = LambdaCallback(on_epoch_end=on_epoch_end)
model.fit(x, y, batch_size=128, epochs=1, callbacks=[print_callback])
Epoch 1/1
31412/31412 [==============================] - 31s 992us/step - loss: 2.7320
<keras.callbacks.callbacks.History at 0x20c382edb38>
# 运行此代码尝试不同的输入,而不必重新训练模型。
generate_output() # 博主输入"Forsooth this maketh no sense"
Write the beginning of your poem, the Shakespeare machine will complete it. Your input is: Forsooth this maketh no sense
Here is your poem:
Forsooth this maketh no senses,
sin love that hiss of o no crusth,
and doth my berod fear i be two deared,
the hames for sichn three from when his biml age,
when thene his frotery i con dost,
whone knife whace a haor wor, wire not so,
the mesters shound be for yet befanot dight,
i unding thy beauty holl o lide to vears,
and bu thy grint hapl a do before.
that i hast to cell i pand
co whot i womd do bester is have racpeated,
RNN-Shakespeare模型与你为恐龙名称构建的模型非常相似。唯一的区别是:
- LSTM代替基本的RNN来捕获更远的依赖
- 模型是更深的堆叠的LSTM模型(2层)
- 使用Keras而不是python来简化代码