dataset:构造数据集(数据集应该支持索引,能够用下标操作快速把数据拿出来) dataloader :主要目标用来拿出一个mini-batch来供训练时快速使用。
我们之前学过在进行梯度下降时,有两种选择:
①全部的数据都用(Batch)
②随机梯度下降:只用一个样本
只用一个样本可以得到比较好的随机性,可以帮助我们跨越在优化中所遇到的鞍点,而用Batch(所有数据)的优点是可以最大化地利用向量计算的优势提升计算速度。
都用一个样本的随机梯度下降训练出的模型效果可能会比其他模型都更好,但是会导致优化用的时间更长,因为每次一个样本没法使用cpu或gpu的并行能力,训练的时间会很长,而使用Batch计算速度快,但是在求得性能上会遇到一些问题,所以在深度学习中我们使用Mini-Batch来平衡训练时间和训练速度上的要求
使用Mini-Batch之后训练循环要写成嵌套循环
外层是循环的次数,循环一次是一个epoch;每一次epoch中执行一次内层;内层每循环一次,执行一次Mini-batch。
Epoch:所有的训练样本都进行了一次前向传播和反向传播的过程。
Batch-Size:每一个Mini_batch训练时所用的样本数量。
iterations:内层的迭代一共执行了多少次,即:total_batch中执行了多少次Mini_batch
shuffle:为了提高训练样本的随机性,设置为True可以随机打乱dataset,这样每一次生成的MiNi-batch数据集数据样本都是随机的。
dataset 需要支持索引,需要知道Dataset长度,这样DataLoader就可以对Dataset进行自动的小批量的数据集的生成。
自己构建的数据集
Dataset 是个抽象类,不能实例化,只能被其他子类继承,所以我们将来要想定义Dataset,我们必须要由Dataset来继承,构造一个我们自己的自定义的类。
DataLoader 这个类用来加载数据,自动完成shuffle,batch-size
构造数据集:
有两种方法构造数据集,第一种在init中把所有的数据都读到内存中,然后每次使用getitem时就把其中第i个样本传出去,适用于样本不大的情况。第二种,如果读取的是较大(10g)图像数据集,在init中把数据都读进来不可能,我们就在init中定义一个列表,每一条数据的文件名放在列表中,标签读到内存中(输出是简单的分类回归数值)或文件名放在列表里,然后getitem读取第i个文件,那x,y的第i个元素去读出来,然后返回,来保证内存的高效使用。(读取文件名,根据文件名加载文件)
import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
class XXXDataset(Dataset): # XXXDataset继承自Dataset
def __init__(self):
pass
#将来实例化这个类之后,这个对象能够支持下标操作,可以通过一个索引,
#把里面的dataset[index]的第index条数据给拿出来
def __getitem__(self,index):
pass
# magic function ,把整个数据的数量取出来
def __len__(self):
pass
#用自定义的类把它实例化一个数据对象dataset,
#这个dataset最重要的功能是getitem()和len()
dataset = XXXDataset()
train_loader = DataLoader(dataset=dataset,batch_size=32,shuffle=True,num_workers=2)
# num_workers:要几个多线程并行读取数据
应用实例:
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,index):
return self.x_data[index],self.y_data[index]
def __len__(self):
return self.len
dataset = DiabetesDataset('diabetes.csv.gz')
train_loader = DataLoader(dataset=dataset,batch_size=32,shuffle=True,num_workers=2)
# num_workers=2表示使用2个并行进程来读取数据,Cpu核心数较多的话,可以加高
#(并行化可以提高读取效率)
其中
self.len = xy.shape[0]
表示 x,y N行9列,N是数据样本的数量,shape是(N,9)元组,通过取第0个元素,把N的值给取出来,这样就知道数据集有多少个了
加载已有的数据集
以MNIST为例:
import torch
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision import datasets
train_dataset = datasets.MNIST(root='../dataset/mnist',train=True,transform=transforms.ToTensor(),download=True)
test_dataset = datasets.MNIST(root='../dataset/mnist',train=False,transform=transforms.ToTensor(),download=True)
train_loader = DataLoader(dataset=train_dataset,batch_size=32,shuffle=True)
test_loader = DataLoader(dataset=test_dataset,batch_size=32,shuffle=False)
for batch_index,(inputs,target) in enumerate(train_loader):
...
DataLoader是pytorch提供的加载器,初始化要设置:dataset=,bbatch-size=,shufflle=,
num—_workers=(超线程,win直接使用会报错,用if main语句包起来即可)
多进程的库不一样,在Windows使用spawn代替fork
完整代码:
import numpy as np
import torch
from torch.utils.data import Dataset,DataLoader
#准备数据集
class DiabetesDataset(Dataset):
def __init__(self,filepath): #filepath 表示数据来自什么地方
# np.loadtxt为读取文本文档的函数
xy = np.loadtxt(filepath,delimiter=',',dtype=np.float32)
#shape为(N,9)元组,取出N的值
self.len = xy.shape[0]
# 第一个‘:’是指读取所有行,第二个‘:’是指从第一列开始,最后一列不要
self.x_data = torch.from_numpy(xy[:, :-1])
# 要最后一列,且最后得到的是个矩阵,所以要加[]
self.y_data = torch.from_numpy(xy[:, [-1]])
# 把里面的x_data[index],y_data[index]的第index条数据给拿出来
def __getitem__(self, index):
return self.x_data[index], self.y_data[index]
# 把整个数据的数量取出来,返回数据集的数据条数
def __len__(self):
return self.len
dataset = DiabetesDataset('diabetes.csv.gz')#diabetes.csv.gz数据路径
#用 DataLoader 构造了一个加载器
train_loader = DataLoader(dataset = dataset,batch_size=32,shuffle=True,num_workers=2)
#设计模型
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.linearl1 = torch.nn.Linear(8, 6)
self.linearl2 = torch.nn.Linear(6, 4)
self.linearl3 = torch.nn.Linear(4, 1)
self.sigmoid = torch.nn.Sigmoid()
def forward(self, x):
x = self.sigmoid(self.linearl1(x))
x = self.sigmoid(self.linearl2(x))
x = self.sigmoid(self.linearl3(x))
return x
model = Model()
#构造损失函数和优化器
criterion = torch.nn.BCELoss(reduction='mean')
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
#训练
if __name__ == '__main__':
for epoch in range(100):
# 对train_loader做迭代,用 enumerate是为了获得当前是第几次迭代
# 把从train_loader拿出来的(x,y)元组放到data里面
for i ,data in enumerate(train_loader,0):
# 在训练之前把x,y从data里面拿出来,inputs=x,labels=y,
# 此时inputs,labels都已经被自动转换为张量(tensor)
inputs, labels = data
#Forward
y_pred = model(inputs)
loss = criterion(y_pred, labels)
print(epoch, i, loss.item())
# backward
optimizer.zero_grad()
loss.backward()
# update
optimizer.step()