【PyTorch基础教程8】dataset和dataloader(学不会来打我啊)
学习心得
(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
的读取可以使用next
和iter
来完成。
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_data
和self.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
- 点赞
- 收藏
- 关注作者
评论(0)