目录

mini-batch梯度下降

随机梯度下降


在上一篇(拼拼凑凑的pytorch学习——神经网络训练)中我们说到过,pytorch中SGD优化器会使用全部传入的数据来计算梯度,所以如果传入了所有数据,那么就是相当于批量梯度下降,那么如果实现mini-batch梯度下降以及随机梯度下降呢?可以从数据供给的角度去考虑。

这里仍旧使用上一篇中的例子

mini-batch梯度下降

mini-batch梯度下降,就是将数据分为多个批次,每次投入一批数据进行训练,所有的数据全部训练过一遍后为一个epoch

pytorch的utils模块中提供了很多帮助训练神经网络的工具,其中的data模块专门用来处理数据

为了实现mini-batch梯度下降,需要自己构建一个Dataloader实例,这个类可以实现自定义的数据提供方式

dataset需要传入TensorDataset类的实例,用上之前的数据构建一个即可,batch_size规定每批次的大小,shuffle代表数据输入顺序是否随机

import torch.utils.data as data

batch_size = 64
loader = data.DataLoader(
    dataset=data.TensorDataset(x_train, y_train),
    batch_size=batch_size,
    shuffle=True
)

在定义好DataLoader之后,只需要在训练步骤中稍稍修改一下

epochs = 100000
for epoch in range(epochs):

    for step, (batch_x, batch_y) in enumerate(loader):
        #  计算输出
        output = net(batch_x)
        #  计算损失值
        loss = criterion(output, batch_y)
        #  清零梯度缓存
        optimizer.zero_grad()
        # 计算梯度
        loss.backward()
        #  更新参数
        optimizer.step()

    	print(epoch, 'times loss:', loss)

上面相比于原来的代码加了一个循环,并且把x_train和y_train改成了batch_x和batch_y,因为每次训练的时候只使用了当前批次的数据

注意:这里计算出来的损失值也只是相对于当前批次的数据,如果需要计算所有数据在当前参数下的损失值,需要额外的计算

output2 = net(x_train)
loss2 = criterion(output2, y_train)
print(data_size/batch_size*epoch+step, 'times loss:', loss2)

随机梯度下降

至于随机梯度下降的话,一种实现方式就是把上面的代码batch_size改成1,这样每次都只使用一个数据计算梯度的依据。

不过这样实现仍然不是完全的随机,因为dataloader会保证每个数据都出现一遍,为了实现一种完全随机的随机梯度下降(即每次随机取一个数据进行计算),可以使用自定义Dateset类的方式

# 自定义Dataset类
class CustomDataSet(data.Dataset):

    def __init__(self, x_train, y_train):
        self.x_train = x_train
        self.y_train = y_train
        self.size = len(x_train)

    def __getitem__(self, item):
        i = random.randint(0, self.size-1)
        # print("使用的数据为"+str(self.x_train[i]), str(self.y_train[i]))
        return self.x_train[i], self.y_train[i]

    def __len__(self):
        return self.size

这里最重要地方是重写__getitem__这个方法,每次对dataset进行索引读取数据的时候,这里采用了随机提供的方式

其余的代码也稍作修改,注意batch_size数值改为1

batch_size = 1
loader = data.DataLoader(
    dataset=CustomDataSet(x_train, y_train),
    batch_size=batch_size,
    shuffle=True
)

epochs = 1000
for epoch in range(epochs):

    for step, (batch_x, batch_y) in enumerate(loader):
        #  计算输出
        output = net(batch_x)
        #  计算损失值
        loss = criterion(output, batch_y)
        #  清零梯度缓存
        optimizer.zero_grad()
        # 计算梯度
        loss.backward()
        #  更新参数
        optimizer.step()

        output2 = net(x_train)
        loss2 = criterion(output2, y_train)
        print(data_size / batch_size * epoch + step, 'times loss:', loss2)