pytorch处理不定长序列;实现不定长输入的RNN / LSTM / GRU

情景描述

As we all know,RNN循环神经网络(及其改进模型LSTM、GRU)可以处理序列的顺序信息,如人类自然语言。但是在实际场景中,我们常常向模型输入一个批次(batch)的数据,这个批次中的每个序列往往不是等长的。

pytorch提供的模型(nn.RNN,nn.LSTM,nn.GRU)是支持可变长序列的处理的,但条件是传入的数据必须按序列长度排序。本文针对以下两种场景提出解决方法。

  1. 每个样本只有一个序列:(seq,label),其中seq是一个长度不定的序列。则使用pytorch训练时,我们将按列把一个批次的数据输入网络,seq这一列的形状就是(batch_size, seq_len),经过编码层(如word2vec)之后的形状是(batch_size, seq_len, emb_size)
  2. 情况1的拓展:每个样本有两个(或多个)序列,如(seq1, seq2, label)。这种样本形式在问答系统、推荐系统多见。

通用解决方案

定义CustomRNN类,继承自nn.RNN,nn.LSTM,nn.GRU三者之一。其实就是封装了一下,使用方法与nn.RNN,nn.LSTM,nn.GRU完全相同。

import torch
from torch import nn


class CustomRNN(nn.LSTM):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def forward(self, input, lengths):
        """
        以batch_first=True为例,
        input shape(batch_size, seq_length, input_size)
        lengths shape(batch_size),每个句子的长度,无序

        1. 使用pack_padded_sequence进行打包
          输入:
            lengths只支持放在cpu上运行,不支持cuda
            enforce_sorted默认为True,表示传入的lengths已按从大到小排序
            由于我们传入的初始数据input没有按长度排序,所以设enforce_sorted=False
          输出:
            包

        2. 将包喂入RNN网络
          输入:
            包
          输出:
            result(包)和hn;
            result依然是个打包状态的包
            hn是最后一个单词喂入RNN网络是的输出
            当使用nn.RNN或nn.GRU时,hn是Tensor;使用nn.LSTM时,hn=(h_n, c_n)是一个元组
            注意:无论batch_first为何值,hn形状总是(num_directions * num_layers, batch_size, hidden_size)
            注意:输出hn的batch维度已经恢复为原始输入input的句子顺序

        3.使用pad_packed_sequence对上一步的result解包(即填充0)
          输入:
            total_length参数一般不需要,因为lengths列表中一般含最大值。但分布式训练时是将一个batch切分了,故一定要有!
          输出:
            output和lens
            lens和输入的lengths相等。
            注意:output的形状为(batch_size, seq_length, num_directions * hidden_size)
            注意:output的batch维度已经恢复为元时输入input的句子顺序
        """
        package = nn.utils.rnn.pack_padded_sequence(input, lengths.cpu(), batch_first=self.batch_first, enforce_sorted=False)
        result, hn = super().forward(package)
        output, lens = nn.utils.rnn.pad_packed_sequence(result, batch_first=self.batch_first, total_length=input.shape[self.batch_first])
        return output, hn


# 测试示例
if __name__ == '__main__':
    # 词向量矩阵:10个单词,每个词向量5维
    E = nn.Embedding(10, 5, _weight=torch.arange(50).float().view(10, 5)).cuda()
    # 定义句子
    seqs = torch.LongTensor([
        [8, 9, 0, 0, 0, 0, 0],
        [1, 2, 3, 4, 5, 6, 7],
        [5, 6, 7, 8, 0, 0, 0],
    ]).cuda()
    lens = torch.Tensor([2, 7, 4])  # 每个句子的真实长度
    # 定义网络
    lstm = CustomRNN(input_size=5, hidden_size=8, batch_first=True, num_layers=3, bidirectional=True).cuda()  # 改进的LSTM模型

    x = E(seqs)  # shape(3,7,5)
    out, hn = lstm(x, lens)

    print('RNN output shape:', out.shape)  # out shape(3,7,16)  # 双向LSTM
    if isinstance(hn, tuple):
        print('hn shape', hn[0].shape)
        print('cn shape', hn[1].shape)
    else:
        print('hn shape', hn.shape)

参考资料

  • 8
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
短期记忆网络(LSTM)和门控循环单元网络(GRU)是循环神经网络(RNN)的两个变种,用于处理时序数据。在PyTorch中,可以使用torch.nn模块来实现LSTMGRU。 下面是LSTMGRUPyTorch实现示例: 1. LSTMPyTorch实现: ```python import torch import torch.nn as nn # 定义LSTM模型 class LSTMModel(nn.Module): def __init__(self, input_size, hidden_size, num_layers, output_size): super(LSTMModel, self).__init__() self.hidden_size = hidden_size self.num_layers = num_layers self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True) self.fc = nn.Linear(hidden_size, output_size) def forward(self, x): h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device) c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device) out, _ = self.lstm(x, (h0, c0)) out = self.fc(out[:, -1, :]) return out # 创建LSTM模型实例 input_size = 10 hidden_size = 20 num_layers = 2 output_size = 1 lstm_model = LSTMModel(input_size, hidden_size, num_layers, output_size) ``` 2. GRUPyTorch实现: ```python import torch import torch.nn as nn # 定义GRU模型 class GRUModel(nn.Module): def __init__(self, input_size, hidden_size, num_layers, output_size): super(GRUModel, self).__init__() self.hidden_size = hidden_size self.num_layers = num_layers self.gru = nn.GRU(input_size, hidden_size, num_layers, batch_first=True) self.fc = nn.Linear(hidden_size, output_size) def forward(self, x): h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device) out, _ = self.gru(x, h0) out = self.fc(out[:, -1, :]) return out # 创建GRU模型实例 input_size = 10 hidden_size = 20 num_layers = 2 output_size = 1 gru_model = GRUModel(input_size, hidden_size, num_layers, output_size) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

雪的期许

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

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

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

打赏作者

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

抵扣说明:

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

余额充值