重组代码以避免训练循环中的循环?

Restructure code to avoid for loops in training loop?

我正在定义一个训练函数,我传入一个 data_loader 作为字典。

我创建了一个循环,循环遍历我所处的阶段(train 或 val),并相应地将模型设置为 model.train() 或 model.eval()。但是我觉得我这里有太多嵌套的 for 循环,这使得它在计算上很昂贵。谁能推荐一种更好的方法来构建我的火车功能?我应该创建一个单独的函数来进行验证吗?

以下是我目前所拥有的:

#Make train function (simple at first)
def train_network(model, optimizer, data_loader, no_epochs):

  total_epochs = notebook.tqdm(range(no_epochs))

  for epoch in total_epochs:
    
    for phase in ['train', 'val']:
      if phase == 'train':
        model.train()
      else:
        model.eval()

      for i, (images, g_truth) in enumerate(data_loader[phase]):
        images = images.to(device)
        g_truth = g_truth.to(device)

outer-most 和 inner-most for 循环在编写训练脚本时很常见。

我看到的最常见的模式是:

total_epochs = notebook.tqdm(range(no_epochs))

for epoch in total_epochs:
    # Training
    for i, (images, g_truth) in enumerate(train_data_loader):
        model.train()
        images = images.to(device)
        g_truth = g_truth.to(device)
        ...

    # Validating
    for i, (images, g_truth) in enumerate(val_data_loader):
        model.eval()
        images = images.to(device)
        g_truth = g_truth.to(device)
        ...

如果您需要使用之前的变量 data_loader,您可以将 train_data_loader 替换为 data_loader["train"],将 val_data_loader 替换为 data_loader["val"]

这种布局很常见,因为我们通常希望在验证时做一些与训练不同的事情。这也可以更好地构建代码并避免在 inner-most 循环的不同部分可能需要的大量 if phase == "train"。然而,这确实意味着您可能需要复制一些代码。这种权衡是普遍接受的,如果我们有 3 个或更多阶段,例如多个验证阶段或评估阶段,则可能会考虑您的原始代码。