Python吴恩达深度学习作业19 -- 字符级语言模型

字母级语言模型 - Dinosaurus land

欢迎来到恐龙大陆! 6500万年前,恐龙就已经存在,并且在该作业下它们又回来了。假设你负责一项特殊任务,领先的生物学研究人员正在创造新的恐龙品种,并计划将它们带入地球,而你的工作就是为这些新恐龙起名字。如果恐龙不喜欢它的名字,它可能会变得疯狂,所以需要明智地选择!
在这里插入图片描述

幸运的是,你掌握了深度学习的一些知识,你将使用它来节省时间。你的助手已收集了它们可以找到的所有恐龙名称的列表,并将其编译到此dataset中。(请单击上一个链接查看)要创建新的恐龙名称,你将构建一个字母级语言模型来生成新名称。你的算法将学习不同的名称模式,并随机生成新名称。希望该算法可以使你和你的团队免受恐龙的愤怒!

完成此作业,你将学习:

  • 如何存储文本数据以供RNN使用
  • 如何在每个时间步采样预测并将其传递给下一个RNN单元以合成数据
  • 如何建立一个字母级的文本生成循环神经网络
  • 为什么梯度裁剪很重要

外面将加载rnn_utils中为你提供的一些函数开始。具体来说,你可以访问诸如rnn_forwardrnn_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_ixix_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=(x1,x2,...,xTx)是训练集中的字符列表,而 Y = ( y ⟨ 1 ⟩ , y ⟨ 2 ⟩ , . . . , y ⟨ T x ⟩ ) Y = (y^{\langle 1 \rangle}, y^{\langle 2 \rangle}, ..., y^{\langle T_x \rangle}) Y=(y1,y2,...,yTx)使得每个时间步 t t t,我们有 y ⟨ t ⟩ = x ⟨ t + 1 ⟩ y^{\langle t \rangle} = x^{\langle t+1 \rangle} yt=xt+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} x1=0 ,然后让网络一次采样一个字符。

练习:实现以下的sample函数来采样字母。你需要执行4个步骤:

  • 步骤1:将第一个"dummy"输入 x ⟨ 1 ⟩ = 0 ⃗ x^{\langle 1 \rangle} = \vec{0} x1=0 (零向量)传递给网络。这是我们生成任意字母之前的默认输入。我们还设置 x ⟨ 0 ⟩ = 0 ⃗ x^{\langle 0 \rangle} = \vec{0} x0=0
  • 步骤2:执行向正向传播的步骤,即可获得 a ⟨ 1 ⟩ a^{\langle 1 \rangle} a1 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} at+1=tanh(Waxxt+Waaat+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} zt+1=Wyaat+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(zt+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^it+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^it+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 } xt,其值为 x ⟨ t + 1 ⟩ x^{\langle t+1 \rangle } xt+1。通过创建与预测字符相对应的one-hot向量以表示为 x ⟨ t + 1 ⟩ x^{\langle t+1 \rangle } xt+1。然后,你将在步骤1中前向传播 x ⟨ t + 1 ⟩ x^{\langle t+1 \rangle } xt+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} x0=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”。你的模型还有望了解到恐龙名称往往以saurusdonaurator等结尾。

如果你的模型生成了一些不酷的名字,请不要完全怪罪模型-并非所有实际的恐龙名字听起来都很酷。(例如,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来简化代码
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Puzzle harvester

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值