PyTorch 数据加载器显示字符串数据集的奇怪行为

Posted

技术标签:

【中文标题】PyTorch 数据加载器显示字符串数据集的奇怪行为【英文标题】:PyTorch dataloader shows odd behavior with string dataset 【发布时间】:2021-03-01 04:26:38 【问题描述】:

我正在处理一个 NLP 问题并且正在使用 PyTorch。 由于某种原因,我的数据加载器返回了格式错误的批次。我输入了包含句子和整数标签的数据。 句子可以是句子列表或标记列表列表。稍后我将在下游组件中将标记转换为整数。

list_labels = [ 0, 1, 0]

# List of sentences.
list_sentences = [ 'the movie is terrible',
                   'The Film was great.',
                   'It was just awful.']

# Or list of list of tokens.
list_sentences = [['the', 'movie', 'is', 'terrible'],
                  ['The', 'Film', 'was', 'great.'],
                  ['It', 'was', 'just', 'awful.']]

我创建了以下自定义数据集:

import torch
from torch.utils.data import DataLoader, Dataset

class MyDataset(torch.utils.data.Dataset):

    def __init__(self, sentences, labels):

        self.sentences = sentences
        self.labels = labels

    def __getitem__(self, i):
        result = 
        result['sentences'] = self.sentences[i]
        result['label'] = self.labels[i]
        return result

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

当我以句子列表的形式提供输入时,数据加载器正确返回成批的完整句子。注意batch_size=2:

list_sentences = [ 'the movie is terrible', 'The Film was great.', 'It was just awful.']
list_labels = [ 0, 1, 0]


dataset = MyDataset(list_sentences, list_labels)
dataloader = DataLoader(dataset, batch_size=2)

batch = next(iter(dataloader))
print(batch)
# 'sentences': ['the movie is terrible', 'The Film was great.'], <-- Great! 2 sentences in batch!
#  'label': tensor([0, 1])

批次正确包含两个句子和两个标签,因为batch_size=2

但是,当我将句子输入为标记列表的预标记列表时,我得到了奇怪的结果:

list_sentences = [['the', 'movie', 'is', 'terrible'], ['The', 'Film', 'was', 'great.'], ['It', 'was', 'just', 'awful.']]
list_labels = [ 0, 1, 0]


dataset = MyDataset(list_sentences, list_labels)
dataloader = DataLoader(dataset, batch_size=2)

batch = next(iter(dataloader))
print(batch)
# 'sentences': [('the', 'The'), ('movie', 'Film'), ('is', 'was'), ('terrible', 'great.')], <-- WHAT?
#  'label': tensor([0, 1])

请注意,这批的 sentences 是一个包含单词对元组的单个列表。 我原以为sentences 是两个列表的列表,如下所示:

'sentences': [['the', 'movie', 'is', 'terrible'], ['The', 'Film', 'was', 'great.']

发生了什么事?

【问题讨论】:

我也遇到了这个问题。这似乎是一个真正的问题——pytorch 应该能够整理成批的字符串。我可以看到很多情况下您可能希望在数据加载器步骤之后处理字符串。 【参考方案1】:

另一种解决方案是将字符串编码为字节并在您的Dataset 中,然后在您的前向传递中对其进行解码。如果您想包含元数据字符串(例如数据来自的文件路径),但实际上不需要将数据传递到模型中,这很有用。

例如:

class MyDataset(torch.utils.data.Dataset):
    def __next__(self):
        return np.array("this is a sentence").bytes()

然后在你的前向传球中你会这样做:

sentences: List[str] = []
for sentence in batch:
    sentences.append(sentence.decode("ascii"))

【讨论】:

【参考方案2】:

这种行为是因为默认collate_fn 在必须整理lists 时执行following(['sentences'] 就是这种情况):

# [...]
elif isinstance(elem, container_abcs.Sequence):
    # check to make sure that the elements in batch have consistent size
    it = iter(batch)
    elem_size = len(next(it))
    if not all(len(elem) == elem_size for elem in it):
        raise RuntimeError('each element in list of batch should be of equal size')
    transposed = zip(*batch)
    return [default_collate(samples) for samples in transposed]

之所以会出现“问题”,是因为在最后两行中,它将递归调用 zip(*batch),而批处理是 container_abcs.Sequence(和 list 是),而 zip 的行为是这样的。

如你所见:

batch = [['the', 'movie', 'is', 'terrible'], ['The', 'Film', 'was', 'great.']]
list(zip(*batch))

# [('the', 'The'), ('movie', 'Film'), ('is', 'was'), ('terrible', 'great.')]

除了实现一个新的整理器并将其传递给DataLoader(..., collate_fn=mycollator) 之外,我没有在您的情况下看到解决方法。例如,一个简单的可能是:

def mycollator(batch):
    assert all('sentences' in x for x in batch)
    assert all('label' in x for x in batch)
    return 
        'sentences': [x['sentences'] for x in batch],
        'label': torch.tensor([x['label'] for x in batch])
    

【讨论】:

谢谢。我应该像你一样深入研究批处理生成器。 我也应该认识到,当您在两个列表的同一索引处看到成对的事物时,例如 ('the', 'The'),它可能是 zip() 的输出。

以上是关于PyTorch 数据加载器显示字符串数据集的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

Pytorch 自定义数据加载器

pytorch中的数据加载(dataset基类,以及pytorch自带数据集)

Pytorch数据加载

Pytorch加载数据集的方式总结

如何更改 Pytorch 数据集的大小?

[基于Pytorch的MNIST识别02]用户数据集的读取