web-dev-qa-db-fra.com

Fonction de perte personnalisée dans PyTorch

J'ai trois questions simples.

  1. Que se passera-t-il si ma fonction de perte personnalisée n'est pas différenciable? Pytorch par erreur ou fera-t-il autre chose?
  2. Si je déclare une variable de perte dans ma fonction personnalisée qui représentera la perte finale du modèle, dois-je mettre requires_grad = True pour cette variable? ou ça n'a pas d'importance? Si cela n'a pas d'importance, alors pourquoi?
  3. J'ai vu des gens écrire parfois un calque séparé et calculer la perte dans la fonction forward. Quelle approche est préférable, écrire une fonction ou un calque? Pourquoi?

J'ai besoin d'une explication claire et agréable de ces questions pour résoudre mes confusions. Veuillez aider.

12
Wasi Ahmad

Laisse-moi essayer.

  1. Cela dépend de ce que vous entendez par "non différenciable". La première définition qui a du sens ici est que PyTorch ne sait pas calculer les gradients. Si vous essayez néanmoins de calculer des gradients, cela générera une erreur. Les deux scénarios possibles sont:

    a) Vous utilisez une opération PyTorch personnalisée pour laquelle les dégradés n'ont pas été implémentés, par ex. torch.svd(). Dans ce cas, vous obtiendrez un TypeError:

    import torch
    from torch.autograd import Function
    from torch.autograd import Variable
    
    A = Variable(torch.randn(10,10), requires_grad=True)
    u, s, v = torch.svd(A) # raises TypeError
    

    b) Vous avez implémenté votre propre opération, mais vous n'avez pas défini backward(). Dans ce cas, vous obtiendrez un NotImplementedError:

    class my_function(Function): # forgot to define backward()
    
        def forward(self, x):
            return 2 * x
    
    A = Variable(torch.randn(10,10))
    B = my_function()(A)
    C = torch.sum(B)
    C.backward() # will raise NotImplementedError
    

    La deuxième définition qui a du sens est "mathématiquement non différenciable". De toute évidence, une opération qui n'est pas mathématiquement différenciable ne devrait pas avoir une méthode backward() implémentée ou un sous-gradient sensible. Considérez par exemple torch.abs() dont la méthode backward() renvoie le sous-gradient 0 à 0:

    A = Variable(torch.Tensor([-1,0,1]),requires_grad=True)
    B = torch.abs(A)
    B.backward(torch.Tensor([1,1,1]))
    A.grad.data
    

    Pour ces cas, vous devez vous référer directement à la documentation PyTorch et extraire directement la méthode backward() de l'opération respective.

  2. Ça n'a pas d'importance. L'utilisation de requires_grad Permet d'éviter les calculs inutiles de gradients pour les sous-graphiques. S'il existe une seule entrée pour une opération qui nécessite un gradient, sa sortie nécessitera également un gradient. Inversement, seulement si toutes les entrées ne nécessitent pas de dégradé, la sortie ne le requiert pas non plus. Le calcul en arrière n'est jamais effectué dans les sous-graphiques, où toutes les variables ne nécessitaient pas de dégradés.

    Puisqu'il y a très probablement quelques Variables (par exemple les paramètres d'une sous-classe de nn.Module()), votre loss variable nécessitera également des gradients automatiquement. Cependant, vous devez noter que pour le fonctionnement exact de requires_grad (Voir ci-dessus à nouveau), vous ne pouvez de toute façon modifier que requires_grad Pour les variables feuille de votre graphique.

  3. Toutes les fonctions de perte PyTorch personnalisées sont des sous-classes de _Loss Qui est une sous-classe de nn.Module. Voir ici. Si vous souhaitez vous en tenir à cette convention, vous devez sous-classer _Loss Lors de la définition de votre fonction de perte personnalisée. Outre la cohérence, un avantage est que votre sous-classe lèvera un AssertionError, si vous n'avez pas marqué vos variables cibles comme volatile ou requires_grad = False. Un autre avantage est que vous pouvez imbriquer votre fonction de perte dans nn.Sequential(), car c'est un nn.Module Je recommanderais cette approche pour ces raisons.

11
mexmex