在上一篇(拼拼凑凑的pytorch学习——神经网络训练)中我们说到过,pytorch中SGD优化器会使用全部传入的数据来计算梯度,所以如果传入了所有数据,那么就是相当于批量梯度下降,那么如果实现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)