• [深度学习]动手学深度学习笔记-6


    Task-3——循环神经网络进阶
    在这里插入图片描述

    6.1 长短期记忆(LSTM)

    6.1.1 理论知识理解

    在这里插入图片描述
    在这里插入图片描述
    理解LSTM网络

    6.1.2 LSTM的从零开始实现

    以下附上代码:
    导入相应的包

    import numpy as np
    import torch
    from torch import nn, optim
    import torch.nn.functional as F
    
    import sys
    sys.path.append("..") 
    import d2lzh_pytorch as d2l
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    
    (corpus_indices, char_to_idx, idx_to_char, vocab_size) = d2l.load_data_jay_lyrics()
    

    初始化模型参数

    num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
    print('will use', device)
    
    def get_params():
        def _one(shape):
            ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32)
            return torch.nn.Parameter(ts, requires_grad=True)
        def _three():
            return (_one((num_inputs, num_hiddens)),
                    _one((num_hiddens, num_hiddens)),
                    torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))
        
        W_xi, W_hi, b_i = _three()  # 输入门参数
        W_xf, W_hf, b_f = _three()  # 遗忘门参数
        W_xo, W_ho, b_o = _three()  # 输出门参数
        W_xc, W_hc, b_c = _three()  # 候选记忆细胞参数
        
        # 输出层参数
        W_hq = _one((num_hiddens, num_outputs))
        b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)
        return nn.ParameterList([W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q])
    

    定义模型

    def init_lstm_state(batch_size, num_hiddens, device):
        return (torch.zeros((batch_size, num_hiddens), device=device), 
                torch.zeros((batch_size, num_hiddens), device=device))
    
    def lstm(inputs, state, params):
        [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q] = params
        (H, C) = state
        outputs = []
        for X in inputs:
            I = torch.sigmoid(torch.matmul(X, W_xi) + torch.matmul(H, W_hi) + b_i)
            F = torch.sigmoid(torch.matmul(X, W_xf) + torch.matmul(H, W_hf) + b_f)
            O = torch.sigmoid(torch.matmul(X, W_xo) + torch.matmul(H, W_ho) + b_o)
            C_tilda = torch.tanh(torch.matmul(X, W_xc) + torch.matmul(H, W_hc) + b_c)
            C = F * C + I * C_tilda
            H = O * C.tanh()
            Y = torch.matmul(H, W_hq) + b_q
            outputs.append(Y)
        return outputs, (H, C)
    

    训练模型并创作歌词

    num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
    pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']
    
    d2l.train_and_predict_rnn(lstm, get_params, init_lstm_state, num_hiddens,
                              vocab_size, device, corpus_indices, idx_to_char,
                              char_to_idx, False, num_epochs, num_steps, lr,
                              clipping_theta, batch_size, pred_period, pred_len,
                              prefixes)
    

    输出

    epoch 40, perplexity 211.416571, time 1.37 sec
     - 分开 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我
     - 不分开 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我
    epoch 80, perplexity 67.048346, time 1.35 sec
     - 分开 我想你你 我不要再想 我不要这我 我不要这我 我不要这我 我不要这我 我不要这我 我不要这我 我不
     - 不分开 我想你你想你 我不要这不样 我不要这我 我不要这我 我不要这我 我不要这我 我不要这我 我不要这我
    epoch 120, perplexity 15.552743, time 1.36 sec
     - 分开 我想带你的微笑 像这在 你想我 我想你 说你我 说你了 说给怎么么 有你在空 你在在空 在你的空 
     - 不分开 我想要你已经堡 一样样 说你了 我想就这样着你 不知不觉 你已了离开活 后知后觉 我该了这生活 我
    epoch 160, perplexity 4.274031, time 1.35 sec
     - 分开 我想带你 你不一外在半空 我只能够远远著她 这些我 你想我难难头 一话看人对落我一望望我 我不那这
     - 不分开 我想你这生堡 我知好烦 你不的节我 后知后觉 我该了这节奏 后知后觉 又过了一个秋 后知后觉 我该
    

    6.1.3 LSTM的pytorch实现

    lr = 1e-2 # 注意调整学习率
    lstm_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens)
    model = d2l.RNNModel(lstm_layer, vocab_size)
    d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
                                    corpus_indices, idx_to_char, char_to_idx,
                                    num_epochs, num_steps, lr, clipping_theta,
                                    batch_size, pred_period, pred_len, prefixes)
    

    输出:

    epoch 40, perplexity 1.020401, time 1.54 sec
     - 分开始想担 妈跟我 一定是我妈在 因为分手前那句抱歉 在感动 穿梭时间的画面的钟 从反方向开始移动 回到
     - 不分开始想像 妈跟我 我将我的寂寞封闭 然后在这里 不限日期 然后将过去 慢慢温习 让我爱上你 那场悲剧 
    epoch 80, perplexity 1.011164, time 1.34 sec
     - 分开始想担 你的 从前的可爱女人 温柔的让我心疼的可爱女人 透明的让我感动的可爱女人 坏坏的让我疯狂的可
     - 不分开 我满了 让我疯狂的可爱女人 漂亮的让我面红的可爱女人 温柔的让我心疼的可爱女人 透明的让我感动的可
    epoch 120, perplexity 1.025348, time 1.39 sec
     - 分开始共渡每一天 手牵手 一步两步三步四步望著天 看星星 一颗两颗三颗四颗 连成线背著背默默许下心愿 看
     - 不分开 我不懂 说了没用 他的笑容 有何不同 在你心中 我不再受宠 我的天空 是雨是风 还是彩虹 你在操纵
    epoch 160, perplexity 1.017492, time 1.42 sec
     - 分开始乡相信命运 感谢地心引力 让我碰到你 漂亮的让我面红的可爱女人 温柔的让我心疼的可爱女人 透明的让
     - 不分开 我不能再想 我不 我不 我不能 爱情走的太快就像龙卷风 不能承受我已无处可躲 我不要再想 我不要再
    

    6.2 门控循环单元(GRU)

    在这里插入图片描述

    6.2.1 GRU的从零开始实现

    代码如下:
    导入相应的包:

    import numpy as np
    import torch
    from torch import nn, optim
    import torch.nn.functional as F
    
    import sys
    sys.path.append("..") 
    import d2lzh_pytorch as d2l
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    
    (corpus_indices, char_to_idx, idx_to_char, vocab_size) = d2l.load_data_jay_lyrics()
    

    初始化模型参数

    num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
    print('will use', device)
    
    def get_params():
        def _one(shape):
            ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32)
            return torch.nn.Parameter(ts, requires_grad=True)
        def _three():
            return (_one((num_inputs, num_hiddens)),
                    _one((num_hiddens, num_hiddens)),
                    torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))
        
        W_xz, W_hz, b_z = _three()  # 更新门参数
        W_xr, W_hr, b_r = _three()  # 重置门参数
        W_xh, W_hh, b_h = _three()  # 候选隐藏状态参数
        
        # 输出层参数
        W_hq = _one((num_hiddens, num_outputs))
        b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)
        return nn.ParameterList([W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q])
    

    定义模型

    def init_gru_state(batch_size, num_hiddens, device):
        return (torch.zeros((batch_size, num_hiddens), device=device), )
    
    def gru(inputs, state, params):
        W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q = params
        H, = state
        outputs = []
        for X in inputs:
            Z = torch.sigmoid(torch.matmul(X, W_xz) + torch.matmul(H, W_hz) + b_z)
            R = torch.sigmoid(torch.matmul(X, W_xr) + torch.matmul(H, W_hr) + b_r)
            H_tilda = torch.tanh(torch.matmul(X, W_xh) + torch.matmul(R * H, W_hh) + b_h)
            H = Z * H + (1 - Z) * H_tilda
            Y = torch.matmul(H, W_hq) + b_q
            outputs.append(Y)
        return outputs, (H,)
    

    训练模型并创作歌词

    num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
    pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']
    
    d2l.train_and_predict_rnn(gru, get_params, init_gru_state, num_hiddens,
                              vocab_size, device, corpus_indices, idx_to_char,
                              char_to_idx, False, num_epochs, num_steps, lr,
                              clipping_theta, batch_size, pred_period, pred_len,
                              prefixes)
    

    输出:

    epoch 40, perplexity 149.477598, time 1.08 sec
     - 分开 我不不你 我想你你的爱我 你不你的让我 你不你的让我 你不你的让我 你不你的让我 你不你的让我 你
     - 不分开 我想你你的让我 你不你的让我 你不你的让我 你不你的让我 你不你的让我 你不你的让我 你不你的让我
    epoch 80, perplexity 31.689210, time 1.10 sec
     - 分开 我想要你 我不要再想 我不要再想 我不要再想 我不要再想 我不要再想 我不要再想 我不要再想 我不
     - 不分开 我想要你 我不要再想 我不要再想 我不要再想 我不要再想 我不要再想 我不要再想 我不要再想 我不
    epoch 120, perplexity 4.866115, time 1.08 sec
     - 分开 我想要这样牵着你的手不放开 爱过 让我来的肩膀 一起好酒 你来了这节秋 后知后觉 我该好好生活 我
     - 不分开 你已经不了我不要 我不要再想你 我不要再想你 我不要再想你 不知不觉 我跟了这节奏 后知后觉 又过
    epoch 160, perplexity 1.442282, time 1.51 sec
     - 分开 我一定好生忧 唱着歌 一直走 我想就这样牵着你的手不放开 爱可不可以简简单单没有伤害 你 靠着我的
     - 不分开 你已经离开我 不知不觉 我跟了这节奏 后知后觉 又过了一个秋 后知后觉 我该好好生活 我该好好生活
    

    6.2.2 GRU的pytorch实现

    lr = 1e-2 # 注意调整学习率
    gru_layer = nn.GRU(input_size=vocab_size, hidden_size=num_hiddens)
    model = d2l.RNNModel(gru_layer, vocab_size).to(device)
    d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
                                    corpus_indices, idx_to_char, char_to_idx,
                                    num_epochs, num_steps, lr, clipping_theta,
                                    batch_size, pred_period, pred_len, prefixes)
    

    输出:

    epoch 40, perplexity 1.022157, time 1.02 sec
     - 分开手牵手 一步两步三步四步望著天 看星星 一颗两颗三颗四颗 连成线背著背默默许下心愿 看远方的星是否听
     - 不分开暴风圈来不及逃 我不能再想 我不能再想 我不 我不 我不能 爱情走的太快就像龙卷风 不能承受我已无处
    epoch 80, perplexity 1.014535, time 1.04 sec
     - 分开始想像 爸和妈当年的模样 说著一口吴侬软语的姑娘缓缓走过外滩 消失的 旧时光 一九四三 在回忆 的路
     - 不分开始爱像  不知不觉 你已经离开我 不知不觉 我跟了这节奏 后知后觉 又过了一个秋 后知后觉 我该好好
    epoch 120, perplexity 1.147843, time 1.04 sec
     - 分开都靠我 你拿着球不投 又不会掩护我 选你这种队友 瞎透了我 说你说 分数怎么停留 所有回忆对着我进攻
     - 不分开球我有多烦恼多 牧草有没有危险 一场梦 我面对我 甩开球我满腔的怒火 我想揍你已经很久 别想躲 说你
    epoch 160, perplexity 1.018370, time 1.05 sec
     - 分开爱上你 那场悲剧 是你完美演出的一场戏 宁愿心碎哭泣 再狠狠忘记 你爱过我的证据 让晶莹的泪滴 闪烁
     - 不分开始 担心今天的你过得好不好 整个画面是你 想你想的睡不著 嘴嘟嘟那可爱的模样 还有在你身上香香的味道
    

    6.3 错题反析

    本次练习,错了两个题:
    在这里插入图片描述
    每个循环单元中的记忆细胞和循环单元的值为LSTM模型中的隐状态,而非参数,因此不需要初始化。
    在这里插入图片描述
    此题,当时的想法是LSTM和GRU只能解决梯度爆炸问题,故而没细看一眼就能知道粗的C选型。
    我为此参考了一篇比较好的博客https://www.cnblogs.com/pinking/p/9362966.html

    为了解决RNN的梯度问题,首先有人提出了渗透单元的办法,即在时间轴上增加跳跃连接,后推广成LSTM。LSTM其门结构,提供了一种对梯度的选择的作用。
    在这里插入图片描述
    对于门结构,其实如果关闭,则会一直保存以前的信息,其实也就是缩短了链式求导。
    在这里插入图片描述
    譬如,对某些输入张量训练得到的ft一直为1,则Ct-1的信息可以一直保存,直到有输入x得到的ft为0,则和前面的信息就没有关系了。故解决了长时间的依赖问题。因为门控机制的存在,我们通过控制门的打开、关闭等操作,让梯度计算沿着梯度乘积接近1的部分创建路径。
    在这里插入图片描述
    如上,可以通过门的控制,看到红色和蓝色箭头代表的路径下,yt+1的在这个路径下的梯度与上一时刻梯度保持不变。

    对于信息增加门与忘记门的“+”操作,其求导是加法操作而不是乘法操作,该环节梯度为1,不会产生链式求导。如后面的求导,绿色路径和蓝色路径是相加的关系,保留了之前的梯度。

    在这里插入图片描述

  • 相关阅读:
    装饰者模式
    Linux 编译的一些注意参数
    CodeBlocks使用静态链接库
    [BOOST] BOOST::String
    [BOOST] BOOST::Regex
    如何启动仿真环境里的网络联接?
    BlackCore
    Silverlight 中为Dialog添加委托
    c# windows应用程序或C# 控制台应用程序 添加web引用步骤
    Hyperv Server Q&A
  • 原文地址:https://www.cnblogs.com/Jack-Tim-TYJ/p/12831952.html
Copyright © 2020-2023  润新知