用pytorch做rnn的时候,如果输入数据不一样长,可以用两种方式解决。
一种是自定义collate方法,
#自定义collate_fn
dataloader.DataLoader(dataset,4,True,collate_fn=my_collate)
然后里面写数据和标签载入方式即可
def my_collate(batch):
data = [item[0] for item in batch]
target = [item[1] for item in batch]
return [data, target]
压缩:nn.utils.rnn.pack_padded_sequence
解压是 这个pad_packed_sequence
核心思想是对padding补充过的数据进行压缩,这种方式可以加速运算在大数据集中效果较好
(原理是把数据中为0的padding给压缩掉,进入RNN计算的时候直接跳过输出0)
当你拿到长短不一致的数据的时候
第一步自然是PADDING,无论什么框架都一样,自己手写一个就行了
def custompad(X,max_len=10):
X = torch.Tensor(X)
m=X.shape[0]
pad = torch.zeros(1,50)
for i in range(max_len-m):
X = torch.cat([X,pad],dim=0)
return X
这样比如我的数据是[a,b,c][d,e] 输出来就是[a,b,c,0…][d,e,0…]
这样保持固定步长序列的数据
这时候可以直接丢rnn跑了
但是如果我的神经网络要求的是输入最后一层的数据,怎么办?
直接进行压缩!
from torch.nn.utils.rnn import pack_padded_sequence as pack, pad_packed_sequence as unpack
## 这里我把压缩写进了collate方法里,这样dataloader取的时候就直接拿压缩数据了
def my_collate(batch):
data = torch.stack([item[0] for item in batch])
lens = [item[1] for item in batch]
## 重点,压缩! 如果不排序enforce要false
res = pack(data,lens,batch_first=True,enforce_sorted=False)
target = [item[2] for item in batch]
target = torch.LongTensor(target)
return [res, target]
#我的dataset 可以作为参考
class traindata(Dataset):
def __init__(self) -> None:
super().__init__()
X, self.Y = read_csv('data/train_emoji.csv')
self.X,self.lens=sentence_to_avg(X,word_to_vec_map)
def __getitem__(self, index):
return self.X[index],self.lens[index],self.Y[index]
def __len__(self):
return self.X.shape[0]
这样写完 我每次拿数据就是取的压缩数据
注意压缩数据是2维的 第二维是lenth(重要**) 因为这个length可以帮我们取最后一层计算数据
举个例子:
我的输入是[a,b],padding完是[a,b,0,0]
压缩后进入lstm,再解压出来
这时候的输出可能是[1,2,0,0]
也就是说,padding为0的地方是没有进入rnn循环的,也就是只循环了2次
所以这里需要拿到最后一个输出的数据2
就要用到上面那个方法了
直接看代码比较快:
class Net(nn.Module):
def __init__(self):
super().__init__()
self.lstm = nn.LSTM(50,64,1)
self.dense = nn.Linear(64,256)
self.fc = nn.Linear(256,5)
def forward(self,x,h=None,c=None):
hp=(h,c) if h!=None and c!=None else None
x,hid = self.lstm(x,hp)
x = unpack(x,batch_first=True)
## 注意看这里 进入全连接层之前 我要取得压缩数据,并且是最后一维的输出
##所以要过滤掉0的输出
seq_len_indices = [length - 1 for length in x[1]]
batch_indices = [i for i in range(x[0].shape[0])]
final_output = x[0][batch_indices, seq_len_indices, :]
x = F.relu(self.dense(final_output))
return self.fc(x)