解决pytorch rnn 变长输入序列的问题

pytorch实现变长输入的rnn分类

输入数据是长度不固定的序列数据,主要讲解两个部分

1、Data.DataLoader的collate_fn用法,以及按batch进行padding数据

2、pack_padded_sequence和pad_packed_sequence来处理变长序列

collate_fn

Dataloader的collate_fn参数,定义数据处理和合并成batch的方式。

由于pack_padded_sequence用到的tensor必须按照长度从大到小排过序的,所以在Collate_fn中,需要完成两件事,一是把当前batch的样本按照当前batch最大长度进行padding,二是将padding后的数据从大到小进行排序。

def pad_tensor(vec, pad):
    """
    args:
        vec - tensor to pad
        pad - the size to pad to
    return:
        a new tensor padded to 'pad'
    """
    return torch.cat([vec, torch.zeros(pad - len(vec), dtype=torch.float)], dim=0).data.numpy()
class Collate:
    """
    a variant of callate_fn that pads according to the longest sequence in
    a batch of sequences
    """
    def __init__(self):
        pass
    def _collate(self, batch):
        """
        args:
            batch - list of (tensor, label)
        reutrn:
            xs - a tensor of all examples in 'batch' before padding like:
                '''
                [tensor([1,2,3,4]),
                 tensor([1,2]),
                 tensor([1,2,3,4,5])]
                '''
            ys - a LongTensor of all labels in batch like:
                '''
                [1,0,1]
                '''
        """
        xs = [torch.FloatTensor(v[0]) for v in batch]
        ys = torch.LongTensor([v[1] for v in batch])
        # 获得每个样本的序列长度
        seq_lengths = torch.LongTensor([v for v in map(len, xs)])
        max_len = max([len(v) for v in xs])
        # 每个样本都padding到当前batch的最大长度
        xs = torch.FloatTensor([pad_tensor(v, max_len) for v in xs])
        # 把xs和ys按照序列长度从大到小排序
        seq_lengths, perm_idx = seq_lengths.sort(0, descending=True)
        xs = xs[perm_idx]
        ys = ys[perm_idx]
        return xs, seq_lengths, ys
    def __call__(self, batch):
        return self._collate(batch)

定义完collate类以后,在DataLoader中直接使用

train_data = Data.DataLoader(dataset=train_dataset, batch_size=32, num_workers=0, collate_fn=Collate())

torch.nn.utils.rnn.pack_padded_sequence()

pack_padded_sequence将一个填充过的变长序列压紧。输入参数包括

input(Variable)- 被填充过后的变长序列组成的batch data

lengths (list[int]) - 变长序列的原始序列长度

batch_first (bool,optional) - 如果是True,input的形状应该是(batch_size,seq_len,input_size)

返回值:一个PackedSequence对象,可以直接作为rnn,lstm,gru的传入数据。

用法:

from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence
# x是填充过后的batch数据,seq_lengths是每个样本的序列长度
packed_input = pack_padded_sequence(x, seq_lengths, batch_first=True)

RNN模型

定义了一个单向的LSTM模型,因为处理的是变长序列,forward函数传入的值是一个PackedSequence对象,返回值也是一个PackedSequence对象

class Model(nn.Module):
    def __init__(self, in_size, hid_size, n_layer, drop=0.1, bi=False):
        super(Model, self).__init__()
        self.lstm = nn.LSTM(input_size=in_size,
                            hidden_size=hid_size,
                            num_layers=n_layer,
                            batch_first=True,
                            dropout=drop,
                            bidirectional=bi)
        # 分类类别数目为2
        self.fc = nn.Linear(in_features=hid_size, out_features=2)
    def forward(self, x):
        '''
        :param x: 变长序列时,x是一个PackedSequence对象
        :return: PackedSequence对象
        '''
        # lstm_out: tensor of shape (batch, seq_len, num_directions * hidden_size)
        lstm_out, _ = self.lstm(x)  

        return lstm_out
model = Model()
lstm_out = model(packed_input)

torch.nn.utils.rnn.pad_packed_sequence()

这个操作和pack_padded_sequence()是相反的,把压紧的序列再填充回来。因为前面提到的LSTM模型传入和返回的都是PackedSequence对象,所以我们如果想要把返回的PackedSequence对象转换回Tensor,就需要用到pad_packed_sequence函数。

参数说明:

sequence (PackedSequence) – 将要被填充的 batch

batch_first (bool, optional) – 如果为True,返回的数据的形状为(batch_size,seq_len,input_size)

返回值: 一个tuple,包含被填充后的序列,和batch中序列的长度列表。

用法:

# 此处lstm_out是一个PackedSequence对象
output, _ = pad_packed_sequence(lstm_out)

返回的output是一个形状为(batch_size,seq_len,input_size)的tensor。

总结

1、pytorch在自定义dataset时,可以在DataLoader的collate_fn参数中定义对数据的变换,操作以及合成batch的方式。

2、处理变长rnn问题时,通过pack_padded_sequence()将填充的batch数据转换成PackedSequence对象,直接传入rnn模型中。通过pad_packed_sequence()来将rnn模型输出的PackedSequence对象转换回相应的Tensor。

补充: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)。这种样本形式在问答系统、推荐系统多见。

通用解决方案

定义ImprovedRnn类。与nn.RNN,nn.LSTM,nn.GRU相比,除了此两点【①forward函数多一个参数lengths表示每个seq的长度】【②初始化函数(__init__)第一个参数module必须指定三者之一】外,使用方法完全相同。

import torch
from torch import nn
class ImprovedRnn(nn.Module):
    def __init__(self, module, *args, **kwargs):
        assert module in (nn.RNN, nn.LSTM, nn.GRU)
        super().__init__()
        self.module = module(*args, **kwargs)
    def forward(self, input, lengths):  # input shape(batch_size, seq_len, input_size)
        if not hasattr(self, '_flattened'):
            self.module.flatten_parameters()
            setattr(self, '_flattened', True)
        max_len = input.shape[1]
        # enforce_sorted=False则自动按lengths排序,并且返回值package.unsorted_indices可用于恢复原顺序
        package = nn.utils.rnn.pack_padded_sequence(input, lengths.cpu(), batch_first=self.module.batch_first, enforce_sorted=False)
        result, hidden = self.module(package)
        # total_length参数一般不需要,因为lengths列表中一般含最大值。但分布式训练时是将一个batch切分了,故一定要有!
        result, lens = nn.utils.rnn.pad_packed_sequence(result, batch_first=self.module.batch_first, total_length=max_len)
        return result[package.unsorted_indices], hidden  # output shape(batch_size, seq_len, rnn_hidden_size)

使用示例:

class TestNet(nn.Module):
    def __init__(self, word_emb, gru_in, gru_out):
        super().__init__()
        self.encode = nn.Embedding.from_pretrained(torch.Tensor(word_emb))
        self.rnn = ImprovedRnn(nn.RNN, input_size=gru_in, hidden_size=gru_out,
		        				batch_first=True, bidirectional=True)
    def forward(self, seq1, seq1_lengths, seq2, seq2_lengths):
        seq1_emb = self.encode(seq1)
        seq2_emb = self.encode(seq2)
        rnn1, hn = self.rnn(seq1_emb, seq1_lengths)
        rnn2, hn = self.rnn(seq2_emb, seq2_lengths)
        """
        此处略去rnn1和rnn2的后续计算,当前网络最后计算结果记为prediction
        """
        return prediction

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 基于pytorch的lstm参数使用详解

    lstm(*input, **kwargs) 将多层长短时记忆(LSTM)神经网络应用于输入序列. 参数: input_size:输入'x'中预期特性的数量 hidden_size:隐藏状态'h'中的特性数量 num_layers:循环层的数量.例如,设置' ' num_layers=2 ' '意味着将两个LSTM堆叠在一起,形成一个'堆叠的LSTM ',第二个LSTM接收第一个LSTM的输出并计算最终结果.默认值:1 bias:如果' False',则该层不使用偏置权重' b_ih '和' b

  • Pytorch实现基于CharRNN的文本分类与生成示例

    1 简介 本篇主要介绍使用pytorch实现基于CharRNN来进行文本分类与内容生成所需要的相关知识,并最终给出完整的实现代码. 2 相关API的说明 pytorch框架中每种网络模型都有构造函数,在构造函数中定义模型的静态参数,这些参数将对模型所包含weights参数的维度进行设置.在运行时,模型的实例将接收动态的tensor数据并调用forword,在得到模型输出之后便可以和真实的标签数据进行误差计算,并通过优化器进行反向传播以调整模型的参数.下面重点介绍NLP常用到的模型和相关方法. 2

  • pytorch-RNN进行回归曲线预测方式

    任务 通过输入的sin曲线与预测出对应的cos曲线 #初始加载包 和定义参数 import torch from torch import nn import numpy as np import matplotlib.pyplot as plt torch.manual_seed(1) #为了可复现 #超参数设定 TIME_SETP=10 INPUT_SIZE=1 LR=0.02 DOWNLoad_MNIST=True 定义RNN网络结构 from torch.autograd import

  • Pytorch DataLoader 变长数据处理方式

    关于Pytorch中怎么自定义Dataset数据集类.怎样使用DataLoader迭代加载数据,这篇官方文档已经说得很清楚了,这里就不在赘述. 现在的问题:有的时候,特别对于NLP任务来说,输入的数据可能不是定长的,比如多个句子的长度一般不会一致,这时候使用DataLoader加载数据时,不定长的句子会被胡乱切分,这肯定是不行的. 解决方法是重写DataLoader的collate_fn,具体方法如下: # 假如每一个样本为: sample = { # 一个句子中各个词的id 'token_li

  • 解决pytorch rnn 变长输入序列的问题

    pytorch实现变长输入的rnn分类 输入数据是长度不固定的序列数据,主要讲解两个部分 1.Data.DataLoader的collate_fn用法,以及按batch进行padding数据 2.pack_padded_sequence和pad_packed_sequence来处理变长序列 collate_fn Dataloader的collate_fn参数,定义数据处理和合并成batch的方式. 由于pack_padded_sequence用到的tensor必须按照长度从大到小排过序的,所以在

  • 对pytorch中不定长序列补齐的操作

    第二种方法通常是在load一个batch数据时, 在collate_fn中进行补齐的. 以下给出两种思路: 第一种思路是比较容易想到的, 就是对一个batch的样本进行遍历, 然后使用np.pad对每一个样本进行补齐. for unit in data: mask = np.zeros(max_length) s_len = len(unit[0]) # calculate the length of sequence in each unit mask[: s_len] = 1 unit[0]

  • keras在构建LSTM模型时对变长序列的处理操作

    我就废话不多说了,大家还是直接看代码吧~ print(np.shape(X))#(1920, 45, 20) X=sequence.pad_sequences(X, maxlen=100, padding='post') print(np.shape(X))#(1920, 100, 20) model = Sequential() model.add(Masking(mask_value=0,input_shape=(100,20))) model.add(LSTM(128,dropout_W=

  • 变长双向rnn的正确使用姿势教学

    如何使用双向RNN 在<深度学习之TensorFlow入门.原理与进阶实战>一书的9.4.2中的第4小节中,介绍过变长动态RNN的实现. 这里在来延伸的讲解一下双向动态rnn在处理变长序列时的应用.其实双向RNN的使用中,有一个隐含的注意事项,非常容易犯错. 本文就在介绍下双向RNN的常用函数.用法及注意事项. 动态双向rnn有两个函数: stack_bidirectional_dynamic_rnn bidirectional_dynamic_rnn 二者的实现上大同小异,放置的位置也不一样

  • tensorflow 变长序列存储实例

    问题 问题是这样的,要把一个数组存到tfrecord中,然后读取 a = np.array([[0, 54, 91, 153, 177,1], [0, 50, 89, 147, 196], [0, 38, 79, 157], [0, 49, 89, 147, 177], [0, 32, 73, 145]]) 图片我都存储了,这个不还是小意思,一顿操作 import tensorflow as tf import numpy as np def _int64_feature(value): if

  • 使用tensorflow DataSet实现高效加载变长文本输入

    DataSet是tensorflow 1.3版本推出的一个high-level的api,在1.3版本还只是处于测试阶段,1.4版本已经正式推出. 在网上搜了一遍,发现关于使用DataSet加载文本的资料比较少,官方举的例子只是csv格式的,要求csv文件中所有样本必须具有相同的维度,也就是padding必须在写入csv文件之前做掉,这会增加文件的大小. 经过一番折腾试验,这里给出一个DataSet+TFRecords加载变长样本的范例. 首先先把变长的数据写入到TFRecords文件: def

  • 解决IDEA中不能正常输入光标变粗的问题

    遇到的问题 解决方法 win7: insert键切回 win10: fn+insert切回 Mac: fn+i 切回 到此这篇关于解决IDEA中不能正常输入光标变粗的问题的文章就介绍到这了,更多相关IDEA光标变粗内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

  • Python技巧之变长和定长序列拆分

    目录 1.元组拆分 2.字符串拆分 3.拆分时丢弃值 4.嵌套序列拆分 5.从任意长度的可迭代对象中拆分 Python中的任何序列(可迭代的对象)都可以通过赋值操作进行拆分,包括但不限于元组.列表.字符串.文件.迭代器.生成器等. 1.元组拆分 元组拆分是最为常见的一种拆分,示例如下: p = (4, 5) x, y = p print(x, y) # 4 5 如果写成 x, y, z = p 那么就会抛出ValueError异常: "not enough values to unpack (e

  • C++动态规划之最长公子序列实例

    本文实例讲述了C++动态规划之最长公子序列解决方法.分享给大家供大家参考.具体分析如下: 问题描述: 求出两个字符串中的最长公子序列的长度. 输入: csblog belong 输出: max length = 4 实现代码: #include <stdio.h> #include <string.h> int arr[200][200]; /* 表示str1的前i位和str2的前j位的最长公子序列的长度 */ int main() { char str1[100],str2[10

随机推荐