在前面的内容中,在训练的时候我们使用的都是全部的数据进行训练,这种在时间上比较好,但是性能上会遇到一些问题,比如鞍点;此外,我们还知道一种随机梯度下降,这种训练是每次取一个数据去计算梯度,这种方法能够有效的解决鞍点的问题,而且性能相对较好,但是每次一个点时间消耗多,而且不能利用CPU/GPU的并行计算能力;把全部的梯度下降和随机梯度下降进行一个折中(时间和性能进行折中),得出了批量梯度下降,也就是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时需要两个基本参数:
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(): 返回数据集的长度
在构造数据集的时候需要注意有两点:
关于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()
root :数据集的保存路径
train:是否下载训练集,Train代表训练集,false代表测试集
transform:代表对数据集格式的转化,比如图像数据集的格式为PIL image,通过transform的ToTensor将其转变为Tensor类型。
download:是否需要下载,已经下载好了可以设置为false。
另外一点需要注意:对于训练数据集的dataloader,一般要打乱顺序,而对于测试集的dataloader,一般不需要打乱数据,因为我们要观察测试结果咋样。
# 准备数据集
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])
这里使用到了pd.get_dummies进行独热编码,可以参考这篇博文进行理解【特征提取】pd.get_dummies() 详解(One-Hot Encoding)
# 模型
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()
# 优化器和损失函数
creation = nn.BCELoss(reduction='mean')
optimizer = torch.optim.SGD(model.parameters(),lr=0.01)
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()
# 训练完成之后进行测试
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加载我们的数据集。