一、DataLoader背景知识与概念

        定义:DataLoader是 PyTorch 提供的一个数据加载器,用于对数据进行批量加载和处理。它在训练神经网络时起到了重要的作用。它存在于 torch.utils.data 包下,需要的时候应该在该包下导入DataLoader。

        问题1:为什么需要DataLoader?

        神经网络的训练过程通常需要大量的数据,而将所有数据一次性加载到内存中是不可行的。这时候就需要使用 DataLoader 将数据分成小批次进行加载。DataLoader 可以自动完成数据的批量加载、随机洗牌(shuffle)、并发预取等操作,从而提高模型训练的效率。

        问题2:DataLoader有什么特点?

        (1)批处理: DataLoader 可以将数据分成小批次进行加载,从而使得每次迭代都能处理多个数据样本。

        (2)随机洗牌(shuffle): DataLoader 具有随机洗牌功能,可以在每个迭代之前将数据顺序打乱,从而减少模型对输入数据的依赖性。

        (3)并发预取(prefetching): DataLoader 可以使用多线程或多进程来提前预取数据,充分利用 CPU 的性能,加速数据加载过程。

        (4)方便的数据转换: DataLoader 可以通过自定义的数据转换函数对原始数据进行预处理,如裁剪、缩放、标准化等。

二、DataLoader的参数设计

1、DataLoader 的常用参数

dataset: 指定数据集,通常是 torch.utils.data.Dataset 类的实例。

batch_size: 每个批次的样本数量。

shuffle: 是否对数据进行洗牌操作。

num_workers: 加载数据时的并发线程或进程数。

drop_last: 当数据样本数量不能被批次大小整除时,是否丢弃最后一个不完整的批次。

collate_fn: 自定义的数据处理函数,用于将多个样本汇集到一个批次中。‘’

2、DataLoader的参数设计建议

        在调整 DataLoader 的参数时,需要根据具体问题和数据集的特点进行考虑。以下是一些常见的调整方法:

(1)batch_size根据可用内存和模型的需求,增加或减少批次大小。较大的批次大小可以提高训练速度,但也可能会增加内存占用。

(2)shuffle是否进行随机洗牌:对于训练数据,通常会设置为 True来减少模型对输入数据顺序的依赖性。对于验证和测试数据,通常会设置为False。

(3)num_workers根据计算机硬件的配置和数据加载的速度来设置,并发加载数据以加快训练速度。但是,过多的并行加载也可能会导致性能下降,因此需要进行适当的调整。

(4)drop_last默认情况下, DataLoader 不会丢弃最后一个不完整的批次。但是,在内存有限的情况下,如果内存无法容纳一个完整的批次,可以将 drop_last 设置为 True,丢弃最后一个不完整的批次。

三、用DataLoader实现神经网络训练

0、导包

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

1、准备数据集与DataLoader

class DiabetesDataset(Dataset):

    #   DiabetesDataset类继承自torch.utils.data.Dataset
    #   重写了__init__、__getitem__和__len__方法。

    #   1、__init__方法:它加载了名为"diabetes.csv.gz"的数据文件
    #       并将数据划分为输入特征(x_data)和目标标签(y_data)。
    #       x_data是除最后一列之外的所有列,y_data是最后一列。
    #       同时,它还记录了数据集的长度。

    #   2、__getitem__方法:用于获取指定索引的样本,返回的是该索引
    #       处的输入特征和目标标签。

    #   3、__len__方法:返回数据集的长度。



    def __init__(self, filePath):
        xy_data_set = np.loadtxt(filePath, delimiter=',', dtype=np.float32)
        self.len = xy_data_set.shape[0]
        self.x_data = torch.from_numpy(xy_data_set[:, :-1])
        self.y_data = torch.from_numpy(xy_data_set[:, [-1]])


    def __getitem__(self, index):
        return self.x_data[index], self.y_data[index]


    def __len__(self):
        return self.len

#   最后,在创建了DiabetesDataset对象后,使用DataLoader对数据集
#   进行批处理,每批大小为32,打乱顺序,并开启2个工作进程。

dataset = DiabetesDataset("diabetes.csv.gz")
train_loader = DataLoader(dataset=dataset, batch_size=32, shuffle=True, num_workers=2)

        要使用DataLoader去训练神经网络,首先要构建一个类,让这个类继承至Dataset,在这个类中重写3个方法,分别是:init,getitem,len。它们的作用分别是:

        (1)init:初始化代码,给定数据集的filePath,可以进行输入和输出的切割操作,并且记录了这个数据集有多少行,即:有多少个样本;

        (2)getitem:主要是返回指定index处的输入特征以及输出标签值

        (3)len:主要是返回数据集的样本数

        完成类的编写后,利用其构造器,传入数据集的文件路径,生成一个Dataset的实例化对象,此时开始生成DataLoader对象,利用DataLoader类的构造器,传入代表数据集的变量(dataset=?),传入批量处理数据的容量(batch_size=?),决定是否打乱数据集的数据内容(shuffle=?),设置并行线程数量(num_workers=?),这样我们就得到了一个DataLoader对象,并已经设置好了它的参数。

2、创建模型

class Model(torch.nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.linear1 = torch.nn.Linear(8, 6)
        self.linear2 = torch.nn.Linear(6, 4)
        self.linear3 = torch.nn.Linear(4, 1)
        self.sigmoid = torch.nn.Sigmoid()

    def forward(self, x):
        x = self.sigmoid(self.linear1(x))
        x = self.sigmoid(self.linear2(x))
        y_pred = self.sigmoid(self.linear3(x))
        return y_pred

model = Model()

3、创建损失函数和优化器

BCE_Loss = torch.nn.BCELoss(reduction="mean")
SGD_optm = torch.optim.SGD(model.parameters(), lr=0.03)

4、开始训练

if __name__ == '__main__':
    epochs = 100

    # 设定训练的总轮数(epoch),每个epoch表示将整个数据集完整地通过模型一次。
    for epoch in range(epochs):

        # 使用enumerate函数遍历train_loader中的每个批次数据。
        # enumerate(train_loader, 0)的返回结果是一个可迭代对象iteration
        # 每次迭代会返回一个元组(index, dataTuple),其中包含了索引和对应的数据元组。
        # i表示当前批次的索引,data包含当前批次的输入特征和目标标签。(索引从0开始)

        for i, data in enumerate(train_loader, 0):

            x_data, y_label = data
            y_predict = model(x_data)
            loss = BCE_Loss(y_predict, y_label)

            print(f"Epoch = {epoch+1}, Part = {i}, Loss = {loss.item()}")

            SGD_optm.zero_grad()
            loss.backward()
            SGD_optm.step()

        这里需要注意的是:我们准备训练100轮,一轮训练32个试验数据,共计会训练3200个样本用于训练模型,循环开始时,我们需要用enumerate()函数把第一批得到的32个样本进行拆解,该函数需要传入一个DataLoader,再传入一个初始索引值,即:enumerate(train_loader , 0)。

        传入参数后,该函数会返回2个结果,第一个是index值(本节初始index为0),第二个是data元组,里面包含了这一批量样本的输入特征与输出标签值,所以要用 x_data 和 y_label 来接收这个元组中的数据。

        接收到数据后,就可以开始训练模型,以及计算损失函数了。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐