• 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
    • 2
    • 3
    • 4

    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))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    ['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个
    
    • 1
    • 2

    这些字符是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)
    
    • 1
    • 2
    • 3
    {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

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    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])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    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]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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())
    
    • 1
    • 2
    • 3
    这意味着你将根据分布选择`index`:$P(index = 0) = 0.1, P(index = 1) = 0.0, P(index = 2) = 0.7, P(index = 3) = 0.2$。
    
    • 1
    • 步骤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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    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])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    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']
    
    • 1
    • 2
    • 3

    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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    我们来构建优化函数:

    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]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    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])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    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.]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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"]]
    
    • 1
    • 2
    • 3

    注意,我们使用: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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    #开始时间
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    第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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184

    结论

    你可以看到,在训练即将结束时,你的算法已开始生成合理的恐龙名称。刚开始时,它会生成随机字符,但是到最后,你会看到恐龙名字的结尾很酷。运行该算法更长时间,并调整超参数来看看是否可以获得更好的结果。我们的实现产生了一些非常酷的名称,例如“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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    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 '
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    为了节省你的时间,我们已经在莎士比亚的十四行诗"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])
    
    • 1
    • 2
    • 3
    Epoch 1/1
    31412/31412 [==============================] - 31s 992us/step - loss: 2.7320
    
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    # 运行此代码尝试不同的输入,而不必重新训练模型。
    generate_output() # 博主输入"Forsooth this maketh no sense"
    
    • 1
    • 2
    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,
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    RNN-Shakespeare模型与你为恐龙名称构建的模型非常相似。唯一的区别是:

    • LSTM代替基本的RNN来捕获更远的依赖
    • 模型是更深的堆叠的LSTM模型(2层)
    • 使用Keras而不是python来简化代码
  • 相关阅读:
    计算机指令
    QToolButton几个小知识点总结
    不会做项目惨遭部门领导批评,连刷35天分布式小册轻松拿下
    文理导航杂志文理导航杂志社文理导航编辑部2022年第12期目录
    在unity中利用公开变量引用物体和组件(有手就会)
    Eclipse的MAT的支配树
    0基础学习VR全景平台篇 第95篇:VR实景智慧导航操作手册
    达人评测酷睿i5 12450h和锐龙R5 6600u选哪个 i512450h和锐龙R56600u对比
    openjudge 1.8.15 细菌的繁殖与扩散
    2022软件创新实验室暑假集训21级大作业说明
  • 原文地址:https://blog.csdn.net/qq_41476257/article/details/126058322