UP:B站-刘二大人
原视频链接:08.加载数据集_哔哩哔哩_bilibili
'''
随机梯度下降可以有效解决鞍点问题,但是每次训练一个样本,没办法利用GPU或者CPU的并行计算能力,所以计算速度慢,计算时间长
整个Batch一起进行梯度下降计算,虽然计算速度很快,但是没办法有效解决鞍点问题,性能会差一些
所以在这里提出mini-batch来均衡性能和时间的需求
'''
'''
使用mini-batch训练时候是嵌套循环
for epoch in range(training_epochs): # 外层表示训练的周期
for i in range(total_batch): # 内层对batch进行迭代
'''
# epoch:所有样本进行一次训练是一个epoch;
# batch—size:每次训练时候用的样本数量;
# Iteration: 样本总数/batch-size
'''
1,获取一个batch数据的步骤
(假定数据集的特征和标签分别表示为张量X和Y,数据集可以表示为(X,Y), 假定batch大小为m)
1,首先我们要确定数据集的长度n。
结果类似:n = 1000。
2,然后我们从0到n-1的范围中抽样出m个数(batch大小)。
假定m=4, 拿到的结果是一个列表,类似:indices = [1,4,8,9]
3,接着我们从数据集中去取这m个数对应下标的元素。
拿到的结果是一个元组列表,类似:samples = [(X[1],Y[1]),(X[4],Y[4]),(X[8],Y[8]),(X[9],Y[9])]
4,最后我们将结果整理成两个张量作为输出。
拿到的结果是两个张量,类似batch = (features,labels),
其中 features = torch.stack([X[1],X[4],X[8],X[9]])
labels = torch.stack([Y[1],Y[4],Y[8],Y[9]])
上述第1个步骤确定数据集的长度是由 Dataset的__len__ 方法实现的。
第2个步骤从0到n-1的范围中抽样出m个数的方法是由 DataLoader的 sampler和batch_sampler参数指定的。
sampler参数指定单个元素抽样方法,一般无需用户设置,程序默认在DataLoader的参数shuffle=True时采用随机抽样,shuffle=False时采用顺序抽样。
batch_sampler参数将多个抽样的元素整理成一个列表,一般无需用户设置,默认(False)方法在DataLoader的参数drop_last=True时会丢弃数据集最后一个长度不能被batch大小整除的批次,在drop_last=False时保留最后一个批次。
第3个步骤的核心逻辑根据下标取数据集中的元素,是由 Dataset的 __getitem__方法实现的。
第4个步骤的逻辑由DataLoader的参数collate_fn指定。一般情况下也无需用户设置。
'''
import torch
from torch.utils.data import Dataset # Dataset是一个抽象类,不可以直接实例化,需要自己定义子类去继承Dataset
from torch.utils.data import DataLoader # DataLoader是一个可以进行实例化的类
import numpy as np
import matplotlib.pyplot as plt
class DiabetesDataset(Dataset): # DiabetesDataset作为子类去继承Dataset
'''
__init__()是初始化函数,之后我们可以提供数据集路径进行数据的加载
__getitem__()帮助我们通过索引找到某个样本
__len__()帮助我们返回数据集大小
'''
def __init__(self, filepath):
xy = np.loadtxt(filepath, delimiter=",", dtype=np.float32)
self.len = xy.shape[0] # 糖尿病这个数据集是N行,8列指标加1列评价共9列,xy.shape就是[N, 9],所以这步操作实际是得到样本的数量N
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] # python中return x, y 返回的是(x, y)这样一个元组
def __len__(self): # 使用len()函数可以把数据的长度返回
return self.len
dataset = DiabetesDataset("diabetes.csv")
# 四个参数:第一个是数据集, 第二个是每个mini-batch的容量, 第三个是是否需要打乱,第四个num_workers在读入数据时是不是要多线程读取,设置成2就是双线并行
train_loader = DataLoader(dataset = dataset, batch_size=32, shuffle=True) # DataLoader每次会读出一个batch_size
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()
self.active = torch.nn.ReLU()
def forward(self, x):
x = self.active(self.linear1(x))
x = self.active(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)
loss_list = []
epoch_list = []
# 使用多线程时候可能会遇到报错,解决报错的方法就是把训练过程封装到函数里面,即:加下面这行代码
if __name__ == '__main__':
for epoch in range(100):
loss_minibatch_sum = 0
for i, (inputs, labels) in enumerate(train_loader, 0):
y_pred = model(inputs)
loss = criterion(y_pred, labels)
print("epoch:", epoch, "loss:", loss.data.item())
loss_minibatch_sum += loss.item()
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_list.append(loss_minibatch_sum/train_loader.batch_size)
loss_minibatch_sum = 0
epoch_list.append(epoch) # 绘图用
plt.plot(epoch_list, loss_list)
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()