在进行深度学习处理的时候,我们需要将数据输入到神经网络中进行训练,训练网络的学习能力,其实是根据一定的规则更新网络节点中的参数,而这个规则的来源就是依赖于数据与标签。我们需要将数据与标签相匹配,才能让网络进行训练,比如说网络学习到了一定的特征,而查阅此时的标签信息,比如说是车,那么网络就可以记住这样的特征表示的是车。这就要求我们输入的数据与数据标签是要对应的,在pytorch中,我们使用torch.utils.data 类来实现。
函数的中文文档:
torch.uutils.datahttps://pytorch-cn.readthedocs.io/zh/latest/package_references/data/函数的torch官网文档:torch.uutils.data
https://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_fn
pin_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()
这样就完成了数据与标签的读入。