Pytorch 如何确保一批样品中包含所有使用Pytork'的工人的样品;什么是数据加载器?

Pytorch 如何确保一批样品中包含所有使用Pytork'的工人的样品;什么是数据加载器?,pytorch,dataloader,Pytorch,Dataloader,我想知道如何在PyTorch中使用torch.utils.data.DataLoader,尤其是在多工作人员的情况下 我发现,DataLoader的一批输出总是来自一个工人。 我希望DataLoader中有一个队列,它存储来自所有Worker的数据,DataLoader在队列中洗牌它们以输出随机批处理数据。我认为这是Tensorflow中tf.data.Dataset的方式。 我们可以在PyTorch中实现类似的功能吗?我想使用多个worker从大型序列化文件(如Tfrecord)加载数据集。在

我想知道如何在PyTorch中使用
torch.utils.data.DataLoader
,尤其是在多工作人员的情况下

我发现,
DataLoader
的一批输出总是来自一个工人。 我希望DataLoader中有一个队列,它存储来自所有Worker的数据,DataLoader在队列中洗牌它们以输出随机批处理数据。我认为这是Tensorflow中
tf.data.Dataset
的方式。 我们可以在PyTorch中实现类似的功能吗?我想使用多个worker从大型序列化文件(如
Tfrecord
)加载数据集。在这种情况下,在一批中混合源文件,这意味着混合工作程序的源文件,这一点很重要

请参阅以下代码:

import random
import time

import torch


class MyDataset(torch.utils.data.Dataset):
    def __len__(self):
        return 50

    def __getitem__(self, idx):
        info = torch.utils.data.get_worker_info()

        time.sleep(random.uniform(0, 1))
        print("[{}]:{}".format(info.id, idx))
        return idx, info.id


if __name__ == '__main__':
    dataset = MyDataset()
    dataloader = torch.utils.data.DataLoader(dataset, batch_size=5, shuffle=False, num_workers=2)
    for batch in dataloader:
        print(batch)
输出:

[0]:0
[1]:5
[0]:1
[1]:6
[0]:2
[0]:3
[1]:7
[0]:4
[tensor([0, 1, 2, 3, 4]), tensor([0, 0, 0, 0, 0])]
[1]:8
[1]:9
[tensor([5, 6, 7, 8, 9]), tensor([1, 1, 1, 1, 1])]
[0]:10
[0]:11
[1]:15
[1]:16
[0]:12
[1]:17
...
这里,
[0,1,2,3,4]
[0,0,0,0,0]
中的
[tensor([0,1,2,3,4])、tensor([0,0,0,0,0])]
意味着该批包含来自工作者id的第0到第4个索引数据
0
。 请注意,
shuffle=True
无法解决此问题,因为它只会更改数据的索引


在本例中,我希望得到一个类似于:
[tensor([0,5,1,6,2])、tensor([0,1,0,1,0])]的批处理。
请注意,具有指定批处理大小的多工作者
数据加载器将并行加载多个批处理,因此本质上一个批处理总是来自工作者。然而,我通过以下方式实现了接近您要求的目标:

  • 将批次大小设置为1,这样每个工人一次只能生产一个样本

  • 编写一个后台进程,该进程遍历DataLoader,一次获取一个样本并将其插入队列。这使得可以在队列中以不同的顺序排列样本,而不是使用特定于工人的批次

  • 有一个批处理机制,比如
    collate\u fn
    ,它从队列中获取与批大小相等的样本数,并将其提供给模型


  • 如果您想在批处理创建中更具体一些,比如从特定的工作人员中拾取特定的样本,那么您可以有多个队列。应修改您的整理过程,以考虑多个队列并从中进行选择。但我怀疑是否需要这种特殊性。

    我已经实现了一些简单的方法来解决类似的问题,我有大量视频文件作为培训数据,每个工作人员负责加载和预处理单个文件,然后从中生成样本。问题是,正如OP所描述的,使用Pytorch的默认数据加载机制,每个批只包含来自单个视频文件的样本

    首先,让我们回顾一下这个问题。在这个简化的代码示例中,每个worker生成一个包含其零索引worker id的张量。对于32和4个worker的批大小,我们希望每个批包含8个零、8个一、8个二和8个三

    从集合导入defaultdict
    进口火炬
    将torch.utils.data作为tdata导入
    类数据集(tdata.IterableDataset):
    定义初始值(自身、批次大小:int):
    self.\u bs=批量大小
    定义(自我):
    worker\u info=tdata.get\u worker\u info()
    如果不是工人信息:
    raise NOTEImplementedError('Not implemented for num\u workers=0')
    对于范围内的(自身):
    屈服T.张量([worker\u info.id])
    批量大小=32
    工人数量=4
    数据集=数据集(批量大小)
    loader=tdata.DataLoader(数据集,
    批次大小=批次大小,
    num_工人=num_工人)
    对于装入器中的批处理:
    计数=defaultdict(int)
    对于批处理中的n.numpy().flatte():
    计数[n]+=1
    打印(笔录(计数))
    
    相反,代码打印:

    {0: 32}
    {1: 32}
    {2: 32}
    {3: 32}
    
    这意味着第一批仅包含来自工作程序0的样本,第二批仅包含来自工作程序1的样本,等等。为了解决这一问题,我们将在
    DataLoader
    中将批大小设置为
    batch\u size//num\u workers
    ,并在
    DataLoader
    上使用一个简单的包装器为我们的批收集来自每个工作程序的样本:

    def池_批次(加载器):
    加载器=iter(加载器)
    尽管如此:
    样本=[]
    对于范围内的(装入器数量):
    尝试:
    samples.append(下一个(loader_it))
    除停止迭代外:
    通过
    如果len(样本)==0:
    打破
    其他:
    产量T.cat(样本,尺寸=0)
    批量大小=32
    工人数量=4
    数据集=数据集(批量大小)
    每个工人=批量大小//工人数量
    loader=tdata.DataLoader(数据集,
    批量大小=每名工人,
    num_工人=num_工人)
    对于池中的批(加载器):
    计数=defaultdict(int)
    对于批处理中的n.numpy().flatte():
    计数[n]+=1
    打印(笔录(计数))
    
    代码现在打印出来了

    {0: 8, 1: 8, 2: 8, 3: 8}
    {0: 8, 1: 8, 2: 8, 3: 8}
    {0: 8, 1: 8, 2: 8, 3: 8}
    {0: 8, 1: 8, 2: 8, 3: 8}
    

    正如所料。

    谢谢你的回答,它解决了我的问题。我将考虑实现一种嵌套的<代码>数据集< /代码>类,它内部有一个批处理大小为1的<代码> DataLoader < /代码>。