文章目录(Table of Contents)
简介
之前我们完成线性回归模型的时候,在训练的时候是一下子把所有的数据都输入模型的,但是在深度学习的时候,数据量一般都是极大的,我们无法一次性将数据加载到内存中,于是就有了数据加载器。
关于 Pytorch 数据加载,之前曾经写过一篇 Pytorch训练时候导入大量数据(How to load large data)。关于重写重写 Dataset 类的方法,我们也会在这一篇中重新进行说明。
在具体的内容之前,先简单说一下在 Pytorch 中,数据加载器的一个总的关系:
- 原始数据集;
- 创建 dataset 类(这里包含重写 dataset 类);
- 将 dataset 传入 dataloader,并告诉 batchsize 等系数;
- 每一个 batch 进行正向传播,梯度下降;
数据的分批加载
由于深度学习中的数据量一般都是极大的, 我们无法一次性将所有数据全部加载到内存中. 因此, 在模型训练之前, 一般我们都会对训练数据进行分批. 将数据随机分成等量的几份, 每次迭代只将一份作为输入.
所以在训练的时候, 我们会按照下面的形式: 每一个epoch中, 有不同的batch (这个就是将一个数据集分成不同的batch)
- # training loop
- for epoch in range(num_epochs):
- # 遍历所有的批次
- for i in range(total_batches):
- batch_x, batch_y = ..
那么如何对数据集进行分批呢 (也就是分成不同的batch), PyTorch 为我们提供了torch.utils.data.DataLoader
加载器, 该加载器可以自动的将传入的数据进行打乱和分批.
DataLoader的输入如下:
- dataset: 需要打乱的数据集(这里的dataset也是需要Pytorch中的dataset, 下面看一下详细的例子).
- batch_size: 每一批的数据条数.
- shuffle: True或者False,表示是否将数据打乱后再分批.
关于显示dataloader中的内容, 我们可以使用next(iter(dataloader))来进行. 详细的可以查看下面"torchvision.datasets.ImageFolder"的介绍部分和最后一部分.
MNIST分批
首先看一下Pytorch中自带的数据集, 该数据集合存在于torchvision.datasets
中, 可以直接利用torchvision.datasets.MNIST
获得.
- import torch
- import torchvision
- # 将数据集合下载到指定目录下
- train_dataset = torchvision.datasets.MNIST(root='./data',
- train=True,
- transform=torchvision.transforms.ToTensor(),
- download=True)
- train_dataset
接着我们将其放入dataloader中, 之后训练的时候可以分批加载.
- from torch.utils.data import DataLoader
- train_loader = DataLoader(dataset=train_dataset,
- batch_size=500,
- shuffle=True)
- num_epochs = 2 # 迭代次数
- total_step = len(train_dataset)/500
- for epoch in range(num_epochs):
- for i, (inputs, labels) in enumerate(train_loader):
- # 这里可以放具体的模型训练
- # 每 10 个批次展示一次
- if (i+1) % 10 == 0:
- print('Epoch: {}/{},Step {}/{}| Inputs {} | Labels {}'.format(epoch+1, num_epochs, i+1, total_step, inputs.shape, labels.shape))
- """
- Epoch: 1/2,Step 10/120.0| Inputs torch.Size([500, 1, 28, 28]) | Labels torch.Size([500])
- Epoch: 1/2,Step 20/120.0| Inputs torch.Size([500, 1, 28, 28]) | Labels torch.Size([500])
- Epoch: 1/2,Step 110/120.0| Inputs torch.Size([500, 1, 28, 28]) | Labels torch.Size([500])
- Epoch: 1/2,Step 120/120.0| Inputs torch.Size([500, 1, 28, 28]) | Labels torch.Size([500])
- Epoch: 2/2,Step 10/120.0| Inputs torch.Size([500, 1, 28, 28]) | Labels torch.Size([500])
- """
在实际操作中, 我们只需要将循环内的代码换成模型训练的代码即可.
重写 Dataset 类
在 DataLoader
传入的参数中,我们需要注意的是dataset
参数。该参数是一个 Dataset 类,即只有继承了 PyTorch 中的 Dataset
接口的类,才能够被传入DataLoader
中。上面的 MNIST 是 Pytorch 中自带的,要是是一般的数据集,这个时候我们需要重写 dataset 类。关于这个部分,推荐查看下面的链接,里面有更加详细的介绍,Pytorch 基础。
这一部分的代码可以参考Github仓库 (包括使用的示例数据也放在了Github仓库): 数据加载器(重写Dataset类的例子)。如果想要继承了Dataset
类, 我们就必须实现下面三个函数:
__init__(self)
: 用于初始化类中所需要的一些变量;__len__(self)
: 返回数据集合的长度,即数据量大小;__getitem__(self, index)
: 返回第 index 条数据;
- class WineDataset(Dataset):
- # 建立一个数据集合继承 Dataset 即可
- def __init__(self):
- # I初始化数据
- # 以pandas的形式读入数据
- winedata = pd.read_csv("./dataset/wine.csv", header=None) # 读取数据
- self.n_samples = winedata.shape[0] # 数据集大小
- # 将 pandas 类型的数据转换成 numpy 类型
- # size [n_samples, n_features]
- self.x_data = torch.from_numpy(winedata.values[:, 1:]) # 特征数据
- self.y_data = torch.from_numpy(winedata.values[:, 0].reshape(-1,1)) # size [n_samples, 1]
- # 返回 dataset[index]
- def __getitem__(self, index):
- return self.x_data[index], self.y_data[index]
- # 返回数据长度
- def __len__(self):
- return self.n_samples
- dataset = WineDataset()
- dataset[0] # 查看样例数据
- """
- (tensor([1.4230e+01, 1.7100e+00, 2.4300e+00, 1.5600e+01, 1.2700e+02, 2.8000e+00,
- 3.0600e+00, 2.8000e-01, 2.2900e+00, 5.6400e+00, 1.0400e+00, 3.9200e+00,
- 1.0650e+03], dtype=torch.float64), tensor([1.], dtype=torch.float64))
- """
- # 放入DataLoader
- import math
- # 传入加载器
- train_loader = DataLoader(dataset=dataset,
- batch_size=4,
- shuffle=True)
- num_epochs = 2
- total_samples = len(dataset)
- n_iterations = math.ceil(total_samples/4) #
- print("该数据集合共有{}条数据,被分成了{}个批次".format(total_samples, n_iterations))
- for epoch in range(num_epochs):
- for i, (inputs, labels) in enumerate(train_loader):
- # 模型训练步骤
- # 178 个样本, batch_size = 4, n_iters=178/4=44.5 -> 45 个批次
- if (i+1) % 5 == 0:
- print('Epoch: {}/{},Step {}/{}| Inputs {} | Labels {}'.format(epoch+1, num_epochs, i+1, n_iterations, inputs.shape, labels.shape))
- """
- 该数据集合共有178条数据,被分成了45个批次
- Epoch: 1/2,Step 5/45| Inputs torch.Size([4, 13]) | Labels torch.Size([4, 1])
- Epoch: 2/2,Step 45/45| Inputs torch.Size([2, 13]) | Labels torch.Size([2, 1])
- """
Data.TensorDataset介绍
对于一些特征类的数据, 可以一下全部读取内容, 就不需要重写Dataset类, 可以直接使用Data.TensorDataset来进行数据的导入. 例如上面的csv文件, 我们就不需要麻烦的重写Dataset类, 只需要使用TensorDataset即可.
这一部分的内容, 可以参考github仓库链接: 数据加载器(TeensorDataset的使用)
首先我们先导入数据, 并将其转换为tensor的格式.
- winedata = pd.read_csv("./dataset/wine.csv", header=None)
- x_data = torch.from_numpy(winedata.values[:, 1:]) # 特征数据
- y_data = torch.from_numpy(winedata.values[:, 0].reshape(-1,1)) # size [n_samples, 1]
- print(x_data.shape, y_data.shape)
- """
- torch.Size([178, 13]) torch.Size([178, 1])
- """
接着我们将其转化为dataset的格式, 这样就可以被传入dataloader中.
- dataset = torch.utils.data.TensorDataset(x_data, y_data) # 合并训练数据和目标数据
- dataset[0]
- """
- (tensor([1.4230e+01, 1.7100e+00, 2.4300e+00, 1.5600e+01, 1.2700e+02, 2.8000e+00,
- 3.0600e+00, 2.8000e-01, 2.2900e+00, 5.6400e+00, 1.0400e+00, 3.9200e+00,
- 1.0650e+03], dtype=torch.float64), tensor([1.], dtype=torch.float64))
- """
最后还是将其传入dataloader, 并做简单的测试.
- MINIBATCH_SIZE = 30
- train_loader = torch.utils.data.DataLoader(
- dataset=dataset,
- batch_size=MINIBATCH_SIZE,
- shuffle=True,
- num_workers=1 # set multi-work num read data
- )
- num_epochs = 2
- total_samples = len(dataset)
- n_iterations = math.ceil(total_samples/4) #
- print("该数据集合共有{}条数据,被分成了{}个批次".format(total_samples, n_iterations))
- for epoch in range(num_epochs):
- for i, (inputs, labels) in enumerate(train_loader):
- # 模型训练步骤
- # 178 个样本, batch_size = 4, n_iters=178/4=44.5 -> 45 个批次
- if (i+1) % 5 == 0:
- print('Epoch: {}/{},Step {}/{}| Inputs {} | Labels {}'.format(epoch+1, num_epochs, i+1, n_iterations, inputs.shape, labels.shape))
- """
- 该数据集合共有178条数据,被分成了45个批次
- Epoch: 1/2,Step 5/45| Inputs torch.Size([30, 13]) | Labels torch.Size([30, 1])
- Epoch: 2/2,Step 5/45| Inputs torch.Size([30, 13]) | Labels torch.Size([30, 1])
- """
torchvision.datasets.ImageFolder介绍
对于图片的数据, 我们其实也是不用单独设计dataset的, 可以直接使用现成的ImageFolder. (关于train和test就可以使用两次ImageFolder)
官方文档: ImageFolder介绍
下面看一个简单的例子.
- trans = transforms.Compose([
- transforms.Resize(64),
- transforms.ToTensor(),
- transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
- ])
- # 注意这里文件夹的路径
- dataset = datasets.ImageFolder('./data', transform=trans) # 数据路径
- dataloader = torch.utils.data.DataLoader(dataset,
- batch_size=16, # 批量大小
- shuffle=True, # 乱序
- num_workers=2 # 多进程
- )
同时我们需要注意的是, 这个类的root路径的书写. 他的文件格式是如下所示的.
- root/dog/xxx.png
- root/dog/xxy.png
- root/dog/xxz.png
- root/cat/123.png
- root/cat/nsdf3.png
- root/cat/asd932_.png
也就是他只需要写到root目录, 然后把每一类图片分别放在对应的文件夹内. 我们不能直接将图片放在root目录下, 不然就会出现如下的报错: RuntimeError: Found 0 images in subfolders of: ./data
上面报错解决链接: RuntimeError: Found 0 images in subfolders of: ./data
Dataloader结果展示
下面看一下如何查看定义好的数据集中的数据. 我们使用图片作为例子. 其他的就更加简单. 首先需要将tensor转换为numpy, 同时需要反归一化, 并且转换颜色通道.
- import matplotlib.pyplot as plt
- %matplotlib inline
- def imshow(inp, title):
- """Imshow for Tensor."""
- inp = inp.numpy().transpose((1, 2, 0))
- inp = std * inp + mean
- inp = np.clip(inp, 0, 1)
- plt.imshow(inp)
- plt.title(title)
- plt.show()
之后从dataloader中取一个batch的数据出来, 来进行显示.
- # 获得训练数据中的一个批次的数据
- inputs, classes = next(iter(dataloaders['train'])) # 这个大小和batch size是有关的
- # 将图片拼成一个网格
- out = torchvision.utils.make_grid(inputs)
- # 展示图像
- imshow(out, title=[class_names[x] for x in classes])
- 微信公众号
- 关注微信公众号
- QQ群
- 我们的QQ群号
评论