数据加载器

简介

数据说深度学习的基础,我们解决的大多数深度学习问题都是需要数据的. 而每一种深度学习框架都对数据格式有自己的要求.因此, 本节我们主要讲解PyTorch对输入数据的格式要求,以及如何将现实中的数据处理成yTorch能够识别的数据集合.

技能点:

  1. 数据的分批
  2. 手写字符数据的分批
  3. 葡萄酒数据的分批

数据的分批

由于深度学习中的数据量一般都是极大的,我们无法一次性将所有数据全部加载到内存中.音痴在模型训练之前,一般我们都会对训练集进行分批,将数据随机分成等量的几份, 每次迭代只训练一份

  1. # 循环训练
  2. for epoch in range(num_epochs):
  3. # 遍历所有批次的数据
  4. for i in range(total_batches):

从上面的伪代码可以会看出:

  • epoch 每增加一次,表示完成了所有数据的一次正向传播和反向传播
  • total_batches表示分批后的数据集合
  • i 每增加一次,表示完成了一批数据的一次正向传播和反向传播

那么如何对数据集合进行分批呢? 我们可以自己尝试编写数据分批的代码.

当然, PyTorch中也为我们提供了相应的接口, 我们可以很容易的实现数据分批的过程. PyTorch为我们提供了torch.utils.data.DataLoader加载器, 该加载器可以自动的将传入的数据进行打乱和分批.

DataLoader()的加载参数如下:

  • dataset: 需要打乱的数据集
  • batch_size: 每一批数据条数
  • shuffle: True或者False, 表示是否将数据打乱后再分批

当然利用该加载器并不仅仅是对数据进行打乱和分批的操作,该加载器还可以对数据进行格式化处理,使其能够被放入后面的神经网络模型中.

接下来, 让我们首先利用该加载器对PyTorch中自带的数据集合MNIST进行分批操作

MNIST的分批

首先让我们加载PyTorch中自带的数据集合,该数据集合存在于torchvision.datasets中, 可以直接利用torchvision.datasets.MNIST获得:

  1. import torch
  2. import torchvision
  3. # 将数据集合下载到指定的目录下
  4. train_dataset = torchvision.datasets.MNIST(root="./data",
  5. train=True,
  6. transform=torchvision.transforms.ToTensor(),
  7. download=False)
  8. print(train_dataset)

显示其中的一条数据看一下

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. %matplotlib inline
  4. data,label = train_dataset[0]
  5. print("第一个数据对应的标签名称:",label)
  6. # 原始数据是归一化后的数据,因此这里需要反归一化
  7. img = np.array(data)*255
  8. img = img.reshape(28,28).astype(np.uint8)
  9. plt.imshow(img,"gray")
  10. plt.show()

该数据集合的分批步骤很简单, 只需要将我们得到的数据集合传入DataLoader中即可:

  1. from torch.utils.data import DataLoader
  2. train_loader = DataLoader(dataset=train_dataset, batch_size=100, shuffle=True)
  3. num_epochs = 1 # 迭代次数
  4. for epoch in range(num_epochs):
  5. for i, (inputs, labels) in enumerate(train_loader):
  6. # 每 10 个批次展示一次
  7. if (i+1) % 10 == 0:
  8. print(
  9. f'Epoch: {epoch+1}/{num_epochs},Step {i+1}/{len(train_dataset)/100}| Inputs {inputs.shape} | Labels {labels.shape}')

如上所示, 我们对MNIST数据集合进行分批, 且利用循环对其进行了遍历. 在模型训练时, 我们将内循环中的代码换乘模型训练的代码即可.

在DataLoader传入的参数值, 我们需要注意的是dataset参数.该参数是一个Dataset类,即只有继承了PyTorch中的Dataset接口的类,才能够被传入DataLoader中

因此, 针对我们自己的数据集合,我们就需要封装一个继承了Dataset的Python类. 这样, 我们才能够将其传入PyTorch之中, 进行数据的分批和模型的训练.

接下来, 我们再以葡萄酒的种类预测为例, 详细的阐述自定义数据应当如何进行封装和分批.

葡萄酒数据的分批

首先,我们先加载数据集合

  1. import pandas as pd
  2. df = pd.read_csv("./wine.csv")

第一列表示该条数据属于哪一种葡萄酒(0,1,2。而后面 13 列的数据表示的就是葡萄酒的每种化学成分的浓度。这些化学成分分别为:酒精 、苹果酸 、灰分 、灰分的碱度、镁 、总酚、 黄酮类化合物 、非类黄酮酚 、原花色素 、颜色强度 、色相 、稀释酒的 OD280/OD315 和脯氨酸

我们需要对这些数据进行分批, 那么我们就需要将该数据转为PyTorch认识的数据集合. 我们可以建立一个类WineDataset去继承Dataset

如果继承了Dataset类, 我们就必须实现下面三个函数:

  • __init__(self): 用于初始化类中所需要的一些变量
  • __len__(self): 返回数据集合的长度, 即数据量大小
  • __getitem__(self, index): 返回第index条数据
  1. from torch.utils.data import Dataset
  2. class WineDataset(Dataset):
  3. # 建立一个数据集合
  4. def __init__(self):
  5. xy = pd.read_csv("./wine.csv")
  6. self.n_samples = xy.shape[0]
  7. # 将pandas类型的数据转换为numpy类型
  8. self.x_data = torch.from_numpy(xy.values[:,1:])
  9. self.y_data = torch.from_numpy(xy.values[:,[0]])
  10. def __getitem__(self, index):
  11. return self.x_data[index], self.y_data[index]
  12. def __len__(self.n_samples)
  13. return self.n_samples

至此,我们就将葡萄酒的数据包装成了Dataset,使用PyTorch能够识别出该数据集合.接下来, 我们只需要利用DataLoader加载该数据集合即可:

  1. # 使用DataLoader去加载数据集合
  2. from torch.utils.data import DataLoader
  3. import math
  4. # 传入加载器
  5. train_loader = DataLoader(dataset=wineData,batch_size=4,shuffle=True)
  6. # 分批训练
  7. # 迭代次数
  8. epoch_num = 5
  9. total_samples = len(wineData)
  10. print("total_samples:",total_samples)
  11. # 开始训练
  12. for epoch in range(epoch_num):
  13. for i,(inputs,labels) in enumerate(train_loader):
  14. print(i,labels)

从结果可以看出, 我们按照每个批次的batch_size, 将数据分成了45个批次, 并对这些数据进行了2次迭代. 由于数据总量不是batch_size的整数倍,因此最后一个批次的数据是有余数的

小结

本节主要阐述了数据分批的重要性.并以手写字符数据集合葡萄酒数据集为例, 阐述了如何将一个数据集合转成PyTorch能够使用的数据集