Pytorch入门教程学习笔记(六)pytorch实现LSTM和GRU(学周杰伦写歌)

6.7 GRU-Pytorch实现

6.7.2 读取数据集

为了实现并展示门控循环单元,下面依然使用周杰伦歌词数据集来训练模型作词。这里除门控循环单元以外的实现已在6.2节(循环神经网络)中介绍过。以下为读取数据集部分。

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()

6.7.3 从零实现

6.7.3.1 初始化参数

下面的代码对模型参数进行初始化。超参数num_hiddens定义了隐藏单元的个数。

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])
will use cuda

6.7.3.2 模型定义

下面的代码定义隐藏状态初始化函数init_gru_state。同6.4节(循环神经网络的从零开始实现)中定义的init_rnn_state函数一样,它返回由一个形状为(批量大小, 隐藏单元个数)的值为0的Tensor组成的元组

然后根据门控循环单元的计算表达式定义模型

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,)

6.7.3.3 训练并创作

我们在训练模型时只使用相邻采样。设置好超参数后,我们将训练模型并根据前缀“分开”和“不分开”分别创作长度为50个字符的一段歌词。

num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']
#每过40个迭代周期便根据当前训练的模型创作一段歌词。
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.940658, time 0.27 sec
 - 分开 我想你的让我 我想你的让我 我想你的让我 我想你的让我 我想你的让我 我想你的让我 我想你的让我 
 - 不分开 我想你的让我 我想你的让我 我想你的让我 我想你的让我 我想你的让我 我想你的让我 我想你的让我 
epoch 80, perplexity 33.187388, time 0.27 sec
 - 分开 我想要这样的怒笑 一定我不多 你不再这样 我想要这样 我不要再想 我不要 爱你的美笑 不知不觉 你
 - 不分开 我想要这样 我不要再想 我不要 爱你的美笑 不知不觉 你不再再想 我不要再想 我不要 爱你的美笑 
epoch 120, perplexity 5.834922, time 0.27 sec
 - 分开 我想就这样牵着你的手不放开 爱可不可以简简单单没有伤害 你 靠着我的肩膀 你 在我胸口睡著 一壶好
 - 不分开  没有你在我不多 难散你没不堡 我该就这样牵着你的手不放开 爱可不可以简简单单没有伤害 你 靠着我
epoch 160, perplexity 1.741729, time 0.27 sec
 - 分开 我想就这宣牵 对你依依不舍 连隔壁邻居都猜到我现在的感受 河边的风 在吹着头发飘动 牵着你的手 一
 - 不分开 一个走 是你她空 停慢就真的没用 还有激忆 有一条有现在在在 痛牵有沙片片步步射进进堂的角度 能知

6.7.4 简洁实现

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.015492, time 0.07 sec
 - 分开球我爱你看棒球 想这样没担忧 唱着歌 一直走 我想就这样牵着你的手不放开 爱可不可以简简单单没有伤害
 - 不分开始打我 你拿着球不投 又不会掩护我 选你这种队友 瞎透了我 说你说 分数怎么停留 一直在停留 谁让它
epoch 80, perplexity 1.011640, time 0.07 sec
 - 分开 它在空中停留 所有人看着我 抛物线进球 单手过人运球 篮下妙传出手 漂亮的假动作 帅呆了我 全场盯
 - 不分开始移动 回到当初爱你的时空 停格内容不忠 所有回忆对着我进攻 我的伤口被你拆封 誓言太沉重泪被纵容 
epoch 120, perplexity 1.017915, time 0.07 sec
 - 分开 在回忆 的路上 时间变好慢 老街坊 小弄堂 是属于那年代白墙黑瓦的淡淡的忧伤 消失的 旧时光 一九
 - 不分开暴力因素一定都会有原因 但是呢 妈跟我都没有错亏我叫你一声爸  爸我回来了 不要再这样打我妈妈 我说
epoch 160, perplexity 1.009228, time 0.07 sec
 - 分开的脑袋有问题 随便说说 其实我早已经猜透看透不想多说 只是我怕眼泪撑不住 不懂 你的黑色幽默 想通 
 - 不分开始打我妈妈 我说的话你甘会听 不要再这样打我妈妈 难道你手不会痛吗 不要再这样打我妈妈 难道你手不会

6.8 长短期记忆LSTM

6.8.3 从零开始

6.8.3.1 初始化参数

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])
will use cuda

6.8.4 定义模型

在初始化函数中,LSTM的隐藏状态需要返回额外的形状为(批量大小, 隐藏单元个数)的值为0的记忆细胞。

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))

下面根据LSTM的计算表达式定义模型。需要注意的是,只有隐藏状态会传递到输出层,而记忆细胞不参与输出层的计算。

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)

6.8.4.1 训练并创作歌词

同上一节一样,我们在训练模型时只使用相邻采样。设置好超参数后,我们将训练模型并根据前缀“分开”和“不分开”分别创作长度为50个字符的一段歌词。

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 210.540953, time 0.32 sec
 - 分开 我不不的我 我不不的我 我不不的我 我不不的我 我不不的我 我不不的我 我不不的我 我不不的我 我
 - 不分开 我不不的我 我不不的我 我不不的我 我不不的我 我不不的我 我不不的我 我不不的我 我不不的我 我
epoch 80, perplexity 69.000204, time 0.32 sec
 - 分开 我想你这你我 我想要这我 我不要这我 我不要这我 我不要这我 我不要这我 我不要这我 我不要这我 
 - 不分开 我想你这你我 不要 我不我 我不要这我 我不要这我 我不要这我 我不要这我 我不要这我 我不要这我
epoch 120, perplexity 14.788829, time 0.33 sec
 - 分开 你是了我不多 让你去人不多 一直悄我 你已了双截奏 我知好觉 我跟了好个奏 后知后觉 快使用双截棍
 - 不分开 我说你的生笑 我爱你 你爱我 我想就这样牵你的手不放放 爱可不可我简简的可不女人 坏坏的让我疯狂的
epoch 160, perplexity 3.868930, time 0.32 sec
 - 分开 一直是人不多 景色哈兮 漫天黄空凉过 塞色的客栈人多 牧草有没有 我马儿有些瘦 天涯尽头 满脸风双
 - 不分开 我已经你在我有听的熬不放  不知道烦已圈来不及逃 我不能再想 我不能再想 我不 我不 我不能 爱情

6.8.5 简洁实现

我们可以直接调用rnn模块中的LSTM类。

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.022456, time 0.09 sec
 - 分开始乡相信命运 感谢地心引力 让我碰到你 漂亮的让我面红的可爱女人 温柔的让我心疼的可爱女人 透明的让
 - 不分开的感动 我给你的爱写在西元前 深埋在美索不达米亚平原 几十个世纪后出土发现 泥板上的字迹依然清晰可见
epoch 80, perplexity 1.020850, time 0.09 sec
 - 分开始乡相信命运 感谢地心引力 让我碰到你 漂亮的让我面红的可爱女人 温柔的让我心疼的可爱女人 透明的让
 - 不分开的感动 我给你的爱写在西元前 深埋在美索不达米亚平原 用楔形文字刻下了永远 那已风化千年的誓言 一切
epoch 120, perplexity 1.020341, time 0.09 sec
 - 分开始打呼 管家是一只会说法语举止优雅的猪 吸血前会念约翰福音做为弥补 拥有一双蓝色眼睛的凯萨琳公主 专
 - 不分开的话像语言 游荡在蓝天边 一片云掉落在我面前 捏成你的形状 随风跟著我 一口一口吃掉忧愁 载著你 彷
epoch 160, perplexity 1.011106, time 0.09 sec
 - 分开始乡相信命运 感谢地心引力 让我碰到你 漂亮的让我面红的可爱女人 温柔的让我心疼的可爱女人 透明的让
 - 不分开在感动 穿梭时间的画面的钟 从反方向开始移动 回到当初爱你的时空 停格内容不忠 所有回忆对着我进攻 

说明

说明:本博客是对如何使用pytorch用于深度学习 学习过程的记录和总结。
学习教程为:《动手学深度学习》和https://tangshusen.me/Dive-into-DL-PyTorch/#/
这里推荐这个网址,将动手学深度学习改为了Pytorch实现,很有意义!
代码是借鉴了学习教程并从自己写的Jupyter中导出的,复制进Jupyter可以运行

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值