初学者学习Pytorch系列
第一篇 Pytorch初学简单的线性模型 代码实操
第二篇 Pytorch实现逻辑斯蒂回归模型 代码实操
第三篇 Pytorch实现多特征输入的分类模型 代码实操
第四篇 Pytorch实现Dataset数据集导入 必要性解释及代码实操
文章目录
前言
- 本文在第三篇文章的数据基础上,加上Dataset的数据导入功能。并阐述了为什么使用Dataset及代码实操。
一、先上代码
代码如下(解释已经写在代码中):
import numpy as np
import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
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("../data/diabetes.csv.gz")
train_loader = DataLoader(dataset=dataset, shuffle=True, batch_size=32, num_workers=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))
x = self.sigmoid(self.linear3(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):
for i, data in enumerate(train_loader, 0):
inputs, labels = data # 这里获取就是我们定义的getitem函数
y_pred = model(inputs)
loss = criterion(y_pred, labels)
print(epoch, i, loss.item())
optimizer.zero_grad()
loss.backward()
optimizer.step()
二、必要性解释
首先,要认识到深度学习使用到的三种基本的样本学习方式
1. 批量梯度下降(Batch Gradient Descent)
此方式是在每一个epoch的学习中,使用所有的样本进行计算loss,求平均值进行更新。
优点:可以使用电脑CPU或GPU的并行计算能力,速度较快
缺点:训练出的模型性能可能没有随机梯度下降训练出来的好
2. 随机梯度下降(Stochastic Gradient Descent)
此方式是在每一个epoch的学习中,在对每一个样本计算完loss的时候,就进行参数的迭代更新。
优点:训练出的模型性能比较好,可以跨越训练中的鞍点(函数到达鞍点处,梯度为零,可能一直处在鞍点附近,没有到达最优点)
缺点:训练速度较慢,因为每一次只有一个样本
3. 小批量梯度下降(Mini-batch Gradient Descent)
此方式是在每一个epoch的学习中,对本一定数量的样本计算loss,进行参数的迭代更新。即epoch中多次迭代,但是迭代次数没随机梯度下降多。
优点:能更好地兼顾随机梯度下降和梯度下降的优点
三、代码编写
- 首先我们要从torch.utils.data导入Dataset、DataLoader这两个类,Dataset是一个抽象类,需要作为父类被继承。
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类,需要编写以上三个魔法方法,以便后面配合DataLoader的使用。
- 实现DataLoader类
train_loader = DataLoader(dataset=dataset, shuffle=True, batch_size=32, num_workers=2)
在使用DataLoader类的时候,需要传入我们已经实现的dataset类,作为输入。
- shuffle表示是否在每一轮epoch训练中,要不要对训练集进行随机排列打乱==(不打乱的情况下,模型学到的可能只是数据次序,并未学到有用的信息,比如数据进行了排序,可能会导致模型在分类问题中,一会过拟合这个类别,一会过拟合那个)==
- batch_size代表mini-batch的大小,为自定义数值
- num_workers是加载数据(batch)的线程数目(可参考下面文章)
- num_workers线程
-
文中在epoch循环前加上了if name == ‘main’:
这是因为window和Linux系统多进程的不同,DataLoader在window中运行需要包裹起来,所以使用上面的语句。 -
下面的代码变化不大,新手对python不熟悉,可能会对for i, data in enumerate(train_loader, 0): 这个循环不太理解,其实i代表就是轮数,这是python枚举的语法
四、疑问
小批量体现在哪?
我们在代码中让每一个epoch中,每一个DataLoader训练输出loss损失。
print(epoch, i, loss.item())
结果如下:
0 0 0.6875641345977783
0 1 0.6793625950813293
0 2 0.6869551539421082
0 3 0.691533625125885
0 4 0.6870102286338806
0 5 0.6823495626449585
0 6 0.6845288872718811
0 7 0.6837712526321411
0 8 0.681125819683075
0 9 0.6887457370758057
0 10 0.683203399181366
0 11 0.6830952167510986
0 12 0.6772065162658691
0 13 0.6679650545120239
0 14 0.6759853363037109
0 15 0.6785972118377686
0 16 0.6714340448379517
0 17 0.6844414472579956
0 18 0.6739678382873535
0 19 0.6733191013336182
0 20 0.6691502928733826
0 21 0.6798030734062195
0 22 0.6758182644844055
0 23 0.6541876792907715
1 0 0.6665661334991455
1 1 0.6571947932243347
1 2 0.6955416202545166
1 3 0.6735390424728394
1 4 0.6644106507301331
1 5 0.6822395324707031
1 6 0.6772632002830505
1 7 0.663568377494812
1 8 0.662426233291626
1 9 0.6763331890106201
1 10 0.6614290475845337
1 11 0.6709282398223877
1 12 0.6913875937461853
1 13 0.6553581357002258
1 14 0.6650232076644897
1 15 0.6750929951667786
1 16 0.6478442549705505
1 17 0.6746488809585571
1 18 0.6802167296409607
1 19 0.6801486611366272
1 20 0.6685871481895447
1 21 0.6740914583206177
1 22 0.6854969263076782
1 23 0.6770240068435669
是不是觉得为什么只有23轮?我们不是在DataLoader中设置了32轮吗?
这是因为我们使用小批量梯度下降,DataLoader已经帮我们分好组了,打开我们的数据集会发现有759条数据,batch_size为32,759/32=23.7,所以从0-23,有24个分组,每组DataLoader都帮我们分好了,它内部是用一组数据去计算的。
总结
以上就是今天要讲的内容,介绍了pytorch实现Dataset。讲解过程属于个人理解,如表述有误,请谅解。