从零实现循环神经网络:基于d2l-pytorch项目的实践指南

从零实现循环神经网络:基于d2l-pytorch项目的实践指南

d2l-pytorch dsgiitr/d2l-pytorch: d2l-pytorch 是Deep Learning (DL) from Scratch with PyTorch系列教程的配套代码库,通过从零开始构建常见的深度学习模型,帮助用户深入理解PyTorch框架以及深度学习算法的工作原理。 d2l-pytorch 项目地址: https://gitcode.com/gh_mirrors/d2/d2l-pytorch

引言

循环神经网络(RNN)是处理序列数据的强大工具,在自然语言处理、时间序列预测等领域有广泛应用。本文将基于d2l-pytorch项目,带您从零开始实现一个字符级RNN语言模型,使用H.G. Wells的《时间机器》作为训练数据。

数据准备与预处理

首先我们需要加载并预处理文本数据。在字符级语言模型中,我们将文本视为字符序列而非单词序列。这种处理方式简化了模型结构,特别适合学习RNN的基本原理。

import d2l
import torch
import torch.nn.functional as F

# 加载《时间机器》数据集
corpus_indices, vocab = d2l.load_data_time_machine()

独热编码(One-hot Encoding)

独热编码是将离散特征表示为向量的常用方法。对于字符级模型,每个字符被映射为一个长度等于词汇表大小的向量,其中对应字符索引的位置为1,其余为0。

# 示例:字符索引0和2的独热编码
F.one_hot(torch.Tensor([0, 2]).long(), len(vocab))

输出结果将显示两个向量,每个向量的长度等于词汇表大小(44个唯一字符),在相应索引位置为1。

为了批量处理数据,我们需要将形状为(批量大小, 时间步数)的输入转换为一系列形状为(批量大小, 词汇表大小)的矩阵:

def to_onehot(X, size):
    return F.one_hot(X.long().transpose(0,-1), size)

X = torch.arange(10).reshape((2, 5))
inputs = to_onehot(X, len(vocab))

模型参数初始化

RNN的核心参数包括输入到隐藏层、隐藏层到隐藏层以及隐藏层到输出层的权重矩阵和偏置项:

num_inputs, num_hiddens, num_outputs = len(vocab), 512, len(vocab)
ctx = d2l.try_gpu()  # 尝试使用GPU

def get_params():
    def _one(shape):
        return torch.Tensor(size=shape, device=ctx).normal_(std=0.01)
    
    # 初始化参数
    W_xh = _one((num_inputs, num_hiddens))  # 输入到隐藏层
    W_hh = _one((num_hiddens, num_hiddens))  # 隐藏层到隐藏层
    b_h = torch.zeros(num_hiddens, device=ctx)  # 隐藏层偏置
    W_hq = _one((num_hiddens, num_outputs))  # 隐藏层到输出层
    b_q = torch.zeros(num_outputs, device=ctx)  # 输出层偏置
    
    params = [W_xh, W_hh, b_h, W_hq, b_q]
    for param in params:
        param.requires_grad_(True)
    return params

RNN模型实现

RNN的核心在于如何在时间步之间传递隐藏状态。我们首先定义初始化隐藏状态的函数:

def init_rnn_state(batch_size, num_hiddens, ctx):
    return (torch.zeros(size=(batch_size, num_hiddens), device=ctx), )

接下来是RNN前向传播的实现,使用tanh作为激活函数:

def rnn(inputs, state, params):
    W_xh, W_hh, b_h, W_hq, b_q = params
    H, = state
    outputs = []
    for X in inputs:
        H = torch.tanh(torch.matmul(X.float(), W_xh) + 
                      torch.matmul(H.float(), W_hh) + b_h)
        Y = torch.matmul(H.float(), W_hq) + b_q
        outputs.append(Y)
    return outputs, (H,)

预测函数

为了测试模型,我们实现一个预测函数,根据给定的前缀字符序列生成后续字符:

def predict_rnn(prefix, num_chars, rnn, params, init_rnn_state,
               num_hiddens, vocab, ctx):
    state = init_rnn_state(1, num_hiddens, ctx)
    output = [vocab[prefix[0]]]
    for t in range(num_chars + len(prefix) - 1):
        X = to_onehot(torch.Tensor([output[-1]],device=ctx), len(vocab))
        (Y, state) = rnn(X, state, params)
        if t < len(prefix) - 1:
            output.append(vocab[prefix[t + 1]])
        else:
            output.append(int(Y[0].argmax(dim=1).item()))
    return ''.join([vocab.idx_to_token[i] for i in output])

测试未经训练的模型预测结果:

predict_rnn('traveller ', 10, rnn, params, init_rnn_state, num_hiddens,
           vocab, ctx)

梯度裁剪

RNN训练中常遇到梯度爆炸问题,梯度裁剪是解决这一问题的有效技术:

def grad_clipping(params, theta, ctx):
    norm = torch.Tensor([0], device=ctx)
    for param in params:
        norm += (param.grad ** 2).sum()
    norm = norm.sqrt().item()
    if norm > theta:
        for param in params:
            param.grad.data.mul_(theta / norm)

困惑度(Perplexity)

困惑度是评估语言模型性能的重要指标,衡量模型预测下一个字符的不确定性:

困惑度 = exp(平均负对数似然)

较低的困惑度表示模型对序列预测更有信心。在实践中,我们通常同时关注训练困惑度和验证困惑度,以避免过拟合。

总结

本文从零实现了字符级RNN语言模型的关键组件,包括:

  1. 数据预处理和独热编码
  2. 模型参数初始化
  3. RNN前向传播实现
  4. 预测函数
  5. 梯度裁剪技术
  6. 困惑度评估指标

这些基础组件为后续训练和优化RNN模型奠定了基础。在实际应用中,您可能需要进一步实现训练循环、学习率调度等组件来完善模型。

d2l-pytorch dsgiitr/d2l-pytorch: d2l-pytorch 是Deep Learning (DL) from Scratch with PyTorch系列教程的配套代码库,通过从零开始构建常见的深度学习模型,帮助用户深入理解PyTorch框架以及深度学习算法的工作原理。 d2l-pytorch 项目地址: https://gitcode.com/gh_mirrors/d2/d2l-pytorch

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

黎牧联Wood

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

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

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

打赏作者

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

抵扣说明:

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

余额充值