9

我目前正在使用从 pytorch 预训练的 Faster-RCNN 模型(如 torchvision教程)中的迁移学习对自定义数据集进行对象检测。我想在每个时期结束时计算验证损失字典(如在训练模式下)。我可以在训练模式下运行模型进行验证,如下所示:

model.train()
for images, targets in data_loader_val:
    images = [image.to(device) for image in images]
    targets = [{k: v.to(device) for k, v in t.items()} for t in targets]

    with torch.no_grad():
         val_loss_dict = model(images, targets)
         print(val_loss_dict)

但我不认为这是“正确”的验证方式(导致一些特殊层,如 dropout 和 batch norm 在 eval/train 模式下的工作方式不同)。并且在 eval 模式下,模型返回预测的 bbox(如预期的那样)。我可以为此使用一些内置功能吗?

谢谢。

4

3 回答 3

2

这里有一些关于这个问题的讨论。结论是在训练模式下计算验证损失是绝对有效的。val loss的数值本身没有意义,只有趋势对防止过拟合很重要。因此,虽然训练模式确实改变了损失的数值,但它仍然可以使用。


然而,这里还有另一个效率问题,如果您在验证过程中还需要模型输出(通常用于计算 IoU、准确性等)。现在,torchvision 中的 RCNN 会根据训练/评估模式为您提供损失或输出。

更新: 不幸的是,我意识到此修复程序不起作用。必须修补所有子模块以计算损失和输出。太糟糕了。

我的肮脏解决方案是修补继承的GeneralizedRCNNFasterRCNN。问题出在 这一 行,在eager_outputs(). 解决方法:

    return losses, detections

model = fasterrcnn_resnet50_fpn() model.eager_outputs =
eager_outputs_patch

现在您可以在一次推理运行后获得两个输出:model.train() with torch.no_grad(): loss_dict, outputs = model(images, targets). # yaay, now we have both!请注意,您仍然需要将模型置于训练模式才能获得损失。在 eval modeGeneralizedRCNN的子模块 (rpn, roi_heads) 不计算任何损失,并且loss_dict 是空的。

于 2020-12-17T20:14:29.033 回答
1

我通过编辑Generalized RCNN、RPN、roi_heads 解决了这个问题。只需添加一个 if 语句来处理传递目标时即使不在训练模式下仍会计算损失。例如在 RPN 中:

losses = {}
    if self.training:
        assert targets is not None
        labels, matched_gt_boxes = self.assign_targets_to_anchors(anchors, targets)
        regression_targets = self.box_coder.encode(matched_gt_boxes, anchors)
        loss_objectness, loss_rpn_box_reg = self.compute_loss(
            objectness, pred_bbox_deltas, labels, regression_targets)
        losses = {
            "loss_objectness": loss_objectness,
            "loss_rpn_box_reg": loss_rpn_box_reg,
        }
    else:
        if targets is not None:
            labels, matched_gt_boxes = self.assign_targets_to_anchors(anchors, targets)
            regression_targets = self.box_coder.encode(matched_gt_boxes, anchors)
            loss_objectness, loss_rpn_box_reg = self.compute_loss(
                objectness, pred_bbox_deltas, labels, regression_targets)
            losses = {
                "loss_objectness": loss_objectness,
                "loss_rpn_box_reg": loss_rpn_box_reg,
            }
于 2021-02-11T14:19:33.683 回答
0

这会是一个好的实现吗?

@torch.no_grad()
def evaluate_loss(model, data_loader, device):
    val_loss = 0
    model.train()
    for images, targets in (data_loader):
        images = list(image.to(device) for image in images)
        targets = [{k: v.to(device) for k, v in t.items()} for t in targets]

        loss_dict = model(images, targets)

        losses = sum(loss for loss in loss_dict.values())

        # reduce losses over all GPUs for logging purposes
        loss_dict_reduced = utils.reduce_dict(loss_dict)
        losses_reduced = sum(loss for loss in loss_dict_reduced.values())
        val_loss += losses_reduced

    validation_loss = val_loss/ len(data_loader)

    return validation_loss

然后我在以下 for 循环中使用它:

for epoch in range(args.num_epochs):
        # train for one epoch, printing every 10 iterations
        train_one_epoch(model, optimizer, train_data_loader, device, epoch, print_freq=10)
    
        # update the learning rate
        lr_scheduler.step()

        validation_loss = evaluate_loss(model, valid_data_loader, device=device)
        print('validation loss', validation_loss)

        # evaluate on the test dataset
        evaluate(model, valid_data_loader, device=device)

我想知道它是否是一个正确的实现,可以在 lr_scheduler.step()...之后使用

于 2022-02-27T23:53:33.107 回答