Pytorch Dataloader 如何处理可变大小的数据?

Posted

技术标签:

【中文标题】Pytorch Dataloader 如何处理可变大小的数据?【英文标题】:How does Pytorch Dataloader handle variable size data? 【发布时间】:2019-07-29 04:18:36 【问题描述】:

我有一个如下所示的数据集。也就是说,第一项是用户 ID,然后是用户单击的一组项目。

0   24104   27359   6684
0   24104   27359
1   16742   31529   31485
1   16742   31529
2   6579    19316   13091   7181    6579    19316   13091
2   6579    19316   13091   7181    6579    19316
2   6579    19316   13091   7181    6579    19316   13091   6579
2   6579    19316   13091   7181    6579
4   19577   21608
4   19577   21608
4   19577   21608   18373
5   3541    9529
5   3541    9529
6   6832    19218   14144
6   6832    19218
7   9751    23424   25067   12606   26245   23083   12606

我定义了一个自定义数据集来处理我的点击日志数据。

import torch.utils.data as data
class ClickLogDataset(data.Dataset):
    def __init__(self, data_path):
        self.data_path = data_path
        self.uids = []
        self.streams = []

        with open(self.data_path, 'r') as fdata:
            for row in fdata:
                row = row.strip('\n').split('\t')
                self.uids.append(int(row[0]))
                self.streams.append(list(map(int, row[1:])))

    def __len__(self):
        return len(self.uids)

    def __getitem__(self, idx):
        uid, stream = self.uids[idx], self.streams[idx]
        return uid, stream

然后我使用 DataLoader 从数据中检索小批量进行训练。

from torch.utils.data.dataloader import DataLoader
clicklog_dataset = ClickLogDataset(data_path)
clicklog_data_loader = DataLoader(dataset=clicklog_dataset, batch_size=16)

for uid_batch, stream_batch in stream_data_loader:
    print(uid_batch)
    print(stream_batch)

上面的代码返回的结果与我预期的不同,我希望stream_batch 是长度为16 的整数类型的二维张量。但是,我得到的是一个长度为 16 的一维张量列表,并且该列表只有一个元素,如下所示。这是为什么呢?

#stream_batch
[tensor([24104, 24104, 16742, 16742,  6579,  6579,  6579,  6579, 19577, 19577,
        19577,  3541,  3541,  6832,  6832,  9751])]

【问题讨论】:

交叉发布:quora.com/unanswered/… 【参考方案1】:

那么您如何处理样本长度不同的事实? torch.utils.data.DataLoader 有一个 collate_fn 参数,用于将样本列表转换为批次。通过default 它对列表执行this。您可以编写自己的collate_fn,例如0-填充输入,将其截断为某个预定义的长度或应用您选择的任何其他操作。

【讨论】:

如果我不想填充额外的数字怎么办?我的意思是,如果我有一个完全卷积的神经网络,我不需要相同大小的输入,特别是我不想通过填充来改变输入(我正在做一个可解释的 AI 实验)? @RedFloyd 一切都很好,只是您需要进行一些调整并且会损失一些性能。在 PyTorch(以及几乎所有其他框架)中,CNN 操作(例如 Conv2d)在第一个维度(通常称为批处理维度)上以“矢量化”方式执行。在你的情况下,你只需要让这个维度等于 1 并调用你的网络,就像你有图像一样多次,而不是仅仅将它们堆叠成一个大张量并在所有这些张量上执行一次你的网络。这可能会降低您的性能,但仅此而已。 感谢您的回复。澄清一下,这样做本质上是 SGD,训练起来会很吵而且很麻烦(即,可能不会收敛)?l【参考方案2】:

这就是我的做法:

def collate_fn_padd(batch):
    '''
    Padds batch of variable length

    note: it converts things ToTensor manually here since the ToTensor transform
    assume it takes in images rather than arbitrary tensors.
    '''
    ## get sequence lengths
    lengths = torch.tensor([ t.shape[0] for t in batch ]).to(device)
    ## padd
    batch = [ torch.Tensor(t).to(device) for t in batch ]
    batch = torch.nn.utils.rnn.pad_sequence(batch)
    ## compute mask
    mask = (batch != 0).to(device)
    return batch, lengths, mask

然后我将它作为 collate_fn 传递给数据加载器类。


pytorch 论坛中似乎有大量不同帖子的列表。让我链接到所有这些。他们都有自己的答案和讨论。在我看来,没有一种“标准方法”,但如果有权威参考,请分享。

如果理想的答案提到会很好

效率,例如如果在 collat​​e 函数 vs numpy 中使用 Torch 在 GPU 中进行处理

那种东西。

列表:

https://discuss.pytorch.org/t/how-to-create-batches-of-a-list-of-varying-dimension-tensors/50773 https://discuss.pytorch.org/t/how-to-create-a-dataloader-with-variable-size-input/8278 https://discuss.pytorch.org/t/using-variable-sized-input-is-padding-required/18131 https://discuss.pytorch.org/t/dataloader-for-various-length-of-data/6418 https://discuss.pytorch.org/t/how-to-do-padding-based-on-lengths/24442

分桶: - https://discuss.pytorch.org/t/tensorflow-esque-bucket-by-sequence-length/41284

【讨论】:

是否习惯将张量放在 GPU 上整理?我的印象是这意味着如果你这样做,你就不能在你的数据加载器中使用多个工作人员。我很想知道哪种方法通常具有更好的性能。 @Pinocchio 为什么要计算序列长度和掩码?如果我理解正确,一旦批次传入网络,网络就无法使用掩码或修剪输入,对吧? 如果有人偶然发现这个问题,我认为 David Ng 提供的答案是最好的方法***.com/questions/51030782/…【参考方案3】:

正如@Jatentaki 所建议的那样,我编写了我的自定义整理函数,它运行良好。

def get_max_length(x):
    return len(max(x, key=len))

def pad_sequence(seq):
    def _pad(_it, _max_len):
        return [0] * (_max_len - len(_it)) + _it
    return [_pad(it, get_max_length(seq)) for it in seq]

def custom_collate(batch):
    transposed = zip(*batch)
    lst = []
    for samples in transposed:
        if isinstance(samples[0], int):
            lst.append(torch.LongTensor(samples))
        elif isinstance(samples[0], float):
            lst.append(torch.DoubleTensor(samples))
        elif isinstance(samples[0], collections.Sequence):
            lst.append(torch.LongTensor(pad_sequence(samples)))
    return lst

stream_dataset = StreamDataset(data_path)
stream_data_loader = torch.utils.data.dataloader.DataLoader(dataset=stream_dataset,                                                         
                                                            batch_size=batch_size,                                            
                                                        collate_fn=custom_collate,
                                                        shuffle=False)

【讨论】:

以上是关于Pytorch Dataloader 如何处理可变大小的数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何处理 RecyclerView 中的可变高度图像?

PyTorch 在加载图像/掩码文件以进行图像分割时如何处理标签?

pytorch中如何处理RNN输入变长序列padding

Pytorch 中如何处理 RNN 输入变长序列 padding

深度学习实战pytorch中如何处理RNN输入变长序列padding

如何处理最终字符串?