pytorch载入数据与对应的标签,使用torch.utils.data详解,DataLoader的使用

在进行深度学习处理的时候,我们需要将数据输入到神经网络中进行训练,训练网络的学习能力,其实是根据一定的规则更新网络节点中的参数,而这个规则的来源就是依赖于数据与标签。我们需要将数据与标签相匹配,才能让网络进行训练,比如说网络学习到了一定的特征,而查阅此时的标签信息,比如说是车,那么网络就可以记住这样的特征表示的是车。这就要求我们输入的数据与数据标签是要对应的,在pytorch中,我们使用torch.utils.data 类来实现。

函数的中文文档:

torch.uutils.datahttps://pytorch-cn.readthedocs.io/zh/latest/package_references/data/函数的torch官网文档:torch.uutils.datahttps://pytorch.org/docs/stable/data.html

PyTorch数据加载实用程序的核心是torch.utils.data.DataLoader类。它表示对数据集的Python可迭代,支持

1.地图样式和可迭代样式的数据集,

2.自定义数据的加在顺序 ,

3.自动进程,

4.单线程和多线程数据加载,

5.自动内存固定。

这些都由DataLoader的构造函数参数配置:

DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
           batch_sampler=None, num_workers=0, collate_fn=None,
           pin_memory=False, drop_last=False, timeout=0,
           worker_init_fn=None, *, prefetch_factor=2,
           persistent_workers=False)

dataset:数据存储的地址

batch_size:每次处理的数据批量大小,一般为2的次方,如2,4,8,16,32,64等等

shuffle:是否随机读入数据,在训练集的时候一般随机读入,在验证集的时候一般不随机读入。

num_works:多线程传入数据,设置的数字即使传入的线程数,可以加快数据的读取。

其余的参数一般不做设置,除非你是炼丹大师,可以自己去尝试一下。

pin_memory:内存是否固定:对于数据加载,传递给DataLoader会自动将提取的数据张量放入固定内存中,从而更快地将数据传输到支持 CUDA 的 GPU。默认内存固定逻辑仅识别张量以及包含张量的映射和可迭代对象。默认情况下,如果固定逻辑看到自定义类型的批处理(如果有返回自定义批处理类型的批处理,则会发生这种情况),或者如果批的每个元素都是自定义类型,则固定逻辑将无法识别它们,并且它将返回该批处理(或这些元素),而不固定内存。若要为自定义批处理或数据类型启用内存固定,请在自定义类型上定义一个方法。collate_fnpin_memory()

prefetch_factor:预加载数据。每个工作线程提前加载的样本数。 意味着所有工人总共将有2 * num_workers个样本预取.

Persisitent_workers:如果 ,则数据加载程序在数据集使用一次后不会关闭工作进程。这允许保持工作线程数据集实例处于活动状态。

drop_last:如果数据集的大小不能被批大小整除,则设置为删除最后一个未完成的批处理。如果数据集的大小不能被批整除,则最后一个批将更小。

class SimpleCustomBatch:
    def __init__(self, data):
        transposed_data = list(zip(*data))
        self.inp = torch.stack(transposed_data[0], 0)
        self.tgt = torch.stack(transposed_data[1], 0)

    # custom memory pinning method on custom type
    def pin_memory(self):
        self.inp = self.inp.pin_memory()
        self.tgt = self.tgt.pin_memory()
        return self

def collate_wrapper(batch):
    return SimpleCustomBatch(batch)

inps = torch.arange(10 * 5, dtype=torch.float32).view(10, 5)
tgts = torch.arange(10 * 5, dtype=torch.float32).view(10, 5)
dataset = TensorDataset(inps, tgts)

loader = DataLoader(dataset, batch_size=2, collate_fn=collate_wrapper,
                    pin_memory=True)

for batch_ndx, sample in enumerate(loader):
    print(sample.inp.is_pinned())
    print(sample.tgt.is_pinned())

DataLoader是数据的交在程序,将数据集和采样器组合在一起,并提供对给定数据集的可迭代。DataLoader支持地图样式和可迭代样式的数据集,具有单进程和多进程加载,自定义加载顺序以及可选的自动批处理和内存固定功能。

torch.utils.data.Dataset(*args, **kwds)

表示的是Dataset的抽象类,所有其他数据都应该进行子类化。

表示从键到数据样本的映射的所有数据集都应对其进行子类化。所有子类都应该覆盖,支持为给定的键获取数据样本。子类也可以选择覆盖 ,这有望通过许多 Sampler实现和 DataLoader的默认选项返回数据集的大小。__getitem__()__len__()

默认情况下,DataLoader构造一个生成整数索引的索引采样器。要使其适应具有非整数索引的地图样式数据集,必须自己定采样器。

torch.utils.data.IterableDataset(*args, **kwds)

可迭代数据集。

表示数据样本可迭代的所有数据集都应对其进行子类化。当数据来自流时,这种形式的数据集特别有用。

所有子类都应该覆盖 ,这将返回此数据集中样本的迭代器。__iter__()

当子类与 DataLoader一起使用时,数据集中的每个项都将从 DataLoader迭代器生成。当 时,每个工作进程将具有数据集对象的不同副本,因此通常需要单独配置每个副本,以避免从工作线程返回重复数据。在工作进程中调用get_worker_info()时,将返回有关工作进程的信息。它可以在数据集的方法或DataLoader的选项中使用,以修改每个副本的行为。num_workers > 0__iter__()worker_init_fn

示例 1:在以下位置的所有工作线程之间拆分工作负载:__iter__()

class MyIterableDataset(torch.utils.data.IterableDataset):
    def __init__(self, start, end):
        super(MyIterableDataset).__init__()
        assert end > start, "this example code only works with end >= start"
        self.start = start
        self.end = end
    def __iter__(self):
        worker_info = torch.utils.data.get_worker_info()
        if worker_info is None:  # single-process data loading, return the full iterator
            iter_start = self.start
            iter_end = self.end
        else:  # in a worker process
            # split workload
            per_worker = int(math.ceil((self.end - self.start) / float(worker_info.num_workers)))
            worker_id = worker_info.id
            iter_start = self.start + worker_id * per_worker
            iter_end = min(iter_start + per_worker, self.end)
        return iter(range(iter_start, iter_end))
# should give same set of data as range(3, 7), i.e., [3, 4, 5, 6].
ds = MyIterableDataset(start=3, end=7)

# Single-process loading
print(list(torch.utils.data.DataLoader(ds, num_workers=0)))

# Mult-process loading with two worker processes
# Worker 0 fetched [3, 4].  Worker 1 fetched [5, 6].
print(list(torch.utils.data.DataLoader(ds, num_workers=2)))

# With even more workers
print(list(torch.utils.data.DataLoader(ds, num_workers=20)))

示例 2:使用 :worker_init_fn

class MyIterableDataset(torch.utils.data.IterableDataset):
    def __init__(self, start, end):
        super(MyIterableDataset).__init__()
        assert end > start, "this example code only works with end >= start"
        self.start = start
        self.end = end
    def __iter__(self):
        return iter(range(self.start, self.end))
# should give same set of data as range(3, 7), i.e., [3, 4, 5, 6].
ds = MyIterableDataset(start=3, end=7)

# Single-process loading
print(list(torch.utils.data.DataLoader(ds, num_workers=0)))
# Directly doing multi-process loading yields duplicate data
print(list(torch.utils.data.DataLoader(ds, num_workers=2)))

# Define a `worker_init_fn` that configures each dataset copy differently
def worker_init_fn(worker_id):
    worker_info = torch.utils.data.get_worker_info()
    dataset = worker_info.dataset  # the dataset copy in this worker process
    overall_start = dataset.start
    overall_end = dataset.end
    # configure the dataset to only process the split workload
    per_worker = int(math.ceil((overall_end - overall_start) / float(worker_info.num_workers)))
    worker_id = worker_info.id
    dataset.start = overall_start + worker_id * per_worker
    dataset.end = min(dataset.start + per_worker, overall_end)

# Mult-process loading with the custom `worker_init_fn`
# Worker 0 fetched [3, 4].  Worker 1 fetched [5, 6].
print(list(torch.utils.data.DataLoader(ds, num_workers=2, worker_init_fn=worker_init_fn)))

# With even more workers
print(list(torch.utils.data.DataLoader(ds, num_workers=20, worker_init_fn=worker_init_fn)))

在实际的应用中,如果我们的数据存储方式如下:

每个文件夹的名字就是数据的类名,那么数据可以使用一下方式载入:

使用datasets.ImageFolder读取数据,root是文件存储路径, transform是数据的处理方式,如裁剪,缩放,归一化等等。

train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
                                         transform=data_transform["train"])

读取到数据总数有6149个,一共有102个文件夹,即分为102个类,每个类的标签从0开始,即数据标签中的前几张图片都是一个类的,且类的名字是0类。 

计算train_dataset的个数:

train_num = len(train_dataset)

 载入数据到 train_loader。

 train_loader = torch.utils.data.DataLoader(train_dataset,
                                               batch_size=batch_size, shuffle=True,
                                               num_workers=nw)

这样便将数据与标签都存储到了 train_loader中。

这是 DataLoader中的一些参数设置,没有设置的参数都是默认参数。 DataLoader主要是提供一个迭代器。

然后在使用数据的时候,我们将数据加载到进度条中,然后进行使用:

train_bar = tqdm(train_loader)

 然后进行数据正向传播,损失计算,损失反向传播,优化器迭代,损失计算:

        for step, data in enumerate(train_bar): 
            images, labels = data
            optimizer.zero_grad()
            outputs = net(images.to(device))
            loss = loss_function(outputs, labels.to(device))
            loss.backward()
            optimizer.step()
            running_loss += loss.item()

这样就完成了数据与标签的读入。

  • 7
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值