分批训练会导致更多的过拟合
training by batches leads to more over-fitting
我正在训练一个序列到序列 (seq2seq) 模型,我有不同的值来训练 input_sequence_length
。
对于值 10
和 15
,我得到了可接受的结果,但是当我尝试使用 20
进行训练时,我得到 内存错误 所以我将训练切换为分批训练,但是模型 over-fit 和验证损失激增,即使有累积梯度,我也会得到相同的行为,所以我正在寻找提示并导致更准确的更新方法。
这是我的训练函数(只有批处理部分):
if batch_size is not None:
k=len(list(np.arange(0,(X_train_tensor_1.size()[0]//batch_size-1), batch_size )))
for epoch in range(num_epochs):
optimizer.zero_grad()
epoch_loss=0
for i in list(np.arange(0,(X_train_tensor_1.size()[0]//batch_size-1), batch_size )): # by using equidistant batch till the last one it becomes much faster than using the X.size()[0] directly
sequence = X_train_tensor[i:i+batch_size,:,:].reshape(-1, sequence_length, input_size).to(device)
labels = y_train_tensor[i:i+batch_size,:,:].reshape(-1, sequence_length, output_size).to(device)
# Forward pass
outputs = model(sequence)
loss = criterion(outputs, labels)
epoch_loss+=loss.item()
# Backward and optimize
loss.backward()
optimizer.step()
epoch_loss=epoch_loss/k
model.eval
validation_loss,_= evaluate(model,X_test_hard_tensor_1,y_test_hard_tensor_1)
model.train()
training_loss_log.append(epoch_loss)
print ('Epoch [{}/{}], Train MSELoss: {}, Validation : {} {}'.format(epoch+1, num_epochs,epoch_loss,validation_loss))
编辑:
这是我正在训练的参数:
batch_size = 1024
num_epochs = 25000
learning_rate = 10e-04
optimizer=torch.optim.Adam(model.parameters(), lr=learning_rate)
criterion = nn.MSELoss(reduction='mean')
批量大小影响正则化。一次训练一个样本非常嘈杂,这使得过拟合变得更加困难。批量训练可以平滑所有内容,从而更容易过度拟合。转换回正则化:
- 小批量添加正则化。
- 较大的批次会减少正则化。
我也很好奇你的学习率。每次调用 loss.backward()
都会累积梯度。如果您已将学习率设置为一次期望一个示例,而不是减少它以解决批量累积,那么将发生以下两种情况之一。
对于现在累积的梯度,学习率会过高,训练会发散,训练和验证错误都会爆炸。
学习率不会太高,不会发散。该模型只会更快更有效地训练。如果模型对于适合的数据来说太大,那么训练错误将变为 0,但验证错误将由于过度拟合而爆炸。
更新
这里有一些关于梯度累积的更多细节。
每次调用 loss.backward()
都会累积梯度,直到您使用 optimizer.zero_grad()
重置它。当您调用 optimizer.step()
时,它将根据它所积累的内容进行操作。
你的代码是这样写的,你每次通过内循环都会调用loss.backward()
,然后在重置之前在外循环中调用optimizer.step()
。所以梯度已经累积,也就是对批次中的所有样本进行求和,而不是一次只计算一个样本。
在大多数假设下,这将使批量累积梯度大于单个示例的梯度。如果梯度全部对齐,对于B批次,它会大B倍。如果梯度是 i.i.d. 那么它将更像是 sqrt(B)
倍大。
如果您没有考虑到这一点,那么您实际上已经通过该因素提高了学习率。其中一些将通过更大的批次的平滑效果来减轻,然后可以容忍更高的学习率。较大的批次减少正则化,较大的学习率将其加回来。但这并不是补偿的完美匹配,因此您仍然需要相应地进行调整。
一般来说,无论何时更改批量大小时,您还需要重新调整学习率以进行补偿。
Leslie N. Smith 撰写了一些关于超参数调整的有条理方法的优秀论文。一个很好的起点是 A disciplined approach to neural network hyper-parameters: Part 1 -- learning rate, batch size, momentum, and weight decay。他建议您从阅读图表开始,这些图表做得很好。
我正在训练一个序列到序列 (seq2seq) 模型,我有不同的值来训练 input_sequence_length
。
对于值 10
和 15
,我得到了可接受的结果,但是当我尝试使用 20
进行训练时,我得到 内存错误 所以我将训练切换为分批训练,但是模型 over-fit 和验证损失激增,即使有累积梯度,我也会得到相同的行为,所以我正在寻找提示并导致更准确的更新方法。
这是我的训练函数(只有批处理部分):
if batch_size is not None:
k=len(list(np.arange(0,(X_train_tensor_1.size()[0]//batch_size-1), batch_size )))
for epoch in range(num_epochs):
optimizer.zero_grad()
epoch_loss=0
for i in list(np.arange(0,(X_train_tensor_1.size()[0]//batch_size-1), batch_size )): # by using equidistant batch till the last one it becomes much faster than using the X.size()[0] directly
sequence = X_train_tensor[i:i+batch_size,:,:].reshape(-1, sequence_length, input_size).to(device)
labels = y_train_tensor[i:i+batch_size,:,:].reshape(-1, sequence_length, output_size).to(device)
# Forward pass
outputs = model(sequence)
loss = criterion(outputs, labels)
epoch_loss+=loss.item()
# Backward and optimize
loss.backward()
optimizer.step()
epoch_loss=epoch_loss/k
model.eval
validation_loss,_= evaluate(model,X_test_hard_tensor_1,y_test_hard_tensor_1)
model.train()
training_loss_log.append(epoch_loss)
print ('Epoch [{}/{}], Train MSELoss: {}, Validation : {} {}'.format(epoch+1, num_epochs,epoch_loss,validation_loss))
编辑: 这是我正在训练的参数:
batch_size = 1024
num_epochs = 25000
learning_rate = 10e-04
optimizer=torch.optim.Adam(model.parameters(), lr=learning_rate)
criterion = nn.MSELoss(reduction='mean')
批量大小影响正则化。一次训练一个样本非常嘈杂,这使得过拟合变得更加困难。批量训练可以平滑所有内容,从而更容易过度拟合。转换回正则化:
- 小批量添加正则化。
- 较大的批次会减少正则化。
我也很好奇你的学习率。每次调用 loss.backward()
都会累积梯度。如果您已将学习率设置为一次期望一个示例,而不是减少它以解决批量累积,那么将发生以下两种情况之一。
对于现在累积的梯度,学习率会过高,训练会发散,训练和验证错误都会爆炸。
学习率不会太高,不会发散。该模型只会更快更有效地训练。如果模型对于适合的数据来说太大,那么训练错误将变为 0,但验证错误将由于过度拟合而爆炸。
更新
这里有一些关于梯度累积的更多细节。
每次调用 loss.backward()
都会累积梯度,直到您使用 optimizer.zero_grad()
重置它。当您调用 optimizer.step()
时,它将根据它所积累的内容进行操作。
你的代码是这样写的,你每次通过内循环都会调用loss.backward()
,然后在重置之前在外循环中调用optimizer.step()
。所以梯度已经累积,也就是对批次中的所有样本进行求和,而不是一次只计算一个样本。
在大多数假设下,这将使批量累积梯度大于单个示例的梯度。如果梯度全部对齐,对于B批次,它会大B倍。如果梯度是 i.i.d. 那么它将更像是 sqrt(B)
倍大。
如果您没有考虑到这一点,那么您实际上已经通过该因素提高了学习率。其中一些将通过更大的批次的平滑效果来减轻,然后可以容忍更高的学习率。较大的批次减少正则化,较大的学习率将其加回来。但这并不是补偿的完美匹配,因此您仍然需要相应地进行调整。
一般来说,无论何时更改批量大小时,您还需要重新调整学习率以进行补偿。
Leslie N. Smith 撰写了一些关于超参数调整的有条理方法的优秀论文。一个很好的起点是 A disciplined approach to neural network hyper-parameters: Part 1 -- learning rate, batch size, momentum, and weight decay。他建议您从阅读图表开始,这些图表做得很好。