首先 pad_sequence 是用来对对tensor做padding 的,先看官方示例:
文档地址https://pytorch.org/docs/stable/generated/torch.nn.utils.rnn.pad_sequence.html?highlight=pad_sequence#torch.nn.utils.rnn.pad_sequence
from torch.nn.utils.rnn import pad_sequence
a = torch.ones(25, 300)
b = torch.ones(22, 300)
c = torch.ones(15, 300)
pad_sequence([a, b, c]).size()
也就是说我们有了几个矩阵,除了第一个维度不一样,其他维度是一样的
常见的就是很多个sequence( length * embedding_dimension )
此时我们希望用做神经网络的input,但是input都是定长的,所以我们需要统一处理成定长的,以保证input layer
此时,我们需要将它们处理成各个维度都一样
问题描述:
a = [[1,2,3,4,5],[1,1,1,1],[2,2,2]]
pad_sequence(a, batch_first = True, padding_value = 0 )
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_79801/829534548.py in <module>
1 a = [[1,2,3,4,5],[1,1,1,1],[2,2,2]]
----> 2 pad_sequence(a, batch_first = True, padding_value = 0 )
~/anaconda3/envs/torch/lib/python3.8/site-packages/torch/nn/utils/rnn.py in pad_sequence(sequences, batch_first, padding_value)
361 # assuming trailing dimensions and type of all the Tensors
362 # in sequences are same and fetching those from sequences[0]
--> 363 return torch._C._nn.pad_sequence(sequences, batch_first, padding_value)
364
365
TypeError: expected Tensor as element 0 in argument 0, but got list
原因:我的矩阵是由数字组成的矩阵,也就是基本单元都是数字,但是,再高一层次(既是+2 也是-2 个维度)的数据类型是list,而不是数组。
解决:使用 torch.tensor() 对第二层的数据类型做一个类型转换,不适用list
a = [torch.tensor([1,2,3,4,5]),torch.tensor([1,1,1,1]),torch.tensor([2,2,2])]
pad_sequence(a, batch_first = True, padding_value = 0 )
tensor([[1, 2, 3, 4, 5],
[1, 1, 1, 1, 0],
[2, 2, 2, 0, 0]])
使用pad_sequence 时,如果是对一个完整的数据集进行pad,那么操作很简单,但是计算的代价会很大。
比如,100 个sequence 组成的list,当其中只有一个是长度为1000 的,其余都是100,那么多pad的部分就高达99 X 900,
而总的有效部分才 99 X 100 + 1000 ,pad 部分比有效部分还多。
所以,应当基于一个batch 去做pad_sequence
dataloader
发现,训练使用的dataloader构建时,传入的是class Dataset 的实例,目标是从实例中sample出来一个个小的batch,这些batchs 被dataloader组成了一个list。
注意,list组成单元类型要一样,但是组成单元的基本单元并不管,因为dataloader只负责给你返回batchs
但是sample出来一个个小的batch 过程也是一条一条数据的从dataset 中获取的。
class Dataset 作用是什么呢?
pytorch 的class Dataset 有torch.utils.data.Dataset, torch.utils.data.IterableDataset 等,区别只是取出来一条数据的方式不同
由于我使用的是torch.utils.data.Dataset,所以以此为例。
Dataset 数据集嘛,所以其自身的变量里面是有一个大的数据单位。(强调一下,你自己怎么组织无所谓,字典也好,列表也好,数组也好,只要给你一下index 索引,你能够返回一条数据就行)
看一下官方解释
CLASStorch.utils.data.Dataset(*args, **kwds)[SOURCE]
An abstract class representing a Dataset.
All datasets that represent a map from keys to data samples should subclass it. All subclasses should overwrite getitem(), supporting fetching a data sample for a given key. Subclasses could also optionally overwrite len(), which is expected to return the size of the dataset by many Sampler implementations and the default options of DataLoader.
就是你只要是通过继承torch.utils.data.Dataset构建自己的dataset,那么你一定要实现__getitem__()方法,而这个方法就是输入一个下标,返回一条数据。
首先看一下说明:
collate_fn (callable, optional) – merges a list of samples to form a mini-batch of Tensor(s). Used when using batched loading from a map-style dataset.
也就是说在你从dataset 获取一个batch 数据后,你准备对这批数据怎么处理
我们基于batch 做pad_sequence其实就是通过这个参数指向的函数实现,所以我们实现一个这样的函数
def collate_func(batch):
# print(batch)
x_list = [dic['x'] for dic in batch]
y_list = [dic['y'] for dic in batch]
x_padded = pad_sequence(x_list, batch_first = True, padding_value = 0 )
y_padded = pad_sequence(y_list, batch_first = True, padding_value = structrue_index_dict['X'] )
# return tuple(x_padded, y_padded)
return {'x':x_padded, 'y':y_padded}
就是刚才说的,输入的是一个batch ,batch是通过list 组织的,list 的基本单位dataloader 不在乎,只要你自己能够知道怎么取出来用就行了。
所以我返回的时候是一个字典,字典的key ‘x’ 是input 部分,key ‘y’ 对应的是 label。
也就是说,我这一个batch 组成单元是dict,但是dict的基本单元是torch的数组
我处理成一个字典,字典里面两个key,每个key是一个torch数组。
但是x_list 不等长,此处对这一个batch做pad_sequence,目的达到。