【PyTorch基础教程8】dataset和dataloader(学不会来打我啊)

举报
野猪佩奇996 发表于 2022/01/23 01:34:13 2022/01/23
【摘要】 学习心得 (1)mini-batch:外层for为训练周期,内层for迭代mini-batch。 1)epoch:将所有的训练样本都进行了一次前向传递和反向传播,是一个epoch。 2)Batch-si...

学习心得

(1)mini-batch:外层for为训练周期,内层for迭代mini-batch。
1)epoch:将所有的训练样本都进行了一次前向传递和反向传播,是一个epoch
2)Batch-size:每次训练所用的样本数量
3)Iteration:内层for执行的次数。
ex:共有10000个样本,batch-size=1000,即每次拿1000个样本进行训练,则Iteration=10次。
(2)linux和win系统的多进程库是不同的:在win中是用spawn替代linux的fork(创建一个新进程)。所以要将把loader进行迭代的代码进行封装起来(如if语句或者函数里面,即不能直接写在外面), 也可以直接外面加上if __name__ == '__main__':

零、训练DNN的过程

在这里插入图片描述
使用torch.nn创建神经网络nn包会使用autograd包定义模型和求梯度。一个nn.Module对象包括了许多网络层,并且用forward(input)方法来计算损失值,返回output。
训练一个神经网络通畅需要以下步骤:

  • 定义一个神经网络,通常有一些可以训练的参数
  • 迭代一个数据集(Dataset)
  • 处理网络的输入
  • 计算损失(会调用Module对象的forward()方法)
  • 计算损失函数对参数的梯度
  • 更新参数,通常使用如下的梯度下降方法来更新:weight=weight-learning_rate × gradien

一、读入数据

1.1 参数解读

PyTorch数据读入是通过Dataset+Dataloader的方式完成的。

Dataset定义好数据的格式和数据变换形式,用来构造数据集。从而数据集支持【索引】,通过下标将样本取出
Dataloader用iterative的方式不断读入批次数据。Dataloader用来拿出一个mini-batch。

我们可以定义自己的Dataset类来实现灵活的数据读取,定义的类需要继承PyTorch自身的Dataset类。主要包含三个函数:

  • __init__: 用于向类中传入外部参数,同时定义样本集
  • __getitem__: 用于逐个读取样本集合中的元素,可以进行一定的变换,并将返回训练/验证所需的数据
  • __len__: 用于返回数据集的样本数

1.2 代码栗子

下面以cifar10数据集为例给出构建Dataset类的方式:

train_data = datasets.ImageFolder(train_path, 
								  transform=data_transform)
val_data = datasets.ImageFolder(val_path, 
								transform=data_transform)

  
 
  • 1
  • 2
  • 3
  • 4

这里使用了PyTorch自带的ImageFolder类的用于读取按一定结构存储的图片数据(path对应图片存放的目录,目录下包含若干子目录,每个子目录对应一个类的图片)。

其中data_transform可以对图像进行一定的变换,如翻转、裁剪等操作,可自己定义。

另一个例子:其中图片存放在一个文件夹,另外有一个csv文件给出了图片名称对应的标签。这种情况下需要自己来定义Dataset类:

class MyDataset(Dataset):
    def __init__(self, data_dir, info_csv, image_list, transform=None):
        """
        Args:
            data_dir: path to image directory.
            info_csv: path to the csv file containing image indexes
                with corresponding labels.
            image_list: path to the txt file contains image names to training/validation set
            transform: optional transform to be applied on a sample.
        """
        label_info = pd.read_csv(info_csv)
        image_file = open(image_list).readlines()
        self.data_dir = data_dir
        self.image_file = image_file
        self.label_info = label_info
        self.transform = transform

    def __getitem__(self, index):
        """
        Args:
            index: the index of item
        Returns:
            image and its labels
        """
        image_name = self.image_file[index].strip('\n')
        raw_label = self.label_info.loc[self.label_info['Image_index'] == image_name]
        label = raw_label.iloc[:,0]
        image_name = os.path.join(self.data_dir, image_name)
        image = Image.open(image_name).convert('RGB')
        if self.transform is not None:
            image = self.transform(image)
        return image, label

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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

构建好Datadet后,就可以使用DataLoader来按批次读入数据了,实现代码如下:

train_loader = torch.utils.data.DataLoader( train_data, 
											batch_size=batch_size, 
											num_workers=4, 
											shuffle=True, 
											drop_last=True)
val_loader = torch.utils.data.DataLoader(val_data, 
										 batch_size=batch_size, 
										 num_workers=4, 
										 shuffle=False)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

其中:

  • batch_size:样本是按“批”读入的,batch_size就是每次读入的样本数
  • num_workers:有多少个进程用于读取数据(在构成mini-batch的过程中)
  • shuffle:是否将读入的数据打乱
  • drop_last:对于样本最后一部分没有达到批次数的样本,不再参与训练

看加载的数据。PyTorch中的DataLoader的读取可以使用nextiter来完成。

import matplotlib.pyplot as plt
images, labels = next(iter(val_loader))
print(images.shape)
plt.imshow(images[0].transpose(1,2,0))
plt.show()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

二、Epoch,Batch-size,Iterations

外层for为训练周期,内层for迭代mini-batch。

(1)epoch:将所有的训练样本都进行了一次前向传递和反向传播,是一个epoch
(2)Batch-size:每次训练所用的样本数量
(3)Iteration:内层for执行的次数。

ex:共有10000个样本,batch-size=1000,即每次拿1000个样本进行训练,则Iteration=10次。
在这里插入图片描述
加载数据集的两种方法:
(1)all data:适合数据集比较小的
(2)大数据类似几十G的图像数据或语音等无结构数据:加载部分。

linux和win系统的多进程库是不同的:在win中是用spawn替代linux的fork(创建一个新进程)。所以要将把loader进行迭代的代码进行封装起来(如if语句或者函数里面,即不能直接写在外面), 也可以直接外面加上if __name__ == '__main__':

三、糖尿病数据集处理

(1)因为该数据集较小,所以将所有的数据和标签都加载到self.x_dataself.y_data(在内存中)中了。 在__getitem__函数内当传参(x, y)时,别看形式参数好像只有index,但其实返回的是元组(x, y)(回顾之前的*args)。

(2)为了获得当前是第几次迭代,可以用enumerate,这样就可以获得下标i即当前的迭代次数。在训练的内循环中,每次拿出的data是一个元组(x, y)。而dataloader会自动将多个data组合成Tensor矩阵(所以不需要我们自己组成tensor)。

(3)训练过程和之前类似,只是为了使用mini-batch方式训练,嵌套了内层的for循环。
在测试中的dataloader一般设置shuffle = False即不用shuffle(洗不洗影响不大)。

# -*- coding: utf-8 -*-
"""
Created on Mon Oct 18 14:06:02 2021

@author: 86493
"""
import numpy as np
import torch
import torch.nn as nn
# Dataset是抽象类(不能实例化,只能被子类继承)
from torch.utils.data import Dataset 
# dataloader做shuffle、batchsize等,可实例
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

losslst = []

# 构建自己的数据集类
class DiabetesDataset(Dataset):
    
    def __init__(self, filepath):
        # 一般类型用np.float32位,而不用double
        xy = np.loadtxt(filepath, 
                        delimiter = ' ',
                        dtype = np.float32)
        self.len = xy.shape[0]
        # 最后一列不要,即要前9列,不要最后的一列
        self.x_data = torch.from_numpy(xy[:, :-1])
        # [-1]则拿出来的是一个矩阵,去了中括号则拿出向量
        self.y_data = torch.from_numpy(xy[:, [-1]])
                
        
    # magic function
    # 后面可以用dataset[index]
    def __getitem__(self, index): 
        return self.x_data[index], self.y_data[index]
    
    # 返回数据集的length,即数据条数
    def __len__(self): 
        return self.len 

dataset = DiabetesDataset('diabetes.csv')
"""
train_loader = DataLoader(dataset = dataset,
                          batch_size = 4,
                          shuffle = True,
                          num_workers = 1)
"""
train_loader = DataLoader(dataset = dataset,
                          batch_size = 32,
                          shuffle = True)

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.linear1 = nn.Linear(9, 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()
# 使用交叉熵
# criterion = nn.BCELoss(size_average = True)
criterion = nn.BCELoss(reduction = 'mean')
optimizer = torch.optim.SGD(model.parameters(),
                            lr = 0.01)


# batch处理
if __name__ == '__main__':
    for epoch in range(50):
        # 内层循环执行一个mini-batch
        for i, data in enumerate(train_loader, 0):
        # 也可以写成for i, (input, labels) in enumerate(train_loader, 0):
            
            # 1.准备数据
            inputs, labels = data 
        
            # 2.向前传递
            y_predict = model(inputs)
            loss = criterion(y_predict, labels)
            losslst.append(loss.item())
            print(epoch, i, loss.item())
        
            # 3.反向传播
            optimizer.zero_grad()
            loss.backward()
        
            # 4.更新参数
            optimizer.step()
            
# 画图
plt.plot(range(700), losslst)
plt.xlabel("epoch")
plt.ylabel("Loss")
plt.show()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102

在这里插入图片描述
可以看到Loss图像后面是震荡地下降的,后面可以调整学习速率。

四、作业

(1)使用torchvision.datasets,其中也有很多数据集,如MNIST、Fashion-MNIST、EMNIST、COCO、LSUN、ImageFolder、DatasetFolder、Imagenet-12、CIFAR、STL10、PhotoTour等数据集。
在这里插入图片描述
(2)Build DataLoader for:
• Titanic dataset: https://www.kaggle.com/c/titanic/data
• Build a classifier using the DataLoader

Reference

(1)pytorch: RuntimeError: DataLoader worker (pid(s) 27292) exited unexpectedly
(2)datawhale notebook

文章来源: andyguo.blog.csdn.net,作者:山顶夕景,版权归原作者所有,如需转载,请联系作者。

原文链接:andyguo.blog.csdn.net/article/details/120794077

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。