Pytorch:涉及端到端雅可比矩阵范数的自定义损失

Pytorch:涉及端到端雅可比矩阵范数的自定义损失,pytorch,loss-function,autodiff,Pytorch,Loss Function,Autodiff,我想使用一个修改的损失函数来训练一个网络,该函数既有典型的分类损失(例如,nn.CrossEntropyLoss),也有对端到端雅可比矩阵的Frobenius范数的惩罚(即,如果f(x)是网络的输出,\nabla_x f(x)) 我已经实现了一个模型,可以使用nn.CrossEntropyLoss成功地学习。然而,当我尝试添加第二个损失函数(通过两次向后传球)时,我的训练循环运行,但模型从未学习。此外,如果我计算端到端雅可比矩阵,但不将其包含在损失函数中,那么模型也永远不会学习。在较高级别上,我

我想使用一个修改的损失函数来训练一个网络,该函数既有典型的分类损失(例如,
nn.CrossEntropyLoss
),也有对端到端雅可比矩阵的Frobenius范数的惩罚(即,如果f(x)是网络的输出,\nabla_x f(x))

我已经实现了一个模型,可以使用
nn.CrossEntropyLoss
成功地学习。然而,当我尝试添加第二个损失函数(通过两次向后传球)时,我的训练循环运行,但模型从未学习。此外,如果我计算端到端雅可比矩阵,但不将其包含在损失函数中,那么模型也永远不会学习。在较高级别上,我的代码执行以下操作:

  • 向前传递,从输入中获取预测类,
    yhat
  • 调用
    yhat.backward(火炬式火炬(适当形状),retain\u graph=True)
  • 雅可比范数=
    x.grad.data.norm(2)
  • 设置损失等于分类损失+标量系数*雅可比范数
  • 运行
    loss.backward()
  • 我怀疑我误解了
    backward()
    在运行两次时是如何工作的,但我还没有找到任何好的资源来澄清这一点

    生成一个工作示例需要太多,因此我尝试提取相关代码:

    def train_model(model, train_dataloader, optimizer, loss_fn, device=None):
    
        if device is None:
            device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    
        model.train()
        train_loss = 0
        correct = 0
        for batch_idx, (batch_input, batch_target) in enumerate(train_dataloader):
            batch_input, batch_target = batch_input.to(device), batch_target.to(device)
            optimizer.zero_grad()
            batch_input.requires_grad_(True)
            model_batch_output = model(batch_input)
            loss = loss_fn(model_output=model_batch_output, model_input=batch_input, model=model, target=batch_target)
            train_loss += loss.item()  # sum up batch loss
            loss.backward()
            optimizer.step()
    

    编辑1:我将以前的实现与
    .backward()
    交换为
    autograd.grad
    ,它显然有效!有什么区别

        def end_to_end_jacobian_loss(model_output, model_input):
            jacobian = autograd.grad(
                outputs=model_output['penultimate_layer'],
                inputs=model_input,
                grad_outputs=torch.ones(*model_output['penultimate_layer'].shape),
                retain_graph=True,
                only_inputs=True)[0]
            jacobian_norm = jacobian.norm(2)
            return jacobian_norm
    
        def end_to_end_jacobian_loss(model_output, model_input):
            jacobian = autograd.grad(
                outputs=model_output['penultimate_layer'],
                inputs=model_input,
                grad_outputs=torch.ones(*model_output['penultimate_layer'].shape),
                retain_graph=True,
                only_inputs=True)[0]
            jacobian_norm = jacobian.norm(2)
            return jacobian_norm