神经网络(LSTM)中的变长序列处理及多GPU训练

个人写lstm为核心的算法时遇到的一些小问题,主要是变长序列的处理,多GPU训练的使用,以及在使用多GPU训练时同时处理变长序列时遇到数据不能正常传入模型等问题,总结如下。

1、变长序列处理

变长序列构成的数据集如果想以batch size>1的形式读入算法模型,需要统一长度。

pytorch架构下,使用:

“from torch.nn.utils.rnn import pad_sequence, pack_padded_sequence, pack_sequence, pad_packed_sequence”导入对应函数,各函数简要说明如下,详细的还是去看官方的说明文档和示例程序,更便于理解:

(1)、pad_sequence

对变长序列进行填充,填充后得到shape为{batch_size, length, dimension}的tensor。其中batch_size是自己设置的batch大小,length是该batch中各向量的最大长度,dimension是该batch中各向量的维数(需要一致)。

(2)、pack_padded_sequence

输入为经过“pad_sequence”或者已经手动填充补齐长度的向量组成的tensor,输出数据格式为“padedsequence”,内部由已根据时间点及batch排序的向量和对应的输入参数构成,这个不太容易说清,新手还是在python里自己试一下会有更好的理解。

(3)、pad_packed_sequence

输入“padedsequence”,输出为tuple,由填充好的序列组,shape为{batch_size, length, dimension},及该batch中各序列的真实长度数值构成的一维tensor。相当与把封装好的“padedsequence”还原成填充后的tensor。

(4)、pack_sequence

相当于pad_sequence()+pack_padded_sequence()

输入长度不等的多个序列,输出封装好的序列数据。

了解完各个函数,现在说明一下这几个函数具体的用法:

pytorch 的dataloder里有一个可以重写的函数叫做collate_fn, 在dataset.py(就是数据集文件)里重写此函数。(此处写法并非唯一,还有更简洁的写法,该函数的用法也不仅止于此,只要数据形式合理理论上它可以完成所有你设想的数据读取方式)

import torch
from torch.utils.data import dataset
from torch.nn.utils.rnn import pad_sequence, pack_padded_sequence, pack_sequence, pad_packed_sequence
....


class Dataset(dataset.Dataset):

    def __init__(self, configs):
        ...

    def __len__(self):
        ...

    def __getitem__(self, item):
        ...

def collate_fn(data):
    # data: 通过__getitem__()函数读到的数据,如果batch size设置为k,则此处读到的“data”即包含k个数据样本
    # print(len(data))
    datas = []  # 准备一个空的列表
    
    for each in data:
        datas.append(each[0])
    
    datas.sort(key=lambda x: len(x), reverse=True)  # 从长到短排序
    seq_len = [s.size(0) for s in ndi_data] # real length of data list
    datas = pad_sequence(datas, batch_first=True)  # tensor:{batch-size,length,dim}
 
    datas = pack_padded_sequence(datas, seq_len, batch_first=True)
    
    return datas   # packedsepuence:{tensor,length,...,...}

重写完collate_fn()函数后,在DataLoader()函数中加上collate_fn=collate_fn即可实现变长序列封装成mini-batch进行训练。数据以序列的形式经过LSTM等循环神经网络后需要使用pad_packed_sequence函数将序列还原为tensor!!(切记!!)

2、使用多个GPU 进行训练

有时候模型太大或者参数量太大需要使用多个GPU并行训练,语句很简单,就在main函数里加上

(1)os.environ["CUDA_VISIBLE_DEVICES"] = "0,1"

(2)device_id = [0,1]

(3)model = nn.DataParallel(model, device_ids=device_id)等几句话就好,(1)(2)可以放在训练入口处(就是if__name__ == '__main__':那里),(3)可以放在(1)(2)之后,如果使用的是多折交叉验证,(3)也可以写到多折交叉的函数里,要放在“...=train()之前”。

       不过同时使用变长序列填充和多GPU训练时,会出一点小小的bug,因为从流程上来说,数据是先由dataloader函数读入训练函数中,在传进模型的,如果在dataloader读出数据之时,数据已经被封装成序列,在传入模型的时候序列数据“padedsequence”会被直接改成tuple类型数据,并且“padedsequence”中记录每个时间点该输入几个数据点的列表不能被分割而是复制后传入两个GPU,导致模型不能正常运行(lstm之类的模型可以接受tensor或者“padedsequence”输入,不能接受tuple,而且每个时间点该输入几个数据点的列表不能合理传入会导致状态矩阵不能正常初始化,其他模型我没试,可能还会有别的类型的bug。)

      出现上述问题时,需要在collate_fn()函数中使输出为排好序的样本序列。在train函数中读到数据后将读到的数据列表,排序,统计长度信息列表(此列表必须转换成一维tensor!!),填充成长度一致的序列拼合的tensor(即完成pad_sequence),在模型的forward()函数中将传入的tensor封装成序列(padedsequence)再进行接下来的运算。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LSTM神经网络是一种特殊的循环神经网络,它在序列数据处理方面表现出了许多优越性。相比于传统的RNN模型,LSTM在解决梯度消失和梯度爆炸问题上有很好的表现,并且在长序列处理上也能保持长期的记忆能力。此外,LSTM对于输入数据的缩放和平移也是具有较好的鲁棒性的。 LSTM神经网络的优越性主要有以下几个方面: 1. 长期记忆能力:LSTM网络的记忆单元能够长期地存储信息,避免了传统RNN模型梯度消失/爆炸的问题。因此,LSTM处理序列数据时表现出了很好的性能。 2. 输入输出鲁棒性:LSTM对于输入数据的缩放和平移具有一定的鲁棒性,这使得它能够在不同的任务表现出比传统神经网络更好的稳定性和可靠性。 3. 可并行计算:LSTM神经网络的每个时刻的计算是相互独立的,因此可以通过GPU等硬件实现高效的并行计算,大大提高其训练和测试的效率。 下面是一个使用LSTM神经网络进行文本情感分类的Python代码范例[^1]: ```python import tensorflow as tf from tensorflow.keras.datasets import imdb from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Embedding, LSTM, SpatialDropout1D # 加载IMDB数据集 (x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=5000) # 对序列进行填充 max_words = 500 x_train = tf.keras.preprocessing.sequence.pad_sequences(x_train, maxlen=max_words) x_test = tf.keras.preprocessing.sequence.pad_sequences(x_test, maxlen=max_words) # 构建LSTM模型 model = Sequential() model.add(Embedding(input_dim=5000, output_dim=128, input_length=max_words)) model.add(SpatialDropout1D(0.4)) model.add(LSTM(units=196, dropout=0.2, recurrent_dropout=0.2)) model.add(Dense(units=1, activation='sigmoid')) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) # 训练模型 model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=10, batch_size=64) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值