pytorch中的dataset和DataLoader创建数据集进行训练

序言

1.每次采用一个样本进行随机梯度下降,会得到随机性较好的训练结果,但是速度较慢,训练时间长

2.加入batch,用全部的样本进行训练时速度十分快,但是会训练效果会下降。

所以在这里引入mini-batch,综合二者的优点,得到折衷的结果。

batch_size(批量大小):进行一次前馈、反馈、更新所用到的样本数量

iteration(迭代次数):样本数量/批量大小

一般来说PyTorch中深度学习训练的流程

  1. 创建Dateset
  2. Dataset传递给DataLoader
  3. DataLoader迭代产生训练数据提供给模型

Dataset负责建立索引到样本的映射,DataLoader负责以特定的方式从数据集中迭代的产生 一个个batch的样本集合。在enumerate过程中实际上是dataloader按照其参数sampler规定的策略调用了其dataset的getitem方法。

Dataset和DataLoader

Dataset

简介:

1.torch.utils.data.Dataset是代表自定义数据集方法的抽象类,你可以自己定义你的数据类继承这个抽象类,非常简单,只需要定义__len__和__getitem__这两个方法就可以。

2.通过继承torch.utils.data.Dataset的这个抽象类,我们可以定义好我们需要的数据类。当我们通过迭代的方式来取得每一个数据,但是这样很难实现取batch,shuffle或者多线程读取数据,所以pytorch还提供了一个简单的方法来做这件事情,通过torch.utils.data.DataLoader类来定义一个新的迭代器,用来将自定义的数据读取接口的输出或者PyTorch已有的数据读取接口的输入按照batch size封装成Tensor。

Dataset成员函数:

下面通过介绍python定制容器的方式来介绍__len__(self),getitem(self)两种方法。
在python中,像序列类型(如列表,元组和字符串)或映射类型(如字典)都属于容器类型。讲定制容器,那就必须要知道,定制容器有关的一些协议:

如果你希望定制的容器是不可变的话,你只需要定义__len__()和__getitem__这两个魔法方法。
如果你希望定制的容器是可变的话,除了__len__()和__getitem__这两个方法,还需要定义__setitem__()和__delitem__()两个方法。

举例说明(处理图像数据集):

这里我们的图片文件储存在“./data/faces/”文件夹下,图片的名字并不是从1开始,而是从final_train_tag_dict.txt这个文件保存的字典中读取,label信息也是用这个文件中读取。

from torch.utils import data
import numpy as np
from PIL import Image
class face_dataset(data.Dataset):
    def __init__(self):
        self.file_path = './data/faces/'
        f=open("final_train_tag_dict.txt","r")  #读取图片名字典
        self.label_dict=eval(f.read())  #将读取到的字符串转化为相应的字典值
        f.close()

    def __getitem__(self,index):
        label = list(self.label_dict.values())[index-1] #获取标签
        img_id = list(self.label_dict.keys())[index-1]  #id
        img_path = self.file_path+str(img_id)+".jpg"  #图片路径
        img = np.array(Image.open(img_path))  #获取图片
        return img,label   #返回图片和标签

    def __len__(self):
        return len(self.label_dict)

DataLoader

DataLoader负责以特定的方式从数据集中迭代的产生 一个个batch的样本集合,不需要我们过多的编写内部实现,只要会用即可(正常情况下只需要关注加粗的参数即可)。

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)

参数介绍:

  1. dataset(Dataset): 传入的数据集
  2. batch_size(int, optional): 每个batch有多少个样本
  3. shuffle(bool, optional): 在每个epoch开始的时候,对数据进行重新排序
  4. sampler(Sampler, optional): 自定义从数据集中取样本的策略,如果指定这个参数,那么shuffle必须为False
  5. batch_sampler(Sampler, optional): 与sampler类似,但是一次只返回一个batch的indices(索引),需要注意的是,一旦指定了这个参数,那么batch_size,shuffle,sampler,drop_last就不能再制定了(互斥——Mutually exclusive)
  6. num_workers (int, optional): 这个参数决定了有几个进程来处理data loading。0意味着所有的数据都会被load进主进程。(默认为0)
  7. collate_fn (callable, optional): 将一个list的sample组成一个mini-batch的函数
  8. pin_memory (bool, optional): 如果设置为True,那么data loader将会在返回它们之前,将tensors拷贝到CUDA中的固定内存(CUDA pinned memory)中.
  9. drop_last (bool, optional): 如果设置为True:这个是对最后的未完成的batch来说的,比如你的batch_size设置为64,而一个epoch只有100个样本,那么训练的时候后面的36个就被扔掉了…
    如果为False(默认),那么会继续正常执行,只是最后的batch_size会小一点。
重点讲一下collate_fn

collate_fn:如何取样本的,我们可以定义自己的函数来准确地实现想要的功能
MINIST数据集的dataset是由一张图片和一个label组成的元组
collate_fn为lamda x:x时表示对传入进来的数据不做处理,当我们需要进行处理时,就需要自定义一个函数
自定义一个collate_fn函数:

def collate(data):  #对如何取样本进行自定义
    img = []
    label = []
    for each in data:
        img.append(each[0])
        label.append(each[1])
    return img,label  
dataloader = torch.utils.data.DataLoader(dataset=mnist, batch_size=2, shuffle=True,collate_fn=lambda x:collate(x))
for each in dataloader:
	pass

具体实现(构造数据集、加载数据集、训练)

1.把所有的数据加载进来,在使用getitem时将第i个样本传出去(适合样本总量较小的数据集)

2.定义一个列表,将文件名放入列表中,在使用getitem时,读取第i个文件中的内容(如几十个G的图像集)

import numpy as np
import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader

class DiabetesDataset(Dataset):   #由Dataset(抽象类)继承
    def __init__(self, filepath):
        xy = np.loadtxt(filepath, delimiter=',', dtype=np.float32)
        self.len = xy.shape[0]
        self.x_data = torch.from_numpy(xy[:, :-1])
        self.y_data = torch.from_numpy(xy[:, [-1]])

    def __getitem__(self, index):  #通过索引得到数据
        return self.x_data[index], self.y_data[index]

    def __len__(self):
        return self.len

dataset = DiabetesDataset('diabetes.csv')  #实例化为一个对象
#dataset每次可以拿到一个数据样本
#加载器  DataLoader根据batch_size的数量将多个dataset的x和y组合成矩阵,并自动生成Tensor
train_loader = DataLoader(dataset=dataset,
                          batch_size=32,
                          shuffle=True,
                          num_workers=2   #多线程
                          )

相应的训练过程为:

for epoch in range(100):
    for i, data in enumerate(train_loader, 0):
        #得到数据
        inputs, labels = data
        #前馈
        y_pred = model(inputs)
        loss = criterion(y_pred, labels)
        print(epoch, i, loss.item())
        #反向传播
        optimizer.zero_grad()
        loss.backward()
        #更新
        optimizer.step()

猜你喜欢

转载自blog.csdn.net/gary101818/article/details/124561007