分批训练会导致更多的过拟合

training by batches leads to more over-fitting

我正在训练一个序列到序列 (seq2seq) 模型,我有不同的值来训练 input_sequence_length

对于值 1015,我得到了可接受的结果,但是当我尝试使用 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() 都会累积梯度。如果您已将学习率设置为一次期望一个示例,而不是减少它以解决批量累积,那么将发生以下两种情况之一。

  1. 对于现在累积的梯度,学习率会过高,训练会发散,训练和验证错误都会爆炸。

  2. 学习率不会太高,不会发散。该模型只会更快更有效地训练。如果模型对于适合的数据来说太大,那么训练错误将变为 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。他建议您从阅读图表开始,这些图表做得很好。