目录
前言
我们经常会遇到这样的情况:使用的数据并不是库自带的,这样的话,不管是训练还是测试,都需要我们来自行实现索引我们的data,并且是每一次按照一个batch_size的规格来索引。我刚开始想的是(假设我有100个样本),自己建一个index表,如idx = np.arange(1,100,1)
,先将它shuffle(idx)
一下,然后每一次取batch_size个下标进行索引,但总觉得有一丢丢不自然。
方法一:继承Dataset & DataLoader
from torch.utils.data import Dataset,DataLoader
import torch
train_X = torch.randn(100,28,28)
train_Y = torch.randn(100)
class Dataset(Dataset):
def __init__(self,x,y):
# self.x = x.unsqueeze(dim=1)
self.y = y
def __len__(self):
return len(self.x)
def __getitem__(self,idx):
return self.x[idx],self.y[idx]
train_DL = DataLoader(Dataset(train_X,train_Y),batch_size=10,shuffle=True)
for idx,(xb,yb) in enumerate(train_DL):
print(idx,xb.shape)
官网解释如下图:
- 在上面的样例中,我创建了Dataset的同名子类
- 并且按照要求,复写了getitem以及len
- 接着,把该类传入DataLoader,并设定参数:batch_size 和 shuffle
方法二:继承Dataset & 封装DataLoader
class DataLoader:
def __init__(self, dl, func):
self.dl = dl # 我们的数据集
self.func = func # 我们的操作对象
def __len__(self):
return len(self.dl)
def __iter__(self):
batches = iter(self.dl)
for b in batches:
yield (self.func(*b))
在方法一的基础上,我们自行封装DataLoader,其中的func可以是我们自定义的函数,用于在加载数据时就把数据处理好,例如是:def preprocess(x,y): return x.view(...),y
这样的函数。
方法三:TensorDataset & DataLoader
我基本用这种方式,就两行代码:
from torch.utils.data import TensorDataset,DataLoader
Train_DS = TensorDataset(train_x,train_y)
Train_DL = DataLoader(Train_DS,shuffle=True,batch_size = 10)
进阶:DataLoader的collate_fn参数设置
看到的例子都是图象方面的,那我说说我在情感分类时遇到的问题。其实跟每个图片的label长度不同的情况一样,每一个句子的单词个数也是不一样的。在一个batch中,如何保证它们的维度相同呢?句子的维度 = 单词数 * 词向量维度,后者不需要担心。我们要做的就是保证每个句子的单词数都一样。而策略就是,找到一个batch中,单词数最多的句子作为基准,然后对其它的较短的句子进行补充(补充全零向量即可)。 那么。DataLoader中的collate_fn就可以实现这样的"padding"过程。
由于暂时用的是"笨"办法,就先把大致思路用代码表示一下,完整的代码以后再补充:
size = 100 # 表示每一个单词表示的向量维度
def collate_fn(batch):
batch.sort(key = lambda x:len(x[0]),reverse=True)
x, y = zip(*batch)
pad_x = []
nums = []
max_num = len(x[0])
for i in range(len(x)):
temp = x[i] # num_of_words * size
for j in range(max_num - len(temp)):
temp.append([0] * size)
pad_x.append(temp_x)
nums.append(len(x[i]))
return pad_x,y,lens
# Then
Train_DS = TensorDataset(train_x,train_y)
Train_DL = DataLoader(Train_DS,shuffle=True,batch_size = 10,collate_fn = collate_fn)
明白了吗? 不明白的话,我尽力解释一下(毕竟自己水平也有限):
首先,collate_fn传入的batch是一个列表!列表的长度是batch_size,也就是说里面有batch_size个元素。每一个元素也是一个列表!这个列表里有两个元素,分别是train_x中的一个样本和train_y中的一个分类标签。我们要解决的就是train_x中的这一个样本,它的第0维(代表着单词数),与其他样本的单词数会有所不同。
那么,我们先把batch中的各个列表,按照列表中第一个元素(也就是train_x的一个样本)的0维进行排序(降序)。
接着就很好理解了,max_num代表最大的单词数,每一个train_x中的样本,如果小于这个单词数。就先计算一下max_num - len(temp),也就是缺了多少单词(n),然后补充n个size大小的0向量。
以上参考简书:PyTorch实现自由的数据读取
另外,使用collate_fn参数,还可以解决"被batch_size整除,而余下的样本"该怎么处理的问题:
具体查看博客:Pytorch技巧1:DataLoader的collate_fn参数
更新
好吧,有用到dataloader了,collate_fn不是进阶,是基础(特别在NLP)。
总结
总的来说,方法三 再 加上collate_fn参数,应该就够用了。虽然方法二可以自定义func,但是如何实现batch_size个样本一起读取,如何实现shuffle,又是一个问题。而相比之下,方法一中,也可以自定义func,在设置dataset的步骤,就可以进行一些操作,比如用unsqueeze(dim=1)增加1维等。
如果觉得哪里讲错了,或者讲得不清楚,请在评论区批评或指出,谢谢!!