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