《PyTorch深度学习实践》学习笔记:加载数据集


前言

在前面的内容中,在训练的时候我们使用的都是全部的数据进行训练,这种在时间上比较好,但是性能上会遇到一些问题,比如鞍点;此外,我们还知道一种随机梯度下降,这种训练是每次取一个数据去计算梯度,这种方法能够有效的解决鞍点的问题,而且性能相对较好,但是每次一个点时间消耗多,而且不能利用CPU/GPU的并行计算能力;把全部的梯度下降和随机梯度下降进行一个折中(时间和性能进行折中),得出了批量梯度下降,也就是Mini-batch梯度下降,也就是每次训练时去一小批数据进行训练。


一、Mini-batch基础概念

当我们使用Mini-batch进行训练的时候,我们就需要通过两层循环还进行:

for epoch in range(trainging_epochs):  # 外层代表训练轮数
	for i in range(total_batchs):      # 内存对batch进行迭代
  • Epoch:1次epoch表示把所有的训练样本都进行了一次前馈和反馈的训练;

  • Batch_Size:表示一次前馈和反馈所使用的样本数量;

  • Iteration:将样本一共分成了几个Mini-Batch。

例:10000个样本,其中Batch_size=1000,则Iteration=10000/1000=10。

采用Mini-Batch的方法来训练数据时,需要使用Dataset和DataLoader两个工具类:

Dataset:用来构造数据集,使数据集可以通过索引快速取出

DataLoader:取出一个Mini-Batch,一组数据


二、DataLoader使用

使用DataLoader时需要两个基本参数:

  1. shuffle :是否打乱数据顺序,打乱的话每次epoch的Mini-Batch数据都是随机的
  2. batch-size:每个Mini-Batch需要的数据量

dataset提供了索引和长度,这样的话dataloader就可以根据dataset进行小批量的数据集的生成。
在这里插入图片描述
上图batch_size = 2,dataloader的工作先通过shuffle将数据样本打乱,打乱之后进行分组,按照每batch_size为一组,之后在迭代的过程中就是按照每组进行Mini-batch取数据。

在代码层面实现dataset和dataloader:

pytorch的utils.data里面提供Dataset和Dataloader。Dataset是抽象类,而抽象类是不能够实例化的,他只能被其他的子类进行继承,所以我们只能通过继承的方式来自定义我们自己的类。Dataloader是一个类帮助我们来加载数据,可以shuffle以及batch_size,因此我们可以实例化一个Dataloader。

接下来定义我们自己数据集的类:

class DiabetesDataset(Dataset):
    def __init__(self):
        pass
    def __getitem__(self, index):
        pass
    def __len__(self):
        pass

getitem(): 实现数据集的索引,dataset[index]

len(): 返回数据集的长度

在构造数据集的时候需要注意有两点:

  1. 我们把所有的数据都通过init()加载到内存当中,然后我们通过getitem()把构造好的数据集的第i个样本给传出去,这种方法适合于本身的数据集不大,但是对于打的数据集比如图像数据集,init()把数据都读到内存是不可能的,对于图像有时候会把它变成一个一个的文件,有时候会把它放到一个package文件里,所以在init()里面只是做一些初始化,或者说都是读写的,定义一些列表,然后数据集里面的每一条数据的文件名放到相应的列表里面,相应的标签可以读到内存里(比较简单的标签),但是一些数据集的标签也很复杂,例如输入一张图像,输出是图像的每一个像素是属于背景还是前景,这就使得输出的结果也很大,所以对于简单的输出标签,可以选择加载到内存;对于复杂的输出,就可以把它的文件名放到列表里面,等到getitem()可以读取列表里面的第i个文件,然后输入X和输出Y再从文件里读出来,然后再进行返回,这样才能保证内存的高效使用。
  2. 如果数据比较小,就直接读到内存;如果数据集比较大,我们应该考虑是否可以把它直接加载到内存。

关于Dataloader的参数,我们常使用的有四个:

train_loader = DataLoader(dataset=dataset,   # 数据集对象
                          batch_size=32,     # 小批量的样本数量
                          shuffle=True,      # 数据是否打乱
                          num_workers=2)     # 读数据的是否需要多线程(并行化)

关于num_workers在windows中使用需要注意的:
在这里插入图片描述
如果直接使用trainloader进行训练的话,可以会报错(pytorch 0.4),报错的原因是在Linux和windows下实现多进程的方式是不同的,在windows中使用spawn替代了fork,可以会出现runtimeError。

解决问题方法:把loader迭代的训练代码封装起来,封装到if语句里面,或者是封装到函数里面,不直接写到程序的主体里面。
在这里插入图片描述
最终实现我们自己的数据集和加载器:

class DiabetesDataset(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, item):
        return self.x_data[item],self.y_data[item]                # 数据索引

    def __len__(self):
        return self.len                                           # 数据集的长度

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

三、模型准备、损失函数、优化器

# 模型
class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.linear1 = nn.Linear(8,6)
        self.linear2 = nn.Linear(6,4)
        self.linear3 = nn.Linear(4,1)
        self.sigmoid = nn.Sigmoid()
    def forward(self, x):
        x = self.sigmoid(self.linear1(x))
        x = self.sigmoid(self.linear2(x))
        x = self.sigmoid(self.linear3(x))
        return x
model = Model()
# 损失函数
creation = nn.BCELoss(reduction='mean')
# 优化器
optimizer = torch.optim.SGD(model.parameters(),lr=0.01)

四、完整代码

import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
import numpy as np
import torch.nn as nn
import matplotlib.pyplot as plt
class DiabetesDataset(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, item):
        return self.x_data[item],self.y_data[item]                # 数据索引

    def __len__(self):
        return self.len                                           # 数据集的长度

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

# 模型
class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.linear1 = nn.Linear(8,6)
        self.linear2 = nn.Linear(6,4)
        self.linear3 = nn.Linear(4,1)
        self.sigmoid = nn.Sigmoid()
    def forward(self, x):
        x = self.sigmoid(self.linear1(x))
        x = self.sigmoid(self.linear2(x))
        x = self.sigmoid(self.linear3(x))
        return x
model = Model()
# 损失函数
creation = nn.BCELoss(reduction='mean')
# 优化器
optimizer = torch.optim.SGD(model.parameters(),lr=0.01)
size = len(dataset)

if __name__ == '__main__':
    # 训练
    epoch_list = []
    loss_list = []
    for epoch in range(10): # 模型轮数
        for i,data in enumerate(train_loader,0): # 每次通过dataloader加载一个mini—batch,i代表迭代次数=数据集的大小/batchsize,i从0开始计数
            # 1.准备数据
            inputs,labels = data    # 这一句可以在for循环里面将data直接改为(inputs,labels)这样就可以省略这一步
            # 2.前馈计算
            y_pred = model(inputs)
            # 3.计算损失
            loss = creation(y_pred,labels)
            print(epoch,i,loss.item())
            # 4.梯度清0
            optimizer.zero_grad()
            # 5.反馈计算
            loss.backward()
            # 6.梯度更新
            optimizer.step()
        epoch_list.append(epoch)
        loss_list.append(loss.item())

    plt.plot(epoch_list, loss_list)
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.show()

运行结果:
在这里插入图片描述
在这里插入图片描述

五、Pytorch内置数据集

在这里插入图片描述
在这里插入图片描述
关于直接加载MNIST数据集的一些参数:

root :数据集的保存路径
train:是否下载训练集,Train代表训练集,false代表测试集
transform:代表对数据集格式的转化,比如图像数据集的格式为PIL image,通过transform的ToTensor将其转变为Tensor类型。
download:是否需要下载,已经下载好了可以设置为false。

另外一点需要注意:对于训练数据集的dataloader,一般要打乱顺序,而对于测试集的dataloader,一般不需要打乱数据,因为我们要观察测试结果咋样。

六、课后练习

官网先下载数据集:下载到自己的项目文件夹下面即可
在这里插入图片描述

1.准备数据集

# 准备数据集
class TitanicDataset(Dataset):
    def __init__(self,filepath):
        xy = pd.read_csv(filepath)
        # 获取数据的总长度
        self.len = xy.shape[0]
        # 选取相关的数据特征
        feature = ["Pclass","Sex","SibSp","Parch","Fare"]
        # 数据变成张量
        self.x_data = torch.from_numpy(np.array(pd.get_dummies(xy[feature])))
        self.y_data = torch.from_numpy(np.array(xy["Survived"]))

    # getitem函数,可以使用索引来访问数据
    def __getitem__(self, item):
        return self.x_data[item],self.y_data[item]

    # 返回数据的条数/长度
    def __len__(self):
        return self.len

这里可以看一下选取的特征以及数据集是长什么样子的:

filepath = "D:\\Documents\\Desktop\\CHENXU\\Machine Learning\\PYTORCH_exercise\\train.csv"
xy = pd.read_csv(filepath)
# 获取数据的总长度
len = xy.shape[0]
# 选取相关的数据特征
feature = ["Pclass", "Sex", "SibSp", "Parch", "Fare"]
# 数据变成张量
a = pd.get_dummies(xy[feature])
print(a.head)
x_data = torch.from_numpy(np.array(pd.get_dummies(xy[feature])))
y_data = torch.from_numpy(np.array(xy["Survived"]))
print(x_data[0:5,:])
print(y_data[0:5])

在这里插入图片描述
同时在这里我们可以看到训练数据x是6维的。

这里使用到了pd.get_dummies进行独热编码,可以参考这篇博文进行理解【特征提取】pd.get_dummies() 详解(One-Hot Encoding)

2.定义模型

# 模型
class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.linear1 = nn.Linear(6,3)
        self.linear2 = nn.Linear(3,1)
        self.sigmoid = nn.Sigmoid()
    
    def forwar(self,x):
        x = self.sigmoid(self.linear1(x))
        x = self.sigmoid(self.linear2(x))
        return x
    
    # 测试模型
    def test(self,x):
        with torch.no_grad():
            x = self.sigmoid(self.linear1(x))
            x = self.sigmoid(self.linear2(x))
            y = []
            for i in x:
                if i>0.5:
                    y.append(1)
                else:
                    y.append(0)
            return y

# 实例化模型
model = Model()

3.优化器和损失函数

# 优化器和损失函数
creation = nn.BCELoss(reduction='mean')
optimizer = torch.optim.SGD(model.parameters(),lr=0.01)

4.训练模型

if __name__ == '__main__':
    for epoch in range(100):
        for i,(x,y) in enumerate(trainloader,0):
            x = x.float()
            y = y.float()
            y_pred = model(x)
            y_pred = y_pred.squeeze(-1)   # 之前我们查看过y的数据样式是一维的,所以我们需要将预测输出的y也变为一维,这样才能计算loss
            loss = creation(y_pred,y)
            print(epoch,i,loss.item())
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

5.测试模型

# 训练完成之后进行测试
test_data = pd.read_csv('test.csv')
feature = ["Pclass","Sex","SibSp","Parch","Fare"]
test=torch.from_numpy(np.array(pd.get_dummies(test_data[feature])))
test = test.float()
y = model.test(test)

# 输出预测结果
output = pd.DataFrame({'PassengerId':test_data.PassengerId,'Survived':y})
output.to_csv('my_predict.csv',index=False)

提交测试结果到kaggle上,查看结果:
在这里插入图片描述
在这里插入图片描述
第一次提交0.69,然后改变了训练轮数,让多训练了会,分数到了0.73,整个模型还有很大得改变空间,以及数据处理方面应该还有更优的方法,课后还应该接着探索更优的策略。

总结

本节主要学习了pytorch数据集的加载,关键在于怎么继承Dataset父类来自定义我们的子类,从而通过Dataloader加载我们的数据集。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值